From 34956fa757491949dc68396d090580d500249546 Mon Sep 17 00:00:00 2001 From: Yaco Date: Thu, 22 Aug 2019 21:22:00 -0300 Subject: agrega ModernTimeline y actualiza otras extensiones --- www/wiki/LocalSettings.Reevo.php | 5 +- www/wiki/composer.local.json | 3 +- www/wiki/composer.lock | 278 +- www/wiki/extensions/Maps/.travis.install.sh | 1 + www/wiki/extensions/Maps/.travis.yml | 3 + www/wiki/extensions/Maps/DefaultSettings.php | 9 - www/wiki/extensions/Maps/INSTALL.md | 2 +- www/wiki/extensions/Maps/Maps.resources.php | 92 - www/wiki/extensions/Maps/RELEASE-NOTES.md | 13 + www/wiki/extensions/Maps/composer.json | 2 +- www/wiki/extensions/Maps/extension.json | 232 +- www/wiki/extensions/Maps/i18n/ar.json | 2 +- www/wiki/extensions/Maps/i18n/en.json | 2 +- www/wiki/extensions/Maps/i18n/fr.json | 2 +- www/wiki/extensions/Maps/i18n/pl.json | 2 + www/wiki/extensions/Maps/i18n/pt-br.json | 2 +- www/wiki/extensions/Maps/i18n/ru.json | 12 +- www/wiki/extensions/Maps/i18n/uk.json | 2 +- .../extensions/Maps/resources/ext.maps.common.js | 4 - .../extensions/Maps/resources/ext.maps.services.js | 86 - .../extensions/Maps/resources/ext.sm.common.js | 76 - .../Maps/resources/leaflet/ext.maps.leaflet.js | 5 +- www/wiki/extensions/Maps/resources/maps.common.js | 4 + .../extensions/Maps/resources/maps.services.js | 86 + www/wiki/extensions/Maps/resources/sm.common.js | 76 + www/wiki/extensions/Maps/src/MapsSetup.php | 256 - .../MediaWiki/ParserHooks/DisplayMapRenderer.php | 31 +- .../Maps/src/Presentation/MapHtmlBuilder.php | 37 + .../src/SemanticMW/ResultPrinters/MapPrinter.php | 36 +- .../ValueDescriptions/AreaDescription.php | 20 +- .../ValueDescriptions/CoordinateDescription.php | 16 +- .../tests/Integration/Parser/DisplayMapTest.php | 28 + www/wiki/extensions/Mermaid/.gitignore | 11 + www/wiki/extensions/Mermaid/.scrutinizer.yml | 28 + www/wiki/extensions/Mermaid/.travis.yml | 33 + www/wiki/extensions/Mermaid/COPYING | 103 + www/wiki/extensions/Mermaid/DefaultSettings.php | 20 + www/wiki/extensions/Mermaid/Mermaid.php | 194 + www/wiki/extensions/Mermaid/README.md | 52 + www/wiki/extensions/Mermaid/composer.json | 43 + www/wiki/extensions/Mermaid/docs/INSTALL.md | 71 + www/wiki/extensions/Mermaid/docs/ISSUE_TEMPLATE.md | 15 + .../Mermaid/docs/PULL_REQUEST_TEMPLATE.md | 12 + www/wiki/extensions/Mermaid/docs/RELEASE-NOTES.md | 42 + www/wiki/extensions/Mermaid/docs/USAGE.md | 27 + www/wiki/extensions/Mermaid/extension.json | 25 + www/wiki/extensions/Mermaid/i18n/Mermaid.magic.php | 13 + www/wiki/extensions/Mermaid/i18n/ar.json | 8 + www/wiki/extensions/Mermaid/i18n/ast.json | 8 + www/wiki/extensions/Mermaid/i18n/be-tarask.json | 8 + www/wiki/extensions/Mermaid/i18n/de.json | 8 + www/wiki/extensions/Mermaid/i18n/en.json | 8 + www/wiki/extensions/Mermaid/i18n/es.json | 8 + www/wiki/extensions/Mermaid/i18n/eu.json | 8 + www/wiki/extensions/Mermaid/i18n/fr.json | 8 + www/wiki/extensions/Mermaid/i18n/gl.json | 8 + www/wiki/extensions/Mermaid/i18n/ia.json | 8 + www/wiki/extensions/Mermaid/i18n/ko.json | 8 + www/wiki/extensions/Mermaid/i18n/li.json | 8 + www/wiki/extensions/Mermaid/i18n/mk.json | 8 + www/wiki/extensions/Mermaid/i18n/nb.json | 8 + www/wiki/extensions/Mermaid/i18n/pl.json | 8 + www/wiki/extensions/Mermaid/i18n/pt-br.json | 8 + www/wiki/extensions/Mermaid/i18n/pt.json | 8 + www/wiki/extensions/Mermaid/i18n/qqq.json | 8 + www/wiki/extensions/Mermaid/i18n/roa-tara.json | 8 + www/wiki/extensions/Mermaid/i18n/ru.json | 9 + www/wiki/extensions/Mermaid/i18n/sv.json | 8 + www/wiki/extensions/Mermaid/i18n/tr.json | 8 + www/wiki/extensions/Mermaid/i18n/uk.json | 8 + www/wiki/extensions/Mermaid/i18n/zh-hans.json | 8 + www/wiki/extensions/Mermaid/i18n/zh-hant.json | 8 + www/wiki/extensions/Mermaid/phpunit.xml.dist | 28 + www/wiki/extensions/Mermaid/res/d3/d3.min.js | 8 + www/wiki/extensions/Mermaid/res/d3/dagre-d3.min.js | 8113 +++++++++++ www/wiki/extensions/Mermaid/res/ext.mermaid.css | 12 + www/wiki/extensions/Mermaid/res/ext.mermaid.js | 38 + .../Mermaid/res/mermaid/7.0.5/mermaid.dark.min.css | 1 + .../res/mermaid/7.0.5/mermaid.forest.min.css | 1 + .../Mermaid/res/mermaid/7.0.5/mermaid.min.css | 1 + .../Mermaid/res/mermaid/7.0.5/mermaid.min.js | 28 + .../res/mermaid/7.0.5/mermaid.neutral.min.css | 1 + .../Mermaid/res/mermaid/7.0.5/mermaidAPI.min.js | 1 + .../Mermaid/res/mermaid/8.0.0/mermaid.dark.min.css | 1 + .../res/mermaid/8.0.0/mermaid.forest.min.css | 1 + .../Mermaid/res/mermaid/8.0.0/mermaid.min.css | 1 + .../Mermaid/res/mermaid/8.0.0/mermaid.min.js | 9 + .../res/mermaid/8.0.0/mermaid.neutral.min.css | 1 + .../Mermaid/res/mermaid/8.0.0/mermaidAPI.min.js | 1 + www/wiki/extensions/Mermaid/src/HookRegistry.php | 116 + .../Mermaid/src/MermaidParserFunction.php | 139 + www/wiki/extensions/Mermaid/tests/bootstrap.php | 31 + .../Integration/I18nJsonFileIntegrityTest.php | 77 + .../tests/phpunit/Unit/HookRegistryTest.php | 119 + .../phpunit/Unit/MermaidParserFunctionTest.php | 109 + .../Mermaid/tests/travis/install-mediawiki.sh | 43 + .../Mermaid/tests/travis/install-mermaid.sh | 72 + .../extensions/Mermaid/tests/travis/run-tests.sh | 14 + .../Mermaid/tests/travis/upload-coverage-report.sh | 10 + .../extensions/ModernTimeline/.scrutinizer.yml | 16 + .../extensions/ModernTimeline/.travis.install.sh | 46 + www/wiki/extensions/ModernTimeline/.travis.yml | 32 + www/wiki/extensions/ModernTimeline/COPYING | 347 + www/wiki/extensions/ModernTimeline/README.md | 150 + .../extensions/ModernTimeline/RELEASE-NOTES.md | 11 + www/wiki/extensions/ModernTimeline/composer.json | 44 + .../ModernTimeline/docs/ISSUE_TEMPLATE.md | 30 + www/wiki/extensions/ModernTimeline/extension.json | 106 + www/wiki/extensions/ModernTimeline/i18n/ar.json | 21 + www/wiki/extensions/ModernTimeline/i18n/de.json | 22 + www/wiki/extensions/ModernTimeline/i18n/en.json | 25 + www/wiki/extensions/ModernTimeline/i18n/qqq.json | 23 + .../extensions/ModernTimeline/phpunit.xml.dist | 29 + .../ModernTimeline/resources/modernTimeline.css | 4 + .../ModernTimeline/resources/modernTimeline.js | 26 + .../resources/vendor/font.default.css | 9 + .../ModernTimeline/resources/vendor/timeline.css | 27 + .../ModernTimeline/resources/vendor/timeline.js | 14042 +++++++++++++++++++ .../ModernTimeline/resources/vendor/tl-icons.eot | Bin 0 -> 14644 bytes .../ModernTimeline/resources/vendor/tl-icons.svg | 63 + .../ModernTimeline/resources/vendor/tl-icons.ttf | Bin 0 -> 14472 bytes .../ModernTimeline/resources/vendor/tl-icons.woff | Bin 0 -> 14548 bytes www/wiki/extensions/ModernTimeline/src/Event.php | 34 + .../extensions/ModernTimeline/src/JsonBuilder.php | 122 + .../ModernTimeline/src/ModernTimelinePrinter.php | 59 + .../ModernTimeline/src/ModernTimelineSetup.php | 30 + .../src/ResultFacade/PropertyValueCollection.php | 35 + .../src/ResultFacade/ResultSimplifier.php | 57 + .../ModernTimeline/src/ResultFacade/Subject.php | 39 + .../src/ResultFacade/SubjectCollection.php | 23 + .../src/SlidePresenter/SimpleSlidePresenter.php | 40 + .../src/SlidePresenter/SlidePresenter.php | 13 + .../src/SlidePresenter/TemplateSlidePresenter.php | 60 + .../ModernTimeline/src/TimelineOptions.php | 143 + .../ModernTimeline/src/TimelinePresenter.php | 105 + .../tests/Integration/OptionsTest.php | 150 + .../tests/System/JsonScript/escaping.json | 51 + .../tests/System/JsonScript/parameters.json | 35 + .../tests/System/JsonScript/query.json | 93 + .../tests/System/JsonScript/smwdoc.json | 37 + .../tests/System/JsonScript/template.json | 54 + .../ModernTimeline/tests/System/JsonScriptTest.php | 15 + .../ModernTimeline/tests/Unit/JsonBuilderTest.php | 162 + .../SlidePresenter/TemplateSlidePresenterTest.php | 81 + .../extensions/ModernTimeline/tests/bootstrap.php | 18 + .../extensions/SemanticResultFormats/.travis.yml | 23 +- .../SemanticResultFormats/DefaultSettings.php | 11 +- .../extensions/SemanticResultFormats/README.md | 5 +- .../SemanticResultFormats/RELEASE-NOTES.md | 24 +- .../extensions/SemanticResultFormats/Resources.php | 14 +- .../SemanticResultFormats.hooks.php | 20 +- .../SemanticResultFormats.php | 15 +- .../travis/install-semantic-result-formats.sh | 11 + .../extensions/SemanticResultFormats/composer.json | 9 +- .../SemanticResultFormats/docs/COMPATIBILITY.md | 111 + .../SemanticResultFormats/docs/INSTALL.md | 187 +- .../SemanticResultFormats/docs/README.md | 7 +- .../SemanticResultFormats/extension.json | 13 +- .../formats/Gantt/GanttPrinter.php | 284 + .../formats/Gantt/resources/ext.gantt.js | 81 + .../formats/Gantt/src/Gantt.php | 203 + .../formats/Gantt/src/GanttSection.php | 66 + .../formats/Gantt/src/GanttTask.php | 99 + .../SemanticResultFormats/formats/bibtex/README | 88 - .../formats/bibtex/SRF_BibTeX.php | 444 - .../resources/ext.srf.formats.eventcalendar.js | 7 + .../formats/dygraphs/SRF_Dygraphs.php | 28 +- .../formats/excel/SRF_Excel.php | 364 - .../formats/filtered/src/ResultItem.php | 2 +- .../formats/filtered/src/View/CalendarView.php | 2 +- .../formats/filtered/src/View/MapView.php | 18 +- .../formats/filtered/src/View/TableView.php | 12 +- .../formats/gallery/Gallery.php | 7 +- .../formats/graphviz/SRF_Graph.php | 385 - .../formats/media/MediaPlayer.php | 37 +- .../formats/outline/SRF_Outline.php | 257 - .../formats/slideshow/SRF_SlideShow.php | 6 +- .../formats/slideshow/SRF_SlideShowApi.php | 17 +- .../formats/spreadsheet/SpreadsheetPrinter.php | 461 + .../formats/tagcloud/TagCloud.php | 17 +- .../formats/timeline/SRF_Timeline.php | 2 +- .../resources/ext.srf.timeseries.flot.js | 5 +- .../formats/tree/TreeResultPrinter.php | 11 +- .../formats/valuerank/SRF_ValueRank.php | 4 +- .../extensions/SemanticResultFormats/i18n/ar.json | 33 +- .../extensions/SemanticResultFormats/i18n/ast.json | 6 - .../extensions/SemanticResultFormats/i18n/br.json | 4 +- .../extensions/SemanticResultFormats/i18n/ca.json | 3 +- .../extensions/SemanticResultFormats/i18n/cs.json | 271 +- .../extensions/SemanticResultFormats/i18n/da.json | 99 + .../extensions/SemanticResultFormats/i18n/de.json | 35 +- .../extensions/SemanticResultFormats/i18n/dsb.json | 1 - .../extensions/SemanticResultFormats/i18n/el.json | 3 - .../extensions/SemanticResultFormats/i18n/en.json | 38 +- .../extensions/SemanticResultFormats/i18n/es.json | 14 +- .../extensions/SemanticResultFormats/i18n/eu.json | 12 +- .../extensions/SemanticResultFormats/i18n/fa.json | 3 +- .../extensions/SemanticResultFormats/i18n/fi.json | 17 +- .../extensions/SemanticResultFormats/i18n/fr.json | 31 +- .../extensions/SemanticResultFormats/i18n/gl.json | 7 +- .../extensions/SemanticResultFormats/i18n/he.json | 12 +- .../extensions/SemanticResultFormats/i18n/hsb.json | 4 +- .../extensions/SemanticResultFormats/i18n/hu.json | 1 - .../extensions/SemanticResultFormats/i18n/ia.json | 1 - .../extensions/SemanticResultFormats/i18n/it.json | 10 +- .../extensions/SemanticResultFormats/i18n/ja.json | 2 - .../extensions/SemanticResultFormats/i18n/ko.json | 18 +- .../extensions/SemanticResultFormats/i18n/lb.json | 4 +- .../extensions/SemanticResultFormats/i18n/lt.json | 3 +- .../extensions/SemanticResultFormats/i18n/mk.json | 28 +- .../extensions/SemanticResultFormats/i18n/nb.json | 7 +- .../extensions/SemanticResultFormats/i18n/nl.json | 9 +- .../extensions/SemanticResultFormats/i18n/oc.json | 4 +- .../extensions/SemanticResultFormats/i18n/pl.json | 3 - .../extensions/SemanticResultFormats/i18n/pms.json | 1 - .../SemanticResultFormats/i18n/pt-br.json | 34 +- .../extensions/SemanticResultFormats/i18n/pt.json | 30 +- .../extensions/SemanticResultFormats/i18n/qqq.json | 26 +- .../SemanticResultFormats/i18n/roa-tara.json | 14 +- .../extensions/SemanticResultFormats/i18n/ru.json | 48 +- .../extensions/SemanticResultFormats/i18n/si.json | 1 - .../extensions/SemanticResultFormats/i18n/sv.json | 12 +- .../extensions/SemanticResultFormats/i18n/tl.json | 1 - .../extensions/SemanticResultFormats/i18n/uk.json | 22 +- .../SemanticResultFormats/i18n/zh-hans.json | 22 +- .../SemanticResultFormats/i18n/zh-hant.json | 26 +- .../SemanticResultFormats/resources/ext.srf.css | 47 +- .../src/BibTex/BibTexFileExportPrinter.php | 190 + .../SemanticResultFormats/src/BibTex/Item.php | 185 + .../SemanticResultFormats/src/BibTex/README.md | 91 + .../src/Graph/GraphPrinter.php | 419 + .../src/Outline/ListTreeBuilder.php | 162 + .../src/Outline/OutlineItem.php | 62 + .../src/Outline/OutlineResultPrinter.php | 139 + .../src/Outline/OutlineTree.php | 90 + .../SemanticResultFormats/src/Outline/README.md | 53 + .../src/Outline/TemplateBuilder.php | 156 + .../src/iCalendar/iCalendarFileExportPrinter.php | 14 +- .../SemanticResultFormats/src/vCard/Address.php | 21 +- .../SemanticResultFormats/src/vCard/vCard.php | 14 +- .../src/vCard/vCardFileExportPrinter.php | 543 +- .../SemanticResultFormats/tests/bootstrap.php | 4 + .../JSONScript/Fixtures/bibtex-01-0.bib | 2 + .../JSONScript/Fixtures/bibtex-01-1.bib | 10 + .../JSONScript/Fixtures/bibtex-01-2.bib | 8 + .../JSONScript/Fixtures/bibtex-01-3.bib | 16 + .../JSONScript/JsonTestCaseScriptRunnerTest.php | 35 +- .../JSONScript/TestCases/bibtex-01.json | 159 + .../JSONScript/TestCases/filtered-01.json | 8 +- .../JSONScript/TestCases/filtered-02.json | 4 +- .../Integration/JSONScript/TestCases/gantt-01.json | 167 + .../JSONScript/TestCases/outline-01.json | 101 + .../JSONScript/TestCases/outline-02.json | 105 + .../Integration/JSONScript/TestCases/tree-01.json | 112 +- .../Integration/JSONScript/TestCases/tree-02.json | 18 +- .../Integration/JSONScript/TestCases/tree-06.json | 36 +- .../Integration/JSONScript/TestCases/tree-07.json | 10 +- .../tests/phpunit/Integration/ResourcesTest.php | 12 +- .../tests/phpunit/ResultPrinterReflector.php | 55 + .../Unit/BibTex/BibTexFileExportPrinterTest.php | 149 + .../tests/phpunit/Unit/BibTex/ItemTest.php | 111 + .../tests/phpunit/Unit/Formats/ExcelTest.php | 45 - .../tests/phpunit/Unit/Formats/GanttTest.php | 30 + .../tests/phpunit/Unit/Formats/GraphTest.php | 2 +- .../tests/phpunit/Unit/Formats/SpreadsheetTest.php | 51 + .../phpunit/Unit/Outline/ListTreeBuilderTest.php | 41 + .../tests/phpunit/Unit/Outline/OutlineItemTest.php | 36 + .../Unit/Outline/OutlineResultPrinterTest.php | 96 + .../tests/phpunit/Unit/Outline/OutlineTreeTest.php | 49 + .../phpunit/Unit/Outline/TemplateBuilderTest.php | 43 + .../tests/phpunit/Unit/vCard/AddressTest.php | 12 +- .../Unit/vCard/vCardFileExportPrinterTest.php | 133 + .../tests/phpunit/Unit/vCard/vCardTest.php | 5 +- www/wiki/skins/bo/reevo.less | 12 + www/wiki/vendor/composer/autoload_classmap.php | 51 +- www/wiki/vendor/composer/autoload_files.php | 1 + www/wiki/vendor/composer/autoload_psr4.php | 3 + www/wiki/vendor/composer/autoload_static.php | 67 +- www/wiki/vendor/composer/installed.json | 178 +- www/wiki/vendor/composer/installers/composer.json | 2 + .../src/Composer/Installers/DframeInstaller.php | 10 + .../src/Composer/Installers/DrupalInstaller.php | 20 +- .../src/Composer/Installers/Installer.php | 4 + .../src/Composer/Installers/KnownInstaller.php | 11 + .../Composer/Installers/MicroweberInstaller.php | 100 +- .../src/Composer/Installers/MoodleInstaller.php | 1 + .../src/Composer/Installers/Redaxo5Installer.php | 10 + .../src/Composer/Installers/TaoInstaller.php | 12 + .../src/Composer/Installers/WHMCSInstaller.php | 13 +- .../param-processor/param-processor/.travis.yml | 4 +- .../param-processor/param-processor/IdeHelper.php | 10 + .../param-processor/param-processor/README.md | 80 +- .../param-processor/param-processor/composer.json | 14 +- .../param-processor/param-processor/phpcs.xml | 12 +- .../param-processor/param-processor/phpmd.xml | 16 +- .../src/Definition/DimensionParam.php | 65 - .../param-processor/src/Definition/StringParam.php | 4 +- .../param-processor/param-processor/src/IParam.php | 4 +- .../param-processor/src/IParamDefinition.php | 37 +- .../param-processor/src/Options.php | 6 + .../src/PackagePrivate/DimensionParser.php | 44 + .../param-processor/src/PackagePrivate/Param.php | 418 + .../src/PackagePrivate/ParamType.php | 58 + .../param-processor/param-processor/src/Param.php | 516 +- .../param-processor/src/ParamDefinition.php | 360 +- .../param-processor/src/ParamDefinitionFactory.php | 171 +- .../param-processor/src/ParameterTypes.php | 71 +- .../param-processor/src/ProcessingError.php | 12 +- .../param-processor/src/ProcessingErrorHandler.php | 28 +- .../param-processor/src/ProcessingResult.php | 13 + .../param-processor/src/Processor.php | 92 +- .../param-processor/src/Settings.php | 2 +- .../param-processor/src/TopologicalSort.php | 15 +- .../Integration/Definitions/BoolParamTest.php | 72 + .../Integration/Definitions/FloatParamTest.php | 72 + .../tests/Integration/Definitions/IntParamTest.php | 98 + .../Integration/Definitions/NumericParamTest.php | 69 + .../Definitions/ParamDefinitionTest.php | 166 + .../Integration/Definitions/StringParamTest.php | 45 + .../tests/Integration/DimensionTypeTest.php | 208 + .../tests/Integration/ProcessorTest.php | 486 + .../param-processor/tests/Unit/OptionsTest.php | 48 + .../Unit/PackagePrivate/DimensionParserTest.php | 51 + .../param-processor/tests/Unit/ParamAliasTest.php | 21 + .../tests/Unit/ParamDefinitionFactoryTest.php | 140 + .../param-processor/tests/Unit/ParamTest.php | 28 + .../tests/Unit/ParameterTypesTest.php | 75 + .../tests/Unit/ProcessingResultTest.php | 89 + .../param-processor/tests/Unit/SettingsTest.php | 42 + .../tests/phpunit/Definitions/BoolParamTest.php | 72 - .../phpunit/Definitions/DimensionParamTest.php | 106 - .../tests/phpunit/Definitions/FloatParamTest.php | 72 - .../tests/phpunit/Definitions/IntParamTest.php | 98 - .../tests/phpunit/Definitions/NumericParamTest.php | 69 - .../phpunit/Definitions/ParamDefinitionTest.php | 159 - .../tests/phpunit/Definitions/StringParamTest.php | 56 - .../param-processor/tests/phpunit/OptionsTest.php | 50 - .../tests/phpunit/ParamDefinitionFactoryTest.php | 23 - .../param-processor/tests/phpunit/ParamTest.php | 28 - .../tests/phpunit/ProcessingResultTest.php | 79 - .../tests/phpunit/ProcessorTest.php | 461 - .../param-processor/tests/phpunit/SettingsTest.php | 42 - 342 files changed, 35795 insertions(+), 5725 deletions(-) delete mode 100644 www/wiki/extensions/Maps/Maps.resources.php delete mode 100644 www/wiki/extensions/Maps/resources/ext.maps.common.js delete mode 100644 www/wiki/extensions/Maps/resources/ext.maps.services.js delete mode 100644 www/wiki/extensions/Maps/resources/ext.sm.common.js create mode 100644 www/wiki/extensions/Maps/resources/maps.common.js create mode 100644 www/wiki/extensions/Maps/resources/maps.services.js create mode 100644 www/wiki/extensions/Maps/resources/sm.common.js create mode 100644 www/wiki/extensions/Maps/src/Presentation/MapHtmlBuilder.php create mode 100644 www/wiki/extensions/Mermaid/.gitignore create mode 100644 www/wiki/extensions/Mermaid/.scrutinizer.yml create mode 100644 www/wiki/extensions/Mermaid/.travis.yml create mode 100644 www/wiki/extensions/Mermaid/COPYING create mode 100644 www/wiki/extensions/Mermaid/DefaultSettings.php create mode 100644 www/wiki/extensions/Mermaid/Mermaid.php create mode 100644 www/wiki/extensions/Mermaid/README.md create mode 100644 www/wiki/extensions/Mermaid/composer.json create mode 100644 www/wiki/extensions/Mermaid/docs/INSTALL.md create mode 100644 www/wiki/extensions/Mermaid/docs/ISSUE_TEMPLATE.md create mode 100644 www/wiki/extensions/Mermaid/docs/PULL_REQUEST_TEMPLATE.md create mode 100644 www/wiki/extensions/Mermaid/docs/RELEASE-NOTES.md create mode 100644 www/wiki/extensions/Mermaid/docs/USAGE.md create mode 100644 www/wiki/extensions/Mermaid/extension.json create mode 100644 www/wiki/extensions/Mermaid/i18n/Mermaid.magic.php create mode 100644 www/wiki/extensions/Mermaid/i18n/ar.json create mode 100644 www/wiki/extensions/Mermaid/i18n/ast.json create mode 100644 www/wiki/extensions/Mermaid/i18n/be-tarask.json create mode 100644 www/wiki/extensions/Mermaid/i18n/de.json create mode 100644 www/wiki/extensions/Mermaid/i18n/en.json create mode 100644 www/wiki/extensions/Mermaid/i18n/es.json create mode 100644 www/wiki/extensions/Mermaid/i18n/eu.json create mode 100644 www/wiki/extensions/Mermaid/i18n/fr.json create mode 100644 www/wiki/extensions/Mermaid/i18n/gl.json create mode 100644 www/wiki/extensions/Mermaid/i18n/ia.json create mode 100644 www/wiki/extensions/Mermaid/i18n/ko.json create mode 100644 www/wiki/extensions/Mermaid/i18n/li.json create mode 100644 www/wiki/extensions/Mermaid/i18n/mk.json create mode 100644 www/wiki/extensions/Mermaid/i18n/nb.json create mode 100644 www/wiki/extensions/Mermaid/i18n/pl.json create mode 100644 www/wiki/extensions/Mermaid/i18n/pt-br.json create mode 100644 www/wiki/extensions/Mermaid/i18n/pt.json create mode 100644 www/wiki/extensions/Mermaid/i18n/qqq.json create mode 100644 www/wiki/extensions/Mermaid/i18n/roa-tara.json create mode 100644 www/wiki/extensions/Mermaid/i18n/ru.json create mode 100644 www/wiki/extensions/Mermaid/i18n/sv.json create mode 100644 www/wiki/extensions/Mermaid/i18n/tr.json create mode 100644 www/wiki/extensions/Mermaid/i18n/uk.json create mode 100644 www/wiki/extensions/Mermaid/i18n/zh-hans.json create mode 100644 www/wiki/extensions/Mermaid/i18n/zh-hant.json create mode 100644 www/wiki/extensions/Mermaid/phpunit.xml.dist create mode 100644 www/wiki/extensions/Mermaid/res/d3/d3.min.js create mode 100644 www/wiki/extensions/Mermaid/res/d3/dagre-d3.min.js create mode 100644 www/wiki/extensions/Mermaid/res/ext.mermaid.css create mode 100644 www/wiki/extensions/Mermaid/res/ext.mermaid.js create mode 100644 www/wiki/extensions/Mermaid/res/mermaid/7.0.5/mermaid.dark.min.css create mode 100644 www/wiki/extensions/Mermaid/res/mermaid/7.0.5/mermaid.forest.min.css create mode 100644 www/wiki/extensions/Mermaid/res/mermaid/7.0.5/mermaid.min.css create mode 100644 www/wiki/extensions/Mermaid/res/mermaid/7.0.5/mermaid.min.js create mode 100644 www/wiki/extensions/Mermaid/res/mermaid/7.0.5/mermaid.neutral.min.css create mode 100644 www/wiki/extensions/Mermaid/res/mermaid/7.0.5/mermaidAPI.min.js create mode 100644 www/wiki/extensions/Mermaid/res/mermaid/8.0.0/mermaid.dark.min.css create mode 100644 www/wiki/extensions/Mermaid/res/mermaid/8.0.0/mermaid.forest.min.css create mode 100644 www/wiki/extensions/Mermaid/res/mermaid/8.0.0/mermaid.min.css create mode 100644 www/wiki/extensions/Mermaid/res/mermaid/8.0.0/mermaid.min.js create mode 100644 www/wiki/extensions/Mermaid/res/mermaid/8.0.0/mermaid.neutral.min.css create mode 100644 www/wiki/extensions/Mermaid/res/mermaid/8.0.0/mermaidAPI.min.js create mode 100644 www/wiki/extensions/Mermaid/src/HookRegistry.php create mode 100644 www/wiki/extensions/Mermaid/src/MermaidParserFunction.php create mode 100644 www/wiki/extensions/Mermaid/tests/bootstrap.php create mode 100644 www/wiki/extensions/Mermaid/tests/phpunit/Integration/I18nJsonFileIntegrityTest.php create mode 100644 www/wiki/extensions/Mermaid/tests/phpunit/Unit/HookRegistryTest.php create mode 100644 www/wiki/extensions/Mermaid/tests/phpunit/Unit/MermaidParserFunctionTest.php create mode 100644 www/wiki/extensions/Mermaid/tests/travis/install-mediawiki.sh create mode 100644 www/wiki/extensions/Mermaid/tests/travis/install-mermaid.sh create mode 100644 www/wiki/extensions/Mermaid/tests/travis/run-tests.sh create mode 100644 www/wiki/extensions/Mermaid/tests/travis/upload-coverage-report.sh create mode 100644 www/wiki/extensions/ModernTimeline/.scrutinizer.yml create mode 100644 www/wiki/extensions/ModernTimeline/.travis.install.sh create mode 100644 www/wiki/extensions/ModernTimeline/.travis.yml create mode 100644 www/wiki/extensions/ModernTimeline/COPYING create mode 100644 www/wiki/extensions/ModernTimeline/README.md create mode 100644 www/wiki/extensions/ModernTimeline/RELEASE-NOTES.md create mode 100644 www/wiki/extensions/ModernTimeline/composer.json create mode 100644 www/wiki/extensions/ModernTimeline/docs/ISSUE_TEMPLATE.md create mode 100644 www/wiki/extensions/ModernTimeline/extension.json create mode 100644 www/wiki/extensions/ModernTimeline/i18n/ar.json create mode 100644 www/wiki/extensions/ModernTimeline/i18n/de.json create mode 100644 www/wiki/extensions/ModernTimeline/i18n/en.json create mode 100644 www/wiki/extensions/ModernTimeline/i18n/qqq.json create mode 100644 www/wiki/extensions/ModernTimeline/phpunit.xml.dist create mode 100644 www/wiki/extensions/ModernTimeline/resources/modernTimeline.css create mode 100644 www/wiki/extensions/ModernTimeline/resources/modernTimeline.js create mode 100644 www/wiki/extensions/ModernTimeline/resources/vendor/font.default.css create mode 100644 www/wiki/extensions/ModernTimeline/resources/vendor/timeline.css create mode 100644 www/wiki/extensions/ModernTimeline/resources/vendor/timeline.js create mode 100644 www/wiki/extensions/ModernTimeline/resources/vendor/tl-icons.eot create mode 100644 www/wiki/extensions/ModernTimeline/resources/vendor/tl-icons.svg create mode 100644 www/wiki/extensions/ModernTimeline/resources/vendor/tl-icons.ttf create mode 100644 www/wiki/extensions/ModernTimeline/resources/vendor/tl-icons.woff create mode 100644 www/wiki/extensions/ModernTimeline/src/Event.php create mode 100644 www/wiki/extensions/ModernTimeline/src/JsonBuilder.php create mode 100644 www/wiki/extensions/ModernTimeline/src/ModernTimelinePrinter.php create mode 100644 www/wiki/extensions/ModernTimeline/src/ModernTimelineSetup.php create mode 100644 www/wiki/extensions/ModernTimeline/src/ResultFacade/PropertyValueCollection.php create mode 100644 www/wiki/extensions/ModernTimeline/src/ResultFacade/ResultSimplifier.php create mode 100644 www/wiki/extensions/ModernTimeline/src/ResultFacade/Subject.php create mode 100644 www/wiki/extensions/ModernTimeline/src/ResultFacade/SubjectCollection.php create mode 100644 www/wiki/extensions/ModernTimeline/src/SlidePresenter/SimpleSlidePresenter.php create mode 100644 www/wiki/extensions/ModernTimeline/src/SlidePresenter/SlidePresenter.php create mode 100644 www/wiki/extensions/ModernTimeline/src/SlidePresenter/TemplateSlidePresenter.php create mode 100644 www/wiki/extensions/ModernTimeline/src/TimelineOptions.php create mode 100644 www/wiki/extensions/ModernTimeline/src/TimelinePresenter.php create mode 100644 www/wiki/extensions/ModernTimeline/tests/Integration/OptionsTest.php create mode 100644 www/wiki/extensions/ModernTimeline/tests/System/JsonScript/escaping.json create mode 100644 www/wiki/extensions/ModernTimeline/tests/System/JsonScript/parameters.json create mode 100644 www/wiki/extensions/ModernTimeline/tests/System/JsonScript/query.json create mode 100644 www/wiki/extensions/ModernTimeline/tests/System/JsonScript/smwdoc.json create mode 100644 www/wiki/extensions/ModernTimeline/tests/System/JsonScript/template.json create mode 100644 www/wiki/extensions/ModernTimeline/tests/System/JsonScriptTest.php create mode 100644 www/wiki/extensions/ModernTimeline/tests/Unit/JsonBuilderTest.php create mode 100644 www/wiki/extensions/ModernTimeline/tests/Unit/SlidePresenter/TemplateSlidePresenterTest.php create mode 100644 www/wiki/extensions/ModernTimeline/tests/bootstrap.php create mode 100644 www/wiki/extensions/SemanticResultFormats/docs/COMPATIBILITY.md create mode 100644 www/wiki/extensions/SemanticResultFormats/formats/Gantt/GanttPrinter.php create mode 100644 www/wiki/extensions/SemanticResultFormats/formats/Gantt/resources/ext.gantt.js create mode 100644 www/wiki/extensions/SemanticResultFormats/formats/Gantt/src/Gantt.php create mode 100644 www/wiki/extensions/SemanticResultFormats/formats/Gantt/src/GanttSection.php create mode 100644 www/wiki/extensions/SemanticResultFormats/formats/Gantt/src/GanttTask.php delete mode 100644 www/wiki/extensions/SemanticResultFormats/formats/bibtex/README delete mode 100644 www/wiki/extensions/SemanticResultFormats/formats/bibtex/SRF_BibTeX.php delete mode 100644 www/wiki/extensions/SemanticResultFormats/formats/excel/SRF_Excel.php delete mode 100644 www/wiki/extensions/SemanticResultFormats/formats/graphviz/SRF_Graph.php delete mode 100644 www/wiki/extensions/SemanticResultFormats/formats/outline/SRF_Outline.php create mode 100644 www/wiki/extensions/SemanticResultFormats/formats/spreadsheet/SpreadsheetPrinter.php create mode 100644 www/wiki/extensions/SemanticResultFormats/i18n/da.json create mode 100644 www/wiki/extensions/SemanticResultFormats/src/BibTex/BibTexFileExportPrinter.php create mode 100644 www/wiki/extensions/SemanticResultFormats/src/BibTex/Item.php create mode 100644 www/wiki/extensions/SemanticResultFormats/src/BibTex/README.md create mode 100644 www/wiki/extensions/SemanticResultFormats/src/Graph/GraphPrinter.php create mode 100644 www/wiki/extensions/SemanticResultFormats/src/Outline/ListTreeBuilder.php create mode 100644 www/wiki/extensions/SemanticResultFormats/src/Outline/OutlineItem.php create mode 100644 www/wiki/extensions/SemanticResultFormats/src/Outline/OutlineResultPrinter.php create mode 100644 www/wiki/extensions/SemanticResultFormats/src/Outline/OutlineTree.php create mode 100644 www/wiki/extensions/SemanticResultFormats/src/Outline/README.md create mode 100644 www/wiki/extensions/SemanticResultFormats/src/Outline/TemplateBuilder.php create mode 100644 www/wiki/extensions/SemanticResultFormats/tests/phpunit/Integration/JSONScript/Fixtures/bibtex-01-0.bib create mode 100644 www/wiki/extensions/SemanticResultFormats/tests/phpunit/Integration/JSONScript/Fixtures/bibtex-01-1.bib create mode 100644 www/wiki/extensions/SemanticResultFormats/tests/phpunit/Integration/JSONScript/Fixtures/bibtex-01-2.bib create mode 100644 www/wiki/extensions/SemanticResultFormats/tests/phpunit/Integration/JSONScript/Fixtures/bibtex-01-3.bib create mode 100644 www/wiki/extensions/SemanticResultFormats/tests/phpunit/Integration/JSONScript/TestCases/bibtex-01.json create mode 100644 www/wiki/extensions/SemanticResultFormats/tests/phpunit/Integration/JSONScript/TestCases/gantt-01.json create mode 100644 www/wiki/extensions/SemanticResultFormats/tests/phpunit/Integration/JSONScript/TestCases/outline-01.json create mode 100644 www/wiki/extensions/SemanticResultFormats/tests/phpunit/Integration/JSONScript/TestCases/outline-02.json create mode 100644 www/wiki/extensions/SemanticResultFormats/tests/phpunit/ResultPrinterReflector.php create mode 100644 www/wiki/extensions/SemanticResultFormats/tests/phpunit/Unit/BibTex/BibTexFileExportPrinterTest.php create mode 100644 www/wiki/extensions/SemanticResultFormats/tests/phpunit/Unit/BibTex/ItemTest.php delete mode 100644 www/wiki/extensions/SemanticResultFormats/tests/phpunit/Unit/Formats/ExcelTest.php create mode 100644 www/wiki/extensions/SemanticResultFormats/tests/phpunit/Unit/Formats/GanttTest.php create mode 100644 www/wiki/extensions/SemanticResultFormats/tests/phpunit/Unit/Formats/SpreadsheetTest.php create mode 100644 www/wiki/extensions/SemanticResultFormats/tests/phpunit/Unit/Outline/ListTreeBuilderTest.php create mode 100644 www/wiki/extensions/SemanticResultFormats/tests/phpunit/Unit/Outline/OutlineItemTest.php create mode 100644 www/wiki/extensions/SemanticResultFormats/tests/phpunit/Unit/Outline/OutlineResultPrinterTest.php create mode 100644 www/wiki/extensions/SemanticResultFormats/tests/phpunit/Unit/Outline/OutlineTreeTest.php create mode 100644 www/wiki/extensions/SemanticResultFormats/tests/phpunit/Unit/Outline/TemplateBuilderTest.php create mode 100644 www/wiki/extensions/SemanticResultFormats/tests/phpunit/Unit/vCard/vCardFileExportPrinterTest.php create mode 100644 www/wiki/vendor/composer/installers/src/Composer/Installers/DframeInstaller.php create mode 100644 www/wiki/vendor/composer/installers/src/Composer/Installers/KnownInstaller.php create mode 100644 www/wiki/vendor/composer/installers/src/Composer/Installers/Redaxo5Installer.php create mode 100644 www/wiki/vendor/composer/installers/src/Composer/Installers/TaoInstaller.php create mode 100644 www/wiki/vendor/param-processor/param-processor/IdeHelper.php delete mode 100644 www/wiki/vendor/param-processor/param-processor/src/Definition/DimensionParam.php create mode 100644 www/wiki/vendor/param-processor/param-processor/src/PackagePrivate/DimensionParser.php create mode 100644 www/wiki/vendor/param-processor/param-processor/src/PackagePrivate/Param.php create mode 100644 www/wiki/vendor/param-processor/param-processor/src/PackagePrivate/ParamType.php create mode 100644 www/wiki/vendor/param-processor/param-processor/tests/Integration/Definitions/BoolParamTest.php create mode 100644 www/wiki/vendor/param-processor/param-processor/tests/Integration/Definitions/FloatParamTest.php create mode 100644 www/wiki/vendor/param-processor/param-processor/tests/Integration/Definitions/IntParamTest.php create mode 100644 www/wiki/vendor/param-processor/param-processor/tests/Integration/Definitions/NumericParamTest.php create mode 100644 www/wiki/vendor/param-processor/param-processor/tests/Integration/Definitions/ParamDefinitionTest.php create mode 100644 www/wiki/vendor/param-processor/param-processor/tests/Integration/Definitions/StringParamTest.php create mode 100644 www/wiki/vendor/param-processor/param-processor/tests/Integration/DimensionTypeTest.php create mode 100644 www/wiki/vendor/param-processor/param-processor/tests/Integration/ProcessorTest.php create mode 100644 www/wiki/vendor/param-processor/param-processor/tests/Unit/OptionsTest.php create mode 100644 www/wiki/vendor/param-processor/param-processor/tests/Unit/PackagePrivate/DimensionParserTest.php create mode 100644 www/wiki/vendor/param-processor/param-processor/tests/Unit/ParamAliasTest.php create mode 100644 www/wiki/vendor/param-processor/param-processor/tests/Unit/ParamDefinitionFactoryTest.php create mode 100644 www/wiki/vendor/param-processor/param-processor/tests/Unit/ParamTest.php create mode 100644 www/wiki/vendor/param-processor/param-processor/tests/Unit/ParameterTypesTest.php create mode 100644 www/wiki/vendor/param-processor/param-processor/tests/Unit/ProcessingResultTest.php create mode 100644 www/wiki/vendor/param-processor/param-processor/tests/Unit/SettingsTest.php delete mode 100644 www/wiki/vendor/param-processor/param-processor/tests/phpunit/Definitions/BoolParamTest.php delete mode 100644 www/wiki/vendor/param-processor/param-processor/tests/phpunit/Definitions/DimensionParamTest.php delete mode 100644 www/wiki/vendor/param-processor/param-processor/tests/phpunit/Definitions/FloatParamTest.php delete mode 100644 www/wiki/vendor/param-processor/param-processor/tests/phpunit/Definitions/IntParamTest.php delete mode 100644 www/wiki/vendor/param-processor/param-processor/tests/phpunit/Definitions/NumericParamTest.php delete mode 100644 www/wiki/vendor/param-processor/param-processor/tests/phpunit/Definitions/ParamDefinitionTest.php delete mode 100644 www/wiki/vendor/param-processor/param-processor/tests/phpunit/Definitions/StringParamTest.php delete mode 100644 www/wiki/vendor/param-processor/param-processor/tests/phpunit/OptionsTest.php delete mode 100644 www/wiki/vendor/param-processor/param-processor/tests/phpunit/ParamDefinitionFactoryTest.php delete mode 100644 www/wiki/vendor/param-processor/param-processor/tests/phpunit/ParamTest.php delete mode 100644 www/wiki/vendor/param-processor/param-processor/tests/phpunit/ProcessingResultTest.php delete mode 100644 www/wiki/vendor/param-processor/param-processor/tests/phpunit/ProcessorTest.php delete mode 100644 www/wiki/vendor/param-processor/param-processor/tests/phpunit/SettingsTest.php (limited to 'www/wiki') diff --git a/www/wiki/LocalSettings.Reevo.php b/www/wiki/LocalSettings.Reevo.php index c080b226..1028b1e3 100644 --- a/www/wiki/LocalSettings.Reevo.php +++ b/www/wiki/LocalSettings.Reevo.php @@ -163,6 +163,9 @@ enableSemantics( $REEVO_URL ); wfLoadExtension( 'SemanticResultFormats' ); wfLoadExtension( 'SemanticFormsSelect' ); +## ModernTimeline +wfLoadExtension( 'ModernTimeline' ); + ## PageForms wfLoadExtension( 'PageForms' ); @@ -289,7 +292,7 @@ wfLoadExtension( 'MsUpload' ); $wgMSU_useDragDrop = true; // Should the drag & drop area be shown? (Not set by default) $wgMSU_showAutoCat = true; // Files uploaded while editing a category page will be added to that category $wgMSU_checkAutoCat = true; // Whether the checkbox for adding a category to a page is checked by default -$wgMSU_useMsLinks = false; // Insert links in Extension:MsLinks style? +$wgMSU_useMsLinks = true; // Insert links in Extension:MsLinks style? $wgMSU_confirmReplace = true; // Show the "Replace file?" checkbox $wgMSU_imgParams = 'thumb|none'; // Any image parameter separated by a semicolon. Examples are: {width}px, left, right, center, none, border, frameless, frame, thumb etc etc diff --git a/www/wiki/composer.local.json b/www/wiki/composer.local.json index 21e919d5..c16110f3 100644 --- a/www/wiki/composer.local.json +++ b/www/wiki/composer.local.json @@ -4,6 +4,7 @@ "mediawiki/semantic-result-formats": "~3.0", "mediawiki/semantic-forms-select": "~3.0", "mediawiki/maps": "^7", - "mediawiki/chameleon-skin": "1.7.1" + "mediawiki/chameleon-skin": "1.7.1", + "professional-wiki/modern-timeline": "~1.0" } } diff --git a/www/wiki/composer.lock b/www/wiki/composer.lock index 22eabd3d..a1a6f305 100644 --- a/www/wiki/composer.lock +++ b/www/wiki/composer.lock @@ -8,16 +8,16 @@ "packages": [ { "name": "composer/installers", - "version": "v1.6.0", + "version": "v1.7.0", "source": { "type": "git", "url": "https://github.com/composer/installers.git", - "reference": "cfcca6b1b60bc4974324efb5783c13dca6932b5b" + "reference": "141b272484481432cda342727a427dc1e206bfa0" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/composer/installers/zipball/cfcca6b1b60bc4974324efb5783c13dca6932b5b", - "reference": "cfcca6b1b60bc4974324efb5783c13dca6932b5b", + "url": "https://api.github.com/repos/composer/installers/zipball/141b272484481432cda342727a427dc1e206bfa0", + "reference": "141b272484481432cda342727a427dc1e206bfa0", "shasum": "" }, "require": { @@ -73,6 +73,7 @@ "RadPHP", "SMF", "Thelia", + "Whmcs", "WolfCMS", "agl", "aimeos", @@ -95,6 +96,7 @@ "installer", "itop", "joomla", + "known", "kohana", "laravel", "lavalite", @@ -124,7 +126,7 @@ "zend", "zikula" ], - "time": "2018-08-27T06:10:37+00:00" + "time": "2019-08-12T15:00:31+00:00" }, { "name": "composer/semver", @@ -1104,16 +1106,16 @@ }, { "name": "mediawiki/maps", - "version": "7.3.2", + "version": "7.4.0", "source": { "type": "git", "url": "https://github.com/JeroenDeDauw/Maps.git", - "reference": "57e5b822dacb7ed4eacb4a2d996ce8e6be4ae13a" + "reference": "d4d195d5f3e57d5361882b94284286b7599e23dc" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/JeroenDeDauw/Maps/zipball/57e5b822dacb7ed4eacb4a2d996ce8e6be4ae13a", - "reference": "57e5b822dacb7ed4eacb4a2d996ce8e6be4ae13a", + "url": "https://api.github.com/repos/JeroenDeDauw/Maps/zipball/d4d195d5f3e57d5361882b94284286b7599e23dc", + "reference": "d4d195d5f3e57d5361882b94284286b7599e23dc", "shasum": "" }, "require": { @@ -1125,7 +1127,7 @@ "jeroen/simple-geocoder": "~1.2", "mediawiki/parser-hooks": "~1.5", "mediawiki/validator": "~2.2", - "param-processor/param-processor": "~1.4.2", + "param-processor/param-processor": "^1.4.2", "php": ">=7.1" }, "replace": { @@ -1179,7 +1181,58 @@ "mediawiki", "osm" ], - "time": "2019-07-25T04:17:46+00:00" + "time": "2019-08-09T18:15:24+00:00" + }, + { + "name": "mediawiki/mermaid", + "version": "2.1.1", + "source": { + "type": "git", + "url": "https://github.com/SemanticMediaWiki/Mermaid.git", + "reference": "9fe1279a9aed7afdecbbed4c183a81302ce63dce" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/SemanticMediaWiki/Mermaid/zipball/9fe1279a9aed7afdecbbed4c183a81302ce63dce", + "reference": "9fe1279a9aed7afdecbbed4c183a81302ce63dce", + "shasum": "" + }, + "require": { + "composer/installers": "1.*,>=1.0.1", + "php": ">=5.6.0" + }, + "type": "mediawiki-extension", + "extra": { + "branch-alias": { + "dev-master": "2.x-dev" + } + }, + "autoload": { + "files": [ + "Mermaid.php" + ], + "psr-4": { + "Mermaid\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "GPL-2.0-or-later" + ], + "authors": [ + { + "name": "James Hong Kong", + "role": "Developer" + } + ], + "description": "Provides a parser function to generate diagrams and flowcharts with the help of the mermaid script language", + "homepage": "https://github.com/SemanticMediaWiki/Mermaid", + "keywords": [ + "mediawiki", + "mermaid", + "parser function" + ], + "time": "2019-05-20T10:17:44+00:00" }, { "name": "mediawiki/page-forms", @@ -1465,34 +1518,36 @@ }, { "name": "mediawiki/semantic-result-formats", - "version": "3.0.1", + "version": "3.1.0", "source": { "type": "git", "url": "https://github.com/SemanticMediaWiki/SemanticResultFormats.git", - "reference": "e1956df8796b8df9a156083801e7dcc0ab2b9a25" + "reference": "aa0649f28e536fd9da363dcdff9405dc402ff275" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/SemanticMediaWiki/SemanticResultFormats/zipball/e1956df8796b8df9a156083801e7dcc0ab2b9a25", - "reference": "e1956df8796b8df9a156083801e7dcc0ab2b9a25", + "url": "https://api.github.com/repos/SemanticMediaWiki/SemanticResultFormats/zipball/aa0649f28e536fd9da363dcdff9405dc402ff275", + "reference": "aa0649f28e536fd9da363dcdff9405dc402ff275", "shasum": "" }, "require": { "composer/installers": "1.*,>=1.0.1", "data-values/geo": "~4.0|~3.0|~2.0", + "mediawiki/mermaid": "~2.1", "mediawiki/semantic-media-wiki": "~3.0", "nicmart/tree": "^0.2.7", - "php": ">=5.6.0", + "php": ">=7.0", "symfony/css-selector": "^3.3" }, "suggest": { "mediawiki/graph-viz": "Required for 'format=graph' and 'format=process'", - "phpoffice/phpexcel": "Required for 'format=excel'" + "phpoffice/phpexcel": "Required for 'format=excel'", + "phpoffice/phpspreadsheet": "Required for 'format=spreadsheet'" }, "type": "mediawiki-extension", "extra": { "branch-alias": { - "dev-master": "3.0.x-dev" + "dev-master": "3.1.x-dev" } }, "autoload": { @@ -1547,7 +1602,7 @@ "mediawiki", "wiki" ], - "time": "2019-03-27T22:43:56+00:00" + "time": "2019-08-18T17:55:25+00:00" }, { "name": "mediawiki/validator", @@ -2104,16 +2159,16 @@ }, { "name": "param-processor/param-processor", - "version": "1.4.2", + "version": "1.10.0", "source": { "type": "git", "url": "https://github.com/JeroenDeDauw/ParamProcessor.git", - "reference": "40cd2140aa339f59c5500760a9f6f1fa6c0ef0a6" + "reference": "5dec3e41bdc684640faf334db650e9bd32092e23" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/JeroenDeDauw/ParamProcessor/zipball/40cd2140aa339f59c5500760a9f6f1fa6c0ef0a6", - "reference": "40cd2140aa339f59c5500760a9f6f1fa6c0ef0a6", + "url": "https://api.github.com/repos/JeroenDeDauw/ParamProcessor/zipball/5dec3e41bdc684640faf334db650e9bd32092e23", + "reference": "5dec3e41bdc684640faf334db650e9bd32092e23", "shasum": "" }, "require": { @@ -2132,17 +2187,13 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "1.4.x-dev" + "dev-master": "1.x-dev" } }, "autoload": { "psr-4": { "ParamProcessor\\": "src" }, - "classmap": [ - "tests/phpunit/Definitions/ParamDefinitionTest.php", - "tests/phpunit/Definitions/NumericParamTest.php" - ], "files": [ "DefaultConfig.php" ] @@ -2168,7 +2219,7 @@ "mediawiki", "validator" ], - "time": "2018-11-26T05:02:09+00:00" + "time": "2019-08-03T15:18:54+00:00" }, { "name": "pear/console_getopt", @@ -2533,6 +2584,63 @@ ], "time": "2015-02-10T20:07:52+00:00" }, + { + "name": "professional-wiki/modern-timeline", + "version": "1.0.0", + "source": { + "type": "git", + "url": "https://github.com/ProfessionalWiki/ModernTimeline.git", + "reference": "458b66e8c1e4d05ba3819a0f73b655b97203144c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/ProfessionalWiki/ModernTimeline/zipball/458b66e8c1e4d05ba3819a0f73b655b97203144c", + "reference": "458b66e8c1e4d05ba3819a0f73b655b97203144c", + "shasum": "" + }, + "require": { + "composer/installers": "^1.0.1", + "param-processor/param-processor": "~1.10", + "php": ">=7.1" + }, + "type": "mediawiki-extension", + "autoload": { + "psr-4": { + "ModernTimeline\\": "src/", + "ModernTimeline\\Tests\\": "tests/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "GPL-2.0-or-later" + ], + "authors": [ + { + "name": "Professional.Wiki", + "role": "Creator", + "email": "info@professional.wiki", + "homepage": "https://professional.wiki" + }, + { + "name": "Jeroen De Dauw", + "role": "Creator and lead developer", + "email": "jeroendedauw@gmail.com", + "homepage": "https://www.entropywins.wtf" + } + ], + "description": "Adds a modern timeline visualization as Semantic MediaWiki result format", + "homepage": "https://github.com/ProfessionalWiki/ModernTimeline", + "keywords": [ + "Result format", + "Semantic MediaWiki", + "mediawiki", + "timeline", + "timelineJS3", + "visualization", + "wiki" + ], + "time": "2019-08-16T00:14:56+00:00" + }, { "name": "psr/log", "version": "1.0.2", @@ -4405,16 +4513,16 @@ }, { "name": "myclabs/deep-copy", - "version": "1.9.1", + "version": "1.9.3", "source": { "type": "git", "url": "https://github.com/myclabs/DeepCopy.git", - "reference": "e6828efaba2c9b79f4499dae1d66ef8bfa7b2b72" + "reference": "007c053ae6f31bba39dfa19a7726f56e9763bbea" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/e6828efaba2c9b79f4499dae1d66ef8bfa7b2b72", - "reference": "e6828efaba2c9b79f4499dae1d66ef8bfa7b2b72", + "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/007c053ae6f31bba39dfa19a7726f56e9763bbea", + "reference": "007c053ae6f31bba39dfa19a7726f56e9763bbea", "shasum": "" }, "require": { @@ -4449,7 +4557,7 @@ "object", "object graph" ], - "time": "2019-04-07T13:18:21+00:00" + "time": "2019-08-09T12:45:53+00:00" }, { "name": "nikic/php-parser", @@ -4580,18 +4688,18 @@ "authors": [ { "name": "Arne Blankerts", - "email": "arne@blankerts.de", - "role": "Developer" + "role": "Developer", + "email": "arne@blankerts.de" }, { "name": "Sebastian Heuer", - "email": "sebastian@phpeople.de", - "role": "Developer" + "role": "Developer", + "email": "sebastian@phpeople.de" }, { "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "Developer" + "role": "Developer", + "email": "sebastian@phpunit.de" } ], "description": "Component for reading phar.io manifest information from a PHP Archive (PHAR)", @@ -4627,18 +4735,18 @@ "authors": [ { "name": "Arne Blankerts", - "email": "arne@blankerts.de", - "role": "Developer" + "role": "Developer", + "email": "arne@blankerts.de" }, { "name": "Sebastian Heuer", - "email": "sebastian@phpeople.de", - "role": "Developer" + "role": "Developer", + "email": "sebastian@phpeople.de" }, { "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "Developer" + "role": "Developer", + "email": "sebastian@phpunit.de" } ], "description": "Library for handling version information and constraints", @@ -4957,8 +5065,8 @@ "authors": [ { "name": "Sebastian Bergmann", - "email": "sb@sebastian-bergmann.de", - "role": "lead" + "role": "lead", + "email": "sb@sebastian-bergmann.de" } ], "description": "FilterIterator implementation that filters files based on a list of suffixes.", @@ -4999,8 +5107,8 @@ "authors": [ { "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "lead" + "role": "lead", + "email": "sebastian@phpunit.de" } ], "description": "Simple template engine.", @@ -5048,8 +5156,8 @@ "authors": [ { "name": "Sebastian Bergmann", - "email": "sb@sebastian-bergmann.de", - "role": "lead" + "role": "lead", + "email": "sb@sebastian-bergmann.de" } ], "description": "Utility class for timing", @@ -5538,16 +5646,16 @@ }, { "name": "sebastian/exporter", - "version": "3.1.0", + "version": "3.1.1", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/exporter.git", - "reference": "234199f4528de6d12aaa58b612e98f7d36adb937" + "reference": "06a9a5947f47b3029d76118eb5c22802e5869687" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/234199f4528de6d12aaa58b612e98f7d36adb937", - "reference": "234199f4528de6d12aaa58b612e98f7d36adb937", + "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/06a9a5947f47b3029d76118eb5c22802e5869687", + "reference": "06a9a5947f47b3029d76118eb5c22802e5869687", "shasum": "" }, "require": { @@ -5574,6 +5682,10 @@ "BSD-3-Clause" ], "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + }, { "name": "Jeff Welch", "email": "whatthejeff@gmail.com" @@ -5582,17 +5694,13 @@ "name": "Volker Dusch", "email": "github@wallbash.com" }, - { - "name": "Bernhard Schussek", - "email": "bschussek@2bepublished.at" - }, - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" - }, { "name": "Adam Harvey", "email": "aharvey@php.net" + }, + { + "name": "Bernhard Schussek", + "email": "bschussek@gmail.com" } ], "description": "Provides the functionality to export PHP variables for visualization", @@ -5601,7 +5709,7 @@ "export", "exporter" ], - "time": "2017-04-03T13:19:02+00:00" + "time": "2019-08-11T12:43:14+00:00" }, { "name": "sebastian/global-state", @@ -5876,8 +5984,8 @@ "authors": [ { "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "lead" + "role": "lead", + "email": "sebastian@phpunit.de" } ], "description": "Library that helps with managing the version number of Git-hosted PHP projects", @@ -6065,16 +6173,16 @@ }, { "name": "symfony/polyfill-ctype", - "version": "v1.11.0", + "version": "v1.12.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-ctype.git", - "reference": "82ebae02209c21113908c229e9883c419720738a" + "reference": "550ebaac289296ce228a706d0867afc34687e3f4" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/82ebae02209c21113908c229e9883c419720738a", - "reference": "82ebae02209c21113908c229e9883c419720738a", + "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/550ebaac289296ce228a706d0867afc34687e3f4", + "reference": "550ebaac289296ce228a706d0867afc34687e3f4", "shasum": "" }, "require": { @@ -6086,7 +6194,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "1.11-dev" + "dev-master": "1.12-dev" } }, "autoload": { @@ -6102,13 +6210,13 @@ "MIT" ], "authors": [ - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - }, { "name": "Gert de Pagter", "email": "BackEndTea@gmail.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" } ], "description": "Symfony polyfill for ctype functions", @@ -6119,20 +6227,20 @@ "polyfill", "portable" ], - "time": "2019-02-06T07:57:58+00:00" + "time": "2019-08-06T08:03:45+00:00" }, { "name": "symfony/polyfill-mbstring", - "version": "v1.11.0", + "version": "v1.12.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-mbstring.git", - "reference": "fe5e94c604826c35a32fa832f35bd036b6799609" + "reference": "b42a2f66e8f1b15ccf25652c3424265923eb4f17" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/fe5e94c604826c35a32fa832f35bd036b6799609", - "reference": "fe5e94c604826c35a32fa832f35bd036b6799609", + "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/b42a2f66e8f1b15ccf25652c3424265923eb4f17", + "reference": "b42a2f66e8f1b15ccf25652c3424265923eb4f17", "shasum": "" }, "require": { @@ -6144,7 +6252,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "1.11-dev" + "dev-master": "1.12-dev" } }, "autoload": { @@ -6178,7 +6286,7 @@ "portable", "shim" ], - "time": "2019-02-06T07:57:58+00:00" + "time": "2019-08-06T08:03:45+00:00" }, { "name": "symfony/var-dumper", @@ -6282,8 +6390,8 @@ "authors": [ { "name": "Arne Blankerts", - "email": "arne@blankerts.de", - "role": "Developer" + "role": "Developer", + "email": "arne@blankerts.de" } ], "description": "A small library for converting tokenized PHP source code into XML and potentially other formats", diff --git a/www/wiki/extensions/Maps/.travis.install.sh b/www/wiki/extensions/Maps/.travis.install.sh index 0e60bf2d..8d155391 100644 --- a/www/wiki/extensions/Maps/.travis.install.sh +++ b/www/wiki/extensions/Maps/.travis.install.sh @@ -60,6 +60,7 @@ fi echo 'error_reporting(E_ALL| E_STRICT);' >> LocalSettings.php echo 'ini_set("display_errors", 1);' >> LocalSettings.php echo '$wgShowExceptionDetails = true;' >> LocalSettings.php +echo '$wgShowDBErrorBacktrace = true;' >> LocalSettings.php echo '$wgDevelopmentWarnings = true;' >> LocalSettings.php echo "putenv( 'MW_INSTALL_PATH=$(pwd)' );" >> LocalSettings.php diff --git a/www/wiki/extensions/Maps/.travis.yml b/www/wiki/extensions/Maps/.travis.yml index 226ea1d5..49373ca8 100644 --- a/www/wiki/extensions/Maps/.travis.yml +++ b/www/wiki/extensions/Maps/.travis.yml @@ -1,5 +1,8 @@ language: php +services: + - mysql + sudo: false matrix: diff --git a/www/wiki/extensions/Maps/DefaultSettings.php b/www/wiki/extensions/Maps/DefaultSettings.php index 45053c1b..a3834788 100644 --- a/www/wiki/extensions/Maps/DefaultSettings.php +++ b/www/wiki/extensions/Maps/DefaultSettings.php @@ -38,15 +38,6 @@ return [ 'egMapsMapWidth' => 'auto', 'egMapsMapHeight' => 350, - // Array. The minimum and maximum width and height for all maps. First min and - // max for absolute values, then min and max for percentage values. When the - // height or width exceed their limits, they will be changed to the closest - // allowed value. - 'egMapsSizeRestrictions' => [ - 'width' => [ 50, 1020, 1, 100 ], - 'height' => [ 50, 1000, 1, 100 ], - ], - // Strings. The default content for all pop-ups. This value will only be used // when the user does not provide one. 'egMapsDefaultTitle' => '', diff --git a/www/wiki/extensions/Maps/INSTALL.md b/www/wiki/extensions/Maps/INSTALL.md index ad84a414..20a4b3ad 100644 --- a/www/wiki/extensions/Maps/INSTALL.md +++ b/www/wiki/extensions/Maps/INSTALL.md @@ -29,7 +29,7 @@ minimum requirements are indicated in bold. For a detailed list of changes, see 7.1 - 7.4+ 1.31 - 1.33+ 3.0+ - Planned Q2 2019 + Planned Q3 2019 7.3.x diff --git a/www/wiki/extensions/Maps/Maps.resources.php b/www/wiki/extensions/Maps/Maps.resources.php deleted file mode 100644 index 9c61887b..00000000 --- a/www/wiki/extensions/Maps/Maps.resources.php +++ /dev/null @@ -1,92 +0,0 @@ - - * @author Daniel Werner < daniel.a.r.werner@gmail.com > - * - * @codeCoverageIgnoreStart - */ -return call_user_func( function() { - $moduleTemplate = [ - 'position' => 'top', - 'group' => 'ext.maps', - 'localBasePath' => __DIR__, - 'remoteExtPath' => 'Maps', - 'targets' => [ - 'mobile', - 'desktop' - ] - ]; - - return [ - 'ext.maps.common' => $moduleTemplate + [ - 'messages' => [ - 'maps-load-failed', - ] , - 'scripts' => [ - 'resources/ext.maps.common.js', - ], - ], - - 'ext.maps.resizable' => $moduleTemplate + [ - 'dependencies' => 'jquery.ui.resizable', - ], - - 'mapeditor' => $moduleTemplate + [ - 'scripts' => [ - 'resources/editor/js/jquery.miniColors.js', - 'resources/editor/js/mapeditor.iefixes.js', - 'resources/editor/js/mapeditor.js', - ], - 'styles' => [ - 'resources/editor/css/jquery.miniColors.css', - 'resources/editor/css/mapeditor.css', - ], - 'messages' => [ - 'mapeditor-parser-error', - 'mapeditor-none-text', - 'mapeditor-done-button', - 'mapeditor-remove-button', - 'mapeditor-import-button', - 'mapeditor-export-button', - 'mapeditor-import-button2', - 'mapeditor-select-button', - 'mapeditor-mapparam-button', - 'mapeditor-clear-button', - 'mapeditor-imageoverlay-button', - ], - 'dependencies' => [ - 'ext.maps.common', - 'jquery.ui.autocomplete', - 'jquery.ui.slider', - 'jquery.ui.dialog', - ], - ], - - 'ext.maps.services' => $moduleTemplate + [ - 'scripts' => [ - 'resources/ext.maps.services.js', - ], - 'dependencies' => [ - 'ext.maps.common', - ] - ], - - 'ext.sm.common' => $moduleTemplate + [ - 'scripts' => [ - 'resources/ext.sm.common.js' - ], - 'dependencies' => [ - 'ext.maps.common', - 'ext.maps.services' - ] - ], - ]; - -} ); -// @codeCoverageIgnoreEnd diff --git a/www/wiki/extensions/Maps/RELEASE-NOTES.md b/www/wiki/extensions/Maps/RELEASE-NOTES.md index 5655f36b..118272e2 100644 --- a/www/wiki/extensions/Maps/RELEASE-NOTES.md +++ b/www/wiki/extensions/Maps/RELEASE-NOTES.md @@ -3,6 +3,19 @@ different releases and which versions of PHP and MediaWiki they support, see the [platform compatibility tables](INSTALL.md#platform-compatibility-and-release-status). +## Maps 7.4.0 + +Released on August 9th, 2019. + +* Fixed default map height bug occurring with recent versions of an used library +* Removed `egMapsSizeRestrictions` setting, unused since Maps 3.0.0 + +## Maps 7.3.3 + +Released on August 2nd, 2019. + +* Fixed error in height parameter description + ## Maps 7.3.2 Released on July 25th, 2019. diff --git a/www/wiki/extensions/Maps/composer.json b/www/wiki/extensions/Maps/composer.json index 0ff12364..ae9b63ed 100644 --- a/www/wiki/extensions/Maps/composer.json +++ b/www/wiki/extensions/Maps/composer.json @@ -35,7 +35,7 @@ "composer/installers": "^1.0.1", "mediawiki/validator": "~2.2", "mediawiki/parser-hooks": "~1.5", - "param-processor/param-processor": "~1.4.2", + "param-processor/param-processor": "^1.4.2", "data-values/geo": "~4.0|~3.0", "jeroen/file-fetcher": "~6.0|~5.0", "jeroen/file-fetcher-cache": "~1.0", diff --git a/www/wiki/extensions/Maps/extension.json b/www/wiki/extensions/Maps/extension.json index a631e18d..f0bcddc8 100644 --- a/www/wiki/extensions/Maps/extension.json +++ b/www/wiki/extensions/Maps/extension.json @@ -1,9 +1,9 @@ { "name": "Maps", - "version": "7.3.2", + "version": "7.4.0", "author": [ - "[https://www.mediawiki.org/wiki/User:Jeroen_De_Dauw Jeroen De Dauw]", + "[https://www.entropywins.wtf/mediawiki Jeroen De Dauw]", "..." ], "url": "https://github.com/JeroenDeDauw/Maps/blob/master/README.md#maps", @@ -47,6 +47,234 @@ "defaultcontentmodel": "wikitext" } ], + + "ResourceFileModulePaths": { + "localBasePath": "resources", + "remoteExtPath": "Maps/resources" + }, + + "ResourceModules": { + "ext.maps.common": { + "scripts": [ + "maps.common.js" + ], + "messages": [ + "maps-load-failed" + ] + }, + + "ext.maps.services": { + "dependencies": [ + "ext.maps.common" + ], + "scripts": [ + "maps.services.js" + ] + }, + + "ext.sm.common": { + "dependencies": [ + "ext.maps.common", + "ext.maps.services" + ], + "scripts": [ + "sm.common.js" + ] + }, + + "mapeditor": { + "dependencies": [ + "ext.maps.common", + "jquery.ui.autocomplete", + "jquery.ui.slider", + "jquery.ui.dialog" + ], + "scripts": [ + "editor/js/jquery.miniColors.js", + "editor/js/mapeditor.iefixes.js", + "editor/js/mapeditor.js" + ], + "styles": [ + "editor/css/jquery.miniColors.css", + "editor/css/mapeditor.css" + ], + "messages": [ + "mapeditor-parser-error", + "mapeditor-none-text", + "mapeditor-done-button", + "mapeditor-remove-button", + "mapeditor-import-button", + "mapeditor-export-button", + "mapeditor-import-button2", + "mapeditor-select-button", + "mapeditor-mapparam-button", + "mapeditor-clear-button", + "mapeditor-imageoverlay-button" + ] + }, + + "ext.maps.resizable": { + "dependencies": [ + "jquery.ui.resizable" + ] + }, + + "ext.maps.leaflet.base": { + "scripts": [ + "leaflet/leaflet/leaflet.js" + ], + "styles": [ + "leaflet/leaflet/leaflet.css" + ] + }, + + "ext.maps.leaflet": { + "dependencies": [ + "ext.maps.common", + "ext.maps.services", + "ext.maps.leaflet.base" + ], + "scripts": [ + "leaflet/jquery.leaflet.js", + "leaflet/ext.maps.leaflet.js" + ], + "messages": [ + "maps-markers", + "maps-copycoords-prompt", + "maps-searchmarkers-text" + ] + }, + + "ext.maps.leaflet.fullscreen": { + "dependencies": [ + "ext.maps.leaflet.base" + ], + "scripts": [ + "leaflet/leaflet.fullscreen/Control.FullScreen.js" + ], + "styles": [ + "leaflet/leaflet.fullscreen/Control.FullScreen.css" + ] + }, + + "ext.maps.leaflet.markercluster": { + "dependencies": [ + "ext.maps.leaflet.base" + ], + "scripts": [ + "leaflet/leaflet.markercluster/leaflet.markercluster.js" + ], + "styles": [ + "leaflet/leaflet.markercluster/MarkerCluster.css" + ] + }, + + "ext.maps.leaflet.providers": { + "dependencies": [ + "ext.maps.leaflet.base" + ], + "scripts": [ + "leaflet/leaflet-providers/leaflet-providers.js" + ] + }, + + "ext.maps.leaflet.editable": { + "dependencies": [ + "ext.maps.leaflet.base" + ], + "scripts": [ + "leaflet/leaflet.editable/Leaflet.Editable.js" + ] + }, + + "ext.maps.leaflet.editor": { + "dependencies": [ + "ext.maps.leaflet" + ], + "scripts": [ + "leaflet/leaflet.editor.js" + ] + }, + + "ext.maps.leaflet.leafletajax": { + "dependencies": [ + "ext.maps.leaflet", + "ext.sm.common" + ], + "scripts": [ + "leaflet/ext.sm.leafletajax.js" + ] + }, + + "ext.maps.googlemaps3": { + "dependencies": [ + "ext.maps.common" + ], + "scripts": [ + "GoogleMaps/jquery.googlemap.js", + "GoogleMaps/ext.maps.googlemaps3.js" + ], + "messages": [ + "maps-googlemaps3-incompatbrowser", + "maps-copycoords-prompt", + "maps-searchmarkers-text", + "maps-fullscreen-button", + "maps-fullscreen-button-tooltip" + ] + }, + + "ext.maps.gm3.markercluster": { + "dependencies": [ + "ext.maps.googlemaps3" + ], + "scripts": [ + "GoogleMaps/gm3-util-library/markerclusterer.js" + ] + }, + + "ext.maps.gm3.markerwithlabel": { + "dependencies": [ + "ext.maps.googlemaps3" + ], + "scripts": [ + "GoogleMaps/gm3-util-library/markerwithlabel.js" + ], + "styles": [ + "GoogleMaps/gm3-util-library/markerwithlabel.css" + ] + }, + + "ext.maps.gm3.geoxml": { + "dependencies": [ + "ext.maps.googlemaps3" + ], + "scripts": [ + "GoogleMaps/geoxml3/geoxml3.js", + "GoogleMaps/geoxml3/ZipFile.complete.js", + "GoogleMaps/geoxml3/ProjectedOverlay.js" + ] + }, + + "ext.maps.gm3.earth": { + "dependencies": [ + "ext.maps.googlemaps3" + ], + "scripts": [ + "GoogleMaps/gm3-util-library/googleearth-compiled.js" + ] + }, + + "ext.sm.googlemaps3ajax": { + "dependencies": [ + "ext.maps.googlemaps3", + "ext.sm.common" + ], + "scripts": [ + "GoogleMaps/ext.sm.googlemaps3ajax.js" + ] + } + }, + "load_composer_autoloader": true, "manifest_version": 2 } diff --git a/www/wiki/extensions/Maps/i18n/ar.json b/www/wiki/extensions/Maps/i18n/ar.json index c6d9c13a..3dcd2223 100644 --- a/www/wiki/extensions/Maps/i18n/ar.json +++ b/www/wiki/extensions/Maps/i18n/ar.json @@ -126,7 +126,7 @@ "maps-par-searchmarkers": "يسمح بالبحث عن علامات محددة عبر حقل مضمن في الخريطة.", "maps-par-zoom": "مستوى التكبير للخريطة، بالنسبة للخرائط التي تحتوي على علامات، سيؤدي هذا إلى الوضع الافتراضي إلى أقصى مستوى من التكبير الذي لا يزال يظهر جميع العلامات.", "maps-par-width": "يسمح بتعيين عرض الخريطة، سيتم افتراض وحدات البكسل الافتراضية على أنها وحدة، ولكن يمكنك تحديد إحدى هذه الوحدات بشكل صريح: px، ex، em.", - "maps-par-height": "يسمح بتعيين ارتفاع الخريطة، سيتم افتراض وحدات البكسل الافتراضية على أنها وحدة، ولكن يمكنك تحديد إحدى هذه الوحدات بشكل صريح: px، ex، em.", + "maps-par-height": "يسمح بتعيين ارتفاع الخريطة، سيتم افتراض وحدات البكسل الافتراضية على أنها وحدة، ولكن يمكنك تحديد إحدى هذه الوحدات بشكل صريح: px، ex، emK %.", "maps-par-centre": "الموقع الذي يجب أن تركز عليه الخريطة", "maps-par-enable-fullscreen": "تمكين زر ملء الشاشة", "maps-par-kml": "ملفات KML لتحميلها على الخريطة.", diff --git a/www/wiki/extensions/Maps/i18n/en.json b/www/wiki/extensions/Maps/i18n/en.json index c26f7bdb..1b5fa3b2 100644 --- a/www/wiki/extensions/Maps/i18n/en.json +++ b/www/wiki/extensions/Maps/i18n/en.json @@ -124,7 +124,7 @@ "maps-par-searchmarkers": "Allows to search for specific markers via a field embedded into the map.", "maps-par-zoom": "The zoom level for the map. For maps with markers this will default to the most zoomed in level that still shows all markers.", "maps-par-width": "Allows setting the width of the map. By default pixels will be assumed as unit, but you can explicitly specify one of these units: px, ex, em, %.", - "maps-par-height": "Allows setting the height of the map. By default pixels will be assumed as unit, but you can explicitly specify one of these units: px, ex, em, %.", + "maps-par-height": "Allows setting the height of the map. By default pixels will be assumed as unit, but you can explicitly specify one of these units: px, ex, em.", "maps-par-centre": "The location on which the map should be centered", "maps-par-enable-fullscreen": "Enable fullscreen button", "maps-par-kml": "KML files to load onto the map.", diff --git a/www/wiki/extensions/Maps/i18n/fr.json b/www/wiki/extensions/Maps/i18n/fr.json index 56e5e2bc..b1984cc9 100644 --- a/www/wiki/extensions/Maps/i18n/fr.json +++ b/www/wiki/extensions/Maps/i18n/fr.json @@ -143,7 +143,7 @@ "maps-par-searchmarkers": "Permet de rechercher des marqueurs spécifiques via un champ inclus dans la carte.", "maps-par-zoom": "Le niveau de zoom de la carte. Pour les cartes avec des marqueurs, ceci positionne la valeur par défaut du plus grand zoom qui montre encore tous les marqueurs.", "maps-par-width": "Permet de définir la largeur de la carte. Par défaut les pixels seront considérés comme unité, mais vous pouvez spécifier explicitement une de ces unités : px, ex, em, %.", - "maps-par-height": "Permet de définir la hauteur de la carte. Par défaut les pixels seront considérés comme unité, mais vous pouvez spécifier explicitement une de ces unités : px, ex, em, %.", + "maps-par-height": "Permet de définir la hauteur de la carte. Par défaut les pixels seront considérés comme unité, mais vous pouvez spécifier explicitement une de ces unités : px, ex, em.", "maps-par-centre": "Le lieu sur lequel la carte devra être centrée", "maps-par-enable-fullscreen": "Activer le bouton plein écran", "maps-par-kml": "Fichiers KML à charger sur la carte.", diff --git a/www/wiki/extensions/Maps/i18n/pl.json b/www/wiki/extensions/Maps/i18n/pl.json index 54a813bf..bb71af4d 100644 --- a/www/wiki/extensions/Maps/i18n/pl.json +++ b/www/wiki/extensions/Maps/i18n/pl.json @@ -14,7 +14,9 @@ }, "maps-desc": "Umożliwia zamieszczanie na stronach wiki map dynamicznych, geokodowanych adresów i innych danych geograficznych", "right-geocode": "Geokodowanie", + "action-geocode": "geokodowania na tej wiki", "maps_map": "Mapa", + "maps-tracking-category": "Strony z mapami renderowanymi przy użyciu rozszerzenia map", "maps-loading-map": "Wczytywanie mapy…", "maps-load-failed": "Nie można załadować mapy!", "maps-markers": "Zaznaczenia", diff --git a/www/wiki/extensions/Maps/i18n/pt-br.json b/www/wiki/extensions/Maps/i18n/pt-br.json index 75526cd6..e1b7df59 100644 --- a/www/wiki/extensions/Maps/i18n/pt-br.json +++ b/www/wiki/extensions/Maps/i18n/pt-br.json @@ -131,7 +131,7 @@ "maps-par-searchmarkers": "Permite pesquisar marcadores específicos usando um campo incorporado no mapa.", "maps-par-zoom": "O nível de zoom do mapa. Nos mapas com marcadores será usado o maior zoom que, mesmo assim, mostre todos os marcadores.", "maps-par-width": "Permite definir a largura do mapa. A unidade por omissão é o píxel, mas pode defini-la explicitamente como: px, ex, em, %.", - "maps-par-height": "Permite definir a altura do mapa. A unidade por omissão é o píxel, mas pode defini-la explicitamente como: px, ex, em, %.", + "maps-par-height": "Permite definir a altura do mapa. A unidade por omissão é o píxel, mas pode defini-la explicitamente como: px, ex, em.", "maps-par-centre": "A localização na qual o mapa deve estar centrado", "maps-par-enable-fullscreen": "Ativar o botão de tela cheia", "maps-par-kml": "Arquivos KML que serão carregados no mapa.", diff --git a/www/wiki/extensions/Maps/i18n/ru.json b/www/wiki/extensions/Maps/i18n/ru.json index 08a7acf6..ac387d8e 100644 --- a/www/wiki/extensions/Maps/i18n/ru.json +++ b/www/wiki/extensions/Maps/i18n/ru.json @@ -42,7 +42,7 @@ "maps-load-failed": "Невозможно загрузить карту!", "maps-markers": "Маркеры", "maps-copycoords-prompt": "CTRL+C, ENTER", - "maps-searchmarkers-text": "Маркеры фильтра", + "maps-searchmarkers-text": "Фильтр маркеров", "maps-others": "другие", "maps-kml-parsing-failed": "Не удалось выполнить разбор одного или нескольких KML-файлов, обычно вследствие отказа в получении или некорректного XML.", "maps-ns-layer": "Слой", @@ -143,7 +143,7 @@ "maps-leaflet-par-layers": "Слои, которые будут доступны при выборе. Первый слой будет отображаться при загрузке карты.", "maps-leaflet-par-overlaylayers": "Слои подложки, которые будут показаны при загрузке карты.", "maps-leaflet-par-maxclusterradius": "Максимальный радиус, который кластер будет покрывать от центрального маркера (в пикселях).", - "maps-leaflet-par-clusterspiderfy": "Когда вы нажимаете кластер в нижнем уровне масштабирования, мы spiderfy его, чтобы вы могли видеть все его маркеры.", + "maps-leaflet-par-clusterspiderfy": "Когда вы нажимаете на кластер на нижнем уровне масштаба, то он разворачивается таким образом, чтобы вы могли увидеть все его маркеры.", "maps_click_to_activate": "Нажмите для активации карты", "maps_centred_on": "Центр карты — $1, $2.", "maps-par-mappingservice": "Позволяет выбрать сервис карт, который будет использоваться.", @@ -170,10 +170,10 @@ "maps-googlemaps3-par-poi": "Показать достопримечательности.", "maps-googlemaps3-par-clustergridsize": "Размер сетки кластера в пикселях.", "maps-par-clustermaxzoom": "Максимальный уровень увеличения, при котором может существовать кластер.", - "maps-par-clusterzoomonclick": "Является ли поведение по умолчанию нажатием на кластер, чтобы увеличить его.", + "maps-par-clusterzoomonclick": "Является ли увеличение кластера поведением по умолчанию при нажатии.", "maps-par-maxclusterradius": "Максимальный радиус, охватываемый кластером.", - "maps-googlemaps3-par-clusteraveragecenter": "Должен ли центр каждого кластера быть средним по всем маркерам в кластере.", - "maps-googlemaps3-par-clusterminsize": "Минимальное количество маркеров, которые должны быть в кластере до того, как маркеры будут скрыты, и будет показано количество.", + "maps-googlemaps3-par-clusteraveragecenter": "Должен ли центр каждого кластера быть средним по всем маркерам в нём.", + "maps-googlemaps3-par-clusterminsize": "Минимальное число маркеров в кластере, начиная с которого маркеры будут скрыты, а вместо них будет показано количество маркеров.", "mapeditor": "Редактор карт", "specialpages-group-maps": "Карты", "mapeditor-parser-error": "Произошла ошибка обработки метаданных. Введённые данные проигнорированы.", @@ -225,7 +225,7 @@ "semanticmaps-par-geocodecontrol": "Показать панель управления геокодированием.", "semanticmaps-par-activeicon": "Значок, который будет отображаться вместо стандартного маркера в случаях, когда активная страница совпадает с результатом запроса", "semanticmaps-par-pagelabel": "Если задано значение «yes» («да»), все маркеры будут иметь «inlineLabel» со ссылкой на страницу, содержащую координаты для маркера", - "semanticmaps-par-ajaxcoordproperty": "Имя свойства координат, которое используется для построения запроса ajax.", + "semanticmaps-par-ajaxcoordproperty": "Имя свойства координат, которое используется для построения ajax-запроса.", "semanticmaps-par-ajaxquery": "Второй запрос, который отправляется через ajax для извлечения дополнительных координат.", "semanticmaps-par-userparam": "Значение, которое передается за каждый вызов шаблона, если шаблон используется", "semanticmaps-kml-text": "Текст, связанный с каждой страницы. Переопределяется дополнительными запрашиваемыми свойствами, если таковые имеются.", diff --git a/www/wiki/extensions/Maps/i18n/uk.json b/www/wiki/extensions/Maps/i18n/uk.json index 24dffd97..8cadcaec 100644 --- a/www/wiki/extensions/Maps/i18n/uk.json +++ b/www/wiki/extensions/Maps/i18n/uk.json @@ -132,7 +132,7 @@ "maps-par-searchmarkers": "Дозволяє шукати специфічні маркери через поле, вбудоване в карту.", "maps-par-zoom": "Рівень масштабування карти. Для карт з маркерами він типово буде найменшим рівнем, який показує всі маркери.", "maps-par-width": "Дозволяє задати ширину карти. За замовчуванням пікселі прийняті як одиниці, але ви можете вибрати одну з цих одиниць: px, ex, em, %.", - "maps-par-height": "Дозволяє задати висоту карти. За замовчуванням пікселі прийняті як одиниці, але ви можете вибрати одну з цих одиниць: px, ex, em, %.", + "maps-par-height": "Дозволяє задати висоту карти. За замовчуванням пікселі прийняті як одиниці, але ви можете вибрати одну з цих одиниць: px, ex, em.", "maps-par-centre": "Місцевість відносно якої карта має центруватися", "maps-par-enable-fullscreen": "Увімкнути кнопку повноекранного режиму", "maps-par-kml": "Файли KML для завантаження на карту.", diff --git a/www/wiki/extensions/Maps/resources/ext.maps.common.js b/www/wiki/extensions/Maps/resources/ext.maps.common.js deleted file mode 100644 index 8e0180b4..00000000 --- a/www/wiki/extensions/Maps/resources/ext.maps.common.js +++ /dev/null @@ -1,4 +0,0 @@ -window.maps = new ( function() { - this.googlemapsList = []; - this.leafletList = []; -} )(); diff --git a/www/wiki/extensions/Maps/resources/ext.maps.services.js b/www/wiki/extensions/Maps/resources/ext.maps.services.js deleted file mode 100644 index a23ff0c8..00000000 --- a/www/wiki/extensions/Maps/resources/ext.maps.services.js +++ /dev/null @@ -1,86 +0,0 @@ -/*global jQuery, mediaWiki, maps */ -/*global confirm */ -( function ( $, mw, maps ) { - 'use strict'; - - /** - * @since 3.5 - * - * @param {object} container - * @return {this} - */ - var services = function ( container ) { - - if ( $.type( container ) !== 'object' ) { - throw new Error( 'The container is not of the correct type ' + $.type( container ) ); - } - - this.container = container; - - return this; - }; - - /* Public methods */ - - services.prototype = { - - constructor: services, - - /** - * @since 3.5 - * - * @param {string} service - */ - render: function( service ) { - if ( service === 'googlemaps' || service === 'maps' || service === 'googlemaps3' ) { - this.google(); - } - - if ( service === 'leaflet' || service === 'leafletmaps' ) { - this.leaflet(); - } - }, - - /** - * Google service - * - * @since 3.5 - */ - google: function() { - - var self = this; - - // https://www.mediawiki.org/wiki/ResourceLoader/Modules#mw.loader.using - mw.loader.using( 'ext.maps.googlemaps3' ).done( function () { - - if ( typeof google === 'undefined' ) { - throw new Error( 'The google map service is unknown, please ensure that the API or module is loaded correctly.' ); - } - - self.container.find( '.maps-googlemaps3' ).each( function() { - var $this = $( this ); - $this.googlemaps( $.parseJSON( $this.find( 'div').text() ) ); - } ); - } ); - }, - - /** - * Leaflet service - * - * @since 3.5 - */ - leaflet: function() { - mw.loader.using( 'ext.maps.leaflet' ).done( function () { - $( '.maps-leaflet' ).each( function() { - var $this = $( this ); - maps.leafletList.push( - $this.leafletmaps( $.parseJSON( $this.find( 'div').text() ) ) - ); - } ); - } ); - } - }; - - maps.services = services; - -}( jQuery, mediaWiki, maps ) ); diff --git a/www/wiki/extensions/Maps/resources/ext.sm.common.js b/www/wiki/extensions/Maps/resources/ext.sm.common.js deleted file mode 100644 index 373c1dd9..00000000 --- a/www/wiki/extensions/Maps/resources/ext.sm.common.js +++ /dev/null @@ -1,76 +0,0 @@ -/** - * JavaScript the Semantic Maps extension. - * @see https://www.mediawiki.org/wiki/Extension:Semantic_Maps - * - * @licence GNU GPL v2++ - * @author Peter Grassberger < petertheone@gmail.com > - */ -window.sm = new ( function( $, mw ) { - - this.buildQueryString = function( query, ajaxcoordproperty, top, right, bottom, left ) { - var isCompoundQuery = query.indexOf( '|' ) > -1; - var query = query.split( '|' ); - $.each( query, function( index ) { - query[index] += ' [[' + ajaxcoordproperty + '::+]] '; - query[index] += '[[' + ajaxcoordproperty + '::>' + bottom + '°, ' + left + '°]] '; - query[index] += '[[' + ajaxcoordproperty + '::<' + top + '°, ' + right + '°]]'; - if( !isCompoundQuery ) { - query[index] += '|?' + ajaxcoordproperty; - } else { - query[index] += ';?' + ajaxcoordproperty; - } - } ); - return query.join( ' | ' ); - }; - - /** - * Detects semicolons `;` not in square brackets `[]`. - * - * @param string - * @returns {boolean} - */ - this.hasCompoundQuerySemicolon = function( string ) { - return /;(?![^[]*])/g.test( string ); - }; - - this.sendQuery = function( query ) { - var action = this.hasCompoundQuerySemicolon( query ) ? 'compoundquery' : 'ask'; - return $.ajax( { - method: 'GET', - url: mw.util.wikiScript( 'api' ), - data: { - 'action': action, - 'query': query, - 'format': 'json' - }, - dataType: 'json' - } ); - }; - - this.ajaxUpdateMarker = function( map, query, icon ) { - return this.sendQuery( query ).done( function( data ) { - if( !data.hasOwnProperty( 'query' ) || - !data.query.hasOwnProperty( 'results' ) ) { - return; - } - // todo: don't remove and recreate all markers.. - // only add new ones. - map.removeMarkers(); - for( var property in data.query.results ) { - if( data.query.results.hasOwnProperty( property ) ) { - var location = data.query.results[property]; - var coordinates = location.printouts[map.options.ajaxcoordproperty][0]; - var markerOptions = { - lat: coordinates.lat, - lon: coordinates.lon, - title: location.fulltext, - text: '' + location.fulltext + '', - icon: icon - }; - map.addMarker( markerOptions ); - } - } - } ); - }; - -} )( jQuery, mediaWiki ); diff --git a/www/wiki/extensions/Maps/resources/leaflet/ext.maps.leaflet.js b/www/wiki/extensions/Maps/resources/leaflet/ext.maps.leaflet.js index ce3e757f..087d4acf 100644 --- a/www/wiki/extensions/Maps/resources/leaflet/ext.maps.leaflet.js +++ b/www/wiki/extensions/Maps/resources/leaflet/ext.maps.leaflet.js @@ -1,3 +1,4 @@ -window.jQuery( document ).ready( function() { - ( new maps.services( window.jQuery( document ) ) ).leaflet(); + +mediaWiki.loader.using( [ 'ext.maps.leaflet' ] ).done( function () { + ( new maps.services( jQuery( document ) ) ).leaflet(); } ); \ No newline at end of file diff --git a/www/wiki/extensions/Maps/resources/maps.common.js b/www/wiki/extensions/Maps/resources/maps.common.js new file mode 100644 index 00000000..8e0180b4 --- /dev/null +++ b/www/wiki/extensions/Maps/resources/maps.common.js @@ -0,0 +1,4 @@ +window.maps = new ( function() { + this.googlemapsList = []; + this.leafletList = []; +} )(); diff --git a/www/wiki/extensions/Maps/resources/maps.services.js b/www/wiki/extensions/Maps/resources/maps.services.js new file mode 100644 index 00000000..a23ff0c8 --- /dev/null +++ b/www/wiki/extensions/Maps/resources/maps.services.js @@ -0,0 +1,86 @@ +/*global jQuery, mediaWiki, maps */ +/*global confirm */ +( function ( $, mw, maps ) { + 'use strict'; + + /** + * @since 3.5 + * + * @param {object} container + * @return {this} + */ + var services = function ( container ) { + + if ( $.type( container ) !== 'object' ) { + throw new Error( 'The container is not of the correct type ' + $.type( container ) ); + } + + this.container = container; + + return this; + }; + + /* Public methods */ + + services.prototype = { + + constructor: services, + + /** + * @since 3.5 + * + * @param {string} service + */ + render: function( service ) { + if ( service === 'googlemaps' || service === 'maps' || service === 'googlemaps3' ) { + this.google(); + } + + if ( service === 'leaflet' || service === 'leafletmaps' ) { + this.leaflet(); + } + }, + + /** + * Google service + * + * @since 3.5 + */ + google: function() { + + var self = this; + + // https://www.mediawiki.org/wiki/ResourceLoader/Modules#mw.loader.using + mw.loader.using( 'ext.maps.googlemaps3' ).done( function () { + + if ( typeof google === 'undefined' ) { + throw new Error( 'The google map service is unknown, please ensure that the API or module is loaded correctly.' ); + } + + self.container.find( '.maps-googlemaps3' ).each( function() { + var $this = $( this ); + $this.googlemaps( $.parseJSON( $this.find( 'div').text() ) ); + } ); + } ); + }, + + /** + * Leaflet service + * + * @since 3.5 + */ + leaflet: function() { + mw.loader.using( 'ext.maps.leaflet' ).done( function () { + $( '.maps-leaflet' ).each( function() { + var $this = $( this ); + maps.leafletList.push( + $this.leafletmaps( $.parseJSON( $this.find( 'div').text() ) ) + ); + } ); + } ); + } + }; + + maps.services = services; + +}( jQuery, mediaWiki, maps ) ); diff --git a/www/wiki/extensions/Maps/resources/sm.common.js b/www/wiki/extensions/Maps/resources/sm.common.js new file mode 100644 index 00000000..373c1dd9 --- /dev/null +++ b/www/wiki/extensions/Maps/resources/sm.common.js @@ -0,0 +1,76 @@ +/** + * JavaScript the Semantic Maps extension. + * @see https://www.mediawiki.org/wiki/Extension:Semantic_Maps + * + * @licence GNU GPL v2++ + * @author Peter Grassberger < petertheone@gmail.com > + */ +window.sm = new ( function( $, mw ) { + + this.buildQueryString = function( query, ajaxcoordproperty, top, right, bottom, left ) { + var isCompoundQuery = query.indexOf( '|' ) > -1; + var query = query.split( '|' ); + $.each( query, function( index ) { + query[index] += ' [[' + ajaxcoordproperty + '::+]] '; + query[index] += '[[' + ajaxcoordproperty + '::>' + bottom + '°, ' + left + '°]] '; + query[index] += '[[' + ajaxcoordproperty + '::<' + top + '°, ' + right + '°]]'; + if( !isCompoundQuery ) { + query[index] += '|?' + ajaxcoordproperty; + } else { + query[index] += ';?' + ajaxcoordproperty; + } + } ); + return query.join( ' | ' ); + }; + + /** + * Detects semicolons `;` not in square brackets `[]`. + * + * @param string + * @returns {boolean} + */ + this.hasCompoundQuerySemicolon = function( string ) { + return /;(?![^[]*])/g.test( string ); + }; + + this.sendQuery = function( query ) { + var action = this.hasCompoundQuerySemicolon( query ) ? 'compoundquery' : 'ask'; + return $.ajax( { + method: 'GET', + url: mw.util.wikiScript( 'api' ), + data: { + 'action': action, + 'query': query, + 'format': 'json' + }, + dataType: 'json' + } ); + }; + + this.ajaxUpdateMarker = function( map, query, icon ) { + return this.sendQuery( query ).done( function( data ) { + if( !data.hasOwnProperty( 'query' ) || + !data.query.hasOwnProperty( 'results' ) ) { + return; + } + // todo: don't remove and recreate all markers.. + // only add new ones. + map.removeMarkers(); + for( var property in data.query.results ) { + if( data.query.results.hasOwnProperty( property ) ) { + var location = data.query.results[property]; + var coordinates = location.printouts[map.options.ajaxcoordproperty][0]; + var markerOptions = { + lat: coordinates.lat, + lon: coordinates.lon, + title: location.fulltext, + text: '' + location.fulltext + '', + icon: icon + }; + map.addMarker( markerOptions ); + } + } + } ); + }; + +} )( jQuery, mediaWiki ); diff --git a/www/wiki/extensions/Maps/src/MapsSetup.php b/www/wiki/extensions/Maps/src/MapsSetup.php index d397e632..245d3915 100644 --- a/www/wiki/extensions/Maps/src/MapsSetup.php +++ b/www/wiki/extensions/Maps/src/MapsSetup.php @@ -48,9 +48,7 @@ class MapsSetup { } private function registerAllTheThings() { - $this->registerWebResources(); $this->registerParserHooks(); - $this->registerMappingServices(); $this->registerPermissions(); $this->registerParameterTypes(); $this->registerHooks(); @@ -76,13 +74,6 @@ class MapsSetup { } } - private function registerWebResources() { - $this->mwGlobals['wgResourceModules'] = array_merge( - $this->mwGlobals['wgResourceModules'], - include __DIR__ . '/../Maps.resources.php' - ); - } - private function registerParserHooks() { if ( $this->mwGlobals['egMapsEnableCoordinateFunction'] ) { $this->mwGlobals['wgHooks']['ParserFirstCallInit'][] = function ( Parser &$parser ) { @@ -154,253 +145,6 @@ class MapsSetup { }; } - private function registerMappingServices() { - $localBasePath = __DIR__ . '/../resources'; - $remoteExtPath = 'Maps/resources'; - - $this->registerGoogleMapsModules( $localBasePath, $remoteExtPath ); - - $this->registerLeafletModules( $localBasePath, $remoteExtPath ); - } - - private function registerGoogleMapsModules( string $localBasePath, string $remoteExtPath ) { - global $wgResourceModules; - - $localBasePath = $localBasePath . '/GoogleMaps'; - $remoteExtPath = $remoteExtPath . '/GoogleMaps'; - - $wgResourceModules['ext.maps.googlemaps3'] = [ - 'dependencies' => [ 'ext.maps.common' ], - 'localBasePath' => $localBasePath, - 'remoteExtPath' => $remoteExtPath, - 'group' => 'ext.maps', - 'targets' => [ - 'mobile', - 'desktop' - ], - 'scripts' => [ - 'jquery.googlemap.js', - 'ext.maps.googlemaps3.js' - ], - 'messages' => [ - 'maps-googlemaps3-incompatbrowser', - 'maps-copycoords-prompt', - 'maps-searchmarkers-text', - 'maps-fullscreen-button', - 'maps-fullscreen-button-tooltip', - ] - ]; - - $wgResourceModules['ext.maps.gm3.markercluster'] = [ - 'localBasePath' => $localBasePath . '/gm3-util-library', - 'remoteExtPath' => $remoteExtPath . '/gm3-util-library', - 'group' => 'ext.maps', - 'targets' => [ - 'mobile', - 'desktop' - ], - 'scripts' => [ - 'markerclusterer.js', - ], - ]; - - $wgResourceModules['ext.maps.gm3.markerwithlabel'] = [ - 'localBasePath' => $localBasePath . '/gm3-util-library', - 'remoteExtPath' => $remoteExtPath . '/gm3-util-library', - 'group' => 'ext.maps', - 'targets' => [ - 'mobile', - 'desktop' - ], - 'scripts' => [ - 'markerwithlabel.js', - ], - 'styles' => [ - 'markerwithlabel.css', - ], - ]; - - $wgResourceModules['ext.maps.gm3.geoxml'] = [ - 'localBasePath' => $localBasePath . '/geoxml3', - 'remoteExtPath' => $remoteExtPath . '/geoxml3', - 'group' => 'ext.maps', - 'targets' => [ - 'mobile', - 'desktop' - ], - 'scripts' => [ - 'geoxml3.js', - 'ZipFile.complete.js', //kmz handling - 'ProjectedOverlay.js', //Overlay handling - ], - ]; - - $wgResourceModules['ext.maps.gm3.earth'] = [ - 'localBasePath' => $localBasePath . '/gm3-util-library', - 'remoteExtPath' => $remoteExtPath . '/gm3-util-library', - 'group' => 'ext.maps', - 'targets' => [ - 'mobile', - 'desktop' - ], - 'scripts' => [ - 'googleearth-compiled.js', - ], - ]; - - $wgResourceModules['ext.sm.googlemaps3ajax'] = [ - 'localBasePath' => $localBasePath, - 'remoteExtPath' => $remoteExtPath, - 'group' => 'ext.maps', - 'dependencies' => [ - 'ext.maps.googlemaps3', - 'ext.sm.common' - ], - 'scripts' => [ - 'ext.sm.googlemaps3ajax.js' - ] - ]; - } - - private function registerLeafletModules( string $localBasePath, string $remoteExtPath ) { - global $wgResourceModules; - - $localBasePath = $localBasePath . '/leaflet'; - $remoteExtPath = $remoteExtPath . '/leaflet'; - - $wgResourceModules['ext.maps.leaflet.base'] = [ - 'localBasePath' => $localBasePath . '/leaflet', - 'remoteExtPath' => $remoteExtPath . '/leaflet', - 'group' => 'ext.maps', - 'targets' => [ - 'mobile', - 'desktop' - ], - 'scripts' => [ - 'leaflet.js', - ], - 'styles' => [ - 'leaflet.css', - ], - ]; - - $wgResourceModules['ext.maps.leaflet'] = [ - 'dependencies' => [ - 'ext.maps.common', - 'ext.maps.services', - 'ext.maps.leaflet.base' - ], - 'localBasePath' => $localBasePath, - 'remoteExtPath' => $remoteExtPath, - 'group' => 'ext.maps', - 'targets' => [ - 'mobile', - 'desktop' - ], - 'scripts' => [ - 'jquery.leaflet.js', - 'ext.maps.leaflet.js', - ], - 'messages' => [ - 'maps-markers', - 'maps-copycoords-prompt', - 'maps-searchmarkers-text', - ], - ]; - - $wgResourceModules['ext.maps.leaflet.fullscreen'] = [ - 'dependencies' => [ 'ext.maps.leaflet' ], - 'localBasePath' => $localBasePath . '/leaflet.fullscreen', - 'remoteExtPath' => $remoteExtPath . '/leaflet.fullscreen', - 'group' => 'ext.maps', - 'targets' => [ - 'mobile', - 'desktop' - ], - 'scripts' => [ - 'Control.FullScreen.js', - ], - 'styles' => [ - 'Control.FullScreen.css', - ], - ]; - - $wgResourceModules['ext.maps.leaflet.markercluster'] = [ - 'dependencies' => [ 'ext.maps.leaflet' ], - 'localBasePath' => $localBasePath . '/leaflet.markercluster', - 'remoteExtPath' => $remoteExtPath . '/leaflet.markercluster', - 'group' => 'ext.maps', - 'targets' => [ - 'mobile', - 'desktop' - ], - 'scripts' => [ - 'leaflet.markercluster.js', - ], - 'styles' => [ - 'MarkerCluster.css', - ], - ]; - - $wgResourceModules['ext.maps.leaflet.providers'] = [ - 'dependencies' => [ 'ext.maps.leaflet' ], - 'localBasePath' => $localBasePath . '/leaflet-providers', - 'remoteExtPath' => $remoteExtPath . '/leaflet-providers', - 'group' => 'ext.maps', - 'targets' => [ - 'mobile', - 'desktop' - ], - 'scripts' => [ - 'leaflet-providers.js', - ], - ]; - - $wgResourceModules['ext.maps.leaflet.editable'] = [ - 'dependencies' => [ 'ext.maps.leaflet.base' ], - 'localBasePath' => $localBasePath . '/leaflet.editable', - 'remoteExtPath' => $remoteExtPath . '/leaflet.editable', - 'group' => 'ext.maps', - 'targets' => [ - 'mobile', - 'desktop' - ], - 'scripts' => [ - 'Leaflet.Editable.js', - ], - ]; - - $wgResourceModules['ext.maps.leaflet.editor'] = [ - 'dependencies' => [ - 'ext.maps.leaflet.base', - //'ext.maps.leaflet.editable' - ], - 'localBasePath' => $localBasePath, - 'remoteExtPath' => $remoteExtPath, - 'group' => 'ext.maps', - 'targets' => [ - 'mobile', - 'desktop' - ], - 'scripts' => [ - 'leaflet.editor.js', - ], - ]; - - $wgResourceModules['ext.sm.leafletajax'] = [ - 'localBasePath' => $localBasePath, - 'remoteExtPath' => $remoteExtPath, - 'group' => 'ext.maps', - 'dependencies' => [ - 'ext.maps.leaflet', - 'ext.sm.common' - ], - 'scripts' => [ - 'ext.sm.leafletajax.js' - ] - ]; - } - private function registerPermissions() { $this->mwGlobals['wgAvailableRights'][] = 'geocode'; diff --git a/www/wiki/extensions/Maps/src/MediaWiki/ParserHooks/DisplayMapRenderer.php b/www/wiki/extensions/Maps/src/MediaWiki/ParserHooks/DisplayMapRenderer.php index 25a80683..8c757acd 100644 --- a/www/wiki/extensions/Maps/src/MediaWiki/ParserHooks/DisplayMapRenderer.php +++ b/www/wiki/extensions/Maps/src/MediaWiki/ParserHooks/DisplayMapRenderer.php @@ -8,6 +8,7 @@ use Maps\DataAccess\MediaWikiFileUrlFinder; use Maps\Elements\Location; use Maps\MappingService; use Maps\Presentation\ElementJsonSerializer; +use Maps\Presentation\MapHtmlBuilder; use Maps\Presentation\WikitextParser; use Maps\Presentation\WikitextParsers\LocationParser; use Parser; @@ -66,9 +67,10 @@ class DisplayMapRenderer { $this->handleMarkerData( $params ); - $output = $this->getMapHTML( + $output = ( new MapHtmlBuilder() )->getMapHTML( $params, - $this->service->newMapId() + $this->service->newMapId(), + $this->service->getName() ); $dependencies = $this->service->getDependencyHtml( $params ); @@ -177,29 +179,4 @@ class DisplayMapRenderer { } } - /** - * Returns the HTML to display the map. - * - * @param array $params - * @param string $mapName - * - * @return string - */ - protected function getMapHTML( array $params, $mapName ) { - return Html::rawElement( - 'div', - [ - 'id' => $mapName, - 'style' => "width: {$params['width']}; height: {$params['height']}; background-color: #cccccc; overflow: hidden;", - 'class' => 'maps-map maps-' . $this->service->getName() - ], - wfMessage( 'maps-loading-map' )->inContentLanguage()->escaped() . - Html::element( - 'div', - [ 'style' => 'display:none', 'class' => 'mapdata' ], - FormatJson::encode( $params ) - ) - ); - } - } diff --git a/www/wiki/extensions/Maps/src/Presentation/MapHtmlBuilder.php b/www/wiki/extensions/Maps/src/Presentation/MapHtmlBuilder.php new file mode 100644 index 00000000..aa47e558 --- /dev/null +++ b/www/wiki/extensions/Maps/src/Presentation/MapHtmlBuilder.php @@ -0,0 +1,37 @@ + $mapName, + 'style' => "width: {$params['width']}; height: {$params['height']}; background-color: #cccccc; overflow: hidden;", + 'class' => 'maps-map maps-' . $serviceName + ], + wfMessage( 'maps-loading-map' )->inContentLanguage()->escaped() . + Html::element( + 'div', + [ 'style' => 'display:none', 'class' => 'mapdata' ], + FormatJson::encode( $params ) + ) + ); + } + +} diff --git a/www/wiki/extensions/Maps/src/SemanticMW/ResultPrinters/MapPrinter.php b/www/wiki/extensions/Maps/src/SemanticMW/ResultPrinters/MapPrinter.php index d3b17adb..6ab945f6 100644 --- a/www/wiki/extensions/Maps/src/SemanticMW/ResultPrinters/MapPrinter.php +++ b/www/wiki/extensions/Maps/src/SemanticMW/ResultPrinters/MapPrinter.php @@ -11,6 +11,7 @@ use Maps\FileUrlFinder; use Maps\MappingService; use Maps\MapsFunctions; use Maps\Presentation\ElementJsonSerializer; +use Maps\Presentation\MapHtmlBuilder; use Maps\Presentation\WikitextParser; use Maps\Presentation\WikitextParsers\LocationParser; use ParamProcessor\ParamDefinition; @@ -151,10 +152,10 @@ class MapPrinter extends ResultPrinter { $params['zoom'] = false; } - $mapName = $this->service->newMapId(); + $mapId = $this->service->newMapId(); SMWOutputs::requireHeadItem( - $mapName, + $mapId, $this->service->getDependencyHtml( $params ) ); @@ -166,7 +167,11 @@ class MapPrinter extends ResultPrinter { unset( $params['source'] ); } - return $this->getMapHTML( $params, $mapName ); + return ( new MapHtmlBuilder() )->getMapHTML( + $params, + $mapId, + $this->service->getName() + ); } private function elementsToJson( array $elements ) { @@ -295,31 +300,6 @@ class MapPrinter extends ResultPrinter { return $locationsJson; } - /** - * Returns the HTML to display the map. - * - * @param array $params - * @param string $mapName - * - * @return string - */ - private function getMapHTML( array $params, string $mapName ): string { - return Html::rawElement( - 'div', - [ - 'id' => $mapName, - 'style' => "width: {$params['width']}; height: {$params['height']}; background-color: #cccccc; overflow: hidden;", - 'class' => 'maps-map maps-' . $this->service->getName() - ], - wfMessage( 'maps-loading-map' )->inContentLanguage()->escaped() . - Html::element( - 'div', - [ 'style' => 'display:none', 'class' => 'mapdata' ], - FormatJson::encode( $params ) - ) - ); - } - /** * Returns the internationalized name of the mapping service. * diff --git a/www/wiki/extensions/Maps/src/SemanticMW/ValueDescriptions/AreaDescription.php b/www/wiki/extensions/Maps/src/SemanticMW/ValueDescriptions/AreaDescription.php index e0f62d3b..ac885095 100644 --- a/www/wiki/extensions/Maps/src/SemanticMW/ValueDescriptions/AreaDescription.php +++ b/www/wiki/extensions/Maps/src/SemanticMW/ValueDescriptions/AreaDescription.php @@ -2,7 +2,6 @@ namespace Maps\SemanticMW\ValueDescriptions; -use DatabaseBase; use DataValues\Geo\Values\LatLongValue; use InvalidArgumentException; use Maps\GeoFunctions; @@ -13,6 +12,7 @@ use SMW\Query\Language\ValueDescription; use SMWDataItem; use SMWDIGeoCoord; use SMWThingDescription; +use Wikimedia\Rdbms\IDatabase; /** * Description of a geographical area defined by a coordinates set and a distance to the bounds. @@ -73,17 +73,17 @@ class AreaDescription extends ValueDescription { } /** - * @see Description::getSQLCondition + * @see SomePropertyInterpreter::mapValueDescription * * FIXME: store specific code should be in the store component * * @param string $tableName - * @param array $fieldNames - * @param DatabaseBase $dbs + * @param string[] $fieldNames + * @param IDatabase $db * * @return string|false */ - public function getSQLCondition( $tableName, array $fieldNames, DatabaseBase $dbs ) { + public function getSQLCondition( $tableName, array $fieldNames, IDatabase $db ) { if ( $this->center->getDIType() != SMWDataItem::TYPE_GEO ) { throw new \LogicException( 'Constructor should have prevented this' ); } @@ -94,10 +94,10 @@ class AreaDescription extends ValueDescription { $bounds = $this->getBoundingBox(); - $north = $dbs->addQuotes( $bounds['north'] ); - $east = $dbs->addQuotes( $bounds['east'] ); - $south = $dbs->addQuotes( $bounds['south'] ); - $west = $dbs->addQuotes( $bounds['west'] ); + $north = $db->addQuotes( $bounds['north'] ); + $east = $db->addQuotes( $bounds['east'] ); + $south = $db->addQuotes( $bounds['south'] ); + $west = $db->addQuotes( $bounds['west'] ); $isEq = $this->getComparator() == SMW_CMP_EQ; @@ -143,4 +143,4 @@ class AreaDescription extends ValueDescription { ]; } -} \ No newline at end of file +} diff --git a/www/wiki/extensions/Maps/src/SemanticMW/ValueDescriptions/CoordinateDescription.php b/www/wiki/extensions/Maps/src/SemanticMW/ValueDescriptions/CoordinateDescription.php index 815a540f..a63ea9a0 100644 --- a/www/wiki/extensions/Maps/src/SemanticMW/ValueDescriptions/CoordinateDescription.php +++ b/www/wiki/extensions/Maps/src/SemanticMW/ValueDescriptions/CoordinateDescription.php @@ -2,10 +2,10 @@ namespace Maps\SemanticMW\ValueDescriptions; -use DatabaseBase; use SMW\DataValueFactory; use SMW\Query\Language\ValueDescription; use SMWDIGeoCoord; +use Wikimedia\Rdbms\IDatabase; /** * Description of one data value of type Geographical Coordinates. @@ -24,17 +24,17 @@ class CoordinateDescription extends ValueDescription { } /** - * @see SMWDescription::getSQLCondition + * @see SomePropertyInterpreter::mapValueDescription * * FIXME: store specific code should be in the store component * * @param string $tableName - * @param array $fieldNames - * @param DatabaseBase $dbs + * @param string[] $fieldNames + * @param IDatabase $db * * @return string|false */ - public function getSQLCondition( $tableName, array $fieldNames, DatabaseBase $dbs ) { + public function getSQLCondition( $tableName, array $fieldNames, IDatabase $db ) { $dataItem = $this->getDataItem(); // Only execute the query when the description's type is geographical coordinates, @@ -57,8 +57,8 @@ class CoordinateDescription extends ValueDescription { return false; } - $lat = $dbs->addQuotes( $dataItem->getLatitude() ); - $lon = $dbs->addQuotes( $dataItem->getLongitude() ); + $lat = $db->addQuotes( $dataItem->getLatitude() ); + $lon = $db->addQuotes( $dataItem->getLongitude() ); $conditions = []; @@ -71,4 +71,4 @@ class CoordinateDescription extends ValueDescription { return false; } -} \ No newline at end of file +} diff --git a/www/wiki/extensions/Maps/tests/Integration/Parser/DisplayMapTest.php b/www/wiki/extensions/Maps/tests/Integration/Parser/DisplayMapTest.php index ef841f13..9bd64bcf 100644 --- a/www/wiki/extensions/Maps/tests/Integration/Parser/DisplayMapTest.php +++ b/www/wiki/extensions/Maps/tests/Integration/Parser/DisplayMapTest.php @@ -10,6 +10,19 @@ use PHPUnit\Framework\TestCase; */ class DisplayMapTest extends TestCase { + private $originalHeight; + private $originalWidth; + + public function setUp() { + $this->originalHeight = $GLOBALS['egMapsMapHeight']; + $this->originalWidth = $GLOBALS['egMapsMapWidth']; + } + + public function tearDown() { + $GLOBALS['egMapsMapHeight'] = $this->originalHeight; + $GLOBALS['egMapsMapWidth'] = $this->originalWidth; + } + public function testMapIdIsSet() { $this->assertContains( 'id="map_leaflet_', @@ -144,6 +157,21 @@ class DisplayMapTest extends TestCase { ); } + public function testDimensionDefaultsAsInteger() { + $GLOBALS['egMapsMapHeight'] = 420; + $GLOBALS['egMapsMapWidth'] = 230; + + $this->assertContains( + 'height: 420px;', + $this->parse( '{{#display_map:1,1}}' ) + ); + + $this->assertContains( + 'width: 230px;', + $this->parse( '{{#display_map:1,1}}' ) + ); + } + // TODO: need DI to test // public function testWhenLocationHasVisitedIconModifier_itIsUsed() { // $this->assertContains( diff --git a/www/wiki/extensions/Mermaid/.gitignore b/www/wiki/extensions/Mermaid/.gitignore new file mode 100644 index 00000000..0745b001 --- /dev/null +++ b/www/wiki/extensions/Mermaid/.gitignore @@ -0,0 +1,11 @@ +*~ +*.kate-swp + +vendor/ +extensions/ + +composer.phar +composer.lock + +!.* +.idea/ \ No newline at end of file diff --git a/www/wiki/extensions/Mermaid/.scrutinizer.yml b/www/wiki/extensions/Mermaid/.scrutinizer.yml new file mode 100644 index 00000000..6f59b5cb --- /dev/null +++ b/www/wiki/extensions/Mermaid/.scrutinizer.yml @@ -0,0 +1,28 @@ +filter: + excluded_paths: + - 'vendor/*' + +tools: + php_mess_detector: + config: + controversial_rules: { superglobals: false } + php_cpd: true + php_pdepend: true + php_code_coverage: false + php_code_sniffer: true + php_cs_fixer: true + php_loc: true + php_analyzer: true + sensiolabs_security_checker: true + external_code_coverage: + timeout: '900' + +checks: + php: + psr2_class_declaration: false + psr2_switch_declaration: false + sql_injection_vulnerabilities: true + security_vulnerabilities: true + no_eval: true + code_rating: true + duplication: true diff --git a/www/wiki/extensions/Mermaid/.travis.yml b/www/wiki/extensions/Mermaid/.travis.yml new file mode 100644 index 00000000..72d3574a --- /dev/null +++ b/www/wiki/extensions/Mermaid/.travis.yml @@ -0,0 +1,33 @@ +# http://docs.travis-ci.com/user/workers/container-based-infrastructure/ +sudo: false + +language: php + +matrix: + fast_finish: true + include: + - env: DB=mysql; MW=REL1_31; TYPE=coverage; PHPUNIT=4.8.* + php: 5.6 + - env: DB=sqlite; MW=REL1_32; PHPUNIT=5.7.* + php: 7.1 + - env: DB=mysql; MW=master; PHPUNIT=6.5.* + php: 7.2 + +install: + - bash ./tests/travis/install-mediawiki.sh + - bash ./tests/travis/install-mermaid.sh + +script: + - bash ./tests/travis/run-tests.sh + +after_success: + - bash ./tests/travis/upload-coverage-report.sh + +notifications: + email: + on_success: change + on_failure: always + +cache: + directories: + - $HOME/.composer/cache diff --git a/www/wiki/extensions/Mermaid/COPYING b/www/wiki/extensions/Mermaid/COPYING new file mode 100644 index 00000000..68606fba --- /dev/null +++ b/www/wiki/extensions/Mermaid/COPYING @@ -0,0 +1,103 @@ +'''[https://github.com/SemanticMediaWiki/Mermaid Mermaid]''' is a free, open-source MediaWiki extension that adds a parser function to help generate diagrams and flowcharts using the mermaid script language. + +Copyright (C) 2017 + + +The license text below "====" applies to all files within this distribution, other than those that are in a directory which contains files named "LICENSE" or "COPYING", or a subdirectory thereof. For those files, the license text contained in said file overrides any license information contained in directories of smaller depth. Alternative licenses are typically used for software that is provided by external parties, and merely packaged with this extension for convenience. +---- +---- + +== GNU GENERAL PUBLIC LICENSE == +Version 2, June 1991 + +Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
+51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +Everyone is permitted to copy and distribute verbatim copies
+of this license document, but changing it is not allowed. + +=== Preamble === +The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software - to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Lesser General Public License instead.) You can apply it to your programs, too. + +When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. + +To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. + +For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. + +We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. + +Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original +authors' reputations. + +Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. + +The precise terms and conditions for copying, distribution and modification follow. + +=== TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION === + +'''0.''' This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does. + +'''1.''' You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program. + +You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. + +'''2.''' You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: + +: '''a)''' You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. + +: '''b)''' You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License. + +: '''c)''' If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. + +'''3.''' You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following: + +: '''a)''' Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, + +: '''b)''' Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, + +: '''c)''' Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. + +If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. + +'''4.''' You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. + +'''5.''' You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it. + +'''6.''' Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. + +'''7.''' If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances. + +It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot impose that choice. + +This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. + +'''8.''' If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. + +'''9.''' The Free Software Foundation may publish revised and/or new versions of the 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 Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation. + +'''10.''' If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. + +'''NO WARRANTY''' + +'''11.''' BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + +'''12.''' IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. + +'''END OF TERMS AND CONDITIONS''' diff --git a/www/wiki/extensions/Mermaid/DefaultSettings.php b/www/wiki/extensions/Mermaid/DefaultSettings.php new file mode 100644 index 00000000..9349bf4b --- /dev/null +++ b/www/wiki/extensions/Mermaid/DefaultSettings.php @@ -0,0 +1,20 @@ + 'forest' +]; diff --git a/www/wiki/extensions/Mermaid/Mermaid.php b/www/wiki/extensions/Mermaid/Mermaid.php new file mode 100644 index 00000000..8e631ff3 --- /dev/null +++ b/www/wiki/extensions/Mermaid/Mermaid.php @@ -0,0 +1,194 @@ + $value ) { + if ( !isset( $GLOBALS[$key] ) ) { + $GLOBALS[$key] = $value; + } + } + + define( 'MERMAID_VERSION', $credits['version'] ); + + // Register message files + $GLOBALS['wgMessagesDirs']['Mermaid'] = __DIR__ . '/i18n'; + $GLOBALS['wgExtensionMessagesFiles']['MermaidMagic'] = __DIR__ . '/i18n/Mermaid.magic.php'; + + $GLOBALS['wgResourceModules']['ext.mermaid.styles'] = [ + 'localBasePath' => __DIR__ , + 'remoteExtPath' => 'Mermaid', + 'position' => 'top', + 'styles' => [ + 'res/ext.mermaid.css' + ], + 'messages' => [], + 'targets' => [ + 'mobile', + 'desktop' + ] + ]; + + $GLOBALS['wgResourceModules']['ext.mermaid'] = [ + 'localBasePath' => __DIR__ , + 'remoteExtPath' => 'Mermaid', + 'position' => 'top', + 'scripts' => [ + 'res/ext.mermaid.js' + ], + 'dependencies' => [ + 'mediawiki.api', + 'ext.mermaid.styles', + 'ext.mermaid.theme.default', + 'ext.mermaid.core' + ], + 'targets' => [ + 'mobile', + 'desktop' + ] + ]; + + $GLOBALS['wgResourceModules']['ext.mermaid.theme.default'] = [ + 'localBasePath' => __DIR__ , + 'remoteExtPath' => 'Mermaid', + 'position' => 'top', + 'styles' => [ + 'res/mermaid/8.0.0/mermaid.min.css' + ], + 'messages' => [], + 'targets' => [ + 'mobile', + 'desktop' + ] + ]; + + $GLOBALS['wgResourceModules']['ext.mermaid.theme.forest'] = [ + 'localBasePath' => __DIR__ , + 'remoteExtPath' => 'Mermaid', + 'position' => 'top', + 'styles' => [ + 'res/mermaid/8.0.0/mermaid.forest.min.css' + ], + 'messages' => [], + 'targets' => [ + 'mobile', + 'desktop' + ] + ]; + + $GLOBALS['wgResourceModules']['ext.mermaid.theme.dark'] = [ + 'localBasePath' => __DIR__ , + 'remoteExtPath' => 'Mermaid', + 'position' => 'top', + 'styles' => [ + 'res/mermaid/8.0.0/mermaid.dark.min.css' + ], + 'messages' => [], + 'targets' => [ + 'mobile', + 'desktop' + ] + ]; + + $GLOBALS['wgResourceModules']['ext.mermaid.theme.neutral'] = [ + 'localBasePath' => __DIR__ , + 'remoteExtPath' => 'Mermaid', + 'position' => 'top', + 'styles' => [ + 'res/mermaid/8.0.0/mermaid.neutral.min.css' + ], + 'messages' => [], + 'targets' => [ + 'mobile', + 'desktop' + ] + ]; + + $GLOBALS['wgResourceModules']['ext.mermaid.d3'] = [ + 'localBasePath' => __DIR__ , + 'remoteExtPath' => 'Mermaid', + 'position' => 'top', + 'scripts' => [ + 'res/d3/d3.min.js', + 'res/d3/dagre-d3.min.js' + ], + 'messages' => [], + 'targets' => [ + 'mobile', + 'desktop' + ] + ]; + + $GLOBALS['wgResourceModules']['ext.mermaid.core'] = [ + 'localBasePath' => __DIR__ , + 'remoteExtPath' => 'Mermaid', + 'position' => 'top', + 'scripts' => [ + 'res/mermaid/8.0.0/mermaid.min.js' + ], + 'dependencies' => [ + 'ext.mermaid.d3' + ], + 'messages' => [], + 'targets' => [ + 'mobile', + 'desktop' + ] + ]; + + $GLOBALS['wgResourceModules']['ext.mermaid.api'] = [ + 'localBasePath' => __DIR__ , + 'remoteExtPath' => 'Mermaid', + 'position' => 'top', + 'scripts' => [ + 'res/mermaid/8.0.0/mermaidAPI.min.js' + ], + 'dependencies' => [ + 'ext.mermaid.d3' + ], + 'messages' => [], + 'targets' => [ + 'mobile', + 'desktop' + ] + ]; + } + + /** + * @since 1.0 + */ + public static function onExtensionFunction() { + $hookRegistry = new HookRegistry(); + $hookRegistry->register(); + } + + /** + * @since 1.0 + * + * @return string|null + */ + public static function getVersion() { + return MERMAID_VERSION; + } + +} diff --git a/www/wiki/extensions/Mermaid/README.md b/www/wiki/extensions/Mermaid/README.md new file mode 100644 index 00000000..1031f5b5 --- /dev/null +++ b/www/wiki/extensions/Mermaid/README.md @@ -0,0 +1,52 @@ +# Mermaid + +[![Build Status](https://secure.travis-ci.org/SemanticMediaWiki/Mermaid.svg?branch=master)](http://travis-ci.org/SemanticMediaWiki/Mermaid) +[![Latest Stable Version](https://poser.pugx.org/mediawiki/mermaid/version.png)](https://packagist.org/packages/mediawiki/mermaid) +[![Packagist download count](https://poser.pugx.org/mediawiki/mermaid/d/total.png)](https://packagist.org/packages/mediawiki/mermaid) + +This extension provides the `#mermaid` parser function to support the generation of diagrams and flowcharts with the help of the [mermaid][mermaid] script language. Supported diagram forms include: + +- Flowchart +- Sequence diagram +- Gantt diagram + +## Requirements + +- PHP 5.6 or later +- MediaWiki 1.31 or later + +## Installation and configuration + +See the information on [installing and configuring] this extension. + +## Usage + +See the information on [using] this extension. + +## Contribution and support + +If you want to contribute work to the project please subscribe to the developers mailing list and +have a look at the contribution guideline. + +* [File an issue](https://github.com/SemanticMediaWiki/Mermaid/issues) +* [Submit a pull request](https://github.com/SemanticMediaWiki/Mermaid/pulls) +* Ask a question on [the mailing list](https://www.semantic-mediawiki.org/wiki/Mailing_list) +* Ask a question on the #semantic-mediawiki IRC channel on Freenode. + +## Tests + +This extension provides unit and integration tests that are run by a [continues integration platform][travis] +but can also be executed using `composer phpunit` from the extension base directory. + +## License + +[GNU General Public License, version 2 or later][gpl-licence]. + +[gpl-licence]: https://www.gnu.org/copyleft/gpl.html +[travis]: https://travis-ci.org/SemanticMediaWiki/Mermaid +[smw]: https://github.com/SemanticMediaWiki/SemanticMediaWiki +[composer]: https://getcomposer.org/ +[mermaid]: https://github.com/knsv/mermaid +[installing and configuring]: https://github.com/SemanticMediaWiki/Mermaid/blob/master/docs/INSTALL.md +[using]: https://github.com/SemanticMediaWiki/Mermaid/blob/master/docs/USAGE.md + diff --git a/www/wiki/extensions/Mermaid/composer.json b/www/wiki/extensions/Mermaid/composer.json new file mode 100644 index 00000000..b4f3972f --- /dev/null +++ b/www/wiki/extensions/Mermaid/composer.json @@ -0,0 +1,43 @@ +{ + "name": "mediawiki/mermaid", + "type": "mediawiki-extension", + "description": "Provides a parser function to generate diagrams and flowcharts with the help of the mermaid script language", + "keywords": [ + "mediawiki", + "mermaid", + "parser function" + ], + "homepage": "https://github.com/SemanticMediaWiki/Mermaid", + "license": "GPL-2.0-or-later", + "authors": [ + { + "name": "James Hong Kong", + "role": "Developer" + } + ], + "require": { + "php": ">=5.6.0", + "composer/installers": "1.*,>=1.0.1" + }, + "extra": { + "branch-alias": { + "dev-master": "2.x-dev" + } + }, + "autoload": { + "files" : [ + "Mermaid.php" + ], + "psr-4": { + "Mermaid\\": "src/" + } + }, + "config": { + "process-timeout": 0 + }, + "scripts":{ + "phpunit": "php ../../tests/phpunit/phpunit.php -c phpunit.xml.dist", + "phpdbg": "phpdbg -qrr ../../tests/phpunit/phpunit.php -c phpunit.xml.dist", + "test": "php ../../tests/phpunit/phpunit.php -c phpunit.xml.dist" + } +} diff --git a/www/wiki/extensions/Mermaid/docs/INSTALL.md b/www/wiki/extensions/Mermaid/docs/INSTALL.md new file mode 100644 index 00000000..9d65af40 --- /dev/null +++ b/www/wiki/extensions/Mermaid/docs/INSTALL.md @@ -0,0 +1,71 @@ +This file contains the *installation and configuration instructions* for the **Mermaid** extension. See also the +[readme], the [release notes] and [usage examples]. + +## Installation + +The recommended way to install this extension is using [Composer](http://getcomposer.org) with +[MediaWiki's built-in support for Composer](https://www.mediawiki.org/wiki/Composer). + +### Step 1 + +Change to the base directory of your MediaWiki installation. This is where the "LocalSettings.php" +file is located. If you have not yet installed Composer do it now by running the following command +in your shell: + + wget https://getcomposer.org/composer.phar + +### Step 2 + +If you do not have a "composer.local.json" file yet, create one and add the following content to it: + +``` +{ + "require": { + "mediawiki/mermaid": "~2.1" + } +} +``` + +If you already have a "composer.local.json" file add the following line to the end of the "require" +section in your file: + + "mediawiki/mermaid": "~2.1" + +Remember to add a comma to the end of the preceding line in this section. + +### Step 3 + +Run the following command in your shell: + + php composer.phar update --no-dev + +Note if you have Git installed on your system add the `--prefer-source` flag to the above command. Also +note that it may be necessary to run this command twice. If unsure do it twice right away. + +### Step 4 + +Add the following line to the end of your "LocalSettings.php" file: + + wfLoadExtension( 'Mermaid' ); + +### Verify installation success + +As final step, you can verify Mermaid got installed by looking at the "Special:Version" page on your wiki and +check that it is listed. + +### Configuration + +One configuration parameter is provided allowing to choose the basic theme for rendereing the graphs, diagrams +and charts. By default the rendering is set to use the `forest` theme: + + $mermaidgDefaultTheme = 'forest'; + +If one would like to use one of the other themes provided (`default`, `neutral` or `dark`) the configuration +parameter can be set to it after invoking the extension, e.g.: + + wfLoadExtension( 'Mermaid' ); + $mermaidgDefaultTheme = 'neutral'; + +[readme]: https://github.com/SemanticMediaWiki/Mermaid/blob/master/README.md +[release notes]: https://github.com/SemanticMediaWiki/Mermaid/blob/master/docs/RELEASE-NOTES.md +[usage examples]: https://github.com/SemanticMediaWiki/Mermaid/blob/master/docs/USAGE.md diff --git a/www/wiki/extensions/Mermaid/docs/ISSUE_TEMPLATE.md b/www/wiki/extensions/Mermaid/docs/ISSUE_TEMPLATE.md new file mode 100644 index 00000000..2ef2a952 --- /dev/null +++ b/www/wiki/extensions/Mermaid/docs/ISSUE_TEMPLATE.md @@ -0,0 +1,15 @@ +### Setup and configuration + +- Mermaid version: +- MediaWiki version: +- PHP version: +- Database system (MySQL, PostgresQL, etc.) and version: + +### Issue + +Produces a [stack trace](https://www.semantic-mediawiki.org/wiki/Help:Identifying_bugs) and outputs: + +``` +``` + +Steps to reproduce the observation (recommendation is to use the [sandbox](http://www.sandbox.semantic-mediawiki.org)): diff --git a/www/wiki/extensions/Mermaid/docs/PULL_REQUEST_TEMPLATE.md b/www/wiki/extensions/Mermaid/docs/PULL_REQUEST_TEMPLATE.md new file mode 100644 index 00000000..baa76247 --- /dev/null +++ b/www/wiki/extensions/Mermaid/docs/PULL_REQUEST_TEMPLATE.md @@ -0,0 +1,12 @@ +This PR is made in reference to: # + +This PR addresses or contains: +- ... +- ... +- ... + +This PR includes: +- [ ] Tests (unit/integration) +- [ ] CI build passed + +Fixes # diff --git a/www/wiki/extensions/Mermaid/docs/RELEASE-NOTES.md b/www/wiki/extensions/Mermaid/docs/RELEASE-NOTES.md new file mode 100644 index 00000000..3b9e81d1 --- /dev/null +++ b/www/wiki/extensions/Mermaid/docs/RELEASE-NOTES.md @@ -0,0 +1,42 @@ +This file contains the *release notes* of the **Mermaid** extension. See also the +[readme], the [installation and configuration information] and [usage examples]. + + +### 2.1.1 + +Released on May 20, 2019. + +* Fixes loading of configuration settings + +### 2.1.0 + +Released on March 5, 2019. + +* Drops support for MediaWiki 1.30 and earlier +* Removes deprecated `mediawiki.api.parse` alias + +### 2.0.0 + +Released on February 18, 2019. + +* Updates to using version 8.0.0 of the mermaid script language. +* Translation updates from https://translatewiki.net + +### 1.0.1 + +Released on February 13, 2018. + +* Improves ID entropy for generated diagrams and charts +* Translation updates from https://translatewiki.net + +### 1.0.0 + +Released on January 16, 2018. + +* Initial release using version 7.0.5 of the mermaid script language. +* Added an `MERMAID` parser to easily generate diagrams and flowcharts with the help of the mermaid script language +* Localizations from https://translatewiki.net + +[readme]: https://github.com/SemanticMediaWiki/Mermaid/blob/master/README.md +[installation and configuration information]: https://github.com/SemanticMediaWiki/Mermaid/blob/master/docs/INSTALL.md +[usage examples]: https://github.com/SemanticMediaWiki/Mermaid/blob/master/docs/USAGE.md diff --git a/www/wiki/extensions/Mermaid/docs/USAGE.md b/www/wiki/extensions/Mermaid/docs/USAGE.md new file mode 100644 index 00000000..b84ed126 --- /dev/null +++ b/www/wiki/extensions/Mermaid/docs/USAGE.md @@ -0,0 +1,27 @@ +This file contains basic *usage information* for the **Mermaid** extension. See also the [readme]. + +The `#mermaid` parser function allows to add [mermaid][mermaid] typed content to a wiki article. Copying, +[example syntax][examplemjs] is as easy as: + +``` +{{#mermaid:sequenceDiagram +participant Alice +participant Bob + Alice->John: Hello John, how are you? + loop Healthcheck + John->John: Fight against hypochondria + end + Note right of John: Rational thoughts
prevail... + John-->Alice: Great! + John->Bob: How about you? + Bob-->John: Jolly good! +}} +``` +![image](https://user-images.githubusercontent.com/1245473/34535703-14a32100-f106-11e7-9201-ea90a6286c58.png) + +Further [examples][examplesmw] have been created on wiki. + +[readme]: https://github.com/SemanticMediaWiki/Mermaid/blob/master/README.md +[mermaid]: https://github.com/knsv/mermaid +[examplemjs]: https://mermaidjs.github.io/ +[examplesmw]: https://sandbox.semantic-mediawiki.org/wiki/Mermaid diff --git a/www/wiki/extensions/Mermaid/extension.json b/www/wiki/extensions/Mermaid/extension.json new file mode 100644 index 00000000..9a0a3909 --- /dev/null +++ b/www/wiki/extensions/Mermaid/extension.json @@ -0,0 +1,25 @@ +{ + "name": "Mermaid", + "version": "2.1.1", + "author": [ + "James Hong Kong" + ], + "url": "https://github.com/SemanticMediaWiki/Mermaid/", + "descriptionmsg": "mermaid-desc", + "license-name": "GPL-2.0-or-later", + "type": "parserhook", + "requires": { + "MediaWiki": ">= 1.31" + }, + "MessagesDirs": { + "Mermaid": [ + "i18n" + ] + }, + "callback": "Mermaid::initExtension", + "ExtensionFunctions": [ + "Mermaid::onExtensionFunction" + ], + "load_composer_autoloader": true, + "manifest_version": 2 +} diff --git a/www/wiki/extensions/Mermaid/i18n/Mermaid.magic.php b/www/wiki/extensions/Mermaid/i18n/Mermaid.magic.php new file mode 100644 index 00000000..31e79252 --- /dev/null +++ b/www/wiki/extensions/Mermaid/i18n/Mermaid.magic.php @@ -0,0 +1,13 @@ + array( 0, 'mermaid' ) +); diff --git a/www/wiki/extensions/Mermaid/i18n/ar.json b/www/wiki/extensions/Mermaid/i18n/ar.json new file mode 100644 index 00000000..d722f805 --- /dev/null +++ b/www/wiki/extensions/Mermaid/i18n/ar.json @@ -0,0 +1,8 @@ +{ + "@metadata": { + "authors": [ + "ديفيد" + ] + }, + "mermaid-desc": "توفر وظيفة محلل للمساعدة في توليد الرسوم البيانية ومخططات الانسيابية باستخدام لغة البرنامج النصي [https://mermaidjs.github.io/ الحورية]" +} diff --git a/www/wiki/extensions/Mermaid/i18n/ast.json b/www/wiki/extensions/Mermaid/i18n/ast.json new file mode 100644 index 00000000..3c018525 --- /dev/null +++ b/www/wiki/extensions/Mermaid/i18n/ast.json @@ -0,0 +1,8 @@ +{ + "@metadata": { + "authors": [ + "Xuacu" + ] + }, + "mermaid-desc": "Ufre una función del analizador sintácticu p'ayudar a xenerar diagrames y organigrames usando'l llinguaxe de script [https://mermaidjs.github.io/ mermaid]" +} diff --git a/www/wiki/extensions/Mermaid/i18n/be-tarask.json b/www/wiki/extensions/Mermaid/i18n/be-tarask.json new file mode 100644 index 00000000..d0d12e3a --- /dev/null +++ b/www/wiki/extensions/Mermaid/i18n/be-tarask.json @@ -0,0 +1,8 @@ +{ + "@metadata": { + "authors": [ + "Red Winged Duck" + ] + }, + "mermaid-desc": "Забясьпечвае функцыю парсэру для дапамогі ў стварэньні дыяграмаў і блёк-схемаў з выкарыстаньнем скрыптавай мовы [https://mermaidjs.github.io/ mermaid]" +} diff --git a/www/wiki/extensions/Mermaid/i18n/de.json b/www/wiki/extensions/Mermaid/i18n/de.json new file mode 100644 index 00000000..60223223 --- /dev/null +++ b/www/wiki/extensions/Mermaid/i18n/de.json @@ -0,0 +1,8 @@ +{ + "@metadata": { + "authors": [ + "Kghbln" + ] + }, + "mermaid-desc": "Stellt eine Parserfunktion zur Erstellung von (Fluss-)Diagrammen mithilfe der Skriptsprache „[https://mermaidjs.github.io/ mermaid]“ bereit" +} diff --git a/www/wiki/extensions/Mermaid/i18n/en.json b/www/wiki/extensions/Mermaid/i18n/en.json new file mode 100644 index 00000000..96d8a14e --- /dev/null +++ b/www/wiki/extensions/Mermaid/i18n/en.json @@ -0,0 +1,8 @@ +{ + "@metadata": { + "authors": [ + "mwjames" + ] + }, + "mermaid-desc": "Provides a parser function to help generate diagrams and flowcharts using the [https://mermaidjs.github.io/ mermaid] script language" +} diff --git a/www/wiki/extensions/Mermaid/i18n/es.json b/www/wiki/extensions/Mermaid/i18n/es.json new file mode 100644 index 00000000..8e6c350c --- /dev/null +++ b/www/wiki/extensions/Mermaid/i18n/es.json @@ -0,0 +1,8 @@ +{ + "@metadata": { + "authors": [ + "Fitoschido" + ] + }, + "mermaid-desc": "Proporciona una función del analizador sintáctico para ayudar a generar diagramas y organigramas mediante el lenguaje de programación [https://mermaidjs.github.io/ mermaid]" +} diff --git a/www/wiki/extensions/Mermaid/i18n/eu.json b/www/wiki/extensions/Mermaid/i18n/eu.json new file mode 100644 index 00000000..eab5fd26 --- /dev/null +++ b/www/wiki/extensions/Mermaid/i18n/eu.json @@ -0,0 +1,8 @@ +{ + "@metadata": { + "authors": [ + "Amaia" + ] + }, + "mermaid-desc": "Eman funtzio aztertzaile sintaktiko bat, diagramak eta organigramak sortzen laguntzeko, [https://mermaidjs.github.io/ mermaid] programazio hizkuntza erabiliz" +} diff --git a/www/wiki/extensions/Mermaid/i18n/fr.json b/www/wiki/extensions/Mermaid/i18n/fr.json new file mode 100644 index 00000000..37402ce1 --- /dev/null +++ b/www/wiki/extensions/Mermaid/i18n/fr.json @@ -0,0 +1,8 @@ +{ + "@metadata": { + "authors": [ + "Wladek92" + ] + }, + "mermaid-desc": "Fournit une fonction d'analyse pour aider à la génération des diagrammes et des organigrammes en utilisant le langage de script [https://mermaidjs.github.io/ mermaid]" +} diff --git a/www/wiki/extensions/Mermaid/i18n/gl.json b/www/wiki/extensions/Mermaid/i18n/gl.json new file mode 100644 index 00000000..0af41c0a --- /dev/null +++ b/www/wiki/extensions/Mermaid/i18n/gl.json @@ -0,0 +1,8 @@ +{ + "@metadata": { + "authors": [ + "Elisardojm" + ] + }, + "mermaid-desc": "Proporciona unha función do analizador sintáctico para axudar a xerar diagramas e organigramas mediante a linguaxe de comandos [https://mermaidjs.github.io/ mermaid]" +} diff --git a/www/wiki/extensions/Mermaid/i18n/ia.json b/www/wiki/extensions/Mermaid/i18n/ia.json new file mode 100644 index 00000000..eff3c94e --- /dev/null +++ b/www/wiki/extensions/Mermaid/i18n/ia.json @@ -0,0 +1,8 @@ +{ + "@metadata": { + "authors": [ + "McDutchie" + ] + }, + "mermaid-desc": "Forni un function analysator pro adjutar a generar diagrammas e fluxogrammas usante le linguage de script [https://mermaidjs.github.io/ mermaid]" +} diff --git a/www/wiki/extensions/Mermaid/i18n/ko.json b/www/wiki/extensions/Mermaid/i18n/ko.json new file mode 100644 index 00000000..3a99ee87 --- /dev/null +++ b/www/wiki/extensions/Mermaid/i18n/ko.json @@ -0,0 +1,8 @@ +{ + "@metadata": { + "authors": [ + "Jay94ks" + ] + }, + "mermaid-desc": "[https://mermaidjs.github.io/mermaid] 스크립트 언어를 사용하여 다이어그램 및 흐름도를 생성하기 위해 사용할 구문 분석기 함수를 제공하세요." +} diff --git a/www/wiki/extensions/Mermaid/i18n/li.json b/www/wiki/extensions/Mermaid/i18n/li.json new file mode 100644 index 00000000..b0d98925 --- /dev/null +++ b/www/wiki/extensions/Mermaid/i18n/li.json @@ -0,0 +1,8 @@ +{ + "@metadata": { + "authors": [ + "Ooswesthoesbes" + ] + }, + "mermaid-desc": "Bèdj 'n parserfunctie veur diagramme en vleujkaarte te make mit gebroek van 't [https://mermaidjs.github.io/ mermaid-sjrif]" +} diff --git a/www/wiki/extensions/Mermaid/i18n/mk.json b/www/wiki/extensions/Mermaid/i18n/mk.json new file mode 100644 index 00000000..8ae60a08 --- /dev/null +++ b/www/wiki/extensions/Mermaid/i18n/mk.json @@ -0,0 +1,8 @@ +{ + "@metadata": { + "authors": [ + "Bjankuloski06" + ] + }, + "mermaid-desc": "Дава расчленувачка функција за создавање на дијаграми и блок-шеми користејќи го скриптниот јазик [https://mermaidjs.github.io/ mermaid]" +} diff --git a/www/wiki/extensions/Mermaid/i18n/nb.json b/www/wiki/extensions/Mermaid/i18n/nb.json new file mode 100644 index 00000000..53acff28 --- /dev/null +++ b/www/wiki/extensions/Mermaid/i18n/nb.json @@ -0,0 +1,8 @@ +{ + "@metadata": { + "authors": [ + "Jon Harald Søby" + ] + }, + "mermaid-desc": "Gir en parserfunksjon for å generere diagrammer og flytkart med skriptspråket [https://mermaidjs.github.io/ mermaid]" +} diff --git a/www/wiki/extensions/Mermaid/i18n/pl.json b/www/wiki/extensions/Mermaid/i18n/pl.json new file mode 100644 index 00000000..afc5541f --- /dev/null +++ b/www/wiki/extensions/Mermaid/i18n/pl.json @@ -0,0 +1,8 @@ +{ + "@metadata": { + "authors": [ + "Railfail536" + ] + }, + "mermaid-desc": "Dostarcza funkcję parsera pomagającą w generowaniu diagramów i schematów blokowych przy wykorzystaniu języka skryptowego [https://mermaidjs.github.io/ mermaid]" +} diff --git a/www/wiki/extensions/Mermaid/i18n/pt-br.json b/www/wiki/extensions/Mermaid/i18n/pt-br.json new file mode 100644 index 00000000..7eea36f7 --- /dev/null +++ b/www/wiki/extensions/Mermaid/i18n/pt-br.json @@ -0,0 +1,8 @@ +{ + "@metadata": { + "authors": [ + "Eduardo Addad de Oliveira" + ] + }, + "mermaid-desc": "Fornece uma função do analisador sintático para ajudar a gerar diagramas e fluxogramas, utilizando a linguagem de comandos [https://mermaidjs.github.io/ mermaid]" +} diff --git a/www/wiki/extensions/Mermaid/i18n/pt.json b/www/wiki/extensions/Mermaid/i18n/pt.json new file mode 100644 index 00000000..e4a6b2a6 --- /dev/null +++ b/www/wiki/extensions/Mermaid/i18n/pt.json @@ -0,0 +1,8 @@ +{ + "@metadata": { + "authors": [ + "Hamilton Abreu" + ] + }, + "mermaid-desc": "Fornece uma função do analisador sintático para ajudar a gerar diagramas e fluxogramas, utilizando a linguagem de comandos [https://mermaidjs.github.io/ mermaid]" +} diff --git a/www/wiki/extensions/Mermaid/i18n/qqq.json b/www/wiki/extensions/Mermaid/i18n/qqq.json new file mode 100644 index 00000000..b90ab4f4 --- /dev/null +++ b/www/wiki/extensions/Mermaid/i18n/qqq.json @@ -0,0 +1,8 @@ +{ + "@metadata": { + "authors": [ + "Kghbln" + ] + }, + "mermaid-desc": "{{desc|name=Mermaid|url=https://www.mediawiki.org/wiki/Extension:Mermaid}}" +} diff --git a/www/wiki/extensions/Mermaid/i18n/roa-tara.json b/www/wiki/extensions/Mermaid/i18n/roa-tara.json new file mode 100644 index 00000000..a1a1f91e --- /dev/null +++ b/www/wiki/extensions/Mermaid/i18n/roa-tara.json @@ -0,0 +1,8 @@ +{ + "@metadata": { + "authors": [ + "Joetaras" + ] + }, + "mermaid-desc": "Dèje 'na funzione ca analizze pe aijutà a generà le diagramme e le flusse ausanne 'u lenguagge de script [https://mermaidjs.github.io/ mermaid]" +} diff --git a/www/wiki/extensions/Mermaid/i18n/ru.json b/www/wiki/extensions/Mermaid/i18n/ru.json new file mode 100644 index 00000000..552759b4 --- /dev/null +++ b/www/wiki/extensions/Mermaid/i18n/ru.json @@ -0,0 +1,9 @@ +{ + "@metadata": { + "authors": [ + "Staspotanin2", + "Kaganer" + ] + }, + "mermaid-desc": "Предоставляет функции парсера, для помощи в генерации диаграмм и блок-схем, с использованием скриптового языка [https://mermaidjs.github.io/ mermaid]" +} diff --git a/www/wiki/extensions/Mermaid/i18n/sv.json b/www/wiki/extensions/Mermaid/i18n/sv.json new file mode 100644 index 00000000..4049c386 --- /dev/null +++ b/www/wiki/extensions/Mermaid/i18n/sv.json @@ -0,0 +1,8 @@ +{ + "@metadata": { + "authors": [ + "Psl85" + ] + }, + "mermaid-desc": "Begär en parserfunksjon för att generera diagram och flödekort med skriptspråket [https://mermaidjs.github.io/ mermaid]" +} diff --git a/www/wiki/extensions/Mermaid/i18n/tr.json b/www/wiki/extensions/Mermaid/i18n/tr.json new file mode 100644 index 00000000..b313390c --- /dev/null +++ b/www/wiki/extensions/Mermaid/i18n/tr.json @@ -0,0 +1,8 @@ +{ + "@metadata": { + "authors": [ + "Hedda" + ] + }, + "mermaid-desc": "[https://mermaidjs.github.io/ Deniz kızı] script dilini kullanarak diyagram ve akış şemaları oluşturmaya yardımcı olacak ayrıştırıcı bir işlev sağlar." +} diff --git a/www/wiki/extensions/Mermaid/i18n/uk.json b/www/wiki/extensions/Mermaid/i18n/uk.json new file mode 100644 index 00000000..7f24f5d3 --- /dev/null +++ b/www/wiki/extensions/Mermaid/i18n/uk.json @@ -0,0 +1,8 @@ +{ + "@metadata": { + "authors": [ + "Vlad5250" + ] + }, + "mermaid-desc": "Надає функції парсера для допомоги у генерації діаграм і блок-схем, з використанням скриптової мови [https://mermaidjs.github.io/ mermaid]" +} diff --git a/www/wiki/extensions/Mermaid/i18n/zh-hans.json b/www/wiki/extensions/Mermaid/i18n/zh-hans.json new file mode 100644 index 00000000..7a98ffca --- /dev/null +++ b/www/wiki/extensions/Mermaid/i18n/zh-hans.json @@ -0,0 +1,8 @@ +{ + "@metadata": { + "authors": [ + "Liuxinyu970226" + ] + }, + "mermaid-desc": "提供解析器函数以帮助生成使用[https://mermaidjs.github.io/ mermaid] 脚本语言的图表和流程图" +} diff --git a/www/wiki/extensions/Mermaid/i18n/zh-hant.json b/www/wiki/extensions/Mermaid/i18n/zh-hant.json new file mode 100644 index 00000000..128cdc94 --- /dev/null +++ b/www/wiki/extensions/Mermaid/i18n/zh-hant.json @@ -0,0 +1,8 @@ +{ + "@metadata": { + "authors": [ + "Kly" + ] + }, + "mermaid-desc": "提供解析函式以協助產生使用 [https://mermaidjs.github.io/ mermaid] 腳本語言的圖表與流程圖" +} diff --git a/www/wiki/extensions/Mermaid/phpunit.xml.dist b/www/wiki/extensions/Mermaid/phpunit.xml.dist new file mode 100644 index 00000000..8a48edb8 --- /dev/null +++ b/www/wiki/extensions/Mermaid/phpunit.xml.dist @@ -0,0 +1,28 @@ + + + + + tests/phpunit/Unit + + + tests/phpunit/Integration + + + + + src + + + diff --git a/www/wiki/extensions/Mermaid/res/d3/d3.min.js b/www/wiki/extensions/Mermaid/res/d3/d3.min.js new file mode 100644 index 00000000..4f9d80b7 --- /dev/null +++ b/www/wiki/extensions/Mermaid/res/d3/d3.min.js @@ -0,0 +1,8 @@ +// https://d3js.org Version 4.0.0. Copyright 2016 Mike Bostock. +!function(t,n){"object"==typeof exports&&"undefined"!=typeof module?n(exports):"function"==typeof define&&define.amd?define(["exports"],n):n(t.d3=t.d3||{})}(this,function(t){"use strict";function n(t,n){return tn?1:t>=n?0:NaN}function e(t){return 1===t.length&&(t=r(t)),{left:function(n,e,r,i){for(null==r&&(r=0),null==i&&(i=n.length);r>>1;t(n[o],e)<0?r=o+1:i=o}return r},right:function(n,e,r,i){for(null==r&&(r=0),null==i&&(i=n.length);r>>1;t(n[o],e)>0?i=o:r=o+1}return r}}}function r(t){return function(e,r){return n(t(e),r)}}function i(t,n){return nt?1:n>=t?0:NaN}function o(t){return null===t?NaN:+t}function u(t,n){var e,r,i=t.length,u=0,a=0,c=-1,s=0;if(null==n)for(;++c1)return a/(s-1)}function a(t,n){var e=u(t,n);return e?Math.sqrt(e):e}function c(t,n){var e,r,i,o=-1,u=t.length;if(null==n){for(;++o=r){e=i=r;break}for(;++or&&(e=r),i=r){e=i=r;break}for(;++or&&(e=r),i=wd?i*=10:o>=Md?i*=5:o>=Td&&(i*=2),n=f;)l.pop(),--p;var d,v=new Array(p+1);for(i=0;i<=p;++i)d=v[i]=[],d.x0=i>0?l[i-1]:s,d.x1=i=1)return+e(t[r-1],r-1,t);var r,i=(r-1)*n,u=Math.floor(i),a=+e(t[u],u,t),c=+e(t[u+1],u+1,t);return a+(c-a)*(i-u)}}function y(t,e,r){return t=bd.call(t,o).sort(n),Math.ceil((r-e)/(2*(_(t,.75)-_(t,.25))*Math.pow(t.length,-1/3)))}function g(t,n,e){return Math.ceil((e-n)/(3.5*a(t)*Math.pow(t.length,-1/3)))}function m(t,n){var e,r,i=-1,o=t.length;if(null==n){for(;++i=r){e=r;break}for(;++ie&&(e=r)}else{for(;++i=r){e=r;break}for(;++ie&&(e=r)}return e}function x(t,n){var e,r=0,i=t.length,u=-1,a=i;if(null==n)for(;++u=0;)for(r=t[i],n=r.length;--n>=0;)e[--u]=r[n];return e}function M(t,n){var e,r,i=-1,o=t.length;if(null==n){for(;++i=r){e=r;break}for(;++ir&&(e=r)}else{for(;++i=r){e=r;break}for(;++ir&&(e=r)}return e}function T(t){for(var n=0,e=t.length-1,r=t[0],i=new Array(e<0?0:e);n=o.length)return null!=r?r(n):null!=e?n.sort(e):n;for(var c,s,f,l=-1,h=n.length,p=o[i++],d=L(),v=u();++lo.length)return t;var i,a=u[e-1];return null!=r&&e>=o.length?i=t.entries():(i=[],t.each(function(t,r){i.push({key:r,values:n(t,e)})})),null!=a?i.sort(function(t,n){return a(t.key,n.key)}):i}var e,r,i,o=[],u=[];return i={object:function(n){return t(n,0,U,R)},map:function(n){return t(n,0,D,O)},entries:function(e){return n(t(e,0,D,O),0)},key:function(t){return o.push(t),i},sortKeys:function(t){return u[o.length-1]=t,i},sortValues:function(t){return e=t,i},rollup:function(t){return r=t,i}}}function U(){return{}}function R(t,n,e){t[n]=e}function D(){return L()}function O(t,n,e){t.set(n,e)}function F(){}function I(t,n){var e=new F;if(t instanceof F)t.each(function(t){e.add(t)});else if(t){var r=-1,i=t.length;if(null==n)for(;++r1);return t+n*i*Math.sqrt(-2*Math.log(r)/r)}}function V(){var t=X.apply(this,arguments);return function(){return Math.exp(t())}}function W(t){return function(){for(var n=0,e=0;e1&&yt(t[e[r-2]],t[e[r-1]],t[i])<=0;)--r;e[r++]=i}return e.slice(0,r)}function xt(t){if((e=t.length)<3)return null;var n,e,r=new Array(e),i=new Array(e);for(n=0;n=0;--n)s.push(t[r[o[n]][2]]);for(n=+a;na!=s>a&&u<(c-e)*(a-r)/(s-r)+e&&(f=!f),c=e,s=r;return f}function wt(t){for(var n,e,r=-1,i=t.length,o=t[i-1],u=o[0],a=o[1],c=0;++r=(o=(v+y)/2))?v=o:y=o,(f=e>=(u=(_+g)/2))?_=u:g=u,i=p,!(p=p[l=f<<1|s]))return i[l]=d,t;if(a=+t._x.call(null,p.data),c=+t._y.call(null,p.data),n===a&&e===c)return d.next=p,i?i[l]=d:t._root=d,t;do i=i?i[l]=new Array(4):t._root=new Array(4),(s=n>=(o=(v+y)/2))?v=o:y=o,(f=e>=(u=(_+g)/2))?_=u:g=u;while((l=f<<1|s)===(h=(c>=u)<<1|a>=o));return i[h]=p,i[l]=d,t}function St(t){var n,e,r,i,o=t.length,u=new Array(o),a=new Array(o),c=1/0,s=1/0,f=-(1/0),l=-(1/0);for(e=0;ef&&(f=r),il&&(l=i));for(ft||t>i||r>n||n>o))return this;var u,a,c=i-e,s=this._root;switch(a=(n<(r+o)/2)<<1|t<(e+i)/2){case 0:do u=new Array(4),u[a]=s,s=u;while(c*=2,i=e+c,o=r+c,t>i||n>o);break;case 1:do u=new Array(4),u[a]=s,s=u;while(c*=2,e=i-c,o=r+c,e>t||n>o);break;case 2:do u=new Array(4),u[a]=s,s=u;while(c*=2,i=e+c,r=o-c,t>i||r>n);break;case 3:do u=new Array(4),u[a]=s,s=u;while(c*=2,e=i-c,r=o-c,e>t||r>n)}this._root&&this._root.length&&(this._root=s)}return this._x0=e,this._y0=r,this._x1=i,this._y1=o,this}function At(){var t=[];return this.visit(function(n){if(!n.length)do t.push(n.data);while(n=n.next)}),t}function Ct(t){return arguments.length?this.cover(+t[0][0],+t[0][1]).cover(+t[1][0],+t[1][1]):isNaN(this._x0)?void 0:[[this._x0,this._y0],[this._x1,this._y1]]}function zt(t,n,e,r,i){this.node=t,this.x0=n,this.y0=e,this.x1=r,this.y1=i}function Pt(t,n,e){var r,i,o,u,a,c,s,f=this._x0,l=this._y0,h=this._x1,p=this._y1,d=[],v=this._root;for(v&&d.push(new zt(v,f,l,h,p)),null==e?e=1/0:(f=t-e,l=n-e,h=t+e,p=n+e,e*=e);c=d.pop();)if(!(!(v=c.node)||(i=c.x0)>h||(o=c.y0)>p||(u=c.x1)=y)<<1|t>=_)&&(c=d[d.length-1],d[d.length-1]=d[d.length-1-s],d[d.length-1-s]=c)}else{var g=t-+this._x.call(null,v.data),m=n-+this._y.call(null,v.data),x=g*g+m*m;if(x=(a=(d+_)/2))?d=a:_=a,(f=u>=(c=(v+y)/2))?v=c:y=c,n=p,!(p=p[l=f<<1|s]))return this;if(!p.length)break;(n[l+1&3]||n[l+2&3]||n[l+3&3])&&(e=n,h=l)}for(;p.data!==t;)if(r=p,!(p=p.next))return this;return(i=p.next)&&delete p.next,r?(i?r.next=i:delete r.next,this):n?(i?n[l]=i:delete n[l],(p=n[0]||n[1]||n[2]||n[3])&&p===(n[3]||n[2]||n[1]||n[0])&&!p.length&&(e?e[h]=p:this._root=p),this):(this._root=i,this)}function qt(t){for(var n=0,e=t.length;n=1))throw new Error;this._size=t,this._call=this._error=null,this._tasks=[],this._data=[],this._waiting=this._active=this._ended=this._start=0}function Wt(t){if(!t._start)try{$t(t)}catch(n){t._tasks[t._ended+t._active-1]&&Gt(t,n)}}function $t(t){for(;t._start=t._waiting&&t._active=0;)if((e=t._tasks[r])&&(t._tasks[r]=null,e.abort))try{e.abort()}catch(n){}t._active=NaN,Jt(t)}function Jt(t){!t._active&&t._call&&t._call(t._error,t._data)}function Qt(t){return new Vt(arguments.length?+t:1/0)}function Kt(t){return function(){return t}}function tn(t){return t.innerRadius}function nn(t){return t.outerRadius}function en(t){return t.startAngle}function rn(t){return t.endAngle}function on(t){return t&&t.padAngle}function un(t){return t>=1?cv:t<=-1?-cv:Math.asin(t)}function an(t,n,e,r,i,o,u,a){var c=e-t,s=r-n,f=u-i,l=a-o,h=(f*(n-o)-l*(t-i))/(l*c-f*s);return[t+h*c,n+h*s]}function cn(t,n,e,r,i,o,u){var a=t-e,c=n-r,s=(u?o:-o)/Math.sqrt(a*a+c*c),f=s*c,l=-s*a,h=t+f,p=n+l,d=e+f,v=r+l,_=(h+d)/2,y=(p+v)/2,g=d-h,m=v-p,x=g*g+m*m,b=i-o,w=h*v-d*p,M=(m<0?-1:1)*Math.sqrt(Math.max(0,b*b*x-w*w)),T=(w*m-g*M)/x,k=(-w*g-m*M)/x,N=(w*m+g*M)/x,S=(-w*g+m*M)/x,E=T-_,A=k-y,C=N-_,z=S-y;return E*E+A*A>C*C+z*z&&(T=N,k=S),{cx:T,cy:k,x01:-f,y01:-l,x11:T*(i/b-1),y11:k*(i/b-1)}}function sn(){function t(){var t,s,f=+n.apply(this,arguments),l=+e.apply(this,arguments),h=o.apply(this,arguments)-cv,p=u.apply(this,arguments)-cv,d=Math.abs(p-h),v=p>h;if(c||(c=t=Tt()),luv)if(d>sv-uv)c.moveTo(l*Math.cos(h),l*Math.sin(h)),c.arc(0,0,l,h,p,!v),f>uv&&(c.moveTo(f*Math.cos(p),f*Math.sin(p)),c.arc(0,0,f,p,h,v));else{var _,y,g=h,m=p,x=h,b=p,w=d,M=d,T=a.apply(this,arguments)/2,k=T>uv&&(i?+i.apply(this,arguments):Math.sqrt(f*f+l*l)),N=Math.min(Math.abs(l-f)/2,+r.apply(this,arguments)),S=N,E=N;if(k>uv){var A=un(k/f*Math.sin(T)),C=un(k/l*Math.sin(T));(w-=2*A)>uv?(A*=v?1:-1,x+=A,b-=A):(w=0,x=b=(h+p)/2),(M-=2*C)>uv?(C*=v?1:-1,g+=C,m-=C):(M=0,g=m=(h+p)/2)}var z=l*Math.cos(g),P=l*Math.sin(g),L=f*Math.cos(b),q=f*Math.sin(b);if(N>uv){var U=l*Math.cos(m),R=l*Math.sin(m),D=f*Math.cos(x),O=f*Math.sin(x);if(duv?an(z,P,D,O,U,R,L,q):[L,q],I=z-F[0],Y=P-F[1],B=U-F[0],j=R-F[1],H=1/Math.sin(Math.acos((I*B+Y*j)/(Math.sqrt(I*I+Y*Y)*Math.sqrt(B*B+j*j)))/2),X=Math.sqrt(F[0]*F[0]+F[1]*F[1]);S=Math.min(N,(f-X)/(H-1)),E=Math.min(N,(l-X)/(H+1))}}M>uv?E>uv?(_=cn(D,O,z,P,l,E,v),y=cn(U,R,L,q,l,E,v),c.moveTo(_.cx+_.x01,_.cy+_.y01),Euv&&w>uv?S>uv?(_=cn(L,q,U,R,f,-S,v),y=cn(z,P,D,O,f,-S,v),c.lineTo(_.cx+_.x01,_.cy+_.y01),S=f;--l)s.point(_[l],y[l]);s.lineEnd(),s.areaEnd()}v&&(_[n]=+e(h,n,t),y[n]=+i(h,n,t),s.point(r?+r(h,n,t):_[n],o?+o(h,n,t):y[n]))}if(p)return s=null,p+""||null}function n(){return dn().defined(u).curve(c).context(a)}var e=hn,r=null,i=Kt(0),o=pn,u=Kt(!0),a=null,c=ln,s=null;return t.x=function(n){return arguments.length?(e="function"==typeof n?n:Kt(+n),r=null,t):e},t.x0=function(n){return arguments.length?(e="function"==typeof n?n:Kt(+n),t):e},t.x1=function(n){return arguments.length?(r=null==n?null:"function"==typeof n?n:Kt(+n),t):r},t.y=function(n){return arguments.length?(i="function"==typeof n?n:Kt(+n),o=null,t):i},t.y0=function(n){return arguments.length?(i="function"==typeof n?n:Kt(+n),t):i},t.y1=function(n){return arguments.length?(o=null==n?null:"function"==typeof n?n:Kt(+n),t):o},t.lineX0=t.lineY0=function(){return n().x(e).y(i)},t.lineY1=function(){return n().x(e).y(o)},t.lineX1=function(){return n().x(r).y(i)},t.defined=function(n){return arguments.length?(u="function"==typeof n?n:Kt(!!n),t):u},t.curve=function(n){return arguments.length?(c=n,null!=a&&(s=c(a)),t):c},t.context=function(n){return arguments.length?(null==n?a=s=null:s=c(a=n),t):a},t}function _n(t,n){return nt?1:n>=t?0:NaN}function yn(t){return t}function gn(){function t(t){var a,c,s,f,l,h=t.length,p=0,d=new Array(h),v=new Array(h),_=+i.apply(this,arguments),y=Math.min(sv,Math.max(-sv,o.apply(this,arguments)-_)),g=Math.min(Math.abs(y)/h,u.apply(this,arguments)),m=g*(y<0?-1:1);for(a=0;a0&&(p+=l);for(null!=e?d.sort(function(t,n){return e(v[t],v[n])}):null!=r&&d.sort(function(n,e){return r(t[n],t[e])}),a=0,s=p?(y-h*m)/p:0;a0?l*s:0)+m,v[c]={data:t[c],index:a,value:l,startAngle:_,endAngle:f,padAngle:g};return v}var n=yn,e=_n,r=null,i=Kt(0),o=Kt(sv),u=Kt(0);return t.value=function(e){return arguments.length?(n="function"==typeof e?e:Kt(+e),t):n},t.sortValues=function(n){return arguments.length?(e=n,r=null,t):e},t.sort=function(n){return arguments.length?(r=n,e=null,t):r},t.startAngle=function(n){return arguments.length?(i="function"==typeof n?n:Kt(+n),t):i},t.endAngle=function(n){return arguments.length?(o="function"==typeof n?n:Kt(+n),t):o},t.padAngle=function(n){return arguments.length?(u="function"==typeof n?n:Kt(+n),t):u},t}function mn(t){this._curve=t}function xn(t){function n(n){return new mn(t(n))}return n._curve=t,n}function bn(t){var n=t.curve;return t.angle=t.x,delete t.x,t.radius=t.y,delete t.y,t.curve=function(t){return arguments.length?n(xn(t)):n()._curve},t}function wn(){return bn(dn().curve(fv))}function Mn(){var t=vn().curve(fv),n=t.curve,e=t.lineX0,r=t.lineX1,i=t.lineY0,o=t.lineY1;return t.angle=t.x,delete t.x,t.startAngle=t.x0,delete t.x0,t.endAngle=t.x1,delete t.x1,t.radius=t.y,delete t.y,t.innerRadius=t.y0,delete t.y0,t.outerRadius=t.y1,delete t.y1,t.lineStartAngle=function(){return bn(e())},delete t.lineX0,t.lineEndAngle=function(){return bn(r())},delete t.lineX1,t.lineInnerRadius=function(){return bn(i())},delete t.lineY0,t.lineOuterRadius=function(){return bn(o())},delete t.lineY1,t.curve=function(t){return arguments.length?n(xn(t)):n()._curve},t}function Tn(){function t(){var t;if(r||(r=t=Tt()),n.apply(this,arguments).draw(r,+e.apply(this,arguments)),t)return r=null,t+""||null}var n=Kt(lv),e=Kt(64),r=null;return t.type=function(e){return arguments.length?(n="function"==typeof e?e:Kt(e),t):n},t.size=function(n){return arguments.length?(e="function"==typeof n?n:Kt(+n),t):e},t.context=function(n){return arguments.length?(r=null==n?null:n,t):r},t}function kn(){}function Nn(t,n,e){t._context.bezierCurveTo((2*t._x0+t._x1)/3,(2*t._y0+t._y1)/3,(t._x0+2*t._x1)/3,(t._y0+2*t._y1)/3,(t._x0+4*t._x1+n)/6,(t._y0+4*t._y1+e)/6)}function Sn(t){this._context=t}function En(t){return new Sn(t)}function An(t){this._context=t}function Cn(t){return new An(t)}function zn(t){this._context=t}function Pn(t){return new zn(t)}function Ln(t,n){this._basis=new Sn(t),this._beta=n}function qn(t,n,e){t._context.bezierCurveTo(t._x1+t._k*(t._x2-t._x0),t._y1+t._k*(t._y2-t._y0),t._x2+t._k*(t._x1-n),t._y2+t._k*(t._y1-e),t._x2,t._y2)}function Un(t,n){this._context=t,this._k=(1-n)/6}function Rn(t,n){this._context=t,this._k=(1-n)/6}function Dn(t,n){this._context=t,this._k=(1-n)/6}function On(t,n,e){var r=t._x1,i=t._y1,o=t._x2,u=t._y2;if(t._l01_a>uv){var a=2*t._l01_2a+3*t._l01_a*t._l12_a+t._l12_2a,c=3*t._l01_a*(t._l01_a+t._l12_a);r=(r*a-t._x0*t._l12_2a+t._x2*t._l01_2a)/c,i=(i*a-t._y0*t._l12_2a+t._y2*t._l01_2a)/c}if(t._l23_a>uv){var s=2*t._l23_2a+3*t._l23_a*t._l12_a+t._l12_2a,f=3*t._l23_a*(t._l23_a+t._l12_a);o=(o*s+t._x1*t._l23_2a-n*t._l12_2a)/f,u=(u*s+t._y1*t._l23_2a-e*t._l12_2a)/f}t._context.bezierCurveTo(r,i,o,u,t._x2,t._y2)}function Fn(t,n){this._context=t,this._alpha=n}function In(t,n){this._context=t,this._alpha=n}function Yn(t,n){this._context=t,this._alpha=n}function Bn(t){this._context=t}function jn(t){return new Bn(t)}function Hn(t){return t<0?-1:1}function Xn(t,n,e){var r=t._x1-t._x0,i=n-t._x1,o=(t._y1-t._y0)/(r||i<0&&-0),u=(e-t._y1)/(i||r<0&&-0),a=(o*i+u*r)/(r+i);return(Hn(o)+Hn(u))*Math.min(Math.abs(o),Math.abs(u),.5*Math.abs(a))||0}function Vn(t,n){var e=t._x1-t._x0;return e?(3*(t._y1-t._y0)/e-n)/2:n}function Wn(t,n,e){var r=t._x0,i=t._y0,o=t._x1,u=t._y1,a=(o-r)/3;t._context.bezierCurveTo(r+a,i+a*n,o-a,u-a*e,o,u)}function $n(t){this._context=t}function Zn(t){this._context=new Gn(t)}function Gn(t){this._context=t}function Jn(t){return new $n(t)}function Qn(t){return new Zn(t)}function Kn(t){this._context=t}function te(t){var n,e,r=t.length-1,i=new Array(r),o=new Array(r),u=new Array(r);for(i[0]=0,o[0]=2,u[0]=t[0]+2*t[1],n=1;n=0;--n)i[n]=(u[n]-i[n+1])/o[n];for(o[r-1]=(t[r]+i[r-1])/2,n=0;n1)for(var e,r,i=1,o=t[n[0]],u=o.length;i=0;)e[n]=n;return e}function ce(t,n){return t[n]}function se(){function t(t){var o,u,a=n.apply(this,arguments),c=t.length,s=a.length,f=new Array(s);for(o=0;o0){for(var e,r,i,o=0,u=t[0].length;o0){for(var e,r=0,i=t[n[0]],o=i.length;r0&&(r=(e=t[n[0]]).length)>0){for(var e,r,i,o=0,u=1;u>8&15|n>>4&240,n>>4&15|240&n,(15&n)<<4|15&n,1)):(n=Yv.exec(t))?we(parseInt(n[1],16)):(n=Bv.exec(t))?new Ne(n[1],n[2],n[3],1):(n=jv.exec(t))?new Ne(255*n[1]/100,255*n[2]/100,255*n[3]/100,1):(n=Hv.exec(t))?Me(n[1],n[2],n[3],n[4]):(n=Xv.exec(t))?Me(255*n[1]/100,255*n[2]/100,255*n[3]/100,n[4]):(n=Vv.exec(t))?Se(n[1],n[2]/100,n[3]/100,1):(n=Wv.exec(t))?Se(n[1],n[2]/100,n[3]/100,n[4]):$v.hasOwnProperty(t)?we($v[t]):"transparent"===t?new Ne(NaN,NaN,NaN,0):null}function we(t){return new Ne(t>>16&255,t>>8&255,255&t,1)}function Me(t,n,e,r){return r<=0&&(t=n=e=NaN),new Ne(t,n,e,r)}function Te(t){return t instanceof xe||(t=be(t)),t?(t=t.rgb(),new Ne(t.r,t.g,t.b,t.opacity)):new Ne}function ke(t,n,e,r){return 1===arguments.length?Te(t):new Ne(t,n,e,null==r?1:r)}function Ne(t,n,e,r){this.r=+t,this.g=+n,this.b=+e,this.opacity=+r}function Se(t,n,e,r){return r<=0?t=n=e=NaN:e<=0||e>=1?t=n=NaN:n<=0&&(t=NaN),new Ce(t,n,e,r)}function Ee(t){if(t instanceof Ce)return new Ce(t.h,t.s,t.l,t.opacity);if(t instanceof xe||(t=be(t)),!t)return new Ce;if(t instanceof Ce)return t;t=t.rgb();var n=t.r/255,e=t.g/255,r=t.b/255,i=Math.min(n,e,r),o=Math.max(n,e,r),u=NaN,a=o-i,c=(o+i)/2;return a?(u=n===o?(e-r)/a+6*(e0&&c<1?0:u,new Ce(u,a,c,t.opacity)}function Ae(t,n,e,r){return 1===arguments.length?Ee(t):new Ce(t,n,e,null==r?1:r)}function Ce(t,n,e,r){this.h=+t,this.s=+n,this.l=+e,this.opacity=+r}function ze(t,n,e){return 255*(t<60?n+(e-n)*t/60:t<180?e:t<240?n+(e-n)*(240-t)/60:n)}function Pe(t){if(t instanceof qe)return new qe(t.l,t.a,t.b,t.opacity);if(t instanceof Ye){var n=t.h*Zv;return new qe(t.l,Math.cos(n)*t.c,Math.sin(n)*t.c,t.opacity)}t instanceof Ne||(t=Te(t));var e=Oe(t.r),r=Oe(t.g),i=Oe(t.b),o=Ue((.4124564*e+.3575761*r+.1804375*i)/Qv),u=Ue((.2126729*e+.7151522*r+.072175*i)/Kv),a=Ue((.0193339*e+.119192*r+.9503041*i)/t_);return new qe(116*u-16,500*(o-u),200*(u-a),t.opacity)}function Le(t,n,e,r){return 1===arguments.length?Pe(t):new qe(t,n,e,null==r?1:r)}function qe(t,n,e,r){this.l=+t,this.a=+n,this.b=+e,this.opacity=+r}function Ue(t){return t>i_?Math.pow(t,1/3):t/r_+n_}function Re(t){return t>e_?t*t*t:r_*(t-n_)}function De(t){return 255*(t<=.0031308?12.92*t:1.055*Math.pow(t,1/2.4)-.055)}function Oe(t){return(t/=255)<=.04045?t/12.92:Math.pow((t+.055)/1.055,2.4)}function Fe(t){if(t instanceof Ye)return new Ye(t.h,t.c,t.l,t.opacity);t instanceof qe||(t=Pe(t));var n=Math.atan2(t.b,t.a)*Gv;return new Ye(n<0?n+360:n,Math.sqrt(t.a*t.a+t.b*t.b),t.l,t.opacity)}function Ie(t,n,e,r){return 1===arguments.length?Fe(t):new Ye(t,n,e,null==r?1:r)}function Ye(t,n,e,r){this.h=+t,this.c=+n,this.l=+e,this.opacity=+r}function Be(t){if(t instanceof He)return new He(t.h,t.s,t.l,t.opacity);t instanceof Ne||(t=Te(t));var n=t.r/255,e=t.g/255,r=t.b/255,i=(h_*r+f_*n-l_*e)/(h_+f_-l_),o=r-i,u=(s_*(e-i)-a_*o)/c_,a=Math.sqrt(u*u+o*o)/(s_*i*(1-i)),c=a?Math.atan2(u,o)*Gv-120:NaN;return new He(c<0?c+360:c,a,i,t.opacity)}function je(t,n,e,r){return 1===arguments.length?Be(t):new He(t,n,e,null==r?1:r)}function He(t,n,e,r){this.h=+t,this.s=+n,this.l=+e,this.opacity=+r}function Xe(t,n,e,r,i){var o=t*t,u=o*t;return((1-3*t+3*o-u)*n+(4-6*o+3*u)*e+(1+3*t+3*o-3*u)*r+u*i)/6}function Ve(t){var n=t.length-1;return function(e){var r=e<=0?e=0:e>=1?(e=1,n-1):Math.floor(e*n),i=t[r],o=t[r+1],u=r>0?t[r-1]:2*i-o,a=r180||e<-180?e-360*Math.round(e/360):e):$e(isNaN(t)?n:t)}function Qe(t){return 1===(t=+t)?Ke:function(n,e){return e-n?Ge(n,e,t):$e(isNaN(n)?e:n)}}function Ke(t,n){var e=n-t;return e?Ze(t,e):$e(isNaN(t)?n:t)}function tr(t){return function(n){var e,r,i=n.length,o=new Array(i),u=new Array(i),a=new Array(i);for(e=0;eo&&(i=n.slice(o,i),a[u]?a[u]+=i:a[++u]=i),(e=e[0])===(r=r[0])?a[u]?a[u]+=r:a[++u]=r:(a[++u]=null,c.push({i:u,x:er(e,r)})),o=b_.lastIndex;return o180?n+=360:n-t>180&&(t+=360),o.push({i:e.push(i(e)+"rotate(",null,r)-2,x:er(t,n)})):n&&e.push(i(e)+"rotate("+n+r)}function a(t,n,e,o){t!==n?o.push({i:e.push(i(e)+"skewX(",null,r)-2,x:er(t,n)}):n&&e.push(i(e)+"skewX("+n+r)}function c(t,n,e,r,o,u){if(t!==e||n!==r){var a=o.push(i(o)+"scale(",null,",",null,")");u.push({i:a-4,x:er(t,e)},{i:a-2,x:er(n,r)})}else 1===e&&1===r||o.push(i(o)+"scale("+e+","+r+")")}return function(n,e){var r=[],i=[];return n=t(n),e=t(e),o(n.translateX,n.translateY,e.translateX,e.translateY,r,i),u(n.rotate,e.rotate,r,i),a(n.skewX,e.skewX,r,i),c(n.scaleX,n.scaleY,e.scaleX,e.scaleY,r,i),n=e=null,function(t){for(var n,e=-1,o=i.length;++e=0&&(e=t.slice(r+1),t=t.slice(0,r)),t&&!n.hasOwnProperty(t))throw new Error("unknown type: "+t);return{type:t,name:e}})}function kr(t,n){for(var e,r=0,i=t.length;r=s)return u;if(i)return i=!1,o;var n,e=f;if(34===t.charCodeAt(e)){for(var r=e;r++=200&&e<300||304===e){if(o)try{n=o.call(r,s)}catch(i){return void a.call("error",r,i)}else n=s;a.call("load",r,n)}else a.call("error",r,t)}var r,i,o,u,a=wr("beforesend","progress","load","error"),c=L(),s=new XMLHttpRequest,f=null,l=null,h=0;return"undefined"==typeof XDomainRequest||"withCredentials"in s||!/^(http(s)?:)?\/\//.test(t)||(s=new XDomainRequest),"onload"in s?s.onload=s.onerror=s.ontimeout=e:s.onreadystatechange=function(t){s.readyState>3&&e(t)},s.onprogress=function(t){a.call("progress",r,t)},r={header:function(t,n){return t=(t+"").toLowerCase(),arguments.length<2?c.get(t):(null==n?c.remove(t):c.set(t,n+""),r)},mimeType:function(t){return arguments.length?(i=null==t?null:t+"",r):i},responseType:function(t){return arguments.length?(u=t,r):u},timeout:function(t){return arguments.length?(h=+t,r):h},user:function(t){return arguments.length<1?f:(f=null==t?null:t+"",r)},password:function(t){return arguments.length<1?l:(l=null==t?null:t+"",r)},response:function(t){return o=t,r},get:function(t,n){return r.send("GET",t,n)},post:function(t,n){return r.send("POST",t,n)},send:function(n,e,o){return o||"function"!=typeof e||(o=e,e=null),o&&1===o.length&&(o=Pr(o)),s.open(n,t,!0,f,l),null==i||c.has("accept")||c.set("accept",i+",*/*"),s.setRequestHeader&&c.each(function(t,n){s.setRequestHeader(n,t)}),null!=i&&s.overrideMimeType&&s.overrideMimeType(i),null!=u&&(s.responseType=u),h>0&&(s.timeout=h),o&&r.on("error",o).on("load",function(t){o(null,t)}),a.call("beforesend",r,s),s.send(null==e?null:e),r},abort:function(){return s.abort(),r},on:function(){var t=a.on.apply(a,arguments);return t===a?r:t}},n?r.get(n):r}function Pr(t){return function(n,e){t(null==n?e:null)}}function Lr(t){var n=t.responseType;return n&&"text"!==n?t.response:t.responseText}function qr(t,n){return function(e,r){var i=zr(e).mimeType(t).response(n);return r?i.get(r):i}}function Ur(t,n){return function(e,r,i){arguments.length<3&&(i=r,r=null);var o=zr(e).mimeType(t);return o.row=function(t){return arguments.length?o.response(Rr(n,r=t)):r},o.row(r),i?o.get(i):o}}function Rr(t,n){return function(e){return t(e.responseText,n)}}function Dr(){return uy||(sy(Or),uy=cy.now()+ay)}function Or(){uy=0}function Fr(){this._call=this._time=this._next=null}function Ir(t,n,e){var r=new Fr;return r.restart(t,n,e),r}function Yr(){Dr(),++ny;for(var t,n=D_;n;)(t=uy-n._time)>=0&&n._call.call(null,t),n=n._next;--ny}function Br(t){uy=(oy=t||cy.now())+ay,ny=ey=0;try{Yr()}finally{ny=0,Hr(),uy=0}}function jr(){var t=cy.now(),n=t-oy;n>iy&&(ay-=n,oy=t)}function Hr(){for(var t,n,e=D_,r=1/0;e;)e._call?(r>e._time&&(r=e._time),t=e,e=e._next):(n=e._next,e._next=null,e=t?t._next=n:D_=n);O_=t,Xr(r)}function Xr(t){if(!ny){ey&&(ey=clearTimeout(ey));var n=t-uy;n>24?(t<1/0&&(ey=setTimeout(Br,n)),ry&&(ry=clearInterval(ry))):(ry||(ry=setInterval(jr,iy)),ny=1,sy(Br))}}function Vr(t,n,e){var r=new Fr;return n=null==n?0:+n,r.restart(function(e){r.stop(),t(e+n)},n,e),r}function Wr(t,n,e){var r=new Fr,i=n;return null==n?(r.restart(t,n,e),r):(n=+n,e=null==e?Dr():+e,r.restart(function o(u){u+=i,r.restart(o,i+=n,e),t(u)},n,e),r)}function $r(t,n,e,r){function i(n){return t(n=new Date((+n))),n}return i.floor=i,i.ceil=function(e){return t(e=new Date(e-1)),n(e,1),t(e),e},i.round=function(t){var n=i(t),e=i.ceil(t);return t-n0))return u;do u.push(new Date((+e)));while(n(e,o),t(e),e=0;)for(;n(t,1),!e(t););})},e&&(i.count=function(n,r){return fy.setTime(+n),ly.setTime(+r),t(fy),t(ly),Math.floor(e(fy,ly))},i.every=function(t){return t=Math.floor(t),isFinite(t)&&t>0?t>1?i.filter(r?function(n){return r(n)%t===0}:function(n){return i.count(0,n)%t===0}):i:null}),i}function Zr(t){return $r(function(n){n.setDate(n.getDate()-(n.getDay()+7-t)%7),n.setHours(0,0,0,0)},function(t,n){t.setDate(t.getDate()+7*n)},function(t,n){return(n-t-(n.getTimezoneOffset()-t.getTimezoneOffset())*vy)/gy})}function Gr(t){return $r(function(n){n.setUTCDate(n.getUTCDate()-(n.getUTCDay()+7-t)%7),n.setUTCHours(0,0,0,0)},function(t,n){t.setUTCDate(t.getUTCDate()+7*n)},function(t,n){return(n-t)/gy})}function Jr(t,n){if((e=(t=n?t.toExponential(n-1):t.toExponential()).indexOf("e"))<0)return null;var e,r=t.slice(0,e);return[r.length>1?r[0]+r.slice(2):r,+t.slice(e+1)]}function Qr(t){return t=Jr(Math.abs(t)),t?t[1]:NaN}function Kr(t,n){return function(e,r){for(var i=e.length,o=[],u=0,a=t[0],c=0;i>0&&a>0&&(c+a+1>r&&(a=Math.max(1,r-c)),o.push(e.substring(i-=a,i+a)),!((c+=a+1)>r));)a=t[u=(u+1)%t.length];return o.reverse().join(n)}}function ti(t,n){t=t.toPrecision(n);t:for(var e,r=t.length,i=1,o=-1;i0&&(o=0)}return o>0?t.slice(0,o)+t.slice(e+1):t}function ni(t,n){var e=Jr(t,n);if(!e)return t+"";var r=e[0],i=e[1],o=i-(dg=3*Math.max(-8,Math.min(8,Math.floor(i/3))))+1,u=r.length;return o===u?r:o>u?r+new Array(o-u+1).join("0"):o>0?r.slice(0,o)+"."+r.slice(o):"0."+new Array(1-o).join("0")+Jr(t,Math.max(0,n+o-1))[0]}function ei(t,n){var e=Jr(t,n);if(!e)return t+"";var r=e[0],i=e[1];return i<0?"0."+new Array((-i)).join("0")+r:r.length>i+1?r.slice(0,i+1)+"."+r.slice(i+1):r+new Array(i-r.length+2).join("0")}function ri(t){return new ii(t)}function ii(t){if(!(n=yg.exec(t)))throw new Error("invalid format: "+t);var n,e=n[1]||" ",r=n[2]||">",i=n[3]||"-",o=n[4]||"",u=!!n[5],a=n[6]&&+n[6],c=!!n[7],s=n[8]&&+n[8].slice(1),f=n[9]||"";"n"===f?(c=!0,f="g"):_g[f]||(f=""),(u||"0"===e&&"="===r)&&(u=!0,e="0",r="="),this.fill=e,this.align=r,this.sign=i,this.symbol=o,this.zero=u,this.width=a,this.comma=c,this.precision=s,this.type=f}function oi(t){return t}function ui(t){function n(t){function n(t){var n,i,c,g=d,m=v;if("c"===p)m=_(t)+m,t="";else{t=+t;var x=(t<0||1/t<0)&&(t*=-1,!0);if(t=_(t,h),x)for(n=-1,i=t.length,x=!1;++nc||c>57){m=(46===c?o+t.slice(n+1):t.slice(n))+m,t=t.slice(0,n);break}}l&&!s&&(t=r(t,1/0));var b=g.length+t.length+m.length,w=b>1)+g+t+m+w.slice(b)}return w+g+t+m}t=ri(t);var e=t.fill,u=t.align,a=t.sign,c=t.symbol,s=t.zero,f=t.width,l=t.comma,h=t.precision,p=t.type,d="$"===c?i[0]:"#"===c&&/[boxX]/.test(p)?"0"+p.toLowerCase():"",v="$"===c?i[1]:/[%p]/.test(p)?"%":"",_=_g[p],y=!p||/[defgprs%]/.test(p);return h=null==h?p?6:12:/[gprs]/.test(p)?Math.max(1,Math.min(21,h)):Math.max(0,Math.min(20,h)),n.toString=function(){return t+""},n}function e(t,e){var r=n((t=ri(t),t.type="f",t)),i=3*Math.max(-8,Math.min(8,Math.floor(Qr(e)/3))),o=Math.pow(10,-i),u=mg[8+i/3];return function(t){return r(o*t)+u}}var r=t.grouping&&t.thousands?Kr(t.grouping,t.thousands):oi,i=t.currency,o=t.decimal;return{format:n,formatPrefix:e}}function ai(n){return gg=ui(n),t.format=gg.format,t.formatPrefix=gg.formatPrefix,gg}function ci(t){return Math.max(0,-Qr(Math.abs(t)))}function si(t,n){return Math.max(0,3*Math.max(-8,Math.min(8,Math.floor(Qr(n)/3)))-Qr(Math.abs(t)))}function fi(t,n){return t=Math.abs(t),n=Math.abs(n)-t,Math.max(0,Qr(n)-Qr(t))+1}function li(t){if(0<=t.y&&t.y<100){var n=new Date((-1),t.m,t.d,t.H,t.M,t.S,t.L);return n.setFullYear(t.y),n}return new Date(t.y,t.m,t.d,t.H,t.M,t.S,t.L)}function hi(t){if(0<=t.y&&t.y<100){var n=new Date(Date.UTC(-1,t.m,t.d,t.H,t.M,t.S,t.L));return n.setUTCFullYear(t.y),n}return new Date(Date.UTC(t.y,t.m,t.d,t.H,t.M,t.S,t.L))}function pi(t){return{y:t,m:0,d:1,H:0,M:0,S:0,L:0}}function di(t){function n(t,n){return function(e){var r,i,o,u=[],a=-1,c=0,s=t.length;for(e instanceof Date||(e=new Date((+e)));++a=c)return-1;if(i=n.charCodeAt(u++),37===i){if(i=n.charAt(u++),o=B[i in bg?n.charAt(u++):i],!o||(r=o(t,e,r))<0)return-1}else if(i!=e.charCodeAt(r++))return-1}return r}function i(t,n,e){var r=C.exec(n.slice(e));return r?(t.p=z[r[0].toLowerCase()],e+r[0].length):-1}function o(t,n,e){var r=q.exec(n.slice(e));return r?(t.w=U[r[0].toLowerCase()],e+r[0].length):-1}function u(t,n,e){var r=P.exec(n.slice(e));return r?(t.w=L[r[0].toLowerCase()],e+r[0].length):-1}function a(t,n,e){var r=O.exec(n.slice(e));return r?(t.m=F[r[0].toLowerCase()],e+r[0].length):-1}function c(t,n,e){var r=R.exec(n.slice(e));return r?(t.m=D[r[0].toLowerCase()],e+r[0].length):-1}function s(t,n,e){return r(t,w,n,e)}function f(t,n,e){return r(t,M,n,e)}function l(t,n,e){return r(t,T,n,e)}function h(t){return S[t.getDay()]}function p(t){return N[t.getDay()]}function d(t){return A[t.getMonth()]}function v(t){return E[t.getMonth()]}function _(t){return k[+(t.getHours()>=12)]}function y(t){return S[t.getUTCDay()]}function g(t){return N[t.getUTCDay()]}function m(t){return A[t.getUTCMonth()]}function x(t){return E[t.getUTCMonth()]}function b(t){return k[+(t.getUTCHours()>=12)]}var w=t.dateTime,M=t.date,T=t.time,k=t.periods,N=t.days,S=t.shortDays,E=t.months,A=t.shortMonths,C=yi(k),z=gi(k),P=yi(N),L=gi(N),q=yi(S),U=gi(S),R=yi(E),D=gi(E),O=yi(A),F=gi(A),I={a:h,A:p,b:d,B:v,c:null,d:Li,e:Li,H:qi,I:Ui,j:Ri,L:Di,m:Oi,M:Fi,p:_,S:Ii,U:Yi,w:Bi,W:ji,x:null,X:null,y:Hi,Y:Xi,Z:Vi,"%":ao},Y={a:y,A:g,b:m,B:x,c:null,d:Wi,e:Wi,H:$i,I:Zi,j:Gi,L:Ji,m:Qi,M:Ki,p:b,S:to,U:no,w:eo,W:ro,x:null,X:null,y:io,Y:oo,Z:uo,"%":ao},B={a:o,A:u,b:a,B:c,c:s,d:Ni,e:Ni,H:Ei,I:Ei,j:Si,L:zi,m:ki,M:Ai,p:i,S:Ci,U:xi,w:mi,W:bi,x:f,X:l,y:Mi,Y:wi,Z:Ti,"%":Pi};return I.x=n(M,I),I.X=n(T,I),I.c=n(w,I),Y.x=n(M,Y),Y.X=n(T,Y),Y.c=n(w,Y),{format:function(t){var e=n(t+="",I);return e.toString=function(){return t},e},parse:function(t){var n=e(t+="",li);return n.toString=function(){return t},n},utcFormat:function(t){var e=n(t+="",Y);return e.toString=function(){return t},e},utcParse:function(t){var n=e(t,hi);return n.toString=function(){return t},n}}}function vi(t,n,e){var r=t<0?"-":"",i=(r?-t:t)+"",o=i.length;return r+(o68?1900:2e3),e+r[0].length):-1}function Ti(t,n,e){var r=/^(Z)|([+-]\d\d)(?:\:?(\d\d))?/.exec(n.slice(e,e+6));return r?(t.Z=r[1]?0:-(r[2]+(r[3]||"00")),e+r[0].length):-1}function ki(t,n,e){var r=wg.exec(n.slice(e,e+2));return r?(t.m=r[0]-1,e+r[0].length):-1}function Ni(t,n,e){var r=wg.exec(n.slice(e,e+2));return r?(t.d=+r[0],e+r[0].length):-1}function Si(t,n,e){var r=wg.exec(n.slice(e,e+3));return r?(t.m=0,t.d=+r[0],e+r[0].length):-1}function Ei(t,n,e){var r=wg.exec(n.slice(e,e+2));return r?(t.H=+r[0],e+r[0].length):-1}function Ai(t,n,e){var r=wg.exec(n.slice(e,e+2));return r?(t.M=+r[0],e+r[0].length):-1}function Ci(t,n,e){var r=wg.exec(n.slice(e,e+2));return r?(t.S=+r[0],e+r[0].length):-1}function zi(t,n,e){var r=wg.exec(n.slice(e,e+3));return r?(t.L=+r[0],e+r[0].length):-1}function Pi(t,n,e){var r=Mg.exec(n.slice(e,e+1));return r?e+r[0].length:-1}function Li(t,n){return vi(t.getDate(),n,2)}function qi(t,n){return vi(t.getHours(),n,2)}function Ui(t,n){return vi(t.getHours()%12||12,n,2)}function Ri(t,n){return vi(1+ky.count(jy(t),t),n,3)}function Di(t,n){return vi(t.getMilliseconds(),n,3)}function Oi(t,n){return vi(t.getMonth()+1,n,2)}function Fi(t,n){return vi(t.getMinutes(),n,2)}function Ii(t,n){return vi(t.getSeconds(),n,2)}function Yi(t,n){return vi(Sy.count(jy(t),t),n,2)}function Bi(t){return t.getDay()}function ji(t,n){return vi(Ey.count(jy(t),t),n,2)}function Hi(t,n){return vi(t.getFullYear()%100,n,2)}function Xi(t,n){return vi(t.getFullYear()%1e4,n,4)}function Vi(t){var n=t.getTimezoneOffset();return(n>0?"-":(n*=-1,"+"))+vi(n/60|0,"0",2)+vi(n%60,"0",2)}function Wi(t,n){return vi(t.getUTCDate(),n,2)}function $i(t,n){return vi(t.getUTCHours(),n,2)}function Zi(t,n){return vi(t.getUTCHours()%12||12,n,2)}function Gi(t,n){return vi(1+Zy.count(pg(t),t),n,3)}function Ji(t,n){return vi(t.getUTCMilliseconds(),n,3)}function Qi(t,n){return vi(t.getUTCMonth()+1,n,2)}function Ki(t,n){return vi(t.getUTCMinutes(),n,2)}function to(t,n){return vi(t.getUTCSeconds(),n,2)}function no(t,n){return vi(Jy.count(pg(t),t),n,2)}function eo(t){return t.getUTCDay()}function ro(t,n){return vi(Qy.count(pg(t),t),n,2)}function io(t,n){return vi(t.getUTCFullYear()%100,n,2)}function oo(t,n){return vi(t.getUTCFullYear()%1e4,n,4)}function uo(){return"+0000"}function ao(){return"%"}function co(n){return xg=di(n),t.timeFormat=xg.format,t.timeParse=xg.parse,t.utcFormat=xg.utcFormat,t.utcParse=xg.utcParse,xg}function so(t){return t.toISOString()}function fo(t){var n=new Date(t);return isNaN(n)?null:n}function lo(t){function n(n){var o=n+"",u=e.get(o);if(!u){if(i!==zg)return i;e.set(o,u=r.push(n))}return t[(u-1)%t.length]}var e=L(),r=[],i=zg;return t=null==t?[]:Cg.call(t),n.domain=function(t){if(!arguments.length)return r.slice();r=[],e=L();for(var i,o,u=-1,a=t.length;++u=e?1:r(t)}}}function xo(t){return function(n,e){var r=t(n=+n,e=+e);return function(t){return t<=0?n:t>=1?e:r(t)}}}function bo(t,n,e,r){var i=t[0],o=t[1],u=n[0],a=n[1];return o2?wo:bo,o=u=null,r}function r(n){return(o||(o=i(a,c,f?mo(t):t,s)))(+n)}var i,o,u,a=Pg,c=Pg,s=ar,f=!1;return r.invert=function(t){return(u||(u=i(c,a,go,f?xo(n):n)))(+t)},r.domain=function(t){return arguments.length?(a=Ag.call(t,yo),e()):a.slice()},r.range=function(t){return arguments.length?(c=Cg.call(t),e()):c.slice()},r.rangeRound=function(t){return c=Cg.call(t),s=cr,e()},r.clamp=function(t){return arguments.length?(f=!!t,e()):f},r.interpolate=function(t){return arguments.length?(s=t,e()):s},e()}function ko(n,e,r){var i,o=n[0],u=n[n.length-1],a=p(o,u,null==e?10:e);switch(r=ri(null==r?",f":r),r.type){case"s":var c=Math.max(Math.abs(o),Math.abs(u));return null!=r.precision||isNaN(i=si(a,c))||(r.precision=i),t.formatPrefix(r,c);case"":case"e":case"g":case"p":case"r":null!=r.precision||isNaN(i=fi(a,Math.max(Math.abs(o),Math.abs(u))))||(r.precision=i-("e"===r.type));break;case"f":case"%":null!=r.precision||isNaN(i=ci(a))||(r.precision=i-2*("%"===r.type))}return t.format(r)}function No(t){var n=t.domain;return t.ticks=function(t){var e=n();return h(e[0],e[e.length-1],null==t?10:t)},t.tickFormat=function(t,e){return ko(n(),t,e)},t.nice=function(e){var r=n(),i=r.length-1,o=null==e?10:e,u=r[0],a=r[i],c=p(u,a,o);return c&&(c=p(Math.floor(u/c)*c,Math.ceil(a/c)*c,o),r[0]=Math.floor(u/c)*c,r[i]=Math.ceil(a/c)*c,n(r)),t},t}function So(){var t=To(go,er);return t.copy=function(){return Mo(t,So())},No(t)}function Eo(){function t(t){return+t}var n=[0,1];return t.invert=t,t.domain=t.range=function(e){return arguments.length?(n=Ag.call(e,yo),t):n.slice()},t.copy=function(){return Eo().domain(n)},No(t)}function Ao(t,n){t=t.slice();var e,r=0,i=t.length-1,o=t[r],u=t[i];return u0){for(;pc)break;_.push(l)}}else for(;p=1;--f)if(l=s*f,!(lc)break;_.push(l)}}else _=h(p,d,Math.min(d-p,v)).map(u);return n?_.reverse():_},e.tickFormat=function(n,r){if(null==r&&(r=10===i?".0e":","),"function"!=typeof r&&(r=t.format(r)),n===1/0)return r;null==n&&(n=10);var a=Math.max(1,i*n/e.ticks().length);return function(t){var n=t/u(Math.round(o(t)));return n*i0?o[n-1]:r[0],n=i?[o[i-1],r]:[o[n-1],o[n]]},t.copy=function(){return Yo().domain([e,r]).range(u)},No(t)}function Bo(){function t(t){if(t<=t)return e[yd(n,t,0,r)]}var n=[.5],e=[0,1],r=1;return t.domain=function(i){return arguments.length?(n=Cg.call(i),r=Math.min(n.length,e.length-1),t):n.slice()},t.range=function(i){return arguments.length?(e=Cg.call(i),r=Math.min(n.length,e.length-1),t):e.slice()},t.invertExtent=function(t){var r=e.indexOf(t);return[n[r-1],n[r]]},t.copy=function(){return Bo().domain(n).range(e)},t}function jo(t){return new Date(t)}function Ho(t){return t instanceof Date?+t:+new Date((+t))}function Xo(t,n,r,i,o,u,a,c,s){function f(e){return(a(e)1)&&(t-=Math.floor(t));var n=Math.abs(t-.5);return Wg.h=360*t-100,Wg.s=1.5-1.5*n,Wg.l=.8-.9*n,Wg+""}function Go(t){var n=t.length;return function(e){return t[Math.max(0,Math.min(n-1,Math.floor(e*n)))]}}function Jo(t){function n(n){var o=(n-e)/(r-e);return t(i?Math.max(0,Math.min(1,o)):o)}var e=0,r=1,i=!1;return n.domain=function(t){return arguments.length?(e=+t[0],r=+t[1],n):[e,r]},n.clamp=function(t){return arguments.length?(i=!!t,n):i},n.interpolator=function(e){return arguments.length?(t=e,n):t},n.copy=function(){return Jo(t).domain([e,r]).clamp(i)},No(n)}function Qo(t){var n=t+="",e=n.indexOf(":");return e>=0&&"xmlns"!==(n=t.slice(0,e))&&(t=t.slice(e+1)),Kg.hasOwnProperty(n)?{space:Kg[n],local:t}:t}function Ko(t){return function(){var n=this.ownerDocument,e=this.namespaceURI;return e===Qg&&n.documentElement.namespaceURI===Qg?n.createElement(t):n.createElementNS(e,t)}}function tu(t){return function(){return this.ownerDocument.createElementNS(t.space,t.local)}}function nu(t){var n=Qo(t);return(n.local?tu:Ko)(n)}function eu(){return new ru}function ru(){this._="@"+(++tm).toString(36)}function iu(t,n,e){return t=ou(t,n,e),function(n){var e=n.relatedTarget;e&&(e===this||8&e.compareDocumentPosition(this))||t.call(this,n)}}function ou(n,e,r){return function(i){var o=t.event;t.event=i;try{n.call(this,this.__data__,e,r)}finally{t.event=o}}}function uu(t){return t.trim().split(/^|\s+/).map(function(t){var n="",e=t.indexOf(".");return e>=0&&(n=t.slice(e+1),t=t.slice(0,e)),{type:t,name:n}})}function au(t){return function(){var n=this.__on;if(n){for(var e,r=0,i=-1,o=n.length;r=b&&(b=x+1);!(m=_[b])&&++b=0;)(r=i[o])&&(u&&u!==r.nextSibling&&u.parentNode.insertBefore(r,u),u=r);return this}function zu(t){function n(n,e){return n&&e?t(n.__data__,e.__data__):!n-!e}t||(t=Pu);for(var e=this._groups,r=e.length,i=new Array(r),o=0;on?1:t>=n?0:NaN}function Lu(){var t=arguments[0];return arguments[0]=this,t.apply(null,arguments),this}function qu(){var t=new Array(this.size()),n=-1;return this.each(function(){t[++n]=this}),t}function Uu(){for(var t=this._groups,n=0,e=t.length;n1?this.each((null==n?Wu:"function"==typeof n?Zu:$u)(t,n,null==e?"":e)):Vu(r=this.node()).getComputedStyle(r,null).getPropertyValue(t)}function Ju(t){return function(){delete this[t]}}function Qu(t,n){return function(){this[t]=n}}function Ku(t,n){return function(){var e=n.apply(this,arguments);null==e?delete this[t]:this[t]=e}}function ta(t,n){return arguments.length>1?this.each((null==n?Ju:"function"==typeof n?Ku:Qu)(t,n)):this.node()[t]}function na(t){return t.trim().split(/^|\s+/)}function ea(t){return t.classList||new ra(t)}function ra(t){this._node=t,this._names=na(t.getAttribute("class")||"")}function ia(t,n){for(var e=ea(t),r=-1,i=n.length;++rlm)throw new Error("too late");return e}function Ia(t,n){var e=t.__transition;if(!e||!(e=e[n])||e.state>pm)throw new Error("too late");return e}function Ya(t,n){var e=t.__transition;if(!e||!(e=e[n]))throw new Error("too late");return e}function Ba(t,n,e){function r(t){e.state=hm,e.delay<=t?i(t-e.delay):e.timer.restart(i,e.delay,e.time)}function i(r){var i,c,s,f;for(i in a)f=a[i],f.name===e.name&&(f.state===dm?(f.state=_m,f.timer.stop(),f.on.call("interrupt",t,t.__data__,f.index,f.group),delete a[i]):+i=0&&(t=t.slice(0,n)),!t||"start"===t})}function yc(t,n,e){var r,i,o=_c(n)?Fa:Ia;return function(){var u=o(this,t),a=u.on;a!==r&&(i=(r=a).copy()).on(n,e),u.on=i}}function gc(t,n){var e=this._id;return arguments.length<2?Ya(this.node(),e).on.on(t):this.each(yc(e,t,n))}function mc(t){return function(){var n=this.parentNode;for(var e in this.__transition)if(+e!==t)return;n&&n.removeChild(this)}}function xc(){return this.on("end.remove",mc(this._id))}function bc(t){var n=this._name,e=this._id;"function"!=typeof t&&(t=vu(t));for(var r=this._groups,i=r.length,o=new Array(i),u=0;uhm&&e.name===n)return new Uc([[t]],bm,n,(+r))}return null}function Yc(t){return t}function Bc(t,n,e){var r=t(e);return"translate("+(isFinite(r)?r:n(e))+",0)"}function jc(t,n,e){var r=t(e);return"translate(0,"+(isFinite(r)?r:n(e))+")"}function Hc(t){var n=t.bandwidth()/2;return function(e){return t(e)+n}}function Xc(){return!this.__axis}function Vc(t,n){function e(e){var s,f=null==i?n.ticks?n.ticks.apply(n,r):n.domain():i,l=null==o?n.tickFormat?n.tickFormat.apply(n,r):Yc:o,h=Math.max(u,0)+c,p=t===Mm||t===km?Bc:jc,d=n.range(),v=d[0]+.5,_=d[d.length-1]+.5,y=(n.bandwidth?Hc:Yc)(n.copy()),g=e.selection?e.selection():e,m=g.selectAll(".domain").data([null]),x=g.selectAll(".tick").data(f,n).order(),b=x.exit(),w=x.enter().append("g").attr("class","tick"),M=x.select("line"),T=x.select("text"),k=t===Mm||t===Nm?-1:1,N=t===Nm||t===Tm?(s="x","y"):(s="y","x");m=m.merge(m.enter().insert("path",".tick").attr("class","domain").attr("stroke","#000")),x=x.merge(w),M=M.merge(w.append("line").attr("stroke","#000").attr(s+"2",k*u).attr(N+"1",.5).attr(N+"2",.5)),T=T.merge(w.append("text").attr("fill","#000").attr(s,k*h).attr(N,.5).attr("dy",t===Mm?"0em":t===km?".71em":".32em")),e!==g&&(m=m.transition(e),x=x.transition(e),M=M.transition(e),T=T.transition(e),b=b.transition(e).attr("opacity",Sm).attr("transform",function(t){return p(y,this.parentNode.__axis||y,t)}),w.attr("opacity",Sm).attr("transform",function(t){return p(this.parentNode.__axis||y,y,t)})),b.remove(),m.attr("d",t===Nm||t==Tm?"M"+k*a+","+v+"H0.5V"+_+"H"+k*a:"M"+v+","+k*a+"V0.5H"+_+"V"+k*a),x.attr("opacity",1).attr("transform",function(t){return p(y,y,t)}),M.attr(s+"2",k*u),T.attr(s,k*h).text(l),g.filter(Xc).attr("fill","none").attr("font-size",10).attr("font-family","sans-serif").attr("text-anchor",t===Tm?"start":t===Nm?"end":"middle"),g.each(function(){this.__axis=y})}var r=[],i=null,o=null,u=6,a=6,c=3;return e.scale=function(t){return arguments.length?(n=t,e):n},e.ticks=function(){return r=wm.call(arguments),e},e.tickArguments=function(t){return arguments.length?(r=null==t?[]:wm.call(t),e):r.slice()},e.tickValues=function(t){return arguments.length?(i=null==t?null:wm.call(t),e):i&&i.slice()},e.tickFormat=function(t){return arguments.length?(o=t,e):o},e.tickSize=function(t){return arguments.length?(u=a=+t,e):u},e.tickSizeInner=function(t){return arguments.length?(u=+t,e):u},e.tickSizeOuter=function(t){return arguments.length?(a=+t,e):a},e.tickPadding=function(t){return arguments.length?(c=+t,e):c},e}function Wc(t){return Vc(Mm,t)}function $c(t){return Vc(Tm,t)}function Zc(t){return Vc(km,t)}function Gc(t){return Vc(Nm,t)}function Jc(t,n){return t.parent===n.parent?1:2}function Qc(t){return t.reduce(Kc,0)/t.length}function Kc(t,n){return t+n.x}function ts(t){return 1+t.reduce(ns,0)}function ns(t,n){return Math.max(t,n.y)}function es(t){for(var n;n=t.children;)t=n[0];return t}function rs(t){for(var n;n=t.children;)t=n[n.length-1];return t}function is(){function t(t){var o,u=0;t.eachAfter(function(t){var e=t.children;e?(t.x=Qc(e),t.y=ts(e)):(t.x=o?u+=n(t,o):0,t.y=0,o=t)});var a=es(t),c=rs(t),s=a.x-n(a,c)/2,f=c.x+n(c,a)/2;return t.eachAfter(i?function(n){n.x=(n.x-t.x)*e,n.y=(t.y-n.y)*r}:function(n){n.x=(n.x-s)/(f-s)*e,n.y=(1-(t.y?n.y/t.y:1))*r})}var n=Jc,e=1,r=1,i=!1;return t.separation=function(e){return arguments.length?(n=e,t):n},t.size=function(n){return arguments.length?(i=!1,e=+n[0],r=+n[1],t):i?null:[e,r]},t.nodeSize=function(n){return arguments.length?(i=!0,e=+n[0],r=+n[1],t):i?[e,r]:null},t}function os(t){var n,e,r,i,o=this,u=[o];do for(n=u.reverse(),u=[];o=n.pop();)if(t(o),e=o.children)for(r=0,i=e.length;r=0;--e)i.push(n[e]);return this}function as(t){for(var n,e,r,i=this,o=[i],u=[];i=o.pop();)if(u.push(i),n=i.children)for(e=0,r=n.length;e=0;)e+=r[i].value;n.value=e})}function ss(t){return this.eachBefore(function(n){n.children&&n.children.sort(t)})}function fs(t){for(var n=this,e=ls(n,t),r=[n];n!==e;)n=n.parent,r.push(n);for(var i=r.length;t!==e;)r.splice(i,0,t),t=t.parent;return r}function ls(t,n){if(t===n)return t;var e=t.ancestors(),r=n.ancestors(),i=null;for(t=e.pop(),n=r.pop();t===n;)i=t,t=e.pop(),n=r.pop();return i}function hs(){for(var t=this,n=[t];t=t.parent;)n.push(t);return n}function ps(){var t=[];return this.each(function(n){t.push(n)}),t}function ds(){var t=[];return this.eachBefore(function(n){n.children||t.push(n)}),t}function vs(){var t=this,n=[];return t.each(function(e){e!==t&&n.push({source:e.parent,target:e})}),n}function _s(t,n){var e,r,i,o,u,a=new bs(t),c=+t.value&&(a.value=t.value),s=[a];for(null==n&&(n=gs);e=s.pop();)if(c&&(e.value=+e.data.value),(i=n(e.data))&&(u=i.length))for(e.children=new Array(u),o=u-1;o>=0;--o)s.push(r=e.children[o]=new bs(i[o])),r.parent=e,r.depth=e.depth+1;return a.eachBefore(xs)}function ys(){return _s(this).eachBefore(ms)}function gs(t){return t.children}function ms(t){t.data=t.data.data}function xs(t){var n=0;do t.height=n;while((t=t.parent)&&t.height<++n)}function bs(t){this.data=t,this.depth=this.height=0,this.parent=null}function ws(t){this._=t,this.next=null}function Ms(t){for(var n,e=(t=t.slice()).length,r=null,i=r;e;){var o=new ws(t[e-1]);i=i?i.next=o:r=o,t[n]=t[--e]}return{head:r,tail:i}}function Ts(t){return Ns(Ms(t),[])}function ks(t,n){var e=n.x-t.x,r=n.y-t.y,i=t.r-n.r;return i*i+1e-6>e*e+r*r}function Ns(t,n){var e,r,i,o=null,u=t.head;switch(n.length){case 1:e=Ss(n[0]);break;case 2:e=Es(n[0],n[1]);break;case 3:e=As(n[0],n[1],n[2])}for(;u;)i=u._,r=u.next,e&&ks(e,i)?o=u:(o?(t.tail=o,o.next=null):t.head=t.tail=null,n.push(i),e=Ns(t,n),n.pop(),t.head?(u.next=t.head,t.head=u):(u.next=null,t.head=t.tail=u),o=t.tail,o.next=r),u=r;return t.tail=o,e}function Ss(t){return{x:t.x,y:t.y,r:t.r}}function Es(t,n){var e=t.x,r=t.y,i=t.r,o=n.x,u=n.y,a=n.r,c=o-e,s=u-r,f=a-i,l=Math.sqrt(c*c+s*s);return{x:(e+o+c/l*f)/2,y:(r+u+s/l*f)/2,r:(l+i+a)/2}}function As(t,n,e){var r=t.x,i=t.y,o=t.r,u=n.x,a=n.y,c=n.r,s=e.x,f=e.y,l=e.r,h=2*(r-u),p=2*(i-a),d=2*(c-o),v=r*r+i*i-o*o-u*u-a*a+c*c,_=2*(r-s),y=2*(i-f),g=2*(l-o),m=r*r+i*i-o*o-s*s-f*f+l*l,x=_*p-h*y,b=(p*m-y*v)/x-r,w=(y*d-p*g)/x,M=(_*v-h*m)/x-i,T=(h*g-_*d)/x,k=w*w+T*T-1,N=2*(b*w+M*T+o),S=b*b+M*M-o*o,E=(-N-Math.sqrt(N*N-4*k*S))/(2*k);return{x:b+w*E+r,y:M+T*E+i,r:E}}function Cs(t,n,e){var r=t.x,i=t.y,o=n.r+e.r,u=t.r+e.r,a=n.x-r,c=n.y-i,s=a*a+c*c;if(s){var f=.5+((u*=u)-(o*=o))/(2*s),l=Math.sqrt(Math.max(0,2*o*(u+s)-(u-=s)*u-o*o))/(2*s);e.x=r+f*a+l*c,e.y=i+f*c-l*a}else e.x=r+u,e.y=i}function zs(t,n){var e=n.x-t.x,r=n.y-t.y,i=t.r+n.r;return i*i>e*e+r*r}function Ps(t,n,e){var r=t.x-n,i=t.y-e;return r*r+i*i}function Ls(t){this._=t,this.next=null,this.previous=null}function qs(t){if(!(i=t.length))return 0;var n,e,r,i;if(n=t[0],n.x=0,n.y=0,!(i>1))return n.r;if(e=t[1],n.x=-e.r,e.x=n.r,e.y=0,!(i>2))return n.r+e.r;Cs(e,n,r=t[2]);var o,u,a,c,s,f,l,h=n.r*n.r,p=e.r*e.r,d=r.r*r.r,v=h+p+d,_=h*n.x+p*e.x+d*r.x,y=h*n.y+p*e.y+d*r.y;n=new Ls(n),e=new Ls(e),r=new Ls(r),n.next=r.previous=e,e.next=n.previous=r,r.next=e.previous=n;t:for(a=3;a0)throw new Error("cycle");return o}var n=$s,e=Zs;return t.id=function(e){return arguments.length?(n=Ds(e),t):n},t.parentId=function(n){return arguments.length?(e=Ds(n),t):e},t}function Js(t,n){return t.parent===n.parent?1:2}function Qs(t){var n=t.children;return n?n[0]:t.t}function Ks(t){var n=t.children;return n?n[n.length-1]:t.t}function tf(t,n,e){var r=e/(n.i-t.i);n.c-=r,n.s+=e,t.c+=r,n.z+=e,n.m+=e}function nf(t){for(var n,e=0,r=0,i=t.children,o=i.length;--o>=0;)n=i[o],n.z+=e,n.m+=e,e+=n.s+(r+=n.c)}function ef(t,n,e){return t.a.parent===n.parent?t.a:e}function rf(t,n){this._=t,this.parent=null,this.children=null,this.A=null,this.a=this,this.z=0,this.m=0,this.c=0,this.s=0,this.t=null,this.i=n}function of(t){for(var n,e,r,i,o,u=new rf(t,0),a=[u];n=a.pop();)if(r=n._.children)for(n.children=new Array(o=r.length),i=o-1;i>=0;--i)a.push(e=n.children[i]=new rf(r[i],i)),e.parent=n;return(u.parent=new rf(null,0)).children=[u],u}function uf(){function t(t){var r=of(t);if(r.eachAfter(n),r.parent.m=-r.z,r.eachBefore(e),c)t.eachBefore(i);else{var s=t,f=t,l=t;t.eachBefore(function(t){t.xf.x&&(f=t),t.depth>l.depth&&(l=t)});var h=s===f?1:o(s,f)/2,p=h-s.x,d=u/(f.x+h+p),v=a/(l.depth||1);t.eachBefore(function(t){t.x=(t.x+p)*d,t.y=t.depth*v})}return t}function n(t){var n=t.children,e=t.parent.children,i=t.i?e[t.i-1]:null;if(n){nf(t);var u=(n[0].z+n[n.length-1].z)/2;i?(t.z=i.z+o(t._,i._),t.m=t.z-u):t.z=u}else i&&(t.z=i.z+o(t._,i._));t.parent.A=r(t,i,t.parent.A||e[0])}function e(t){t._.x=t.z+t.parent.m,t.m+=t.parent.m}function r(t,n,e){if(n){for(var r,i=t,u=t,a=n,c=i.parent.children[0],s=i.m,f=u.m,l=a.m,h=c.m;a=Ks(a),i=Qs(i),a&&i;)c=Qs(c),u=Ks(u),u.a=t,r=a.z+l-i.z-s+o(a._,i._),r>0&&(tf(ef(a,t,e),t,r),s+=r,f+=r),l+=a.m,s+=i.m,h+=c.m,f+=u.m;a&&!Ks(u)&&(u.t=a,u.m+=l-f),i&&!Qs(c)&&(c.t=i,c.m+=s-h,e=t)}return e}function i(t){t.x*=u,t.y=t.depth*a}var o=Js,u=1,a=1,c=null;return t.separation=function(n){return arguments.length?(o=n,t):o},t.size=function(n){return arguments.length?(c=!1,u=+n[0],a=+n[1],t):c?null:[u,a]},t.nodeSize=function(n){return arguments.length?(c=!0,u=+n[0],a=+n[1],t):c?[u,a]:null},t}function af(t,n,e,r,i){for(var o,u=t.children,a=-1,c=u.length,s=t.value&&(i-e)/t.value;++ap&&(p=a),y=l*l*_,d=Math.max(p/y,y/h),d>v){l-=a;break}v=d}g.push(u={value:l,dice:s=n-1){var s=c[t];return s.x0=r,s.y0=i,s.x1=u,s.y1=a,void 0}for(var l=f[t],h=e/2+l,p=t+1,d=n-1;p>>1;f[v]u-r){var g=(i*y+a*_)/e;o(t,p,_,r,i,u,g),o(p,n,y,r,g,u,a)}else{var m=(r*y+u*_)/e;o(t,p,_,r,i,m,a),o(p,n,y,m,i,u,a)}}var u,a,c=t.children,s=c.length,f=new Array(s+1);for(f[0]=a=u=0;us+d||if+d||un){var v=s-a.x-a.vx,_=f-a.y-a.vy,y=v*v+_*_;yt.r&&(t.r=t[n].r)}var r,i,o=1,u=1;return"function"!=typeof t&&(t=pf(null==t?1:+t)),n.initialize=function(n){var e,o=(r=n).length;for(i=new Array(o),e=0;e1?(null==n?l.remove(t):l.set(t,i(n)),o):l.get(t)},find:function(n,e,r){var i,o,u,a,c,s=0,f=t.length;for(null==r?r=1/0:r*=r,s=0;s1?(p.on(t,n),o):p.on(t)}}}function Mf(){function t(t){var n,a=i.length,c=jt(i,xf,bf).visitAfter(e);for(u=t,n=0;n=f)){(t.data!==o||t.next)&&(0===i&&(i=df(),p+=i*i),0===c&&(c=df(),p+=c*c),p0)){if(o/=d,d<0){if(o0){if(o>p)return;o>h&&(h=o)}if(o=r-c,d||!(o<0)){if(o/=d,d<0){if(o>p)return;o>h&&(h=o)}else if(d>0){if(o0)){if(o/=v,v<0){if(o0){if(o>p)return;o>h&&(h=o)}if(o=i-s,v||!(o<0)){if(o/=v,v<0){if(o>p)return;o>h&&(h=o)}else if(v>0){if(o0||p<1)||(h>0&&(t[0]=[c+h*d,s+h*v]),p<1&&(t[1]=[c+p*d,s+p*v]),!0)}}}}}function $f(t,n,e,r,i){var o=t[1];if(o)return!0;var u,a,c=t[0],s=t.left,f=t.right,l=s[0],h=s[1],p=f[0],d=f[1],v=(l+p)/2,_=(h+d)/2;if(d===h){if(v=r)return;if(l>p){if(c){if(c[1]>=i)return}else c=[v,e];o=[v,i]}else{if(c){if(c[1]1)if(l>p){if(c){if(c[1]>=i)return}else c=[(e-a)/u,e];o=[(i-a)/u,i]}else{if(c){if(c[1]=r)return}else c=[n,u*n+a];o=[r,u*r+a]}else{if(c){if(c[0]jm||Math.abs(i[0][1]-i[1][1])>jm)||delete Im[o]}function Gf(t){return Om[t.index]={site:t,halfedges:[]}}function Jf(t,n){var e=t.site,r=n.left,i=n.right;return e===i&&(i=r,r=e),i?Math.atan2(i[1]-r[1],i[0]-r[0]):(e===r?(r=n[1],i=n[0]):(r=n[0],i=n[1]),Math.atan2(r[0]-i[0],i[1]-r[1]))}function Qf(t,n){return n[+(n.left!==t.site)]}function Kf(t,n){return n[+(n.left===t.site)]}function tl(){for(var t,n,e,r,i=0,o=Om.length;ijm||Math.abs(v-h)>jm)&&(c.splice(a,0,Im.push(Xf(u,p,Math.abs(d-t)jm?[t,Math.abs(l-t)jm?[Math.abs(h-r)jm?[e,Math.abs(l-e)jm?[Math.abs(h-n)=-Hm)){var p=c*c+s*s,d=f*f+l*l,v=(l*p-s*d)/h,_=(c*d-f*p)/h,y=Ym.pop()||new el;y.arc=t,y.site=i,y.x=v+u,y.y=(y.cy=_+a)+Math.sqrt(v*v+_*_),t.circle=y;for(var g=null,m=Fm._;m;)if(y.yjm)a=a.L;else{if(i=o-ll(a,u),!(i>jm)){r>-jm?(n=a.P,e=a):i>-jm?(n=a,e=a.N):n=e=a;break}if(!a.R){n=a;break}a=a.R}Gf(t);var c=ul(t);if(Dm.insert(n,c),n||e){if(n===e)return il(n),e=ul(n.site),Dm.insert(c,e),c.edge=e.edge=Hf(n.site,c.site),rl(n),void rl(e);if(!e)return void(c.edge=Hf(n.site,c.site));il(n),il(e);var s=n.site,f=s[0],l=s[1],h=t[0]-f,p=t[1]-l,d=e.site,v=d[0]-f,_=d[1]-l,y=2*(h*_-p*v),g=h*h+p*p,m=v*v+_*_,x=[(_*g-p*m)/y+f,(h*m-v*g)/y+l];Vf(e.edge,s,d,x),c.edge=Hf(s,t,null,x),e.edge=Hf(t,d,null,x),rl(n),rl(e)}}function fl(t,n){var e=t.site,r=e[0],i=e[1],o=i-n;if(!o)return r;var u=t.P;if(!u)return-(1/0);e=u.site;var a=e[0],c=e[1],s=c-n;if(!s)return a;var f=a-r,l=1/o-1/s,h=f/s;return l?(-h+Math.sqrt(h*h-2*l*(f*f/(-2*s)-c+s/2+i-o/2)))/l+r:(r+a)/2}function ll(t,n){var e=t.N;if(e)return fl(e,n);var r=t.site;return r[1]===n?r[0]:1/0}function hl(t,n,e){return(t[0]-e[0])*(n[1]-t[1])-(t[0]-n[0])*(e[1]-t[1])}function pl(t,n){return n[1]-t[1]||n[0]-t[0]}function dl(t,n){var e,r,i,o=t.sort(pl).pop();for(Im=[],Om=new Array(t.length),Dm=new Ff,Fm=new Ff;;)if(i=Rm,o&&(!i||o[1]0?qa(this).transition().duration(A).call(u,f,a):qa(this).call(n.transform,f)}}function h(){if(b.apply(this,arguments)){var n,e,r,i=a(this,arguments),o=t.event.changedTouches,u=o.length;for(xl(),n=0;nMath.abs(t[1]-O[1])?M=!0:w=!0),O=t,b=!0,Al(),o()}function o(){var t;switch(m=O[0]-D[0],x=O[1]-D[1],N){case Wm:case Vm:S&&(m=Math.max(P-l,Math.min(q-v,m)),h=l+m,_=v+m),E&&(x=Math.max(L-p,Math.min(U-y,x)),d=p+x,g=y+x);break;case $m:S<0?(m=Math.max(P-l,Math.min(q-l,m)),h=l+m,_=v):S>0&&(m=Math.max(P-v,Math.min(q-v,m)),h=l,_=v+m),E<0?(x=Math.max(L-p,Math.min(U-p,x)),d=p+x,g=y):E>0&&(x=Math.max(L-y,Math.min(U-y,x)),d=p,g=y+x);break;case Zm:S&&(h=Math.max(P,Math.min(q,l-m*S)),_=Math.max(P,Math.min(q,v+m*S))),E&&(d=Math.max(L,Math.min(U,p-x*E)),g=Math.max(L,Math.min(U,y+x*E)))}_0&&(l=h-m),E<0?y=g-x:E>0&&(p=d-x),N=Wm,Y.attr("cursor",Km.selection),o());break;default:return}Al()}function s(){switch(t.event.keyCode){case 16:R&&(w=M=R=!1,o());break;case 18:N===Zm&&(S<0?v=_:S>0&&(l=h),E<0?y=g:E>0&&(p=d),N=$m,o());break;case 32:N===Wm&&(t.event.altKey?(S&&(v=_-m*S,l=h+m*S),E&&(y=g-x*E,p=d+x*E),N=Zm):(S<0?v=_:S>0&&(l=h),E<0?y=g:E>0&&(p=d),N=$m),Y.attr("cursor",Km[k]),o());break;default:return}Al()}if(t.event.touches){if(t.event.changedTouches.length1?0:t<-1?Yx:Math.acos(t)}function Hl(t){return t>1?Bx:t<-1?-Bx:Math.asin(t)}function Xl(t){return(t=nb(t/2))*t}function Vl(){}function Wl(t,n){t&&ub.hasOwnProperty(t.type)&&ub[t.type](t,n)}function $l(t,n,e){var r,i=-1,o=t.length-e;for(n.lineStart();++i=0?1:-1,i=r*e,o=Gx(n),u=nb(n),a=fx*u,c=sx*o+a*Gx(i),s=a*r*nb(i);ix.add(Zx(s,c)),cx=t,sx=o,fx=u}function nh(t){return ox?ox.reset():(ox=Il(),ix=Il()),Gl(t,ab),2*ox}function eh(t){return[Zx(t[1],t[0]),Hl(t[2])]}function rh(t){var n=t[0],e=t[1],r=Gx(e);return[r*Gx(n),r*nb(n),nb(e)]}function ih(t,n){return t[0]*n[0]+t[1]*n[1]+t[2]*n[2]}function oh(t,n){return[t[1]*n[2]-t[2]*n[1],t[2]*n[0]-t[0]*n[2],t[0]*n[1]-t[1]*n[0]]}function uh(t,n){t[0]+=n[0],t[1]+=n[1],t[2]+=n[2]}function ah(t,n){return[t[0]*n,t[1]*n,t[2]*n]}function ch(t){var n=rb(t[0]*t[0]+t[1]*t[1]+t[2]*t[2]);t[0]/=n,t[1]/=n,t[2]/=n}function sh(t,n){xx.push(bx=[lx=t,px=t]),ndx&&(dx=n)}function fh(t,n){ +var e=rh([t*Vx,n*Vx]);if(gx){var r=oh(gx,e),i=[r[1],-r[0],0],o=oh(i,r);ch(o),o=eh(o);var u,a=t-vx,c=a>0?1:-1,s=o[0]*Xx*c,f=Wx(a)>180;f^(c*vxdx&&(dx=u)):(s=(s+360)%360-180,f^(c*vxdx&&(dx=n))),f?t_h(lx,px)&&(px=t):_h(t,px)>_h(lx,px)&&(lx=t):px>=lx?(tpx&&(px=t)):t>vx?_h(lx,t)>_h(lx,px)&&(px=t):_h(t,px)>_h(lx,px)&&(lx=t)}else sh(t,n);gx=e,vx=t}function lh(){cb.point=fh}function hh(){bx[0]=lx,bx[1]=px,cb.point=sh,gx=null}function ph(t,n){if(gx){var e=t-vx;mx.add(Wx(e)>180?e+(e>0?360:-360):e)}else _x=t,yx=n;ab.point(t,n),fh(t,n)}function dh(){ab.lineStart()}function vh(){ph(_x,yx),ab.lineEnd(),Wx(mx)>Fx&&(lx=-(px=180)),bx[0]=lx,bx[1]=px,gx=null}function _h(t,n){return(n-=t)<0?n+360:n}function yh(t,n){return t[0]-n[0]}function gh(t,n){return t[0]<=t[1]?t[0]<=n&&n<=t[1]:n_h(r[0],r[1])&&(r[1]=i[1]),_h(i[0],r[1])>_h(r[0],r[1])&&(r[0]=i[0])):o.push(r=i);for(u=-(1/0),e=o.length-1,n=0,r=o[e];n<=e;r=i,++n)i=o[n],(a=_h(r[1],i[0]))>u&&(u=a,lx=i[0],px=r[1])}return xx=bx=null,lx===1/0||hx===1/0?[[NaN,NaN],[NaN,NaN]]:[[lx,hx],[px,dx]]}function xh(t,n){t*=Vx,n*=Vx;var e=Gx(n);bh(e*Gx(t),e*nb(t),nb(n))}function bh(t,n,e){++wx,Tx+=(t-Tx)/wx,kx+=(n-kx)/wx,Nx+=(e-Nx)/wx}function wh(){sb.point=Mh}function Mh(t,n){t*=Vx,n*=Vx;var e=Gx(n);Ux=e*Gx(t),Rx=e*nb(t),Dx=nb(n),sb.point=Th,bh(Ux,Rx,Dx)}function Th(t,n){t*=Vx,n*=Vx;var e=Gx(n),r=e*Gx(t),i=e*nb(t),o=nb(n),u=Zx(rb((u=Rx*o-Dx*i)*u+(u=Dx*r-Ux*o)*u+(u=Ux*i-Rx*r)*u),Ux*r+Rx*i+Dx*o);Mx+=u,Sx+=u*(Ux+(Ux=r)),Ex+=u*(Rx+(Rx=i)),Ax+=u*(Dx+(Dx=o)),bh(Ux,Rx,Dx)}function kh(){sb.point=xh}function Nh(){sb.point=Eh}function Sh(){Ah(Lx,qx),sb.point=xh}function Eh(t,n){Lx=t,qx=n,t*=Vx,n*=Vx,sb.point=Ah;var e=Gx(n);Ux=e*Gx(t),Rx=e*nb(t),Dx=nb(n),bh(Ux,Rx,Dx)}function Ah(t,n){t*=Vx,n*=Vx;var e=Gx(n),r=e*Gx(t),i=e*nb(t),o=nb(n),u=Rx*o-Dx*i,a=Dx*r-Ux*o,c=Ux*i-Rx*r,s=rb(u*u+a*a+c*c),f=Ux*r+Rx*i+Dx*o,l=s&&-jl(f)/s,h=Zx(s,f);Cx+=l*u,zx+=l*a,Px+=l*c,Mx+=h,Sx+=h*(Ux+(Ux=r)),Ex+=h*(Rx+(Rx=i)),Ax+=h*(Dx+(Dx=o)),bh(Ux,Rx,Dx)}function Ch(t){wx=Mx=Tx=kx=Nx=Sx=Ex=Ax=Cx=zx=Px=0,Gl(t,sb);var n=Cx,e=zx,r=Px,i=n*n+e*e+r*r;return iYx?t-Hx:t<-Yx?t+Hx:t,n]}function qh(t,n,e){return(t%=Hx)?n||e?Ph(Rh(t),Dh(n,e)):Rh(t):n||e?Dh(n,e):Lh}function Uh(t){return function(n,e){return n+=t,[n>Yx?n-Hx:n<-Yx?n+Hx:n,e]}}function Rh(t){var n=Uh(t);return n.invert=Uh(-t),n}function Dh(t,n){function e(t,n){var e=Gx(n),a=Gx(t)*e,c=nb(t)*e,s=nb(n),f=s*r+a*i;return[Zx(c*o-f*u,a*r-s*i),Hl(f*o+c*u)]}var r=Gx(t),i=nb(t),o=Gx(n),u=nb(n);return e.invert=function(t,n){var e=Gx(n),a=Gx(t)*e,c=nb(t)*e,s=nb(n),f=s*o-c*u;return[Zx(c*o+s*u,a*r+f*i),Hl(f*r-a*i)]},e}function Oh(t){function n(n){return n=t(n[0]*Vx,n[1]*Vx),n[0]*=Xx,n[1]*=Xx,n}return t=qh(t[0]*Vx,t[1]*Vx,t.length>2?t[2]*Vx:0),n.invert=function(n){return n=t.invert(n[0]*Vx,n[1]*Vx),n[0]*=Xx,n[1]*=Xx,n},n}function Fh(t,n,e,r,i,o){if(e){var u=Gx(n),a=nb(n),c=r*e;null==i?(i=n+r*Hx,o=n-c/2):(i=Ih(u,i),o=Ih(u,o),(r>0?io)&&(i+=r*Hx));for(var s,f=i;r>0?f>o:f1&&n.push(n.pop().concat(n.shift()))},result:function(){var e=n;return n=[],t=null,e}}}function jh(t,n,e,r,i,o){var u,a=t[0],c=t[1],s=n[0],f=n[1],l=0,h=1,p=s-a,d=f-c;if(u=e-a,p||!(u>0)){if(u/=p,p<0){if(u0){if(u>h)return;u>l&&(l=u)}if(u=i-a,p||!(u<0)){if(u/=p,p<0){if(u>h)return;u>l&&(l=u)}else if(p>0){if(u0)){if(u/=d,d<0){if(u0){if(u>h)return;u>l&&(l=u)}if(u=o-c,d||!(u<0)){if(u/=d,d<0){if(u>h)return;u>l&&(l=u)}else if(d>0){if(u0&&(t[0]=a+l*p,t[1]=c+l*d),h<1&&(n[0]=a+h*p,n[1]=c+h*d),!0}}}}}function Hh(t,n){return Wx(t[0]-n[0])=0;--o)i.point((f=s[o])[0],f[1]);else r(h.x,h.p.x,-1,i);h=h.p}h=h.o,s=h.z,p=!p}while(!h.v);i.lineEnd()}}}function Wh(t){if(n=t.length){for(var n,e,r=0,i=t[0];++r0){do s.point(0===f||3===f?t:e,f>1?r:n);while((f=(f+a+4)%4)!==l)}else s.point(o[0],o[1])}function u(r,i){return Wx(r[0]-t)0?0:3:Wx(r[0]-e)0?2:1:Wx(r[1]-n)0?1:0:i>0?3:2}function a(t,n){return c(t.x,n.x)}function c(t,n){var e=u(t,1),r=u(n,1);return e!==r?e-r:0===e?n[1]-t[1]:1===e?t[0]-n[0]:2===e?t[1]-n[1]:n[0]-t[0]}return function(u){function c(t,n){i(t,n)&&S.point(t,n)}function s(){for(var n=0,e=0,i=_.length;er&&(l-o)*(r-u)>(h-u)*(t-o)&&++n:h<=r&&(l-o)*(r-u)<(h-u)*(t-o)&&--n;return n}function f(){S=E,v=[],_=[],N=!0}function l(){var t=s(),n=N&&t,e=(v=w(v)).length;(n||e)&&(u.polygonStart(),n&&(u.lineStart(),o(null,null,1,u),u.lineEnd()),e&&Vh(v,a,t,o,u),u.polygonEnd()),S=u,v=_=y=null}function h(){A.point=d,_&&_.push(y=[]),k=!0,T=!1,b=M=NaN}function p(){v&&(d(g,m),x&&T&&E.rejoin(),v.push(E.result())),A.point=c,T&&S.lineEnd()}function d(o,u){var a=i(o,u);if(_&&y.push([o,u]),k)g=o,m=u,x=a,k=!1,a&&(S.lineStart(),S.point(o,u));else if(a&&T)S.point(o,u);else{var c=[b=Math.max(Mb,Math.min(wb,b)),M=Math.max(Mb,Math.min(wb,M))],s=[o=Math.max(Mb,Math.min(wb,o)),u=Math.max(Mb,Math.min(wb,u))];jh(c,s,t,n,e,r)?(T||(S.lineStart(),S.point(c[0],c[1])),S.point(s[0],s[1]),a||S.lineEnd(),N=!1):a&&(S.lineStart(),S.point(o,u),N=!1)}b=o,M=u,T=a}var v,_,y,g,m,x,b,M,T,k,N,S=u,E=Bh(),A={point:c,lineStart:h,lineEnd:p,polygonStart:f,polygonEnd:l};return A}}function Zh(){var t,n,e,r=0,i=0,o=960,u=500;return e={stream:function(e){return t&&n===e?t:t=$h(r,i,o,u)(n=e)},extent:function(a){return arguments.length?(r=+a[0][0],i=+a[0][1],o=+a[1][0],u=+a[1][1],t=n=null,e):[[r,i],[o,u]]}}}function Gh(){Tb.point=Qh,Tb.lineEnd=Jh}function Jh(){Tb.point=Tb.lineEnd=Vl}function Qh(t,n){t*=Vx,n*=Vx,lb=t,hb=nb(n),pb=Gx(n),Tb.point=Kh}function Kh(t,n){t*=Vx,n*=Vx;var e=nb(n),r=Gx(n),i=Wx(t-lb),o=Gx(i),u=nb(i),a=r*u,c=pb*e-hb*r*o,s=hb*e+pb*r*o;fb.add(Zx(rb(a*a+c*c),s)),lb=t,hb=e,pb=r}function tp(t){return fb?fb.reset():fb=Il(),Gl(t,Tb),+fb}function np(t,n){return kb[0]=t,kb[1]=n,tp(Nb)}function ep(t,n,e){var r=l(t,n-Fx,e).concat(n);return function(t){return r.map(function(n){return[t,n]})}}function rp(t,n,e){var r=l(t,n-Fx,e).concat(n);return function(t){return r.map(function(n){return[n,t]})}}function ip(){function t(){return{type:"MultiLineString",coordinates:n()}}function n(){return l(Jx(o/y)*y,i,y).map(p).concat(l(Jx(s/g)*g,c,g).map(d)).concat(l(Jx(r/v)*v,e,v).filter(function(t){return Wx(t%y)>Fx}).map(f)).concat(l(Jx(a/_)*_,u,_).filter(function(t){return Wx(t%g)>Fx}).map(h))}var e,r,i,o,u,a,c,s,f,h,p,d,v=10,_=v,y=90,g=360,m=2.5;return t.lines=function(){return n().map(function(t){return{type:"LineString",coordinates:t}})},t.outline=function(){return{type:"Polygon",coordinates:[p(o).concat(d(c).slice(1),p(i).reverse().slice(1),d(s).reverse().slice(1))]}},t.extent=function(n){return arguments.length?t.extentMajor(n).extentMinor(n):t.extentMinor()},t.extentMajor=function(n){return arguments.length?(o=+n[0][0],i=+n[1][0],s=+n[0][1],c=+n[1][1],o>i&&(n=o,o=i,i=n),s>c&&(n=s,s=c,c=n),t.precision(m)):[[o,s],[i,c]]},t.extentMinor=function(n){return arguments.length?(r=+n[0][0],e=+n[1][0],a=+n[0][1],u=+n[1][1],r>e&&(n=r,r=e,e=n),a>u&&(n=a,a=u,u=n),t.precision(m)):[[r,a],[e,u]]},t.step=function(n){return arguments.length?t.stepMajor(n).stepMinor(n):t.stepMinor()},t.stepMajor=function(n){return arguments.length?(y=+n[0],g=+n[1],t):[y,g]},t.stepMinor=function(n){return arguments.length?(v=+n[0],_=+n[1],t):[v,_]},t.precision=function(n){return arguments.length?(m=+n,f=ep(a,u,90),h=rp(r,e,m),p=ep(s,c,90),d=rp(o,i,m),t):m},t.extentMajor([[-180,-90+Fx],[180,90-Fx]]).extentMinor([[-180,-80-Fx],[180,80+Fx]])}function op(t,n){var e=t[0]*Vx,r=t[1]*Vx,i=n[0]*Vx,o=n[1]*Vx,u=Gx(r),a=nb(r),c=Gx(o),s=nb(o),f=u*Gx(e),l=u*nb(e),h=c*Gx(i),p=c*nb(i),d=2*Hl(rb(Xl(o-r)+u*c*Xl(i-e))),v=nb(d),_=d?function(t){var n=nb(t*=d)/v,e=nb(d-t)/v,r=e*f+n*h,i=e*l+n*p,o=e*a+n*s;return[Zx(i,r)*Xx,Zx(o,rb(r*r+i*i))*Xx]}:function(){return[e*Xx,r*Xx]};return _.distance=d,_}function up(t){return t}function ap(){Ab.point=cp}function cp(t,n){Ab.point=sp,db=_b=t,vb=yb=n}function sp(t,n){Eb.add(yb*t-_b*n),_b=t,yb=n}function fp(){sp(db,vb)}function lp(t,n){tPb&&(Pb=t),nLb&&(Lb=n)}function hp(t,n){Ub+=t,Rb+=n,++Db}function pp(){Hb.point=dp}function dp(t,n){Hb.point=vp,hp(xb=t,bb=n)}function vp(t,n){var e=t-xb,r=n-bb,i=rb(e*e+r*r);Ob+=i*(xb+t)/2,Fb+=i*(bb+n)/2,Ib+=i,hp(xb=t,bb=n)}function _p(){Hb.point=hp}function yp(){Hb.point=mp}function gp(){xp(gb,mb)}function mp(t,n){Hb.point=xp,hp(gb=xb=t,mb=bb=n)}function xp(t,n){var e=t-xb,r=n-bb,i=rb(e*e+r*r);Ob+=i*(xb+t)/2,Fb+=i*(bb+n)/2,Ib+=i,i=bb*t-xb*n,Yb+=i*(xb+t),Bb+=i*(bb+n),jb+=3*i,hp(xb=t,bb=n)}function bp(t){function n(n,e){t.moveTo(n+u,e),t.arc(n,e,u,0,Hx)}function e(n,e){t.moveTo(n,e),a.point=r}function r(n,e){t.lineTo(n,e)}function i(){a.point=n}function o(){t.closePath()}var u=4.5,a={point:n,lineStart:function(){a.point=e},lineEnd:i,polygonStart:function(){a.lineEnd=o},polygonEnd:function(){a.lineEnd=i,a.point=n},pointRadius:function(t){return u=t,a},result:Vl};return a}function wp(){function t(t,n){a.push("M",t,",",n,u)}function n(t,n){a.push("M",t,",",n),c.point=e}function e(t,n){a.push("L",t,",",n)}function r(){c.point=n}function i(){c.point=t}function o(){a.push("Z")}var u=Mp(4.5),a=[],c={point:t,lineStart:r,lineEnd:i,polygonStart:function(){c.lineEnd=o},polygonEnd:function(){c.lineEnd=i,c.point=t},pointRadius:function(t){return u=Mp(t),c},result:function(){if(a.length){var t=a.join("");return a=[],t}}};return c}function Mp(t){return"m0,"+t+"a"+t+","+t+" 0 1,1 0,"+-2*t+"a"+t+","+t+" 0 1,1 0,"+2*t+"z"}function Tp(){function t(t){return t&&("function"==typeof o&&i.pointRadius(+o.apply(this,arguments)),Gl(t,e(i))),i.result()}var n,e,r,i,o=4.5;return t.area=function(t){return Gl(t,e(Ab)),Ab.result()},t.bounds=function(t){return Gl(t,e(qb)),qb.result()},t.centroid=function(t){return Gl(t,e(Hb)),Hb.result()},t.projection=function(r){return arguments.length?(e=null==(n=r)?up:r.stream,t):n},t.context=function(n){return arguments.length?(i=null==(r=n)?new wp:new bp(n),"function"!=typeof o&&i.pointRadius(o),t):r},t.pointRadius=function(n){return arguments.length?(o="function"==typeof n?n:(i.pointRadius(+n),+n),t):o},t.projection(null).context(null)}function kp(t,n){for(var e=n[0],r=n[1],i=[nb(e),-Gx(e),0],o=0,u=0,a=0,c=t.length;a=0?1:-1,T=M*w,k=T>Yx,N=d*x;if(Xb.add(Zx(N*M*nb(T),v*b+N*Gx(T))),o+=k?w+M*Hx:w,k^h>=e^g>=e){var S=oh(rh(l),rh(y));ch(S);var E=oh(i,S);ch(E);var A=(k^w>=0?-1:1)*Hl(E[2]);(r>A||r===A&&(S[0]||S[1]))&&(u+=k^w>=0?1:-1)}}var C=(o<-Fx||o0){for(x||(o.polygonStart(),x=!0),o.lineStart(),t=0;t1&&2&i&&u.push(u.pop().concat(u.shift())),d.push(u.filter(Sp))}var p,d,v,_=n(o),y=i.invert(r[0],r[1]),g=Bh(),m=n(g),x=!1,b={point:u,lineStart:c,lineEnd:s,polygonStart:function(){b.point=f,b.lineStart=l,b.lineEnd=h,d=[],p=[]},polygonEnd:function(){b.point=u,b.lineStart=c,b.lineEnd=s,d=w(d);var t=kp(p,y);d.length?(x||(o.polygonStart(),x=!0),Vh(d,Ep,t,e,o)):t&&(x||(o.polygonStart(),x=!0),o.lineStart(),e(null,null,1,o),o.lineEnd()),x&&(o.polygonEnd(),x=!1),d=p=null},sphere:function(){o.polygonStart(),o.lineStart(),e(null,null,1,o),o.lineEnd(),o.polygonEnd()}};return b}}function Sp(t){return t.length>1}function Ep(t,n){return((t=t.x)[0]<0?t[1]-Bx-Fx:Bx-t[1])-((n=n.x)[0]<0?n[1]-Bx-Fx:Bx-n[1])}function Ap(t){var n,e=NaN,r=NaN,i=NaN;return{lineStart:function(){t.lineStart(),n=1},point:function(o,u){var a=o>0?Yx:-Yx,c=Wx(o-e);Wx(c-Yx)0?Bx:-Bx),t.point(i,r),t.lineEnd(),t.lineStart(),t.point(a,r),t.point(o,r),n=0):i!==a&&c>=Yx&&(Wx(e-i)Fx?$x((nb(n)*(o=Gx(r))*nb(e)-nb(r)*(i=Gx(n))*nb(t))/(i*o*u)):(n+r)/2}function zp(t,n,e,r){var i;if(null==t)i=e*Bx,r.point(-Yx,i),r.point(0,i),r.point(Yx,i),r.point(Yx,0),r.point(Yx,-i),r.point(0,-i),r.point(-Yx,-i),r.point(-Yx,0),r.point(-Yx,i);else if(Wx(t[0]-n[0])>Fx){var o=t[0]a}function i(t){var n,e,i,a,f;return{lineStart:function(){a=i=!1,f=1},point:function(l,h){var p,d=[l,h],v=r(l,h),_=c?v?0:u(l,h):v?u(l+(l<0?Yx:-Yx),h):0;if(!n&&(a=i=v)&&t.lineStart(),v!==i&&(p=o(n,d),(Hh(n,p)||Hh(d,p))&&(d[0]+=Fx,d[1]+=Fx,v=r(d[0],d[1]))),v!==i)f=0,v?(t.lineStart(),p=o(d,n),t.point(p[0],p[1])):(p=o(n,d),t.point(p[0],p[1]),t.lineEnd()),n=p;else if(s&&n&&c^v){var y;_&e||!(y=o(d,n,!0))||(f=0,c?(t.lineStart(),t.point(y[0][0],y[0][1]),t.point(y[1][0],y[1][1]),t.lineEnd()):(t.point(y[1][0],y[1][1]),t.lineEnd(),t.lineStart(),t.point(y[0][0],y[0][1])))}!v||n&&Hh(n,d)||t.point(d[0],d[1]),n=d,i=v,e=_},lineEnd:function(){i&&t.lineEnd(),n=null},clean:function(){return f|(a&&i)<<1}}}function o(t,n,e){var r=rh(t),i=rh(n),o=[1,0,0],u=oh(r,i),c=ih(u,u),s=u[0],f=c-s*s;if(!f)return!e&&t;var l=a*c/f,h=-a*s/f,p=oh(o,u),d=ah(o,l),v=ah(u,h);uh(d,v);var _=p,y=ih(d,_),g=ih(_,_),m=y*y-g*(ih(d,d)-1);if(!(m<0)){var x=rb(m),b=ah(_,(-y-x)/g);if(uh(b,d),b=eh(b),!e)return b;var w,M=t[0],T=n[0],k=t[1],N=n[1];T0^b[1]<(Wx(b[0]-M)Yx^(M<=b[0]&&b[0]<=T)){var C=ah(_,(-y+x)/g);return uh(C,d),[b,eh(C)]}}}function u(n,e){var r=c?t:Yx-t,i=0;return n<-r?i|=1:n>r&&(i|=2),e<-r?i|=4:e>r&&(i|=8),i}var a=Gx(t),c=a>0,s=Wx(a)>Fx;return Np(r,i,e,c?[0,-t]:[-Yx,t-Yx])}function Lp(t){return{stream:qp(t)}}function qp(t){function n(){}var e=n.prototype=Object.create(Up.prototype);for(var r in t)e[r]=t[r];return function(t){var e=new n;return e.stream=t,e}}function Up(){}function Rp(t,n){return+n?Op(t,n):Dp(t)}function Dp(t){return qp({point:function(n,e){n=t(n,e),this.stream.point(n[0],n[1])}})}function Op(t,n){function e(r,i,o,u,a,c,s,f,l,h,p,d,v,_){var y=s-r,g=f-i,m=y*y+g*g;if(m>4*n&&v--){var x=u+h,b=a+p,w=c+d,M=rb(x*x+b*b+w*w),T=Hl(w/=M),k=Wx(Wx(w)-1)n||Wx((y*A+g*C)/m-.5)>.3||u*h+a*p+c*d<$b)&&(e(r,i,o,u,a,c,S,E,k,x/=M,b/=M,w,v,_),_.point(S,E),e(S,E,k,x,b,w,s,f,l,h,p,d,v,_))}}return function(n){function r(e,r){e=t(e,r),n.point(e[0],e[1])}function i(){y=NaN,w.point=o,n.lineStart()}function o(r,i){var o=rh([r,i]),u=t(r,i);e(y,g,_,m,x,b,y=u[0],g=u[1],_=r,m=o[0],x=o[1],b=o[2],Wb,n),n.point(y,g)}function u(){w.point=r,n.lineEnd()}function a(){i(),w.point=c,w.lineEnd=s}function c(t,n){o(f=t,n),l=y,h=g,p=m,d=x,v=b,w.point=o}function s(){e(y,g,_,m,x,b,l,h,f,p,d,v,Wb,n),w.lineEnd=u,u()}var f,l,h,p,d,v,_,y,g,m,x,b,w={point:r,lineStart:i,lineEnd:u,polygonStart:function(){n.polygonStart(),w.lineStart=a},polygonEnd:function(){n.polygonEnd(),w.lineStart=i}};return w}}function Fp(t){return Ip(function(){return t})()}function Ip(t){function n(t){return t=f(t[0]*Vx,t[1]*Vx),[t[0]*_+a,c-t[1]*_]}function e(t){return t=f.invert((t[0]-a)/_,(c-t[1])/_),t&&[t[0]*Xx,t[1]*Xx]}function r(t,n){return t=u(t,n),[t[0]*_+a,c-t[1]*_]}function i(){f=Ph(s=qh(b,w,M),u);var t=u(m,x);return a=y-t[0]*_,c=g+t[1]*_,o()}function o(){return d=v=null,n}var u,a,c,s,f,l,h,p,d,v,_=150,y=480,g=250,m=0,x=0,b=0,w=0,M=0,T=null,k=Vb,N=null,S=up,E=.5,A=Rp(r,E);return n.stream=function(t){return d&&v===t?d:d=Zb(k(s,A(S(v=t))))},n.clipAngle=function(t){return arguments.length?(k=+t?Pp(T=t*Vx,6*Vx):(T=null,Vb),o()):T*Xx},n.clipExtent=function(t){return arguments.length?(S=null==t?(N=l=h=p=null,up):$h(N=+t[0][0],l=+t[0][1],h=+t[1][0],p=+t[1][1]),o()):null==N?null:[[N,l],[h,p]]},n.scale=function(t){return arguments.length?(_=+t,i()):_},n.translate=function(t){return arguments.length?(y=+t[0],g=+t[1],i()):[y,g]},n.center=function(t){return arguments.length?(m=t[0]%360*Vx,x=t[1]%360*Vx,i()):[m*Xx,x*Xx]},n.rotate=function(t){return arguments.length?(b=t[0]%360*Vx,w=t[1]%360*Vx,M=t.length>2?t[2]%360*Vx:0,i()):[b*Xx,w*Xx,M*Xx]},n.precision=function(t){return arguments.length?(A=Rp(r,E=t*t),o()):rb(E)},function(){return u=t.apply(this,arguments),n.invert=u.invert&&e,i()}}function Yp(t){var n=0,e=Yx/3,r=Ip(t),i=r(n,e);return i.parallels=function(t){return arguments.length?r(n=t[0]*Vx,e=t[1]*Vx):[n*Xx,e*Xx]},i}function Bp(t,n){function e(t,n){var e=rb(o-2*i*nb(n))/i;return[e*nb(t*=i),u-e*Gx(t)]}var r=nb(t),i=(r+nb(n))/2,o=1+r*(2*i-r),u=rb(o)/i;return e.invert=function(t,n){var e=u-n;return[Zx(t,e)/i,Hl((o-(t*t+e*e)*i*i)/(2*i))]},e}function jp(){return Yp(Bp).scale(151).translate([480,347])}function Hp(){return jp().parallels([29.5,45.5]).scale(1070).translate([480,250]).rotate([96,0]).center([-.6,38.7])}function Xp(t){var n=t.length;return{point:function(e,r){for(var i=-1;++i=.12&&i<.234&&r>=-.425&&r<-.214?c:i>=.166&&i<.234&&r>=-.214&&r<-.115?s:a).invert(t)},t.stream=function(t){return n&&e===t?n:n=Xp([a.stream(e=t),c.stream(t),s.stream(t)])},t.precision=function(n){return arguments.length?(a.precision(n),c.precision(n),s.precision(n),t):a.precision()},t.scale=function(n){return arguments.length?(a.scale(n),c.scale(.35*n),s.scale(n),t.translate(a.translate())):a.scale()},t.translate=function(n){if(!arguments.length)return a.translate();var e=a.scale(),u=+n[0],l=+n[1];return r=a.translate(n).clipExtent([[u-.455*e,l-.238*e],[u+.455*e,l+.238*e]]).stream(f),i=c.translate([u-.307*e,l+.201*e]).clipExtent([[u-.425*e+Fx,l+.12*e+Fx],[u-.214*e-Fx,l+.234*e-Fx]]).stream(f),o=s.translate([u-.205*e,l+.212*e]).clipExtent([[u-.214*e+Fx,l+.166*e+Fx],[u-.115*e-Fx,l+.234*e-Fx]]).stream(f),t},t.scale(1070)}function Wp(t){return function(n,e){var r=Gx(n),i=Gx(e),o=t(r*i);return[o*i*nb(n),o*nb(e)]}}function $p(t){return function(n,e){var r=rb(n*n+e*e),i=t(r),o=nb(i),u=Gx(i);return[Zx(n*o,r*u),Hl(r&&e*o/r)]}}function Zp(){return Fp(Gb).scale(120).clipAngle(179.999)}function Gp(){return Fp(Jb).scale(480/Hx).clipAngle(179.999)}function Jp(t,n){return[t,Kx(ib((Bx+n)/2))]}function Qp(){return Kp(Jp)}function Kp(t){var n,e=Fp(t),r=e.scale,i=e.translate,o=e.clipExtent;return e.scale=function(t){return arguments.length?(r(t),n&&e.clipExtent(null),e):r()},e.translate=function(t){return arguments.length?(i(t),n&&e.clipExtent(null),e):i()},e.clipExtent=function(t){if(!arguments.length)return n?null:o();if(n=null==t){var u=Yx*r(),a=i();t=[[a[0]-u,a[1]-u],[a[0]+u,a[1]+u]]}return o(t),e},e.clipExtent(null).scale(961/Hx)}function td(t){return ib((Bx+t)/2)}function nd(t,n){function e(t,n){o>0?n<-Bx+Fx&&(n=-Bx+Fx):n>Bx-Fx&&(n=Bx-Fx);var e=o/tb(td(n),i);return[e*nb(i*t),o-e*Gx(i*t)]}var r=Gx(t),i=t===n?nb(t):Kx(r/Gx(n))/Kx(td(n)/td(t)),o=r*tb(td(t),i)/i;return i?(e.invert=function(t,n){var e=o-n,r=eb(i)*rb(t*t+e*e);return[Zx(t,e)/i,2*$x(tb(o/r,1/i))-Bx]},e):Jp}function ed(){return Yp(nd)}function rd(t,n){return[t,n]}function id(){return Fp(rd).scale(480/Yx)}function od(t,n){function e(t,n){var e=o-n,r=i*t;return[e*nb(r),o-e*Gx(r)]}var r=Gx(t),i=t===n?nb(t):(r-Gx(n))/(n-t),o=r/i+t;return Wx(i)2?t[2]+90:90]):(t=e(),[t[0],t[1],t[2]-90])},e([0,0,90])}var vd="4.0.0",_d=e(n),yd=_d.right,gd=_d.left,md=Array.prototype,xd=md.slice,bd=md.map,wd=Math.sqrt(50),Md=Math.sqrt(10),Td=Math.sqrt(2),kd="$";P.prototype=L.prototype={constructor:P,has:function(t){return kd+t in this},get:function(t){return this[kd+t]},set:function(t,n){return this[kd+t]=n,this},remove:function(t){var n=kd+t;return n in this&&delete this[n]},clear:function(){for(var t in this)t[0]===kd&&delete this[t]},keys:function(){var t=[];for(var n in this)n[0]===kd&&t.push(n.slice(1));return t},values:function(){var t=[];for(var n in this)n[0]===kd&&t.push(this[n]);return t},entries:function(){var t=[];for(var n in this)n[0]===kd&&t.push({key:n.slice(1),value:this[n]});return t},size:function(){var t=0;for(var n in this)n[0]===kd&&++t;return t},empty:function(){for(var t in this)if(t[0]===kd)return!1;return!0},each:function(t){for(var n in this)n[0]===kd&&t(this[n],n.slice(1),this)}};var Nd=L.prototype;F.prototype=I.prototype={constructor:F,has:Nd.has,add:function(t){return t+="",this[kd+t]=t,this},remove:Nd.remove,clear:Nd.clear,values:Nd.keys,size:Nd.size,empty:Nd.empty,each:Nd.each};var Sd=3,Ed=function Qb(t){function n(n){return Math.pow(n,t)}return t=+t,n.exponent=Qb,n}(Sd),Ad=function Kb(t){function n(n){return 1-Math.pow(1-n,t)}return t=+t,n.exponent=Kb,n}(Sd),Cd=function tw(t){function n(n){return((n*=2)<=1?Math.pow(n,t):2-Math.pow(2-n,t))/2}return t=+t,n.exponent=tw,n}(Sd),zd=Math.PI,Pd=zd/2,Ld=4/11,qd=6/11,Ud=8/11,Rd=.75,Dd=9/11,Od=10/11,Fd=.9375,Id=21/22,Yd=63/64,Bd=1/Ld/Ld,jd=1.70158,Hd=function nw(t){function n(n){return n*n*((t+1)*n-t)}return t=+t,n.overshoot=nw,n}(jd),Xd=function ew(t){function n(n){return--n*n*((t+1)*n+t)+1}return t=+t,n.overshoot=ew,n}(jd),Vd=function rw(t){function n(n){return((n*=2)<1?n*n*((t+1)*n-t):(n-=2)*n*((t+1)*n+t)+2)/2}return t=+t,n.overshoot=rw,n}(jd),Wd=2*Math.PI,$d=1,Zd=.3,Gd=function iw(t,n){function e(e){return t*Math.pow(2,10*--e)*Math.sin((r-e)/n)}var r=Math.asin(1/(t=Math.max(1,t)))*(n/=Wd);return e.amplitude=function(t){return iw(t,n*Wd)},e.period=function(n){return iw(t,n)},e}($d,Zd),Jd=function ow(t,n){function e(e){return 1-t*Math.pow(2,-10*(e=+e))*Math.sin((e+r)/n)}var r=Math.asin(1/(t=Math.max(1,t)))*(n/=Wd);return e.amplitude=function(t){return ow(t,n*Wd)},e.period=function(n){return ow(t,n)},e}($d,Zd),Qd=function uw(t,n){function e(e){return((e=2*e-1)<0?t*Math.pow(2,10*e)*Math.sin((r-e)/n):2-t*Math.pow(2,-10*e)*Math.sin((r+e)/n))/2}var r=Math.asin(1/(t=Math.max(1,t)))*(n/=Wd);return e.amplitude=function(t){return uw(t,n*Wd)},e.period=function(n){return uw(t,n)},e}($d,Zd),Kd=Math.PI,tv=2*Kd,nv=1e-6,ev=tv-nv;Mt.prototype=Tt.prototype={constructor:Mt,moveTo:function(t,n){this._.push("M",this._x0=this._x1=+t,",",this._y0=this._y1=+n)},closePath:function(){null!==this._x1&&(this._x1=this._x0,this._y1=this._y0,this._.push("Z"))},lineTo:function(t,n){this._.push("L",this._x1=+t,",",this._y1=+n)},quadraticCurveTo:function(t,n,e,r){this._.push("Q",+t,",",+n,",",this._x1=+e,",",this._y1=+r)},bezierCurveTo:function(t,n,e,r,i,o){this._.push("C",+t,",",+n,",",+e,",",+r,",",this._x1=+i,",",this._y1=+o)},arcTo:function(t,n,e,r,i){t=+t,n=+n,e=+e,r=+r,i=+i;var o=this._x1,u=this._y1,a=e-t,c=r-n,s=o-t,f=u-n,l=s*s+f*f;if(i<0)throw new Error("negative radius: "+i);if(null===this._x1)this._.push("M",this._x1=t,",",this._y1=n);else if(l>nv)if(Math.abs(f*a-c*s)>nv&&i){var h=e-o,p=r-u,d=a*a+c*c,v=h*h+p*p,_=Math.sqrt(d),y=Math.sqrt(l),g=i*Math.tan((Kd-Math.acos((d+l-v)/(2*_*y)))/2),m=g/y,x=g/_;Math.abs(m-1)>nv&&this._.push("L",t+m*s,",",n+m*f),this._.push("A",i,",",i,",0,0,",+(f*h>s*p),",",this._x1=t+x*a,",",this._y1=n+x*c)}else this._.push("L",this._x1=t,",",this._y1=n);else;},arc:function(t,n,e,r,i,o){t=+t,n=+n,e=+e;var u=e*Math.cos(r),a=e*Math.sin(r),c=t+u,s=n+a,f=1^o,l=o?r-i:i-r;if(e<0)throw new Error("negative radius: "+e);null===this._x1?this._.push("M",c,",",s):(Math.abs(this._x1-c)>nv||Math.abs(this._y1-s)>nv)&&this._.push("L",c,",",s),e&&(l>ev?this._.push("A",e,",",e,",0,1,",f,",",t-u,",",n-a,"A",e,",",e,",0,1,",f,",",this._x1=c,",",this._y1=s):(l<0&&(l=l%tv+tv),this._.push("A",e,",",e,",0,",+(l>=Kd),",",f,",",this._x1=t+e*Math.cos(i),",",this._y1=n+e*Math.sin(i))))},rect:function(t,n,e,r){this._.push("M",this._x0=this._x1=+t,",",this._y0=this._y1=+n,"h",+e,"v",+r,"h",-e,"Z")},toString:function(){return this._.join("")}};var rv=jt.prototype=Ht.prototype;rv.copy=function(){var t,n,e=new Ht(this._x,this._y,this._x0,this._y0,this._x1,this._y1),r=this._root;if(!r)return e;if(!r.length)return e._root=Xt(r),e;for(t=[{source:r,target:e._root=new Array(4)}];r=t.pop();)for(var i=0;i<4;++i)(n=r.source[i])&&(n.length?t.push({source:n,target:r.target[i]=new Array(4)}):r.target[i]=Xt(n));return e},rv.add=kt,rv.addAll=St,rv.cover=Et,rv.data=At,rv.extent=Ct,rv.find=Pt,rv.remove=Lt,rv.removeAll=qt,rv.root=Ut,rv.size=Rt,rv.visit=Dt,rv.visitAfter=Ot,rv.x=It,rv.y=Bt;var iv=[].slice,ov={};Vt.prototype=Qt.prototype={constructor:Vt,defer:function(t){if("function"!=typeof t||this._call)throw new Error;if(null!=this._error)return this;var n=iv.call(arguments,1);return n.push(t),++this._waiting,this._tasks.push(n),Wt(this),this},abort:function(){return null==this._error&&Gt(this,new Error("abort")),this},await:function(t){if("function"!=typeof t||this._call)throw new Error;return this._call=function(n,e){t.apply(null,[n].concat(e))},Jt(this),this},awaitAll:function(t){if("function"!=typeof t||this._call)throw new Error;return this._call=t,Jt(this),this}};var uv=1e-12,av=Math.PI,cv=av/2,sv=2*av;fn.prototype={areaStart:function(){this._line=0},areaEnd:function(){this._line=NaN},lineStart:function(){this._point=0},lineEnd:function(){(this._line||0!==this._line&&1===this._point)&&this._context.closePath(),this._line=1-this._line},point:function(t,n){switch(t=+t,n=+n,this._point){case 0:this._point=1,this._line?this._context.lineTo(t,n):this._context.moveTo(t,n);break;case 1:this._point=2;default:this._context.lineTo(t,n)}}};var fv=xn(ln);mn.prototype={areaStart:function(){this._curve.areaStart()},areaEnd:function(){this._curve.areaEnd()},lineStart:function(){this._curve.lineStart()},lineEnd:function(){this._curve.lineEnd()},point:function(t,n){this._curve.point(n*Math.sin(t),n*-Math.cos(t))}};var lv={draw:function(t,n){var e=Math.sqrt(n/av);t.moveTo(e,0),t.arc(0,0,e,0,sv)}},hv={draw:function(t,n){var e=Math.sqrt(n/5)/2;t.moveTo(-3*e,-e),t.lineTo(-e,-e),t.lineTo(-e,-3*e),t.lineTo(e,-3*e),t.lineTo(e,-e),t.lineTo(3*e,-e),t.lineTo(3*e,e),t.lineTo(e,e),t.lineTo(e,3*e),t.lineTo(-e,3*e),t.lineTo(-e,e),t.lineTo(-3*e,e),t.closePath()}},pv=Math.sqrt(1/3),dv=2*pv,vv={draw:function(t,n){var e=Math.sqrt(n/dv),r=e*pv;t.moveTo(0,-e),t.lineTo(r,0),t.lineTo(0,e),t.lineTo(-r,0),t.closePath()}},_v=.8908130915292852,yv=Math.sin(av/10)/Math.sin(7*av/10),gv=Math.sin(sv/10)*yv,mv=-Math.cos(sv/10)*yv,xv={draw:function(t,n){var e=Math.sqrt(n*_v),r=gv*e,i=mv*e;t.moveTo(0,-e),t.lineTo(r,i);for(var o=1;o<5;++o){var u=sv*o/5,a=Math.cos(u),c=Math.sin(u);t.lineTo(c*e,-a*e),t.lineTo(a*r-c*i,c*r+a*i)}t.closePath()}},bv={draw:function(t,n){var e=Math.sqrt(n),r=-e/2;t.rect(r,r,e,e)}},wv=Math.sqrt(3),Mv={draw:function(t,n){var e=-Math.sqrt(n/(3*wv));t.moveTo(0,2*e),t.lineTo(-wv*e,-e),t.lineTo(wv*e,-e),t.closePath()}},Tv=-.5,kv=Math.sqrt(3)/2,Nv=1/Math.sqrt(12),Sv=3*(Nv/2+1),Ev={draw:function(t,n){var e=Math.sqrt(n/Sv),r=e/2,i=e*Nv,o=r,u=e*Nv+e,a=-o,c=u;t.moveTo(r,i),t.lineTo(o,u),t.lineTo(a,c),t.lineTo(Tv*r-kv*i,kv*r+Tv*i),t.lineTo(Tv*o-kv*u,kv*o+Tv*u),t.lineTo(Tv*a-kv*c,kv*a+Tv*c),t.lineTo(Tv*r+kv*i,Tv*i-kv*r),t.lineTo(Tv*o+kv*u,Tv*u-kv*o),t.lineTo(Tv*a+kv*c,Tv*c-kv*a),t.closePath()}},Av=[lv,hv,vv,bv,xv,Mv,Ev];Sn.prototype={areaStart:function(){this._line=0},areaEnd:function(){this._line=NaN},lineStart:function(){this._x0=this._x1=this._y0=this._y1=NaN,this._point=0},lineEnd:function(){switch(this._point){case 3:Nn(this,this._x1,this._y1);case 2:this._context.lineTo(this._x1,this._y1)}(this._line||0!==this._line&&1===this._point)&&this._context.closePath(),this._line=1-this._line},point:function(t,n){switch(t=+t,n=+n,this._point){case 0:this._point=1,this._line?this._context.lineTo(t,n):this._context.moveTo(t,n);break;case 1:this._point=2;break;case 2:this._point=3,this._context.lineTo((5*this._x0+this._x1)/6,(5*this._y0+this._y1)/6);default:Nn(this,t,n)}this._x0=this._x1,this._x1=t,this._y0=this._y1,this._y1=n}},An.prototype={areaStart:kn,areaEnd:kn, +lineStart:function(){this._x0=this._x1=this._x2=this._x3=this._x4=this._y0=this._y1=this._y2=this._y3=this._y4=NaN,this._point=0},lineEnd:function(){switch(this._point){case 1:this._context.moveTo(this._x2,this._y2),this._context.closePath();break;case 2:this._context.moveTo((this._x2+2*this._x3)/3,(this._y2+2*this._y3)/3),this._context.lineTo((this._x3+2*this._x2)/3,(this._y3+2*this._y2)/3),this._context.closePath();break;case 3:this.point(this._x2,this._y2),this.point(this._x3,this._y3),this.point(this._x4,this._y4)}},point:function(t,n){switch(t=+t,n=+n,this._point){case 0:this._point=1,this._x2=t,this._y2=n;break;case 1:this._point=2,this._x3=t,this._y3=n;break;case 2:this._point=3,this._x4=t,this._y4=n,this._context.moveTo((this._x0+4*this._x1+t)/6,(this._y0+4*this._y1+n)/6);break;default:Nn(this,t,n)}this._x0=this._x1,this._x1=t,this._y0=this._y1,this._y1=n}},zn.prototype={areaStart:function(){this._line=0},areaEnd:function(){this._line=NaN},lineStart:function(){this._x0=this._x1=this._y0=this._y1=NaN,this._point=0},lineEnd:function(){(this._line||0!==this._line&&3===this._point)&&this._context.closePath(),this._line=1-this._line},point:function(t,n){switch(t=+t,n=+n,this._point){case 0:this._point=1;break;case 1:this._point=2;break;case 2:this._point=3;var e=(this._x0+4*this._x1+t)/6,r=(this._y0+4*this._y1+n)/6;this._line?this._context.lineTo(e,r):this._context.moveTo(e,r);break;case 3:this._point=4;default:Nn(this,t,n)}this._x0=this._x1,this._x1=t,this._y0=this._y1,this._y1=n}},Ln.prototype={lineStart:function(){this._x=[],this._y=[],this._basis.lineStart()},lineEnd:function(){var t=this._x,n=this._y,e=t.length-1;if(e>0)for(var r,i=t[0],o=n[0],u=t[e]-i,a=n[e]-o,c=-1;++c<=e;)r=c/e,this._basis.point(this._beta*t[c]+(1-this._beta)*(i+r*u),this._beta*n[c]+(1-this._beta)*(o+r*a));this._x=this._y=null,this._basis.lineEnd()},point:function(t,n){this._x.push(+t),this._y.push(+n)}};var Cv=function aw(t){function n(n){return 1===t?new Sn(n):new Ln(n,t)}return n.beta=function(t){return aw(+t)},n}(.85);Un.prototype={areaStart:function(){this._line=0},areaEnd:function(){this._line=NaN},lineStart:function(){this._x0=this._x1=this._x2=this._y0=this._y1=this._y2=NaN,this._point=0},lineEnd:function(){switch(this._point){case 2:this._context.lineTo(this._x2,this._y2);break;case 3:qn(this,this._x1,this._y1)}(this._line||0!==this._line&&1===this._point)&&this._context.closePath(),this._line=1-this._line},point:function(t,n){switch(t=+t,n=+n,this._point){case 0:this._point=1,this._line?this._context.lineTo(t,n):this._context.moveTo(t,n);break;case 1:this._point=2,this._x1=t,this._y1=n;break;case 2:this._point=3;default:qn(this,t,n)}this._x0=this._x1,this._x1=this._x2,this._x2=t,this._y0=this._y1,this._y1=this._y2,this._y2=n}};var zv=function cw(t){function n(n){return new Un(n,t)}return n.tension=function(t){return cw(+t)},n}(0);Rn.prototype={areaStart:kn,areaEnd:kn,lineStart:function(){this._x0=this._x1=this._x2=this._x3=this._x4=this._x5=this._y0=this._y1=this._y2=this._y3=this._y4=this._y5=NaN,this._point=0},lineEnd:function(){switch(this._point){case 1:this._context.moveTo(this._x3,this._y3),this._context.closePath();break;case 2:this._context.lineTo(this._x3,this._y3),this._context.closePath();break;case 3:this.point(this._x3,this._y3),this.point(this._x4,this._y4),this.point(this._x5,this._y5)}},point:function(t,n){switch(t=+t,n=+n,this._point){case 0:this._point=1,this._x3=t,this._y3=n;break;case 1:this._point=2,this._context.moveTo(this._x4=t,this._y4=n);break;case 2:this._point=3,this._x5=t,this._y5=n;break;default:qn(this,t,n)}this._x0=this._x1,this._x1=this._x2,this._x2=t,this._y0=this._y1,this._y1=this._y2,this._y2=n}};var Pv=function sw(t){function n(n){return new Rn(n,t)}return n.tension=function(t){return sw(+t)},n}(0);Dn.prototype={areaStart:function(){this._line=0},areaEnd:function(){this._line=NaN},lineStart:function(){this._x0=this._x1=this._x2=this._y0=this._y1=this._y2=NaN,this._point=0},lineEnd:function(){(this._line||0!==this._line&&3===this._point)&&this._context.closePath(),this._line=1-this._line},point:function(t,n){switch(t=+t,n=+n,this._point){case 0:this._point=1;break;case 1:this._point=2;break;case 2:this._point=3,this._line?this._context.lineTo(this._x2,this._y2):this._context.moveTo(this._x2,this._y2);break;case 3:this._point=4;default:qn(this,t,n)}this._x0=this._x1,this._x1=this._x2,this._x2=t,this._y0=this._y1,this._y1=this._y2,this._y2=n}};var Lv=function fw(t){function n(n){return new Dn(n,t)}return n.tension=function(t){return fw(+t)},n}(0);Fn.prototype={areaStart:function(){this._line=0},areaEnd:function(){this._line=NaN},lineStart:function(){this._x0=this._x1=this._x2=this._y0=this._y1=this._y2=NaN,this._l01_a=this._l12_a=this._l23_a=this._l01_2a=this._l12_2a=this._l23_2a=this._point=0},lineEnd:function(){switch(this._point){case 2:this._context.lineTo(this._x2,this._y2);break;case 3:this.point(this,this._x2,this._y2)}(this._line||0!==this._line&&1===this._point)&&this._context.closePath(),this._line=1-this._line},point:function(t,n){if(t=+t,n=+n,this._point){var e=this._x2-t,r=this._y2-n;this._l23_a=Math.sqrt(this._l23_2a=Math.pow(e*e+r*r,this._alpha))}switch(this._point){case 0:this._point=1,this._line?this._context.lineTo(t,n):this._context.moveTo(t,n);break;case 1:this._point=2;break;case 2:this._point=3;default:On(this,t,n)}this._l01_a=this._l12_a,this._l12_a=this._l23_a,this._l01_2a=this._l12_2a,this._l12_2a=this._l23_2a,this._x0=this._x1,this._x1=this._x2,this._x2=t,this._y0=this._y1,this._y1=this._y2,this._y2=n}};var qv=function lw(t){function n(n){return t?new Fn(n,t):new Un(n,0)}return n.alpha=function(t){return lw(+t)},n}(.5);In.prototype={areaStart:kn,areaEnd:kn,lineStart:function(){this._x0=this._x1=this._x2=this._x3=this._x4=this._x5=this._y0=this._y1=this._y2=this._y3=this._y4=this._y5=NaN,this._l01_a=this._l12_a=this._l23_a=this._l01_2a=this._l12_2a=this._l23_2a=this._point=0},lineEnd:function(){switch(this._point){case 1:this._context.moveTo(this._x3,this._y3),this._context.closePath();break;case 2:this._context.lineTo(this._x3,this._y3),this._context.closePath();break;case 3:this.point(this._x3,this._y3),this.point(this._x4,this._y4),this.point(this._x5,this._y5)}},point:function(t,n){if(t=+t,n=+n,this._point){var e=this._x2-t,r=this._y2-n;this._l23_a=Math.sqrt(this._l23_2a=Math.pow(e*e+r*r,this._alpha))}switch(this._point){case 0:this._point=1,this._x3=t,this._y3=n;break;case 1:this._point=2,this._context.moveTo(this._x4=t,this._y4=n);break;case 2:this._point=3,this._x5=t,this._y5=n;break;default:On(this,t,n)}this._l01_a=this._l12_a,this._l12_a=this._l23_a,this._l01_2a=this._l12_2a,this._l12_2a=this._l23_2a,this._x0=this._x1,this._x1=this._x2,this._x2=t,this._y0=this._y1,this._y1=this._y2,this._y2=n}};var Uv=function hw(t){function n(n){return t?new In(n,t):new Rn(n,0)}return n.alpha=function(t){return hw(+t)},n}(.5);Yn.prototype={areaStart:function(){this._line=0},areaEnd:function(){this._line=NaN},lineStart:function(){this._x0=this._x1=this._x2=this._y0=this._y1=this._y2=NaN,this._l01_a=this._l12_a=this._l23_a=this._l01_2a=this._l12_2a=this._l23_2a=this._point=0},lineEnd:function(){(this._line||0!==this._line&&3===this._point)&&this._context.closePath(),this._line=1-this._line},point:function(t,n){if(t=+t,n=+n,this._point){var e=this._x2-t,r=this._y2-n;this._l23_a=Math.sqrt(this._l23_2a=Math.pow(e*e+r*r,this._alpha))}switch(this._point){case 0:this._point=1;break;case 1:this._point=2;break;case 2:this._point=3,this._line?this._context.lineTo(this._x2,this._y2):this._context.moveTo(this._x2,this._y2);break;case 3:this._point=4;default:On(this,t,n)}this._l01_a=this._l12_a,this._l12_a=this._l23_a,this._l01_2a=this._l12_2a,this._l12_2a=this._l23_2a,this._x0=this._x1,this._x1=this._x2,this._x2=t,this._y0=this._y1,this._y1=this._y2,this._y2=n}};var Rv=function pw(t){function n(n){return t?new Yn(n,t):new Dn(n,0)}return n.alpha=function(t){return pw(+t)},n}(.5);Bn.prototype={areaStart:kn,areaEnd:kn,lineStart:function(){this._point=0},lineEnd:function(){this._point&&this._context.closePath()},point:function(t,n){t=+t,n=+n,this._point?this._context.lineTo(t,n):(this._point=1,this._context.moveTo(t,n))}},$n.prototype={areaStart:function(){this._line=0},areaEnd:function(){this._line=NaN},lineStart:function(){this._x0=this._x1=this._y0=this._y1=this._t0=NaN,this._point=0},lineEnd:function(){switch(this._point){case 2:this._context.lineTo(this._x1,this._y1);break;case 3:Wn(this,this._t0,Vn(this,this._t0))}(this._line||0!==this._line&&1===this._point)&&this._context.closePath(),this._line=1-this._line},point:function(t,n){var e=NaN;if(t=+t,n=+n,t!==this._x1||n!==this._y1){switch(this._point){case 0:this._point=1,this._line?this._context.lineTo(t,n):this._context.moveTo(t,n);break;case 1:this._point=2;break;case 2:this._point=3,Wn(this,Vn(this,e=Xn(this,t,n)),e);break;default:Wn(this,this._t0,e=Xn(this,t,n))}this._x0=this._x1,this._x1=t,this._y0=this._y1,this._y1=n,this._t0=e}}},(Zn.prototype=Object.create($n.prototype)).point=function(t,n){$n.prototype.point.call(this,n,t)},Gn.prototype={moveTo:function(t,n){this._context.moveTo(n,t)},closePath:function(){this._context.closePath()},lineTo:function(t,n){this._context.lineTo(n,t)},bezierCurveTo:function(t,n,e,r,i,o){this._context.bezierCurveTo(n,t,r,e,o,i)}},Kn.prototype={areaStart:function(){this._line=0},areaEnd:function(){this._line=NaN},lineStart:function(){this._x=[],this._y=[]},lineEnd:function(){var t=this._x,n=this._y,e=t.length;if(e)if(this._line?this._context.lineTo(t[0],n[0]):this._context.moveTo(t[0],n[0]),2===e)this._context.lineTo(t[1],n[1]);else for(var r=te(t),i=te(n),o=0,u=1;u=0&&(this._t=1-this._t,this._line=1-this._line)},point:function(t,n){switch(t=+t,n=+n,this._point){case 0:this._point=1,this._line?this._context.lineTo(t,n):this._context.moveTo(t,n);break;case 1:this._point=2;default:if(this._t<=0)this._context.lineTo(this._x,n),this._context.lineTo(t,n);else{var e=this._x*(1-this._t)+t*this._t;this._context.lineTo(e,this._y),this._context.lineTo(e,n)}}this._x=t,this._y=n}};var Dv=Array.prototype.slice,Ov=.7,Fv=1/Ov,Iv=/^#([0-9a-f]{3})$/,Yv=/^#([0-9a-f]{6})$/,Bv=/^rgb\(\s*([-+]?\d+)\s*,\s*([-+]?\d+)\s*,\s*([-+]?\d+)\s*\)$/,jv=/^rgb\(\s*([-+]?\d+(?:\.\d+)?)%\s*,\s*([-+]?\d+(?:\.\d+)?)%\s*,\s*([-+]?\d+(?:\.\d+)?)%\s*\)$/,Hv=/^rgba\(\s*([-+]?\d+)\s*,\s*([-+]?\d+)\s*,\s*([-+]?\d+)\s*,\s*([-+]?\d+(?:\.\d+)?)\s*\)$/,Xv=/^rgba\(\s*([-+]?\d+(?:\.\d+)?)%\s*,\s*([-+]?\d+(?:\.\d+)?)%\s*,\s*([-+]?\d+(?:\.\d+)?)%\s*,\s*([-+]?\d+(?:\.\d+)?)\s*\)$/,Vv=/^hsl\(\s*([-+]?\d+(?:\.\d+)?)\s*,\s*([-+]?\d+(?:\.\d+)?)%\s*,\s*([-+]?\d+(?:\.\d+)?)%\s*\)$/,Wv=/^hsla\(\s*([-+]?\d+(?:\.\d+)?)\s*,\s*([-+]?\d+(?:\.\d+)?)%\s*,\s*([-+]?\d+(?:\.\d+)?)%\s*,\s*([-+]?\d+(?:\.\d+)?)\s*\)$/,$v={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};ge(xe,be,{displayable:function(){return this.rgb().displayable()},toString:function(){return this.rgb()+""}}),ge(Ne,ke,me(xe,{brighter:function(t){return t=null==t?Fv:Math.pow(Fv,t),new Ne(this.r*t,this.g*t,this.b*t,this.opacity)},darker:function(t){return t=null==t?Ov:Math.pow(Ov,t),new Ne(this.r*t,this.g*t,this.b*t,this.opacity)},rgb:function(){return this},displayable:function(){return 0<=this.r&&this.r<=255&&0<=this.g&&this.g<=255&&0<=this.b&&this.b<=255&&0<=this.opacity&&this.opacity<=1},toString:function(){var t=this.opacity;return t=isNaN(t)?1:Math.max(0,Math.min(1,t)),(1===t?"rgb(":"rgba(")+Math.max(0,Math.min(255,Math.round(this.r)||0))+", "+Math.max(0,Math.min(255,Math.round(this.g)||0))+", "+Math.max(0,Math.min(255,Math.round(this.b)||0))+(1===t?")":", "+t+")")}})),ge(Ce,Ae,me(xe,{brighter:function(t){return t=null==t?Fv:Math.pow(Fv,t),new Ce(this.h,this.s,this.l*t,this.opacity)},darker:function(t){return t=null==t?Ov:Math.pow(Ov,t),new Ce(this.h,this.s,this.l*t,this.opacity)},rgb:function(){var t=this.h%360+360*(this.h<0),n=isNaN(t)||isNaN(this.s)?0:this.s,e=this.l,r=e+(e<.5?e:1-e)*n,i=2*e-r;return new Ne(ze(t>=240?t-240:t+120,i,r),ze(t,i,r),ze(t<120?t+240:t-120,i,r),this.opacity)},displayable:function(){return(0<=this.s&&this.s<=1||isNaN(this.s))&&0<=this.l&&this.l<=1&&0<=this.opacity&&this.opacity<=1}}));var Zv=Math.PI/180,Gv=180/Math.PI,Jv=18,Qv=.95047,Kv=1,t_=1.08883,n_=4/29,e_=6/29,r_=3*e_*e_,i_=e_*e_*e_;ge(qe,Le,me(xe,{brighter:function(t){return new qe(this.l+Jv*(null==t?1:t),this.a,this.b,this.opacity)},darker:function(t){return new qe(this.l-Jv*(null==t?1:t),this.a,this.b,this.opacity)},rgb:function(){var t=(this.l+16)/116,n=isNaN(this.a)?t:t+this.a/500,e=isNaN(this.b)?t:t-this.b/200;return t=Kv*Re(t),n=Qv*Re(n),e=t_*Re(e),new Ne(De(3.2404542*n-1.5371385*t-.4985314*e),De(-.969266*n+1.8760108*t+.041556*e),De(.0556434*n-.2040259*t+1.0572252*e),this.opacity)}})),ge(Ye,Ie,me(xe,{brighter:function(t){return new Ye(this.h,this.c,this.l+Jv*(null==t?1:t),this.opacity)},darker:function(t){return new Ye(this.h,this.c,this.l-Jv*(null==t?1:t),this.opacity)},rgb:function(){return Pe(this).rgb()}}));var o_=-.14861,u_=1.78277,a_=-.29227,c_=-.90649,s_=1.97294,f_=s_*c_,l_=s_*u_,h_=u_*a_-c_*o_;ge(He,je,me(xe,{brighter:function(t){return t=null==t?Fv:Math.pow(Fv,t),new He(this.h,this.s,this.l*t,this.opacity)},darker:function(t){return t=null==t?Ov:Math.pow(Ov,t),new He(this.h,this.s,this.l*t,this.opacity)},rgb:function(){var t=isNaN(this.h)?0:(this.h+120)*Zv,n=+this.l,e=isNaN(this.s)?0:this.s*n*(1-n),r=Math.cos(t),i=Math.sin(t);return new Ne(255*(n+e*(o_*r+u_*i)),255*(n+e*(a_*r+c_*i)),255*(n+e*(s_*r)),this.opacity)}}));var p_,d_,v_,__,y_=function dw(t){function n(t,n){var r=e((t=ke(t)).r,(n=ke(n)).r),i=e(t.g,n.g),o=e(t.b,n.b),u=e(t.opacity,n.opacity);return function(n){return t.r=r(n),t.g=i(n),t.b=o(n),t.opacity=u(n),t+""}}var e=Qe(t);return n.gamma=dw,n}(1),g_=tr(Ve),m_=tr(We),x_=/[-+]?(?:\d+\.?\d*|\.?\d+)(?:[eE][-+]?\d+)?/g,b_=new RegExp(x_.source,"g"),w_=180/Math.PI,M_={translateX:0,translateY:0,rotate:0,skewX:0,scaleX:1,scaleY:1},T_=hr(fr,"px, ","px)","deg)"),k_=hr(lr,", ",")",")"),N_=Math.SQRT2,S_=2,E_=4,A_=1e-12,C_=yr(Je),z_=yr(Ke),P_=mr(Je),L_=mr(Ke),q_=xr(Je),U_=xr(Ke),R_={value:function(){}};Mr.prototype=wr.prototype={constructor:Mr,on:function(t,n){var e,r=this._,i=Tr(t+"",r),o=-1,u=i.length;{if(!(arguments.length<2)){if(null!=n&&"function"!=typeof n)throw new Error("invalid callback: "+n);for(;++o0)for(var e,r,i=new Array(e),o=0;o0?t>1?$r(function(n){n.setTime(Math.floor(n/t)*t)},function(n,e){n.setTime(+n+e*t)},function(n,e){return(e-n)/t}):hy:null};var py=hy.range,dy=1e3,vy=6e4,_y=36e5,yy=864e5,gy=6048e5,my=$r(function(t){t.setTime(Math.floor(t/dy)*dy)},function(t,n){t.setTime(+t+n*dy)},function(t,n){return(n-t)/dy},function(t){return t.getUTCSeconds()}),xy=my.range,by=$r(function(t){t.setTime(Math.floor(t/vy)*vy)},function(t,n){t.setTime(+t+n*vy)},function(t,n){return(n-t)/vy},function(t){return t.getMinutes()}),wy=by.range,My=$r(function(t){var n=t.getTimezoneOffset()*vy%_y;n<0&&(n+=_y),t.setTime(Math.floor((+t-n)/_y)*_y+n)},function(t,n){t.setTime(+t+n*_y)},function(t,n){return(n-t)/_y},function(t){return t.getHours()}),Ty=My.range,ky=$r(function(t){t.setHours(0,0,0,0)},function(t,n){t.setDate(t.getDate()+n)},function(t,n){return(n-t-(n.getTimezoneOffset()-t.getTimezoneOffset())*vy)/yy},function(t){return t.getDate()-1}),Ny=ky.range,Sy=Zr(0),Ey=Zr(1),Ay=Zr(2),Cy=Zr(3),zy=Zr(4),Py=Zr(5),Ly=Zr(6),qy=Sy.range,Uy=Ey.range,Ry=Ay.range,Dy=Cy.range,Oy=zy.range,Fy=Py.range,Iy=Ly.range,Yy=$r(function(t){t.setDate(1),t.setHours(0,0,0,0)},function(t,n){t.setMonth(t.getMonth()+n)},function(t,n){return n.getMonth()-t.getMonth()+12*(n.getFullYear()-t.getFullYear())},function(t){return t.getMonth()}),By=Yy.range,jy=$r(function(t){t.setMonth(0,1),t.setHours(0,0,0,0)},function(t,n){t.setFullYear(t.getFullYear()+n)},function(t,n){return n.getFullYear()-t.getFullYear()},function(t){return t.getFullYear()});jy.every=function(t){return isFinite(t=Math.floor(t))&&t>0?$r(function(n){n.setFullYear(Math.floor(n.getFullYear()/t)*t),n.setMonth(0,1),n.setHours(0,0,0,0)},function(n,e){n.setFullYear(n.getFullYear()+e*t)}):null};var Hy=jy.range,Xy=$r(function(t){t.setUTCSeconds(0,0)},function(t,n){t.setTime(+t+n*vy)},function(t,n){return(n-t)/vy},function(t){return t.getUTCMinutes()}),Vy=Xy.range,Wy=$r(function(t){t.setUTCMinutes(0,0,0)},function(t,n){t.setTime(+t+n*_y)},function(t,n){return(n-t)/_y},function(t){return t.getUTCHours()}),$y=Wy.range,Zy=$r(function(t){t.setUTCHours(0,0,0,0)},function(t,n){t.setUTCDate(t.getUTCDate()+n)},function(t,n){return(n-t)/yy},function(t){return t.getUTCDate()-1}),Gy=Zy.range,Jy=Gr(0),Qy=Gr(1),Ky=Gr(2),tg=Gr(3),ng=Gr(4),eg=Gr(5),rg=Gr(6),ig=Jy.range,og=Qy.range,ug=Ky.range,ag=tg.range,cg=ng.range,sg=eg.range,fg=rg.range,lg=$r(function(t){t.setUTCDate(1),t.setUTCHours(0,0,0,0)},function(t,n){t.setUTCMonth(t.getUTCMonth()+n)},function(t,n){return n.getUTCMonth()-t.getUTCMonth()+12*(n.getUTCFullYear()-t.getUTCFullYear())},function(t){return t.getUTCMonth()}),hg=lg.range,pg=$r(function(t){t.setUTCMonth(0,1),t.setUTCHours(0,0,0,0)},function(t,n){t.setUTCFullYear(t.getUTCFullYear()+n)},function(t,n){return n.getUTCFullYear()-t.getUTCFullYear()},function(t){return t.getUTCFullYear()});pg.every=function(t){return isFinite(t=Math.floor(t))&&t>0?$r(function(n){n.setUTCFullYear(Math.floor(n.getUTCFullYear()/t)*t),n.setUTCMonth(0,1),n.setUTCHours(0,0,0,0)},function(n,e){n.setUTCFullYear(n.getUTCFullYear()+e*t)}):null};var dg,vg=pg.range,_g={"":ti,"%":function(t,n){return(100*t).toFixed(n)},b:function(t){return Math.round(t).toString(2)},c:function(t){return t+""},d:function(t){return Math.round(t).toString(10)},e:function(t,n){return t.toExponential(n)},f:function(t,n){return t.toFixed(n)},g:function(t,n){return t.toPrecision(n)},o:function(t){return Math.round(t).toString(8)},p:function(t,n){return ei(100*t,n)},r:ei,s:ni,X:function(t){return Math.round(t).toString(16).toUpperCase()},x:function(t){return Math.round(t).toString(16)}},yg=/^(?:(.)?([<>=^]))?([+\-\( ])?([$#])?(0)?(\d+)?(,)?(\.\d+)?([a-z%])?$/i;ii.prototype.toString=function(){return this.fill+this.align+this.sign+this.symbol+(this.zero?"0":"")+(null==this.width?"":Math.max(1,0|this.width))+(this.comma?",":"")+(null==this.precision?"":"."+Math.max(0,0|this.precision))+this.type};var gg,mg=["y","z","a","f","p","n","µ","m","","k","M","G","T","P","E","Z","Y"];t.format,t.formatPrefix,ai({decimal:".",thousands:",",grouping:[3],currency:["$",""]});var xg,bg={"-":"",_:" ",0:"0"},wg=/^\s*\d+/,Mg=/^%/,Tg=/[\\\^\$\*\+\?\|\[\]\(\)\.\{\}]/g;t.timeFormat,t.timeParse,t.utcFormat,t.utcParse,co({dateTime:"%x, %X",date:"%-m/%-d/%Y",time:"%-I:%M:%S %p",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"]});var kg="%Y-%m-%dT%H:%M:%S.%LZ",Ng=Date.prototype.toISOString?so:t.utcFormat(kg),Sg=+new Date("2000-01-01T00:00:00.000Z")?fo:t.utcParse(kg),Eg=Array.prototype,Ag=Eg.map,Cg=Eg.slice,zg={name:"implicit"},Pg=[0,1],Lg=1e3,qg=60*Lg,Ug=60*qg,Rg=24*Ug,Dg=7*Rg,Og=30*Rg,Fg=365*Rg,Ig=$o("1f77b4ff7f0e2ca02cd627289467bd8c564be377c27f7f7fbcbd2217becf"),Yg=$o("393b795254a36b6ecf9c9ede6379398ca252b5cf6bcedb9c8c6d31bd9e39e7ba52e7cb94843c39ad494ad6616be7969c7b4173a55194ce6dbdde9ed6"),Bg=$o("3182bd6baed69ecae1c6dbefe6550dfd8d3cfdae6bfdd0a231a35474c476a1d99bc7e9c0756bb19e9ac8bcbddcdadaeb636363969696bdbdbdd9d9d9"),jg=$o("1f77b4aec7e8ff7f0effbb782ca02c98df8ad62728ff98969467bdc5b0d58c564bc49c94e377c2f7b6d27f7f7fc7c7c7bcbd22dbdb8d17becf9edae5"),Hg=U_(je(300,.5,0),je(-240,.5,1)),Xg=U_(je(-100,.75,.35),je(80,1.5,.8)),Vg=U_(je(260,.75,.35),je(80,1.5,.8)),Wg=je(),$g=Go($o("44015444025645045745055946075a46085c460a5d460b5e470d60470e6147106347116447136548146748166848176948186a481a6c481b6d481c6e481d6f481f70482071482173482374482475482576482677482878482979472a7a472c7a472d7b472e7c472f7d46307e46327e46337f463480453581453781453882443983443a83443b84433d84433e85423f854240864241864142874144874045884046883f47883f48893e49893e4a893e4c8a3d4d8a3d4e8a3c4f8a3c508b3b518b3b528b3a538b3a548c39558c39568c38588c38598c375a8c375b8d365c8d365d8d355e8d355f8d34608d34618d33628d33638d32648e32658e31668e31678e31688e30698e306a8e2f6b8e2f6c8e2e6d8e2e6e8e2e6f8e2d708e2d718e2c718e2c728e2c738e2b748e2b758e2a768e2a778e2a788e29798e297a8e297b8e287c8e287d8e277e8e277f8e27808e26818e26828e26828e25838e25848e25858e24868e24878e23888e23898e238a8d228b8d228c8d228d8d218e8d218f8d21908d21918c20928c20928c20938c1f948c1f958b1f968b1f978b1f988b1f998a1f9a8a1e9b8a1e9c891e9d891f9e891f9f881fa0881fa1881fa1871fa28720a38620a48621a58521a68522a78522a88423a98324aa8325ab8225ac8226ad8127ad8128ae8029af7f2ab07f2cb17e2db27d2eb37c2fb47c31b57b32b67a34b67935b77937b87838b9773aba763bbb753dbc743fbc7340bd7242be7144bf7046c06f48c16e4ac16d4cc26c4ec36b50c46a52c56954c56856c66758c7655ac8645cc8635ec96260ca6063cb5f65cb5e67cc5c69cd5b6ccd5a6ece5870cf5773d05675d05477d1537ad1517cd2507fd34e81d34d84d44b86d54989d5488bd6468ed64590d74393d74195d84098d83e9bd93c9dd93ba0da39a2da37a5db36a8db34aadc32addc30b0dd2fb2dd2db5de2bb8de29bade28bddf26c0df25c2df23c5e021c8e020cae11fcde11dd0e11cd2e21bd5e21ad8e219dae319dde318dfe318e2e418e5e419e7e419eae51aece51befe51cf1e51df4e61ef6e620f8e621fbe723fde725")),Zg=Go($o("00000401000501010601010802010902020b02020d03030f03031204041405041606051806051a07061c08071e0907200a08220b09240c09260d0a290e0b2b100b2d110c2f120d31130d34140e36150e38160f3b180f3d19103f1a10421c10441d11471e114920114b21114e22115024125325125527125829115a2a115c2c115f2d11612f116331116533106734106936106b38106c390f6e3b0f703d0f713f0f72400f74420f75440f764510774710784910784a10794c117a4e117b4f127b51127c52137c54137d56147d57157e59157e5a167e5c167f5d177f5f187f601880621980641a80651a80671b80681c816a1c816b1d816d1d816e1e81701f81721f817320817521817621817822817922827b23827c23827e24828025828125818326818426818627818827818928818b29818c29818e2a81902a81912b81932b80942c80962c80982d80992d809b2e7f9c2e7f9e2f7fa02f7fa1307ea3307ea5317ea6317da8327daa337dab337cad347cae347bb0357bb2357bb3367ab5367ab73779b83779ba3878bc3978bd3977bf3a77c03a76c23b75c43c75c53c74c73d73c83e73ca3e72cc3f71cd4071cf4070d0416fd2426fd3436ed5446dd6456cd8456cd9466bdb476adc4869de4968df4a68e04c67e24d66e34e65e44f64e55064e75263e85362e95462ea5661eb5760ec5860ed5a5fee5b5eef5d5ef05f5ef1605df2625df2645cf3655cf4675cf4695cf56b5cf66c5cf66e5cf7705cf7725cf8745cf8765cf9785df9795df97b5dfa7d5efa7f5efa815ffb835ffb8560fb8761fc8961fc8a62fc8c63fc8e64fc9065fd9266fd9467fd9668fd9869fd9a6afd9b6bfe9d6cfe9f6dfea16efea36ffea571fea772fea973feaa74feac76feae77feb078feb27afeb47bfeb67cfeb77efeb97ffebb81febd82febf84fec185fec287fec488fec68afec88cfeca8dfecc8ffecd90fecf92fed194fed395fed597fed799fed89afdda9cfddc9efddea0fde0a1fde2a3fde3a5fde5a7fde7a9fde9aafdebacfcecaefceeb0fcf0b2fcf2b4fcf4b6fcf6b8fcf7b9fcf9bbfcfbbdfcfdbf")),Gg=Go($o("00000401000501010601010802010a02020c02020e03021004031204031405041706041907051b08051d09061f0a07220b07240c08260d08290e092b10092d110a30120a32140b34150b37160b39180c3c190c3e1b0c411c0c431e0c451f0c48210c4a230c4c240c4f260c51280b53290b552b0b572d0b592f0a5b310a5c320a5e340a5f3609613809623909633b09643d09653e0966400a67420a68440a68450a69470b6a490b6a4a0c6b4c0c6b4d0d6c4f0d6c510e6c520e6d540f6d550f6d57106e59106e5a116e5c126e5d126e5f136e61136e62146e64156e65156e67166e69166e6a176e6c186e6d186e6f196e71196e721a6e741a6e751b6e771c6d781c6d7a1d6d7c1d6d7d1e6d7f1e6c801f6c82206c84206b85216b87216b88226a8a226a8c23698d23698f24699025689225689326679526679727669827669a28659b29649d29649f2a63a02a63a22b62a32c61a52c60a62d60a82e5fa92e5eab2f5ead305dae305cb0315bb1325ab3325ab43359b63458b73557b93556ba3655bc3754bd3853bf3952c03a51c13a50c33b4fc43c4ec63d4dc73e4cc83f4bca404acb4149cc4248ce4347cf4446d04545d24644d34743d44842d54a41d74b3fd84c3ed94d3dda4e3cdb503bdd513ade5238df5337e05536e15635e25734e35933e45a31e55c30e65d2fe75e2ee8602de9612bea632aeb6429eb6628ec6726ed6925ee6a24ef6c23ef6e21f06f20f1711ff1731df2741cf3761bf37819f47918f57b17f57d15f67e14f68013f78212f78410f8850ff8870ef8890cf98b0bf98c0af98e09fa9008fa9207fa9407fb9606fb9706fb9906fb9b06fb9d07fc9f07fca108fca309fca50afca60cfca80dfcaa0ffcac11fcae12fcb014fcb216fcb418fbb61afbb81dfbba1ffbbc21fbbe23fac026fac228fac42afac62df9c72ff9c932f9cb35f8cd37f8cf3af7d13df7d340f6d543f6d746f5d949f5db4cf4dd4ff4df53f4e156f3e35af3e55df2e661f2e865f2ea69f1ec6df1ed71f1ef75f1f179f2f27df2f482f3f586f3f68af4f88ef5f992f6fa96f8fb9af9fc9dfafda1fcffa4")),Jg=Go($o("0d088710078813078916078a19068c1b068d1d068e20068f2206902406912605912805922a05932c05942e05952f059631059733059735049837049938049a3a049a3c049b3e049c3f049c41049d43039e44039e46039f48039f4903a04b03a14c02a14e02a25002a25102a35302a35502a45601a45801a45901a55b01a55c01a65e01a66001a66100a76300a76400a76600a76700a86900a86a00a86c00a86e00a86f00a87100a87201a87401a87501a87701a87801a87a02a87b02a87d03a87e03a88004a88104a78305a78405a78606a68707a68808a68a09a58b0aa58d0ba58e0ca48f0da4910ea3920fa39410a29511a19613a19814a099159f9a169f9c179e9d189d9e199da01a9ca11b9ba21d9aa31e9aa51f99a62098a72197a82296aa2395ab2494ac2694ad2793ae2892b02991b12a90b22b8fb32c8eb42e8db52f8cb6308bb7318ab83289ba3388bb3488bc3587bd3786be3885bf3984c03a83c13b82c23c81c33d80c43e7fc5407ec6417dc7427cc8437bc9447aca457acb4679cc4778cc4977cd4a76ce4b75cf4c74d04d73d14e72d24f71d35171d45270d5536fd5546ed6556dd7566cd8576bd9586ada5a6ada5b69db5c68dc5d67dd5e66de5f65de6164df6263e06363e16462e26561e26660e3685fe4695ee56a5de56b5de66c5ce76e5be76f5ae87059e97158e97257ea7457eb7556eb7655ec7754ed7953ed7a52ee7b51ef7c51ef7e50f07f4ff0804ef1814df1834cf2844bf3854bf3874af48849f48948f58b47f58c46f68d45f68f44f79044f79143f79342f89441f89540f9973ff9983ef99a3efa9b3dfa9c3cfa9e3bfb9f3afba139fba238fca338fca537fca636fca835fca934fdab33fdac33fdae32fdaf31fdb130fdb22ffdb42ffdb52efeb72dfeb82cfeba2cfebb2bfebd2afebe2afec029fdc229fdc328fdc527fdc627fdc827fdca26fdcb26fccd25fcce25fcd025fcd225fbd324fbd524fbd724fad824fada24f9dc24f9dd25f8df25f8e125f7e225f7e425f6e626f6e826f5e926f5eb27f4ed27f3ee27f3f027f2f227f1f426f1f525f0f724f0f921")),Qg="http://www.w3.org/1999/xhtml",Kg={svg:"http://www.w3.org/2000/svg",xhtml:Qg,xlink:"http://www.w3.org/1999/xlink",xml:"http://www.w3.org/XML/1998/namespace",xmlns:"http://www.w3.org/2000/xmlns/"},tm=0;ru.prototype=eu.prototype={constructor:ru,get:function(t){for(var n=this._;!(n in t);)if(!(t=t.parentNode))return;return t[n]},set:function(t,n){return t[this._]=n},remove:function(t){return this._ in t&&delete t[this._]},toString:function(){return this._}};var nm=function(t){return function(){return this.matches(t)}};if("undefined"!=typeof document){var em=document.documentElement;if(!em.matches){var rm=em.webkitMatchesSelector||em.msMatchesSelector||em.mozMatchesSelector||em.oMatchesSelector;nm=function(t){return function(){return rm.call(this,t)}}}}var im=nm,om={};if(t.event=null,"undefined"!=typeof document){var um=document.documentElement;"onmouseenter"in um||(om={mouseenter:"mouseover", +mouseleave:"mouseout"})}Mu.prototype={constructor:Mu,appendChild:function(t){return this._parent.insertBefore(t,this._next)},insertBefore:function(t,n){return this._parent.insertBefore(t,n)},querySelector:function(t){return this._parent.querySelector(t)},querySelectorAll:function(t){return this._parent.querySelectorAll(t)}};var am="$";ra.prototype={add:function(t){var n=this._names.indexOf(t);n<0&&(this._names.push(t),this._node.setAttribute("class",this._names.join(" ")))},remove:function(t){var n=this._names.indexOf(t);n>=0&&(this._names.splice(n,1),this._node.setAttribute("class",this._names.join(" ")))},contains:function(t){return this._names.indexOf(t)>=0}};var cm=[null];Pa.prototype=La.prototype={constructor:Pa,select:_u,selectAll:mu,filter:xu,data:Su,enter:wu,exit:Eu,merge:Au,order:Cu,sort:zu,call:Lu,nodes:qu,node:Uu,size:Ru,empty:Du,each:Ou,attr:Xu,style:Gu,property:ta,classed:sa,text:pa,html:ya,raise:ma,lower:ba,append:wa,insert:Ta,remove:Na,datum:Sa,on:su,dispatch:za};var sm=wr("start","end","interrupt"),fm=[],lm=0,hm=1,pm=2,dm=3,vm=4,_m=5,ym=La.prototype.constructor,gm=0,mm=La.prototype;Uc.prototype=Rc.prototype={constructor:Uc,select:bc,selectAll:wc,filter:dc,merge:vc,selection:Mc,transition:qc,call:mm.call,nodes:mm.nodes,node:mm.node,size:mm.size,empty:mm.empty,each:mm.each,on:gc,attr:ec,attrTween:oc,style:Ec,styleTween:Cc,text:Lc,remove:xc,tween:Wa,delay:cc,duration:lc,ease:pc};var xm={time:null,delay:0,duration:250,ease:et};La.prototype.interrupt=Ha,La.prototype.transition=Fc;var bm=[null],wm=Array.prototype.slice,Mm=1,Tm=2,km=3,Nm=4,Sm=1e-6;bs.prototype=_s.prototype={constructor:bs,each:os,eachAfter:as,eachBefore:us,sum:cs,sort:ss,path:fs,ancestors:hs,descendants:ps,leaves:ds,links:vs,copy:ys};var Em="$",Am={depth:-1},Cm={};rf.prototype=Object.create(bs.prototype);var zm=(1+Math.sqrt(5))/2,Pm=function vw(t){function n(n,e,r,i,o){cf(t,n,e,r,i,o)}return n.ratio=function(t){return vw((t=+t)>1?t:1)},n}(zm),Lm=function _w(t){function n(n,e,r,i,o){if((u=n._squarify)&&u.ratio===t)for(var u,a,c,s,f,l=-1,h=u.length,p=n.value;++l1?t:1)},n}(zm),qm=10,Um=Math.PI*(3-Math.sqrt(5));zf.prototype.on=function(){var t=this._.on.apply(this._,arguments);return t===this._?this:t},Ff.prototype={constructor:Ff,insert:function(t,n){var e,r,i;if(t){if(n.P=t,n.N=t.N,t.N&&(t.N.P=n),t.N=n,t.R){for(t=t.R;t.L;)t=t.L;t.L=n}else t.R=n;e=t}else this._?(t=jf(this._),n.P=null,n.N=t,t.P=t.L=n,e=t):(n.P=n.N=null,this._=n,e=null);for(n.L=n.R=null,n.U=e,n.C=!0,t=n;e&&e.C;)r=e.U,e===r.L?(i=r.R,i&&i.C?(e.C=i.C=!1,r.C=!0,t=r):(t===e.R&&(Yf(this,e),t=e,e=t.U),e.C=!1,r.C=!0,Bf(this,r))):(i=r.L,i&&i.C?(e.C=i.C=!1,r.C=!0,t=r):(t===e.L&&(Bf(this,e),t=e,e=t.U),e.C=!1,r.C=!0,Yf(this,r))),e=t.U;this._.C=!1},remove:function(t){t.N&&(t.N.P=t.P),t.P&&(t.P.N=t.N),t.N=t.P=null;var n,e,r,i=t.U,o=t.L,u=t.R;if(e=o?u?jf(u):o:u,i?i.L===t?i.L=e:i.R=e:this._=e,o&&u?(r=e.C,e.C=t.C,e.L=o,o.U=e,e!==u?(i=e.U,e.U=t.U,t=e.R,i.L=t,e.R=u,u.U=e):(e.U=i,i=e,t=e.R)):(r=t.C,t=e),t&&(t.U=i),!r){if(t&&t.C)return void(t.C=!1);do{if(t===this._)break;if(t===i.L){if(n=i.R,n.C&&(n.C=!1,i.C=!0,Yf(this,i),n=i.R),n.L&&n.L.C||n.R&&n.R.C){n.R&&n.R.C||(n.L.C=!1,n.C=!0,Bf(this,n),n=i.R),n.C=i.C,i.C=n.R.C=!1,Yf(this,i),t=this._;break}}else if(n=i.L,n.C&&(n.C=!1,i.C=!0,Bf(this,i),n=i.L),n.L&&n.L.C||n.R&&n.R.C){n.L&&n.L.C||(n.R.C=!1,n.C=!0,Yf(this,n),n=i.L),n.C=i.C,i.C=n.L.C=!1,Bf(this,i),t=this._;break}n.C=!0,t=i,i=i.U}while(!t.C);t&&(t.C=!1)}}};var Rm,Dm,Om,Fm,Im,Ym=[],Bm=[],jm=1e-6,Hm=1e-12;dl.prototype={constructor:dl,polygons:function(){var t=this.edges;return this.cells.map(function(n){var e=n.halfedges.map(function(e){return Qf(n,t[e])});return e.data=n.site.data,e})},triangles:function(){var t=[],n=this.edges;return this.cells.forEach(function(e,r){for(var i,o=e.site,u=e.halfedges,a=-1,c=u.length,s=n[u[c-1]],f=s.left===o?s.right:s.left;++a0?1:t<0?-1:0},rb=Math.sqrt,ib=Math.tan,ob={Feature:function(t,n){Wl(t.geometry,n)},FeatureCollection:function(t,n){for(var e=t.features,r=-1,i=e.length;++rFx?dx=90:mx<-Fx&&(hx=-90),bx[0]=lx,bx[1]=px}},sb={sphere:Vl,point:xh,lineStart:wh,lineEnd:kh,polygonStart:function(){sb.lineStart=Nh,sb.lineEnd=Sh},polygonEnd:function(){sb.lineStart=wh,sb.lineEnd=kh}};Lh.invert=Lh;var fb,lb,hb,pb,db,vb,_b,yb,gb,mb,xb,bb,wb=1e9,Mb=-wb,Tb={sphere:Vl,point:Vl,lineStart:Gh,lineEnd:Vl,polygonStart:Vl,polygonEnd:Vl},kb=[null,null],Nb={type:"LineString",coordinates:kb},Sb=Il(),Eb=Il(),Ab={point:Vl,lineStart:Vl,lineEnd:Vl,polygonStart:function(){Ab.lineStart=ap,Ab.lineEnd=fp},polygonEnd:function(){Ab.lineStart=Ab.lineEnd=Ab.point=Vl,Sb.add(Wx(Eb)),Eb.reset()},result:function(){var t=Sb/2;return Sb.reset(),t}},Cb=1/0,zb=Cb,Pb=-Cb,Lb=Pb,qb={point:lp,lineStart:Vl,lineEnd:Vl,polygonStart:Vl,polygonEnd:Vl,result:function(){var t=[[Cb,zb],[Pb,Lb]];return Pb=Lb=-(zb=Cb=1/0),t}},Ub=0,Rb=0,Db=0,Ob=0,Fb=0,Ib=0,Yb=0,Bb=0,jb=0,Hb={point:hp,lineStart:pp,lineEnd:_p,polygonStart:function(){Hb.lineStart=yp,Hb.lineEnd=gp},polygonEnd:function(){Hb.point=hp,Hb.lineStart=pp,Hb.lineEnd=_p},result:function(){var t=jb?[Yb/jb,Bb/jb]:Ib?[Ob/Ib,Fb/Ib]:Db?[Ub/Db,Rb/Db]:[NaN,NaN];return Ub=Rb=Db=Ob=Fb=Ib=Yb=Bb=jb=0,t}},Xb=Il(),Vb=Np(function(){return!0},Ap,zp,[-Yx,-Bx]);Up.prototype={point:function(t,n){this.stream.point(t,n)},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()}};var Wb=16,$b=Gx(30*Vx),Zb=qp({point:function(t,n){this.stream.point(t*Vx,n*Vx)}}),Gb=Wp(function(t){return rb(2/(1+t))});Gb.invert=$p(function(t){return 2*Hl(t/2)});var Jb=Wp(function(t){return(t=jl(t))&&t/nb(t)});Jb.invert=$p(function(t){return t}),Jp.invert=function(t,n){return[t,2*$x(Qx(n))-Bx]},rd.invert=rd,ad.invert=$p($x),sd.invert=$p(Hl),ld.invert=$p(function(t){return 2+$x(t)}),pd.invert=function(t,n){return[-n,2*$x(Qx(t))-Bx]},t.version=vd,t.bisect=yd,t.bisectRight=yd,t.bisectLeft=gd,t.ascending=n,t.bisector=e,t.descending=i,t.deviation=a,t.extent=c,t.histogram=v,t.thresholdFreedmanDiaconis=y,t.thresholdScott=g,t.thresholdSturges=d,t.max=m,t.mean=x,t.median=b,t.merge=w,t.min=M,t.pairs=T,t.permute=k,t.quantile=_,t.range=l,t.scan=N,t.shuffle=S,t.sum=E,t.ticks=h,t.tickStep=p,t.transpose=A,t.variance=u,t.zip=z,t.entries=j,t.keys=Y,t.values=B,t.map=L,t.set=I,t.nest=q,t.randomUniform=H,t.randomNormal=X,t.randomLogNormal=V,t.randomBates=$,t.randomIrwinHall=W,t.randomExponential=Z,t.easeLinear=G,t.easeQuad=K,t.easeQuadIn=J,t.easeQuadOut=Q,t.easeQuadInOut=K,t.easeCubic=et,t.easeCubicIn=tt,t.easeCubicOut=nt,t.easeCubicInOut=et,t.easePoly=Cd,t.easePolyIn=Ed,t.easePolyOut=Ad,t.easePolyInOut=Cd,t.easeSin=ot,t.easeSinIn=rt,t.easeSinOut=it,t.easeSinInOut=ot,t.easeExp=ct,t.easeExpIn=ut,t.easeExpOut=at,t.easeExpInOut=ct,t.easeCircle=lt,t.easeCircleIn=st,t.easeCircleOut=ft,t.easeCircleInOut=lt,t.easeBounce=pt,t.easeBounceIn=ht,t.easeBounceOut=pt,t.easeBounceInOut=dt,t.easeBack=Vd,t.easeBackIn=Hd,t.easeBackOut=Xd,t.easeBackInOut=Vd,t.easeElastic=Jd,t.easeElasticIn=Gd,t.easeElasticOut=Jd,t.easeElasticInOut=Qd,t.polygonArea=vt,t.polygonCentroid=_t,t.polygonHull=xt,t.polygonContains=bt,t.polygonLength=wt,t.path=Tt,t.quadtree=jt,t.queue=Qt,t.arc=sn,t.area=vn,t.line=dn,t.pie=gn,t.radialArea=Mn,t.radialLine=wn,t.symbol=Tn,t.symbols=Av,t.symbolCircle=lv,t.symbolCross=hv,t.symbolDiamond=vv,t.symbolSquare=bv,t.symbolStar=xv,t.symbolTriangle=Mv,t.symbolWye=Ev,t.curveBasisClosed=Cn,t.curveBasisOpen=Pn,t.curveBasis=En,t.curveBundle=Cv,t.curveCardinalClosed=Pv,t.curveCardinalOpen=Lv,t.curveCardinal=zv,t.curveCatmullRomClosed=Uv,t.curveCatmullRomOpen=Rv,t.curveCatmullRom=qv,t.curveLinearClosed=jn,t.curveLinear=ln,t.curveMonotoneX=Jn,t.curveMonotoneY=Qn,t.curveNatural=ne,t.curveStep=re,t.curveStepAfter=oe,t.curveStepBefore=ie,t.stack=se,t.stackOffsetExpand=fe,t.stackOffsetNone=ue,t.stackOffsetSilhouette=le,t.stackOffsetWiggle=he,t.stackOrderAscending=pe,t.stackOrderDescending=ve,t.stackOrderInsideOut=_e,t.stackOrderNone=ae,t.stackOrderReverse=ye,t.color=be,t.rgb=ke,t.hsl=Ae,t.lab=Le,t.hcl=Ie,t.cubehelix=je,t.interpolate=ar,t.interpolateArray=nr,t.interpolateNumber=er,t.interpolateObject=rr,t.interpolateRound=cr,t.interpolateString=ur,t.interpolateTransformCss=T_,t.interpolateTransformSvg=k_,t.interpolateZoom=_r,t.interpolateRgb=y_,t.interpolateRgbBasis=g_,t.interpolateRgbBasisClosed=m_,t.interpolateHsl=C_,t.interpolateHslLong=z_,t.interpolateLab=gr,t.interpolateHcl=P_,t.interpolateHclLong=L_,t.interpolateCubehelix=q_,t.interpolateCubehelixLong=U_,t.interpolateBasis=Ve,t.interpolateBasisClosed=We,t.quantize=br,t.dispatch=wr,t.dsvFormat=Cr,t.csvParse=I_,t.csvParseRows=Y_,t.csvFormat=B_,t.csvFormatRows=j_,t.tsvParse=X_,t.tsvParseRows=V_,t.tsvFormat=W_,t.tsvFormatRows=$_,t.request=zr,t.html=Z_,t.json=G_,t.text=J_,t.xml=Q_,t.csv=K_,t.tsv=ty,t.now=Dr,t.timer=Ir,t.timerFlush=Yr,t.timeout=Vr,t.interval=Wr,t.timeInterval=$r,t.timeMillisecond=hy,t.timeMilliseconds=py,t.timeSecond=my,t.timeSeconds=xy,t.timeMinute=by,t.timeMinutes=wy,t.timeHour=My,t.timeHours=Ty,t.timeDay=ky,t.timeDays=Ny,t.timeWeek=Sy,t.timeWeeks=qy,t.timeSunday=Sy,t.timeSundays=qy,t.timeMonday=Ey,t.timeMondays=Uy,t.timeTuesday=Ay,t.timeTuesdays=Ry,t.timeWednesday=Cy,t.timeWednesdays=Dy,t.timeThursday=zy,t.timeThursdays=Oy,t.timeFriday=Py,t.timeFridays=Fy,t.timeSaturday=Ly,t.timeSaturdays=Iy,t.timeMonth=Yy,t.timeMonths=By,t.timeYear=jy,t.timeYears=Hy,t.utcMillisecond=hy,t.utcMilliseconds=py,t.utcSecond=my,t.utcSeconds=xy,t.utcMinute=Xy,t.utcMinutes=Vy,t.utcHour=Wy,t.utcHours=$y,t.utcDay=Zy,t.utcDays=Gy,t.utcWeek=Jy,t.utcWeeks=ig,t.utcSunday=Jy,t.utcSundays=ig,t.utcMonday=Qy,t.utcMondays=og,t.utcTuesday=Ky,t.utcTuesdays=ug,t.utcWednesday=tg,t.utcWednesdays=ag,t.utcThursday=ng,t.utcThursdays=cg,t.utcFriday=eg,t.utcFridays=sg,t.utcSaturday=rg,t.utcSaturdays=fg,t.utcMonth=lg,t.utcMonths=hg,t.utcYear=pg,t.utcYears=vg,t.formatLocale=ui,t.formatDefaultLocale=ai,t.formatSpecifier=ri,t.precisionFixed=ci,t.precisionPrefix=si,t.precisionRound=fi,t.isoFormat=Ng,t.isoParse=Sg,t.timeFormatLocale=di,t.timeFormatDefaultLocale=co,t.scaleBand=ho,t.scalePoint=vo,t.scaleIdentity=Eo,t.scaleLinear=So,t.scaleLog=Ro,t.scaleOrdinal=lo,t.scaleImplicit=zg,t.scalePow=Oo,t.scaleSqrt=Fo,t.scaleQuantile=Io,t.scaleQuantize=Yo,t.scaleThreshold=Bo,t.scaleTime=Vo,t.scaleUtc=Wo,t.schemeCategory10=Ig,t.schemeCategory20b=Yg,t.schemeCategory20c=Bg,t.schemeCategory20=jg,t.scaleSequential=Jo,t.interpolateCubehelixDefault=Hg,t.interpolateRainbow=Zo,t.interpolateWarm=Xg,t.interpolateCool=Vg,t.interpolateViridis=$g,t.interpolateMagma=Zg,t.interpolateInferno=Gg,t.interpolatePlasma=Jg,t.creator=nu,t.customEvent=fu,t.local=eu,t.matcher=im,t.mouse=pu,t.namespace=Qo,t.namespaces=Kg,t.select=qa,t.selectAll=Ua,t.selection=La,t.selector=vu,t.selectorAll=gu,t.touch=Ra,t.touches=Da,t.window=Vu,t.active=Ic,t.interrupt=ja,t.transition=Rc,t.axisTop=Wc,t.axisRight=$c,t.axisBottom=Zc,t.axisLeft=Gc,t.cluster=is,t.hierarchy=_s,t.pack=Ys,t.packSiblings=Us,t.packEnclose=Ts,t.partition=Ws,t.stratify=Gs,t.tree=uf,t.treemap=sf,t.treemapBinary=ff,t.treemapDice=Vs,t.treemapSlice=af,t.treemapSliceDice=lf,t.treemapSquarify=Pm,t.treemapResquarify=Lm,t.forceCenter=hf,t.forceCollide=yf,t.forceLink=mf,t.forceManyBody=Mf,t.forceSimulation=wf,t.forceX=Tf,t.forceY=kf,t.drag=Uf,t.dragDisable=Ef,t.dragEnable=Af,t.voronoi=vl,t.zoom=kl,t.zoomIdentity=Xm,t.zoomTransform=ml,t.brush=Ol,t.brushX=Rl,t.brushY=Dl,t.brushSelection=Ul,t.geoArea=nh,t.geoBounds=mh,t.geoCentroid=Ch,t.geoCircle=Yh,t.geoClipExtent=Zh,t.geoDistance=np,t.geoGraticule=ip,t.geoInterpolate=op,t.geoLength=tp,t.geoPath=Tp,t.geoAlbers=Hp,t.geoAlbersUsa=Vp,t.geoAzimuthalEqualArea=Zp,t.geoAzimuthalEquidistant=Gp,t.geoConicConformal=ed,t.geoConicEqualArea=jp,t.geoConicEquidistant=ud,t.geoEquirectangular=id,t.geoGnomonic=cd,t.geoProjection=Fp,t.geoProjectionMutator=Ip,t.geoMercator=Qp,t.geoOrthographic=fd,t.geoStereographic=hd,t.geoTransverseMercator=dd,t.geoRotation=Oh,t.geoStream=Gl,t.geoTransform=Lp,Object.defineProperty(t,"__esModule",{value:!0})}); \ No newline at end of file diff --git a/www/wiki/extensions/Mermaid/res/d3/dagre-d3.min.js b/www/wiki/extensions/Mermaid/res/d3/dagre-d3.min.js new file mode 100644 index 00000000..e0fadb50 --- /dev/null +++ b/www/wiki/extensions/Mermaid/res/d3/dagre-d3.min.js @@ -0,0 +1,8113 @@ +(function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.dagreD3=f()}})(function(){var define,module,exports;return function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o0}},{}],14:[function(require,module,exports){module.exports=intersectNode;function intersectNode(node,point){return node.intersect(point)}},{}],15:[function(require,module,exports){var intersectLine=require("./intersect-line");module.exports=intersectPolygon; +/* + * Returns the point ({x, y}) at which the point argument intersects with the + * node argument assuming that it has the shape specified by polygon. + */function intersectPolygon(node,polyPoints,point){var x1=node.x;var y1=node.y;var intersections=[];var minX=Number.POSITIVE_INFINITY,minY=Number.POSITIVE_INFINITY;polyPoints.forEach(function(entry){minX=Math.min(minX,entry.x);minY=Math.min(minY,entry.y)});var left=x1-node.width/2-minX;var top=y1-node.height/2-minY;for(var i=0;i1){ +// More intersections, find the one nearest to edge end point +intersections.sort(function(p,q){var pdx=p.x-point.x,pdy=p.y-point.y,distp=Math.sqrt(pdx*pdx+pdy*pdy),qdx=q.x-point.x,qdy=q.y-point.y,distq=Math.sqrt(qdx*qdx+qdy*qdy);return distpMath.abs(dx)*h){ +// Intersection is top or bottom of rect. +if(dy<0){h=-h}sx=dy===0?0:h*dx/dy;sy=h}else{ +// Intersection is left or right of rect. +if(dx<0){w=-w}sx=w;sy=dx===0?0:w*dy/dx}return{x:x+sx,y:y+sy}}},{}],17:[function(require,module,exports){var util=require("../util");module.exports=addHtmlLabel;function addHtmlLabel(root,node){var fo=root.append("foreignObject").attr("width","100000");var div=fo.append("xhtml:div");div.attr("xmlns","http://www.w3.org/1999/xhtml");var label=node.label;switch(typeof label){case"function":div.insert(label);break;case"object": +// Currently we assume this is a DOM object. +div.insert(function(){return label});break;default:div.html(label)}util.applyStyle(div,node.labelStyle);div.style("display","inline-block"); +// Fix for firefox +div.style("white-space","nowrap");var client=div.node().getBoundingClientRect();fo.attr("width",client.width).attr("height",client.height);return fo}},{"../util":27}],18:[function(require,module,exports){var addTextLabel=require("./add-text-label"),addHtmlLabel=require("./add-html-label"),addSVGLabel=require("./add-svg-label");module.exports=addLabel;function addLabel(root,node,location){var label=node.label;var labelSvg=root.append("g"); +// Allow the label to be a string, a function that returns a DOM element, or +// a DOM element itself. +if(node.labelType==="svg"){addSVGLabel(labelSvg,node)}else if(typeof label!=="string"||node.labelType==="html"){addHtmlLabel(labelSvg,node)}else{addTextLabel(labelSvg,node)}var labelBBox=labelSvg.node().getBBox();var y;switch(location){case"top":y=-node.height/2;break;case"bottom":y=node.height/2-labelBBox.height;break;default:y=-labelBBox.height/2}labelSvg.attr("transform","translate("+-labelBBox.width/2+","+y+")");return labelSvg}},{"./add-html-label":17,"./add-svg-label":19,"./add-text-label":20}],19:[function(require,module,exports){var util=require("../util");module.exports=addSVGLabel;function addSVGLabel(root,node){var domNode=root;domNode.node().appendChild(node.label);util.applyStyle(domNode,node.labelStyle);return domNode}},{"../util":27}],20:[function(require,module,exports){var util=require("../util");module.exports=addTextLabel; +/* + * Attaches a text label to the specified root. Handles escape sequences. + */function addTextLabel(root,node){var domNode=root.append("text");var lines=processEscapeSequences(node.label).split("\n");for(var i=0;i0;--i){entry=buckets[i].dequeue();if(entry){results=results.concat(removeNode(g,buckets,zeroIdx,entry,true));break}}}}return results}function removeNode(g,buckets,zeroIdx,entry,collectPredecessors){var results=collectPredecessors?[]:undefined;_.each(g.inEdges(entry.v),function(edge){var weight=g.edge(edge),uEntry=g.node(edge.v);if(collectPredecessors){results.push({v:edge.v,w:edge.w})}uEntry.out-=weight;assignBucket(buckets,zeroIdx,uEntry)});_.each(g.outEdges(entry.v),function(edge){var weight=g.edge(edge),w=edge.w,wEntry=g.node(w);wEntry["in"]-=weight;assignBucket(buckets,zeroIdx,wEntry)});g.removeNode(entry.v);return results}function buildState(g,weightFn){var fasGraph=new Graph,maxIn=0,maxOut=0;_.each(g.nodes(),function(v){fasGraph.setNode(v,{v:v,in:0,out:0})}); +// Aggregate weights on nodes, but also sum the weights across multi-edges +// into a single edge for the fasGraph. +_.each(g.edges(),function(e){var prevWeight=fasGraph.edge(e.v,e.w)||0,weight=weightFn(e),edgeWeight=prevWeight+weight;fasGraph.setEdge(e.v,e.w,edgeWeight);maxOut=Math.max(maxOut,fasGraph.node(e.v).out+=weight);maxIn=Math.max(maxIn,fasGraph.node(e.w)["in"]+=weight)});var buckets=_.range(maxOut+maxIn+3).map(function(){return new List});var zeroIdx=maxIn+1;_.each(fasGraph.nodes(),function(v){assignBucket(buckets,zeroIdx,fasGraph.node(v))});return{graph:fasGraph,buckets:buckets,zeroIdx:zeroIdx}}function assignBucket(buckets,zeroIdx,entry){if(!entry.out){buckets[0].enqueue(entry)}else if(!entry["in"]){buckets[buckets.length-1].enqueue(entry)}else{buckets[entry.out-entry["in"]+zeroIdx].enqueue(entry)}}},{"./data/list":33,"./graphlib":35,"./lodash":38}],37:[function(require,module,exports){"use strict";var _=require("./lodash"),acyclic=require("./acyclic"),normalize=require("./normalize"),rank=require("./rank"),normalizeRanks=require("./util").normalizeRanks,parentDummyChains=require("./parent-dummy-chains"),removeEmptyRanks=require("./util").removeEmptyRanks,nestingGraph=require("./nesting-graph"),addBorderSegments=require("./add-border-segments"),coordinateSystem=require("./coordinate-system"),order=require("./order"),position=require("./position"),util=require("./util"),Graph=require("./graphlib").Graph;module.exports=layout;function layout(g,opts){var time=opts&&opts.debugTiming?util.time:util.notime;time("layout",function(){var layoutGraph=time(" buildLayoutGraph",function(){return buildLayoutGraph(g)});time(" runLayout",function(){runLayout(layoutGraph,time)});time(" updateInputGraph",function(){updateInputGraph(g,layoutGraph)})})}function runLayout(g,time){time(" makeSpaceForEdgeLabels",function(){makeSpaceForEdgeLabels(g)});time(" removeSelfEdges",function(){removeSelfEdges(g)});time(" acyclic",function(){acyclic.run(g)});time(" nestingGraph.run",function(){nestingGraph.run(g)});time(" rank",function(){rank(util.asNonCompoundGraph(g))});time(" injectEdgeLabelProxies",function(){injectEdgeLabelProxies(g)});time(" removeEmptyRanks",function(){removeEmptyRanks(g)});time(" nestingGraph.cleanup",function(){nestingGraph.cleanup(g)});time(" normalizeRanks",function(){normalizeRanks(g)});time(" assignRankMinMax",function(){assignRankMinMax(g)});time(" removeEdgeLabelProxies",function(){removeEdgeLabelProxies(g)});time(" normalize.run",function(){normalize.run(g)});time(" parentDummyChains",function(){parentDummyChains(g)});time(" addBorderSegments",function(){addBorderSegments(g)});time(" order",function(){order(g)});time(" insertSelfEdges",function(){insertSelfEdges(g)});time(" adjustCoordinateSystem",function(){coordinateSystem.adjust(g)});time(" position",function(){position(g)});time(" positionSelfEdges",function(){positionSelfEdges(g)});time(" removeBorderNodes",function(){removeBorderNodes(g)});time(" normalize.undo",function(){normalize.undo(g)});time(" fixupEdgeLabelCoords",function(){fixupEdgeLabelCoords(g)});time(" undoCoordinateSystem",function(){coordinateSystem.undo(g)});time(" translateGraph",function(){translateGraph(g)});time(" assignNodeIntersects",function(){assignNodeIntersects(g)});time(" reversePoints",function(){reversePointsForReversedEdges(g)});time(" acyclic.undo",function(){acyclic.undo(g)})} +/* + * Copies final layout information from the layout graph back to the input + * graph. This process only copies whitelisted attributes from the layout graph + * to the input graph, so it serves as a good place to determine what + * attributes can influence layout. + */function updateInputGraph(inputGraph,layoutGraph){_.each(inputGraph.nodes(),function(v){var inputLabel=inputGraph.node(v),layoutLabel=layoutGraph.node(v);if(inputLabel){inputLabel.x=layoutLabel.x;inputLabel.y=layoutLabel.y;if(layoutGraph.children(v).length){inputLabel.width=layoutLabel.width;inputLabel.height=layoutLabel.height}}});_.each(inputGraph.edges(),function(e){var inputLabel=inputGraph.edge(e),layoutLabel=layoutGraph.edge(e);inputLabel.points=layoutLabel.points;if(_.has(layoutLabel,"x")){inputLabel.x=layoutLabel.x;inputLabel.y=layoutLabel.y}});inputGraph.graph().width=layoutGraph.graph().width;inputGraph.graph().height=layoutGraph.graph().height}var graphNumAttrs=["nodesep","edgesep","ranksep","marginx","marginy"],graphDefaults={ranksep:50,edgesep:20,nodesep:50,rankdir:"tb"},graphAttrs=["acyclicer","ranker","rankdir","align"],nodeNumAttrs=["width","height"],nodeDefaults={width:0,height:0},edgeNumAttrs=["minlen","weight","width","height","labeloffset"],edgeDefaults={minlen:1,weight:1,width:0,height:0,labeloffset:10,labelpos:"r"},edgeAttrs=["labelpos"]; +/* + * Constructs a new graph from the input graph, which can be used for layout. + * This process copies only whitelisted attributes from the input graph to the + * layout graph. Thus this function serves as a good place to determine what + * attributes can influence layout. + */function buildLayoutGraph(inputGraph){var g=new Graph({multigraph:true,compound:true}),graph=canonicalize(inputGraph.graph());g.setGraph(_.merge({},graphDefaults,selectNumberAttrs(graph,graphNumAttrs),_.pick(graph,graphAttrs)));_.each(inputGraph.nodes(),function(v){var node=canonicalize(inputGraph.node(v));g.setNode(v,_.defaults(selectNumberAttrs(node,nodeNumAttrs),nodeDefaults));g.setParent(v,inputGraph.parent(v))});_.each(inputGraph.edges(),function(e){var edge=canonicalize(inputGraph.edge(e));g.setEdge(e,_.merge({},edgeDefaults,selectNumberAttrs(edge,edgeNumAttrs),_.pick(edge,edgeAttrs)))});return g} +/* + * This idea comes from the Gansner paper: to account for edge labels in our + * layout we split each rank in half by doubling minlen and halving ranksep. + * Then we can place labels at these mid-points between nodes. + * + * We also add some minimal padding to the width to push the label for the edge + * away from the edge itself a bit. + */function makeSpaceForEdgeLabels(g){var graph=g.graph();graph.ranksep/=2;_.each(g.edges(),function(e){var edge=g.edge(e);edge.minlen*=2;if(edge.labelpos.toLowerCase()!=="c"){if(graph.rankdir==="TB"||graph.rankdir==="BT"){edge.width+=edge.labeloffset}else{edge.height+=edge.labeloffset}}})} +/* + * Creates temporary dummy nodes that capture the rank in which each edge's + * label is going to, if it has one of non-zero width and height. We do this + * so that we can safely remove empty ranks while preserving balance for the + * label's position. + */function injectEdgeLabelProxies(g){_.each(g.edges(),function(e){var edge=g.edge(e);if(edge.width&&edge.height){var v=g.node(e.v),w=g.node(e.w),label={rank:(w.rank-v.rank)/2+v.rank,e:e};util.addDummyNode(g,"edge-proxy",label,"_ep")}})}function assignRankMinMax(g){var maxRank=0;_.each(g.nodes(),function(v){var node=g.node(v);if(node.borderTop){node.minRank=g.node(node.borderTop).rank;node.maxRank=g.node(node.borderBottom).rank;maxRank=_.max(maxRank,node.maxRank)}});g.graph().maxRank=maxRank}function removeEdgeLabelProxies(g){_.each(g.nodes(),function(v){var node=g.node(v);if(node.dummy==="edge-proxy"){g.edge(node.e).labelRank=node.rank;g.removeNode(v)}})}function translateGraph(g){var minX=Number.POSITIVE_INFINITY,maxX=0,minY=Number.POSITIVE_INFINITY,maxY=0,graphLabel=g.graph(),marginX=graphLabel.marginx||0,marginY=graphLabel.marginy||0;function getExtremes(attrs){var x=attrs.x,y=attrs.y,w=attrs.width,h=attrs.height;minX=Math.min(minX,x-w/2);maxX=Math.max(maxX,x+w/2);minY=Math.min(minY,y-h/2);maxY=Math.max(maxY,y+h/2)}_.each(g.nodes(),function(v){getExtremes(g.node(v))});_.each(g.edges(),function(e){var edge=g.edge(e);if(_.has(edge,"x")){getExtremes(edge)}});minX-=marginX;minY-=marginY;_.each(g.nodes(),function(v){var node=g.node(v);node.x-=minX;node.y-=minY});_.each(g.edges(),function(e){var edge=g.edge(e);_.each(edge.points,function(p){p.x-=minX;p.y-=minY});if(_.has(edge,"x")){edge.x-=minX}if(_.has(edge,"y")){edge.y-=minY}});graphLabel.width=maxX-minX+marginX;graphLabel.height=maxY-minY+marginY}function assignNodeIntersects(g){_.each(g.edges(),function(e){var edge=g.edge(e),nodeV=g.node(e.v),nodeW=g.node(e.w),p1,p2;if(!edge.points){edge.points=[];p1=nodeW;p2=nodeV}else{p1=edge.points[0];p2=edge.points[edge.points.length-1]}edge.points.unshift(util.intersectRect(nodeV,p1));edge.points.push(util.intersectRect(nodeW,p2))})}function fixupEdgeLabelCoords(g){_.each(g.edges(),function(e){var edge=g.edge(e);if(_.has(edge,"x")){if(edge.labelpos==="l"||edge.labelpos==="r"){edge.width-=edge.labeloffset}switch(edge.labelpos){case"l":edge.x-=edge.width/2+edge.labeloffset;break;case"r":edge.x+=edge.width/2+edge.labeloffset;break}}})}function reversePointsForReversedEdges(g){_.each(g.edges(),function(e){var edge=g.edge(e);if(edge.reversed){edge.points.reverse()}})}function removeBorderNodes(g){_.each(g.nodes(),function(v){if(g.children(v).length){var node=g.node(v),t=g.node(node.borderTop),b=g.node(node.borderBottom),l=g.node(_.last(node.borderLeft)),r=g.node(_.last(node.borderRight));node.width=Math.abs(r.x-l.x);node.height=Math.abs(b.y-t.y);node.x=l.x+node.width/2;node.y=t.y+node.height/2}});_.each(g.nodes(),function(v){if(g.node(v).dummy==="border"){g.removeNode(v)}})}function removeSelfEdges(g){_.each(g.edges(),function(e){if(e.v===e.w){var node=g.node(e.v);if(!node.selfEdges){node.selfEdges=[]}node.selfEdges.push({e:e,label:g.edge(e)});g.removeEdge(e)}})}function insertSelfEdges(g){var layers=util.buildLayerMatrix(g);_.each(layers,function(layer){var orderShift=0;_.each(layer,function(v,i){var node=g.node(v);node.order=i+orderShift;_.each(node.selfEdges,function(selfEdge){util.addDummyNode(g,"selfedge",{width:selfEdge.label.width,height:selfEdge.label.height,rank:node.rank,order:i+ ++orderShift,e:selfEdge.e,label:selfEdge.label},"_se")});delete node.selfEdges})})}function positionSelfEdges(g){_.each(g.nodes(),function(v){var node=g.node(v);if(node.dummy==="selfedge"){var selfNode=g.node(node.e.v),x=selfNode.x+selfNode.width/2,y=selfNode.y,dx=node.x-x,dy=selfNode.height/2;g.setEdge(node.e,node.label);g.removeNode(v);node.label.points=[{x:x+2*dx/3,y:y-dy},{x:x+5*dx/6,y:y-dy},{x:x+dx,y:y},{x:x+5*dx/6,y:y+dy},{x:x+2*dx/3,y:y+dy}];node.label.x=node.x;node.label.y=node.y}})}function selectNumberAttrs(obj,attrs){return _.mapValues(_.pick(obj,attrs),Number)}function canonicalize(attrs){var newAttrs={};_.each(attrs,function(v,k){newAttrs[k.toLowerCase()]=v});return newAttrs}},{"./acyclic":30,"./add-border-segments":31,"./coordinate-system":32,"./graphlib":35,"./lodash":38,"./nesting-graph":39,"./normalize":40,"./order":45,"./parent-dummy-chains":50,"./position":52,"./rank":54,"./util":57}],38:[function(require,module,exports){ +/* global window */ +var lodash;if(typeof require==="function"){try{lodash=require("lodash")}catch(e){}}if(!lodash){lodash=window._}module.exports=lodash},{lodash:79}],39:[function(require,module,exports){var _=require("./lodash"),util=require("./util");module.exports={run:run,cleanup:cleanup}; +/* + * A nesting graph creates dummy nodes for the tops and bottoms of subgraphs, + * adds appropriate edges to ensure that all cluster nodes are placed between + * these boundries, and ensures that the graph is connected. + * + * In addition we ensure, through the use of the minlen property, that nodes + * and subgraph border nodes to not end up on the same rank. + * + * Preconditions: + * + * 1. Input graph is a DAG + * 2. Nodes in the input graph has a minlen attribute + * + * Postconditions: + * + * 1. Input graph is connected. + * 2. Dummy nodes are added for the tops and bottoms of subgraphs. + * 3. The minlen attribute for nodes is adjusted to ensure nodes do not + * get placed on the same rank as subgraph border nodes. + * + * The nesting graph idea comes from Sander, "Layout of Compound Directed + * Graphs." + */function run(g){var root=util.addDummyNode(g,"root",{},"_root"),depths=treeDepths(g),height=_.max(depths)-1,nodeSep=2*height+1;g.graph().nestingRoot=root; +// Multiply minlen by nodeSep to align nodes on non-border ranks. +_.each(g.edges(),function(e){g.edge(e).minlen*=nodeSep}); +// Calculate a weight that is sufficient to keep subgraphs vertically compact +var weight=sumWeights(g)+1; +// Create border nodes and link them up +_.each(g.children(),function(child){dfs(g,root,nodeSep,weight,height,depths,child)}); +// Save the multiplier for node layers for later removal of empty border +// layers. +g.graph().nodeRankFactor=nodeSep}function dfs(g,root,nodeSep,weight,height,depths,v){var children=g.children(v);if(!children.length){if(v!==root){g.setEdge(root,v,{weight:0,minlen:nodeSep})}return}var top=util.addBorderNode(g,"_bt"),bottom=util.addBorderNode(g,"_bb"),label=g.node(v);g.setParent(top,v);label.borderTop=top;g.setParent(bottom,v);label.borderBottom=bottom;_.each(children,function(child){dfs(g,root,nodeSep,weight,height,depths,child);var childNode=g.node(child),childTop=childNode.borderTop?childNode.borderTop:child,childBottom=childNode.borderBottom?childNode.borderBottom:child,thisWeight=childNode.borderTop?weight:2*weight,minlen=childTop!==childBottom?1:height-depths[v]+1;g.setEdge(top,childTop,{weight:thisWeight,minlen:minlen,nestingEdge:true});g.setEdge(childBottom,bottom,{weight:thisWeight,minlen:minlen,nestingEdge:true})});if(!g.parent(v)){g.setEdge(root,top,{weight:0,minlen:height+depths[v]})}}function treeDepths(g){var depths={};function dfs(v,depth){var children=g.children(v);if(children&&children.length){_.each(children,function(child){dfs(child,depth+1)})}depths[v]=depth}_.each(g.children(),function(v){dfs(v,1)});return depths}function sumWeights(g){return _.reduce(g.edges(),function(acc,e){return acc+g.edge(e).weight},0)}function cleanup(g){var graphLabel=g.graph();g.removeNode(graphLabel.nestingRoot);delete graphLabel.nestingRoot;_.each(g.edges(),function(e){var edge=g.edge(e);if(edge.nestingEdge){g.removeEdge(e)}})}},{"./lodash":38,"./util":57}],40:[function(require,module,exports){"use strict";var _=require("./lodash"),util=require("./util");module.exports={run:run,undo:undo}; +/* + * Breaks any long edges in the graph into short segments that span 1 layer + * each. This operation is undoable with the denormalize function. + * + * Pre-conditions: + * + * 1. The input graph is a DAG. + * 2. Each node in the graph has a "rank" property. + * + * Post-condition: + * + * 1. All edges in the graph have a length of 1. + * 2. Dummy nodes are added where edges have been split into segments. + * 3. The graph is augmented with a "dummyChains" attribute which contains + * the first dummy in each chain of dummy nodes produced. + */function run(g){g.graph().dummyChains=[];_.each(g.edges(),function(edge){normalizeEdge(g,edge)})}function normalizeEdge(g,e){var v=e.v,vRank=g.node(v).rank,w=e.w,wRank=g.node(w).rank,name=e.name,edgeLabel=g.edge(e),labelRank=edgeLabel.labelRank;if(wRank===vRank+1)return;g.removeEdge(e);var dummy,attrs,i;for(i=0,++vRank;vRank0){if(index%2){weightSum+=tree[index+1]}index=index-1>>1;tree[index]+=entry.weight}cc+=entry.weight*weightSum}));return cc}},{"../lodash":38}],45:[function(require,module,exports){"use strict";var _=require("../lodash"),initOrder=require("./init-order"),crossCount=require("./cross-count"),sortSubgraph=require("./sort-subgraph"),buildLayerGraph=require("./build-layer-graph"),addSubgraphConstraints=require("./add-subgraph-constraints"),Graph=require("../graphlib").Graph,util=require("../util");module.exports=order; +/* + * Applies heuristics to minimize edge crossings in the graph and sets the best + * order solution as an order attribute on each node. + * + * Pre-conditions: + * + * 1. Graph must be DAG + * 2. Graph nodes must be objects with a "rank" attribute + * 3. Graph edges must have the "weight" attribute + * + * Post-conditions: + * + * 1. Graph nodes will have an "order" attribute based on the results of the + * algorithm. + */function order(g){var maxRank=util.maxRank(g),downLayerGraphs=buildLayerGraphs(g,_.range(1,maxRank+1),"inEdges"),upLayerGraphs=buildLayerGraphs(g,_.range(maxRank-1,-1,-1),"outEdges");var layering=initOrder(g);assignOrder(g,layering);var bestCC=Number.POSITIVE_INFINITY,best;for(var i=0,lastBest=0;lastBest<4;++i,++lastBest){sweepLayerGraphs(i%2?downLayerGraphs:upLayerGraphs,i%4>=2);layering=util.buildLayerMatrix(g);var cc=crossCount(g,layering);if(cc=vEntry.barycenter){mergeEntries(vEntry,uEntry)}}}function handleOut(vEntry){return function(wEntry){wEntry["in"].push(vEntry);if(--wEntry.indegree===0){sourceSet.push(wEntry)}}}while(sourceSet.length){var entry=sourceSet.pop();entries.push(entry);_.each(entry["in"].reverse(),handleIn(entry));_.each(entry.out,handleOut(entry))}return _.chain(entries).filter(function(entry){return!entry.merged}).map(function(entry){return _.pick(entry,["vs","i","barycenter","weight"])}).value()}function mergeEntries(target,source){var sum=0,weight=0;if(target.weight){sum+=target.barycenter*target.weight;weight+=target.weight}if(source.weight){sum+=source.barycenter*source.weight;weight+=source.weight}target.vs=source.vs.concat(target.vs);target.barycenter=sum/weight;target.weight=weight;target.i=Math.min(source.i,target.i);source.merged=true}},{"../lodash":38}],48:[function(require,module,exports){var _=require("../lodash"),barycenter=require("./barycenter"),resolveConflicts=require("./resolve-conflicts"),sort=require("./sort");module.exports=sortSubgraph;function sortSubgraph(g,v,cg,biasRight){var movable=g.children(v),node=g.node(v),bl=node?node.borderLeft:undefined,br=node?node.borderRight:undefined,subgraphs={};if(bl){movable=_.filter(movable,function(w){return w!==bl&&w!==br})}var barycenters=barycenter(g,movable);_.each(barycenters,function(entry){if(g.children(entry.v).length){var subgraphResult=sortSubgraph(g,entry.v,cg,biasRight);subgraphs[entry.v]=subgraphResult;if(_.has(subgraphResult,"barycenter")){mergeBarycenters(entry,subgraphResult)}}});var entries=resolveConflicts(barycenters,cg);expandSubgraphs(entries,subgraphs);var result=sort(entries,biasRight);if(bl){result.vs=_.flatten([bl,result.vs,br],true);if(g.predecessors(bl).length){var blPred=g.node(g.predecessors(bl)[0]),brPred=g.node(g.predecessors(br)[0]);if(!_.has(result,"barycenter")){result.barycenter=0;result.weight=0}result.barycenter=(result.barycenter*result.weight+blPred.order+brPred.order)/(result.weight+2);result.weight+=2}}return result}function expandSubgraphs(entries,subgraphs){_.each(entries,function(entry){entry.vs=_.flatten(entry.vs.map(function(v){if(subgraphs[v]){return subgraphs[v].vs}return v}),true)})}function mergeBarycenters(target,other){if(!_.isUndefined(target.barycenter)){target.barycenter=(target.barycenter*target.weight+other.barycenter*other.weight)/(target.weight+other.weight);target.weight+=other.weight}else{target.barycenter=other.barycenter;target.weight=other.weight}}},{"../lodash":38,"./barycenter":42,"./resolve-conflicts":47,"./sort":49}],49:[function(require,module,exports){var _=require("../lodash"),util=require("../util");module.exports=sort;function sort(entries,biasRight){var parts=util.partition(entries,function(entry){return _.has(entry,"barycenter")});var sortable=parts.lhs,unsortable=_.sortBy(parts.rhs,function(entry){return-entry.i}),vs=[],sum=0,weight=0,vsIndex=0;sortable.sort(compareWithBias(!!biasRight));vsIndex=consumeUnsortable(vs,unsortable,vsIndex);_.each(sortable,function(entry){vsIndex+=entry.vs.length;vs.push(entry.vs);sum+=entry.barycenter*entry.weight;weight+=entry.weight;vsIndex=consumeUnsortable(vs,unsortable,vsIndex)});var result={vs:_.flatten(vs,true)};if(weight){result.barycenter=sum/weight;result.weight=weight}return result}function consumeUnsortable(vs,unsortable,index){var last;while(unsortable.length&&(last=_.last(unsortable)).i<=index){unsortable.pop();vs.push(last.vs);index++}return index}function compareWithBias(bias){return function(entryV,entryW){if(entryV.barycenterentryW.barycenter){return 1}return!bias?entryV.i-entryW.i:entryW.i-entryV.i}}},{"../lodash":38,"../util":57}],50:[function(require,module,exports){var _=require("./lodash");module.exports=parentDummyChains;function parentDummyChains(g){var postorderNums=postorder(g);_.each(g.graph().dummyChains,function(v){var node=g.node(v),edgeObj=node.edgeObj,pathData=findPath(g,postorderNums,edgeObj.v,edgeObj.w),path=pathData.path,lca=pathData.lca,pathIdx=0,pathV=path[pathIdx],ascending=true;while(v!==edgeObj.w){node=g.node(v);if(ascending){while((pathV=path[pathIdx])!==lca&&g.node(pathV).maxRanklow||lim>postorderNums[parent].lim));lca=parent; +// Traverse from w to LCA +parent=w;while((parent=g.parent(parent))!==lca){wPath.push(parent)}return{path:vPath.concat(wPath.reverse()),lca:lca}}function postorder(g){var result={},lim=0;function dfs(v){var low=lim;_.each(g.children(v),dfs);result[v]={low:low,lim:lim++}}_.each(g.children(),dfs);return result}},{"./lodash":38}],51:[function(require,module,exports){"use strict";var _=require("../lodash"),Graph=require("../graphlib").Graph,util=require("../util"); +/* + * This module provides coordinate assignment based on Brandes and Köpf, "Fast + * and Simple Horizontal Coordinate Assignment." + */module.exports={positionX:positionX,findType1Conflicts:findType1Conflicts,findType2Conflicts:findType2Conflicts,addConflict:addConflict,hasConflict:hasConflict,verticalAlignment:verticalAlignment,horizontalCompaction:horizontalCompaction,alignCoordinates:alignCoordinates,findSmallestWidthAlignment:findSmallestWidthAlignment,balance:balance}; +/* + * Marks all edges in the graph with a type-1 conflict with the "type1Conflict" + * property. A type-1 conflict is one where a non-inner segment crosses an + * inner segment. An inner segment is an edge with both incident nodes marked + * with the "dummy" property. + * + * This algorithm scans layer by layer, starting with the second, for type-1 + * conflicts between the current layer and the previous layer. For each layer + * it scans the nodes from left to right until it reaches one that is incident + * on an inner segment. It then scans predecessors to determine if they have + * edges that cross that inner segment. At the end a final scan is done for all + * nodes on the current rank to see if they cross the last visited inner + * segment. + * + * This algorithm (safely) assumes that a dummy node will only be incident on a + * single node in the layers being scanned. + */function findType1Conflicts(g,layering){var conflicts={};function visitLayer(prevLayer,layer){var +// last visited node in the previous layer that is incident on an inner +// segment. +k0=0, +// Tracks the last node in this layer scanned for crossings with a type-1 +// segment. +scanPos=0,prevLayerLength=prevLayer.length,lastNode=_.last(layer);_.each(layer,function(v,i){var w=findOtherInnerSegmentNode(g,v),k1=w?g.node(w).order:prevLayerLength;if(w||v===lastNode){_.each(layer.slice(scanPos,i+1),function(scanNode){_.each(g.predecessors(scanNode),function(u){var uLabel=g.node(u),uPos=uLabel.order;if((uPosnextNorthBorder)){addConflict(conflicts,u,v)}})}})}function visitLayer(north,south){var prevNorthPos=-1,nextNorthPos,southPos=0;_.each(south,function(v,southLookahead){if(g.node(v).dummy==="border"){var predecessors=g.predecessors(v);if(predecessors.length){nextNorthPos=g.node(predecessors[0]).order;scan(south,southPos,southLookahead,prevNorthPos,nextNorthPos);southPos=southLookahead;prevNorthPos=nextNorthPos}}scan(south,southPos,south.length,nextNorthPos,north.length)});return south}_.reduce(layering,visitLayer);return conflicts}function findOtherInnerSegmentNode(g,v){if(g.node(v).dummy){return _.find(g.predecessors(v),function(u){return g.node(u).dummy})}}function addConflict(conflicts,v,w){if(v>w){var tmp=v;v=w;w=tmp}var conflictsV=conflicts[v];if(!conflictsV){conflicts[v]=conflictsV={}}conflictsV[w]=true}function hasConflict(conflicts,v,w){if(v>w){var tmp=v;v=w;w=tmp}return _.has(conflicts[v],w)} +/* + * Try to align nodes into vertical "blocks" where possible. This algorithm + * attempts to align a node with one of its median neighbors. If the edge + * connecting a neighbor is a type-1 conflict then we ignore that possibility. + * If a previous node has already formed a block with a node after the node + * we're trying to form a block with, we also ignore that possibility - our + * blocks would be split in that scenario. + */function verticalAlignment(g,layering,conflicts,neighborFn){var root={},align={},pos={}; +// We cache the position here based on the layering because the graph and +// layering may be out of sync. The layering matrix is manipulated to +// generate different extreme alignments. +_.each(layering,function(layer){_.each(layer,function(v,order){root[v]=v;align[v]=v;pos[v]=order})});_.each(layering,function(layer){var prevIdx=-1;_.each(layer,function(v){var ws=neighborFn(v);if(ws.length){ws=_.sortBy(ws,function(w){return pos[w]});var mp=(ws.length-1)/2;for(var i=Math.floor(mp),il=Math.ceil(mp);i<=il;++i){var w=ws[i];if(align[v]===v&&prevIdxwLabel.lim){tailLabel=wLabel;flip=true}var candidates=_.filter(g.edges(),function(edge){return flip===isDescendant(t,t.node(edge.v),tailLabel)&&flip!==isDescendant(t,t.node(edge.w),tailLabel)});return _.min(candidates,function(edge){return slack(g,edge)})}function exchangeEdges(t,g,e,f){var v=e.v,w=e.w;t.removeEdge(v,w);t.setEdge(f.v,f.w,{});initLowLimValues(t);initCutValues(t,g);updateRanks(t,g)}function updateRanks(t,g){var root=_.find(t.nodes(),function(v){return!g.node(v).parent}),vs=preorder(t,root);vs=vs.slice(1);_.each(vs,function(v){var parent=t.node(v).parent,edge=g.edge(v,parent),flipped=false;if(!edge){edge=g.edge(parent,v);flipped=true}g.node(v).rank=g.node(parent).rank+(flipped?edge.minlen:-edge.minlen)})} +/* + * Returns true if the edge is in the tree. + */function isTreeEdge(tree,u,v){return tree.hasEdge(u,v)} +/* + * Returns true if the specified node is descendant of the root node per the + * assigned low and lim attributes in the tree. + */function isDescendant(tree,vLabel,rootLabel){return rootLabel.low<=vLabel.lim&&vLabel.lim<=rootLabel.lim}},{"../graphlib":35,"../lodash":38,"../util":57,"./feasible-tree":53,"./util":56}],56:[function(require,module,exports){"use strict";var _=require("../lodash");module.exports={longestPath:longestPath,slack:slack}; +/* + * Initializes ranks for the input graph using the longest path algorithm. This + * algorithm scales well and is fast in practice, it yields rather poor + * solutions. Nodes are pushed to the lowest layer possible, leaving the bottom + * ranks wide and leaving edges longer than necessary. However, due to its + * speed, this algorithm is good for getting an initial ranking that can be fed + * into other algorithms. + * + * This algorithm does not normalize layers because it will be used by other + * algorithms in most cases. If using this algorithm directly, be sure to + * run normalize at the end. + * + * Pre-conditions: + * + * 1. Input graph is a DAG. + * 2. Input graph node labels can be assigned properties. + * + * Post-conditions: + * + * 1. Each node will be assign an (unnormalized) "rank" property. + */function longestPath(g){var visited={};function dfs(v){var label=g.node(v);if(_.has(visited,v)){return label.rank}visited[v]=true;var rank=_.min(_.map(g.outEdges(v),function(e){return dfs(e.w)-g.edge(e).minlen}));if(rank===Number.POSITIVE_INFINITY){rank=0}return label.rank=rank}_.each(g.sources(),dfs)} +/* + * Returns the amount of slack for the given edge. The slack is defined as the + * difference between the length of the edge and its minimum length. + */function slack(g,e){return g.node(e.w).rank-g.node(e.v).rank-g.edge(e).minlen}},{"../lodash":38}],57:[function(require,module,exports){"use strict";var _=require("./lodash"),Graph=require("./graphlib").Graph;module.exports={addDummyNode:addDummyNode,simplify:simplify,asNonCompoundGraph:asNonCompoundGraph,successorWeights:successorWeights,predecessorWeights:predecessorWeights,intersectRect:intersectRect,buildLayerMatrix:buildLayerMatrix,normalizeRanks:normalizeRanks,removeEmptyRanks:removeEmptyRanks,addBorderNode:addBorderNode,maxRank:maxRank,partition:partition,time:time,notime:notime}; +/* + * Adds a dummy node to the graph and return v. + */function addDummyNode(g,type,attrs,name){var v;do{v=_.uniqueId(name)}while(g.hasNode(v));attrs.dummy=type;g.setNode(v,attrs);return v} +/* + * Returns a new graph with only simple edges. Handles aggregation of data + * associated with multi-edges. + */function simplify(g){var simplified=(new Graph).setGraph(g.graph());_.each(g.nodes(),function(v){simplified.setNode(v,g.node(v))});_.each(g.edges(),function(e){var simpleLabel=simplified.edge(e.v,e.w)||{weight:0,minlen:1},label=g.edge(e);simplified.setEdge(e.v,e.w,{weight:simpleLabel.weight+label.weight,minlen:Math.max(simpleLabel.minlen,label.minlen)})});return simplified}function asNonCompoundGraph(g){var simplified=new Graph({multigraph:g.isMultigraph()}).setGraph(g.graph());_.each(g.nodes(),function(v){if(!g.children(v).length){simplified.setNode(v,g.node(v))}});_.each(g.edges(),function(e){simplified.setEdge(e,g.edge(e))});return simplified}function successorWeights(g){var weightMap=_.map(g.nodes(),function(v){var sucs={};_.each(g.outEdges(v),function(e){sucs[e.w]=(sucs[e.w]||0)+g.edge(e).weight});return sucs});return _.zipObject(g.nodes(),weightMap)}function predecessorWeights(g){var weightMap=_.map(g.nodes(),function(v){var preds={};_.each(g.inEdges(v),function(e){preds[e.v]=(preds[e.v]||0)+g.edge(e).weight});return preds});return _.zipObject(g.nodes(),weightMap)} +/* + * Finds where a line starting at point ({x, y}) would intersect a rectangle + * ({x, y, width, height}) if it were pointing at the rectangle's center. + */function intersectRect(rect,point){var x=rect.x;var y=rect.y; +// Rectangle intersection algorithm from: +// http://math.stackexchange.com/questions/108113/find-edge-between-two-boxes +var dx=point.x-x;var dy=point.y-y;var w=rect.width/2;var h=rect.height/2;if(!dx&&!dy){throw new Error("Not possible to find intersection inside of the rectangle")}var sx,sy;if(Math.abs(dy)*w>Math.abs(dx)*h){ +// Intersection is top or bottom of rect. +if(dy<0){h=-h}sx=h*dx/dy;sy=h}else{ +// Intersection is left or right of rect. +if(dx<0){w=-w}sx=w;sy=w*dy/dx}return{x:x+sx,y:y+sy}} +/* + * Given a DAG with each node assigned "rank" and "order" properties, this + * function will produce a matrix with the ids of each node. + */function buildLayerMatrix(g){var layering=_.map(_.range(maxRank(g)+1),function(){return[]});_.each(g.nodes(),function(v){var node=g.node(v),rank=node.rank;if(!_.isUndefined(rank)){layering[rank][node.order]=v}});return layering} +/* + * Adjusts the ranks for all nodes in the graph such that all nodes v have + * rank(v) >= 0 and at least one node w has rank(w) = 0. + */function normalizeRanks(g){var min=_.min(_.map(g.nodes(),function(v){return g.node(v).rank}));_.each(g.nodes(),function(v){var node=g.node(v);if(_.has(node,"rank")){node.rank-=min}})}function removeEmptyRanks(g){ +// Ranks may not start at 0, so we need to offset them +var offset=_.min(_.map(g.nodes(),function(v){return g.node(v).rank}));var layers=[];_.each(g.nodes(),function(v){var rank=g.node(v).rank-offset;if(!layers[rank]){layers[rank]=[]}layers[rank].push(v)});var delta=0,nodeRankFactor=g.graph().nodeRankFactor;_.each(layers,function(vs,i){if(_.isUndefined(vs)&&i%nodeRankFactor!==0){--delta}else if(delta){_.each(vs,function(v){g.node(v).rank+=delta})}})}function addBorderNode(g,prefix,rank,order){var node={width:0,height:0};if(arguments.length>=4){node.rank=rank;node.order=order}return addDummyNode(g,"border",node,prefix)}function maxRank(g){return _.max(_.map(g.nodes(),function(v){var rank=g.node(v).rank;if(!_.isUndefined(rank)){return rank}}))} +/* + * Partition a collection into two groups: `lhs` and `rhs`. If the supplied + * function returns true for an entry it goes into `lhs`. Otherwise it goes + * into `rhs. + */function partition(collection,fn){var result={lhs:[],rhs:[]};_.each(collection,function(value){if(fn(value)){result.lhs.push(value)}else{result.rhs.push(value)}});return result} +/* + * Returns a new function that wraps `fn` with a timer. The wrapper logs the + * time it takes to execute the function. + */function time(name,fn){var start=_.now();try{return fn()}finally{console.log(name+" time: "+(_.now()-start)+"ms")}}function notime(name,fn){return fn()}},{"./graphlib":35,"./lodash":38}],58:[function(require,module,exports){module.exports="0.7.4"},{}],59:[function(require,module,exports){ +/** + * Copyright (c) 2014, Chris Pettitt + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. Neither the name of the copyright holder 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 HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +var lib=require("./lib");module.exports={Graph:lib.Graph,json:require("./lib/json"),alg:require("./lib/alg"),version:lib.version}},{"./lib":75,"./lib/alg":66,"./lib/json":76}],60:[function(require,module,exports){var _=require("../lodash");module.exports=components;function components(g){var visited={},cmpts=[],cmpt;function dfs(v){if(_.has(visited,v))return;visited[v]=true;cmpt.push(v);_.each(g.successors(v),dfs);_.each(g.predecessors(v),dfs)}_.each(g.nodes(),function(v){cmpt=[];dfs(v);if(cmpt.length){cmpts.push(cmpt)}});return cmpts}},{"../lodash":77}],61:[function(require,module,exports){var _=require("../lodash");module.exports=dfs; +/* + * A helper that preforms a pre- or post-order traversal on the input graph + * and returns the nodes in the order they were visited. This algorithm treats + * the input as undirected. + * + * Order must be one of "pre" or "post". + */function dfs(g,vs,order){if(!_.isArray(vs)){vs=[vs]}var acc=[],visited={};_.each(vs,function(v){if(!g.hasNode(v)){throw new Error("Graph does not have node: "+v)}doDfs(g,v,order==="post",visited,acc)});return acc}function doDfs(g,v,postorder,visited,acc){if(!_.has(visited,v)){visited[v]=true;if(!postorder){acc.push(v)}_.each(g.neighbors(v),function(w){doDfs(g,w,postorder,visited,acc)});if(postorder){acc.push(v)}}}},{"../lodash":77}],62:[function(require,module,exports){var dijkstra=require("./dijkstra"),_=require("../lodash");module.exports=dijkstraAll;function dijkstraAll(g,weightFunc,edgeFunc){return _.transform(g.nodes(),function(acc,v){acc[v]=dijkstra(g,v,weightFunc,edgeFunc)},{})}},{"../lodash":77,"./dijkstra":63}],63:[function(require,module,exports){var _=require("../lodash"),PriorityQueue=require("../data/priority-queue");module.exports=dijkstra;var DEFAULT_WEIGHT_FUNC=_.constant(1);function dijkstra(g,source,weightFn,edgeFn){return runDijkstra(g,String(source),weightFn||DEFAULT_WEIGHT_FUNC,edgeFn||function(v){return g.outEdges(v)})}function runDijkstra(g,source,weightFn,edgeFn){var results={},pq=new PriorityQueue,v,vEntry;var updateNeighbors=function(edge){var w=edge.v!==v?edge.v:edge.w,wEntry=results[w],weight=weightFn(edge),distance=vEntry.distance+weight;if(weight<0){throw new Error("dijkstra does not allow negative edge weights. "+"Bad edge: "+edge+" Weight: "+weight)}if(distance0){v=pq.removeMin();vEntry=results[v];if(vEntry.distance===Number.POSITIVE_INFINITY){break}edgeFn(v).forEach(updateNeighbors)}return results}},{"../data/priority-queue":73,"../lodash":77}],64:[function(require,module,exports){var _=require("../lodash"),tarjan=require("./tarjan");module.exports=findCycles;function findCycles(g){return _.filter(tarjan(g),function(cmpt){return cmpt.length>1||cmpt.length===1&&g.hasEdge(cmpt[0],cmpt[0])})}},{"../lodash":77,"./tarjan":71}],65:[function(require,module,exports){var _=require("../lodash");module.exports=floydWarshall;var DEFAULT_WEIGHT_FUNC=_.constant(1);function floydWarshall(g,weightFn,edgeFn){return runFloydWarshall(g,weightFn||DEFAULT_WEIGHT_FUNC,edgeFn||function(v){return g.outEdges(v)})}function runFloydWarshall(g,weightFn,edgeFn){var results={},nodes=g.nodes();nodes.forEach(function(v){results[v]={};results[v][v]={distance:0};nodes.forEach(function(w){if(v!==w){results[v][w]={distance:Number.POSITIVE_INFINITY}}});edgeFn(v).forEach(function(edge){var w=edge.v===v?edge.w:edge.v,d=weightFn(edge);results[v][w]={distance:d,predecessor:v}})});nodes.forEach(function(k){var rowK=results[k];nodes.forEach(function(i){var rowI=results[i];nodes.forEach(function(j){var ik=rowI[k];var kj=rowK[j];var ij=rowI[j];var altDistance=ik.distance+kj.distance;if(altDistance0){v=pq.removeMin();if(_.has(parents,v)){result.setEdge(v,parents[v])}else if(init){throw new Error("Input graph is not connected: "+g)}else{init=true}g.nodeEdges(v).forEach(updateNeighbors)}return result}},{"../data/priority-queue":73,"../graph":74,"../lodash":77}],71:[function(require,module,exports){var _=require("../lodash");module.exports=tarjan;function tarjan(g){var index=0,stack=[],visited={},// node id -> { onStack, lowlink, index } +results=[];function dfs(v){var entry=visited[v]={onStack:true,lowlink:index,index:index++};stack.push(v);g.successors(v).forEach(function(w){if(!_.has(visited,w)){dfs(w);entry.lowlink=Math.min(entry.lowlink,visited[w].lowlink)}else if(visited[w].onStack){entry.lowlink=Math.min(entry.lowlink,visited[w].index)}});if(entry.lowlink===entry.index){var cmpt=[],w;do{w=stack.pop();visited[w].onStack=false;cmpt.push(w)}while(v!==w);results.push(cmpt)}}g.nodes().forEach(function(v){if(!_.has(visited,v)){dfs(v)}});return results}},{"../lodash":77}],72:[function(require,module,exports){var _=require("../lodash");module.exports=topsort;topsort.CycleException=CycleException;function topsort(g){var visited={},stack={},results=[];function visit(node){if(_.has(stack,node)){throw new CycleException}if(!_.has(visited,node)){stack[node]=true;visited[node]=true;_.each(g.predecessors(node),visit);delete stack[node];results.push(node)}}_.each(g.sinks(),visit);if(_.size(visited)!==g.nodeCount()){throw new CycleException}return results}function CycleException(){}},{"../lodash":77}],73:[function(require,module,exports){var _=require("../lodash");module.exports=PriorityQueue; +/** + * A min-priority queue data structure. This algorithm is derived from Cormen, + * et al., "Introduction to Algorithms". The basic idea of a min-priority + * queue is that you can efficiently (in O(1) time) get the smallest key in + * the queue. Adding and removing elements takes O(log n) time. A key can + * have its priority decreased in O(log n) time. + */function PriorityQueue(){this._arr=[];this._keyIndices={}} +/** + * Returns the number of elements in the queue. Takes `O(1)` time. + */PriorityQueue.prototype.size=function(){return this._arr.length}; +/** + * Returns the keys that are in the queue. Takes `O(n)` time. + */PriorityQueue.prototype.keys=function(){return this._arr.map(function(x){return x.key})}; +/** + * Returns `true` if **key** is in the queue and `false` if not. + */PriorityQueue.prototype.has=function(key){return _.has(this._keyIndices,key)}; +/** + * Returns the priority for **key**. If **key** is not present in the queue + * then this function returns `undefined`. Takes `O(1)` time. + * + * @param {Object} key + */PriorityQueue.prototype.priority=function(key){var index=this._keyIndices[key];if(index!==undefined){return this._arr[index].priority}}; +/** + * Returns the key for the minimum element in this queue. If the queue is + * empty this function throws an Error. Takes `O(1)` time. + */PriorityQueue.prototype.min=function(){if(this.size()===0){throw new Error("Queue underflow")}return this._arr[0].key}; +/** + * Inserts a new key into the priority queue. If the key already exists in + * the queue this function returns `false`; otherwise it will return `true`. + * Takes `O(n)` time. + * + * @param {Object} key the key to add + * @param {Number} priority the initial priority for the key + */PriorityQueue.prototype.add=function(key,priority){var keyIndices=this._keyIndices;key=String(key);if(!_.has(keyIndices,key)){var arr=this._arr;var index=arr.length;keyIndices[key]=index;arr.push({key:key,priority:priority});this._decrease(index);return true}return false}; +/** + * Removes and returns the smallest key in the queue. Takes `O(log n)` time. + */PriorityQueue.prototype.removeMin=function(){this._swap(0,this._arr.length-1);var min=this._arr.pop();delete this._keyIndices[min.key];this._heapify(0);return min.key}; +/** + * Decreases the priority for **key** to **priority**. If the new priority is + * greater than the previous priority, this function will throw an Error. + * + * @param {Object} key the key for which to raise priority + * @param {Number} priority the new priority for the key + */PriorityQueue.prototype.decrease=function(key,priority){var index=this._keyIndices[key];if(priority>this._arr[index].priority){throw new Error("New priority is greater than current priority. "+"Key: "+key+" Old: "+this._arr[index].priority+" New: "+priority)}this._arr[index].priority=priority;this._decrease(index)};PriorityQueue.prototype._heapify=function(i){var arr=this._arr;var l=2*i,r=l+1,largest=i;if(l>1;if(arr[parent].priority label +this._nodes={};if(this._isCompound){ +// v -> parent +this._parent={}; +// v -> children +this._children={};this._children[GRAPH_NODE]={}} +// v -> edgeObj +this._in={}; +// u -> v -> Number +this._preds={}; +// v -> edgeObj +this._out={}; +// v -> w -> Number +this._sucs={}; +// e -> edgeObj +this._edgeObjs={}; +// e -> label +this._edgeLabels={}} +/* Number of nodes in the graph. Should only be changed by the implementation. */Graph.prototype._nodeCount=0; +/* Number of edges in the graph. Should only be changed by the implementation. */Graph.prototype._edgeCount=0; +/* === Graph functions ========= */Graph.prototype.isDirected=function(){return this._isDirected};Graph.prototype.isMultigraph=function(){return this._isMultigraph};Graph.prototype.isCompound=function(){return this._isCompound};Graph.prototype.setGraph=function(label){this._label=label;return this};Graph.prototype.graph=function(){return this._label}; +/* === Node functions ========== */Graph.prototype.setDefaultNodeLabel=function(newDefault){if(!_.isFunction(newDefault)){newDefault=_.constant(newDefault)}this._defaultNodeLabelFn=newDefault;return this};Graph.prototype.nodeCount=function(){return this._nodeCount};Graph.prototype.nodes=function(){return _.keys(this._nodes)};Graph.prototype.sources=function(){return _.filter(this.nodes(),function(v){return _.isEmpty(this._in[v])},this)};Graph.prototype.sinks=function(){return _.filter(this.nodes(),function(v){return _.isEmpty(this._out[v])},this)};Graph.prototype.setNodes=function(vs,value){var args=arguments;_.each(vs,function(v){if(args.length>1){this.setNode(v,value)}else{this.setNode(v)}},this);return this};Graph.prototype.setNode=function(v,value){if(_.has(this._nodes,v)){if(arguments.length>1){this._nodes[v]=value}return this}this._nodes[v]=arguments.length>1?value:this._defaultNodeLabelFn(v);if(this._isCompound){this._parent[v]=GRAPH_NODE;this._children[v]={};this._children[GRAPH_NODE][v]=true}this._in[v]={};this._preds[v]={};this._out[v]={};this._sucs[v]={};++this._nodeCount;return this};Graph.prototype.node=function(v){return this._nodes[v]};Graph.prototype.hasNode=function(v){return _.has(this._nodes,v)};Graph.prototype.removeNode=function(v){var self=this;if(_.has(this._nodes,v)){var removeEdge=function(e){self.removeEdge(self._edgeObjs[e])};delete this._nodes[v];if(this._isCompound){this._removeFromParentsChildList(v);delete this._parent[v];_.each(this.children(v),function(child){this.setParent(child)},this);delete this._children[v]}_.each(_.keys(this._in[v]),removeEdge);delete this._in[v];delete this._preds[v];_.each(_.keys(this._out[v]),removeEdge);delete this._out[v];delete this._sucs[v];--this._nodeCount}return this};Graph.prototype.setParent=function(v,parent){if(!this._isCompound){throw new Error("Cannot set parent in a non-compound graph")}if(_.isUndefined(parent)){parent=GRAPH_NODE}else{ +// Coerce parent to string +parent+="";for(var ancestor=parent;!_.isUndefined(ancestor);ancestor=this.parent(ancestor)){if(ancestor===v){throw new Error("Setting "+parent+" as parent of "+v+" would create create a cycle")}}this.setNode(parent)}this.setNode(v);this._removeFromParentsChildList(v);this._parent[v]=parent;this._children[parent][v]=true;return this};Graph.prototype._removeFromParentsChildList=function(v){delete this._children[this._parent[v]][v]};Graph.prototype.parent=function(v){if(this._isCompound){var parent=this._parent[v];if(parent!==GRAPH_NODE){return parent}}};Graph.prototype.children=function(v){if(_.isUndefined(v)){v=GRAPH_NODE}if(this._isCompound){var children=this._children[v];if(children){return _.keys(children)}}else if(v===GRAPH_NODE){return this.nodes()}else if(this.hasNode(v)){return[]}};Graph.prototype.predecessors=function(v){var predsV=this._preds[v];if(predsV){return _.keys(predsV)}};Graph.prototype.successors=function(v){var sucsV=this._sucs[v];if(sucsV){return _.keys(sucsV)}};Graph.prototype.neighbors=function(v){var preds=this.predecessors(v);if(preds){return _.union(preds,this.successors(v))}};Graph.prototype.filterNodes=function(filter){var copy=new this.constructor({directed:this._isDirected,multigraph:this._isMultigraph,compound:this._isCompound});copy.setGraph(this.graph());_.each(this._nodes,function(value,v){if(filter(v)){copy.setNode(v,value)}},this);_.each(this._edgeObjs,function(e){if(copy.hasNode(e.v)&©.hasNode(e.w)){copy.setEdge(e,this.edge(e))}},this);var self=this;var parents={};function findParent(v){var parent=self.parent(v);if(parent===undefined||copy.hasNode(parent)){parents[v]=parent;return parent}else if(parent in parents){return parents[parent]}else{return findParent(parent)}}if(this._isCompound){_.each(copy.nodes(),function(v){copy.setParent(v,findParent(v))})}return copy}; +/* === Edge functions ========== */Graph.prototype.setDefaultEdgeLabel=function(newDefault){if(!_.isFunction(newDefault)){newDefault=_.constant(newDefault)}this._defaultEdgeLabelFn=newDefault;return this};Graph.prototype.edgeCount=function(){return this._edgeCount};Graph.prototype.edges=function(){return _.values(this._edgeObjs)};Graph.prototype.setPath=function(vs,value){var self=this,args=arguments;_.reduce(vs,function(v,w){if(args.length>1){self.setEdge(v,w,value)}else{self.setEdge(v,w)}return w});return this}; +/* + * setEdge(v, w, [value, [name]]) + * setEdge({ v, w, [name] }, [value]) + */Graph.prototype.setEdge=function(){var v,w,name,value,valueSpecified=false,arg0=arguments[0];if(typeof arg0==="object"&&arg0!==null&&"v"in arg0){v=arg0.v;w=arg0.w;name=arg0.name;if(arguments.length===2){value=arguments[1];valueSpecified=true}}else{v=arg0;w=arguments[1];name=arguments[3];if(arguments.length>2){value=arguments[2];valueSpecified=true}}v=""+v;w=""+w;if(!_.isUndefined(name)){name=""+name}var e=edgeArgsToId(this._isDirected,v,w,name);if(_.has(this._edgeLabels,e)){if(valueSpecified){this._edgeLabels[e]=value}return this}if(!_.isUndefined(name)&&!this._isMultigraph){throw new Error("Cannot set a named edge when isMultigraph = false")} +// It didn't exist, so we need to create it. +// First ensure the nodes exist. +this.setNode(v);this.setNode(w);this._edgeLabels[e]=valueSpecified?value:this._defaultEdgeLabelFn(v,w,name);var edgeObj=edgeArgsToObj(this._isDirected,v,w,name); +// Ensure we add undirected edges in a consistent way. +v=edgeObj.v;w=edgeObj.w;Object.freeze(edgeObj);this._edgeObjs[e]=edgeObj;incrementOrInitEntry(this._preds[w],v);incrementOrInitEntry(this._sucs[v],w);this._in[w][e]=edgeObj;this._out[v][e]=edgeObj;this._edgeCount++;return this};Graph.prototype.edge=function(v,w,name){var e=arguments.length===1?edgeObjToId(this._isDirected,arguments[0]):edgeArgsToId(this._isDirected,v,w,name);return this._edgeLabels[e]};Graph.prototype.hasEdge=function(v,w,name){var e=arguments.length===1?edgeObjToId(this._isDirected,arguments[0]):edgeArgsToId(this._isDirected,v,w,name);return _.has(this._edgeLabels,e)};Graph.prototype.removeEdge=function(v,w,name){var e=arguments.length===1?edgeObjToId(this._isDirected,arguments[0]):edgeArgsToId(this._isDirected,v,w,name),edge=this._edgeObjs[e];if(edge){v=edge.v;w=edge.w;delete this._edgeLabels[e];delete this._edgeObjs[e];decrementOrRemoveEntry(this._preds[w],v);decrementOrRemoveEntry(this._sucs[v],w);delete this._in[w][e];delete this._out[v][e];this._edgeCount--}return this};Graph.prototype.inEdges=function(v,u){var inV=this._in[v];if(inV){var edges=_.values(inV);if(!u){return edges}return _.filter(edges,function(edge){return edge.v===u})}};Graph.prototype.outEdges=function(v,w){var outV=this._out[v];if(outV){var edges=_.values(outV);if(!w){return edges}return _.filter(edges,function(edge){return edge.w===w})}};Graph.prototype.nodeEdges=function(v,w){var inEdges=this.inEdges(v,w);if(inEdges){return inEdges.concat(this.outEdges(v,w))}};function incrementOrInitEntry(map,k){if(map[k]){map[k]++}else{map[k]=1}}function decrementOrRemoveEntry(map,k){if(!--map[k]){delete map[k]}}function edgeArgsToId(isDirected,v_,w_,name){var v=""+v_;var w=""+w_;if(!isDirected&&v>w){var tmp=v;v=w;w=tmp}return v+EDGE_KEY_DELIM+w+EDGE_KEY_DELIM+(_.isUndefined(name)?DEFAULT_EDGE_NAME:name)}function edgeArgsToObj(isDirected,v_,w_,name){var v=""+v_;var w=""+w_;if(!isDirected&&v>w){var tmp=v;v=w;w=tmp}var edgeObj={v:v,w:w};if(name){edgeObj.name=name}return edgeObj}function edgeObjToId(isDirected,edgeObj){return edgeArgsToId(isDirected,edgeObj.v,edgeObj.w,edgeObj.name)}},{"./lodash":77}],75:[function(require,module,exports){ +// Includes only the "core" of graphlib +module.exports={Graph:require("./graph"),version:require("./version")}},{"./graph":74,"./version":78}],76:[function(require,module,exports){var _=require("./lodash"),Graph=require("./graph");module.exports={write:write,read:read};function write(g){var json={options:{directed:g.isDirected(),multigraph:g.isMultigraph(),compound:g.isCompound()},nodes:writeNodes(g),edges:writeEdges(g)};if(!_.isUndefined(g.graph())){json.value=_.clone(g.graph())}return json}function writeNodes(g){return _.map(g.nodes(),function(v){var nodeValue=g.node(v),parent=g.parent(v),node={v:v};if(!_.isUndefined(nodeValue)){node.value=nodeValue}if(!_.isUndefined(parent)){node.parent=parent}return node})}function writeEdges(g){return _.map(g.edges(),function(e){var edgeValue=g.edge(e),edge={v:e.v,w:e.w};if(!_.isUndefined(e.name)){edge.name=e.name}if(!_.isUndefined(edgeValue)){edge.value=edgeValue}return edge})}function read(json){var g=new Graph(json.options).setGraph(json.value);_.each(json.nodes,function(entry){g.setNode(entry.v,entry.value);if(entry.parent){g.setParent(entry.v,entry.parent)}});_.each(json.edges,function(entry){g.setEdge({v:entry.v,w:entry.w,name:entry.name},entry.value)});return g}},{"./graph":74,"./lodash":77}],77:[function(require,module,exports){arguments[4][38][0].apply(exports,arguments)},{dup:38,lodash:79}],78:[function(require,module,exports){module.exports="1.0.7"},{}],79:[function(require,module,exports){(function(global){(function(){ +/** Used as a safe reference for `undefined` in pre-ES5 environments. */ +var undefined; +/** Used as the semantic version number. */var VERSION="3.10.1"; +/** Used to compose bitmasks for wrapper metadata. */var BIND_FLAG=1,BIND_KEY_FLAG=2,CURRY_BOUND_FLAG=4,CURRY_FLAG=8,CURRY_RIGHT_FLAG=16,PARTIAL_FLAG=32,PARTIAL_RIGHT_FLAG=64,ARY_FLAG=128,REARG_FLAG=256; +/** Used as default options for `_.trunc`. */var DEFAULT_TRUNC_LENGTH=30,DEFAULT_TRUNC_OMISSION="..."; +/** Used to detect when a function becomes hot. */var HOT_COUNT=150,HOT_SPAN=16; +/** Used as the size to enable large array optimizations. */var LARGE_ARRAY_SIZE=200; +/** Used to indicate the type of lazy iteratees. */var LAZY_FILTER_FLAG=1,LAZY_MAP_FLAG=2; +/** Used as the `TypeError` message for "Functions" methods. */var FUNC_ERROR_TEXT="Expected a function"; +/** Used as the internal argument placeholder. */var PLACEHOLDER="__lodash_placeholder__"; +/** `Object#toString` result references. */var argsTag="[object Arguments]",arrayTag="[object Array]",boolTag="[object Boolean]",dateTag="[object Date]",errorTag="[object Error]",funcTag="[object Function]",mapTag="[object Map]",numberTag="[object Number]",objectTag="[object Object]",regexpTag="[object RegExp]",setTag="[object Set]",stringTag="[object String]",weakMapTag="[object WeakMap]";var arrayBufferTag="[object ArrayBuffer]",float32Tag="[object Float32Array]",float64Tag="[object Float64Array]",int8Tag="[object Int8Array]",int16Tag="[object Int16Array]",int32Tag="[object Int32Array]",uint8Tag="[object Uint8Array]",uint8ClampedTag="[object Uint8ClampedArray]",uint16Tag="[object Uint16Array]",uint32Tag="[object Uint32Array]"; +/** Used to match empty string literals in compiled template source. */var reEmptyStringLeading=/\b__p \+= '';/g,reEmptyStringMiddle=/\b(__p \+=) '' \+/g,reEmptyStringTrailing=/(__e\(.*?\)|\b__t\)) \+\n'';/g; +/** Used to match HTML entities and HTML characters. */var reEscapedHtml=/&(?:amp|lt|gt|quot|#39|#96);/g,reUnescapedHtml=/[&<>"'`]/g,reHasEscapedHtml=RegExp(reEscapedHtml.source),reHasUnescapedHtml=RegExp(reUnescapedHtml.source); +/** Used to match template delimiters. */var reEscape=/<%-([\s\S]+?)%>/g,reEvaluate=/<%([\s\S]+?)%>/g,reInterpolate=/<%=([\s\S]+?)%>/g; +/** Used to match property names within property paths. */var reIsDeepProp=/\.|\[(?:[^[\]]*|(["'])(?:(?!\1)[^\n\\]|\\.)*?\1)\]/,reIsPlainProp=/^\w*$/,rePropName=/[^.[\]]+|\[(?:(-?\d+(?:\.\d+)?)|(["'])((?:(?!\2)[^\n\\]|\\.)*?)\2)\]/g; +/** + * Used to match `RegExp` [syntax characters](http://ecma-international.org/ecma-262/6.0/#sec-patterns) + * and those outlined by [`EscapeRegExpPattern`](http://ecma-international.org/ecma-262/6.0/#sec-escaperegexppattern). + */var reRegExpChars=/^[:!,]|[\\^$.*+?()[\]{}|\/]|(^[0-9a-fA-Fnrtuvx])|([\n\r\u2028\u2029])/g,reHasRegExpChars=RegExp(reRegExpChars.source); +/** Used to match [combining diacritical marks](https://en.wikipedia.org/wiki/Combining_Diacritical_Marks). */var reComboMark=/[\u0300-\u036f\ufe20-\ufe23]/g; +/** Used to match backslashes in property paths. */var reEscapeChar=/\\(\\)?/g; +/** Used to match [ES template delimiters](http://ecma-international.org/ecma-262/6.0/#sec-template-literal-lexical-components). */var reEsTemplate=/\$\{([^\\}]*(?:\\.[^\\}]*)*)\}/g; +/** Used to match `RegExp` flags from their coerced string values. */var reFlags=/\w*$/; +/** Used to detect hexadecimal string values. */var reHasHexPrefix=/^0[xX]/; +/** Used to detect host constructors (Safari > 5). */var reIsHostCtor=/^\[object .+?Constructor\]$/; +/** Used to detect unsigned integer values. */var reIsUint=/^\d+$/; +/** Used to match latin-1 supplementary letters (excluding mathematical operators). */var reLatin1=/[\xc0-\xd6\xd8-\xde\xdf-\xf6\xf8-\xff]/g; +/** Used to ensure capturing order of template delimiters. */var reNoMatch=/($^)/; +/** Used to match unescaped characters in compiled string literals. */var reUnescapedString=/['\n\r\u2028\u2029\\]/g; +/** Used to match words to create compound words. */var reWords=function(){var upper="[A-Z\\xc0-\\xd6\\xd8-\\xde]",lower="[a-z\\xdf-\\xf6\\xf8-\\xff]+";return RegExp(upper+"+(?="+upper+lower+")|"+upper+"?"+lower+"|"+upper+"+|[0-9]+","g")}(); +/** Used to assign default `context` object properties. */var contextProps=["Array","ArrayBuffer","Date","Error","Float32Array","Float64Array","Function","Int8Array","Int16Array","Int32Array","Math","Number","Object","RegExp","Set","String","_","clearTimeout","isFinite","parseFloat","parseInt","setTimeout","TypeError","Uint8Array","Uint8ClampedArray","Uint16Array","Uint32Array","WeakMap"]; +/** Used to make template sourceURLs easier to identify. */var templateCounter=-1; +/** Used to identify `toStringTag` values of typed arrays. */var typedArrayTags={};typedArrayTags[float32Tag]=typedArrayTags[float64Tag]=typedArrayTags[int8Tag]=typedArrayTags[int16Tag]=typedArrayTags[int32Tag]=typedArrayTags[uint8Tag]=typedArrayTags[uint8ClampedTag]=typedArrayTags[uint16Tag]=typedArrayTags[uint32Tag]=true;typedArrayTags[argsTag]=typedArrayTags[arrayTag]=typedArrayTags[arrayBufferTag]=typedArrayTags[boolTag]=typedArrayTags[dateTag]=typedArrayTags[errorTag]=typedArrayTags[funcTag]=typedArrayTags[mapTag]=typedArrayTags[numberTag]=typedArrayTags[objectTag]=typedArrayTags[regexpTag]=typedArrayTags[setTag]=typedArrayTags[stringTag]=typedArrayTags[weakMapTag]=false; +/** Used to identify `toStringTag` values supported by `_.clone`. */var cloneableTags={};cloneableTags[argsTag]=cloneableTags[arrayTag]=cloneableTags[arrayBufferTag]=cloneableTags[boolTag]=cloneableTags[dateTag]=cloneableTags[float32Tag]=cloneableTags[float64Tag]=cloneableTags[int8Tag]=cloneableTags[int16Tag]=cloneableTags[int32Tag]=cloneableTags[numberTag]=cloneableTags[objectTag]=cloneableTags[regexpTag]=cloneableTags[stringTag]=cloneableTags[uint8Tag]=cloneableTags[uint8ClampedTag]=cloneableTags[uint16Tag]=cloneableTags[uint32Tag]=true;cloneableTags[errorTag]=cloneableTags[funcTag]=cloneableTags[mapTag]=cloneableTags[setTag]=cloneableTags[weakMapTag]=false; +/** Used to map latin-1 supplementary letters to basic latin letters. */var deburredLetters={"À":"A","Á":"A","Â":"A","Ã":"A","Ä":"A","Å":"A","à":"a","á":"a","â":"a","ã":"a","ä":"a","å":"a","Ç":"C","ç":"c","Ð":"D","ð":"d","È":"E","É":"E","Ê":"E","Ë":"E","è":"e","é":"e","ê":"e","ë":"e","Ì":"I","Í":"I","Î":"I","Ï":"I","ì":"i","í":"i","î":"i","ï":"i","Ñ":"N","ñ":"n","Ò":"O","Ó":"O","Ô":"O","Õ":"O","Ö":"O","Ø":"O","ò":"o","ó":"o","ô":"o","õ":"o","ö":"o","ø":"o","Ù":"U","Ú":"U","Û":"U","Ü":"U","ù":"u","ú":"u","û":"u","ü":"u","Ý":"Y","ý":"y","ÿ":"y","Æ":"Ae","æ":"ae","Þ":"Th","þ":"th","ß":"ss"}; +/** Used to map characters to HTML entities. */var htmlEscapes={"&":"&","<":"<",">":">",'"':""","'":"'","`":"`"}; +/** Used to map HTML entities to characters. */var htmlUnescapes={"&":"&","<":"<",">":">",""":'"',"'":"'","`":"`"}; +/** Used to determine if values are of the language type `Object`. */var objectTypes={function:true,object:true}; +/** Used to escape characters for inclusion in compiled regexes. */var regexpEscapes={0:"x30",1:"x31",2:"x32",3:"x33",4:"x34",5:"x35",6:"x36",7:"x37",8:"x38",9:"x39",A:"x41",B:"x42",C:"x43",D:"x44",E:"x45",F:"x46",a:"x61",b:"x62",c:"x63",d:"x64",e:"x65",f:"x66",n:"x6e",r:"x72",t:"x74",u:"x75",v:"x76",x:"x78"}; +/** Used to escape characters for inclusion in compiled string literals. */var stringEscapes={"\\":"\\","'":"'","\n":"n","\r":"r","\u2028":"u2028","\u2029":"u2029"}; +/** Detect free variable `exports`. */var freeExports=objectTypes[typeof exports]&&exports&&!exports.nodeType&&exports; +/** Detect free variable `module`. */var freeModule=objectTypes[typeof module]&&module&&!module.nodeType&&module; +/** Detect free variable `global` from Node.js. */var freeGlobal=freeExports&&freeModule&&typeof global=="object"&&global&&global.Object&&global; +/** Detect free variable `self`. */var freeSelf=objectTypes[typeof self]&&self&&self.Object&&self; +/** Detect free variable `window`. */var freeWindow=objectTypes[typeof window]&&window&&window.Object&&window; +/** Detect the popular CommonJS extension `module.exports`. */var moduleExports=freeModule&&freeModule.exports===freeExports&&freeExports; +/** + * Used as a reference to the global object. + * + * The `this` value is used if it's the global object to avoid Greasemonkey's + * restricted `window` object, otherwise the `window` object is used. + */var root=freeGlobal||freeWindow!==(this&&this.window)&&freeWindow||freeSelf||this; +/*--------------------------------------------------------------------------*/ +/** + * The base implementation of `compareAscending` which compares values and + * sorts them in ascending order without guaranteeing a stable sort. + * + * @private + * @param {*} value The value to compare. + * @param {*} other The other value to compare. + * @returns {number} Returns the sort order indicator for `value`. + */function baseCompareAscending(value,other){if(value!==other){var valIsNull=value===null,valIsUndef=value===undefined,valIsReflexive=value===value;var othIsNull=other===null,othIsUndef=other===undefined,othIsReflexive=other===other;if(value>other&&!othIsNull||!valIsReflexive||valIsNull&&!othIsUndef&&othIsReflexive||valIsUndef&&othIsReflexive){return 1}if(value-1){}return index} +/** + * Used by `_.trim` and `_.trimRight` to get the index of the last character + * of `string` that is not found in `chars`. + * + * @private + * @param {string} string The string to inspect. + * @param {string} chars The characters to find. + * @returns {number} Returns the index of the last character not found in `chars`. + */function charsRightIndex(string,chars){var index=string.length;while(index--&&chars.indexOf(string.charAt(index))>-1){}return index} +/** + * Used by `_.sortBy` to compare transformed elements of a collection and stable + * sort them in ascending order. + * + * @private + * @param {Object} object The object to compare. + * @param {Object} other The other object to compare. + * @returns {number} Returns the sort order indicator for `object`. + */function compareAscending(object,other){return baseCompareAscending(object.criteria,other.criteria)||object.index-other.index} +/** + * Used by `_.sortByOrder` to compare multiple properties of a value to another + * and stable sort them. + * + * If `orders` is unspecified, all valuess are sorted in ascending order. Otherwise, + * a value is sorted in ascending order if its corresponding order is "asc", and + * descending if "desc". + * + * @private + * @param {Object} object The object to compare. + * @param {Object} other The other object to compare. + * @param {boolean[]} orders The order to sort by for each property. + * @returns {number} Returns the sort order indicator for `object`. + */function compareMultiple(object,other,orders){var index=-1,objCriteria=object.criteria,othCriteria=other.criteria,length=objCriteria.length,ordersLength=orders.length;while(++index=ordersLength){return result}var order=orders[index];return result*(order==="asc"||order===true?1:-1)}} +// Fixes an `Array#sort` bug in the JS engine embedded in Adobe applications +// that causes it, under certain circumstances, to provide the same value for +// `object` and `other`. See https://github.com/jashkenas/underscore/pull/1247 +// for more details. +// +// This also ensures a stable sort in V8 and other engines. +// See https://code.google.com/p/v8/issues/detail?id=90 for more details. +return object.index-other.index} +/** + * Used by `_.deburr` to convert latin-1 supplementary letters to basic latin letters. + * + * @private + * @param {string} letter The matched letter to deburr. + * @returns {string} Returns the deburred letter. + */function deburrLetter(letter){return deburredLetters[letter]} +/** + * Used by `_.escape` to convert characters to HTML entities. + * + * @private + * @param {string} chr The matched character to escape. + * @returns {string} Returns the escaped character. + */function escapeHtmlChar(chr){return htmlEscapes[chr]} +/** + * Used by `_.escapeRegExp` to escape characters for inclusion in compiled regexes. + * + * @private + * @param {string} chr The matched character to escape. + * @param {string} leadingChar The capture group for a leading character. + * @param {string} whitespaceChar The capture group for a whitespace character. + * @returns {string} Returns the escaped character. + */function escapeRegExpChar(chr,leadingChar,whitespaceChar){if(leadingChar){chr=regexpEscapes[chr]}else if(whitespaceChar){chr=stringEscapes[chr]}return"\\"+chr} +/** + * Used by `_.template` to escape characters for inclusion in compiled string literals. + * + * @private + * @param {string} chr The matched character to escape. + * @returns {string} Returns the escaped character. + */function escapeStringChar(chr){return"\\"+stringEscapes[chr]} +/** + * Gets the index at which the first occurrence of `NaN` is found in `array`. + * + * @private + * @param {Array} array The array to search. + * @param {number} fromIndex The index to search from. + * @param {boolean} [fromRight] Specify iterating from right to left. + * @returns {number} Returns the index of the matched `NaN`, else `-1`. + */function indexOfNaN(array,fromIndex,fromRight){var length=array.length,index=fromIndex+(fromRight?0:-1);while(fromRight?index--:++index=9&&charCode<=13)||charCode==32||charCode==160||charCode==5760||charCode==6158||charCode>=8192&&(charCode<=8202||charCode==8232||charCode==8233||charCode==8239||charCode==8287||charCode==12288||charCode==65279)} +/** + * Replaces all `placeholder` elements in `array` with an internal placeholder + * and returns an array of their indexes. + * + * @private + * @param {Array} array The array to modify. + * @param {*} placeholder The placeholder to replace. + * @returns {Array} Returns the new array of placeholder indexes. + */function replaceHolders(array,placeholder){var index=-1,length=array.length,resIndex=-1,result=[];while(++index true + * _.isFunction(_.bar); + * // => false + * + * lodash.isFunction(lodash.foo); + * // => false + * lodash.isFunction(lodash.bar); + * // => true + * + * // using `context` to mock `Date#getTime` use in `_.now` + * var mock = _.runInContext({ + * 'Date': function() { + * return { 'getTime': getTimeMock }; + * } + * }); + * + * // or creating a suped-up `defer` in Node.js + * var defer = _.runInContext({ 'setTimeout': setImmediate }).defer; + */function runInContext(context){ +// Avoid issues with some ES3 environments that attempt to use values, named +// after built-in constructors like `Object`, for the creation of literals. +// ES5 clears this up by stating that literals must use built-in constructors. +// See https://es5.github.io/#x11.1.5 for more details. +context=context?_.defaults(root.Object(),context,_.pick(root,contextProps)):root; +/** Native constructor references. */var Array=context.Array,Date=context.Date,Error=context.Error,Function=context.Function,Math=context.Math,Number=context.Number,Object=context.Object,RegExp=context.RegExp,String=context.String,TypeError=context.TypeError; +/** Used for native method references. */var arrayProto=Array.prototype,objectProto=Object.prototype,stringProto=String.prototype; +/** Used to resolve the decompiled source of functions. */var fnToString=Function.prototype.toString; +/** Used to check objects for own properties. */var hasOwnProperty=objectProto.hasOwnProperty; +/** Used to generate unique IDs. */var idCounter=0; +/** + * Used to resolve the [`toStringTag`](http://ecma-international.org/ecma-262/6.0/#sec-object.prototype.tostring) + * of values. + */var objToString=objectProto.toString; +/** Used to restore the original `_` reference in `_.noConflict`. */var oldDash=root._; +/** Used to detect if a method is native. */var reIsNative=RegExp("^"+fnToString.call(hasOwnProperty).replace(/[\\^$.*+?()[\]{}|]/g,"\\$&").replace(/hasOwnProperty|(function).*?(?=\\\()| for .+?(?=\\\])/g,"$1.*?")+"$"); +/** Native method references. */var ArrayBuffer=context.ArrayBuffer,clearTimeout=context.clearTimeout,parseFloat=context.parseFloat,pow=Math.pow,propertyIsEnumerable=objectProto.propertyIsEnumerable,Set=getNative(context,"Set"),setTimeout=context.setTimeout,splice=arrayProto.splice,Uint8Array=context.Uint8Array,WeakMap=getNative(context,"WeakMap"); +/* Native method references for those with the same name as other `lodash` methods. */var nativeCeil=Math.ceil,nativeCreate=getNative(Object,"create"),nativeFloor=Math.floor,nativeIsArray=getNative(Array,"isArray"),nativeIsFinite=context.isFinite,nativeKeys=getNative(Object,"keys"),nativeMax=Math.max,nativeMin=Math.min,nativeNow=getNative(Date,"now"),nativeParseInt=context.parseInt,nativeRandom=Math.random; +/** Used as references for `-Infinity` and `Infinity`. */var NEGATIVE_INFINITY=Number.NEGATIVE_INFINITY,POSITIVE_INFINITY=Number.POSITIVE_INFINITY; +/** Used as references for the maximum length and index of an array. */var MAX_ARRAY_LENGTH=4294967295,MAX_ARRAY_INDEX=MAX_ARRAY_LENGTH-1,HALF_MAX_ARRAY_LENGTH=MAX_ARRAY_LENGTH>>>1; +/** + * Used as the [maximum length](http://ecma-international.org/ecma-262/6.0/#sec-number.max_safe_integer) + * of an array-like value. + */var MAX_SAFE_INTEGER=9007199254740991; +/** Used to store function metadata. */var metaMap=WeakMap&&new WeakMap; +/** Used to lookup unminified function names. */var realNames={}; +/*------------------------------------------------------------------------*/ +/** + * Creates a `lodash` object which wraps `value` to enable implicit chaining. + * Methods that operate on and return arrays, collections, and functions can + * be chained together. Methods that retrieve a single value or may return a + * primitive value will automatically end the chain returning the unwrapped + * value. Explicit chaining may be enabled using `_.chain`. The execution of + * chained methods is lazy, that is, execution is deferred until `_#value` + * is implicitly or explicitly called. + * + * Lazy evaluation allows several methods to support shortcut fusion. Shortcut + * fusion is an optimization strategy which merge iteratee calls; this can help + * to avoid the creation of intermediate data structures and greatly reduce the + * number of iteratee executions. + * + * Chaining is supported in custom builds as long as the `_#value` method is + * directly or indirectly included in the build. + * + * In addition to lodash methods, wrappers have `Array` and `String` methods. + * + * The wrapper `Array` methods are: + * `concat`, `join`, `pop`, `push`, `reverse`, `shift`, `slice`, `sort`, + * `splice`, and `unshift` + * + * The wrapper `String` methods are: + * `replace` and `split` + * + * The wrapper methods that support shortcut fusion are: + * `compact`, `drop`, `dropRight`, `dropRightWhile`, `dropWhile`, `filter`, + * `first`, `initial`, `last`, `map`, `pluck`, `reject`, `rest`, `reverse`, + * `slice`, `take`, `takeRight`, `takeRightWhile`, `takeWhile`, `toArray`, + * and `where` + * + * The chainable wrapper methods are: + * `after`, `ary`, `assign`, `at`, `before`, `bind`, `bindAll`, `bindKey`, + * `callback`, `chain`, `chunk`, `commit`, `compact`, `concat`, `constant`, + * `countBy`, `create`, `curry`, `debounce`, `defaults`, `defaultsDeep`, + * `defer`, `delay`, `difference`, `drop`, `dropRight`, `dropRightWhile`, + * `dropWhile`, `fill`, `filter`, `flatten`, `flattenDeep`, `flow`, `flowRight`, + * `forEach`, `forEachRight`, `forIn`, `forInRight`, `forOwn`, `forOwnRight`, + * `functions`, `groupBy`, `indexBy`, `initial`, `intersection`, `invert`, + * `invoke`, `keys`, `keysIn`, `map`, `mapKeys`, `mapValues`, `matches`, + * `matchesProperty`, `memoize`, `merge`, `method`, `methodOf`, `mixin`, + * `modArgs`, `negate`, `omit`, `once`, `pairs`, `partial`, `partialRight`, + * `partition`, `pick`, `plant`, `pluck`, `property`, `propertyOf`, `pull`, + * `pullAt`, `push`, `range`, `rearg`, `reject`, `remove`, `rest`, `restParam`, + * `reverse`, `set`, `shuffle`, `slice`, `sort`, `sortBy`, `sortByAll`, + * `sortByOrder`, `splice`, `spread`, `take`, `takeRight`, `takeRightWhile`, + * `takeWhile`, `tap`, `throttle`, `thru`, `times`, `toArray`, `toPlainObject`, + * `transform`, `union`, `uniq`, `unshift`, `unzip`, `unzipWith`, `values`, + * `valuesIn`, `where`, `without`, `wrap`, `xor`, `zip`, `zipObject`, `zipWith` + * + * The wrapper methods that are **not** chainable by default are: + * `add`, `attempt`, `camelCase`, `capitalize`, `ceil`, `clone`, `cloneDeep`, + * `deburr`, `endsWith`, `escape`, `escapeRegExp`, `every`, `find`, `findIndex`, + * `findKey`, `findLast`, `findLastIndex`, `findLastKey`, `findWhere`, `first`, + * `floor`, `get`, `gt`, `gte`, `has`, `identity`, `includes`, `indexOf`, + * `inRange`, `isArguments`, `isArray`, `isBoolean`, `isDate`, `isElement`, + * `isEmpty`, `isEqual`, `isError`, `isFinite` `isFunction`, `isMatch`, + * `isNative`, `isNaN`, `isNull`, `isNumber`, `isObject`, `isPlainObject`, + * `isRegExp`, `isString`, `isUndefined`, `isTypedArray`, `join`, `kebabCase`, + * `last`, `lastIndexOf`, `lt`, `lte`, `max`, `min`, `noConflict`, `noop`, + * `now`, `pad`, `padLeft`, `padRight`, `parseInt`, `pop`, `random`, `reduce`, + * `reduceRight`, `repeat`, `result`, `round`, `runInContext`, `shift`, `size`, + * `snakeCase`, `some`, `sortedIndex`, `sortedLastIndex`, `startCase`, + * `startsWith`, `sum`, `template`, `trim`, `trimLeft`, `trimRight`, `trunc`, + * `unescape`, `uniqueId`, `value`, and `words` + * + * The wrapper method `sample` will return a wrapped value when `n` is provided, + * otherwise an unwrapped value is returned. + * + * @name _ + * @constructor + * @category Chain + * @param {*} value The value to wrap in a `lodash` instance. + * @returns {Object} Returns the new `lodash` wrapper instance. + * @example + * + * var wrapped = _([1, 2, 3]); + * + * // returns an unwrapped value + * wrapped.reduce(function(total, n) { + * return total + n; + * }); + * // => 6 + * + * // returns a wrapped value + * var squares = wrapped.map(function(n) { + * return n * n; + * }); + * + * _.isArray(squares); + * // => false + * + * _.isArray(squares.value()); + * // => true + */function lodash(value){if(isObjectLike(value)&&!isArray(value)&&!(value instanceof LazyWrapper)){if(value instanceof LodashWrapper){return value}if(hasOwnProperty.call(value,"__chain__")&&hasOwnProperty.call(value,"__wrapped__")){return wrapperClone(value)}}return new LodashWrapper(value)} +/** + * The function whose prototype all chaining wrappers inherit from. + * + * @private + */function baseLodash(){ +// No operation performed. +} +/** + * The base constructor for creating `lodash` wrapper objects. + * + * @private + * @param {*} value The value to wrap. + * @param {boolean} [chainAll] Enable chaining for all wrapper methods. + * @param {Array} [actions=[]] Actions to peform to resolve the unwrapped value. + */function LodashWrapper(value,chainAll,actions){this.__wrapped__=value;this.__actions__=actions||[];this.__chain__=!!chainAll} +/** + * An object environment feature flags. + * + * @static + * @memberOf _ + * @type Object + */var support=lodash.support={}; +/** + * By default, the template delimiters used by lodash are like those in + * embedded Ruby (ERB). Change the following template settings to use + * alternative delimiters. + * + * @static + * @memberOf _ + * @type Object + */lodash.templateSettings={ +/** + * Used to detect `data` property values to be HTML-escaped. + * + * @memberOf _.templateSettings + * @type RegExp + */ +escape:reEscape, +/** + * Used to detect code to be evaluated. + * + * @memberOf _.templateSettings + * @type RegExp + */ +evaluate:reEvaluate, +/** + * Used to detect `data` property values to inject. + * + * @memberOf _.templateSettings + * @type RegExp + */ +interpolate:reInterpolate, +/** + * Used to reference the data object in the template text. + * + * @memberOf _.templateSettings + * @type string + */ +variable:"", +/** + * Used to import variables into the compiled template. + * + * @memberOf _.templateSettings + * @type Object + */ +imports:{ +/** + * A reference to the `lodash` function. + * + * @memberOf _.templateSettings.imports + * @type Function + */ +_:lodash}}; +/*------------------------------------------------------------------------*/ +/** + * Creates a lazy wrapper object which wraps `value` to enable lazy evaluation. + * + * @private + * @param {*} value The value to wrap. + */function LazyWrapper(value){this.__wrapped__=value;this.__actions__=[];this.__dir__=1;this.__filtered__=false;this.__iteratees__=[];this.__takeCount__=POSITIVE_INFINITY;this.__views__=[]} +/** + * Creates a clone of the lazy wrapper object. + * + * @private + * @name clone + * @memberOf LazyWrapper + * @returns {Object} Returns the cloned `LazyWrapper` object. + */function lazyClone(){var result=new LazyWrapper(this.__wrapped__);result.__actions__=arrayCopy(this.__actions__);result.__dir__=this.__dir__;result.__filtered__=this.__filtered__;result.__iteratees__=arrayCopy(this.__iteratees__);result.__takeCount__=this.__takeCount__;result.__views__=arrayCopy(this.__views__);return result} +/** + * Reverses the direction of lazy iteration. + * + * @private + * @name reverse + * @memberOf LazyWrapper + * @returns {Object} Returns the new reversed `LazyWrapper` object. + */function lazyReverse(){if(this.__filtered__){var result=new LazyWrapper(this);result.__dir__=-1;result.__filtered__=true}else{result=this.clone();result.__dir__*=-1}return result} +/** + * Extracts the unwrapped value from its lazy wrapper. + * + * @private + * @name value + * @memberOf LazyWrapper + * @returns {*} Returns the unwrapped value. + */function lazyValue(){var array=this.__wrapped__.value(),dir=this.__dir__,isArr=isArray(array),isRight=dir<0,arrLength=isArr?array.length:0,view=getView(0,arrLength,this.__views__),start=view.start,end=view.end,length=end-start,index=isRight?end:start-1,iteratees=this.__iteratees__,iterLength=iteratees.length,resIndex=0,takeCount=nativeMin(length,this.__takeCount__);if(!isArr||arrLength=LARGE_ARRAY_SIZE?createCache(values):null,valuesLength=values.length;if(cache){indexOf=cacheIndexOf;isCommon=false;values=cache}outer:while(++indexlength?0:length+start}end=end===undefined||end>length?length:+end||0;if(end<0){end+=length}length=start>end?0:end>>>0;start>>>=0;while(startlength?0:length+start}end=end===undefined||end>length?length:+end||0;if(end<0){end+=length}length=start>end?0:end-start>>>0;start>>>=0;var result=Array(length);while(++index=LARGE_ARRAY_SIZE,seen=isLarge?createCache():null,result=[];if(seen){indexOf=cacheIndexOf;isCommon=false}else{isLarge=false;seen=iteratee?[]:result}outer:while(++index>>1,computed=array[mid];if((retHighest?computed<=value:computed2?sources[length-2]:undefined,guard=length>2?sources[2]:undefined,thisArg=length>1?sources[length-1]:undefined;if(typeof customizer=="function"){customizer=bindCallback(customizer,thisArg,5);length-=2}else{customizer=typeof thisArg=="function"?thisArg:undefined;length-=customizer?1:0}if(guard&&isIterateeCall(sources[0],sources[1],guard)){customizer=length<3?undefined:customizer;length=1}while(++index-1?collection[index]:undefined}return baseFind(collection,predicate,eachFunc)}} +/** + * Creates a `_.findIndex` or `_.findLastIndex` function. + * + * @private + * @param {boolean} [fromRight] Specify iterating from right to left. + * @returns {Function} Returns the new find function. + */function createFindIndex(fromRight){return function(array,predicate,thisArg){if(!(array&&array.length)){return-1}predicate=getCallback(predicate,thisArg,3);return baseFindIndex(array,predicate,fromRight)}} +/** + * Creates a `_.findKey` or `_.findLastKey` function. + * + * @private + * @param {Function} objectFunc The function to iterate over an object. + * @returns {Function} Returns the new find function. + */function createFindKey(objectFunc){return function(object,predicate,thisArg){predicate=getCallback(predicate,thisArg,3);return baseFind(object,predicate,objectFunc,true)}} +/** + * Creates a `_.flow` or `_.flowRight` function. + * + * @private + * @param {boolean} [fromRight] Specify iterating from right to left. + * @returns {Function} Returns the new flow function. + */function createFlow(fromRight){return function(){var wrapper,length=arguments.length,index=fromRight?length:-1,leftIndex=0,funcs=Array(length);while(fromRight?index--:++index=LARGE_ARRAY_SIZE){return wrapper.plant(value).value()}var index=0,result=length?funcs[index].apply(this,args):value;while(++index=length||!nativeIsFinite(length)){return""}var padLength=length-strLength;chars=chars==null?" ":chars+"";return repeat(chars,nativeCeil(padLength/chars.length)).slice(0,padLength)} +/** + * Creates a function that wraps `func` and invokes it with the optional `this` + * binding of `thisArg` and the `partials` prepended to those provided to + * the wrapper. + * + * @private + * @param {Function} func The function to partially apply arguments to. + * @param {number} bitmask The bitmask of flags. See `createWrapper` for more details. + * @param {*} thisArg The `this` binding of `func`. + * @param {Array} partials The arguments to prepend to those provided to the new function. + * @returns {Function} Returns the new bound function. + */function createPartialWrapper(func,bitmask,thisArg,partials){var isBind=bitmask&BIND_FLAG,Ctor=createCtorWrapper(func);function wrapper(){ +// Avoid `arguments` object use disqualifying optimizations by +// converting it to an array before providing it `func`. +var argsIndex=-1,argsLength=arguments.length,leftIndex=-1,leftLength=partials.length,args=Array(leftLength+argsLength);while(++leftIndexarrLength)){return false} +// Ignore non-index properties. +while(++index-1&&value%1==0&&value-1&&value%1==0&&value<=MAX_SAFE_INTEGER} +/** + * Checks if `value` is suitable for strict equality comparisons, i.e. `===`. + * + * @private + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` if suitable for strict + * equality comparisons, else `false`. + */function isStrictComparable(value){return value===value&&!isObject(value)} +/** + * Merges the function metadata of `source` into `data`. + * + * Merging metadata reduces the number of wrappers required to invoke a function. + * This is possible because methods like `_.bind`, `_.curry`, and `_.partial` + * may be applied regardless of execution order. Methods like `_.ary` and `_.rearg` + * augment function arguments, making the order in which they are executed important, + * preventing the merging of metadata. However, we make an exception for a safe + * common case where curried functions have `_.ary` and or `_.rearg` applied. + * + * @private + * @param {Array} data The destination metadata. + * @param {Array} source The source metadata. + * @returns {Array} Returns `data`. + */function mergeData(data,source){var bitmask=data[1],srcBitmask=source[1],newBitmask=bitmask|srcBitmask,isCommon=newBitmask0){if(++count>=HOT_COUNT){return key}}else{count=0}return baseSetData(key,value)}}(); +/** + * A fallback implementation of `Object.keys` which creates an array of the + * own enumerable property names of `object`. + * + * @private + * @param {Object} object The object to query. + * @returns {Array} Returns the array of property names. + */function shimKeys(object){var props=keysIn(object),propsLength=props.length,length=propsLength&&object.length;var allowIndexes=!!length&&isLength(length)&&(isArray(object)||isArguments(object));var index=-1,result=[];while(++index [['a', 'b'], ['c', 'd']] + * + * _.chunk(['a', 'b', 'c', 'd'], 3); + * // => [['a', 'b', 'c'], ['d']] + */function chunk(array,size,guard){if(guard?isIterateeCall(array,size,guard):size==null){size=1}else{size=nativeMax(nativeFloor(size)||1,1)}var index=0,length=array?array.length:0,resIndex=-1,result=Array(nativeCeil(length/size));while(index [1, 2, 3] + */function compact(array){var index=-1,length=array?array.length:0,resIndex=-1,result=[];while(++index [1, 3] + */var difference=restParam(function(array,values){return isObjectLike(array)&&isArrayLike(array)?baseDifference(array,baseFlatten(values,false,true)):[]}); +/** + * Creates a slice of `array` with `n` elements dropped from the beginning. + * + * @static + * @memberOf _ + * @category Array + * @param {Array} array The array to query. + * @param {number} [n=1] The number of elements to drop. + * @param- {Object} [guard] Enables use as a callback for functions like `_.map`. + * @returns {Array} Returns the slice of `array`. + * @example + * + * _.drop([1, 2, 3]); + * // => [2, 3] + * + * _.drop([1, 2, 3], 2); + * // => [3] + * + * _.drop([1, 2, 3], 5); + * // => [] + * + * _.drop([1, 2, 3], 0); + * // => [1, 2, 3] + */function drop(array,n,guard){var length=array?array.length:0;if(!length){return[]}if(guard?isIterateeCall(array,n,guard):n==null){n=1}return baseSlice(array,n<0?0:n)} +/** + * Creates a slice of `array` with `n` elements dropped from the end. + * + * @static + * @memberOf _ + * @category Array + * @param {Array} array The array to query. + * @param {number} [n=1] The number of elements to drop. + * @param- {Object} [guard] Enables use as a callback for functions like `_.map`. + * @returns {Array} Returns the slice of `array`. + * @example + * + * _.dropRight([1, 2, 3]); + * // => [1, 2] + * + * _.dropRight([1, 2, 3], 2); + * // => [1] + * + * _.dropRight([1, 2, 3], 5); + * // => [] + * + * _.dropRight([1, 2, 3], 0); + * // => [1, 2, 3] + */function dropRight(array,n,guard){var length=array?array.length:0;if(!length){return[]}if(guard?isIterateeCall(array,n,guard):n==null){n=1}n=length-(+n||0);return baseSlice(array,0,n<0?0:n)} +/** + * Creates a slice of `array` excluding elements dropped from the end. + * Elements are dropped until `predicate` returns falsey. The predicate is + * bound to `thisArg` and invoked with three arguments: (value, index, array). + * + * If a property name is provided for `predicate` the created `_.property` + * style callback returns the property value of the given element. + * + * If a value is also provided for `thisArg` the created `_.matchesProperty` + * style callback returns `true` for elements that have a matching property + * value, else `false`. + * + * If an object is provided for `predicate` the created `_.matches` style + * callback returns `true` for elements that match the properties of the given + * object, else `false`. + * + * @static + * @memberOf _ + * @category Array + * @param {Array} array The array to query. + * @param {Function|Object|string} [predicate=_.identity] The function invoked + * per iteration. + * @param {*} [thisArg] The `this` binding of `predicate`. + * @returns {Array} Returns the slice of `array`. + * @example + * + * _.dropRightWhile([1, 2, 3], function(n) { + * return n > 1; + * }); + * // => [1] + * + * var users = [ + * { 'user': 'barney', 'active': true }, + * { 'user': 'fred', 'active': false }, + * { 'user': 'pebbles', 'active': false } + * ]; + * + * // using the `_.matches` callback shorthand + * _.pluck(_.dropRightWhile(users, { 'user': 'pebbles', 'active': false }), 'user'); + * // => ['barney', 'fred'] + * + * // using the `_.matchesProperty` callback shorthand + * _.pluck(_.dropRightWhile(users, 'active', false), 'user'); + * // => ['barney'] + * + * // using the `_.property` callback shorthand + * _.pluck(_.dropRightWhile(users, 'active'), 'user'); + * // => ['barney', 'fred', 'pebbles'] + */function dropRightWhile(array,predicate,thisArg){return array&&array.length?baseWhile(array,getCallback(predicate,thisArg,3),true,true):[]} +/** + * Creates a slice of `array` excluding elements dropped from the beginning. + * Elements are dropped until `predicate` returns falsey. The predicate is + * bound to `thisArg` and invoked with three arguments: (value, index, array). + * + * If a property name is provided for `predicate` the created `_.property` + * style callback returns the property value of the given element. + * + * If a value is also provided for `thisArg` the created `_.matchesProperty` + * style callback returns `true` for elements that have a matching property + * value, else `false`. + * + * If an object is provided for `predicate` the created `_.matches` style + * callback returns `true` for elements that have the properties of the given + * object, else `false`. + * + * @static + * @memberOf _ + * @category Array + * @param {Array} array The array to query. + * @param {Function|Object|string} [predicate=_.identity] The function invoked + * per iteration. + * @param {*} [thisArg] The `this` binding of `predicate`. + * @returns {Array} Returns the slice of `array`. + * @example + * + * _.dropWhile([1, 2, 3], function(n) { + * return n < 3; + * }); + * // => [3] + * + * var users = [ + * { 'user': 'barney', 'active': false }, + * { 'user': 'fred', 'active': false }, + * { 'user': 'pebbles', 'active': true } + * ]; + * + * // using the `_.matches` callback shorthand + * _.pluck(_.dropWhile(users, { 'user': 'barney', 'active': false }), 'user'); + * // => ['fred', 'pebbles'] + * + * // using the `_.matchesProperty` callback shorthand + * _.pluck(_.dropWhile(users, 'active', false), 'user'); + * // => ['pebbles'] + * + * // using the `_.property` callback shorthand + * _.pluck(_.dropWhile(users, 'active'), 'user'); + * // => ['barney', 'fred', 'pebbles'] + */function dropWhile(array,predicate,thisArg){return array&&array.length?baseWhile(array,getCallback(predicate,thisArg,3),true):[]} +/** + * Fills elements of `array` with `value` from `start` up to, but not + * including, `end`. + * + * **Note:** This method mutates `array`. + * + * @static + * @memberOf _ + * @category Array + * @param {Array} array The array to fill. + * @param {*} value The value to fill `array` with. + * @param {number} [start=0] The start position. + * @param {number} [end=array.length] The end position. + * @returns {Array} Returns `array`. + * @example + * + * var array = [1, 2, 3]; + * + * _.fill(array, 'a'); + * console.log(array); + * // => ['a', 'a', 'a'] + * + * _.fill(Array(3), 2); + * // => [2, 2, 2] + * + * _.fill([4, 6, 8], '*', 1, 2); + * // => [4, '*', 8] + */function fill(array,value,start,end){var length=array?array.length:0;if(!length){return[]}if(start&&typeof start!="number"&&isIterateeCall(array,value,start)){start=0;end=length}return baseFill(array,value,start,end)} +/** + * This method is like `_.find` except that it returns the index of the first + * element `predicate` returns truthy for instead of the element itself. + * + * If a property name is provided for `predicate` the created `_.property` + * style callback returns the property value of the given element. + * + * If a value is also provided for `thisArg` the created `_.matchesProperty` + * style callback returns `true` for elements that have a matching property + * value, else `false`. + * + * If an object is provided for `predicate` the created `_.matches` style + * callback returns `true` for elements that have the properties of the given + * object, else `false`. + * + * @static + * @memberOf _ + * @category Array + * @param {Array} array The array to search. + * @param {Function|Object|string} [predicate=_.identity] The function invoked + * per iteration. + * @param {*} [thisArg] The `this` binding of `predicate`. + * @returns {number} Returns the index of the found element, else `-1`. + * @example + * + * var users = [ + * { 'user': 'barney', 'active': false }, + * { 'user': 'fred', 'active': false }, + * { 'user': 'pebbles', 'active': true } + * ]; + * + * _.findIndex(users, function(chr) { + * return chr.user == 'barney'; + * }); + * // => 0 + * + * // using the `_.matches` callback shorthand + * _.findIndex(users, { 'user': 'fred', 'active': false }); + * // => 1 + * + * // using the `_.matchesProperty` callback shorthand + * _.findIndex(users, 'active', false); + * // => 0 + * + * // using the `_.property` callback shorthand + * _.findIndex(users, 'active'); + * // => 2 + */var findIndex=createFindIndex(); +/** + * This method is like `_.findIndex` except that it iterates over elements + * of `collection` from right to left. + * + * If a property name is provided for `predicate` the created `_.property` + * style callback returns the property value of the given element. + * + * If a value is also provided for `thisArg` the created `_.matchesProperty` + * style callback returns `true` for elements that have a matching property + * value, else `false`. + * + * If an object is provided for `predicate` the created `_.matches` style + * callback returns `true` for elements that have the properties of the given + * object, else `false`. + * + * @static + * @memberOf _ + * @category Array + * @param {Array} array The array to search. + * @param {Function|Object|string} [predicate=_.identity] The function invoked + * per iteration. + * @param {*} [thisArg] The `this` binding of `predicate`. + * @returns {number} Returns the index of the found element, else `-1`. + * @example + * + * var users = [ + * { 'user': 'barney', 'active': true }, + * { 'user': 'fred', 'active': false }, + * { 'user': 'pebbles', 'active': false } + * ]; + * + * _.findLastIndex(users, function(chr) { + * return chr.user == 'pebbles'; + * }); + * // => 2 + * + * // using the `_.matches` callback shorthand + * _.findLastIndex(users, { 'user': 'barney', 'active': true }); + * // => 0 + * + * // using the `_.matchesProperty` callback shorthand + * _.findLastIndex(users, 'active', false); + * // => 2 + * + * // using the `_.property` callback shorthand + * _.findLastIndex(users, 'active'); + * // => 0 + */var findLastIndex=createFindIndex(true); +/** + * Gets the first element of `array`. + * + * @static + * @memberOf _ + * @alias head + * @category Array + * @param {Array} array The array to query. + * @returns {*} Returns the first element of `array`. + * @example + * + * _.first([1, 2, 3]); + * // => 1 + * + * _.first([]); + * // => undefined + */function first(array){return array?array[0]:undefined} +/** + * Flattens a nested array. If `isDeep` is `true` the array is recursively + * flattened, otherwise it is only flattened a single level. + * + * @static + * @memberOf _ + * @category Array + * @param {Array} array The array to flatten. + * @param {boolean} [isDeep] Specify a deep flatten. + * @param- {Object} [guard] Enables use as a callback for functions like `_.map`. + * @returns {Array} Returns the new flattened array. + * @example + * + * _.flatten([1, [2, 3, [4]]]); + * // => [1, 2, 3, [4]] + * + * // using `isDeep` + * _.flatten([1, [2, 3, [4]]], true); + * // => [1, 2, 3, 4] + */function flatten(array,isDeep,guard){var length=array?array.length:0;if(guard&&isIterateeCall(array,isDeep,guard)){isDeep=false}return length?baseFlatten(array,isDeep):[]} +/** + * Recursively flattens a nested array. + * + * @static + * @memberOf _ + * @category Array + * @param {Array} array The array to recursively flatten. + * @returns {Array} Returns the new flattened array. + * @example + * + * _.flattenDeep([1, [2, 3, [4]]]); + * // => [1, 2, 3, 4] + */function flattenDeep(array){var length=array?array.length:0;return length?baseFlatten(array,true):[]} +/** + * Gets the index at which the first occurrence of `value` is found in `array` + * using [`SameValueZero`](http://ecma-international.org/ecma-262/6.0/#sec-samevaluezero) + * for equality comparisons. If `fromIndex` is negative, it is used as the offset + * from the end of `array`. If `array` is sorted providing `true` for `fromIndex` + * performs a faster binary search. + * + * @static + * @memberOf _ + * @category Array + * @param {Array} array The array to search. + * @param {*} value The value to search for. + * @param {boolean|number} [fromIndex=0] The index to search from or `true` + * to perform a binary search on a sorted array. + * @returns {number} Returns the index of the matched value, else `-1`. + * @example + * + * _.indexOf([1, 2, 1, 2], 2); + * // => 1 + * + * // using `fromIndex` + * _.indexOf([1, 2, 1, 2], 2, 2); + * // => 3 + * + * // performing a binary search + * _.indexOf([1, 1, 2, 2], 2, true); + * // => 2 + */function indexOf(array,value,fromIndex){var length=array?array.length:0;if(!length){return-1}if(typeof fromIndex=="number"){fromIndex=fromIndex<0?nativeMax(length+fromIndex,0):fromIndex}else if(fromIndex){var index=binaryIndex(array,value);if(index [1, 2] + */function initial(array){return dropRight(array,1)} +/** + * Creates an array of unique values that are included in all of the provided + * arrays using [`SameValueZero`](http://ecma-international.org/ecma-262/6.0/#sec-samevaluezero) + * for equality comparisons. + * + * @static + * @memberOf _ + * @category Array + * @param {...Array} [arrays] The arrays to inspect. + * @returns {Array} Returns the new array of shared values. + * @example + * _.intersection([1, 2], [4, 2], [2, 1]); + * // => [2] + */var intersection=restParam(function(arrays){var othLength=arrays.length,othIndex=othLength,caches=Array(length),indexOf=getIndexOf(),isCommon=indexOf==baseIndexOf,result=[];while(othIndex--){var value=arrays[othIndex]=isArrayLike(value=arrays[othIndex])?value:[];caches[othIndex]=isCommon&&value.length>=120?createCache(othIndex&&value):null}var array=arrays[0],index=-1,length=array?array.length:0,seen=caches[0];outer:while(++index 3 + */function last(array){var length=array?array.length:0;return length?array[length-1]:undefined} +/** + * This method is like `_.indexOf` except that it iterates over elements of + * `array` from right to left. + * + * @static + * @memberOf _ + * @category Array + * @param {Array} array The array to search. + * @param {*} value The value to search for. + * @param {boolean|number} [fromIndex=array.length-1] The index to search from + * or `true` to perform a binary search on a sorted array. + * @returns {number} Returns the index of the matched value, else `-1`. + * @example + * + * _.lastIndexOf([1, 2, 1, 2], 2); + * // => 3 + * + * // using `fromIndex` + * _.lastIndexOf([1, 2, 1, 2], 2, 2); + * // => 1 + * + * // performing a binary search + * _.lastIndexOf([1, 1, 2, 2], 2, true); + * // => 3 + */function lastIndexOf(array,value,fromIndex){var length=array?array.length:0;if(!length){return-1}var index=length;if(typeof fromIndex=="number"){index=(fromIndex<0?nativeMax(length+fromIndex,0):nativeMin(fromIndex||0,length-1))+1}else if(fromIndex){index=binaryIndex(array,value,true)-1;var other=array[index];if(value===value?value===other:other!==other){return index}return-1}if(value!==value){return indexOfNaN(array,index,true)}while(index--){if(array[index]===value){return index}}return-1} +/** + * Removes all provided values from `array` using + * [`SameValueZero`](http://ecma-international.org/ecma-262/6.0/#sec-samevaluezero) + * for equality comparisons. + * + * **Note:** Unlike `_.without`, this method mutates `array`. + * + * @static + * @memberOf _ + * @category Array + * @param {Array} array The array to modify. + * @param {...*} [values] The values to remove. + * @returns {Array} Returns `array`. + * @example + * + * var array = [1, 2, 3, 1, 2, 3]; + * + * _.pull(array, 2, 3); + * console.log(array); + * // => [1, 1] + */function pull(){var args=arguments,array=args[0];if(!(array&&array.length)){return array}var index=0,indexOf=getIndexOf(),length=args.length;while(++index-1){splice.call(array,fromIndex,1)}}return array} +/** + * Removes elements from `array` corresponding to the given indexes and returns + * an array of the removed elements. Indexes may be specified as an array of + * indexes or as individual arguments. + * + * **Note:** Unlike `_.at`, this method mutates `array`. + * + * @static + * @memberOf _ + * @category Array + * @param {Array} array The array to modify. + * @param {...(number|number[])} [indexes] The indexes of elements to remove, + * specified as individual indexes or arrays of indexes. + * @returns {Array} Returns the new array of removed elements. + * @example + * + * var array = [5, 10, 15, 20]; + * var evens = _.pullAt(array, 1, 3); + * + * console.log(array); + * // => [5, 15] + * + * console.log(evens); + * // => [10, 20] + */var pullAt=restParam(function(array,indexes){indexes=baseFlatten(indexes);var result=baseAt(array,indexes);basePullAt(array,indexes.sort(baseCompareAscending));return result}); +/** + * Removes all elements from `array` that `predicate` returns truthy for + * and returns an array of the removed elements. The predicate is bound to + * `thisArg` and invoked with three arguments: (value, index, array). + * + * If a property name is provided for `predicate` the created `_.property` + * style callback returns the property value of the given element. + * + * If a value is also provided for `thisArg` the created `_.matchesProperty` + * style callback returns `true` for elements that have a matching property + * value, else `false`. + * + * If an object is provided for `predicate` the created `_.matches` style + * callback returns `true` for elements that have the properties of the given + * object, else `false`. + * + * **Note:** Unlike `_.filter`, this method mutates `array`. + * + * @static + * @memberOf _ + * @category Array + * @param {Array} array The array to modify. + * @param {Function|Object|string} [predicate=_.identity] The function invoked + * per iteration. + * @param {*} [thisArg] The `this` binding of `predicate`. + * @returns {Array} Returns the new array of removed elements. + * @example + * + * var array = [1, 2, 3, 4]; + * var evens = _.remove(array, function(n) { + * return n % 2 == 0; + * }); + * + * console.log(array); + * // => [1, 3] + * + * console.log(evens); + * // => [2, 4] + */function remove(array,predicate,thisArg){var result=[];if(!(array&&array.length)){return result}var index=-1,indexes=[],length=array.length;predicate=getCallback(predicate,thisArg,3);while(++index [2, 3] + */function rest(array){return drop(array,1)} +/** + * Creates a slice of `array` from `start` up to, but not including, `end`. + * + * **Note:** This method is used instead of `Array#slice` to support node + * lists in IE < 9 and to ensure dense arrays are returned. + * + * @static + * @memberOf _ + * @category Array + * @param {Array} array The array to slice. + * @param {number} [start=0] The start position. + * @param {number} [end=array.length] The end position. + * @returns {Array} Returns the slice of `array`. + */function slice(array,start,end){var length=array?array.length:0;if(!length){return[]}if(end&&typeof end!="number"&&isIterateeCall(array,start,end)){start=0;end=length}return baseSlice(array,start,end)} +/** + * Uses a binary search to determine the lowest index at which `value` should + * be inserted into `array` in order to maintain its sort order. If an iteratee + * function is provided it is invoked for `value` and each element of `array` + * to compute their sort ranking. The iteratee is bound to `thisArg` and + * invoked with one argument; (value). + * + * If a property name is provided for `iteratee` the created `_.property` + * style callback returns the property value of the given element. + * + * If a value is also provided for `thisArg` the created `_.matchesProperty` + * style callback returns `true` for elements that have a matching property + * value, else `false`. + * + * If an object is provided for `iteratee` the created `_.matches` style + * callback returns `true` for elements that have the properties of the given + * object, else `false`. + * + * @static + * @memberOf _ + * @category Array + * @param {Array} array The sorted array to inspect. + * @param {*} value The value to evaluate. + * @param {Function|Object|string} [iteratee=_.identity] The function invoked + * per iteration. + * @param {*} [thisArg] The `this` binding of `iteratee`. + * @returns {number} Returns the index at which `value` should be inserted + * into `array`. + * @example + * + * _.sortedIndex([30, 50], 40); + * // => 1 + * + * _.sortedIndex([4, 4, 5, 5], 5); + * // => 2 + * + * var dict = { 'data': { 'thirty': 30, 'forty': 40, 'fifty': 50 } }; + * + * // using an iteratee function + * _.sortedIndex(['thirty', 'fifty'], 'forty', function(word) { + * return this.data[word]; + * }, dict); + * // => 1 + * + * // using the `_.property` callback shorthand + * _.sortedIndex([{ 'x': 30 }, { 'x': 50 }], { 'x': 40 }, 'x'); + * // => 1 + */var sortedIndex=createSortedIndex(); +/** + * This method is like `_.sortedIndex` except that it returns the highest + * index at which `value` should be inserted into `array` in order to + * maintain its sort order. + * + * @static + * @memberOf _ + * @category Array + * @param {Array} array The sorted array to inspect. + * @param {*} value The value to evaluate. + * @param {Function|Object|string} [iteratee=_.identity] The function invoked + * per iteration. + * @param {*} [thisArg] The `this` binding of `iteratee`. + * @returns {number} Returns the index at which `value` should be inserted + * into `array`. + * @example + * + * _.sortedLastIndex([4, 4, 5, 5], 5); + * // => 4 + */var sortedLastIndex=createSortedIndex(true); +/** + * Creates a slice of `array` with `n` elements taken from the beginning. + * + * @static + * @memberOf _ + * @category Array + * @param {Array} array The array to query. + * @param {number} [n=1] The number of elements to take. + * @param- {Object} [guard] Enables use as a callback for functions like `_.map`. + * @returns {Array} Returns the slice of `array`. + * @example + * + * _.take([1, 2, 3]); + * // => [1] + * + * _.take([1, 2, 3], 2); + * // => [1, 2] + * + * _.take([1, 2, 3], 5); + * // => [1, 2, 3] + * + * _.take([1, 2, 3], 0); + * // => [] + */function take(array,n,guard){var length=array?array.length:0;if(!length){return[]}if(guard?isIterateeCall(array,n,guard):n==null){n=1}return baseSlice(array,0,n<0?0:n)} +/** + * Creates a slice of `array` with `n` elements taken from the end. + * + * @static + * @memberOf _ + * @category Array + * @param {Array} array The array to query. + * @param {number} [n=1] The number of elements to take. + * @param- {Object} [guard] Enables use as a callback for functions like `_.map`. + * @returns {Array} Returns the slice of `array`. + * @example + * + * _.takeRight([1, 2, 3]); + * // => [3] + * + * _.takeRight([1, 2, 3], 2); + * // => [2, 3] + * + * _.takeRight([1, 2, 3], 5); + * // => [1, 2, 3] + * + * _.takeRight([1, 2, 3], 0); + * // => [] + */function takeRight(array,n,guard){var length=array?array.length:0;if(!length){return[]}if(guard?isIterateeCall(array,n,guard):n==null){n=1}n=length-(+n||0);return baseSlice(array,n<0?0:n)} +/** + * Creates a slice of `array` with elements taken from the end. Elements are + * taken until `predicate` returns falsey. The predicate is bound to `thisArg` + * and invoked with three arguments: (value, index, array). + * + * If a property name is provided for `predicate` the created `_.property` + * style callback returns the property value of the given element. + * + * If a value is also provided for `thisArg` the created `_.matchesProperty` + * style callback returns `true` for elements that have a matching property + * value, else `false`. + * + * If an object is provided for `predicate` the created `_.matches` style + * callback returns `true` for elements that have the properties of the given + * object, else `false`. + * + * @static + * @memberOf _ + * @category Array + * @param {Array} array The array to query. + * @param {Function|Object|string} [predicate=_.identity] The function invoked + * per iteration. + * @param {*} [thisArg] The `this` binding of `predicate`. + * @returns {Array} Returns the slice of `array`. + * @example + * + * _.takeRightWhile([1, 2, 3], function(n) { + * return n > 1; + * }); + * // => [2, 3] + * + * var users = [ + * { 'user': 'barney', 'active': true }, + * { 'user': 'fred', 'active': false }, + * { 'user': 'pebbles', 'active': false } + * ]; + * + * // using the `_.matches` callback shorthand + * _.pluck(_.takeRightWhile(users, { 'user': 'pebbles', 'active': false }), 'user'); + * // => ['pebbles'] + * + * // using the `_.matchesProperty` callback shorthand + * _.pluck(_.takeRightWhile(users, 'active', false), 'user'); + * // => ['fred', 'pebbles'] + * + * // using the `_.property` callback shorthand + * _.pluck(_.takeRightWhile(users, 'active'), 'user'); + * // => [] + */function takeRightWhile(array,predicate,thisArg){return array&&array.length?baseWhile(array,getCallback(predicate,thisArg,3),false,true):[]} +/** + * Creates a slice of `array` with elements taken from the beginning. Elements + * are taken until `predicate` returns falsey. The predicate is bound to + * `thisArg` and invoked with three arguments: (value, index, array). + * + * If a property name is provided for `predicate` the created `_.property` + * style callback returns the property value of the given element. + * + * If a value is also provided for `thisArg` the created `_.matchesProperty` + * style callback returns `true` for elements that have a matching property + * value, else `false`. + * + * If an object is provided for `predicate` the created `_.matches` style + * callback returns `true` for elements that have the properties of the given + * object, else `false`. + * + * @static + * @memberOf _ + * @category Array + * @param {Array} array The array to query. + * @param {Function|Object|string} [predicate=_.identity] The function invoked + * per iteration. + * @param {*} [thisArg] The `this` binding of `predicate`. + * @returns {Array} Returns the slice of `array`. + * @example + * + * _.takeWhile([1, 2, 3], function(n) { + * return n < 3; + * }); + * // => [1, 2] + * + * var users = [ + * { 'user': 'barney', 'active': false }, + * { 'user': 'fred', 'active': false}, + * { 'user': 'pebbles', 'active': true } + * ]; + * + * // using the `_.matches` callback shorthand + * _.pluck(_.takeWhile(users, { 'user': 'barney', 'active': false }), 'user'); + * // => ['barney'] + * + * // using the `_.matchesProperty` callback shorthand + * _.pluck(_.takeWhile(users, 'active', false), 'user'); + * // => ['barney', 'fred'] + * + * // using the `_.property` callback shorthand + * _.pluck(_.takeWhile(users, 'active'), 'user'); + * // => [] + */function takeWhile(array,predicate,thisArg){return array&&array.length?baseWhile(array,getCallback(predicate,thisArg,3)):[]} +/** + * Creates an array of unique values, in order, from all of the provided arrays + * using [`SameValueZero`](http://ecma-international.org/ecma-262/6.0/#sec-samevaluezero) + * for equality comparisons. + * + * @static + * @memberOf _ + * @category Array + * @param {...Array} [arrays] The arrays to inspect. + * @returns {Array} Returns the new array of combined values. + * @example + * + * _.union([1, 2], [4, 2], [2, 1]); + * // => [1, 2, 4] + */var union=restParam(function(arrays){return baseUniq(baseFlatten(arrays,false,true))}); +/** + * Creates a duplicate-free version of an array, using + * [`SameValueZero`](http://ecma-international.org/ecma-262/6.0/#sec-samevaluezero) + * for equality comparisons, in which only the first occurence of each element + * is kept. Providing `true` for `isSorted` performs a faster search algorithm + * for sorted arrays. If an iteratee function is provided it is invoked for + * each element in the array to generate the criterion by which uniqueness + * is computed. The `iteratee` is bound to `thisArg` and invoked with three + * arguments: (value, index, array). + * + * If a property name is provided for `iteratee` the created `_.property` + * style callback returns the property value of the given element. + * + * If a value is also provided for `thisArg` the created `_.matchesProperty` + * style callback returns `true` for elements that have a matching property + * value, else `false`. + * + * If an object is provided for `iteratee` the created `_.matches` style + * callback returns `true` for elements that have the properties of the given + * object, else `false`. + * + * @static + * @memberOf _ + * @alias unique + * @category Array + * @param {Array} array The array to inspect. + * @param {boolean} [isSorted] Specify the array is sorted. + * @param {Function|Object|string} [iteratee] The function invoked per iteration. + * @param {*} [thisArg] The `this` binding of `iteratee`. + * @returns {Array} Returns the new duplicate-value-free array. + * @example + * + * _.uniq([2, 1, 2]); + * // => [2, 1] + * + * // using `isSorted` + * _.uniq([1, 1, 2], true); + * // => [1, 2] + * + * // using an iteratee function + * _.uniq([1, 2.5, 1.5, 2], function(n) { + * return this.floor(n); + * }, Math); + * // => [1, 2.5] + * + * // using the `_.property` callback shorthand + * _.uniq([{ 'x': 1 }, { 'x': 2 }, { 'x': 1 }], 'x'); + * // => [{ 'x': 1 }, { 'x': 2 }] + */function uniq(array,isSorted,iteratee,thisArg){var length=array?array.length:0;if(!length){return[]}if(isSorted!=null&&typeof isSorted!="boolean"){thisArg=iteratee;iteratee=isIterateeCall(array,isSorted,thisArg)?undefined:isSorted;isSorted=false}var callback=getCallback();if(!(iteratee==null&&callback===baseCallback)){iteratee=callback(iteratee,thisArg,3)}return isSorted&&getIndexOf()==baseIndexOf?sortedUniq(array,iteratee):baseUniq(array,iteratee)} +/** + * This method is like `_.zip` except that it accepts an array of grouped + * elements and creates an array regrouping the elements to their pre-zip + * configuration. + * + * @static + * @memberOf _ + * @category Array + * @param {Array} array The array of grouped elements to process. + * @returns {Array} Returns the new array of regrouped elements. + * @example + * + * var zipped = _.zip(['fred', 'barney'], [30, 40], [true, false]); + * // => [['fred', 30, true], ['barney', 40, false]] + * + * _.unzip(zipped); + * // => [['fred', 'barney'], [30, 40], [true, false]] + */function unzip(array){if(!(array&&array.length)){return[]}var index=-1,length=0;array=arrayFilter(array,function(group){if(isArrayLike(group)){length=nativeMax(group.length,length);return true}});var result=Array(length);while(++index [[1, 10, 100], [2, 20, 200]] + * + * _.unzipWith(zipped, _.add); + * // => [3, 30, 300] + */function unzipWith(array,iteratee,thisArg){var length=array?array.length:0;if(!length){return[]}var result=unzip(array);if(iteratee==null){return result}iteratee=bindCallback(iteratee,thisArg,4);return arrayMap(result,function(group){return arrayReduce(group,iteratee,undefined,true)})} +/** + * Creates an array excluding all provided values using + * [`SameValueZero`](http://ecma-international.org/ecma-262/6.0/#sec-samevaluezero) + * for equality comparisons. + * + * @static + * @memberOf _ + * @category Array + * @param {Array} array The array to filter. + * @param {...*} [values] The values to exclude. + * @returns {Array} Returns the new array of filtered values. + * @example + * + * _.without([1, 2, 1, 3], 1, 2); + * // => [3] + */var without=restParam(function(array,values){return isArrayLike(array)?baseDifference(array,values):[]}); +/** + * Creates an array of unique values that is the [symmetric difference](https://en.wikipedia.org/wiki/Symmetric_difference) + * of the provided arrays. + * + * @static + * @memberOf _ + * @category Array + * @param {...Array} [arrays] The arrays to inspect. + * @returns {Array} Returns the new array of values. + * @example + * + * _.xor([1, 2], [4, 2]); + * // => [1, 4] + */function xor(){var index=-1,length=arguments.length;while(++index [['fred', 30, true], ['barney', 40, false]] + */var zip=restParam(unzip); +/** + * The inverse of `_.pairs`; this method returns an object composed from arrays + * of property names and values. Provide either a single two dimensional array, + * e.g. `[[key1, value1], [key2, value2]]` or two arrays, one of property names + * and one of corresponding values. + * + * @static + * @memberOf _ + * @alias object + * @category Array + * @param {Array} props The property names. + * @param {Array} [values=[]] The property values. + * @returns {Object} Returns the new object. + * @example + * + * _.zipObject([['fred', 30], ['barney', 40]]); + * // => { 'fred': 30, 'barney': 40 } + * + * _.zipObject(['fred', 'barney'], [30, 40]); + * // => { 'fred': 30, 'barney': 40 } + */function zipObject(props,values){var index=-1,length=props?props.length:0,result={};if(length&&!values&&!isArray(props[0])){values=[]}while(++index [111, 222] + */var zipWith=restParam(function(arrays){var length=arrays.length,iteratee=length>2?arrays[length-2]:undefined,thisArg=length>1?arrays[length-1]:undefined;if(length>2&&typeof iteratee=="function"){length-=2}else{iteratee=length>1&&typeof thisArg=="function"?(--length,thisArg):undefined;thisArg=undefined}arrays.length=length;return unzipWith(arrays,iteratee,thisArg)}); +/*------------------------------------------------------------------------*/ +/** + * Creates a `lodash` object that wraps `value` with explicit method + * chaining enabled. + * + * @static + * @memberOf _ + * @category Chain + * @param {*} value The value to wrap. + * @returns {Object} Returns the new `lodash` wrapper instance. + * @example + * + * var users = [ + * { 'user': 'barney', 'age': 36 }, + * { 'user': 'fred', 'age': 40 }, + * { 'user': 'pebbles', 'age': 1 } + * ]; + * + * var youngest = _.chain(users) + * .sortBy('age') + * .map(function(chr) { + * return chr.user + ' is ' + chr.age; + * }) + * .first() + * .value(); + * // => 'pebbles is 1' + */function chain(value){var result=lodash(value);result.__chain__=true;return result} +/** + * This method invokes `interceptor` and returns `value`. The interceptor is + * bound to `thisArg` and invoked with one argument; (value). The purpose of + * this method is to "tap into" a method chain in order to perform operations + * on intermediate results within the chain. + * + * @static + * @memberOf _ + * @category Chain + * @param {*} value The value to provide to `interceptor`. + * @param {Function} interceptor The function to invoke. + * @param {*} [thisArg] The `this` binding of `interceptor`. + * @returns {*} Returns `value`. + * @example + * + * _([1, 2, 3]) + * .tap(function(array) { + * array.pop(); + * }) + * .reverse() + * .value(); + * // => [2, 1] + */function tap(value,interceptor,thisArg){interceptor.call(thisArg,value);return value} +/** + * This method is like `_.tap` except that it returns the result of `interceptor`. + * + * @static + * @memberOf _ + * @category Chain + * @param {*} value The value to provide to `interceptor`. + * @param {Function} interceptor The function to invoke. + * @param {*} [thisArg] The `this` binding of `interceptor`. + * @returns {*} Returns the result of `interceptor`. + * @example + * + * _(' abc ') + * .chain() + * .trim() + * .thru(function(value) { + * return [value]; + * }) + * .value(); + * // => ['abc'] + */function thru(value,interceptor,thisArg){return interceptor.call(thisArg,value)} +/** + * Enables explicit method chaining on the wrapper object. + * + * @name chain + * @memberOf _ + * @category Chain + * @returns {Object} Returns the new `lodash` wrapper instance. + * @example + * + * var users = [ + * { 'user': 'barney', 'age': 36 }, + * { 'user': 'fred', 'age': 40 } + * ]; + * + * // without explicit chaining + * _(users).first(); + * // => { 'user': 'barney', 'age': 36 } + * + * // with explicit chaining + * _(users).chain() + * .first() + * .pick('user') + * .value(); + * // => { 'user': 'barney' } + */function wrapperChain(){return chain(this)} +/** + * Executes the chained sequence and returns the wrapped result. + * + * @name commit + * @memberOf _ + * @category Chain + * @returns {Object} Returns the new `lodash` wrapper instance. + * @example + * + * var array = [1, 2]; + * var wrapped = _(array).push(3); + * + * console.log(array); + * // => [1, 2] + * + * wrapped = wrapped.commit(); + * console.log(array); + * // => [1, 2, 3] + * + * wrapped.last(); + * // => 3 + * + * console.log(array); + * // => [1, 2, 3] + */function wrapperCommit(){return new LodashWrapper(this.value(),this.__chain__)} +/** + * Creates a new array joining a wrapped array with any additional arrays + * and/or values. + * + * @name concat + * @memberOf _ + * @category Chain + * @param {...*} [values] The values to concatenate. + * @returns {Array} Returns the new concatenated array. + * @example + * + * var array = [1]; + * var wrapped = _(array).concat(2, [3], [[4]]); + * + * console.log(wrapped.value()); + * // => [1, 2, 3, [4]] + * + * console.log(array); + * // => [1] + */var wrapperConcat=restParam(function(values){values=baseFlatten(values);return this.thru(function(array){return arrayConcat(isArray(array)?array:[toObject(array)],values)})}); +/** + * Creates a clone of the chained sequence planting `value` as the wrapped value. + * + * @name plant + * @memberOf _ + * @category Chain + * @returns {Object} Returns the new `lodash` wrapper instance. + * @example + * + * var array = [1, 2]; + * var wrapped = _(array).map(function(value) { + * return Math.pow(value, 2); + * }); + * + * var other = [3, 4]; + * var otherWrapped = wrapped.plant(other); + * + * otherWrapped.value(); + * // => [9, 16] + * + * wrapped.value(); + * // => [1, 4] + */function wrapperPlant(value){var result,parent=this;while(parent instanceof baseLodash){var clone=wrapperClone(parent);if(result){previous.__wrapped__=clone}else{result=clone}var previous=clone;parent=parent.__wrapped__}previous.__wrapped__=value;return result} +/** + * Reverses the wrapped array so the first element becomes the last, the + * second element becomes the second to last, and so on. + * + * **Note:** This method mutates the wrapped array. + * + * @name reverse + * @memberOf _ + * @category Chain + * @returns {Object} Returns the new reversed `lodash` wrapper instance. + * @example + * + * var array = [1, 2, 3]; + * + * _(array).reverse().value() + * // => [3, 2, 1] + * + * console.log(array); + * // => [3, 2, 1] + */function wrapperReverse(){var value=this.__wrapped__;var interceptor=function(value){return wrapped&&wrapped.__dir__<0?value:value.reverse()};if(value instanceof LazyWrapper){var wrapped=value;if(this.__actions__.length){wrapped=new LazyWrapper(this)}wrapped=wrapped.reverse();wrapped.__actions__.push({func:thru,args:[interceptor],thisArg:undefined});return new LodashWrapper(wrapped,this.__chain__)}return this.thru(interceptor)} +/** + * Produces the result of coercing the unwrapped value to a string. + * + * @name toString + * @memberOf _ + * @category Chain + * @returns {string} Returns the coerced string value. + * @example + * + * _([1, 2, 3]).toString(); + * // => '1,2,3' + */function wrapperToString(){return this.value()+""} +/** + * Executes the chained sequence to extract the unwrapped value. + * + * @name value + * @memberOf _ + * @alias run, toJSON, valueOf + * @category Chain + * @returns {*} Returns the resolved unwrapped value. + * @example + * + * _([1, 2, 3]).value(); + * // => [1, 2, 3] + */function wrapperValue(){return baseWrapperValue(this.__wrapped__,this.__actions__)} +/*------------------------------------------------------------------------*/ +/** + * Creates an array of elements corresponding to the given keys, or indexes, + * of `collection`. Keys may be specified as individual arguments or as arrays + * of keys. + * + * @static + * @memberOf _ + * @category Collection + * @param {Array|Object|string} collection The collection to iterate over. + * @param {...(number|number[]|string|string[])} [props] The property names + * or indexes of elements to pick, specified individually or in arrays. + * @returns {Array} Returns the new array of picked elements. + * @example + * + * _.at(['a', 'b', 'c'], [0, 2]); + * // => ['a', 'c'] + * + * _.at(['barney', 'fred', 'pebbles'], 0, 2); + * // => ['barney', 'pebbles'] + */var at=restParam(function(collection,props){return baseAt(collection,baseFlatten(props))}); +/** + * Creates an object composed of keys generated from the results of running + * each element of `collection` through `iteratee`. The corresponding value + * of each key is the number of times the key was returned by `iteratee`. + * The `iteratee` is bound to `thisArg` and invoked with three arguments: + * (value, index|key, collection). + * + * If a property name is provided for `iteratee` the created `_.property` + * style callback returns the property value of the given element. + * + * If a value is also provided for `thisArg` the created `_.matchesProperty` + * style callback returns `true` for elements that have a matching property + * value, else `false`. + * + * If an object is provided for `iteratee` the created `_.matches` style + * callback returns `true` for elements that have the properties of the given + * object, else `false`. + * + * @static + * @memberOf _ + * @category Collection + * @param {Array|Object|string} collection The collection to iterate over. + * @param {Function|Object|string} [iteratee=_.identity] The function invoked + * per iteration. + * @param {*} [thisArg] The `this` binding of `iteratee`. + * @returns {Object} Returns the composed aggregate object. + * @example + * + * _.countBy([4.3, 6.1, 6.4], function(n) { + * return Math.floor(n); + * }); + * // => { '4': 1, '6': 2 } + * + * _.countBy([4.3, 6.1, 6.4], function(n) { + * return this.floor(n); + * }, Math); + * // => { '4': 1, '6': 2 } + * + * _.countBy(['one', 'two', 'three'], 'length'); + * // => { '3': 2, '5': 1 } + */var countBy=createAggregator(function(result,value,key){hasOwnProperty.call(result,key)?++result[key]:result[key]=1}); +/** + * Checks if `predicate` returns truthy for **all** elements of `collection`. + * The predicate is bound to `thisArg` and invoked with three arguments: + * (value, index|key, collection). + * + * If a property name is provided for `predicate` the created `_.property` + * style callback returns the property value of the given element. + * + * If a value is also provided for `thisArg` the created `_.matchesProperty` + * style callback returns `true` for elements that have a matching property + * value, else `false`. + * + * If an object is provided for `predicate` the created `_.matches` style + * callback returns `true` for elements that have the properties of the given + * object, else `false`. + * + * @static + * @memberOf _ + * @alias all + * @category Collection + * @param {Array|Object|string} collection The collection to iterate over. + * @param {Function|Object|string} [predicate=_.identity] The function invoked + * per iteration. + * @param {*} [thisArg] The `this` binding of `predicate`. + * @returns {boolean} Returns `true` if all elements pass the predicate check, + * else `false`. + * @example + * + * _.every([true, 1, null, 'yes'], Boolean); + * // => false + * + * var users = [ + * { 'user': 'barney', 'active': false }, + * { 'user': 'fred', 'active': false } + * ]; + * + * // using the `_.matches` callback shorthand + * _.every(users, { 'user': 'barney', 'active': false }); + * // => false + * + * // using the `_.matchesProperty` callback shorthand + * _.every(users, 'active', false); + * // => true + * + * // using the `_.property` callback shorthand + * _.every(users, 'active'); + * // => false + */function every(collection,predicate,thisArg){var func=isArray(collection)?arrayEvery:baseEvery;if(thisArg&&isIterateeCall(collection,predicate,thisArg)){predicate=undefined}if(typeof predicate!="function"||thisArg!==undefined){predicate=getCallback(predicate,thisArg,3)}return func(collection,predicate)} +/** + * Iterates over elements of `collection`, returning an array of all elements + * `predicate` returns truthy for. The predicate is bound to `thisArg` and + * invoked with three arguments: (value, index|key, collection). + * + * If a property name is provided for `predicate` the created `_.property` + * style callback returns the property value of the given element. + * + * If a value is also provided for `thisArg` the created `_.matchesProperty` + * style callback returns `true` for elements that have a matching property + * value, else `false`. + * + * If an object is provided for `predicate` the created `_.matches` style + * callback returns `true` for elements that have the properties of the given + * object, else `false`. + * + * @static + * @memberOf _ + * @alias select + * @category Collection + * @param {Array|Object|string} collection The collection to iterate over. + * @param {Function|Object|string} [predicate=_.identity] The function invoked + * per iteration. + * @param {*} [thisArg] The `this` binding of `predicate`. + * @returns {Array} Returns the new filtered array. + * @example + * + * _.filter([4, 5, 6], function(n) { + * return n % 2 == 0; + * }); + * // => [4, 6] + * + * var users = [ + * { 'user': 'barney', 'age': 36, 'active': true }, + * { 'user': 'fred', 'age': 40, 'active': false } + * ]; + * + * // using the `_.matches` callback shorthand + * _.pluck(_.filter(users, { 'age': 36, 'active': true }), 'user'); + * // => ['barney'] + * + * // using the `_.matchesProperty` callback shorthand + * _.pluck(_.filter(users, 'active', false), 'user'); + * // => ['fred'] + * + * // using the `_.property` callback shorthand + * _.pluck(_.filter(users, 'active'), 'user'); + * // => ['barney'] + */function filter(collection,predicate,thisArg){var func=isArray(collection)?arrayFilter:baseFilter;predicate=getCallback(predicate,thisArg,3);return func(collection,predicate)} +/** + * Iterates over elements of `collection`, returning the first element + * `predicate` returns truthy for. The predicate is bound to `thisArg` and + * invoked with three arguments: (value, index|key, collection). + * + * If a property name is provided for `predicate` the created `_.property` + * style callback returns the property value of the given element. + * + * If a value is also provided for `thisArg` the created `_.matchesProperty` + * style callback returns `true` for elements that have a matching property + * value, else `false`. + * + * If an object is provided for `predicate` the created `_.matches` style + * callback returns `true` for elements that have the properties of the given + * object, else `false`. + * + * @static + * @memberOf _ + * @alias detect + * @category Collection + * @param {Array|Object|string} collection The collection to search. + * @param {Function|Object|string} [predicate=_.identity] The function invoked + * per iteration. + * @param {*} [thisArg] The `this` binding of `predicate`. + * @returns {*} Returns the matched element, else `undefined`. + * @example + * + * var users = [ + * { 'user': 'barney', 'age': 36, 'active': true }, + * { 'user': 'fred', 'age': 40, 'active': false }, + * { 'user': 'pebbles', 'age': 1, 'active': true } + * ]; + * + * _.result(_.find(users, function(chr) { + * return chr.age < 40; + * }), 'user'); + * // => 'barney' + * + * // using the `_.matches` callback shorthand + * _.result(_.find(users, { 'age': 1, 'active': true }), 'user'); + * // => 'pebbles' + * + * // using the `_.matchesProperty` callback shorthand + * _.result(_.find(users, 'active', false), 'user'); + * // => 'fred' + * + * // using the `_.property` callback shorthand + * _.result(_.find(users, 'active'), 'user'); + * // => 'barney' + */var find=createFind(baseEach); +/** + * This method is like `_.find` except that it iterates over elements of + * `collection` from right to left. + * + * @static + * @memberOf _ + * @category Collection + * @param {Array|Object|string} collection The collection to search. + * @param {Function|Object|string} [predicate=_.identity] The function invoked + * per iteration. + * @param {*} [thisArg] The `this` binding of `predicate`. + * @returns {*} Returns the matched element, else `undefined`. + * @example + * + * _.findLast([1, 2, 3, 4], function(n) { + * return n % 2 == 1; + * }); + * // => 3 + */var findLast=createFind(baseEachRight,true); +/** + * Performs a deep comparison between each element in `collection` and the + * source object, returning the first element that has equivalent property + * values. + * + * **Note:** This method supports comparing arrays, booleans, `Date` objects, + * numbers, `Object` objects, regexes, and strings. Objects are compared by + * their own, not inherited, enumerable properties. For comparing a single + * own or inherited property value see `_.matchesProperty`. + * + * @static + * @memberOf _ + * @category Collection + * @param {Array|Object|string} collection The collection to search. + * @param {Object} source The object of property values to match. + * @returns {*} Returns the matched element, else `undefined`. + * @example + * + * var users = [ + * { 'user': 'barney', 'age': 36, 'active': true }, + * { 'user': 'fred', 'age': 40, 'active': false } + * ]; + * + * _.result(_.findWhere(users, { 'age': 36, 'active': true }), 'user'); + * // => 'barney' + * + * _.result(_.findWhere(users, { 'age': 40, 'active': false }), 'user'); + * // => 'fred' + */function findWhere(collection,source){return find(collection,baseMatches(source))} +/** + * Iterates over elements of `collection` invoking `iteratee` for each element. + * The `iteratee` is bound to `thisArg` and invoked with three arguments: + * (value, index|key, collection). Iteratee functions may exit iteration early + * by explicitly returning `false`. + * + * **Note:** As with other "Collections" methods, objects with a "length" property + * are iterated like arrays. To avoid this behavior `_.forIn` or `_.forOwn` + * may be used for object iteration. + * + * @static + * @memberOf _ + * @alias each + * @category Collection + * @param {Array|Object|string} collection The collection to iterate over. + * @param {Function} [iteratee=_.identity] The function invoked per iteration. + * @param {*} [thisArg] The `this` binding of `iteratee`. + * @returns {Array|Object|string} Returns `collection`. + * @example + * + * _([1, 2]).forEach(function(n) { + * console.log(n); + * }).value(); + * // => logs each value from left to right and returns the array + * + * _.forEach({ 'a': 1, 'b': 2 }, function(n, key) { + * console.log(n, key); + * }); + * // => logs each value-key pair and returns the object (iteration order is not guaranteed) + */var forEach=createForEach(arrayEach,baseEach); +/** + * This method is like `_.forEach` except that it iterates over elements of + * `collection` from right to left. + * + * @static + * @memberOf _ + * @alias eachRight + * @category Collection + * @param {Array|Object|string} collection The collection to iterate over. + * @param {Function} [iteratee=_.identity] The function invoked per iteration. + * @param {*} [thisArg] The `this` binding of `iteratee`. + * @returns {Array|Object|string} Returns `collection`. + * @example + * + * _([1, 2]).forEachRight(function(n) { + * console.log(n); + * }).value(); + * // => logs each value from right to left and returns the array + */var forEachRight=createForEach(arrayEachRight,baseEachRight); +/** + * Creates an object composed of keys generated from the results of running + * each element of `collection` through `iteratee`. The corresponding value + * of each key is an array of the elements responsible for generating the key. + * The `iteratee` is bound to `thisArg` and invoked with three arguments: + * (value, index|key, collection). + * + * If a property name is provided for `iteratee` the created `_.property` + * style callback returns the property value of the given element. + * + * If a value is also provided for `thisArg` the created `_.matchesProperty` + * style callback returns `true` for elements that have a matching property + * value, else `false`. + * + * If an object is provided for `iteratee` the created `_.matches` style + * callback returns `true` for elements that have the properties of the given + * object, else `false`. + * + * @static + * @memberOf _ + * @category Collection + * @param {Array|Object|string} collection The collection to iterate over. + * @param {Function|Object|string} [iteratee=_.identity] The function invoked + * per iteration. + * @param {*} [thisArg] The `this` binding of `iteratee`. + * @returns {Object} Returns the composed aggregate object. + * @example + * + * _.groupBy([4.2, 6.1, 6.4], function(n) { + * return Math.floor(n); + * }); + * // => { '4': [4.2], '6': [6.1, 6.4] } + * + * _.groupBy([4.2, 6.1, 6.4], function(n) { + * return this.floor(n); + * }, Math); + * // => { '4': [4.2], '6': [6.1, 6.4] } + * + * // using the `_.property` callback shorthand + * _.groupBy(['one', 'two', 'three'], 'length'); + * // => { '3': ['one', 'two'], '5': ['three'] } + */var groupBy=createAggregator(function(result,value,key){if(hasOwnProperty.call(result,key)){result[key].push(value)}else{result[key]=[value]}}); +/** + * Checks if `value` is in `collection` using + * [`SameValueZero`](http://ecma-international.org/ecma-262/6.0/#sec-samevaluezero) + * for equality comparisons. If `fromIndex` is negative, it is used as the offset + * from the end of `collection`. + * + * @static + * @memberOf _ + * @alias contains, include + * @category Collection + * @param {Array|Object|string} collection The collection to search. + * @param {*} target The value to search for. + * @param {number} [fromIndex=0] The index to search from. + * @param- {Object} [guard] Enables use as a callback for functions like `_.reduce`. + * @returns {boolean} Returns `true` if a matching element is found, else `false`. + * @example + * + * _.includes([1, 2, 3], 1); + * // => true + * + * _.includes([1, 2, 3], 1, 2); + * // => false + * + * _.includes({ 'user': 'fred', 'age': 40 }, 'fred'); + * // => true + * + * _.includes('pebbles', 'eb'); + * // => true + */function includes(collection,target,fromIndex,guard){var length=collection?getLength(collection):0;if(!isLength(length)){collection=values(collection);length=collection.length}if(typeof fromIndex!="number"||guard&&isIterateeCall(target,fromIndex,guard)){fromIndex=0}else{fromIndex=fromIndex<0?nativeMax(length+fromIndex,0):fromIndex||0}return typeof collection=="string"||!isArray(collection)&&isString(collection)?fromIndex<=length&&collection.indexOf(target,fromIndex)>-1:!!length&&getIndexOf(collection,target,fromIndex)>-1} +/** + * Creates an object composed of keys generated from the results of running + * each element of `collection` through `iteratee`. The corresponding value + * of each key is the last element responsible for generating the key. The + * iteratee function is bound to `thisArg` and invoked with three arguments: + * (value, index|key, collection). + * + * If a property name is provided for `iteratee` the created `_.property` + * style callback returns the property value of the given element. + * + * If a value is also provided for `thisArg` the created `_.matchesProperty` + * style callback returns `true` for elements that have a matching property + * value, else `false`. + * + * If an object is provided for `iteratee` the created `_.matches` style + * callback returns `true` for elements that have the properties of the given + * object, else `false`. + * + * @static + * @memberOf _ + * @category Collection + * @param {Array|Object|string} collection The collection to iterate over. + * @param {Function|Object|string} [iteratee=_.identity] The function invoked + * per iteration. + * @param {*} [thisArg] The `this` binding of `iteratee`. + * @returns {Object} Returns the composed aggregate object. + * @example + * + * var keyData = [ + * { 'dir': 'left', 'code': 97 }, + * { 'dir': 'right', 'code': 100 } + * ]; + * + * _.indexBy(keyData, 'dir'); + * // => { 'left': { 'dir': 'left', 'code': 97 }, 'right': { 'dir': 'right', 'code': 100 } } + * + * _.indexBy(keyData, function(object) { + * return String.fromCharCode(object.code); + * }); + * // => { 'a': { 'dir': 'left', 'code': 97 }, 'd': { 'dir': 'right', 'code': 100 } } + * + * _.indexBy(keyData, function(object) { + * return this.fromCharCode(object.code); + * }, String); + * // => { 'a': { 'dir': 'left', 'code': 97 }, 'd': { 'dir': 'right', 'code': 100 } } + */var indexBy=createAggregator(function(result,value,key){result[key]=value}); +/** + * Invokes the method at `path` of each element in `collection`, returning + * an array of the results of each invoked method. Any additional arguments + * are provided to each invoked method. If `methodName` is a function it is + * invoked for, and `this` bound to, each element in `collection`. + * + * @static + * @memberOf _ + * @category Collection + * @param {Array|Object|string} collection The collection to iterate over. + * @param {Array|Function|string} path The path of the method to invoke or + * the function invoked per iteration. + * @param {...*} [args] The arguments to invoke the method with. + * @returns {Array} Returns the array of results. + * @example + * + * _.invoke([[5, 1, 7], [3, 2, 1]], 'sort'); + * // => [[1, 5, 7], [1, 2, 3]] + * + * _.invoke([123, 456], String.prototype.split, ''); + * // => [['1', '2', '3'], ['4', '5', '6']] + */var invoke=restParam(function(collection,path,args){var index=-1,isFunc=typeof path=="function",isProp=isKey(path),result=isArrayLike(collection)?Array(collection.length):[];baseEach(collection,function(value){var func=isFunc?path:isProp&&value!=null?value[path]:undefined;result[++index]=func?func.apply(value,args):invokePath(value,path,args)});return result}); +/** + * Creates an array of values by running each element in `collection` through + * `iteratee`. The `iteratee` is bound to `thisArg` and invoked with three + * arguments: (value, index|key, collection). + * + * If a property name is provided for `iteratee` the created `_.property` + * style callback returns the property value of the given element. + * + * If a value is also provided for `thisArg` the created `_.matchesProperty` + * style callback returns `true` for elements that have a matching property + * value, else `false`. + * + * If an object is provided for `iteratee` the created `_.matches` style + * callback returns `true` for elements that have the properties of the given + * object, else `false`. + * + * Many lodash methods are guarded to work as iteratees for methods like + * `_.every`, `_.filter`, `_.map`, `_.mapValues`, `_.reject`, and `_.some`. + * + * The guarded methods are: + * `ary`, `callback`, `chunk`, `clone`, `create`, `curry`, `curryRight`, + * `drop`, `dropRight`, `every`, `fill`, `flatten`, `invert`, `max`, `min`, + * `parseInt`, `slice`, `sortBy`, `take`, `takeRight`, `template`, `trim`, + * `trimLeft`, `trimRight`, `trunc`, `random`, `range`, `sample`, `some`, + * `sum`, `uniq`, and `words` + * + * @static + * @memberOf _ + * @alias collect + * @category Collection + * @param {Array|Object|string} collection The collection to iterate over. + * @param {Function|Object|string} [iteratee=_.identity] The function invoked + * per iteration. + * @param {*} [thisArg] The `this` binding of `iteratee`. + * @returns {Array} Returns the new mapped array. + * @example + * + * function timesThree(n) { + * return n * 3; + * } + * + * _.map([1, 2], timesThree); + * // => [3, 6] + * + * _.map({ 'a': 1, 'b': 2 }, timesThree); + * // => [3, 6] (iteration order is not guaranteed) + * + * var users = [ + * { 'user': 'barney' }, + * { 'user': 'fred' } + * ]; + * + * // using the `_.property` callback shorthand + * _.map(users, 'user'); + * // => ['barney', 'fred'] + */function map(collection,iteratee,thisArg){var func=isArray(collection)?arrayMap:baseMap;iteratee=getCallback(iteratee,thisArg,3);return func(collection,iteratee)} +/** + * Creates an array of elements split into two groups, the first of which + * contains elements `predicate` returns truthy for, while the second of which + * contains elements `predicate` returns falsey for. The predicate is bound + * to `thisArg` and invoked with three arguments: (value, index|key, collection). + * + * If a property name is provided for `predicate` the created `_.property` + * style callback returns the property value of the given element. + * + * If a value is also provided for `thisArg` the created `_.matchesProperty` + * style callback returns `true` for elements that have a matching property + * value, else `false`. + * + * If an object is provided for `predicate` the created `_.matches` style + * callback returns `true` for elements that have the properties of the given + * object, else `false`. + * + * @static + * @memberOf _ + * @category Collection + * @param {Array|Object|string} collection The collection to iterate over. + * @param {Function|Object|string} [predicate=_.identity] The function invoked + * per iteration. + * @param {*} [thisArg] The `this` binding of `predicate`. + * @returns {Array} Returns the array of grouped elements. + * @example + * + * _.partition([1, 2, 3], function(n) { + * return n % 2; + * }); + * // => [[1, 3], [2]] + * + * _.partition([1.2, 2.3, 3.4], function(n) { + * return this.floor(n) % 2; + * }, Math); + * // => [[1.2, 3.4], [2.3]] + * + * var users = [ + * { 'user': 'barney', 'age': 36, 'active': false }, + * { 'user': 'fred', 'age': 40, 'active': true }, + * { 'user': 'pebbles', 'age': 1, 'active': false } + * ]; + * + * var mapper = function(array) { + * return _.pluck(array, 'user'); + * }; + * + * // using the `_.matches` callback shorthand + * _.map(_.partition(users, { 'age': 1, 'active': false }), mapper); + * // => [['pebbles'], ['barney', 'fred']] + * + * // using the `_.matchesProperty` callback shorthand + * _.map(_.partition(users, 'active', false), mapper); + * // => [['barney', 'pebbles'], ['fred']] + * + * // using the `_.property` callback shorthand + * _.map(_.partition(users, 'active'), mapper); + * // => [['fred'], ['barney', 'pebbles']] + */var partition=createAggregator(function(result,value,key){result[key?0:1].push(value)},function(){return[[],[]]}); +/** + * Gets the property value of `path` from all elements in `collection`. + * + * @static + * @memberOf _ + * @category Collection + * @param {Array|Object|string} collection The collection to iterate over. + * @param {Array|string} path The path of the property to pluck. + * @returns {Array} Returns the property values. + * @example + * + * var users = [ + * { 'user': 'barney', 'age': 36 }, + * { 'user': 'fred', 'age': 40 } + * ]; + * + * _.pluck(users, 'user'); + * // => ['barney', 'fred'] + * + * var userIndex = _.indexBy(users, 'user'); + * _.pluck(userIndex, 'age'); + * // => [36, 40] (iteration order is not guaranteed) + */function pluck(collection,path){return map(collection,property(path))} +/** + * Reduces `collection` to a value which is the accumulated result of running + * each element in `collection` through `iteratee`, where each successive + * invocation is supplied the return value of the previous. If `accumulator` + * is not provided the first element of `collection` is used as the initial + * value. The `iteratee` is bound to `thisArg` and invoked with four arguments: + * (accumulator, value, index|key, collection). + * + * Many lodash methods are guarded to work as iteratees for methods like + * `_.reduce`, `_.reduceRight`, and `_.transform`. + * + * The guarded methods are: + * `assign`, `defaults`, `defaultsDeep`, `includes`, `merge`, `sortByAll`, + * and `sortByOrder` + * + * @static + * @memberOf _ + * @alias foldl, inject + * @category Collection + * @param {Array|Object|string} collection The collection to iterate over. + * @param {Function} [iteratee=_.identity] The function invoked per iteration. + * @param {*} [accumulator] The initial value. + * @param {*} [thisArg] The `this` binding of `iteratee`. + * @returns {*} Returns the accumulated value. + * @example + * + * _.reduce([1, 2], function(total, n) { + * return total + n; + * }); + * // => 3 + * + * _.reduce({ 'a': 1, 'b': 2 }, function(result, n, key) { + * result[key] = n * 3; + * return result; + * }, {}); + * // => { 'a': 3, 'b': 6 } (iteration order is not guaranteed) + */var reduce=createReduce(arrayReduce,baseEach); +/** + * This method is like `_.reduce` except that it iterates over elements of + * `collection` from right to left. + * + * @static + * @memberOf _ + * @alias foldr + * @category Collection + * @param {Array|Object|string} collection The collection to iterate over. + * @param {Function} [iteratee=_.identity] The function invoked per iteration. + * @param {*} [accumulator] The initial value. + * @param {*} [thisArg] The `this` binding of `iteratee`. + * @returns {*} Returns the accumulated value. + * @example + * + * var array = [[0, 1], [2, 3], [4, 5]]; + * + * _.reduceRight(array, function(flattened, other) { + * return flattened.concat(other); + * }, []); + * // => [4, 5, 2, 3, 0, 1] + */var reduceRight=createReduce(arrayReduceRight,baseEachRight); +/** + * The opposite of `_.filter`; this method returns the elements of `collection` + * that `predicate` does **not** return truthy for. + * + * @static + * @memberOf _ + * @category Collection + * @param {Array|Object|string} collection The collection to iterate over. + * @param {Function|Object|string} [predicate=_.identity] The function invoked + * per iteration. + * @param {*} [thisArg] The `this` binding of `predicate`. + * @returns {Array} Returns the new filtered array. + * @example + * + * _.reject([1, 2, 3, 4], function(n) { + * return n % 2 == 0; + * }); + * // => [1, 3] + * + * var users = [ + * { 'user': 'barney', 'age': 36, 'active': false }, + * { 'user': 'fred', 'age': 40, 'active': true } + * ]; + * + * // using the `_.matches` callback shorthand + * _.pluck(_.reject(users, { 'age': 40, 'active': true }), 'user'); + * // => ['barney'] + * + * // using the `_.matchesProperty` callback shorthand + * _.pluck(_.reject(users, 'active', false), 'user'); + * // => ['fred'] + * + * // using the `_.property` callback shorthand + * _.pluck(_.reject(users, 'active'), 'user'); + * // => ['barney'] + */function reject(collection,predicate,thisArg){var func=isArray(collection)?arrayFilter:baseFilter;predicate=getCallback(predicate,thisArg,3);return func(collection,function(value,index,collection){return!predicate(value,index,collection)})} +/** + * Gets a random element or `n` random elements from a collection. + * + * @static + * @memberOf _ + * @category Collection + * @param {Array|Object|string} collection The collection to sample. + * @param {number} [n] The number of elements to sample. + * @param- {Object} [guard] Enables use as a callback for functions like `_.map`. + * @returns {*} Returns the random sample(s). + * @example + * + * _.sample([1, 2, 3, 4]); + * // => 2 + * + * _.sample([1, 2, 3, 4], 2); + * // => [3, 1] + */function sample(collection,n,guard){if(guard?isIterateeCall(collection,n,guard):n==null){collection=toIterable(collection);var length=collection.length;return length>0?collection[baseRandom(0,length-1)]:undefined}var index=-1,result=toArray(collection),length=result.length,lastIndex=length-1;n=nativeMin(n<0?0:+n||0,length);while(++index [4, 1, 3, 2] + */function shuffle(collection){return sample(collection,POSITIVE_INFINITY)} +/** + * Gets the size of `collection` by returning its length for array-like + * values or the number of own enumerable properties for objects. + * + * @static + * @memberOf _ + * @category Collection + * @param {Array|Object|string} collection The collection to inspect. + * @returns {number} Returns the size of `collection`. + * @example + * + * _.size([1, 2, 3]); + * // => 3 + * + * _.size({ 'a': 1, 'b': 2 }); + * // => 2 + * + * _.size('pebbles'); + * // => 7 + */function size(collection){var length=collection?getLength(collection):0;return isLength(length)?length:keys(collection).length} +/** + * Checks if `predicate` returns truthy for **any** element of `collection`. + * The function returns as soon as it finds a passing value and does not iterate + * over the entire collection. The predicate is bound to `thisArg` and invoked + * with three arguments: (value, index|key, collection). + * + * If a property name is provided for `predicate` the created `_.property` + * style callback returns the property value of the given element. + * + * If a value is also provided for `thisArg` the created `_.matchesProperty` + * style callback returns `true` for elements that have a matching property + * value, else `false`. + * + * If an object is provided for `predicate` the created `_.matches` style + * callback returns `true` for elements that have the properties of the given + * object, else `false`. + * + * @static + * @memberOf _ + * @alias any + * @category Collection + * @param {Array|Object|string} collection The collection to iterate over. + * @param {Function|Object|string} [predicate=_.identity] The function invoked + * per iteration. + * @param {*} [thisArg] The `this` binding of `predicate`. + * @returns {boolean} Returns `true` if any element passes the predicate check, + * else `false`. + * @example + * + * _.some([null, 0, 'yes', false], Boolean); + * // => true + * + * var users = [ + * { 'user': 'barney', 'active': true }, + * { 'user': 'fred', 'active': false } + * ]; + * + * // using the `_.matches` callback shorthand + * _.some(users, { 'user': 'barney', 'active': false }); + * // => false + * + * // using the `_.matchesProperty` callback shorthand + * _.some(users, 'active', false); + * // => true + * + * // using the `_.property` callback shorthand + * _.some(users, 'active'); + * // => true + */function some(collection,predicate,thisArg){var func=isArray(collection)?arraySome:baseSome;if(thisArg&&isIterateeCall(collection,predicate,thisArg)){predicate=undefined}if(typeof predicate!="function"||thisArg!==undefined){predicate=getCallback(predicate,thisArg,3)}return func(collection,predicate)} +/** + * Creates an array of elements, sorted in ascending order by the results of + * running each element in a collection through `iteratee`. This method performs + * a stable sort, that is, it preserves the original sort order of equal elements. + * The `iteratee` is bound to `thisArg` and invoked with three arguments: + * (value, index|key, collection). + * + * If a property name is provided for `iteratee` the created `_.property` + * style callback returns the property value of the given element. + * + * If a value is also provided for `thisArg` the created `_.matchesProperty` + * style callback returns `true` for elements that have a matching property + * value, else `false`. + * + * If an object is provided for `iteratee` the created `_.matches` style + * callback returns `true` for elements that have the properties of the given + * object, else `false`. + * + * @static + * @memberOf _ + * @category Collection + * @param {Array|Object|string} collection The collection to iterate over. + * @param {Function|Object|string} [iteratee=_.identity] The function invoked + * per iteration. + * @param {*} [thisArg] The `this` binding of `iteratee`. + * @returns {Array} Returns the new sorted array. + * @example + * + * _.sortBy([1, 2, 3], function(n) { + * return Math.sin(n); + * }); + * // => [3, 1, 2] + * + * _.sortBy([1, 2, 3], function(n) { + * return this.sin(n); + * }, Math); + * // => [3, 1, 2] + * + * var users = [ + * { 'user': 'fred' }, + * { 'user': 'pebbles' }, + * { 'user': 'barney' } + * ]; + * + * // using the `_.property` callback shorthand + * _.pluck(_.sortBy(users, 'user'), 'user'); + * // => ['barney', 'fred', 'pebbles'] + */function sortBy(collection,iteratee,thisArg){if(collection==null){return[]}if(thisArg&&isIterateeCall(collection,iteratee,thisArg)){iteratee=undefined}var index=-1;iteratee=getCallback(iteratee,thisArg,3);var result=baseMap(collection,function(value,key,collection){return{criteria:iteratee(value,key,collection),index:++index,value:value}});return baseSortBy(result,compareAscending)} +/** + * This method is like `_.sortBy` except that it can sort by multiple iteratees + * or property names. + * + * If a property name is provided for an iteratee the created `_.property` + * style callback returns the property value of the given element. + * + * If an object is provided for an iteratee the created `_.matches` style + * callback returns `true` for elements that have the properties of the given + * object, else `false`. + * + * @static + * @memberOf _ + * @category Collection + * @param {Array|Object|string} collection The collection to iterate over. + * @param {...(Function|Function[]|Object|Object[]|string|string[])} iteratees + * The iteratees to sort by, specified as individual values or arrays of values. + * @returns {Array} Returns the new sorted array. + * @example + * + * var users = [ + * { 'user': 'fred', 'age': 48 }, + * { 'user': 'barney', 'age': 36 }, + * { 'user': 'fred', 'age': 42 }, + * { 'user': 'barney', 'age': 34 } + * ]; + * + * _.map(_.sortByAll(users, ['user', 'age']), _.values); + * // => [['barney', 34], ['barney', 36], ['fred', 42], ['fred', 48]] + * + * _.map(_.sortByAll(users, 'user', function(chr) { + * return Math.floor(chr.age / 10); + * }), _.values); + * // => [['barney', 36], ['barney', 34], ['fred', 48], ['fred', 42]] + */var sortByAll=restParam(function(collection,iteratees){if(collection==null){return[]}var guard=iteratees[2];if(guard&&isIterateeCall(iteratees[0],iteratees[1],guard)){iteratees.length=1}return baseSortByOrder(collection,baseFlatten(iteratees),[])}); +/** + * This method is like `_.sortByAll` except that it allows specifying the + * sort orders of the iteratees to sort by. If `orders` is unspecified, all + * values are sorted in ascending order. Otherwise, a value is sorted in + * ascending order if its corresponding order is "asc", and descending if "desc". + * + * If a property name is provided for an iteratee the created `_.property` + * style callback returns the property value of the given element. + * + * If an object is provided for an iteratee the created `_.matches` style + * callback returns `true` for elements that have the properties of the given + * object, else `false`. + * + * @static + * @memberOf _ + * @category Collection + * @param {Array|Object|string} collection The collection to iterate over. + * @param {Function[]|Object[]|string[]} iteratees The iteratees to sort by. + * @param {boolean[]} [orders] The sort orders of `iteratees`. + * @param- {Object} [guard] Enables use as a callback for functions like `_.reduce`. + * @returns {Array} Returns the new sorted array. + * @example + * + * var users = [ + * { 'user': 'fred', 'age': 48 }, + * { 'user': 'barney', 'age': 34 }, + * { 'user': 'fred', 'age': 42 }, + * { 'user': 'barney', 'age': 36 } + * ]; + * + * // sort by `user` in ascending order and by `age` in descending order + * _.map(_.sortByOrder(users, ['user', 'age'], ['asc', 'desc']), _.values); + * // => [['barney', 36], ['barney', 34], ['fred', 48], ['fred', 42]] + */function sortByOrder(collection,iteratees,orders,guard){if(collection==null){return[]}if(guard&&isIterateeCall(iteratees,orders,guard)){orders=undefined}if(!isArray(iteratees)){iteratees=iteratees==null?[]:[iteratees]}if(!isArray(orders)){orders=orders==null?[]:[orders]}return baseSortByOrder(collection,iteratees,orders)} +/** + * Performs a deep comparison between each element in `collection` and the + * source object, returning an array of all elements that have equivalent + * property values. + * + * **Note:** This method supports comparing arrays, booleans, `Date` objects, + * numbers, `Object` objects, regexes, and strings. Objects are compared by + * their own, not inherited, enumerable properties. For comparing a single + * own or inherited property value see `_.matchesProperty`. + * + * @static + * @memberOf _ + * @category Collection + * @param {Array|Object|string} collection The collection to search. + * @param {Object} source The object of property values to match. + * @returns {Array} Returns the new filtered array. + * @example + * + * var users = [ + * { 'user': 'barney', 'age': 36, 'active': false, 'pets': ['hoppy'] }, + * { 'user': 'fred', 'age': 40, 'active': true, 'pets': ['baby puss', 'dino'] } + * ]; + * + * _.pluck(_.where(users, { 'age': 36, 'active': false }), 'user'); + * // => ['barney'] + * + * _.pluck(_.where(users, { 'pets': ['dino'] }), 'user'); + * // => ['fred'] + */function where(collection,source){return filter(collection,baseMatches(source))} +/*------------------------------------------------------------------------*/ +/** + * Gets the number of milliseconds that have elapsed since the Unix epoch + * (1 January 1970 00:00:00 UTC). + * + * @static + * @memberOf _ + * @category Date + * @example + * + * _.defer(function(stamp) { + * console.log(_.now() - stamp); + * }, _.now()); + * // => logs the number of milliseconds it took for the deferred function to be invoked + */var now=nativeNow||function(){return(new Date).getTime()}; +/*------------------------------------------------------------------------*/ +/** + * The opposite of `_.before`; this method creates a function that invokes + * `func` once it is called `n` or more times. + * + * @static + * @memberOf _ + * @category Function + * @param {number} n The number of calls before `func` is invoked. + * @param {Function} func The function to restrict. + * @returns {Function} Returns the new restricted function. + * @example + * + * var saves = ['profile', 'settings']; + * + * var done = _.after(saves.length, function() { + * console.log('done saving!'); + * }); + * + * _.forEach(saves, function(type) { + * asyncSave({ 'type': type, 'complete': done }); + * }); + * // => logs 'done saving!' after the two async saves have completed + */function after(n,func){if(typeof func!="function"){if(typeof n=="function"){var temp=n;n=func;func=temp}else{throw new TypeError(FUNC_ERROR_TEXT)}}n=nativeIsFinite(n=+n)?n:0;return function(){if(--n<1){return func.apply(this,arguments)}}} +/** + * Creates a function that accepts up to `n` arguments ignoring any + * additional arguments. + * + * @static + * @memberOf _ + * @category Function + * @param {Function} func The function to cap arguments for. + * @param {number} [n=func.length] The arity cap. + * @param- {Object} [guard] Enables use as a callback for functions like `_.map`. + * @returns {Function} Returns the new function. + * @example + * + * _.map(['6', '8', '10'], _.ary(parseInt, 1)); + * // => [6, 8, 10] + */function ary(func,n,guard){if(guard&&isIterateeCall(func,n,guard)){n=undefined}n=func&&n==null?func.length:nativeMax(+n||0,0);return createWrapper(func,ARY_FLAG,undefined,undefined,undefined,undefined,n)} +/** + * Creates a function that invokes `func`, with the `this` binding and arguments + * of the created function, while it is called less than `n` times. Subsequent + * calls to the created function return the result of the last `func` invocation. + * + * @static + * @memberOf _ + * @category Function + * @param {number} n The number of calls at which `func` is no longer invoked. + * @param {Function} func The function to restrict. + * @returns {Function} Returns the new restricted function. + * @example + * + * jQuery('#add').on('click', _.before(5, addContactToList)); + * // => allows adding up to 4 contacts to the list + */function before(n,func){var result;if(typeof func!="function"){if(typeof n=="function"){var temp=n;n=func;func=temp}else{throw new TypeError(FUNC_ERROR_TEXT)}}return function(){if(--n>0){result=func.apply(this,arguments)}if(n<=1){func=undefined}return result}} +/** + * Creates a function that invokes `func` with the `this` binding of `thisArg` + * and prepends any additional `_.bind` arguments to those provided to the + * bound function. + * + * The `_.bind.placeholder` value, which defaults to `_` in monolithic builds, + * may be used as a placeholder for partially applied arguments. + * + * **Note:** Unlike native `Function#bind` this method does not set the "length" + * property of bound functions. + * + * @static + * @memberOf _ + * @category Function + * @param {Function} func The function to bind. + * @param {*} thisArg The `this` binding of `func`. + * @param {...*} [partials] The arguments to be partially applied. + * @returns {Function} Returns the new bound function. + * @example + * + * var greet = function(greeting, punctuation) { + * return greeting + ' ' + this.user + punctuation; + * }; + * + * var object = { 'user': 'fred' }; + * + * var bound = _.bind(greet, object, 'hi'); + * bound('!'); + * // => 'hi fred!' + * + * // using placeholders + * var bound = _.bind(greet, object, _, '!'); + * bound('hi'); + * // => 'hi fred!' + */var bind=restParam(function(func,thisArg,partials){var bitmask=BIND_FLAG;if(partials.length){var holders=replaceHolders(partials,bind.placeholder);bitmask|=PARTIAL_FLAG}return createWrapper(func,bitmask,thisArg,partials,holders)}); +/** + * Binds methods of an object to the object itself, overwriting the existing + * method. Method names may be specified as individual arguments or as arrays + * of method names. If no method names are provided all enumerable function + * properties, own and inherited, of `object` are bound. + * + * **Note:** This method does not set the "length" property of bound functions. + * + * @static + * @memberOf _ + * @category Function + * @param {Object} object The object to bind and assign the bound methods to. + * @param {...(string|string[])} [methodNames] The object method names to bind, + * specified as individual method names or arrays of method names. + * @returns {Object} Returns `object`. + * @example + * + * var view = { + * 'label': 'docs', + * 'onClick': function() { + * console.log('clicked ' + this.label); + * } + * }; + * + * _.bindAll(view); + * jQuery('#docs').on('click', view.onClick); + * // => logs 'clicked docs' when the element is clicked + */var bindAll=restParam(function(object,methodNames){methodNames=methodNames.length?baseFlatten(methodNames):functions(object);var index=-1,length=methodNames.length;while(++index 'hi fred!' + * + * object.greet = function(greeting, punctuation) { + * return greeting + 'ya ' + this.user + punctuation; + * }; + * + * bound('!'); + * // => 'hiya fred!' + * + * // using placeholders + * var bound = _.bindKey(object, 'greet', _, '!'); + * bound('hi'); + * // => 'hiya fred!' + */var bindKey=restParam(function(object,key,partials){var bitmask=BIND_FLAG|BIND_KEY_FLAG;if(partials.length){var holders=replaceHolders(partials,bindKey.placeholder);bitmask|=PARTIAL_FLAG}return createWrapper(key,bitmask,object,partials,holders)}); +/** + * Creates a function that accepts one or more arguments of `func` that when + * called either invokes `func` returning its result, if all `func` arguments + * have been provided, or returns a function that accepts one or more of the + * remaining `func` arguments, and so on. The arity of `func` may be specified + * if `func.length` is not sufficient. + * + * The `_.curry.placeholder` value, which defaults to `_` in monolithic builds, + * may be used as a placeholder for provided arguments. + * + * **Note:** This method does not set the "length" property of curried functions. + * + * @static + * @memberOf _ + * @category Function + * @param {Function} func The function to curry. + * @param {number} [arity=func.length] The arity of `func`. + * @param- {Object} [guard] Enables use as a callback for functions like `_.map`. + * @returns {Function} Returns the new curried function. + * @example + * + * var abc = function(a, b, c) { + * return [a, b, c]; + * }; + * + * var curried = _.curry(abc); + * + * curried(1)(2)(3); + * // => [1, 2, 3] + * + * curried(1, 2)(3); + * // => [1, 2, 3] + * + * curried(1, 2, 3); + * // => [1, 2, 3] + * + * // using placeholders + * curried(1)(_, 3)(2); + * // => [1, 2, 3] + */var curry=createCurry(CURRY_FLAG); +/** + * This method is like `_.curry` except that arguments are applied to `func` + * in the manner of `_.partialRight` instead of `_.partial`. + * + * The `_.curryRight.placeholder` value, which defaults to `_` in monolithic + * builds, may be used as a placeholder for provided arguments. + * + * **Note:** This method does not set the "length" property of curried functions. + * + * @static + * @memberOf _ + * @category Function + * @param {Function} func The function to curry. + * @param {number} [arity=func.length] The arity of `func`. + * @param- {Object} [guard] Enables use as a callback for functions like `_.map`. + * @returns {Function} Returns the new curried function. + * @example + * + * var abc = function(a, b, c) { + * return [a, b, c]; + * }; + * + * var curried = _.curryRight(abc); + * + * curried(3)(2)(1); + * // => [1, 2, 3] + * + * curried(2, 3)(1); + * // => [1, 2, 3] + * + * curried(1, 2, 3); + * // => [1, 2, 3] + * + * // using placeholders + * curried(3)(1, _)(2); + * // => [1, 2, 3] + */var curryRight=createCurry(CURRY_RIGHT_FLAG); +/** + * Creates a debounced function that delays invoking `func` until after `wait` + * milliseconds have elapsed since the last time the debounced function was + * invoked. The debounced function comes with a `cancel` method to cancel + * delayed invocations. Provide an options object to indicate that `func` + * should be invoked on the leading and/or trailing edge of the `wait` timeout. + * Subsequent calls to the debounced function return the result of the last + * `func` invocation. + * + * **Note:** If `leading` and `trailing` options are `true`, `func` is invoked + * on the trailing edge of the timeout only if the the debounced function is + * invoked more than once during the `wait` timeout. + * + * See [David Corbacho's article](http://drupalmotion.com/article/debounce-and-throttle-visual-explanation) + * for details over the differences between `_.debounce` and `_.throttle`. + * + * @static + * @memberOf _ + * @category Function + * @param {Function} func The function to debounce. + * @param {number} [wait=0] The number of milliseconds to delay. + * @param {Object} [options] The options object. + * @param {boolean} [options.leading=false] Specify invoking on the leading + * edge of the timeout. + * @param {number} [options.maxWait] The maximum time `func` is allowed to be + * delayed before it is invoked. + * @param {boolean} [options.trailing=true] Specify invoking on the trailing + * edge of the timeout. + * @returns {Function} Returns the new debounced function. + * @example + * + * // avoid costly calculations while the window size is in flux + * jQuery(window).on('resize', _.debounce(calculateLayout, 150)); + * + * // invoke `sendMail` when the click event is fired, debouncing subsequent calls + * jQuery('#postbox').on('click', _.debounce(sendMail, 300, { + * 'leading': true, + * 'trailing': false + * })); + * + * // ensure `batchLog` is invoked once after 1 second of debounced calls + * var source = new EventSource('/stream'); + * jQuery(source).on('message', _.debounce(batchLog, 250, { + * 'maxWait': 1000 + * })); + * + * // cancel a debounced call + * var todoChanges = _.debounce(batchLog, 1000); + * Object.observe(models.todo, todoChanges); + * + * Object.observe(models, function(changes) { + * if (_.find(changes, { 'user': 'todo', 'type': 'delete'})) { + * todoChanges.cancel(); + * } + * }, ['delete']); + * + * // ...at some point `models.todo` is changed + * models.todo.completed = true; + * + * // ...before 1 second has passed `models.todo` is deleted + * // which cancels the debounced `todoChanges` call + * delete models.todo; + */function debounce(func,wait,options){var args,maxTimeoutId,result,stamp,thisArg,timeoutId,trailingCall,lastCalled=0,maxWait=false,trailing=true;if(typeof func!="function"){throw new TypeError(FUNC_ERROR_TEXT)}wait=wait<0?0:+wait||0;if(options===true){var leading=true;trailing=false}else if(isObject(options)){leading=!!options.leading;maxWait="maxWait"in options&&nativeMax(+options.maxWait||0,wait);trailing="trailing"in options?!!options.trailing:trailing}function cancel(){if(timeoutId){clearTimeout(timeoutId)}if(maxTimeoutId){clearTimeout(maxTimeoutId)}lastCalled=0;maxTimeoutId=timeoutId=trailingCall=undefined}function complete(isCalled,id){if(id){clearTimeout(id)}maxTimeoutId=timeoutId=trailingCall=undefined;if(isCalled){lastCalled=now();result=func.apply(thisArg,args);if(!timeoutId&&!maxTimeoutId){args=thisArg=undefined}}}function delayed(){var remaining=wait-(now()-stamp);if(remaining<=0||remaining>wait){complete(trailingCall,maxTimeoutId)}else{timeoutId=setTimeout(delayed,remaining)}}function maxDelayed(){complete(trailing,timeoutId)}function debounced(){args=arguments;stamp=now();thisArg=this;trailingCall=trailing&&(timeoutId||!leading);if(maxWait===false){var leadingCall=leading&&!timeoutId}else{if(!maxTimeoutId&&!leading){lastCalled=stamp}var remaining=maxWait-(stamp-lastCalled),isCalled=remaining<=0||remaining>maxWait;if(isCalled){if(maxTimeoutId){maxTimeoutId=clearTimeout(maxTimeoutId)}lastCalled=stamp;result=func.apply(thisArg,args)}else if(!maxTimeoutId){maxTimeoutId=setTimeout(maxDelayed,remaining)}}if(isCalled&&timeoutId){timeoutId=clearTimeout(timeoutId)}else if(!timeoutId&&wait!==maxWait){timeoutId=setTimeout(delayed,wait)}if(leadingCall){isCalled=true;result=func.apply(thisArg,args)}if(isCalled&&!timeoutId&&!maxTimeoutId){args=thisArg=undefined}return result}debounced.cancel=cancel;return debounced} +/** + * Defers invoking the `func` until the current call stack has cleared. Any + * additional arguments are provided to `func` when it is invoked. + * + * @static + * @memberOf _ + * @category Function + * @param {Function} func The function to defer. + * @param {...*} [args] The arguments to invoke the function with. + * @returns {number} Returns the timer id. + * @example + * + * _.defer(function(text) { + * console.log(text); + * }, 'deferred'); + * // logs 'deferred' after one or more milliseconds + */var defer=restParam(function(func,args){return baseDelay(func,1,args)}); +/** + * Invokes `func` after `wait` milliseconds. Any additional arguments are + * provided to `func` when it is invoked. + * + * @static + * @memberOf _ + * @category Function + * @param {Function} func The function to delay. + * @param {number} wait The number of milliseconds to delay invocation. + * @param {...*} [args] The arguments to invoke the function with. + * @returns {number} Returns the timer id. + * @example + * + * _.delay(function(text) { + * console.log(text); + * }, 1000, 'later'); + * // => logs 'later' after one second + */var delay=restParam(function(func,wait,args){return baseDelay(func,wait,args)}); +/** + * Creates a function that returns the result of invoking the provided + * functions with the `this` binding of the created function, where each + * successive invocation is supplied the return value of the previous. + * + * @static + * @memberOf _ + * @category Function + * @param {...Function} [funcs] Functions to invoke. + * @returns {Function} Returns the new function. + * @example + * + * function square(n) { + * return n * n; + * } + * + * var addSquare = _.flow(_.add, square); + * addSquare(1, 2); + * // => 9 + */var flow=createFlow(); +/** + * This method is like `_.flow` except that it creates a function that + * invokes the provided functions from right to left. + * + * @static + * @memberOf _ + * @alias backflow, compose + * @category Function + * @param {...Function} [funcs] Functions to invoke. + * @returns {Function} Returns the new function. + * @example + * + * function square(n) { + * return n * n; + * } + * + * var addSquare = _.flowRight(square, _.add); + * addSquare(1, 2); + * // => 9 + */var flowRight=createFlow(true); +/** + * Creates a function that memoizes the result of `func`. If `resolver` is + * provided it determines the cache key for storing the result based on the + * arguments provided to the memoized function. By default, the first argument + * provided to the memoized function is coerced to a string and used as the + * cache key. The `func` is invoked with the `this` binding of the memoized + * function. + * + * **Note:** The cache is exposed as the `cache` property on the memoized + * function. Its creation may be customized by replacing the `_.memoize.Cache` + * constructor with one whose instances implement the [`Map`](http://ecma-international.org/ecma-262/6.0/#sec-properties-of-the-map-prototype-object) + * method interface of `get`, `has`, and `set`. + * + * @static + * @memberOf _ + * @category Function + * @param {Function} func The function to have its output memoized. + * @param {Function} [resolver] The function to resolve the cache key. + * @returns {Function} Returns the new memoizing function. + * @example + * + * var upperCase = _.memoize(function(string) { + * return string.toUpperCase(); + * }); + * + * upperCase('fred'); + * // => 'FRED' + * + * // modifying the result cache + * upperCase.cache.set('fred', 'BARNEY'); + * upperCase('fred'); + * // => 'BARNEY' + * + * // replacing `_.memoize.Cache` + * var object = { 'user': 'fred' }; + * var other = { 'user': 'barney' }; + * var identity = _.memoize(_.identity); + * + * identity(object); + * // => { 'user': 'fred' } + * identity(other); + * // => { 'user': 'fred' } + * + * _.memoize.Cache = WeakMap; + * var identity = _.memoize(_.identity); + * + * identity(object); + * // => { 'user': 'fred' } + * identity(other); + * // => { 'user': 'barney' } + */function memoize(func,resolver){if(typeof func!="function"||resolver&&typeof resolver!="function"){throw new TypeError(FUNC_ERROR_TEXT)}var memoized=function(){var args=arguments,key=resolver?resolver.apply(this,args):args[0],cache=memoized.cache;if(cache.has(key)){return cache.get(key)}var result=func.apply(this,args);memoized.cache=cache.set(key,result);return result};memoized.cache=new memoize.Cache;return memoized} +/** + * Creates a function that runs each argument through a corresponding + * transform function. + * + * @static + * @memberOf _ + * @category Function + * @param {Function} func The function to wrap. + * @param {...(Function|Function[])} [transforms] The functions to transform + * arguments, specified as individual functions or arrays of functions. + * @returns {Function} Returns the new function. + * @example + * + * function doubled(n) { + * return n * 2; + * } + * + * function square(n) { + * return n * n; + * } + * + * var modded = _.modArgs(function(x, y) { + * return [x, y]; + * }, square, doubled); + * + * modded(1, 2); + * // => [1, 4] + * + * modded(5, 10); + * // => [25, 20] + */var modArgs=restParam(function(func,transforms){transforms=baseFlatten(transforms);if(typeof func!="function"||!arrayEvery(transforms,baseIsFunction)){throw new TypeError(FUNC_ERROR_TEXT)}var length=transforms.length;return restParam(function(args){var index=nativeMin(args.length,length);while(index--){args[index]=transforms[index](args[index])}return func.apply(this,args)})}); +/** + * Creates a function that negates the result of the predicate `func`. The + * `func` predicate is invoked with the `this` binding and arguments of the + * created function. + * + * @static + * @memberOf _ + * @category Function + * @param {Function} predicate The predicate to negate. + * @returns {Function} Returns the new function. + * @example + * + * function isEven(n) { + * return n % 2 == 0; + * } + * + * _.filter([1, 2, 3, 4, 5, 6], _.negate(isEven)); + * // => [1, 3, 5] + */function negate(predicate){if(typeof predicate!="function"){throw new TypeError(FUNC_ERROR_TEXT)}return function(){return!predicate.apply(this,arguments)}} +/** + * Creates a function that is restricted to invoking `func` once. Repeat calls + * to the function return the value of the first call. The `func` is invoked + * with the `this` binding and arguments of the created function. + * + * @static + * @memberOf _ + * @category Function + * @param {Function} func The function to restrict. + * @returns {Function} Returns the new restricted function. + * @example + * + * var initialize = _.once(createApplication); + * initialize(); + * initialize(); + * // `initialize` invokes `createApplication` once + */function once(func){return before(2,func)} +/** + * Creates a function that invokes `func` with `partial` arguments prepended + * to those provided to the new function. This method is like `_.bind` except + * it does **not** alter the `this` binding. + * + * The `_.partial.placeholder` value, which defaults to `_` in monolithic + * builds, may be used as a placeholder for partially applied arguments. + * + * **Note:** This method does not set the "length" property of partially + * applied functions. + * + * @static + * @memberOf _ + * @category Function + * @param {Function} func The function to partially apply arguments to. + * @param {...*} [partials] The arguments to be partially applied. + * @returns {Function} Returns the new partially applied function. + * @example + * + * var greet = function(greeting, name) { + * return greeting + ' ' + name; + * }; + * + * var sayHelloTo = _.partial(greet, 'hello'); + * sayHelloTo('fred'); + * // => 'hello fred' + * + * // using placeholders + * var greetFred = _.partial(greet, _, 'fred'); + * greetFred('hi'); + * // => 'hi fred' + */var partial=createPartial(PARTIAL_FLAG); +/** + * This method is like `_.partial` except that partially applied arguments + * are appended to those provided to the new function. + * + * The `_.partialRight.placeholder` value, which defaults to `_` in monolithic + * builds, may be used as a placeholder for partially applied arguments. + * + * **Note:** This method does not set the "length" property of partially + * applied functions. + * + * @static + * @memberOf _ + * @category Function + * @param {Function} func The function to partially apply arguments to. + * @param {...*} [partials] The arguments to be partially applied. + * @returns {Function} Returns the new partially applied function. + * @example + * + * var greet = function(greeting, name) { + * return greeting + ' ' + name; + * }; + * + * var greetFred = _.partialRight(greet, 'fred'); + * greetFred('hi'); + * // => 'hi fred' + * + * // using placeholders + * var sayHelloTo = _.partialRight(greet, 'hello', _); + * sayHelloTo('fred'); + * // => 'hello fred' + */var partialRight=createPartial(PARTIAL_RIGHT_FLAG); +/** + * Creates a function that invokes `func` with arguments arranged according + * to the specified indexes where the argument value at the first index is + * provided as the first argument, the argument value at the second index is + * provided as the second argument, and so on. + * + * @static + * @memberOf _ + * @category Function + * @param {Function} func The function to rearrange arguments for. + * @param {...(number|number[])} indexes The arranged argument indexes, + * specified as individual indexes or arrays of indexes. + * @returns {Function} Returns the new function. + * @example + * + * var rearged = _.rearg(function(a, b, c) { + * return [a, b, c]; + * }, 2, 0, 1); + * + * rearged('b', 'c', 'a') + * // => ['a', 'b', 'c'] + * + * var map = _.rearg(_.map, [1, 0]); + * map(function(n) { + * return n * 3; + * }, [1, 2, 3]); + * // => [3, 6, 9] + */var rearg=restParam(function(func,indexes){return createWrapper(func,REARG_FLAG,undefined,undefined,undefined,baseFlatten(indexes))}); +/** + * Creates a function that invokes `func` with the `this` binding of the + * created function and arguments from `start` and beyond provided as an array. + * + * **Note:** This method is based on the [rest parameter](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/rest_parameters). + * + * @static + * @memberOf _ + * @category Function + * @param {Function} func The function to apply a rest parameter to. + * @param {number} [start=func.length-1] The start position of the rest parameter. + * @returns {Function} Returns the new function. + * @example + * + * var say = _.restParam(function(what, names) { + * return what + ' ' + _.initial(names).join(', ') + + * (_.size(names) > 1 ? ', & ' : '') + _.last(names); + * }); + * + * say('hello', 'fred', 'barney', 'pebbles'); + * // => 'hello fred, barney, & pebbles' + */function restParam(func,start){if(typeof func!="function"){throw new TypeError(FUNC_ERROR_TEXT)}start=nativeMax(start===undefined?func.length-1:+start||0,0);return function(){var args=arguments,index=-1,length=nativeMax(args.length-start,0),rest=Array(length);while(++index 'fred says hello' + * + * // with a Promise + * var numbers = Promise.all([ + * Promise.resolve(40), + * Promise.resolve(36) + * ]); + * + * numbers.then(_.spread(function(x, y) { + * return x + y; + * })); + * // => a Promise of 76 + */function spread(func){if(typeof func!="function"){throw new TypeError(FUNC_ERROR_TEXT)}return function(array){return func.apply(this,array)}} +/** + * Creates a throttled function that only invokes `func` at most once per + * every `wait` milliseconds. The throttled function comes with a `cancel` + * method to cancel delayed invocations. Provide an options object to indicate + * that `func` should be invoked on the leading and/or trailing edge of the + * `wait` timeout. Subsequent calls to the throttled function return the + * result of the last `func` call. + * + * **Note:** If `leading` and `trailing` options are `true`, `func` is invoked + * on the trailing edge of the timeout only if the the throttled function is + * invoked more than once during the `wait` timeout. + * + * See [David Corbacho's article](http://drupalmotion.com/article/debounce-and-throttle-visual-explanation) + * for details over the differences between `_.throttle` and `_.debounce`. + * + * @static + * @memberOf _ + * @category Function + * @param {Function} func The function to throttle. + * @param {number} [wait=0] The number of milliseconds to throttle invocations to. + * @param {Object} [options] The options object. + * @param {boolean} [options.leading=true] Specify invoking on the leading + * edge of the timeout. + * @param {boolean} [options.trailing=true] Specify invoking on the trailing + * edge of the timeout. + * @returns {Function} Returns the new throttled function. + * @example + * + * // avoid excessively updating the position while scrolling + * jQuery(window).on('scroll', _.throttle(updatePosition, 100)); + * + * // invoke `renewToken` when the click event is fired, but not more than once every 5 minutes + * jQuery('.interactive').on('click', _.throttle(renewToken, 300000, { + * 'trailing': false + * })); + * + * // cancel a trailing throttled call + * jQuery(window).on('popstate', throttled.cancel); + */function throttle(func,wait,options){var leading=true,trailing=true;if(typeof func!="function"){throw new TypeError(FUNC_ERROR_TEXT)}if(options===false){leading=false}else if(isObject(options)){leading="leading"in options?!!options.leading:leading;trailing="trailing"in options?!!options.trailing:trailing}return debounce(func,wait,{leading:leading,maxWait:+wait,trailing:trailing})} +/** + * Creates a function that provides `value` to the wrapper function as its + * first argument. Any additional arguments provided to the function are + * appended to those provided to the wrapper function. The wrapper is invoked + * with the `this` binding of the created function. + * + * @static + * @memberOf _ + * @category Function + * @param {*} value The value to wrap. + * @param {Function} wrapper The wrapper function. + * @returns {Function} Returns the new function. + * @example + * + * var p = _.wrap(_.escape, function(func, text) { + * return '

' + func(text) + '

'; + * }); + * + * p('fred, barney, & pebbles'); + * // => '

fred, barney, & pebbles

' + */function wrap(value,wrapper){wrapper=wrapper==null?identity:wrapper;return createWrapper(wrapper,PARTIAL_FLAG,undefined,[value],[])} +/*------------------------------------------------------------------------*/ +/** + * Creates a clone of `value`. If `isDeep` is `true` nested objects are cloned, + * otherwise they are assigned by reference. If `customizer` is provided it is + * invoked to produce the cloned values. If `customizer` returns `undefined` + * cloning is handled by the method instead. The `customizer` is bound to + * `thisArg` and invoked with two argument; (value [, index|key, object]). + * + * **Note:** This method is loosely based on the + * [structured clone algorithm](http://www.w3.org/TR/html5/infrastructure.html#internal-structured-cloning-algorithm). + * The enumerable properties of `arguments` objects and objects created by + * constructors other than `Object` are cloned to plain `Object` objects. An + * empty object is returned for uncloneable values such as functions, DOM nodes, + * Maps, Sets, and WeakMaps. + * + * @static + * @memberOf _ + * @category Lang + * @param {*} value The value to clone. + * @param {boolean} [isDeep] Specify a deep clone. + * @param {Function} [customizer] The function to customize cloning values. + * @param {*} [thisArg] The `this` binding of `customizer`. + * @returns {*} Returns the cloned value. + * @example + * + * var users = [ + * { 'user': 'barney' }, + * { 'user': 'fred' } + * ]; + * + * var shallow = _.clone(users); + * shallow[0] === users[0]; + * // => true + * + * var deep = _.clone(users, true); + * deep[0] === users[0]; + * // => false + * + * // using a customizer callback + * var el = _.clone(document.body, function(value) { + * if (_.isElement(value)) { + * return value.cloneNode(false); + * } + * }); + * + * el === document.body + * // => false + * el.nodeName + * // => BODY + * el.childNodes.length; + * // => 0 + */function clone(value,isDeep,customizer,thisArg){if(isDeep&&typeof isDeep!="boolean"&&isIterateeCall(value,isDeep,customizer)){isDeep=false}else if(typeof isDeep=="function"){thisArg=customizer;customizer=isDeep;isDeep=false}return typeof customizer=="function"?baseClone(value,isDeep,bindCallback(customizer,thisArg,1)):baseClone(value,isDeep)} +/** + * Creates a deep clone of `value`. If `customizer` is provided it is invoked + * to produce the cloned values. If `customizer` returns `undefined` cloning + * is handled by the method instead. The `customizer` is bound to `thisArg` + * and invoked with two argument; (value [, index|key, object]). + * + * **Note:** This method is loosely based on the + * [structured clone algorithm](http://www.w3.org/TR/html5/infrastructure.html#internal-structured-cloning-algorithm). + * The enumerable properties of `arguments` objects and objects created by + * constructors other than `Object` are cloned to plain `Object` objects. An + * empty object is returned for uncloneable values such as functions, DOM nodes, + * Maps, Sets, and WeakMaps. + * + * @static + * @memberOf _ + * @category Lang + * @param {*} value The value to deep clone. + * @param {Function} [customizer] The function to customize cloning values. + * @param {*} [thisArg] The `this` binding of `customizer`. + * @returns {*} Returns the deep cloned value. + * @example + * + * var users = [ + * { 'user': 'barney' }, + * { 'user': 'fred' } + * ]; + * + * var deep = _.cloneDeep(users); + * deep[0] === users[0]; + * // => false + * + * // using a customizer callback + * var el = _.cloneDeep(document.body, function(value) { + * if (_.isElement(value)) { + * return value.cloneNode(true); + * } + * }); + * + * el === document.body + * // => false + * el.nodeName + * // => BODY + * el.childNodes.length; + * // => 20 + */function cloneDeep(value,customizer,thisArg){return typeof customizer=="function"?baseClone(value,true,bindCallback(customizer,thisArg,1)):baseClone(value,true)} +/** + * Checks if `value` is greater than `other`. + * + * @static + * @memberOf _ + * @category Lang + * @param {*} value The value to compare. + * @param {*} other The other value to compare. + * @returns {boolean} Returns `true` if `value` is greater than `other`, else `false`. + * @example + * + * _.gt(3, 1); + * // => true + * + * _.gt(3, 3); + * // => false + * + * _.gt(1, 3); + * // => false + */function gt(value,other){return value>other} +/** + * Checks if `value` is greater than or equal to `other`. + * + * @static + * @memberOf _ + * @category Lang + * @param {*} value The value to compare. + * @param {*} other The other value to compare. + * @returns {boolean} Returns `true` if `value` is greater than or equal to `other`, else `false`. + * @example + * + * _.gte(3, 1); + * // => true + * + * _.gte(3, 3); + * // => true + * + * _.gte(1, 3); + * // => false + */function gte(value,other){return value>=other} +/** + * Checks if `value` is classified as an `arguments` object. + * + * @static + * @memberOf _ + * @category Lang + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is correctly classified, else `false`. + * @example + * + * _.isArguments(function() { return arguments; }()); + * // => true + * + * _.isArguments([1, 2, 3]); + * // => false + */function isArguments(value){return isObjectLike(value)&&isArrayLike(value)&&hasOwnProperty.call(value,"callee")&&!propertyIsEnumerable.call(value,"callee")} +/** + * Checks if `value` is classified as an `Array` object. + * + * @static + * @memberOf _ + * @category Lang + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is correctly classified, else `false`. + * @example + * + * _.isArray([1, 2, 3]); + * // => true + * + * _.isArray(function() { return arguments; }()); + * // => false + */var isArray=nativeIsArray||function(value){return isObjectLike(value)&&isLength(value.length)&&objToString.call(value)==arrayTag}; +/** + * Checks if `value` is classified as a boolean primitive or object. + * + * @static + * @memberOf _ + * @category Lang + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is correctly classified, else `false`. + * @example + * + * _.isBoolean(false); + * // => true + * + * _.isBoolean(null); + * // => false + */function isBoolean(value){return value===true||value===false||isObjectLike(value)&&objToString.call(value)==boolTag} +/** + * Checks if `value` is classified as a `Date` object. + * + * @static + * @memberOf _ + * @category Lang + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is correctly classified, else `false`. + * @example + * + * _.isDate(new Date); + * // => true + * + * _.isDate('Mon April 23 2012'); + * // => false + */function isDate(value){return isObjectLike(value)&&objToString.call(value)==dateTag} +/** + * Checks if `value` is a DOM element. + * + * @static + * @memberOf _ + * @category Lang + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is a DOM element, else `false`. + * @example + * + * _.isElement(document.body); + * // => true + * + * _.isElement(''); + * // => false + */function isElement(value){return!!value&&value.nodeType===1&&isObjectLike(value)&&!isPlainObject(value)} +/** + * Checks if `value` is empty. A value is considered empty unless it is an + * `arguments` object, array, string, or jQuery-like collection with a length + * greater than `0` or an object with own enumerable properties. + * + * @static + * @memberOf _ + * @category Lang + * @param {Array|Object|string} value The value to inspect. + * @returns {boolean} Returns `true` if `value` is empty, else `false`. + * @example + * + * _.isEmpty(null); + * // => true + * + * _.isEmpty(true); + * // => true + * + * _.isEmpty(1); + * // => true + * + * _.isEmpty([1, 2, 3]); + * // => false + * + * _.isEmpty({ 'a': 1 }); + * // => false + */function isEmpty(value){if(value==null){return true}if(isArrayLike(value)&&(isArray(value)||isString(value)||isArguments(value)||isObjectLike(value)&&isFunction(value.splice))){return!value.length}return!keys(value).length} +/** + * Performs a deep comparison between two values to determine if they are + * equivalent. If `customizer` is provided it is invoked to compare values. + * If `customizer` returns `undefined` comparisons are handled by the method + * instead. The `customizer` is bound to `thisArg` and invoked with three + * arguments: (value, other [, index|key]). + * + * **Note:** This method supports comparing arrays, booleans, `Date` objects, + * numbers, `Object` objects, regexes, and strings. Objects are compared by + * their own, not inherited, enumerable properties. Functions and DOM nodes + * are **not** supported. Provide a customizer function to extend support + * for comparing other values. + * + * @static + * @memberOf _ + * @alias eq + * @category Lang + * @param {*} value The value to compare. + * @param {*} other The other value to compare. + * @param {Function} [customizer] The function to customize value comparisons. + * @param {*} [thisArg] The `this` binding of `customizer`. + * @returns {boolean} Returns `true` if the values are equivalent, else `false`. + * @example + * + * var object = { 'user': 'fred' }; + * var other = { 'user': 'fred' }; + * + * object == other; + * // => false + * + * _.isEqual(object, other); + * // => true + * + * // using a customizer callback + * var array = ['hello', 'goodbye']; + * var other = ['hi', 'goodbye']; + * + * _.isEqual(array, other, function(value, other) { + * if (_.every([value, other], RegExp.prototype.test, /^h(?:i|ello)$/)) { + * return true; + * } + * }); + * // => true + */function isEqual(value,other,customizer,thisArg){customizer=typeof customizer=="function"?bindCallback(customizer,thisArg,3):undefined;var result=customizer?customizer(value,other):undefined;return result===undefined?baseIsEqual(value,other,customizer):!!result} +/** + * Checks if `value` is an `Error`, `EvalError`, `RangeError`, `ReferenceError`, + * `SyntaxError`, `TypeError`, or `URIError` object. + * + * @static + * @memberOf _ + * @category Lang + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is an error object, else `false`. + * @example + * + * _.isError(new Error); + * // => true + * + * _.isError(Error); + * // => false + */function isError(value){return isObjectLike(value)&&typeof value.message=="string"&&objToString.call(value)==errorTag} +/** + * Checks if `value` is a finite primitive number. + * + * **Note:** This method is based on [`Number.isFinite`](http://ecma-international.org/ecma-262/6.0/#sec-number.isfinite). + * + * @static + * @memberOf _ + * @category Lang + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is a finite number, else `false`. + * @example + * + * _.isFinite(10); + * // => true + * + * _.isFinite('10'); + * // => false + * + * _.isFinite(true); + * // => false + * + * _.isFinite(Object(10)); + * // => false + * + * _.isFinite(Infinity); + * // => false + */function isFinite(value){return typeof value=="number"&&nativeIsFinite(value)} +/** + * Checks if `value` is classified as a `Function` object. + * + * @static + * @memberOf _ + * @category Lang + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is correctly classified, else `false`. + * @example + * + * _.isFunction(_); + * // => true + * + * _.isFunction(/abc/); + * // => false + */function isFunction(value){ +// The use of `Object#toString` avoids issues with the `typeof` operator +// in older versions of Chrome and Safari which return 'function' for regexes +// and Safari 8 equivalents which return 'object' for typed array constructors. +return isObject(value)&&objToString.call(value)==funcTag} +/** + * Checks if `value` is the [language type](https://es5.github.io/#x8) of `Object`. + * (e.g. arrays, functions, objects, regexes, `new Number(0)`, and `new String('')`) + * + * @static + * @memberOf _ + * @category Lang + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is an object, else `false`. + * @example + * + * _.isObject({}); + * // => true + * + * _.isObject([1, 2, 3]); + * // => true + * + * _.isObject(1); + * // => false + */function isObject(value){ +// Avoid a V8 JIT bug in Chrome 19-20. +// See https://code.google.com/p/v8/issues/detail?id=2291 for more details. +var type=typeof value;return!!value&&(type=="object"||type=="function")} +/** + * Performs a deep comparison between `object` and `source` to determine if + * `object` contains equivalent property values. If `customizer` is provided + * it is invoked to compare values. If `customizer` returns `undefined` + * comparisons are handled by the method instead. The `customizer` is bound + * to `thisArg` and invoked with three arguments: (value, other, index|key). + * + * **Note:** This method supports comparing properties of arrays, booleans, + * `Date` objects, numbers, `Object` objects, regexes, and strings. Functions + * and DOM nodes are **not** supported. Provide a customizer function to extend + * support for comparing other values. + * + * @static + * @memberOf _ + * @category Lang + * @param {Object} object The object to inspect. + * @param {Object} source The object of property values to match. + * @param {Function} [customizer] The function to customize value comparisons. + * @param {*} [thisArg] The `this` binding of `customizer`. + * @returns {boolean} Returns `true` if `object` is a match, else `false`. + * @example + * + * var object = { 'user': 'fred', 'age': 40 }; + * + * _.isMatch(object, { 'age': 40 }); + * // => true + * + * _.isMatch(object, { 'age': 36 }); + * // => false + * + * // using a customizer callback + * var object = { 'greeting': 'hello' }; + * var source = { 'greeting': 'hi' }; + * + * _.isMatch(object, source, function(value, other) { + * return _.every([value, other], RegExp.prototype.test, /^h(?:i|ello)$/) || undefined; + * }); + * // => true + */function isMatch(object,source,customizer,thisArg){customizer=typeof customizer=="function"?bindCallback(customizer,thisArg,3):undefined;return baseIsMatch(object,getMatchData(source),customizer)} +/** + * Checks if `value` is `NaN`. + * + * **Note:** This method is not the same as [`isNaN`](https://es5.github.io/#x15.1.2.4) + * which returns `true` for `undefined` and other non-numeric values. + * + * @static + * @memberOf _ + * @category Lang + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is `NaN`, else `false`. + * @example + * + * _.isNaN(NaN); + * // => true + * + * _.isNaN(new Number(NaN)); + * // => true + * + * isNaN(undefined); + * // => true + * + * _.isNaN(undefined); + * // => false + */function isNaN(value){ +// An `NaN` primitive is the only value that is not equal to itself. +// Perform the `toStringTag` check first to avoid errors with some host objects in IE. +return isNumber(value)&&value!=+value} +/** + * Checks if `value` is a native function. + * + * @static + * @memberOf _ + * @category Lang + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is a native function, else `false`. + * @example + * + * _.isNative(Array.prototype.push); + * // => true + * + * _.isNative(_); + * // => false + */function isNative(value){if(value==null){return false}if(isFunction(value)){return reIsNative.test(fnToString.call(value))}return isObjectLike(value)&&reIsHostCtor.test(value)} +/** + * Checks if `value` is `null`. + * + * @static + * @memberOf _ + * @category Lang + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is `null`, else `false`. + * @example + * + * _.isNull(null); + * // => true + * + * _.isNull(void 0); + * // => false + */function isNull(value){return value===null} +/** + * Checks if `value` is classified as a `Number` primitive or object. + * + * **Note:** To exclude `Infinity`, `-Infinity`, and `NaN`, which are classified + * as numbers, use the `_.isFinite` method. + * + * @static + * @memberOf _ + * @category Lang + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is correctly classified, else `false`. + * @example + * + * _.isNumber(8.4); + * // => true + * + * _.isNumber(NaN); + * // => true + * + * _.isNumber('8.4'); + * // => false + */function isNumber(value){return typeof value=="number"||isObjectLike(value)&&objToString.call(value)==numberTag} +/** + * Checks if `value` is a plain object, that is, an object created by the + * `Object` constructor or one with a `[[Prototype]]` of `null`. + * + * **Note:** This method assumes objects created by the `Object` constructor + * have no inherited enumerable properties. + * + * @static + * @memberOf _ + * @category Lang + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is a plain object, else `false`. + * @example + * + * function Foo() { + * this.a = 1; + * } + * + * _.isPlainObject(new Foo); + * // => false + * + * _.isPlainObject([1, 2, 3]); + * // => false + * + * _.isPlainObject({ 'x': 0, 'y': 0 }); + * // => true + * + * _.isPlainObject(Object.create(null)); + * // => true + */function isPlainObject(value){var Ctor; +// Exit early for non `Object` objects. +if(!(isObjectLike(value)&&objToString.call(value)==objectTag&&!isArguments(value))||!hasOwnProperty.call(value,"constructor")&&(Ctor=value.constructor,typeof Ctor=="function"&&!(Ctor instanceof Ctor))){return false} +// IE < 9 iterates inherited properties before own properties. If the first +// iterated property is an object's own property then there are no inherited +// enumerable properties. +var result; +// In most environments an object's own properties are iterated before +// its inherited properties. If the last iterated property is an object's +// own property then there are no inherited enumerable properties. +baseForIn(value,function(subValue,key){result=key});return result===undefined||hasOwnProperty.call(value,result)} +/** + * Checks if `value` is classified as a `RegExp` object. + * + * @static + * @memberOf _ + * @category Lang + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is correctly classified, else `false`. + * @example + * + * _.isRegExp(/abc/); + * // => true + * + * _.isRegExp('/abc/'); + * // => false + */function isRegExp(value){return isObject(value)&&objToString.call(value)==regexpTag} +/** + * Checks if `value` is classified as a `String` primitive or object. + * + * @static + * @memberOf _ + * @category Lang + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is correctly classified, else `false`. + * @example + * + * _.isString('abc'); + * // => true + * + * _.isString(1); + * // => false + */function isString(value){return typeof value=="string"||isObjectLike(value)&&objToString.call(value)==stringTag} +/** + * Checks if `value` is classified as a typed array. + * + * @static + * @memberOf _ + * @category Lang + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is correctly classified, else `false`. + * @example + * + * _.isTypedArray(new Uint8Array); + * // => true + * + * _.isTypedArray([]); + * // => false + */function isTypedArray(value){return isObjectLike(value)&&isLength(value.length)&&!!typedArrayTags[objToString.call(value)]} +/** + * Checks if `value` is `undefined`. + * + * @static + * @memberOf _ + * @category Lang + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is `undefined`, else `false`. + * @example + * + * _.isUndefined(void 0); + * // => true + * + * _.isUndefined(null); + * // => false + */function isUndefined(value){return value===undefined} +/** + * Checks if `value` is less than `other`. + * + * @static + * @memberOf _ + * @category Lang + * @param {*} value The value to compare. + * @param {*} other The other value to compare. + * @returns {boolean} Returns `true` if `value` is less than `other`, else `false`. + * @example + * + * _.lt(1, 3); + * // => true + * + * _.lt(3, 3); + * // => false + * + * _.lt(3, 1); + * // => false + */function lt(value,other){return value true + * + * _.lte(3, 3); + * // => true + * + * _.lte(3, 1); + * // => false + */function lte(value,other){return value<=other} +/** + * Converts `value` to an array. + * + * @static + * @memberOf _ + * @category Lang + * @param {*} value The value to convert. + * @returns {Array} Returns the converted array. + * @example + * + * (function() { + * return _.toArray(arguments).slice(1); + * }(1, 2, 3)); + * // => [2, 3] + */function toArray(value){var length=value?getLength(value):0;if(!isLength(length)){return values(value)}if(!length){return[]}return arrayCopy(value)} +/** + * Converts `value` to a plain object flattening inherited enumerable + * properties of `value` to own properties of the plain object. + * + * @static + * @memberOf _ + * @category Lang + * @param {*} value The value to convert. + * @returns {Object} Returns the converted plain object. + * @example + * + * function Foo() { + * this.b = 2; + * } + * + * Foo.prototype.c = 3; + * + * _.assign({ 'a': 1 }, new Foo); + * // => { 'a': 1, 'b': 2 } + * + * _.assign({ 'a': 1 }, _.toPlainObject(new Foo)); + * // => { 'a': 1, 'b': 2, 'c': 3 } + */function toPlainObject(value){return baseCopy(value,keysIn(value))} +/*------------------------------------------------------------------------*/ +/** + * Recursively merges own enumerable properties of the source object(s), that + * don't resolve to `undefined` into the destination object. Subsequent sources + * overwrite property assignments of previous sources. If `customizer` is + * provided it is invoked to produce the merged values of the destination and + * source properties. If `customizer` returns `undefined` merging is handled + * by the method instead. The `customizer` is bound to `thisArg` and invoked + * with five arguments: (objectValue, sourceValue, key, object, source). + * + * @static + * @memberOf _ + * @category Object + * @param {Object} object The destination object. + * @param {...Object} [sources] The source objects. + * @param {Function} [customizer] The function to customize assigned values. + * @param {*} [thisArg] The `this` binding of `customizer`. + * @returns {Object} Returns `object`. + * @example + * + * var users = { + * 'data': [{ 'user': 'barney' }, { 'user': 'fred' }] + * }; + * + * var ages = { + * 'data': [{ 'age': 36 }, { 'age': 40 }] + * }; + * + * _.merge(users, ages); + * // => { 'data': [{ 'user': 'barney', 'age': 36 }, { 'user': 'fred', 'age': 40 }] } + * + * // using a customizer callback + * var object = { + * 'fruits': ['apple'], + * 'vegetables': ['beet'] + * }; + * + * var other = { + * 'fruits': ['banana'], + * 'vegetables': ['carrot'] + * }; + * + * _.merge(object, other, function(a, b) { + * if (_.isArray(a)) { + * return a.concat(b); + * } + * }); + * // => { 'fruits': ['apple', 'banana'], 'vegetables': ['beet', 'carrot'] } + */var merge=createAssigner(baseMerge); +/** + * Assigns own enumerable properties of source object(s) to the destination + * object. Subsequent sources overwrite property assignments of previous sources. + * If `customizer` is provided it is invoked to produce the assigned values. + * The `customizer` is bound to `thisArg` and invoked with five arguments: + * (objectValue, sourceValue, key, object, source). + * + * **Note:** This method mutates `object` and is based on + * [`Object.assign`](http://ecma-international.org/ecma-262/6.0/#sec-object.assign). + * + * @static + * @memberOf _ + * @alias extend + * @category Object + * @param {Object} object The destination object. + * @param {...Object} [sources] The source objects. + * @param {Function} [customizer] The function to customize assigned values. + * @param {*} [thisArg] The `this` binding of `customizer`. + * @returns {Object} Returns `object`. + * @example + * + * _.assign({ 'user': 'barney' }, { 'age': 40 }, { 'user': 'fred' }); + * // => { 'user': 'fred', 'age': 40 } + * + * // using a customizer callback + * var defaults = _.partialRight(_.assign, function(value, other) { + * return _.isUndefined(value) ? other : value; + * }); + * + * defaults({ 'user': 'barney' }, { 'age': 36 }, { 'user': 'fred' }); + * // => { 'user': 'barney', 'age': 36 } + */var assign=createAssigner(function(object,source,customizer){return customizer?assignWith(object,source,customizer):baseAssign(object,source)}); +/** + * Creates an object that inherits from the given `prototype` object. If a + * `properties` object is provided its own enumerable properties are assigned + * to the created object. + * + * @static + * @memberOf _ + * @category Object + * @param {Object} prototype The object to inherit from. + * @param {Object} [properties] The properties to assign to the object. + * @param- {Object} [guard] Enables use as a callback for functions like `_.map`. + * @returns {Object} Returns the new object. + * @example + * + * function Shape() { + * this.x = 0; + * this.y = 0; + * } + * + * function Circle() { + * Shape.call(this); + * } + * + * Circle.prototype = _.create(Shape.prototype, { + * 'constructor': Circle + * }); + * + * var circle = new Circle; + * circle instanceof Circle; + * // => true + * + * circle instanceof Shape; + * // => true + */function create(prototype,properties,guard){var result=baseCreate(prototype);if(guard&&isIterateeCall(prototype,properties,guard)){properties=undefined}return properties?baseAssign(result,properties):result} +/** + * Assigns own enumerable properties of source object(s) to the destination + * object for all destination properties that resolve to `undefined`. Once a + * property is set, additional values of the same property are ignored. + * + * **Note:** This method mutates `object`. + * + * @static + * @memberOf _ + * @category Object + * @param {Object} object The destination object. + * @param {...Object} [sources] The source objects. + * @returns {Object} Returns `object`. + * @example + * + * _.defaults({ 'user': 'barney' }, { 'age': 36 }, { 'user': 'fred' }); + * // => { 'user': 'barney', 'age': 36 } + */var defaults=createDefaults(assign,assignDefaults); +/** + * This method is like `_.defaults` except that it recursively assigns + * default properties. + * + * **Note:** This method mutates `object`. + * + * @static + * @memberOf _ + * @category Object + * @param {Object} object The destination object. + * @param {...Object} [sources] The source objects. + * @returns {Object} Returns `object`. + * @example + * + * _.defaultsDeep({ 'user': { 'name': 'barney' } }, { 'user': { 'name': 'fred', 'age': 36 } }); + * // => { 'user': { 'name': 'barney', 'age': 36 } } + * + */var defaultsDeep=createDefaults(merge,mergeDefaults); +/** + * This method is like `_.find` except that it returns the key of the first + * element `predicate` returns truthy for instead of the element itself. + * + * If a property name is provided for `predicate` the created `_.property` + * style callback returns the property value of the given element. + * + * If a value is also provided for `thisArg` the created `_.matchesProperty` + * style callback returns `true` for elements that have a matching property + * value, else `false`. + * + * If an object is provided for `predicate` the created `_.matches` style + * callback returns `true` for elements that have the properties of the given + * object, else `false`. + * + * @static + * @memberOf _ + * @category Object + * @param {Object} object The object to search. + * @param {Function|Object|string} [predicate=_.identity] The function invoked + * per iteration. + * @param {*} [thisArg] The `this` binding of `predicate`. + * @returns {string|undefined} Returns the key of the matched element, else `undefined`. + * @example + * + * var users = { + * 'barney': { 'age': 36, 'active': true }, + * 'fred': { 'age': 40, 'active': false }, + * 'pebbles': { 'age': 1, 'active': true } + * }; + * + * _.findKey(users, function(chr) { + * return chr.age < 40; + * }); + * // => 'barney' (iteration order is not guaranteed) + * + * // using the `_.matches` callback shorthand + * _.findKey(users, { 'age': 1, 'active': true }); + * // => 'pebbles' + * + * // using the `_.matchesProperty` callback shorthand + * _.findKey(users, 'active', false); + * // => 'fred' + * + * // using the `_.property` callback shorthand + * _.findKey(users, 'active'); + * // => 'barney' + */var findKey=createFindKey(baseForOwn); +/** + * This method is like `_.findKey` except that it iterates over elements of + * a collection in the opposite order. + * + * If a property name is provided for `predicate` the created `_.property` + * style callback returns the property value of the given element. + * + * If a value is also provided for `thisArg` the created `_.matchesProperty` + * style callback returns `true` for elements that have a matching property + * value, else `false`. + * + * If an object is provided for `predicate` the created `_.matches` style + * callback returns `true` for elements that have the properties of the given + * object, else `false`. + * + * @static + * @memberOf _ + * @category Object + * @param {Object} object The object to search. + * @param {Function|Object|string} [predicate=_.identity] The function invoked + * per iteration. + * @param {*} [thisArg] The `this` binding of `predicate`. + * @returns {string|undefined} Returns the key of the matched element, else `undefined`. + * @example + * + * var users = { + * 'barney': { 'age': 36, 'active': true }, + * 'fred': { 'age': 40, 'active': false }, + * 'pebbles': { 'age': 1, 'active': true } + * }; + * + * _.findLastKey(users, function(chr) { + * return chr.age < 40; + * }); + * // => returns `pebbles` assuming `_.findKey` returns `barney` + * + * // using the `_.matches` callback shorthand + * _.findLastKey(users, { 'age': 36, 'active': true }); + * // => 'barney' + * + * // using the `_.matchesProperty` callback shorthand + * _.findLastKey(users, 'active', false); + * // => 'fred' + * + * // using the `_.property` callback shorthand + * _.findLastKey(users, 'active'); + * // => 'pebbles' + */var findLastKey=createFindKey(baseForOwnRight); +/** + * Iterates over own and inherited enumerable properties of an object invoking + * `iteratee` for each property. The `iteratee` is bound to `thisArg` and invoked + * with three arguments: (value, key, object). Iteratee functions may exit + * iteration early by explicitly returning `false`. + * + * @static + * @memberOf _ + * @category Object + * @param {Object} object The object to iterate over. + * @param {Function} [iteratee=_.identity] The function invoked per iteration. + * @param {*} [thisArg] The `this` binding of `iteratee`. + * @returns {Object} Returns `object`. + * @example + * + * function Foo() { + * this.a = 1; + * this.b = 2; + * } + * + * Foo.prototype.c = 3; + * + * _.forIn(new Foo, function(value, key) { + * console.log(key); + * }); + * // => logs 'a', 'b', and 'c' (iteration order is not guaranteed) + */var forIn=createForIn(baseFor); +/** + * This method is like `_.forIn` except that it iterates over properties of + * `object` in the opposite order. + * + * @static + * @memberOf _ + * @category Object + * @param {Object} object The object to iterate over. + * @param {Function} [iteratee=_.identity] The function invoked per iteration. + * @param {*} [thisArg] The `this` binding of `iteratee`. + * @returns {Object} Returns `object`. + * @example + * + * function Foo() { + * this.a = 1; + * this.b = 2; + * } + * + * Foo.prototype.c = 3; + * + * _.forInRight(new Foo, function(value, key) { + * console.log(key); + * }); + * // => logs 'c', 'b', and 'a' assuming `_.forIn ` logs 'a', 'b', and 'c' + */var forInRight=createForIn(baseForRight); +/** + * Iterates over own enumerable properties of an object invoking `iteratee` + * for each property. The `iteratee` is bound to `thisArg` and invoked with + * three arguments: (value, key, object). Iteratee functions may exit iteration + * early by explicitly returning `false`. + * + * @static + * @memberOf _ + * @category Object + * @param {Object} object The object to iterate over. + * @param {Function} [iteratee=_.identity] The function invoked per iteration. + * @param {*} [thisArg] The `this` binding of `iteratee`. + * @returns {Object} Returns `object`. + * @example + * + * function Foo() { + * this.a = 1; + * this.b = 2; + * } + * + * Foo.prototype.c = 3; + * + * _.forOwn(new Foo, function(value, key) { + * console.log(key); + * }); + * // => logs 'a' and 'b' (iteration order is not guaranteed) + */var forOwn=createForOwn(baseForOwn); +/** + * This method is like `_.forOwn` except that it iterates over properties of + * `object` in the opposite order. + * + * @static + * @memberOf _ + * @category Object + * @param {Object} object The object to iterate over. + * @param {Function} [iteratee=_.identity] The function invoked per iteration. + * @param {*} [thisArg] The `this` binding of `iteratee`. + * @returns {Object} Returns `object`. + * @example + * + * function Foo() { + * this.a = 1; + * this.b = 2; + * } + * + * Foo.prototype.c = 3; + * + * _.forOwnRight(new Foo, function(value, key) { + * console.log(key); + * }); + * // => logs 'b' and 'a' assuming `_.forOwn` logs 'a' and 'b' + */var forOwnRight=createForOwn(baseForOwnRight); +/** + * Creates an array of function property names from all enumerable properties, + * own and inherited, of `object`. + * + * @static + * @memberOf _ + * @alias methods + * @category Object + * @param {Object} object The object to inspect. + * @returns {Array} Returns the new array of property names. + * @example + * + * _.functions(_); + * // => ['after', 'ary', 'assign', ...] + */function functions(object){return baseFunctions(object,keysIn(object))} +/** + * Gets the property value at `path` of `object`. If the resolved value is + * `undefined` the `defaultValue` is used in its place. + * + * @static + * @memberOf _ + * @category Object + * @param {Object} object The object to query. + * @param {Array|string} path The path of the property to get. + * @param {*} [defaultValue] The value returned if the resolved value is `undefined`. + * @returns {*} Returns the resolved value. + * @example + * + * var object = { 'a': [{ 'b': { 'c': 3 } }] }; + * + * _.get(object, 'a[0].b.c'); + * // => 3 + * + * _.get(object, ['a', '0', 'b', 'c']); + * // => 3 + * + * _.get(object, 'a.b.c', 'default'); + * // => 'default' + */function get(object,path,defaultValue){var result=object==null?undefined:baseGet(object,toPath(path),path+"");return result===undefined?defaultValue:result} +/** + * Checks if `path` is a direct property. + * + * @static + * @memberOf _ + * @category Object + * @param {Object} object The object to query. + * @param {Array|string} path The path to check. + * @returns {boolean} Returns `true` if `path` is a direct property, else `false`. + * @example + * + * var object = { 'a': { 'b': { 'c': 3 } } }; + * + * _.has(object, 'a'); + * // => true + * + * _.has(object, 'a.b.c'); + * // => true + * + * _.has(object, ['a', 'b', 'c']); + * // => true + */function has(object,path){if(object==null){return false}var result=hasOwnProperty.call(object,path);if(!result&&!isKey(path)){path=toPath(path);object=path.length==1?object:baseGet(object,baseSlice(path,0,-1));if(object==null){return false}path=last(path);result=hasOwnProperty.call(object,path)}return result||isLength(object.length)&&isIndex(path,object.length)&&(isArray(object)||isArguments(object))} +/** + * Creates an object composed of the inverted keys and values of `object`. + * If `object` contains duplicate values, subsequent values overwrite property + * assignments of previous values unless `multiValue` is `true`. + * + * @static + * @memberOf _ + * @category Object + * @param {Object} object The object to invert. + * @param {boolean} [multiValue] Allow multiple values per key. + * @param- {Object} [guard] Enables use as a callback for functions like `_.map`. + * @returns {Object} Returns the new inverted object. + * @example + * + * var object = { 'a': 1, 'b': 2, 'c': 1 }; + * + * _.invert(object); + * // => { '1': 'c', '2': 'b' } + * + * // with `multiValue` + * _.invert(object, true); + * // => { '1': ['a', 'c'], '2': ['b'] } + */function invert(object,multiValue,guard){if(guard&&isIterateeCall(object,multiValue,guard)){multiValue=undefined}var index=-1,props=keys(object),length=props.length,result={};while(++index ['a', 'b'] (iteration order is not guaranteed) + * + * _.keys('hi'); + * // => ['0', '1'] + */var keys=!nativeKeys?shimKeys:function(object){var Ctor=object==null?undefined:object.constructor;if(typeof Ctor=="function"&&Ctor.prototype===object||typeof object!="function"&&isArrayLike(object)){return shimKeys(object)}return isObject(object)?nativeKeys(object):[]}; +/** + * Creates an array of the own and inherited enumerable property names of `object`. + * + * **Note:** Non-object values are coerced to objects. + * + * @static + * @memberOf _ + * @category Object + * @param {Object} object The object to query. + * @returns {Array} Returns the array of property names. + * @example + * + * function Foo() { + * this.a = 1; + * this.b = 2; + * } + * + * Foo.prototype.c = 3; + * + * _.keysIn(new Foo); + * // => ['a', 'b', 'c'] (iteration order is not guaranteed) + */function keysIn(object){if(object==null){return[]}if(!isObject(object)){object=Object(object)}var length=object.length;length=length&&isLength(length)&&(isArray(object)||isArguments(object))&&length||0;var Ctor=object.constructor,index=-1,isProto=typeof Ctor=="function"&&Ctor.prototype===object,result=Array(length),skipIndexes=length>0;while(++index { 'a1': 1, 'b2': 2 } + */var mapKeys=createObjectMapper(true); +/** + * Creates an object with the same keys as `object` and values generated by + * running each own enumerable property of `object` through `iteratee`. The + * iteratee function is bound to `thisArg` and invoked with three arguments: + * (value, key, object). + * + * If a property name is provided for `iteratee` the created `_.property` + * style callback returns the property value of the given element. + * + * If a value is also provided for `thisArg` the created `_.matchesProperty` + * style callback returns `true` for elements that have a matching property + * value, else `false`. + * + * If an object is provided for `iteratee` the created `_.matches` style + * callback returns `true` for elements that have the properties of the given + * object, else `false`. + * + * @static + * @memberOf _ + * @category Object + * @param {Object} object The object to iterate over. + * @param {Function|Object|string} [iteratee=_.identity] The function invoked + * per iteration. + * @param {*} [thisArg] The `this` binding of `iteratee`. + * @returns {Object} Returns the new mapped object. + * @example + * + * _.mapValues({ 'a': 1, 'b': 2 }, function(n) { + * return n * 3; + * }); + * // => { 'a': 3, 'b': 6 } + * + * var users = { + * 'fred': { 'user': 'fred', 'age': 40 }, + * 'pebbles': { 'user': 'pebbles', 'age': 1 } + * }; + * + * // using the `_.property` callback shorthand + * _.mapValues(users, 'age'); + * // => { 'fred': 40, 'pebbles': 1 } (iteration order is not guaranteed) + */var mapValues=createObjectMapper(); +/** + * The opposite of `_.pick`; this method creates an object composed of the + * own and inherited enumerable properties of `object` that are not omitted. + * + * @static + * @memberOf _ + * @category Object + * @param {Object} object The source object. + * @param {Function|...(string|string[])} [predicate] The function invoked per + * iteration or property names to omit, specified as individual property + * names or arrays of property names. + * @param {*} [thisArg] The `this` binding of `predicate`. + * @returns {Object} Returns the new object. + * @example + * + * var object = { 'user': 'fred', 'age': 40 }; + * + * _.omit(object, 'age'); + * // => { 'user': 'fred' } + * + * _.omit(object, _.isNumber); + * // => { 'user': 'fred' } + */var omit=restParam(function(object,props){if(object==null){return{}}if(typeof props[0]!="function"){var props=arrayMap(baseFlatten(props),String);return pickByArray(object,baseDifference(keysIn(object),props))}var predicate=bindCallback(props[0],props[1],3);return pickByCallback(object,function(value,key,object){return!predicate(value,key,object)})}); +/** + * Creates a two dimensional array of the key-value pairs for `object`, + * e.g. `[[key1, value1], [key2, value2]]`. + * + * @static + * @memberOf _ + * @category Object + * @param {Object} object The object to query. + * @returns {Array} Returns the new array of key-value pairs. + * @example + * + * _.pairs({ 'barney': 36, 'fred': 40 }); + * // => [['barney', 36], ['fred', 40]] (iteration order is not guaranteed) + */function pairs(object){object=toObject(object);var index=-1,props=keys(object),length=props.length,result=Array(length);while(++index { 'user': 'fred' } + * + * _.pick(object, _.isString); + * // => { 'user': 'fred' } + */var pick=restParam(function(object,props){if(object==null){return{}}return typeof props[0]=="function"?pickByCallback(object,bindCallback(props[0],props[1],3)):pickByArray(object,baseFlatten(props))}); +/** + * This method is like `_.get` except that if the resolved value is a function + * it is invoked with the `this` binding of its parent object and its result + * is returned. + * + * @static + * @memberOf _ + * @category Object + * @param {Object} object The object to query. + * @param {Array|string} path The path of the property to resolve. + * @param {*} [defaultValue] The value returned if the resolved value is `undefined`. + * @returns {*} Returns the resolved value. + * @example + * + * var object = { 'a': [{ 'b': { 'c1': 3, 'c2': _.constant(4) } }] }; + * + * _.result(object, 'a[0].b.c1'); + * // => 3 + * + * _.result(object, 'a[0].b.c2'); + * // => 4 + * + * _.result(object, 'a.b.c', 'default'); + * // => 'default' + * + * _.result(object, 'a.b.c', _.constant('default')); + * // => 'default' + */function result(object,path,defaultValue){var result=object==null?undefined:object[path];if(result===undefined){if(object!=null&&!isKey(path,object)){path=toPath(path);object=path.length==1?object:baseGet(object,baseSlice(path,0,-1));result=object==null?undefined:object[last(path)]}result=result===undefined?defaultValue:result}return isFunction(result)?result.call(object):result} +/** + * Sets the property value of `path` on `object`. If a portion of `path` + * does not exist it is created. + * + * @static + * @memberOf _ + * @category Object + * @param {Object} object The object to augment. + * @param {Array|string} path The path of the property to set. + * @param {*} value The value to set. + * @returns {Object} Returns `object`. + * @example + * + * var object = { 'a': [{ 'b': { 'c': 3 } }] }; + * + * _.set(object, 'a[0].b.c', 4); + * console.log(object.a[0].b.c); + * // => 4 + * + * _.set(object, 'x[0].y.z', 5); + * console.log(object.x[0].y.z); + * // => 5 + */function set(object,path,value){if(object==null){return object}var pathKey=path+"";path=object[pathKey]!=null||isKey(path,object)?[pathKey]:toPath(path);var index=-1,length=path.length,lastIndex=length-1,nested=object;while(nested!=null&&++index [4, 9] + * + * _.transform({ 'a': 1, 'b': 2 }, function(result, n, key) { + * result[key] = n * 3; + * }); + * // => { 'a': 3, 'b': 6 } + */function transform(object,iteratee,accumulator,thisArg){var isArr=isArray(object)||isTypedArray(object);iteratee=getCallback(iteratee,thisArg,4);if(accumulator==null){if(isArr||isObject(object)){var Ctor=object.constructor;if(isArr){accumulator=isArray(object)?new Ctor:[]}else{accumulator=baseCreate(isFunction(Ctor)?Ctor.prototype:undefined)}}else{accumulator={}}}(isArr?arrayEach:baseForOwn)(object,function(value,index,object){return iteratee(accumulator,value,index,object)});return accumulator} +/** + * Creates an array of the own enumerable property values of `object`. + * + * **Note:** Non-object values are coerced to objects. + * + * @static + * @memberOf _ + * @category Object + * @param {Object} object The object to query. + * @returns {Array} Returns the array of property values. + * @example + * + * function Foo() { + * this.a = 1; + * this.b = 2; + * } + * + * Foo.prototype.c = 3; + * + * _.values(new Foo); + * // => [1, 2] (iteration order is not guaranteed) + * + * _.values('hi'); + * // => ['h', 'i'] + */function values(object){return baseValues(object,keys(object))} +/** + * Creates an array of the own and inherited enumerable property values + * of `object`. + * + * **Note:** Non-object values are coerced to objects. + * + * @static + * @memberOf _ + * @category Object + * @param {Object} object The object to query. + * @returns {Array} Returns the array of property values. + * @example + * + * function Foo() { + * this.a = 1; + * this.b = 2; + * } + * + * Foo.prototype.c = 3; + * + * _.valuesIn(new Foo); + * // => [1, 2, 3] (iteration order is not guaranteed) + */function valuesIn(object){return baseValues(object,keysIn(object))} +/*------------------------------------------------------------------------*/ +/** + * Checks if `n` is between `start` and up to but not including, `end`. If + * `end` is not specified it is set to `start` with `start` then set to `0`. + * + * @static + * @memberOf _ + * @category Number + * @param {number} n The number to check. + * @param {number} [start=0] The start of the range. + * @param {number} end The end of the range. + * @returns {boolean} Returns `true` if `n` is in the range, else `false`. + * @example + * + * _.inRange(3, 2, 4); + * // => true + * + * _.inRange(4, 8); + * // => true + * + * _.inRange(4, 2); + * // => false + * + * _.inRange(2, 2); + * // => false + * + * _.inRange(1.2, 2); + * // => true + * + * _.inRange(5.2, 4); + * // => false + */function inRange(value,start,end){start=+start||0;if(end===undefined){end=start;start=0}else{end=+end||0}return value>=nativeMin(start,end)&&value an integer between 0 and 5 + * + * _.random(5); + * // => also an integer between 0 and 5 + * + * _.random(5, true); + * // => a floating-point number between 0 and 5 + * + * _.random(1.2, 5.2); + * // => a floating-point number between 1.2 and 5.2 + */function random(min,max,floating){if(floating&&isIterateeCall(min,max,floating)){max=floating=undefined}var noMin=min==null,noMax=max==null;if(floating==null){if(noMax&&typeof min=="boolean"){floating=min;min=1}else if(typeof max=="boolean"){floating=max;noMax=true}}if(noMin&&noMax){max=1;noMax=false}min=+min||0;if(noMax){max=min;min=0}else{max=+max||0}if(floating||min%1||max%1){var rand=nativeRandom();return nativeMin(min+rand*(max-min+parseFloat("1e-"+((rand+"").length-1))),max)}return baseRandom(min,max)} +/*------------------------------------------------------------------------*/ +/** + * Converts `string` to [camel case](https://en.wikipedia.org/wiki/CamelCase). + * + * @static + * @memberOf _ + * @category String + * @param {string} [string=''] The string to convert. + * @returns {string} Returns the camel cased string. + * @example + * + * _.camelCase('Foo Bar'); + * // => 'fooBar' + * + * _.camelCase('--foo-bar'); + * // => 'fooBar' + * + * _.camelCase('__foo_bar__'); + * // => 'fooBar' + */var camelCase=createCompounder(function(result,word,index){word=word.toLowerCase();return result+(index?word.charAt(0).toUpperCase()+word.slice(1):word)}); +/** + * Capitalizes the first character of `string`. + * + * @static + * @memberOf _ + * @category String + * @param {string} [string=''] The string to capitalize. + * @returns {string} Returns the capitalized string. + * @example + * + * _.capitalize('fred'); + * // => 'Fred' + */function capitalize(string){string=baseToString(string);return string&&string.charAt(0).toUpperCase()+string.slice(1)} +/** + * Deburrs `string` by converting [latin-1 supplementary letters](https://en.wikipedia.org/wiki/Latin-1_Supplement_(Unicode_block)#Character_table) + * to basic latin letters and removing [combining diacritical marks](https://en.wikipedia.org/wiki/Combining_Diacritical_Marks). + * + * @static + * @memberOf _ + * @category String + * @param {string} [string=''] The string to deburr. + * @returns {string} Returns the deburred string. + * @example + * + * _.deburr('déjà vu'); + * // => 'deja vu' + */function deburr(string){string=baseToString(string);return string&&string.replace(reLatin1,deburrLetter).replace(reComboMark,"")} +/** + * Checks if `string` ends with the given target string. + * + * @static + * @memberOf _ + * @category String + * @param {string} [string=''] The string to search. + * @param {string} [target] The string to search for. + * @param {number} [position=string.length] The position to search from. + * @returns {boolean} Returns `true` if `string` ends with `target`, else `false`. + * @example + * + * _.endsWith('abc', 'c'); + * // => true + * + * _.endsWith('abc', 'b'); + * // => false + * + * _.endsWith('abc', 'b', 2); + * // => true + */function endsWith(string,target,position){string=baseToString(string);target=target+"";var length=string.length;position=position===undefined?length:nativeMin(position<0?0:+position||0,length);position-=target.length;return position>=0&&string.indexOf(target,position)==position} +/** + * Converts the characters "&", "<", ">", '"', "'", and "\`", in `string` to + * their corresponding HTML entities. + * + * **Note:** No other characters are escaped. To escape additional characters + * use a third-party library like [_he_](https://mths.be/he). + * + * Though the ">" character is escaped for symmetry, characters like + * ">" and "/" don't need escaping in HTML and have no special meaning + * unless they're part of a tag or unquoted attribute value. + * See [Mathias Bynens's article](https://mathiasbynens.be/notes/ambiguous-ampersands) + * (under "semi-related fun fact") for more details. + * + * Backticks are escaped because in Internet Explorer < 9, they can break out + * of attribute values or HTML comments. See [#59](https://html5sec.org/#59), + * [#102](https://html5sec.org/#102), [#108](https://html5sec.org/#108), and + * [#133](https://html5sec.org/#133) of the [HTML5 Security Cheatsheet](https://html5sec.org/) + * for more details. + * + * When working with HTML you should always [quote attribute values](http://wonko.com/post/html-escaping) + * to reduce XSS vectors. + * + * @static + * @memberOf _ + * @category String + * @param {string} [string=''] The string to escape. + * @returns {string} Returns the escaped string. + * @example + * + * _.escape('fred, barney, & pebbles'); + * // => 'fred, barney, & pebbles' + */function escape(string){ +// Reset `lastIndex` because in IE < 9 `String#replace` does not. +string=baseToString(string);return string&&reHasUnescapedHtml.test(string)?string.replace(reUnescapedHtml,escapeHtmlChar):string} +/** + * Escapes the `RegExp` special characters "\", "/", "^", "$", ".", "|", "?", + * "*", "+", "(", ")", "[", "]", "{" and "}" in `string`. + * + * @static + * @memberOf _ + * @category String + * @param {string} [string=''] The string to escape. + * @returns {string} Returns the escaped string. + * @example + * + * _.escapeRegExp('[lodash](https://lodash.com/)'); + * // => '\[lodash\]\(https:\/\/lodash\.com\/\)' + */function escapeRegExp(string){string=baseToString(string);return string&&reHasRegExpChars.test(string)?string.replace(reRegExpChars,escapeRegExpChar):string||"(?:)"} +/** + * Converts `string` to [kebab case](https://en.wikipedia.org/wiki/Letter_case#Special_case_styles). + * + * @static + * @memberOf _ + * @category String + * @param {string} [string=''] The string to convert. + * @returns {string} Returns the kebab cased string. + * @example + * + * _.kebabCase('Foo Bar'); + * // => 'foo-bar' + * + * _.kebabCase('fooBar'); + * // => 'foo-bar' + * + * _.kebabCase('__foo_bar__'); + * // => 'foo-bar' + */var kebabCase=createCompounder(function(result,word,index){return result+(index?"-":"")+word.toLowerCase()}); +/** + * Pads `string` on the left and right sides if it's shorter than `length`. + * Padding characters are truncated if they can't be evenly divided by `length`. + * + * @static + * @memberOf _ + * @category String + * @param {string} [string=''] The string to pad. + * @param {number} [length=0] The padding length. + * @param {string} [chars=' '] The string used as padding. + * @returns {string} Returns the padded string. + * @example + * + * _.pad('abc', 8); + * // => ' abc ' + * + * _.pad('abc', 8, '_-'); + * // => '_-abc_-_' + * + * _.pad('abc', 3); + * // => 'abc' + */function pad(string,length,chars){string=baseToString(string);length=+length;var strLength=string.length;if(strLength>=length||!nativeIsFinite(length)){return string}var mid=(length-strLength)/2,leftLength=nativeFloor(mid),rightLength=nativeCeil(mid);chars=createPadding("",rightLength,chars);return chars.slice(0,leftLength)+string+chars} +/** + * Pads `string` on the left side if it's shorter than `length`. Padding + * characters are truncated if they exceed `length`. + * + * @static + * @memberOf _ + * @category String + * @param {string} [string=''] The string to pad. + * @param {number} [length=0] The padding length. + * @param {string} [chars=' '] The string used as padding. + * @returns {string} Returns the padded string. + * @example + * + * _.padLeft('abc', 6); + * // => ' abc' + * + * _.padLeft('abc', 6, '_-'); + * // => '_-_abc' + * + * _.padLeft('abc', 3); + * // => 'abc' + */var padLeft=createPadDir(); +/** + * Pads `string` on the right side if it's shorter than `length`. Padding + * characters are truncated if they exceed `length`. + * + * @static + * @memberOf _ + * @category String + * @param {string} [string=''] The string to pad. + * @param {number} [length=0] The padding length. + * @param {string} [chars=' '] The string used as padding. + * @returns {string} Returns the padded string. + * @example + * + * _.padRight('abc', 6); + * // => 'abc ' + * + * _.padRight('abc', 6, '_-'); + * // => 'abc_-_' + * + * _.padRight('abc', 3); + * // => 'abc' + */var padRight=createPadDir(true); +/** + * Converts `string` to an integer of the specified radix. If `radix` is + * `undefined` or `0`, a `radix` of `10` is used unless `value` is a hexadecimal, + * in which case a `radix` of `16` is used. + * + * **Note:** This method aligns with the [ES5 implementation](https://es5.github.io/#E) + * of `parseInt`. + * + * @static + * @memberOf _ + * @category String + * @param {string} string The string to convert. + * @param {number} [radix] The radix to interpret `value` by. + * @param- {Object} [guard] Enables use as a callback for functions like `_.map`. + * @returns {number} Returns the converted integer. + * @example + * + * _.parseInt('08'); + * // => 8 + * + * _.map(['6', '08', '10'], _.parseInt); + * // => [6, 8, 10] + */function parseInt(string,radix,guard){ +// Firefox < 21 and Opera < 15 follow ES3 for `parseInt`. +// Chrome fails to trim leading whitespace characters. +// See https://code.google.com/p/v8/issues/detail?id=3109 for more details. +if(guard?isIterateeCall(string,radix,guard):radix==null){radix=0}else if(radix){radix=+radix}string=trim(string);return nativeParseInt(string,radix||(reHasHexPrefix.test(string)?16:10))} +/** + * Repeats the given string `n` times. + * + * @static + * @memberOf _ + * @category String + * @param {string} [string=''] The string to repeat. + * @param {number} [n=0] The number of times to repeat the string. + * @returns {string} Returns the repeated string. + * @example + * + * _.repeat('*', 3); + * // => '***' + * + * _.repeat('abc', 2); + * // => 'abcabc' + * + * _.repeat('abc', 0); + * // => '' + */function repeat(string,n){var result="";string=baseToString(string);n=+n;if(n<1||!string||!nativeIsFinite(n)){return result} +// Leverage the exponentiation by squaring algorithm for a faster repeat. +// See https://en.wikipedia.org/wiki/Exponentiation_by_squaring for more details. +do{if(n%2){result+=string}n=nativeFloor(n/2);string+=string}while(n);return result} +/** + * Converts `string` to [snake case](https://en.wikipedia.org/wiki/Snake_case). + * + * @static + * @memberOf _ + * @category String + * @param {string} [string=''] The string to convert. + * @returns {string} Returns the snake cased string. + * @example + * + * _.snakeCase('Foo Bar'); + * // => 'foo_bar' + * + * _.snakeCase('fooBar'); + * // => 'foo_bar' + * + * _.snakeCase('--foo-bar'); + * // => 'foo_bar' + */var snakeCase=createCompounder(function(result,word,index){return result+(index?"_":"")+word.toLowerCase()}); +/** + * Converts `string` to [start case](https://en.wikipedia.org/wiki/Letter_case#Stylistic_or_specialised_usage). + * + * @static + * @memberOf _ + * @category String + * @param {string} [string=''] The string to convert. + * @returns {string} Returns the start cased string. + * @example + * + * _.startCase('--foo-bar'); + * // => 'Foo Bar' + * + * _.startCase('fooBar'); + * // => 'Foo Bar' + * + * _.startCase('__foo_bar__'); + * // => 'Foo Bar' + */var startCase=createCompounder(function(result,word,index){return result+(index?" ":"")+(word.charAt(0).toUpperCase()+word.slice(1))}); +/** + * Checks if `string` starts with the given target string. + * + * @static + * @memberOf _ + * @category String + * @param {string} [string=''] The string to search. + * @param {string} [target] The string to search for. + * @param {number} [position=0] The position to search from. + * @returns {boolean} Returns `true` if `string` starts with `target`, else `false`. + * @example + * + * _.startsWith('abc', 'a'); + * // => true + * + * _.startsWith('abc', 'b'); + * // => false + * + * _.startsWith('abc', 'b', 1); + * // => true + */function startsWith(string,target,position){string=baseToString(string);position=position==null?0:nativeMin(position<0?0:+position||0,string.length);return string.lastIndexOf(target,position)==position} +/** + * Creates a compiled template function that can interpolate data properties + * in "interpolate" delimiters, HTML-escape interpolated data properties in + * "escape" delimiters, and execute JavaScript in "evaluate" delimiters. Data + * properties may be accessed as free variables in the template. If a setting + * object is provided it takes precedence over `_.templateSettings` values. + * + * **Note:** In the development build `_.template` utilizes + * [sourceURLs](http://www.html5rocks.com/en/tutorials/developertools/sourcemaps/#toc-sourceurl) + * for easier debugging. + * + * For more information on precompiling templates see + * [lodash's custom builds documentation](https://lodash.com/custom-builds). + * + * For more information on Chrome extension sandboxes see + * [Chrome's extensions documentation](https://developer.chrome.com/extensions/sandboxingEval). + * + * @static + * @memberOf _ + * @category String + * @param {string} [string=''] The template string. + * @param {Object} [options] The options object. + * @param {RegExp} [options.escape] The HTML "escape" delimiter. + * @param {RegExp} [options.evaluate] The "evaluate" delimiter. + * @param {Object} [options.imports] An object to import into the template as free variables. + * @param {RegExp} [options.interpolate] The "interpolate" delimiter. + * @param {string} [options.sourceURL] The sourceURL of the template's compiled source. + * @param {string} [options.variable] The data object variable name. + * @param- {Object} [otherOptions] Enables the legacy `options` param signature. + * @returns {Function} Returns the compiled template function. + * @example + * + * // using the "interpolate" delimiter to create a compiled template + * var compiled = _.template('hello <%= user %>!'); + * compiled({ 'user': 'fred' }); + * // => 'hello fred!' + * + * // using the HTML "escape" delimiter to escape data property values + * var compiled = _.template('<%- value %>'); + * compiled({ 'value': '"; + + // API Call + this._el.content_item.innerHTML = content; + + // After Loaded + this.onLoaded(); + }, + + // Update Media Display + _updateMediaDisplay: function() { + this._el.content_item.style.height = this.options.height + "px"; + } + + +}); + + +/* ********************************************** + Begin TL.Media.Text.js +********************************************** */ + +TL.Media.Text = TL.Class.extend({ + + includes: [TL.Events], + + // DOM ELEMENTS + _el: { + container: {}, + content_container: {}, + content: {}, + headline: {}, + date: {} + }, + + // Data + data: { + unique_id: "", + headline: "headline", + text: "text" + }, + + // Options + options: { + title: false + }, + + /* Constructor + ================================================== */ + initialize: function(data, options, add_to_container) { + + TL.Util.setData(this, data); + + // Merge Options + TL.Util.mergeData(this.options, options); + + this._el.container = TL.Dom.create("div", "tl-text"); + this._el.container.id = this.data.unique_id; + + this._initLayout(); + + if (add_to_container) { + add_to_container.appendChild(this._el.container); + }; + + }, + + /* Adding, Hiding, Showing etc + ================================================== */ + show: function() { + + }, + + hide: function() { + + }, + + addTo: function(container) { + container.appendChild(this._el.container); + //this.onAdd(); + }, + + removeFrom: function(container) { + container.removeChild(this._el.container); + }, + + headlineHeight: function() { + return this._el.headline.offsetHeight + 40; + }, + + addDateText: function(str) { + this._el.date.innerHTML = str; + }, + + /* Events + ================================================== */ + onLoaded: function() { + this.fire("loaded", this.data); + }, + + onAdd: function() { + this.fire("added", this.data); + }, + + onRemove: function() { + this.fire("removed", this.data); + }, + + /* Private Methods + ================================================== */ + _initLayout: function () { + + // Create Layout + this._el.content_container = TL.Dom.create("div", "tl-text-content-container", this._el.container); + + // Date + this._el.date = TL.Dom.create("h3", "tl-headline-date", this._el.content_container); + + // Headline + if (this.data.headline != "") { + var headline_class = "tl-headline"; + if (this.options.title) { + headline_class = "tl-headline tl-headline-title"; + } + this._el.headline = TL.Dom.create("h2", headline_class, this._el.content_container); + this._el.headline.innerHTML = this.data.headline; + } + + // Text + if (this.data.text != "") { + var text_content = ""; + + text_content += TL.Util.htmlify(this.options.autolink == true ? TL.Util.linkify(this.data.text) : this.data.text); + trace(this.data.text); + this._el.content = TL.Dom.create("div", "tl-text-content", this._el.content_container); + this._el.content.innerHTML = text_content; + trace(text_content); + trace(this._el.content) + } + + // Fire event that the slide is loaded + this.onLoaded(); + + } + +}); + + +/* ********************************************** + Begin TL.Media.Twitter.js +********************************************** */ + +/* TL.Media.Twitter + Produces Twitter Display +================================================== */ + +TL.Media.Twitter = TL.Media.extend({ + + includes: [TL.Events], + + + + /* Load the media + ================================================== */ + + _loadMedia: function() { + var api_url, + self = this; + + // Create Dom element + this._el.content_item = TL.Dom.create("div", "tl-media-twitter", this._el.content); + this._el.content_container.className = "tl-media-content-container tl-media-content-container-text"; + + // Get Media ID + if(this.data.url.match("^(https?:)?\/*(www.)?twitter\.com")) + { + if (this.data.url.match("status\/")) { + this.media_id = this.data.url.split("status\/")[1]; + } else if (this.data.url.match("statuses\/")) { + this.media_id = this.data.url.split("statuses\/")[1]; + } else { + this.media_id = ""; + } + } + + else if(this.data.url.match("
2) { + this.media_id = found[2]; + } else { + self.loadErrorDisplay(self._("twitterembed_invalidurl_err")); + return; + } + } + + // API URL + api_url = "https://api.twitter.com/1/statuses/oembed.json?id=" + this.media_id + "&omit_script=true&include_entities=true&callback=?"; + + // API Call + TL.ajax({ + type: 'GET', + url: api_url, + dataType: 'json', //json data type + success: function(d){ + self.createMedia(d); + }, + error:function(xhr, type){ + var error_text = ""; + error_text += self._("twitter_load_err") + "
" + self.media_id + "
" + type; + self.loadErrorDisplay(error_text); + } + }); + + }, + + createMedia: function(d) { + trace("create_media") + var tweet = "", + tweet_text = "", + tweetuser = "", + tweet_status_temp = "", + tweet_status_url = "", + tweet_status_date = "", + self = this; + + // TWEET CONTENT + tweet_text = d.html.split("<\/p>\—")[0] + "

"; + tweetuser = d.author_url.split("twitter.com\/")[1]; + tweet_status_temp = d.html.split("<\/p>\—")[1].split("")[0]; + tweet_status_date = tweet_status_temp.split("\"\>")[1].split("<\/a>")[0]; + + // Open links in new window + tweet_text = tweet_text.replace(/"; + tweet += "" + ""; + tweet += ""; + tweet += ""; + + + // Add to DOM + this._el.content_item.innerHTML = tweet; + + // After Loaded + this.onLoaded(); + } + }, + + + updateMediaDisplay: function() { + + }, + + _updateMediaDisplay: function() { + + }, + + + + +}); + + +/* ********************************************** + Begin TL.Media.TwitterEmbed.js +********************************************** */ + +/* TL.Media.TwitterEmbed + Produces Twitter Display +================================================== */ + +var mediaID; + +TL.Media.TwitterEmbed = TL.Media.extend({ + includes: [TL.Events], + + + + /* Load the media + ================================================== */ + _loadMedia: function() { + var api_url, + self = this; + + // Create Dom element + this._el.content_item = TL.Dom.create("div", "tl-media-twitter", this._el.content); + this._el.content_container.className = "tl-media-content-container tl-media-content-container-text"; + + // Get Media ID + var found = this.data.url.match(/(status|statuses)\/(\d+)/); + if (found && found.length > 2) { + this.media_id = found[2]; + } else { + self.loadErrorDisplay(self._("twitterembed_invalidurl_err")); + return; + } + + // API URL + api_url = "https://api.twitter.com/1/statuses/oembed.json?id=" + this.media_id + "&omit_script=true&include_entities=true&callback=?"; + + window.twttr = (function(d, s, id) { + var js, fjs = d.getElementsByTagName(s)[0], + t = window.twttr || {}; + if (d.getElementById(id)) return t; + js = d.createElement(s); + js.id = id; + js.src = "https://platform.twitter.com/widgets.js"; + fjs.parentNode.insertBefore(js, fjs); + + t._e = []; + t.ready = function(f) { + t._e.push(f); + }; + + return t; + }(document, "script", "twitter-wjs")); + + mediaID = this.media_id; + + // API Call + TL.ajax({ + type: 'GET', + url: api_url, + dataType: 'json', //json data type + success: function(d){ + self.createMedia(d); + }, + error:function(xhr, type){ + var error_text = ""; + error_text += self._("twitter_load_err") + "
" + self.media_id + "
" + type; + self.loadErrorDisplay(error_text); + } + }); + + }, + + createMedia: function(d) { + trace("create_media") + var tweet = "", + tweet_text = "", + tweetuser = "", + tweet_status_temp = "", + tweet_status_url = "", + tweet_status_date = ""; + + // TWEET CONTENT + tweet_text = d.html.split("<\/p>\—")[0] + "

"; + console.log(tweet_text); + tweetuser = d.author_url.split("twitter.com\/")[1]; + tweet_status_temp = d.html.split("<\/p>\—")[1].split("")[0]; + tweet_status_date = tweet_status_temp.split("\"\>")[1].split("<\/a>")[0]; + + // Open links in new window + tweet_text = tweet_text.replace(/"; + tweet += ""; + tweet += ""; + + + // Add to DOM + this._el.content_item.innerHTML = tweet; + + // After Loaded + this.onLoaded(); + } + + }, + + updateMediaDisplay: function() { + + }, + + _updateMediaDisplay: function() { + + } + + + +}); + +/* ********************************************** + Begin TL.Media.Vimeo.js +********************************************** */ + +/* TL.Media.Vimeo +================================================== */ + +TL.Media.Vimeo = TL.Media.extend({ + + includes: [TL.Events], + + /* Load the media + ================================================== */ + _loadMedia: function() { + var api_url, + self = this; + + // Create Dom element + this._el.content_item = TL.Dom.create("div", "tl-media-item tl-media-iframe tl-media-vimeo tl-media-shadow", this._el.content); + + // Get Media ID + this.media_id = this.data.url.split(/video\/|\/\/vimeo\.com\//)[1].split(/[?&]/)[0]; + var start_time = null; + + // Get start time + if (this.data.url.match(/#t=([^&]+).*/)) { + start_time = this.data.url.match(/#t=([^&]+).*/)[1]; + } + + // API URL + api_url = "https://player.vimeo.com/video/" + this.media_id + "?api=1&title=0&byline=0&portrait=0&color=ffffff"; + if (start_time) { + api_url = api_url += '&#t=' + start_time; + } + + this.player = TL.Dom.create("iframe", "", this._el.content_item); + + // Media Loaded Event + this.player.addEventListener('load', function(e) { + self.onMediaLoaded(); + }); + + this.player.width = "100%"; + this.player.height = "100%"; + this.player.frameBorder = "0"; + this.player.src = api_url; + + this.player.setAttribute('allowfullscreen', ''); + this.player.setAttribute('webkitallowfullscreen', ''); + this.player.setAttribute('mozallowfullscreen', ''); + + // After Loaded + this.onLoaded(); + }, + + // Update Media Display + _updateMediaDisplay: function() { + this._el.content_item.style.height = TL.Util.ratio.r16_9({w:this._el.content_item.offsetWidth}) + "px"; + }, + + _stopMedia: function() { + + try { + this.player.contentWindow.postMessage(JSON.stringify({method: "pause"}), "https://player.vimeo.com"); + } + catch(err) { + trace(err); + } + } +}); + + +/* ********************************************** + Begin TL.Media.Vine.js +********************************************** */ + +/* TL.Media.Vine + +================================================== */ + +TL.Media.Vine = TL.Media.extend({ + + includes: [TL.Events], + + /* Load the media + ================================================== */ + _loadMedia: function() { + var api_url, + self = this; + + // Create Dom element + this._el.content_item = TL.Dom.create("div", "tl-media-item tl-media-iframe tl-media-vine tl-media-shadow", this._el.content); + + // Get Media ID + this.media_id = this.data.url.split("vine.co/v/")[1]; + + // API URL + api_url = "https://vine.co/v/" + this.media_id + "/embed/simple"; + + // API Call + this._el.content_item.innerHTML = "" + + // After Loaded + this.onLoaded(); + }, + + // Update Media Display + _updateMediaDisplay: function() { + var size = TL.Util.ratio.square({w:this._el.content_item.offsetWidth , h:this.options.height}); + this._el.content_item.style.height = size.h + "px"; + }, + + _stopMedia: function() { + this._el.content_item.querySelector("iframe").contentWindow.postMessage('pause', '*'); + } + +}); + + +/* ********************************************** + Begin TL.Media.Website.js +********************************************** */ + +/* TL.Media.Website + Uses Embedly + http://embed.ly/docs/api/extract/endpoints/1/extract +================================================== */ + +TL.Media.Website = TL.Media.extend({ + + includes: [TL.Events], + + /* Load the media + ================================================== */ + _loadMedia: function() { + var self = this; + + // Get Media ID + this.media_id = this.data.url.replace(/.*?:\/\//g, ""); + + if (this.options.api_key_embedly) { + // API URL + api_url = "https://api.embed.ly/1/extract?key=" + this.options.api_key_embedly + "&url=" + this.media_id + "&callback=?"; + + // API Call + TL.getJSON(api_url, function(d) { + self.createMedia(d); + }); + } else { + this.createCardContent(); + } + }, + + createCardContent: function() { + (function(w, d){ + var id='embedly-platform', n = 'script'; + if (!d.getElementById(id)){ + w.embedly = w.embedly || function() {(w.embedly.q = w.embedly.q || []).push(arguments);}; + var e = d.createElement(n); e.id = id; e.async=1; + e.src = ('https:' === document.location.protocol ? 'https' : 'http') + '://cdn.embedly.com/widgets/platform.js'; + var s = d.getElementsByTagName(n)[0]; + s.parentNode.insertBefore(e, s); + } + })(window, document); + + var content = "" + this.data.url + ""; + this._setContent(content); + + }, + createMedia: function(d) { // this costs API credits... + var content = ""; + + + content += "

" + d.title + "

"; + if (d.images) { + if (d.images[0]) { + trace(d.images[0].url); + content += ""; + } + } + if (d.favicon_url) { + content += ""; + } + content += "" + d.provider_name + "
"; + content += "

" + d.description + "

"; + + this._setContent(content); + }, + + _setContent: function(content) { + // Create Dom element + this._el.content_item = TL.Dom.create("div", "tl-media-item tl-media-website", this._el.content); + this._el.content_container.className = "tl-media-content-container tl-media-content-container-text"; + this._el.content_item.innerHTML = content; + + // After Loaded + this.onLoaded(); + + }, + + updateMediaDisplay: function() { + + }, + + _updateMediaDisplay: function() { + + } + + +}); + + +/* ********************************************** + Begin TL.Media.Wikipedia.js +********************************************** */ + +/* TL.Media.Wikipedia +================================================== */ + +TL.Media.Wikipedia = TL.Media.extend({ + + includes: [TL.Events], + + /* Load the media + ================================================== */ + _loadMedia: function() { + var api_url, + api_language, + self = this; + + // Create Dom element + this._el.content_item = TL.Dom.create("div", "tl-media-item tl-media-wikipedia", this._el.content); + this._el.content_container.className = "tl-media-content-container tl-media-content-container-text"; + + // Get Media ID + this.media_id = this.data.url.split("wiki\/")[1].split("#")[0].replace("_", " "); + this.media_id = this.media_id.replace(" ", "%20"); + api_language = this.data.url.split("//")[1].split(".wikipedia")[0]; + + // API URL + api_url = "https://" + api_language + ".wikipedia.org/w/api.php?action=query&prop=extracts|pageimages&redirects=&titles=" + this.media_id + "&exintro=1&format=json&callback=?"; + + // API Call + TL.ajax({ + type: 'GET', + url: api_url, + dataType: 'json', //json data type + + success: function(d){ + self.createMedia(d); + }, + error:function(xhr, type){ + var error_text = ""; + error_text += self._("wikipedia_load_err") + "
" + self.media_id + "
" + type; + self.loadErrorDisplay(error_text); + } + }); + + }, + + createMedia: function(d) { + var wiki = ""; + + if (d.query) { + var content = "", + wiki = { + entry: {}, + title: "", + text: "", + extract: "", + paragraphs: 1, + page_image: "", + text_array: [] + }; + + wiki.entry = TL.Util.getObjectAttributeByIndex(d.query.pages, 0); + wiki.extract = wiki.entry.extract; + wiki.title = wiki.entry.title; + wiki.page_image = wiki.entry.thumbnail; + + if (wiki.extract.match("

")) { + wiki.text_array = wiki.extract.split("

"); + } else { + wiki.text_array.push(wiki.extract); + } + + for(var i = 0; i < wiki.text_array.length; i++) { + if (i+1 <= wiki.paragraphs && i+1 < wiki.text_array.length) { + wiki.text += "

" + wiki.text_array[i+1]; + } + } + + + content += ""; + content += "

" + wiki.title + "

"; + content += "" + this._('wikipedia') + "
"; + + if (wiki.page_image) { + //content += ""; + } + + content += wiki.text; + + if (wiki.extract.match("REDIRECT")) { + + } else { + // Add to DOM + this._el.content_item.innerHTML = content; + // After Loaded + this.onLoaded(); + } + + + } + + }, + + updateMediaDisplay: function() { + + }, + + _updateMediaDisplay: function() { + + } + +}); + + +/* ********************************************** + Begin TL.Media.Wistia.js +********************************************** */ + +/* TL.Media.Wistia +================================================== */ + +TL.Media.Wistia = TL.Media.extend({ + + includes: [TL.Events], + + /* Load the media + ================================================== */ + _loadMedia: function() { + var api_url, + self = this; + + // Create Dom element + this._el.content_item = TL.Dom.create("div", "tl-media-item tl-media-iframe tl-media-wistia tl-media-shadow", this._el.content); + + // Get Media ID + this.media_id = this.data.url.split(/https?:\/\/(.+)?(wistia\.com|wi\.st)\/medias\/(.*)/)[3]; + + // API URL + api_url = "https://fast.wistia.com/embed/iframe/" + this.media_id + "?version=v1&controlsVisibleOnLoad=true&playerColor=aae3d8"; + + this.player = TL.Dom.create("iframe", "", this._el.content_item); + + // Media Loaded Event + this.player.addEventListener('load', function(e) { + self.onMediaLoaded(); + }); + + this.player.width = "100%"; + this.player.height = "100%"; + this.player.frameBorder = "0"; + this.player.src = api_url; + + this.player.setAttribute('allowfullscreen', ''); + this.player.setAttribute('webkitallowfullscreen', ''); + this.player.setAttribute('mozallowfullscreen', ''); + + // After Loaded + this.onLoaded(); + }, + + // Update Media Display + _updateMediaDisplay: function() { + this._el.content_item.style.height = TL.Util.ratio.r16_9({w:this._el.content_item.offsetWidth}) + "px"; + }, + + _stopMedia: function() { + try { + this.player.contentWindow.postMessage(JSON.stringify({method: "pause"}), "https://player.vimeo.com"); + } + catch(err) { + trace(err); + } + } +}); + + +/* ********************************************** + Begin TL.Media.YouTube.js +********************************************** */ + +/* TL.Media.YouTube +================================================== */ + +TL.Media.YouTube = TL.Media.extend({ + + includes: [TL.Events], + + /* Load the media + ================================================== */ + _loadMedia: function() { + var self = this, + url_vars; + + this.youtube_loaded = false; + + // Create Dom element + this._el.content_item = TL.Dom.create("div", "tl-media-item tl-media-youtube tl-media-shadow", this._el.content); + this._el.content_item.id = TL.Util.unique_ID(7) + + // URL Vars + url_vars = TL.Util.getUrlVars(this.data.url); + + // Get Media ID + this.media_id = {}; + + if (this.data.url.match('v=')) { + this.media_id.id = url_vars["v"]; + } else if (this.data.url.match('\/embed\/')) { + this.media_id.id = this.data.url.split("embed\/")[1].split(/[?&]/)[0]; + } else if (this.data.url.match(/v\/|v=|youtu\.be\//)){ + this.media_id.id = this.data.url.split(/v\/|v=|youtu\.be\//)[1].split(/[?&]/)[0]; + } else { + trace("YOUTUBE IN URL BUT NOT A VALID VIDEO"); + } + + // Get start second + if (this.data.url.match("start=")) { + this.media_id.start = parseInt(this.data.url.split("start=")[1], 10); + } + else if (this.data.url.match("t=")) { + this.media_id.start = parseInt(this.data.url.split("t=")[1], 10); + } + + //Get end second + if (this.data.url.match("end=")) { + this.media_id.end = parseInt(this.data.url.split("end=")[1], 10); + } + + this.media_id.hd = Boolean(typeof(url_vars["hd"]) != 'undefined'); + + + // API Call + TL.Load.js('https://www.youtube.com/iframe_api', function() { + self.createMedia(); + }); + + }, + + // Update Media Display + _updateMediaDisplay: function() { + //this.el.content_item = document.getElementById(this._el.content_item.id); + this._el.content_item.style.height = TL.Util.ratio.r16_9({w:this.options.width}) + "px"; + this._el.content_item.style.width = this.options.width + "px"; + }, + + _stopMedia: function() { + if (this.youtube_loaded) { + try { + if(this.player.getPlayerState() == YT.PlayerState.PLAYING) { + this.player.pauseVideo(); + } + } + catch(err) { + trace(err); + } + + } + }, + createMedia: function() { + var self = this; + + clearTimeout(this.timer); + + if(typeof YT != 'undefined' && typeof YT.Player != 'undefined') { + // Create Player + this.player = new YT.Player(this._el.content_item.id, { + playerVars: { + enablejsapi: 1, + color: 'white', + controls: 1, + start: this.media_id.start, + end: this.media_id.end, + fs: 1 + }, + videoId: this.media_id.id, + events: { + onReady: function() { + self.onPlayerReady(); + // After Loaded + self.onLoaded(); + }, + 'onStateChange': self.onStateChange + } + }); + } else { + this.timer = setTimeout(function() { + self.createMedia(); + }, 1000); + } + }, + + /* Events + ================================================== */ + onPlayerReady: function(e) { + this.youtube_loaded = true; + this._el.content_item = document.getElementById(this._el.content_item.id); + this.onMediaLoaded(); + + }, + + onStateChange: function(e) { + if(e.data == YT.PlayerState.ENDED) { + e.target.seekTo(0); + e.target.pauseVideo(); + } + } + + +}); + + +/* ********************************************** + Begin TL.Media.Audio.js +********************************************** */ + +/* TL.Media.Audio + Produces audio assets. + Takes a data object and populates a dom object +================================================== */ + +TL.Media.Audio = TL.Media.extend({ + + includes: [TL.Events], + + /* Load the media + ================================================== */ + _loadMedia: function() { + // Loading Message + this.loadingMessage(); + + // Create media? + if(!this.options.background) { + this.createMedia(); + } + + // After loaded + this.onLoaded(); + }, + + createMedia: function() { + var self = this, + audio_class = "tl-media-item tl-media-audio tl-media-shadow"; + + // Link + if (this.data.link) { + this._el.content_link = TL.Dom.create("a", "", this._el.content); + this._el.content_link.href = this.data.link; + this._el.content_link.target = "_blank"; + this._el.content_item = TL.Dom.create("audio", audio_class, this._el.content_link); + } else { + this._el.content_item = TL.Dom.create("audio", audio_class, this._el.content); + } + + this._el.content_item.controls = true; + this._el.source_item = TL.Dom.create("source", "", this._el.content_item); + + // Media Loaded Event + this._el.content_item.addEventListener('load', function(e) { + self.onMediaLoaded(); + }); + + this._el.source_item.src = this.data.url; + this._el.source_item.type = this._getType(this.data.url, this.data.mediatype.match_str); + this._el.content_item.innerHTML += "Your browser doesn't support HTML5 audio with " + this._el.source_item.type; + }, + + _updateMediaDisplay: function(layout) { + if(TL.Browser.firefox) { + this._el.content_item.style.width = "auto"; + } + }, + + _getType: function(url, reg) { + var ext = url.match(reg); + var type = "audio/" + switch(ext[1]) { + case "mp3": + type += "mpeg"; + break; + case "wav": + type += "wav"; + break; + case "m4a": + type += "mp4"; + break; + default: + type = "audio"; + break; + } + return type + } + +}); + + +/* ********************************************** + Begin TL.Media.Video.js +********************************************** */ + +/* TL.Media.Video + Produces video assets. + Takes a data object and populates a dom object +================================================== */ + +TL.Media.Video = TL.Media.extend({ + + includes: [TL.Events], + + /* Load the media + ================================================== */ + _loadMedia: function() { + // Loading Message + this.loadingMessage(); + + // Create media? + if(!this.options.background) { + this.createMedia(); + } + + // After loaded + this.onLoaded(); + }, + + createMedia: function() { + var self = this, + video_class = "tl-media-item tl-media-video tl-media-shadow"; + + // Link + if (this.data.link) { + this._el.content_link = TL.Dom.create("a", "", this._el.content); + this._el.content_link.href = this.data.link; + this._el.content_link.target = "_blank"; + this._el.content_item = TL.Dom.create("video", video_class, this._el.content_link); + } else { + this._el.content_item = TL.Dom.create("video", video_class, this._el.content); + } + + this._el.content_item.controls = true; + this._el.source_item = TL.Dom.create("source", "", this._el.content_item); + + // Media Loaded Event + this._el.content_item.addEventListener('load', function(e) { + self.onMediaLoaded(); + }); + + this._el.source_item.src = this.data.url; + this._el.source_item.type = this._getType(this.data.url, this.data.mediatype.match_str); + this._el.content_item.innerHTML += "Your browser doesn't support HTML5 video with " + this._el.source_item.type; + }, + + _updateMediaDisplay: function(layout) { + if(TL.Browser.firefox) { + this._el.content_item.style.width = "auto"; + } + }, + + _getType: function(url, reg) { + var ext = url.match(reg); + var type = "video/" + switch(ext[1]) { + case "mp4": + type += "mp4"; + break; + default: + type = "video"; + break; + } + return type + } + +}); + + +/* ********************************************** + Begin TL.Slide.js +********************************************** */ + +/* TL.Slide + Creates a slide. Takes a data object and + populates the slide with content. +================================================== */ + +TL.Slide = TL.Class.extend({ + + includes: [TL.Events, TL.DomMixins, TL.I18NMixins], + + _el: {}, + + /* Constructor + ================================================== */ + initialize: function(data, options, title_slide) { + // DOM Elements + this._el = { + container: {}, + scroll_container: {}, + background: {}, + content_container: {}, + content: {} + }; + + // Components + this._media = null; + this._mediaclass = {}; + this._text = {}; + this._background_media = null; + + // State + this._state = { + loaded: false + }; + + this.has = { + headline: false, + text: false, + media: false, + title: false, + background: { + image: false, + color: false, + color_value :"" + } + } + + this.has.title = title_slide; + + // Data + this.data = { + unique_id: null, + background: null, + start_date: null, + end_date: null, + location: null, + text: null, + media: null, + autolink: true + }; + + // Options + this.options = { + // animation + duration: 1000, + slide_padding_lr: 40, + ease: TL.Ease.easeInSpline, + width: 600, + height: 600, + skinny_size: 650, + media_name: "" + }; + + // Actively Displaying + this.active = false; + + // Animation Object + this.animator = {}; + + // Merge Data and Options + TL.Util.mergeData(this.options, options); + TL.Util.mergeData(this.data, data); + + this._initLayout(); + this._initEvents(); + + + }, + + /* Adding, Hiding, Showing etc + ================================================== */ + show: function() { + this.animator = TL.Animate(this._el.slider_container, { + left: -(this._el.container.offsetWidth * n) + "px", + duration: this.options.duration, + easing: this.options.ease + }); + }, + + hide: function() { + + }, + + setActive: function(is_active) { + this.active = is_active; + + if (this.active) { + if (this.data.background) { + this.fire("background_change", this.has.background); + } + this.loadMedia(); + } else { + this.stopMedia(); + } + }, + + addTo: function(container) { + container.appendChild(this._el.container); + //this.onAdd(); + }, + + removeFrom: function(container) { + container.removeChild(this._el.container); + }, + + updateDisplay: function(w, h, l) { + this._updateDisplay(w, h, l); + }, + + loadMedia: function() { + var self = this; + + if (this._media && !this._state.loaded) { + this._media.loadMedia(); + this._state.loaded = true; + } + + if(this._background_media && !this._background_media._state.loaded) { + this._background_media.on("loaded", function() { + self._updateBackgroundDisplay(); + }); + this._background_media.loadMedia(); + } + }, + + stopMedia: function() { + if (this._media && this._state.loaded) { + this._media.stopMedia(); + } + }, + + getBackground: function() { + return this.has.background; + }, + + scrollToTop: function() { + this._el.container.scrollTop = 0; + }, + + getFormattedDate: function() { + + if (TL.Util.trim(this.data.display_date).length > 0) { + return this.data.display_date; + } + var date_text = ""; + + if(!this.has.title) { + if (this.data.end_date) { + date_text = " — " + this.data.end_date.getDisplayDate(this.getLanguage()); + } + if (this.data.start_date) { + date_text = this.data.start_date.getDisplayDate(this.getLanguage()) + date_text; + } + } + return date_text; + }, + + /* Events + ================================================== */ + + + /* Private Methods + ================================================== */ + _initLayout: function () { + // Create Layout + this._el.container = TL.Dom.create("div", "tl-slide"); + + if (this.has.title) { + this._el.container.className = "tl-slide tl-slide-titleslide"; + } + + if (this.data.unique_id) { + this._el.container.id = this.data.unique_id; + } + this._el.scroll_container = TL.Dom.create("div", "tl-slide-scrollable-container", this._el.container); + this._el.content_container = TL.Dom.create("div", "tl-slide-content-container", this._el.scroll_container); + this._el.content = TL.Dom.create("div", "tl-slide-content", this._el.content_container); + this._el.background = TL.Dom.create("div", "tl-slide-background", this._el.container); + // Style Slide Background + if (this.data.background) { + if (this.data.background.url) { + var media_type = TL.MediaType(this.data.background, true); + if(media_type) { + this._background_media = new media_type.cls(this.data.background, {background: 1}); + + this.has.background.image = true; + this._el.container.className += ' tl-full-image-background'; + this.has.background.color_value = "#000"; + this._el.background.style.display = "block"; + } + } + if (this.data.background.color) { + this.has.background.color = true; + this._el.container.className += ' tl-full-color-background'; + this.has.background.color_value = this.data.background.color; + //this._el.container.style.backgroundColor = this.data.background.color; + //this._el.background.style.backgroundColor = this.data.background.color; + //this._el.background.style.display = "block"; + } + if (this.data.background.text_background) { + this._el.container.className += ' tl-text-background'; + } + + } + + + + // Determine Assets for layout and loading + if (this.data.media && this.data.media.url && this.data.media.url != "") { + this.has.media = true; + } + if (this.data.text && this.data.text.text) { + this.has.text = true; + } + if (this.data.text && this.data.text.headline) { + this.has.headline = true; + } + + // Create Media + if (this.has.media) { + // Determine the media type + this.data.media.mediatype = TL.MediaType(this.data.media); + this.options.media_name = this.data.media.mediatype.name; + this.options.media_type = this.data.media.mediatype.type; + this.options.autolink = this.data.autolink; + + // Create a media object using the matched class name + this._media = new this.data.media.mediatype.cls(this.data.media, this.options); + } + + // Create Text + if (this.has.text || this.has.headline) { + this._text = new TL.Media.Text(this.data.text, {title:this.has.title,language: this.options.language, autolink: this.data.autolink }); + this._text.addDateText(this.getFormattedDate()); + } + + + + // Add to DOM + if (!this.has.text && !this.has.headline && this.has.media) { + TL.DomUtil.addClass(this._el.container, 'tl-slide-media-only'); + this._media.addTo(this._el.content); + } else if (this.has.headline && this.has.media && !this.has.text) { + TL.DomUtil.addClass(this._el.container, 'tl-slide-media-only'); + this._text.addTo(this._el.content); + this._media.addTo(this._el.content); + } else if (this.has.text && this.has.media) { + this._media.addTo(this._el.content); + this._text.addTo(this._el.content); + } else if (this.has.text || this.has.headline) { + TL.DomUtil.addClass(this._el.container, 'tl-slide-text-only'); + this._text.addTo(this._el.content); + } + + // Fire event that the slide is loaded + this.onLoaded(); + + }, + + _initEvents: function() { + + }, + + // Update Display + _updateDisplay: function(width, height, layout) { + var content_width, + content_padding_left = this.options.slide_padding_lr, + content_padding_right = this.options.slide_padding_lr; + + if (width) { + this.options.width = width; + } else { + this.options.width = this._el.container.offsetWidth; + } + + content_width = this.options.width - (this.options.slide_padding_lr * 2); + + if(TL.Browser.mobile && (this.options.width <= this.options.skinny_size)) { + content_padding_left = 0; + content_padding_right = 0; + content_width = this.options.width; + } else if (layout == "landscape") { + + } else if (this.options.width <= this.options.skinny_size) { + content_padding_left = 50; + content_padding_right = 50; + content_width = this.options.width - content_padding_left - content_padding_right; + } else { + + } + + this._el.content.style.paddingLeft = content_padding_left + "px"; + this._el.content.style.paddingRight = content_padding_right + "px"; + this._el.content.style.width = content_width + "px"; + + if (height) { + this.options.height = height; + //this._el.scroll_container.style.height = this.options.height + "px"; + + } else { + this.options.height = this._el.container.offsetHeight; + } + + if (this._media) { + + if (!this.has.text && this.has.headline) { + this._media.updateDisplay(content_width, (this.options.height - this._text.headlineHeight()), layout); + } else if (!this.has.text && !this.has.headline) { + this._media.updateDisplay(content_width, this.options.height, layout); + } else if (this.options.width <= this.options.skinny_size) { + this._media.updateDisplay(content_width, this.options.height, layout); + } else { + this._media.updateDisplay(content_width/2, this.options.height, layout); + } + } + + this._updateBackgroundDisplay(); + }, + + _updateBackgroundDisplay: function() { + if(this._background_media && this._background_media._state.loaded) { + this._el.background.style.backgroundImage = "url('" + this._background_media.getImageURL(this.options.width, this.options.height) + "')"; + } + } + +}); + + +/* ********************************************** + Begin TL.SlideNav.js +********************************************** */ + +/* TL.SlideNav + encapsulate DOM display/events for the + 'next' and 'previous' buttons on a slide. +================================================== */ +// TODO null out data + +TL.SlideNav = TL.Class.extend({ + + includes: [TL.Events, TL.DomMixins], + + _el: {}, + + /* Constructor + ================================================== */ + initialize: function(data, options, add_to_container) { + // DOM ELEMENTS + this._el = { + container: {}, + content_container: {}, + icon: {}, + title: {}, + description: {} + }; + + // Media Type + this.mediatype = {}; + + // Data + this.data = { + title: "Navigation", + description: "Description", + date: "Date" + }; + + //Options + this.options = { + direction: "previous" + }; + + this.animator = null; + + // Merge Data and Options + TL.Util.mergeData(this.options, options); + TL.Util.mergeData(this.data, data); + + + this._el.container = TL.Dom.create("div", "tl-slidenav-" + this.options.direction); + + if (TL.Browser.mobile) { + this._el.container.setAttribute("ontouchstart"," "); + } + + this._initLayout(); + this._initEvents(); + + if (add_to_container) { + add_to_container.appendChild(this._el.container); + }; + + }, + + /* Update Content + ================================================== */ + update: function(slide) { + var d = { + title: "", + description: "", + date: slide.getFormattedDate() + }; + + if (slide.data.text) { + if (slide.data.text.headline) { + d.title = slide.data.text.headline; + } + } + + this._update(d); + }, + + /* Color + ================================================== */ + setColor: function(inverted) { + if (inverted) { + this._el.content_container.className = 'tl-slidenav-content-container tl-slidenav-inverted'; + } else { + this._el.content_container.className = 'tl-slidenav-content-container'; + } + }, + + /* Events + ================================================== */ + _onMouseClick: function() { + this.fire("clicked", this.options); + }, + + /* Private Methods + ================================================== */ + _update: function(d) { + // update data + this.data = TL.Util.mergeData(this.data, d); + + // Title + this._el.title.innerHTML = TL.Util.unlinkify(this.data.title); + + // Date + this._el.description.innerHTML = TL.Util.unlinkify(this.data.date); + }, + + _initLayout: function () { + + // Create Layout + this._el.content_container = TL.Dom.create("div", "tl-slidenav-content-container", this._el.container); + this._el.icon = TL.Dom.create("div", "tl-slidenav-icon", this._el.content_container); + this._el.title = TL.Dom.create("div", "tl-slidenav-title", this._el.content_container); + this._el.description = TL.Dom.create("div", "tl-slidenav-description", this._el.content_container); + + this._el.icon.innerHTML = " " + + this._update(); + }, + + _initEvents: function () { + TL.DomEvent.addListener(this._el.container, 'click', this._onMouseClick, this); + } + + +}); + +/* ********************************************** + Begin TL.StorySlider.js +********************************************** */ + +/* StorySlider + is the central class of the API - it is used to create a StorySlider + + Events: + nav_next + nav_previous + slideDisplayUpdate + loaded + slideAdded + slideLoaded + slideRemoved + + +================================================== */ + +TL.StorySlider = TL.Class.extend({ + + includes: [TL.Events, TL.I18NMixins], + + /* Private Methods + ================================================== */ + initialize: function (elem, data, options, init) { + + // DOM ELEMENTS + this._el = { + container: {}, + background: {}, + slider_container_mask: {}, + slider_container: {}, + slider_item_container: {} + }; + + this._nav = {}; + this._nav.previous = {}; + this._nav.next = {}; + + // Slide Spacing + this.slide_spacing = 0; + + // Slides Array + this._slides = []; + + // Swipe Object + this._swipable; + + // Preload Timer + this.preloadTimer; + + // Message + this._message; + + // Current Slide + this.current_id = ''; + + // Data Object + this.data = {}; + + this.options = { + id: "", + layout: "portrait", + width: 600, + height: 600, + default_bg_color: {r:255, g:255, b:255}, + slide_padding_lr: 40, // padding on slide of slide + start_at_slide: 1, + slide_default_fade: "0%", // landscape fade + // animation + duration: 1000, + ease: TL.Ease.easeInOutQuint, + // interaction + dragging: true, + trackResize: true + }; + + // Main element ID + if (typeof elem === 'object') { + this._el.container = elem; + this.options.id = TL.Util.unique_ID(6, "tl"); + } else { + this.options.id = elem; + this._el.container = TL.Dom.get(elem); + } + + if (!this._el.container.id) { + this._el.container.id = this.options.id; + } + + // Animation Object + this.animator = null; + + // Merge Data and Options + TL.Util.mergeData(this.options, options); + TL.Util.mergeData(this.data, data); + + if (init) { + this.init(); + } + }, + + init: function() { + this._initLayout(); + this._initEvents(); + this._initData(); + this._updateDisplay(); + + // Go to initial slide + this.goTo(this.options.start_at_slide); + + this._onLoaded(); + }, + + /* Slides + ================================================== */ + _addSlide:function(slide) { + slide.addTo(this._el.slider_item_container); + slide.on('added', this._onSlideAdded, this); + slide.on('background_change', this._onBackgroundChange, this); + }, + + _createSlide: function(d, title_slide, n) { + var slide = new TL.Slide(d, this.options, title_slide); + this._addSlide(slide); + if(n < 0) { + this._slides.push(slide); + } else { + this._slides.splice(n, 0, slide); + } + }, + + _createSlides: function(array) { + for (var i = 0; i < array.length; i++) { + if (array[i].unique_id == "") { + array[i].unique_id = TL.Util.unique_ID(6, "tl-slide"); + } + this._createSlide(array[i], false, -1); + } + }, + + _removeSlide: function(slide) { + slide.removeFrom(this._el.slider_item_container); + slide.off('added', this._onSlideRemoved, this); + slide.off('background_change', this._onBackgroundChange); + }, + + _destroySlide: function(n) { + this._removeSlide(this._slides[n]); + this._slides.splice(n, 1); + }, + + _findSlideIndex: function(n) { + var _n = n; + if (typeof n == 'string' || n instanceof String) { + _n = TL.Util.findArrayNumberByUniqueID(n, this._slides, "unique_id"); + } + return _n; + }, + + /* Public + ================================================== */ + updateDisplay: function(w, h, a, l) { + this._updateDisplay(w, h, a, l); + }, + + // Create a slide + createSlide: function(d, n) { + this._createSlide(d, false, n); + }, + + // Create Many Slides from an array + createSlides: function(array) { + this._createSlides(array); + }, + + // Destroy slide by index + destroySlide: function(n) { + this._destroySlide(n); + }, + + // Destroy slide by id + destroySlideId: function(id) { + this.destroySlide(this._findSlideIndex(id)); + }, + + /* Navigation + ================================================== */ + goTo: function(n, fast, displayupdate) { + n = parseInt(n); + if (isNaN(n)) n = 0; + + var self = this; + + this.changeBackground({color_value:"", image:false}); + + // Clear Preloader Timer + if (this.preloadTimer) { + clearTimeout(this.preloadTimer); + } + + // Set Slide Active State + for (var i = 0; i < this._slides.length; i++) { + this._slides[i].setActive(false); + } + + if (n < this._slides.length && n >= 0) { + this.current_id = this._slides[n].data.unique_id; + + // Stop animation + if (this.animator) { + this.animator.stop(); + } + if (this._swipable) { + this._swipable.stopMomentum(); + } + + if (fast) { + this._el.slider_container.style.left = -(this.slide_spacing * n) + "px"; + this._onSlideChange(displayupdate); + } else { + this.animator = TL.Animate(this._el.slider_container, { + left: -(this.slide_spacing * n) + "px", + duration: this.options.duration, + easing: this.options.ease, + complete: this._onSlideChange(displayupdate) + }); + } + + // Set Slide Active State + this._slides[n].setActive(true); + + // Update Navigation and Info + if (this._slides[n + 1]) { + this.showNav(this._nav.next, true); + this._nav.next.update(this._slides[n + 1]); + } else { + this.showNav(this._nav.next, false); + } + if (this._slides[n - 1]) { + this.showNav(this._nav.previous, true); + this._nav.previous.update(this._slides[n - 1]); + } else { + this.showNav(this._nav.previous, false); + } + + // Preload Slides + this.preloadTimer = setTimeout(function() { + self.preloadSlides(n); + }, this.options.duration); + } + }, + + goToId: function(id, fast, displayupdate) { + this.goTo(this._findSlideIndex(id), fast, displayupdate); + }, + + preloadSlides: function(n) { + if (this._slides[n + 1]) { + this._slides[n + 1].loadMedia(); + this._slides[n + 1].scrollToTop(); + } + if (this._slides[n + 2]) { + this._slides[n + 2].loadMedia(); + this._slides[n + 2].scrollToTop(); + } + if (this._slides[n - 1]) { + this._slides[n - 1].loadMedia(); + this._slides[n - 1].scrollToTop(); + } + if (this._slides[n - 2]) { + this._slides[n - 2].loadMedia(); + this._slides[n - 2].scrollToTop(); + } + }, + + next: function() { + var n = this._findSlideIndex(this.current_id); + if ((n + 1) < (this._slides.length)) { + this.goTo(n + 1); + } else { + this.goTo(n); + } + }, + + previous: function() { + var n = this._findSlideIndex(this.current_id); + if (n - 1 >= 0) { + this.goTo(n - 1); + } else { + this.goTo(n); + } + }, + + showNav: function(nav_obj, show) { + + if (this.options.width <= 500 && TL.Browser.mobile) { + + } else { + if (show) { + nav_obj.show(); + } else { + nav_obj.hide(); + } + + } + }, + + + + changeBackground: function(bg) { + var bg_color = {r:256, g:256, b:256}, + bg_color_rgb; + + if (bg.color_value && bg.color_value != "") { + bg_color = TL.Util.hexToRgb(bg.color_value); + if (!bg_color) { + trace("Invalid color value " + bg.color_value); + bg_color = this.options.default_bg_color; + } + } else { + bg_color = this.options.default_bg_color; + bg.color_value = "rgb(" + bg_color.r + " , " + bg_color.g + ", " + bg_color.b + ")"; + } + + bg_color_rgb = bg_color.r + "," + bg_color.g + "," + bg_color.b; + this._el.background.style.backgroundImage = "none"; + + + if (bg.color_value) { + this._el.background.style.backgroundColor = bg.color_value; + } else { + this._el.background.style.backgroundColor = "transparent"; + } + + if (bg_color.r < 255 || bg_color.g < 255 || bg_color.b < 255 || bg.image) { + this._nav.next.setColor(true); + this._nav.previous.setColor(true); + } else { + this._nav.next.setColor(false); + this._nav.previous.setColor(false); + } + }, + /* Private Methods + ================================================== */ + + // Update Display + _updateDisplay: function(width, height, animate, layout) { + var nav_pos, _layout; + + if(typeof layout === 'undefined'){ + _layout = this.options.layout; + } else { + _layout = layout; + } + + this.options.layout = _layout; + + this.slide_spacing = this.options.width*2; + + if (width) { + this.options.width = width; + } else { + this.options.width = this._el.container.offsetWidth; + } + + if (height) { + this.options.height = height; + } else { + this.options.height = this._el.container.offsetHeight; + } + + //this._el.container.style.height = this.options.height; + + // position navigation + nav_pos = (this.options.height/2); + this._nav.next.setPosition({top:nav_pos}); + this._nav.previous.setPosition({top:nav_pos}); + + + // Position slides + for (var i = 0; i < this._slides.length; i++) { + this._slides[i].updateDisplay(this.options.width, this.options.height, _layout); + this._slides[i].setPosition({left:(this.slide_spacing * i), top:0}); + + }; + + // Go to the current slide + this.goToId(this.current_id, true, true); + }, + + // Reposition and redraw slides + _updateDrawSlides: function() { + var _layout = this.options.layout; + + for (var i = 0; i < this._slides.length; i++) { + this._slides[i].updateDisplay(this.options.width, this.options.height, _layout); + this._slides[i].setPosition({left:(this.slide_spacing * i), top:0}); + }; + + this.goToId(this.current_id, true, false); + }, + + + /* Init + ================================================== */ + _initLayout: function () { + + TL.DomUtil.addClass(this._el.container, 'tl-storyslider'); + + // Create Layout + this._el.slider_container_mask = TL.Dom.create('div', 'tl-slider-container-mask', this._el.container); + this._el.background = TL.Dom.create('div', 'tl-slider-background tl-animate', this._el.container); + this._el.slider_container = TL.Dom.create('div', 'tl-slider-container tlanimate', this._el.slider_container_mask); + this._el.slider_item_container = TL.Dom.create('div', 'tl-slider-item-container', this._el.slider_container); + + + // Update Size + this.options.width = this._el.container.offsetWidth; + this.options.height = this._el.container.offsetHeight; + + // Create Navigation + this._nav.previous = new TL.SlideNav({title: "Previous", description: "description"}, {direction:"previous"}); + this._nav.next = new TL.SlideNav({title: "Next",description: "description"}, {direction:"next"}); + + // add the navigation to the dom + this._nav.next.addTo(this._el.container); + this._nav.previous.addTo(this._el.container); + + + + this._el.slider_container.style.left="0px"; + + if (TL.Browser.touch) { + //this._el.slider_touch_mask = TL.Dom.create('div', 'tl-slider-touch-mask', this._el.slider_container_mask); + this._swipable = new TL.Swipable(this._el.slider_container_mask, this._el.slider_container, { + enable: {x:true, y:false}, + snap: true + }); + this._swipable.enable(); + + // Message + this._message = new TL.Message({}, { + message_class: "tl-message-full", + message_icon_class: "tl-icon-swipe-left" + }); + this._message.updateMessage(this._("swipe_to_navigate")); + this._message.addTo(this._el.container); + } + + }, + + _initEvents: function () { + this._nav.next.on('clicked', this._onNavigation, this); + this._nav.previous.on('clicked', this._onNavigation, this); + + if (this._message) { + this._message.on('clicked', this._onMessageClick, this); + } + + if (this._swipable) { + this._swipable.on('swipe_left', this._onNavigation, this); + this._swipable.on('swipe_right', this._onNavigation, this); + this._swipable.on('swipe_nodirection', this._onSwipeNoDirection, this); + } + + + }, + + _initData: function() { + if(this.data.title) { + this._createSlide(this.data.title, true, -1); + } + this._createSlides(this.data.events); + }, + + /* Events + ================================================== */ + _onBackgroundChange: function(e) { + var n = this._findSlideIndex(this.current_id); + var slide_background = this._slides[n].getBackground(); + this.changeBackground(e); + this.fire("colorchange", slide_background); + }, + + _onMessageClick: function(e) { + this._message.hide(); + }, + + _onSwipeNoDirection: function(e) { + this.goToId(this.current_id); + }, + + _onNavigation: function(e) { + + if (e.direction == "next" || e.direction == "left") { + this.next(); + } else if (e.direction == "previous" || e.direction == "right") { + this.previous(); + } + this.fire("nav_" + e.direction, this.data); + }, + + _onSlideAdded: function(e) { + trace("slideadded") + this.fire("slideAdded", this.data); + }, + + _onSlideRemoved: function(e) { + this.fire("slideRemoved", this.data); + }, + + _onSlideChange: function(displayupdate) { + if (!displayupdate) { + this.fire("change", {unique_id: this.current_id}); + } + }, + + _onMouseClick: function(e) { + + }, + + _fireMouseEvent: function (e) { + if (!this._loaded) { + return; + } + + var type = e.type; + type = (type === 'mouseenter' ? 'mouseover' : (type === 'mouseleave' ? 'mouseout' : type)); + + if (!this.hasEventListeners(type)) { + return; + } + + if (type === 'contextmenu') { + TL.DomEvent.preventDefault(e); + } + + this.fire(type, { + latlng: "something", //this.mouseEventToLatLng(e), + layerPoint: "something else" //this.mouseEventToLayerPoint(e) + }); + }, + + _onLoaded: function() { + this.fire("loaded", this.data); + } + + +}); + + +/* ********************************************** + Begin TL.TimeNav.js +********************************************** */ + +/* TL.TimeNav + +================================================== */ + +TL.TimeNav = TL.Class.extend({ + + includes: [TL.Events, TL.DomMixins], + + _el: {}, + + /* Constructor + ================================================== */ + initialize: function (elem, timeline_config, options, init) { + // DOM ELEMENTS + this._el = { + parent: {}, + container: {}, + slider: {}, + slider_background: {}, + line: {}, + marker_container_mask: {}, + marker_container: {}, + marker_item_container: {}, + timeaxis: {}, + timeaxis_background: {}, + attribution: {} + }; + + this.collapsed = false; + + if (typeof elem === 'object') { + this._el.container = elem; + } else { + this._el.container = TL.Dom.get(elem); + } + + this.config = timeline_config; + + //Options + this.options = { + width: 600, + height: 600, + duration: 1000, + ease: TL.Ease.easeInOutQuint, + has_groups: false, + optimal_tick_width: 50, + scale_factor: 2, // How many screen widths wide should the timeline be + marker_padding: 5, + timenav_height_min: 150, // Minimum timenav height + marker_height_min: 30, // Minimum Marker Height + marker_width_min: 100, // Minimum Marker Width + zoom_sequence: [0.5, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89] // Array of Fibonacci numbers for TimeNav zoom levels http://www.maths.surrey.ac.uk/hosted-sites/R.Knott/Fibonacci/fibtable.html + }; + + // Animation + this.animator = null; + + // Ready state + this.ready = false; + + // Markers Array + this._markers = []; + + // Eras Array + this._eras = []; + this.has_eras = false; + + // Groups Array + this._groups = []; + + // Row Height + this._calculated_row_height = 100; + + // Current Marker + this.current_id = ""; + + // TimeScale + this.timescale = {}; + + // TimeAxis + this.timeaxis = {}; + this.axishelper = {}; + + // Max Rows + this.max_rows = 6; + + // Animate CSS + this.animate_css = false; + + // Swipe Object + this._swipable; + + // Merge Data and Options + TL.Util.mergeData(this.options, options); + + if (init) { + this.init(); + } + }, + + init: function() { + this._initLayout(); + this._initEvents(); + this._initData(); + this._updateDisplay(); + + this._onLoaded(); + }, + + /* Public + ================================================== */ + positionMarkers: function() { + this._positionMarkers(); + }, + + /* Update Display + ================================================== */ + updateDisplay: function(w, h, a, l) { + this._updateDisplay(w, h, a, l); + }, + + + /* TimeScale + ================================================== */ + _getTimeScale: function() { + /* maybe the establishing config values (marker_height_min and max_rows) should be + separated from making a TimeScale object, which happens in another spot in this file with duplicate mapping of properties of this TimeNav into the TimeScale options object? */ + // Set Max Rows + var marker_height_min = 0; + try { + marker_height_min = parseInt(this.options.marker_height_min); + } catch(e) { + trace("Invalid value for marker_height_min option."); + marker_height_min = 30; + } + if (marker_height_min == 0) { + trace("marker_height_min option must not be zero.") + marker_height_min = 30; + } + this.max_rows = Math.round((this.options.height - this._el.timeaxis_background.offsetHeight - (this.options.marker_padding)) / marker_height_min); + if (this.max_rows < 1) { + this.max_rows = 1; + } + return new TL.TimeScale(this.config, { + display_width: this._el.container.offsetWidth, + screen_multiplier: this.options.scale_factor, + max_rows: this.max_rows + + }); + }, + + _updateTimeScale: function(new_scale) { + this.options.scale_factor = new_scale; + this._updateDrawTimeline(); + }, + + zoomIn: function() { // move the the next "higher" scale factor + var new_scale = TL.Util.findNextGreater(this.options.zoom_sequence, this.options.scale_factor); + this.setZoomFactor(new_scale); + }, + + zoomOut: function() { // move the the next "lower" scale factor + var new_scale = TL.Util.findNextLesser(this.options.zoom_sequence, this.options.scale_factor); + this.setZoomFactor(new_scale); + }, + + setZoom: function(level) { + var zoom_factor = this.options.zoom_sequence[level]; + if (typeof(zoom_factor) == 'number') { + this.setZoomFactor(zoom_factor); + } else { + console.warn("Invalid zoom level. Please use an index number between 0 and " + (this.options.zoom_sequence.length - 1)); + } + }, + + setZoomFactor: function(factor) { + if (factor <= this.options.zoom_sequence[0]) { + this.fire("zoomtoggle", {zoom:"out", show:false}); + } else { + this.fire("zoomtoggle", {zoom:"out", show:true}); + } + + if (factor >= this.options.zoom_sequence[this.options.zoom_sequence.length-1]) { + this.fire("zoomtoggle", {zoom:"in", show:false}); + } else { + this.fire("zoomtoggle", {zoom:"in", show:true}); + } + + if (factor == 0) { + console.warn("Zoom factor must be greater than zero. Using 0.1"); + factor = 0.1; + } + this.options.scale_factor = factor; + //this._updateDrawTimeline(true); + this.goToId(this.current_id, !this._updateDrawTimeline(true), true); + }, + + /* Groups + ================================================== */ + _createGroups: function() { + this._groups = []; + var group_labels = this.timescale.getGroupLabels(); + + if (group_labels) { + this.options.has_groups = true; + for (var i = 0; i < group_labels.length; i++) { + this._createGroup(group_labels[i]); + } + } + + }, + + _createGroup: function(group_label) { + var group = new TL.TimeGroup(group_label); + this._addGroup(group); + this._groups.push(group); + }, + + _addGroup:function(group) { + group.addTo(this._el.container); + + }, + + _positionGroups: function() { + if (this.options.has_groups) { + var available_height = (this.options.height - this._el.timeaxis_background.offsetHeight ), + group_height = Math.floor((available_height /this.timescale.getNumberOfRows()) - this.options.marker_padding), + group_labels = this.timescale.getGroupLabels(); + + for (var i = 0, group_rows = 0; i < this._groups.length; i++) { + var group_y = Math.floor(group_rows * (group_height + this.options.marker_padding)); + var group_hide = false; + if (group_y > (available_height- this.options.marker_padding)) { + group_hide = true; + } + + this._groups[i].setRowPosition(group_y, this._calculated_row_height + this.options.marker_padding/2); + this._groups[i].setAlternateRowColor(TL.Util.isEven(i), group_hide); + + group_rows += this._groups[i].data.rows; // account for groups spanning multiple rows + } + } + }, + + /* Markers + ================================================== */ + _addMarker:function(marker) { + marker.addTo(this._el.marker_item_container); + marker.on('markerclick', this._onMarkerClick, this); + marker.on('added', this._onMarkerAdded, this); + }, + + _createMarker: function(data, n) { + var marker = new TL.TimeMarker(data, this.options); + this._addMarker(marker); + if(n < 0) { + this._markers.push(marker); + } else { + this._markers.splice(n, 0, marker); + } + }, + + _createMarkers: function(array) { + for (var i = 0; i < array.length; i++) { + this._createMarker(array[i], -1); + } + }, + + _removeMarker: function(marker) { + marker.removeFrom(this._el.marker_item_container); + //marker.off('added', this._onMarkerRemoved, this); + }, + + _destroyMarker: function(n) { + this._removeMarker(this._markers[n]); + this._markers.splice(n, 1); + }, + + _positionMarkers: function(fast) { + // POSITION X + for (var i = 0; i < this._markers.length; i++) { + var pos = this.timescale.getPositionInfo(i); + if (fast) { + this._markers[i].setClass("tl-timemarker tl-timemarker-fast"); + } else { + this._markers[i].setClass("tl-timemarker"); + } + this._markers[i].setPosition({left:pos.start}); + this._markers[i].setWidth(pos.width); + }; + + }, + + _calculateMarkerHeight: function(h) { + return ((h /this.timescale.getNumberOfRows()) - this.options.marker_padding); + }, + + _calculateRowHeight: function(h) { + return (h /this.timescale.getNumberOfRows()); + }, + + _calculateAvailableHeight: function() { + return (this.options.height - this._el.timeaxis_background.offsetHeight - (this.options.marker_padding)); + }, + + _calculateMinimumTimeNavHeight: function() { + return (this.timescale.getNumberOfRows() * this.options.marker_height_min) + this._el.timeaxis_background.offsetHeight + (this.options.marker_padding); + + }, + + getMinimumHeight: function() { + return this._calculateMinimumTimeNavHeight(); + }, + + _assignRowsToMarkers: function() { + var available_height = this._calculateAvailableHeight(), + marker_height = this._calculateMarkerHeight(available_height); + + + this._positionGroups(); + + this._calculated_row_height = this._calculateRowHeight(available_height); + + for (var i = 0; i < this._markers.length; i++) { + + // Set Height + this._markers[i].setHeight(marker_height); + + //Position by Row + var row = this.timescale.getPositionInfo(i).row; + + var marker_y = Math.floor(row * (marker_height + this.options.marker_padding)) + this.options.marker_padding; + + var remainder_height = available_height - marker_y + this.options.marker_padding; + this._markers[i].setRowPosition(marker_y, remainder_height); + }; + + }, + + _resetMarkersActive: function() { + for (var i = 0; i < this._markers.length; i++) { + this._markers[i].setActive(false); + }; + }, + + _findMarkerIndex: function(n) { + var _n = -1; + if (typeof n == 'string' || n instanceof String) { + _n = TL.Util.findArrayNumberByUniqueID(n, this._markers, "unique_id", _n); + } + return _n; + }, + + /* ERAS + ================================================== */ + _createEras: function(array) { + for (var i = 0; i < array.length; i++) { + this._createEra(array[i], -1); + } + }, + + _createEra: function(data, n) { + var era = new TL.TimeEra(data, this.options); + this._addEra(era); + if(n < 0) { + this._eras.push(era); + } else { + this._eras.splice(n, 0, era); + } + }, + + _addEra:function(era) { + era.addTo(this._el.marker_item_container); + era.on('added', this._onEraAdded, this); + }, + + _removeEra: function(era) { + era.removeFrom(this._el.marker_item_container); + //marker.off('added', this._onMarkerRemoved, this); + }, + + _destroyEra: function(n) { + this._removeEra(this._eras[n]); + this._eras.splice(n, 1); + }, + + _positionEras: function(fast) { + + var era_color = 0; + // POSITION X + for (var i = 0; i < this._eras.length; i++) { + var pos = { + start:0, + end:0, + width:0 + }; + + pos.start = this.timescale.getPosition(this._eras[i].data.start_date.getTime()); + pos.end = this.timescale.getPosition(this._eras[i].data.end_date.getTime()); + pos.width = pos.end - pos.start; + + if (fast) { + this._eras[i].setClass("tl-timeera tl-timeera-fast"); + } else { + this._eras[i].setClass("tl-timeera"); + } + this._eras[i].setPosition({left:pos.start}); + this._eras[i].setWidth(pos.width); + + era_color++; + if (era_color > 5) { + era_color = 0; + } + this._eras[i].setColor(era_color); + }; + + }, + + /* Public + ================================================== */ + + // Create a marker + createMarker: function(d, n) { + this._createMarker(d, n); + }, + + // Create many markers from an array + createMarkers: function(array) { + this._createMarkers(array); + }, + + // Destroy marker by index + destroyMarker: function(n) { + this._destroyMarker(n); + }, + + // Destroy marker by id + destroyMarkerId: function(id) { + this.destroyMarker(this._findMarkerIndex(id)); + }, + + /* Navigation + ================================================== */ + goTo: function(n, fast, css_animation) { + var self = this, + _ease = this.options.ease, + _duration = this.options.duration, + _n = (n < 0) ? 0 : n; + + // Set Marker active state + this._resetMarkersActive(); + if(n >= 0 && n < this._markers.length) { + this._markers[n].setActive(true); + } + // Stop animation + if (this.animator) { + this.animator.stop(); + } + + if (fast) { + this._el.slider.className = "tl-timenav-slider"; + this._el.slider.style.left = -this._markers[_n].getLeft() + (this.options.width/2) + "px"; + } else { + if (css_animation) { + this._el.slider.className = "tl-timenav-slider tl-timenav-slider-animate"; + this.animate_css = true; + this._el.slider.style.left = -this._markers[_n].getLeft() + (this.options.width/2) + "px"; + } else { + this._el.slider.className = "tl-timenav-slider"; + this.animator = TL.Animate(this._el.slider, { + left: -this._markers[_n].getLeft() + (this.options.width/2) + "px", + duration: _duration, + easing: _ease + }); + } + } + + if(n >= 0 && n < this._markers.length) { + this.current_id = this._markers[n].data.unique_id; + } else { + this.current_id = ''; + } + }, + + goToId: function(id, fast, css_animation) { + this.goTo(this._findMarkerIndex(id), fast, css_animation); + }, + + /* Events + ================================================== */ + _onLoaded: function() { + this.ready = true; + this.fire("loaded", this.config); + }, + + _onMarkerAdded: function(e) { + this.fire("dateAdded", this.config); + }, + + _onEraAdded: function(e) { + this.fire("eraAdded", this.config); + }, + + _onMarkerRemoved: function(e) { + this.fire("dateRemoved", this.config); + }, + + _onMarkerClick: function(e) { + // Go to the clicked marker + this.goToId(e.unique_id); + this.fire("change", {unique_id: e.unique_id}); + }, + + _onMouseScroll: function(e) { + + var delta = 0, + scroll_to = 0, + constraint = { + right: -(this.timescale.getPixelWidth() - (this.options.width/2)), + left: this.options.width/2 + }; + if (!e) { + e = window.event; + } + if (e.originalEvent) { + e = e.originalEvent; + } + + // Webkit and browsers able to differntiate between up/down and left/right scrolling + if (typeof e.wheelDeltaX != 'undefined' ) { + delta = e.wheelDeltaY/6; + if (Math.abs(e.wheelDeltaX) > Math.abs(e.wheelDeltaY)) { + delta = e.wheelDeltaX/6; + } else { + //delta = e.wheelDeltaY/6; + delta = 0; + } + } + if (delta) { + if (e.preventDefault) { + e.preventDefault(); + } + e.returnValue = false; + } + // Stop from scrolling too far + scroll_to = parseInt(this._el.slider.style.left.replace("px", "")) + delta; + + + if (scroll_to > constraint.left) { + scroll_to = constraint.left; + } else if (scroll_to < constraint.right) { + scroll_to = constraint.right; + } + + if (this.animate_css) { + this._el.slider.className = "tl-timenav-slider"; + this.animate_css = false; + } + + this._el.slider.style.left = scroll_to + "px"; + + }, + + _onDragMove: function(e) { + if (this.animate_css) { + this._el.slider.className = "tl-timenav-slider"; + this.animate_css = false; + } + + }, + + /* Private Methods + ================================================== */ + // Update Display + _updateDisplay: function(width, height, animate) { + + if (width) { + this.options.width = width; + } + if (height && height != this.options.height) { + this.options.height = height; + this.timescale = this._getTimeScale(); + } + + // Size Markers + this._assignRowsToMarkers(); + + // Size swipable area + this._el.slider_background.style.width = this.timescale.getPixelWidth() + this.options.width + "px"; + this._el.slider_background.style.left = -(this.options.width/2) + "px"; + this._el.slider.style.width = this.timescale.getPixelWidth() + this.options.width + "px"; + + // Update Swipable constraint + this._swipable.updateConstraint({top: false,bottom: false,left: (this.options.width/2),right: -(this.timescale.getPixelWidth() - (this.options.width/2))}); + + // Go to the current slide + this.goToId(this.current_id, true); + }, + + _drawTimeline: function(fast) { + this.timescale = this._getTimeScale(); + this.timeaxis.drawTicks(this.timescale, this.options.optimal_tick_width); + this._positionMarkers(fast); + this._assignRowsToMarkers(); + this._createGroups(); + this._positionGroups(); + + if (this.has_eras) { + + this._positionEras(fast); + } + }, + + _updateDrawTimeline: function(check_update) { + var do_update = false; + + // Check to see if redraw is needed + if (check_update) { + /* keep this aligned with _getTimeScale or reduce code duplication */ + var temp_timescale = new TL.TimeScale(this.config, { + display_width: this._el.container.offsetWidth, + screen_multiplier: this.options.scale_factor, + max_rows: this.max_rows + + }); + + if (this.timescale.getMajorScale() == temp_timescale.getMajorScale() + && this.timescale.getMinorScale() == temp_timescale.getMinorScale()) { + do_update = true; + } + } else { + do_update = true; + } + + // Perform update or redraw + if (do_update) { + this.timescale = this._getTimeScale(); + this.timeaxis.positionTicks(this.timescale, this.options.optimal_tick_width); + this._positionMarkers(); + this._assignRowsToMarkers(); + this._positionGroups(); + if (this.has_eras) { + this._positionEras(); + } + this._updateDisplay(); + } else { + this._drawTimeline(true); + } + + return do_update; + + }, + + + /* Init + ================================================== */ + _initLayout: function () { + // Create Layout + this._el.attribution = TL.Dom.create('div', 'tl-attribution', this._el.container); + this._el.line = TL.Dom.create('div', 'tl-timenav-line', this._el.container); + this._el.slider = TL.Dom.create('div', 'tl-timenav-slider', this._el.container); + this._el.slider_background = TL.Dom.create('div', 'tl-timenav-slider-background', this._el.slider); + this._el.marker_container_mask = TL.Dom.create('div', 'tl-timenav-container-mask', this._el.slider); + this._el.marker_container = TL.Dom.create('div', 'tl-timenav-container', this._el.marker_container_mask); + this._el.marker_item_container = TL.Dom.create('div', 'tl-timenav-item-container', this._el.marker_container); + this._el.timeaxis = TL.Dom.create('div', 'tl-timeaxis', this._el.slider); + this._el.timeaxis_background = TL.Dom.create('div', 'tl-timeaxis-background', this._el.container); + + + // Knight Lab Logo + this._el.attribution.innerHTML = "Timeline JS" + + // Time Axis + this.timeaxis = new TL.TimeAxis(this._el.timeaxis, this.options); + + // Swipable + this._swipable = new TL.Swipable(this._el.slider_background, this._el.slider, { + enable: {x:true, y:false}, + constraint: {top: false,bottom: false,left: (this.options.width/2),right: false}, + snap: false + }); + this._swipable.enable(); + + }, + + _initEvents: function () { + // Drag Events + this._swipable.on('dragmove', this._onDragMove, this); + + // Scroll Events + TL.DomEvent.addListener(this._el.container, 'mousewheel', this._onMouseScroll, this); + TL.DomEvent.addListener(this._el.container, 'DOMMouseScroll', this._onMouseScroll, this); + }, + + _initData: function() { + // Create Markers and then add them + this._createMarkers(this.config.events); + + if (this.config.eras) { + this.has_eras = true; + this._createEras(this.config.eras); + } + + this._drawTimeline(); + + } + + +}); + + +/* ********************************************** + Begin TL.TimeMarker.js +********************************************** */ + +/* TL.TimeMarker + +================================================== */ + +TL.TimeMarker = TL.Class.extend({ + + includes: [TL.Events, TL.DomMixins], + + _el: {}, + + /* Constructor + ================================================== */ + initialize: function(data, options) { + + // DOM Elements + this._el = { + container: {}, + content_container: {}, + media_container: {}, + timespan: {}, + line_left: {}, + line_right: {}, + content: {}, + text: {}, + media: {}, + }; + + // Components + this._text = {}; + + // State + this._state = { + loaded: false + }; + + + // Data + this.data = { + unique_id: "", + background: null, + date: { + year: 0, + month: 0, + day: 0, + hour: 0, + minute: 0, + second: 0, + millisecond: 0, + thumbnail: "", + format: "" + }, + text: { + headline: "", + text: "" + }, + media: null + }; + + // Options + this.options = { + duration: 1000, + ease: TL.Ease.easeInSpline, + width: 600, + height: 600, + marker_width_min: 100 // Minimum Marker Width + }; + + // Actively Displaying + this.active = false; + + // Animation Object + this.animator = {}; + + // End date + this.has_end_date = false; + + // Merge Data and Options + TL.Util.mergeData(this.options, options); + TL.Util.mergeData(this.data, data); + + this._initLayout(); + this._initEvents(); + + + }, + + /* Adding, Hiding, Showing etc + ================================================== */ + show: function() { + + }, + + hide: function() { + + }, + + setActive: function(is_active) { + this.active = is_active; + + if (this.active && this.has_end_date) { + this._el.container.className = 'tl-timemarker tl-timemarker-with-end tl-timemarker-active'; + } else if (this.active){ + this._el.container.className = 'tl-timemarker tl-timemarker-active'; + } else if (this.has_end_date){ + this._el.container.className = 'tl-timemarker tl-timemarker-with-end'; + } else { + this._el.container.className = 'tl-timemarker'; + } + }, + + addTo: function(container) { + container.appendChild(this._el.container); + }, + + removeFrom: function(container) { + container.removeChild(this._el.container); + }, + + updateDisplay: function(w, h) { + this._updateDisplay(w, h); + }, + + loadMedia: function() { + + if (this._media && !this._state.loaded) { + this._media.loadMedia(); + this._state.loaded = true; + } + }, + + stopMedia: function() { + if (this._media && this._state.loaded) { + this._media.stopMedia(); + } + }, + + getLeft: function() { + return this._el.container.style.left.slice(0, -2); + }, + + getTime: function() { // TODO does this need to know about the end date? + return this.data.start_date.getTime(); + }, + + getEndTime: function() { + + if (this.data.end_date) { + return this.data.end_date.getTime(); + } else { + return false; + } + }, + + setHeight: function(h) { + var text_line_height = 12, + text_lines = 1; + + this._el.content_container.style.height = h + "px"; + this._el.timespan_content.style.height = h + "px"; + // Handle Line height for better display of text + if (h <= 30) { + this._el.content.className = "tl-timemarker-content tl-timemarker-content-small"; + } else { + this._el.content.className = "tl-timemarker-content"; + } + + if (h <= 56) { + TL.DomUtil.addClass(this._el.content_container, "tl-timemarker-content-container-small"); + } else { + TL.DomUtil.removeClass(this._el.content_container, "tl-timemarker-content-container-small"); + } + + // Handle number of lines visible vertically + + if (TL.Browser.webkit) { + text_lines = Math.floor(h / (text_line_height + 2)); + if (text_lines < 1) { + text_lines = 1; + } + this._text.className = "tl-headline"; + this._text.style.webkitLineClamp = text_lines; + } else { + text_lines = h / text_line_height; + if (text_lines > 1) { + this._text.className = "tl-headline tl-headline-fadeout"; + } else { + this._text.className = "tl-headline"; + } + this._text.style.height = (text_lines * text_line_height) + "px"; + } + + }, + + setWidth: function(w) { + if (this.data.end_date) { + this._el.container.style.width = w + "px"; + + if (w > this.options.marker_width_min) { + this._el.content_container.style.width = w + "px"; + this._el.content_container.className = "tl-timemarker-content-container tl-timemarker-content-container-long"; + } else { + this._el.content_container.style.width = this.options.marker_width_min + "px"; + this._el.content_container.className = "tl-timemarker-content-container"; + } + } + + }, + + setClass: function(n) { + this._el.container.className = n; + }, + + setRowPosition: function(n, remainder) { + this.setPosition({top:n}); + this._el.timespan.style.height = remainder + "px"; + + if (remainder < 56) { + //TL.DomUtil.removeClass(this._el.content_container, "tl-timemarker-content-container-small"); + } + }, + + /* Events + ================================================== */ + _onMarkerClick: function(e) { + this.fire("markerclick", {unique_id:this.data.unique_id}); + }, + + /* Private Methods + ================================================== */ + _initLayout: function () { + //trace(this.data) + // Create Layout + this._el.container = TL.Dom.create("div", "tl-timemarker"); + if (this.data.unique_id) { + this._el.container.id = this.data.unique_id + "-marker"; + } + + if (this.data.end_date) { + this.has_end_date = true; + this._el.container.className = 'tl-timemarker tl-timemarker-with-end'; + } + + this._el.timespan = TL.Dom.create("div", "tl-timemarker-timespan", this._el.container); + this._el.timespan_content = TL.Dom.create("div", "tl-timemarker-timespan-content", this._el.timespan); + this._el.content_container = TL.Dom.create("div", "tl-timemarker-content-container", this._el.container); + + this._el.content = TL.Dom.create("div", "tl-timemarker-content", this._el.content_container); + + this._el.line_left = TL.Dom.create("div", "tl-timemarker-line-left", this._el.timespan); + this._el.line_right = TL.Dom.create("div", "tl-timemarker-line-right", this._el.timespan); + + // Thumbnail or Icon + if (this.data.media) { + this._el.media_container = TL.Dom.create("div", "tl-timemarker-media-container", this._el.content); + // ugh. needs an overhaul + var mtd = {url: this.data.media.thumbnail}; + var thumbnail_media_type = (this.data.media.thumbnail) ? TL.MediaType(mtd, true) : null; + if (thumbnail_media_type) { + var thumbnail_media = new thumbnail_media_type.cls(mtd); + thumbnail_media.on("loaded", function() { + this._el.media = TL.Dom.create("img", "tl-timemarker-media", this._el.media_container); + this._el.media.src = thumbnail_media.getImageURL(); + }.bind(this)); + thumbnail_media.loadMedia(); + } else { + var media_type = TL.MediaType(this.data.media).type; + this._el.media = TL.Dom.create("span", "tl-icon-" + media_type, this._el.media_container); + + } + + } + + + // Text + this._el.text = TL.Dom.create("div", "tl-timemarker-text", this._el.content); + this._text = TL.Dom.create("h2", "tl-headline", this._el.text); + if (this.data.text.headline && this.data.text.headline != "") { + this._text.innerHTML = TL.Util.unlinkify(this.data.text.headline); + } else if (this.data.text.text && this.data.text.text != "") { + this._text.innerHTML = TL.Util.unlinkify(this.data.text.text); + } else if (this.data.media && this.data.media.caption && this.data.media.caption != "") { + this._text.innerHTML = TL.Util.unlinkify(this.data.media.caption); + } + + + + // Fire event that the slide is loaded + this.onLoaded(); + + }, + + _initEvents: function() { + TL.DomEvent.addListener(this._el.container, 'click', this._onMarkerClick, this); + }, + + // Update Display + _updateDisplay: function(width, height, layout) { + + if (width) { + this.options.width = width; + } + + if (height) { + this.options.height = height; + } + + } + +}); + + +/* ********************************************** + Begin TL.TimeEra.js +********************************************** */ + +/* TL.TimeMarker + +================================================== */ + +TL.TimeEra = TL.Class.extend({ + + includes: [TL.Events, TL.DomMixins], + + _el: {}, + + /* Constructor + ================================================== */ + initialize: function(data, options) { + + // DOM Elements + this._el = { + container: {}, + background: {}, + content_container: {}, + content: {}, + text: {} + }; + + // Components + this._text = {}; + + // State + this._state = { + loaded: false + }; + + + // Data + this.data = { + unique_id: "", + date: { + year: 0, + month: 0, + day: 0, + hour: 0, + minute: 0, + second: 0, + millisecond: 0, + thumbnail: "", + format: "" + }, + text: { + headline: "", + text: "" + } + }; + + // Options + this.options = { + duration: 1000, + ease: TL.Ease.easeInSpline, + width: 600, + height: 600, + marker_width_min: 100 // Minimum Marker Width + }; + + // Actively Displaying + this.active = false; + + // Animation Object + this.animator = {}; + + // End date + this.has_end_date = false; + + // Merge Data and Options + TL.Util.mergeData(this.options, options); + TL.Util.mergeData(this.data, data); + + this._initLayout(); + this._initEvents(); + + + }, + + /* Adding, Hiding, Showing etc + ================================================== */ + show: function() { + + }, + + hide: function() { + + }, + + setActive: function(is_active) { + + }, + + addTo: function(container) { + container.appendChild(this._el.container); + }, + + removeFrom: function(container) { + container.removeChild(this._el.container); + }, + + updateDisplay: function(w, h) { + this._updateDisplay(w, h); + }, + + getLeft: function() { + return this._el.container.style.left.slice(0, -2); + }, + + getTime: function() { // TODO does this need to know about the end date? + return this.data.start_date.getTime(); + }, + + getEndTime: function() { + + if (this.data.end_date) { + return this.data.end_date.getTime(); + } else { + return false; + } + }, + + setHeight: function(h) { + var text_line_height = 12, + text_lines = 1; + + this._el.content_container.style.height = h + "px"; + this._el.content.className = "tl-timeera-content"; + + // Handle number of lines visible vertically + + if (TL.Browser.webkit) { + text_lines = Math.floor(h / (text_line_height + 2)); + if (text_lines < 1) { + text_lines = 1; + } + this._text.className = "tl-headline"; + this._text.style.webkitLineClamp = text_lines; + } else { + text_lines = h / text_line_height; + if (text_lines > 1) { + this._text.className = "tl-headline tl-headline-fadeout"; + } else { + this._text.className = "tl-headline"; + } + this._text.style.height = (text_lines * text_line_height) + "px"; + } + + }, + + setWidth: function(w) { + if (this.data.end_date) { + this._el.container.style.width = w + "px"; + + if (w > this.options.marker_width_min) { + this._el.content_container.style.width = w + "px"; + this._el.content_container.className = "tl-timeera-content-container tl-timeera-content-container-long"; + } else { + this._el.content_container.style.width = this.options.marker_width_min + "px"; + this._el.content_container.className = "tl-timeera-content-container"; + } + } + + }, + + setClass: function(n) { + this._el.container.className = n; + }, + + setRowPosition: function(n, remainder) { + this.setPosition({top:n}); + + if (remainder < 56) { + //TL.DomUtil.removeClass(this._el.content_container, "tl-timeera-content-container-small"); + } + }, + + setColor: function(color_num) { + this._el.container.className = 'tl-timeera tl-timeera-color' + color_num; + }, + + /* Events + ================================================== */ + + + /* Private Methods + ================================================== */ + _initLayout: function () { + //trace(this.data) + // Create Layout + this._el.container = TL.Dom.create("div", "tl-timeera"); + if (this.data.unique_id) { + this._el.container.id = this.data.unique_id + "-era"; + } + + if (this.data.end_date) { + this.has_end_date = true; + this._el.container.className = 'tl-timeera tl-timeera-with-end'; + } + + this._el.content_container = TL.Dom.create("div", "tl-timeera-content-container", this._el.container); + + this._el.background = TL.Dom.create("div", "tl-timeera-background", this._el.content_container); + + this._el.content = TL.Dom.create("div", "tl-timeera-content", this._el.content_container); + + + + // Text + this._el.text = TL.Dom.create("div", "tl-timeera-text", this._el.content); + this._text = TL.Dom.create("h2", "tl-headline", this._el.text); + if (this.data.text.headline && this.data.text.headline != "") { + this._text.innerHTML = TL.Util.unlinkify(this.data.text.headline); + } + + + + // Fire event that the slide is loaded + this.onLoaded(); + + }, + + _initEvents: function() { + + }, + + // Update Display + _updateDisplay: function(width, height, layout) { + + if (width) { + this.options.width = width; + } + + if (height) { + this.options.height = height; + } + + } + +}); + + +/* ********************************************** + Begin TL.TimeGroup.js +********************************************** */ + +/* TL.TimeGroup + +================================================== */ + +TL.TimeGroup = TL.Class.extend({ + + includes: [TL.Events, TL.DomMixins], + + _el: {}, + + /* Constructor + ================================================== */ + initialize: function(data) { + + // DOM ELEMENTS + this._el = { + parent: {}, + container: {}, + message: {} + }; + + //Options + this.options = { + width: 600, + height: 600 + }; + + // Data + this.data = { + label: "", + rows: 1 + }; + + + this._el.container = TL.Dom.create("div", "tl-timegroup"); + + // Merge Data + TL.Util.mergeData(this.data, data); + + // Animation + this.animator = {}; + + + this._initLayout(); + this._initEvents(); + }, + + /* Public + ================================================== */ + + + + /* Update Display + ================================================== */ + updateDisplay: function(w, h) { + + }, + + setRowPosition: function(n, h) { + // trace(n); + // trace(this._el.container) + this.options.height = h * this.data.rows; + this.setPosition({top:n}); + this._el.container.style.height = this.options.height + "px"; + + }, + + setAlternateRowColor: function(alternate, hide) { + var class_name = "tl-timegroup"; + if (alternate) { + class_name += " tl-timegroup-alternate"; + } + if (hide) { + class_name += " tl-timegroup-hidden"; + } + this._el.container.className = class_name; + }, + + /* Events + ================================================== */ + + + _onMouseClick: function() { + this.fire("clicked", this.options); + }, + + + /* Private Methods + ================================================== */ + _initLayout: function () { + + // Create Layout + this._el.message = TL.Dom.create("div", "tl-timegroup-message", this._el.container); + this._el.message.innerHTML = this.data.label; + + + }, + + _initEvents: function () { + TL.DomEvent.addListener(this._el.container, 'click', this._onMouseClick, this); + }, + + // Update Display + _updateDisplay: function(width, height, animate) { + + } + +}); + +/* ********************************************** + Begin TL.TimeScale.js +********************************************** */ + +/* TL.TimeScale + Strategies for laying out the timenav + make a new one if the slides change + + TODOS: deal with clustering +================================================== */ +TL.TimeScale = TL.Class.extend({ + + initialize: function (timeline_config, options) { + + var slides = timeline_config.events; + this._scale = timeline_config.scale; + + options = TL.Util.mergeData({ // establish defaults + display_width: 500, + screen_multiplier: 3, + max_rows: null + }, options); + + this._display_width = options.display_width; + this._screen_multiplier = options.screen_multiplier; + this._pixel_width = this._screen_multiplier * this._display_width; + + this._group_labels = undefined; + this._positions = []; + this._pixels_per_milli = 0; + + this._earliest = timeline_config.getEarliestDate().getTime(); + this._latest = timeline_config.getLatestDate().getTime(); + this._span_in_millis = this._latest - this._earliest; + if (this._span_in_millis <= 0) { + this._span_in_millis = this._computeDefaultSpan(timeline_config); + } + this._average = (this._span_in_millis)/slides.length; + + this._pixels_per_milli = this.getPixelWidth() / this._span_in_millis; + + this._axis_helper = TL.AxisHelper.getBestHelper(this); + + this._scaled_padding = (1/this.getPixelsPerTick()) * (this._display_width/2) + this._computePositionInfo(slides, options.max_rows); + }, + + _computeDefaultSpan: function(timeline_config) { + // this gets called when all events are at the same instant, + // or maybe when the span_in_millis is > 0 but still below a desired threshold + // TODO: does this need smarts about eras? + if (timeline_config.scale == 'human') { + var formats = {} + for (var i = 0; i < timeline_config.events.length; i++) { + var fmt = timeline_config.events[i].start_date.findBestFormat(); + formats[fmt] = (formats[fmt]) ? formats[fmt] + 1 : 1; + }; + + for (var i = TL.Date.SCALES.length - 1; i >= 0; i--) { + if (formats.hasOwnProperty(TL.Date.SCALES[i][0])) { + var scale = TL.Date.SCALES[TL.Date.SCALES.length - 1]; // default + if (TL.Date.SCALES[i+1]) { + scale = TL.Date.SCALES[i+1]; // one larger than the largest in our data + } + return scale[1] + } + }; + return 365 * 24 * 60 * 60 * 1000; // default to a year? + } + + return 200000; // what is the right handling for cosmo dates? + }, + getGroupLabels: function() { /* + return an array of objects, one per group, in the order (top to bottom) that the groups are expected to appear. Each object will have two properties: + * label (the string as specified in one or more 'group' properties of events in the configuration) + * rows (the number of rows occupied by events associated with the label. ) + */ + return (this._group_labels || []); + }, + + getScale: function() { + return this._scale; + }, + + getNumberOfRows: function() { + return this._number_of_rows + }, + + getPixelWidth: function() { + return this._pixel_width; + }, + + getPosition: function(time_in_millis) { + // be careful using millis, as they won't scale to cosmological time. + // however, we're moving to make the arg to this whatever value + // comes from TL.Date.getTime() which could be made smart about that -- + // so it may just be about the naming. + return ( time_in_millis - this._earliest ) * this._pixels_per_milli + }, + + getPositionInfo: function(idx) { + return this._positions[idx]; + }, + + getPixelsPerTick: function() { + return this._axis_helper.getPixelsPerTick(this._pixels_per_milli); + }, + + getTicks: function() { + return { + major: this._axis_helper.getMajorTicks(this), + minor: this._axis_helper.getMinorTicks(this) } + }, + + getDateFromTime: function(t) { + if(this._scale == 'human') { + return new TL.Date(t); + } else if(this._scale == 'cosmological') { + return new TL.BigDate(new TL.BigYear(t)); + } + throw new TL.Error("time_scale_scale_err", this._scale); + }, + + getMajorScale: function() { + return this._axis_helper.major.name; + }, + + getMinorScale: function() { + return this._axis_helper.minor.name; + }, + + _assessGroups: function(slides) { + var groups = []; + var empty_group = false; + for (var i = 0; i < slides.length; i++) { + if(slides[i].group) { + if(groups.indexOf(slides[i].group) < 0) { + groups.push(slides[i].group); + } else { + empty_group = true; + } + } + }; + if (groups.length && empty_group) { + groups.push(''); + } + return groups; + }, + + /* Compute the marker row positions, minimizing the number of + overlaps. + + @positions = list of objects from this._positions + @rows_left = number of rows available (assume > 0) + */ + _computeRowInfo: function(positions, rows_left) { + var lasts_in_row = []; + var n_overlaps = 0; + + for (var i = 0; i < positions.length; i++) { + var pos_info = positions[i]; + var overlaps = []; + + // See if we can add item to an existing row without + // overlapping the previous item in that row + delete pos_info.row; + + for (var j = 0; j < lasts_in_row.length; j++) { + overlaps.push(lasts_in_row[j].end - pos_info.start); + if(overlaps[j] <= 0) { + pos_info.row = j; + lasts_in_row[j] = pos_info; + break; + } + } + + // If we couldn't add to an existing row without overlap... + if (typeof(pos_info.row) == 'undefined') { + if (rows_left === null) { + // Make a new row + pos_info.row = lasts_in_row.length; + lasts_in_row.push(pos_info); + } else if (rows_left > 0) { + // Make a new row + pos_info.row = lasts_in_row.length; + lasts_in_row.push(pos_info); + rows_left--; + } else { + // Add to existing row with minimum overlap. + var min_overlap = Math.min.apply(null, overlaps); + var idx = overlaps.indexOf(min_overlap); + pos_info.row = idx; + if (pos_info.end > lasts_in_row[idx].end) { + lasts_in_row[idx] = pos_info; + } + n_overlaps++; + } + } + } + + return {n_rows: lasts_in_row.length, n_overlaps: n_overlaps}; + }, + + /* Compute marker positions. If using groups, this._number_of_rows + will never be less than the number of groups. + + @max_rows = total number of available rows + @default_marker_width should be in pixels + */ + _computePositionInfo: function(slides, max_rows, default_marker_width) { + default_marker_width = default_marker_width || 100; + + var groups = []; + var empty_group = false; + + // Set start/end/width; enumerate groups + for (var i = 0; i < slides.length; i++) { + var pos_info = { + start: this.getPosition(slides[i].start_date.getTime()) + }; + this._positions.push(pos_info); + + if (typeof(slides[i].end_date) != 'undefined') { + var end_pos = this.getPosition(slides[i].end_date.getTime()); + pos_info.width = end_pos - pos_info.start; + if (pos_info.width > default_marker_width) { + pos_info.end = pos_info.start + pos_info.width; + } else { + pos_info.end = pos_info.start + default_marker_width; + } + } else { + pos_info.width = default_marker_width; + pos_info.end = pos_info.start + default_marker_width; + } + + if(slides[i].group) { + if(groups.indexOf(slides[i].group) < 0) { + groups.push(slides[i].group); + } + } else { + empty_group = true; + } + } + + if(!(groups.length)) { + var result = this._computeRowInfo(this._positions, max_rows); + this._number_of_rows = result.n_rows; + } else { + if(empty_group) { + groups.push(""); + } + + // Init group info + var group_info = []; + + for(var i = 0; i < groups.length; i++) { + group_info[i] = { + label: groups[i], + idx: i, + positions: [], + n_rows: 1, // default + n_overlaps: 0 + }; + } + + for(var i = 0; i < this._positions.length; i++) { + var pos_info = this._positions[i]; + + pos_info.group = groups.indexOf(slides[i].group || ""); + pos_info.row = 0; + + var gi = group_info[pos_info.group]; + for(var j = gi.positions.length - 1; j >= 0; j--) { + if(gi.positions[j].end > pos_info.start) { + gi.n_overlaps++; + } + } + + gi.positions.push(pos_info); + } + + var n_rows = groups.length; // start with 1 row per group + + while(true) { + // Count free rows available + var rows_left = Math.max(0, max_rows - n_rows); + if(!rows_left) { + break; // no free rows, nothing to do + } + + // Sort by # overlaps, idx + group_info.sort(function(a, b) { + if(a.n_overlaps > b.n_overlaps) { + return -1; + } else if(a.n_overlaps < b.n_overlaps) { + return 1; + } + return a.idx - b.idx; + }); + if(!group_info[0].n_overlaps) { + break; // no overlaps, nothing to do + } + + // Distribute free rows among groups with overlaps + var n_rows = 0; + for(var i = 0; i < group_info.length; i++) { + var gi = group_info[i]; + + if(gi.n_overlaps && rows_left) { + var res = this._computeRowInfo(gi.positions, gi.n_rows + 1); + gi.n_rows = res.n_rows; // update group info + gi.n_overlaps = res.n_overlaps; + rows_left--; // update rows left + } + + n_rows += gi.n_rows; // update rows used + } + } + + // Set number of rows + this._number_of_rows = n_rows; + + // Set group labels; offset row positions + this._group_labels = []; + + group_info.sort(function(a, b) {return a.idx - b.idx; }); + + for(var i = 0, row_offset = 0; i < group_info.length; i++) { + this._group_labels.push({ + label: group_info[i].label, + rows: group_info[i].n_rows + }); + + for(var j = 0; j < group_info[i].positions.length; j++) { + var pos_info = group_info[i].positions[j]; + pos_info.row += row_offset; + } + + row_offset += group_info[i].n_rows; + } + } + + } +}); + + +/* ********************************************** + Begin TL.TimeAxis.js +********************************************** */ + +/* TL.TimeAxis + Display element for showing timescale ticks +================================================== */ + +TL.TimeAxis = TL.Class.extend({ + + includes: [TL.Events, TL.DomMixins, TL.I18NMixins], + + _el: {}, + + /* Constructor + ================================================== */ + initialize: function(elem, options) { + // DOM Elements + this._el = { + container: {}, + content_container: {}, + major: {}, + minor: {}, + }; + + // Components + this._text = {}; + + // State + this._state = { + loaded: false + }; + + + // Data + this.data = {}; + + // Options + this.options = { + duration: 1000, + ease: TL.Ease.easeInSpline, + width: 600, + height: 600 + }; + + // Actively Displaying + this.active = false; + + // Animation Object + this.animator = {}; + + // Axis Helper + this.axis_helper = {}; + + // Minor tick dom element array + this.minor_ticks = []; + + // Minor tick dom element array + this.major_ticks = []; + + // Date Format Lookup, map TL.Date.SCALES names to... + this.dateformat_lookup = { + millisecond: 'time_milliseconds', // ...TL.Language..dateformats + second: 'time_short', + minute: 'time_no_seconds_short', + hour: 'time_no_minutes_short', + day: 'full_short', + month: 'month_short', + year: 'year', + decade: 'year', + century: 'year', + millennium: 'year', + age: 'compact', // ...TL.Language..bigdateformats + epoch: 'compact', + era: 'compact', + eon: 'compact', + eon2: 'compact' + } + + // Main element + if (typeof elem === 'object') { + this._el.container = elem; + } else { + this._el.container = TL.Dom.get(elem); + } + + // Merge Data and Options + TL.Util.mergeData(this.options, options); + + this._initLayout(); + this._initEvents(); + + }, + + /* Adding, Hiding, Showing etc + ================================================== */ + show: function() { + + }, + + hide: function() { + + }, + + addTo: function(container) { + container.appendChild(this._el.container); + }, + + removeFrom: function(container) { + container.removeChild(this._el.container); + }, + + updateDisplay: function(w, h) { + this._updateDisplay(w, h); + }, + + getLeft: function() { + return this._el.container.style.left.slice(0, -2); + }, + + drawTicks: function(timescale, optimal_tick_width) { + + var ticks = timescale.getTicks(); + + var controls = { + minor: { + el: this._el.minor, + dateformat: this.dateformat_lookup[ticks['minor'].name], + ts_ticks: ticks['minor'].ticks, + tick_elements: this.minor_ticks + }, + major: { + el: this._el.major, + dateformat: this.dateformat_lookup[ticks['major'].name], + ts_ticks: ticks['major'].ticks, + tick_elements: this.major_ticks + } + } + // FADE OUT + this._el.major.className = "tl-timeaxis-major"; + this._el.minor.className = "tl-timeaxis-minor"; + this._el.major.style.opacity = 0; + this._el.minor.style.opacity = 0; + + // CREATE MAJOR TICKS + this.major_ticks = this._createTickElements( + ticks['major'].ticks, + this._el.major, + this.dateformat_lookup[ticks['major'].name] + ); + + // CREATE MINOR TICKS + this.minor_ticks = this._createTickElements( + ticks['minor'].ticks, + this._el.minor, + this.dateformat_lookup[ticks['minor'].name], + ticks['major'].ticks + ); + + this.positionTicks(timescale, optimal_tick_width, true); + + // FADE IN + this._el.major.className = "tl-timeaxis-major tl-animate-opacity tl-timeaxis-animate-opacity"; + this._el.minor.className = "tl-timeaxis-minor tl-animate-opacity tl-timeaxis-animate-opacity"; + this._el.major.style.opacity = 1; + this._el.minor.style.opacity = 1; + }, + + _createTickElements: function(ts_ticks,tick_element,dateformat,ticks_to_skip) { + tick_element.innerHTML = ""; + var skip_times = {}; + + var yearZero = new Date(-1,13,-30); + skip_times[yearZero.getTime()] = true; + + if (ticks_to_skip){ + for (var i = 0; i < ticks_to_skip.length; i++) { + skip_times[ticks_to_skip[i].getTime()] = true; + } + } + + var tick_elements = [] + for (var i = 0; i < ts_ticks.length; i++) { + var ts_tick = ts_ticks[i]; + if (!(ts_tick.getTime() in skip_times)) { + var tick = TL.Dom.create("div", "tl-timeaxis-tick", tick_element), + tick_text = TL.Dom.create("span", "tl-timeaxis-tick-text tl-animate-opacity", tick); + + tick_text.innerHTML = ts_tick.getDisplayDate(this.getLanguage(), dateformat); + + tick_elements.push({ + tick:tick, + tick_text:tick_text, + display_date:ts_tick.getDisplayDate(this.getLanguage(), dateformat), + date:ts_tick + }); + } + } + return tick_elements; + }, + + positionTicks: function(timescale, optimal_tick_width, no_animate) { + + // Handle Animation + if (no_animate) { + this._el.major.className = "tl-timeaxis-major"; + this._el.minor.className = "tl-timeaxis-minor"; + } else { + this._el.major.className = "tl-timeaxis-major tl-timeaxis-animate"; + this._el.minor.className = "tl-timeaxis-minor tl-timeaxis-animate"; + } + + this._positionTickArray(this.major_ticks, timescale, optimal_tick_width); + this._positionTickArray(this.minor_ticks, timescale, optimal_tick_width); + + }, + + _positionTickArray: function(tick_array, timescale, optimal_tick_width) { + // Poition Ticks & Handle density of ticks + if (tick_array[1] && tick_array[0]) { + var distance = ( timescale.getPosition(tick_array[1].date.getMillisecond()) - timescale.getPosition(tick_array[0].date.getMillisecond()) ), + fraction_of_array = 1; + + + if (distance < optimal_tick_width) { + fraction_of_array = Math.round(optimal_tick_width/timescale.getPixelsPerTick()); + } + + var show = 1; + + for (var i = 0; i < tick_array.length; i++) { + + var tick = tick_array[i]; + + // Poition Ticks + tick.tick.style.left = timescale.getPosition(tick.date.getMillisecond()) + "px"; + tick.tick_text.innerHTML = tick.display_date; + + // Handle density of ticks + if (fraction_of_array > 1) { + if (show >= fraction_of_array) { + show = 1; + tick.tick_text.style.opacity = 1; + tick.tick.className = "tl-timeaxis-tick"; + } else { + show++; + tick.tick_text.style.opacity = 0; + tick.tick.className = "tl-timeaxis-tick tl-timeaxis-tick-hidden"; + } + } else { + tick.tick_text.style.opacity = 1; + tick.tick.className = "tl-timeaxis-tick"; + } + + }; + } + }, + + /* Events + ================================================== */ + + + /* Private Methods + ================================================== */ + _initLayout: function () { + this._el.content_container = TL.Dom.create("div", "tl-timeaxis-content-container", this._el.container); + this._el.major = TL.Dom.create("div", "tl-timeaxis-major", this._el.content_container); + this._el.minor = TL.Dom.create("div", "tl-timeaxis-minor", this._el.content_container); + + // Fire event that the slide is loaded + this.onLoaded(); + }, + + _initEvents: function() { + + }, + + // Update Display + _updateDisplay: function(width, height, layout) { + + if (width) { + this.options.width = width; + } + + if (height) { + this.options.height = height; + } + + } + +}); + + +/* ********************************************** + Begin TL.AxisHelper.js +********************************************** */ + +/* TL.AxisHelper + Strategies for laying out the timenav + markers and time axis + Intended as a private class -- probably only known to TimeScale +================================================== */ +TL.AxisHelper = TL.Class.extend({ + initialize: function (options) { + if (options) { + this.scale = options.scale; + this.minor = options.minor; + this.major = options.major; + } else { + throw new TL.Error("axis_helper_no_options_err") + } + + }, + + getPixelsPerTick: function(pixels_per_milli) { + return pixels_per_milli * this.minor.factor; + }, + + getMajorTicks: function(timescale) { + return this._getTicks(timescale, this.major) + }, + + getMinorTicks: function(timescale) { + return this._getTicks(timescale, this.minor) + }, + + _getTicks: function(timescale, option) { + + var factor_scale = timescale._scaled_padding * option.factor; + var first_tick_time = timescale._earliest - factor_scale; + var last_tick_time = timescale._latest + factor_scale; + var ticks = [] + for (var i = first_tick_time; i < last_tick_time; i += option.factor) { + ticks.push(timescale.getDateFromTime(i).floor(option.name)); + } + + return { + name: option.name, + ticks: ticks + } + + } + +}); + +(function(cls){ // add some class-level behavior + + var HELPERS = {}; + + var setHelpers = function(scale_type, scales) { + HELPERS[scale_type] = []; + + for (var idx = 0; idx < scales.length - 1; idx++) { + var minor = scales[idx]; + var major = scales[idx+1]; + HELPERS[scale_type].push(new cls({ + scale: minor[3], + minor: { name: minor[0], factor: minor[1]}, + major: { name: major[0], factor: major[1]} + })); + } + }; + + setHelpers('human', TL.Date.SCALES); + setHelpers('cosmological', TL.BigDate.SCALES); + + cls.HELPERS = HELPERS; + + cls.getBestHelper = function(ts,optimal_tick_width) { + if (typeof(optimal_tick_width) != 'number' ) { + optimal_tick_width = 100; + } + var ts_scale = ts.getScale(); + var helpers = HELPERS[ts_scale]; + + if (!helpers) { + throw new TL.Error("axis_helper_scale_err", ts_scale); + } + + var prev = null; + for (var idx = 0; idx < helpers.length; idx++) { + var curr = helpers[idx]; + var pixels_per_tick = curr.getPixelsPerTick(ts._pixels_per_milli); + if (pixels_per_tick > optimal_tick_width) { + if (prev == null) return curr; + var curr_dist = Math.abs(optimal_tick_width - pixels_per_tick); + var prev_dist = Math.abs(optimal_tick_width - pixels_per_tick); + if (curr_dist < prev_dist) { + return curr; + } else { + return prev; + } + } + prev = curr; + } + return helpers[helpers.length - 1]; // last resort + } +})(TL.AxisHelper); + + +/* ********************************************** + Begin TL.Timeline.js +********************************************** */ + +/* TimelineJS +Designed and built by Zach Wise at KnightLab + +This Source Code Form is subject to the terms of the Mozilla Public +License, v. 2.0. If a copy of the MPL was not distributed with this +file, You can obtain one at https://mozilla.org/MPL/2.0/. + +================================================== */ +/* +TODO + +*/ + +/* Required Files +CodeKit Import +https://incident57.com/codekit/ +================================================== */ + +// CORE +// @codekit-prepend "core/TL.js"; +// @codekit-prepend "core/TL.Error.js"; +// @codekit-prepend "core/TL.Util.js"; +// @codekit-prepend "data/TL.Data.js"; +// @codekit-prepend "core/TL.Class.js"; +// @codekit-prepend "core/TL.Events.js"; +// @codekit-prepend "core/TL.Browser.js"; +// @codekit-prepend "core/TL.Load.js"; +// @codekit-prepend "core/TL.TimelineConfig.js"; +// @codekit-prepend "core/TL.ConfigFactory.js"; + + +// LANGUAGE +// @codekit-prepend "language/TL.Language.js"; +// @codekit-prepend "language/TL.I18NMixins.js"; + +// ANIMATION +// @codekit-prepend "animation/TL.Ease.js"; +// @codekit-prepend "animation/TL.Animate.js"; + +// DOM +// @codekit-prepend "dom/TL.Point.js"; +// @codekit-prepend "dom/TL.DomMixins.js"; +// @codekit-prepend "dom/TL.Dom.js"; +// @codekit-prepend "dom/TL.DomUtil.js"; +// @codekit-prepend "dom/TL.DomEvent.js"; +// @codekit-prepend "dom/TL.StyleSheet.js"; + +// Date +// @codekit-prepend "date/TL.Date.js"; +// @codekit-prepend "date/TL.DateUtil.js"; + +// UI +// @codekit-prepend "ui/TL.Draggable.js"; +// @codekit-prepend "ui/TL.Swipable.js"; +// @codekit-prepend "ui/TL.MenuBar.js"; +// @codekit-prepend "ui/TL.Message.js"; + +// MEDIA +// @codekit-prepend "media/TL.MediaType.js"; +// @codekit-prepend "media/TL.Media.js"; + +// MEDIA TYPES +// @codekit-prepend "media/types/TL.Media.Blockquote.js"; +// @codekit-prepend "media/types/TL.Media.DailyMotion.js"; +// @codekit-prepend "media/types/TL.Media.DocumentCloud.js"; +// @codekit-prepend "media/types/TL.Media.Flickr.js"; +// @codekit-prepend "media/types/TL.Media.GoogleDoc.js"; +// @codekit-prepend "media/types/TL.Media.GooglePlus.js"; +// @codekit-prepend "media/types/TL.Media.IFrame.js"; +// @codekit-prepend "media/types/TL.Media.Image.js"; +// @codekit-prepend "media/types/TL.Media.Imgur.js"; +// @codekit-prepend "media/types/TL.Media.Instagram.js"; +// @codekit-prepend "media/types/TL.Media.GoogleMap.js"; +// @codekit-prepend "media/types/TL.Media.PDF.js"; +// @codekit-prepend "media/types/TL.Media.Profile.js"; +// @codekit-prepend "media/types/TL.Media.Slider.js"; +// @codekit-prepend "media/types/TL.Media.SoundCloud.js"; +// @codekit-prepend "media/types/TL.Media.Spotify.js"; +// @codekit-prepend "media/types/TL.Media.Storify.js"; +// @codekit-prepend "media/types/TL.Media.Text.js"; +// @codekit-prepend "media/types/TL.Media.Twitter.js"; +// @codekit-prepend "media/types/TL.Media.TwitterEmbed.js"; +// @codekit-prepend "media/types/TL.Media.Vimeo.js"; +// @codekit-prepend "media/types/TL.Media.Vine.js"; +// @codekit-prepend "media/types/TL.Media.Website.js"; +// @codekit-prepend "media/types/TL.Media.Wikipedia.js"; +// @codekit-prepend "media/types/TL.Media.Wistia.js"; +// @codekit-prepend "media/types/TL.Media.YouTube.js"; +// @codekit-prepend "media/types/TL.Media.Audio.js"; +// @codekit-prepend "media/types/TL.Media.Video.js"; + +// STORYSLIDER +// @codekit-prepend "slider/TL.Slide.js"; +// @codekit-prepend "slider/TL.SlideNav.js"; +// @codekit-prepend "slider/TL.StorySlider.js"; + +// TIMENAV +// @codekit-prepend "timenav/TL.TimeNav.js"; +// @codekit-prepend "timenav/TL.TimeMarker.js"; +// @codekit-prepend "timenav/TL.TimeEra.js"; +// @codekit-prepend "timenav/TL.TimeGroup.js"; +// @codekit-prepend "timenav/TL.TimeScale.js"; +// @codekit-prepend "timenav/TL.TimeAxis.js"; +// @codekit-prepend "timenav/TL.AxisHelper.js"; + + +TL.Timeline = TL.Class.extend({ + includes: [TL.Events, TL.I18NMixins], + + /* Private Methods + ================================================== */ + initialize: function (elem, data, options) { + var self = this; + if (!options) { options = {}}; + // Version + this.version = "3.2.6"; + + // Ready + this.ready = false; + + // DOM ELEMENTS + this._el = { + container: {}, + storyslider: {}, + timenav: {}, + menubar: {} + }; + + // Determine Container Element + if (typeof elem === 'object') { + this._el.container = elem; + } else { + this._el.container = TL.Dom.get(elem); + } + + // Slider + this._storyslider = {}; + + // Style Sheet + this._style_sheet = new TL.StyleSheet(); + + // TimeNav + this._timenav = {}; + + // Menu Bar + this._menubar = {}; + + // Loaded State + this._loaded = {storyslider:false, timenav:false}; + + // Data Object + this.config = null; + + this.options = { + script_path: "", + height: this._el.container.offsetHeight, + width: this._el.container.offsetWidth, + debug: false, + is_embed: false, + is_full_embed: false, + hash_bookmark: false, + default_bg_color: {r:255, g:255, b:255}, + scale_factor: 2, // How many screen widths wide should the timeline be + layout: "landscape", // portrait or landscape + timenav_position: "bottom", // timeline on top or bottom + optimal_tick_width: 60, // optimal distance (in pixels) between ticks on axis + base_class: "tl-timeline", // removing tl-timeline will break all default stylesheets... + timenav_height: null, + timenav_height_percentage: 25, // Overrides timenav height as a percentage of the screen + timenav_mobile_height_percentage: 40, // timenav height as a percentage on mobile devices + timenav_height_min: 175, // Minimum timenav height + marker_height_min: 30, // Minimum Marker Height + marker_width_min: 100, // Minimum Marker Width + marker_padding: 5, // Top Bottom Marker Padding + start_at_slide: 0, + start_at_end: false, + menubar_height: 0, + skinny_size: 650, + medium_size: 800, + relative_date: false, // Use momentjs to show a relative date from the slide.text.date.created_time field + use_bc: false, // Use declared suffix on dates earlier than 0 + // animation + duration: 1000, + ease: TL.Ease.easeInOutQuint, + // interaction + dragging: true, + trackResize: true, + map_type: "stamen:toner-lite", + slide_padding_lr: 100, // padding on slide of slide + slide_default_fade: "0%", // landscape fade + zoom_sequence: [0.5, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89], // Array of Fibonacci numbers for TimeNav zoom levels + language: "en", + ga_property_id: null, + track_events: ['back_to_start','nav_next','nav_previous','zoom_in','zoom_out' ] + }; + + // Animation Objects + this.animator_timenav = null; + this.animator_storyslider = null; + this.animator_menubar = null; + + // Add message to DOM + this.message = new TL.Message({}, {message_class: "tl-message-full"}, this._el.container); + + // Merge Options + if (typeof(options.default_bg_color) == "string") { + var parsed = TL.Util.hexToRgb(options.default_bg_color); // will clear it out if its invalid + if (parsed) { + options.default_bg_color = parsed; + } else { + delete options.default_bg_color + trace("Invalid default background color. Ignoring."); + } + } + TL.Util.mergeData(this.options, options); + + window.addEventListener("resize", function(e){ + self.updateDisplay(); + }); + + // Set Debug Mode + TL.debug = this.options.debug; + + // Apply base class to container + TL.DomUtil.addClass(this._el.container, 'tl-timeline'); + + if (this.options.is_embed) { + TL.DomUtil.addClass(this._el.container, 'tl-timeline-embed'); + } + + if (this.options.is_full_embed) { + TL.DomUtil.addClass(this._el.container, 'tl-timeline-full-embed'); + } + + document.addEventListener("keydown", function(event) { + var keyName = event.key; + var currentSlide = self._getSlideIndex(self.current_id); + var _n = self.config.events.length - 1; + var lastSlide = self.config.title ? _n + 1 : _n; + var firstSlide = 0; + + if (keyName == 'ArrowLeft'){ + if (currentSlide!=firstSlide){ + self.goToPrev(); + } + } + else if (keyName == 'ArrowRight'){ + if (currentSlide!=lastSlide){ + self.goToNext(); + } + } + }); + + // Use Relative Date Calculations + // NOT YET IMPLEMENTED + if(this.options.relative_date) { + if (typeof(moment) !== 'undefined') { + self._loadLanguage(data); + } else { + TL.Load.js(this.options.script_path + "/library/moment.js", function() { + self._loadLanguage(data); + trace("LOAD MOMENTJS") + }); + } + } else { + self._loadLanguage(data); + } + + }, + _translateError: function(e) { + if(e.hasOwnProperty('stack')) { + trace(e.stack); + } + if(e.message_key) { + return this._(e.message_key) + (e.detail ? ' [' + e.detail +']' : '') + } + return e; + }, + + /* Load Language + ================================================== */ + _loadLanguage: function(data) { + try { + this.options.language = new TL.Language(this.options); + + this._initData(data); + } catch(e) { + this.showMessage(this._translateError(e)); + } + }, + + + /* Navigation + ================================================== */ + + // Goto slide with id + goToId: function(id) { + if (this.current_id != id) { + this.current_id = id; + this._timenav.goToId(this.current_id); + this._storyslider.goToId(this.current_id, false, true); + this.fire("change", {unique_id: this.current_id}, this); + } + }, + + // Goto slide n + goTo: function(n) { + if(this.config.title) { + if(n == 0) { + this.goToId(this.config.title.unique_id); + } else { + this.goToId(this.config.events[n - 1].unique_id); + } + } else { + this.goToId(this.config.events[n].unique_id); + } + }, + + // Goto first slide + goToStart: function() { + this.goTo(0); + }, + + // Goto last slide + goToEnd: function() { + var _n = this.config.events.length - 1; + this.goTo(this.config.title ? _n + 1 : _n); + }, + + // Goto previous slide + goToPrev: function() { + this.goTo(this._getSlideIndex(this.current_id) - 1); + }, + + // Goto next slide + goToNext: function() { + this.goTo(this._getSlideIndex(this.current_id) + 1); + }, + + /* Event maniupluation + ================================================== */ + + // Add an event + add: function(data) { + var unique_id = this.config.addEvent(data); + + var n = this._getEventIndex(unique_id); + var d = this.config.events[n]; + + this._storyslider.createSlide(d, this.config.title ? n+1 : n); + this._storyslider._updateDrawSlides(); + + this._timenav.createMarker(d, n); + this._timenav._updateDrawTimeline(false); + + this.fire("added", {unique_id: unique_id}); + }, + + // Remove an event + remove: function(n) { + if(n >= 0 && n < this.config.events.length) { + // If removing the current, nav to new one first + if(this.config.events[n].unique_id == this.current_id) { + if(n < this.config.events.length - 1) { + this.goTo(n + 1); + } else { + this.goTo(n - 1); + } + } + + var event = this.config.events.splice(n, 1); + delete this.config.event_dict[event[0].unique_id]; + this._storyslider.destroySlide(this.config.title ? n+1 : n); + this._storyslider._updateDrawSlides(); + + this._timenav.destroyMarker(n); + this._timenav._updateDrawTimeline(false); + + this.fire("removed", {unique_id: event[0].unique_id}); + } + }, + + removeId: function(id) { + this.remove(this._getEventIndex(id)); + }, + + /* Get slide data + ================================================== */ + + getData: function(n) { + if(this.config.title) { + if(n == 0) { + return this.config.title; + } else if(n > 0 && n <= this.config.events.length) { + return this.config.events[n - 1]; + } + } else if(n >= 0 && n < this.config.events.length) { + return this.config.events[n]; + } + return null; + }, + + getDataById: function(id) { + return this.getData(this._getSlideIndex(id)); + }, + + /* Get slide object + ================================================== */ + + getSlide: function(n) { + if(n >= 0 && n < this._storyslider._slides.length) { + return this._storyslider._slides[n]; + } + return null; + }, + + getSlideById: function(id) { + return this.getSlide(this._getSlideIndex(id)); + }, + + getCurrentSlide: function() { + return this.getSlideById(this.current_id); + }, + + + /* Display + ================================================== */ + updateDisplay: function() { + if (this.ready) { + this._updateDisplay(); + } + }, + + /* + Compute the height of the navigation section of the Timeline, taking into account + the possibility of an explicit height or height percentage, but also honoring the + `timenav_height_min` option value. If `timenav_height` is specified it takes precedence over `timenav_height_percentage` but in either case, if the resultant pixel height is less than `options.timenav_height_min` then the value of `options.timenav_height_min` will be returned. (A minor adjustment is made to the returned value to account for marker padding.) + + Arguments: + @timenav_height (optional): an integer value for the desired height in pixels + @timenav_height_percentage (optional): an integer between 1 and 100 + + */ + _calculateTimeNavHeight: function(timenav_height, timenav_height_percentage) { + + var height = 0; + + if (timenav_height) { + height = timenav_height; + } else { + if (this.options.timenav_height_percentage || timenav_height_percentage) { + if (timenav_height_percentage) { + height = Math.round((this.options.height/100)*timenav_height_percentage); + } else { + height = Math.round((this.options.height/100)*this.options.timenav_height_percentage); + } + + } + } + + // Set new minimum based on how many rows needed + if (this._timenav.ready) { + if (this.options.timenav_height_min < this._timenav.getMinimumHeight()) { + this.options.timenav_height_min = this._timenav.getMinimumHeight(); + } + } + + // If height is less than minimum set it to minimum + if (height < this.options.timenav_height_min) { + height = this.options.timenav_height_min; + } + + height = height - (this.options.marker_padding * 2); + + return height; + }, + + /* Private Methods + ================================================== */ + + // Update View + _updateDisplay: function(timenav_height, animate, d) { + var duration = this.options.duration, + display_class = this.options.base_class, + menu_position = 0, + self = this; + + if (d) { + duration = d; + } + + // Update width and height + this.options.width = this._el.container.offsetWidth; + this.options.height = this._el.container.offsetHeight; + + // Check if skinny + if (this.options.width <= this.options.skinny_size) { + display_class += " tl-skinny"; + this.options.layout = "portrait"; + } else if (this.options.width <= this.options.medium_size) { + display_class += " tl-medium"; + this.options.layout = "landscape"; + } else { + this.options.layout = "landscape"; + } + + // Detect Mobile and Update Orientation on Touch devices + if (TL.Browser.touch) { + this.options.layout = TL.Browser.orientation(); + } + + if (TL.Browser.mobile) { + display_class += " tl-mobile"; + // Set TimeNav Height + this.options.timenav_height = this._calculateTimeNavHeight(timenav_height, this.options.timenav_mobile_height_percentage); + } else { + // Set TimeNav Height + this.options.timenav_height = this._calculateTimeNavHeight(timenav_height); + } + + // LAYOUT + if (this.options.layout == "portrait") { + // Portrait + display_class += " tl-layout-portrait"; + + } else { + // Landscape + display_class += " tl-layout-landscape"; + + } + + // Set StorySlider Height + this.options.storyslider_height = (this.options.height - this.options.timenav_height); + + // Positon Menu + if (this.options.timenav_position == "top") { + menu_position = ( Math.ceil(this.options.timenav_height)/2 ) - (this._el.menubar.offsetHeight/2) - (39/2) ; + } else { + menu_position = Math.round(this.options.storyslider_height + 1 + ( Math.ceil(this.options.timenav_height)/2 ) - (this._el.menubar.offsetHeight/2) - (35/2)); + } + + + if (animate) { + + // Animate TimeNav + + /* + if (this.animator_timenav) { + this.animator_timenav.stop(); + } + + this.animator_timenav = TL.Animate(this._el.timenav, { + height: (this.options.timenav_height) + "px", + duration: duration/4, + easing: TL.Ease.easeOutStrong, + complete: function () { + //self._map.updateDisplay(self.options.width, self.options.timenav_height, animate, d, self.options.menubar_height); + } + }); + */ + + this._el.timenav.style.height = Math.ceil(this.options.timenav_height) + "px"; + + // Animate StorySlider + if (this.animator_storyslider) { + this.animator_storyslider.stop(); + } + this.animator_storyslider = TL.Animate(this._el.storyslider, { + height: this.options.storyslider_height + "px", + duration: duration/2, + easing: TL.Ease.easeOutStrong + }); + + // Animate Menubar + if (this.animator_menubar) { + this.animator_menubar.stop(); + } + + this.animator_menubar = TL.Animate(this._el.menubar, { + top: menu_position + "px", + duration: duration/2, + easing: TL.Ease.easeOutStrong + }); + + } else { + // TimeNav + this._el.timenav.style.height = Math.ceil(this.options.timenav_height) + "px"; + + // StorySlider + this._el.storyslider.style.height = this.options.storyslider_height + "px"; + + // Menubar + this._el.menubar.style.top = menu_position + "px"; + } + + if (this.message) { + this.message.updateDisplay(this.options.width, this.options.height); + } + // Update Component Displays + this._timenav.updateDisplay(this.options.width, this.options.timenav_height, animate); + this._storyslider.updateDisplay(this.options.width, this.options.storyslider_height, animate, this.options.layout); + + if (this.options.language.direction == 'rtl') { + display_class += ' tl-rtl'; + } + + + // Apply class + this._el.container.className = display_class; + + }, + + // Update hashbookmark in the url bar + _updateHashBookmark: function(id) { + var hash = "#" + "event-" + id.toString(); + if (window.location.protocol != 'file:') { + window.history.replaceState(null, "Browsing TimelineJS", hash); + } + this.fire("hash_updated", {unique_id:this.current_id, hashbookmark:"#" + "event-" + id.toString()}, this); + }, + + /* Init + ================================================== */ + // Initialize the data + _initData: function(data) { + var self = this; + + if (typeof data == 'string') { + var self = this; + TL.ConfigFactory.makeConfig(data, function(config) { + self.setConfig(config); + }); + } else if (TL.TimelineConfig == data.constructor) { + this.setConfig(data); + } else { + this.setConfig(new TL.TimelineConfig(data)); + } + }, + + setConfig: function(config) { + this.config = config; + this.config.validate(); + this._validateOptions(); + if (this.config.isValid()) { + try { + this._onDataLoaded(); + } catch(e) { + this.showMessage(""+ this._('error') +": " + this._translateError(e)); + } + } else { + var translated_errs = []; + + for(var i = 0, errs = this.config.getErrors(); i < errs.length; i++) { + translated_errs.push(this._translateError(errs[i])); + } + + this.showMessage(""+ this._('error') +": " + translated_errs.join('
')); + // should we set 'self.ready'? if not, it won't resize, + // but most resizing would only work + // if more setup happens + } + }, + _validateOptions: function() { + // assumes that this.options and this.config have been set. + var INTEGER_PROPERTIES = ['timenav_height', 'timenav_height_min', 'marker_height_min', 'marker_width_min', 'marker_padding', 'start_at_slide', 'slide_padding_lr' ]; + + for (var i = 0; i < INTEGER_PROPERTIES.length; i++) { + var opt = INTEGER_PROPERTIES[i]; + var value = this.options[opt]; + valid = true; + if (typeof(value) == 'number') { + valid = (value == parseInt(value)) + } else if (typeof(value) == "string") { + valid = (value.match(/^\s*(\-?\d+)?\s*$/)); + } + if (!valid) { + this.config.logError({ message_key: 'invalid_integer_option', detail: opt }); + } + } + }, + // Initialize the layout + _initLayout: function () { + var self = this; + + this.message.removeFrom(this._el.container); + this._el.container.innerHTML = ""; + + // Create Layout + if (this.options.timenav_position == "top") { + this._el.timenav = TL.Dom.create('div', 'tl-timenav', this._el.container); + this._el.storyslider = TL.Dom.create('div', 'tl-storyslider', this._el.container); + } else { + this._el.storyslider = TL.Dom.create('div', 'tl-storyslider', this._el.container); + this._el.timenav = TL.Dom.create('div', 'tl-timenav', this._el.container); + } + + this._el.menubar = TL.Dom.create('div', 'tl-menubar', this._el.container); + + + // Initial Default Layout + this.options.width = this._el.container.offsetWidth; + this.options.height = this._el.container.offsetHeight; + // this._el.storyslider.style.top = "1px"; + + // Set TimeNav Height + this.options.timenav_height = this._calculateTimeNavHeight(this.options.timenav_height); + + // Create TimeNav + this._timenav = new TL.TimeNav(this._el.timenav, this.config, this.options); + this._timenav.on('loaded', this._onTimeNavLoaded, this); + this._timenav.on('update_timenav_min', this._updateTimeNavHeightMin, this); + this._timenav.options.height = this.options.timenav_height; + this._timenav.init(); + + // intial_zoom cannot be applied before the timenav has been created + if (this.options.initial_zoom) { + // at this point, this.options refers to the merged set of options + this.setZoom(this.options.initial_zoom); + } + + // Create StorySlider + this._storyslider = new TL.StorySlider(this._el.storyslider, this.config, this.options); + this._storyslider.on('loaded', this._onStorySliderLoaded, this); + this._storyslider.init(); + + // Create Menu Bar + this._menubar = new TL.MenuBar(this._el.menubar, this._el.container, this.options); + + // LAYOUT + if (this.options.layout == "portrait") { + this.options.storyslider_height = (this.options.height - this.options.timenav_height - 1); + } else { + this.options.storyslider_height = (this.options.height - 1); + } + + + // Update Display + this._updateDisplay(this._timenav.options.height, true, 2000); + + }, + + /* Depends upon _initLayout because these events are on things the layout initializes */ + _initEvents: function () { + // TimeNav Events + this._timenav.on('change', this._onTimeNavChange, this); + this._timenav.on('zoomtoggle', this._onZoomToggle, this); + + // StorySlider Events + this._storyslider.on('change', this._onSlideChange, this); + this._storyslider.on('colorchange', this._onColorChange, this); + this._storyslider.on('nav_next', this._onStorySliderNext, this); + this._storyslider.on('nav_previous', this._onStorySliderPrevious, this); + + // Menubar Events + this._menubar.on('zoom_in', this._onZoomIn, this); + this._menubar.on('zoom_out', this._onZoomOut, this); + this._menubar.on('back_to_start', this._onBackToStart, this); + + }, + + /* Analytics + ================================================== */ + _initGoogleAnalytics: function() { + (function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){(i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)})(window,document,'script','//www.google-analytics.com/analytics.js','ga'); + + ga('create', this.options.ga_property_id, 'auto'); + ga('set', 'anonymizeIp', true); + }, + + _initAnalytics: function() { + if (this.options.ga_property_id === null) { return; } + this._initGoogleAnalytics(); + ga('send', 'pageview'); + var events = this.options.track_events; + for (i=0; i < events.length; i++) { + var event_ = events[i]; + this.addEventListener(event_, function(e) { + ga('send', 'event', e.type, 'clicked'); + }); + } + }, + + _onZoomToggle: function(e) { + if (e.zoom == "in") { + this._menubar.toogleZoomIn(e.show); + } else if (e.zoom == "out") { + this._menubar.toogleZoomOut(e.show); + } + + }, + + /* Get index of event by id + ================================================== */ + _getEventIndex: function(id) { + for(var i = 0; i < this.config.events.length; i++) { + if(id == this.config.events[i].unique_id) { + return i; + } + } + return -1; + }, + + /* Get index of slide by id + ================================================== */ + _getSlideIndex: function(id) { + if(this.config.title && this.config.title.unique_id == id) { + return 0; + } + for(var i = 0; i < this.config.events.length; i++) { + if(id == this.config.events[i].unique_id) { + return this.config.title ? i+1 : i; + } + } + return -1; + }, + + /* Events + ================================================== */ + + _onDataLoaded: function(e) { + this.fire("dataloaded"); + this._initLayout(); + this._initEvents(); + this._initAnalytics(); + if (this.message) { + this.message.hide(); + } + + this.ready = true; + + }, + + showMessage: function(msg) { + if (this.message) { + this.message.updateMessage(msg); + } else { + trace("No message display available.") + trace(msg); + } + }, + + _onColorChange: function(e) { + this.fire("color_change", {unique_id:this.current_id}, this); + if (e.color || e.image) { + + } else { + + } + }, + + _onSlideChange: function(e) { + if (this.current_id != e.unique_id) { + this.current_id = e.unique_id; + this._timenav.goToId(this.current_id); + this._onChange(e); + } + }, + + _onTimeNavChange: function(e) { + if (this.current_id != e.unique_id) { + this.current_id = e.unique_id; + this._storyslider.goToId(this.current_id); + this._onChange(e); + } + }, + + _onChange: function(e) { + this.fire("change", {unique_id:this.current_id}, this); + if (this.options.hash_bookmark && this.current_id) { + this._updateHashBookmark(this.current_id); + } + }, + + _onBackToStart: function(e) { + this._storyslider.goTo(0); + this.fire("back_to_start", {unique_id:this.current_id}, this); + }, + + /** + * Zoom in and zoom out should be part of the public API. + */ + zoomIn: function() { + this._timenav.zoomIn(); + }, + zoomOut: function() { + this._timenav.zoomOut(); + }, + + setZoom: function(level) { + this._timenav.setZoom(level); + }, + + _onZoomIn: function(e) { + this._timenav.zoomIn(); + this.fire("zoom_in", {zoom_level:this._timenav.options.scale_factor}, this); + }, + + _onZoomOut: function(e) { + this._timenav.zoomOut(); + this.fire("zoom_out", {zoom_level:this._timenav.options.scale_factor}, this); + }, + + _onTimeNavLoaded: function() { + this._loaded.timenav = true; + this._onLoaded(); + }, + + _onStorySliderLoaded: function() { + this._loaded.storyslider = true; + this._onLoaded(); + }, + + _onStorySliderNext: function(e) { + this.fire("nav_next", e); + }, + + _onStorySliderPrevious: function(e) { + this.fire("nav_previous", e); + }, + + _onLoaded: function() { + if (this._loaded.storyslider && this._loaded.timenav) { + this.fire("loaded", this.config); + // Go to proper slide + if (this.options.hash_bookmark && window.location.hash != "") { + this.goToId(window.location.hash.replace("#event-", "")); + } else { + if( TL.Util.isTrue(this.options.start_at_end) || this.options.start_at_slide > this.config.events.length ) { + this.goToEnd(); + } else { + this.goTo(this.options.start_at_slide); + } + if (this.options.hash_bookmark ) { + this._updateHashBookmark(this.current_id); + } + } + + } + } + +}); + +TL.Timeline.source_path = (function() { + var script_tags = document.getElementsByTagName('script'); + var src = script_tags[script_tags.length-1].src; + return src.substr(0,src.lastIndexOf('/')); +})(); + diff --git a/www/wiki/extensions/ModernTimeline/resources/vendor/tl-icons.eot b/www/wiki/extensions/ModernTimeline/resources/vendor/tl-icons.eot new file mode 100644 index 00000000..179ed2d3 Binary files /dev/null and b/www/wiki/extensions/ModernTimeline/resources/vendor/tl-icons.eot differ diff --git a/www/wiki/extensions/ModernTimeline/resources/vendor/tl-icons.svg b/www/wiki/extensions/ModernTimeline/resources/vendor/tl-icons.svg new file mode 100644 index 00000000..e90b1c32 --- /dev/null +++ b/www/wiki/extensions/ModernTimeline/resources/vendor/tl-icons.svg @@ -0,0 +1,63 @@ + + + +Generated by IcoMoon + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/www/wiki/extensions/ModernTimeline/resources/vendor/tl-icons.ttf b/www/wiki/extensions/ModernTimeline/resources/vendor/tl-icons.ttf new file mode 100644 index 00000000..74eb8c4a Binary files /dev/null and b/www/wiki/extensions/ModernTimeline/resources/vendor/tl-icons.ttf differ diff --git a/www/wiki/extensions/ModernTimeline/resources/vendor/tl-icons.woff b/www/wiki/extensions/ModernTimeline/resources/vendor/tl-icons.woff new file mode 100644 index 00000000..d065e98e Binary files /dev/null and b/www/wiki/extensions/ModernTimeline/resources/vendor/tl-icons.woff differ diff --git a/www/wiki/extensions/ModernTimeline/src/Event.php b/www/wiki/extensions/ModernTimeline/src/Event.php new file mode 100644 index 00000000..821e5810 --- /dev/null +++ b/www/wiki/extensions/ModernTimeline/src/Event.php @@ -0,0 +1,34 @@ +subject = $subject; + $this->startDate = $startDate; + $this->endDate = $endDate; + } + + public function getSubject(): Subject { + return $this->subject; + } + + public function getStartDate(): SMWDITime { + return $this->startDate; + } + + public function getEndDate(): ?SMWDITime { + return $this->endDate; + } + +} diff --git a/www/wiki/extensions/ModernTimeline/src/JsonBuilder.php b/www/wiki/extensions/ModernTimeline/src/JsonBuilder.php new file mode 100644 index 00000000..2f727df1 --- /dev/null +++ b/www/wiki/extensions/ModernTimeline/src/JsonBuilder.php @@ -0,0 +1,122 @@ +slidePresenter = $slidePresenter; + } + + public function buildTimelineJson( SubjectCollection $pages ): array { + $events = []; + + foreach ( $pages->getSubjects() as $subject ) { + [ $startDate, $endDate ] = $this->getDates( $subject ); + + if ( $startDate !== null ) { + $events[] = new Event( $subject, $startDate, $endDate ); + } + } + + return $this->eventsToTimelineJson( $events ); + } + + private function getDates( Subject $subject ): array { + $startDate = null; + $endDate = null; + + foreach ( $this->getPropertyValueCollectionsWithDates( $subject ) as $propertyValues ) { + $dataItem = $propertyValues->getDataItems()[0]; + + if ( $dataItem instanceof SMWDITime ) { + if ( $startDate === null ) { + $startDate = $dataItem; + } + else if ( $endDate === null ) { + $endDate = $dataItem; + } + else { + break; + } + } + } + + return [ $startDate, $endDate ]; + } + + /** + * @param Event[] $events + * @return array + */ + public function eventsToTimelineJson( array $events ): array { + $jsonEvents = []; + + foreach ( $events as $event ) { + $jsonEvents[] = $this->buildEvent( $event ); + } + + return [ 'events' => $jsonEvents ]; + } + + private function buildEvent( Event $event ): array { + $jsonEvent = [ + 'text' => [ + 'headline' => $this->newHeadline( $event->getSubject()->getWikiPage()->getTitle() ), + 'text' => $this->slidePresenter->getText( $event->getSubject() ) + ], + 'start_date' => $this->timeToJson( $event->getStartDate() ) + ]; + + if ( $event->getEndDate() !== null ) { + $jsonEvent['end_date'] = $this->timeToJson( $event->getEndDate() ); + } + + return $jsonEvent; + } + + /** + * @return PropertyValueCollection[] + */ + private function getPropertyValueCollectionsWithDates( Subject $subject ) { + return array_filter( + $subject->getPropertyValueCollections(), + function( PropertyValueCollection $pvc ) { + return $pvc->getPrintRequest()->getTypeID() === '_dat' + && $pvc->getDataItems() !== []; + } + ); + } + + private function newHeadline( \Title $title ): string { + return \Html::element( + 'a', + [ 'href' => $title->getFullURL() ], + $title->getText() + ); + +// return DataValueFactory::getInstance()->newDataValueByItem( $subject->getWikiPage() )->getLongHTMLText( smwfGetLinker() ); + } + + private function timeToJson( SMWDITime $time ): array { + return [ + 'year' => $time->getYear(), + 'month' => $time->getMonth(), + 'day' => $time->getDay(), + 'hour' => $time->getHour(), + 'minute' => $time->getMinute(), + 'second' => (int)$time->getSecond(), + ]; + } + +} diff --git a/www/wiki/extensions/ModernTimeline/src/ModernTimelinePrinter.php b/www/wiki/extensions/ModernTimeline/src/ModernTimelinePrinter.php new file mode 100644 index 00000000..51d8d6dc --- /dev/null +++ b/www/wiki/extensions/ModernTimeline/src/ModernTimelinePrinter.php @@ -0,0 +1,59 @@ +text(); + } + + public function getParamDefinitions( array $definitions ) { + return array_merge( $definitions, TimelineOptions::getTimelineParameterDefinitions() ); + } + + /** + * @param SMWQueryResult $result + * @param ProcessedParam[] $parameters Note: currently getting Param[] from SMW but lets pretend the legacy refactor happened already + * @param int $outputMode + * + * @return string + */ + public function getResult( SMWQueryResult $result, array $parameters, $outputMode ): string { + return ( new TimelinePresenter( $parameters ) )->getResult( $result ); + } + + public function getQueryMode( $context ): int { + return SMWQuery::MODE_INSTANCES; + } + + public function setShowErrors( $show ) { + } + + public function isExportFormat(): bool { + return false; + } + + public function getDefaultSort(): string { + return 'ASC'; + } + + public function isDeferrable(): bool { + return false; + } + + public function supportsRecursiveAnnotation(): bool { + return false; + } + + public function setRecursiveTextProcessor( RecursiveTextProcessor $recursiveTextProcessor ) { + } +} \ No newline at end of file diff --git a/www/wiki/extensions/ModernTimeline/src/ModernTimelineSetup.php b/www/wiki/extensions/ModernTimeline/src/ModernTimelineSetup.php new file mode 100644 index 00000000..f2853953 --- /dev/null +++ b/www/wiki/extensions/ModernTimeline/src/ModernTimelineSetup.php @@ -0,0 +1,30 @@ +Error: The Modern Timeline ' . + 'extension requires Semantic MediaWiki to be ' . + 'installed and enabled.
' + ); + } + } + +} diff --git a/www/wiki/extensions/ModernTimeline/src/ResultFacade/PropertyValueCollection.php b/www/wiki/extensions/ModernTimeline/src/ResultFacade/PropertyValueCollection.php new file mode 100644 index 00000000..d70d7e92 --- /dev/null +++ b/www/wiki/extensions/ModernTimeline/src/ResultFacade/PropertyValueCollection.php @@ -0,0 +1,35 @@ +printRequest = $printRequest; + $this->dataItems = $dataItems; + } + + public function getPrintRequest(): PrintRequest { + return $this->printRequest; + } + + /** + * @return SMWDataItem[] + */ + public function getDataItems(): array { + return $this->dataItems; + } + +} diff --git a/www/wiki/extensions/ModernTimeline/src/ResultFacade/ResultSimplifier.php b/www/wiki/extensions/ModernTimeline/src/ResultFacade/ResultSimplifier.php new file mode 100644 index 00000000..1c58345a --- /dev/null +++ b/www/wiki/extensions/ModernTimeline/src/ResultFacade/ResultSimplifier.php @@ -0,0 +1,57 @@ +getResults() as $diWikiPage ) { + $subjects[] = $this->newSubject( $diWikiPage, $result->getPrintRequests(), $result ); + } + + return new SubjectCollection( $subjects ); + } + + /** + * @param DIWikiPage $resultPage + * @param PrintRequest[] $printRequests + * @param SMWQueryResult $result + * @return Subject + */ + private function newSubject( DIWikiPage $resultPage, array $printRequests, SMWQueryResult $result ): Subject { + $propertyValueCollections = []; + + foreach ( $printRequests as $printRequest ) { + $dataItems = $this->newResultArray( $resultPage, $printRequest, $result )->getContent(); + + $propertyValueCollections[] = new PropertyValueCollection( + $printRequest, + $dataItems === false ? [] : $dataItems + ); + } + + return new Subject( $resultPage, $propertyValueCollections ); + } + + /** + * Compat with SMW 3.0 + * In 3.1+ do: ResultArray::factory( $resultPage, $printRequest, $result ) + */ + private function newResultArray( DIWikiPage $resultPage, PrintRequest $printRequest, SMWQueryResult $result ): \SMWResultArray { + return new \SMWResultArray( + $resultPage, + $printRequest, + $result->getStore(), + method_exists( $result, 'getFieldItemFinder' ) ? $result->getFieldItemFinder() : null + ); + } + +} diff --git a/www/wiki/extensions/ModernTimeline/src/ResultFacade/Subject.php b/www/wiki/extensions/ModernTimeline/src/ResultFacade/Subject.php new file mode 100644 index 00000000..2d67bcda --- /dev/null +++ b/www/wiki/extensions/ModernTimeline/src/ResultFacade/Subject.php @@ -0,0 +1,39 @@ +wikiPage = $wikiPage; + $this->propertyValueCollections = $propertyValueCollections; + } + + public function getWikiPage(): DIWikiPage { + return $this->wikiPage; + } + + /** + * @return PropertyValueCollection[] + */ + public function getPropertyValueCollections(): array { + return $this->propertyValueCollections; + } + + + +} \ No newline at end of file diff --git a/www/wiki/extensions/ModernTimeline/src/ResultFacade/SubjectCollection.php b/www/wiki/extensions/ModernTimeline/src/ResultFacade/SubjectCollection.php new file mode 100644 index 00000000..77b31123 --- /dev/null +++ b/www/wiki/extensions/ModernTimeline/src/ResultFacade/SubjectCollection.php @@ -0,0 +1,23 @@ +pages = $pages; + } + + /** + * @return Subject[] + */ + public function getSubjects(): iterable { + return $this->pages; + } + +} diff --git a/www/wiki/extensions/ModernTimeline/src/SlidePresenter/SimpleSlidePresenter.php b/www/wiki/extensions/ModernTimeline/src/SlidePresenter/SimpleSlidePresenter.php new file mode 100644 index 00000000..14be23e1 --- /dev/null +++ b/www/wiki/extensions/ModernTimeline/src/SlidePresenter/SimpleSlidePresenter.php @@ -0,0 +1,40 @@ +', iterator_to_array( $this->getDisplayValues( $subject ) ) ); + } + + private function getDisplayValues( Subject $subject ) { + foreach ( $subject->getPropertyValueCollections() as $propertyValues ) { + foreach ( $propertyValues->getDataItems() as $dataItem ) { + yield $this->getDisplayValue( $propertyValues->getPrintRequest(), $dataItem ); + } + } + } + + private function getDisplayValue( PrintRequest $pr, \SMWDataItem $dataItem ) { + $property = $pr->getText( null ); + $value = $this->dataItemToText( $dataItem ); + + if ( $property === '' ) { + return $value; + } + + return $property . ': ' . $value; + } + + private function dataItemToText( \SMWDataItem $dataItem ): string { + return DataValueFactory::getInstance()->newDataValueByItem( $dataItem )->getLongHTMLText(); + } + +} diff --git a/www/wiki/extensions/ModernTimeline/src/SlidePresenter/SlidePresenter.php b/www/wiki/extensions/ModernTimeline/src/SlidePresenter/SlidePresenter.php new file mode 100644 index 00000000..b272eb1c --- /dev/null +++ b/www/wiki/extensions/ModernTimeline/src/SlidePresenter/SlidePresenter.php @@ -0,0 +1,13 @@ +templateName = $templateName; + } + + public function getText( Subject $subject ): string { + $parser = $this->getParser(); + + return $parser->recursiveTagParseFully( + ( new TemplateSlidePresenter( $this->templateName ) )->getTemplateText( $subject ) + ); + } + + private function getParser(): \Parser { + return $GLOBALS['wgParser']; + } + + public function getTemplateText( Subject $subject ): string { + return '{{' . implode( '|', $this->getTemplateSegments( $subject ) ) . '}}'; + } + + private function getTemplateSegments( Subject $subject ): array { + return array_merge( + [ + $this->templateName, + $this->parameter( 'title', $subject->getWikiPage()->getTitle()->getFullText() ) + ], + array_map( + function( PropertyValueCollection $pvc ) { + return $this->parameter( + $pvc->getPrintRequest()->getText( null ) ?? '', + $pvc->getDataItems() === [] ? '' : $this->dataItemToText( $pvc->getDataItems()[0] ) + ); + }, + $subject->getPropertyValueCollections() + ) + ); + } + + private function dataItemToText( \SMWDataItem $dataItem ): string { + return DataValueFactory::getInstance()->newDataValueByItem( $dataItem )->getLongHTMLText(); + } + + private function parameter( string $name, string $value ): string { + return $name . '=' . $value; + } +} diff --git a/www/wiki/extensions/ModernTimeline/src/TimelineOptions.php b/www/wiki/extensions/ModernTimeline/src/TimelineOptions.php new file mode 100644 index 00000000..934358d5 --- /dev/null +++ b/www/wiki/extensions/ModernTimeline/src/TimelineOptions.php @@ -0,0 +1,143 @@ + ParameterTypes::DIMENSION, + 'allowauto' => true, + 'units' => [ 'px', 'ex', 'em', '%', '' ], + 'default' => $GLOBALS['wgModernTimelineWidth'], + ]; + + $definitions[self::PARAM_HEIGHT] = [ + 'type' => ParameterTypes::DIMENSION, + 'units' => [ 'px', 'ex', 'em', '' ], + 'default' => $GLOBALS['wgModernTimelineHeight'], + ]; + + $definitions[self::PARAM_BOOKMARK] = [ + 'type' => ParameterTypes::BOOLEAN, + 'default' => $GLOBALS['wgModernTimelineBookmark'], + ]; + + $definitions[self::PARAM_BACKGROUND] = [ + 'type' => ParameterTypes::STRING, + 'default' => $GLOBALS['wgModernTimelineBackground'], + ]; + + $definitions[self::PARAM_SCALE_FACTOR] = [ + 'type' => ParameterTypes::INTEGER, + 'default' => $GLOBALS['wgModernTimelineScaleFactor'], + 'lowerbound' => 1 + ]; + + $definitions[self::PARAM_POSITION] = [ + 'type' => ParameterTypes::STRING, + 'default' => $GLOBALS['wgModernTimelinePosition'], + 'values' => [ 'top', 'bottom' ], + ]; + + $definitions[self::PARAM_TICK_WIDTH] = [ + 'type' => ParameterTypes::INTEGER, + 'default' => $GLOBALS['wgModernTimelineTickWidth'] + ]; + + $definitions[self::PARAM_START_SLIDE] = [ + 'type' => ParameterTypes::INTEGER, + 'default' => $GLOBALS['wgModernTimelineStartSlide'], + 'lowerbound' => 1 + ]; + + $definitions[self::PARAM_START_AT_END] = [ + 'type' => ParameterTypes::BOOLEAN, + 'default' => $GLOBALS['wgModernTimelineStartAtEnd'] + ]; + + $definitions[self::PARAM_TRANSITION_DURATION] = [ + 'type' => ParameterTypes::INTEGER, + 'aliases' => 'duration', + 'default' => $GLOBALS['wgModernTimelineTransitionDuration'], + 'lowerbound' => 1 + ]; + + $definitions[self::PARAM_NAV_HEIGHT] = [ + 'type' => ParameterTypes::DIMENSION, + 'units' => [ 'px', '%' ], + 'default' => $GLOBALS['wgModernTimelineNavHeight'], + ]; + + $definitions[self::PARAM_TEMPLATE] = [ + 'type' => ParameterTypes::STRING, + 'default' => $GLOBALS['wgModernTimelineTemplate'] + ]; + + foreach ( $definitions as $name => $definition ) { + $definitions[$name]['message'] = 'modern-timeline-param-' . str_replace( ' ', '-', $name ); + + if ( strpos( $name, ' ' ) !== false ) { + $definitions[$name]['aliases'] = array_merge( + array_key_exists( 'aliases', $definitions[$name] ) ? (array)$definitions[$name]['aliases'] : [], + [ str_replace( ' ', '', $name ) ] + ); + } + } + + return $definitions; + } + + /** + * @param ProcessedParam[] $parameters + * @return array + */ + public static function processedParamsToJson( array $parameters ): array { + $json = [ + 'hash_bookmark' => $parameters[self::PARAM_BOOKMARK]->getValue(), + 'default_bg_color' => $parameters[self::PARAM_BACKGROUND]->getValue(), + 'scale_factor' => $parameters[self::PARAM_SCALE_FACTOR]->getValue(), + 'timenav_position' => $parameters[self::PARAM_POSITION]->getValue(), + 'optimal_tick_width' => $parameters[self::PARAM_TICK_WIDTH]->getValue(), + 'start_at_slide' => self::getStartAtSlide( $parameters ), + 'start_at_end' => $parameters[self::PARAM_START_AT_END]->getValue(), + 'duration' => $parameters[self::PARAM_TRANSITION_DURATION]->getValue(), + ]; + + $height = $parameters[self::PARAM_NAV_HEIGHT]->getValue(); + + if ( strpos( $height, '%' ) === false ) { + $json['timenav_height'] = (int)substr( $height, 0, -2 ); + } + else { + $json['timenav_height_percentage'] = (int)substr( $height, 0, -1 ); + } + + return $json; + } + + private static function getStartAtSlide( array $parameters ): int { + return $parameters[self::PARAM_START_SLIDE]->getValue() - 1; + } + +} \ No newline at end of file diff --git a/www/wiki/extensions/ModernTimeline/src/TimelinePresenter.php b/www/wiki/extensions/ModernTimeline/src/TimelinePresenter.php new file mode 100644 index 00000000..b0d00689 --- /dev/null +++ b/www/wiki/extensions/ModernTimeline/src/TimelinePresenter.php @@ -0,0 +1,105 @@ +parameters = $parameters; + $this->id = $this->newTimelineId(); + } + + private function newTimelineId(): string { + static $timelineNumber = 0; + return 'modern_timeline_' . ++$timelineNumber; + } + + public function getResult( SMWQueryResult $result ): string { + SMWOutputs::requireResource( 'ext.modern.timeline' ); + + $json = $this->createJsonString( $result ); + + SMWOutputs::requireHeadItem( + $this->id, + $this->createJs( $json ) + ); + + return $this->createDiv( $json ); + } + + private function createJsonString( SMWQueryResult $result ) { + $preJson = ( new JsonBuilder( $this->getSlidePresenter() ) )->buildTimelineJson( + ( new ResultSimplifier() )->newSubjectCollection( $result ) + ); + + $preJson['options'] = TimelineOptions::processedParamsToJson( $this->parameters ); + + return json_encode( $preJson ); + } + + private function getSlidePresenter(): SlidePresenter { + if ( $this->getTemplateName() === '' ) { + return new SimpleSlidePresenter(); + } + + return new TemplateSlidePresenter( $this->getTemplateName() ); + } + + private function getTemplateName(): string { + return $this->parameters['template']->getValue(); + } + + private function createJs( string $json ): string { + return \Html::rawElement( + 'script', + [ + 'type' => 'text/javascript' + ], + "if (!window.hasOwnProperty('modernTimeline')) {window.modernTimeline = {};}" + . "\n window.modernTimeline.{$this->id} = $json;" + ); + } + + private function createDiv( string $json ): string { + $width = $this->parameters[TimelineOptions::PARAM_WIDTH]->getValue(); + $height = $this->parameters[TimelineOptions::PARAM_HEIGHT]->getValue(); + + return \Html::rawElement( + 'div', + [ + 'id' => $this->id, + 'style' => "width: $width; height: $height", + 'class' => 'modern_timeline_outer_div' + ], + \Html::element( + 'div', + [ + 'class' => 'modern_timeline_inner_div', + 'style' => 'width: 100%; height: calc(100% - 10px); background-color: rgba(0, 0, 0, 0.05); margin-top: 5px; margin-bottom: 5px;' + ] + ) + ) + . \Html::element( // TODO: remove when system tests can test head items + 'div', + [ 'style' => 'display:none' ], + $json + ); + } + +} diff --git a/www/wiki/extensions/ModernTimeline/tests/Integration/OptionsTest.php b/www/wiki/extensions/ModernTimeline/tests/Integration/OptionsTest.php new file mode 100644 index 00000000..d5352632 --- /dev/null +++ b/www/wiki/extensions/ModernTimeline/tests/Integration/OptionsTest.php @@ -0,0 +1,150 @@ +assertSame( + [ + 'hash_bookmark' => false, + 'default_bg_color' => 'white', + 'scale_factor' => self::DEFAULT_SCALE_FACTOR, + 'timenav_position' => 'bottom', + 'optimal_tick_width' => 100, + 'start_at_slide' => self::DEFAULT_START_SLIDE, + 'start_at_end' => false, + 'duration' => 1000, + 'timenav_height' => 200, + ], + $this->processUserInputToTimelineOptions( [] ) + ); + } + + private function processUserInputToTimelineOptions( array $userInput ): array { + return TimelineOptions::processedParamsToJson( + $this->processUserInput( $userInput )->getParameters() + ); + } + + private function processUserInput( array $userInput ): ProcessingResult { + $processor = Processor::newDefault(); + + $processor->setParameters( $userInput ); + $processor->setParameterDefinitions( $this->getParameterDefinitions() ); + + return $processor->processParameters(); + } + + private function getParameterDefinitions(): array { + return ParamDefinitionFactory::newDefault()->newDefinitionsFromArrays( + TimelineOptions::getTimelineParameterDefinitions() + ); + } + + public function testDefaultWidthAndHeight() { + $parameters = $this->processUserInput( [] )->getParameterArray(); + + $this->assertSame( $GLOBALS['wgModernTimelineWidth'], $parameters['width'] ); + $this->assertSame( $GLOBALS['wgModernTimelineHeight'], $parameters['height'] ); + } + + /** + * @dataProvider widthProvider + */ + public function testWidth( string $input, string $expected ) { + $parameters = $this->processUserInput( [ + 'width' => $input, + ] )->getParameterArray(); + + $this->assertSame( $expected, $parameters['width'] ); + } + + public function widthProvider() { + yield [ '10', '10px' ]; + yield [ '10px', '10px' ]; + yield [ '10%', '10%' ]; + yield [ '10em', '10em' ]; + yield [ '10ex', '10ex' ]; + yield [ 'auto', 'auto' ]; + } + + /** + * @dataProvider heightProvider + */ + public function testHeight( string $input, string $expected ) { + $parameters = $this->processUserInput( [ + 'height' => $input, + ] )->getParameterArray(); + + $this->assertSame( $expected, $parameters['height'] ); + } + + public function heightProvider() { + yield [ '10', '10px' ]; + yield [ '10px', '10px' ]; + yield [ '10em', '10em' ]; + yield [ '10ex', '10ex' ]; + } + + public function testTooLowScaleFactorDefaults() { + $this->assertProcesses( 'scale factor', '0', self::DEFAULT_SCALE_FACTOR ); + } + + private function assertProcesses( string $paramName, string $input, $expected ) { + $parameters = $this->processUserInput( [ + $paramName => $input, + ] )->getParameterArray(); + + $this->assertSame( $expected, $parameters[$paramName] ); + } + + public function testTooLowStartSlideDefaults() { + $this->assertProcesses( 'start slide', '0', 1 ); + } + + public function testStartSlideIsOneBased() { + $this->assertSame( + 2, + $this->processUserInputToTimelineOptions( [ 'start slide' => '3' ] )['start_at_slide'] + ); + } + + /** + * @dataProvider animationDurationAliasProvider + */ + public function testAnimationDurationAliases( string $alias ) { + $parameters = $this->processUserInput( [ + $alias => '42', + ] )->getParameterArray(); + + $this->assertSame( 42, $parameters['transition duration'] ); + } + + public function animationDurationAliasProvider() { + yield 'automatic alias' => [ 'transitionduration' ]; + yield 'manual alias' => [ 'duration' ]; + } + + public function testNavHeightUsesCorrectJsonFieldWhenGivenPercentage() { + $this->assertSame( + 50, + $this->processUserInputToTimelineOptions( [ 'navigation height' => '50%' ] )['timenav_height_percentage'] + ); + } + +} diff --git a/www/wiki/extensions/ModernTimeline/tests/System/JsonScript/escaping.json b/www/wiki/extensions/ModernTimeline/tests/System/JsonScript/escaping.json new file mode 100644 index 00000000..d26547d1 --- /dev/null +++ b/www/wiki/extensions/ModernTimeline/tests/System/JsonScript/escaping.json @@ -0,0 +1,51 @@ +{ + "description": "Modern Timeline - escaping", + "setup": [ + { + "page": "News date", + "namespace": "SMW_NS_PROPERTY", + "contents": "[[Has type::Date]]" + }, + { + "page": "Description", + "namespace": "SMW_NS_PROPERTY", + "contents": "[[Has type::Text]]" + }, + { + "page": "Evil page", + "contents": "[[News date::August 11, 2019]] [[Description::]]" + }, + { + "page": "Query page", + "contents": "{{ #ask: [[Description::+]] | format=moderntimeline | ?News date | ?Description }}" + } + ], + "tests": [ + { + "about": "Evil descriptions are escaped", + "type": "parser", + "subject": "Query page", + "assert-output": { + "include-head-items": true, + "to-contain": [ + "Evil page", + "2019", + "Description: &lt;script&gt;&lt;\\/script&gt" + ] + } + } + ], + "settings": { + "wgContLang": "en", + "wgLang": "en", + "smwgNamespacesWithSemanticLinks": { + "NS_MAIN": true, + "SMW_NS_PROPERTY": true + } + }, + "meta": { + "version": "2", + "is-incomplete": false, + "debug": false + } +} \ No newline at end of file diff --git a/www/wiki/extensions/ModernTimeline/tests/System/JsonScript/parameters.json b/www/wiki/extensions/ModernTimeline/tests/System/JsonScript/parameters.json new file mode 100644 index 00000000..a12a5fc5 --- /dev/null +++ b/www/wiki/extensions/ModernTimeline/tests/System/JsonScript/parameters.json @@ -0,0 +1,35 @@ +{ + "description": "Modern Timeline - parameters", + "setup": [ + { + "page": "Query with modified height", + "contents": "{{ #ask: [[News date::+]] | format=moderntimeline | ?News date | height=1337 }}" + } + ], + "tests": [ + { + "about": "Width and height parameters", + "type": "parser", + "subject": "Query with modified height", + "assert-output": { + "to-contain": [ + "width: 100%", + "height: 1337px" + ] + } + } + ], + "settings": { + "wgContLang": "en", + "wgLang": "en", + "smwgNamespacesWithSemanticLinks": { + "NS_MAIN": true, + "SMW_NS_PROPERTY": true + } + }, + "meta": { + "version": "2", + "is-incomplete": false, + "debug": false + } +} \ No newline at end of file diff --git a/www/wiki/extensions/ModernTimeline/tests/System/JsonScript/query.json b/www/wiki/extensions/ModernTimeline/tests/System/JsonScript/query.json new file mode 100644 index 00000000..bca28efc --- /dev/null +++ b/www/wiki/extensions/ModernTimeline/tests/System/JsonScript/query.json @@ -0,0 +1,93 @@ +{ + "description": "Modern Timeline - query result content", + "setup": [ + { + "page": "News date", + "namespace": "SMW_NS_PROPERTY", + "contents": "[[Has type::Date]]" + }, + { + "page": "End date", + "namespace": "SMW_NS_PROPERTY", + "contents": "[[Has type::Date]]" + }, + { + "page": "Description", + "namespace": "SMW_NS_PROPERTY", + "contents": "[[Has type::Text]]" + }, + { + "page": "First news event", + "contents": "[[News date::August 1, 2019]] [[End date::August 3, 2019]] [[Description::first desc]]" + }, + { + "page": "Second news event", + "contents": "[[News date::August 6, 2019]] [[Description::second desc]]" + }, + { + "page": "Page without date", + "contents": "[[Description::No date here]]" + }, + { + "page": "Query page", + "contents": "{{ #ask: [[Description::+]] | format=moderntimeline | ?News date | ?End date | ?Description }}" + } + ], + "tests": [ + { + "about": "Only pages with dates are shown", + "type": "parser", + "subject": "Query page", + "assert-output": { + "include-head-items": true, + "to-contain": [ + "First news event", + "Second news event" + ], + "not-contain": [ + "No date", + "without date" + ] + } + }, + { + "about": "Dates are included", + "type": "parser", + "subject": "Query page", + "assert-output": { + "include-head-items": true, + "to-contain": [ + "month\":8", + "day\":1", + "day\":3", + "day\":6" + ] + } + }, + { + "about": "Extra printouts are included", + "type": "parser", + "subject": "Query page", + "assert-output": { + "include-head-items": true, + "to-contain": [ + "first desc", + "second desc" + ] + } + } + ], + "settings": { + "wgContLang": "en", + "wgLang": "en", + "smwgNamespacesWithSemanticLinks": { + "NS_MAIN": true, + "SMW_NS_PROPERTY": true + } + }, + "meta": { + "version": "2", + "is-incomplete": false, + "debug": false + } +} \ No newline at end of file diff --git a/www/wiki/extensions/ModernTimeline/tests/System/JsonScript/smwdoc.json b/www/wiki/extensions/ModernTimeline/tests/System/JsonScript/smwdoc.json new file mode 100644 index 00000000..24333029 --- /dev/null +++ b/www/wiki/extensions/ModernTimeline/tests/System/JsonScript/smwdoc.json @@ -0,0 +1,37 @@ +{ + "description": "Modern Timeline - smwdoc", + "setup": [ + { + "page": "SMWDoc", + "contents": "{{#smwdoc:moderntimeline}}" + } + ], + "tests": [ + { + "about": "All parameters have a message", + "type": "parser", + "subject": "SMWDoc", + "assert-output": { + "to-contain": [ + "Background" + ], + "not-contain": [ + "modern-timeline-" + ] + } + } + ], + "settings": { + "wgContLang": "en", + "wgLang": "en", + "smwgNamespacesWithSemanticLinks": { + "NS_MAIN": true, + "SMW_NS_PROPERTY": true + } + }, + "meta": { + "version": "2", + "is-incomplete": false, + "debug": false + } +} \ No newline at end of file diff --git a/www/wiki/extensions/ModernTimeline/tests/System/JsonScript/template.json b/www/wiki/extensions/ModernTimeline/tests/System/JsonScript/template.json new file mode 100644 index 00000000..99f883c9 --- /dev/null +++ b/www/wiki/extensions/ModernTimeline/tests/System/JsonScript/template.json @@ -0,0 +1,54 @@ +{ + "description": "Modern Timeline - template", + "setup": [ + { + "page": "News date", + "namespace": "SMW_NS_PROPERTY", + "contents": "[[Has type::Date]]" + }, + { + "page": "Description", + "namespace": "SMW_NS_PROPERTY", + "contents": "[[Has type::Text]]" + }, + { + "page": "TimelineTest", + "namespace": "NS_TEMPLATE", + "contents": "Hi! {{{title}}} {{{Description}}} {{{News date}}}" + }, + { + "page": "First news event", + "contents": "[[News date::August 1, 2019]] [[Description::first desc]]" + }, + { + "page": "Query page", + "contents": "{{ #ask: [[Description::+]] | format=moderntimeline | ?News date | ?Description | template=TimelineTest }}" + } + ], + "tests": [ + { + "about": "Template parameter", + "type": "parser", + "subject": "Query page", + "assert-output": { + "include-head-items": true, + "to-contain": [ + "Hi! First news event first desc 1 August 2019" + ] + } + } + ], + "settings": { + "wgContLang": "en", + "wgLang": "en", + "smwgNamespacesWithSemanticLinks": { + "NS_MAIN": true, + "SMW_NS_PROPERTY": true + } + }, + "meta": { + "version": "2", + "is-incomplete": false, + "debug": false + } +} \ No newline at end of file diff --git a/www/wiki/extensions/ModernTimeline/tests/System/JsonScriptTest.php b/www/wiki/extensions/ModernTimeline/tests/System/JsonScriptTest.php new file mode 100644 index 00000000..4ee5aa4e --- /dev/null +++ b/www/wiki/extensions/ModernTimeline/tests/System/JsonScriptTest.php @@ -0,0 +1,15 @@ +assertBuildsJson( + [], + new SubjectCollection( [] ) + ); + } + + public function assertBuildsJson( array $expectedJson, SubjectCollection $input ) { + $this->assertSame( + [ + 'events' => $expectedJson + ], + $this->toJson( $input ) + ); + } + + private function toJson( SubjectCollection $input ): array { + return ( new JsonBuilder( new SimpleSlidePresenter() ) )->buildTimelineJson( $input ); + } + + public function testOnlySubjectsWithNoValues() { + $this->assertBuildsJson( + [], + new SubjectCollection( + [ + new Subject( + $this->newDiWikiPage(), + [] + ) + ] + ) + ); + } + + private function newDiWikiPage( string $pageName = self::PAGE_NAME ): DIWikiPage { + $page = $this->createMock( DIWikiPage::class ); + + $page->method( 'getTitle' )->willReturn( \Title::newFromText( $pageName ) ); + + return $page; + } + + private function newSinglePageWithStartAndEndDate(): SubjectCollection { + return new SubjectCollection( + [ + new Subject( + $this->newDiWikiPage(), + [ + new PropertyValueCollection( + $this->newDatePrintRequestWithLabel( 'Has date' ), + [ + new SMWDITime( + SMWDITime::CM_GREGORIAN, + 2019, + 8, + 2, + 16, + 7, + 42 + ) + ] + ), + new PropertyValueCollection( + $this->newDatePrintRequestWithLabel( 'End date' ), + [ + new SMWDITime( + SMWDITime::CM_GREGORIAN, + 2019, + 8, + 5, + 17, + 39, + 23 + ) + ] + ) + ] + ) + ] + ); + } + + private function newDatePrintRequestWithLabel( string $label ): PrintRequest { + $pr = $this->createMock( PrintRequest::class ); + $pr->method( 'getLabel' )->willReturn( $label ); + $pr->method( 'getTypeID' )->willReturn( '_dat' ); + return $pr; + } + + public function testStartDate() { + $json = $this->toJson( $this->newSinglePageWithStartAndEndDate() ); + + $this->assertSame( + [ + 'year' => 2019, + 'month' => 8, + 'day' => 2, + 'hour' => 16, + 'minute' => 7, + 'second' => 42, + ], + $json['events'][0]['start_date'] + ); + } + + public function testEndDate() { + $json = $this->toJson( $this->newSinglePageWithStartAndEndDate() ); + + $this->assertSame( + [ + 'year' => 2019, + 'month' => 8, + 'day' => 5, + 'hour' => 17, + 'minute' => 39, + 'second' => 23, + ], + $json['events'][0]['end_date'] + ); + } + + public function testHeadline() { + $json = $this->toJson( $this->newSinglePageWithStartAndEndDate() ); + + $this->assertContains( + self::PAGE_NAME, + $json['events'][0]['text']['headline'] + ); + } + + public function testPageWithStartAndEndDateOnlyLeadsToOneEvent() { + $this->assertCount( + 1, + $this->toJson( $this->newSinglePageWithStartAndEndDate() )['events'] + ); + } + +} diff --git a/www/wiki/extensions/ModernTimeline/tests/Unit/SlidePresenter/TemplateSlidePresenterTest.php b/www/wiki/extensions/ModernTimeline/tests/Unit/SlidePresenter/TemplateSlidePresenterTest.php new file mode 100644 index 00000000..64626df7 --- /dev/null +++ b/www/wiki/extensions/ModernTimeline/tests/Unit/SlidePresenter/TemplateSlidePresenterTest.php @@ -0,0 +1,81 @@ +assertSame( + '{{TemplateName|title=Some Page|Has date=2 August 2019 16:07:42|End date=5 August 2019 17:39:23}}', + ( new TemplateSlidePresenter( 'TemplateName' ) )->getTemplateText( $this->newSinglePageWithStartAndEndDate() ) + ); + } + + private function newSinglePageWithStartAndEndDate(): Subject { + return new Subject( + $this->newDiWikiPage(), + [ + new PropertyValueCollection( + $this->newDatePrintRequestWithLabel( 'Has date' ), + [ + new SMWDITime( + SMWDITime::CM_GREGORIAN, + 2019, + 8, + 2, + 16, + 7, + 42 + ) + ] + ), + new PropertyValueCollection( + $this->newDatePrintRequestWithLabel( 'End date' ), + [ + new SMWDITime( + SMWDITime::CM_GREGORIAN, + 2019, + 8, + 5, + 17, + 39, + 23 + ) + ] + ) + ] + ); + } + + private function newDiWikiPage(): DIWikiPage { + $page = $this->createMock( DIWikiPage::class ); + + $page->method( 'getTitle' )->willReturn( \Title::newFromText( self::PAGE_NAME ) ); + + return $page; + } + + private function newDatePrintRequestWithLabel( string $label ): PrintRequest { + $pr = $this->createMock( PrintRequest::class ); + $pr->method( 'getLabel' )->willReturn( $label ); + $pr->method( 'getText' )->willReturn( $label ); + $pr->method( 'getTypeID' )->willReturn( '_dat' ); + return $pr; + } + +} diff --git a/www/wiki/extensions/ModernTimeline/tests/bootstrap.php b/www/wiki/extensions/ModernTimeline/tests/bootstrap.php new file mode 100644 index 00000000..1df89fdf --- /dev/null +++ b/www/wiki/extensions/ModernTimeline/tests/bootstrap.php @@ -0,0 +1,18 @@ +addPsr4( 'SMW\\Tests\\', __DIR__ . '/../../SemanticMediaWiki/tests/phpunit' ); diff --git a/www/wiki/extensions/SemanticResultFormats/.travis.yml b/www/wiki/extensions/SemanticResultFormats/.travis.yml index 8a339f12..6dfe5634 100644 --- a/www/wiki/extensions/SemanticResultFormats/.travis.yml +++ b/www/wiki/extensions/SemanticResultFormats/.travis.yml @@ -1,21 +1,24 @@ language: php +services: + - mysql + matrix: fast_finish: true include: - - env: DB=mysql; MW=master; SMW=~3.0@dev - php: 7.1 - - env: DB=mysql; MW=1.28.2; SMW=~3.0@dev; TYPE=coverage + - env: DB=sqlite; MW=master; SMW=~3.0@dev; TYPE=coverage + php: 7.3 + - env: DB=sqlite; MW=REL1_31; SMW=~3.0; MERMAID=dev-master php: 7.0 - - env: DB=sqlite; MW=1.28.2; SMW=~3.0@dev + - env: DB=mysql; MW=REL1_31; SMW=~3.0@dev php: 7.0 - - env: DB=mysql; MW=1.27.3; SMW=~3.0@dev - php: 5.6 - - env: DB=postgres; MW=1.27.3; SMW=~3.0@dev - php: 5.6 + - env: DB=postgres; MW=REL1_32; SMW=~3.0@dev + php: 7.2 + - env: DB=mysql; MW=REL1_33; SMW=~3.0@dev + php: 7.2 allow_failures: - # Broken due to https://phabricator.wikimedia.org/T188840 - - env: DB=mysql; MW=master; SMW=~3.0@dev + # postgres broken due to https://phabricator.wikimedia.org/T188840 + - env: DB=postgres; MW=REL1_32; SMW=~3.0@dev install: - bash ./build/travis/install-mediawiki.sh diff --git a/www/wiki/extensions/SemanticResultFormats/DefaultSettings.php b/www/wiki/extensions/SemanticResultFormats/DefaultSettings.php index c05bdfa9..b7482584 100644 --- a/www/wiki/extensions/SemanticResultFormats/DefaultSettings.php +++ b/www/wiki/extensions/SemanticResultFormats/DefaultSettings.php @@ -52,7 +52,9 @@ $GLOBALS['srfgFormats'] = [ 'dygraphs', 'media', 'datatables', - + 'spreadsheet', + 'gantt', + 'graph' // Boilerplate // Enable access to the format identifier // 'boilerplate', @@ -67,7 +69,7 @@ $GLOBALS['srfgFormats'] = [ // Still in alpha: // 'jitgraph', // Several issues need to be fixed before this can be enabled, most notably it does not work properly with the RL. - // Disabled by default sicne they contact external sites: + // Disabled by default since they contact external sites: // 'googlebar', // 'googlepie', @@ -84,11 +86,6 @@ if( array_key_exists( 'ExtHashTables', $GLOBALS['wgAutoloadClasses'] ) && define $GLOBALS['srfgFormats'][] = 'hash'; } -// Enable the excel format only if PHPExcel can be loaded. -if( class_exists( 'PHPExcel' ) ){ - $GLOBALS['srfgFormats'][] = 'excel'; -} - // Used for Array and Hash formats. // Allows value as string or object instances of Title or Article classes or an array // where index 0 is the page title and 1 is the namespace-index (by default NS_MAIN) diff --git a/www/wiki/extensions/SemanticResultFormats/README.md b/www/wiki/extensions/SemanticResultFormats/README.md index f2cd9fe4..4806f992 100644 --- a/www/wiki/extensions/SemanticResultFormats/README.md +++ b/www/wiki/extensions/SemanticResultFormats/README.md @@ -13,10 +13,9 @@ installation independently. For more information, visit the [SRF homepage][srf] ## Requirements -- PHP 5.6 or later -- MediaWiki 1.27 or later +- PHP 7.0 or later +- MediaWiki 1.31 or later - Semantic MediaWiki 3.0 or later -- MySQL 5 or later or SQLite 3 or later ## Installation diff --git a/www/wiki/extensions/SemanticResultFormats/RELEASE-NOTES.md b/www/wiki/extensions/SemanticResultFormats/RELEASE-NOTES.md index 824be8e0..2c63772c 100644 --- a/www/wiki/extensions/SemanticResultFormats/RELEASE-NOTES.md +++ b/www/wiki/extensions/SemanticResultFormats/RELEASE-NOTES.md @@ -1,4 +1,26 @@ -These are the release notes for the [Semantic Result Formats](https://www.semantic-mediawiki.org/wiki/Extension:Semantic_Result_Formats) MediaWiki extension. +These are the release notes for the [Semantic Result Formats](https://www.semantic-mediawiki.org/wiki/Extension:Semantic_Result_Formats) (a.k.a SRF) MediaWiki extension. + +## SRF 3.1.0 + +Released on August 18, 2019. + +* Minimum requirement for + * PHP changed to version 7.0 and later + * MediaWiki changed to version 1.31 and later +* Added compatibility with Semantic MediaWiki 3.1.x +* Improved compatibility with PHP 7.2+ +* Added `spreadsheet` format (by Stephan Gambke) +* Deprecated `excel` format (by Stephan Gambke) +* Added `gantt` result format (by Sebastian Schmid) +* Added `filename` parameter to the `vcard` format (by James Hong Kong) +* Added `template` parameter to the `outline` format (by James Hong Kong) +* Added css `class` parameter to the `tree` format (by Stephan Gambke) +* Improved `timeseries` format (by Christian Zagrodnick) + * Fixed `uncaught exception: Invalid dimensions for plot` + * Only correct plot height when there are tabs +* Other bug fixes and code improvements +* Made the extension installable without the `php-gd` PHP extension +* Updated translations (by translatewiki.net community) ## SRF 3.0.1 diff --git a/www/wiki/extensions/SemanticResultFormats/Resources.php b/www/wiki/extensions/SemanticResultFormats/Resources.php index ce2f004c..26871a9e 100644 --- a/www/wiki/extensions/SemanticResultFormats/Resources.php +++ b/www/wiki/extensions/SemanticResultFormats/Resources.php @@ -235,7 +235,7 @@ return [ 'styles' => 'dygraphs/resources/ext.srf.dygraphs.css', 'dependencies' => [ 'jquery.client', - 'jquery.async', + 'ext.jquery.async', 'ext.srf.util', 'ext.smw.tooltip', 'ext.dygraphs.combined', @@ -473,7 +473,7 @@ return [ 'styles' => 'jqplot/resources/ext.srf.jqlpot.chart.css', 'dependencies' => [ 'ext.jquery.jqplot.core', - 'jquery.async', + 'ext.jquery.async', 'ext.srf.util', 'ext.srf.jqplot.themes' ] @@ -571,7 +571,6 @@ return [ 'scripts' => 'timeline/resources/ext.srf.timeline.js', 'dependencies' => [ 'ext.smile.timeline', - 'mediawiki.legacy.wikibits' ], 'position' => 'top', 'targets' => [ 'mobile', 'desktop' ] @@ -634,7 +633,6 @@ return [ 'JitGraph/base.css', ], 'dependencies' => [ - 'mediawiki.legacy.wikibits', 'ext.srf.jquery.progressbar', 'ext.srf.jit', ], @@ -866,7 +864,7 @@ return [ 'ext.srf.timeseries.flot' => $formatModule + [ 'scripts' => 'timeseries/resources/ext.srf.timeseries.flot.js', 'dependencies' => [ - 'jquery.async', + 'ext.jquery.async', 'ext.jquery.flot', 'ext.srf.util', 'ext.srf.flot.core' @@ -1012,6 +1010,12 @@ return [ 'dependencies' => 'ext.srf.datatables' ], + // Mermaid Format + 'ext.srf.gantt' => $formatModule + [ + 'scripts' => 'Gantt/resources/ext.gantt.js', + 'dependencies' => 'ext.mermaid' + ] + // Boilerplate example registration /* // Simple implementation diff --git a/www/wiki/extensions/SemanticResultFormats/SemanticResultFormats.hooks.php b/www/wiki/extensions/SemanticResultFormats/SemanticResultFormats.hooks.php index cda03e33..fba34807 100644 --- a/www/wiki/extensions/SemanticResultFormats/SemanticResultFormats.hooks.php +++ b/www/wiki/extensions/SemanticResultFormats/SemanticResultFormats.hooks.php @@ -189,19 +189,13 @@ final class SRFHooks { // Intro text, do not escape the message here as it contains // href links - $preferences['srf-prefs-intro'] = - [ - 'type' => 'info', - 'label' => ' ', - 'default' => Html::rawElement( - 'span', - [ 'class' => 'srf-prefs-intro plainlinks' ], - wfMessage( 'srf-prefs-intro-text' )->parseAsBlock() - ), - 'section' => 'smw/srf', - 'raw' => 1, - 'rawrow' => 1, - ]; + $preferences['srf-prefs-intro'] = [ + 'type' => 'info', + 'label' => ' ', + 'default' => wfMessage( 'srf-prefs-intro-text' )->parseAsBlock(), + 'section' => 'smw/srf', + 'raw' => 1, + ]; // Enable auto update during a page refresh $preferences['srf-prefs-eventcalendar-options-update-default'] = [ diff --git a/www/wiki/extensions/SemanticResultFormats/SemanticResultFormats.php b/www/wiki/extensions/SemanticResultFormats/SemanticResultFormats.php index 90d89f53..36a66555 100644 --- a/www/wiki/extensions/SemanticResultFormats/SemanticResultFormats.php +++ b/www/wiki/extensions/SemanticResultFormats/SemanticResultFormats.php @@ -109,10 +109,10 @@ class SemanticResultFormats { 'eventline' => 'SRFTimeline', 'vcard' => 'SRF\vCard\vCardFileExportPrinter', 'icalendar' => 'SRF\iCalendar\iCalendarFileExportPrinter', - 'bibtex' => 'SRFBibTeX', + 'bibtex' => 'SRF\BibTex\BibTexFileExportPrinter', 'calendar' => 'SRFCalendar', 'eventcalendar' => 'SRF\EventCalendar', - 'outline' => 'SRFOutline', + 'outline' => 'SRF\Outline\OutlineResultPrinter', 'sum' => 'SRFMath', 'product' => 'SRFMath', 'average' => 'SRFMath', @@ -125,7 +125,7 @@ class SemanticResultFormats { 'jitgraph' => 'SRFJitGraph', 'jqplotchart' => 'SRFjqPlotChart', 'jqplotseries' => 'SRFjqPlotSeries', - 'graph' => 'SRFGraph', + 'graph' => 'SRF\Graph\GraphPrinter', 'process' => 'SRFProcess', 'gallery' => 'SRF\Gallery', 'tagcloud' => 'SRF\TagCloud', @@ -147,8 +147,8 @@ class SemanticResultFormats { 'dygraphs' => 'SRFDygraphs', 'incoming' => 'SRFIncoming', 'media' => 'SRF\MediaPlayer', - 'excel' => 'SRF\SRFExcel', - 'datatables' => 'SRF\DataTables' + 'datatables' => 'SRF\DataTables', + 'gantt' => 'SRF\Gantt\GanttPrinter' ]; $formatAliases = [ @@ -161,6 +161,11 @@ class SemanticResultFormats { 'jqplotseries' => [ 'jqplot series' ], ]; + if ( class_exists( '\PhpOffice\PhpSpreadsheet\Spreadsheet' ) ) { + $formatClasses['spreadsheet'] = 'SRF\SpreadsheetPrinter'; + $formatAliases['spreadsheet'] = [ 'excel' ]; + } + foreach ( $GLOBALS['srfgFormats'] as $format ) { if ( array_key_exists( $format, $formatClasses ) ) { $GLOBALS['smwgResultFormats'][$format] = $formatClasses[$format]; diff --git a/www/wiki/extensions/SemanticResultFormats/build/travis/install-semantic-result-formats.sh b/www/wiki/extensions/SemanticResultFormats/build/travis/install-semantic-result-formats.sh index cc75d251..fb79ef4b 100644 --- a/www/wiki/extensions/SemanticResultFormats/build/travis/install-semantic-result-formats.sh +++ b/www/wiki/extensions/SemanticResultFormats/build/travis/install-semantic-result-formats.sh @@ -26,6 +26,12 @@ function installToMediaWikiRoot { installPHPUnitWithComposer installSMWWithComposer + + if [ "$MERMAID" != "" ] + then + composer require 'mediawiki/mermaid='$MERMAID --update-with-dependencies + fi + composer require mediawiki/semantic-result-formats "dev-master" cd extensions @@ -57,6 +63,11 @@ function updateConfiguration { # SMW#1732 echo 'wfLoadExtension( "SemanticMediaWiki" );' >> LocalSettings.php + if [ "$MERMAID" != "" ] + then + echo 'wfLoadExtension( "Mermaid" );' >> LocalSettings.php + fi + echo 'wfLoadExtension( "SemanticResultFormats" );' >> LocalSettings.php echo 'error_reporting(E_ALL| E_STRICT);' >> LocalSettings.php diff --git a/www/wiki/extensions/SemanticResultFormats/composer.json b/www/wiki/extensions/SemanticResultFormats/composer.json index 2c3ace08..abb877c5 100644 --- a/www/wiki/extensions/SemanticResultFormats/composer.json +++ b/www/wiki/extensions/SemanticResultFormats/composer.json @@ -44,14 +44,16 @@ "source": "https://github.com/SemanticMediaWiki/SemanticResultFormats" }, "require": { - "php": ">=5.6.0", + "php": ">=7.0", "composer/installers": "1.*,>=1.0.1", "mediawiki/semantic-media-wiki": "~3.0", "nicmart/tree": "^0.2.7", "data-values/geo": "~4.0|~3.0|~2.0", - "symfony/css-selector": "^3.3" + "symfony/css-selector": "^3.3", + "mediawiki/mermaid": "~2.1" }, "suggest": { + "phpoffice/phpspreadsheet": "Required for 'format=spreadsheet'", "phpoffice/phpexcel": "Required for 'format=excel'", "mediawiki/graph-viz": "Required for 'format=graph' and 'format=process'" }, @@ -71,13 +73,14 @@ }, "extra": { "branch-alias": { - "dev-master": "3.0.x-dev" + "dev-master": "3.1.x-dev" } }, "config": { "process-timeout": 0 }, "scripts":{ + "test": "php ../../tests/phpunit/phpunit.php -c phpunit.xml.dist", "phpunit": "php ../../tests/phpunit/phpunit.php -c phpunit.xml.dist", "integration": "composer phpunit -- --testsuite=semantic-result-formats-integration" } diff --git a/www/wiki/extensions/SemanticResultFormats/docs/COMPATIBILITY.md b/www/wiki/extensions/SemanticResultFormats/docs/COMPATIBILITY.md new file mode 100644 index 00000000..e340b7b2 --- /dev/null +++ b/www/wiki/extensions/SemanticResultFormats/docs/COMPATIBILITY.md @@ -0,0 +1,111 @@ +This document holds the **compatibility information** for the Semantic Result Formats (SRF) extension. + +- For a full list of changes in each release, see the [release notes](https://github.com/SemanticMediaWiki/SemanticResultFormats/blob/master/RELEASE-NOTES.md). +- For instructions on how to install the latest version, see the [installation instructions](https://github.com/SemanticMediaWiki/SemanticResultFormats/blob/master/docs/INSTALL.md). + +### Platform compatibility + +The PHP and MediaWiki version ranges listed are those in which SRF is known to work. It might also +work with more recent versions of PHP and MediaWiki, though this is not guaranteed. Increases of +minimum requirements are indicated in bold. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
SRFPHPMediaWikiSemantic MediaWikiRelease status
3.2.x7.1.0 - 7.4.x1.31 - 1.33+3.0.x - 3.1.x+Future release
3.1.x7.0.0 - 7.3.x1.31 - 1.333.0.x - 3.1.xStable release
3.0.x5.6.0 - 7.1.x1.27 - 1.333.0.xObsolete release, no support
2.5.x5.5.0 - 7.0.x1.23 - 1.292.1.x - 2.5.xObsolete release, no support
2.4.x5.3.2 - 7.0.x1.19 - 1.282.1.xObsolete release, no support
2.3.x5.3.2 - 5.6.x1.19 - 1.252.1.xObsolete release, no support
2.2.x5.3.2 - 5.6.x1.19 - 1.252.1.xObsolete release, no support
2.1.x5.3.2 - 5.6.x1.19 - 1.242.1.xObsolete release, no support
2.0.x5.3.2 - 5.5.x1.19 - 1.232.0.xObsolete release, no support
1.9.x5.3.2 - 5.5.x1.19 - 1.231.9.xObsolete release, no support
1.8.x5.2.0 - 5.5.x1.17 - 1.221.8.xObsolete release, no support
1.7.x5.2.0 - 5.4.x1.16 - 1.191.7.xObsolete release, no support
+ +**Note:** +* It is strongly recommended to also always upgrade the underlying MediaWiki software to supported versions. +See the [version lifecycle](https://www.mediawiki.org/wiki/Version_lifecycle) for current information on +supported versions. +* It is strongly recommended to also always upgrade the underlying Semantic MediaWiki software to supported +versions. See the page on [compatibility](https://www.semantic-mediawiki.org/wiki/Help:Compatibility) for +current information on supported versions. diff --git a/www/wiki/extensions/SemanticResultFormats/docs/INSTALL.md b/www/wiki/extensions/SemanticResultFormats/docs/INSTALL.md index 42fba85c..39087a47 100644 --- a/www/wiki/extensions/SemanticResultFormats/docs/INSTALL.md +++ b/www/wiki/extensions/SemanticResultFormats/docs/INSTALL.md @@ -1,161 +1,10 @@ -# Installation +This document holds the **installation and configuration instructions** for the [Semantic Result Formats](README.md) (SRF) extension. + +- For information on the release series, see the [version overview](https://github.com/SemanticMediaWiki/SemanticResultFormats/blob/master/VERSIONS.md). +- For a full list of changes in each release, see the [release notes](https://github.com/SemanticMediaWiki/SemanticResultFormats/blob/master/RELEASE-NOTES.md). +- For instructions on how to install the latest version, see the [installation instructions](https://github.com/SemanticMediaWiki/SemanticResultFormats/blob/master/docs/INSTALL.md). -These are the installation and configuration instructions for [Semantic Result Formats](README.md) (SRF). - -## Versions - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
StatusRelease dateGit branch
SRF 3.0.1Stable version2019-03-273.0.x
SRF 2.5.6Obsolete version2018-09-072.5.x
SRF 2.4.3Obsolete version2017-05-062.4.x
SRF 2.3.0Obsolete version2015-09-242.3
SRF 2.2.0Obsolete version2015-07-302.2
SRF 2.1.2Obsolete version2015-02-262.1.2
SRF 2.0.0Obsolete version2014-08-062.0
SRF 1.9.1Obsolete version2014-04-251.9.1
SRF 1.9.0Obsolete version2014-01-101.9
SRF 1.8.0Obsolete version2012-12-021.8
- -### Platform compatibility - -The PHP and MediaWiki version ranges listed are those in which SRF is known to work. It might also -work with more recent versions of PHP and MediaWiki, though this is not guaranteed. Increases of -minimum requirements are indicated in bold. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
PHPMediaWikiSemantic MediaWiki
SRF 3.0.x5.6.x - latest1.27 - latest3.0.x
SRF 2.5.x5.5.x - 7.0.x1.23 - 1.292.1.x - 2.5.x
SRF 2.4.x5.3.2 - 7.0.x1.19 - 1.282.1.x
SRF 2.3.x5.3.2 - 5.6.x1.19 - 1.252.1.x
SRF 2.2.x5.3.2 - 5.6.x1.19 - 1.252.1.x
SRF 2.1.x5.3.2 - 5.6.x1.19 - 1.242.1.x
SRF 2.0.x5.3.2 - 5.5.x1.19 - 1.232.0.x
SRF 1.9.x5.3.2 - 5.5.x1.19 - 1.231.9.x
SRF 1.8.x5.2.0 - 5.5.x1.17 - 1.221.8.x
SRF 1.7.x5.2.0 - 5.4.x1.16 - 1.191.7.x
- -**Note:** -* It is strongly recommended to also always upgrade the underlying MediaWiki software to supported versions. -See the [version lifecycle](https://www.mediawiki.org/wiki/Version_lifecycle) for current information on -supported versions. -* It is strongly recommended to also always upgrade the underlying Semantic MediaWiki software to supported -versions. See the page on [compatibility](https://www.semantic-mediawiki.org/wiki/Help:Compatibility) for -current information on supported versions. - -## Installation +# Installation The recommended way to install Semantic Result Formats is using [Composer](http://getcomposer.org) with [MediaWiki's built-in support for Composer](https://www.mediawiki.org/wiki/Composer). @@ -165,20 +14,13 @@ instructions provided. ### Step 1 -Change to the base directory of your MediaWiki installation. This is where the "LocalSettings.php" -file is located. If you have not yet installed Composer do it now by running the following command -in your shell: - - wget https://getcomposer.org/composer.phar - -### Step 2 - -If you do not have a "composer.local.json" file yet, create one and add the following content to it: +Change to the base directory of your MediaWiki installation. If you do not have a "composer.local.json" file yet, +create one and add the following content to it: ``` { "require": { - "mediawiki/semantic-result-formats": "~3.0" + "mediawiki/semantic-result-formats": "~3.1" } } ``` @@ -186,11 +28,11 @@ If you do not have a "composer.local.json" file yet, create one and add the foll If you already have a "composer.local.json" file add the following line to the end of the "require" section in your file: - "mediawiki/semantic-result-formats": "~3.0" + "mediawiki/semantic-result-formats": "~3.1" Remember to add a comma to the end of the preceding line in this section. -### Step 3 +### Step 2 Run the following command in your shell: @@ -199,17 +41,12 @@ Run the following command in your shell: Note if you have Git installed on your system add the `--prefer-source` flag to the above command. Also note that it may be necessary to run this command twice. If unsure do it twice right away. -### Step 4 +### Step 3 Add the following line to the end of your "LocalSettings.php" file: wfLoadExtension( 'SemanticResultFormats' ); -### Verify installation success - -As final step, you can verify SRF got installed by looking at the "Special:Version" page on your wiki and -check that it is listed in the semantic extensions section. - ## Configuration A default set of formats is enabled. These are the formats that satisfy the following criteria: diff --git a/www/wiki/extensions/SemanticResultFormats/docs/README.md b/www/wiki/extensions/SemanticResultFormats/docs/README.md index 2e35ccc0..898bcf8e 100644 --- a/www/wiki/extensions/SemanticResultFormats/docs/README.md +++ b/www/wiki/extensions/SemanticResultFormats/docs/README.md @@ -1,6 +1,9 @@ +## General documentation -* [Installation](INSTALL.md) -* [Extension:Semantic Result Formats](https://www.semantic-mediawiki.org/wiki/Extension:Semantic_Result_Formats) +* [Installation and configuration](INSTALL.md) +* [Release notes (changes in each version)](../RELEASE-NOTES.md) +* [Platform compatibility per version](COMPATIBILITY.md) +* [Homepage](https://www.semantic-mediawiki.org/wiki/Extension:Semantic_Result_Formats) ## Technical documentation diff --git a/www/wiki/extensions/SemanticResultFormats/extension.json b/www/wiki/extensions/SemanticResultFormats/extension.json index 66d24818..4af484ef 100644 --- a/www/wiki/extensions/SemanticResultFormats/extension.json +++ b/www/wiki/extensions/SemanticResultFormats/extension.json @@ -1,20 +1,23 @@ { "name": "SemanticResultFormats", - "version": "3.0.1", + "version": "3.1.0", "author": [ "James Hong Kong", "Stephan Gambke", - "Jeroen De Dauw", + "[https://www.entropywins.wtf/mediawiki Jeroen De Dauw]", "Yaron Koren", "..." ], - "url": "https://www.semantic-mediawiki.org/wiki/Extension:Semantic_Result_Formats", + "url": "https://www.semantic-mediawiki.org/wiki/Semantic_Result_Formats", "descriptionmsg": "srf-desc", "namemsg": "srf-name", "license-name": "GPL-2.0-or-later", "type": "semantic", "requires": { - "MediaWiki": ">= 1.27" + "MediaWiki": ">= 1.31", + "extensions": { + "SemanticMediaWiki": ">= 3.0" + } }, "MessagesDirs": { "SemanticResultFormats": [ @@ -26,5 +29,5 @@ "SemanticResultFormats::onExtensionFunction" ], "load_composer_autoloader":true, - "manifest_version": 1 + "manifest_version": 2 } diff --git a/www/wiki/extensions/SemanticResultFormats/formats/Gantt/GanttPrinter.php b/www/wiki/extensions/SemanticResultFormats/formats/Gantt/GanttPrinter.php new file mode 100644 index 00000000..9b59fdf0 --- /dev/null +++ b/www/wiki/extensions/SemanticResultFormats/formats/Gantt/GanttPrinter.php @@ -0,0 +1,284 @@ +text(); + } + + public function getParamDefinitions( array $definitions ) { + $params = parent::getParamDefinitions( $definitions ); + + $params[] = [ + 'type' => 'string', + 'name' => 'diagramtitle', + 'message' => 'srf-paramdesc-gantt-diagramtitle', + 'default' => '' + ]; + + $params[] = [ + 'type' => 'string', + 'name' => 'theme', + 'message' => 'srf-paramdesc-gantt-diagramtheme', + 'default' => 'default' + ]; + + $params[] = [ + 'type' => 'string', + 'name' => 'axisformat', + 'message' => 'srf-paramdesc-gantt-axisformat', + 'default' => '%m/%d/%Y' + ]; + + $params[] = [ + 'type' => 'string', + 'name' => 'statusmapping', + 'message' => 'srf-paramdesc-gantt-statusmapping', + 'default' => '' + ]; + + $params[] = [ + 'type' => 'string', + 'name' => 'prioritymapping', + 'message' => 'srf-paramdesc-gantt-prioritymapping', + 'default' => '' + ]; + + $params[] = [ + 'type' => 'integer', + 'name' => 'titletopmargin', + 'message' => 'srf-paramdesc-gantt-titletopmargin', + 'default' => 25 + ]; + + $params[] = [ + 'type' => 'integer', + 'name' => 'barheight', + 'message' => 'srf-paramdesc-gantt-barheight', + 'default' => 20 + ]; + + $params[] = [ + 'type' => 'integer', + 'name' => 'leftpadding', + 'message' => 'srf-paramdesc-gantt-leftpadding', + 'default' => 75 + ]; + + $params[] = [ + 'type' => 'integer', + 'name' => 'bargap', + 'message' => 'srf-paramdesc-gantt-bargap', + 'default' => 4 + ]; + + return $params; + } + + /** + * Handle (set) the result format parameters + * + * @see SMWResultPrinter::handleParameters() + */ + protected function handleParameters( array $params, $outputmode ) { + + //Set header params + $this->params['title'] = trim( $params['diagramtitle'] ); + $this->params['axisformat'] = trim( $params['axisformat'] ); + $this->params['statusmapping'] = $this->getValidatedMapping( $params[ 'statusmapping' ], 'statusmapping', [ 'active', 'done' ] ); + $this->params['prioritymapping'] = $this->getValidatedMapping( $params[ 'prioritymapping' ], 'prioritymapping', [ 'crit' ] ); + $this->params['theme'] = $this->getValidatedTheme($params['theme']); + + $this->mGantt = $this->getGantt(); + } + + /** + * Return serialised results in specified format. + * @param SMWQueryResult $queryResult + * @param $outputmode + * @return string + */ + protected function getResultText( SMWQueryResult $queryResult, $outputmode ) { + + // Show warning if Extension:Mermaid is not available + if ( !class_exists( 'Mermaid' ) && !class_exists( 'Mermaid\\MermaidParserFunction' ) ) { + $queryResult->addErrors( [wfMessage('')->text()] ); + return ''; + } + + // Load general Modules + SMWOutputs::requireResource( 'ext.srf.gantt' ); + + //Add Tasks & Sections + while ( $row = $queryResult->getNext() ) { + + $status = []; + $priority = []; + $startDate = ''; + $endDate = ''; + $taskID = ''; + $taskTitle = ''; + $sections = []; + + // Loop through all field of a row + foreach ( $row as $field ) { + + $fieldLabel = $field->getPrintRequest()->getLabel(); + + //get values + foreach ( $field->getContent() as $dataItem ) { + + switch ( $fieldLabel ) { + case 'section': + $sections[$dataItem->getTitle()->getPrefixedDBKey()] = $dataItem->getSortKey(); + break; + case 'task': + if ( $dataItem instanceof SMWDIBlob ) { + $taskTitle = $dataItem->getString(); + $taskID = $field->getResultSubject()->getTitle()->getPrefixedDBKey(); + } + break; + case 'startdate': + if ( $dataItem instanceof SMWDITime ) { + $startDate = $dataItem->getMwTimestamp(); + } + break; + case 'enddate': + if ( $dataItem instanceof SMWDITime ) { + $endDate = $dataItem->getMwTimestamp(); + } + break; + case 'status': + if ( $dataItem instanceof SMWDIBlob ) { + $status[] = $dataItem->getString(); + } + break; + case 'priority': + if ( $dataItem instanceof SMWDIBlob ) { + $priority[] = $dataItem->getString(); + } + break; + } + } + } + + // Add section/Task + // Title, TaskID, StartDate and EndDate are required + if ( $taskID !== '' && $taskTitle !== '' && $startDate !== '' && $endDate !== '' ) { + $this->mGantt->addTask( $taskID, $taskTitle, $status, $priority, $startDate, $endDate ); + + // If no section was found, put task into a dummy section object + // "gantt-no-section#21780240" is used to identify Tasks that with no section (dummy section) + if ( count( $sections ) == 0 ) { + $this->mGantt->addSection( 'gantt-no-section#21780240', '', $startDate, $endDate, $taskID ); + } else { + foreach ( $sections as $sectionID => $sectionTitle ) { + $this->mGantt->addSection( $sectionID, $sectionTitle, $startDate, $endDate, $taskID ); + } + } + } + } + + // Improve unique id by adding a random number + $id = uniqid( 'srf-gantt-' . rand( 1, 10000 ) ); + + // Add gantt configurations + $config = [ + 'theme' => $this->params['theme'], + 'gantt' => [ + 'leftPadding' => intval( $this->params['leftpadding'] ), + 'titleTopMargin' => intval( $this->params['titletopmargin'] ), + 'barHeight' => intval( $this->params['barheight'] ), + 'barGap' => intval( $this->params['bargap'] ) + ] + ]; + + + // Manage Output + if ( !empty( $this->mErrors ) ) { + return $queryResult->addErrors( $this->mErrors ); + } else { + return Html::rawElement( 'div', [ + 'id' => $id, + 'class' => 'srf-gantt', + 'data-mermaid' => html_entity_decode( json_encode( [ + 'content' => $this->mGantt->getGanttOutput(), + 'config' => $config + ])) + ], Html::rawElement( 'div', [ 'class' => 'mermaid-dots' ])); + } + } + + private function getGantt(){ + return new Gantt( $this->params ); + } + + /** + * Return valid theme as string + * @param String $theme + * + * @return string + */ + private function getValidatedTheme( $theme ) { + $theme = trim( $theme ); + + if ( !in_array( $this->params['theme'], [ 'default', 'neutral', 'dark', 'forest' ] ) ) { + $this->mErrors[] = wfMessage( 'srf-error-gantt-theme' )->text(); + } + + return $theme; + } + + + private function getValidatedMapping( $params, $mappingType, array $mappingKeys){ + //Validate mapping + $mapping = []; + + if ( !empty( $params ) ) { + $paramMapping = explode( ';', trim( $params ) ); + + foreach ( $paramMapping as $pm ) { + $pmKeyVal = explode( '=>', $pm, 2); + + // if no key value pair + if ( count( $pmKeyVal ) !== 2 ) { + $this->mErrors[] = wfMessage( 'srf-error-gantt-mapping-assignment', $mappingType )->text(); + } else { + $mapping[trim( $pmKeyVal[0] )] = trim( $pmKeyVal[1] ); + + if(!in_array(trim( $pmKeyVal[1] ), $mappingKeys)){ + $this->mErrors[] = wfMessage( 'srf-error-gantt-mapping-keywords' )->text(); + } + } + } + return $mapping; + } + return ''; + } +} \ No newline at end of file diff --git a/www/wiki/extensions/SemanticResultFormats/formats/Gantt/resources/ext.gantt.js b/www/wiki/extensions/SemanticResultFormats/formats/Gantt/resources/ext.gantt.js new file mode 100644 index 00000000..77067319 --- /dev/null +++ b/www/wiki/extensions/SemanticResultFormats/formats/Gantt/resources/ext.gantt.js @@ -0,0 +1,81 @@ +/*! + * @file + * @ingroup SMW + * + * @licence GNU GPL v2+ + * @author Sebastian Schmid + */ + +( function ($, mw) { + + 'use strict'; + + mw.loader.using(['mediawiki.api', 'ext.mermaid']).then(function () { + + $(document).ready(function () { + + $('.srf-gantt').each(function () { + + var id = $(this).attr('id') + '-diagram'; + var data = $(this).data('mermaid'); + + $(this).find('.mermaid-dots').hide(); + $(this).append('
' + data.content + '
'); + mermaid.initialize(data.config); + mermaid.init(undefined, $("#" + id)); + + // replace 35 with # Tag + $(".srf-gantt svg text:contains('35')").each(function () { + var text = $(this).text().replace('35', '#'); + $(this).text(text); + }); + + // word wrapping in + $('.srf-gantt text.sectionTitle').each(function(index, value){ + forceTextWrappingOn( this, data.config.gantt.leftPadding); + }); + }); + }); + }); + + /* + * Credits + * Project: https://gist.github.com/jkappers/3892971 + * Author jkappers (Joshua Kappers) + */ + function forceTextWrappingOn(node, width) { + var svgns = "http://www.w3.org/2000/svg"; + + if(node.firstChild != null){ + var chars = node.firstChild.nodeValue.split(' '), + x = parseInt(node.getAttribute('x'), 10), + y = parseInt(node.getAttribute('y'), 10), + nodeBB = node.getBBox(), + index = 0, + tspan, tspanWidth, textNode; + + node.removeChild(node.firstChild); + + for (var c in chars) { + if (chars.hasOwnProperty(c)) { + tspanWidth = tspan == null ? 0 : tspan.getComputedTextLength(); + if (tspanWidth > width || tspanWidth === 0) { + if(index !== 0){ + y = y + nodeBB.height; + } + tspan = document.createElementNS(svgns, 'tspan'); + tspan.setAttribute('x', x); + tspan.setAttribute('y', y); + node.appendChild(tspan); + index = 0; + } + + textNode = document.createTextNode(index === 0 ? chars[c] : " " + chars[c]); + tspan.appendChild(textNode); + index++; + } + } + } + } + +}(jQuery, mediaWiki) ); diff --git a/www/wiki/extensions/SemanticResultFormats/formats/Gantt/src/Gantt.php b/www/wiki/extensions/SemanticResultFormats/formats/Gantt/src/Gantt.php new file mode 100644 index 00000000..28e6f95f --- /dev/null +++ b/www/wiki/extensions/SemanticResultFormats/formats/Gantt/src/Gantt.php @@ -0,0 +1,203 @@ +setTitle( $headerParam['title'] ); + $this->setAxisFormat( $headerParam['axisformat'] ); + $this->setStatusMapping( $headerParam['statusmapping'] ); + $this->setPriorityMapping( $headerParam['prioritymapping'] ); + } + + private function setPriorityMapping( $priorityMapping ) { + $this->mPriorityMapping = $priorityMapping; + } + + private function getPriorityMapping() { + return $this->mPriorityMapping; + } + + private function setStatusMapping( $statusMapping ) { + $this->mStatusMapping = $statusMapping; + } + + private function getStatusMapping() { + return $this->mStatusMapping; + } + + private function setAxisFormat( $axisFormat ) { + $this->mAxisFormat = $axisFormat; + } + + private function getAxisFormat() { + return $this->mAxisFormat; + } + + private function setTitle( $title ) { + $this->mTitle = $title; + } + + private function getTitle() { + return $this->mTitle; + } + + private function getSections() { + return $this->mSections; + } + + private function getTasks() { + return $this->mTasks; + } + + /** + * Adds a new Task to array + * + * @param string $taskID + * @param string $taskTitle + * @param array $status + * @param array $priority + * @param string $startDate + * @param string $endDate + * + */ + + public function addTask( $taskID, $taskTitle, $status, $priority, $startDate, $endDate ) { + $task = new GanttTask(); + $task->setID( $taskID ); + $task->setTitle( $taskTitle ); + $task->setTaskParam( $status, $this->getStatusMapping(), 'status' ); + $task->setTaskParam( $priority, $this->getPriorityMapping(), 'priority' ); + $task->setStartDate( $startDate ); + $task->setEndDate( $endDate ); + $this->mTasks[$taskID] = $task; + } + + + /** + * Creats a new Section with related tasks + * + * @param string $sectionID + * @param string $sectionTitle + * @param string $startDate + * @param string $endDate + * @param string $taskID + * + */ + public function addSection( $sectionID, $sectionTitle, $startDate, $endDate, $taskID ) { + + $sections = $this->getSections(); + + if ( array_key_exists( $sectionID, $sections ) ) { + + if ( $sections[$sectionID]->getEarliestStartDate() > $startDate ) { + $sections[$sectionID]->setEarliestStartDate( $startDate ); + } + if ( $sections[$sectionID]->getLatestEndDate() < $endDate ) { + $sections[$sectionID]->setLatestEndDate( $endDate ); + } + $sections[$sectionID]->addTask( $taskID ); + + } else { + $this->createNewSection( $sectionID, $sectionTitle, $startDate, $endDate, $taskID ); + } + } + + private function createNewSection( $sectionID, $sectionTitle, $startDate, $endDate, $taskID ) { + $ganttSection = new GanttSection(); + //check if the id in the object is realy needed or is it enough to have it as array key + $ganttSection->setID( $sectionID ); + $ganttSection->setTitle( $sectionTitle ); + $ganttSection->setEarliestStartDate( $startDate ); + $ganttSection->setLatestEndDate( $endDate ); + $ganttSection->addTask( $taskID ); + + $this->mSections[$sectionID] = $ganttSection; + } + + /** + * Creates output for mermaidjs + * + * @return string + */ + public function getGanttOutput() { + + $sections = $this->getSections(); + $tasks = $this->getTasks(); + + /* + * Bring the "section" with no title to the first position. + * This "section" is the one that hold tasks without any section. + * If we don't display it at the beginning we have to put them into a dummy section + */ + foreach ( $sections as $key => $section ) { + if ( $section->getTitle() === '' ) { + $noSection = $section; + unset( $sections[$key] ); + } + } + // push now the dummy task to the first place of the array + if ( isset( $noSection ) ) { + array_unshift( $sections, $noSection ); + } + + $title = $this->getTitle(); + $axisFormat = $this->getAxisFormat(); + + $mermaidOut = "gantt\n"; + $mermaidOut .= "dateFormat YYYY-MM-DD\n"; + $mermaidOut .= ( !empty( $title ) ) ? "title $title\n" : ''; + $mermaidOut .= "axisFormat $axisFormat\n"; + + // Output section and all related Issues + foreach ( $sections as $section ) { + if ( $section->getTitle() !== "" ) { + $mermaidOut .= 'section ' . $section->getTitle() . "\n"; + } + + //loop through related section tasks + foreach ( $section->getTasks() as $sectionTask ) { + + $status = $tasks[$sectionTask]->getStatus(); + + // Get Date from timestamp + $date = date_create(); + date_timestamp_set( $date, $tasks[$sectionTask]->getStartDate() ); + $startDate = date_format( $date, 'Y-m-d' ) . ', '; + date_timestamp_set( $date, $tasks[$sectionTask]->getEndDate() ); + $endDate = date_format( $date, 'Y-m-d' ); + + //get Priority + $priority = $tasks[$sectionTask]->getPriority(); + + $mermaidOut .= $tasks[$sectionTask]->getTitle() . "\t :" . $priority . $status . $startDate . $endDate . + "\n"; + } + } + + //Hashtags mark a Comment in Mermaid, so we need to replace it with 35 to replace it again after rendering + $mermaidOut = str_replace( '#', '35', $mermaidOut ); + + return $mermaidOut; + } +} \ No newline at end of file diff --git a/www/wiki/extensions/SemanticResultFormats/formats/Gantt/src/GanttSection.php b/www/wiki/extensions/SemanticResultFormats/formats/Gantt/src/GanttSection.php new file mode 100644 index 00000000..a7938117 --- /dev/null +++ b/www/wiki/extensions/SemanticResultFormats/formats/Gantt/src/GanttSection.php @@ -0,0 +1,66 @@ +mTitle = $title; + } + + public function getTitle() { + return $this->mTitle; + } + + public function setID( $id ) { + $this->mID = $id; + } + + public function getID() { + return $this->mID; + } + + public function setEarliestStartDate( $earliestStartDate ) { + $this->mEarliestStartDate = $earliestStartDate; + } + + public function getEarliestStartDate() { + return $this->mEarliestStartDate; + } + + public function setLatestEndDate( $latestEndDate ) { + $this->mLatestEndDate = $latestEndDate; + } + + public function getLatestEndDate() { + return $this->mLatestEndDate; + } + + public function getTasks() { + return $this->mTasks; + } + + // If we reorder the tasks we need to reset it with the ordered tasks + public function setTasks( $tasks ) { + $this->mTasks = $tasks; + } + + public function addTask( $task ) { + $this->mTasks[] = $task; + } +} \ No newline at end of file diff --git a/www/wiki/extensions/SemanticResultFormats/formats/Gantt/src/GanttTask.php b/www/wiki/extensions/SemanticResultFormats/formats/Gantt/src/GanttTask.php new file mode 100644 index 00000000..964f493a --- /dev/null +++ b/www/wiki/extensions/SemanticResultFormats/formats/Gantt/src/GanttTask.php @@ -0,0 +1,99 @@ +mTitle = $title; + } + + public function getTitle() { + return $this->mTitle; + } + + public function setID( $id ) { + $this->mID = $id; + } + + public function getID() { + return $this->mID; + } + + public function setStatus($status){ + $this->mStatus = $this->mStatus . $status . ', '; + } + + public function setPriority($priority){ + $this->mPriority = $this->mPriority . $priority . ', '; + } + + /** + * Either set the status or priority of the task + * + * @param array $params + * @param string $paramMapping + * @param string $type + * + */ + public function setTaskParam( $params, $paramMapping, $type ) { + + // skip if $paramMapping is empty and + // output errormessage if wrong mapping + if ( !empty( $paramMapping ) ) { + + foreach ( $paramMapping as $pKey => $pVal ) { + if ( in_array( $pKey, $params ) ) { + if ( $type === 'status' ) { + $this->setStatus( trim( $pVal ) ); + } + if ( $type === 'priority' ) { + $this->setPriority( trim( $pVal ) ); + } + } + } + } + } + + public function getStatus() { + return $this->mStatus; + } + + public function getPriority() { + return $this->mPriority; + } + + public function setStartDate( $startDate ) { + $this->mStartDate = $startDate; + } + + public function getStartDate() { + return $this->mStartDate; + } + + public function setEndDate( $endDate ) { + $this->mEndDate = $endDate; + } + + public function getEndDate() { + return $this->mEndDate; + } +} \ No newline at end of file diff --git a/www/wiki/extensions/SemanticResultFormats/formats/bibtex/README b/www/wiki/extensions/SemanticResultFormats/formats/bibtex/README deleted file mode 100644 index e911d149..00000000 --- a/www/wiki/extensions/SemanticResultFormats/formats/bibtex/README +++ /dev/null @@ -1,88 +0,0 @@ -Info is copied from http://en.wikipedia.org/wiki/Bibtex - -# address: Publisher's address (usually just the city, but can be the full address for lesser-known publishers) -# annote: An annotation for annotated bibliography styles (not typical) -# author: The name(s) of the author(s) (in the case of more than one author, separated by and) -# booktitle: The title of the book, if only part of it is being cited -# chapter: The chapter number -# crossref: The key of the cross-referenced entry -# doi: The DOI number of the entry -# edition: The edition of a book, long form (such as "first" or "second") -# editor: The name(s) of the editor(s) -# eprint: A specification of an electronic publication, often a preprint or a technical report -# howpublished: How it was published, if the publishing method is nonstandard -# institution: The institution that was involved in the publishing, but not necessarily the publisher -# journal: The journal or magazine the work was published in -# key: A hidden field used for specifying or overriding the alphabetical order of entries (when the "author" and "editor" fields are missing). Note that this is very different from the key (mentioned just after this list) that is used to cite or cross-reference the entry. -# month: The month of publication (or, if unpublished, the month of creation) -# note: Miscellaneous extra information -# number: The "number" of a journal, magazine, or tech-report, if applicable. (Most publications have a "volume", but no "number" field.) -# organization: The conference sponsor -# pages: Page numbers, separated either by commas or double-hyphens. For books, the total number of pages. -# publisher: The publisher's name -# school: The school where the thesis was written -# series: The series of books the book was published in (e.g. "The Hardy Boys" or "Lecture Notes in Computer Science") -# title: The title of the work -# type: The type of tech-report, for example, "Research Note" -# url: The WWW address -# volume: The volume of a journal or multi-volume book -# year: The year of publication (or, if unpublished, the year of creation) - - - -article - An article from a journal or magazine. - Required fields: author, title, journal, year - Optional fields: volume, number, pages, month, note, key -book - A book with an explicit publisher. - Required fields: author/editor, title, publisher, year - Optional fields: volume, series, address, edition, month, note, key, pages -booklet - A work that is printed and bound, but without a named publisher or sponsoring institution. - Required fields: title - Optional fields: author, howpublished, address, month, year, note, key -conference - The same as inproceedings, included for Scribe compatibility. - Required fields: author, title, booktitle, year - Optional fields: editor, pages, organization, publisher, address, month, note, key -inbook - A part of a book, usually untitled. May be a chapter (or section or whatever) and/or a range of pages. - Required fields: author/editor, title, chapter/pages, publisher, year - Optional fields: volume, series, address, edition, month, note, key -incollection - A part of a book having its own title. - Required fields: author, title, booktitle, year - Optional fields: editor, pages, organization, publisher, address, month, note, key -inproceedings - An article in a conference proceedings. - Required fields: author, title, booktitle, year - Optional fields: editor, pages, organization, publisher, address, month, note, key -manual - Technical documentation. - Required fields: title - Optional fields: author, organization, address, edition, month, year, note, key -mastersthesis - A Master's thesis. - Required fields: author, title, school, year - Optional fields: address, month, note, key -misc - For use when nothing else fits. - Required fields: none - Optional fields: author, title, howpublished, month, year, note, key -phdthesis - A Ph.D. thesis. - Required fields: author, title, school, year - Optional fields: address, month, note, key -proceedings - The proceedings of a conference. - Required fields: title, year - Optional fields: editor, publisher, organization, address, month, note, key -techreport - A report published by a school or other institution, usually numbered within a series. - Required fields: author, title, institution, year - Optional fields: type, number, address, month, note, key -unpublished - A document having an author and title, but not formally published. - Required fields: author, title, note - Optional fields: month, year, key diff --git a/www/wiki/extensions/SemanticResultFormats/formats/bibtex/SRF_BibTeX.php b/www/wiki/extensions/SemanticResultFormats/formats/bibtex/SRF_BibTeX.php deleted file mode 100644 index 87d3bcdc..00000000 --- a/www/wiki/extensions/SemanticResultFormats/formats/bibtex/SRF_BibTeX.php +++ /dev/null @@ -1,444 +0,0 @@ -getSearchLabel( SMW_OUTPUT_WIKI ) != '' ) { - return str_replace( ' ', '_', $this->getSearchLabel( SMW_OUTPUT_WIKI ) ) . '.bib'; - } else { - return 'BibTeX.bib'; - } - } - - public function getQueryMode( $context ) { - return ( $context == SMWQueryProcessor::SPECIAL_PAGE ) ? SMWQuery::MODE_INSTANCES : SMWQuery::MODE_NONE; - } - - public function getName() { - return wfMessage( 'srf_printername_bibtex' )->text(); - } - - protected function getResultText( SMWQueryResult $res, $outputmode ) { - global $wgSitename; - $result = ''; - - if ( $outputmode == SMW_OUTPUT_FILE ) { // make file - if ( $this->m_title == '' ) { - $this->m_title = $wgSitename; - } - - $items = []; - - while ( $row = $res->getNext() ) { - $items[] = $this->getItemForResultRow( $row )->text(); - } - - $result = implode( '', $items ); - } else { // just make link to export - if ( $this->getSearchLabel( $outputmode ) ) { - $label = $this->getSearchLabel( $outputmode ); - } else { - $label = wfMessage( 'srf_bibtex_link' )->inContentLanguage()->text(); - } - - $link = $res->getQueryLink( $label ); - $link->setParameter( 'bibtex', 'format' ); - - if ( $this->getSearchLabel( SMW_OUTPUT_WIKI ) != '' ) { - $link->setParameter( $this->getSearchLabel( SMW_OUTPUT_WIKI ), 'searchlabel' ); - } - - $result .= $link->getText( $outputmode, $this->mLinker ); - $this->isHTML = ( $outputmode == SMW_OUTPUT_HTML ); // yes, our code can be viewed as HTML if requested, no more parsing needed - } - - return $result; - } - - /** - * Gets a SMWBibTeXEntry for the row. - * - * @since 1.6 - * - * @param $row array of SMWResultArray - * - * @return SMWBibTeXEntry - */ - protected function getItemForResultRow( array /* of SMWResultArray */ - $row ) { - $address = ''; - $annote = ''; - $author = ''; - $booktitle = ''; - $chapter = ''; - $crossref = ''; - $doi = ''; - $edition = ''; - $editor = ''; - $eprint = ''; - $howpublished = ''; - $institution = ''; - $journal = ''; - $key = ''; - $month = ''; - $note = ''; - $number = ''; - $organization = ''; - $pages = ''; - $publisher = ''; - $school = ''; - $series = ''; - $title = ''; - $type = ''; - $url = ''; - $volume = ''; - $year = ''; - - foreach ( $row as /* SMWResultArray */ - $field ) { - $req = $field->getPrintRequest(); - $label = strtolower( $req->getLabel() ); - $var = false; - - switch ( $label ) { - case 'type': - $var =& $type; - break; - case 'address': - $var =& $address; - break; - case 'annote': - $var =& $annote; - break; - case 'booktitle': - $var =& $booktitle; - break; - case 'chapter': - $var =& $chapter; - break; - case 'crossref': - $var =& $crossref; - break; - case 'doi': - $var =& $doi; - break; - case 'edition': - $var =& $edition; - break; - case 'eprint': - $var =& $eprint; - break; - case 'howpublished': - $var =& $howpublished; - break; - case 'institution': - $var =& $institution; - break; - case 'journal': - $var =& $journal; - break; - case 'key': - $var =& $key; - break; - case 'note': - $var =& $note; - break; - case 'number': - $var =& $number; - break; - case 'organization': - $var =& $organization; - break; - case 'pages': - $var =& $pages; - break; - case 'publisher': - $var =& $publisher; - break; - case 'school': - $var =& $school; - break; - case 'series': - $var =& $series; - break; - case 'title': - $var =& $title; - break; - case 'url': - $var =& $url; - break; - case 'year': - $var =& $year; - break; - case 'month': - $var =& $month; - break; - case 'volume': - case 'journal_volume': - $var =& $volume; - break; - } - - if ( $var !== false ) { - $dataValue = $field->getNextDataValue(); - - if ( $dataValue !== false ) { - $var = $dataValue->getShortWikiText(); - } - - unset( $var ); - } else { - switch ( $label ) { - case 'author': - case 'authors': - case 'editor' : - case 'editors': - $wikiTexts = []; - while ( ( /* SMWDataValue */ - $dataValue = $field->getNextDataValue() ) !== false ) { - $wikiTexts[] = $dataValue->getShortWikiText(); - } - $wikiText = $GLOBALS['wgLang']->listToText( $wikiTexts ); - - if ( $label == 'author' || $label == 'authors' ) { - $author = $wikiText; - } else { - $editor = $wikiText; - } - break; - case 'date': - $dataValue = $field->getNextDataValue(); - - if ( $dataValue !== false && get_class( $dataValue ) == 'SMWTimeValue' ) { - $year = $dataValue->getYear(); - $month = $dataValue->getMonth(); - } - break; - } - } - } - - return new SMWBibTeXEntry( - $type, - $address, - $annote, - $author, - $booktitle, - $chapter, - $crossref, - $doi, - $edition, - $editor, - $eprint, - $howpublished, - $institution, - $journal, - $key, - $month, - $note, - $number, - $organization, - $pages, - $publisher, - $school, - $series, - $title, - $url, - $volume, - $year - ); - } -} - -/** - * Represents a single entry in an BibTeX - * - * @ingroup SMWQuery - */ -class SMWBibTeXEntry { - - private $bibTeXtype; - private $URI; - private $fields = []; - - public function __construct( $type, $address, $annote, $author, $booktitle, $chapter, $crossref, $doi, $edition, $editor, $eprint, $howpublished, $institution, $journal, $key, $month, $note, $number, $organization, $pages, $publisher, $school, $series, $title, $url, $volume, $year ) { - if ( $type ) { - $this->bibTeXtype = ucfirst( $type ); - } else { - $this->bibTeXtype = 'Book'; - } - - $fields = []; - - if ( $address ) { - $fields['address'] = $address; - } - if ( $annote ) { - $fields['annote'] = $annote; - } - if ( $author ) { - $fields['author'] = $author; - } - if ( $booktitle ) { - $fields['booktitle'] = $booktitle; - } - if ( $chapter ) { - $fields['chapter'] = $chapter; - } - if ( $crossref ) { - $fields['crossref'] = $crossref; - } - if ( $doi ) { - $fields['doi'] = $doi; - } - if ( $edition ) { - $fields['edition'] = $edition; - } - if ( $editor ) { - $fields['editor'] = $editor; - } - if ( $eprint ) { - $fields['eprint'] = $eprint; - } - if ( $howpublished ) { - $fields['howpublished'] = $howpublished; - } - if ( $institution ) { - $fields['institution'] = $institution; - } - if ( $journal ) { - $fields['journal'] = $journal; - } - if ( $key ) { - $fields['key'] = $key; - } - if ( $month ) { - $fields['month'] = $month; - } - if ( $note ) { - $fields['note'] = $note; - } - if ( $number ) { - $fields['number'] = $number; - } - if ( $organization ) { - $fields['organization'] = $organization; - } - if ( $pages ) { - $fields['pages'] = $pages; - } - if ( $publisher ) { - $fields['publisher'] = $publisher; - } - if ( $school ) { - $fields['school'] = $school; - } - if ( $series ) { - $fields['series'] = $series; - } - if ( $title ) { - $fields['title'] = $title; - } - if ( $url ) { - $fields['url'] = $url; - } - if ( $volume ) { - $fields['volume'] = $volume; - } - if ( $year ) { - $fields['year'] = $year; - } - - $this->fields = $fields; - - // generating the URI: author last name + year + first letters of title - $URI = ''; - if ( $author ) { - $authors = explode( ',', $author ); - $authors = explode( wfMessage( 'and' )->text(), $authors[0] ); - $arrayAuthor = explode( ' ', $authors[0], 2 ); - $URI .= str_replace( ' ', '', $arrayAuthor[array_key_exists( 1, $arrayAuthor ) ? 1 : 0] ); - } - - if ( $year ) { - $URI .= $year; - } - - if ( $title ) { - foreach ( explode( ' ', $title ) as $titleWord ) { - $charsTitleWord = preg_split( '//', $titleWord, -1, PREG_SPLIT_NO_EMPTY ); - - if ( !empty( $charsTitleWord ) ) { - $URI .= $charsTitleWord[0]; - } - } - } - - $this->URI = strtolower( $URI ); - } - - /** - * Creates the BibTeX output for a single item. - */ - public function text() { - $text = '@' . $this->bibTeXtype . '{' . $this->URI . ",\r\n"; - - foreach ( $this->fields as $key => $value ) { - $text .= ' ' . $key . ' = "' . $value . '", ' . "\r\n"; - } - - $text .= "}\r\n\r\n"; - - return $text; - } -} diff --git a/www/wiki/extensions/SemanticResultFormats/formats/calendar/resources/ext.srf.formats.eventcalendar.js b/www/wiki/extensions/SemanticResultFormats/formats/calendar/resources/ext.srf.formats.eventcalendar.js index 0220031d..1867ad8f 100644 --- a/www/wiki/extensions/SemanticResultFormats/formats/calendar/resources/ext.srf.formats.eventcalendar.js +++ b/www/wiki/extensions/SemanticResultFormats/formats/calendar/resources/ext.srf.formats.eventcalendar.js @@ -1110,6 +1110,13 @@ } ); } + // Allow to fetch the `srf.eventcalendar.fullCalendarRefresh` and trigger a click + // event to restore the fullCalendar in case the instance was part of tab (hidden + // during initialization) + $( document ).on( 'srf.eventcalendar.fullCalendarRefresh', function( event ) { + context.find( '.srf-calendarbutton-refresh' ).trigger( 'click' ); + } ); + // console.log( 'Data', data, 'Objects', _calendar ); } ); diff --git a/www/wiki/extensions/SemanticResultFormats/formats/dygraphs/SRF_Dygraphs.php b/www/wiki/extensions/SemanticResultFormats/formats/dygraphs/SRF_Dygraphs.php index 28a91508..02ca3f1b 100644 --- a/www/wiki/extensions/SemanticResultFormats/formats/dygraphs/SRF_Dygraphs.php +++ b/www/wiki/extensions/SemanticResultFormats/formats/dygraphs/SRF_Dygraphs.php @@ -90,27 +90,29 @@ class SRFDygraphs extends SMWResultPrinter { while ( ( $dataValue = $field->getNextDataValue() ) !== false ) { // Data values // Jump the column (indicated by continue) because we don't want the data source being part of the annotation array - if ( $dataValue->getDataItem()->getDIType( - ) == SMWDataItem::TYPE_WIKIPAGE && $this->params['datasource'] === 'raw' && !$dataSource ) { + $dataItem = $dataValue->getDataItem(); + + if ( $dataItem->getDIType() === SMWDataItem::TYPE_WIKIPAGE ) { + $title = $dataItem->getTitle(); + } + + if ( $dataItem->getDIType() == SMWDataItem::TYPE_WIKIPAGE && $this->params['datasource'] === 'raw' && !$dataSource ) { // Support data source = raw which pulls the url from a wikipage in raw format $aggregatedValues['subject'] = $this->makePageFromTitle( - $dataValue->getTitle() + $title )->getLongHTMLText( $this->getLinker( $field->getResultSubject() ) ); - $aggregatedValues['url'] = $dataValue->getTitle()->getLocalURL( 'action=raw' ); + $aggregatedValues['url'] = $title->getLocalURL( 'action=raw' ); $dataSource = true; continue; - } elseif ( $dataValue->getDataItem()->getDIType( - ) == SMWDataItem::TYPE_WIKIPAGE && $this->params['datasource'] === 'file' && $dataValue->getTitle( - )->getNamespace() === NS_FILE && !$dataSource ) { + } elseif ( $dataItem->getDIType() == SMWDataItem::TYPE_WIKIPAGE && $this->params['datasource'] === 'file' && $title->getNamespace() === NS_FILE && !$dataSource ) { // Support data source = file which pulls the url from a uploaded file $aggregatedValues['subject'] = $this->makePageFromTitle( - $dataValue->getTitle() + $title )->getLongHTMLText( $this->getLinker( $field->getResultSubject() ) ); - $aggregatedValues['url'] = wfFindFile( $dataValue->getTitle() )->getUrl(); + $aggregatedValues['url'] = wfFindFile( $title )->getUrl(); $dataSource = true; continue; - } elseif ( $dataValue->getDataItem()->getDIType( - ) == SMWDataItem::TYPE_URI && $this->params['datasource'] === 'url' && !$dataSource ) { + } elseif ( $dataItem->getDIType() == SMWDataItem::TYPE_URI && $this->params['datasource'] === 'url' && !$dataSource ) { // Support data source = url, pointing to an url data source $aggregatedValues['link'] = $dataValue->getShortHTMLText( $this->getLinker( false ) ); $aggregatedValues['url'] = $dataValue->getURL(); @@ -125,7 +127,7 @@ class SRFDygraphs extends SMWResultPrinter { // text -> A longer description of the annotation // @see http://dygraphs.com/annotations.html if ( in_array( $propertyLabel, [ 'series', 'x', 'shortText', 'text' ] ) ) { - if ( $dataValue->getDataItem()->getDIType() == SMWDataItem::TYPE_NUMBER ) { + if ( $dataItem->getDIType() == SMWDataItem::TYPE_NUMBER ) { // Set unit if available $dataValue->setOutputFormat( $this->params['unit'] ); // Check if unit is available @@ -325,4 +327,4 @@ class SRFDygraphs extends SMWResultPrinter { return $params; } -} \ No newline at end of file +} diff --git a/www/wiki/extensions/SemanticResultFormats/formats/excel/SRF_Excel.php b/www/wiki/extensions/SemanticResultFormats/formats/excel/SRF_Excel.php deleted file mode 100644 index 8cd0321b..00000000 --- a/www/wiki/extensions/SemanticResultFormats/formats/excel/SRF_Excel.php +++ /dev/null @@ -1,364 +0,0 @@ -params['filename'] ? $this->params['filename'] : round( microtime( true ) * 1000 ) . '.xls'; - } - - public function outputAsFile( SMWQueryResult $queryResult, array $params ) { - if ( $this->isPHPExcelInstalled() ) { - parent::outputAsFile( $queryResult, $params ); - } else { - header( 'Cache-Control: no-store, no-cache, must-revalidate' ); - echo $this->getResult( $queryResult, $params, SMW_OUTPUT_FILE ); - } - } - - /** - * @param $definitions \ParamProcessor\ParamDefinition[] - * - * @return array - */ - public function getParamDefinitions( array $definitions ) { - $params = parent::getParamDefinitions( $definitions ); - - $definitions['searchlabel']->setDefault( wfMessage( 'srf-excel-link' )->inContentLanguage()->text() ); - - $params['templatefile'] = [ - 'type' => 'string', - 'name' => 'templatefile', - 'default' => '', - 'message' => 'srf-paramdesc-excel-templatefile', - ]; - - $params['filename'] = [ - 'type' => 'string', - 'name' => 'filename', - 'default' => '', - 'message' => 'srf-paramdesc-excel-filename', - ]; - - return $params; - } - - /** - * Return serialised results in specified format. - */ - protected function getResultText( SMWQueryResult $res, $outputMode ) { - - if ( $outputMode === SMW_OUTPUT_FILE ) { - return $this->getResultFileContents( $res ); - } - - $this->isHTML = ( $outputMode === SMW_OUTPUT_HTML ); - return $this->getLink( $res, $outputMode )->getText( $outputMode, $this->mLinker ); - } - - /* - * Turns the PHPExcel document object into a string - */ - /** - * @param $document - * - * @return string - */ - protected function writeDocumentToString( $document ) { - $objWriter = PHPExcel_IOFactory::createWriter( $document, 'Excel5' ); - - ob_start(); - $objWriter->save( 'php://output' ); - return ob_get_clean(); - } - - /** - * Populates the PHPExcel document with the query data - * - * @param $res SMWQueryResult the query result - */ - protected function populateDocumentWithQueryData( $res ) { - while ( $row = $res->getNext() ) { - $this->rowNum++; - $this->colNum = 0; - $this->readRowData( $row ); - } - } - - /** - * @param SMWQueryResult $res - * - * @return string - */ - protected function getResultFileContents( SMWQueryResult $res ) { - if ( !$this->isPHPExcelInstalled() ) { - throw new \RuntimeException( wfMessage( 'srf-excel-missing-phpexcel' )->parse() ); - } - - $document = $this->createExcelDocument(); - $this->sheet = $document->getSheet( 0 ); - - $this->rowNum = 0; - //Get headers - if ( $this->mShowHeaders ) { - $this->populateDocumentWithHeaders( $res ); - $this->rowNum++; - } - - //Get data rows - $this->populateDocumentWithQueryData( $res ); - - $document->getActiveSheet()->getDefaultRowDimension()->setRowHeight(); - - $result = $this->writeDocumentToString( $document ); - return $result; - } - - /** - * Sets or appends a string value at the given col,row location - * - * If there already exists a value at a given col,row location, then - * convert the cell to a string and append the data value. Creating - * a list of comma separated entries. - * - * @param $object \SMWDataValue the raw data value object - */ - protected function setOrAppendStringDataValue( $object ) { - $type = PHPExcel_Cell_DataType::TYPE_STRING; - $value = $object->getWikiValue(); - $value = Sanitizer::decodeCharReferences( $value ); - $value = PHPExcel_Cell_DataType::checkString( $value ); - - $cell = $this->sheet->getCellByColumnAndRow( $this->colNum, $this->rowNum ); - $existingValue = $cell->getValue(); - if ( $existingValue ) { - $value = $existingValue . ', ' . $value; - } - $cell->setValueExplicit( $value, $type ); - } - - /** - * Sets a numeric value at the given col,row location - * - * @param $object \SMWDataValue the raw data value object - */ - protected function setNumberDataValue( $object ) { - $type = PHPExcel_Cell_DataType::TYPE_NUMERIC; - $value = $object->getDataItem()->getNumber(); - - $this->sheet->getCellByColumnAndRow( $this->colNum, $this->rowNum ) - ->setValueExplicit( $value, $type ); - } - - /** - * Sets a quantity value at the given col,row location - * - * @param $object \SMWDataValue the raw data value object - */ - protected function setQuantityDataValue( $object ) { - $type = PHPExcel_Cell_DataType::TYPE_NUMERIC; - $unit = $object->getUnit(); - $value = $object->getNumber(); - - $this->sheet->getCellByColumnAndRow( $this->colNum, $this->rowNum ) - ->setValueExplicit( $value, $type ); - - if ( !$this->styled ) { - $this->sheet->getStyleByColumnAndRow( $this->colNum, $this->rowNum ) - ->getNumberFormat() - ->setFormatCode( '0 "' . $unit . '"' ); - } - } - - /** - * Sets a date/time value at the given col,row location - * - * @param \SMWTimeValue $object the raw data value object - */ - protected function setTimeDataValue( \SMWTimeValue $object ) { - $type = PHPExcel_Cell_DataType::TYPE_NUMERIC; - $value = \PHPExcel_Shared_Date::stringToExcel( str_replace( 'T', ' ', $object->getISO8601Date() ) ); - - $this->sheet - ->getCellByColumnAndRow( $this->colNum, $this->rowNum ) - ->setValueExplicit( $value, $type ); - - if ( !$this->styled ) { - $this->sheet - ->getStyleByColumnAndRow( $this->colNum, $this->rowNum ) - ->getNumberFormat() - ->setFormatCode( \PHPExcel_Style_NumberFormat::FORMAT_DATE_DDMMYYYY ); - } - } - - /** - * Populates the PHPExcel sheet with the headers from the result query - * - * @param SMWQueryResult $res The query result - */ - protected function populateDocumentWithHeaders( SMWQueryResult $res ) { - $this->colNum = 0; - foreach ( $res->getPrintRequests() as $pr ) { - $header = $pr->getLabel(); - if ( $this->showLabel( $header ) ) { - $this->sheet->setCellValueByColumnAndRow( $this->colNum, self::HEADER_ROW_OFFSET, $header ) - ->getStyleByColumnAndRow( $this->colNum, self::HEADER_ROW_OFFSET ) - ->getFont() - ->setBold( true ); - $this->colNum++; - } - } - } - - /** - * Creates a new PHPExcel document and returns it - * - * @return PHPExcel - */ - protected function createExcelDocument() { - - $fileTitle = Title::newFromText( $this->params['templatefile'], NS_FILE ); - - if ( $fileTitle !== null && $fileTitle->exists() ) { - - $filePage = new ImagePage( $fileTitle, $this ); - - $virtualFile = $filePage->getDisplayedFile(); - $virtualFilePath = $virtualFile->getPath(); - - $localFile = $virtualFile->getRepo()->getLocalReference( $virtualFilePath ); - $localFilePath = $localFile->getPath(); - - $objPHPExcel = PHPExcel_IOFactory::load( $localFilePath ); - - $this->styled = true; - - } else { - - $objPHPExcel = new PHPExcel(); - - } - - // Set document properties - $objPHPExcel->getProperties()->setCreator( "SemanticMediaWiki PHPExcel Export" ); - - return $objPHPExcel; - } - - /** - * Check for the existence of the extra mainlabel. - * - * @param $label - * - * @return bool - */ - private function showLabel( $label ) { - return !( array_key_exists( "mainlabel", $this->params ) && $label === $this->params["mainlabel"] . '#' ); - } - - protected function readFieldValue( $field ) { - $valueCount = 0; - while ( ( $object = $field->getNextDataValue() ) !== false ) { - if ( $valueCount === 0 ) { - $this->setValueAccordingToType( $object ); - } else { - $this->setOrAppendStringDataValue( $object ); - } - $valueCount++; - } - } - - /** - * Checks the type of the value, and set's it in the sheet accordingly - * - * @param $object - */ - protected function setValueAccordingToType( $object ) { - //NOTE: must check against subclasses before superclasses - if ( $object instanceof \SMWQuantityValue ) { - $this->setQuantityDataValue( $object ); - } else { - if ( $object instanceof \SMWNumberValue ) { - $this->setNumberDataValue( $object ); - } else { - if ( $object instanceof \SMWTimeValue ) { - $this->setTimeDataValue( $object ); - } else { - $this->setOrAppendStringDataValue( $object ); - } - } - } - } - - /** - * @param $row - */ - protected function readRowData( $row ) { - foreach ( $row as $field ) { - if ( $this->showLabel( $field->getPrintRequest()->getLabel() ) ) { - $this->readFieldValue( $field ); - $this->colNum++; - } - } - } - - private function isPHPExcelInstalled() { - return class_exists( "PHPExcel" ); - } - -} - diff --git a/www/wiki/extensions/SemanticResultFormats/formats/filtered/src/ResultItem.php b/www/wiki/extensions/SemanticResultFormats/formats/filtered/src/ResultItem.php index 619d81f1..ab5c57ba 100644 --- a/www/wiki/extensions/SemanticResultFormats/formats/filtered/src/ResultItem.php +++ b/www/wiki/extensions/SemanticResultFormats/formats/filtered/src/ResultItem.php @@ -83,7 +83,7 @@ class ResultItem { $sorted[] = $dataItem->getSortKey(); } elseif ( $dataItem instanceof SMWDIWikiPage ) { $values[] = $dataValue->getShortWikiText(); - $sorted[] = $dataValue->getSortKey(); + $sorted[] = $dataItem->getSortKey(); } else { $values[] = $dataValue->getShortWikiText(); $sorted[] = $dataValue->getShortWikiText(); diff --git a/www/wiki/extensions/SemanticResultFormats/formats/filtered/src/View/CalendarView.php b/www/wiki/extensions/SemanticResultFormats/formats/filtered/src/View/CalendarView.php index c828e5a4..0ac5441b 100644 --- a/www/wiki/extensions/SemanticResultFormats/formats/filtered/src/View/CalendarView.php +++ b/www/wiki/extensions/SemanticResultFormats/formats/filtered/src/View/CalendarView.php @@ -75,7 +75,7 @@ class CalendarView extends View { // found specified column for title if ( $datavalue !== false ) { if ( $datavalue instanceof \SMWWikiPageValue ) { - $data['url'] = $datavalue->getTitle()->getLocalURL(); + $data['url'] = $datavalue->getDataItem()->getTitle()->getLocalURL(); } $data['title'] = $datavalue->getShortHTMLText(); } diff --git a/www/wiki/extensions/SemanticResultFormats/formats/filtered/src/View/MapView.php b/www/wiki/extensions/SemanticResultFormats/formats/filtered/src/View/MapView.php index aed54d8f..ceade356 100644 --- a/www/wiki/extensions/SemanticResultFormats/formats/filtered/src/View/MapView.php +++ b/www/wiki/extensions/SemanticResultFormats/formats/filtered/src/View/MapView.php @@ -4,7 +4,6 @@ namespace SRF\Filtered\View; use DataValues\Geo\Parsers\LatLongParser; use Exception; -use Message; use SMWPropertyValue; use SRF\Filtered\ResultItem; @@ -64,7 +63,7 @@ class MapView extends View { $value = $field->getNextDataItem(); } - } elseif ( class_exists( 'DataValues\Geo\Parsers\GeoCoordinateParser' ) ) { + } else { $coordParser = new LatLongParser(); while ( $value instanceof \SMWDataItem ) { @@ -78,11 +77,6 @@ class MapView extends View { } } - } else { - $this->getQueryPrinter()->addError( - Message::newFromKey( 'srf-filtered-map-geocoordinateparser-missing-error' )->inContentLanguage( - )->text() - ); } return [ 'positions' => $values, ]; @@ -162,7 +156,6 @@ class MapView extends View { 'name' => 'map view height', 'message' => 'srf-paramdesc-filtered-map-height', 'default' => 'auto', - // 'islist' => false, ]; $params['zoom'] = [ @@ -170,7 +163,6 @@ class MapView extends View { 'name' => 'map view zoom', 'message' => 'srf-paramdesc-filtered-map-zoom', 'default' => '', - // 'islist' => false, ]; $params['minZoom'] = [ @@ -178,7 +170,6 @@ class MapView extends View { 'name' => 'map view min zoom', 'message' => 'srf-paramdesc-filtered-map-min-zoom', 'default' => '', - // 'islist' => false, ]; $params['maxZoom'] = [ @@ -186,7 +177,6 @@ class MapView extends View { 'name' => 'map view max zoom', 'message' => 'srf-paramdesc-filtered-map-max-zoom', 'default' => '', - // 'islist' => false, ]; //markercluster @@ -195,7 +185,6 @@ class MapView extends View { 'name' => 'map view marker cluster', 'message' => 'srf-paramdesc-filtered-map-marker-cluster', 'default' => true, - // 'islist' => false, ]; $params['marker cluster max zoom'] = [ @@ -203,7 +192,6 @@ class MapView extends View { 'name' => 'map view marker cluster max zoom', 'message' => 'srf-paramdesc-filtered-map-marker-cluster-max-zoom', 'default' => '', - // 'islist' => false, ]; //clustermaxradius - maxClusterRadius: The maximum radius that a cluster will cover from the central marker (in pixels). Default 80. @@ -212,7 +200,6 @@ class MapView extends View { 'name' => 'map view marker cluster radius', 'message' => 'srf-paramdesc-filtered-map-marker-cluster-max-radius', 'default' => '', - // 'islist' => false, ]; //clusterzoomonclick - zoomToBoundsOnClick: When you click a cluster we zoom to its bounds. @@ -221,7 +208,6 @@ class MapView extends View { 'name' => 'map view marker cluster zoom on click', 'message' => 'srf-paramdesc-filtered-map-marker-cluster-zoom-on-click', 'default' => true, - // 'islist' => false, ]; self::$viewParams = $params; @@ -328,4 +314,4 @@ class MapView extends View { return $this->getMapProvider() === '' ? 'srf-filtered-map-provider-missing-error' : null; } -} \ No newline at end of file +} diff --git a/www/wiki/extensions/SemanticResultFormats/formats/filtered/src/View/TableView.php b/www/wiki/extensions/SemanticResultFormats/formats/filtered/src/View/TableView.php index c1f6b0b2..7f3c7664 100644 --- a/www/wiki/extensions/SemanticResultFormats/formats/filtered/src/View/TableView.php +++ b/www/wiki/extensions/SemanticResultFormats/formats/filtered/src/View/TableView.php @@ -86,12 +86,12 @@ class TableView extends View { private function getTableHeaders() { $headers = []; - /** - * Get first QueryResult and assign array members to variables - * - * @var \SRF\Filtered\ResultItem $queryResultValue - */ - list( , $queryResultValue ) = each( $this->getQueryResults() ); + $queryResults = $this->getQueryResults(); + $queryResultValue = reset( $queryResults ); + + if ( !is_a( $queryResultValue, ResultItem::class ) ) { + return ''; + } foreach ( $queryResultValue->getValue() as $field ) { $printRequest = $field->getPrintRequest(); diff --git a/www/wiki/extensions/SemanticResultFormats/formats/gallery/Gallery.php b/www/wiki/extensions/SemanticResultFormats/formats/gallery/Gallery.php index 18abb24c..0599c54f 100644 --- a/www/wiki/extensions/SemanticResultFormats/formats/gallery/Gallery.php +++ b/www/wiki/extensions/SemanticResultFormats/formats/gallery/Gallery.php @@ -216,7 +216,7 @@ class Gallery extends ResultPrinter { if ( $resultArray->getPrintRequest()->getLabel() == $imageProperty ) { while ( ( $dataValue = $resultArray->getNextDataValue() ) !== false ) { // Property values if ( $dataValue->getTypeID() == '_wpg' ) { - $images[] = $dataValue->getTitle(); + $images[] = $dataValue->getDataItem()->getTitle(); } } } elseif ( $label == $captionProperty ) { @@ -269,10 +269,13 @@ class Gallery extends ResultPrinter { * @var \SMWResultArray $firstField */ $firstField = $row[0]; + + /** @var \SMWDataValue $nextObject */ $nextObject = $firstField->getNextDataValue(); if ( $nextObject !== false ) { - $imgTitle = method_exists( $nextObject, 'getTitle' ) ? $nextObject->getTitle() : null; + $dataItem = $nextObject->getDataItem(); + $imgTitle = method_exists( $dataItem, 'getTitle' ) ? $dataItem->getTitle() : null; // Ensure the title belongs to the image namespace if ( $imgTitle instanceof Title && $imgTitle->getNamespace() === NS_FILE ) { diff --git a/www/wiki/extensions/SemanticResultFormats/formats/graphviz/SRF_Graph.php b/www/wiki/extensions/SemanticResultFormats/formats/graphviz/SRF_Graph.php deleted file mode 100644 index 6d4aa786..00000000 --- a/www/wiki/extensions/SemanticResultFormats/formats/graphviz/SRF_Graph.php +++ /dev/null @@ -1,385 +0,0 @@ - - */ -class SRFGraph extends SMWResultPrinter { - - public static $NODE_SHAPES = [ - 'box', - 'box3d', - 'circle', - 'component', - 'diamond', - 'doublecircle', - 'doubleoctagon', - 'egg', - 'ellipse', - 'folder', - 'hexagon', - 'house', - 'invhouse', - 'invtrapezium', - 'invtriangle', - 'Mcircle', - 'Mdiamond', - 'Msquare', - 'none', - 'note', - 'octagon', - 'parallelogram', - 'pentagon ', - 'plaintext', - 'point', - 'polygon', - 'rect', - 'rectangle', - 'septagon', - 'square', - 'tab', - 'trapezium', - 'triangle', - 'tripleoctagon', - ]; - - protected $m_graphName; - protected $m_graphLabel; - protected $m_graphColor; - protected $m_graphLegend; - protected $m_graphLink; - protected $m_rankdir; - protected $m_graphSize; - protected $m_labelArray = []; - protected $m_graphColors = [ - 'black', - 'red', - 'green', - 'blue', - 'darkviolet', - 'gold', - 'deeppink', - 'brown', - 'bisque', - 'darkgreen', - 'yellow', - 'darkblue', - 'magenta', - 'steelblue2' ]; - protected $m_nameProperty; - protected $m_nodeShape; - protected $m_parentRelation; - protected $m_wordWrapLimit; - - /** - * (non-PHPdoc) - * @see SMWResultPrinter::handleParameters() - */ - protected function handleParameters( array $params, $outputmode ) { - parent::handleParameters( $params, $outputmode ); - - $this->m_graphName = trim( $params['graphname'] ); - $this->m_graphSize = trim( $params['graphsize'] ); - - $this->m_graphLegend = $params['graphlegend']; - $this->m_graphLabel = $params['graphlabel']; - - $this->m_rankdir = strtoupper( trim( $params['arrowdirection'] ) ); - - $this->m_graphLink = $params['graphlink']; - $this->m_graphColor = $params['graphcolor']; - - $this->m_nameProperty = $params['nameproperty'] === false ? false : trim( $params['nameproperty'] ); - - $this->m_parentRelation = strtolower( trim( $params['relation'] ) ) == 'parent'; - - $this->m_nodeShape = $params['nodeshape']; - $this->m_wordWrapLimit = $params['wordwraplimit']; - } - - protected function getResultText( SMWQueryResult $res, $outputmode ) { - - if ( !class_exists( 'GraphViz' ) - && !class_exists( '\\MediaWiki\\Extension\\GraphViz\\GraphViz' ) - ) { - wfWarn( 'The SRF Graph printer needs the GraphViz extension to be installed.' ); - return ''; - } - - $this->isHTML = true; - - $graphInput = "digraph $this->m_graphName {"; - if ( $this->m_graphSize != '' ) { - $graphInput .= "size=\"$this->m_graphSize\";"; - } - if ( $this->m_nodeShape ) { - $graphInput .= "node [shape=$this->m_nodeShape];"; - } - $graphInput .= "rankdir=$this->m_rankdir;"; - - while ( $row = $res->getNext() ) { - $graphInput .= $this->getGVForItem( $row, $outputmode ); - } - - $graphInput .= "}"; - - // Calls graphvizParserHook function from MediaWiki GraphViz extension - $result = $GLOBALS['wgParser']->recursiveTagParse( "$graphInput" ); - - if ( $this->m_graphLegend && $this->m_graphColor ) { - $arrayCount = 0; - $arraySize = count( $this->m_graphColors ); - $result .= "

"; - - foreach ( $this->m_labelArray as $m_label ) { - if ( $arrayCount >= $arraySize ) { - $arrayCount = 0; - } - - $color = $this->m_graphColors[$arrayCount]; - $result .= "$color: $m_label
"; - - $arrayCount += 1; - } - - $result .= "

"; - } - - return $result; - } - - /** - * Returns the GV for a single subject. - * - * @since 1.5.4 - * - * @param array $row - * @param $outputmode - * - * @return string - */ - protected function getGVForItem( array /* of SMWResultArray */ - $row, $outputmode ) { - $segments = []; - - // Loop throught all fields of the record. - foreach ( $row as $i => $resultArray ) { - - // Loop throught all the parts of the field value. - while ( ( $object = $resultArray->getNextDataValue() ) !== false ) { - $propName = $resultArray->getPrintRequest()->getLabel(); - $isName = $this->m_nameProperty ? ( $i != 0 && $this->m_nameProperty === $propName ) : $i == 0; - - if ( $isName ) { - $name = $this->getWordWrappedText( $object->getShortText( $outputmode ), $this->m_wordWrapLimit ); - } - - if ( !( $this->m_nameProperty && $i == 0 ) ) { - $segments[] = $this->getGVForDataValue( $object, $outputmode, $isName, $name, $propName ); - } - } - } - - return implode( "\n", $segments ); - } - - /** - * Returns the GV for a single SMWDataValue. - * - * @since 1.5.4 - * - * @param SMWDataValue $object - * @param $outputmode - * @param boolean $isName Is this the name that should be used for the node? - * @param string $name - * @param string $labelName - * - * @return string - */ - protected function getGVForDataValue( SMWDataValue $object, $outputmode, $isName, $name, $labelName ) { - $graphInput = ''; - $text = $object->getShortText( $outputmode ); - - if ( $this->m_graphLink ) { - $nodeLinkURL = "[[" . $text . "]]"; - } - - $text = $this->getWordWrappedText( $text, $this->m_wordWrapLimit ); - - if ( $this->m_graphLink ) { - $graphInput .= " \"$text\" [URL = \"$nodeLinkURL\"]; "; - } - - if ( !$isName ) { - $graphInput .= $this->m_parentRelation ? " \"$text\" -> \"$name\" " : " \"$name\" -> \"$text\" "; - - if ( $this->m_graphLabel || $this->m_graphColor ) { - $graphInput .= ' ['; - - if ( array_search( $labelName, $this->m_labelArray, true ) === false ) { - $this->m_labelArray[] = $labelName; - } - - $color = $this->m_graphColors[array_search( $labelName, $this->m_labelArray, true )]; - - if ( $this->m_graphLabel ) { - $graphInput .= "label=\"$labelName\""; - if ( $this->m_graphColor ) { - $graphInput .= ",fontcolor=$color,"; - } - } - - if ( $this->m_graphColor ) { - $graphInput .= "color=$color"; - } - - $graphInput .= ']'; - - } - - $graphInput .= ';'; - } - - return $graphInput; - } - - /** - * Returns the word wrapped version of the provided text. - * - * @since 1.5.4 - * - * @param string $text - * @param integer $charLimit - * - * @return string - */ - protected function getWordWrappedText( $text, $charLimit ) { - $charLimit = max( [ $charLimit, 1 ] ); - $segments = []; - - while ( strlen( $text ) > $charLimit ) { - // Find the last space in the allowed range. - $splitPosition = strrpos( substr( $text, 0, $charLimit ), ' ' ); - - if ( $splitPosition === false ) { - // If there is no space (lond word), find the next space. - $splitPosition = strpos( $text, ' ' ); - - if ( $splitPosition === false ) { - // If there are no spaces, everything goes on one line. - $splitPosition = strlen( $text ) - 1; - } - } - - $segments[] = substr( $text, 0, $splitPosition + 1 ); - $text = substr( $text, $splitPosition + 1 ); - } - - $segments[] = $text; - - return implode( '\n', $segments ); - } - - /** - * (non-PHPdoc) - * @see SMWResultPrinter::getName() - */ - public function getName() { - return wfMessage( 'srf-printername-graph' )->text(); - } - - /** - * @see SMWResultPrinter::getParamDefinitions - * - * @since 1.8 - * - * @param $definitions array of IParamDefinition - * - * @return array of IParamDefinition|array - */ - public function getParamDefinitions( array $definitions ) { - $params = parent::getParamDefinitions( $definitions ); - - $params['graphname'] = [ - 'default' => 'QueryResult', - 'message' => 'srf-paramdesc-graphname', - ]; - - $params['graphsize'] = [ - 'type' => 'string', - 'default' => '', - 'message' => 'srf-paramdesc-graphsize', - 'manipulatedefault' => false, - ]; - - $params['graphlegend'] = [ - 'type' => 'boolean', - 'default' => false, - 'message' => 'srf-paramdesc-graphlegend', - ]; - - $params['graphlabel'] = [ - 'type' => 'boolean', - 'default' => false, - 'message' => 'srf-paramdesc-graphlabel', - ]; - - $params['graphlink'] = [ - 'type' => 'boolean', - 'default' => false, - 'message' => 'srf-paramdesc-graphlink', - ]; - - $params['graphcolor'] = [ - 'type' => 'boolean', - 'default' => false, - 'message' => 'srf-paramdesc-graphcolor', - ]; - - $params['arrowdirection'] = [ - 'aliases' => 'rankdir', - 'default' => 'LR', - 'message' => 'srf-paramdesc-rankdir', - 'values' => [ 'LR', 'RL', 'TB', 'BT' ], - ]; - - $params['nodeshape'] = [ - 'default' => false, - 'message' => 'srf-paramdesc-graph-nodeshape', - 'manipulatedefault' => false, - 'values' => self::$NODE_SHAPES, - ]; - - $params['relation'] = [ - 'default' => 'child', - 'message' => 'srf-paramdesc-graph-relation', - 'manipulatedefault' => false, - 'values' => [ 'parent', 'child' ], - ]; - - $params['nameproperty'] = [ - 'default' => false, - 'message' => 'srf-paramdesc-graph-nameprop', - 'manipulatedefault' => false, - ]; - - $params['wordwraplimit'] = [ - 'type' => 'integer', - 'default' => 25, - 'message' => 'srf-paramdesc-graph-wwl', - 'manipulatedefault' => false, - ]; - - return $params; - } - -} diff --git a/www/wiki/extensions/SemanticResultFormats/formats/media/MediaPlayer.php b/www/wiki/extensions/SemanticResultFormats/formats/media/MediaPlayer.php index 18f02046..613895d3 100644 --- a/www/wiki/extensions/SemanticResultFormats/formats/media/MediaPlayer.php +++ b/www/wiki/extensions/SemanticResultFormats/formats/media/MediaPlayer.php @@ -139,7 +139,6 @@ class MediaPlayer extends ResultPrinter { // Get other data value item details $value = $this->getDataValueItem( $propertyLabel, - $dataValue->getDataItem()->getDIType(), $dataValue, $mediaType, $mimeType, @@ -208,29 +207,37 @@ class MediaPlayer extends ResultPrinter { * * @return mixed */ - private function getDataValueItem( &$label, $type, SMWDataValue $dataValue, &$mediaType, &$mimeType, &$rowData ) { + private function getDataValueItem( &$label, SMWDataValue $dataValue, &$mediaType, &$mimeType, &$rowData ) { - if ( $type == SMWDataItem::TYPE_WIKIPAGE && $dataValue->getTitle()->getNamespace() === NS_FILE ) { + $dataItem = $dataValue->getDataItem(); + $type = $dataItem->getDIType(); - if ( $label === 'source' && $mimeType === null ) { + if ( $type === SMWDataItem::TYPE_WIKIPAGE ) { - // Identify the media source - // and get media information - list( $mediaType, $mimeType, $source ) = $this->getMediaSource( $dataValue->getTitle() ); - $label = $mimeType; - return $source; - } elseif ( $label === 'poster' ) { - $mediaType = 'video'; + $title = $dataItem->getTitle(); - // Get the cover art image url - $source = wfFindFile( $dataValue->getTitle() ); - return $source->getUrl(); + if ( $title instanceof Title && $title->getNamespace() === NS_FILE ) { + + if ( $label === 'source' && $mimeType === null ) { + + // Identify the media source + // and get media information + list( $mediaType, $mimeType, $source ) = $this->getMediaSource( $title ); + $label = $mimeType; + return $source; + } elseif ( $label === 'poster' ) { + $mediaType = 'video'; + + // Get the cover art image url + $source = wfFindFile( $title ); + return $source->getUrl(); + } } } if ( $type == SMWDataItem::TYPE_URI ) { - $source = $dataValue->getDataItem()->getURI(); + $source = $dataItem->getURI(); $mimeType = ''; // Get file extension from the URI diff --git a/www/wiki/extensions/SemanticResultFormats/formats/outline/SRF_Outline.php b/www/wiki/extensions/SemanticResultFormats/formats/outline/SRF_Outline.php deleted file mode 100644 index 8e9d2529..00000000 --- a/www/wiki/extensions/SemanticResultFormats/formats/outline/SRF_Outline.php +++ /dev/null @@ -1,257 +0,0 @@ -mRow = $row; - $this->mVals = []; - } - - function addFieldValue( $field_name, $field_val ) { - if ( array_key_exists( $field_name, $this->mVals ) ) { - $this->mVals[$field_name][] = $field_val; - } else { - $this->mVals[$field_name] = [ $field_val ]; - } - } - - function getFieldValues( $field_name ) { - if ( array_key_exists( $field_name, $this->mVals ) ) { - return $this->mVals[$field_name]; - } else { - return [ wfMessage( 'srf_outline_novalue' )->text() ]; - } - } -} - -/** - * A tree structure for holding the outline data - */ -class SRFOutlineTree { - - var $mTree; - var $mUnsortedItems; - - function __construct( $items = [] ) { - $this->mTree = []; - $this->mUnsortedItems = $items; - } - - function addItem( $item ) { - $this->mUnsortedItems[] = $item; - } - - function categorizeItem( $vals, $item ) { - foreach ( $vals as $val ) { - if ( array_key_exists( $val, $this->mTree ) ) { - $this->mTree[$val]->mUnsortedItems[] = $item; - } else { - $this->mTree[$val] = new SRFOutlineTree( [ $item ] ); - } - } - } - - function addProperty( $property ) { - if ( count( $this->mUnsortedItems ) > 0 ) { - foreach ( $this->mUnsortedItems as $item ) { - $cur_vals = $item->getFieldValues( $property ); - $this->categorizeItem( $cur_vals, $item ); - } - $this->mUnsortedItems = null; - } else { - foreach ( $this->mTree as $i => $node ) { - $this->mTree[$i]->addProperty( $property ); - } - } - } -} - -class SRFOutline extends SMWResultPrinter { - - protected $mOutlineProperties = []; - protected $mInnerFormat = ''; - - protected function handleParameters( array $params, $outputmode ) { - parent::handleParameters( $params, $outputmode ); - $this->mOutlineProperties = $params['outlineproperties']; - } - - public function getName() { - return wfMessage( 'srf_printername_outline' )->text(); - } - - /** - * Code mostly copied from SMW's SMWListResultPrinter::getResultText() - */ - function printItem( $item ) { - $first_col = true; - $found_values = false; // has anything but the first column been printed? - $result = ""; - foreach ( $item->mRow as $orig_ra ) { - // handling is somewhat simpler for SMW 1.5+ - $realFunction = [ 'SMWQueryResult', 'getResults' ]; - if ( is_callable( $realFunction ) ) { - // make a new copy of this, so that the call to - // getNextText() will work again - $ra = clone ( $orig_ra ); - } else { - // make a new copy of this, so that the call to - // getNextText() will work again - $ra = new SMWResultArray( $orig_ra->getContent(), $orig_ra->getPrintRequest() ); - } - $val = $ra->getPrintRequest()->getText( SMW_OUTPUT_WIKI, null ); - if ( in_array( $val, $this->mOutlineProperties ) ) { - continue; - } - $first_value = true; - while ( ( $text = $ra->getNextText( SMW_OUTPUT_WIKI, $this->mLinker ) ) !== false ) { - if ( !$first_col && !$found_values ) { // first values after first column - $result .= ' ('; - $found_values = true; - } elseif ( $found_values || !$first_value ) { - // any value after '(' or non-first values on first column - $result .= ', '; - } - if ( $first_value ) { // first value in any column, print header - $first_value = false; - if ( $this->mShowHeaders && ( '' != $ra->getPrintRequest()->getLabel() ) ) { - $result .= $ra->getPrintRequest()->getText( SMW_OUTPUT_WIKI, $this->mLinker ) . ' '; - } - } - $result .= $text; // actual output value - } - $first_col = false; - } - if ( $found_values ) { - $result .= ')'; - } - return $result; - } - - function printTree( $outline_tree, $level = 0 ) { - $text = ""; - if ( !is_null( $outline_tree->mUnsortedItems ) ) { - $text .= "
    \n"; - foreach ( $outline_tree->mUnsortedItems as $item ) { - $text .= "
  • {$this->printItem($item)}
  • \n"; - } - $text .= "
\n"; - } - if ( $level > 0 ) { - $text .= "
    \n"; - } - $num_levels = count( $this->mOutlineProperties ); - // set font size and weight depending on level we're at - $font_level = $level; - if ( $num_levels < 4 ) { - $font_level += ( 4 - $num_levels ); - } - if ( $font_level == 0 ) { - $font_size = 'x-large'; - } elseif ( $font_level == 1 ) { - $font_size = 'large'; - } elseif ( $font_level == 2 ) { - $font_size = 'medium'; - } else { - $font_size = 'small'; - } - if ( $font_level == 3 ) { - $font_weight = 'bold'; - } else { - $font_weight = 'regular'; - } - foreach ( $outline_tree->mTree as $key => $node ) { - $text .= "

    $key

    \n"; - $text .= $this->printTree( $node, $level + 1 ); - } - if ( $level > 0 ) { - $text .= "
\n"; - } - return $text; - } - - protected function getResultText( SMWQueryResult $res, $outputmode ) { - $print_fields = []; - foreach ( $res->getPrintRequests() as $pr ) { - $field_name = $pr->getText( $outputmode, $this->mLinker ); - // only print it if it's not already part of the - // outline - if ( !in_array( $field_name, $this->mOutlineProperties ) ) { - $print_fields[] = $field_name; - } - } - - // for each result row, create an array of the row itself - // and all its sorted-on fields, and add it to the initial - // 'tree' - $outline_tree = new SRFOutlineTree(); - while ( $row = $res->getNext() ) { - $item = new SRFOutlineItem( $row ); - foreach ( $row as $field ) { - $field_name = $field->getPrintRequest()->getText( SMW_OUTPUT_HTML ); - if ( in_array( $field_name, $this->mOutlineProperties ) ) { - while ( ( $object = $field->getNextDataValue() ) !== false ) { - $field_val = $object->getLongWikiText( $this->mLinker ); - $item->addFieldValue( $field_name, $field_val ); - } - } - } - $outline_tree->addItem( $item ); - } - - // now, cycle through the outline properties, creating the - // tree - foreach ( $this->mOutlineProperties as $outline_prop ) { - $outline_tree->addProperty( $outline_prop ); - } - $result = $this->printTree( $outline_tree ); - - // print further results footer - if ( $this->linkFurtherResults( $res ) ) { - $link = $res->getQueryLink(); - if ( $this->getSearchLabel( $outputmode ) ) { - $link->setCaption( $this->getSearchLabel( $outputmode ) ); - } - $link->setParameter( 'outline', 'format' ); - if ( array_key_exists( 'outlineproperties', $this->params ) ) { - $link->setParameter( $this->params['outlineproperties'], 'outlineproperties' ); - } - $result .= $link->getText( $outputmode, $this->mLinker ) . "\n"; - } - return $result; - } - - /** - * @see SMWResultPrinter::getParamDefinitions - * - * @since 1.8 - * - * @param $definitions array of IParamDefinition - * - * @return array of IParamDefinition|array - */ - public function getParamDefinitions( array $definitions ) { - $params = parent::getParamDefinitions( $definitions ); - - $params['outlineproperties'] = [ - 'islist' => true, - 'default' => [], - 'message' => 'srf_paramdesc_outlineproperties', - ]; - - return $params; - } - -} diff --git a/www/wiki/extensions/SemanticResultFormats/formats/slideshow/SRF_SlideShow.php b/www/wiki/extensions/SemanticResultFormats/formats/slideshow/SRF_SlideShow.php index 6c62aba8..81b92304 100644 --- a/www/wiki/extensions/SemanticResultFormats/formats/slideshow/SRF_SlideShow.php +++ b/www/wiki/extensions/SemanticResultFormats/formats/slideshow/SRF_SlideShow.php @@ -34,11 +34,11 @@ class SRFSlideShow extends SMWResultPrinter { // build an array of article IDs contained in the result set $objects = []; - foreach ( $res->getResults() as $key => $object ) { + foreach ( $res->getResults() as $key => $dataItem ) { - $objects[] = [ $object->getTitle()->getArticleId() ]; + $objects[] = [ $dataItem->getTitle()->getArticleId() ]; - $html .= $key . ': ' . $object->getSerialization() . "
\n"; + $html .= $key . ': ' . $dataItem->getSerialization() . "
\n"; } // build an array of data about the printrequests diff --git a/www/wiki/extensions/SemanticResultFormats/formats/slideshow/SRF_SlideShowApi.php b/www/wiki/extensions/SemanticResultFormats/formats/slideshow/SRF_SlideShowApi.php index 9cc856fc..b1c2430c 100644 --- a/www/wiki/extensions/SemanticResultFormats/formats/slideshow/SRF_SlideShowApi.php +++ b/www/wiki/extensions/SemanticResultFormats/formats/slideshow/SRF_SlideShowApi.php @@ -87,18 +87,19 @@ class SRFSlideShowApi extends ApiBase { } // query SMWQueryProcessor and set query result as API call result + $query = SMWQueryProcessor::createQuery( + '[[' . $title . ']]', + $queryParams, + SMWQueryProcessor::INLINE_QUERY, + '', + $printouts + ); + $this->getResult()->addValue( null, $requestParams['pageid'], - SMWQueryProcessor::getResultFromQueryString( - '[[' . $title . ']]', - $queryParams, - $printouts, - SMW_OUTPUT_HTML, - SMWQueryProcessor::INLINE_QUERY - ) + SMWQueryProcessor::getResultFromQuery( $query, $queryParams, SMW_OUTPUT_HTML, SMWQueryProcessor::INLINE_QUERY ) ); - } /** diff --git a/www/wiki/extensions/SemanticResultFormats/formats/spreadsheet/SpreadsheetPrinter.php b/www/wiki/extensions/SemanticResultFormats/formats/spreadsheet/SpreadsheetPrinter.php new file mode 100644 index 00000000..7a59bb63 --- /dev/null +++ b/www/wiki/extensions/SemanticResultFormats/formats/spreadsheet/SpreadsheetPrinter.php @@ -0,0 +1,461 @@ + [ + 'writer' => 'Xlsx', + 'mimetype' => 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', + 'extension' => '.xlsx', + ], + 'xls' => [ + 'writer' => 'Xls', + 'mimetype' => 'aapplication/vnd.ms-excel', + 'extension' => '.xls', + ], + 'ods' => [ + 'writer' => 'Ods', + 'mimetype' => 'application/vnd.oasis.opendocument.spreadsheet', + 'extension' => '.ods', + ], + 'csv' => [ + 'writer' => 'Csv', + 'mimetype' => 'text/csv', + 'extension' => '.csv', + ], + ]; + + protected $styled = false; + protected $fileFormat; + + /** + * @see ExportPrinter::getMimeType() + * + * @since 1.8 + * + * @param SMWQueryResult $queryResult + * + * @return string + */ + public function getMimeType( SMWQueryResult $queryResult ) { + return $this->fileFormat[ 'mimetype' ]; + } + + /** + * @see ExportPrinter::getFileName + * + * @param SMWQueryResult $queryResult + * + * @return string + */ + public function getFileName( SMWQueryResult $queryResult ) { + return ( $this->params[ 'filename' ] ?: base_convert( uniqid(), 16, 36 ) ) . $this->fileFormat[ 'extension' ]; + } + + /** + * @see ExportPrinter::outputAsFile + * + * @param SMWQueryResult $queryResult + * @param array $params + */ + public function outputAsFile( SMWQueryResult $queryResult, array $params ) { + + if ( array_key_exists( 'fileformat', $params) && array_key_exists( $params[ 'fileformat' ]->getValue(), $this->fileFormats )) { + $this->fileFormat = $this->fileFormats[ $params[ 'fileformat' ]->getValue() ]; + } else { + $this->fileFormat = $this->fileFormats[ 'xlsx' ]; + } + + parent::outputAsFile( $queryResult, $params ); + } + + /** + * @param $definitions \ParamProcessor\ParamDefinition[] + * + * @return array + */ + public function getParamDefinitions( array $definitions ) { + $params = parent::getParamDefinitions( $definitions ); + + $definitions[ 'searchlabel' ]->setDefault( wfMessage( 'srf-spreadsheet-link' )->inContentLanguage()->text() ); + + $params[ 'templatefile' ] = [ + 'type' => 'string', + 'name' => 'templatefile', + 'default' => '', + 'message' => 'srf-paramdesc-spreadsheet-templatefile', + ]; + + $params[ 'filename' ] = [ + 'type' => 'string', + 'name' => 'filename', + 'default' => '', + 'message' => 'srf-paramdesc-spreadsheet-filename', + ]; + + $params[ 'fileformat' ] = [ + 'type' => 'string', + 'name' => 'fileformat', + 'default' => 'xlsx', + 'tolower' => true, + 'message' => 'srf-paramdesc-spreadsheet-fileformat', + ]; + + return $params; + } + + /* + * Turns the PhpSpreadsheet document object into a string + */ + + /** + * Return serialised results in specified format. + * + * @throws \PhpOffice\PhpSpreadsheet\Exception + * @throws \PhpOffice\PhpSpreadsheet\Reader\Exception + */ + protected function getResultText( SMWQueryResult $queryResult, $outputMode ) { + + if ( $outputMode === SMW_OUTPUT_FILE ) { + return $this->getResultFileContents( $queryResult ); + } + + $this->isHTML = ( $outputMode === SMW_OUTPUT_HTML ); + return $this->getLink( $queryResult, $outputMode )->getText( $outputMode, $this->mLinker ); + } + + /** + * @param SMWQueryResult $queryResult + * + * @return string + * @throws \PhpOffice\PhpSpreadsheet\Exception + * @throws \PhpOffice\PhpSpreadsheet\Reader\Exception + */ + protected function getResultFileContents( SMWQueryResult $queryResult ) { + + $spreadsheet = $this->createSpreadsheet(); + $worksheet = $spreadsheet->getSheet( 0 ); + + $this->populateWorksheet( $worksheet, $queryResult ); + + //$spreadsheet->getActiveSheet()->getDefaultRowDimension()->setRowHeight(); + + for ( $i = 0; $i < count( $queryResult->getPrintRequests() ); $i++ ) { + $worksheet->getColumnDimensionByColumn( $i )->setAutoSize( true ); + } + + $result = $this->getStringFromSpreadsheet( $spreadsheet ); + return $result; + } + + /** + * Creates a new PhpSpreadsheet document and returns it + * + * @return Spreadsheet + * @throws \PhpOffice\PhpSpreadsheet\Reader\Exception + */ + protected function createSpreadsheet() { + + $fileTitle = Title::newFromText( $this->params[ 'templatefile' ], NS_FILE ); + + if ( $fileTitle !== null && $fileTitle->exists() ) { + + $spreadsheet = $this->createSpreadsheetFromTemplate( $fileTitle ); + + $this->styled = true; + + } else { + + $spreadsheet = new Spreadsheet(); + + } + + // Set document properties + $spreadsheet->getProperties()->setCreator( 'SemanticMediaWiki Spreadsheet Export' ); + + return $spreadsheet; + } + + /** + * @param $fileTitle + * + * @return Spreadsheet + * @throws \PhpOffice\PhpSpreadsheet\Reader\Exception + */ + private function createSpreadsheetFromTemplate( $fileTitle ) { + $filePage = new ImagePage( $fileTitle, $this ); + + $virtualFile = $filePage->getDisplayedFile(); + $virtualFilePath = $virtualFile->getPath(); + + $localFile = $virtualFile->getRepo()->getLocalReference( $virtualFilePath ); + $localFilePath = $localFile->getPath(); + + return IOFactory::load( $localFilePath ); + } + + /** + * @param SMWQueryResult $queryResult + * @param $worksheet + * + * @throws \PhpOffice\PhpSpreadsheet\Exception + */ + protected function populateWorksheet( Worksheet $worksheet, SMWQueryResult $queryResult ) { + + $rowIterator = $worksheet->getRowIterator( self::HEADER_ROW_OFFSET ); + + //Get headers + if ( $this->mShowHeaders ) { + $this->populateHeaderRow( $rowIterator->current(), $queryResult ); + $rowIterator->next(); + } + + while ( $resultRow = $queryResult->getNext() ) { + + //Get data rows + $this->populateRow( $rowIterator->current(), $resultRow ); + $rowIterator->next(); + } + } + + /** + * Populates the PhpSpreadsheet sheet with the headers from the result query + * + * @param Row $row + * @param SMWQueryResult $queryResult The query result + * + * @return Row + * @throws \PhpOffice\PhpSpreadsheet\Exception + */ + protected function populateHeaderRow( Row $row, SMWQueryResult $queryResult ) { + + $printRequests = $queryResult->getPrintRequests(); + $cellIterator = $row->getCellIterator(); + + foreach ( $printRequests as $printRequest ) { + + $cell = $cellIterator->current(); + + $cell->setValue( $printRequest->getLabel() ); + + $cell->getStyle() + ->getFont() + ->setBold( true ); + + $cellIterator->next(); + + } + + return $row; + } + + /** + * Populates the PhpSpreadsheet document with the query data + * + * @param Row $row + * @param $resultRow + * + * @throws \PhpOffice\PhpSpreadsheet\Exception + */ + protected function populateRow( Row $row, $resultRow ) { + + $cellIterator = $row->getCellIterator(); + + foreach ( $resultRow as $resultField ) { + + $this->populateCell( $cellIterator->current(), $resultField ); + $cellIterator->next(); + } + } + + /** + * @param Cell $cell + * @param SMWResultArray $field + * + * @throws \PhpOffice\PhpSpreadsheet\Exception + */ + protected function populateCell( Cell $cell, SMWResultArray $field ) { + + $dataItems = $field->getContent(); + + if ( $dataItems === false ) { + return; + } + + if ( count( $dataItems ) > 1 ) { + + $values = []; + + while ( $value = $field->getNextText( SMW_OUTPUT_FILE ) ) { + $values[] = $value; + } + + $cell->setValueExplicit( join( ', ', $values ), DataType::TYPE_STRING ); + + } else { + + $nextDataValue = $field->getNextDataValue(); + + if ( $nextDataValue !== false ) { + $this->populateCellAccordingToType( $cell, $nextDataValue ); + } + + } + } + + /** + * Checks the type of the value, and set's it in the sheet accordingly + * + * @param Cell $cell + * @param SMWDataValue $value + * + * @throws \PhpOffice\PhpSpreadsheet\Exception + */ + protected function populateCellAccordingToType( Cell $cell, SMWDataValue $value ) { + + //NOTE: must check against subclasses before superclasses + if ( $value instanceof \SMWQuantityValue ) { + + $this->setQuantityDataValue( $cell, $value ); + + } elseif ( $value instanceof \SMWNumberValue ) { + + $this->setNumberDataValue( $cell, $value ); + + } elseif ( $value instanceof \SMWTimeValue ) { + + $this->setTimeDataValue( $cell, $value ); + + } else { + + $this->setStringDataValue( $cell, $value ); + + } + + } + + /** + * Sets a quantity value at the given col,row location + * + * @param Cell $cell + * @param \SMWQuantityValue $value SMWDataValue the raw data value object + * + * @throws \PhpOffice\PhpSpreadsheet\Exception + */ + protected function setQuantityDataValue( Cell $cell, \SMWQuantityValue $value ) { + + $type = DataType::TYPE_NUMERIC; + $unit = $value->getUnit(); + $number = $value->getNumber(); + + $cell->setValueExplicit( $number, $type ); + + if ( !$this->styled ) { + $cell->getStyle() + ->getNumberFormat() + ->setFormatCode( '0 "' . $unit . '"' ); + } + } + + /** + * Sets a numeric value at the given col,row location + * + * @param Cell $cell + * @param \SMWNumberValue $value SMWDataValue the raw data value object + * + * @throws \PhpOffice\PhpSpreadsheet\Exception + */ + protected function setNumberDataValue( Cell $cell, \SMWNumberValue $value ) { + + $type = DataType::TYPE_NUMERIC; + $number = $value->getNumber(); + + $cell->setValueExplicit( $number, $type ); + } + + /** + * Sets a date/time value at the given col,row location + * + * @param Cell $cell + * @param \SMWTimeValue $value the raw data value object + * + * @throws \PhpOffice\PhpSpreadsheet\Exception + */ + protected function setTimeDataValue( Cell $cell, \SMWTimeValue $value ) { + + $type = DataType::TYPE_NUMERIC; + $number = DateTime::DATEVALUE( str_replace( 'T', ' ', $value->getISO8601Date() ) ); + + $cell->setValueExplicit( $number, $type ); + + if ( !$this->styled ) { + $cell->getStyle() + ->getNumberFormat() + ->setFormatCode( NumberFormat::FORMAT_DATE_DDMMYYYY ); + } + } + + /** + * Sets or appends a string value at the given col,row location + * + * If there already exists a value at a given col,row location, then + * convert the cell to a string and append the data value. Creating + * a list of comma separated entries. + * + * @param Cell $cell + * @param $value SMWDataValue the raw data value object + * + * @throws \PhpOffice\PhpSpreadsheet\Exception + */ + protected function setStringDataValue( Cell $cell, SMWDataValue $value ) { + + $type = DataType::TYPE_STRING; + $text = $value->getWikiValue(); + $text = Sanitizer::decodeCharReferences( $text ); + $text = DataType::checkString( $text ); + + $cell->setValueExplicit( $text, $type ); + } + + /** + * @param Spreadsheet $spreadsheet + * + * @return string + * @throws \PhpOffice\PhpSpreadsheet\Writer\Exception + */ + protected function getStringFromSpreadsheet( Spreadsheet $spreadsheet ) { + + $writer = IOFactory::createWriter( $spreadsheet, $this->fileFormat[ 'writer' ] ); + + ob_start(); + $writer->save( 'php://output' ); + return ob_get_clean(); + } + +} + diff --git a/www/wiki/extensions/SemanticResultFormats/formats/tagcloud/TagCloud.php b/www/wiki/extensions/SemanticResultFormats/formats/tagcloud/TagCloud.php index 2ad18154..a10a7ab7 100644 --- a/www/wiki/extensions/SemanticResultFormats/formats/tagcloud/TagCloud.php +++ b/www/wiki/extensions/SemanticResultFormats/formats/tagcloud/TagCloud.php @@ -119,11 +119,20 @@ class TagCloud extends ResultPrinter { continue; } + $value = null; + // Get the HTML for the tag content. Pages are linked, other stuff is just plaintext. - if ( $dataValue->getTypeID() === '_wpg' && $dataValue->getTitle() instanceof Title ) { - $value = $dataValue->getTitle()->getPrefixedText(); - $html = $dataValue->getLongText( $outputMode, $this->getLinker( $isSubject ) ); - } else { + if ( $dataValue->getTypeID() === '_wpg' ) { + + $title = $dataValue->getDataItem()->getTitle(); + + if ( $title instanceof Title ) { + $value = $title->getPrefixedText(); + $html = $dataValue->getLongText( $outputMode, $this->getLinker( $isSubject ) ); + } + } + + if ( $value === null ) { $html = $dataValue->getShortText( $outputMode, $this->getLinker( false ) ); $value = $html; } diff --git a/www/wiki/extensions/SemanticResultFormats/formats/timeline/SRF_Timeline.php b/www/wiki/extensions/SemanticResultFormats/formats/timeline/SRF_Timeline.php index 9618ff64..0af1a110 100644 --- a/www/wiki/extensions/SemanticResultFormats/formats/timeline/SRF_Timeline.php +++ b/www/wiki/extensions/SemanticResultFormats/formats/timeline/SRF_Timeline.php @@ -341,7 +341,7 @@ class SRFTimeline extends SMWResultPrinter { if ( $pr->getMode() == SMWPrintRequest::PRINT_THIS ) { $curarticle = $object->getLongText( $outputmode, $l ); - $cururl = $object->getTitle()->getFullUrl(); + $cururl = $object->getDataItem()->getTitle()->getFullUrl(); } // NOTE: type Title of $object implied diff --git a/www/wiki/extensions/SemanticResultFormats/formats/timeseries/resources/ext.srf.timeseries.flot.js b/www/wiki/extensions/SemanticResultFormats/formats/timeseries/resources/ext.srf.timeseries.flot.js index 46c16fd4..ec4b4f64 100644 --- a/www/wiki/extensions/SemanticResultFormats/formats/timeseries/resources/ext.srf.timeseries.flot.js +++ b/www/wiki/extensions/SemanticResultFormats/formats/timeseries/resources/ext.srf.timeseries.flot.js @@ -135,7 +135,10 @@ // Tabs height can vary (due to CSS) therefore after tabs instance was // created get the height var _tabs = chart.find( '.ui-tabs-nav' ); - container.find( '.' + plotClass ).css( { 'height': height - _tabs.outerHeight() , 'width': width } ); + if (_tabs.length > 0) { + height -= _tabs.outerHeight(); + } + container.find( '.' + plotClass ).css( { 'height': height, 'width': width } ); // Draw chart container.srfFlotTimeSeries( 'chart', settings ); diff --git a/www/wiki/extensions/SemanticResultFormats/formats/tree/TreeResultPrinter.php b/www/wiki/extensions/SemanticResultFormats/formats/tree/TreeResultPrinter.php index d29fed80..a5011d0e 100644 --- a/www/wiki/extensions/SemanticResultFormats/formats/tree/TreeResultPrinter.php +++ b/www/wiki/extensions/SemanticResultFormats/formats/tree/TreeResultPrinter.php @@ -137,7 +137,11 @@ class TreeResultPrinter extends ListResultPrinter { $this->setQueryResult( null ); - return Html::rawElement( 'div', [ 'class' => 'srf-tree' ], $resultText ); + if ( trim( $this->params[ 'class' ] ) === '' ) { + return $resultText; + } + + return Html::rawElement( 'div', [ 'class' => $this->params[ 'class' ] ], $resultText ); } /** @@ -194,6 +198,11 @@ class TreeResultPrinter extends ListResultPrinter { 'message' => 'smw-paramdesc-template-arguments', ]; + $params['class'] = [ + 'default' => '', + 'message' => 'srf-paramdesc-class', + ]; + return $params; } diff --git a/www/wiki/extensions/SemanticResultFormats/formats/valuerank/SRF_ValueRank.php b/www/wiki/extensions/SemanticResultFormats/formats/valuerank/SRF_ValueRank.php index f3df915e..eba7fd3f 100644 --- a/www/wiki/extensions/SemanticResultFormats/formats/valuerank/SRF_ValueRank.php +++ b/www/wiki/extensions/SemanticResultFormats/formats/valuerank/SRF_ValueRank.php @@ -92,7 +92,7 @@ class SRFValueRank extends SMWResultPrinter { // Get the HTML for the tag content. Pages are linked, other stuff is just plaintext. if ( $dataValue->getTypeID() == '_wpg' ) { - $value = $dataValue->getTitle()->getText(); + $value = $dataValue->getDataItem()->getTitle()->getText(); $html = $dataValue->getLongText( $outputMode, $this->getLinker( $isSubject ) ); } else { $html = $dataValue->getShortText( $outputMode, $this->getLinker( false ) ); @@ -262,4 +262,4 @@ class SRFValueRank extends SMWResultPrinter { return $params; } -} \ No newline at end of file +} diff --git a/www/wiki/extensions/SemanticResultFormats/i18n/ar.json b/www/wiki/extensions/SemanticResultFormats/i18n/ar.json index 19386d1c..7961fa47 100644 --- a/www/wiki/extensions/SemanticResultFormats/i18n/ar.json +++ b/www/wiki/extensions/SemanticResultFormats/i18n/ar.json @@ -8,9 +8,8 @@ ] }, "srf-desc": "صيغ نتائج إضافية لاستعلامات ميدياويكي الدلالية", - "srf-name": "صيغ النتائج الدلالية", - "prefs-srf": "تمديد:صيغ النتيجة الدلالية", - "srf-prefs-intro-text": "لقد قمت بتثبيت ملحق صيغ النتائج الدلالية، للحصول على مساعدة إضافية; الرجاء زيارة صفحة المساعدة [https://www.semantic-mediawiki.org/wiki/Help:Result_formats صيغ النتائج].", + "prefs-srf": "صيغ النتيجة الدلالية", + "srf-prefs-intro-text": "لقد قمت بتثبيت ملحق صيغ النتائج الدلالية، الرجاء زيارة صفحة المساعدة [https://www.semantic-mediawiki.org/wiki/Help:Result_formats صيغ النتائج] للحصول على مساعدة إضافية فيما يتعلق بتفضيلات المستخدم.", "prefs-srf-eventcalendar-options": "خيارات تقويم الحدث", "srf-prefs-eventcalendar-options-update-default": "تمكين [https://www.semantic-mediawiki.org/wiki/Help:User_preferences#Automatic_updates التحديثات التلقائية] لأحداث التقويم أثناء تحديث الصفحة", "srf-prefs-eventcalendar-options-paneview-default": "تمكين عرض الجزء بشكل افتراضي", @@ -128,7 +127,7 @@ "srf-paramdesc-paneview": "حدد موضع الجزء الذي يحتوي على مخطط سطر صغي،. القيم المسموح بها: \"أسفل\" و\"أعلى\" و\"لا شيء\"، الافتراضي: \"أسفل\"", "srf-paramdesc-infotext": "عرض معلومات إضافية على علامة التبويب معلومات المقابلة", "srf-paramdesc-clicktarget": "حدد صفحة أو سلسلة استعلام كهدف عند النقر على تاريخ التقويم.", - "srf-ui-gridview-label-item": "بند البيانات", + "srf-ui-gridview-label-item": "عنصر البيانات", "srf-ui-gridview-label-value": "قيمة البيانات", "srf-ui-gridview-label-series": "سلسلة البيانات", "srf-ui-gridview-label-chart-tab": "رسم بياني", @@ -267,7 +266,6 @@ "srf-filtered-noscript-error": "لا يمكن عرض النتائج نظرا لعدم تمكين جافا سكريبت; انتقل إلى $1.", "srf-filtered-noscript-link-caption": "النتائج في شكل جدول", "srf-filtered-map-provider-missing-error": "لم يتم تحديد موفر الخريطة للعرض \"الخريطة\".", - "srf-filtered-map-geocoordinateparser-missing-error": "لم يتم العثور على محلل الإحداثيات; تحتاج إلى تثبيت 'قيم البيانات/الجغرافية'.", "srf-filtered-value-filter-and": "و", "srf-filtered-value-filter-or": "أو", "srf-filtered-value-filter-placeholder": "حدد قيمة مرشح", @@ -327,9 +325,24 @@ "srf-ui-mediaplayer-label-repeat-off": "كرر", "srf-ui-mediaplayer-label-full-screen": "ملء الشاشة", "srf-ui-mediaplayer-label-restore-screen": "استعادة الشاشة", - "srf-excel-link": "إكسل", - "srf-excel-missing-phpexcel": "تعذر تصدير MS-Excel لأنه لم يتم تثبيت الإضافة [https://www.mediawiki.org/wiki/Extension:PHPExcel PHPExcel].", - "srf-paramdesc-excel-filename": "اسم ملف التنزيل للملف الذي تم إنشاؤه", - "srf-paramdesc-excel-templatefile": "اسم ملف إكسيل من نطاق ''ملف'' المستخدم لتنسيق الملف الذي تم إنشاؤه", - "srf-paramdesc-icalendar-timezone": "قائمة مفصولة بفواصل بالمناطق الزمنية" + "srf-spreadsheet-link": "جدول ممتد", + "srf-paramdesc-spreadsheet-filename": "اسم ملف لتنزيل ملف جدول البيانات المنشأ", + "srf-paramdesc-spreadsheet-fileformat": "التنسيق الذي سيتم إنتاجه لملف جدول البيانات، القيم المسموح بها: xlsx وxls وods وcsv، الافتراضي: xlsx", + "srf-paramdesc-spreadsheet-templatefile": "اسم ملف جدول ممتد من نطاق ''ملف'' المستخدمة لتنسيق الملف المنشأ", + "srf-paramdesc-icalendar-timezone": "قائمة مفصولة بفواصل بالمناطق الزمنية", + "srf-paramdesc-gantt-diagramtitle": "اسم المخطط", + "srf-paramdesc-gantt-diagramtheme": "موضوع الرسم البياني", + "srf-paramdesc-gantt-axisformat": "المحور السيني: صيغة التاريخ", + "srf-paramdesc-gantt-sortkey": "مفتاح لفرز الأقسام والمهام", + "srf-paramdesc-gantt-titletopmargin": "الهامش العلوي من عنوان المخطط", + "srf-paramdesc-gantt-barheight": "ارتفاع أشرطة المهام", + "srf-paramdesc-gantt-leftpadding": "عرض عنوان القسم", + "srf-paramdesc-gantt-bargap": "المسافة العمودية لشريط المهام من الهامش", + "srf-error-gantt-mapping-assignment": "تعيين خاطئ للقيم المعينة في '''$1'''", + "srf-error-gantt-mapping-keywords": "المفتاح المستخدم في وسيط التعيين غير معتمد", + "srf-error-gantt-theme": "'''السمة''' التي اخترتها غير مدعومة", + "srf-error-gantt-sortkey": "'''مفتاح الفرز''' الذي اخترته غير مدعوم", + "srf-error-gantt-mermaid-not-installed": "امتداد Mermaid يحتاج إلى تثبيت.", + "srf-printername-gantt": "جانت", + "srf-paramdesc-nodelabel": "استخدام تسمية عقدة الرسم البياني، القيم المسموح بها: عنوان العرض." } diff --git a/www/wiki/extensions/SemanticResultFormats/i18n/ast.json b/www/wiki/extensions/SemanticResultFormats/i18n/ast.json index 61ca58f2..31a71611 100644 --- a/www/wiki/extensions/SemanticResultFormats/i18n/ast.json +++ b/www/wiki/extensions/SemanticResultFormats/i18n/ast.json @@ -5,7 +5,6 @@ ] }, "srf-desc": "Formatos de resultancies adicionales pa consultes de Semantic MediaWiki", - "srf-name": "Formatos de resultancia semántica", "prefs-srf": "Estensión:Formatos de resultancies semántiques", "srf-prefs-intro-text": "Tienes instalada la estensión Formatos de resultancia semántica. Pa llograr ayuda adicional, visita la páxina d'ayuda sobre los [https://@www.semantic-mediawiki.org/wiki/Help:Result_formats formatos de les resultancies].", "prefs-srf-eventcalendar-options": "Opciones de calendariu d'actividaes", @@ -261,7 +260,6 @@ "srf-filtered-noscript-error": "Nun pueden amosase los resultaos porque nun ta activáu Javascript. Visita $1.", "srf-filtered-noscript-link-caption": "resultaos en forma de tabla", "srf-filtered-map-provider-missing-error": "Nun s'indicó un orixe de mapa pa la vista «mapa».", - "srf-filtered-map-geocoordinateparser-missing-error": "Nun s'alcontró l'analizador de coordenaes xeográfiques. Precises instalar 'data-values/geo'.", "srf-filtered-value-filter-and": "Y", "srf-filtered-value-filter-or": "O", "srf-filtered-value-filter-placeholder": "Seleccionar un valor de filtru", @@ -321,9 +319,5 @@ "srf-ui-mediaplayer-label-repeat-off": "Desactivar repetición", "srf-ui-mediaplayer-label-full-screen": "Pantalla completa", "srf-ui-mediaplayer-label-restore-screen": "Restaurar la pantalla", - "srf-excel-link": "Excel", - "srf-excel-missing-phpexcel": "Nun pudo esportase a MS-Excel porque la estensión [https://www.mediawiki.org/wiki/Extension:PHPExcel PHPExcel] nun ta instalada.", - "srf-paramdesc-excel-filename": "El nome de ficheru de la descarga pal ficheru xeneráu", - "srf-paramdesc-excel-templatefile": "El nome d'un ficheru d'Excel del espaciu de nomes «Ficheru» utilizáu pa dar formatu al ficheru xeneráu", "srf-paramdesc-icalendar-timezone": "Llista d'estayes horaries separaes con comes" } diff --git a/www/wiki/extensions/SemanticResultFormats/i18n/br.json b/www/wiki/extensions/SemanticResultFormats/i18n/br.json index 159f2a73..301f753c 100644 --- a/www/wiki/extensions/SemanticResultFormats/i18n/br.json +++ b/www/wiki/extensions/SemanticResultFormats/i18n/br.json @@ -11,7 +11,6 @@ ] }, "srf-desc": "Furmadoù ouzhpenn evit ar rekedoù Semantic MediaWiki", - "srf-name": "Furmadoù an disoc'hoù semantek", "srf-module-loading": "O kargañ...", "srf-paramdesc-height": "Uhelder", "srf-paramdesc-width": "Ledander", @@ -135,6 +134,5 @@ "srf-ui-mediaplayer-label-unmute": "Gant son", "srf-ui-mediaplayer-label-repeat": "Adober", "srf-ui-mediaplayer-label-full-screen": "Skramm leun", - "srf-ui-mediaplayer-label-restore-screen": "Assevel ar skramm", - "srf-excel-link": "Excel" + "srf-ui-mediaplayer-label-restore-screen": "Assevel ar skramm" } diff --git a/www/wiki/extensions/SemanticResultFormats/i18n/ca.json b/www/wiki/extensions/SemanticResultFormats/i18n/ca.json index 867ebf6f..b20e83ef 100644 --- a/www/wiki/extensions/SemanticResultFormats/i18n/ca.json +++ b/www/wiki/extensions/SemanticResultFormats/i18n/ca.json @@ -10,7 +10,7 @@ ] }, "srf-desc": "Formats addicionals per a les consultes en línia del Semantic MediaWiki.", - "srf-name": "Semantic Result Formats", + "prefs-srf": "Formats de resultats semàntics", "srf-module-loading": "S'està carregant...", "srf-paramdesc-height": "Alçada", "srf-paramdesc-width": "Amplada", @@ -115,6 +115,7 @@ "srf-ui-eventcalendar-label-listday": "Dia (llista)", "srf-ui-eventcalendar-label-allday": "Tot el dia", "srf-paramdesc-unit": "Unitat", + "srf-printername-media": "Reproductor multimèdia", "srf-ui-mediaplayer-label-previous": "Anterior", "srf-ui-mediaplayer-label-play": "Reprodueix", "srf-ui-mediaplayer-label-pause": "Posa en pausa", diff --git a/www/wiki/extensions/SemanticResultFormats/i18n/cs.json b/www/wiki/extensions/SemanticResultFormats/i18n/cs.json index cbf1b8ef..42824070 100644 --- a/www/wiki/extensions/SemanticResultFormats/i18n/cs.json +++ b/www/wiki/extensions/SemanticResultFormats/i18n/cs.json @@ -2,26 +2,62 @@ "@metadata": { "authors": [ "Vks", - "XenoPheX" + "XenoPheX", + "Dvorapa", + "Jaroslav Cerny", + "Nemo bis" ] }, + "srf-desc": "Další formáty inline požadavků rozšíření Semantic MediaWiki", + "prefs-srf": "Rozšíření:Formáty sémantických výsledků", + "srf-prefs-intro-text": "Nainstalovali jste si rozšíření Formáty sémantických výsledků. Podporu k tomuto rozšíření najdete na stránce nápovědy [https://www.semantic-mediawiki.org/wiki/Help:Result_formats k formátům výsledků].", + "prefs-srf-eventcalendar-options": "Volby kalendáře událostí", + "srf-prefs-eventcalendar-options-update-default": "Povolit [https://www.semantic-mediawiki.org/wiki/Help:User_preferences#Automatic_updates automatické aktualizace] událostí kalendáře během aktualizace stránky", + "srf-prefs-eventcalendar-options-paneview-default": "Povolit implicitně zobrazení podokna", + "prefs-srf-datatables-options": "Volby datových tabulek", + "srf-prefs-datatables-options-update-default": "Povolit [https://www.semantic-mediawiki.org/wiki/Help:User_preferences#Automatic_updates automatické aktualizace] obsahu tabulky během aktualizace stránky", + "srf-prefs-datatables-options-cache-default": "Povolit [https://www.semantic-mediawiki.org/wiki/Help:User_preferences#Local_storage místní úložiště] pro zkrácení času odezvy", "srf-module-loading": "Načítání...", + "srf-paramdesc-layout": "Dostupné rozložení", "srf-paramdesc-height": "Výška", "srf-paramdesc-width": "Šířka", + "srf-paramdesc-class": "Zadejte další třídu stylu CSS", + "srf-module-nomatch": "Nenalezena žádná shoda", + "srf-paramdesc-charttype": "Dostupný typ grafu", "srf-navigation-previous": "Předchozí", "srf-ui-navigation-prev": "Předch.", "srf-ui-navigation-next": "Další", "srf-ui-common-label-source": "Zdroj", "srf-ui-common-label-datasource": "Zdroj dat", + "srf-ui-common-label-ajax-error": "Server ohlásil nezdařenou komunikaci pro toto $1. Zkuste obnovit stránku nebo se obraťte na $2.", + "srf-ui-common-label-request-object": "objekt požadavku", + "srf-ui-common-label-help-section": "část nápovědy", "srf-ui-tooltip-title-options": "Nastavení", "srf-ui-tooltip-title-scope": "Rozsah", - "srf-error-missing-layout": "Rozložení chybí", + "srf-ui-tooltip-title-legend": "Legenda", + "srf-ui-tooltip-title-filter": "Filtr", + "srf-ui-common-label-refresh": "Obnovit", + "srf-ui-common-label-parameters": "Parametry", + "srf-ui-common-label-query": "Dotaz", + "srf-ui-common-label-paneview": "Zobrazení podokna", + "srf-ui-common-label-daterange": "Časové období", + "srf-ui-widgets-label-parameter-limit": "Parametr limitu", + "srf-error-option-mix": "Volba ($1) není dostupná", + "srf-error-option-link-all": "Volba ($1) vyžaduje, aby parametr \"link\" měl hodnotu \"all\"", + "srf-error-missing-layout": "Diagram nebo graf nelze zobrazit, protože chybí rozložení.", + "srf-error-jqplot-bubble-data-length": "Diagram nebo graf nelze zobrazit, protože chybí data.", + "srf-error-jqplot-stackseries-data-length": "Diagram nebo graf nelze zobrazit, protože pruhy nebo čáry nemají shodný počet prvků.", + "srf-warn-empy-chart": "Diagram nebo graf je prázdný, protože chybí data.", + "srf-paramdesc-color": "Barva položek kalendáře", "srfc_previousmonth": "Předchozí měsíc", "srfc_nextmonth": "Další měsíc", "srfc_today": "Dnes", "srfc_gotomonth": "Přejdi na měsíc", "srf_printername_calendar": "Měsíční kalendář", "srf_paramdesc_calendarlang": "Kód jazyka, ve kteréem bude zobrazován kalendář", + "srf_paramdesc_calendarcolors": "Barva jednotlivých datových vlastností (příklad: \"Datum začátku=>green,Datum konce=>#09c\")", + "srf-paramdesc-calendar-startmonth": "Počáteční zobrazený měsíc kalendáře (výchozí = aktuální měsíc)", + "srf-paramdesc-calendar-startyear": "Počáteční zobrazený rok kalendáře (výchozí = aktuální rok)", "srf_printername_vcard": "export vCard", "srf_printername_icalendar": "export iCalendar", "srf_paramdesc_icalendartitle": "titulek souboru s kalendářem", @@ -29,47 +65,266 @@ "srf_printername_bibtex": "export BibTeX", "srf_outline_novalue": "Žádná hodnota", "srf_printername_outline": "Náčrtek", + "srf_paramdesc_outlineproperties": "Seznam vlastností, které budou zobrazeny jako záhlaví osnovy, oddělené čárkami", "srf_printername_sum": "Součet čísel", "srf_printername_average": "Průměr čísel", "srf_printername_max": "Maximum mezi čísly", "srf_printername_min": "Minimum mezi čísly", + "srf_paramdesc_limit": "Maximální počet stránek zahrnutých do dotazu", "srf_printername_product": "Součin čísel", "srf_printername_median": "Medián čísel", "srf-paramdesc-default": "Výchozí hodnota, která se zobrazí, když neexistují žádné číselné výsledky", + "srf_printername_earliest": "Nejdřívější čas", + "srf_printername_latest": "Nejpozdější čas", + "srf_printername_timeline": "Časová osa", + "srf_printername_eventline": "Časová osa událostí", + "srf_paramdesc_timelinebands": "Určuje, které pruhy jsou zobrazeny ve výsledku.", + "srf_paramdesc_timelineposition": "Určuje událost, na kterou se časová osa na začátku vystředí.", + "srf_paramdesc_timelinestart": "Název vlastnosti použité k definování prvního časového bodu", + "srf_paramdesc_timelineend": "Název vlastnosti použité k definování druhého časového bodu", + "srf_paramdesc_timelinesize": "Výška časové osy", + "srf-timeline-allresults": "Další výsledky pro tento dotaz.", + "srf-timeline-nojs": "K zobrazení interaktivní časové osy musíte mít povolen JavaScript.", + "srf_paramdesc_views": "Zobrazené pohledy", + "srf_paramdesc_facets": "Sada vlastností zobrazených na každé stránce", + "srf_paramdesc_lens": "Název šablony, přes kterou se zobrazí vlastnosti stránky", + "srf_printername_googlebar": "Pruhový graf Google", + "srf_printername_googlepie": "Výsečový graf Google", + "srf-printername-jqplotchart": "Graf jqPlot", + "srf-printername-jqplotseries": "Řada jqPlot", + "srf_paramdesc_chartheight": "Určuje výšku diagramu nebo grafu (v pixelech)", + "srf_paramdesc_chartwidth": "Určuje šířku diagramu nebo grafu (v pixelech nebo procentech)", "srf_paramdesc_charttitle": "Název grafu", + "srf_paramdesc_barcolor": "Určuje barvy grafu", + "srf_paramdesc_bardirection": "Určuje směr grafu", + "srf-paramdesc-direction": "Určuje směr diagramu nebo grafu", + "srf_paramdesc_barnumbersaxislabel": "Popisek osy čísel", + "srf-paramdesc-labelaxislabel": "Popisek osy popisků", + "srf-paramdesc-ticklabels": "Povolit zobrazení popisků značek", + "srf-paramdesc-minvalue": "Minimální hodnota zobrazená na ose Y", + "srf-paramdesc-pointlabels": "Zobrazit datové body definované v grafu", + "srf-paramdesc-chartlegend": "Umístění legendy grafu", + "srf-paramdesc-datalabels": "Popisky dat diagramu/grafu", + "srf-paramdesc-charttext": "Popisný text grafu", + "srf-paramdesc-chartclass": "Další třída CSS", + "srf-paramdesc-renderer": "Vyberte vykreslovač diagramu/grafu", + "srf-paramdesc-filling": "Individuální volba výplně", + "srf-paramdesc-theme": "Vyberte motiv mřížky", + "srf-paramdesc-chartcolor": "Přiřadit individuální barvy grafu", + "srf-paramdesc-colorscheme": "Vyberte barevné schéma", + "srf-paramdesc-valueformat": "Určuje pravidlo formátování hodnot", + "srf-paramdesc-highlighter": "Zobrazit zvýrazňovač datového bodu", + "srf-paramdesc-smoothlines": "Použít algoritmus vyhlazování ve spojnicových grafech", + "srf-paramdesc-stackseries": "Zobrazit graf jako skládané řady", + "srf-paramdesc-seriesgroup": "Vybrat seskupení řad", + "srf-paramdesc-serieslabel": "Určuje popisek řady", + "srf-paramdesc-grouplabel": "Určuje popisek skupiny", + "srf-paramdesc-chartcursor": "Volba zobrazení ukazatele v grafu", + "srf-paramdesc-trendline": "Povolit současné zobrazení grafu a jeho spojnice trendu", + "srf-paramdesc-gridview": "Zobrazit současně graf i sady dat. Povolené hodnoty: \"none\" a \"tabs\". Výchozí hodnota: \"none\"", + "srf-paramdesc-paneview": "Určuje umístění podokna s malým spojnicovým grafem. Povolené hodnoty: \"bottom\", \"top\" a \"none\". Výchozí hodnota: \"bottom\"", + "srf-paramdesc-infotext": "Zobrazit další informace na odpovídající informační záložce", + "srf-paramdesc-clicktarget": "Definuje stránku nebo řetězec dotazu jako cíl kliknutí na datum v kalendáři.", + "srf-ui-gridview-label-item": "Datová položka", + "srf-ui-gridview-label-value": "Datová hodnota", + "srf-ui-gridview-label-series": "Datová řada", "srf-ui-gridview-label-chart-tab": "Graf", + "srf-ui-gridview-label-data-tab": "Data", + "srf-ui-gridview-label-info-tab": "Informace", + "srf_printername_gallery": "Galerie", + "srf_paramdesc_perrow": "Počet obrázků na 1 řádek", + "srf_paramdesc_widths": "Šířka obrázků", + "srf_paramdesc_heights": "Výška obrázků", + "srf_paramdesc_autocaptions": "Použít název souboru jako titulek, není-li žádný zadán", + "srf_paramdesc_fileextensions": "Při použití názvu souboru jako titulku zobrazit také příponu souboru", + "srf_paramdesc_captionproperty": "Název sémantické vlastnosti přítomné v dotazovaných stránkách, které budou použity jako titulek", + "srf_paramdesc_imageproperty": "Název sémantické vlastnosti přítomné v dotazovaných stránkách, která odkazuje na používané obrázky. Je-li vlastnost nastavena, nebudou dotazované stránky zobrazeny jako obrázky", + "srf-paramdesc-redirects": "Název sémantické vlastnosti přítomné v dotazovaných stránkách, která obsahuje cíl přesměrování", + "srf-paramdesc-navigation": "Ovládací prvek rozložení výstupu", + "srf-paramdesc-overlay": "Povolit překrytí obrázků", "srf-gallery-navigation-previous": "Předchozí", "srf-gallery-navigation-next": "Následujicí", "srf-gallery-overlay-count": "Obrázek $1 z $2", + "srf-gallery-image-url-error": "Obrázek nebyl nalezen.", "srf_printername_tagcloud": "Oblak nálepek", + "srf_paramdesc_includesubject": "Zahrnout také názvy předmětů", + "srf_paramdesc_increase": "Jak zvětšit velikost značek", + "srf_paramdesc_tagorder": "Pořadí značek", + "srf_paramdesc_mincount": "Minimální počet výskytů hodnoty k zařazení do seznamu", + "srf_paramdesc_minsize": "Velikost nejmenších značek v procentech", + "srf_paramdesc_maxsize": "Velikost největších značek v procentech", + "srf_paramdesc_maxtags": "Maximální počet značek v cloudu", + "srf-paramdesc-excludetags": "Vyloučit značky (oddělovač: \";\")", + "srf_printername_valuerank": "Postavení značky", "srf_printername_array": "Pole", + "srf_paramdesc_pagetitle": "Mají být zobrazeny nadpisy stránek jako položky ve výstupu, nebo vynechány?", + "srf_paramdesc_hidegaps": "Mají být tištěny a odděleny pomocí oddělovačů vyžadované, ale nedostupné hodnoty vlastností a záznamů, nebo vynechány?", + "srf_paramdesc_arrayname": "Je-li zadáno a je-li dostupné rozšíření ArrayExtension, vytvoří se pole se zadaným názvem (bez viditelného výstupu)", + "srf_paramdesc_propsep": "Oddělovač mezi požadovanými vlastnostmi", + "srf_paramdesc_manysep": "Oddělovač mezi hodnotami vícehodnotových vlastností", + "srf_paramdesc_recordsep": "Oddělovač mezi hodnotami vlastností záznamu", + "srf_paramdesc_headersep": "Oddělovač mezi názvem vlastnosti a hodnotou, když je \"headers\" nastaveno na \"show\" nebo \"plain\"", "srf_printername_hash": "Asociativní pole (Haš)", + "srf_paramdesc_hashname": "Je-li zadáno a je-li dostupné rozšíření HashTables, vytvoří se hash se zadaným názvem (bez viditelného výstupu)", "srf-printername-graph": "Graf", - "srf_paramdesc_graphname": "Název", - "srf_paramdesc_graphsize": "Velikost grafu (v pixelech)", - "srf_paramdesc_graphlabel": "Popisek grafu", - "srf_paramdesc_rankdir": "Směr šipky", - "srf_paramdesc_graphcolor": "Barva grafu", + "srf-paramdesc-graph-relation": "Určuje, zda předměty nebo vlastnosti názvů jsou nadřízené či podřízené prvky", + "srf-paramdesc-graph-nameprop": "Nastaví vlastnost, která bude použita jako předmět namísto skutečného předmětu, např. název stránky", + "srf-paramdesc-graph-nodeshape": "Nastaví tvar všech uzlů ve grafu", + "srf-paramdesc-graphname": "Nastaví název grafu", + "srf-paramdesc-graphsize": "Nastaví velikost grafu v pixelech", + "srf-paramdesc-graphlegend": "Určuje, zda bude zobrazena legenda grafu", + "srf-paramdesc-graphlabel": "Nastaví popisek grafu", + "srf-paramdesc-rankdir": "Nastaví směr šipek", + "srf-paramdesc-graphlink": "Určuje, zda mají uzly odkazovat na příslušné wiki stránky", + "srf-paramdesc-graphcolor": "Nastaví barvu grafu", + "srf-paramdesc-graph-wwl": "Nastaví limit zalomení slova (jako počet znaků)", + "srf-paramdesc-clustercolor": "Nastaví barvy rámečků shluku", + "srf-paramdesc-highlight": "Určí uzel, který bude zvýrazněn", + "srf-paramdesc-highlightcolor": "Nastaví barvu písma zvýrazněného uzlu", + "srf-paramdesc-redlinkcolor": "Nastaví barvu písma odkazů na neexistující stránky", + "srf-paramdesc-processcategory": "Nastaví kategorii wiki, která bude shromažďovat procesní kroky", + "srf-paramdesc-showroles": "Zobrazí odpovídající role v grafu", + "srf-paramdesc-showstatus": "Určuje, zda má být vykreslen stav procesního kroku", + "srf-paramdesc-showresources": "Zobrazí v grafu odpovídající zdroje", + "srf-paramdesc-showdiscussion": "Určuje, zda má být vykreslena diskuze", + "srf-paramdesc-showredlinks": "Určuje, zda budou odkazy na neexistující stránky zaškrtnuty a zvýrazněny", + "srf-paramdesc-showcompound": "Určuje, zda mají být zvýrazněny složené uzly, např. dílčí procesy", + "srf-paramdesc-debug": "Určuje, zda má být kód grafu procesů zobrazen uvnitř značek PRE", + "srf-paramdesc-graphvalidation": "Zobrazí červeně ty procesní kroky, které nemají přiřazenou roli", + "srf-printername-datatables": "Datové tabulky", + "srf-ui-datatables-label-conditions": "Podmínky", + "srf-ui-datatables-label-parameters": "Parametry", + "srf-ui-datatables-label-filters": "Filtry sloupce a hledání", + "srf-ui-datatables-label-information": "Informace", + "srf-ui-datatables-panel-disclaimer": "Parametry a podmínky lze změnit, ale všechny změny jsou dočasné a po aktualizaci stránky se ztratí", + "srf-ui-datatables-label-update-success": "Aktualizace tabulky byla úspěšná", + "srf-ui-datatables-label-update-error": "Aktualizace tabulky selhala.", + "srf-ui-datatables-label-placeholder-column-search": "Hledat...", + "srf-ui-datatables-label-content-cache": "Obsah byl odvozen z místní cache.", + "srf-ui-datatables-label-content-server": "Obsah byl odvozen ze serveru.", + "srf-ui-datatables-label-multiselect-column-header": "Dostupné sloupce", + "srf-ui-datatables-label-multiselect-column-noneselectedtext": "Nastavení filtru", + "srf-ui-datatables-label-multiselect-column-selectedtext": "Sloupce jsou vidět", + "srf-ui-datatables-label-sEmptyTable": "V tabulce nejsou dostupná žádná data", + "srf-ui-datatables-label-sInfo": "Zobrazují se položky _START_ až _END_ z _TOTAL_ celkem", + "srf-ui-datatables-label-sInfoEmpty": "Zobrazují se položky 0 až 0 z 0 celkem", + "srf-ui-datatables-label-sInfoFiltered": "(filtrováno z _MAX_ položek celkem)", + "srf-ui-datatables-label-sInfoThousands": ",", + "srf-ui-datatables-label-sLengthMenu": "Zobrazují se položky _MENU_", + "srf-ui-datatables-label-sLoadingRecords": "Načítání...", + "srf-ui-datatables-label-sProcessing": "Zpracovává se...", + "srf-ui-datatables-label-sSearch": "Hledat:", + "srf-ui-datatables-label-sZeroRecords": "Nenalezeny žádné odpovídající záznamy", + "srf-ui-datatables-label-oPaginate-sFirst": "První", + "srf-ui-datatables-label-oPaginate-sLast": "Poslední", + "srf-ui-datatables-label-oPaginate-sNext": "Další", + "srf-ui-datatables-label-oPaginate-sPrevious": "Předchozí", + "srf-ui-datatables-label-oAria-sSortAscending": ": po aktivaci bude sloupec seřazen vzestupně", + "srf-ui-datatables-label-oAria-sSortDescending": ": po aktivaci bude sloupec seřazen sestupně", "srf-printername-tree": "Strom", + "srf-printername-ultree": "Funkce UItree", + "srf-printername-oltree": "Funkce OItree", + "srf-tree-noparentprop": "není zadána nadřízená vlastnost. Strom nelze vytvořit bez určené nadřízené vlastnosti.", + "srf-tree-rootinvalid": "$1 není platný nadpis stránky.", + "srf-tree-circledetected": "Při pokusu o vložení $1 do stromu byl rozpoznán kruhový odkaz.", + "srf-paramdesc-tree-parent": "Vlastnost obsahující nadřízenou stránku", + "srf-paramdesc-tree-root": "Kořenová stránka stromu", + "srf-paramdesc-tree-startlevel": "Počáteční úroveň stromu, např. pro integraci do jiného stromu", "srf-printername-slideshow": "Prezentace", + "srf-paramdesc-delay": "Prodleva mezi snímky v sekundách", + "srf-paramdesc-navigation-controls": "Zobrazit navigační prvky nebo ne", + "srf-paramdesc-effect": "Efekt použitý při přechodu ze snímku na další snímek", + "srf-printername-filtered": "Filtrováno", + "srf-paramdesc-filtered-views": "Pohledy, které mají být dostupné v zobrazení výsledků.", + "srf-paramdesc-filtered-filter-position": "Pozice filtrů ve vztahu k zobrazením. Povolené hodnoty: \"top\", \"bottom\". Default: \"top\".", + "srf-paramdesc-filtered-list-type": "Typ seznamu. Povolené hodnoty: \"list\", \"ul\", \"ol\". Výchozí: \"list\".", + "srf-paramdesc-filtered-list-template": "Šablona, která bude použita pro formátování položek seznamu.", + "srf-paramdesc-filtered-list-named-args": "Název argumentu předaného do šablony.", + "srf-paramdesc-filtered-list-introtemplate": "Název šablony, která se zobrazí před výsledky dotazu, pokud nějaké jsou.", + "srf-paramdesc-filtered-list-outrotemplate": "Název šablony, která se zobrazí za výsledky dotazu, pokud nějaké jsou.", + "srf-paramdesc-filtered-calendar-start": "Výpis obsahující počáteční datum události.", + "srf-paramdesc-filtered-calendar-end": "Výpis obsahující koncové datum události.", + "srf-paramdesc-filtered-calendar-title": "Výpis obsahující nadpis události. Nelze použít spolu se šablonou nadpisu.", + "srf-paramdesc-filtered-calendar-title-template": "Šablona použitá k formátování nadpisu událostí v kalendáři.", + "srf-paramdesc-filtered-map-position": "Výpis obsahující geografické umístění.", + "srf-paramdesc-filtered-map-icon": "Výpis určující použitou ikonu mapy.", + "srf-paramdesc-filtered-map-icons": "Mapování hodnot na ikony, které bude použito pro tyto hodnoty v mapě.", + "srf-paramdesc-filtered-map-height": "Výška mapy.", + "srf-paramdesc-filtered-map-zoom": "Úroveň přiblížení při načtení mapy. Uživatel může tento údaj změnit.
Viz: minimální přiblížení zobrazení mapy a maximální přiblížení zobrazení mapy", + "srf-paramdesc-filtered-map-min-zoom": "Minimální volitelná úroveň přiblížení mapy", + "srf-paramdesc-filtered-map-max-zoom": "Maximální volitelná úroveň přiblížení mapy", + "srf-paramdesc-filtered-map-marker-cluster": "Vypne nebo zapne shlukování značek.", + "srf-paramdesc-filtered-map-marker-cluster-max-zoom": "Maximální úroveň přiblížení, ve které jsou značky mapě stále ve shlucích. Nad touto úrovní budou značky vždy samostatně.", + "srf-paramdesc-filtered-map-marker-cluster-max-radius": "Maximální poloměr oblasti zabrané shlukem od centrální značky (v obrazových bodech). Výchozí hodnota je 80.", + "srf-paramdesc-filtered-map-marker-cluster-zoom-on-click": "Je-li zapnuto, shluk se po kliknutí zvětší tak, že pokryje celou zobrazenou plochu.", "srf-filtered-selectorlabel-list": "Seznam", + "srf-filtered-selectorlabel-table": "Tabulka", "srf-filtered-selectorlabel-calendar": "Kalendář", + "srf-filtered-selectorlabel-map": "Mapa", + "srf-filtered-noscript-error": "Výsledky nelze zobrazit, protože není zapnutý Javascript. Přejděte na $1.", + "srf-filtered-noscript-link-caption": "výsledky v tabulkovém formátu", + "srf-filtered-map-provider-missing-error": "Pro zobrazení mapy není zadán žádný poskytovatel.", + "srf-filtered-value-filter-and": "A", + "srf-filtered-value-filter-or": "NEBO", + "srf-filtered-value-filter-placeholder": "Vyberte hodnotu filtru", + "srf-printername-d3chart": "Graf D3", + "srf-printername-timeseries": "Graf časových řad", + "srf-paramdesc-group": "Řady seskupeny podle", "srf-paramdesc-zoom": "Povolit přiblížení", + "srf-paramdesc-datatable": "Povolit tabulku dat", + "srf-timeseries-zoom-out-of-range": "V rozsahu přiblížení nejsou žádná dostatečná data", + "srf-printername-sparkline": "Minigraf", + "srf-printername-listwidget": "Widget seznamu", + "srf-paramdesc-listtype": "Určuje typ seznamu", + "srf-paramdesc-widget": "Dostupný widget", "srf-paramdesc-pageitems": "Položek na stránku", + "srf-printername-eventcalendar": "Kalendář událostí", "srf-paramdesc-calendarfirstday": "Den, kterým začíná týden", "srf-paramdesc-calendardefaultview": "Výchozí zobrazení po načtení kalendáře", + "srf-paramdesc-calendarstart": "Začátek kalendáře (hodnoty typu datum nebo datum/čas)", + "srf-paramdesc-calendarlegend": "Určuje umístění legendy a volby přiřazeného filtru", + "srf-paramdesc-dayview": "Aktivovat zobrazení dne kliknutím na číslo dne", "srf-ui-eventcalendar-label-today": "Dnes", "srf-ui-eventcalendar-label-month": "Měsíc", "srf-ui-eventcalendar-label-week": "Týden", "srf-ui-eventcalendar-label-day": "Den", + "srf-ui-eventcalendar-label-listmonth": "Měsíc (seznam)", + "srf-ui-eventcalendar-label-listweek": "Týden (seznam)", + "srf-ui-eventcalendar-label-listday": "Den (seznam)", "srf-ui-eventcalendar-label-allday": "Celý den", + "srf-ui-eventcalendar-label-update-success": "Aktualizace kalendáře událostí byla úspěšná.", + "srf-ui-eventcalendar-label-update-error": "Aktualizace kalendáře událostí selhala.", + "srf-ui-eventcalendar-click-popup": "Chcete vytvořit událost?", + "srf-printername-dygraphs": "Graf knihovny Dygraphs", + "srf-paramdesc-datasource": "Zdroj, ze kterého jsou data dostupná. Povolené hodnoty: \"file\", \"raw\" a \"url\". Výchozí hodnota: \"file\"", + "srf-paramdesc-errorbar": "Použitá chybová úsečka. Povolené hodnoty: \"fraction\" (intervaly důvěry pro hodnoty), \"sigma\" (standardní odchylka hodnot) a \"range\" (vlastní rozsah hodnot)", + "srf-paramdesc-movingaverage": "Zobrazí průměr za počet dnů (0 = žádný klouzavý průměr)", "srf-paramdesc-yaxislabel": "Popisek, který se objeví na ose y", "srf-paramdesc-xaxislabel": "Popisek, který se objeví na ose x", + "srf-paramdesc-unit": "Jednotka", + "srf-printername-pagewidget": "Widget stránky", + "srf-printername-incoming": "Příchozí vlastnosti", + "srf-paramdesc-count": "Určuje, zda má být evidován počet příchozích vlastností", + "srf-paramdesc-min": "Minimální nebo prahová hodnota", + "srf-paramdesc-excludeproperty": "Vyloučit vlastnost ze sady výsledků", + "srf-printername-media": "Přehrávač médií", + "srf-paramdesc-mediainspector": "Zobrazuje detailní informace o zadaném mediálním prvku", "srf-ui-mediaplayer-label-previous": "Předchozí", "srf-ui-mediaplayer-label-play": "Přehrát", "srf-ui-mediaplayer-label-pause": "Pozastavit", "srf-ui-mediaplayer-label-next": "Následujicí", "srf-ui-mediaplayer-label-stop": "Zastavit", + "srf-ui-mediaplayer-label-mute": "Ztlumit", + "srf-ui-mediaplayer-label-unmute": "Zapnout zvuk", + "srf-ui-mediaplayer-label-volume-max": "Maximální hlasitost", + "srf-ui-mediaplayer-label-shuffle": "Náhodné přehrávání", + "srf-ui-mediaplayer-label-shuffle-off": "Vypnout náhodné přehrávání", "srf-ui-mediaplayer-label-repeat": "Opakovat", - "srf-ui-mediaplayer-label-full-screen": "Celá obrazovka" + "srf-ui-mediaplayer-label-repeat-off": "Vypnout opakování", + "srf-ui-mediaplayer-label-full-screen": "Celá obrazovka", + "srf-ui-mediaplayer-label-restore-screen": "Obnovit obrazovku", + "srf-spreadsheet-link": "Tabulka", + "srf-paramdesc-spreadsheet-filename": "Formát vytvořeného souboru. Povolené hodnoty: xlsx, xls, ods, csv. Výchozí hodnota: xlsx", + "srf-paramdesc-spreadsheet-templatefile": "Název tabulkového souboru z prostoru názvů ''Soubor'', který je použit k formátování vytvořeného souboru", + "srf-paramdesc-icalendar-timezone": "Seznam časových pásem oddělených čárkami" } diff --git a/www/wiki/extensions/SemanticResultFormats/i18n/da.json b/www/wiki/extensions/SemanticResultFormats/i18n/da.json new file mode 100644 index 00000000..339b43b4 --- /dev/null +++ b/www/wiki/extensions/SemanticResultFormats/i18n/da.json @@ -0,0 +1,99 @@ +{ + "@metadata": { + "authors": [ + "Saederup92", + "Kranix" + ] + }, + "srf-module-loading": "Indlæser...", + "srf-paramdesc-height": "Højde", + "srf-paramdesc-width": "Bredde", + "srf-navigation-previous": "Forrige", + "srf-ui-navigation-prev": "Forrige", + "srf-ui-navigation-next": "Næste", + "srf-ui-common-label-source": "Kilde", + "srf-ui-common-label-datasource": "Datakilde", + "srf-ui-tooltip-title-scope": "Afgrænsning", + "srf-ui-tooltip-title-filter": "Filtrer", + "srf-ui-common-label-parameters": "Parametre", + "srf-ui-common-label-query": "Forespørgsel", + "srfc_previousmonth": "Forrige måned", + "srfc_nextmonth": "Næste måned", + "srfc_today": "I dag", + "srfc_gotomonth": "Gå til måned", + "srf_printername_calendar": "Månedlig kalender", + "srf_outline_novalue": "Ingen værdi", + "srf_printername_sum": "Sum af tal", + "srf_printername_average": "Gennemsnit af tal", + "srf_printername_max": "Højeste tal", + "srf_printername_min": "Mindste tal", + "srf_printername_earliest": "Tidligste tidspunkt", + "srf_printername_latest": "Seneste tidspunkt", + "srf_printername_timeline": "Tidslinje", + "srf_paramdesc_timelinesize": "Højden på tidslinjen", + "srf_paramdesc_charttitle": "Titlen på diagrammet", + "srf-ui-gridview-label-item": "Dataelement", + "srf-ui-gridview-label-value": "Dataværdi", + "srf-ui-gridview-label-series": "Datarække", + "srf-ui-gridview-label-chart-tab": "Diagram", + "srf-ui-gridview-label-data-tab": "Data", + "srf-ui-gridview-label-info-tab": "Info", + "srf_printername_gallery": "Galleri", + "srf_paramdesc_widths": "Bredden på billederne", + "srf_paramdesc_heights": "Højden på billederne", + "srf-gallery-navigation-previous": "Forrige", + "srf-gallery-navigation-next": "Næste", + "srf-gallery-overlay-count": "Billede $1 af $2", + "srf-gallery-image-url-error": "Billedet blev ikke fundet.", + "srf_printername_array": "Array", + "srf-printername-graph": "Graf", + "srf-ui-datatables-label-parameters": "Parametre", + "srf-ui-datatables-label-information": "Information", + "srf-ui-datatables-label-placeholder-column-search": "Søg...", + "srf-ui-datatables-label-multiselect-column-header": "Tilgængelige kolonner", + "srf-ui-datatables-label-multiselect-column-selectedtext": "Kolonner er synlige", + "srf-ui-datatables-label-sLoadingRecords": "Indlæser...", + "srf-ui-datatables-label-sProcessing": "Bearbejder...", + "srf-ui-datatables-label-sSearch": "Søg:", + "srf-ui-datatables-label-oPaginate-sFirst": "Første", + "srf-ui-datatables-label-oPaginate-sLast": "Sidste", + "srf-ui-datatables-label-oPaginate-sNext": "Næste", + "srf-ui-datatables-label-oPaginate-sPrevious": "Forrige", + "srf-printername-tree": "Træ", + "srf-printername-filtered": "Filtreret", + "srf-paramdesc-filtered-map-height": "Korthøjden", + "srf-filtered-selectorlabel-list": "Liste", + "srf-filtered-selectorlabel-table": "Tabel", + "srf-filtered-selectorlabel-calendar": "Kalender", + "srf-filtered-selectorlabel-map": "Kort", + "srf-filtered-value-filter-and": "OG", + "srf-filtered-value-filter-or": "ELLER", + "srf-paramdesc-zoom": "Aktiver zoom", + "srf-paramdesc-datatable": "Aktiver en datatabel", + "srf-paramdesc-widget": "Tilgængelig widget", + "srf-printername-eventcalendar": "Begivenhedskalender", + "srf-paramdesc-calendarfirstday": "Første dag i ugen", + "srf-ui-eventcalendar-label-today": "I dag", + "srf-ui-eventcalendar-label-month": "Måned", + "srf-ui-eventcalendar-label-week": "Uge", + "srf-ui-eventcalendar-label-day": "Dag", + "srf-ui-eventcalendar-label-listmonth": "Måned (liste)", + "srf-ui-eventcalendar-label-listweek": "Uge (liste)", + "srf-ui-eventcalendar-label-listday": "Dag (liste)", + "srf-ui-eventcalendar-label-allday": "Hele dagen", + "srf-ui-eventcalendar-click-popup": "Ønsker du at oprette en begivenhed?", + "srf-paramdesc-unit": "Enhed", + "srf-printername-media": "Medieafspiller", + "srf-ui-mediaplayer-label-previous": "Forrige", + "srf-ui-mediaplayer-label-play": "Afspil", + "srf-ui-mediaplayer-label-pause": "Sæt på pause", + "srf-ui-mediaplayer-label-next": "Næste", + "srf-ui-mediaplayer-label-stop": "Stop", + "srf-ui-mediaplayer-label-mute": "Slå lyd fra", + "srf-ui-mediaplayer-label-unmute": "Slå lyd til", + "srf-ui-mediaplayer-label-repeat": "Gentag", + "srf-ui-mediaplayer-label-full-screen": "Fuldskærm", + "srf-spreadsheet-link": "Regneark", + "srf-paramdesc-gantt-diagramtitle": "Diagramnavn", + "srf-paramdesc-gantt-diagramtheme": "Diagramtema" +} diff --git a/www/wiki/extensions/SemanticResultFormats/i18n/de.json b/www/wiki/extensions/SemanticResultFormats/i18n/de.json index 8f8c0298..a1e7d363 100644 --- a/www/wiki/extensions/SemanticResultFormats/i18n/de.json +++ b/www/wiki/extensions/SemanticResultFormats/i18n/de.json @@ -14,7 +14,6 @@ ] }, "srf-desc": "Stellt zusätzliche Ergebnisformate bereit", - "srf-name": "Semantic Result Formats", "prefs-srf": "Erweiterung „Semantic Result Formats“", "srf-prefs-intro-text": "Die Erweiterung „Semantic Result Formats“ ist installiert. Besuch bitte die [https://www.semantic-mediawiki.org/wiki/Help:Ergebnisformate Hilfeseite zu Ergebnisformaten] für weitere Informationen.", "prefs-srf-eventcalendar-options": "Optionen zum Veranstaltungskalender", @@ -270,7 +269,6 @@ "srf-filtered-noscript-error": "Die Ergebnisse können nicht angezeigt werden, da JavaScript nicht aktiviert ist. Zu „$1“ gehen.", "srf-filtered-noscript-link-caption": "Ergebnisse in Form einer Tabelle", "srf-filtered-map-provider-missing-error": "Für die Kartenansicht wurde kein Kartenanbieter angegeben.", - "srf-filtered-map-geocoordinateparser-missing-error": "Der Parser für Geokoordinaten wurde nicht gefunden. Die Bibliothek „data-values/geo“ muss hierzu installiert werden.", "srf-filtered-value-filter-and": "UND", "srf-filtered-value-filter-or": "ODER", "srf-filtered-value-filter-placeholder": "Einen Wert zum Filtern auswählen", @@ -299,10 +297,10 @@ "srf-ui-eventcalendar-label-listweek": "Woche (Liste)", "srf-ui-eventcalendar-label-listday": "Tag (Liste)", "srf-ui-eventcalendar-label-allday": "Gesamter Tag", - "srf-ui-eventcalendar-format-title-week": "d. [ MMM] [ yyyy]{ '—'d. MMM yyyy}", - "srf-ui-eventcalendar-format-title-day": "dddd, d. MMM yyyy", - "srf-ui-eventcalendar-format-column-week": "ddd d.M.", - "srf-ui-eventcalendar-format-column-day": "dddd d.M.", + "srf-ui-eventcalendar-format-title-week": "dddd, D. MMM YYYY", + "srf-ui-eventcalendar-format-title-day": "D. MMMM YYYY", + "srf-ui-eventcalendar-format-column-week": "ddd., DD.MM.", + "srf-ui-eventcalendar-format-column-day": "ddd., DD.MM.", "srf-ui-eventcalendar-label-update-success": "Der Kalender wurde erfolgreich aktualisiert.", "srf-ui-eventcalendar-label-update-error": "Die Aktualisierung der Kalenders ist gescheitert.", "srf-ui-eventcalendar-click-popup": "Soll ein Termin erstellt werden?", @@ -334,9 +332,24 @@ "srf-ui-mediaplayer-label-repeat-off": "Wiederholen beenden", "srf-ui-mediaplayer-label-full-screen": "Vollbild", "srf-ui-mediaplayer-label-restore-screen": "Bildschirm wiederherstellen", - "srf-excel-link": "Excel", - "srf-excel-missing-phpexcel": "Die Daten konnten nicht für MS-Excel exportiert werden, da die notwendige Softwareerweiterung [https://www.mediawiki.org/wiki/Extension:PHPExcel PHPExcel] nicht installiert ist.", - "srf-paramdesc-excel-filename": "Der Dateiname für die zum Herunterladen erzeugte Datei", - "srf-paramdesc-excel-templatefile": "Der Name einer MS-Excel-Datei aus dem Namensraum „Datei“, der zur Formatierung der erzeugten Datei verwendet werden soll", - "srf-paramdesc-icalendar-timezone": "Legt eine durch Kommata getrennte Liste mit Zeitzonen fest" + "srf-spreadsheet-link": "Datei (Tabellenkalkulation)", + "srf-paramdesc-spreadsheet-filename": "Legt fest, welchen Namen die zu erstellende Datei haben soll", + "srf-paramdesc-spreadsheet-fileformat": "Legt fest, welches Dateiformat die zu erstellende Datei haben soll. Mögliche Werte: xlsx, xls, ods, csv", + "srf-paramdesc-spreadsheet-templatefile": "Legt fest, wie die Tabellenkalkulationsdatei im Namensraum ''Datei'' heißt, die zur Formatierung der zu erstellende Datei verwendet werden soll.", + "srf-paramdesc-icalendar-timezone": "Legt eine durch Kommata getrennte Liste mit Zeitzonen fest", + "srf-paramdesc-gantt-diagramtitle": "Name des Diagramms", + "srf-paramdesc-gantt-diagramtheme": "Farbmotiv des Diagrams", + "srf-paramdesc-gantt-axisformat": "Formatierung der X-Achse", + "srf-paramdesc-gantt-sortkey": "Sortierschlüssel", + "srf-paramdesc-gantt-titletopmargin": "Obere Abstand des Diagrammtitels", + "srf-paramdesc-gantt-barheight": "Höhe der Tasks", + "srf-paramdesc-gantt-leftpadding": "Breite des Titels der Sections", + "srf-paramdesc-gantt-bargap": "Vertikaler Abstand der Tasks", + "srf-error-gantt-mapping-assignment": "Falsche Zuweisung der Werte in '''$1'''", + "srf-error-gantt-mapping-keywords": "Der verwendete Schlüssel in deinem Mapping wird nicht unterstüzt", + "srf-error-gantt-theme": "Diese '''theme''' wird nicht unterstützt", + "srf-error-gantt-sortkey": "Dieser '''sortkey''' wird nicht unterstützt", + "srf-error-gantt-mermaid-not-installed": "Die Mermaid Extension muss installiert sein.", + "srf-printername-gantt": "Diagramm (Gantt)", + "srf-paramdesc-nodelabel": "Legt fest, ob die Knoten der Grafik ein Beschriftung erhalten sollen. Möglicher Wert: displaytitle" } diff --git a/www/wiki/extensions/SemanticResultFormats/i18n/dsb.json b/www/wiki/extensions/SemanticResultFormats/i18n/dsb.json index 48f1e2b2..86c407d1 100644 --- a/www/wiki/extensions/SemanticResultFormats/i18n/dsb.json +++ b/www/wiki/extensions/SemanticResultFormats/i18n/dsb.json @@ -6,7 +6,6 @@ ] }, "srf-desc": "Pśidatne formaty za rědowe wótpšašanja Semantic MediaWiki", - "srf-name": "Formaty semantiskich wuslědkow", "srf-module-loading": "Zacytujo se...", "srf-paramdesc-layout": "K dispoziciji stojecy layout", "srf-paramdesc-height": "Wusokosć (w pikselach)", diff --git a/www/wiki/extensions/SemanticResultFormats/i18n/el.json b/www/wiki/extensions/SemanticResultFormats/i18n/el.json index 18b5de9b..c64272fd 100644 --- a/www/wiki/extensions/SemanticResultFormats/i18n/el.json +++ b/www/wiki/extensions/SemanticResultFormats/i18n/el.json @@ -13,7 +13,6 @@ ] }, "srf-desc": "Πρόσθετες μορφές αποτελεσμάτων για ερωτήματα του Σημασιολογικού MediaWiki", - "srf-name": "Μορφές σημασιολογικών αποτελεσμάτων", "prefs-srf": "Επέκταση:Μορφές σημασιολογικών αποτελεσμάτων", "srf-prefs-intro-text": "Έχετε εγκαταστήσει την επέκταση Μορφές σημασιολογικών αποτελεσμάτων. Για πρόσθετη βοήθεια, παρακαλούμε επισκεφθείτε τη σελίδα βοήθειας σχετικά με τις [https://www.semantic-mediawiki.org/wiki/Help:Result_formats μορφές αποτελεσμάτων].", "prefs-srf-eventcalendar-options": "Επιλογές ημερολογίου εκδηλώσεων", @@ -266,7 +265,5 @@ "srf-ui-mediaplayer-label-repeat-off": "Απενεργοποίηση επανάληψης", "srf-ui-mediaplayer-label-full-screen": "Πλήρης οθόνη", "srf-ui-mediaplayer-label-restore-screen": "Επαναφορά οθόνης", - "srf-excel-link": "Excel", - "srf-excel-missing-phpexcel": "Δεν ήταν δυνατή η εξαγωγή για MS-Excel, επειδή η επέκταση [https://www.mediawiki.org/wiki/Extension:PHPExcel PHPExcel] δεν είναι εγκατεστημένη.", "srf-paramdesc-icalendar-timezone": "Μια λίστα τοπικών ωρών χωρισμένη με κόμμα?" } diff --git a/www/wiki/extensions/SemanticResultFormats/i18n/en.json b/www/wiki/extensions/SemanticResultFormats/i18n/en.json index 163ec637..57c68e5b 100644 --- a/www/wiki/extensions/SemanticResultFormats/i18n/en.json +++ b/www/wiki/extensions/SemanticResultFormats/i18n/en.json @@ -4,8 +4,8 @@ }, "srf-desc": "Additional result formats for Semantic MediaWiki queries", "srf-name": "Semantic Result Formats", - "prefs-srf": "Extension:Semantic Result Formats", - "srf-prefs-intro-text": "You have installed the Semantic Result Formats extension. For additional assistance, please visit the [https://www.semantic-mediawiki.org/wiki/Help:Result_formats result formats] help page.", + "prefs-srf": "Semantic Result Formats", + "srf-prefs-intro-text": "You have installed the Semantic Result Formats extension. Please visit the [https://www.semantic-mediawiki.org/wiki/Help:Result_formats result formats] help page for additional assistance in regards to the user preferences.", "prefs-srf-eventcalendar-options": "Event calendar options", "srf-prefs-eventcalendar-options-update-default": "Enable [https://www.semantic-mediawiki.org/wiki/Help:User_preferences#Automatic_updates automatic updates] of calendar events during page refresh", "srf-prefs-eventcalendar-options-paneview-default": "Enable the pane view by default", @@ -264,7 +264,6 @@ "srf-filtered-noscript-error": "Results can not be displayed because Javascript is not enabled. Go to $1.", "srf-filtered-noscript-link-caption": "results in tabular form", "srf-filtered-map-provider-missing-error": "No map provider specified for \"map\" view.", - "srf-filtered-map-geocoordinateparser-missing-error": "Geocoordinate parser not found. You need to install 'data-values/geo'.", "srf-filtered-value-filter-and": "AND", "srf-filtered-value-filter-or": "OR", "srf-filtered-value-filter-placeholder": "Select a filter value", @@ -297,11 +296,11 @@ "srf-ui-eventcalendar-format-time-agenda": "H:mm( - H:mm)", "srf-ui-eventcalendar-format-axis": "H:mm", "srf-ui-eventcalendar-format-title-month": "MMMM YYYY", - "srf-ui-eventcalendar-format-title-week": "D MMM YYYY", - "srf-ui-eventcalendar-format-title-day": "dddd, D MMM, YYYY", + "srf-ui-eventcalendar-format-title-week": "MMM D, YYYY", + "srf-ui-eventcalendar-format-title-day": "MMMM D, YYYY", "srf-ui-eventcalendar-format-column-month": "ddd", - "srf-ui-eventcalendar-format-column-week": "ddd d/M", - "srf-ui-eventcalendar-format-column-day": "dddd d/M", + "srf-ui-eventcalendar-format-column-week": "ddd M/D", + "srf-ui-eventcalendar-format-column-day": "dddd M/D", "srf-ui-eventcalendar-label-update-success": "The event calendar update was successful.", "srf-ui-eventcalendar-label-update-error": "The event calendar update failed.", "srf-ui-eventcalendar-click-popup": "Do you want to create an event?", @@ -333,9 +332,24 @@ "srf-ui-mediaplayer-label-repeat-off": "Repeat off", "srf-ui-mediaplayer-label-full-screen": "Full screen", "srf-ui-mediaplayer-label-restore-screen": "Restore screen", - "srf-excel-link": "Excel", - "srf-excel-missing-phpexcel": "Could not export for MS-Excel because extension [https://www.mediawiki.org/wiki/Extension:PHPExcel PHPExcel] is not installed.", - "srf-paramdesc-excel-filename": "The download filename for the generated file", - "srf-paramdesc-excel-templatefile": "The name of an Excel file from the ''File'' namespace used for formatting the generated file", - "srf-paramdesc-icalendar-timezone": "A comma separated list of timezones" + "srf-spreadsheet-link": "Spreadsheet", + "srf-paramdesc-spreadsheet-filename": "The filename for the download of the generated spreadsheet file", + "srf-paramdesc-spreadsheet-fileformat": "The format to be produced for the spreadsheet file. Allowed values: xlsx, xls, ods, csv. Default: xlsx", + "srf-paramdesc-spreadsheet-templatefile": "The name of a spreadsheet file from the ''File'' namespace used for formatting the generated file", + "srf-paramdesc-icalendar-timezone": "A comma separated list of timezones", + "srf-paramdesc-gantt-diagramtitle": "Diagram name", + "srf-paramdesc-gantt-diagramtheme": "Diagram theme", + "srf-paramdesc-gantt-axisformat": "X-axis: Date format", + "srf-paramdesc-gantt-sortkey": "Key to sort sections and tasks", + "srf-paramdesc-gantt-titletopmargin": "Top margin of the diagram title", + "srf-paramdesc-gantt-barheight": "Height of task bars", + "srf-paramdesc-gantt-leftpadding": "Width of section title", + "srf-paramdesc-gantt-bargap": "Vertical distance of tasks bars from the margin", + "srf-error-gantt-mapping-assignment": "Wrong assignment of mapped values in '''$1'''", + "srf-error-gantt-mapping-keywords": "The key used in your mapping parameter is not supported", + "srf-error-gantt-theme" : "The '''theme''' you have choosen is not supported", + "srf-error-gantt-sortkey" : "The '''sortkey''' you have choosen is not supported", + "srf-error-gantt-mermaid-not-installed": "Mermaid Extension needs to be installed.", + "srf-printername-gantt": "Gantt", + "srf-paramdesc-nodelabel": "Use a graph node label. Allowed values: displaytitle." } diff --git a/www/wiki/extensions/SemanticResultFormats/i18n/es.json b/www/wiki/extensions/SemanticResultFormats/i18n/es.json index 0554dbcd..c0d46d9c 100644 --- a/www/wiki/extensions/SemanticResultFormats/i18n/es.json +++ b/www/wiki/extensions/SemanticResultFormats/i18n/es.json @@ -25,12 +25,12 @@ "KATRINE1992", "Amaia", "AVIADOR71", - "Yllelder" + "Yllelder", + "Ciencia Al Poder" ] }, "srf-desc": "Formatos de resultados adicionales para consultas de Semantic MediaWiki", - "srf-name": "Formatos de resultado semántico", - "prefs-srf": "Extensión:Formatos de resultados semánticos", + "prefs-srf": "Formatos de resultados semánticos", "srf-prefs-intro-text": "Tienes instalada la extensión para dar formato a los resultados semánticos. Para obtener ayuda adicional, visita la página de ayuda sobre los [https://www.semantic-mediawiki.org/wiki/Help:Result_formats formatos de los resultados].", "prefs-srf-eventcalendar-options": "Opciones de eventos de calendario", "srf-prefs-eventcalendar-options-update-default": "Desactivar [https://www.semantic-mediawiki.org/wiki/Help:User_preferences#Automatic_updates automatic updates] de eventos de calendario durante la recarga de las páginas", @@ -217,7 +217,7 @@ "srf-ui-datatables-label-update-success": "La actualización de la tabla se realizó correctamente", "srf-ui-datatables-label-update-error": "Falló la actualización de la tabla.", "srf-ui-datatables-label-placeholder-column-search": "Buscar…", - "srf-ui-datatables-label-content-cache": "El contenido se deriva de la antememoria local.", + "srf-ui-datatables-label-content-cache": "El contenido se deriva de la caché local.", "srf-ui-datatables-label-content-server": "El contenido se deriva de la del servidor.", "srf-ui-datatables-label-multiselect-column-header": "Columnas disponibles", "srf-ui-datatables-label-multiselect-column-noneselectedtext": "Configuración de filtros", @@ -276,7 +276,6 @@ "srf-filtered-noscript-error": "No se pueden mostrar los resultados porque JavaScript está desactivado. Visita $1.", "srf-filtered-noscript-link-caption": "resultados en formato tabular", "srf-filtered-map-provider-missing-error": "No se especificó ningún proveedor de mapas para la vista «map».", - "srf-filtered-map-geocoordinateparser-missing-error": "No se encontró el analizador de coordenadas geográficas. Es necesario instalar «data-values/geo».", "srf-filtered-value-filter-and": "Y", "srf-filtered-value-filter-or": "O", "srf-filtered-value-filter-placeholder": "Seleccionar un valor de filtro", @@ -335,9 +334,6 @@ "srf-ui-mediaplayer-label-repeat-off": "Desactivar repetición", "srf-ui-mediaplayer-label-full-screen": "Pantalla completa", "srf-ui-mediaplayer-label-restore-screen": "Restaurar la pantalla", - "srf-excel-link": "Excel", - "srf-excel-missing-phpexcel": "No se ha podido exportar a MS-Excel porque la extensión [https://www.mediawiki.org/wiki/Extension:PHPExcel PHPExcel] no está instalada.", - "srf-paramdesc-excel-filename": "El nombre de archivo de descarga del archivo generado", - "srf-paramdesc-excel-templatefile": "El nombre de un archivo de Excel del espacio de nombres «Archivo» utilizado para dar formato al archivo generado", + "srf-paramdesc-spreadsheet-filename": "El nombre de descarga del archivo de hoja de cálculo generado", "srf-paramdesc-icalendar-timezone": "Una lista de husos horarios separados por comas" } diff --git a/www/wiki/extensions/SemanticResultFormats/i18n/eu.json b/www/wiki/extensions/SemanticResultFormats/i18n/eu.json index 22e96534..4c7fd49c 100644 --- a/www/wiki/extensions/SemanticResultFormats/i18n/eu.json +++ b/www/wiki/extensions/SemanticResultFormats/i18n/eu.json @@ -5,10 +5,10 @@ "Subi", "Mikel Ibaiba", "Sator", - "Amaia" + "Amaia", + "Xabier Armendaritz" ] }, - "srf-name": "Emaitza semantikoen formatuak", "prefs-srf": "Emaitza semantikoen formatuak", "prefs-srf-eventcalendar-options": "Gertaera-egutegi aukerak", "srf-module-loading": "Kargatzen...", @@ -68,15 +68,15 @@ "srf-gallery-navigation-next": "Hurrengoa", "srf-gallery-image-url-error": "Irudia ez da aurkitu.", "srf_printername_tagcloud": "Hodei etiketa", - "srf_paramdesc_increase": "Nola handiagotu etiketen tamainia", + "srf_paramdesc_increase": "Nola handiagotu etiketen tamaina", "srf_paramdesc_tagorder": "Etiketen ordena", - "srf_paramdesc_minsize": "Etiketa txikienen tamainia ehunekotan", - "srf_paramdesc_maxsize": "Etiketa handienen tamainia ehunekotan", + "srf_paramdesc_minsize": "Etiketa txikienen tamaina, ehunekotan", + "srf_paramdesc_maxsize": "Etiketa handienen tamaina, ehunekotan", "srf_paramdesc_maxtags": "Etiketa gehienezkoaren kantitatea hodeian", "srf_printername_valuerank": "Balio ranking-a", "srf-printername-graph": "Grafikoa", "srf-paramdesc-graphname": "Grafikoaren izenburua zehazten du", - "srf-paramdesc-graphsize": "Grafikoaren tamainia pixeletan ezartzen du", + "srf-paramdesc-graphsize": "Grafikoak pixeletan duen tamaina ezartzen du", "srf-paramdesc-graphlabel": "Grafikoaren etiketa zehazten du", "srf-paramdesc-rankdir": "Gezien norabidea zehazten du", "srf-paramdesc-graphcolor": "Grafikoaren kolorea zehazten du", diff --git a/www/wiki/extensions/SemanticResultFormats/i18n/fa.json b/www/wiki/extensions/SemanticResultFormats/i18n/fa.json index 31bbff62..049f590f 100644 --- a/www/wiki/extensions/SemanticResultFormats/i18n/fa.json +++ b/www/wiki/extensions/SemanticResultFormats/i18n/fa.json @@ -150,6 +150,5 @@ "srf-ui-mediaplayer-label-shuffle-off": "غیرفعال کردن تصادفی", "srf-ui-mediaplayer-label-repeat": "تکرار", "srf-ui-mediaplayer-label-repeat-off": "خاموش کردن تکرار", - "srf-ui-mediaplayer-label-full-screen": "تمام صفحه", - "srf-excel-link": "اکسل" + "srf-ui-mediaplayer-label-full-screen": "تمام صفحه" } diff --git a/www/wiki/extensions/SemanticResultFormats/i18n/fi.json b/www/wiki/extensions/SemanticResultFormats/i18n/fi.json index 97c81396..beb5b648 100644 --- a/www/wiki/extensions/SemanticResultFormats/i18n/fi.json +++ b/www/wiki/extensions/SemanticResultFormats/i18n/fi.json @@ -8,12 +8,12 @@ "Pyscowicz", "Nemo bis", "Pahkiqaz", - "01miki10" + "01miki10", + "Valtlait" ] }, "srf-desc": "Muita tulosten muotoja Semantic MediaWikin kyselyille", - "srf-name": "Semanttisten tulosten muodot", - "prefs-srf": "Semanttisten tulosten muodot", + "prefs-srf": "Extension:Semanttisten tulosten muodot", "prefs-srf-eventcalendar-options": "Tapahtumakalenterin asetukset", "srf-module-loading": "Ladataan...", "srf-paramdesc-layout": "Asetteluvaihtoehdot", @@ -81,7 +81,7 @@ "srf-paramdesc-valueformat": "Määritä arvojen muotoilusäännöt", "srf-paramdesc-stackseries": "Näytä kaavio pinottuna sarjana", "srf-paramdesc-seriesgroup": "Valitse sarjan ryhmittely", - "srf-paramdesc-gridview": "Näytän kaavio ja datajoukot samanaikaisesti. Sallitut arvot: none ja tabs. Oletusarvo: none.", + "srf-paramdesc-gridview": "Näytän kaavio ja datajoukot samanaikaisesti. Sallitut arvot: \"none\" ja \"tabs\". Oletusarvo: \"none\".", "srf-ui-gridview-label-item": "Kohteen tiedot", "srf-ui-gridview-label-value": "Data-arvo", "srf-ui-gridview-label-series": "Datasarja", @@ -106,7 +106,7 @@ "srf_paramdesc_maxtags": "Tunnisteiden enimmäismäärä pilvessä", "srf_paramdesc_propsep": "Erotin pyydettyjen ominaisuuksien välissä", "srf_paramdesc_manysep": "Erotin moniarvoisen ominaisuuden arvojen välillä", - "srf_paramdesc_headersep": "Erotin ominaisuuden nimen ja arvon välissä, jos \"headers\" on \"show\" tai \"plain\"", + "srf_paramdesc_headersep": "Erotin ominaisuuden nimen ja arvon välissä, jos ”headers” on ”show” tai ”plain”", "srf-printername-graph": "Graafi", "srf-paramdesc-graph-relation": "Ovatko kohteet tai nimiominaisuudet ylätasolla vai alatasolla?", "srf-paramdesc-graphname": "Asettaa taulukon otsikon", @@ -118,6 +118,8 @@ "srf-ui-datatables-label-parameters": "Parametrit", "srf-ui-datatables-label-update-success": "Taulukon päivitys onnistui", "srf-ui-datatables-label-placeholder-column-search": "Hae...", + "srf-ui-datatables-label-sInfo": "Näytetään _START_ - _END_ _TOTAL_ merkinnästä", + "srf-ui-datatables-label-sInfoEmpty": "Näytetään 0-0 0 merkinnästä", "srf-ui-datatables-label-sLoadingRecords": "Ladataan...", "srf-ui-datatables-label-sProcessing": "Käsitellään...", "srf-ui-datatables-label-sSearch": "Haku:", @@ -129,10 +131,11 @@ "srf-printername-ultree": "Puu (luettelo)", "srf-printername-oltree": "Puu (numeroitu)", "srf-tree-noparentprop": "Ylätason ominaisuus puuttuu. Puuta ei voi rakentaa, jos ylätason ominaisuutta ei ole määritetty.", + "srf-tree-rootinvalid": "$1 ei ole kelvollinen sivun nimi.", "srf-printername-slideshow": "Diaesitys", "srf-paramdesc-navigation-controls": "Näytä navigointiohjaimet tai ei", "srf-printername-filtered": "Suodatettu", - "srf-paramdesc-filtered-list-type": "Luettelotyypit. Sallitut arvot: list, ul, ol. Oletusarvo: list.", + "srf-paramdesc-filtered-list-type": "Luettelotyypit. Sallitut arvot: \"list\", \"ul\", \"ol\". Oletusarvo: \"list\".", "srf-paramdesc-filtered-list-template": "Malline, jolla luettelokohdat muotoillaan.", "srf-paramdesc-filtered-list-introtemplate": "Ennen kyselytuloksia näytettävä mallineen nimi, jos sellaista on.", "srf-paramdesc-filtered-list-outrotemplate": "Kyselytulosten jälkeen näytettävä mallineen nimi, jos sellaista on.", @@ -156,7 +159,7 @@ "srf-ui-eventcalendar-label-allday": "Koko päivä", "srf-ui-eventcalendar-click-popup": "Haluatko luoda tapahtuman?", "srf-printername-dygraphs": "Dygraphs-kaavio", - "srf-paramdesc-datasource": "Määritä datan tietolähteen sijainti. Sallitut arvot: \"file\", \"raw\" ja \"url\". Oletusarvo: \"file\"", + "srf-paramdesc-datasource": "Määritä datan tietolähteen sijainti. Sallitut arvot: ”file”, ”raw” ja ”url”. Oletusarvo: ”file”", "srf-paramdesc-yaxislabel": "Y-akselin selite", "srf-paramdesc-xaxislabel": "X-akselin selite", "srf-paramdesc-unit": "Yksikkö", diff --git a/www/wiki/extensions/SemanticResultFormats/i18n/fr.json b/www/wiki/extensions/SemanticResultFormats/i18n/fr.json index f81f1f19..583a71b4 100644 --- a/www/wiki/extensions/SemanticResultFormats/i18n/fr.json +++ b/www/wiki/extensions/SemanticResultFormats/i18n/fr.json @@ -27,9 +27,8 @@ ] }, "srf-desc": "Formats additionnels d'affichage de résultat pour les requêtes de Semantic MediaWiki", - "srf-name": "Formats des résultats sémantiques", - "prefs-srf": "Formats des résultats Extension:Semantic", - "srf-prefs-intro-text": "Vous avez installé l'extension Formats de résultats sémantiques. Pour plus d’aide, veuillez consulter la page [https://www.semantic-mediawiki.org/wiki/Help:Result_formats formats de résultat].", + "prefs-srf": "Formats de résultat sémantique", + "srf-prefs-intro-text": "Vous avez installé l’extension Formats de résultats sémantiques. Veuillez consulter la page [https://www.semantic-mediawiki.org/wiki/Help:Result_formats formats de résultat] pour une aide supplémentaire en fonction des préférences utilisateur.", "prefs-srf-eventcalendar-options": "Options du calendrier d’événements", "srf-prefs-eventcalendar-options-update-default": "Activer les [https://www.semantic-mediawiki.org/wiki/Help:User_preferences#Automatic_updates mises à jour automatiques] des événements du calendrier lors d’un rafraîchissement de page", "srf-prefs-eventcalendar-options-paneview-default": "Activer l’affichage du volet par défaut", @@ -285,7 +284,6 @@ "srf-filtered-noscript-error": "Les résultats ne peuvent pas être affichés parce que Javascript n’est pas activé. Allez à $1.", "srf-filtered-noscript-link-caption": "résultats sous forme tabulée", "srf-filtered-map-provider-missing-error": "Aucun fournisseur de carte spécifié pour la vue « carte ».", - "srf-filtered-map-geocoordinateparser-missing-error": "Analyseur de coordonnées géographiques non trouvé. Vous devez installer 'data-values/geo'.", "srf-filtered-value-filter-and": "ET", "srf-filtered-value-filter-or": "OU", "srf-filtered-value-filter-placeholder": "Selectionner une valeur de filtre", @@ -345,9 +343,24 @@ "srf-ui-mediaplayer-label-repeat-off": "Répétition désactivée", "srf-ui-mediaplayer-label-full-screen": "Plein écran", "srf-ui-mediaplayer-label-restore-screen": "Rétablir l'écran", - "srf-excel-link": "Excel", - "srf-excel-missing-phpexcel": "Impossible d’exporter vers MS-Excel car l’extension [https://www.mediawiki.org/wiki/Extension:PHPExcel PHPExcel] n’est pas installée.", - "srf-paramdesc-excel-filename": "Nom cible du fichier téléchargé correspondant au fichier généré", - "srf-paramdesc-excel-templatefile": "Le nom d'un fichier Excel à partir de l'espace de noms \"File\" utilisé pour la mise en forme du fichier généré", - "srf-paramdesc-icalendar-timezone": "Une liste de fuseaux horaires séparés par des virgules" + "srf-spreadsheet-link": "Feuille de calcul", + "srf-paramdesc-spreadsheet-filename": "Le nom du fichier pour le téléchargement du fichier classeur généré", + "srf-paramdesc-spreadsheet-fileformat": "Le format à produire pour le fichier classeur. Valeurs autorisées : xlsx, xls, ods, csv. Par défaut : xlsx", + "srf-paramdesc-spreadsheet-templatefile": "Le nom d’un fichier feuille de calcul de l’espace de noms « Fichier » utilisé pour mettre en forme le fichier généré", + "srf-paramdesc-icalendar-timezone": "Une liste de fuseaux horaires séparés par des virgules", + "srf-paramdesc-gantt-diagramtitle": "Nom du diagramme", + "srf-paramdesc-gantt-diagramtheme": "Thème du diagramme", + "srf-paramdesc-gantt-axisformat": "Axe X : Format de la date", + "srf-paramdesc-gantt-sortkey": "Clé pour trier les sections et les tâches", + "srf-paramdesc-gantt-titletopmargin": "Marge en haut du titre du diagramme", + "srf-paramdesc-gantt-barheight": "Hauteur des barres de tâche", + "srf-paramdesc-gantt-leftpadding": "Largeur du titre de la section", + "srf-paramdesc-gantt-bargap": "Distance verticale des barres de tâche depuis la marge", + "srf-error-gantt-mapping-assignment": "Mauvaise affectation des valeurs associées dans '''$1'''", + "srf-error-gantt-mapping-keywords": "La clé utilisée dans votre paramètre de correspondance n’est pas supportée", + "srf-error-gantt-theme": "Le '''thème''' que vous avez choisi n’est pas supporté", + "srf-error-gantt-sortkey": "La '''clé de tri''' que vous avez choisie n’est pas supportée", + "srf-error-gantt-mermaid-not-installed": "L’extension Mermaid doit être installée.", + "srf-printername-gantt": "Gantt", + "srf-paramdesc-nodelabel": "Utiliser un libellé de nœud de graphe. Valeurs autorisées : displaytitle." } diff --git a/www/wiki/extensions/SemanticResultFormats/i18n/gl.json b/www/wiki/extensions/SemanticResultFormats/i18n/gl.json index 445f1736..34160f92 100644 --- a/www/wiki/extensions/SemanticResultFormats/i18n/gl.json +++ b/www/wiki/extensions/SemanticResultFormats/i18n/gl.json @@ -11,7 +11,6 @@ ] }, "srf-desc": "Formatos de resultados adicionais para as pescudas de Semantic MediaWiki", - "srf-name": "Formatos dos resultados semánticos", "prefs-srf": "Extensiónː Formatos dos resultados semánticos", "srf-prefs-intro-text": "Ten instalada a extensión para dar formato aos resultados semánticos, pero nestes intres non ten opcións de personalización. Para obter axuda aducuibak, visite a páxina de axuda sobre os [https://www.semantic-mediawiki.org/wiki/Help:Result_formats formatos dos rexultados].", "prefs-srf-eventcalendar-options": "Opcións de calendario de eventos", @@ -266,7 +265,6 @@ "srf-filtered-noscript-error": "Os resultados non poden visualizarse porque o Javascript non está activado. Vaia a $1.", "srf-filtered-noscript-link-caption": "resultados en forma de táboa", "srf-filtered-map-provider-missing-error": "Non se indicou o provedor de mapa para a vista \"mapa\".", - "srf-filtered-map-geocoordinateparser-missing-error": "Non foi atopado o analizador de xeocoordenadas. Precisa instalar 'data-values/geo'.", "srf-filtered-value-filter-and": "E", "srf-filtered-value-filter-or": "OU", "srf-filtered-value-filter-placeholder": "Seleccionar un valor de filtro", @@ -326,9 +324,6 @@ "srf-ui-mediaplayer-label-repeat-off": "Repetición desactivada", "srf-ui-mediaplayer-label-full-screen": "Pantalla completa", "srf-ui-mediaplayer-label-restore-screen": "Restablecer a pantalla", - "srf-excel-link": "Excel", - "srf-excel-missing-phpexcel": "Non pode exportarse a MS-Excel porque a extensión [https://www.mediawiki.org/wiki/Extension:PHPExcel PHPExcel] non está instalada.", - "srf-paramdesc-excel-filename": "O nome do ficheiro descargado correspondente ó ficheiro xerado", - "srf-paramdesc-excel-templatefile": "O nome dun ficheiro Excel do espazo de nomes \"Ficheiro\" usado para formatar o ficheiro xerado", + "srf-spreadsheet-link": "Folla de cálculo", "srf-paramdesc-icalendar-timezone": "Unha lista de fusos horarios separados por comas" } diff --git a/www/wiki/extensions/SemanticResultFormats/i18n/he.json b/www/wiki/extensions/SemanticResultFormats/i18n/he.json index 2ea809d7..7555c301 100644 --- a/www/wiki/extensions/SemanticResultFormats/i18n/he.json +++ b/www/wiki/extensions/SemanticResultFormats/i18n/he.json @@ -8,12 +8,12 @@ "Inkbug", "Guycn2", "Nemo bis", - "המקיסט" + "המקיסט", + "Steeve815" ] }, "srf-desc": "תבניות תוצאה נוספות לשאילתות של מדיה־ויקי סמנטית", - "srf-name": "תבניות של תוצאות סמנטיות", - "prefs-srf": "תבניות תוצאה סמנטיות", + "prefs-srf": "הרחבה:תבניות תוצאה סמנטיות", "srf-prefs-intro-text": "התקנת את ההרחבה תבניות תוצאה סמנטיות (Semantic Result Formats). לעזרה נוספת נא לבקר בדף העזרה [https://www.semantic-mediawiki.org/wiki/Help:Result_formats result formats].", "prefs-srf-eventcalendar-options": "אפשרויות יומן אירועים", "srf-prefs-eventcalendar-options-update-default": "הפעלת [https://www.semantic-mediawiki.org/wiki/Help:User_preferences#Automatic_updates עדכונים אוטומטיים] של אירועים ביומן בזמן רענון דף", @@ -288,9 +288,5 @@ "srf-ui-mediaplayer-label-repeat": "חזרה", "srf-ui-mediaplayer-label-repeat-off": "ביטול חזרה", "srf-ui-mediaplayer-label-full-screen": "מסך מלא", - "srf-ui-mediaplayer-label-restore-screen": "שחזור מסך", - "srf-excel-link": "אקסל", - "srf-excel-missing-phpexcel": "ייצוא למיקרוסופט אקסל לא יכול להתבצע, כי ההרחבה [https://www.mediawiki.org/wiki/Extension:PHPExcel PHPExcel] אינה מותקנת.", - "srf-paramdesc-excel-filename": "השם לקובץ המחולל להורדה", - "srf-paramdesc-excel-templatefile": "שם קובץ האקסל מתוך מרחב קובץ לעיצוב הקובץ המחולל" + "srf-ui-mediaplayer-label-restore-screen": "שחזור מסך" } diff --git a/www/wiki/extensions/SemanticResultFormats/i18n/hsb.json b/www/wiki/extensions/SemanticResultFormats/i18n/hsb.json index 278ff137..2929c46e 100644 --- a/www/wiki/extensions/SemanticResultFormats/i18n/hsb.json +++ b/www/wiki/extensions/SemanticResultFormats/i18n/hsb.json @@ -6,7 +6,6 @@ ] }, "srf-desc": "Přidatne formaty za wotprašowanja Semantic MediaWiki", - "srf-name": "Formaty semantiskich wuslědkow", "prefs-srf": "Formaty semantiskich wuslědkow", "srf-module-loading": "Začituje so...", "srf-paramdesc-layout": "K dispoziciji stejacy layout", @@ -197,6 +196,5 @@ "srf-paramdesc-excludeproperty": "Kajkosć z wuslědka wuzamknyć", "srf-ui-mediaplayer-label-play": "Wothrać", "srf-ui-mediaplayer-label-repeat": "Wospjetować", - "srf-ui-mediaplayer-label-full-screen": "Połna wobrazowka", - "srf-excel-link": "Excel" + "srf-ui-mediaplayer-label-full-screen": "Połna wobrazowka" } diff --git a/www/wiki/extensions/SemanticResultFormats/i18n/hu.json b/www/wiki/extensions/SemanticResultFormats/i18n/hu.json index df7cd49d..d86c56d8 100644 --- a/www/wiki/extensions/SemanticResultFormats/i18n/hu.json +++ b/www/wiki/extensions/SemanticResultFormats/i18n/hu.json @@ -11,7 +11,6 @@ ] }, "srf-desc": "További formátumok a Szemantikus MediaWiki beépített lekérdezéseihez", - "srf-name": "Szemantikus eredményformátumok", "srf-module-loading": "Betöltés…", "srf-paramdesc-height": "Magasság", "srf-paramdesc-width": "Szélesség", diff --git a/www/wiki/extensions/SemanticResultFormats/i18n/ia.json b/www/wiki/extensions/SemanticResultFormats/i18n/ia.json index a2f5c757..3e83dc8b 100644 --- a/www/wiki/extensions/SemanticResultFormats/i18n/ia.json +++ b/www/wiki/extensions/SemanticResultFormats/i18n/ia.json @@ -6,7 +6,6 @@ ] }, "srf-desc": "Additional formatos de resultato pro consultas de Semantic MediaWiki", - "srf-name": "Formatos de resultatos semantic", "srf-paramdesc-height": "Altitude", "srf-paramdesc-width": "Latitude", "srf-paramdesc-class": "Specifica un classe de stilos in cascada (CSS) additional", diff --git a/www/wiki/extensions/SemanticResultFormats/i18n/it.json b/www/wiki/extensions/SemanticResultFormats/i18n/it.json index 257515c2..5813566d 100644 --- a/www/wiki/extensions/SemanticResultFormats/i18n/it.json +++ b/www/wiki/extensions/SemanticResultFormats/i18n/it.json @@ -14,7 +14,6 @@ ] }, "srf-desc": "Formati addizionali per i risultati delle query di Semantic Mediawiki", - "srf-name": "Formati di risultato semantici", "prefs-srf-eventcalendar-options": "Opzioni calendario eventi", "prefs-srf-datatables-options": "Opzioni tabelle dati", "srf-module-loading": "Caricamento in corso...", @@ -139,5 +138,12 @@ "srf-paramdesc-yaxislabel": "Descrizione visualizzata sull'asse y", "srf-paramdesc-xaxislabel": "Descrizione visualizzata sull'asse x", "srf-ui-mediaplayer-label-shuffle": "Mescola", - "srf-paramdesc-icalendar-timezone": "Un elenco di fusi orari separati da virgole" + "srf-spreadsheet-link": "Foglio di calcolo", + "srf-paramdesc-spreadsheet-filename": "Il nome del file per il download del foglio di calcolo generato", + "srf-paramdesc-spreadsheet-fileformat": "Il formato file del foglio di calcolo da produrre. Valori ammessi: xlsx, xls, ods, csv. Predefinito: xlsx", + "srf-paramdesc-icalendar-timezone": "Un elenco di fusi orari separati da virgole", + "srf-paramdesc-gantt-diagramtitle": "Nome del diagramma", + "srf-paramdesc-gantt-diagramtheme": "Tema del diagramma", + "srf-paramdesc-gantt-axisformat": "Asse-X: Formato data", + "srf-printername-gantt": "Gantt" } diff --git a/www/wiki/extensions/SemanticResultFormats/i18n/ja.json b/www/wiki/extensions/SemanticResultFormats/i18n/ja.json index 97ad643d..4a977a05 100644 --- a/www/wiki/extensions/SemanticResultFormats/i18n/ja.json +++ b/www/wiki/extensions/SemanticResultFormats/i18n/ja.json @@ -20,7 +20,6 @@ ] }, "srf-desc": "Semantic MediaWiki のクエリのための追加的な結果の形式", - "srf-name": "意味的結果の形式", "prefs-srf": "意味的結果の形式", "prefs-srf-eventcalendar-options": "イベントカレンダーオプション", "srf-prefs-eventcalendar-options-update-default": "ページ更新中のカレンダーイベントの[https://www.semantic-mediawiki.org/wiki/Help:User_preferences#Automatic_updates 自動更新]を有効にする", @@ -227,6 +226,5 @@ "srf-ui-mediaplayer-label-repeat-off": "連続再生解除", "srf-ui-mediaplayer-label-full-screen": "全画面表示", "srf-ui-mediaplayer-label-restore-screen": "全画面表示解除", - "srf-excel-link": "Excel", "srf-paramdesc-icalendar-timezone": "タイムゾーンのカンマ区切りリスト" } diff --git a/www/wiki/extensions/SemanticResultFormats/i18n/ko.json b/www/wiki/extensions/SemanticResultFormats/i18n/ko.json index 6748a5de..f4ade189 100644 --- a/www/wiki/extensions/SemanticResultFormats/i18n/ko.json +++ b/www/wiki/extensions/SemanticResultFormats/i18n/ko.json @@ -11,8 +11,7 @@ ] }, "srf-desc": "시맨틱 미디어위키 쿼리용 추가 결과 포맷", - "srf-name": "시맨틱 결과 포맷", - "prefs-srf": "Extension:시맨틱 결과 포맷", + "prefs-srf": "시맨틱 결과 포맷", "srf-prefs-intro-text": "시맨틱 결과 포맷 확장 기능을 설치하였습니다. 추가적인 도움을 받으려면 [https://www.semantic-mediawiki.org/wiki/Help:Result_formats 결과 포맷] 도움말 페이지를 방문해 주십시오.", "prefs-srf-eventcalendar-options": "이벤트 캘린더 옵션", "prefs-srf-datatables-options": "데이터테이블 옵션", @@ -188,7 +187,6 @@ "srf-filtered-noscript-error": "자바스크립트를 사용하지 않고 있기 때문에 결과를 표시할 수 없습니다. $1 문서로 이동합니다.", "srf-filtered-noscript-link-caption": "표 형식의 결과", "srf-filtered-map-provider-missing-error": "\"map\" 뷰에 지도 제공자를 지정하지 않았습니다.", - "srf-filtered-map-geocoordinateparser-missing-error": "지리좌표 파서를 찾을 수 없습니다. 'data-values/geo'를 설치해야 합니다.", "srf-filtered-value-filter-and": "그리고", "srf-filtered-value-filter-or": "또는", "srf-filtered-value-filter-placeholder": "필터 값을 선택하세요", @@ -217,6 +215,7 @@ "srf-ui-eventcalendar-format-column-week": "M/d ddd", "srf-ui-eventcalendar-format-column-day": "M/d dddd", "srf-ui-eventcalendar-click-popup": "이벤트를 만드시겠습니까?", + "srf-paramdesc-datasource": "데이터 접근이 가능한 출처입니다. 허용된 값: \"file\", \"raw\", \"url\". 기본값: \"file\"", "srf-paramdesc-yaxislabel": "y축에 표시되는 설명", "srf-paramdesc-xaxislabel": "x축에 표시되는 설명", "srf-paramdesc-unit": "단위", @@ -240,8 +239,13 @@ "srf-ui-mediaplayer-label-repeat-off": "반복 해제", "srf-ui-mediaplayer-label-full-screen": "전체 화면", "srf-ui-mediaplayer-label-restore-screen": "화면 복구", - "srf-excel-link": "엑셀", - "srf-excel-missing-phpexcel": "확장 기능 [https://www.mediawiki.org/wiki/Extension:PHPExcel PHPExcel]이 설치되어 있지 않으므로 MS 액셀로 내보낼 수 없습니다.", - "srf-paramdesc-excel-filename": "생성된 파일의 다운로드 파일 이름", - "srf-paramdesc-icalendar-timezone": "쉼표 구분 시간대 목록" + "srf-spreadsheet-link": "스프레드시트", + "srf-paramdesc-spreadsheet-filename": "다운로드를 위해 생성된 스프레드시트 파일의 파일 이름", + "srf-paramdesc-spreadsheet-templatefile": "생성된 파일의 서식을 제공하기 위한 \"파일\" 이름공간의 스프레드시트 파일의 이름입니다", + "srf-paramdesc-icalendar-timezone": "쉼표 구분 시간대 목록", + "srf-paramdesc-gantt-diagramtitle": "다이어그램 이름", + "srf-paramdesc-gantt-diagramtheme": "다이어그램 테마", + "srf-paramdesc-gantt-axisformat": "X축: 날짜 형식", + "srf-error-gantt-mermaid-not-installed": "Mermaid 확장 기능의 설치가 필요합니다.", + "srf-printername-gantt": "간트" } diff --git a/www/wiki/extensions/SemanticResultFormats/i18n/lb.json b/www/wiki/extensions/SemanticResultFormats/i18n/lb.json index eeb881e2..11f0a5b0 100644 --- a/www/wiki/extensions/SemanticResultFormats/i18n/lb.json +++ b/www/wiki/extensions/SemanticResultFormats/i18n/lb.json @@ -5,7 +5,6 @@ "Nemo bis" ] }, - "srf-name": "Formate vu semantesche Resultater", "prefs-srf-eventcalendar-options": "Optioune vum Kalenner vun den Eventementer", "prefs-srf-datatables-options": "Optioune vun den Tabelle vun Donnéeën", "srf-module-loading": "Lueden...", @@ -132,5 +131,6 @@ "srf-ui-mediaplayer-label-previous": "Vireg", "srf-ui-mediaplayer-label-pause": "Paus", "srf-ui-mediaplayer-label-stop": "Stopp", - "srf-excel-link": "Excel" + "srf-paramdesc-gantt-diagramtitle": "Numm vum Diagramm", + "srf-printername-gantt": "Gantt" } diff --git a/www/wiki/extensions/SemanticResultFormats/i18n/lt.json b/www/wiki/extensions/SemanticResultFormats/i18n/lt.json index 842a4700..601ea992 100644 --- a/www/wiki/extensions/SemanticResultFormats/i18n/lt.json +++ b/www/wiki/extensions/SemanticResultFormats/i18n/lt.json @@ -171,6 +171,5 @@ "srf-ui-mediaplayer-label-repeat-off": "Nekartoti", "srf-ui-mediaplayer-label-full-screen": "Visas ekranas", "srf-ui-mediaplayer-label-restore-screen": "Atkurti ekraną", - "srf-excel-missing-phpexcel": "Nepavyko eksportuoti MS-Excel, nes plėtinys [https://www.mediawiki.org/wiki/Extension:PHPExcel PHPExcel] nėra instaliuotas.", - "srf-paramdesc-excel-filename": "Sugeneruoto failo atsiuntimo pavadinimas" + "srf-spreadsheet-link": "Skaičiuoklė" } diff --git a/www/wiki/extensions/SemanticResultFormats/i18n/mk.json b/www/wiki/extensions/SemanticResultFormats/i18n/mk.json index e6b5dc60..28a6dfe8 100644 --- a/www/wiki/extensions/SemanticResultFormats/i18n/mk.json +++ b/www/wiki/extensions/SemanticResultFormats/i18n/mk.json @@ -2,11 +2,11 @@ "@metadata": { "authors": [ "Bjankuloski06", - "Nemo bis" + "Nemo bis", + "Vlad5250" ] }, "srf-desc": "Дополнителни формати на исходот за барања од Семантички МедијаВики", - "srf-name": "Формати на семантички исход", "prefs-srf": "Додаток:Формати на семантички исход", "srf-prefs-intro-text": "Го воспоставивте додатокот „Формати на семантички исход“. Дополнителна помош ќе добиете на страницата за [https://www.semantic-mediawiki.org/wiki/Help:Result_formats формати на исходот].", "prefs-srf-eventcalendar-options": "Поставки на Календарот на настани", @@ -166,13 +166,13 @@ "srf_printername_hash": "Тараба", "srf_paramdesc_hashname": "Ако е зададено, а на располагање е додатокот зе тарабни табели (HashTables), ова ќе создаде тараба со наведеното име (тогаш нема да има видлив извод)", "srf-printername-graph": "Графикон", - "srf-paramdesc-graph-relation": "Дали предметите или именските својства се матични или зависни?", - "srf-paramdesc-graph-nameprop": "Овозможува задавање на својство што ќе се користи како предмет наместо фактичкиот предмет", - "srf-paramdesc-graph-nodeshape": "Обликот на секој јазол во графиконот", - "srf-paramdesc-graphname": "Наслов", - "srf-paramdesc-graphsize": "Големина на графиконот (во пиксели)", - "srf-paramdesc-graphlegend": "Дали да се прикажува легенда за графиконот", - "srf-paramdesc-graphlabel": "Натпис на графиконот", + "srf-paramdesc-graph-relation": "Одредете дали предметите или именските својства се матични или зависни", + "srf-paramdesc-graph-nameprop": "Го задава својството што ќе се користи како предмет наместо фактичкиот предмет, т.е. име на страницата", + "srf-paramdesc-graph-nodeshape": "Го задава обликот на секој јазол во графиконот", + "srf-paramdesc-graphname": "Го задава насловот на графиконот", + "srf-paramdesc-graphsize": "Ја задава големината на графиконот во пиксели", + "srf-paramdesc-graphlegend": "Одредете дали да се прикажува легенда за графиконот", + "srf-paramdesc-graphlabel": "Го задава натписот на графиконот", "srf-paramdesc-rankdir": "Правец на стрелката", "srf-paramdesc-graphlink": "Врска за графиконот", "srf-paramdesc-graphcolor": "Боја на графиконот", @@ -237,6 +237,7 @@ "srf-filtered-selectorlabel-map": "Карта", "srf-filtered-value-filter-and": "И", "srf-filtered-value-filter-or": "ИЛИ", + "srf-filtered-value-filter-placeholder": "Изберете филтерска вредност", "srf-printername-d3chart": "D3-графикон", "srf-printername-timeseries": "Графикон на временска низа", "srf-paramdesc-group": "Групирање на низата според", @@ -295,9 +296,8 @@ "srf-ui-mediaplayer-label-repeat-off": "Искл. повторување", "srf-ui-mediaplayer-label-full-screen": "На цел екран", "srf-ui-mediaplayer-label-restore-screen": "Поврати екран", - "srf-excel-link": "Excel", - "srf-excel-missing-phpexcel": "Не можев да извезам за MS-Excel бидејќи не е воспоставен додатокот [https://www.mediawiki.org/wiki/Extension:PHPExcel PHPExcel].", - "srf-paramdesc-excel-filename": "Податотечното име за преземање на создадената податотека.", - "srf-paramdesc-excel-templatefile": "Назив на Excel-податотеката од именскиот простор „Податотека“ што служи за форматирање на создадената податотека", - "srf-paramdesc-icalendar-timezone": "Список на часовни појаси одделени со запирки" + "srf-spreadsheet-link": "Табела", + "srf-paramdesc-icalendar-timezone": "Список на часовни појаси одделени со запирки", + "srf-paramdesc-gantt-diagramtitle": "Име на дијаграмот", + "srf-paramdesc-gantt-diagramtheme": "Тема на дијаграмот" } diff --git a/www/wiki/extensions/SemanticResultFormats/i18n/nb.json b/www/wiki/extensions/SemanticResultFormats/i18n/nb.json index 3a5aa301..1684ef06 100644 --- a/www/wiki/extensions/SemanticResultFormats/i18n/nb.json +++ b/www/wiki/extensions/SemanticResultFormats/i18n/nb.json @@ -11,7 +11,6 @@ ] }, "srf-desc": "Tilleggsformater for Semantic MediaWiki-spørringer", - "srf-name": "Semantisk resultatformat", "prefs-srf": "Extenison:Semantic Result Formats", "srf-prefs-intro-text": "Du har installert utvidelsen Semantic Result Formats. For ytterligere assistanse kan du se på [https://www.semantic-mediawiki.org/wiki/Help:Result_formats result formats] hjelpsiden.", "prefs-srf-eventcalendar-options": "Valgmuligheter i hendelseskalenderen.", @@ -301,9 +300,5 @@ "srf-ui-mediaplayer-label-shuffle-off": "Miks av", "srf-ui-mediaplayer-label-repeat": "Repeter", "srf-ui-mediaplayer-label-repeat-off": "Repeter av", - "srf-ui-mediaplayer-label-full-screen": "Fullskjerm", - "srf-excel-link": "Excel", - "srf-excel-missing-phpexcel": "Kunne ikke eksportere for MS-Excel fordi utvidelsen [https://www.mediawiki.org/wiki/Extension:PHPExcel PHPExcel] ikke er installert.", - "srf-paramdesc-excel-filename": "Nedlastingsfilnavnet for den genererte fila", - "srf-paramdesc-excel-templatefile": "Navnet på en Excel-fil fra «File»-navnerommet som brukes for å formatere den genererte fila" + "srf-ui-mediaplayer-label-full-screen": "Fullskjerm" } diff --git a/www/wiki/extensions/SemanticResultFormats/i18n/nl.json b/www/wiki/extensions/SemanticResultFormats/i18n/nl.json index 6ccf786a..a96cbc09 100644 --- a/www/wiki/extensions/SemanticResultFormats/i18n/nl.json +++ b/www/wiki/extensions/SemanticResultFormats/i18n/nl.json @@ -16,11 +16,11 @@ "Festina90", "Mar(c)", "Patio", - "KlaasZ4usV" + "KlaasZ4usV", + "Elderson" ] }, "srf-desc": "Extra resultaatopmaken voor zoekopdrachten van Semantic MediaWiki", - "srf-name": "Semantische resultaatopmaken", "prefs-srf": "Semantische resultaatopmaken", "srf-prefs-intro-text": "U hebt de uitbreiding Semantic Result Formats geïnstalleerd. Raadpleeg de hulppagina voor [https://www.semantic-mediawiki.org/wiki/Help:Result_formats resultaatopmaken] voor meer informatie.", "srf-module-loading": "Bezig met laden…", @@ -216,6 +216,7 @@ "srf-filtered-selectorlabel-list": "Lijst", "srf-filtered-selectorlabel-table": "Tabel", "srf-filtered-selectorlabel-calendar": "Kalender", + "srf-filtered-selectorlabel-map": "Kaart", "srf-filtered-noscript-error": "De resulaten kunnen niet worden weergegeven omdat JavaScript niet is ingeschakeld. Ga naar $1.", "srf-filtered-value-filter-and": "EN", "srf-filtered-value-filter-or": "OF", @@ -270,6 +271,6 @@ "srf-ui-mediaplayer-label-repeat-off": "Herhalen uit", "srf-ui-mediaplayer-label-full-screen": "Volledig scherm", "srf-ui-mediaplayer-label-restore-screen": "Scherm herstellen", - "srf-excel-link": "Excel", - "srf-excel-missing-phpexcel": "Kan niet naar Microsoft Excel exporteren omdat de uitbreiding [https://www.mediawiki.org/wiki/Extension:PHPExcel PHPExcel] niet is geïnstalleerd." + "srf-paramdesc-spreadsheet-filename": "De bestandsnaam voor het downloaden van de gegenereerde speadsheet", + "srf-paramdesc-spreadsheet-fileformat": "De bestandsindeling voor de spreadsheet. Mogelijke waarden: xlsx, xls, ods en csv. Standaard: xlsx." } diff --git a/www/wiki/extensions/SemanticResultFormats/i18n/oc.json b/www/wiki/extensions/SemanticResultFormats/i18n/oc.json index d7bd7926..cd599746 100644 --- a/www/wiki/extensions/SemanticResultFormats/i18n/oc.json +++ b/www/wiki/extensions/SemanticResultFormats/i18n/oc.json @@ -8,7 +8,6 @@ ] }, "srf-desc": "Formats adicionals d'afichatge de resultat per las requèstas de Semantic MediaWiki", - "srf-name": "Formatatge dels resultats semantics", "prefs-srf-datatables-options": "Opcions de las plajas de donadas", "srf-module-loading": "Cargament…", "srf-paramdesc-layout": "Mesa en pagina disponibla", @@ -132,6 +131,5 @@ "srf-ui-mediaplayer-label-unmute": "Pas mut", "srf-ui-mediaplayer-label-volume-max": "Volum maximal", "srf-ui-mediaplayer-label-shuffle": "Lectura aleatòria", - "srf-ui-mediaplayer-label-repeat": "Repetir", - "srf-excel-link": "Excel" + "srf-ui-mediaplayer-label-repeat": "Repetir" } diff --git a/www/wiki/extensions/SemanticResultFormats/i18n/pl.json b/www/wiki/extensions/SemanticResultFormats/i18n/pl.json index 50644378..f9adc7a4 100644 --- a/www/wiki/extensions/SemanticResultFormats/i18n/pl.json +++ b/www/wiki/extensions/SemanticResultFormats/i18n/pl.json @@ -14,7 +14,6 @@ ] }, "srf-desc": "Dodatkowe formaty dla zapytań Semantycznej MediaWiki", - "srf-name": "Formaty semantycznych wyników", "prefs-srf-eventcalendar-options": "Opcje kalendarza wydarzeń", "prefs-srf-datatables-options": "Ustawienia tabel danych", "srf-module-loading": "Ładowanie...", @@ -214,7 +213,5 @@ "srf-ui-mediaplayer-label-repeat-off": "Powtarzanie wyłączone", "srf-ui-mediaplayer-label-full-screen": "Pełny ekran", "srf-ui-mediaplayer-label-restore-screen": "Przywróć ekran", - "srf-excel-link": "Excel", - "srf-excel-missing-phpexcel": "Nie można eksportować do MS Excel, ponieważ rozszerzenie [https://www.mediawiki.org/wiki/Extension:PHPExcel PHPExcel] nie jest zainstalowane.", "srf-paramdesc-icalendar-timezone": "Lista stref czasowych przedzielana przecinkami" } diff --git a/www/wiki/extensions/SemanticResultFormats/i18n/pms.json b/www/wiki/extensions/SemanticResultFormats/i18n/pms.json index cc3ca707..f5d6168a 100644 --- a/www/wiki/extensions/SemanticResultFormats/i18n/pms.json +++ b/www/wiki/extensions/SemanticResultFormats/i18n/pms.json @@ -8,7 +8,6 @@ ] }, "srf-desc": "Formà adissionaj për anterogassion an linia ëd Semantic MediaWiki", - "srf-name": "Formà dj'Arzultà Semàntich", "prefs-srf": "Formà dj'Arzultà Semàntich", "srf-prefs-intro-text": "A l'ha anstalà l'estension Formà dj'Arzultà Semàntich che al moment a l'ha gnun-e opsion utent përsonalisabij. Për assistensa, për piasì, ch'a vìsita la pagina d'agiut [http://www.semantic-mediawiki.org/wiki/Help:Result_formats formà dj'arzultà]", "srf-module-loading": "A caria ...", diff --git a/www/wiki/extensions/SemanticResultFormats/i18n/pt-br.json b/www/wiki/extensions/SemanticResultFormats/i18n/pt-br.json index f591b665..b51e0522 100644 --- a/www/wiki/extensions/SemanticResultFormats/i18n/pt-br.json +++ b/www/wiki/extensions/SemanticResultFormats/i18n/pt-br.json @@ -10,12 +10,12 @@ "!Silent", "Nemo bis", "Eduardo Addad de Oliveira", - "Trigonometria87" + "Trigonometria87", + "Andrecpe" ] }, "srf-desc": "Formatos adicionais de resultado para consultas do Semantic MediaWiki", - "srf-name": "Semantic Result Formats", - "prefs-srf": "Extensão:Semantic Result Formats", + "prefs-srf": "Semantic Result Formats", "srf-prefs-intro-text": "Você instalou a extensão Semantic Result Formats. Para mais informações, por favor visite as [https://www.semantic-mediawiki.org/wiki/Help:Result_formats páginas de ajuda dos formatos de resultado].", "prefs-srf-eventcalendar-options": "Opções do calendário de eventos", "srf-prefs-eventcalendar-options-update-default": "Habilitar as [https://www.semantic-mediawiki.org/wiki/Help:User_preferences#Automatic_updates atualizações automáticas] dos eventos de calendário durante a atualização da página", @@ -272,7 +272,6 @@ "srf-filtered-noscript-error": "Os resultados não podem ser apresentados porque o Javascript não está ativado. Visite $1.", "srf-filtered-noscript-link-caption": "Resultados em forma de tabela", "srf-filtered-map-provider-missing-error": "Nenhum fornecedor de mapa especificado para exibição de \"mapa\".", - "srf-filtered-map-geocoordinateparser-missing-error": "Não foi encontrado o analisador de geocoordenadas. Precisa de instalar 'data_values/geo'.", "srf-filtered-value-filter-and": "E", "srf-filtered-value-filter-or": "OU", "srf-filtered-value-filter-placeholder": "Selecionar um valor de filtro", @@ -301,6 +300,8 @@ "srf-ui-eventcalendar-label-listweek": "Semana (lista)", "srf-ui-eventcalendar-label-listday": "Dia (lista)", "srf-ui-eventcalendar-label-allday": "O dia todo", + "srf-ui-eventcalendar-format-title-week": "MMM D, AAAA", + "srf-ui-eventcalendar-format-title-day": "MMMM D, AAAA", "srf-ui-eventcalendar-label-update-success": "A atualização do calendário de eventos foi executada com êxito.", "srf-ui-eventcalendar-label-update-error": "A atualização do calendário de eventos falhou.", "srf-ui-eventcalendar-click-popup": "Deseja criar um evento?", @@ -332,9 +333,24 @@ "srf-ui-mediaplayer-label-repeat-off": "Desativar repetição", "srf-ui-mediaplayer-label-full-screen": "Tela inteira", "srf-ui-mediaplayer-label-restore-screen": "Restaurar tela", - "srf-excel-link": "Excel", - "srf-excel-missing-phpexcel": "Não foi possível exportar para MS-Excel porque a extensão [https://www.mediawiki.org/wiki/Extension:PHPExcel PHPExcel] não está instalada.", - "srf-paramdesc-excel-filename": "O nome do arquivo para baixar do arquivo gerado", - "srf-paramdesc-excel-templatefile": "O nome de um arquivo Excel do domínio \"File (Arquivo)\", usado para formatar o arquivo gerado", - "srf-paramdesc-icalendar-timezone": "Uma lista de fusos horários, separados por vírgulas" + "srf-spreadsheet-link": "Planilha", + "srf-paramdesc-spreadsheet-filename": "O nome de arquivo para o download da folha de cálculo gerada.", + "srf-paramdesc-spreadsheet-fileformat": "O formato a ser produzido para o arquivo da folha de cálculo. Os valores permitidos são: xlsx, xls, ods, csv. Por omissão: xlsx", + "srf-paramdesc-spreadsheet-templatefile": "O nome de um arquivo de planilha do namespace ''Arquivo'' usado para formatar o arquivo gerado", + "srf-paramdesc-icalendar-timezone": "Uma lista de fusos horários, separados por vírgulas", + "srf-paramdesc-gantt-diagramtitle": "Nome do diagrama", + "srf-paramdesc-gantt-diagramtheme": "Tema do diagrama", + "srf-paramdesc-gantt-axisformat": "X-axis: Formato de data", + "srf-paramdesc-gantt-sortkey": "Chave para ordenar sessões e tarefas", + "srf-paramdesc-gantt-titletopmargin": "Margem superior do título do diagrama", + "srf-paramdesc-gantt-barheight": "Altura das barras de tarefas", + "srf-paramdesc-gantt-leftpadding": "Largura do título da seção", + "srf-paramdesc-gantt-bargap": "Distância vertical das barras de tarefas para a margem", + "srf-error-gantt-mapping-assignment": "Atribuição errada de valores mapeados em '''$1'''", + "srf-error-gantt-mapping-keywords": "A chave usada em seu parâmetro de mapeamento não é suportada", + "srf-error-gantt-theme": "O '''tema''' escolhido não é suportado", + "srf-error-gantt-sortkey": "A '''sortkey''' que você escolheu não é suportado", + "srf-error-gantt-mermaid-not-installed": "A Extensão Mermaid precisa ser instalado.", + "srf-printername-gantt": "Gantt", + "srf-paramdesc-nodelabel": "Use um rótulo de nó de gráfico. Valores permitidos: displaytitle." } diff --git a/www/wiki/extensions/SemanticResultFormats/i18n/pt.json b/www/wiki/extensions/SemanticResultFormats/i18n/pt.json index b3aa08c1..5e1832e8 100644 --- a/www/wiki/extensions/SemanticResultFormats/i18n/pt.json +++ b/www/wiki/extensions/SemanticResultFormats/i18n/pt.json @@ -9,11 +9,11 @@ "Macofe", "Ngl2016", "Nemo bis", - "Athena in Wonderland" + "Athena in Wonderland", + "Waldyrious" ] }, "srf-desc": "Formatos adicionais de resultados para consultas do MediaWiki Semântico", - "srf-name": "Formatos dos resultados semânticos", "prefs-srf": "Extension:Formatos dos resultados semânticos", "srf-prefs-intro-text": "Tem instalada a extensão Formatos dos Resultados Semânticos. Para ajuda adicional, visite a página de ajuda [https://www.semantic-mediawiki.org/wiki/Help:Result_formats formatos de resultados], por favor.", "prefs-srf-eventcalendar-options": "Opções do calendário de eventos", @@ -270,7 +270,6 @@ "srf-filtered-noscript-error": "Os resultados não podem ser apresentados porque o Javascript não está ativado. Visite $1.", "srf-filtered-noscript-link-caption": "resultados em forma de tabela", "srf-filtered-map-provider-missing-error": "Não foi especificado um fornecedor de mapas para a vista \"mapa\".", - "srf-filtered-map-geocoordinateparser-missing-error": "Não foi encontrado o analisador de geocoordenadas. Precisa de instalar 'data-values/geo'.", "srf-filtered-value-filter-and": "E", "srf-filtered-value-filter-or": "OU", "srf-filtered-value-filter-placeholder": "Selecionar um valor de filtro", @@ -330,9 +329,24 @@ "srf-ui-mediaplayer-label-repeat-off": "Não repetir", "srf-ui-mediaplayer-label-full-screen": "Ecrã completo", "srf-ui-mediaplayer-label-restore-screen": "Restaurar o ecrã", - "srf-excel-link": "Excel", - "srf-excel-missing-phpexcel": "Não foi possível exportar para MS-Excel porque a extensão [https://www.mediawiki.org/wiki/Extension:PHPExcel PHPExcel] não está instalada.", - "srf-paramdesc-excel-filename": "O nome de ficheiro para descarregamento do ficheiro gerado", - "srf-paramdesc-excel-templatefile": "O nome de um ficheiro Excel do espaço nominal \"Ficheiro\", usado para formatar o ficheiro gerado", - "srf-paramdesc-icalendar-timezone": "Uma lista de fusos horários, separados por vírgulas" + "srf-spreadsheet-link": "Folha de cálculo", + "srf-paramdesc-spreadsheet-filename": "O nome de ficheiro para o descarregamento da folha de cálculo gerada.", + "srf-paramdesc-spreadsheet-fileformat": "O formato a ser produzido para o ficheiro da folha de cálculo. Os valores permitidos são: xlsx, xls, ods, csv. Por omissão: xlsx", + "srf-paramdesc-spreadsheet-templatefile": "O nome de ficheiro de uma folha de cálculo no espaço nominal/domínio ''Ficheiro'', usado para formatar o ficheiro gerado", + "srf-paramdesc-icalendar-timezone": "Uma lista de fusos horários, separados por vírgulas", + "srf-paramdesc-gantt-diagramtitle": "Nome do diagrama", + "srf-paramdesc-gantt-diagramtheme": "Tema do diagrama", + "srf-paramdesc-gantt-axisformat": "Eixo dos x: Formato de data", + "srf-paramdesc-gantt-sortkey": "Chave para ordenar secções e tarefas", + "srf-paramdesc-gantt-titletopmargin": "Margem superior do título do diagrama", + "srf-paramdesc-gantt-barheight": "Altura das barras de tarefas", + "srf-paramdesc-gantt-leftpadding": "Largura do título da secção", + "srf-paramdesc-gantt-bargap": "Distância vertical das barras de tarefas para a margem", + "srf-error-gantt-mapping-assignment": "Atribuição errada de valores mapeados em '''$1'''", + "srf-error-gantt-mapping-keywords": "A chave usada no seu parâmetro de mapeamento não é suportada", + "srf-error-gantt-theme": "O '''tema''' que escolheu não é suportado", + "srf-error-gantt-sortkey": "A '''chave de ordenação''' que escolheu não é suportada", + "srf-error-gantt-mermaid-not-installed": "A extensão Mermaid precisa de ser instalada.", + "srf-printername-gantt": "Gantt", + "srf-paramdesc-nodelabel": "Usar um rótulo de vértice de grafo. Valores permitidos: displaytitle." } diff --git a/www/wiki/extensions/SemanticResultFormats/i18n/qqq.json b/www/wiki/extensions/SemanticResultFormats/i18n/qqq.json index 21b8ea8d..fd3182d2 100644 --- a/www/wiki/extensions/SemanticResultFormats/i18n/qqq.json +++ b/www/wiki/extensions/SemanticResultFormats/i18n/qqq.json @@ -281,7 +281,6 @@ "srf-filtered-noscript-error": "This is an informatory message.\n\nParameter:\n* $1 - holds the name of a page", "srf-filtered-noscript-link-caption": "This is the caption of a link.", "srf-filtered-map-provider-missing-error": "This is an error message for a missing setting.", - "srf-filtered-map-geocoordinateparser-missing-error": "This is an error message when the 'data-values/geo' is missing.", "srf-filtered-value-filter-and": "Label of the radio button selecting AND behavior of the value filter.", "srf-filtered-value-filter-or": "Label of the radio button selecting OR behavior of the value filter.", "srf-filtered-value-filter-placeholder": "Placeholder text for the value filter input", @@ -350,9 +349,24 @@ "srf-ui-mediaplayer-label-repeat-off": "{{Doc-smw-ui-label-media|repeat-off}}", "srf-ui-mediaplayer-label-full-screen": "{{Doc-smw-ui-label-media|full-screen}}\n{{Identical|Full screen}}", "srf-ui-mediaplayer-label-restore-screen": "{{Doc-smw-ui-label-media|restore-screen}}", - "srf-excel-link": "{{doc-smw-link}}", - "srf-excel-missing-phpexcel": "This is an error message.", - "srf-paramdesc-excel-filename": "{{doc-paramdesc|filename}}", - "srf-paramdesc-excel-templatefile": "{{doc-paramdesc|templatefile}}", - "srf-paramdesc-icalendar-timezone": "{{doc-paramdesc|timezone}}" + "srf-spreadsheet-link": "{{doc-smw-link}}", + "srf-paramdesc-spreadsheet-filename": "{{doc-paramdesc|filename}}", + "srf-paramdesc-spreadsheet-fileformat": "{{doc-paramdesc|fileformat}}", + "srf-paramdesc-spreadsheet-templatefile": "{{doc-paramdesc|templatefile}}", + "srf-paramdesc-icalendar-timezone": "{{doc-paramdesc|timezone}}", + "srf-paramdesc-gantt-diagramtitle": "{{doc-paramdesc|diagram}}", + "srf-paramdesc-gantt-diagramtheme": "{{doc-paramdesc|theme}}", + "srf-paramdesc-gantt-axisformat": "{{doc-paramdesc|axisformat}}", + "srf-paramdesc-gantt-sortkey": "{{doc-paramdesc|sortkey}}", + "srf-paramdesc-gantt-titletopmargin": "{{doc-paramdesc|titletopmargin}}", + "srf-paramdesc-gantt-barheight": "{{doc-paramdesc|barheight}}", + "srf-paramdesc-gantt-leftpadding": "{{doc-paramdesc|leftpadding}}", + "srf-paramdesc-gantt-bargap": "{{doc-paramdesc|bargap}}", + "srf-error-gantt-mapping-assignment": "This is an error message.", + "srf-error-gantt-mapping-keywords": "This is an error message.", + "srf-error-gantt-theme": "This is an error message.", + "srf-error-gantt-sortkey": "This is an error message.", + "srf-error-gantt-mermaid-not-installed": "This is an error message.", + "srf-printername-gantt": "{{doc-smwformat|gantt}}", + "srf-paramdesc-nodelabel": "{{doc-paramdesc|nodelabel}}\n{{doc-important|Do not translate the possible parameters: \"displaytitle\".}}" } diff --git a/www/wiki/extensions/SemanticResultFormats/i18n/roa-tara.json b/www/wiki/extensions/SemanticResultFormats/i18n/roa-tara.json index 785d5849..439327dc 100644 --- a/www/wiki/extensions/SemanticResultFormats/i18n/roa-tara.json +++ b/www/wiki/extensions/SemanticResultFormats/i18n/roa-tara.json @@ -4,7 +4,7 @@ "Joetaras" ] }, - "prefs-srf": "Extension:Semantic Result Formats", + "prefs-srf": "Formate d'u resultate semandeche", "srf-module-loading": "Stoche a scareche…", "srf-paramdesc-height": "Altezze", "srf-paramdesc-width": "Larghezze", @@ -83,5 +83,15 @@ "srf-ui-mediaplayer-label-unmute": "Cu 'a vôsce", "srf-ui-mediaplayer-label-volume-max": "Volume massime", "srf-ui-mediaplayer-label-shuffle": "Miscke", - "srf-excel-link": "Excel" + "srf-spreadsheet-link": "Fogghie de calcole", + "srf-paramdesc-gantt-diagramtitle": "Nome d'u diagramme", + "srf-paramdesc-gantt-diagramtheme": "Teme d'u diagramme", + "srf-paramdesc-gantt-axisformat": "Asse de le X: Formate de le date", + "srf-paramdesc-gantt-sortkey": "Chiave pe ordenà le seziune e le attività", + "srf-paramdesc-gantt-titletopmargin": "Margine de sus d'u titole d'u diagramme", + "srf-paramdesc-gantt-barheight": "Iertezze de le barre de le attività", + "srf-paramdesc-gantt-bargap": "Distanze verticale de le barre de le attività da 'u margine", + "srf-error-gantt-mapping-keywords": "'A chiave ausate jndr'à 'u parametre de mappature non g'è supportate", + "srf-error-gantt-sortkey": "'A '''chiave de ordinamende''' ca è scacchiate non g'è supportate", + "srf-printername-gantt": "Gantt" } diff --git a/www/wiki/extensions/SemanticResultFormats/i18n/ru.json b/www/wiki/extensions/SemanticResultFormats/i18n/ru.json index 6404aee9..81be5238 100644 --- a/www/wiki/extensions/SemanticResultFormats/i18n/ru.json +++ b/www/wiki/extensions/SemanticResultFormats/i18n/ru.json @@ -14,13 +14,16 @@ "Redredsonia", "Nemo bis", "Putnik", - "Vlad5250" + "Vlad5250", + "Nk88", + "Romanko Mikhail", + "Diralik", + "Movses" ] }, "srf-desc": "Дополнительные форматы вывода для запросов Semantic MediaWiki", - "srf-name": "Semantic Result Formats", - "prefs-srf": "Extension:Semantic Result Formats", - "srf-prefs-intro-text": "У вас установлено расширение «Semantic Result Formats». Для получения дополнительной помощи, пожалуйста, посетите страницу [http://www.semantic-mediawiki.org/wiki/Help:Форматы_вывода Форматы вывода].", + "prefs-srf": "Форматы семантического вывода", + "srf-prefs-intro-text": "Расширение «Форматы семантического вывода» установлено. Для дополнительной информации вы можете ознакомиться со страницей [https://www.semantic-mediawiki.org/wiki/Help:Result_formats «Форматы вывода»].", "prefs-srf-eventcalendar-options": "Календарь событий параметры", "srf-prefs-eventcalendar-options-update-default": "Включить [https://www.semantic-mediawiki.org/wiki/Help:User_preferences#Automatic_updates автоматическое обновление] событий календаря при обновлении страницы", "srf-prefs-eventcalendar-options-paneview-default": "Включить по умолчанию вид клеточками", @@ -131,7 +134,7 @@ "srf-paramdesc-serieslabel": "Определите ярлык серии", "srf-paramdesc-grouplabel": "Определите ярлык группы", "srf-paramdesc-chartcursor": "Параметры отображения указателя диаграммы", - "srf-paramdesc-trendline": "Включить одновременное отображение диаграммы и ее линии тренда", + "srf-paramdesc-trendline": "Включить одновременное отображение диаграммы и её линии тренда", "srf-paramdesc-gridview": "Отобразить диаграмму и наборы данных одновременно. Допустимые значения: «none» и «tabs». По умолчанию: «none»", "srf-paramdesc-paneview": "Указать положение панели с небольшим линейным графиком. Допустимые значения: \"bottom\", \"top\" и \"none\". По умолчанию: \"bottom\"", "srf-paramdesc-infotext": "Отображать дополнительную информацию на соответствующей вкладке", @@ -177,17 +180,19 @@ "srf_printername_hash": "Хэш", "srf_paramdesc_hashname": "Если указано, и доступно расширение Hash Tables, это создаст хэш поданному названием (ни видимого выхода)", "srf-printername-graph": "График", - "srf-paramdesc-graph-relation": "Предметы или свойства названий родительские или дочерние?", - "srf-paramdesc-graph-nameprop": "Позволяет установить свойство, которое будет использовано как предмет вместо нынешнего предмета", + "srf-paramdesc-graph-relation": "Определяет, являются ли предметы или свойства названий родительскими или дочерними", + "srf-paramdesc-graph-nameprop": "Позволяет установить свойство, которое будет использовано как предмет вместо нынешнего предмета, то есть названия страницы", "srf-paramdesc-graph-nodeshape": "Устанавливает форму каждого узла графа", "srf-paramdesc-graphname": "Устанавливает заголовок графа", "srf-paramdesc-graphsize": "Устанавливает размер графа (в пикселях)", - "srf-paramdesc-graphlegend": "Показывать легенду или нет?", - "srf-paramdesc-graphlabel": "Подпись графы", + "srf-paramdesc-graphlegend": "Определяет, следует ли показывать легенду", + "srf-paramdesc-graphlabel": "Устанавливает подпись графа", "srf-paramdesc-rankdir": "Устанавливает направление стрелок", - "srf-paramdesc-graphlink": "Ссылка графы", - "srf-paramdesc-graphcolor": "Цвет графы", - "srf-paramdesc-graph-wwl": "Предел переноса слов (в # символов)", + "srf-paramdesc-graphlink": "Определяет, должны ли узлы ссылаться на их вики-страницы", + "srf-paramdesc-graphcolor": "Устанавливает цвет графа", + "srf-paramdesc-graph-wwl": "Определяет предел переноса слов (в символах)", + "srf-paramdesc-clustercolor": "Устанавливает цвета блоков кластера", + "srf-paramdesc-highlightcolor": "Устанавливает цвет шрифта для выделенного узла", "srf-paramdesc-redlinkcolor": "Задать цвет шрифта для красных ссылок", "srf-printername-datatables": "Таблицы данных", "srf-ui-datatables-label-conditions": "Условия", @@ -243,11 +248,23 @@ "srf-paramdesc-filtered-calendar-end": "Распечатка содержит дату окончания события", "srf-paramdesc-filtered-calendar-title": "Распечатка содержит название события. Не может быть использовано вместе с шаблоном заголовка.", "srf-paramdesc-filtered-calendar-title-template": "Шаблон, используемый для форматирования названия события в календаре", + "srf-paramdesc-filtered-map-position": "\nРаспечатка, содержащая географическое положение.", + "srf-paramdesc-filtered-map-icon": "Распечатка, которая определяет значок карты, который будет использоваться.", + "srf-paramdesc-filtered-map-icons": "Карта значений для значков, которые будут использоваться для этих значений на карте.", "srf-paramdesc-filtered-map-height": "Высота карты.", + "srf-paramdesc-filtered-map-min-zoom": "Минимальный выбираемый уровень масштабирования карты", + "srf-paramdesc-filtered-map-max-zoom": "Максимальный выбираемый уровень масштабирования карты", + "srf-paramdesc-filtered-map-marker-cluster": "\nВключить или отключить кластеризацию маркеров.", + "srf-paramdesc-filtered-map-marker-cluster-max-zoom": "\nМаксимальный уровень масштабирования, при котором маркеры карты всё ещё группируются. Выше этого уровня маркеры всегда будут кластеризованы.", + "srf-paramdesc-filtered-map-marker-cluster-max-radius": "\nМаксимальный радиус, который кластер будет охватывать от центрального маркера (в пикселях). По умолчанию 80.", + "srf-paramdesc-filtered-map-marker-cluster-zoom-on-click": "Если включено, при нажатии кластер приблизится к своим границам.", "srf-filtered-selectorlabel-list": "Список", "srf-filtered-selectorlabel-table": "Таблица", "srf-filtered-selectorlabel-calendar": "Календарь", "srf-filtered-selectorlabel-map": "Карта", + "srf-filtered-noscript-error": "Результаты не могут быть отображены, потому что Javascript отключён. Перейти к $1", + "srf-filtered-noscript-link-caption": "\nрезультаты в табличной форме", + "srf-filtered-map-provider-missing-error": "Для представления \"карта\" не указан поставщик карт.", "srf-filtered-value-filter-and": "И", "srf-filtered-value-filter-or": "ИЛИ", "srf-filtered-value-filter-placeholder": "Выберите значение фильтра", @@ -288,6 +305,7 @@ "srf-paramdesc-unit": "Единица", "srf-printername-pagewidget": "Виджет страница", "srf-printername-incoming": "Входящие свойства", + "srf-paramdesc-count": "Устанавливает, нужно ли считать количество входящих свойств", "srf-paramdesc-min": "Минимальное или пороговое значение", "srf-paramdesc-excludeproperty": "Исключить свойство из набора результатов", "srf-printername-media": "Медиапроигрыватель", @@ -306,8 +324,6 @@ "srf-ui-mediaplayer-label-repeat-off": "Повторение выкл", "srf-ui-mediaplayer-label-full-screen": "На весь экран", "srf-ui-mediaplayer-label-restore-screen": "Восстановить экран", - "srf-excel-link": "Excel", - "srf-excel-missing-phpexcel": "Не удалось экспортировать в MS Excel, потому что не установлено расширение [https://www.mediawiki.org/wiki/Extension:PHPExcel PHPExcel].", - "srf-paramdesc-excel-filename": "Имя скачиваемого файла для создаваемого файла", - "srf-paramdesc-excel-templatefile": "Название файла Excel из пространства имён «Файл», используемого для форматирования создаваемого файла" + "srf-spreadsheet-link": "Таблица", + "srf-paramdesc-spreadsheet-templatefile": "Название файла таблицы из пространства имён «Файл», используемого для форматирования создаваемого файла" } diff --git a/www/wiki/extensions/SemanticResultFormats/i18n/si.json b/www/wiki/extensions/SemanticResultFormats/i18n/si.json index 094e0e2c..70719a5e 100644 --- a/www/wiki/extensions/SemanticResultFormats/i18n/si.json +++ b/www/wiki/extensions/SemanticResultFormats/i18n/si.json @@ -5,7 +5,6 @@ "Nemo bis" ] }, - "srf-name": "අර්ථ විචාර ප්‍රතිඑල ආකෘතීන්", "prefs-srf": "අර්ථ විචාර ප්‍රතිඑල ආකෘතීන්", "srf-module-loading": "ප්‍රවේශනය වෙමින්...", "srf-paramdesc-layout": "ලබා ගත හැකි සැලැස්ම", diff --git a/www/wiki/extensions/SemanticResultFormats/i18n/sv.json b/www/wiki/extensions/SemanticResultFormats/i18n/sv.json index 2b61d63c..196c28cc 100644 --- a/www/wiki/extensions/SemanticResultFormats/i18n/sv.json +++ b/www/wiki/extensions/SemanticResultFormats/i18n/sv.json @@ -13,14 +13,14 @@ "Skalman", "Nemo bis", "Josve05a", - "Umeaboy" + "Umeaboy", + "Mjälten" ] }, "srf-desc": "Extra resultatformat för Semantic MediaWiki-frågor", - "srf-name": "Semantiska resultatformat", "srf-prefs-datatables-options-cache-default": "Aktivera [https://www.semantic-mediawiki.org/wiki/Help:User_preferences#Local_storage lokal lagring] för att förbättra svarstiden", "srf-module-loading": "Laddar...", - "srf-paramdesc-layout": "Tillgänglig layout", + "srf-paramdesc-layout": "Tillgänglig utformningar", "srf-paramdesc-height": "Höjd", "srf-paramdesc-width": "Bredd", "srf-paramdesc-class": "Ange ytterligare en CSS-klass som ska gälla för formatet", @@ -130,7 +130,7 @@ "srf_paramdesc_captionproperty": "Namnet på den semantiska egenskap (som är närvarande på de efterfrågade sidorna) som ska användas som figurtext", "srf_paramdesc_imageproperty": "Namnet på en semantisk egenskap på den efterfrågade sidan som pekar på bilder som ska användas. När den här parametern är angiven så kommer de efterfrågade sidorna själv inte att visas som bilder", "srf-paramdesc-redirects": "Namnet på den semantiska egenskap som är närvarande på de efterfrågade sidorna som innehåller förflyttningsmålet", - "srf-paramdesc-navigation": "Specificera navigation för layout", + "srf-paramdesc-navigation": "Specificera navigation för utformning", "srf-paramdesc-overlay": "Möjliggör bilder ovanpå", "srf-gallery-navigation-previous": "Föregående", "srf-gallery-navigation-next": "Nästa", @@ -257,5 +257,7 @@ "srf-ui-mediaplayer-label-shuffle": "Blanda", "srf-ui-mediaplayer-label-repeat": "Upprepa", "srf-ui-mediaplayer-label-full-screen": "Fullskärm", - "srf-excel-link": "Excel" + "srf-spreadsheet-link": "Kalkylblad", + "srf-paramdesc-spreadsheet-filename": "Filformatet att producera. Tillåtna värden: xlsx, xls, ods, csv. Standard: xlsx", + "srf-paramdesc-spreadsheet-templatefile": "Namnet på kalkylbladsfilen från namnrymden \"File\" som används för att formatera den genererade filen" } diff --git a/www/wiki/extensions/SemanticResultFormats/i18n/tl.json b/www/wiki/extensions/SemanticResultFormats/i18n/tl.json index c37ad4cc..7ff5be80 100644 --- a/www/wiki/extensions/SemanticResultFormats/i18n/tl.json +++ b/www/wiki/extensions/SemanticResultFormats/i18n/tl.json @@ -8,7 +8,6 @@ ] }, "srf-desc": "Dagdag na mga anyo para sa nasa loob ng guhit na mga pagtatanong na pang-Semantikong MediaWiki", - "srf-name": "Mga Anyo ng Resultang Semantiko", "srf-module-loading": "Ikinakarga...", "srf-paramdesc-layout": "Makukuhang kalatagan", "srf-paramdesc-height": "Taas", diff --git a/www/wiki/extensions/SemanticResultFormats/i18n/uk.json b/www/wiki/extensions/SemanticResultFormats/i18n/uk.json index 9ba6030c..81cfd67b 100644 --- a/www/wiki/extensions/SemanticResultFormats/i18n/uk.json +++ b/www/wiki/extensions/SemanticResultFormats/i18n/uk.json @@ -8,13 +8,13 @@ "NataChe", "Piramidion", "Bunyk", - "Nemo bis" + "Nemo bis", + "Vlad5250" ] }, "srf-desc": "Додаткові формати результатів для запитів Semantic MediaWiki", - "srf-name": "Формати семантичних результатів", - "prefs-srf": "Extension:Semantic Result Formats", - "srf-prefs-intro-text": "Ви встановили розширення Semantic Result Formats. За отримання допомоги, відвідайте, будь ласка, [http://www.semantic-mediawiki.org/wiki/Help:Result_formats довідкову сторінку форматів результатів].", + "prefs-srf": "Формати семантичних результатів", + "srf-prefs-intro-text": "Ви встановили розширення Semantic Result Formats. Відвідайте, будь ласка, [http://www.semantic-mediawiki.org/wiki/Help:Result_formats довідкову сторінку форматів результатів] для отримання допомоги відносно налаштувань користувача.", "prefs-srf-eventcalendar-options": "Налаштування календаря подій", "srf-prefs-eventcalendar-options-update-default": "Увімкнути [https://www.semantic-mediawiki.org/wiki/Help:User_preferences#Automatic_updates автоматичні оновлення] календаря подій під час оновлення сторінки", "srf-prefs-eventcalendar-options-paneview-default": "Увімкнути за замовчуванням вигляд клітинками", @@ -268,7 +268,6 @@ "srf-filtered-noscript-error": "Результати неможливо відтворити, оскільки JavaScript не увімкнено. Перейдіть до $1.", "srf-filtered-noscript-link-caption": "результати в табличній формі", "srf-filtered-map-provider-missing-error": "Не вказано провайдера мапи для перегляду у форматі «мапа».", - "srf-filtered-map-geocoordinateparser-missing-error": "Парсера геокординат не знайдено. Вам треба встановити 'data-values/geo'.", "srf-filtered-value-filter-and": "ТА", "srf-filtered-value-filter-or": "АБО", "srf-filtered-value-filter-placeholder": "Виберіть значення фільтра", @@ -328,9 +327,12 @@ "srf-ui-mediaplayer-label-repeat-off": "Вимкнути повторення", "srf-ui-mediaplayer-label-full-screen": "Повноекранний режим", "srf-ui-mediaplayer-label-restore-screen": "Відновити екран", - "srf-excel-link": "Excel", - "srf-excel-missing-phpexcel": "Не вдалося експортувати в MS-Excel, оскільки розширення [https://www.mediawiki.org/wiki/Extension:PHPExcel PHPExcel] не встановлене.", - "srf-paramdesc-excel-filename": "Назва файлу для завантаження генерованого файлу", - "srf-paramdesc-excel-templatefile": "Назва файлу Excel з простору назв ''File'', що використовується для форматування генерованого файлу", - "srf-paramdesc-icalendar-timezone": "Список часових поясів через кому" + "srf-spreadsheet-link": "Електронна таблиця", + "srf-paramdesc-spreadsheet-filename": "Назва файлу для завантаження згенерованого файлу-таблиці", + "srf-paramdesc-spreadsheet-templatefile": "Назва електронної таблиці з простору назв «Файл», яка використовується для форматування згенерованого файлу", + "srf-paramdesc-icalendar-timezone": "Список часових поясів через кому", + "srf-paramdesc-gantt-diagramtitle": "Назва діаграми", + "srf-paramdesc-gantt-diagramtheme": "Тема діаграми", + "srf-paramdesc-gantt-axisformat": "X-вісь: Формат дати", + "srf-printername-gantt": "Ґант" } diff --git a/www/wiki/extensions/SemanticResultFormats/i18n/zh-hans.json b/www/wiki/extensions/SemanticResultFormats/i18n/zh-hans.json index 375213c1..a42c8721 100644 --- a/www/wiki/extensions/SemanticResultFormats/i18n/zh-hans.json +++ b/www/wiki/extensions/SemanticResultFormats/i18n/zh-hans.json @@ -10,13 +10,14 @@ "Hudafu", "Fengchao", "Yfdyh000", - "Nemo bis" + "Nemo bis", + "科劳", + "Looong" ] }, "srf-desc": "用于Semantic MediaWiki的附加结果格式", - "srf-name": "语义结果格式", - "prefs-srf": "扩展:语义结果格式", - "srf-prefs-intro-text": "您已安装语义结果格式拓展。额外帮助请参见[https://www.semantic-mediawiki.org/wiki/Help:Result_formats 此拓展的帮助页]。", + "prefs-srf": "语义结果格式", + "srf-prefs-intro-text": "您已安装带有语义的结果格式拓展应用。请访问[https://www.semantic-mediawiki.org/wiki/Help:Result_formats result formats]来获取用户喜好相关的额外帮助。", "prefs-srf-eventcalendar-options": "事件日历选项", "srf-prefs-eventcalendar-options-update-default": "在页面刷新时启用日历活动的[https://www.semantic-mediawiki.org/wiki/Help:User_preferences#Automatic_updates 自动更新]", "srf-prefs-eventcalendar-options-paneview-default": "默认启用窗格视图", @@ -270,7 +271,6 @@ "srf-filtered-noscript-error": "因为Javascript未启用,所以不能显示结果。前往$1。", "srf-filtered-noscript-link-caption": "表格窗体中的结果", "srf-filtered-map-provider-missing-error": "没有为“地图”视图指定地图提供者。", - "srf-filtered-map-geocoordinateparser-missing-error": "找不到地理坐标解析器。您需要安装“data-values/geo”。", "srf-filtered-value-filter-and": "和", "srf-filtered-value-filter-or": "或", "srf-filtered-value-filter-placeholder": "选择过滤器值", @@ -330,9 +330,11 @@ "srf-ui-mediaplayer-label-repeat-off": "关闭重复", "srf-ui-mediaplayer-label-full-screen": "全屏", "srf-ui-mediaplayer-label-restore-screen": "还原屏幕", - "srf-excel-link": "Excel", - "srf-excel-missing-phpexcel": "无法导出成MS-Excel因为[https://www.mediawiki.org/wiki/Extension:PHPExcel PHPExcel]拓展未安装。", - "srf-paramdesc-excel-filename": "用于生成文件的下载文件名", - "srf-paramdesc-excel-templatefile": "来自“File”名字空间的Excel文件名,用于格式化生成的文件", - "srf-paramdesc-icalendar-timezone": "时区的逗号分隔列表" + "srf-paramdesc-icalendar-timezone": "时区的逗号分隔列表", + "srf-paramdesc-gantt-diagramtitle": "数据图名称", + "srf-paramdesc-gantt-diagramtheme": "数据图主题", + "srf-paramdesc-gantt-axisformat": "横轴:日期格式", + "srf-paramdesc-gantt-titletopmargin": "数据图标题的内上边距", + "srf-paramdesc-gantt-barheight": "任务栏高度", + "srf-printername-gantt": "甘特图" } diff --git a/www/wiki/extensions/SemanticResultFormats/i18n/zh-hant.json b/www/wiki/extensions/SemanticResultFormats/i18n/zh-hant.json index 8fb45b8c..614036dc 100644 --- a/www/wiki/extensions/SemanticResultFormats/i18n/zh-hant.json +++ b/www/wiki/extensions/SemanticResultFormats/i18n/zh-hant.json @@ -12,7 +12,6 @@ ] }, "srf-desc": "用於 Semantic MediaWiki 查詢的附加結果格式", - "srf-name": "語義結果格式", "prefs-srf": "擴充:語意結果格式", "srf-prefs-intro-text": "您已安裝 Semantic Result Formats 擴充功能。額外幫助內容請查看[https://www.semantic-mediawiki.org/wiki/Help:Result_formats 結果格式]的說明頁面.", "prefs-srf-eventcalendar-options": "事件行事曆選項", @@ -117,6 +116,7 @@ "srf-paramdesc-chartcolor": "分配具體圖表色彩", "srf-paramdesc-colorscheme": "選擇色彩方案", "srf-paramdesc-valueformat": "指定用於值的格式規範", + "srf-paramdesc-highlighter": "顯示資料點加強凸顯效果", "srf-paramdesc-smoothlines": "在線狀圖上套用平滑演算法", "srf-paramdesc-stackseries": "顯示圖表為疊堆序列", "srf-paramdesc-seriesgroup": "選擇系列分組", @@ -125,6 +125,7 @@ "srf-paramdesc-chartcursor": "圖表游標顯示選項", "srf-paramdesc-trendline": "啟用同時顯示圖表與該趨勢線", "srf-paramdesc-gridview": "同時顯示圖表與資料集。允許的值為:「none」和「tabs」,預設為:「none」", + "srf-paramdesc-paneview": "指定包含小型線狀圖的方格位置。允許的值為:「top」、「bottom」、「none」,預設為:「bottom」。", "srf-paramdesc-infotext": "顯示在對應資訊分頁的額外資訊。", "srf-paramdesc-clicktarget": "定義當點擊行事曆日期時,作為目標的頁面或查詢字串。", "srf-ui-gridview-label-item": "資料項目", @@ -180,6 +181,7 @@ "srf-paramdesc-graphlink": "設定節點是否要連接到它們的 wiki 頁面", "srf-paramdesc-graphcolor": "設定圖表顔色", "srf-paramdesc-graph-wwl": "設定字詞換行限制(採字元數)", + "srf-paramdesc-clustercolor": "設定叢集框色彩", "srf-paramdesc-highlight": "設定凸顯出節點", "srf-paramdesc-highlightcolor": "設定所凸顯節點的字型色彩", "srf-paramdesc-redlinkcolor": "設定紅連的字型色彩", @@ -217,6 +219,7 @@ "srf-ui-datatables-label-sSearch": "搜尋:", "srf-ui-datatables-label-sZeroRecords": "找不到符合的紀錄", "srf-ui-datatables-label-oPaginate-sFirst": "第一", + "srf-ui-datatables-label-oPaginate-sLast": "最末", "srf-ui-datatables-label-oPaginate-sNext": "下一頁", "srf-ui-datatables-label-oPaginate-sPrevious": "上一頁", "srf-ui-datatables-label-oAria-sSortAscending": ":啟用升冪排序欄位", @@ -229,6 +232,7 @@ "srf-tree-circledetected": "當插入$1至樹狀結構時偵測到循環引用。", "srf-paramdesc-tree-parent": "包含父頁面的屬性", "srf-paramdesc-tree-root": "樹狀結構的根頁面", + "srf-paramdesc-tree-startlevel": "樹狀結構的起始級別,例如:整合至其它樹狀結構", "srf-printername-slideshow": "幻燈片", "srf-paramdesc-delay": "以秒為單位的幻燈片延遲時間", "srf-paramdesc-navigation-controls": "是否顯示導航控制", @@ -247,10 +251,13 @@ "srf-paramdesc-filtered-calendar-title-template": "在行事曆裡使用來格式化事件標題的模板", "srf-paramdesc-filtered-map-position": "列印輸出內容包含地理位置。", "srf-paramdesc-filtered-map-icon": "所決定使用之地圖標示的列印輸出。", + "srf-paramdesc-filtered-map-icons": "使用於在地圖裡這些值上的值與圖標對應。", "srf-paramdesc-filtered-map-height": "地圖高度", "srf-paramdesc-filtered-map-zoom": "地圖載入時的縮放層次。這可透過使用者來更改。
請查看:地圖視圖最小縮放地圖視圖最大縮放", "srf-paramdesc-filtered-map-min-zoom": "地圖最小可縮放層次", "srf-paramdesc-filtered-map-max-zoom": "地圖最大可縮放層級", + "srf-paramdesc-filtered-map-marker-cluster": "啟動或關閉聚集標記", + "srf-paramdesc-filtered-map-marker-cluster-max-zoom": "地圖標記仍有聚集時的最大縮放層級。以上層級的標記則不會做出聚集。", "srf-paramdesc-filtered-map-marker-cluster-max-radius": "叢集會從中心點起覆蓋的最大半徑。預設值為 80(像素)。", "srf-paramdesc-filtered-map-marker-cluster-zoom-on-click": "當啟用後,點擊叢集會縮放至界限。", "srf-filtered-selectorlabel-list": "清單", @@ -260,7 +267,6 @@ "srf-filtered-noscript-error": "因 Javascript 未啟用而無法顯示結果。前至$1。", "srf-filtered-noscript-link-caption": "表格式表單的結果", "srf-filtered-map-provider-missing-error": "沒有替「map」視圖指定地圖提供方。", - "srf-filtered-map-geocoordinateparser-missing-error": "找不到地理座標解析器。您必須安裝「data-values/geo」。", "srf-filtered-value-filter-and": "與", "srf-filtered-value-filter-or": "或", "srf-filtered-value-filter-placeholder": "選擇一個篩選值", @@ -279,6 +285,7 @@ "srf-paramdesc-calendarfirstday": "每週開始的第一天", "srf-paramdesc-calendardefaultview": "當行事曆載入時的初始視圖", "srf-paramdesc-calendarstart": "初始行事曆起始(日期或日期時間的值)", + "srf-paramdesc-calendarlegend": "指定圖例位置以及所分配的篩選項目", "srf-paramdesc-dayview": "以點擊日期數目來啟用日期檢視", "srf-ui-eventcalendar-label-today": "今天", "srf-ui-eventcalendar-label-month": "月", @@ -293,6 +300,7 @@ "srf-ui-eventcalendar-click-popup": "您是否要建立事件?", "srf-printername-dygraphs": "Dygraphs 圖表", "srf-paramdesc-datasource": "可存取資料的來源。允許的值為:「file」、「raw」、「url」,預設為:「file」", + "srf-paramdesc-errorbar": "使用到錯誤的條狀。允許的值為:「fraction」(值的信賴區間)、「sigma」(值的標準差)、「range」(自定義的值範圍)", "srf-paramdesc-movingaverage": "顯示一段日子裡的平均(零代表沒有移動平均)", "srf-paramdesc-yaxislabel": "在 Y 軸上顯示的描述", "srf-paramdesc-xaxislabel": "在 X 軸上顯示的描述", @@ -318,9 +326,13 @@ "srf-ui-mediaplayer-label-repeat-off": "關閉重複", "srf-ui-mediaplayer-label-full-screen": "全螢幕", "srf-ui-mediaplayer-label-restore-screen": "還原螢幕", - "srf-excel-link": "Excel", - "srf-excel-missing-phpexcel": "無法匯出成 MS-Excel,因為擴充套件 [https://www.mediawiki.org/wiki/Extension:PHPExcel PHPExcel] 尚未安裝。", - "srf-paramdesc-excel-filename": "用於所產生檔案的下載檔案名稱", - "srf-paramdesc-excel-templatefile": "來自「File」命名空間的 Excel 檔案名稱,用於格式所產生的檔案", - "srf-paramdesc-icalendar-timezone": "逗號分隔的時區清單" + "srf-spreadsheet-link": "試算表", + "srf-paramdesc-spreadsheet-filename": "生成的檔案格式。允許值為:xlsx、xls、ods、csv。預設值為:xlsx", + "srf-paramdesc-spreadsheet-templatefile": "來自「File」命名空間的試算表檔案名稱,用於格式所產生的檔案", + "srf-paramdesc-icalendar-timezone": "逗號分隔的時區清單", + "srf-paramdesc-gantt-diagramtitle": "圖表名稱", + "srf-paramdesc-gantt-diagramtheme": "圖表主題", + "srf-paramdesc-gantt-axisformat": "X 軸:日期格式", + "srf-error-gantt-mermaid-not-installed": "需要安裝 Mermaid 擴充。", + "srf-printername-gantt": "甘特圖" } diff --git a/www/wiki/extensions/SemanticResultFormats/resources/ext.srf.css b/www/wiki/extensions/SemanticResultFormats/resources/ext.srf.css index 644d86dc..18641276 100644 --- a/www/wiki/extensions/SemanticResultFormats/resources/ext.srf.css +++ b/www/wiki/extensions/SemanticResultFormats/resources/ext.srf.css @@ -58,15 +58,52 @@ fieldset#mw-prefsection-smw-srf { border: 0px solid #2a4b8d; border-top: 1px solid #2a4b8d; - margin-top: -5px; + margin-top: 25px; + clear: both; } -fieldset#mw-prefsection-smw-srf-eventcalendar-options, fieldset#mw-prefsection-smw-srf-datatables-options { +fieldset#mw-prefsection-smw-srf-eventcalendar-options { border: 0px solid #2a4b8d; border-top: 1px solid #ddd; - margin-top: -5px; + margin-top: 0px; } -#mw-prefsection-smw-srf legend::before { - content: "▼ "; +fieldset#mw-prefsection-smw-srf-datatables-options { + border: 0px solid #2a4b8d; + border-top: 1px solid #ddd; + margin-top: 15px; +} + +#mw-prefsection-smw-srf .oo-ui-fieldsetLayout-header .oo-ui-labelElement-label:before { + content: "⯈ "; +} + +#mw-prefsection-smw-srf .oo-ui-fieldsetLayout-header .oo-ui-labelElement-label { + color: #2a4b8d; +} + +#mw-prefsection-smw-srf .oo-ui-panelLayout-padded { + padding-top: 0em; + padding-bottom: 0em; +} + +#mw-prefsection-smw-srf-eventcalendar-options .oo-ui-fieldsetLayout-group, +#mw-prefsection-smw-srf-datatables-options .oo-ui-fieldsetLayout-group { + columns: 2; +} + +#mw-prefsection-smw-srf .oo-ui-fieldLayout-field { + margin-top: 10px; +} + +/** + * Responsive settings + */ +@media screen and (max-width: 800px) { + + #mw-prefsection-smw-srf-eventcalendar-options .oo-ui-fieldsetLayout-group, + #mw-prefsection-smw-srf-datatables-options .oo-ui-fieldsetLayout-group { + columns: 1; + } + } diff --git a/www/wiki/extensions/SemanticResultFormats/src/BibTex/BibTexFileExportPrinter.php b/www/wiki/extensions/SemanticResultFormats/src/BibTex/BibTexFileExportPrinter.php new file mode 100644 index 00000000..aa2bc011 --- /dev/null +++ b/www/wiki/extensions/SemanticResultFormats/src/BibTex/BibTexFileExportPrinter.php @@ -0,0 +1,190 @@ +text(); + } + + /** + * @see FileExportPrinter::getMimeType + * + * @since 1.8 + * + * {@inheritDoc} + */ + public function getMimeType( QueryResult $queryResult ) { + return 'text/bibtex'; + } + + /** + * @see FileExportPrinter::getFileName + * + * @since 1.8 + * + * {@inheritDoc} + */ + public function getFileName( QueryResult $queryResult ) { + + if ( $this->params['filename'] !== '' ) { + + if ( strpos( $this->params['filename'], '.bib' ) === false ) { + $this->params['filename'] .= '.bib'; + } + + return str_replace( ' ', '_', $this->params['filename'] ); + } elseif ( $this->getSearchLabel( SMW_OUTPUT_WIKI ) != '' ) { + return str_replace( ' ', '_', $this->getSearchLabel( SMW_OUTPUT_WIKI ) ) . '.bib'; + } + + return 'BibTeX.bib'; + } + + /** + * @see ResultPrinter::getParamDefinitions + * + * @since 1.8 + * + * {@inheritDoc} + */ + public function getParamDefinitions( array $definitions ) { + $params = parent::getParamDefinitions( $definitions ); + + $params['filename'] = [ + 'message' => 'smw-paramdesc-filename', + 'default' => 'bibtex.bib', + ]; + + return $params; + } + + /** + * @since 3.1 + * + * @param array $list + * + * @return string + */ + public function getFormattedList( $key, array $values ) { + return $GLOBALS['wgLang']->listToText( $values ); + } + + /** + * @see ResultPrinter::getResultText + * + * {@inheritDoc} + */ + protected function getResultText( QueryResult $res, $outputMode ) { + + if ( $outputMode !== SMW_OUTPUT_FILE ) { + return $this->getBibTexLink( $res, $outputMode ); + } + + $items = []; + + while ( $row = $res->getNext() ) { + $items[] = $this->newItem( $row )->text(); + } + + return implode( "\r\n\r\n", $items ); + } + + private function getBibTexLink( QueryResult $res, $outputMode ) { + + // Can be viewed as HTML if requested, no more parsing needed + $this->isHTML = $outputMode == SMW_OUTPUT_HTML; + + $link = $this->getLink( + $res, + $outputMode + ); + + return $link->getText( $outputMode, $this->mLinker ); + } + + /** + * @since 3.1 + * + * @param $row array of SMWResultArray + * + * @return bibTexItem + */ + private function newItem( array /* of SMWResultArray */ $row ) { + + $item = new Item(); + $item->setFormatterCallback( [ $this, 'getFormattedList' ] ); + + foreach ( $row as /* SMWResultArray */ $field ) { + $printRequest = $field->getPrintRequest(); + $values = []; + + $label = strtolower( $printRequest->getLabel() ); + $dataValue = $field->getNextDataValue(); + + if ( $dataValue === false ) { + continue; + } + + if ( $label === 'date' && $dataValue instanceof TimeValue ) { + $item->set( 'year', $dataValue->getYear() ); + $item->set( 'month', $dataValue->getMonth() ); + } elseif ( $label === 'author' || $label === 'authors' ) { + $values[] = $dataValue->getShortWikiText(); + + while ( ( /* SMWDataValue */ $dataValue = $field->getNextDataValue() ) !== false ) { + $values[] = $dataValue->getShortWikiText(); + } + + $item->set( 'author', $values ); + } elseif ( $label === 'editor' || $label === 'editors' ) { + $values[] = $dataValue->getShortWikiText(); + + while ( ( /* SMWDataValue */ $dataValue = $field->getNextDataValue() ) !== false ) { + $values[] = $dataValue->getShortWikiText(); + } + + $item->set( 'editor', $values ); + } else { + $item->set( $label, $dataValue->getShortWikiText() ); + } + } + + return $item; + } + +} diff --git a/www/wiki/extensions/SemanticResultFormats/src/BibTex/Item.php b/www/wiki/extensions/SemanticResultFormats/src/BibTex/Item.php new file mode 100644 index 00000000..7ff631ef --- /dev/null +++ b/www/wiki/extensions/SemanticResultFormats/src/BibTex/Item.php @@ -0,0 +1,185 @@ + '', + 'annote' => '', + 'author' => [], + 'booktitle' => '', + 'chapter' => '', + 'crossref' => '', + 'doi' => '', + 'edition' => '', + 'editor' => [], + 'eprint' => '', + 'howpublished' => '', + 'institution' => '', + 'journal' => '', + 'key' => '', + 'month' => '', + 'note' => '', + 'number' => '', + 'organization' => '', + 'pages' => '', + 'publisher' => '', + 'school' => '', + 'series' => '', + 'title' => '', + 'url' => '', + 'volume' => '', + 'year' => '' + ]; + + /** + * @var callable + */ + private $formatterCallback; + + /** + * @since 3.1 + */ + public function __construct() { + $this->type = 'Book'; + } + + /** + * @since 3.1 + * + * @param callable $compoundLabelCallback + */ + public function setFormatterCallback( callable $formatterCallback ) { + $this->formatterCallback = $formatterCallback; + } + + /** + * @since 3.1 + * + * @param $key + * @param string $text + * + * @return string + */ + public function replace( $key, $text ) { + + if ( $key === 'uri' ) { + $text = str_replace( + [ "Ä", "ä", "Ö", "ö", "Ü", "ü", "ß" ], + [ 'Ae', 'ae', 'Oe', 'oe', 'Ue', 'ue', 'ss' ], + $text + ); + $text = preg_replace("/[^a-zA-Z0-9]+/", "", $text ); + } + + return $text; + } + + /** + * @since 3.1 + * + * @param $key + * @param mixed $value + */ + public function set( $key, $value ) { + + $key = strtolower( $key ); + + if ( $key === 'type' ) { + $this->type = ucfirst( $value ); + } + + if ( isset( $this->fields[$key] ) ) { + $this->fields[$key] = $value; + } + } + + /** + * @since 3.1 + * + * @return string + */ + public function text() { + + $formatterCallback = $this->formatterCallback; + + $text = '@' . $this->type . '{' . $this->buildURI() . ",\r\n"; + + foreach ( $this->fields as $key => $value ) { + + if ( ( $key === 'author' || $key === 'editor' ) && is_array( $value ) ) { + if ( is_callable( $formatterCallback ) ) { + $value = $formatterCallback( $key, $value ); + } else { + $value = implode( ', ', $value ); + } + } + + if ( $value === '' ) { + continue; + } + + $text .= ' ' . $key . ' = "' . $value . '", ' . "\r\n"; + } + + $text .= "}"; + + return $text; + } + + /** + * Consist of `author last name` + `year` + `first word of title` + * + * @return string + */ + protected function buildURI() { + + $uri = ''; + + if ( isset( $this->fields['author'] ) ) { + foreach ( $this->fields['author'] as $key => $author ) { + $elements = explode( ' ', $author ); + $uri .= array_pop( $elements ); + break; + } + } + + if ( isset( $this->fields['year'] ) ) { + $uri .= $this->fields['year']; + } + + if ( isset( $this->fields['title'] ) ) { + foreach ( explode( ' ', $this->fields['title'] ) as $titleWord ) { + $charsTitleWord = preg_split( '//', $titleWord, -1, PREG_SPLIT_NO_EMPTY ); + + if ( !empty( $charsTitleWord ) ) { + $uri .= $charsTitleWord[0]; + } + } + } + + return strtolower( $this->replace( 'uri', $uri ) ); + } + +} diff --git a/www/wiki/extensions/SemanticResultFormats/src/BibTex/README.md b/www/wiki/extensions/SemanticResultFormats/src/BibTex/README.md new file mode 100644 index 00000000..1feab534 --- /dev/null +++ b/www/wiki/extensions/SemanticResultFormats/src/BibTex/README.md @@ -0,0 +1,91 @@ +Info is copied from http://en.wikipedia.org/wiki/Bibtex + +## Fields + +- address: Publisher's address (usually just the city, but can be the full address for lesser-known publishers) +- annote: An annotation for annotated bibliography styles (not typical) +- author: The name(s) of the author(s) (in the case of more than one author, separated by and) +- booktitle: The title of the book, if only part of it is being cited +- chapter: The chapter number +- crossref: The key of the cross-referenced entry +- doi: The DOI number of the entry +- edition: The edition of a book, long form (such as "first" or "second") +- editor: The name(s) of the editor(s) +- eprint: A specification of an electronic publication, often a preprint or a technical report +- howpublished: How it was published, if the publishing method is nonstandard +- institution: The institution that was involved in the publishing, but not necessarily the publisher +- journal: The journal or magazine the work was published in +- key: A hidden field used for specifying or overriding the alphabetical order of entries (when the "author" and "editor" fields are missing). Note that this is very different from the key (mentioned just after this list) that is used to cite or cross-reference the entry. +- month: The month of publication (or, if unpublished, the month of creation) +- note: Miscellaneous extra information +- number: The "number" of a journal, magazine, or tech-report, if applicable. (Most publications have a "volume", but no "number" field.) +- organization: The conference sponsor +- pages: Page numbers, separated either by commas or double-hyphens. For books, the total number of pages. +- publisher: The publisher's name +- school: The school where the thesis was written +- series: The series of books the book was published in (e.g. "The Hardy Boys" or "Lecture Notes in Computer Science") +- title: The title of the work +- type: The type of tech-report, for example, "Research Note" +- url: The WWW address +- volume: The volume of a journal or multi-volume book +- year: The year of publication (or, if unpublished, the year of creation) + + +## Types + +- article: + An article from a journal or magazine. + Required fields: author, title, journal, year + Optional fields: volume, number, pages, month, note, key +- book: + A book with an explicit publisher. + Required fields: author/editor, title, publisher, year + Optional fields: volume, series, address, edition, month, note, key, pages +- booklet: + A work that is printed and bound, but without a named publisher or sponsoring institution. + Required fields: title + Optional fields: author, howpublished, address, month, year, note, key +- conference: + The same as inproceedings, included for Scribe compatibility. + Required fields: author, title, booktitle, year + Optional fields: editor, pages, organization, publisher, address, month, note, key +- inbook: + A part of a book, usually untitled. May be a chapter (or section or whatever) and/or a range of pages. + Required fields: author/editor, title, chapter/pages, publisher, year + Optional fields: volume, series, address, edition, month, note, key +- incollection: + A part of a book having its own title. + Required fields: author, title, booktitle, year + Optional fields: editor, pages, organization, publisher, address, month, note, key +- inproceedings: + An article in a conference proceedings. + Required fields: author, title, booktitle, year + Optional fields: editor, pages, organization, publisher, address, month, note, key +- manual: + Technical documentation. + Required fields: title + Optional fields: author, organization, address, edition, month, year, note, key +- mastersthesis: + A Master's thesis. + Required fields: author, title, school, year + Optional fields: address, month, note, key +- misc: + For use when nothing else fits. + Required fields: none + Optional fields: author, title, howpublished, month, year, note, key +- phdthesis: + A Ph.D. thesis. + Required fields: author, title, school, year + Optional fields: address, month, note, key +- proceedings: + The proceedings of a conference. + Required fields: title, year + Optional fields: editor, publisher, organization, address, month, note, key +- techreport: + A report published by a school or other institution, usually numbered within a series. + Required fields: author, title, institution, year + Optional fields: type, number, address, month, note, key +- unpublished: + A document having an author and title, but not formally published. + Required fields: author, title, note + Optional fields: month, year, key diff --git a/www/wiki/extensions/SemanticResultFormats/src/Graph/GraphPrinter.php b/www/wiki/extensions/SemanticResultFormats/src/Graph/GraphPrinter.php new file mode 100644 index 00000000..91def23d --- /dev/null +++ b/www/wiki/extensions/SemanticResultFormats/src/Graph/GraphPrinter.php @@ -0,0 +1,419 @@ + + */ +class GraphPrinter extends SMWResultPrinter { + + const NODELABEL_DISPLAYTITLE = 'displaytitle'; + + public static $NODE_LABELS = [ + self::NODELABEL_DISPLAYTITLE, + ]; + + public static $NODE_SHAPES = [ + 'box', + 'box3d', + 'circle', + 'component', + 'diamond', + 'doublecircle', + 'doubleoctagon', + 'egg', + 'ellipse', + 'folder', + 'hexagon', + 'house', + 'invhouse', + 'invtrapezium', + 'invtriangle', + 'Mcircle', + 'Mdiamond', + 'Msquare', + 'none', + 'note', + 'octagon', + 'parallelogram', + 'pentagon ', + 'plaintext', + 'point', + 'polygon', + 'rect', + 'rectangle', + 'septagon', + 'square', + 'tab', + 'trapezium', + 'triangle', + 'tripleoctagon', + ]; + + protected $m_graphName; + protected $m_graphLabel; + protected $m_graphColor; + protected $m_graphLegend; + protected $m_graphLink; + protected $m_rankdir; + protected $m_graphSize; + protected $m_labelArray = []; + protected $m_graphColors = [ + 'black', + 'red', + 'green', + 'blue', + 'darkviolet', + 'gold', + 'deeppink', + 'brown', + 'bisque', + 'darkgreen', + 'yellow', + 'darkblue', + 'magenta', + 'steelblue2' ]; + protected $m_nameProperty; + protected $m_nodeShape; + protected $m_parentRelation; + protected $m_wordWrapLimit; + protected $m_nodeLabel; + + /** + * (non-PHPdoc) + * @see SMWResultPrinter::handleParameters() + */ + protected function handleParameters( array $params, $outputmode ) { + parent::handleParameters( $params, $outputmode ); + + $this->m_graphName = trim( $params['graphname'] ); + $this->m_graphSize = trim( $params['graphsize'] ); + + $this->m_graphLegend = $params['graphlegend']; + $this->m_graphLabel = $params['graphlabel']; + + $this->m_rankdir = strtoupper( trim( $params['arrowdirection'] ) ); + + $this->m_graphLink = $params['graphlink']; + $this->m_graphColor = $params['graphcolor']; + + $this->m_nameProperty = $params['nameproperty'] === false ? false : trim( $params['nameproperty'] ); + + $this->m_parentRelation = strtolower( trim( $params['relation'] ) ) == 'parent'; + + $this->m_nodeShape = $params['nodeshape']; + $this->m_wordWrapLimit = $params['wordwraplimit']; + + $this->m_nodeLabel = $params['nodelabel']; + } + + protected function getResultText( SMWQueryResult $res, $outputmode ) { + + if ( !class_exists( 'GraphViz' ) + && !class_exists( '\\MediaWiki\\Extension\\GraphViz\\GraphViz' ) + ) { + wfWarn( 'The SRF Graph printer needs the GraphViz extension to be installed.' ); + return ''; + } + + $this->isHTML = true; + + $graphInput = "digraph $this->m_graphName {"; + if ( $this->m_graphSize != '' ) { + $graphInput .= "size=\"$this->m_graphSize\";"; + } + if ( $this->m_nodeShape ) { + $graphInput .= "node [shape=$this->m_nodeShape];"; + } + $graphInput .= "rankdir=$this->m_rankdir;"; + + while ( $row = $res->getNext() ) { + $graphInput .= $this->getGVForItem( $row, $outputmode ); + } + + $graphInput .= "}"; + + // Calls graphvizParserHook function from MediaWiki GraphViz extension + $result = $GLOBALS['wgParser']->recursiveTagParse( "$graphInput" ); + + if ( $this->m_graphLegend && $this->m_graphColor ) { + $arrayCount = 0; + $arraySize = count( $this->m_graphColors ); + $result .= "

"; + + foreach ( $this->m_labelArray as $m_label ) { + if ( $arrayCount >= $arraySize ) { + $arrayCount = 0; + } + + $color = $this->m_graphColors[$arrayCount]; + $result .= "$color: $m_label
"; + + $arrayCount += 1; + } + + $result .= "

"; + } + + return $result; + } + + /** + * Returns the GV for a single subject. + * + * @since 1.5.4 + * + * @param array $row + * @param $outputmode + * + * @return string + */ + protected function getGVForItem( array /* of SMWResultArray */ + $row, $outputmode ) { + $segments = []; + + // Loop throught all fields of the record. + foreach ( $row as $i => $resultArray ) { + + // Loop throught all the parts of the field value. + while ( ( $object = $resultArray->getNextDataValue() ) !== false ) { + $propName = $resultArray->getPrintRequest()->getLabel(); + $isName = $this->m_nameProperty ? ( $i != 0 && $this->m_nameProperty === $propName ) : $i == 0; + + if ( $isName ) { + $name = $this->getWordWrappedText( $object->getShortText( $outputmode ), $this->m_wordWrapLimit ); + } + + if ( !( $this->m_nameProperty && $i == 0 ) ) { + $segments[] = $this->getGVForDataValue( $object, $outputmode, $isName, $name, $propName ); + } + } + } + + return implode( "\n", $segments ); + } + + /** + * Returns the GV for a single SMWDataValue. + * + * @since 1.5.4 + * + * @param SMWDataValue $object + * @param $outputmode + * @param boolean $isName Is this the name that should be used for the node? + * @param string $name + * @param string $labelName + * + * @return string + */ + protected function getGVForDataValue( SMWDataValue $object, $outputmode, $isName, $name, $labelName ) { + $graphInput = ''; + $nodeLabel = ''; + $text = $object->getShortText( $outputmode ); + + if ( $this->m_graphLink ) { + $nodeLinkURL = "[[" . $text . "]]"; + } + + $text = $this->getWordWrappedText( $text, $this->m_wordWrapLimit ); + + if ( $this->m_nodeLabel === self::NODELABEL_DISPLAYTITLE && $object instanceof SMWWikiPageValue ) { + $objectDisplayTitle = $object->getDisplayTitle(); + if ( !empty( $objectDisplayTitle )) { + $nodeLabel = $this->getWordWrappedText( $objectDisplayTitle, $this->m_wordWrapLimit ); + } + } + + if ( $this->m_graphLink ) { + if( $nodeLabel === '' ) { + $graphInput .= " \"$text\" [URL = \"$nodeLinkURL\"]; "; + } else { + $graphInput .= " \"$text\" [URL = \"$nodeLinkURL\", label = \"$nodeLabel\"]; "; + } + } + + if ( !$isName ) { + $graphInput .= $this->m_parentRelation ? " \"$text\" -> \"$name\" " : " \"$name\" -> \"$text\" "; + + if ( $this->m_graphLabel || $this->m_graphColor ) { + $graphInput .= ' ['; + + if ( array_search( $labelName, $this->m_labelArray, true ) === false ) { + $this->m_labelArray[] = $labelName; + } + + $color = $this->m_graphColors[array_search( $labelName, $this->m_labelArray, true )]; + + if ( $this->m_graphLabel ) { + $graphInput .= "label=\"$labelName\""; + if ( $this->m_graphColor ) { + $graphInput .= ",fontcolor=$color,"; + } + } + + if ( $this->m_graphColor ) { + $graphInput .= "color=$color"; + } + + $graphInput .= ']'; + + } + + $graphInput .= ';'; + } + + return $graphInput; + } + + /** + * Returns the word wrapped version of the provided text. + * + * @since 1.5.4 + * + * @param string $text + * @param integer $charLimit + * + * @return string + */ + protected function getWordWrappedText( $text, $charLimit ) { + $charLimit = max( [ $charLimit, 1 ] ); + $segments = []; + + while ( strlen( $text ) > $charLimit ) { + // Find the last space in the allowed range. + $splitPosition = strrpos( substr( $text, 0, $charLimit ), ' ' ); + + if ( $splitPosition === false ) { + // If there is no space (lond word), find the next space. + $splitPosition = strpos( $text, ' ' ); + + if ( $splitPosition === false ) { + // If there are no spaces, everything goes on one line. + $splitPosition = strlen( $text ) - 1; + } + } + + $segments[] = substr( $text, 0, $splitPosition + 1 ); + $text = substr( $text, $splitPosition + 1 ); + } + + $segments[] = $text; + + return implode( '\n', $segments ); + } + + /** + * (non-PHPdoc) + * @see SMWResultPrinter::getName() + */ + public function getName() { + return wfMessage( 'srf-printername-graph' )->text(); + } + + /** + * @see SMWResultPrinter::getParamDefinitions + * + * @since 1.8 + * + * @param $definitions array of IParamDefinition + * + * @return array of IParamDefinition|array + */ + public function getParamDefinitions( array $definitions ) { + $params = parent::getParamDefinitions( $definitions ); + + $params['graphname'] = [ + 'default' => 'QueryResult', + 'message' => 'srf-paramdesc-graphname', + ]; + + $params['graphsize'] = [ + 'type' => 'string', + 'default' => '', + 'message' => 'srf-paramdesc-graphsize', + 'manipulatedefault' => false, + ]; + + $params['graphlegend'] = [ + 'type' => 'boolean', + 'default' => false, + 'message' => 'srf-paramdesc-graphlegend', + ]; + + $params['graphlabel'] = [ + 'type' => 'boolean', + 'default' => false, + 'message' => 'srf-paramdesc-graphlabel', + ]; + + $params['graphlink'] = [ + 'type' => 'boolean', + 'default' => false, + 'message' => 'srf-paramdesc-graphlink', + ]; + + $params['graphcolor'] = [ + 'type' => 'boolean', + 'default' => false, + 'message' => 'srf-paramdesc-graphcolor', + ]; + + $params['arrowdirection'] = [ + 'aliases' => 'rankdir', + 'default' => 'LR', + 'message' => 'srf-paramdesc-rankdir', + 'values' => [ 'LR', 'RL', 'TB', 'BT' ], + ]; + + $params['nodeshape'] = [ + 'default' => false, + 'message' => 'srf-paramdesc-graph-nodeshape', + 'manipulatedefault' => false, + 'values' => self::$NODE_SHAPES, + ]; + + $params['relation'] = [ + 'default' => 'child', + 'message' => 'srf-paramdesc-graph-relation', + 'manipulatedefault' => false, + 'values' => [ 'parent', 'child' ], + ]; + + $params['nameproperty'] = [ + 'default' => false, + 'message' => 'srf-paramdesc-graph-nameprop', + 'manipulatedefault' => false, + ]; + + $params['wordwraplimit'] = [ + 'type' => 'integer', + 'default' => 25, + 'message' => 'srf-paramdesc-graph-wwl', + 'manipulatedefault' => false, + ]; + + $params['nodelabel'] = [ + 'default' => '', + 'message' => 'srf-paramdesc-nodelabel', + 'values' => self::$NODE_LABELS, + ]; + + return $params; + } + +} diff --git a/www/wiki/extensions/SemanticResultFormats/src/Outline/ListTreeBuilder.php b/www/wiki/extensions/SemanticResultFormats/src/Outline/ListTreeBuilder.php new file mode 100644 index 00000000..f64a4366 --- /dev/null +++ b/www/wiki/extensions/SemanticResultFormats/src/Outline/ListTreeBuilder.php @@ -0,0 +1,162 @@ +params = $params; + } + + /** + * @since 3.1 + * + * @param Linker|null|false $linker + */ + public function setLinker( $linker ) { + $this->linker = $linker; + } + + /** + * @since 3.1 + * + * @param OutlineTree $tree + * + * @return string + */ + public function build( OutlineTree $outlineTree ) { + return $this->tree( $outlineTree ); + } + + private function tree( $outline_tree, $level = 0 ) { + $text = ""; + + if ( !is_null( $outline_tree->items ) ) { + $text .= "
    \n"; + foreach ( $outline_tree->items as $item ) { + $text .= "
  • {$this->item($item)}
  • \n"; + } + $text .= "
\n"; + } + + if ( $level > 0 ) { + $text .= "
    \n"; + } + + $num_levels = count( $this->params['outlineproperties'] ); + // set font size and weight depending on level we're at + $font_level = $level; + + if ( $num_levels < 4 ) { + $font_level += ( 4 - $num_levels ); + } + + if ( $font_level == 0 ) { + $font_size = 'x-large'; + } elseif ( $font_level == 1 ) { + $font_size = 'large'; + } elseif ( $font_level == 2 ) { + $font_size = 'medium'; + } else { + $font_size = 'small'; + } + + if ( $font_level == 3 ) { + $font_weight = 'bold'; + } else { + $font_weight = 'regular'; + } + + foreach ( $outline_tree->tree as $key => $node ) { + $text .= "

    $key

    \n"; + $text .= $this->tree( $node, $level + 1 ); + } + + if ( $level > 0 ) { + $text .= "
\n"; + } + + return $text; + } + + private function item( $item ) { + $first_col = true; + $found_values = false; // has anything but the first column been printed? + $result = ""; + + foreach ( $item->row as $resultArray ) { + + $printRequest = $resultArray->getPrintRequest(); + $val = $printRequest->getText( SMW_OUTPUT_WIKI, null ); + $first_value = true; + + if ( in_array( $val, $this->params['outlineproperties'] ) ) { + continue; + } + + $linker = $this->params['link'] === 'all' ? $this->linker : null; + + if ( $this->params['link'] === 'subject' && $printRequest->isMode( PrintRequest::PRINT_THIS ) ) { + $linker = $this->linker; + } + + while ( ( $dv = $resultArray->getNextDataValue() ) !== false ) { + + if ( !$first_col && !$found_values ) { // first values after first column + $result .= ' ('; + $found_values = true; + } elseif ( $found_values || !$first_value ) { + // any value after '(' or non-first values on first column + $result .= ', '; + } + + if ( $first_value ) { // first value in any column, print header + $first_value = false; + if ( $this->params['showHeaders'] && ( '' != $printRequest->getLabel() ) ) { + $result .= $printRequest->getText( SMW_OUTPUT_WIKI, $linker ) . ' '; + } + } + + $dataItem = $dv->getDataItem(); + + if ( $linker === null && $dataItem->getDIType() === DataItem::TYPE_WIKIPAGE && ( $caption = $dv->getDisplayTitle() ) !== '' ) { + $dv->setCaption( $caption ); + } + + $result .= $dv->getShortText( SMW_OUTPUT_WIKI, $linker ); + } + + $first_col = false; + } + + if ( $found_values ) { + $result .= ')'; + } + + return $result; + } + +} \ No newline at end of file diff --git a/www/wiki/extensions/SemanticResultFormats/src/Outline/OutlineItem.php b/www/wiki/extensions/SemanticResultFormats/src/Outline/OutlineItem.php new file mode 100644 index 00000000..7b273c25 --- /dev/null +++ b/www/wiki/extensions/SemanticResultFormats/src/Outline/OutlineItem.php @@ -0,0 +1,62 @@ +row = $row; + $this->vals = []; + } + + /** + * @since 3.1 + * + * @param $name + * @param $value + */ + public function addFieldValue( $key, $value ) { + if ( array_key_exists( $key, $this->vals ) ) { + $this->vals[$key][] = $value; + } else { + $this->vals[$key] = [ $value ]; + } + } + + /** + * @since 3.1 + * + * @param $row + */ + public function getFieldValues( $key ) { + + if ( array_key_exists( $key, $this->vals ) ) { + return $this->vals[$key]; + } + + return [ wfMessage( 'srf_outline_novalue' )->text() ]; + } + +} diff --git a/www/wiki/extensions/SemanticResultFormats/src/Outline/OutlineResultPrinter.php b/www/wiki/extensions/SemanticResultFormats/src/Outline/OutlineResultPrinter.php new file mode 100644 index 00000000..9bb3b042 --- /dev/null +++ b/www/wiki/extensions/SemanticResultFormats/src/Outline/OutlineResultPrinter.php @@ -0,0 +1,139 @@ +text(); + } + + /** + * @see SMWResultPrinter::getParamDefinitions + * + * @since 1.8 + * + * {@inheritDoc} + */ + public function getParamDefinitions( array $definitions ) { + $params = parent::getParamDefinitions( $definitions ); + + $params['outlineproperties'] = [ + 'islist' => true, + 'default' => [], + 'message' => 'srf_paramdesc_outlineproperties', + ]; + + $params[] = [ + 'name' => 'template', + 'message' => 'smw-paramdesc-template', + 'default' => '', + ]; + + $params[] = [ + 'name' => 'userparam', + 'message' => 'smw-paramdesc-userparam', + 'default' => '', + ]; + + $params[] = [ + 'name' => 'named args', + 'type' => 'boolean', + 'message' => 'smw-paramdesc-named_args', + 'default' => true, + ]; + + return $params; + } + + /** + * @see ResultPrinter::getResultText + * + * {@inheritDoc} + */ + protected function getResultText( QueryResult $res, $outputMode ) { + + // for each result row, create an array of the row itself + // and all its sorted-on fields, and add it to the initial + // 'tree' + $outlineTree = new OutlineTree(); + + while ( $row = $res->getNext() ) { + $outlineTree->addItem( $this->newOutlineItem( $row ) ); + } + + // now, cycle through the outline properties, creating the + // tree + foreach ( $this->params['outlineproperties'] as $property ) { + $outlineTree->addProperty( $property ); + } + + if ( $this->params['template'] !== '' ) { + $this->hasTemplates = true; + $templateBuilder = new TemplateBuilder( + $this->params + ); + + $templateBuilder->setLinker( $this->mLinker ); + $result = $templateBuilder->build( $outlineTree ); + } else { + $listTreeBuilder = new ListTreeBuilder( + $this->params + [ 'showHeaders' => $this->mShowHeaders ] + ); + + $listTreeBuilder->setLinker( $this->mLinker ); + $result = $listTreeBuilder->build( $outlineTree ); + } + + if ( $this->linkFurtherResults( $res ) ) { + $link = $this->getFurtherResultsLink( + $res, + $outputMode + ); + + $result .= $link->getText( $outputMode, $this->mLinker ) . "\n"; + } + + return $result; + } + + private function newOutlineItem( $row ) { + + $outlineItem = new OutlineItem( $row ); + + foreach ( $row as $field ) { + $name = $field->getPrintRequest()->getText( SMW_OUTPUT_HTML ); + + if ( !in_array( $name, $this->params['outlineproperties'] ) ) { + continue; + } + + while ( ( $dataValue = $field->getNextDataValue() ) !== false ) { + $outlineItem->addFieldValue( + $name, + $dataValue->getLongWikiText( $this->getLinker() ) + ); + } + } + + return $outlineItem; + } + +} diff --git a/www/wiki/extensions/SemanticResultFormats/src/Outline/OutlineTree.php b/www/wiki/extensions/SemanticResultFormats/src/Outline/OutlineTree.php new file mode 100644 index 00000000..3a3ecabb --- /dev/null +++ b/www/wiki/extensions/SemanticResultFormats/src/Outline/OutlineTree.php @@ -0,0 +1,90 @@ +tree = []; + $this->items = $items; + } + + /** + * @since 3.1 + * + * @param $item + */ + public function addItem( $item ) { + $this->items[] = $item; + $this->itemCount++; + } + + /** + * @since 3.1 + * + * @param $vals + * @param $item + */ + public function categorizeItem( $vals, $item ) { + foreach ( $vals as $val ) { + if ( array_key_exists( $val, $this->tree ) ) { + $this->tree[$val]->items[] = $item; + $this->tree[$val]->leafCount++; + } else { + $this->tree[$val] = new self( [ $item ] ); + $this->tree[$val]->leafCount++; + } + } + } + + /** + * @since 3.1 + * + * @param $property + */ + public function addProperty( $property ) { + if ( $this->items !== null && count( $this->items ) > 0 ) { + foreach ( $this->items as $item ) { + $cur_vals = $item->getFieldValues( $property ); + $this->categorizeItem( $cur_vals, $item ); + } + $this->items = null; + } else { + foreach ( $this->tree as $i => $node ) { + $this->tree[$i]->addProperty( $property ); + } + } + } + +} diff --git a/www/wiki/extensions/SemanticResultFormats/src/Outline/README.md b/www/wiki/extensions/SemanticResultFormats/src/Outline/README.md new file mode 100644 index 00000000..62319dfc --- /dev/null +++ b/www/wiki/extensions/SemanticResultFormats/src/Outline/README.md @@ -0,0 +1,53 @@ +# Outline format + +The format is to display pages in a hierarchical outline form, using one or more of the pages' properties as outline headers. + +## Usage + +``` +{{#ask: [[Category:Task]] + |?Severity + |format=outline + |outlineproperties=Severity, Assigned to + |template=phab-view +}} +``` + +For example, with `phab-view` as template name, the `outline` format will generate two distinct templates `phab-view-header` and `phab-view-item` to be used for the output with `-header` and `-item` being a fixed affix to distinguish the output level of the result content. + +The `...-header` template will provide additional information and includes: +- `#outlinelevel` the level of the header being processed (depends on the iteration level invoked by the `outlineproperties` parameter) +- `#itemcount` provides a count information for the items assigned to the +the level +- `#userparam` + +The `...-item` template will also provide additional information such as: + +- `#itemsection` section number of the item +- `#itemsubject` the subject (aka page) of the item processed +- `#userparam` content provided by a user via the `#ask` `userparam` parameter + + +## Examples + +Using a template can provide means to individually style the result output, for example a simple list can be turned into a phabricator task list. + +``` +{{#ask: [[Category:Task]] + ... + |outlineproperties=Severity + |template=phab-view +}} +``` + +![image](https://user-images.githubusercontent.com/1245473/51059660-d2826b00-15e4-11e9-8ff3-bb1591b04e81.png) + +``` +{{#ask: [[Category:Task]] + ... + |outlineproperties=Severity, Assigned to + |template=phab-view +}} +``` + +![image](https://user-images.githubusercontent.com/1245473/51059791-52103a00-15e5-11e9-85cf-86c503a10b55.png) diff --git a/www/wiki/extensions/SemanticResultFormats/src/Outline/TemplateBuilder.php b/www/wiki/extensions/SemanticResultFormats/src/Outline/TemplateBuilder.php new file mode 100644 index 00000000..e6982e29 --- /dev/null +++ b/www/wiki/extensions/SemanticResultFormats/src/Outline/TemplateBuilder.php @@ -0,0 +1,156 @@ +params = $params; + } + + /** + * @since 3.1 + * + * @param Linker|null|false $linker + */ + public function setLinker( $linker ) { + $this->linker = $linker; + } + + /** + * @since 3.1 + * + * @param OutlineTree $tree + * + * @return string + */ + public function build( OutlineTree $outlineTree ) { + $this->tree( $outlineTree ); + + return $this->template; + } + + private function tree( $outlineTree, $level = 0 ) { + + if ( $outlineTree->items !== null ) { + foreach ( $outlineTree->items as $i => $item ) { + $this->template .= $this->item( $i, $item ); + } + } + + foreach ( $outlineTree->tree as $key => $node ) { + $property = $this->params['outlineproperties'][$level]; + $class = $this->params['template'] . '-section-' . strtolower( str_replace( ' ', '-', $property ) ); + + $this->template .= "
"; + $this->template .= $this->open( $this->params['template'] . "-header" ); + $this->template .= $this->parameter( $property, $key ); + $this->template .= $this->parameter( "#outlinelevel", $level ); + $this->template .= $this->parameter( "#itemcount", $node->leafCount ); + $this->template .= $this->parameter( "#userparam", $this->params['userparam'] ); + $this->template .= $this->close(); + $this->template .= "
"; + $this->tree( $node, $level + 1 ); + $this->template .= "
"; + $this->template .= "
"; + } + } + + private function item( $i, $item ) { + + $first_col = true; + $template = ''; + $linker = $this->params['link'] === 'all' ? $this->linker : null; + $itemnumber = 0; + + foreach ( $item->row as $resultArray ) { + + $printRequest = $resultArray->getPrintRequest(); + $val = $printRequest->getText( SMW_OUTPUT_WIKI, null ); + + if ( in_array( $val, $this->params['outlineproperties'] ) ) { + continue; + } + + while ( ( $dv = $resultArray->getNextDataValue() ) !== false ) { + $template .= $this->open( $this->params['template'] . '-item' ); + $template .= $this->parameter( "#itemsection", $i ); + + $template .= $this->parameter( "#itemnumber", $itemnumber ); + $template .= $this->parameter( "#userparam", $this->params['userparam'] ); + + $template .= $this->itemText( $dv, $linker, $printRequest, $first_col ); + $template .= $this->close(); + + $itemnumber++; + } + } + + return "
" . $template . '
'; + } + + private function itemText( $dv, $linker, $printRequest, &$first_col ) { + + if ( $first_col && $printRequest->isMode( PrintRequest::PRINT_THIS ) ) { + $first_col = false; + + if ( $linker === null && ( $caption = $dv->getDisplayTitle() ) !== '' ) { + $dv->setCaption( $caption ); + } + + $text = $dv->getShortText( + SMW_OUTPUT_WIKI, + $this->params['link'] === 'subject' ? $this->linker : $linker + ); + + return $this->parameter( "#itemsubject", $text ); + } + + $text = $dv->getShortText( + SMW_OUTPUT_WIKI, + $linker + ); + + return $this->parameter( $printRequest->getLabel(), $text ); + } + + private function open( $v ) { + return "{{" . $v; + } + + private function parameter( $k, $v ) { + return " |$k=$v"; + } + + private function close() { + return "}}"; + } + +} \ No newline at end of file diff --git a/www/wiki/extensions/SemanticResultFormats/src/iCalendar/iCalendarFileExportPrinter.php b/www/wiki/extensions/SemanticResultFormats/src/iCalendar/iCalendarFileExportPrinter.php index 90c47a97..e13e243a 100644 --- a/www/wiki/extensions/SemanticResultFormats/src/iCalendar/iCalendarFileExportPrinter.php +++ b/www/wiki/extensions/SemanticResultFormats/src/iCalendar/iCalendarFileExportPrinter.php @@ -2,6 +2,7 @@ namespace SRF\iCalendar; +use SMW\Query\Result\ResultArray; use SMWDataValueFactory as DataValueFactory; use SMWExportPrinter as FileExportPrinter; use SMWQuery as Query; @@ -219,15 +220,20 @@ class iCalendarFileExportPrinter extends FileExportPrinter { /** * Returns the iCal for a single item. + * + * @param ResultArray[] $row + * + * @return string + * @throws \MWException */ private function getIcalForItem( array $row ) { $result = ''; - $subject = $row[0]->getResultSubject(); // get the object - $subject = DataValueFactory::getInstance()->newDataValueByItem( $subject, null ); + $subjectDI = $row[0]->getResultSubject(); // get the object + $subjectDV = DataValueFactory::getInstance()->newDataValueByItem( $subjectDI, null ); $params = [ - 'summary' => $subject->getShortWikiText() + 'summary' => $subjectDV->getShortWikiText() ]; $from = null; @@ -274,7 +280,7 @@ class iCalendarFileExportPrinter extends FileExportPrinter { $this->icalTimezoneFormatter->calcTransitions( $from, $to ); - $title = $subject->getTitle(); + $title = $subjectDI->getTitle(); $timestamp = WikiPage::factory( $title )->getTimestamp(); $url = $title->getFullURL(); diff --git a/www/wiki/extensions/SemanticResultFormats/src/vCard/Address.php b/www/wiki/extensions/SemanticResultFormats/src/vCard/Address.php index 3a8d31be..9145bb21 100644 --- a/www/wiki/extensions/SemanticResultFormats/src/vCard/Address.php +++ b/www/wiki/extensions/SemanticResultFormats/src/vCard/Address.php @@ -30,11 +30,30 @@ class Address { * @param string $type * @param array $adr */ - public function __construct( $type, array $adr ) { + public function __construct( $type, array $adr = [] ) { $this->type = $type; $this->adr = $adr; } + /** + * @since 3.1 + * + * @return boolean + */ + public function hasAddress() { + return $this->adr !== []; + } + + /** + * @since 3.1 + * + * @param string $key + * @param string $value + */ + public function set( $key, $value ) { + $this->adr[$key] = $value; + } + /** * @return string */ diff --git a/www/wiki/extensions/SemanticResultFormats/src/vCard/vCard.php b/www/wiki/extensions/SemanticResultFormats/src/vCard/vCard.php index a52d7aa8..e5d6e0f9 100644 --- a/www/wiki/extensions/SemanticResultFormats/src/vCard/vCard.php +++ b/www/wiki/extensions/SemanticResultFormats/src/vCard/vCard.php @@ -78,6 +78,16 @@ class vCard { $this->vcard = $vcard + $default; } + /** + * @since 3.1 + * + * @param string $key + * @param string $value + */ + public function set( $key, $value ) { + $this->vcard[$key] = $value; + } + /** * @since 3.0 * @@ -146,7 +156,9 @@ class vCard { } foreach ( $vcard['address'] as $a ) { - $text .= $a->text(); + if ( $a->hasAddress() ) { + $text .= $a->text(); + } } foreach ( $vcard['tel'] as $t ) { diff --git a/www/wiki/extensions/SemanticResultFormats/src/vCard/vCardFileExportPrinter.php b/www/wiki/extensions/SemanticResultFormats/src/vCard/vCardFileExportPrinter.php index 0769213d..6ca75fad 100644 --- a/www/wiki/extensions/SemanticResultFormats/src/vCard/vCardFileExportPrinter.php +++ b/www/wiki/extensions/SemanticResultFormats/src/vCard/vCardFileExportPrinter.php @@ -22,6 +22,7 @@ use WikiPage; * @author Markus Krötzsch * @author Denny Vrandecic * @author Frank Dengler + * @author mwjames */ class vCardFileExportPrinter extends FileExportPrinter { @@ -56,7 +57,14 @@ class vCardFileExportPrinter extends FileExportPrinter { */ public function getFileName( QueryResult $queryResult ) { - if ( $this->getSearchLabel( SMW_OUTPUT_WIKI ) != '' ) { + if ( $this->params['filename'] !== '' ) { + + if ( strpos( $this->params['filename'], '.vcf' ) === false ) { + $this->params['filename'] .= '.vcf'; + } + + return str_replace( ' ', '_', $this->params['filename'] ); + } elseif ( $this->getSearchLabel( SMW_OUTPUT_WIKI ) != '' ) { return str_replace( ' ', '_', $this->getSearchLabel( SMW_OUTPUT_WIKI ) ) . '.vcf'; } @@ -74,45 +82,72 @@ class vCardFileExportPrinter extends FileExportPrinter { return ( $context == QueryProcessor::SPECIAL_PAGE ) ? Query::MODE_INSTANCES : Query::MODE_NONE; } + /** + * @see ResultPrinter::getParamDefinitions + * + * @since 1.8 + * + * {@inheritDoc} + */ + public function getParamDefinitions( array $definitions ) { + $params = parent::getParamDefinitions( $definitions ); + + $params['filename'] = [ + 'message' => 'smw-paramdesc-filename', + 'default' => 'vCard.vcf', + ]; + + return $params; + } + /** * @see ResultPrinter::getResultText */ - protected function getResultText( QueryResult $res, $outputmode ) { + protected function getResultText( QueryResult $res, $outputMode ) { - $result = ''; + // Always return a link for when the output mode is not a file request, + // a file request is normally only initiated when resolving the query + // via Special:Ask + if ( $outputMode !== SMW_OUTPUT_FILE ) { + return $this->getVCardLink( $res, $outputMode ); + } - if ( $outputmode == SMW_OUTPUT_FILE ) { // make vCard file - $result = $this->getVCardContent( $res ); - } else { // just make link to vcard + return $this->getVCardContent( $res ); + } - if ( $this->getSearchLabel( $outputmode ) ) { - $label = $this->getSearchLabel( $outputmode ); - } else { - $label = wfMessage( 'srf_vcard_link' )->inContentLanguage()->text(); - } + private function getVCardLink( QueryResult $res, $outputMode ) { - $link = $res->getQueryLink( $label ); - $link->setParameter( 'vcard', 'format' ); + // Can be viewed as HTML if requested, no more parsing needed + $this->isHTML = $outputMode == SMW_OUTPUT_HTML; - if ( $this->getSearchLabel( SMW_OUTPUT_WIKI ) != '' ) { - $link->setParameter( $this->getSearchLabel( SMW_OUTPUT_WIKI ), 'searchlabel' ); - } + if ( $this->getSearchLabel( $outputMode ) ) { + $label = $this->getSearchLabel( $outputMode ); + } else { + $label = wfMessage( 'srf_vcard_link' )->inContentLanguage()->text(); + } - if ( array_key_exists( 'limit', $this->params ) ) { - $link->setParameter( $this->params['limit'], 'limit' ); - } else { // use a reasonable default limit - $link->setParameter( 20, 'limit' ); - } + $link = $res->getQueryLink( $label ); + $link->setParameter( 'vcard', 'format' ); - $result .= $link->getText( $outputmode, $this->mLinker ); + if ( $this->getSearchLabel( SMW_OUTPUT_WIKI ) != '' ) { + $link->setParameter( $this->getSearchLabel( SMW_OUTPUT_WIKI ), 'searchlabel' ); + } - // yes, our code can be viewed as HTML if requested, no more parsing needed - $this->isHTML = ( $outputmode == SMW_OUTPUT_HTML ); + if ( array_key_exists( 'limit', $this->params ) ) { + $link->setParameter( $this->params['limit'], 'limit' ); + } else { // use a reasonable default limit + $link->setParameter( 20, 'limit' ); } - return $result; + return $link->getText( $outputMode, $this->mLinker ); } + /** + * @param QueryResult $res + * + * @return string + * @throws \MWException + */ private function getVCardContent( $res ) { $result = ''; @@ -147,250 +182,232 @@ class vCardFileExportPrinter extends FileExportPrinter { private function newVCard( $row, $uri, $text, $timestamp, $isPublic ) { - // name - $prefix = ''; // something like 'Dr.' - $firstname = ''; // given name - $additionalname = ''; // typically the "middle" name (second first name) - $lastname = ''; // family name - $suffix = ''; // things like "jun." or "sen." - $fullname = ''; // the "formatted name", may be independent from first/lastname & co. - // contacts - $emails = []; - $tels = []; - $addresses = []; - // organisational details: - $organization = ''; // any string - $jobtitle = ''; - $role = ''; - $department = ''; - // other stuff - $category = ''; - $birthday = ''; // a date - $url = ''; // homepage, a legal URL - $note = ''; // any text - $workaddress = false; - $homeaddress = false; - - $workpostofficebox = ''; - $workextendedaddress = ''; - $workstreet = ''; - $worklocality = ''; - $workregion = ''; - $workpostalcode = ''; - $workcountry = ''; - - $homepostofficebox = ''; - $homeextendedaddress = ''; - $homestreet = ''; - $homelocality = ''; - $homeregion = ''; - $homepostalcode = ''; - $homecountry = ''; - - foreach ( $row as $field ) { - // later we may add more things like a generic - // mechanism to add non-standard vCard properties as well - // (could include funny things like geo, description etc.) - $req = $field->getPrintRequest(); - - switch ( strtolower( $req->getLabel() ) ) { - case "name": - $fullname = $this->getFieldValue( $field ); - break; - case "prefix": - $prefix = $this->getFieldCommaList( $field ); - break; - case "suffix": - $suffix = $this->getFieldCommaList( $field ); - break; - case "firstname": - // save only the first - $firstname = $this->getFieldValue( $field ); - break; - case "extraname": - $additionalname = $this->getFieldCommaList( $field ); - break; - case "lastname": - // save only the first - $lastname = $this->getFieldValue( $field ); - break; - case "note": - $note = $this->getFieldCommaList( $field ); - break; - case "category": - $category = $this->getFieldCommaList( $field ); - case "email": - while ( $value = $field->getNextDataValue() ) { - $emails[] = new Email( 'INTERNET', $value->getShortWikiText() ); - } - break; - case "workphone": - while ( $value = $field->getNextDataValue() ) { - $tels[] = new Tel( 'WORK', $value->getShortWikiText() ); - } - break; - case "cellphone": - while ( $value = $field->getNextDataValue() ) { - $tels[] = new Tel( 'CELL', $value->getShortWikiText() ); - } - break; - case "homephone": - while ( $value = $field->getNextDataValue() ) { - $tels[] = new Tel( 'HOME', $value->getShortWikiText() ); - } - break; - case "organization": - $organization = $this->getFieldValue( $field ); - break; - case "workpostofficebox": - if ( ( $workpostofficebox = $this->getFieldValue( $field ) ) !== '' ) { - $workaddress = true; - } - break; - case "workextendedaddress": - if ( ( $workextendedaddress = $this->getFieldValue( $field ) ) !== '' ) { - $workaddress = true; - } - break; - case "workstreet": - if ( ( $workstreet = $this->getFieldValue( $field ) ) !== '' ) { - $workaddress = true; - } - break; - case "worklocality": - if ( ( $worklocality = $this->getFieldValue( $field ) ) !== '' ) { - $workaddress = true; - } - break; - case "workregion": - if ( ( $workregion = $this->getFieldValue( $field ) ) !== '' ) { - $workaddress = true; - } - break; - case "workpostalcode": - if ( ( $workpostalcode = $this->getFieldValue( $field ) ) !== '' ) { - $workaddress = true; - } - break; - case "workcountry": - if ( ( $workcountry = $this->getFieldValue( $field ) ) !== '' ) { - $workaddress = true; - } - break; - case "homepostofficebox": - if ( ( $homepostofficebox = $this->getFieldValue( $field ) ) !== '' ) { - $homeaddress = true; - } - break; - case "homeextendedaddress": - if ( ( $homeextendedaddress = $this->getFieldValue( $field ) ) !== '' ) { - $homeaddress = true; - } - break; - case "homestreet": - if ( ( $homestreet = $this->getFieldValue( $field ) ) !== '' ) { - $homeaddress = true; - } - break; - case "homelocality": - if ( ( $homelocality = $this->getFieldValue( $field ) ) !== '' ) { - $homeaddress = true; - } - break; - case "homeregion": - if ( ( $homeregion = $this->getFieldValue( $field ) ) !== '' ) { - $homeaddress = true; - } - break; - case "homepostalcode": - if ( ( $homepostalcode = $this->getFieldValue( $field ) ) !== '' ) { - $homeaddress = true; - } - break; - case "homecountry": - if ( ( $homecountry = $this->getFieldValue( $field ) ) !== '' ) { - $homeaddress = true; - } - break; - case "birthday": - if ( $req->getTypeID() == TimeValue::TYPE_ID ) { - $value = $field->getNextDataValue(); - if ( $value !== false ) { - $birthday = $value->getISO8601Date(); - } - } - break; - case "homepage": - if ( $req->getTypeID() == "_uri" ) { - $value = $field->getNextDataValue(); - if ( $value !== false ) { - $url = $value->getWikiValue(); - } - } - break; - } - } - - if ( $workaddress ) { - $addresses[] = new Address ( - 'WORK', - [ - 'pobox' => $workpostofficebox, - 'ext' => $workextendedaddress, - 'street' => $workstreet, - 'locality' => $worklocality, - 'region' => $workregion, - 'code' => $workpostalcode, - 'country' => $workcountry - ] - ); - } - - if ( $homeaddress ) { - $addresses[] = new Address ( - 'HOME', - [ - 'pobox' => $homepostofficebox, - 'ext' => $homeextendedaddress, - 'street' => $homestreet, - 'locality' => $homelocality, - 'region' => $homeregion, - 'code' => $homepostalcode, - 'country' => $homecountry - ] - ); - } - $vCard = new vCard( $uri, $text, [ - 'prefix' => $prefix, - 'firstname' => $firstname, - 'lastname' => $lastname, - 'additionalname' => $additionalname, - 'suffix' => $suffix, - 'fullname' => $fullname, - 'tel' => $tels, - 'address' => $addresses, - 'email' => $emails, - 'birthday' => $birthday, - 'title' => $jobtitle, - 'role' => $role, - 'organization' => $organization, - 'department' => $department, - 'category' => $category, - 'url' => $url, - 'note' => $note + + // something like 'Dr.' + 'prefix' => '', + + // given name + 'firstname' => '', + + // family name + 'lastname' => '', + + // typically the "middle" name (second first name) + 'additionalname' => '', + + // things like "jun." or "sen." + 'suffix' => '', + + // the "formatted name", may be independent from + // first/lastname & co. + 'fullname' => '', + + 'tel' => [], + 'address' => [], + 'email' => [], + // a date + 'birthday' => '', + + // organisational details + 'organization' => '', + 'department' => '', + 'title' => '', + 'role' => '', + 'category' => '', + + // homepage, a legal URL + 'url' => '', + + // any text + 'note' => '' ] ); + $tels = []; + $emails = []; + + $addresses['work'] = new Address( 'WORK' ); + $addresses['home'] = new Address( 'HOME' ); + + foreach ( $row as $field ) { + $this->mapField( $field, $vCard, $tels, $addresses, $emails ); + } + + $vCard->set( 'tel', $tels ); + $vCard->set( 'address', $addresses ); + $vCard->set( 'email', $emails ); + $vCard->isPublic( $isPublic ); $vCard->setTimestamp( $timestamp ); return $vCard; } + private function isPublic() { + // heuristic for setting confidentiality level of vCard: + global $wgGroupPermissions; + + if ( ( array_key_exists( '*', $wgGroupPermissions ) ) && ( array_key_exists( + 'read', + $wgGroupPermissions['*'] + ) ) ) { + return $wgGroupPermissions['*']['read']; + } + + return true; + } + + private function mapField( $field, $vCard, &$tels, &$addresses, &$emails ) { + + $printRequest = $field->getPrintRequest(); + + switch ( strtolower( $printRequest->getLabel() ) ) { + case "name": + $vCard->set( 'fullname', $this->getFieldValue( $field ) ); + break; + case "prefix": + $vCard->set( 'prefix', $this->getFieldCommaList( $field ) ); + break; + case "suffix": + $vCard->set( 'suffix', $this->getFieldCommaList( $field ) ); + break; + case "firstname": + // save only the first + $vCard->set( 'firstname', $this->getFieldValue( $field ) ); + break; + case "additionalname": + case "extraname": + $vCard->set( 'additionalname', $this->getFieldCommaList( $field ) ); + break; + case "lastname": + // save only the first + $vCard->set( 'lastname', $this->getFieldValue( $field ) ); + break; + case "note": + $vCard->set( 'note', $this->getFieldCommaList( $field ) ); + break; + case "category": + $vCard->set( 'category', $this->getFieldCommaList( $field ) ); + case "email": + while ( $value = $field->getNextDataValue() ) { + $emails[] = new Email( 'INTERNET', $value->getShortWikiText() ); + } + break; + case "workphone": + while ( $value = $field->getNextDataValue() ) { + $tels[] = new Tel( 'WORK', $value->getShortWikiText() ); + } + break; + case "cellphone": + while ( $value = $field->getNextDataValue() ) { + $tels[] = new Tel( 'CELL', $value->getShortWikiText() ); + } + break; + case "homephone": + while ( $value = $field->getNextDataValue() ) { + $tels[] = new Tel( 'HOME', $value->getShortWikiText() ); + } + break; + case "organization": + $vCard->set( 'organization', $this->getFieldValue( $field ) ); + break; + case "workpostofficebox": + if ( ( $workpostofficebox = $this->getFieldValue( $field ) ) !== '' ) { + $addresses['work']->set( 'pobox', $workpostofficebox ); + } + break; + case "workextendedaddress": + if ( ( $workextendedaddress = $this->getFieldValue( $field ) ) !== '' ) { + $addresses['work']->set( 'ext', $workextendedaddress ); + } + break; + case "workstreet": + if ( ( $workstreet = $this->getFieldValue( $field ) ) !== '' ) { + $addresses['work']->set( 'street', $workstreet ); + } + break; + case "worklocality": + if ( ( $worklocality = $this->getFieldValue( $field ) ) !== '' ) { + $addresses['work']->set( 'locality', $worklocality ); + } + break; + case "workregion": + if ( ( $workregion = $this->getFieldValue( $field ) ) !== '' ) { + $addresses['work']->set( 'region', $workregion ); + } + break; + case "workpostalcode": + if ( ( $workpostalcode = $this->getFieldValue( $field ) ) !== '' ) { + $addresses['work']->set( 'code', $workpostalcode ); + } + break; + case "workcountry": + if ( ( $workcountry = $this->getFieldValue( $field ) ) !== '' ) { + $addresses['work']->set( 'country', $workcountry ); + } + break; + case "homepostofficebox": + if ( ( $homepostofficebox = $this->getFieldValue( $field ) ) !== '' ) { + $addresses['home']->set( 'pobox', $homepostofficebox ); + } + break; + case "homeextendedaddress": + if ( ( $homeextendedaddress = $this->getFieldValue( $field ) ) !== '' ) { + $addresses['home']->set( 'ext', $homeextendedaddress ); + } + break; + case "homestreet": + if ( ( $homestreet = $this->getFieldValue( $field ) ) !== '' ) { + $addresses['home']->set( 'street', $homestreet ); + } + break; + case "homelocality": + if ( ( $homelocality = $this->getFieldValue( $field ) ) !== '' ) { + $addresses['home']->set( 'locality', $homelocality ); + } + break; + case "homeregion": + if ( ( $homeregion = $this->getFieldValue( $field ) ) !== '' ) { + $addresses['home']->set( 'region', $homeregion ); + } + break; + case "homepostalcode": + if ( ( $homepostalcode = $this->getFieldValue( $field ) ) !== '' ) { + $addresses['home']->set( 'code', $homepostalcode ); + } + break; + case "homecountry": + if ( ( $homecountry = $this->getFieldValue( $field ) ) !== '' ) { + $addresses['home']->set( 'country', $homecountry ); + } + break; + case "birthday": + if ( $printRequest->getTypeID() == TimeValue::TYPE_ID ) { + $value = $field->getNextDataValue(); + + if ( $value !== false ) { + $birthday = $value->getISO8601Date(); + $vCard->set( 'birthday', $birthday ); + } + } + break; + case "homepage": + if ( $printRequest->getTypeID() == "_uri" ) { + $value = $field->getNextDataValue(); + + if ( $value !== false ) { + $url = $value->getWikiValue(); + $vCard->set( 'url', $url ); + } + } + break; + } + } + private function getFieldCommaList( $field ) { $list = ''; @@ -413,18 +430,4 @@ class vCardFileExportPrinter extends FileExportPrinter { return $v; } - private function isPublic() { - // heuristic for setting confidentiality level of vCard: - global $wgGroupPermissions; - - if ( ( array_key_exists( '*', $wgGroupPermissions ) ) && ( array_key_exists( - 'read', - $wgGroupPermissions['*'] - ) ) ) { - return $wgGroupPermissions['*']['read']; - } - - return true; - } - } diff --git a/www/wiki/extensions/SemanticResultFormats/tests/bootstrap.php b/www/wiki/extensions/SemanticResultFormats/tests/bootstrap.php index fab8d4df..b8d09067 100644 --- a/www/wiki/extensions/SemanticResultFormats/tests/bootstrap.php +++ b/www/wiki/extensions/SemanticResultFormats/tests/bootstrap.php @@ -14,3 +14,7 @@ $autoloader = require $path; $autoloader->addPsr4( 'SRF\\Tests\\', __DIR__ . '/phpunit' ); $autoloader->addPsr4( 'SMW\\Test\\', __DIR__ . '/../../SemanticMediaWiki/tests/phpunit' ); $autoloader->addPsr4( 'SMW\\Tests\\', __DIR__ . '/../../SemanticMediaWiki/tests/phpunit' ); + +$autoloader->addClassMap( [ + 'SRF\Tests\ResultPrinterReflector' => __DIR__ . '/phpunit/ResultPrinterReflector.php', +] ); diff --git a/www/wiki/extensions/SemanticResultFormats/tests/phpunit/Integration/JSONScript/Fixtures/bibtex-01-0.bib b/www/wiki/extensions/SemanticResultFormats/tests/phpunit/Integration/JSONScript/Fixtures/bibtex-01-0.bib new file mode 100644 index 00000000..6f076aef --- /dev/null +++ b/www/wiki/extensions/SemanticResultFormats/tests/phpunit/Integration/JSONScript/Fixtures/bibtex-01-0.bib @@ -0,0 +1,2 @@ +@Book{, +} \ No newline at end of file diff --git a/www/wiki/extensions/SemanticResultFormats/tests/phpunit/Integration/JSONScript/Fixtures/bibtex-01-1.bib b/www/wiki/extensions/SemanticResultFormats/tests/phpunit/Integration/JSONScript/Fixtures/bibtex-01-1.bib new file mode 100644 index 00000000..8c65f8f1 --- /dev/null +++ b/www/wiki/extensions/SemanticResultFormats/tests/phpunit/Integration/JSONScript/Fixtures/bibtex-01-1.bib @@ -0,0 +1,10 @@ +@Incollection{maskin1985ttoiineas, + address = "Cambridge", + author = "Eric S. Maskin", + booktitle = "Social Goals and Social Organization", + editor = "Leonid Hurwicz, David Schmeidler and Hugo Sonnenschein", + pages = "173-204", + publisher = "Cambridge University Press", + title = "The theory of implementation in nash equilibrium: a survey", + year = "1985", +} \ No newline at end of file diff --git a/www/wiki/extensions/SemanticResultFormats/tests/phpunit/Integration/JSONScript/Fixtures/bibtex-01-2.bib b/www/wiki/extensions/SemanticResultFormats/tests/phpunit/Integration/JSONScript/Fixtures/bibtex-01-2.bib new file mode 100644 index 00000000..5a1d6c5a --- /dev/null +++ b/www/wiki/extensions/SemanticResultFormats/tests/phpunit/Integration/JSONScript/Fixtures/bibtex-01-2.bib @@ -0,0 +1,8 @@ +@Book{abramowitz1964homf, + address = "New York", + author = "Milton Abramowitz and Irene A. Stegun", + edition = "ninth Dover printing, tenth GPO printing", + publisher = "Dover", + title = "Handbook of Mathematical Functions", + year = "1964", +} \ No newline at end of file diff --git a/www/wiki/extensions/SemanticResultFormats/tests/phpunit/Integration/JSONScript/Fixtures/bibtex-01-3.bib b/www/wiki/extensions/SemanticResultFormats/tests/phpunit/Integration/JSONScript/Fixtures/bibtex-01-3.bib new file mode 100644 index 00000000..b5602c23 --- /dev/null +++ b/www/wiki/extensions/SemanticResultFormats/tests/phpunit/Integration/JSONScript/Fixtures/bibtex-01-3.bib @@ -0,0 +1,16 @@ +@Book{maskin1985ttoiineas, + address = "Cambridge", + author = "Eric S. Maskin", + publisher = "Cambridge University Press", + title = "The theory of implementation in nash equilibrium: a survey", + year = "1985", +} + +@Book{abramowitz1964homf, + address = "New York", + author = "Milton Abramowitz and Irene A. Stegun", + edition = "ninth Dover printing, tenth GPO printing", + publisher = "Dover", + title = "Handbook of Mathematical Functions", + year = "1964", +} \ No newline at end of file diff --git a/www/wiki/extensions/SemanticResultFormats/tests/phpunit/Integration/JSONScript/JsonTestCaseScriptRunnerTest.php b/www/wiki/extensions/SemanticResultFormats/tests/phpunit/Integration/JSONScript/JsonTestCaseScriptRunnerTest.php index 458408f4..63e740fc 100644 --- a/www/wiki/extensions/SemanticResultFormats/tests/phpunit/Integration/JSONScript/JsonTestCaseScriptRunnerTest.php +++ b/www/wiki/extensions/SemanticResultFormats/tests/phpunit/Integration/JSONScript/JsonTestCaseScriptRunnerTest.php @@ -1,9 +1,6 @@ [ $this, 'checkMermaidDependency' ] + ]; + } + public function checkMermaidDependency( $val, &$reason ) { + + if ( !defined( 'MERMAID_VERSION' ) ) { + $reason = "Dependency: Mermaid as requirement is not available!"; + return false; + } + + list( $compare, $requiredVersion ) = explode( ' ', $val ); + $version = MERMAID_VERSION; + + if ( !version_compare( $version, $requiredVersion, $compare ) ) { + $reason = "Dependency: Required version of Mermaid($requiredVersion $compare $version) is not available!"; + return false; + } + + return true; + } + +} \ No newline at end of file diff --git a/www/wiki/extensions/SemanticResultFormats/tests/phpunit/Integration/JSONScript/TestCases/bibtex-01.json b/www/wiki/extensions/SemanticResultFormats/tests/phpunit/Integration/JSONScript/TestCases/bibtex-01.json new file mode 100644 index 00000000..2966fbe0 --- /dev/null +++ b/www/wiki/extensions/SemanticResultFormats/tests/phpunit/Integration/JSONScript/TestCases/bibtex-01.json @@ -0,0 +1,159 @@ +{ + "description": "Test `format=bibtex`", + "setup": [ + { + "page": "Has author", + "namespace": "SMW_NS_PROPERTY", + "contents": "[[Has type::Text]]" + }, + { + "page": "Has title", + "namespace": "SMW_NS_PROPERTY", + "contents": "[[Has type::Text]]" + }, + { + "page": "Has publisher", + "namespace": "SMW_NS_PROPERTY", + "contents": "[[Has type::Text]]" + }, + { + "page": "Has year", + "namespace": "SMW_NS_PROPERTY", + "contents": "[[Has type::Date]]" + }, + { + "page": "Has address", + "namespace": "SMW_NS_PROPERTY", + "contents": "[[Has type::Text]]" + }, + { + "page": "Has edition", + "namespace": "SMW_NS_PROPERTY", + "contents": "[[Has type::Text]]" + }, + { + "page": "Has email", + "namespace": "SMW_NS_PROPERTY", + "contents": "[[Has type::Email]]" + }, + { + "page": "BibTex/00", + "contents": "[[Category:Bibtex-00]]" + }, + { + "page": "BibTex/01", + "contents": "[[Category:Bibtex-01]] [[Type::incollection]] [[Has author:: Eric S. Maskin]] [[Has editor::Leonid Hurwicz]] [[Has editor::David Schmeidler]] [[Has editor::Hugo Sonnenschein]] [[Has title::The theory of implementation in nash equilibrium: a survey]] [[Has booktitle::Social Goals and Social Organization]] [[Has year::1985]] [[Has publisher::Cambridge University Press]] [[Has address::Cambridge]] [[Has pages::173-204]]" + }, + { + "page": "BibTex/02", + "contents": "[[Category:Bibtex-02]] [[Has author::Milton Abramowitz]] [[Has author::Irene A. Stegun]] [[Has title::Handbook of Mathematical Functions]] [[Has year::1964]] [[Has publisher::Dover]] [[Has address::New York]] [[Has edition::ninth Dover printing, tenth GPO printing]]" + } + ], + "tests": [ + { + "type": "special", + "about": "#0 `format=bibtex` empty (bibtex-01-0.bib)", + "special-page": { + "page": "Ask", + "request-parameters": { + "p": { + "link": "none", + "limit": "10", + "offset": "0", + "mainlabel": "", + "format": "bibtex" + }, + "q": "[[Category:Bibtex-00]]", + "po": "?Has author=author|?Has title=title|?Has publisher=publisher|?Has year=year|?Has address=address|?Has edition=edition" + } + }, + "assert-output": { + "to-contain": { + "contents-file" : "/../Fixtures/bibtex-01-0.bib" + } + } + }, + { + "type": "special", + "about": "#1 `format=bibtex` single author, editor (bibtex-01-1.bib)", + "special-page": { + "page": "Ask", + "request-parameters": { + "p": { + "link": "none", + "limit": "10", + "offset": "0", + "mainlabel": "", + "format": "bibtex" + }, + "q": "[[Category:Bibtex-01]]", + "po": "?Type=type|?Has author=author|?Has title=title|?Has publisher=publisher|?Has year=year|?Has address=address|?Has edition=edition|?Has editor=editor|?Has pages=pages|?Has booktitle=booktitle" + } + }, + "assert-output": { + "to-contain": { + "contents-file" : "/../Fixtures/bibtex-01-1.bib" + } + } + }, + { + "type": "special", + "about": "#2 `format=bibtex` multiple authors (bibtex-01-2.bib)", + "special-page": { + "page": "Ask", + "request-parameters": { + "p": { + "link": "none", + "limit": "10", + "offset": "0", + "mainlabel": "", + "format": "bibtex" + }, + "q": "[[Category:Bibtex-02]]", + "po": "?Has author=author|?Has title=title|?Has publisher=publisher|?Has year=year|?Has address=address|?Has edition=edition" + } + }, + "assert-output": { + "to-contain": { + "contents-file" : "/../Fixtures/bibtex-01-2.bib" + } + } + }, + { + "type": "special", + "about": "#3 `format=bibtex` multiple records (bibtex-01-3.bib)", + "special-page": { + "page": "Ask", + "request-parameters": { + "p": { + "link": "none", + "limit": "10", + "offset": "0", + "mainlabel": "", + "format": "bibtex" + }, + "q": "[[Category:Bibtex-02]] OR [[Category:Bibtex-01]]", + "po": "?Has author=author|+order=desc|?Has title=title|?Has publisher=publisher|?Has year=year|?Has address=address|?Has edition=edition" + } + }, + "assert-output": { + "to-contain": { + "contents-file" : "/../Fixtures/bibtex-01-3.bib" + } + } + } + ], + "settings": { + "wgContLang": "en", + "wgLang": "en", + "smwgNamespacesWithSemanticLinks": { + "NS_MAIN": true, + "SMW_NS_PROPERTY": true + } + }, + "meta": { + "version": "2", + "is-incomplete": false, + "debug": false + } +} \ No newline at end of file diff --git a/www/wiki/extensions/SemanticResultFormats/tests/phpunit/Integration/JSONScript/TestCases/filtered-01.json b/www/wiki/extensions/SemanticResultFormats/tests/phpunit/Integration/JSONScript/TestCases/filtered-01.json index 84feed09..d86ba5fe 100644 --- a/www/wiki/extensions/SemanticResultFormats/tests/phpunit/Integration/JSONScript/TestCases/filtered-01.json +++ b/www/wiki/extensions/SemanticResultFormats/tests/phpunit/Integration/JSONScript/TestCases/filtered-01.json @@ -187,9 +187,9 @@ [ "div.filtered > div.filtered-views > div.filtered-views-selectors-container > div.filtered-view-selector.filtered-table", 1 ], "div.filtered > div.filtered-views > div.filtered-views-selectors-container > div.filtered-view-selector.filtered-table:contains('Table')", [ "div.filtered > div.filtered-views > div.filtered-views-container > div.filtered-view.filtered-table", 1 ], - [ "div.filtered > div.filtered-views > div.filtered-views-container > div.filtered-view.filtered-table > table > tr > th:not(:empty)", 3 ], - [ "div.filtered > div.filtered-views > div.filtered-views-container > div.filtered-view.filtered-table > table > tr.filtered-table-item:not(:empty)", 8 ], - [ "div.filtered > div.filtered-views > div.filtered-views-container > div.filtered-view.filtered-table > table > tr.filtered-table-item > td", 24 ] + [ "div.filtered > div.filtered-views > div.filtered-views-container > div.filtered-view.filtered-table > table tr > th:not(:empty)", 3 ], + [ "div.filtered > div.filtered-views > div.filtered-views-container > div.filtered-view.filtered-table > table tr.filtered-table-item:not(:empty)", 8 ], + [ "div.filtered > div.filtered-views > div.filtered-views-container > div.filtered-view.filtered-table > table tr.filtered-table-item > td", 24 ] ] } }, @@ -327,4 +327,4 @@ "is-incomplete": false, "debug": false } -} \ No newline at end of file +} diff --git a/www/wiki/extensions/SemanticResultFormats/tests/phpunit/Integration/JSONScript/TestCases/filtered-02.json b/www/wiki/extensions/SemanticResultFormats/tests/phpunit/Integration/JSONScript/TestCases/filtered-02.json index bef26e5b..03512762 100644 --- a/www/wiki/extensions/SemanticResultFormats/tests/phpunit/Integration/JSONScript/TestCases/filtered-02.json +++ b/www/wiki/extensions/SemanticResultFormats/tests/phpunit/Integration/JSONScript/TestCases/filtered-02.json @@ -35,7 +35,7 @@ "assert-output": { "to-be-valid-html": 1, "to-contain": [ - "div.filtered + span.smw-highlighter[data-title=\"Warning\"][title='No map provider specified for \"map\" view.']" + "div.filtered + span.smw-highlighter[data-title=\"Warning\"][title='No map provider specified for \"map\" view.'], div.filtered + p span.smw-highlighter[data-title=\"Warning\"][title='No map provider specified for \"map\" view.']" ] } } @@ -53,4 +53,4 @@ "is-incomplete": false, "debug": false } -} \ No newline at end of file +} diff --git a/www/wiki/extensions/SemanticResultFormats/tests/phpunit/Integration/JSONScript/TestCases/gantt-01.json b/www/wiki/extensions/SemanticResultFormats/tests/phpunit/Integration/JSONScript/TestCases/gantt-01.json new file mode 100644 index 00000000..b35e5d3e --- /dev/null +++ b/www/wiki/extensions/SemanticResultFormats/tests/phpunit/Integration/JSONScript/TestCases/gantt-01.json @@ -0,0 +1,167 @@ +{ + "description": "Test the gantt format", + "requires": { + "Mermaid": ">= 2.0" + }, + "setup": [ + { + "namespace": "SMW_NS_PROPERTY", + "page": "Display Title", + "contents": "[[Has type::Text]]" + }, + { + "namespace": "SMW_NS_PROPERTY", + "page": "Start Date", + "contents": "[[Has type::Date]]" + }, + { + "namespace": "SMW_NS_PROPERTY", + "page": "End Date", + "contents": "[[Has type::Date]]" + }, + { + "namespace": "SMW_NS_PROPERTY", + "page": "Status", + "contents": "[[Has type::Text]]" + }, + { + "namespace": "SMW_NS_PROPERTY", + "page": "Priority", + "contents": "[[Has type::Text]]" + }, + { + "namespace": "SMW_NS_PROPERTY", + "page": "Related To Section", + "contents": "[[Has type::Page]]" + }, + { + "namespace": "NS_TEMPLATE", + "page": "Task", + "contents": "[[Category:Task]][[Display Title::{{{title|}}}]][[Start Date::{{{start|}}}]][[End Date::{{{end|}}}]][[Status::{{{status|}}}]][[Priority::{{{priority|}}}]][[Related To Section::{{{section|}}}]]" + }, + { + "page": "Gantt Test/Task1", + "contents": "{{Task |title=Task01 |start=2019-01-01 |end=2019-01-15 |status=completed |priority=high |section=First Section }}" + }, + { + "page": "Gantt Test/Task2", + "contents": "{{Task |title=Task02 |start=2019-01-15 |end=2019-01-31 |status=backlog |priority=low |section=Second Section }}" + }, + { + "page": "Gantt Test/Task3", + "contents": "{{Task |title=Task03 |start=2019-02-01 |end=2019-02-15 |status=open |priority=critical |section=First Section }}" + }, + { + "page": "Gantt Test/Task4", + "contents": "{{Task |title=Task04 |start=2019-02-15 |end=2019-02-28 |status=done |priority=very high |section=First Section }}" + }, + { + "page": "Example/Gantt Diagram min config", + "contents": "{{#ask:[[Category:Task]] |?Display Title=task |?Status=status |?Start Date=startdate |?End Date=enddate |?Related To Section=section |?Priority=priority| format=gantt }}" + }, + { + "page": "Example/Gantt Diagram config all", + "contents": "{{#ask:[[Category:Task]] |?Display Title=task |?Status=status |?Start Date=startdate |?End Date=enddate |?Related To Section=section |?Priority=priority| format=gantt |theme=forest |sortkey=title |bargap=15 |barheight=40 |titletopmargin=30 |leftpadding=130}}" + }, + { + "page": "Example/Gantt Diagram test axis format 1", + "contents": "{{#ask:[[Category:Task]] |?Display Title=task |?Status=status |?Start Date=startdate |?End Date=enddate |?Related To Section=section |?Priority=priority| format=gantt | axisformat=%m/%d/%Y}}" + }, + { + "page": "Example/Gantt Diagram test axis format 2", + "contents": "{{#ask:[[Category:Task]] |?Display Title=task |?Status=status |?Start Date=startdate |?End Date=enddate |?Related To Section=section |?Priority=priority| format=gantt | axisformat=%m/%Y}}" + }, + { + "page": "Example/Gantt Diagram test axis format 3", + "contents": "{{#ask:[[Category:Task]] |?Display Title=task |?Status=status |?Start Date=startdate |?End Date=enddate |?Related To Section=section |?Priority=priority| format=gantt | axisformat=%B-%Y}}" + }, + { + "page": "Example/Gantt Diagram mapping test 1", + "contents": "{{#ask:[[Category:Task]] |?Display Title=task |?Status=status |?Start Date=startdate |?End Date=enddate |?Related To Section=section |?Priority=priority| format=gantt |theme=forest |sortkey=title |bargap=15 |barheight=40 |titletopmargin=30 |leftpadding=130}}" + }, + { + "page": "Example/Gantt priority mapping test", + "contents": "{{#ask:[[Category:Task]] |?Display Title=task |?Start Date=startdate |?End Date=enddate |?Related To Section=section |?Status=status |?Priority=priority| |prioritymapping=critical=>crit; very high=>crit| format=gantt |theme=forest |sortkey=title |bargap=15 |barheight=40 |titletopmargin=30 |leftpadding=130}}" + }, + { + "page": "Example/Gantt status mapping test", + "contents": "{{#ask:[[Category:Task]] |?Display Title=task |?Start Date=startdate |?End Date=enddate |?Related To Section=section |?Status=status |?Priority=priority| |statusmapping=backlog=>active;open=>active;done=>done| format=gantt |theme=forest |sortkey=title |bargap=15 |barheight=40 |titletopmargin=30 |leftpadding=130}}" + } + ], + "tests": [ + { + "type": "parser-html", + "about": "Test Selector of created SVG", + "subject": "Example/Gantt Diagram min config", + "assert-output": { + "to-be-valid-html": 1, + "to-contain": [ + ["div.mw-parser-output > div.srf-gantt",1] + ] + } + }, + { + "type": "parser", + "about": "Test gantt min config", + "subject": "Example/Gantt Diagram min config", + "assert-output": { + "to-contain": [ + "data-mermaid=\"{"content":"gantt\\ndateFormat YYYY-MM-DD\\naxisFormat %m\\/%d\\/%Y\\nsection First Section\\nTask01\\t :2019-01-01, 2019-01-15\\nTask03\\t :2019-02-01, 2019-02-15\\nTask04\\t :2019-02-15, 2019-02-28\\nsection Second Section\\nTask02\\t :2019-01-15, 2019-01-31\\n","config":{"theme":"default","gantt":{"leftPadding":75,"titleTopMargin":25,"barHeight":20,"barGap":4}}}\">
" + ] + } + }, + { + "type": "parser", + "about": "Test gantt when all config params set", + "subject": "Example/Gantt Diagram config all", + "assert-output": { + "to-contain": [ + "data-mermaid=\"{"content":"gantt\\ndateFormat YYYY-MM-DD\\naxisFormat %m\\/%d\\/%Y\\nsection First Section\\nTask01\\t :2019-01-01, 2019-01-15\\nTask03\\t :2019-02-01, 2019-02-15\\nTask04\\t :2019-02-15, 2019-02-28\\nsection Second Section\\nTask02\\t :2019-01-15, 2019-01-31\\n","config":{"theme":"forest","gantt":{"leftPadding":130,"titleTopMargin":30,"barHeight":40,"barGap":15}}}\">
" + ] + } + }, + { + "type": "parser", + "about": "Test axis forma %m/%d/%Y", + "subject": "Example/Gantt Diagram test axis format 1", + "assert-output": { + "to-contain": [ + "axisFormat %m\\/%d\\/%Y" + ] + } + }, + { + "type": "parser", + "about": "Test priority mapping", + "subject": "Example/Gantt priority mapping test", + "assert-output": { + "to-contain": [ + "data-mermaid=\"{"content":"gantt\\ndateFormat YYYY-MM-DD\\naxisFormat %m\\/%d\\/%Y\\nsection First Section\\nTask01\\t :2019-01-01, 2019-01-15\\nTask03\\t :crit, 2019-02-01, 2019-02-15\\nTask04\\t :crit, 2019-02-15, 2019-02-28\\nsection Second Section\\nTask02\\t :2019-01-15, 2019-01-31\\n","config":{"theme":"forest","gantt":{"leftPadding":130,"titleTopMargin":30,"barHeight":40,"barGap":15}}}\">
" + ] + } + }, + { + "type": "parser", + "about": "Test priority mapping", + "subject": "Example/Gantt status mapping test", + "assert-output": { + "to-contain": [ + "data-mermaid=\"{"content":"gantt\\ndateFormat YYYY-MM-DD\\naxisFormat %m\\/%d\\/%Y\\nsection First Section\\nTask01\\t :2019-01-01, 2019-01-15\\nTask03\\t :active, 2019-02-01, 2019-02-15\\nTask04\\t :done, 2019-02-15, 2019-02-28\\nsection Second Section\\nTask02\\t :active, 2019-01-15, 2019-01-31\\n","config":{"theme":"forest","gantt":{"leftPadding":130,"titleTopMargin":30,"barHeight":40,"barGap":15}}}\">
" + ] + } + } + ], + "settings": { + "wgContLang": "en", + "wgLang": "en", + "smwgNamespacesWithSemanticLinks": { + "NS_MAIN": true, + "SMW_NS_PROPERTY": true + } + }, + "meta": { + "version": "2", + "is-incomplete": false, + "debug": true + } +} \ No newline at end of file diff --git a/www/wiki/extensions/SemanticResultFormats/tests/phpunit/Integration/JSONScript/TestCases/outline-01.json b/www/wiki/extensions/SemanticResultFormats/tests/phpunit/Integration/JSONScript/TestCases/outline-01.json new file mode 100644 index 00000000..f29c8e30 --- /dev/null +++ b/www/wiki/extensions/SemanticResultFormats/tests/phpunit/Integration/JSONScript/TestCases/outline-01.json @@ -0,0 +1,101 @@ +{ + "description": "Test `format=outline` template output", + "setup": [ + { + "namespace": "SMW_NS_PROPERTY", + "page": "Assigned to", + "contents": "[[Has type::Text]]" + }, + { + "namespace": "SMW_NS_PROPERTY", + "page": "Severity", + "contents": "[[Has type::Text]]" + }, + { + "namespace": "NS_TEMPLATE", + "page": "outline-test-header", + "contents": " #outlinelevel: {{{#outlinelevel}}}, #itemcount: {{{#itemcount}}}, Severity: {{{Severity}}}" + }, + { + "namespace": "NS_TEMPLATE", + "page": "outline-test-item", + "contents": " #itemsubject: {{{#itemsubject}}}, #itemsection: {{{#itemsection}}}, #itemnumber: {{{#itemnumber}}}, Assigned to: {{{Assigned to}}}" + }, + { + "page": "SRF/Outline/1", + "contents": "[[Assigned to::John]] [[Severity::Normal]] [[Category:Outline]]" + }, + { + "page": "SRF/Outline/2", + "contents": "[[Assigned to::Jane]] [[Severity::Urgent]] [[Category:Outline]]" + }, + { + "page": "SRF/Outline/3", + "contents": "[[Assigned to::田中]] [[Severity::Urgent]] [[Category:Outline]]" + }, + { + "page": "SRF/Outline/Q.1", + "contents": "{{#ask: [[Category:Outline]] |?Severity |?Assigned to |format=outline |template=outline-test |sort=Severity,Assigned to |outlineproperties=Severity |link=none }}" + }, + { + "page": "SRF/Outline/Q.2", + "contents": "{{#ask: [[Category:Outline]] |?Severity |?Assigned to |format=outline |template=outline-test |sort=Severity,Assigned to |outlineproperties=Severity,Assigned to |link=none }}" + } + ], + "tests": [ + { + "type": "parser", + "about": "#0 (`outlineproperties=Severity`)", + "subject": "SRF/Outline/Q.1", + "assert-output": { + "to-contain": [ + "
#outlinelevel: 0, #itemcount: 1, Severity: Normal", + "
", + "#itemsubject: SRF/Outline/1, #itemsection: 0, #itemnumber: 0, Assigned to: {{{Assigned to}}}", + "#itemsubject: {{{#itemsubject}}}, #itemsection: 0, #itemnumber: 1, Assigned to: John
", + "
#outlinelevel: 0, #itemcount: 2, Severity: Urgent", + "
", + "#itemsubject: SRF/Outline/2, #itemsection: 0, #itemnumber: 0, Assigned to: {{{Assigned to}}}", + "#itemsubject: {{{#itemsubject}}}, #itemsection: 0, #itemnumber: 1, Assigned to: Jane
", + "#itemsubject: SRF/Outline/3, #itemsection: 1, #itemnumber: 0, Assigned to: {{{Assigned to}}}", + "#itemsubject: {{{#itemsubject}}}, #itemsection: 1, #itemnumber: 1, Assigned to: 田中
" + ] + } + }, + { + "type": "parser", + "about": "#1 (`outlineproperties=Severity,Assigned to`)", + "subject": "SRF/Outline/Q.2", + "assert-output": { + "to-contain": [ + "
#outlinelevel: 0, #itemcount: 1, Severity: Normal", + "
", + "#outlinelevel: 1, #itemcount: 1, Severity: {{{Severity}}}", + "
", + "#itemsubject: SRF/Outline/1, #itemsection: 0, #itemnumber: 0, Assigned to: {{{Assigned to}}}
", + "
#outlinelevel: 0, #itemcount: 2, Severity: Urgent", + "
", + "#outlinelevel: 1, #itemcount: 1, Severity: {{{Severity}}}", + "
", + "#itemsubject: SRF/Outline/2, #itemsection: 0, #itemnumber: 0, Assigned to: {{{Assigned to}}}
", + "
#outlinelevel: 1, #itemcount: 1, Severity: {{{Severity}}}", + "
", + "#itemsubject: SRF/Outline/3, #itemsection: 0, #itemnumber: 0, Assigned to: {{{Assigned to}}}
" + ] + } + } + ], + "settings": { + "wgContLang": "en", + "wgLang": "en", + "smwgNamespacesWithSemanticLinks": { + "NS_MAIN": true, + "SMW_NS_PROPERTY": true + } + }, + "meta": { + "version": "2", + "is-incomplete": false, + "debug": false + } +} \ No newline at end of file diff --git a/www/wiki/extensions/SemanticResultFormats/tests/phpunit/Integration/JSONScript/TestCases/outline-02.json b/www/wiki/extensions/SemanticResultFormats/tests/phpunit/Integration/JSONScript/TestCases/outline-02.json new file mode 100644 index 00000000..d9122f8d --- /dev/null +++ b/www/wiki/extensions/SemanticResultFormats/tests/phpunit/Integration/JSONScript/TestCases/outline-02.json @@ -0,0 +1,105 @@ +{ + "description": "Test `format=outline` list output", + "setup": [ + { + "namespace": "SMW_NS_PROPERTY", + "page": "Assigned to", + "contents": "[[Has type::Text]]" + }, + { + "namespace": "SMW_NS_PROPERTY", + "page": "Severity", + "contents": "[[Has type::Text]]" + }, + { + "page": "SRF/Outline/1", + "contents": "[[Assigned to::John]] [[Severity::Normal]] [[Category:Outline]]" + }, + { + "page": "SRF/Outline/2", + "contents": "[[Assigned to::Jane]] [[Severity::Urgent]] [[Category:Outline]]" + }, + { + "page": "SRF/Outline/3", + "contents": "[[Assigned to::田中]] [[Severity::Urgent]] [[Category:Outline]]" + }, + { + "page": "SRF/Outline/Q.1", + "contents": "{{#ask: [[Category:Outline]] |?Severity |?Assigned to |format=outline |sort=Severity,Assigned to |outlineproperties=Severity |link=none }}" + }, + { + "page": "SRF/Outline/Q.2", + "contents": "{{#ask: [[Category:Outline]] |?Severity |?Assigned to |format=outline |sort=Severity,Assigned to |outlineproperties=Severity,Assigned to |link=none }}" + } + ], + "tests": [ + { + "type": "parser", + "about": "#0 (`outlineproperties=Severity`)", + "subject": "SRF/Outline/Q.1", + "assert-output": { + "to-contain": [ + "

Normal

", + "
    ", + "
  • SRF/Outline/1 (Assigned to John)
  • ", + "
", + "
    ", + "
", + "

Urgent

", + "
    ", + "
  • SRF/Outline/2 (Assigned to Jane)
  • ", + "
  • SRF/Outline/3 (Assigned to 田中)
  • ", + "
", + "
    ", + "
" + ] + } + }, + { + "type": "parser", + "about": "#1 (`outlineproperties=Severity,Assigned to`)", + "subject": "SRF/Outline/Q.2", + "assert-output": { + "to-contain": [ + "

Normal

", + "
    ", + "

    John

    ", + "
      ", + "
    • SRF/Outline/1
    • ", + "
    ", + "
      ", + "
    ", + "
", + "

Urgent

", + "
    ", + "

    Jane

    ", + "
      ", + "
    • SRF/Outline/2
    • ", + "
    ", + "
      ", + "
    ", + "

    田中

    ", + "
      ", + "
    • SRF/Outline/3
    • ", + "
    ", + "
      ", + "
    ", + "
" + ] + } + } + ], + "settings": { + "wgContLang": "en", + "wgLang": "en", + "smwgNamespacesWithSemanticLinks": { + "NS_MAIN": true, + "SMW_NS_PROPERTY": true + } + }, + "meta": { + "version": "2", + "is-incomplete": false, + "debug": false + } +} \ No newline at end of file diff --git a/www/wiki/extensions/SemanticResultFormats/tests/phpunit/Integration/JSONScript/TestCases/tree-01.json b/www/wiki/extensions/SemanticResultFormats/tests/phpunit/Integration/JSONScript/TestCases/tree-01.json index 83f0a28a..df5288c0 100644 --- a/www/wiki/extensions/SemanticResultFormats/tests/phpunit/Integration/JSONScript/TestCases/tree-01.json +++ b/www/wiki/extensions/SemanticResultFormats/tests/phpunit/Integration/JSONScript/TestCases/tree-01.json @@ -65,27 +65,27 @@ }, { "page": "Example/Tree 01-01", - "contents": "{{#ask:[[Part of::Tree test]]|format=tree|parent=Has parent}}" + "contents": "
{{#ask:[[Part of::Tree test]]|format=tree|parent=Has parent}}
" }, { "page": "Example/Tree 01-02", - "contents": "{{#ask:[[Part of::Tree test]]|format=tree|parent=Has parent|root=Example/Tree 1}}" + "contents": "
{{#ask:[[Part of::Tree test]]|format=tree|parent=Has parent|root=Example/Tree 1}}
" }, { "page": "Example/Tree 01-03", - "contents": "{{#ask:[[Part of::Tree test]]|format=ultree|parent=Has parent}}" + "contents": "
{{#ask:[[Part of::Tree test]]|format=ultree|parent=Has parent}}
" }, { "page": "Example/Tree 01-04", - "contents": "{{#ask:[[Part of::Tree test]]|format=ultree|parent=Has parent|root=Example/Tree 3}}" + "contents": "
{{#ask:[[Part of::Tree test]]|format=ultree|parent=Has parent|root=Example/Tree 3}}
" }, { "page": "Example/Tree 01-05", - "contents": "{{#ask:[[Part of::Tree test]]|format=oltree|parent=Has parent}}" + "contents": "
{{#ask:[[Part of::Tree test]]|format=oltree|parent=Has parent}}
" }, { "page": "Example/Tree 01-06", - "contents": "{{#ask:[[Part of::Tree test]]|format=oltree|parent=Has parent|root=Example/Tree 2}}" + "contents": "
{{#ask:[[Part of::Tree test]]|format=oltree|parent=Has parent|root=Example/Tree 2}}
" } ], "tests": [ @@ -95,21 +95,21 @@ "subject": "Example/Tree 01-01", "assert-output": { "to-contain": [ - "div.srf-tree > ul > li:nth-child(1) > a[title=\"Example/Tree 1\"] + ul > li:nth-child(1) > a[title=\"Example/Tree 11\"]:only-child", - "div.srf-tree > ul > li:nth-child(1) > a[title=\"Example/Tree 1\"] + ul > li:nth-child(2) > a[title=\"Example/Tree 12\"] + ul > li:only-child > a[title=\"Example/Tree 121\"]:only-child", - "div.srf-tree > ul > li:nth-child(1) > a[title=\"Example/Tree 1\"] + ul > li:nth-child(3) > a[title=\"Example/Tree 13\"]:only-child", + "div > ul > li:nth-child(1) > a[title=\"Example/Tree 1\"] + ul > li:nth-child(1) > a[title=\"Example/Tree 11\"]:only-child", + "div > ul > li:nth-child(1) > a[title=\"Example/Tree 1\"] + ul > li:nth-child(2) > a[title=\"Example/Tree 12\"] + ul > li:only-child > a[title=\"Example/Tree 121\"]:only-child", + "div > ul > li:nth-child(1) > a[title=\"Example/Tree 1\"] + ul > li:nth-child(3) > a[title=\"Example/Tree 13\"]:only-child", - "div.srf-tree > ul > li:nth-child(2) > a[title=\"Example/Tree 2\"] + ul > li:nth-child(1) > a[title=\"Example/Tree 21\"]:only-child", - "div.srf-tree > ul > li:nth-child(2) > a[title=\"Example/Tree 2\"] + ul > li:nth-child(2) > a[title=\"Example/Tree 22\"]:only-child", + "div > ul > li:nth-child(2) > a[title=\"Example/Tree 2\"] + ul > li:nth-child(1) > a[title=\"Example/Tree 21\"]:only-child", + "div > ul > li:nth-child(2) > a[title=\"Example/Tree 2\"] + ul > li:nth-child(2) > a[title=\"Example/Tree 22\"]:only-child", - "div.srf-tree > ul > li:nth-child(3) > a[title=\"Example/Tree 3\"] + ul > li:nth-child(1) > a[title=\"Example/Tree 31\"]:only-child", - "div.srf-tree > ul > li:nth-child(3) > a[title=\"Example/Tree 3\"] + ul > li:nth-child(2) > a[title=\"Example/Tree 32\"] + ul > li:only-child > a[title=\"Example/Tree 321\"]:only-child", - "div.srf-tree > ul > li:nth-child(3) > a[title=\"Example/Tree 3\"] + ul > li:nth-child(3) > a[title=\"Example/Tree 33\"]:only-child", + "div > ul > li:nth-child(3) > a[title=\"Example/Tree 3\"] + ul > li:nth-child(1) > a[title=\"Example/Tree 31\"]:only-child", + "div > ul > li:nth-child(3) > a[title=\"Example/Tree 3\"] + ul > li:nth-child(2) > a[title=\"Example/Tree 32\"] + ul > li:only-child > a[title=\"Example/Tree 321\"]:only-child", + "div > ul > li:nth-child(3) > a[title=\"Example/Tree 3\"] + ul > li:nth-child(3) > a[title=\"Example/Tree 33\"]:only-child", - [ "div.srf-tree > ul:only-child", 1 ], - [ "div.srf-tree > ul > li", 3 ], - [ "div.srf-tree > ul > li > ul > li", 8 ], - [ "div.srf-tree > ul > li > ul > li > ul > li", 2 ] + [ "div > ul:only-child", 1 ], + [ "div > ul > li", 3 ], + [ "div > ul > li > ul > li", 8 ], + [ "div > ul > li > ul > li > ul > li", 2 ] ] } }, @@ -119,13 +119,13 @@ "subject": "Example/Tree 01-02", "assert-output": { "to-contain": [ - "div.srf-tree > ul > li:only-child > a[title=\"Example/Tree 1\"] + ul > li:nth-child(1) > a[title=\"Example/Tree 11\"]:only-child", - "div.srf-tree > ul > li:only-child > a[title=\"Example/Tree 1\"] + ul > li:nth-child(2) > a[title=\"Example/Tree 12\"] + ul > li:only-child > a[title=\"Example/Tree 121\"]:only-child", - "div.srf-tree > ul > li:only-child > a[title=\"Example/Tree 1\"] + ul > li:nth-child(3) > a[title=\"Example/Tree 13\"]:only-child", + "div > ul > li:only-child > a[title=\"Example/Tree 1\"] + ul > li:nth-child(1) > a[title=\"Example/Tree 11\"]:only-child", + "div > ul > li:only-child > a[title=\"Example/Tree 1\"] + ul > li:nth-child(2) > a[title=\"Example/Tree 12\"] + ul > li:only-child > a[title=\"Example/Tree 121\"]:only-child", + "div > ul > li:only-child > a[title=\"Example/Tree 1\"] + ul > li:nth-child(3) > a[title=\"Example/Tree 13\"]:only-child", - [ "div.srf-tree > ul > li", 1 ], - [ "div.srf-tree > ul > li > ul > li", 3 ], - [ "div.srf-tree > ul > li > ul > li > ul > li", 1 ] + [ "div > ul > li", 1 ], + [ "div > ul > li > ul > li", 3 ], + [ "div > ul > li > ul > li > ul > li", 1 ] ] } }, @@ -135,20 +135,20 @@ "subject": "Example/Tree 01-03", "assert-output": { "to-contain": [ - "div.srf-tree > ul > li:nth-child(1) > a[title=\"Example/Tree 1\"] + ul > li:nth-child(1) > a[title=\"Example/Tree 11\"]:only-child", - "div.srf-tree > ul > li:nth-child(1) > a[title=\"Example/Tree 1\"] + ul > li:nth-child(2) > a[title=\"Example/Tree 12\"] + ul > li:only-child > a[title=\"Example/Tree 121\"]:only-child", - "div.srf-tree > ul > li:nth-child(1) > a[title=\"Example/Tree 1\"] + ul > li:nth-child(3) > a[title=\"Example/Tree 13\"]:only-child", + "div > ul > li:nth-child(1) > a[title=\"Example/Tree 1\"] + ul > li:nth-child(1) > a[title=\"Example/Tree 11\"]:only-child", + "div > ul > li:nth-child(1) > a[title=\"Example/Tree 1\"] + ul > li:nth-child(2) > a[title=\"Example/Tree 12\"] + ul > li:only-child > a[title=\"Example/Tree 121\"]:only-child", + "div > ul > li:nth-child(1) > a[title=\"Example/Tree 1\"] + ul > li:nth-child(3) > a[title=\"Example/Tree 13\"]:only-child", - "div.srf-tree > ul > li:nth-child(2) > a[title=\"Example/Tree 2\"] + ul > li:nth-child(1) > a[title=\"Example/Tree 21\"]:only-child", - "div.srf-tree > ul > li:nth-child(2) > a[title=\"Example/Tree 2\"] + ul > li:nth-child(2) > a[title=\"Example/Tree 22\"]:only-child", + "div > ul > li:nth-child(2) > a[title=\"Example/Tree 2\"] + ul > li:nth-child(1) > a[title=\"Example/Tree 21\"]:only-child", + "div > ul > li:nth-child(2) > a[title=\"Example/Tree 2\"] + ul > li:nth-child(2) > a[title=\"Example/Tree 22\"]:only-child", - "div.srf-tree > ul > li:nth-child(3) > a[title=\"Example/Tree 3\"] + ul > li:nth-child(1) > a[title=\"Example/Tree 31\"]:only-child", - "div.srf-tree > ul > li:nth-child(3) > a[title=\"Example/Tree 3\"] + ul > li:nth-child(2) > a[title=\"Example/Tree 32\"] + ul > li:only-child > a[title=\"Example/Tree 321\"]:only-child", - "div.srf-tree > ul > li:nth-child(3) > a[title=\"Example/Tree 3\"] + ul > li:nth-child(3) > a[title=\"Example/Tree 33\"]:only-child", + "div > ul > li:nth-child(3) > a[title=\"Example/Tree 3\"] + ul > li:nth-child(1) > a[title=\"Example/Tree 31\"]:only-child", + "div > ul > li:nth-child(3) > a[title=\"Example/Tree 3\"] + ul > li:nth-child(2) > a[title=\"Example/Tree 32\"] + ul > li:only-child > a[title=\"Example/Tree 321\"]:only-child", + "div > ul > li:nth-child(3) > a[title=\"Example/Tree 3\"] + ul > li:nth-child(3) > a[title=\"Example/Tree 33\"]:only-child", - [ "div.srf-tree > ul > li", 3 ], - [ "div.srf-tree > ul > li > ul > li", 8 ], - [ "div.srf-tree > ul > li > ul > li > ul > li", 2 ] + [ "div > ul > li", 3 ], + [ "div > ul > li > ul > li", 8 ], + [ "div > ul > li > ul > li > ul > li", 2 ] ] } }, @@ -158,13 +158,13 @@ "subject": "Example/Tree 01-04", "assert-output": { "to-contain": [ - "div.srf-tree > ul > li:only-child > a[title=\"Example/Tree 3\"] + ul > li:nth-child(1) > a[title=\"Example/Tree 31\"]:only-child", - "div.srf-tree > ul > li:only-child > a[title=\"Example/Tree 3\"] + ul > li:nth-child(2) > a[title=\"Example/Tree 32\"] + ul > li:only-child > a[title=\"Example/Tree 321\"]:only-child", - "div.srf-tree > ul > li:only-child > a[title=\"Example/Tree 3\"] + ul > li:nth-child(3) > a[title=\"Example/Tree 33\"]:only-child", + "div > ul > li:only-child > a[title=\"Example/Tree 3\"] + ul > li:nth-child(1) > a[title=\"Example/Tree 31\"]:only-child", + "div > ul > li:only-child > a[title=\"Example/Tree 3\"] + ul > li:nth-child(2) > a[title=\"Example/Tree 32\"] + ul > li:only-child > a[title=\"Example/Tree 321\"]:only-child", + "div > ul > li:only-child > a[title=\"Example/Tree 3\"] + ul > li:nth-child(3) > a[title=\"Example/Tree 33\"]:only-child", - [ "div.srf-tree > ul > li", 1 ], - [ "div.srf-tree > ul > li > ul > li", 3 ], - [ "div.srf-tree > ul > li > ul > li > ul > li", 1 ] + [ "div > ul > li", 1 ], + [ "div > ul > li > ul > li", 3 ], + [ "div > ul > li > ul > li > ul > li", 1 ] ] } }, @@ -174,20 +174,20 @@ "subject": "Example/Tree 01-05", "assert-output": { "to-contain": [ - "div.srf-tree > ol > li:nth-child(1) > a[title=\"Example/Tree 1\"] + ol > li:nth-child(1) > a[title=\"Example/Tree 11\"]:only-child", - "div.srf-tree > ol > li:nth-child(1) > a[title=\"Example/Tree 1\"] + ol > li:nth-child(2) > a[title=\"Example/Tree 12\"] + ol > li:only-child > a[title=\"Example/Tree 121\"]:only-child", - "div.srf-tree > ol > li:nth-child(1) > a[title=\"Example/Tree 1\"] + ol > li:nth-child(3) > a[title=\"Example/Tree 13\"]:only-child", + "div > ol > li:nth-child(1) > a[title=\"Example/Tree 1\"] + ol > li:nth-child(1) > a[title=\"Example/Tree 11\"]:only-child", + "div > ol > li:nth-child(1) > a[title=\"Example/Tree 1\"] + ol > li:nth-child(2) > a[title=\"Example/Tree 12\"] + ol > li:only-child > a[title=\"Example/Tree 121\"]:only-child", + "div > ol > li:nth-child(1) > a[title=\"Example/Tree 1\"] + ol > li:nth-child(3) > a[title=\"Example/Tree 13\"]:only-child", - "div.srf-tree > ol > li:nth-child(2) > a[title=\"Example/Tree 2\"] + ol > li:nth-child(1) > a[title=\"Example/Tree 21\"]:only-child", - "div.srf-tree > ol > li:nth-child(2) > a[title=\"Example/Tree 2\"] + ol > li:nth-child(2) > a[title=\"Example/Tree 22\"]:only-child", + "div > ol > li:nth-child(2) > a[title=\"Example/Tree 2\"] + ol > li:nth-child(1) > a[title=\"Example/Tree 21\"]:only-child", + "div > ol > li:nth-child(2) > a[title=\"Example/Tree 2\"] + ol > li:nth-child(2) > a[title=\"Example/Tree 22\"]:only-child", - "div.srf-tree > ol > li:nth-child(3) > a[title=\"Example/Tree 3\"] + ol > li:nth-child(1) > a[title=\"Example/Tree 31\"]:only-child", - "div.srf-tree > ol > li:nth-child(3) > a[title=\"Example/Tree 3\"] + ol > li:nth-child(2) > a[title=\"Example/Tree 32\"] + ol > li:only-child > a[title=\"Example/Tree 321\"]:only-child", - "div.srf-tree > ol > li:nth-child(3) > a[title=\"Example/Tree 3\"] + ol > li:nth-child(3) > a[title=\"Example/Tree 33\"]:only-child", + "div > ol > li:nth-child(3) > a[title=\"Example/Tree 3\"] + ol > li:nth-child(1) > a[title=\"Example/Tree 31\"]:only-child", + "div > ol > li:nth-child(3) > a[title=\"Example/Tree 3\"] + ol > li:nth-child(2) > a[title=\"Example/Tree 32\"] + ol > li:only-child > a[title=\"Example/Tree 321\"]:only-child", + "div > ol > li:nth-child(3) > a[title=\"Example/Tree 3\"] + ol > li:nth-child(3) > a[title=\"Example/Tree 33\"]:only-child", - [ "div.srf-tree > ol > li", 3 ], - [ "div.srf-tree > ol > li > ol > li", 8 ], - [ "div.srf-tree > ol > li > ol > li > ol > li", 2 ] + [ "div > ol > li", 3 ], + [ "div > ol > li > ol > li", 8 ], + [ "div > ol > li > ol > li > ol > li", 2 ] ] } }, @@ -197,11 +197,11 @@ "subject": "Example/Tree 01-06", "assert-output": { "to-contain": [ - "div.srf-tree > ol > li:only-child > a[title=\"Example/Tree 2\"] + ol > li:nth-child(1) > a[title=\"Example/Tree 21\"]:only-child", - "div.srf-tree > ol > li:only-child > a[title=\"Example/Tree 2\"] + ol > li:nth-child(2) > a[title=\"Example/Tree 22\"]:only-child", + "div > ol > li:only-child > a[title=\"Example/Tree 2\"] + ol > li:nth-child(1) > a[title=\"Example/Tree 21\"]:only-child", + "div > ol > li:only-child > a[title=\"Example/Tree 2\"] + ol > li:nth-child(2) > a[title=\"Example/Tree 22\"]:only-child", - [ "div.srf-tree > ol > li", 1 ], - [ "div.srf-tree > ol > li > ol > li", 2 ] + [ "div > ol > li", 1 ], + [ "div > ol > li > ol > li", 2 ] ] } } diff --git a/www/wiki/extensions/SemanticResultFormats/tests/phpunit/Integration/JSONScript/TestCases/tree-02.json b/www/wiki/extensions/SemanticResultFormats/tests/phpunit/Integration/JSONScript/TestCases/tree-02.json index 1a52f698..721c363a 100644 --- a/www/wiki/extensions/SemanticResultFormats/tests/phpunit/Integration/JSONScript/TestCases/tree-02.json +++ b/www/wiki/extensions/SemanticResultFormats/tests/phpunit/Integration/JSONScript/TestCases/tree-02.json @@ -27,6 +27,10 @@ { "page": "Example/Tree 02-02", "contents": "{{#ask:[[Part of::Tree test]]|format=tree|parent=Has parent|template=Example/Tree format template }}" + }, + { + "page": "Example/Tree 02-03", + "contents": "{{#ask:[[Part of::Tree test]]|format=tree|parent=Has parent|class=some-class}}" } ], "tests": [ @@ -36,7 +40,7 @@ "subject": "Example/Tree 02-01", "assert-output": { "to-contain": [ - "div.srf-tree > ul > li:only-child > a[title=\"Example/Tree 1\"]:only-child" + "ul > li:only-child > a[title=\"Example/Tree 1\"]:only-child" ] } }, @@ -46,7 +50,7 @@ "subject": "Example/Tree 02-02", "assert-output": { "to-contain": [ - "div.srf-tree > ul > li:only-child > a[title=\"Example/Tree 1\"]:only-child" + "ul > li:only-child > a[title=\"Example/Tree 1\"]:only-child" ] } }, @@ -59,6 +63,16 @@ "FOO.*Example/Tree 1.*BAR" ] } + }, + { + "type": "parser-html", + "about": "Tree 02-03 (Simple one-page result with class parameter)", + "subject": "Example/Tree 02-03", + "assert-output": { + "to-contain": [ + "div.some-class > ul > li:only-child > a[title=\"Example/Tree 1\"]:only-child" + ] + } } ], "settings": { diff --git a/www/wiki/extensions/SemanticResultFormats/tests/phpunit/Integration/JSONScript/TestCases/tree-06.json b/www/wiki/extensions/SemanticResultFormats/tests/phpunit/Integration/JSONScript/TestCases/tree-06.json index 462be741..c6bc3184 100644 --- a/www/wiki/extensions/SemanticResultFormats/tests/phpunit/Integration/JSONScript/TestCases/tree-06.json +++ b/www/wiki/extensions/SemanticResultFormats/tests/phpunit/Integration/JSONScript/TestCases/tree-06.json @@ -46,11 +46,11 @@ }, { "page": "Example/Tree 06-01", - "contents": "{{#ask:[[Part of::Tree test]] |format=tree |parent=Has parent }}" + "contents": "
{{#ask:[[Part of::Tree test]] |format=tree |parent=Has parent }}
" }, { "page": "Example/Tree 06-02", - "contents": "{{#ask:[[Part of::Tree test]] |format=tree |parent=Has parent |template=Example/Tree format template }}" + "contents": "
{{#ask:[[Part of::Tree test]] |format=tree |parent=Has parent |template=Example/Tree format template }}
" } ], "tests": [ @@ -60,17 +60,17 @@ "subject": "Example/Tree 06-01", "assert-output": { "to-contain": [ - "div.srf-tree > ul > li:nth-child(1) > a[title=\"Example/Tree 1\"] + ul > li:nth-child(1) > a[title=\"Example/Tree 11\"]:only-child", - "div.srf-tree > ul > li:nth-child(1) > a[title=\"Example/Tree 1\"] + ul > li:nth-child(2) > a[title=\"Example/Tree 12\"] + ul > li:only-child > a[title=\"Example/Tree 121\"]:only-child", - "div.srf-tree > ul > li:nth-child(1) > a[title=\"Example/Tree 1\"] + ul > li:nth-child(3) > a[title=\"Example/Tree 13\"]:only-child", + "div > ul > li:nth-child(1) > a[title=\"Example/Tree 1\"] + ul > li:nth-child(1) > a[title=\"Example/Tree 11\"]:only-child", + "div > ul > li:nth-child(1) > a[title=\"Example/Tree 1\"] + ul > li:nth-child(2) > a[title=\"Example/Tree 12\"] + ul > li:only-child > a[title=\"Example/Tree 121\"]:only-child", + "div > ul > li:nth-child(1) > a[title=\"Example/Tree 1\"] + ul > li:nth-child(3) > a[title=\"Example/Tree 13\"]:only-child", - "div.srf-tree > ul > li:nth-child(2) > a[title=\"Example/Tree 2\"] + ul > li:only-child > a[title=\"Example/Tree 12\"] + ul > li:only-child > a[title=\"Example/Tree 121\"]:only-child", + "div > ul > li:nth-child(2) > a[title=\"Example/Tree 2\"] + ul > li:only-child > a[title=\"Example/Tree 12\"] + ul > li:only-child > a[title=\"Example/Tree 121\"]:only-child", - "div.srf-tree > ul > li:nth-child(3) > a[title=\"Example/Tree 3\"]:only-child", + "div > ul > li:nth-child(3) > a[title=\"Example/Tree 3\"]:only-child", - [ "div.srf-tree > ul > li", 3 ], - [ "div.srf-tree > ul > li > ul > li", 4 ], - [ "div.srf-tree > ul > li > ul > li > ul > li", 2 ] + [ "div > ul > li", 3 ], + [ "div > ul > li > ul > li", 4 ], + [ "div > ul > li > ul > li > ul > li", 2 ] ] } }, @@ -80,17 +80,17 @@ "subject": "Example/Tree 06-02", "assert-output": { "to-contain": [ - "div.srf-tree > ul > li:nth-child(1) > a[title=\"Example/Tree 1\"] + ul > li:nth-child(1) > a[title=\"Example/Tree 11\"]:only-child", - "div.srf-tree > ul > li:nth-child(1) > a[title=\"Example/Tree 1\"] + ul > li:nth-child(2) > a[title=\"Example/Tree 12\"] + ul > li:only-child > a[title=\"Example/Tree 121\"]:only-child", - "div.srf-tree > ul > li:nth-child(1) > a[title=\"Example/Tree 1\"] + ul > li:nth-child(3) > a[title=\"Example/Tree 13\"]:only-child", + "div > ul > li:nth-child(1) > a[title=\"Example/Tree 1\"] + ul > li:nth-child(1) > a[title=\"Example/Tree 11\"]:only-child", + "div > ul > li:nth-child(1) > a[title=\"Example/Tree 1\"] + ul > li:nth-child(2) > a[title=\"Example/Tree 12\"] + ul > li:only-child > a[title=\"Example/Tree 121\"]:only-child", + "div > ul > li:nth-child(1) > a[title=\"Example/Tree 1\"] + ul > li:nth-child(3) > a[title=\"Example/Tree 13\"]:only-child", - "div.srf-tree > ul > li:nth-child(2) > a[title=\"Example/Tree 2\"] + ul > li:only-child > a[title=\"Example/Tree 12\"] + ul > li:only-child > a[title=\"Example/Tree 121\"]:only-child", + "div > ul > li:nth-child(2) > a[title=\"Example/Tree 2\"] + ul > li:only-child > a[title=\"Example/Tree 12\"] + ul > li:only-child > a[title=\"Example/Tree 121\"]:only-child", - "div.srf-tree > ul > li:nth-child(3) > a[title=\"Example/Tree 3\"]:only-child", + "div > ul > li:nth-child(3) > a[title=\"Example/Tree 3\"]:only-child", - [ "div.srf-tree > ul > li", 3 ], - [ "div.srf-tree > ul > li > ul > li", 4 ], - [ "div.srf-tree > ul > li > ul > li > ul > li", 2 ] + [ "div > ul > li", 3 ], + [ "div > ul > li > ul > li", 4 ], + [ "div > ul > li > ul > li > ul > li", 2 ] ] } }, diff --git a/www/wiki/extensions/SemanticResultFormats/tests/phpunit/Integration/JSONScript/TestCases/tree-07.json b/www/wiki/extensions/SemanticResultFormats/tests/phpunit/Integration/JSONScript/TestCases/tree-07.json index a2b702c6..8b09e758 100644 --- a/www/wiki/extensions/SemanticResultFormats/tests/phpunit/Integration/JSONScript/TestCases/tree-07.json +++ b/www/wiki/extensions/SemanticResultFormats/tests/phpunit/Integration/JSONScript/TestCases/tree-07.json @@ -56,7 +56,7 @@ "subject": "Example/Tree 07-01", "assert-output": { "to-contain": [ - "div.srf-tree > ul > li > a[title=\"Example/Tree 1\"] + ul > li > a[title=\"Example/Tree 11\"] + a[title=\"Property:Has parent\"] + a[title=\"Example/Tree 1\"]" + "ul > li > a[title=\"Example/Tree 1\"] + ul > li > a[title=\"Example/Tree 11\"] + a[title=\"Property:Has parent\"] + a[title=\"Example/Tree 1\"]" ] } }, @@ -66,7 +66,7 @@ "subject": "Example/Tree 07-02", "assert-output": { "to-contain": [ - "div.srf-tree > ul > li > a[title=\"Example/Tree 1\"] + ul > li > a[title=\"Example/Tree 11\"] + a[title=\"Property:Has parent\"] + a[title=\"Example/Tree 1\"]" + "ul > li > a[title=\"Example/Tree 1\"] + ul > li > a[title=\"Example/Tree 11\"] + a[title=\"Property:Has parent\"] + a[title=\"Example/Tree 1\"]" ] } }, @@ -76,7 +76,7 @@ "subject": "Example/Tree 07-03", "assert-output": { "to-contain": [ - "div.srf-tree > ul > li > a[title=\"Example/Tree 1\"] + ul > li > a[title=\"Example/Tree 11\"]:first-of-type + a[title=\"Property:Has parent\"]:last-of-type" + "ul > li > a[title=\"Example/Tree 1\"] + ul > li > a[title=\"Example/Tree 11\"]:first-of-type + a[title=\"Property:Has parent\"]:last-of-type" ] } @@ -87,7 +87,7 @@ "subject": "Example/Tree 07-04", "assert-output": { "to-contain": [ - "div.srf-tree > ul > li > ul:only-child > li > a[title=\"Property:Has parent\"]:only-child" + "ul > li > ul:only-child > li > a[title=\"Property:Has parent\"]:only-child" ] } }, @@ -107,7 +107,7 @@ "subject": "Example/Tree 07-05", "assert-output": { "to-contain": [ - "div.srf-tree > ul > li > a[title=\"Example/Tree 1\"] + ul > li > a[title=\"Example/Tree 11\"]" + "ul > li > a[title=\"Example/Tree 1\"] + ul > li > a[title=\"Example/Tree 11\"]" ] } }, diff --git a/www/wiki/extensions/SemanticResultFormats/tests/phpunit/Integration/ResourcesTest.php b/www/wiki/extensions/SemanticResultFormats/tests/phpunit/Integration/ResourcesTest.php index d4abad67..20b55c40 100644 --- a/www/wiki/extensions/SemanticResultFormats/tests/phpunit/Integration/ResourcesTest.php +++ b/www/wiki/extensions/SemanticResultFormats/tests/phpunit/Integration/ResourcesTest.php @@ -34,7 +34,15 @@ class ResourcesTest extends \PHPUnit_Framework_TestCase { } public function moduleDataProvider() { - $resourceLoader = new ResourceLoader(); + + // #501 + // MW 1.33+ + if ( class_exists( '\MediaWiki\MediaWikiServices' ) && method_exists( '\MediaWiki\MediaWikiServices', 'getResourceLoader' ) ) { + $resourceLoader = \MediaWiki\MediaWikiServices::getInstance()->getResourceLoader(); + } else { + $resourceLoader = new ResourceLoader(); + } + $context = ResourceLoaderContext::newDummyContext(); $modules = $this->getSRFResourceModules(); @@ -70,4 +78,4 @@ class ResourcesTest extends \PHPUnit_Framework_TestCase { } } -} \ No newline at end of file +} diff --git a/www/wiki/extensions/SemanticResultFormats/tests/phpunit/ResultPrinterReflector.php b/www/wiki/extensions/SemanticResultFormats/tests/phpunit/ResultPrinterReflector.php new file mode 100644 index 00000000..5b9f3ae6 --- /dev/null +++ b/www/wiki/extensions/SemanticResultFormats/tests/phpunit/ResultPrinterReflector.php @@ -0,0 +1,55 @@ +getProperty( 'params' ); + $params->setAccessible( true ); + $params->setValue( $instance, $parameters ); + + if ( isset( $parameters['searchlabel'] ) ) { + $searchlabel = $reflector->getProperty( 'mSearchlabel' ); + $searchlabel->setAccessible( true ); + $searchlabel->setValue( $instance, $parameters['searchlabel'] ); + } + + if ( isset( $parameters['headers'] ) ) { + $searchlabel = $reflector->getProperty( 'mShowHeaders' ); + $searchlabel->setAccessible( true ); + $searchlabel->setValue( $instance, $parameters['headers'] ); + } + + return $instance; + } + + public function invoke( ResultPrinter $instance, $queryResult, $outputMode ) { + + $reflector = new ReflectionClass( $instance ); + $method = $reflector->getMethod( 'getResultText' ); + $method->setAccessible( true ); + + return $method->invoke( $instance, $queryResult, $outputMode ); + } + +} \ No newline at end of file diff --git a/www/wiki/extensions/SemanticResultFormats/tests/phpunit/Unit/BibTex/BibTexFileExportPrinterTest.php b/www/wiki/extensions/SemanticResultFormats/tests/phpunit/Unit/BibTex/BibTexFileExportPrinterTest.php new file mode 100644 index 00000000..86432d11 --- /dev/null +++ b/www/wiki/extensions/SemanticResultFormats/tests/phpunit/Unit/BibTex/BibTexFileExportPrinterTest.php @@ -0,0 +1,149 @@ +assertInstanceOf( + BibTexFileExportPrinter::class, + new BibTexFileExportPrinter( 'bibtex' ) + ); + } + + /** + * @dataProvider filenameProvider + */ + public function testGetFileName( $filename, $searchlabel, $expected ) { + + $parameters = [ + 'filename' => $filename, + 'searchlabel' => $searchlabel + ]; + + $bibTexPrinter = new BibTexFileExportPrinter( 'bibtex' ); + + ( new ResultPrinterReflector() )->addParameters( $bibTexPrinter, $parameters ); + + $this->assertEquals( + $expected, + $bibTexPrinter->getFileName( $this->newQueryResultDummy() ) + ); + } + + public function filenameProvider() { + + yield[ + '', + '', + 'BibTeX.bib' + ]; + + yield[ + '', + 'foo bar', + 'foo_bar.bib' + ]; + + yield[ + 'foo', + '', + 'foo.bib' + ]; + + yield[ + 'foo.bib', + '', + 'foo.bib' + ]; + + yield[ + 'foo bar.bib', + '', + 'foo_bar.bib' + ]; + } + + /** + * @return MockObject|SMWQueryResult + */ + private function newQueryResultDummy() { + return $this->getMockBuilder( SMWQueryResult::class ) + ->disableOriginalConstructor() + ->getMock(); + } + + public function testGetMimeType() { + + $instance = new BibTexFileExportPrinter( + 'bibtex' + ); + + $this->assertEquals( + 'text/bibtex', + $instance->getMimeType( $this->newQueryResultDummy() ) + ); + } + + public function testGetResult_LinkOnNonFileOutput() { + $bibTexPrinter = new BibTexFileExportPrinter( + 'bibtex' + ); + + $this->assertEquals( + 'foo_link', + $bibTexPrinter->getResult( + $this->newMockQueryResultWithLink(), + [], + SMW_OUTPUT_HTML + ) + ); + } + + private function newMockQueryResultWithLink() { + $queryResult = $this->newQueryResultDummy(); + + $queryResult->expects( $this->any() ) + ->method( 'getErrors' ) + ->will( $this->returnValue( [] ) ); + + $queryResult->expects( $this->any() ) + ->method( 'getCount' ) + ->will( $this->returnValue( 1 ) ); + + $queryResult->expects( $this->once() ) + ->method( 'getQueryLink' ) + ->will( $this->returnValue( $this->newInfoLinkStub() ) ); + + return $queryResult; + } + + private function newInfoLinkStub() { + $link = $this->getMockBuilder( SMWInfolink::class ) + ->disableOriginalConstructor() + ->getMock(); + + $link->expects( $this->any() ) + ->method( 'getText' ) + ->will( $this->returnValue( 'foo_link' ) ); + + return $link; + } + +} diff --git a/www/wiki/extensions/SemanticResultFormats/tests/phpunit/Unit/BibTex/ItemTest.php b/www/wiki/extensions/SemanticResultFormats/tests/phpunit/Unit/BibTex/ItemTest.php new file mode 100644 index 00000000..b2102f6a --- /dev/null +++ b/www/wiki/extensions/SemanticResultFormats/tests/phpunit/Unit/BibTex/ItemTest.php @@ -0,0 +1,111 @@ +assertInstanceOf( + Item::class, + new Item() + ); + } + + /** + * @dataProvider fieldsProvider + */ + public function testText( $fields, $expected ) { + + $instance = new Item(); + + foreach ( $fields as $key => $value ) { + $instance->set( $key, $value ); + } + + $this->assertEquals( + $expected, + $instance->text() + ); + } + + /** + * @dataProvider formatterCallbackFieldsProvider + */ + public function testFormatterCallback( $fields, $expected ) { + + $instance = new Item(); + $instance->setFormatterCallback( function( $key, $values ) { + return implode( '#', $values ); + } ); + + foreach ( $fields as $key => $value ) { + $instance->set( $key, $value ); + } + + $this->assertEquals( + $expected, + $instance->text() + ); + } + + /** + * @dataProvider replaceTextProvider + */ + public function testReplace( $key, $text, $expected ) { + + $instance = new Item(); + + $this->assertEquals( + $expected, + $instance->replace( 'uri', $text ) + ); + } + + public function fieldsProvider() { + + yield [ + [ 'foo' => 'test', 'author' => [ 'abc', 'def', '123' ] ], + "@Book{abc,\r\n author = \"abc, def, 123\", \r\n}" + ]; + + yield [ + [ 'foo' => 'test', 'title' => 'foo bar', 'editor' => [ 'abc', 'def', '123' ] ], + "@Book{fb,\r\n editor = \"abc, def, 123\", \r\n title = \"foo bar\", \r\n}" + ]; + } + + public function formatterCallbackFieldsProvider() { + + yield [ + [ 'foo' => 'test', 'author' => [ 'abc', 'def', '123' ] ], + "@Book{abc,\r\n author = \"abc#def#123\", \r\n}" + ]; + + yield [ + [ 'foo' => 'test', 'title' => 'foo bar', 'editor' => [ 'abc', 'def', '123' ] ], + "@Book{fb,\r\n editor = \"abc#def#123\", \r\n title = \"foo bar\", \r\n}" + ]; + } + + public function replaceTextProvider() { + + yield [ + 'uri', + 'abc-_+ÄäÖ', + 'abcAeaeOe' + ]; + } + +} diff --git a/www/wiki/extensions/SemanticResultFormats/tests/phpunit/Unit/Formats/ExcelTest.php b/www/wiki/extensions/SemanticResultFormats/tests/phpunit/Unit/Formats/ExcelTest.php deleted file mode 100644 index ec30881c..00000000 --- a/www/wiki/extensions/SemanticResultFormats/tests/phpunit/Unit/Formats/ExcelTest.php +++ /dev/null @@ -1,45 +0,0 @@ -getMockBuilder( '\SMWInfolink' ) + ->disableOriginalConstructor() + ->getMock(); + + $queryResult = $this->getMockBuilder( '\SMWQueryResult' ) + ->disableOriginalConstructor() + ->getMock(); + + $queryResult->expects( $this->once() ) + ->method( 'getQueryLink' ) + ->will( $this->returnValue( $link ) ); + + $queryResult->expects( $this->any() ) + ->method( 'getCount' ) + ->will( $this->returnValue( 1 ) ); + + $queryResult->expects( $this->any() ) + ->method( 'getErrors' ) + ->will( $this->returnValue( [] ) ); + + $instance = new SpreadsheetPrinter( 'csv' ); + $instance->getResult( $queryResult, [], SMW_OUTPUT_WIKI ); + } + +} diff --git a/www/wiki/extensions/SemanticResultFormats/tests/phpunit/Unit/Outline/ListTreeBuilderTest.php b/www/wiki/extensions/SemanticResultFormats/tests/phpunit/Unit/Outline/ListTreeBuilderTest.php new file mode 100644 index 00000000..80679e90 --- /dev/null +++ b/www/wiki/extensions/SemanticResultFormats/tests/phpunit/Unit/Outline/ListTreeBuilderTest.php @@ -0,0 +1,41 @@ +assertInstanceOf( + ListTreeBuilder::class, + new ListTreeBuilder( [] ) + ); + } + + public function testBuildForEmptyTree() { + + $params = [ + 'outlineproperties' => [ 'Foo' ] + ]; + + $instance = new ListTreeBuilder( $params ); + + $this->assertInternalType( + 'string', + $instance->build( new OutlineTree() ) + ); + } + +} diff --git a/www/wiki/extensions/SemanticResultFormats/tests/phpunit/Unit/Outline/OutlineItemTest.php b/www/wiki/extensions/SemanticResultFormats/tests/phpunit/Unit/Outline/OutlineItemTest.php new file mode 100644 index 00000000..6819ee10 --- /dev/null +++ b/www/wiki/extensions/SemanticResultFormats/tests/phpunit/Unit/Outline/OutlineItemTest.php @@ -0,0 +1,36 @@ +assertInstanceOf( + OutlineItem::class, + new OutlineItem( [] ) + ); + } + + public function testPropertyAccess() { + + $instance = new OutlineItem( [ 'Foo' ] ); + + $this->assertEquals( + [ 'Foo' ], + $instance->row + ); + } + +} diff --git a/www/wiki/extensions/SemanticResultFormats/tests/phpunit/Unit/Outline/OutlineResultPrinterTest.php b/www/wiki/extensions/SemanticResultFormats/tests/phpunit/Unit/Outline/OutlineResultPrinterTest.php new file mode 100644 index 00000000..523ecf9b --- /dev/null +++ b/www/wiki/extensions/SemanticResultFormats/tests/phpunit/Unit/Outline/OutlineResultPrinterTest.php @@ -0,0 +1,96 @@ +queryResult = $this->getMockBuilder( '\SMWQueryResult' ) + ->disableOriginalConstructor() + ->getMock(); + } + + public function testCanConstruct() { + + $this->assertInstanceOf( + OutlineResultPrinter::class, + new OutlineResultPrinter( 'outline' ) + ); + } + + public function testGetResult_LinkOnNonFileOutput() { + + $link = $this->getMockBuilder( '\SMWInfolink' ) + ->disableOriginalConstructor() + ->getMock(); + + $link->expects( $this->any() ) + ->method( 'getText' ) + ->will( $this->returnValue( 'foo_link' ) ); + + $this->queryResult->expects( $this->any() ) + ->method( 'getErrors' ) + ->will( $this->returnValue( [] ) ); + + $this->queryResult->expects( $this->any() ) + ->method( 'getCount' ) + ->will( $this->returnValue( 1 ) ); + + $instance = new OutlineResultPrinter( + 'outline' + ); + + // IParam is an empty interface !!! so we use stdClass + $outlineproperties = $this->getMockBuilder( '\stdClass' ) + ->disableOriginalConstructor() + ->setMethods( [ 'getName', 'getValue' ] ) + ->getMock(); + + $outlineproperties->expects( $this->any() ) + ->method( 'getName' ) + ->will( $this->returnValue( 'outlineproperties' ) ); + + $outlineproperties->expects( $this->any() ) + ->method( 'getValue' ) + ->will( $this->returnValue( [] ) ); + + $template = $this->getMockBuilder( '\stdClass' ) + ->disableOriginalConstructor() + ->setMethods( [ 'getName', 'getValue' ] ) + ->getMock(); + + $template->expects( $this->any() ) + ->method( 'getName' ) + ->will( $this->returnValue( 'template' ) ); + + $template->expects( $this->any() ) + ->method( 'getValue' ) + ->will( $this->returnValue( '' ) ); + + $parameters = [ + $outlineproperties, + $template + ]; + + $this->assertContains( + "
    \n
\n", + $instance->getResult( $this->queryResult, $parameters, SMW_OUTPUT_HTML ) + ); + } + +} diff --git a/www/wiki/extensions/SemanticResultFormats/tests/phpunit/Unit/Outline/OutlineTreeTest.php b/www/wiki/extensions/SemanticResultFormats/tests/phpunit/Unit/Outline/OutlineTreeTest.php new file mode 100644 index 00000000..3af491c0 --- /dev/null +++ b/www/wiki/extensions/SemanticResultFormats/tests/phpunit/Unit/Outline/OutlineTreeTest.php @@ -0,0 +1,49 @@ +assertInstanceOf( + OutlineTree::class, + new OutlineTree( [] ) + ); + } + + public function testPropertyAccess() { + + $instance = new OutlineTree(); + + $this->assertEmpty( + $instance->tree + ); + + $this->assertEmpty( + $instance->items + ); + + $this->assertEquals( + 0, + $instance->itemCount + ); + + $this->assertEquals( + 0, + $instance->leafCount + ); + } + +} diff --git a/www/wiki/extensions/SemanticResultFormats/tests/phpunit/Unit/Outline/TemplateBuilderTest.php b/www/wiki/extensions/SemanticResultFormats/tests/phpunit/Unit/Outline/TemplateBuilderTest.php new file mode 100644 index 00000000..ac00d67c --- /dev/null +++ b/www/wiki/extensions/SemanticResultFormats/tests/phpunit/Unit/Outline/TemplateBuilderTest.php @@ -0,0 +1,43 @@ +assertInstanceOf( + TemplateBuilder::class, + new TemplateBuilder( [] ) + ); + } + + public function testBuildForEmptyTree() { + + $params = [ + 'outlineproperties' => [ 'Foo' ], + 'template' => 'Bar', + 'userparam' => '' + ]; + + $instance = new TemplateBuilder( $params ); + + $this->assertInternalType( + 'string', + $instance->build( new OutlineTree() ) + ); + } + +} diff --git a/www/wiki/extensions/SemanticResultFormats/tests/phpunit/Unit/vCard/AddressTest.php b/www/wiki/extensions/SemanticResultFormats/tests/phpunit/Unit/vCard/AddressTest.php index 52c8b5a1..0b4855b2 100644 --- a/www/wiki/extensions/SemanticResultFormats/tests/phpunit/Unit/vCard/AddressTest.php +++ b/www/wiki/extensions/SemanticResultFormats/tests/phpunit/Unit/vCard/AddressTest.php @@ -23,6 +23,15 @@ class AddressTest extends \PHPUnit_Framework_TestCase { ); } + public function testHasAddress() { + + $instance = new Address( '', [] ); + + $this->assertFalse( + $instance->hasAddress() + ); + } + public function testText() { $adr = [ @@ -36,9 +45,10 @@ class AddressTest extends \PHPUnit_Framework_TestCase { ]; $instance = new Address( '', $adr ); + $instance->set( 'region', 'Bar0042' ); $this->assertSame( - "ADR;TYPE=WORK;CHARSET=UTF-8:;;2 Example Avenue;Anytown;Foo;01111;Bar\r\n", + "ADR;TYPE=WORK;CHARSET=UTF-8:;;2 Example Avenue;Anytown;Bar0042;01111;Bar\r\n", $instance->text() ); } diff --git a/www/wiki/extensions/SemanticResultFormats/tests/phpunit/Unit/vCard/vCardFileExportPrinterTest.php b/www/wiki/extensions/SemanticResultFormats/tests/phpunit/Unit/vCard/vCardFileExportPrinterTest.php new file mode 100644 index 00000000..f094268f --- /dev/null +++ b/www/wiki/extensions/SemanticResultFormats/tests/phpunit/Unit/vCard/vCardFileExportPrinterTest.php @@ -0,0 +1,133 @@ +resultPrinterReflector = new ResultPrinterReflector(); + + $this->queryResult = $this->getMockBuilder( '\SMWQueryResult' ) + ->disableOriginalConstructor() + ->getMock(); + } + + public function testCanConstruct() { + + $this->assertInstanceOf( + vCardFileExportPrinter::class, + new vCardFileExportPrinter( 'vcard' ) + ); + } + + /** + * @dataProvider filenameProvider + */ + public function testGetFileName( $filename, $searchlabel, $expected ) { + + $parameters = [ + 'filename' => $filename, + 'searchlabel' => $searchlabel + ]; + + $instance = new vCardFileExportPrinter( + 'vcard' + ); + + $this->resultPrinterReflector->addParameters( $instance, $parameters ); + + $this->assertEquals( + $expected, + $instance->getFileName( $this->queryResult ) + ); + } + + public function testGetMimeType() { + + $instance = new vCardFileExportPrinter( + 'vcard' + ); + + $this->assertEquals( + 'text/x-vcard', + $instance->getMimeType( $this->queryResult ) + ); + } + + public function testGetResult_LinkOnNonFileOutput() { + + $link = $this->getMockBuilder( '\SMWInfolink' ) + ->disableOriginalConstructor() + ->getMock(); + + $link->expects( $this->any() ) + ->method( 'getText' ) + ->will( $this->returnValue( 'foo_link' ) ); + + $this->queryResult->expects( $this->any() ) + ->method( 'getErrors' ) + ->will( $this->returnValue( [] ) ); + + $this->queryResult->expects( $this->any() ) + ->method( 'getCount' ) + ->will( $this->returnValue( 1 ) ); + + $this->queryResult->expects( $this->once() ) + ->method( 'getQueryLink' ) + ->will( $this->returnValue( $link ) ); + + $instance = new vCardFileExportPrinter( + 'vcard' + ); + + $this->assertEquals( + 'foo_link', + $instance->getResult( $this->queryResult, [], SMW_OUTPUT_HTML ) + ); + } + + public function filenameProvider() { + + yield[ + '', + 'foo bar', + 'foo_bar.vcf' + ]; + + yield[ + 'foo', + '', + 'foo.vcf' + ]; + + yield[ + 'foo.vcf', + '', + 'foo.vcf' + ]; + + yield[ + 'foo bar.vcf', + '', + 'foo_bar.vcf' + ]; + } + +} diff --git a/www/wiki/extensions/SemanticResultFormats/tests/phpunit/Unit/vCard/vCardTest.php b/www/wiki/extensions/SemanticResultFormats/tests/phpunit/Unit/vCard/vCardTest.php index 6d3d46df..2467df95 100644 --- a/www/wiki/extensions/SemanticResultFormats/tests/phpunit/Unit/vCard/vCardTest.php +++ b/www/wiki/extensions/SemanticResultFormats/tests/phpunit/Unit/vCard/vCardTest.php @@ -23,9 +23,10 @@ class vCardTest extends \PHPUnit_Framework_TestCase { ); } - public function testTextOnEmptyCard() { + public function testEmptyCard() { $instance = new vCard( 'http://example.org/Foo', 'Foo', [] ); + $instance->set( 'url', 'http://example.org/Bar' ); $this->assertSame( "BEGIN:VCARD\r\n" . @@ -36,7 +37,7 @@ class vCardTest extends \PHPUnit_Framework_TestCase { "SOURCE;CHARSET=UTF-8:http://example.org/Foo\r\n" . "PRODID:-////Semantic MediaWiki\r\n" . "REV:\r\n" . - "URL:http://example.org/Foo\r\n" . + "URL:http://example.org/Bar\r\n" . "UID:http://example.org/Foo\r\n" . "END:VCARD\r\n", $instance->text() diff --git a/www/wiki/skins/bo/reevo.less b/www/wiki/skins/bo/reevo.less index 9d7283c6..e3b215d9 100644 --- a/www/wiki/skins/bo/reevo.less +++ b/www/wiki/skins/bo/reevo.less @@ -1,5 +1,9 @@ // Adaptaciones hechas para REEVO +body.page-Portada #firstHeading { + display: none !important; +} + .mw-body { z-index: auto !important; padding: 0 !important; @@ -140,3 +144,11 @@ body.mw-mmv-lightbox-open #mw-navigation { .ui-datepicker-header, .ui-datepicker-calendar, .ui-datepicker-buttonpane { background: #ccc; } + +#bodyContent .col-form-label { + text-align: left; +} + +div#bodyContent { + margin: 0 15px; +} diff --git a/www/wiki/vendor/composer/autoload_classmap.php b/www/wiki/vendor/composer/autoload_classmap.php index 03595271..f092b4f6 100644 --- a/www/wiki/vendor/composer/autoload_classmap.php +++ b/www/wiki/vendor/composer/autoload_classmap.php @@ -45,6 +45,7 @@ return array( 'Composer\\Installers\\CraftInstaller' => $vendorDir . '/composer/installers/src/Composer/Installers/CraftInstaller.php', 'Composer\\Installers\\CroogoInstaller' => $vendorDir . '/composer/installers/src/Composer/Installers/CroogoInstaller.php', 'Composer\\Installers\\DecibelInstaller' => $vendorDir . '/composer/installers/src/Composer/Installers/DecibelInstaller.php', + 'Composer\\Installers\\DframeInstaller' => $vendorDir . '/composer/installers/src/Composer/Installers/DframeInstaller.php', 'Composer\\Installers\\DokuWikiInstaller' => $vendorDir . '/composer/installers/src/Composer/Installers/DokuWikiInstaller.php', 'Composer\\Installers\\DolibarrInstaller' => $vendorDir . '/composer/installers/src/Composer/Installers/DolibarrInstaller.php', 'Composer\\Installers\\DrupalInstaller' => $vendorDir . '/composer/installers/src/Composer/Installers/DrupalInstaller.php', @@ -62,6 +63,7 @@ return array( 'Composer\\Installers\\JoomlaInstaller' => $vendorDir . '/composer/installers/src/Composer/Installers/JoomlaInstaller.php', 'Composer\\Installers\\KanboardInstaller' => $vendorDir . '/composer/installers/src/Composer/Installers/KanboardInstaller.php', 'Composer\\Installers\\KirbyInstaller' => $vendorDir . '/composer/installers/src/Composer/Installers/KirbyInstaller.php', + 'Composer\\Installers\\KnownInstaller' => $vendorDir . '/composer/installers/src/Composer/Installers/KnownInstaller.php', 'Composer\\Installers\\KodiCMSInstaller' => $vendorDir . '/composer/installers/src/Composer/Installers/KodiCMSInstaller.php', 'Composer\\Installers\\KohanaInstaller' => $vendorDir . '/composer/installers/src/Composer/Installers/KohanaInstaller.php', 'Composer\\Installers\\LanManagementSystemInstaller' => $vendorDir . '/composer/installers/src/Composer/Installers/LanManagementSystemInstaller.php', @@ -96,6 +98,7 @@ return array( 'Composer\\Installers\\PxcmsInstaller' => $vendorDir . '/composer/installers/src/Composer/Installers/PxcmsInstaller.php', 'Composer\\Installers\\RadPHPInstaller' => $vendorDir . '/composer/installers/src/Composer/Installers/RadPHPInstaller.php', 'Composer\\Installers\\ReIndexInstaller' => $vendorDir . '/composer/installers/src/Composer/Installers/ReIndexInstaller.php', + 'Composer\\Installers\\Redaxo5Installer' => $vendorDir . '/composer/installers/src/Composer/Installers/Redaxo5Installer.php', 'Composer\\Installers\\RedaxoInstaller' => $vendorDir . '/composer/installers/src/Composer/Installers/RedaxoInstaller.php', 'Composer\\Installers\\RoundcubeInstaller' => $vendorDir . '/composer/installers/src/Composer/Installers/RoundcubeInstaller.php', 'Composer\\Installers\\SMFInstaller' => $vendorDir . '/composer/installers/src/Composer/Installers/SMFInstaller.php', @@ -106,6 +109,7 @@ return array( 'Composer\\Installers\\Symfony1Installer' => $vendorDir . '/composer/installers/src/Composer/Installers/Symfony1Installer.php', 'Composer\\Installers\\TYPO3CmsInstaller' => $vendorDir . '/composer/installers/src/Composer/Installers/TYPO3CmsInstaller.php', 'Composer\\Installers\\TYPO3FlowInstaller' => $vendorDir . '/composer/installers/src/Composer/Installers/TYPO3FlowInstaller.php', + 'Composer\\Installers\\TaoInstaller' => $vendorDir . '/composer/installers/src/Composer/Installers/TaoInstaller.php', 'Composer\\Installers\\TheliaInstaller' => $vendorDir . '/composer/installers/src/Composer/Installers/TheliaInstaller.php', 'Composer\\Installers\\TuskInstaller' => $vendorDir . '/composer/installers/src/Composer/Installers/TuskInstaller.php', 'Composer\\Installers\\UserFrostingInstaller' => $vendorDir . '/composer/installers/src/Composer/Installers/UserFrostingInstaller.php', @@ -596,6 +600,7 @@ return array( 'Maps\\Presentation\\CoordinateFormatter' => $baseDir . '/extensions/Maps/src/Presentation/CoordinateFormatter.php', 'Maps\\Presentation\\ElementJsonSerializer' => $baseDir . '/extensions/Maps/src/Presentation/ElementJsonSerializer.php', 'Maps\\Presentation\\KmlFormatter' => $baseDir . '/extensions/Maps/src/Presentation/KmlFormatter.php', + 'Maps\\Presentation\\MapHtmlBuilder' => $baseDir . '/extensions/Maps/src/Presentation/MapHtmlBuilder.php', 'Maps\\Presentation\\MapsDistanceParser' => $baseDir . '/extensions/Maps/src/Presentation/MapsDistanceParser.php', 'Maps\\Presentation\\ParameterExtractor' => $baseDir . '/extensions/Maps/src/Presentation/ParameterExtractor.php', 'Maps\\Presentation\\WikitextParser' => $baseDir . '/extensions/Maps/src/Presentation/WikitextParser.php', @@ -648,6 +653,25 @@ return array( 'Maps\\Tests\\Unit\\Semantic\\DataValues\\CoordinateValueTest' => $baseDir . '/extensions/Maps/tests/Unit/Semantic/DataValues/CoordinateValueTest.php', 'Maps\\Tests\\Util\\PageCreator' => $baseDir . '/extensions/Maps/tests/Util/PageCreator.php', 'Maps\\Tests\\Util\\TestFactory' => $baseDir . '/extensions/Maps/tests/Util/TestFactory.php', + 'Mermaid\\HookRegistry' => $baseDir . '/extensions/Mermaid/src/HookRegistry.php', + 'Mermaid\\MermaidParserFunction' => $baseDir . '/extensions/Mermaid/src/MermaidParserFunction.php', + 'ModernTimeline\\Event' => $baseDir . '/extensions/ModernTimeline/src/Event.php', + 'ModernTimeline\\JsonBuilder' => $baseDir . '/extensions/ModernTimeline/src/JsonBuilder.php', + 'ModernTimeline\\ModernTimelinePrinter' => $baseDir . '/extensions/ModernTimeline/src/ModernTimelinePrinter.php', + 'ModernTimeline\\ModernTimelineSetup' => $baseDir . '/extensions/ModernTimeline/src/ModernTimelineSetup.php', + 'ModernTimeline\\ResultFacade\\PropertyValueCollection' => $baseDir . '/extensions/ModernTimeline/src/ResultFacade/PropertyValueCollection.php', + 'ModernTimeline\\ResultFacade\\ResultSimplifier' => $baseDir . '/extensions/ModernTimeline/src/ResultFacade/ResultSimplifier.php', + 'ModernTimeline\\ResultFacade\\Subject' => $baseDir . '/extensions/ModernTimeline/src/ResultFacade/Subject.php', + 'ModernTimeline\\ResultFacade\\SubjectCollection' => $baseDir . '/extensions/ModernTimeline/src/ResultFacade/SubjectCollection.php', + 'ModernTimeline\\SlidePresenter\\SimpleSlidePresenter' => $baseDir . '/extensions/ModernTimeline/src/SlidePresenter/SimpleSlidePresenter.php', + 'ModernTimeline\\SlidePresenter\\SlidePresenter' => $baseDir . '/extensions/ModernTimeline/src/SlidePresenter/SlidePresenter.php', + 'ModernTimeline\\SlidePresenter\\TemplateSlidePresenter' => $baseDir . '/extensions/ModernTimeline/src/SlidePresenter/TemplateSlidePresenter.php', + 'ModernTimeline\\Tests\\Integration\\OptionsTest' => $baseDir . '/extensions/ModernTimeline/tests/Integration/OptionsTest.php', + 'ModernTimeline\\Tests\\System\\JsonScriptTest' => $baseDir . '/extensions/ModernTimeline/tests/System/JsonScriptTest.php', + 'ModernTimeline\\Tests\\Unit\\JsonBuilderTest' => $baseDir . '/extensions/ModernTimeline/tests/Unit/JsonBuilderTest.php', + 'ModernTimeline\\Tests\\Unit\\SlidePresenter\\TemplateSlidePresenterTest' => $baseDir . '/extensions/ModernTimeline/tests/Unit/SlidePresenter/TemplateSlidePresenterTest.php', + 'ModernTimeline\\TimelineOptions' => $baseDir . '/extensions/ModernTimeline/src/TimelineOptions.php', + 'ModernTimeline\\TimelinePresenter' => $baseDir . '/extensions/ModernTimeline/src/TimelinePresenter.php', 'Net_SMTP' => $vendorDir . '/pear/net_smtp/Net/SMTP.php', 'Net_Socket' => $vendorDir . '/pear/net_socket/Net/Socket.php', 'OOUI\\AccessKeyedElement' => $vendorDir . '/oojs/oojs-ui/php/mixins/AccessKeyedElement.php', @@ -777,13 +801,14 @@ return array( 'PEAR_ErrorStack' => $vendorDir . '/pear/pear-core-minimal/src/PEAR/ErrorStack.php', 'PEAR_Exception' => $vendorDir . '/pear/pear_exception/PEAR/Exception.php', 'PEAR_ExceptionTest' => $vendorDir . '/pear/pear_exception/tests/PEAR/ExceptionTest.php', - 'ParamProcessor\\Definition\\DimensionParam' => $vendorDir . '/param-processor/param-processor/src/Definition/DimensionParam.php', 'ParamProcessor\\Definition\\StringParam' => $vendorDir . '/param-processor/param-processor/src/Definition/StringParam.php', 'ParamProcessor\\IParam' => $vendorDir . '/param-processor/param-processor/src/IParam.php', 'ParamProcessor\\IParamDefinition' => $vendorDir . '/param-processor/param-processor/src/IParamDefinition.php', 'ParamProcessor\\MediaWikiTitleValue' => $baseDir . '/extensions/Validator/src/ParamProcessor/MediaWikiTitleValue.php', 'ParamProcessor\\Options' => $vendorDir . '/param-processor/param-processor/src/Options.php', - 'ParamProcessor\\Param' => $vendorDir . '/param-processor/param-processor/src/Param.php', + 'ParamProcessor\\PackagePrivate\\DimensionParser' => $vendorDir . '/param-processor/param-processor/src/PackagePrivate/DimensionParser.php', + 'ParamProcessor\\PackagePrivate\\Param' => $vendorDir . '/param-processor/param-processor/src/PackagePrivate/Param.php', + 'ParamProcessor\\PackagePrivate\\ParamType' => $vendorDir . '/param-processor/param-processor/src/PackagePrivate/ParamType.php', 'ParamProcessor\\ParamDefinition' => $vendorDir . '/param-processor/param-processor/src/ParamDefinition.php', 'ParamProcessor\\ParamDefinitionFactory' => $vendorDir . '/param-processor/param-processor/src/ParamDefinitionFactory.php', 'ParamProcessor\\ParameterTypes' => $vendorDir . '/param-processor/param-processor/src/ParameterTypes.php', @@ -794,8 +819,6 @@ return array( 'ParamProcessor\\Processor' => $vendorDir . '/param-processor/param-processor/src/Processor.php', 'ParamProcessor\\Settings' => $vendorDir . '/param-processor/param-processor/src/Settings.php', 'ParamProcessor\\TSNode' => $vendorDir . '/param-processor/param-processor/src/TopologicalSort.php', - 'ParamProcessor\\Tests\\Definitions\\NumericParamTest' => $vendorDir . '/param-processor/param-processor/tests/phpunit/Definitions/NumericParamTest.php', - 'ParamProcessor\\Tests\\Definitions\\ParamDefinitionTest' => $vendorDir . '/param-processor/param-processor/tests/phpunit/Definitions/ParamDefinitionTest.php', 'ParamProcessor\\TitleParser' => $baseDir . '/extensions/Validator/src/ParamProcessor/TitleParser.php', 'ParamProcessor\\TopologicalSort' => $vendorDir . '/param-processor/param-processor/src/TopologicalSort.php', 'ParserHook' => $baseDir . '/extensions/Validator/src/legacy/ParserHook.php', @@ -919,7 +942,6 @@ return array( 'SFS\\Output' => $baseDir . '/extensions/SemanticFormsSelect/src/Output.php', 'SFS\\SelectField' => $baseDir . '/extensions/SemanticFormsSelect/src/SelectField.php', 'SFS\\SemanticFormsSelectInput' => $baseDir . '/extensions/SemanticFormsSelect/src/SemanticFormsSelectInput.php', - 'SMWBibTeXEntry' => $baseDir . '/extensions/SemanticResultFormats/formats/bibtex/SRF_BibTeX.php', 'SMWCategoryResultPrinter' => $baseDir . '/extensions/SemanticMediaWiki/includes/IdeAliases.php', 'SMWConceptValue' => $baseDir . '/extensions/SemanticMediaWiki/includes/datavalues/SMW_DV_Concept.php', 'SMWDIBlob' => $baseDir . '/extensions/SemanticMediaWiki/includes/dataitems/SMW_DI_Blob.php', @@ -1714,7 +1736,6 @@ return array( 'SMW\\Utils\\Tokenizer' => $baseDir . '/extensions/SemanticMediaWiki/src/Utils/Tokenizer.php', 'SMW\\WantedPropertiesQueryPage' => $baseDir . '/extensions/SemanticMediaWiki/includes/querypages/WantedPropertiesQueryPage.php', 'SRFArray' => $baseDir . '/extensions/SemanticResultFormats/formats/array/SRF_Array.php', - 'SRFBibTeX' => $baseDir . '/extensions/SemanticResultFormats/formats/bibtex/SRF_BibTeX.php', 'SRFBoilerplate' => $baseDir . '/extensions/SemanticResultFormats/formats/boilerplate/SRF_Boilerplate.php', 'SRFCHistoricalDate' => $baseDir . '/extensions/SemanticResultFormats/formats/calendar/SRFC_HistoricalDate.php', 'SRFCalendar' => $baseDir . '/extensions/SemanticResultFormats/formats/calendar/SRF_Calendar.php', @@ -1723,16 +1744,12 @@ return array( 'SRFExhibit' => $baseDir . '/extensions/SemanticResultFormats/formats/Exhibit/SRF_Exhibit.php', 'SRFGoogleBar' => $baseDir . '/extensions/SemanticResultFormats/formats/googlecharts/SRF_GoogleBar.php', 'SRFGooglePie' => $baseDir . '/extensions/SemanticResultFormats/formats/googlecharts/SRF_GooglePie.php', - 'SRFGraph' => $baseDir . '/extensions/SemanticResultFormats/formats/graphviz/SRF_Graph.php', 'SRFHash' => $baseDir . '/extensions/SemanticResultFormats/formats/array/SRF_Hash.php', 'SRFHooks' => $baseDir . '/extensions/SemanticResultFormats/SemanticResultFormats.hooks.php', 'SRFIncoming' => $baseDir . '/extensions/SemanticResultFormats/formats/incoming/SRF_Incoming.php', 'SRFJitGraph' => $baseDir . '/extensions/SemanticResultFormats/formats/JitGraph/SRF_JitGraph.php', 'SRFListWidget' => $baseDir . '/extensions/SemanticResultFormats/formats/widget/SRF_ListWidget.php', 'SRFMath' => $baseDir . '/extensions/SemanticResultFormats/formats/math/SRF_Math.php', - 'SRFOutline' => $baseDir . '/extensions/SemanticResultFormats/formats/outline/SRF_Outline.php', - 'SRFOutlineItem' => $baseDir . '/extensions/SemanticResultFormats/formats/outline/SRF_Outline.php', - 'SRFOutlineTree' => $baseDir . '/extensions/SemanticResultFormats/formats/outline/SRF_Outline.php', 'SRFPageWidget' => $baseDir . '/extensions/SemanticResultFormats/formats/widget/SRF_PageWidget.php', 'SRFParserFunctions' => $baseDir . '/extensions/SemanticResultFormats/SemanticResultFormats.parser.php', 'SRFProcess' => $baseDir . '/extensions/SemanticResultFormats/formats/graphviz/SRF_Process.php', @@ -1744,6 +1761,8 @@ return array( 'SRFTimeseries' => $baseDir . '/extensions/SemanticResultFormats/formats/timeseries/SRF_Timeseries.php', 'SRFUtils' => $baseDir . '/extensions/SemanticResultFormats/SemanticResultFormats.utils.php', 'SRFValueRank' => $baseDir . '/extensions/SemanticResultFormats/formats/valuerank/SRF_ValueRank.php', + 'SRF\\BibTex\\BibTexFileExportPrinter' => $baseDir . '/extensions/SemanticResultFormats/src/BibTex/BibTexFileExportPrinter.php', + 'SRF\\BibTex\\Item' => $baseDir . '/extensions/SemanticResultFormats/src/BibTex/Item.php', 'SRF\\DataTables' => $baseDir . '/extensions/SemanticResultFormats/formats/datatables/DataTables.php', 'SRF\\EventCalendar' => $baseDir . '/extensions/SemanticResultFormats/formats/calendar/EventCalendar.php', 'SRF\\Filtered\\Filter\\DistanceFilter' => $baseDir . '/extensions/SemanticResultFormats/formats/filtered/src/Filters/DistanceFilter.php', @@ -1762,9 +1781,19 @@ return array( 'SRF\\Formats\\Tree\\TreeNodePrinter' => $baseDir . '/extensions/SemanticResultFormats/formats/tree/TreeNodeVisitor.php', 'SRF\\Formats\\Tree\\TreeResultPrinter' => $baseDir . '/extensions/SemanticResultFormats/formats/tree/TreeResultPrinter.php', 'SRF\\Gallery' => $baseDir . '/extensions/SemanticResultFormats/formats/gallery/Gallery.php', + 'SRF\\Gantt\\Gantt' => $baseDir . '/extensions/SemanticResultFormats/formats/Gantt/src/Gantt.php', + 'SRF\\Gantt\\GanttPrinter' => $baseDir . '/extensions/SemanticResultFormats/formats/Gantt/GanttPrinter.php', + 'SRF\\Gantt\\GanttSection' => $baseDir . '/extensions/SemanticResultFormats/formats/Gantt/src/GanttSection.php', + 'SRF\\Gantt\\GanttTask' => $baseDir . '/extensions/SemanticResultFormats/formats/Gantt/src/GanttTask.php', + 'SRF\\Graph\\GraphPrinter' => $baseDir . '/extensions/SemanticResultFormats/src/Graph/GraphPrinter.php', 'SRF\\MediaPlayer' => $baseDir . '/extensions/SemanticResultFormats/formats/media/MediaPlayer.php', + 'SRF\\Outline\\ListTreeBuilder' => $baseDir . '/extensions/SemanticResultFormats/src/Outline/ListTreeBuilder.php', + 'SRF\\Outline\\OutlineItem' => $baseDir . '/extensions/SemanticResultFormats/src/Outline/OutlineItem.php', + 'SRF\\Outline\\OutlineResultPrinter' => $baseDir . '/extensions/SemanticResultFormats/src/Outline/OutlineResultPrinter.php', + 'SRF\\Outline\\OutlineTree' => $baseDir . '/extensions/SemanticResultFormats/src/Outline/OutlineTree.php', + 'SRF\\Outline\\TemplateBuilder' => $baseDir . '/extensions/SemanticResultFormats/src/Outline/TemplateBuilder.php', 'SRF\\ResourceFormatter' => $baseDir . '/extensions/SemanticResultFormats/src/ResourceFormatter.php', - 'SRF\\SRFExcel' => $baseDir . '/extensions/SemanticResultFormats/formats/excel/SRF_Excel.php', + 'SRF\\SpreadsheetPrinter' => $baseDir . '/extensions/SemanticResultFormats/formats/spreadsheet/SpreadsheetPrinter.php', 'SRF\\TagCloud' => $baseDir . '/extensions/SemanticResultFormats/formats/tagcloud/TagCloud.php', 'SRF\\iCalendar\\IcalTimezoneFormatter' => $baseDir . '/extensions/SemanticResultFormats/src/iCalendar/IcalTimezoneFormatter.php', 'SRF\\iCalendar\\iCalendarFileExportPrinter' => $baseDir . '/extensions/SemanticResultFormats/src/iCalendar/iCalendarFileExportPrinter.php', diff --git a/www/wiki/vendor/composer/autoload_files.php b/www/wiki/vendor/composer/autoload_files.php index b32426db..cc6650f8 100644 --- a/www/wiki/vendor/composer/autoload_files.php +++ b/www/wiki/vendor/composer/autoload_files.php @@ -18,6 +18,7 @@ return array( '23842c11409ef125f7fd90c8b167279e' => $vendorDir . '/wikimedia/at-ease/src/MediaWiki/Functions.php', 'eccc0347283a01e62f5536bcf76b6e62' => $vendorDir . '/wikimedia/at-ease/src/Wikimedia/Functions.php', '909f49809564e82b2d61096b70b21283' => $baseDir . '/extensions/Bootstrap/Bootstrap.php', + '5a1406346dc1f0175f8532cdd2e4a952' => $baseDir . '/extensions/Mermaid/Mermaid.php', 'c3ae67574219cc56cab6c30ef8877b85' => $baseDir . '/extensions/Validator/Validator.php', 'd5ece1612187536cca0f0d445ce68f45' => $baseDir . '/skins/chameleon/Chameleon.php', 'cb42de33506561e6b5fe72b72238a9a0' => $baseDir . '/skins/chameleon/src/SkinChameleon.php', diff --git a/www/wiki/vendor/composer/autoload_psr4.php b/www/wiki/vendor/composer/autoload_psr4.php index 4e71ca4f..b9cf4e20 100644 --- a/www/wiki/vendor/composer/autoload_psr4.php +++ b/www/wiki/vendor/composer/autoload_psr4.php @@ -39,6 +39,9 @@ return array( 'Onoi\\CallbackContainer\\' => array($vendorDir . '/onoi/callback-container/src'), 'Onoi\\Cache\\' => array($vendorDir . '/onoi/cache/src'), 'Onoi\\BlobStore\\' => array($vendorDir . '/onoi/blob-store/src'), + 'ModernTimeline\\Tests\\' => array($baseDir . '/extensions/ModernTimeline/tests'), + 'ModernTimeline\\' => array($baseDir . '/extensions/ModernTimeline/src'), + 'Mermaid\\' => array($baseDir . '/extensions/Mermaid/src'), 'Maps\\Tests\\' => array($baseDir . '/extensions/Maps/tests'), 'Maps\\' => array($baseDir . '/extensions/Maps/src'), 'Jeroen\\SimpleGeocoder\\' => array($vendorDir . '/jeroen/simple-geocoder/src'), diff --git a/www/wiki/vendor/composer/autoload_static.php b/www/wiki/vendor/composer/autoload_static.php index e9970072..58689690 100644 --- a/www/wiki/vendor/composer/autoload_static.php +++ b/www/wiki/vendor/composer/autoload_static.php @@ -19,6 +19,7 @@ class ComposerStaticInit_mediawiki_vendor '23842c11409ef125f7fd90c8b167279e' => __DIR__ . '/..' . '/wikimedia/at-ease/src/MediaWiki/Functions.php', 'eccc0347283a01e62f5536bcf76b6e62' => __DIR__ . '/..' . '/wikimedia/at-ease/src/Wikimedia/Functions.php', '909f49809564e82b2d61096b70b21283' => __DIR__ . '/../..' . '/extensions/Bootstrap/Bootstrap.php', + '5a1406346dc1f0175f8532cdd2e4a952' => __DIR__ . '/../..' . '/extensions/Mermaid/Mermaid.php', 'c3ae67574219cc56cab6c30ef8877b85' => __DIR__ . '/../..' . '/extensions/Validator/Validator.php', 'd5ece1612187536cca0f0d445ce68f45' => __DIR__ . '/../..' . '/skins/chameleon/Chameleon.php', 'cb42de33506561e6b5fe72b72238a9a0' => __DIR__ . '/../..' . '/skins/chameleon/src/SkinChameleon.php', @@ -87,6 +88,9 @@ class ComposerStaticInit_mediawiki_vendor ), 'M' => array ( + 'ModernTimeline\\Tests\\' => 21, + 'ModernTimeline\\' => 15, + 'Mermaid\\' => 8, 'Maps\\Tests\\' => 11, 'Maps\\' => 5, ), @@ -261,6 +265,18 @@ class ComposerStaticInit_mediawiki_vendor array ( 0 => __DIR__ . '/..' . '/onoi/blob-store/src', ), + 'ModernTimeline\\Tests\\' => + array ( + 0 => __DIR__ . '/../..' . '/extensions/ModernTimeline/tests', + ), + 'ModernTimeline\\' => + array ( + 0 => __DIR__ . '/../..' . '/extensions/ModernTimeline/src', + ), + 'Mermaid\\' => + array ( + 0 => __DIR__ . '/../..' . '/extensions/Mermaid/src', + ), 'Maps\\Tests\\' => array ( 0 => __DIR__ . '/../..' . '/extensions/Maps/tests', @@ -428,6 +444,7 @@ class ComposerStaticInit_mediawiki_vendor 'Composer\\Installers\\CraftInstaller' => __DIR__ . '/..' . '/composer/installers/src/Composer/Installers/CraftInstaller.php', 'Composer\\Installers\\CroogoInstaller' => __DIR__ . '/..' . '/composer/installers/src/Composer/Installers/CroogoInstaller.php', 'Composer\\Installers\\DecibelInstaller' => __DIR__ . '/..' . '/composer/installers/src/Composer/Installers/DecibelInstaller.php', + 'Composer\\Installers\\DframeInstaller' => __DIR__ . '/..' . '/composer/installers/src/Composer/Installers/DframeInstaller.php', 'Composer\\Installers\\DokuWikiInstaller' => __DIR__ . '/..' . '/composer/installers/src/Composer/Installers/DokuWikiInstaller.php', 'Composer\\Installers\\DolibarrInstaller' => __DIR__ . '/..' . '/composer/installers/src/Composer/Installers/DolibarrInstaller.php', 'Composer\\Installers\\DrupalInstaller' => __DIR__ . '/..' . '/composer/installers/src/Composer/Installers/DrupalInstaller.php', @@ -445,6 +462,7 @@ class ComposerStaticInit_mediawiki_vendor 'Composer\\Installers\\JoomlaInstaller' => __DIR__ . '/..' . '/composer/installers/src/Composer/Installers/JoomlaInstaller.php', 'Composer\\Installers\\KanboardInstaller' => __DIR__ . '/..' . '/composer/installers/src/Composer/Installers/KanboardInstaller.php', 'Composer\\Installers\\KirbyInstaller' => __DIR__ . '/..' . '/composer/installers/src/Composer/Installers/KirbyInstaller.php', + 'Composer\\Installers\\KnownInstaller' => __DIR__ . '/..' . '/composer/installers/src/Composer/Installers/KnownInstaller.php', 'Composer\\Installers\\KodiCMSInstaller' => __DIR__ . '/..' . '/composer/installers/src/Composer/Installers/KodiCMSInstaller.php', 'Composer\\Installers\\KohanaInstaller' => __DIR__ . '/..' . '/composer/installers/src/Composer/Installers/KohanaInstaller.php', 'Composer\\Installers\\LanManagementSystemInstaller' => __DIR__ . '/..' . '/composer/installers/src/Composer/Installers/LanManagementSystemInstaller.php', @@ -479,6 +497,7 @@ class ComposerStaticInit_mediawiki_vendor 'Composer\\Installers\\PxcmsInstaller' => __DIR__ . '/..' . '/composer/installers/src/Composer/Installers/PxcmsInstaller.php', 'Composer\\Installers\\RadPHPInstaller' => __DIR__ . '/..' . '/composer/installers/src/Composer/Installers/RadPHPInstaller.php', 'Composer\\Installers\\ReIndexInstaller' => __DIR__ . '/..' . '/composer/installers/src/Composer/Installers/ReIndexInstaller.php', + 'Composer\\Installers\\Redaxo5Installer' => __DIR__ . '/..' . '/composer/installers/src/Composer/Installers/Redaxo5Installer.php', 'Composer\\Installers\\RedaxoInstaller' => __DIR__ . '/..' . '/composer/installers/src/Composer/Installers/RedaxoInstaller.php', 'Composer\\Installers\\RoundcubeInstaller' => __DIR__ . '/..' . '/composer/installers/src/Composer/Installers/RoundcubeInstaller.php', 'Composer\\Installers\\SMFInstaller' => __DIR__ . '/..' . '/composer/installers/src/Composer/Installers/SMFInstaller.php', @@ -489,6 +508,7 @@ class ComposerStaticInit_mediawiki_vendor 'Composer\\Installers\\Symfony1Installer' => __DIR__ . '/..' . '/composer/installers/src/Composer/Installers/Symfony1Installer.php', 'Composer\\Installers\\TYPO3CmsInstaller' => __DIR__ . '/..' . '/composer/installers/src/Composer/Installers/TYPO3CmsInstaller.php', 'Composer\\Installers\\TYPO3FlowInstaller' => __DIR__ . '/..' . '/composer/installers/src/Composer/Installers/TYPO3FlowInstaller.php', + 'Composer\\Installers\\TaoInstaller' => __DIR__ . '/..' . '/composer/installers/src/Composer/Installers/TaoInstaller.php', 'Composer\\Installers\\TheliaInstaller' => __DIR__ . '/..' . '/composer/installers/src/Composer/Installers/TheliaInstaller.php', 'Composer\\Installers\\TuskInstaller' => __DIR__ . '/..' . '/composer/installers/src/Composer/Installers/TuskInstaller.php', 'Composer\\Installers\\UserFrostingInstaller' => __DIR__ . '/..' . '/composer/installers/src/Composer/Installers/UserFrostingInstaller.php', @@ -979,6 +999,7 @@ class ComposerStaticInit_mediawiki_vendor 'Maps\\Presentation\\CoordinateFormatter' => __DIR__ . '/../..' . '/extensions/Maps/src/Presentation/CoordinateFormatter.php', 'Maps\\Presentation\\ElementJsonSerializer' => __DIR__ . '/../..' . '/extensions/Maps/src/Presentation/ElementJsonSerializer.php', 'Maps\\Presentation\\KmlFormatter' => __DIR__ . '/../..' . '/extensions/Maps/src/Presentation/KmlFormatter.php', + 'Maps\\Presentation\\MapHtmlBuilder' => __DIR__ . '/../..' . '/extensions/Maps/src/Presentation/MapHtmlBuilder.php', 'Maps\\Presentation\\MapsDistanceParser' => __DIR__ . '/../..' . '/extensions/Maps/src/Presentation/MapsDistanceParser.php', 'Maps\\Presentation\\ParameterExtractor' => __DIR__ . '/../..' . '/extensions/Maps/src/Presentation/ParameterExtractor.php', 'Maps\\Presentation\\WikitextParser' => __DIR__ . '/../..' . '/extensions/Maps/src/Presentation/WikitextParser.php', @@ -1031,6 +1052,25 @@ class ComposerStaticInit_mediawiki_vendor 'Maps\\Tests\\Unit\\Semantic\\DataValues\\CoordinateValueTest' => __DIR__ . '/../..' . '/extensions/Maps/tests/Unit/Semantic/DataValues/CoordinateValueTest.php', 'Maps\\Tests\\Util\\PageCreator' => __DIR__ . '/../..' . '/extensions/Maps/tests/Util/PageCreator.php', 'Maps\\Tests\\Util\\TestFactory' => __DIR__ . '/../..' . '/extensions/Maps/tests/Util/TestFactory.php', + 'Mermaid\\HookRegistry' => __DIR__ . '/../..' . '/extensions/Mermaid/src/HookRegistry.php', + 'Mermaid\\MermaidParserFunction' => __DIR__ . '/../..' . '/extensions/Mermaid/src/MermaidParserFunction.php', + 'ModernTimeline\\Event' => __DIR__ . '/../..' . '/extensions/ModernTimeline/src/Event.php', + 'ModernTimeline\\JsonBuilder' => __DIR__ . '/../..' . '/extensions/ModernTimeline/src/JsonBuilder.php', + 'ModernTimeline\\ModernTimelinePrinter' => __DIR__ . '/../..' . '/extensions/ModernTimeline/src/ModernTimelinePrinter.php', + 'ModernTimeline\\ModernTimelineSetup' => __DIR__ . '/../..' . '/extensions/ModernTimeline/src/ModernTimelineSetup.php', + 'ModernTimeline\\ResultFacade\\PropertyValueCollection' => __DIR__ . '/../..' . '/extensions/ModernTimeline/src/ResultFacade/PropertyValueCollection.php', + 'ModernTimeline\\ResultFacade\\ResultSimplifier' => __DIR__ . '/../..' . '/extensions/ModernTimeline/src/ResultFacade/ResultSimplifier.php', + 'ModernTimeline\\ResultFacade\\Subject' => __DIR__ . '/../..' . '/extensions/ModernTimeline/src/ResultFacade/Subject.php', + 'ModernTimeline\\ResultFacade\\SubjectCollection' => __DIR__ . '/../..' . '/extensions/ModernTimeline/src/ResultFacade/SubjectCollection.php', + 'ModernTimeline\\SlidePresenter\\SimpleSlidePresenter' => __DIR__ . '/../..' . '/extensions/ModernTimeline/src/SlidePresenter/SimpleSlidePresenter.php', + 'ModernTimeline\\SlidePresenter\\SlidePresenter' => __DIR__ . '/../..' . '/extensions/ModernTimeline/src/SlidePresenter/SlidePresenter.php', + 'ModernTimeline\\SlidePresenter\\TemplateSlidePresenter' => __DIR__ . '/../..' . '/extensions/ModernTimeline/src/SlidePresenter/TemplateSlidePresenter.php', + 'ModernTimeline\\Tests\\Integration\\OptionsTest' => __DIR__ . '/../..' . '/extensions/ModernTimeline/tests/Integration/OptionsTest.php', + 'ModernTimeline\\Tests\\System\\JsonScriptTest' => __DIR__ . '/../..' . '/extensions/ModernTimeline/tests/System/JsonScriptTest.php', + 'ModernTimeline\\Tests\\Unit\\JsonBuilderTest' => __DIR__ . '/../..' . '/extensions/ModernTimeline/tests/Unit/JsonBuilderTest.php', + 'ModernTimeline\\Tests\\Unit\\SlidePresenter\\TemplateSlidePresenterTest' => __DIR__ . '/../..' . '/extensions/ModernTimeline/tests/Unit/SlidePresenter/TemplateSlidePresenterTest.php', + 'ModernTimeline\\TimelineOptions' => __DIR__ . '/../..' . '/extensions/ModernTimeline/src/TimelineOptions.php', + 'ModernTimeline\\TimelinePresenter' => __DIR__ . '/../..' . '/extensions/ModernTimeline/src/TimelinePresenter.php', 'Net_SMTP' => __DIR__ . '/..' . '/pear/net_smtp/Net/SMTP.php', 'Net_Socket' => __DIR__ . '/..' . '/pear/net_socket/Net/Socket.php', 'OOUI\\AccessKeyedElement' => __DIR__ . '/..' . '/oojs/oojs-ui/php/mixins/AccessKeyedElement.php', @@ -1160,13 +1200,14 @@ class ComposerStaticInit_mediawiki_vendor 'PEAR_ErrorStack' => __DIR__ . '/..' . '/pear/pear-core-minimal/src/PEAR/ErrorStack.php', 'PEAR_Exception' => __DIR__ . '/..' . '/pear/pear_exception/PEAR/Exception.php', 'PEAR_ExceptionTest' => __DIR__ . '/..' . '/pear/pear_exception/tests/PEAR/ExceptionTest.php', - 'ParamProcessor\\Definition\\DimensionParam' => __DIR__ . '/..' . '/param-processor/param-processor/src/Definition/DimensionParam.php', 'ParamProcessor\\Definition\\StringParam' => __DIR__ . '/..' . '/param-processor/param-processor/src/Definition/StringParam.php', 'ParamProcessor\\IParam' => __DIR__ . '/..' . '/param-processor/param-processor/src/IParam.php', 'ParamProcessor\\IParamDefinition' => __DIR__ . '/..' . '/param-processor/param-processor/src/IParamDefinition.php', 'ParamProcessor\\MediaWikiTitleValue' => __DIR__ . '/../..' . '/extensions/Validator/src/ParamProcessor/MediaWikiTitleValue.php', 'ParamProcessor\\Options' => __DIR__ . '/..' . '/param-processor/param-processor/src/Options.php', - 'ParamProcessor\\Param' => __DIR__ . '/..' . '/param-processor/param-processor/src/Param.php', + 'ParamProcessor\\PackagePrivate\\DimensionParser' => __DIR__ . '/..' . '/param-processor/param-processor/src/PackagePrivate/DimensionParser.php', + 'ParamProcessor\\PackagePrivate\\Param' => __DIR__ . '/..' . '/param-processor/param-processor/src/PackagePrivate/Param.php', + 'ParamProcessor\\PackagePrivate\\ParamType' => __DIR__ . '/..' . '/param-processor/param-processor/src/PackagePrivate/ParamType.php', 'ParamProcessor\\ParamDefinition' => __DIR__ . '/..' . '/param-processor/param-processor/src/ParamDefinition.php', 'ParamProcessor\\ParamDefinitionFactory' => __DIR__ . '/..' . '/param-processor/param-processor/src/ParamDefinitionFactory.php', 'ParamProcessor\\ParameterTypes' => __DIR__ . '/..' . '/param-processor/param-processor/src/ParameterTypes.php', @@ -1177,8 +1218,6 @@ class ComposerStaticInit_mediawiki_vendor 'ParamProcessor\\Processor' => __DIR__ . '/..' . '/param-processor/param-processor/src/Processor.php', 'ParamProcessor\\Settings' => __DIR__ . '/..' . '/param-processor/param-processor/src/Settings.php', 'ParamProcessor\\TSNode' => __DIR__ . '/..' . '/param-processor/param-processor/src/TopologicalSort.php', - 'ParamProcessor\\Tests\\Definitions\\NumericParamTest' => __DIR__ . '/..' . '/param-processor/param-processor/tests/phpunit/Definitions/NumericParamTest.php', - 'ParamProcessor\\Tests\\Definitions\\ParamDefinitionTest' => __DIR__ . '/..' . '/param-processor/param-processor/tests/phpunit/Definitions/ParamDefinitionTest.php', 'ParamProcessor\\TitleParser' => __DIR__ . '/../..' . '/extensions/Validator/src/ParamProcessor/TitleParser.php', 'ParamProcessor\\TopologicalSort' => __DIR__ . '/..' . '/param-processor/param-processor/src/TopologicalSort.php', 'ParserHook' => __DIR__ . '/../..' . '/extensions/Validator/src/legacy/ParserHook.php', @@ -1302,7 +1341,6 @@ class ComposerStaticInit_mediawiki_vendor 'SFS\\Output' => __DIR__ . '/../..' . '/extensions/SemanticFormsSelect/src/Output.php', 'SFS\\SelectField' => __DIR__ . '/../..' . '/extensions/SemanticFormsSelect/src/SelectField.php', 'SFS\\SemanticFormsSelectInput' => __DIR__ . '/../..' . '/extensions/SemanticFormsSelect/src/SemanticFormsSelectInput.php', - 'SMWBibTeXEntry' => __DIR__ . '/../..' . '/extensions/SemanticResultFormats/formats/bibtex/SRF_BibTeX.php', 'SMWCategoryResultPrinter' => __DIR__ . '/../..' . '/extensions/SemanticMediaWiki/includes/IdeAliases.php', 'SMWConceptValue' => __DIR__ . '/../..' . '/extensions/SemanticMediaWiki/includes/datavalues/SMW_DV_Concept.php', 'SMWDIBlob' => __DIR__ . '/../..' . '/extensions/SemanticMediaWiki/includes/dataitems/SMW_DI_Blob.php', @@ -2097,7 +2135,6 @@ class ComposerStaticInit_mediawiki_vendor 'SMW\\Utils\\Tokenizer' => __DIR__ . '/../..' . '/extensions/SemanticMediaWiki/src/Utils/Tokenizer.php', 'SMW\\WantedPropertiesQueryPage' => __DIR__ . '/../..' . '/extensions/SemanticMediaWiki/includes/querypages/WantedPropertiesQueryPage.php', 'SRFArray' => __DIR__ . '/../..' . '/extensions/SemanticResultFormats/formats/array/SRF_Array.php', - 'SRFBibTeX' => __DIR__ . '/../..' . '/extensions/SemanticResultFormats/formats/bibtex/SRF_BibTeX.php', 'SRFBoilerplate' => __DIR__ . '/../..' . '/extensions/SemanticResultFormats/formats/boilerplate/SRF_Boilerplate.php', 'SRFCHistoricalDate' => __DIR__ . '/../..' . '/extensions/SemanticResultFormats/formats/calendar/SRFC_HistoricalDate.php', 'SRFCalendar' => __DIR__ . '/../..' . '/extensions/SemanticResultFormats/formats/calendar/SRF_Calendar.php', @@ -2106,16 +2143,12 @@ class ComposerStaticInit_mediawiki_vendor 'SRFExhibit' => __DIR__ . '/../..' . '/extensions/SemanticResultFormats/formats/Exhibit/SRF_Exhibit.php', 'SRFGoogleBar' => __DIR__ . '/../..' . '/extensions/SemanticResultFormats/formats/googlecharts/SRF_GoogleBar.php', 'SRFGooglePie' => __DIR__ . '/../..' . '/extensions/SemanticResultFormats/formats/googlecharts/SRF_GooglePie.php', - 'SRFGraph' => __DIR__ . '/../..' . '/extensions/SemanticResultFormats/formats/graphviz/SRF_Graph.php', 'SRFHash' => __DIR__ . '/../..' . '/extensions/SemanticResultFormats/formats/array/SRF_Hash.php', 'SRFHooks' => __DIR__ . '/../..' . '/extensions/SemanticResultFormats/SemanticResultFormats.hooks.php', 'SRFIncoming' => __DIR__ . '/../..' . '/extensions/SemanticResultFormats/formats/incoming/SRF_Incoming.php', 'SRFJitGraph' => __DIR__ . '/../..' . '/extensions/SemanticResultFormats/formats/JitGraph/SRF_JitGraph.php', 'SRFListWidget' => __DIR__ . '/../..' . '/extensions/SemanticResultFormats/formats/widget/SRF_ListWidget.php', 'SRFMath' => __DIR__ . '/../..' . '/extensions/SemanticResultFormats/formats/math/SRF_Math.php', - 'SRFOutline' => __DIR__ . '/../..' . '/extensions/SemanticResultFormats/formats/outline/SRF_Outline.php', - 'SRFOutlineItem' => __DIR__ . '/../..' . '/extensions/SemanticResultFormats/formats/outline/SRF_Outline.php', - 'SRFOutlineTree' => __DIR__ . '/../..' . '/extensions/SemanticResultFormats/formats/outline/SRF_Outline.php', 'SRFPageWidget' => __DIR__ . '/../..' . '/extensions/SemanticResultFormats/formats/widget/SRF_PageWidget.php', 'SRFParserFunctions' => __DIR__ . '/../..' . '/extensions/SemanticResultFormats/SemanticResultFormats.parser.php', 'SRFProcess' => __DIR__ . '/../..' . '/extensions/SemanticResultFormats/formats/graphviz/SRF_Process.php', @@ -2127,6 +2160,8 @@ class ComposerStaticInit_mediawiki_vendor 'SRFTimeseries' => __DIR__ . '/../..' . '/extensions/SemanticResultFormats/formats/timeseries/SRF_Timeseries.php', 'SRFUtils' => __DIR__ . '/../..' . '/extensions/SemanticResultFormats/SemanticResultFormats.utils.php', 'SRFValueRank' => __DIR__ . '/../..' . '/extensions/SemanticResultFormats/formats/valuerank/SRF_ValueRank.php', + 'SRF\\BibTex\\BibTexFileExportPrinter' => __DIR__ . '/../..' . '/extensions/SemanticResultFormats/src/BibTex/BibTexFileExportPrinter.php', + 'SRF\\BibTex\\Item' => __DIR__ . '/../..' . '/extensions/SemanticResultFormats/src/BibTex/Item.php', 'SRF\\DataTables' => __DIR__ . '/../..' . '/extensions/SemanticResultFormats/formats/datatables/DataTables.php', 'SRF\\EventCalendar' => __DIR__ . '/../..' . '/extensions/SemanticResultFormats/formats/calendar/EventCalendar.php', 'SRF\\Filtered\\Filter\\DistanceFilter' => __DIR__ . '/../..' . '/extensions/SemanticResultFormats/formats/filtered/src/Filters/DistanceFilter.php', @@ -2145,9 +2180,19 @@ class ComposerStaticInit_mediawiki_vendor 'SRF\\Formats\\Tree\\TreeNodePrinter' => __DIR__ . '/../..' . '/extensions/SemanticResultFormats/formats/tree/TreeNodeVisitor.php', 'SRF\\Formats\\Tree\\TreeResultPrinter' => __DIR__ . '/../..' . '/extensions/SemanticResultFormats/formats/tree/TreeResultPrinter.php', 'SRF\\Gallery' => __DIR__ . '/../..' . '/extensions/SemanticResultFormats/formats/gallery/Gallery.php', + 'SRF\\Gantt\\Gantt' => __DIR__ . '/../..' . '/extensions/SemanticResultFormats/formats/Gantt/src/Gantt.php', + 'SRF\\Gantt\\GanttPrinter' => __DIR__ . '/../..' . '/extensions/SemanticResultFormats/formats/Gantt/GanttPrinter.php', + 'SRF\\Gantt\\GanttSection' => __DIR__ . '/../..' . '/extensions/SemanticResultFormats/formats/Gantt/src/GanttSection.php', + 'SRF\\Gantt\\GanttTask' => __DIR__ . '/../..' . '/extensions/SemanticResultFormats/formats/Gantt/src/GanttTask.php', + 'SRF\\Graph\\GraphPrinter' => __DIR__ . '/../..' . '/extensions/SemanticResultFormats/src/Graph/GraphPrinter.php', 'SRF\\MediaPlayer' => __DIR__ . '/../..' . '/extensions/SemanticResultFormats/formats/media/MediaPlayer.php', + 'SRF\\Outline\\ListTreeBuilder' => __DIR__ . '/../..' . '/extensions/SemanticResultFormats/src/Outline/ListTreeBuilder.php', + 'SRF\\Outline\\OutlineItem' => __DIR__ . '/../..' . '/extensions/SemanticResultFormats/src/Outline/OutlineItem.php', + 'SRF\\Outline\\OutlineResultPrinter' => __DIR__ . '/../..' . '/extensions/SemanticResultFormats/src/Outline/OutlineResultPrinter.php', + 'SRF\\Outline\\OutlineTree' => __DIR__ . '/../..' . '/extensions/SemanticResultFormats/src/Outline/OutlineTree.php', + 'SRF\\Outline\\TemplateBuilder' => __DIR__ . '/../..' . '/extensions/SemanticResultFormats/src/Outline/TemplateBuilder.php', 'SRF\\ResourceFormatter' => __DIR__ . '/../..' . '/extensions/SemanticResultFormats/src/ResourceFormatter.php', - 'SRF\\SRFExcel' => __DIR__ . '/../..' . '/extensions/SemanticResultFormats/formats/excel/SRF_Excel.php', + 'SRF\\SpreadsheetPrinter' => __DIR__ . '/../..' . '/extensions/SemanticResultFormats/formats/spreadsheet/SpreadsheetPrinter.php', 'SRF\\TagCloud' => __DIR__ . '/../..' . '/extensions/SemanticResultFormats/formats/tagcloud/TagCloud.php', 'SRF\\iCalendar\\IcalTimezoneFormatter' => __DIR__ . '/../..' . '/extensions/SemanticResultFormats/src/iCalendar/IcalTimezoneFormatter.php', 'SRF\\iCalendar\\iCalendarFileExportPrinter' => __DIR__ . '/../..' . '/extensions/SemanticResultFormats/src/iCalendar/iCalendarFileExportPrinter.php', diff --git a/www/wiki/vendor/composer/installed.json b/www/wiki/vendor/composer/installed.json index fc2bb924..604f3d8a 100644 --- a/www/wiki/vendor/composer/installed.json +++ b/www/wiki/vendor/composer/installed.json @@ -1,17 +1,17 @@ [ { "name": "composer/installers", - "version": "v1.6.0", - "version_normalized": "1.6.0.0", + "version": "v1.7.0", + "version_normalized": "1.7.0.0", "source": { "type": "git", "url": "https://github.com/composer/installers.git", - "reference": "cfcca6b1b60bc4974324efb5783c13dca6932b5b" + "reference": "141b272484481432cda342727a427dc1e206bfa0" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/composer/installers/zipball/cfcca6b1b60bc4974324efb5783c13dca6932b5b", - "reference": "cfcca6b1b60bc4974324efb5783c13dca6932b5b", + "url": "https://api.github.com/repos/composer/installers/zipball/141b272484481432cda342727a427dc1e206bfa0", + "reference": "141b272484481432cda342727a427dc1e206bfa0", "shasum": "" }, "require": { @@ -25,7 +25,7 @@ "composer/composer": "1.0.*@dev", "phpunit/phpunit": "^4.8.36" }, - "time": "2018-08-27T06:10:37+00:00", + "time": "2019-08-12T15:00:31+00:00", "type": "composer-plugin", "extra": { "class": "Composer\\Installers\\Plugin", @@ -69,6 +69,7 @@ "RadPHP", "SMF", "Thelia", + "Whmcs", "WolfCMS", "agl", "aimeos", @@ -91,6 +92,7 @@ "installer", "itop", "joomla", + "known", "kohana", "laravel", "lavalite", @@ -1133,17 +1135,17 @@ }, { "name": "mediawiki/maps", - "version": "7.3.2", - "version_normalized": "7.3.2.0", + "version": "7.4.0", + "version_normalized": "7.4.0.0", "source": { "type": "git", "url": "https://github.com/JeroenDeDauw/Maps.git", - "reference": "57e5b822dacb7ed4eacb4a2d996ce8e6be4ae13a" + "reference": "d4d195d5f3e57d5361882b94284286b7599e23dc" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/JeroenDeDauw/Maps/zipball/57e5b822dacb7ed4eacb4a2d996ce8e6be4ae13a", - "reference": "57e5b822dacb7ed4eacb4a2d996ce8e6be4ae13a", + "url": "https://api.github.com/repos/JeroenDeDauw/Maps/zipball/d4d195d5f3e57d5361882b94284286b7599e23dc", + "reference": "d4d195d5f3e57d5361882b94284286b7599e23dc", "shasum": "" }, "require": { @@ -1155,7 +1157,7 @@ "jeroen/simple-geocoder": "~1.2", "mediawiki/parser-hooks": "~1.5", "mediawiki/validator": "~2.2", - "param-processor/param-processor": "~1.4.2", + "param-processor/param-processor": "^1.4.2", "php": ">=7.1" }, "replace": { @@ -1167,7 +1169,7 @@ "suggest": { "mediawiki/semantic-media-wiki": "Add, edit, aggregate and visualize structured coordinate data stored with Semantic MediaWiki" }, - "time": "2019-07-25T04:17:46+00:00", + "time": "2019-08-09T18:15:24+00:00", "type": "mediawiki-extension", "extra": { "branch-alias": { @@ -1212,6 +1214,59 @@ "osm" ] }, + { + "name": "mediawiki/mermaid", + "version": "2.1.1", + "version_normalized": "2.1.1.0", + "source": { + "type": "git", + "url": "https://github.com/SemanticMediaWiki/Mermaid.git", + "reference": "9fe1279a9aed7afdecbbed4c183a81302ce63dce" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/SemanticMediaWiki/Mermaid/zipball/9fe1279a9aed7afdecbbed4c183a81302ce63dce", + "reference": "9fe1279a9aed7afdecbbed4c183a81302ce63dce", + "shasum": "" + }, + "require": { + "composer/installers": "1.*,>=1.0.1", + "php": ">=5.6.0" + }, + "time": "2019-05-20T10:17:44+00:00", + "type": "mediawiki-extension", + "extra": { + "branch-alias": { + "dev-master": "2.x-dev" + } + }, + "installation-source": "dist", + "autoload": { + "files": [ + "Mermaid.php" + ], + "psr-4": { + "Mermaid\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "GPL-2.0-or-later" + ], + "authors": [ + { + "name": "James Hong Kong", + "role": "Developer" + } + ], + "description": "Provides a parser function to generate diagrams and flowcharts with the help of the mermaid script language", + "homepage": "https://github.com/SemanticMediaWiki/Mermaid", + "keywords": [ + "mediawiki", + "mermaid", + "parser function" + ] + }, { "name": "mediawiki/page-forms", "version": "4.5.1", @@ -1504,36 +1559,38 @@ }, { "name": "mediawiki/semantic-result-formats", - "version": "3.0.1", - "version_normalized": "3.0.1.0", + "version": "3.1.0", + "version_normalized": "3.1.0.0", "source": { "type": "git", "url": "https://github.com/SemanticMediaWiki/SemanticResultFormats.git", - "reference": "e1956df8796b8df9a156083801e7dcc0ab2b9a25" + "reference": "aa0649f28e536fd9da363dcdff9405dc402ff275" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/SemanticMediaWiki/SemanticResultFormats/zipball/e1956df8796b8df9a156083801e7dcc0ab2b9a25", - "reference": "e1956df8796b8df9a156083801e7dcc0ab2b9a25", + "url": "https://api.github.com/repos/SemanticMediaWiki/SemanticResultFormats/zipball/aa0649f28e536fd9da363dcdff9405dc402ff275", + "reference": "aa0649f28e536fd9da363dcdff9405dc402ff275", "shasum": "" }, "require": { "composer/installers": "1.*,>=1.0.1", "data-values/geo": "~4.0|~3.0|~2.0", + "mediawiki/mermaid": "~2.1", "mediawiki/semantic-media-wiki": "~3.0", "nicmart/tree": "^0.2.7", - "php": ">=5.6.0", + "php": ">=7.0", "symfony/css-selector": "^3.3" }, "suggest": { "mediawiki/graph-viz": "Required for 'format=graph' and 'format=process'", - "phpoffice/phpexcel": "Required for 'format=excel'" + "phpoffice/phpexcel": "Required for 'format=excel'", + "phpoffice/phpspreadsheet": "Required for 'format=spreadsheet'" }, - "time": "2019-03-27T22:43:56+00:00", + "time": "2019-08-18T17:55:25+00:00", "type": "mediawiki-extension", "extra": { "branch-alias": { - "dev-master": "3.0.x-dev" + "dev-master": "3.1.x-dev" } }, "installation-source": "dist", @@ -2167,17 +2224,17 @@ }, { "name": "param-processor/param-processor", - "version": "1.4.2", - "version_normalized": "1.4.2.0", + "version": "1.10.0", + "version_normalized": "1.10.0.0", "source": { "type": "git", "url": "https://github.com/JeroenDeDauw/ParamProcessor.git", - "reference": "40cd2140aa339f59c5500760a9f6f1fa6c0ef0a6" + "reference": "5dec3e41bdc684640faf334db650e9bd32092e23" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/JeroenDeDauw/ParamProcessor/zipball/40cd2140aa339f59c5500760a9f6f1fa6c0ef0a6", - "reference": "40cd2140aa339f59c5500760a9f6f1fa6c0ef0a6", + "url": "https://api.github.com/repos/JeroenDeDauw/ParamProcessor/zipball/5dec3e41bdc684640faf334db650e9bd32092e23", + "reference": "5dec3e41bdc684640faf334db650e9bd32092e23", "shasum": "" }, "require": { @@ -2193,11 +2250,11 @@ "phpunit/phpunit": "~7.4", "squizlabs/php_codesniffer": "~3.3" }, - "time": "2018-11-26T05:02:09+00:00", + "time": "2019-08-03T15:18:54+00:00", "type": "library", "extra": { "branch-alias": { - "dev-master": "1.4.x-dev" + "dev-master": "1.x-dev" } }, "installation-source": "dist", @@ -2205,10 +2262,6 @@ "psr-4": { "ParamProcessor\\": "src" }, - "classmap": [ - "tests/phpunit/Definitions/ParamDefinitionTest.php", - "tests/phpunit/Definitions/NumericParamTest.php" - ], "files": [ "DefaultConfig.php" ] @@ -2612,6 +2665,65 @@ "exception" ] }, + { + "name": "professional-wiki/modern-timeline", + "version": "1.0.0", + "version_normalized": "1.0.0.0", + "source": { + "type": "git", + "url": "https://github.com/ProfessionalWiki/ModernTimeline.git", + "reference": "458b66e8c1e4d05ba3819a0f73b655b97203144c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/ProfessionalWiki/ModernTimeline/zipball/458b66e8c1e4d05ba3819a0f73b655b97203144c", + "reference": "458b66e8c1e4d05ba3819a0f73b655b97203144c", + "shasum": "" + }, + "require": { + "composer/installers": "^1.0.1", + "param-processor/param-processor": "~1.10", + "php": ">=7.1" + }, + "time": "2019-08-16T00:14:56+00:00", + "type": "mediawiki-extension", + "installation-source": "dist", + "autoload": { + "psr-4": { + "ModernTimeline\\": "src/", + "ModernTimeline\\Tests\\": "tests/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "GPL-2.0-or-later" + ], + "authors": [ + { + "name": "Professional.Wiki", + "role": "Creator", + "email": "info@professional.wiki", + "homepage": "https://professional.wiki" + }, + { + "name": "Jeroen De Dauw", + "role": "Creator and lead developer", + "email": "jeroendedauw@gmail.com", + "homepage": "https://www.entropywins.wtf" + } + ], + "description": "Adds a modern timeline visualization as Semantic MediaWiki result format", + "homepage": "https://github.com/ProfessionalWiki/ModernTimeline", + "keywords": [ + "Result format", + "Semantic MediaWiki", + "mediawiki", + "timeline", + "timelineJS3", + "visualization", + "wiki" + ] + }, { "name": "psr/log", "version": "1.0.2", diff --git a/www/wiki/vendor/composer/installers/composer.json b/www/wiki/vendor/composer/installers/composer.json index 6de40853..1f68e72a 100644 --- a/www/wiki/vendor/composer/installers/composer.json +++ b/www/wiki/vendor/composer/installers/composer.json @@ -31,6 +31,7 @@ "iTop", "Joomla", "Kanboard", + "Known", "Kohana", "Lan Management System", "Laravel", @@ -65,6 +66,7 @@ "symfony", "Thelia", "TYPO3", + "WHMCS", "WolfCMS", "WordPress", "YAWIK", diff --git a/www/wiki/vendor/composer/installers/src/Composer/Installers/DframeInstaller.php b/www/wiki/vendor/composer/installers/src/Composer/Installers/DframeInstaller.php new file mode 100644 index 00000000..70788163 --- /dev/null +++ b/www/wiki/vendor/composer/installers/src/Composer/Installers/DframeInstaller.php @@ -0,0 +1,10 @@ + 'modules/{$vendor}/{$name}/', + ); +} diff --git a/www/wiki/vendor/composer/installers/src/Composer/Installers/DrupalInstaller.php b/www/wiki/vendor/composer/installers/src/Composer/Installers/DrupalInstaller.php index fef7c525..f520bfe2 100644 --- a/www/wiki/vendor/composer/installers/src/Composer/Installers/DrupalInstaller.php +++ b/www/wiki/vendor/composer/installers/src/Composer/Installers/DrupalInstaller.php @@ -4,13 +4,17 @@ namespace Composer\Installers; class DrupalInstaller extends BaseInstaller { protected $locations = array( - 'core' => 'core/', - 'module' => 'modules/{$name}/', - 'theme' => 'themes/{$name}/', - 'library' => 'libraries/{$name}/', - 'profile' => 'profiles/{$name}/', - 'drush' => 'drush/{$name}/', - 'custom-theme' => 'themes/custom/{$name}/', - 'custom-module' => 'modules/custom/{$name}/', + 'core' => 'core/', + 'module' => 'modules/{$name}/', + 'theme' => 'themes/{$name}/', + 'library' => 'libraries/{$name}/', + 'profile' => 'profiles/{$name}/', + 'drush' => 'drush/{$name}/', + 'custom-theme' => 'themes/custom/{$name}/', + 'custom-module' => 'modules/custom/{$name}/', + 'custom-profile' => 'profiles/custom/{$name}/', + 'drupal-multisite' => 'sites/{$name}/', + 'console' => 'console/{$name}/', + 'console-language' => 'console/language/{$name}/', ); } diff --git a/www/wiki/vendor/composer/installers/src/Composer/Installers/Installer.php b/www/wiki/vendor/composer/installers/src/Composer/Installers/Installer.php index 352cb7fa..651c1904 100644 --- a/www/wiki/vendor/composer/installers/src/Composer/Installers/Installer.php +++ b/www/wiki/vendor/composer/installers/src/Composer/Installers/Installer.php @@ -35,6 +35,7 @@ class Installer extends LibraryInstaller 'concrete5' => 'Concrete5Installer', 'craft' => 'CraftInstaller', 'croogo' => 'CroogoInstaller', + 'dframe' => 'DframeInstaller', 'dokuwiki' => 'DokuWikiInstaller', 'dolibarr' => 'DolibarrInstaller', 'decibel' => 'DecibelInstaller', @@ -53,6 +54,7 @@ class Installer extends LibraryInstaller 'joomla' => 'JoomlaInstaller', 'kanboard' => 'KanboardInstaller', 'kirby' => 'KirbyInstaller', + 'known' => 'KnownInstaller', 'kodicms' => 'KodiCMSInstaller', 'kohana' => 'KohanaInstaller', 'lms' => 'LanManagementSystemInstaller', @@ -85,6 +87,7 @@ class Installer extends LibraryInstaller 'phifty' => 'PhiftyInstaller', 'porto' => 'PortoInstaller', 'redaxo' => 'RedaxoInstaller', + 'redaxo5' => 'Redaxo5Installer', 'reindex' => 'ReIndexInstaller', 'roundcube' => 'RoundcubeInstaller', 'shopware' => 'ShopwareInstaller', @@ -93,6 +96,7 @@ class Installer extends LibraryInstaller 'smf' => 'SMFInstaller', 'sydes' => 'SyDESInstaller', 'symfony1' => 'Symfony1Installer', + 'tao' => 'TaoInstaller', 'thelia' => 'TheliaInstaller', 'tusk' => 'TuskInstaller', 'typo3-cms' => 'TYPO3CmsInstaller', diff --git a/www/wiki/vendor/composer/installers/src/Composer/Installers/KnownInstaller.php b/www/wiki/vendor/composer/installers/src/Composer/Installers/KnownInstaller.php new file mode 100644 index 00000000..c5d08c5f --- /dev/null +++ b/www/wiki/vendor/composer/installers/src/Composer/Installers/KnownInstaller.php @@ -0,0 +1,11 @@ + 'IdnoPlugins/{$name}/', + 'theme' => 'Themes/{$name}/', + 'console' => 'ConsolePlugins/{$name}/', + ); +} diff --git a/www/wiki/vendor/composer/installers/src/Composer/Installers/MicroweberInstaller.php b/www/wiki/vendor/composer/installers/src/Composer/Installers/MicroweberInstaller.php index 4bbbec8c..b7d97039 100644 --- a/www/wiki/vendor/composer/installers/src/Composer/Installers/MicroweberInstaller.php +++ b/www/wiki/vendor/composer/installers/src/Composer/Installers/MicroweberInstaller.php @@ -4,12 +4,12 @@ namespace Composer\Installers; class MicroweberInstaller extends BaseInstaller { protected $locations = array( - 'module' => 'userfiles/modules/{$name}/', - 'module-skin' => 'userfiles/modules/{$name}/templates/', - 'template' => 'userfiles/templates/{$name}/', - 'element' => 'userfiles/elements/{$name}/', - 'vendor' => 'vendor/{$name}/', - 'components' => 'components/{$name}/' + 'module' => 'userfiles/modules/{$install_item_dir}/', + 'module-skin' => 'userfiles/modules/{$install_item_dir}/templates/', + 'template' => 'userfiles/templates/{$install_item_dir}/', + 'element' => 'userfiles/elements/{$install_item_dir}/', + 'vendor' => 'vendor/{$install_item_dir}/', + 'components' => 'components/{$install_item_dir}/' ); /** @@ -22,89 +22,97 @@ class MicroweberInstaller extends BaseInstaller */ public function inflectPackageVars($vars) { - if ($vars['type'] === 'microweber-template') { - return $this->inflectTemplateVars($vars); - } - if ($vars['type'] === 'microweber-templates') { - return $this->inflectTemplatesVars($vars); - } - if ($vars['type'] === 'microweber-core') { - return $this->inflectCoreVars($vars); - } - if ($vars['type'] === 'microweber-adapter') { - return $this->inflectCoreVars($vars); - } - if ($vars['type'] === 'microweber-module') { - return $this->inflectModuleVars($vars); - } - if ($vars['type'] === 'microweber-modules') { - return $this->inflectModulesVars($vars); - } - if ($vars['type'] === 'microweber-skin') { - return $this->inflectSkinVars($vars); - } - if ($vars['type'] === 'microweber-element' or $vars['type'] === 'microweber-elements') { - return $this->inflectElementVars($vars); + + + if ($this->package->getTargetDir()) { + $vars['install_item_dir'] = $this->package->getTargetDir(); + } else { + $vars['install_item_dir'] = $vars['name']; + if ($vars['type'] === 'microweber-template') { + return $this->inflectTemplateVars($vars); + } + if ($vars['type'] === 'microweber-templates') { + return $this->inflectTemplatesVars($vars); + } + if ($vars['type'] === 'microweber-core') { + return $this->inflectCoreVars($vars); + } + if ($vars['type'] === 'microweber-adapter') { + return $this->inflectCoreVars($vars); + } + if ($vars['type'] === 'microweber-module') { + return $this->inflectModuleVars($vars); + } + if ($vars['type'] === 'microweber-modules') { + return $this->inflectModulesVars($vars); + } + if ($vars['type'] === 'microweber-skin') { + return $this->inflectSkinVars($vars); + } + if ($vars['type'] === 'microweber-element' or $vars['type'] === 'microweber-elements') { + return $this->inflectElementVars($vars); + } } + return $vars; } protected function inflectTemplateVars($vars) { - $vars['name'] = preg_replace('/-template$/', '', $vars['name']); - $vars['name'] = preg_replace('/template-$/', '', $vars['name']); + $vars['install_item_dir'] = preg_replace('/-template$/', '', $vars['install_item_dir']); + $vars['install_item_dir'] = preg_replace('/template-$/', '', $vars['install_item_dir']); return $vars; } protected function inflectTemplatesVars($vars) { - $vars['name'] = preg_replace('/-templates$/', '', $vars['name']); - $vars['name'] = preg_replace('/templates-$/', '', $vars['name']); + $vars['install_item_dir'] = preg_replace('/-templates$/', '', $vars['install_item_dir']); + $vars['install_item_dir'] = preg_replace('/templates-$/', '', $vars['install_item_dir']); return $vars; } protected function inflectCoreVars($vars) { - $vars['name'] = preg_replace('/-providers$/', '', $vars['name']); - $vars['name'] = preg_replace('/-provider$/', '', $vars['name']); - $vars['name'] = preg_replace('/-adapter$/', '', $vars['name']); + $vars['install_item_dir'] = preg_replace('/-providers$/', '', $vars['install_item_dir']); + $vars['install_item_dir'] = preg_replace('/-provider$/', '', $vars['install_item_dir']); + $vars['install_item_dir'] = preg_replace('/-adapter$/', '', $vars['install_item_dir']); return $vars; } protected function inflectModuleVars($vars) { - $vars['name'] = preg_replace('/-module$/', '', $vars['name']); - $vars['name'] = preg_replace('/module-$/', '', $vars['name']); + $vars['install_item_dir'] = preg_replace('/-module$/', '', $vars['install_item_dir']); + $vars['install_item_dir'] = preg_replace('/module-$/', '', $vars['install_item_dir']); return $vars; } protected function inflectModulesVars($vars) { - $vars['name'] = preg_replace('/-modules$/', '', $vars['name']); - $vars['name'] = preg_replace('/modules-$/', '', $vars['name']); + $vars['install_item_dir'] = preg_replace('/-modules$/', '', $vars['install_item_dir']); + $vars['install_item_dir'] = preg_replace('/modules-$/', '', $vars['install_item_dir']); return $vars; } protected function inflectSkinVars($vars) { - $vars['name'] = preg_replace('/-skin$/', '', $vars['name']); - $vars['name'] = preg_replace('/skin-$/', '', $vars['name']); + $vars['install_item_dir'] = preg_replace('/-skin$/', '', $vars['install_item_dir']); + $vars['install_item_dir'] = preg_replace('/skin-$/', '', $vars['install_item_dir']); return $vars; } protected function inflectElementVars($vars) { - $vars['name'] = preg_replace('/-elements$/', '', $vars['name']); - $vars['name'] = preg_replace('/elements-$/', '', $vars['name']); - $vars['name'] = preg_replace('/-element$/', '', $vars['name']); - $vars['name'] = preg_replace('/element-$/', '', $vars['name']); + $vars['install_item_dir'] = preg_replace('/-elements$/', '', $vars['install_item_dir']); + $vars['install_item_dir'] = preg_replace('/elements-$/', '', $vars['install_item_dir']); + $vars['install_item_dir'] = preg_replace('/-element$/', '', $vars['install_item_dir']); + $vars['install_item_dir'] = preg_replace('/element-$/', '', $vars['install_item_dir']); return $vars; } diff --git a/www/wiki/vendor/composer/installers/src/Composer/Installers/MoodleInstaller.php b/www/wiki/vendor/composer/installers/src/Composer/Installers/MoodleInstaller.php index a89c82f7..75dbe71b 100644 --- a/www/wiki/vendor/composer/installers/src/Composer/Installers/MoodleInstaller.php +++ b/www/wiki/vendor/composer/installers/src/Composer/Installers/MoodleInstaller.php @@ -20,6 +20,7 @@ class MoodleInstaller extends BaseInstaller 'calendartype' => 'calendar/type/{$name}/', 'format' => 'course/format/{$name}/', 'coursereport' => 'course/report/{$name}/', + 'customcertelement' => 'mod/customcert/element/{$name}/', 'datafield' => 'mod/data/field/{$name}/', 'datapreset' => 'mod/data/preset/{$name}/', 'editor' => 'lib/editor/{$name}/', diff --git a/www/wiki/vendor/composer/installers/src/Composer/Installers/Redaxo5Installer.php b/www/wiki/vendor/composer/installers/src/Composer/Installers/Redaxo5Installer.php new file mode 100644 index 00000000..23a20347 --- /dev/null +++ b/www/wiki/vendor/composer/installers/src/Composer/Installers/Redaxo5Installer.php @@ -0,0 +1,10 @@ + 'redaxo/src/addons/{$name}/', + 'bestyle-plugin' => 'redaxo/src/addons/be_style/plugins/{$name}/' + ); +} diff --git a/www/wiki/vendor/composer/installers/src/Composer/Installers/TaoInstaller.php b/www/wiki/vendor/composer/installers/src/Composer/Installers/TaoInstaller.php new file mode 100644 index 00000000..108def15 --- /dev/null +++ b/www/wiki/vendor/composer/installers/src/Composer/Installers/TaoInstaller.php @@ -0,0 +1,12 @@ + '{$name}' + ); +} diff --git a/www/wiki/vendor/composer/installers/src/Composer/Installers/WHMCSInstaller.php b/www/wiki/vendor/composer/installers/src/Composer/Installers/WHMCSInstaller.php index 2cbb4a46..b65dbbaf 100644 --- a/www/wiki/vendor/composer/installers/src/Composer/Installers/WHMCSInstaller.php +++ b/www/wiki/vendor/composer/installers/src/Composer/Installers/WHMCSInstaller.php @@ -5,6 +5,17 @@ namespace Composer\Installers; class WHMCSInstaller extends BaseInstaller { protected $locations = array( - 'gateway' => 'modules/gateways/{$name}/', + 'addons' => 'modules/addons/{$vendor}_{$name}/', + 'fraud' => 'modules/fraud/{$vendor}_{$name}/', + 'gateways' => 'modules/gateways/{$vendor}_{$name}/', + 'notifications' => 'modules/notifications/{$vendor}_{$name}/', + 'registrars' => 'modules/registrars/{$vendor}_{$name}/', + 'reports' => 'modules/reports/{$vendor}_{$name}/', + 'security' => 'modules/security/{$vendor}_{$name}/', + 'servers' => 'modules/servers/{$vendor}_{$name}/', + 'social' => 'modules/social/{$vendor}_{$name}/', + 'support' => 'modules/support/{$vendor}_{$name}/', + 'templates' => 'templates/{$vendor}_{$name}/', + 'includes' => 'includes/{$vendor}_{$name}/' ); } diff --git a/www/wiki/vendor/param-processor/param-processor/.travis.yml b/www/wiki/vendor/param-processor/param-processor/.travis.yml index bebb02de..b773815d 100644 --- a/www/wiki/vendor/param-processor/param-processor/.travis.yml +++ b/www/wiki/vendor/param-processor/param-processor/.travis.yml @@ -3,14 +3,14 @@ language: php php: - 7.1 - 7.2 - - master + - 7.3 install: travis_retry composer install script: composer ci after_success: - - if [[ "`phpenv version-name`" != "7.2" ]]; then exit 0; fi + - if [[ "`phpenv version-name`" != "7.3" ]]; then exit 0; fi - vendor/bin/phpunit --coverage-clover coverage.clover - wget https://scrutinizer-ci.com/ocular.phar - php ocular.phar code-coverage:upload --format=php-clover coverage.clover diff --git a/www/wiki/vendor/param-processor/param-processor/IdeHelper.php b/www/wiki/vendor/param-processor/param-processor/IdeHelper.php new file mode 100644 index 00000000..a5292e0d --- /dev/null +++ b/www/wiki/vendor/param-processor/param-processor/IdeHelper.php @@ -0,0 +1,10 @@ +minlength: int or false
  • maxlength: int or false
  • regex: string
  • -
      +
    @@ -190,7 +190,7 @@ The requires fields currently are: name and message
  • upperbound: int, float or false
  • range: [lowerbound, upperbound]
  • withinrange: [float $point, float $deviation]
  • -
      +
    @@ -214,7 +214,7 @@ The requires fields currently are: name and message
  • defaultunit: string
  • lowerbound: int, float or false
  • upperbound: int, float or false
  • -
      +
    @@ -284,12 +284,6 @@ $processor->setFunctionParams( ); ``` -## Tests - -This library comes with a set up PHPUnit tests that cover all non-trivial code. You can run these -tests using the PHPUnit configuration file found in the root directory. The tests can also be run -via TravisCI, as a TravisCI configuration file is also provided in the root directory. - ## Contributing * [File an issue](https://github.com/JeroenDeDauw/ParamProcessor/issues) @@ -297,12 +291,76 @@ via TravisCI, as a TravisCI configuration file is also provided in the root dire ## Authors -ParamProcessor has been written by [Jeroen De Dauw](https://github.com/JeroenDeDauw) to +ParamProcessor has been written by [Jeroen De Dauw](https://www.entropywins.wtf/) to support the [Maps](https://github.com/JeroenDeDauw/Maps) and [Semantic MediaWiki](https://semantic-mediawiki.org/) projects. ## Release notes +### 1.10.0 (2019-08-03) + +* Removed `DimensionParam` +* Fixed bug in parsing of parameters of type `dimension` + +### 1.9.0 (2019-08-03) + +* Added `ParamDefinitionFactory::newDefinitionsFromArrays` + +### 1.8.0 (2019-08-03) + +* Removed `ParamDefinitionFactory::getComponentForType` +* Added `ParamDefinitionFactory` constructor +* Added `ParameterTypes` constructor +* Added `ParameterTypes::addType` +* Added `ParameterTypes::newCoreTypes` +* Added `ProcessingResult::getParameterArray` + +### 1.7.0 (2019-08-02) + +* Added `ParameterTypes` public constants: `BOOLEAN`, `FLOAT`, `INTEGER`, `STRING`, `DIMENSION` +* Deprecated `ParamDefinition::getCleanDefinitions` in favour of `ParamDefinitionFactory` +* Deprecated `ParamDefinition::setDefault` in favour of constructor parameter +* Deprecated `Processor::getParameterValues` in favour of `processParameters` and `ProcessingResult` +* Deprecated `Processor::getErrors` in favour of `processParameters` and `ProcessingResult` +* Deprecated `Processor::getErrorMessages` in favour of `processParameters` and `ProcessingResult` +* Deprecated `Processor::hasErrors` in favour of `processParameters` and `ProcessingResult` +* Deprecated `Processor::hasFatalError` in favour of `processParameters` and `ProcessingResult` +* Deprecated parameter dependencies + * Deprecated `ParamDefinition::hasDependency` + * Deprecated `ParamDefinition::addDependencies` + * Deprecated `ParamDefinition::getDependencies` + * Deprecated `dependencies` key in `ParamDefinition::setArrayValues` parameter + * Deprecated `TopologicalSort` + * Deprecated `TSNode` +* Deprecated extending `ParamDefinition` +* Deprecated `StringParam` +* Deprecated `DimensionParam` +* Deprecated `ParamDefinition::setArrayValues` +* Deprecated `ParamDefinition::$acceptOverriding` +* Deprecated `ParamDefinition::$accumulateParameterErrors` +* Deprecated `Param::$accumulateParameterErrors` +* Deprecated `Settings` +* Deprecated `Options::setRawStringInputs` +* Deprecated `Options::isStringlyTyped` + +### 1.6.1 (2019-07-28) + +* Fixed message defaulting bug in `ParamDefinition` + +### 1.6.0 (2019-07-28) + +* Added `Processor::setParameterDefinitions` +* Deprecated second parameter of `Processor::setParameters` in favour of `setParameterDefinitions` +* Deprecated second parameter of `Processor::setFunctionParams` in favour of `setParameterDefinitions` +* Deprecated second parameter of `ParamDefinitionFactory::newDefinitionFromArray` +* Deprecated return value of `ParamDefinitionFactory::registerType` +* Deprecated `ParamDefinitionFactory::registerGlobals` +* Deprecated `typed-parser` key in parameter type definitions + +### 1.5.0 (2019-07-28) + +* Improved code comments and added extra type checks + ### 1.4.2 (2018-11-26) * Fixed defaulting behaviour of list parameters @@ -315,6 +373,8 @@ support the [Maps](https://github.com/JeroenDeDauw/Maps) and * Dropped support for PHP older than 7.1 * Added `ParameterTypes` to allow gradual migration away from the now deprecated `$wgParamDefinitions` +* Deprecated `$wgParamDefinitions` +* Deprecated `$egValidatorSettings` ### 1.3.4 (2018-05-05) diff --git a/www/wiki/vendor/param-processor/param-processor/composer.json b/www/wiki/vendor/param-processor/param-processor/composer.json index 5827117b..df8036f1 100644 --- a/www/wiki/vendor/param-processor/param-processor/composer.json +++ b/www/wiki/vendor/param-processor/param-processor/composer.json @@ -39,17 +39,18 @@ "psr-4": { "ParamProcessor\\": "src" }, - "classmap": [ - "tests/phpunit/Definitions/ParamDefinitionTest.php", - "tests/phpunit/Definitions/NumericParamTest.php" - ], "files": [ "DefaultConfig.php" ] }, + "autoload-dev": { + "psr-4": { + "ParamProcessor\\Tests\\": "tests" + } + }, "extra": { "branch-alias": { - "dev-master": "1.4.x-dev" + "dev-master": "1.x-dev" } }, "scripts": { @@ -63,7 +64,8 @@ "@phpmd" ], "ci": [ - "@test" + "@test", + "@cs" ], "phpcs": [ "vendor/bin/phpcs src/* tests/* --standard=phpcs.xml --extensions=php -sp" diff --git a/www/wiki/vendor/param-processor/param-processor/phpcs.xml b/www/wiki/vendor/param-processor/param-processor/phpcs.xml index 20f96009..b9008d97 100644 --- a/www/wiki/vendor/param-processor/param-processor/phpcs.xml +++ b/www/wiki/vendor/param-processor/param-processor/phpcs.xml @@ -6,15 +6,17 @@ + + 0 + - - - + + @@ -27,8 +29,8 @@ - - + + diff --git a/www/wiki/vendor/param-processor/param-processor/phpmd.xml b/www/wiki/vendor/param-processor/param-processor/phpmd.xml index 95a85368..66bfad24 100644 --- a/www/wiki/vendor/param-processor/param-processor/phpmd.xml +++ b/www/wiki/vendor/param-processor/param-processor/phpmd.xml @@ -4,19 +4,9 @@ xsi:schemaLocation="http://pmd.sf.net/ruleset/1.0.0 http://pmd.sf.net/ruleset_xml_schema.xsd" xsi:noNamespaceSchemaLocation=" http://pmd.sf.net/ruleset_xml_schema.xsd"> - - - - - - - - - - - + @@ -25,8 +15,4 @@ - - - - \ No newline at end of file diff --git a/www/wiki/vendor/param-processor/param-processor/src/Definition/DimensionParam.php b/www/wiki/vendor/param-processor/param-processor/src/Definition/DimensionParam.php deleted file mode 100644 index 1bf272cc..00000000 --- a/www/wiki/vendor/param-processor/param-processor/src/Definition/DimensionParam.php +++ /dev/null @@ -1,65 +0,0 @@ - - */ -class DimensionParam extends ParamDefinition { - - /** - * Formats the parameter value to it's final result. - * @see ParamDefinition::formatValue - * - * @since 1.0 - * - * @param mixed $value - * @param IParam $param - * @param IParamDefinition[] $definitions - * @param IParam[] $params - * - * @return mixed - * @throws Exception - */ - protected function formatValue( $value, IParam $param, array &$definitions, array $params ) { - if ( $value === 'auto' ) { - return $value; - } - - /** - * @var \ValueValidators\DimensionValidator $validator - */ - $validator = $this->getValueValidator(); - - if ( get_class( $validator ) === 'ValueValidators\DimensionValidator' ) { - foreach ( $validator->getAllowedUnits() as $unit ) { - if ( $unit !== '' && strpos( $value, $unit ) !== false ) { - return $value; - } - } - - return $value . $validator->getDefaultUnit(); - } - else { - throw new Exception( - 'ValueValidator of a DimensionParam should be a ValueValidators\DimensionValidator and not a ' - . get_class( $validator ) - ); - } - } - -} diff --git a/www/wiki/vendor/param-processor/param-processor/src/Definition/StringParam.php b/www/wiki/vendor/param-processor/param-processor/src/Definition/StringParam.php index 6488f697..3c74716d 100644 --- a/www/wiki/vendor/param-processor/param-processor/src/Definition/StringParam.php +++ b/www/wiki/vendor/param-processor/param-processor/src/Definition/StringParam.php @@ -9,7 +9,7 @@ use ParamProcessor\IParam; * Defines the string parameter type. * Specifies the type specific validation and formatting logic. * - * @since 1.0 + * @deprecated since 1.7 * * @licence GNU GPL v2+ * @author Jeroen De Dauw < jeroendedauw@gmail.com > @@ -75,4 +75,4 @@ class StringParam extends ParamDefinition { $this->toLower = $toLower; } -} \ No newline at end of file +} diff --git a/www/wiki/vendor/param-processor/param-processor/src/IParam.php b/www/wiki/vendor/param-processor/param-processor/src/IParam.php index 31766d6c..ebb0a548 100644 --- a/www/wiki/vendor/param-processor/param-processor/src/IParam.php +++ b/www/wiki/vendor/param-processor/param-processor/src/IParam.php @@ -3,10 +3,10 @@ namespace ParamProcessor; /** - * Interface for objects representing an "instance" of a parameter. - * * NOTE: as of version 1.0, this class is for internal use only! * + * Interface for objects representing an "instance" of a parameter. + * * @since 1.0 * @deprecated since 1.0 * diff --git a/www/wiki/vendor/param-processor/param-processor/src/IParamDefinition.php b/www/wiki/vendor/param-processor/param-processor/src/IParamDefinition.php index 38b07179..410b8096 100644 --- a/www/wiki/vendor/param-processor/param-processor/src/IParamDefinition.php +++ b/www/wiki/vendor/param-processor/param-processor/src/IParamDefinition.php @@ -53,7 +53,7 @@ interface IParamDefinition { * * @return string[] */ - public function getAliases(); + public function getAliases(): array; /** * Returns the default value. @@ -72,7 +72,7 @@ interface IParamDefinition { * * @return string */ - public function getDelimiter(); + public function getDelimiter(): string; /** * Returns a list of dependencies the parameter has, in the form of @@ -82,7 +82,7 @@ interface IParamDefinition { * * @return string[] */ - public function getDependencies(); + public function getDependencies(): array; /** * Returns a message that will act as a description message for the parameter. @@ -91,7 +91,7 @@ interface IParamDefinition { * * @return string */ - public function getMessage(); + public function getMessage(): string; /** * Returns the parameters main name. @@ -100,7 +100,7 @@ interface IParamDefinition { * * @return string */ - public function getName(); + public function getName(): string; /** * Returns an identifier for the type of the parameter. @@ -109,7 +109,7 @@ interface IParamDefinition { * * @return string */ - public function getType(); + public function getType(): string; /** * Returns if the parameter has a certain alias. @@ -120,7 +120,7 @@ interface IParamDefinition { * * @return boolean */ - public function hasAlias( $alias ); + public function hasAlias( string $alias ): bool; /** * Returns if the parameter has a certain dependency. @@ -131,7 +131,7 @@ interface IParamDefinition { * * @return boolean */ - public function hasDependency( $dependency ); + public function hasDependency( string $dependency ): bool; /** * Returns if the parameter is a list or not. @@ -140,7 +140,7 @@ interface IParamDefinition { * * @return boolean */ - public function isList(); + public function isList(): bool; /** * Returns if the parameter is a required one or not. @@ -149,7 +149,7 @@ interface IParamDefinition { * * @return boolean */ - public function isRequired(); + public function isRequired(): bool; /** * Sets the default parameter value. Null indicates no default, @@ -170,7 +170,7 @@ interface IParamDefinition { * * @param $delimiter string */ - public function setDelimiter( $delimiter ); + public function setDelimiter( string $delimiter ); /** * Set if the parameter manipulations should be applied to the default value. @@ -179,19 +179,17 @@ interface IParamDefinition { * * @param boolean $manipulateDefault */ - public function setDoManipulationOfDefault( $manipulateDefault ); + public function setDoManipulationOfDefault( bool $manipulateDefault ); /** * Sets a message for the parameter that will act as description. - * This should be a message key, ie something that can be passed - * to wfMsg. Not an actual text. If you do not have a message key, - * but only a text, use setDescription instead. + * * * @since 1.0 * * @param string $message */ - public function setMessage( $message ); + public function setMessage( string $message ); /** * Returns if the parameter manipulations should be applied to the default value. @@ -200,7 +198,7 @@ interface IParamDefinition { * * @return boolean */ - public function shouldManipulateDefault(); + public function shouldManipulateDefault(): bool; /** * Returns a message key for a message describing the parameter type. @@ -209,11 +207,10 @@ interface IParamDefinition { * * @return string */ - public function getTypeMessage(); + public function getTypeMessage(): string; /** * Returns if the value should be trimmed before validation and any further processing. - * @see IParamDefinition::trimDuringClean * * @since 1.0 * @@ -267,7 +264,7 @@ interface IParamDefinition { * * @param callable $validationFunction */ - public function setValidationCallback( /* callable */ $validationFunction ); + public function setValidationCallback( callable $validationFunction ); /** * Sets the parameter definition values contained in the provided array. diff --git a/www/wiki/vendor/param-processor/param-processor/src/Options.php b/www/wiki/vendor/param-processor/param-processor/src/Options.php index 897ed25c..99e11b45 100644 --- a/www/wiki/vendor/param-processor/param-processor/src/Options.php +++ b/www/wiki/vendor/param-processor/param-processor/src/Options.php @@ -39,6 +39,9 @@ class Options { $this->lowercaseNames = $lowercase; } + /** + * @deprecated since 1.7 + */ public function setRawStringInputs( bool $rawInputs ) { $this->rawStringInputs = $rawInputs; } @@ -67,6 +70,9 @@ class Options { return $this->lowercaseNames; } + /** + * @deprecated since 1.7 + */ public function isStringlyTyped(): bool { return $this->rawStringInputs; } diff --git a/www/wiki/vendor/param-processor/param-processor/src/PackagePrivate/DimensionParser.php b/www/wiki/vendor/param-processor/param-processor/src/PackagePrivate/DimensionParser.php new file mode 100644 index 00000000..06aa2ad5 --- /dev/null +++ b/www/wiki/vendor/param-processor/param-processor/src/PackagePrivate/DimensionParser.php @@ -0,0 +1,44 @@ +defaultUnit = $options->hasOption( self::DEFAULT_UNIT ) ? $options->getOption( self::DEFAULT_UNIT ) : self::PIXELS; + } + + public function parse( $value ) { + if ( !is_string( $value ) ) { + throw new ParseException( 'Not a string' ); + } + + $value = $this->removeWhitespace( $value ); + + if ( preg_match( '/^(\d|\.)+$/', $value ) ) { + $value .= $this->defaultUnit; + } + + return $value; + } + + private function removeWhitespace( string $string ): string { + return preg_replace( '/\s+/', '', $string ); + } + +} diff --git a/www/wiki/vendor/param-processor/param-processor/src/PackagePrivate/Param.php b/www/wiki/vendor/param-processor/param-processor/src/PackagePrivate/Param.php new file mode 100644 index 00000000..49011579 --- /dev/null +++ b/www/wiki/vendor/param-processor/param-processor/src/PackagePrivate/Param.php @@ -0,0 +1,418 @@ + + */ +class Param implements IParam { + + /** + * Indicates whether parameters not found in the criteria list + * should be stored in case they are not accepted. The default is false. + * + * @deprecated since 1.7 + */ + public static $accumulateParameterErrors = false; + + /** + * The original parameter name as provided by the user. This can be the + * main name or an alias. + * @var string + */ + protected $originalName; + + /** + * The original value as provided by the user. This is mainly retained for + * usage in error messages when the parameter turns out to be invalid. + * @var string + */ + protected $originalValue; + + /** + * @var mixed + */ + protected $value; + + /** + * Keeps track of how many times the parameter has been set by the user. + * This is used to detect overrides and for figuring out a parameter is missing. + * @var integer + */ + protected $setCount = 0; + + /** + * @var ProcessingError[] + */ + protected $errors = []; + + /** + * Indicates if the parameter was set to it's default. + * @var boolean + */ + protected $defaulted = false; + + /** + * @var ParamDefinition + */ + protected $definition; + + public function __construct( IParamDefinition $definition ) { + $this->definition = $definition; + } + + /** + * Sets and cleans the original value and name. + */ + public function setUserValue( string $paramName, $paramValue, Options $options ): bool { + if ( $this->setCount > 0 && !$options->acceptOverriding() ) { + // TODO + return false; + } + + $this->originalName = $paramName; + $this->originalValue = $paramValue; + + $this->cleanValue( $options ); + + $this->setCount++; + + return true; + } + + /** + * @param mixed $value + */ + public function setValue( $value ) { + $this->value = $value; + } + + /** + * Sets the $value to a cleaned value of $originalValue. + */ + protected function cleanValue( Options $options ) { + if ( $this->definition->isList() ) { + $this->value = explode( $this->definition->getDelimiter(), $this->originalValue ); + } + else { + $this->value = $this->originalValue; + } + + if ( $this->shouldTrim( $options ) ) { + $this->trimValue(); + } + + if ( $this->shouldLowercase( $options ) ) { + $this->lowercaseValue(); + } + } + + private function shouldTrim( Options $options ): bool { + $trim = $this->definition->trimDuringClean(); + + if ( $trim === true ) { + return true; + } + + return is_null( $trim ) && $options->trimValues(); + } + + private function trimValue() { + if ( is_string( $this->value ) ) { + $this->value = trim( $this->value ); + } + elseif ( $this->definition->isList() ) { + foreach ( $this->value as &$element ) { + if ( is_string( $element ) ) { + $element = trim( $element ); + } + } + } + } + + private function shouldLowercase( Options $options ): bool { + if ( $options->lowercaseValues() ) { + return true; + } + + $definitionOptions = $this->definition->getOptions(); + + return array_key_exists( 'tolower', $definitionOptions ) && $definitionOptions['tolower']; + } + + private function lowercaseValue() { + if ( $this->definition->isList() ) { + foreach ( $this->value as &$element ) { + if ( is_string( $element ) ) { + $element = strtolower( $element ); + } + } + } + elseif ( is_string( $this->value ) ) { + $this->value = strtolower( $this->value ); + } + } + + /** + * Parameter processing entry point. + * Processes the parameter. This includes parsing, validation and additional formatting. + * + * @param ParamDefinition[] $definitions + * @param Param[] $params + * @param Options $options + * + * @throws Exception + */ + public function process( array &$definitions, array $params, Options $options ) { + if ( $this->setCount == 0 ) { + if ( $this->definition->isRequired() ) { + // This should not occur, so throw an exception. + throw new Exception( 'Attempted to validate a required parameter without first setting a value.' ); + } + else { + $this->setToDefault(); + } + } + else { + $this->parseAndValidate( $options ); + } + + if ( !$this->hasFatalError() && ( $this->definition->shouldManipulateDefault() || !$this->wasSetToDefault() ) ) { + $this->definition->format( $this, $definitions, $params ); + } + } + + public function getValueParser( Options $options ): ValueParser { + $parser = $this->definition->getValueParser(); + + if ( !( $parser instanceof NullParser ) ) { + return $parser; + } + + // TODO: inject factory + $type = ParamDefinitionFactory::singleton()->getType( $this->definition->getType() ); + + $parserClass = $options->isStringlyTyped() ? $type->getStringParserClass() : $type->getTypedParserClass(); + + return new $parserClass( new \ValueParsers\ParserOptions( $this->definition->getOptions() ) ); + } + + protected function parseAndValidate( Options $options ) { + $parser = $this->getValueParser( $options ); + + if ( $this->definition->isList() ) { + $values = []; + + foreach ( $this->getValue() as $value ) { + $parsedValue = $this->parseAndValidateValue( $parser, $value ); + + if ( is_array( $parsedValue ) ) { + $values[] = $parsedValue[0]; + } + } + + $this->value = $values; + } + else { + $parsedValue = $this->parseAndValidateValue( $parser, $this->getValue() ); + + if ( is_array( $parsedValue ) ) { + $this->value = $parsedValue[0]; + } + } + + $this->setToDefaultIfNeeded(); + } + + /** + * Parses and validates the provided with with specified parser. + * The result is returned in an array on success. On fail, false is returned. + * The result is wrapped in an array since we need to be able to distinguish + * between the method returning false and the value being false. + * + * Parsing and validation errors get added to $this->errors. + * + * @since 1.0 + * + * @param ValueParser $parser + * @param mixed $value + * + * @return array|bool + */ + protected function parseAndValidateValue( ValueParser $parser, $value ) { + try { + $value = $parser->parse( $value ); + } + catch ( ParseException $parseException ) { + $this->registerProcessingError( $parseException->getMessage() ); + return false; + } + + if ( $value instanceof DataValue ) { + $value = $value->getValue(); + } + + $this->validateValue( $value ); + + return [ $value ]; + } + + protected function registerProcessingError( string $message ) { + $this->errors[] = $this->newProcessingError( $message ); + } + + protected function newProcessingError( string $message ): ProcessingError { + $severity = $this->isRequired() ? ProcessingError::SEVERITY_FATAL : ProcessingError::SEVERITY_NORMAL; + return new ProcessingError( $message, $severity ); + } + + /** + * @param mixed $value + */ + protected function validateValue( $value ) { + $validationCallback = $this->definition->getValidationCallback(); + + if ( $validationCallback !== null && $validationCallback( $value ) !== true ) { + $this->registerProcessingError( 'Validation callback failed' ); + } + else { + $validator = $this->definition->getValueValidator(); + if ( method_exists( $validator, 'setOptions' ) ) { + $validator->setOptions( $this->definition->getOptions() ); + } + $validationResult = $validator->validate( $value ); + + if ( !$validationResult->isValid() ) { + foreach ( $validationResult->getErrors() as $error ) { + $this->registerProcessingError( $error->getText() ); + } + } + } + } + + /** + * Sets the parameter value to the default if needed. + */ + protected function setToDefaultIfNeeded() { + if ( $this->shouldSetToDefault() ) { + $this->setToDefault(); + } + } + + private function shouldSetToDefault(): bool { + if ( $this->hasFatalError() ) { + return false; + } + + if ( $this->definition->isList() ) { + return $this->errors !== [] && $this->value === []; + } + + return $this->errors !== []; + } + + /** + * Returns the original use-provided name. + * + * @throws Exception + * @return string + */ + public function getOriginalName(): string { + if ( $this->setCount == 0 ) { + throw new Exception( 'No user input set to the parameter yet, so the original name does not exist' ); + } + return $this->originalName; + } + + /** + * Returns the original use-provided value. + * + * @throws Exception + * @return mixed + */ + public function getOriginalValue() { + if ( $this->setCount == 0 ) { + throw new Exception( 'No user input set to the parameter yet, so the original value does not exist' ); + } + return $this->originalValue; + } + + /** + * Returns all validation errors that occurred so far. + * + * @return ProcessingError[] + */ + public function getErrors(): array { + return $this->errors; + } + + /** + * Sets the parameter value to the default. + */ + protected function setToDefault() { + $this->defaulted = true; + $this->value = $this->definition->getDefault(); + } + + public function wasSetToDefault(): bool { + return $this->defaulted; + } + + public function hasFatalError(): bool { + foreach ( $this->errors as $error ) { + if ( $error->isFatal() ) { + return true; + } + } + + return false; + } + + /** + * Returns the ParamDefinition this Param was constructed from. + */ + public function getDefinition(): ParamDefinition { + return $this->definition; + } + + /** + * @return mixed + */ + public function &getValue() { + return $this->value; + } + + public function isRequired(): bool { + return $this->definition->isRequired(); + } + + public function getName(): string { + return $this->definition->getName(); + } + + /** + * @return string[] + */ + public function getAliases(): array { + return $this->definition->getAliases(); + } + +} \ No newline at end of file diff --git a/www/wiki/vendor/param-processor/param-processor/src/PackagePrivate/ParamType.php b/www/wiki/vendor/param-processor/param-processor/src/PackagePrivate/ParamType.php new file mode 100644 index 00000000..01022a1d --- /dev/null +++ b/www/wiki/vendor/param-processor/param-processor/src/PackagePrivate/ParamType.php @@ -0,0 +1,58 @@ +typeId = $typeId; + } + + public static function newFromArray( string $typeId, array $spec ): self { + $type = new self( $typeId ); + + $type->className = array_key_exists( 'definition', $spec ) ? $spec['definition'] : ParamDefinition::class; + $type->stringParser = array_key_exists( 'string-parser', $spec ) ? $spec['string-parser'] : NullParser::class; + $type->typedParser = array_key_exists( 'typed-parser', $spec ) ? $spec['typed-parser'] : NullParser::class; + $type->validator = array_key_exists( 'validator', $spec ) ? $spec['validator'] : NullValidator::class; + $type->validationCallback = array_key_exists( 'validation-callback', $spec ) ? $spec['validation-callback'] : null; + + return $type; + } + + public function getClassName(): string { + return $this->className; + } + + public function getValidatorClass(): string { + return $this->validator; + } + + public function getValidationCallback(): ?callable { + return $this->validationCallback; + } + + public function getStringParserClass(): string { + return $this->stringParser; + } + + public function getTypedParserClass(): string { + return $this->typedParser; + } + +} diff --git a/www/wiki/vendor/param-processor/param-processor/src/Param.php b/www/wiki/vendor/param-processor/param-processor/src/Param.php index 826cf9a4..aa05c78c 100644 --- a/www/wiki/vendor/param-processor/param-processor/src/Param.php +++ b/www/wiki/vendor/param-processor/param-processor/src/Param.php @@ -1,517 +1,3 @@ - */ -final class Param implements IParam { - - /** - * Indicates whether parameters not found in the criteria list - * should be stored in case they are not accepted. The default is false. - * - * @since 1.0 - * - * @var boolean - */ - public static $accumulateParameterErrors = false; - - /** - * The original parameter name as provided by the user. This can be the - * main name or an alias. - * - * @since 1.0 - * - * @var string - */ - protected $originalName; - - /** - * The original value as provided by the user. This is mainly retained for - * usage in error messages when the parameter turns out to be invalid. - * - * @since 1.0 - * - * @var string - */ - protected $originalValue; - - /** - * The value of the parameter. - * - * @since 1.0 - * - * @var mixed - */ - protected $value; - - /** - * Keeps track of how many times the parameter has been set by the user. - * This is used to detect overrides and for figuring out a parameter is missing. - * - * @since 1.0 - * - * @var integer - */ - protected $setCount = 0; - - /** - * List of validation errors for this parameter. - * - * @since 1.0 - * - * @var array of ProcessingError - */ - protected $errors = []; - - /** - * Indicates if the parameter was set to it's default. - * - * @since 1.0 - * - * @var boolean - */ - protected $defaulted = false; - - /** - * @since 1.0 - * - * @var ParamDefinition - */ - protected $definition; - - /** - * Constructor. - * - * @since 1.0 - * - * @param IParamDefinition $definition - */ - public function __construct( IParamDefinition $definition ) { - $this->definition = $definition; - } - - /** - * Sets and cleans the original value and name. - * @see IParam::setUserValue - * - * @since 1.0 - * - * @param string $paramName - * @param string $paramValue - * @param Options $options - * - * @return boolean - */ - public function setUserValue( $paramName, $paramValue, Options $options ) { - if ( $this->setCount > 0 && !$options->acceptOverriding() ) { - // TODO - return false; - } - else { - $this->originalName = $paramName; - $this->originalValue = $paramValue; - - $this->cleanValue( $options ); - - $this->setCount++; - - return true; - } - } - - /** - * Sets the value. - * - * @since 1.0 - * - * @param mixed $value - */ - public function setValue( $value ) { - $this->value = $value; - } - - /** - * Sets the $value to a cleaned value of $originalValue. - * - * TODO: the per-parameter lowercaseing and trimming here needs some thought - * - * @since 1.0 - * - * @param Options $options - */ - protected function cleanValue( Options $options ) { - $this->value = $this->originalValue; - - $trim = $this->getDefinition()->trimDuringClean(); - - if ( $trim === true || ( is_null( $trim ) && $options->trimValues() ) ) { - if ( is_string( $this->value ) ) { - $this->value = trim( $this->value ); - } - } - - - if ( $this->definition->isList() ) { - $this->value = explode( $this->definition->getDelimiter(), $this->value ); - - if ( $trim === true || ( is_null( $trim ) && $options->trimValues() ) ) { - foreach ( $this->value as &$element ) { - if ( is_string( $element ) ) { - $element = trim( $element ); - } - } - } - } - - $definitionOptions = $this->definition->getOptions(); - - if ( $options->lowercaseValues() || ( array_key_exists( 'tolower', $definitionOptions ) && $definitionOptions['tolower'] ) ) { - if ( $this->definition->isList() ) { - foreach ( $this->value as &$element ) { - if ( is_string( $element ) ) { - $element = strtolower( $element ); - } - } - } - elseif ( is_string( $this->value ) ) { - $this->value = strtolower( $this->value ); - } - } - } - - /** - * Parameter processing entry point. - * Processes the parameter. This includes parsing, validation and additional formatting. - * - * @since 1.0 - * - * @param $definitions array of IParamDefinition - * @param $params array of IParam - * @param Options $options - * - * @throws Exception - */ - public function process( array &$definitions, array $params, Options $options ) { - if ( $this->setCount == 0 ) { - if ( $this->definition->isRequired() ) { - // This should not occur, so throw an exception. - throw new Exception( 'Attempted to validate a required parameter without first setting a value.' ); - } - else { - $this->setToDefault(); - } - } - else { - $this->parseAndValidate( $options ); - } - - if ( !$this->hasFatalError() && ( $this->definition->shouldManipulateDefault() || !$this->wasSetToDefault() ) ) { - $this->definition->format( $this, $definitions, $params ); - } - } - - /** - * @since 1.0 - * - * @param Options $options - * - * @return ValueParser - */ - public function getValueParser( Options $options ) { - $parser = $this->definition->getValueParser(); - - if ( get_class( $parser ) === NullParser::class ) { - $parserType = $options->isStringlyTyped() ? 'string-parser' : 'typed-parser'; - - // TODO: inject factory - $parserClass = ParamDefinitionFactory::singleton()->getComponentForType( $this->definition->getType(), $parserType ); - - if ( $parserClass !== NullParser::class ) { - $parser = new $parserClass( new \ValueParsers\ParserOptions() ); - } - } - - return $parser; - } - - /** - * @since 1.0 - * - * @param Options $options - */ - protected function parseAndValidate( Options $options ) { - $parser = $this->getValueParser( $options ); - - if ( $this->definition->isList() ) { - $values = []; - - foreach ( $this->getValue() as $value ) { - $parsedValue = $this->parseAndValidateValue( $parser, $value ); - - if ( is_array( $parsedValue ) ) { - $values[] = $parsedValue[0]; - } - } - - $this->value = $values; - } - else { - $parsedValue = $this->parseAndValidateValue( $parser, $this->getValue() ); - - if ( is_array( $parsedValue ) ) { - $this->value = $parsedValue[0]; - } - } - - $this->setToDefaultIfNeeded(); - } - - /** - * Parses and validates the provided with with specified parser. - * The result is returned in an array on success. On fail, false is returned. - * The result is wrapped in an array since we need to be able to distinguish - * between the method returning false and the value being false. - * - * Parsing and validation errors get added to $this->errors. - * - * @since 1.0 - * - * @param ValueParser $parser - * @param mixed $value - * - * @return array|bool - */ - protected function parseAndValidateValue( ValueParser $parser, $value ) { - try { - $value = $parser->parse( $value ); - } - catch ( ParseException $parseException ) { - $this->registerProcessingError( $parseException->getMessage() ); - return false; - } - - if ( $value instanceof \DataValues\DataValue ) { - $value = $value->getValue(); - } - - $this->validateValue( $value ); - - return [ $value ]; - } - - /** - * @since 1.0 - * - * @param string $message - */ - protected function registerProcessingError( $message ) { - $this->errors[] = $this->newProcessingError( $message ); - } - - /** - * @since 1.0 - * - * @param string $message - * - * @return ProcessingError - */ - protected function newProcessingError( $message ) { - $severity = $this->isRequired() ? ProcessingError::SEVERITY_FATAL : ProcessingError::SEVERITY_NORMAL; - return new ProcessingError( $message, $severity ); - } - - /** - * @since 1.0 - * - * @param mixed $value - */ - protected function validateValue( $value ) { - $validationCallback = $this->definition->getValidationCallback(); - - if ( $validationCallback !== null && $validationCallback( $value ) !== true ) { - $this->registerProcessingError( 'Validation callback failed' ); - } - else { - $validator = $this->definition->getValueValidator(); - if ( method_exists( $validator, 'setOptions' ) ) { - $validator->setOptions( $this->definition->getOptions() ); - } - $validationResult = $validator->validate( $value ); - - if ( !$validationResult->isValid() ) { - foreach ( $validationResult->getErrors() as $error ) { - $this->registerProcessingError( $error->getText() ); - } - } - } - } - - /** - * Sets the parameter value to the default if needed. - * - * @since 1.0 - */ - protected function setToDefaultIfNeeded() { - if ( $this->shouldSetToDefault() ) { - $this->setToDefault(); - } - } - - private function shouldSetToDefault(): bool { - if ( $this->hasFatalError() ) { - return false; - } - - if ( $this->definition->isList() ) { - return $this->errors !== [] && $this->value === []; - } - - return $this->errors !== []; - } - - /** - * Returns the original use-provided name. - * - * @since 1.0 - * - * @throws Exception - * @return string - */ - public function getOriginalName() { - if ( $this->setCount == 0 ) { - throw new Exception( 'No user input set to the parameter yet, so the original name does not exist' ); - } - return $this->originalName; - } - - /** - * Returns the original use-provided value. - * - * @since 1.0 - * - * @throws Exception - * @return string - */ - public function getOriginalValue() { - if ( $this->setCount == 0 ) { - throw new Exception( 'No user input set to the parameter yet, so the original value does not exist' ); - } - return $this->originalValue; - } - - /** - * Returns all validation errors that occurred so far. - * - * @since 1.0 - * - * @return ProcessingError[] - */ - public function getErrors() { - return $this->errors; - } - - /** - * Sets the parameter value to the default. - * - * @since 1.0 - */ - protected function setToDefault() { - $this->defaulted = true; - $this->value = $this->definition->getDefault(); - } - - /** - * Gets if the parameter was set to it's default. - * - * @since 1.0 - * - * @return boolean - */ - public function wasSetToDefault() { - return $this->defaulted; - } - - /** - * @return boolean - */ - public function hasFatalError() { - foreach ( $this->errors as $error ) { - if ( $error->isFatal() ) { - return true; - } - } - - return false; - } - - /** - * Returns the IParamDefinition this IParam was constructed from. - * - * @since 1.0 - * - * @return IParamDefinition - */ - public function getDefinition() { - return $this->definition; - } - - /** - * Returns the parameters value. - * - * @since 1.0 - * - * @return mixed - */ - public function &getValue() { - return $this->value; - } - - /** - * Returns if the parameter is required or not. - * - * @since 1.0 - * - * @return boolean - */ - public function isRequired() { - return $this->definition->isRequired(); - } - - /** - * Returns if the name of the parameter. - * - * @since 1.0 - * - * @return boolean - */ - public function getName() { - return $this->definition->getName(); - } - - /** - * Returns the parameter name aliases. - * - * @since 1.0 - * - * @return string[] - */ - public function getAliases() { - return $this->definition->getAliases(); - } - -} \ No newline at end of file +class_alias(\ParamProcessor\PackagePrivate\Param::class, 'ParamProcessor\Param'); \ No newline at end of file diff --git a/www/wiki/vendor/param-processor/param-processor/src/ParamDefinition.php b/www/wiki/vendor/param-processor/param-processor/src/ParamDefinition.php index 3990e757..1f49bb4a 100644 --- a/www/wiki/vendor/param-processor/param-processor/src/ParamDefinition.php +++ b/www/wiki/vendor/param-processor/param-processor/src/ParamDefinition.php @@ -3,19 +3,17 @@ namespace ParamProcessor; use Exception; - -use ValueParsers\ValueParser; +use ParamProcessor\PackagePrivate\Param; use ValueParsers\NullParser; - -use ValueValidators\ValueValidator; +use ValueParsers\ValueParser; use ValueValidators\NullValidator; +use ValueValidators\ValueValidator; /** - * Parameter definition. * Specifies what kind of values are accepted, how they should be validated, * how they should be formatted, what their dependencies are and how they should be described. * - * Try to avoid using this interface outside of ParamProcessor for anything else then defining parameters. + * Try to avoid using this interface outside of ParamProcessor for anything else than defining parameters. * In particular, do not derive from this class to implement methods such as formatValue. * * @since 1.0 @@ -23,13 +21,13 @@ use ValueValidators\NullValidator; * @licence GNU GPL v2+ * @author Jeroen De Dauw < jeroendedauw@gmail.com > */ -class ParamDefinition implements IParamDefinition { +/* final */ class ParamDefinition implements IParamDefinition { /** * Indicates whether parameters that are provided more then once should be accepted, * and use the first provided value, or not, and generate an error. * - * @since 1.0 + * @deprected since 1.7 * * @var boolean */ @@ -39,65 +37,44 @@ class ParamDefinition implements IParamDefinition { * Indicates whether parameters not found in the criteria list * should be stored in case they are not accepted. The default is false. * - * @since 1.0 + * @deprected since 1.7 * * @var boolean */ public static $accumulateParameterErrors = false; + protected $type; + protected $name; + protected $default; + protected $isList; + /** - * Indicates if the parameter value should trimmed during the clean process. - * - * @since 1.0 + * A message that acts as description for the parameter or false when there is none. + * Can be obtained via getMessage and set via setMessage. * + * @var string + */ + protected $message; + + /** + * Indicates if the parameter value should trimmed during the clean process. * @var boolean|null */ protected $trimValue = null; /** * Indicates if the parameter manipulations should be applied to the default value. - * - * @since 1.0 - * * @var boolean */ protected $applyManipulationsToDefault = true; /** * Dependency list containing parameters that need to be handled before this one. - * - * @since 1.0 - * * @var string[] */ protected $dependencies = []; /** - * The default value for the parameter, or null when the parameter is required. - * - * @since 1.0 - * - * @var mixed - */ - protected $default; - - /** - * The main name of the parameter. - * - * @since 1.0 - * - * @var string - */ - protected $name; - - /** - * @since 1.0 - * @var boolean - */ - protected $isList; - - /** - * @since 1.0 * @var string */ protected $delimiter = ','; @@ -105,75 +82,43 @@ class ParamDefinition implements IParamDefinition { /** * List of aliases for the parameter name. * - * @since 1.0 - * * @var string[] */ protected $aliases = []; - /** - * A message that acts as description for the parameter or false when there is none. - * Can be obtained via getMessage and set via setMessage. - * - * @since 1.0 - * - * @var string - */ - protected $message = 'validator-message-nodesc'; - /** * Original array definition of the parameter - * - * @since 1.0 - * * @var array */ protected $options = []; /** - * @since 1.0 - * * @var ValueParser|null */ protected $parser = null; /** - * @since 1.0 - * * @var ValueValidator|null */ protected $validator = null; /** - * @since 0.1 - * * @var callable|null */ protected $validationFunction = null; /** - * @since 0.1 - * - * @var string - */ - protected $type; - - /** - * Constructor. - * - * @since 1.0 - * * @param string $type * @param string $name * @param mixed $default Use null for no default (which makes the parameter required) * @param string $message * @param boolean $isList */ - public function __construct( $type, $name, $default = null, $message = null, $isList = false ) { + public function __construct( string $type, string $name, $default = null, string $message = null, bool $isList = false ) { $this->type = $type; $this->name = $name; $this->default = $default; - $this->message = $message; + $this->message = $message ?? 'validator-message-nodesc'; $this->isList = $isList; $this->postConstruct(); @@ -190,61 +135,39 @@ class ParamDefinition implements IParamDefinition { } /** - * @see IParamDefinition::trimDuringClean - * - * @since 1.0 - * - * @return boolean|null + * Returns if the value should be trimmed before validation and any further processing. + * - true: always trim + * - false: never trim + * - null: trim based on context settings */ - public function trimDuringClean() { + public function trimDuringClean(): ?bool { return $this->trimValue; } /** - * @see IParamDefinition::getAliases - * - * @since 1.0 - * + * Returns the parameter name aliases. * @return string[] */ - public function getAliases() { + public function getAliases(): array { return $this->aliases; } - /** - * @see IParamDefinition::hasAlias - * - * @since 1.0 - * - * @param string $alias - * - * @return boolean - */ - public function hasAlias( $alias ) { + public function hasAlias( string $alias ): bool { return in_array( $alias, $this->getAliases() ); } /** - * @see IParamDefinition::hasDependency - * - * @since 1.0 - * - * @param string $dependency - * - * @return boolean + * @deprecated since 1.7 + * Returns if the parameter has a certain dependency. */ - public function hasDependency( $dependency ) { + public function hasDependency( string $dependency ): bool { return in_array( $dependency, $this->getDependencies() ); } /** * Returns the list of allowed values, or an empty array if there is no such restriction. - * - * @since 1.0 - * - * @return array */ - public function getAllowedValues() { + public function getAllowedValues(): array { $allowedValues = []; if ( $this->validator !== null && method_exists( $this->validator, 'getWhitelistedValues' ) ) { @@ -263,9 +186,7 @@ class ParamDefinition implements IParamDefinition { } /** - * @see IParamDefinition::setDefault - * - * @since 1.0 + * @deprecated since 1.7 * * @param mixed $default * @param boolean $manipulate Should the default be manipulated or not? Since 0.4.6. @@ -276,10 +197,7 @@ class ParamDefinition implements IParamDefinition { } /** - * @see IParamDefinition::getDefault - * - * @since 1.0 - * + * Returns the default value. * @return mixed */ public function getDefault() { @@ -287,53 +205,33 @@ class ParamDefinition implements IParamDefinition { } /** - * @see IParamDefinition::getMessage - * - * @since 1.0 - * - * @return string + * Returns a message describing the parameter. */ - public function getMessage() { + public function getMessage(): string { return $this->message; } /** - * @see IParamDefinition::setMessage - * - * @since 1.0 - * - * @param string $message + * This should be a message key, ie something that can be passed + * to wfMsg. Not an actual text. */ - public function setMessage( $message ) { + public function setMessage( string $message ) { $this->message = $message; } /** - * @see IParamDefinition::setDoManipulationOfDefault - * - * @since 1.0 - * - * @param boolean $doOrDoNotThereIsNoTry + * Set if the parameter manipulations should be applied to the default value. */ - public function setDoManipulationOfDefault( $doOrDoNotThereIsNoTry ) { + public function setDoManipulationOfDefault( bool $doOrDoNotThereIsNoTry ) { $this->applyManipulationsToDefault = $doOrDoNotThereIsNoTry; } - /** - * @see IParamDefinition::shouldManipulateDefault - * - * @since 1.0 - * - * @return boolean - */ - public function shouldManipulateDefault() { + public function shouldManipulateDefault(): bool { return $this->applyManipulationsToDefault; } /** - * @see IParamDefinition::addAliases - * - * @since 1.0 + * Adds one or more aliases for the parameter name. * * @param string|string[] $aliases */ @@ -343,9 +241,8 @@ class ParamDefinition implements IParamDefinition { } /** - * @see IParamDefinition::addDependencies - * - * @since 1.0 + * Adds one or more dependencies. There are the names of parameters + * that need to be validated and formatted before this one. * * @param string|string[] $dependencies */ @@ -354,25 +251,14 @@ class ParamDefinition implements IParamDefinition { $this->dependencies = array_merge( $this->dependencies, is_array( $args[0] ) ? $args[0] : $args ); } - /** - * @see IParamDefinition::getName - * - * @since 1.0 - * - * @return string - */ - public function getName() { + public function getName(): string { return $this->name; } /** * Returns a message key for a message describing the parameter type. - * - * @since 1.0 - * - * @return string */ - public function getTypeMessage() { + public function getTypeMessage(): string { $message = 'validator-type-' . $this->getType(); if ( $this->isList() ) { @@ -383,66 +269,45 @@ class ParamDefinition implements IParamDefinition { } /** - * @see IParamDefinition::getDependencies - * - * @since 1.0 + * @deprecated since 1.7 + * Returns a list of dependencies the parameter has, in the form of + * other parameter names. * * @return string[] */ - public function getDependencies() { + public function getDependencies(): array { return $this->dependencies; } - /** - * @see IParamDefinition::isRequired - * - * @since 1.0 - * - * @return boolean - */ - public function isRequired() { + public function isRequired(): bool { return is_null( $this->default ); } - /** - * @see IParamDefinition::isList - * - * @since 1.0 - * - * @return boolean - */ - public function isList() { + public function isList(): bool { return $this->isList; } /** - * @see IParamDefinition::getDelimiter - * - * @since 1.0 - * - * @return string + * Returns the delimiter to use to split the raw value in case the + * parameter is a list. */ - public function getDelimiter() { + public function getDelimiter(): string { return $this->delimiter; } /** - * @see IParamDefinition::setDelimiter - * - * @since 1.0 - * - * @param $delimiter string + * Sets the delimiter to use to split the raw value in case the + * parameter is a list. */ - public function setDelimiter( $delimiter ) { + public function setDelimiter( string $delimiter ) { $this->delimiter = $delimiter; } /** - * @see IParamDefinition::setArrayValues + * Sets the parameter definition values contained in the provided array. * - * @since 1.0 - * - * @param array $param + * @deprecated since 1.7 + * TODO: provide alternative in ParamDefinitionFactory */ public function setArrayValues( array $param ) { if ( array_key_exists( 'aliases', $param ) ) { @@ -471,7 +336,6 @@ class ParamDefinition implements IParamDefinition { /** * @see IParamDefinition::format * - * @since 1.0 * @deprecated * * @param IParam $param @@ -479,6 +343,10 @@ class ParamDefinition implements IParamDefinition { * @param IParam[] $params */ public function format( IParam $param, array &$definitions, array $params ) { + /** + * @var Param $param + */ + if ( $this->isList() && is_array( $param->getValue() ) ) { // TODO: if isList returns true, the value should be an array. // The second check here is to avoid a mysterious error. @@ -516,7 +384,6 @@ class ParamDefinition implements IParamDefinition { * @param $params array of IParam */ protected function formatList( IParam $param, array &$definitions, array $params ) { - // TODO } /** @@ -534,7 +401,6 @@ class ParamDefinition implements IParamDefinition { */ protected function formatValue( $value, IParam $param, array &$definitions, array $params ) { return $value; - // No-op } /** @@ -543,54 +409,28 @@ class ParamDefinition implements IParamDefinition { * ParamDefinition classes and having all keys set to the names of the * corresponding parameters. * - * @since 1.0 + * @deprecated since 1.7 - use ParamDefinitionFactory * - * @param IParamDefinition[] $definitions + * @param ParamDefinition[] $definitions * - * @return IParamDefinition[] + * @return ParamDefinition[] * @throws Exception */ - public static function getCleanDefinitions( array $definitions ) { - $cleanList = []; - - foreach ( $definitions as $key => $definition ) { - if ( is_array( $definition ) ) { - if ( !array_key_exists( 'name', $definition ) && is_string( $key ) ) { - $definition['name'] = $key; - } - - $definition = ParamDefinitionFactory::singleton()->newDefinitionFromArray( $definition ); - } - - if ( !( $definition instanceof IParamDefinition ) ) { - throw new Exception( '$definition not an instance of IParamDefinition' ); - } - - $cleanList[$definition->getName()] = $definition; - } - - return $cleanList; + public static function getCleanDefinitions( array $definitions ): array { + return ParamDefinitionFactory::singleton()->newDefinitionsFromArrays( $definitions ); } /** - * @see IParamDefinition::getType - * - * @since 1.0 - * - * @return string + * Returns an identifier for the type of the parameter. */ - public function getType() { + public function getType(): string { return $this->type; } /** - * @see IParamDefinition::getValueParser - * - * @since 1.0 - * - * @return ValueParser + * Returns a ValueParser object to parse the parameters value. */ - public function getValueParser() { + public function getValueParser(): ValueParser { if ( $this->parser === null ) { $this->parser = new NullParser(); } @@ -599,13 +439,9 @@ class ParamDefinition implements IParamDefinition { } /** - * @see IParamDefinition::getValueValidator - * - * @since 1.0 - * - * @return ValueValidator + * Returns a ValueValidator that can be used to validate the parameters value. */ - public function getValueValidator() { + public function getValueValidator(): ValueValidator { if ( $this->validator === null ) { $this->validator = new NullValidator(); } @@ -613,56 +449,32 @@ class ParamDefinition implements IParamDefinition { return $this->validator; } - /** - * @see IParamDefinition::setValueParser - * - * @since 1.0 - * - * @param ValueParser $parser - */ public function setValueParser( ValueParser $parser ) { $this->parser = $parser; } - /** - * @see IParamDefinition::setValueValidator - * - * @since 1.0 - * - * @param ValueValidator $validator - */ public function setValueValidator( ValueValidator $validator ) { $this->validator = $validator; } /** - * @see IParamDefinition::setValidationCallback + * Sets a validation function that will be run before the ValueValidator. * - * @since 1.0 - * - * @param callable $validationFunction + * This can be used instead of a ValueValidator where validation is very + * trivial, ie checking if something is a boolean can be done with is_bool. */ - public function setValidationCallback( /* callable */ $validationFunction ) { + public function setValidationCallback( ?callable $validationFunction ) { $this->validationFunction = $validationFunction; } /** - * @see IParamDefinition::getValidationCallback - * - * @since 1.0 - * - * @return callable|null + * @see setValidationCallback */ - public function getValidationCallback() { + public function getValidationCallback(): ?callable { return $this->validationFunction; } - /** - * @since 0.1 - * - * @return array - */ - public function getOptions() { + public function getOptions(): array { return $this->options; } diff --git a/www/wiki/vendor/param-processor/param-processor/src/ParamDefinitionFactory.php b/www/wiki/vendor/param-processor/param-processor/src/ParamDefinitionFactory.php index f582d67f..dcf72839 100644 --- a/www/wiki/vendor/param-processor/param-processor/src/ParamDefinitionFactory.php +++ b/www/wiki/vendor/param-processor/param-processor/src/ParamDefinitionFactory.php @@ -4,42 +4,39 @@ namespace ParamProcessor; use Exception; use OutOfBoundsException; +use ParamProcessor\PackagePrivate\ParamType; +use ValueValidators\NullValidator; /** - * Factory for IParamDefinition implementing objects. + * Factory for ParamDefinition implementing objects. * * @licence GNU GPL v2+ * @author Jeroen De Dauw < jeroendedauw@gmail.com > */ class ParamDefinitionFactory { + private $types; + /** - * Maps parameter type to handling IParameterDefinition implementing class. - * - * @since 1.0 - * - * @var array + * @since 1.8 */ - private $typeToClass = []; + public function __construct( ParameterTypes $types = null ) { + $this->types = $types ?? new ParameterTypes(); + } /** - * Maps parameter type to its associated components. - * - * @since 1.0 + * Returns a ParamDefinitionFactory that already has the core parameter types (@see ParameterTypes) registered. * - * @var array + * @since 1.6 */ - private $typeToComponent = []; + public static function newDefault(): self { + return new self( ParameterTypes::newCoreTypes() ); + } /** - * Singleton. - * - * @since 1.0 * @deprecated since 1.0 - * - * @return ParamDefinitionFactory */ - public static function singleton() { + public static function singleton(): self { static $instance = false; if ( $instance === false ) { @@ -52,8 +49,7 @@ class ParamDefinitionFactory { /** * Registers the parameter types specified in the global $wgParamDefinitions. - * - * @since 1.0 + * @deprecated since 1.6 */ public function registerGlobals() { if ( array_key_exists( 'wgParamDefinitions', $GLOBALS ) ) { @@ -75,7 +71,7 @@ class ParamDefinitionFactory { * * - string-parser: the parser to use to transform string values * This class needs to implement ValueParser. Default: NullParser - * - typed-parser: the parser to use to transform typed PHP values + * - typed-parser: DEPRECATED since 1.6 - the parser to use to transform typed PHP values * This class needs to implement ValueParser. Default: NullParser * - validator: the validation object to use * This class needs to implement ValueValidator. Default: NullValidator @@ -87,71 +83,56 @@ class ParamDefinitionFactory { * @param string $type * @param array $data * - * @return boolean Indicates if the type was registered + * @return boolean DEPRECATED since 1.6 - Indicates if the type was registered */ public function registerType( $type, array $data ) { - if ( array_key_exists( $type, $this->typeToClass ) ) { + if ( $this->types->hasType( $type ) ) { return false; } - $class = array_key_exists( 'definition', $data ) ? $data['definition'] : 'ParamProcessor\ParamDefinition'; - $this->typeToClass[$type] = $class; - - $defaults = [ - 'string-parser' => 'ValueParsers\NullParser', - 'typed-parser' => 'ValueParsers\NullParser', - 'validator' => 'ValueValidators\NullValidator', - 'validation-callback' => null, - ]; - - $this->typeToComponent[$type] = []; - - foreach ( $defaults as $component => $default ) { - $this->typeToComponent[$type][$component] = array_key_exists( $component, $data ) ? $data[$component] : $default; - } + $this->types->addType( $type, $data ); return true; } /** - * Creates a new instance of a IParamDefinition based on the provided type. - * - * @since 1.0 + * Creates a new instance of a ParamDefinition based on the provided type. * - * @param string $type + * @param string $typeName * @param string $name * @param mixed $default * @param string $message * @param boolean $isList * - * @return IParamDefinition + * @return ParamDefinition * @throws OutOfBoundsException */ - public function newDefinition( $type, $name, $default, $message, $isList = false ) { - if ( !array_key_exists( $type, $this->typeToClass ) ) { - throw new OutOfBoundsException( 'Unknown parameter type "' . $type . '".' ); + public function newDefinition( string $typeName, string $name, $default, string $message, bool $isList = false ): ParamDefinition { + if ( !$this->types->hasType( $typeName ) ) { + throw new OutOfBoundsException( 'Unknown parameter type "' . $typeName . '".' ); } - $class = $this->typeToClass[$type]; + $type = $this->types->getType( $typeName ); + $class = $type->getClassName(); /** - * @var IParamDefinition $definition + * @var ParamDefinition $definition */ $definition = new $class( - $type, + $typeName, $name, $default, $message, $isList ); - $validator = $this->typeToComponent[$type]['validator']; + $validator = $type->getValidatorClass(); - if ( $validator !== '\ValueValidators\NullValidator' ) { + if ( $validator !== NullValidator::class ) { $definition->setValueValidator( new $validator() ); } - $validationCallback = $this->typeToComponent[$type]['validation-callback']; + $validationCallback = $type->getValidationCallback(); if ( $validationCallback !== null ) { $definition->setValidationCallback( $validationCallback ); @@ -161,43 +142,22 @@ class ParamDefinitionFactory { } /** - * Returns the specified component for the provided parameter type. - * This method is likely to change in the future in a compat breaking way. - * - * @since 1.0 - * - * @param string $paramType - * @param string $componentType - * - * @throws Exception - * @return mixed + * Package private */ - public function getComponentForType( $paramType, $componentType ) { - if ( !array_key_exists( $paramType, $this->typeToComponent ) ) { - throw new Exception( 'Unknown parameter type "' . $paramType . '".' ); - } - - if ( !array_key_exists( $componentType, $this->typeToComponent[$paramType] ) ) { - throw new Exception( 'Unknown parameter component type "' . $paramType . '".' ); - } - - return $this->typeToComponent[$paramType][$componentType]; + public function getType( string $typeName ): ParamType { + return $this->types->getType( $typeName ); } /** - * Construct a new ParamDefinition from an array. - * - * @since 1.0 + * @param array $definitionArray + * @param bool $getMad DEPRECATED since 1.6 * - * @param array $param - * @param bool $getMad - * - * @return IParamDefinition|false + * @return ParamDefinition|false * @throws Exception */ - public function newDefinitionFromArray( array $param, $getMad = true ) { + public function newDefinitionFromArray( array $definitionArray, $getMad = true ) { foreach ( [ 'name', 'message' ] as $requiredElement ) { - if ( !array_key_exists( $requiredElement, $param ) ) { + if ( !array_key_exists( $requiredElement, $definitionArray ) ) { if ( $getMad ) { throw new Exception( 'Could not construct a ParamDefinition from an array without ' . $requiredElement . ' element' ); } @@ -206,17 +166,50 @@ class ParamDefinitionFactory { } } - $parameter = $this->newDefinition( - array_key_exists( 'type', $param ) ? $param['type'] : 'string', - $param['name'], - array_key_exists( 'default', $param ) ? $param['default'] : null, - $param['message'], - array_key_exists( 'islist', $param ) ? $param['islist'] : false + $definition = $this->newDefinition( + array_key_exists( 'type', $definitionArray ) ? $definitionArray['type'] : 'string', + $definitionArray['name'], + array_key_exists( 'default', $definitionArray ) ? $definitionArray['default'] : null, + $definitionArray['message'], + array_key_exists( 'islist', $definitionArray ) ? $definitionArray['islist'] : false ); - $parameter->setArrayValues( $param ); + $definition->setArrayValues( $definitionArray ); + + return $definition; + } + + /** + * @since 1.9 + * + * @param array $definitionArrays Each element must either be + * - A definition array with "name" key + * - A name key pointing to a definition array + * - A ParamDefinition instance (discouraged) + * + * @return ParamDefinition[] + * @throws Exception + */ + public function newDefinitionsFromArrays( array $definitionArrays ): array { + $cleanList = []; + + foreach ( $definitionArrays as $key => $definitionArray ) { + if ( is_array( $definitionArray ) ) { + if ( !array_key_exists( 'name', $definitionArray ) && is_string( $key ) ) { + $definitionArray['name'] = $key; + } + + $definitionArray = $this->newDefinitionFromArray( $definitionArray ); + } + + if ( !( $definitionArray instanceof IParamDefinition ) ) { + throw new Exception( 'Parameter definition not an instance of IParamDefinition' ); + } + + $cleanList[$definitionArray->getName()] = $definitionArray; + } - return $parameter; + return $cleanList; } } diff --git a/www/wiki/vendor/param-processor/param-processor/src/ParameterTypes.php b/www/wiki/vendor/param-processor/param-processor/src/ParameterTypes.php index ca4e66bc..4f984e51 100644 --- a/www/wiki/vendor/param-processor/param-processor/src/ParameterTypes.php +++ b/www/wiki/vendor/param-processor/param-processor/src/ParameterTypes.php @@ -4,8 +4,9 @@ declare( strict_types = 1 ); namespace ParamProcessor; -use ParamProcessor\Definition\DimensionParam; use ParamProcessor\Definition\StringParam; +use ParamProcessor\PackagePrivate\DimensionParser; +use ParamProcessor\PackagePrivate\ParamType; use ValueParsers\BoolParser; use ValueParsers\FloatParser; use ValueParsers\IntParser; @@ -14,37 +15,89 @@ use ValueValidators\RangeValidator; use ValueValidators\StringValidator; /** - * @since 1.4 - * * @licence GNU GPL v2+ * @author Jeroen De Dauw < jeroendedauw@gmail.com > */ class ParameterTypes { + /** + * @since 1.7 + */ + public const BOOLEAN = 'boolean'; + public const FLOAT = 'float'; + public const INTEGER = 'integer'; + public const STRING = 'string'; + public const DIMENSION = 'dimension'; + + /** + * @var ParamType[] + */ + private $types = []; + + /** + * @param array[] $typeSpecs + */ + public function __construct( array $typeSpecs = [] ) { + foreach ( $typeSpecs as $typeName => $typeSpec ) { + $this->addType( $typeName, $typeSpec ); + } + } + + /** + * @since 1.8 + */ + public function addType( string $typeName, array $typeSpec ) { + $this->types[$typeName] = ParamType::newFromArray( $typeName, $typeSpec ); + } + + /** + * Package private + */ + public function hasType( string $typeName ): bool { + return array_key_exists( $typeName, $this->types ); + } + + /** + * Package private + */ + public function getType( string $typeName ): ParamType { + return $this->types[$typeName]; + } + + /** + * @since 1.8 + */ + public static function newCoreTypes(): self { + return new self( self::getCoreTypes() ); + } + + /** + * @since 1.4 + */ public static function getCoreTypes(): array { return [ - 'boolean' => [ + self::BOOLEAN => [ 'string-parser' => BoolParser::class, 'validation-callback' => 'is_bool', ], - 'float' => [ + self::FLOAT => [ 'string-parser' => FloatParser::class, 'validation-callback' => function( $value ) { return is_float( $value ) || is_int( $value ); }, 'validator' => RangeValidator::class, ], - 'integer' => [ + self::INTEGER => [ 'string-parser' => IntParser::class, 'validation-callback' => 'is_int', 'validator' => RangeValidator::class, ], - 'string' => [ + self::STRING => [ 'validator' => StringValidator::class, 'definition' => StringParam::class, ], - 'dimension' => [ - 'definition' => DimensionParam::class, + self::DIMENSION => [ + 'string-parser' => DimensionParser::class, 'validator' => DimensionValidator::class, ], ]; diff --git a/www/wiki/vendor/param-processor/param-processor/src/ProcessingError.php b/www/wiki/vendor/param-processor/param-processor/src/ProcessingError.php index 03418661..2e5cd51a 100644 --- a/www/wiki/vendor/param-processor/param-processor/src/ProcessingError.php +++ b/www/wiki/vendor/param-processor/param-processor/src/ProcessingError.php @@ -83,7 +83,7 @@ class ProcessingError { /** * Returns the severity of the error. - * @return integer Element of the ProcessingError::SEVERITY_ enum + * @return integer Element of the self::SEVERITY_ enum */ public function getSeverity(): int { return $this->severity; @@ -113,16 +113,16 @@ class ProcessingError { /** * Returns the action associated with the errors severity. * - * @return integer Element of the ProcessingError::ACTION_ enum + * @return integer Element of the self::ACTION_ enum * @throws \Exception */ public function getAction(): int { // TODO: as option $errorActions = [ - ProcessingError::SEVERITY_MINOR => ProcessingError::ACTION_LOG, - ProcessingError::SEVERITY_LOW => ProcessingError::ACTION_WARN, - ProcessingError::SEVERITY_NORMAL => ProcessingError::ACTION_SHOW, - ProcessingError::SEVERITY_HIGH => ProcessingError::ACTION_DEMAND, + self::SEVERITY_MINOR => self::ACTION_LOG, + self::SEVERITY_LOW => self::ACTION_WARN, + self::SEVERITY_NORMAL => self::ACTION_SHOW, + self::SEVERITY_HIGH => self::ACTION_DEMAND, ]; if ( $this->severity === self::SEVERITY_FATAL ) { diff --git a/www/wiki/vendor/param-processor/param-processor/src/ProcessingErrorHandler.php b/www/wiki/vendor/param-processor/param-processor/src/ProcessingErrorHandler.php index 8ce9e8ca..faa6fcea 100644 --- a/www/wiki/vendor/param-processor/param-processor/src/ProcessingErrorHandler.php +++ b/www/wiki/vendor/param-processor/param-processor/src/ProcessingErrorHandler.php @@ -4,7 +4,7 @@ namespace ParamProcessor; /** * Static class for error handling. - * + * * @since 0.4 * @deprecated since 1.0 * @@ -12,48 +12,48 @@ namespace ParamProcessor; * @author Jeroen De Dauw < jeroendedauw@gmail.com > */ final class ProcessingErrorHandler { - + /** * @since 0.4 - * + * * @var array of ProcessingError */ private static $errors = []; - + /** * Adds a single ProcessingError. - * + * * @since 0.4 - * + * * @param string $errorMessage * @param integer $severity */ public static function addError( ProcessingError $error ) { self::$errors[$error->getElement()][] = $error; } - + /** * Adds a list of ProcessingError. - * + * * @since 0.4 - * + * * @param array $errors - */ + */ public static function addErrors( array $errors ) { foreach ( $errors as $error ) { self::addError( $error ); } } - + /** * Returns a list of all registered errors. - * + * * @since 0.4 - * + * * @return array of ProcessingError */ public static function getErrors() { return self::$errors; } - + } \ No newline at end of file diff --git a/www/wiki/vendor/param-processor/param-processor/src/ProcessingResult.php b/www/wiki/vendor/param-processor/param-processor/src/ProcessingResult.php index 1f519156..f184808f 100644 --- a/www/wiki/vendor/param-processor/param-processor/src/ProcessingResult.php +++ b/www/wiki/vendor/param-processor/param-processor/src/ProcessingResult.php @@ -29,6 +29,19 @@ class ProcessingResult { return $this->parameters; } + /** + * @since 1.8 + */ + public function getParameterArray(): array { + $parameters = []; + + foreach ( $this->parameters as $parameter ) { + $parameters[$parameter->getName()] = $parameter->getValue(); + } + + return $parameters; + } + /** * @return ProcessingError[] */ diff --git a/www/wiki/vendor/param-processor/param-processor/src/Processor.php b/www/wiki/vendor/param-processor/param-processor/src/Processor.php index a6f2cb46..73a8d67a 100644 --- a/www/wiki/vendor/param-processor/param-processor/src/Processor.php +++ b/www/wiki/vendor/param-processor/param-processor/src/Processor.php @@ -2,6 +2,8 @@ namespace ParamProcessor; +use ParamProcessor\PackagePrivate\Param; + /** * Class for parameter validation of a single parser hook or other parametrized construct. * @@ -41,7 +43,7 @@ class Processor { private $paramsToHandle = []; /** - * @var IParamDefinition[] + * @var ParamDefinition[] */ private $paramDefinitions = []; @@ -58,10 +60,6 @@ class Processor { /** * Constructs and returns a Validator object based on the default options. - * - * @since 1.0 - * - * @return Processor */ public static function newDefault(): self { return new Processor( new Options() ); @@ -69,12 +67,6 @@ class Processor { /** * Constructs and returns a Validator object based on the provided options. - * - * @since 1.0 - * - * @param Options $options - * - * @return Processor */ public static function newFromOptions( Options $options ): self { return new Processor( $options ); @@ -82,10 +74,6 @@ class Processor { /** * Returns the options used by this Validator object. - * - * @since 1.0 - * - * @return Options */ public function getOptions(): Options { return $this->options; @@ -98,18 +86,14 @@ class Processor { * @since 0.4 * * @param string[] $rawParams - * @param array $parameterDefinitions + * @param ParamDefinition[]|array[] $parameterDefinitions DEPRECATED! Use @see setParameterDefinitions instead * @param array $defaultParams array of strings or array of arrays to define which parameters can be used unnamed. * The second value in array-form is reserved for flags. Currently, Processor::PARAM_UNNAMED determines that * the parameter has no name which can be used to set it. Therefore all these parameters must be set before * any named parameter. The effect is, that '=' within the string won't confuse the parameter anymore like * it would happen with default parameters that still have a name as well. */ - public function setFunctionParams( array $rawParams, array $parameterDefinitions, array $defaultParams = [] ) { - $parameters = []; - - $nr = 0; - $defaultNr = 0; + public function setFunctionParams( array $rawParams, array $parameterDefinitions = [], array $defaultParams = [] ) { $lastUnnamedDefaultNr = -1; /* @@ -118,7 +102,7 @@ class Processor { * after the first named param. Wouldn't be possible to determine which unnamed value * belongs to which parameter otherwise. */ - for( $i = count( $defaultParams ) - 1; $i >= 0 ; $i-- ) { + for( $i = count( $defaultParams ) - 1; $i >= 0; $i-- ) { $dflt = $defaultParams[$i]; if( is_array( $dflt ) && !empty( $dflt[1] ) && ( $dflt[1] | self::PARAM_UNNAMED ) ) { $lastUnnamedDefaultNr = $i; @@ -126,6 +110,10 @@ class Processor { } } + $parameters = []; + $nr = 0; + $defaultNr = 0; + foreach ( $rawParams as $arg ) { // Only take into account strings. If the value is not a string, // it is not a raw parameter, and can not be parsed correctly in all cases. @@ -150,9 +138,6 @@ class Processor { ]; $defaultNr++; } - else { - // It might be nice to have some sort of warning or error here, as the value is simply ignored. - } } else { $paramName = trim( strtolower( $parts[0] ) ); @@ -167,7 +152,9 @@ class Processor { $newDefaults = []; foreach( $defaultParams as $defaultParam ) { - if ( $defaultParam != $paramName ) $newDefaults[] = $defaultParam; + if ( $defaultParam != $paramName ) { + $newDefaults[] = $defaultParam; + } } $defaultParams = $newDefaults; @@ -180,14 +167,22 @@ class Processor { $this->setParameters( $parameters, $parameterDefinitions ); } + /** + * @since 1.6.0 + * @param ParamDefinition[] $paramDefinitions + */ + public function setParameterDefinitions( array $paramDefinitions ) { + $this->paramDefinitions = $paramDefinitions; + } + /** * Loops through a list of provided parameters, resolves aliasing and stores errors * for unknown parameters and optionally for parameter overriding. * * @param array $parameters Parameter name as key, parameter value as value - * @param IParamDefinition[] $paramDefinitions List of parameter definitions. Either ParamDefinition objects or equivalent arrays. + * @param ParamDefinition[]|array[] $paramDefinitions DEPRECATED! Use @see setParameterDefinitions instead */ - public function setParameters( array $parameters, array $paramDefinitions ) { + public function setParameters( array $parameters, array $paramDefinitions = [] ) { $this->paramDefinitions = ParamDefinition::getCleanDefinitions( $paramDefinitions ); // Loop through all the user provided parameters, and distinguish between those that are allowed and those that are not. @@ -208,10 +203,10 @@ class Processor { /** * @param string $message - * @param mixed $tags string or array + * @param string[] $tags * @param integer $severity */ - private function registerNewError( $message, $tags = [], $severity = ProcessingError::SEVERITY_NORMAL ) { + private function registerNewError( string $message, array $tags = [], int $severity = ProcessingError::SEVERITY_NORMAL ) { $this->registerError( new ProcessingError( $message, @@ -247,7 +242,7 @@ class Processor { foreach ( $this->rawParameters as $paramName => $paramValue ) { $this->registerNewError( $paramName . ' is not a valid parameter', // TODO - $paramName + [ $paramName ] ); } } @@ -262,9 +257,6 @@ class Processor { $this->params = []; } - /** - * @var Param $parameter - */ foreach ( $this->params as $parameter ) { // TODO $processedParam = new ProcessedParam( @@ -289,9 +281,6 @@ class Processor { ); } - /** - * Does the actual parameter processing. - */ private function doParamProcessing() { $this->errors = []; @@ -361,7 +350,7 @@ class Processor { } /** - * @param IParamDefinition[] $paramDefinitions + * @param ParamDefinition[] $paramDefinitions * @param string[] $paramsToHandle * * @return array @@ -376,8 +365,8 @@ class Processor { throw new \UnexpectedValueException( 'Unexpected parameter name "' . $paramName . '"' ); } - if ( !is_object( $paramDefinitions[$paramName] ) || !( $paramDefinitions[$paramName] instanceof IParamDefinition ) ) { - throw new \UnexpectedValueException( 'Parameter "' . $paramName . '" is not a IParamDefinition' ); + if ( !is_object( $paramDefinitions[$paramName] ) || !( $paramDefinitions[$paramName] instanceof ParamDefinition ) ) { + throw new \UnexpectedValueException( 'Parameter "' . $paramName . '" is not a ParamDefinition' ); } // Only include dependencies that are in the list of parameters to handle. @@ -420,34 +409,24 @@ class Processor { } /** - * Returns the parameters. - * - * @since 0.4 * @deprecated since 1.0 - * - * @return IParam[] + * @return Param[] */ public function getParameters(): array { return $this->params; } /** - * Returns a single parameter. - * - * @since 0.4 * @deprecated since 1.0 - * - * @param string $parameterName The name of the parameter to return - * - * @return IParam */ - public function getParameter( string $parameterName ): IParam { + public function getParameter( string $parameterName ): Param { return $this->params[$parameterName]; } /** * Returns an associative array with the parameter names as key and their * corresponding values as value. + * @deprecated since 1.7 - use processParameters() return value */ public function getParameterValues(): array { $parameters = []; @@ -460,6 +439,7 @@ class Processor { } /** + * @deprecated since 1.7 - use processParameters() return value * @return ProcessingError[] */ public function getErrors(): array { @@ -467,6 +447,7 @@ class Processor { } /** + * @deprecated since 1.7 - use processParameters() return value * @return string[] */ public function getErrorMessages(): array { @@ -480,15 +461,14 @@ class Processor { } /** - * Returns if there where any errors during validation. + * @deprecated since 1.7 - use processParameters() return value */ public function hasErrors(): bool { return !empty( $this->errors ); } /** - * Returns false when there are no fatal errors or an ProcessingError when one is found. - * + * @deprecated since 1.7 - use processParameters() return value * @return ProcessingError|boolean false */ public function hasFatalError() { diff --git a/www/wiki/vendor/param-processor/param-processor/src/Settings.php b/www/wiki/vendor/param-processor/param-processor/src/Settings.php index c43a94cd..b38c59e6 100644 --- a/www/wiki/vendor/param-processor/param-processor/src/Settings.php +++ b/www/wiki/vendor/param-processor/param-processor/src/Settings.php @@ -10,7 +10,7 @@ namespace ParamProcessor; * Changing one of these settings can be done by assigning to $egValidatorSettings, * AFTER the inclusion of the extension itself. * - * @since 1.0 + * @deprecated since 1.7 * * @licence GNU GPL v2+ * @author Jeroen De Dauw < jeroendedauw@gmail.com > diff --git a/www/wiki/vendor/param-processor/param-processor/src/TopologicalSort.php b/www/wiki/vendor/param-processor/param-processor/src/TopologicalSort.php index 9833e5a3..ccb79eac 100644 --- a/www/wiki/vendor/param-processor/param-processor/src/TopologicalSort.php +++ b/www/wiki/vendor/param-processor/param-processor/src/TopologicalSort.php @@ -7,19 +7,8 @@ namespace ParamProcessor; * * Based on http://blog.metafoundry.com/2007/09/topological-sort-in-php.html * - * usage: - * $t = new TopologicalSort($dependency_pairs); - * $load_order = $t->doSort(); - * - * where dependency_pairs is in the form: - * $name => (depends on) $value - * - * @author Eddie Haber - * @author Jeroen De Dauw - * + * @deprecated since 1.7 * @codingStandardsIgnoreFile - * - * TODO: handle dependency loops and other crap more nicely */ class TopologicalSort { @@ -155,7 +144,7 @@ class TopologicalSort { } /** - * Node class for Topological Sort Class + * @deprecated since 1.7 */ class TSNode { public $name; diff --git a/www/wiki/vendor/param-processor/param-processor/tests/Integration/Definitions/BoolParamTest.php b/www/wiki/vendor/param-processor/param-processor/tests/Integration/Definitions/BoolParamTest.php new file mode 100644 index 00000000..d8b23f06 --- /dev/null +++ b/www/wiki/vendor/param-processor/param-processor/tests/Integration/Definitions/BoolParamTest.php @@ -0,0 +1,72 @@ + + */ +class BoolParamTest extends ParamDefinitionTest { + + /** + * @see ParamDefinitionTest::getDefinitions + * @return array + */ + public function getDefinitions() { + $params = parent::getDefinitions(); + + return $params; + } + + /** + * @see ParamDefinitionTest::valueProvider + * + * @param boolean $stringlyTyped + * + * @return array + */ + public function valueProvider( $stringlyTyped = true ) { + $values = [ + 'empty' => [ + [ 'yes', true, true ], + [ 'on', true, true ], + [ '1', true, true ], + [ 'no', true, false ], + [ 'off', true, false ], + [ '0', true, false ], + [ 'foobar', false ], + [ '2', false ], + [ [], false ], + [ 42, false ], + ], + 'values' => [], +// 'values' => array( +// array( '1', true, true ), +// array( 'yes', true, true ), +// array( 'no', false ), +// array( 'foobar', false ), +// ), + ]; + + if ( !$stringlyTyped ) { + foreach ( $values as &$set ) { + foreach ( $set as &$value ) { + if ( in_array( $value[0], [ 'yes', 'on', '1', '0', 'off', 'no' ], true ) ) { + $value[0] = in_array( $value[0], [ 'yes', 'on', '1' ], true ); + } + } + } + } + + return $values; + } + + /** + * @see ParamDefinitionTest::getType + * @return string + */ + public function getType() { + return 'boolean'; + } + +} diff --git a/www/wiki/vendor/param-processor/param-processor/tests/Integration/Definitions/FloatParamTest.php b/www/wiki/vendor/param-processor/param-processor/tests/Integration/Definitions/FloatParamTest.php new file mode 100644 index 00000000..742f1342 --- /dev/null +++ b/www/wiki/vendor/param-processor/param-processor/tests/Integration/Definitions/FloatParamTest.php @@ -0,0 +1,72 @@ + + */ +class FloatParamTest extends NumericParamTest { + + /** + * @see ParamDefinitionTest::getDefinitions + * @return array + */ + public function getDefinitions() { + $params = parent::getDefinitions(); + + return $params; + } + + /** + * @see ParamDefinitionTest::valueProvider + * + * @param boolean $stringlyTyped + * + * @return array + */ + public function valueProvider( $stringlyTyped = true ) { + $values = [ + 'empty' => [ + [ 1, true, 1.0 ], + [ 1.0, true, 1.0 ], + [ 1.1, true, 1.1 ], + [ 0.2555, true, 0.2555 ], + [ '1.1.1', false ], + [ 'foobar', false ], + [ [], false ], + [ 'yes', false ], + [ false, false ], + ], + 'values' => [], +// 'values' => array( +// array( 1, true, 1 ), +// array( 'yes', false ), +// array( 'no', false ), +// array( 0.1, true, 0.1 ), +// array( 0.2555, false ), +// ), + ]; + + if ( $stringlyTyped ) { + foreach ( $values as &$set ) { + foreach ( $set as &$value ) { + if ( is_float( $value[0] ) || is_int( $value[0] ) ) { + $value[0] = (string)$value[0]; + } + } + } + } + + return $values; + } + + /** + * @see ParamDefinitionTest::getType + * @return string + */ + public function getType() { + return 'float'; + } + +} diff --git a/www/wiki/vendor/param-processor/param-processor/tests/Integration/Definitions/IntParamTest.php b/www/wiki/vendor/param-processor/param-processor/tests/Integration/Definitions/IntParamTest.php new file mode 100644 index 00000000..4e8872e8 --- /dev/null +++ b/www/wiki/vendor/param-processor/param-processor/tests/Integration/Definitions/IntParamTest.php @@ -0,0 +1,98 @@ + + */ +class IntParamTest extends NumericParamTest { + + /** + * @see ParamDefinitionTest::getDefinitions + * @return array + */ + public function getDefinitions() { + $params = parent::getDefinitions(); + + $params['count'] = [ + 'type' => 'integer', + ]; + + $params['amount'] = [ + 'type' => 'integer', + 'default' => 42, + 'upperbound' => 99, + ]; + + $params['number'] = [ + 'type' => 'integer', + 'upperbound' => 99, + ]; + + return $params; + } + + /** + * @see ParamDefinitionTest::valueProvider + * + * @param boolean $stringlyTyped + * + * @return array + */ + public function valueProvider( $stringlyTyped = true ) { + $values = [ + 'count' => [ + [ 42, true, 42 ], + [ 'foo', false ], + [ 4.2, false ], + [ [ 42 ], false ], + ], + 'amount' => [ + [ 0, true, 0 ], + [ 'foo', false, 42 ], + [ 100, false, 42 ], + [ 4.2, false, 42 ], + ], + 'number' => [ + [ 42, true, 42 ], + [ 'foo', false ], + [ 100, false ], + [ 4.2, false ], + ], + 'empty' => [ + [ 42, true, 42 ], + [ 4.2, false ], + [ [ 42 ], false ], + ], + 'values' => [ + [ 1, true, 1 ], + [ 'yes', false ], + [ true, false ], + [ 0.1, false ], + [ [], false ], + ], + ]; + + if ( $stringlyTyped ) { + foreach ( $values as &$set ) { + foreach ( $set as &$value ) { + if ( is_float( $value[0] ) || is_int( $value[0] ) ) { + $value[0] = (string)$value[0]; + } + } + } + } + + return $values; + } + + /** + * @see ParamDefinitionTest::getType + * @return string + */ + public function getType() { + return 'integer'; + } + +} diff --git a/www/wiki/vendor/param-processor/param-processor/tests/Integration/Definitions/NumericParamTest.php b/www/wiki/vendor/param-processor/param-processor/tests/Integration/Definitions/NumericParamTest.php new file mode 100644 index 00000000..2af9a2b0 --- /dev/null +++ b/www/wiki/vendor/param-processor/param-processor/tests/Integration/Definitions/NumericParamTest.php @@ -0,0 +1,69 @@ + + */ +abstract class NumericParamTest extends ParamDefinitionTest { + + public function lowerBoundProvider() { + return [ + [ 42, 42, true ], + [ 42, 41, false ], + [ 42, 43, true ], + [ false, 43, true ], + [ false, 0, true ], + [ false, -100, true ], + [ -100, -100, true ], + [ -99, -100, false ], + [ -101, -100, true ], + ]; + } + + /** + * @dataProvider lowerBoundProvider + */ + public function testSetLowerBound( $bound, $testValue, $validity ) { + $definition = $this->getEmptyInstance(); + $definition->setArrayValues( [ 'lowerbound' => $bound ] ); + + $this->validate( $definition, (string)$testValue, $validity ); + + $options = new Options(); + $options->setRawStringInputs( false ); + $this->validate( $definition, $testValue, $validity, $options ); + } + + public function upperBoundProvider() { + return [ + [ 42, 42, true ], + [ 42, 41, true ], + [ 42, 43, false ], + [ false, 43, true ], + [ false, 0, true ], + [ false, -100, true ], + [ -100, -100, true ], + [ -99, -100, true ], + [ -101, -100, false ], + ]; + } + + /** + * @dataProvider upperBoundProvider + */ + public function testSetUpperBound( $bound, $testValue, $validity ) { + $definition = $this->getEmptyInstance(); + $definition->setArrayValues( [ 'upperbound' => $bound ] ); + + $this->validate( $definition, (string)$testValue, $validity ); + + $options = new Options(); + $options->setRawStringInputs( false ); + $this->validate( $definition, $testValue, $validity, $options ); + } + +} diff --git a/www/wiki/vendor/param-processor/param-processor/tests/Integration/Definitions/ParamDefinitionTest.php b/www/wiki/vendor/param-processor/param-processor/tests/Integration/Definitions/ParamDefinitionTest.php new file mode 100644 index 00000000..64ac9004 --- /dev/null +++ b/www/wiki/vendor/param-processor/param-processor/tests/Integration/Definitions/ParamDefinitionTest.php @@ -0,0 +1,166 @@ + + */ +abstract class ParamDefinitionTest extends TestCase { + + /** + * Returns a list of arrays that hold values to test handling of. + * Each array holds the following unnamed elements: + * - value (mixed, required) + * - valid (boolean, required) + * - expected (mixed, optional) + * + * ie array( '42', true, 42 ) + * + * @since 0.1 + * + * @param boolean $stringlyTyped + * + * @return array + */ + public abstract function valueProvider( $stringlyTyped = true ); + + public abstract function getType(); + + public function getDefinitions() { + $params = []; + + $params['empty'] = []; + + $params['values'] = [ + 'values' => [ 'foo', '1', '0.1', 'yes', 1, 0.1 ] + ]; + + return $params; + } + + public function definitionProvider() { + $definitions = $this->getDefinitions(); + + foreach ( $definitions as &$definition ) { + $definition['type'] = $this->getType(); + } + + return $definitions; + } + + public function getEmptyInstance() { + return ParamDefinitionFactory::singleton()->newDefinitionFromArray( [ + 'name' => 'empty', + 'message' => 'test-empty', + 'type' => $this->getType(), + ] ); + } + + public function instanceProvider() { + $definitions = []; + + foreach ( $this->definitionProvider() as $name => $definition ) { + if ( !array_key_exists( 'message', $definition ) ) { + $definition['message'] = 'test-' . $name; + } + + $definition['name'] = $name; + $definitions[] = [ ParamDefinitionFactory::singleton()->newDefinitionFromArray( $definition ) ]; + } + + return $definitions; + } + + /** + * @dataProvider instanceProvider + */ + public function testGetType( IParamDefinition $definition ) { + $this->assertEquals( $this->getType(), $definition->getType() ); + } + + /** + * @dataProvider instanceProvider + */ + public function testValidate( IParamDefinition $definition ) { + foreach ( [ true, false ] as $stringlyTyped ) { + $values = $this->valueProvider( $stringlyTyped ); + $options = new Options(); + $options->setRawStringInputs( $stringlyTyped ); + + foreach ( $values[$definition->getName()] as $data ) { + list( $input, $valid, ) = $data; + + $param = new Param( $definition ); + $param->setUserValue( $definition->getName(), $input, $options ); + $definitions = []; + $param->process( $definitions, [], $options ); + + $this->assertEquals( + $valid, + $param->getErrors() === [], + 'The validation process should ' . ( $valid ? '' : 'not ' ) . 'pass' + ); + } + } + + $this->assertTrue( true ); + } + + /** + * @dataProvider instanceProvider + */ + public function testFormat( IParamDefinition $sourceDefinition ) { + $values = $this->valueProvider(); + $options = new Options(); + + foreach ( $values[$sourceDefinition->getName()] as $data ) { + $definition = clone $sourceDefinition; + + list( $input, $valid, ) = $data; + + $param = new Param( $definition ); + $param->setUserValue( $definition->getName(), $input, $options ); + + if ( $valid && array_key_exists( 2, $data ) ) { + $defs = []; + $param->process( $defs, [], $options ); + + $this->assertEquals( + $data[2], + $param->getValue() + ); + } + } + + $this->assertTrue( true ); + } + + protected function validate( ParamDefinition $definition, $testValue, $validity, Options $options = null ) { + $def = clone $definition; + $options = $options === null ? new Options() : $options; + + $param = new Param( $def ); + $param->setUserValue( $def->getName(), $testValue, $options ); + + $defs = []; + $param->process( $defs, [], $options ); + + $this->assertEquals( $validity, $param->getErrors() === [] ); + } + + public function testConstructingWithoutMessageLeadsToDefaultMessage() { + $this->assertSame( + 'validator-message-nodesc', + ( new ParamDefinition( 'type', 'name' ) )->getMessage() + ); + } + +} \ No newline at end of file diff --git a/www/wiki/vendor/param-processor/param-processor/tests/Integration/Definitions/StringParamTest.php b/www/wiki/vendor/param-processor/param-processor/tests/Integration/Definitions/StringParamTest.php new file mode 100644 index 00000000..2fb31db1 --- /dev/null +++ b/www/wiki/vendor/param-processor/param-processor/tests/Integration/Definitions/StringParamTest.php @@ -0,0 +1,45 @@ + + */ +class StringParamTest extends ParamDefinitionTest { + + /** + * @see ParamDefinitionTest::valueProvider + * + * @param boolean $stringlyTyped + * + * @return array + */ + public function valueProvider( $stringlyTyped = true ) { + return [ + 'empty' => [ + [ 'ohi there', true, 'ohi there' ], + [ 4.2, false ], + [ [ 42 ], false ], + ], + 'values' => [ + [ 'foo', true, 'foo' ], + [ '1', true, '1' ], + [ 'yes', true, 'yes' ], + [ 'bar', false ], + [ true, false ], + [ 0.1, false ], + [ [], false ], + ], + ]; + } + + /** + * @see ParamDefinitionTest::getType + * @return string + */ + public function getType() { + return 'string'; + } + +} diff --git a/www/wiki/vendor/param-processor/param-processor/tests/Integration/DimensionTypeTest.php b/www/wiki/vendor/param-processor/param-processor/tests/Integration/DimensionTypeTest.php new file mode 100644 index 00000000..9fdac669 --- /dev/null +++ b/www/wiki/vendor/param-processor/param-processor/tests/Integration/DimensionTypeTest.php @@ -0,0 +1,208 @@ +process( + [ + 'width' => [ + 'type' => ParameterTypes::DIMENSION, + 'message' => 'test-message' + ] + ], + [ + 'width' => $input, + ] + )->getParameterArray(); + + $this->assertSame( $expected, $parameters['width'] ); + } + + public function widthProvider() { + yield [ '10', '10px' ]; + yield [ '10px', '10px' ]; + yield [ '10%', '10%' ]; + yield [ '10em', '10em' ]; + yield [ '10ex', '10ex' ]; + yield [ 'auto', 'auto' ]; + yield [ ' 10 ', '10px' ]; + yield [ ' 1 ', '1px' ]; + yield [ '1 px', '1px' ]; + yield [ '1 ex', '1ex' ]; + // TODO: make sure unit is after the value + // TODO: make sure only the unit is present + } + + private function process( array $definitionArrays, array $userInput ): ProcessingResult { + $processor = Processor::newDefault(); + + $processor->setParameters( $userInput ); + $processor->setParameterDefinitions( + ParamDefinitionFactory::newDefault()->newDefinitionsFromArrays( $definitionArrays ) + ); + + return $processor->processParameters(); + } + + /** + * @dataProvider heightProvider + */ + public function testHeight( string $input, string $expected ) { + $parameters = $this->process( + [ + 'height' => [ + 'type' => ParameterTypes::DIMENSION, + 'message' => 'test-message' + ] + ], + [ + 'height' => $input, + ] + )->getParameterArray(); + + $this->assertSame( $expected, $parameters['height'] ); + } + + public function heightProvider() { + yield [ '10', '10px' ]; + yield [ '10px', '10px' ]; + yield [ '10em', '10em' ]; + yield [ '10ex', '10ex' ]; + } + + public function testAlternateDefaultUnit() { + $parameters = $this->process( + [ + 'height' => [ + 'type' => ParameterTypes::DIMENSION, + 'defaultunit' => '%', + 'message' => 'test-message' + ] + ], + [ + 'height' => '2.5', + ] + )->getParameterArray(); + + $this->assertSame( '2.5%', $parameters['height'] ); + } + + /** + * @dataProvider invalidDimensionProvider + */ + public function testInvalidInputsDefault( string $invalidDimension ) { + $parameters = $this->process( + [ + 'height' => [ + 'type' => ParameterTypes::DIMENSION, + 'default' => '42%', + 'lowerbound' => 20, + 'upperbound' => 80, + 'minpercentage' => 30, + 'maxpercentage' => 70, + 'message' => 'test-message' + ] + ], + [ + 'height' => $invalidDimension, + ] + )->getParameterArray(); + + $this->assertSame( '42%', $parameters['height'] ); + } + + public function invalidDimensionProvider() { + yield [ 'invalid' ]; + yield [ 'px' ]; + yield [ '19' ]; + yield [ '81' ]; + yield [ '29%' ]; + yield [ '71%' ]; + yield 'auto not allowed' => [ 'auto' ]; + yield 'unit not allowed' => [ '1 wtf' ]; + } + + /** + * @dataProvider validBoundsInput + */ + public function testValidInputWithBounds( string $valid ) { + $parameters = $this->process( + [ + 'height' => [ + 'type' => ParameterTypes::DIMENSION, + 'default' => '42%', + 'lowerbound' => 20, + 'upperbound' => 80, + 'minpercentage' => 30, + 'maxpercentage' => 70, + 'message' => 'test-message' + ] + ], + [ + 'height' => $valid, + ] + )->getParameterArray(); + + $this->assertSame( $valid, $parameters['height'] ); + } + + public function validBoundsInput() { + yield [ '20px' ]; + yield [ '21px' ]; + yield [ '80px' ]; + yield [ '79px' ]; + // FIXME +// yield [ '30%' ]; +// yield [ '31%' ]; +// yield [ '70%' ]; +// yield [ '69%' ]; + } + + public function testAllowAuto() { + $parameters = $this->process( + [ + 'height' => [ + 'type' => ParameterTypes::DIMENSION, + 'allowauto' => true, + 'message' => 'test-message' + ] + ], + [ + 'height' => 'auto', + ] + )->getParameterArray(); + + $this->assertSame( 'auto', $parameters['height'] ); + } + + public function testCanUseSpecialUnit() { + $parameters = $this->process( + [ + 'height' => [ + 'type' => ParameterTypes::DIMENSION, + 'units' => [ 'wtf' ], + 'message' => 'test-message' + ] + ], + [ + 'height' => '4.2 wtf', + ] + )->getParameterArray(); + + $this->assertSame( '4.2wtf', $parameters['height'] ); + } + +} diff --git a/www/wiki/vendor/param-processor/param-processor/tests/Integration/ProcessorTest.php b/www/wiki/vendor/param-processor/param-processor/tests/Integration/ProcessorTest.php new file mode 100644 index 00000000..cf1acde5 --- /dev/null +++ b/www/wiki/vendor/param-processor/param-processor/tests/Integration/ProcessorTest.php @@ -0,0 +1,486 @@ + + */ +class ProcessorTest extends TestCase { + + public function newFromOptionsProvider() { + $options = []; + + $option = new Options(); + + $options[] = clone $option; + + $option->setName( 'foobar' ); + $option->setLowercaseNames( false ); + + $options[] = clone $option; + + return $this->arrayWrap( $options ); + } + + private function arrayWrap( array $elements ) { + return array_map( + function( $element ) { + return [ $element ]; + }, + $elements + ); + } + + public function testNewFromOptions() { + $this->assertEquals( new Options(), Processor::newFromOptions( new Options() )->getOptions() ); + } + + /** + * Simple parameter definitions and values that should all pass. + * + * @return array + */ + private function getSimpleParams() { + $params = [ + 'awesome' => 'yes', + 'Howmuch ' => '9001', + 'FLOAT' => '4.2', + ' page' => 'Ohi there!', + ' text ' => 'foo bar baz o_O', + ]; + + $definitions = [ + 'awesome' => [ + 'type' => 'boolean', + ], + 'howmuch' => [ + 'type' => 'integer', + ], + 'float' => [ + 'type' => 'float', + ], + 'page' => [ + 'type' => 'string', + 'hastoexist' => false, + ], + 'text' => [], + ]; + + $options = new Options(); + + $expected = [ + 'awesome' => true, + 'howmuch' => 9001, + 'float' => 4.2, + 'page' => 'Ohi there!', + 'text' => 'foo bar baz o_O', + ]; + + return [ $params, $definitions, $options, $expected ]; + } + + /** + * Simple parameter definitions with defaults and values + * that are invalid or missing and therefore default. + * + * @return array + */ + private function getDefaultingParams() { + $params = [ + 'awesome' => 'omg!', + 'howmuch' => 'omg!', + 'float' => 'omg!', + 'page' => 42, + 'whot?' => 'O_o', + 'integerr' => ' 9001 ', + ]; + + $definitions = [ + 'awesome' => [ + 'type' => 'boolean', + 'default' => true, + ], + 'howmuch' => [ + 'type' => 'integer', + 'default' => 9001, + ], + 'float' => [ + 'type' => 'float', + 'default' => 4.2, + ], + 'page' => [ + 'type' => 'string', + 'hastoexist' => false, + 'default' => 'Ohi there!', + ], + 'text' => [ + 'default' => 'foo bar baz o_O', + ], + 'integerr' => [ + 'type' => 'integer', + 'default' => 42, + ], + ]; + + $options = new Options(); + $options->setTrimValues( false ); + + $expected = [ + 'awesome' => true, + 'howmuch' => 9001, + 'float' => 4.2, + 'page' => 'Ohi there!', + 'text' => 'foo bar baz o_O', + 'integerr' => 42, + ]; + + return [ $params, $definitions, $options, $expected ]; + } + + /** + * Values and definitions in-system parameter handling. + * Options set to expect non-raw values. + * + * @return array + */ + private function getTypedParams() { + $params = [ + 'awesome' => true, + 'howmuch' => '42', + 'float' => 4.2, + 'page' => 'Ohi there!', + 'Text' => 'foo bar baz o_O', + 'text1 ' => 'foo bar baz o_O', + ' text2' => 'foo bar baz o_O', + ]; + + $definitions = [ + 'awesome' => [ + 'type' => 'boolean', + ], + 'howmuch' => [ + 'type' => 'integer', + 'default' => 9001, + ], + 'float' => [ + 'type' => 'float', + 'lowerbound' => 9001, + 'default' => 9000.1 + ], + 'page' => [ + 'type' => 'string', + 'hastoexist' => false, + ], + 'text' => [ + 'default' => 'some text', + ], + 'text1' => [ + 'default' => 'some text', + ], + 'text2' => [ + 'default' => 'some text', + ], + ]; + + $options = new Options(); + $options->setRawStringInputs( false ); + $options->setLowercaseNames( false ); + $options->setTrimNames( false ); + + $expected = [ + 'awesome' => true, + 'howmuch' => 9001, + 'float' => 9000.1, + 'page' => 'Ohi there!', + 'text' => 'some text', + 'text1' => 'some text', + 'text2' => 'some text', + ]; + + return [ $params, $definitions, $options, $expected ]; + } + + /** + * Values with capitalization and preceding/tailing spaces to test + * of the clean options work. + * + * @return array + */ + private function getUncleanParams() { + $params = [ + 'awesome' => ' yes ', + 'text' => ' FOO bar ', + 'integerr' => ' 9001 ', + ]; + + $definitions = [ + 'awesome' => [ + 'type' => 'boolean', + ], + 'text' => [ + 'default' => 'bar', + ], + 'integerr' => [ + 'type' => 'integer', + 'default' => 42, + ], + ]; + + $options = new Options(); + $options->setLowercaseValues( true ); + $options->setTrimValues( true ); + + $expected = [ + 'awesome' => true, + 'text' => 'foo bar', + 'integerr' => 9001, + ]; + + return [ $params, $definitions, $options, $expected ]; + } + + /** + * List parameters to test if list handling works correctly. + * + * @return array + */ + private function getListParams() { + $params = [ + 'awesome' => ' yes, no, on, off ', + 'float' => ' 9001 ; 42 ; 4.2;0', + ]; + + $definitions = [ + 'awesome' => [ + 'type' => 'boolean', + 'islist' => true, + ], + 'text' => [ + 'default' => [ 'bar' ], + 'islist' => true, + ], + 'float' => [ + 'type' => 'float', + 'islist' => true, + 'delimiter' => ';' + ], + ]; + + $options = new Options(); + $options->setLowercaseValues( true ); + $options->setTrimValues( true ); + + $expected = [ + 'awesome' => [ true, false, true, false ], + 'text' => [ 'bar' ], + 'float' => [ 9001.0, 42.0, 4.2, 0.0 ], + ]; + + return [ $params, $definitions, $options, $expected ]; + } + + public function parameterProvider() { + // $params, $definitions [, $options] + $argLists = []; + + $argLists[] = $this->getSimpleParams(); + + $argLists[] = $this->getDefaultingParams(); + + $argLists[] = $this->getTypedParams(); + + $argLists[] = $this->getUncleanParams(); + + $argLists[] = $this->getListParams(); + + foreach ( $argLists as &$argList ) { + foreach ( $argList[1] as $key => &$definition ) { + $definition['message'] = 'test-' . $key; + } + + if ( !array_key_exists( 2, $argList ) ) { + $argList[2] = new Options(); + } + } + + return $argLists; + } + + /** + * @dataProvider parameterProvider + */ + public function testSetParameters( array $params, array $definitions, Options $options ) { + $validator = Processor::newFromOptions( $options ); + + $validator->setParameters( $params, $definitions ); + + $this->assertTrue( true ); // TODO + } + + /** + * @dataProvider parameterProvider + */ + public function testValidateParameters( array $params, array $definitions, Options $options, array $expected = [] ) { + $validator = Processor::newFromOptions( $options ); + + $validator->setParameters( $params, $definitions ); + + $processingResult = $validator->processParameters(); + + $actualValues = []; + + foreach ( $processingResult->getParameters() as $param ) { + $actualValues[$param->getName()] = $param->getValue(); + } + + $this->assertEquals( $expected, $actualValues ); + + + } + + public function testProcessParametersOnEmptyOptions() { + $processor = Processor::newDefault(); + + $this->assertInstanceOf( + ProcessingResult::class, + $processor->processParameters() + ); + } + + public function testErrorsCanBeRetrievedAfterProcessing() { + $processor = Processor::newDefault(); + + $this->processWithOneError( $processor ); + + $this->assertCount( 1, $processor->getErrors() ); + } + + private function processWithOneError( Processor $processor ) { + $processor->setParameters( + [], + [ + 'awesome' => [ + 'type' => 'boolean', + 'message' => 'test-awesome' + ], + ] + ); + + // There should be a single "missing required parameter" error. + $processor->processParameters(); + } + + public function testErrorsAreClearedBetweenProcessingRuns() { + $processor = Processor::newDefault(); + + $this->processWithOneError( $processor ); + $processor->setParameters( [], [] ); + $processor->processParameters(); + + $this->assertEmpty( $processor->getErrors() ); + } + + public function testInvalidListElementsAreOmitted() { + $processor = Processor::newDefault(); + + $processor->setFunctionParams( + [ + 'some-list=1,2,3, ,4,' + ], + [ + 'some-list' => [ + 'type' => 'integer', + 'message' => 'test', + 'islist' => true + ], + ] + ); + + $this->assertSame( + [ 1, 2, 3, 4 ], + $processor->processParameters()->getParameters()['some-list']->getValue() + ); + } + + public function testListParametersAreNotDefaultedWhenSomeElementsAreInvalid() { + $processor = Processor::newDefault(); + + $processor->setFunctionParams( + [ + 'some-list=1,nan' + ], + [ + 'some-list' => [ + 'type' => 'integer', + 'message' => 'test', + 'islist' => true, + 'default' => [] + ], + ] + ); + + $this->assertSame( + [ 1 ], + $processor->processParameters()->getParameters()['some-list']->getValue() + ); + } + + public function testListParametersAreDefaultedWhenAllElementsAreInvalid() { + $processor = Processor::newDefault(); + + $processor->setFunctionParams( + [ + 'some-list=such,nan' + ], + [ + 'some-list' => [ + 'type' => 'integer', + 'message' => 'test', + 'islist' => true, + 'default' => [ 42 ] + ], + ] + ); + + $this->assertSame( + [ 42 ], + $processor->processParameters()->getParameters()['some-list']->getValue() + ); + } + + public function testSetParameterDefinitions() { + $processor = Processor::newDefault(); + + $processor->setFunctionParams( [ 'some-list=42,23,9001' ] ); + + $processor->setParameterDefinitions( + [ + ( ParamDefinitionFactory::newDefault() )->newDefinitionFromArray( + [ + 'name' => 'some-list', + 'type' => 'integer', + 'message' => 'test', + 'islist' => true + ] + ) + ] + ); + + $this->assertSame( + [ 42, 23, 9001 ], + $processor->processParameters()->getParameters()['some-list']->getValue() + ); + } + +} diff --git a/www/wiki/vendor/param-processor/param-processor/tests/Unit/OptionsTest.php b/www/wiki/vendor/param-processor/param-processor/tests/Unit/OptionsTest.php new file mode 100644 index 00000000..76b826ca --- /dev/null +++ b/www/wiki/vendor/param-processor/param-processor/tests/Unit/OptionsTest.php @@ -0,0 +1,48 @@ + + */ +class OptionsTest extends TestCase { + + public function testBooleanSettersAndGetters() { + $methods = [ + 'setUnknownInvalid' => 'unknownIsInvalid', + 'setLowercaseNames' => 'lowercaseNames', + 'setRawStringInputs' => 'isStringlyTyped', + 'setTrimNames' => 'trimNames', + 'setTrimValues' => 'trimValues', + 'setLowercaseValues' => 'lowercaseValues', + ]; + + foreach ( $methods as $setter => $getter ) { + $options = new Options(); + + foreach ( [ false, true, false ] as $boolean ) { + call_user_func_array( [ $options, $setter ], [ $boolean ] ); + + $this->assertEquals( $boolean, call_user_func( [ $options, $getter ] ) ); + } + } + } + + public function testSetAndGetName() { + $options = new Options(); + + foreach ( [ 'foo', 'bar baz' ] as $name ) { + $options->setName( $name ); + $this->assertEquals( $name, $options->getName() ); + } + } + +} diff --git a/www/wiki/vendor/param-processor/param-processor/tests/Unit/PackagePrivate/DimensionParserTest.php b/www/wiki/vendor/param-processor/param-processor/tests/Unit/PackagePrivate/DimensionParserTest.php new file mode 100644 index 00000000..e82bb8b7 --- /dev/null +++ b/www/wiki/vendor/param-processor/param-processor/tests/Unit/PackagePrivate/DimensionParserTest.php @@ -0,0 +1,51 @@ +expectException( ParseException::class ); + $this->parse( 32202 ); + } + + private function parse( $input ) { + return ( new DimensionParser() )->parse( $input ); + } + + /** + * @dataProvider validDimensionProvider + */ + public function testParsingValidInputs( string $input, string $expected ) { + $this->assertSame( $expected, $this->parse( $input ) ); + } + + public function validDimensionProvider() { + yield [ '10px', '10px' ]; + yield [ '10ex', '10ex' ]; + yield [ '10em', '10em' ]; + yield [ '2.5px', '2.5px' ]; + + yield [ '10 px', '10px' ]; + yield [ '10 px', '10px' ]; + yield [ ' 10 px ', '10px' ]; + + yield [ '10', '10px' ]; + yield [ '2.5', '2.5px' ]; + } + + public function testAlternateDefaultUnit() { + $this->assertSame( + '1%', + ( new DimensionParser( new ParserOptions( [ DimensionParser::DEFAULT_UNIT => '%' ] ) ) )->parse( '1' ) + ); + } + +} diff --git a/www/wiki/vendor/param-processor/param-processor/tests/Unit/ParamAliasTest.php b/www/wiki/vendor/param-processor/param-processor/tests/Unit/ParamAliasTest.php new file mode 100644 index 00000000..bd57dbd3 --- /dev/null +++ b/www/wiki/vendor/param-processor/param-processor/tests/Unit/ParamAliasTest.php @@ -0,0 +1,21 @@ + + */ +class ParamAliasTest extends TestCase { + + public function testListParamProcessingWithEmptyListAsDefault() { + $definition = new ParamDefinition( 'string', 'something' ); + $param = new Param( $definition ); + $this->assertTrue( $param->isRequired() ); + } + +} diff --git a/www/wiki/vendor/param-processor/param-processor/tests/Unit/ParamDefinitionFactoryTest.php b/www/wiki/vendor/param-processor/param-processor/tests/Unit/ParamDefinitionFactoryTest.php new file mode 100644 index 00000000..1e727713 --- /dev/null +++ b/www/wiki/vendor/param-processor/param-processor/tests/Unit/ParamDefinitionFactoryTest.php @@ -0,0 +1,140 @@ + + */ +class ParamDefinitionFactoryTest extends TestCase { + + public function testNewDefinitionFromArray() { + $definition = ParamDefinitionFactory::newDefault()->newDefinitionFromArray( + [ + 'name' => 'some-list', + 'type' => 'integer', + 'message' => 'test-message', + 'islist' => true + ] + ); + + $this->assertSame( 'some-list', $definition->getName() ); + $this->assertSame( 'integer', $definition->getType() ); + $this->assertSame( 'test-message', $definition->getMessage() ); + $this->assertTrue( $definition->isList() ); + $this->assertTrue( $definition->isRequired() ); + } + + public function testNewDefinition() { + $definition = ParamDefinitionFactory::newDefault()->newDefinition( + 'integer', + 'some-list', + null, + 'test-message', + true + ); + + $this->assertSame( 'some-list', $definition->getName() ); + $this->assertSame( 'integer', $definition->getType() ); + $this->assertSame( 'test-message', $definition->getMessage() ); + $this->assertTrue( $definition->isList() ); + $this->assertTrue( $definition->isRequired() ); + } + + public function testNewDefinitionFromArray_typeDefaultsToString() { + $this->assertSame( 'string', $this->newBasicParamFromArray()->getType() ); + } + + private function newBasicParamFromArray(): ParamDefinition { + return ParamDefinitionFactory::newDefault()->newDefinitionFromArray( + [ + 'name' => 'irrelevant', + 'message' => 'irrelevant' + ] + ); + } + + public function testNewDefinitionFromArray_isListDefaultsToFalse() { + $this->assertFalse( $this->newBasicParamFromArray()->isList() ); + } + + public function testNewDefinitionFromArray_isRequiredDefaultsToTrue() { + $this->assertTrue( $this->newBasicParamFromArray()->isRequired() ); + } + + public function testNewDefinitionFromArray_optionsAreSet() { + $arrayDefinition = [ + 'name' => 'some-list', + 'type' => 'integer', + 'message' => 'test-message', + 'islist' => true + ]; + + $definition = ParamDefinitionFactory::newDefault()->newDefinitionFromArray( $arrayDefinition ); + + $this->assertSame( $arrayDefinition, $definition->getOptions() ); + } + + public function testNewDefinitionsFromArrays_preservesDefinitionInstances() { + $this->assertEquals( + [ + 'kittens' => new ParamDefinition( ParameterTypes::STRING, 'kittens' ), + 'cats' => new ParamDefinition( ParameterTypes::INTEGER, 'cats' ), + ], + ParamDefinitionFactory::newDefault()->newDefinitionsFromArrays( + [ + new ParamDefinition( ParameterTypes::STRING, 'kittens' ), + new ParamDefinition( ParameterTypes::INTEGER, 'cats' ), + ] + ) + ); + } + + public function testNewDefinitionsFromArrays_handlesNamesAsKeys() { + $definitions = ParamDefinitionFactory::newDefault()->newDefinitionsFromArrays( + [ + 'kittens' => [ + 'type' => ParameterTypes::STRING, + 'message' => 'test-message', + ] + ] + ); + + $this->assertSame( 'kittens', $definitions['kittens']->getName() ); + } + + public function testNewDefinitionsFromArrays_nameFieldTakesPriorityOverKey() { + $definitions = ParamDefinitionFactory::newDefault()->newDefinitionsFromArrays( + [ + 'cats' => [ + 'name' => 'kittens', + 'type' => ParameterTypes::STRING, + 'message' => 'test-message', + ] + ] + ); + + $this->assertSame( 'kittens', $definitions['kittens']->getName() ); + } + + public function testNewDefinitionsFromArrays_missingNameLeadsToException() { + $this->expectException( \Exception::class ); + + ParamDefinitionFactory::newDefault()->newDefinitionsFromArrays( + [ + [ + 'type' => ParameterTypes::STRING, + 'message' => 'test-message', + ] + ] + ); + } + +} diff --git a/www/wiki/vendor/param-processor/param-processor/tests/Unit/ParamTest.php b/www/wiki/vendor/param-processor/param-processor/tests/Unit/ParamTest.php new file mode 100644 index 00000000..4510409d --- /dev/null +++ b/www/wiki/vendor/param-processor/param-processor/tests/Unit/ParamTest.php @@ -0,0 +1,28 @@ + + */ +class ParamTest extends TestCase { + + public function testListParamProcessingWithEmptyListAsDefault() { + $definition = new ParamDefinition( 'string', 'something', [] ); + $definitions = [ $definition ]; + + $param = new Param( $definition ); + $param->process( $definitions, [], new Options() ); + + $this->assertSame( [], $param->getValue() ); + } + +} diff --git a/www/wiki/vendor/param-processor/param-processor/tests/Unit/ParameterTypesTest.php b/www/wiki/vendor/param-processor/param-processor/tests/Unit/ParameterTypesTest.php new file mode 100644 index 00000000..7f4970a6 --- /dev/null +++ b/www/wiki/vendor/param-processor/param-processor/tests/Unit/ParameterTypesTest.php @@ -0,0 +1,75 @@ +addType( + 'kitten', + [] + ); + + $type = $types->getType( 'kitten' ); + + $this->assertSame( + NullParser::class, + $type->getStringParserClass() + ); + + $this->assertSame( + NullParser::class, + $type->getTypedParserClass() + ); + + $this->assertSame( + NullValidator::class, + $type->getValidatorClass() + ); + + $this->assertNull( + $type->getValidationCallback() + ); + } + + public function testRegisterType_parametersAreUsed() { + $types = new ParameterTypes(); + + $types->addType( + 'kitten', + [ + 'string-parser' => 'KittenParser', + 'validation-callback' => 'is_int', + 'validator' => 'KittenValidator', + ] + ); + + $type = $types->getType( 'kitten' ); + + $this->assertSame( + 'KittenParser', + $type->getStringParserClass() + ); + + $this->assertSame( + 'KittenValidator', + $type->getValidatorClass() + ); + + $this->assertSame( + 'is_int', + $type->getValidationCallback() + ); + } + +} diff --git a/www/wiki/vendor/param-processor/param-processor/tests/Unit/ProcessingResultTest.php b/www/wiki/vendor/param-processor/param-processor/tests/Unit/ProcessingResultTest.php new file mode 100644 index 00000000..e90209a4 --- /dev/null +++ b/www/wiki/vendor/param-processor/param-processor/tests/Unit/ProcessingResultTest.php @@ -0,0 +1,89 @@ + + */ +class ProcessingResultTest extends TestCase { + + public function testGetParameters() { + $processedParams = [ + $this->createMock( ProcessedParam::class ) + ]; + + $result = new ProcessingResult( $processedParams ); + + $this->assertEquals( $processedParams, $result->getParameters() ); + } + + public function testGetErrors() { + $errors = [ + $this->createMock( ProcessingError::class ) + ]; + + $result = new ProcessingResult( [], $errors ); + + $this->assertEquals( $errors, $result->getErrors() ); + } + + public function testGivenNoErrors_HasNoFatal() { + $this->assertNoFatalForErrors( [] ); + } + + private function assertNoFatalForErrors( array $errors ) { + $result = new ProcessingResult( [], $errors ); + + $this->assertFalse( $result->hasFatal() ); + } + + public function testGivenNonfatalErrors_HasNoFatal() { + $this->assertNoFatalForErrors( [ + new ProcessingError( '', ProcessingError::SEVERITY_HIGH ), + new ProcessingError( '', ProcessingError::SEVERITY_LOW ), + new ProcessingError( '', ProcessingError::SEVERITY_MINOR ), + new ProcessingError( '', ProcessingError::SEVERITY_NORMAL ), + ] ); + } + + public function testGivenFatalError_HasFatal() { + $result = new ProcessingResult( [], [ + new ProcessingError( '', ProcessingError::SEVERITY_HIGH ), + new ProcessingError( '', ProcessingError::SEVERITY_LOW ), + new ProcessingError( '', ProcessingError::SEVERITY_FATAL ), + new ProcessingError( '', ProcessingError::SEVERITY_MINOR ), + new ProcessingError( '', ProcessingError::SEVERITY_NORMAL ), + ] ); + + $this->assertTrue( $result->hasFatal() ); + } + + public function testGetParameterArrayWithNoParameters() { + $this->assertSame( + [], + ( new ProcessingResult( [] ) )->getParameterArray() + ); + } + + public function testGetParameterArray() { + $this->assertSame( + [ + 'first' => 42, + 'second' => 23, + ], + ( new ProcessingResult( [ + new ProcessedParam( 'first', 42, false ), + new ProcessedParam( 'second', 23, true ) + ] ) )->getParameterArray() + ); + } + +} \ No newline at end of file diff --git a/www/wiki/vendor/param-processor/param-processor/tests/Unit/SettingsTest.php b/www/wiki/vendor/param-processor/param-processor/tests/Unit/SettingsTest.php new file mode 100644 index 00000000..eec970d9 --- /dev/null +++ b/www/wiki/vendor/param-processor/param-processor/tests/Unit/SettingsTest.php @@ -0,0 +1,42 @@ + + */ +class SettingsTest extends TestCase { + + public function constructorProvider() { + $settingArrays = [ + [ [] ], + [ [ 'foo' => 'bar' ] ], + [ [ 'foo' => 'bar', 'baz' => 'BAH' ] ], + [ [ '~[,,_,,]:3' => [ 9001, 4.2 ] ] ], + ]; + + return $settingArrays; + } + + /** + * @dataProvider constructorProvider + * + * @param array $settings + */ + public function testConstructor( array $settings ) { + $settingsObject = new Settings( $settings ); + + foreach ( $settings as $name => $value ) { + $this->assertEquals( $value, $settingsObject->get( $name ) ); + } + + $this->assertTrue( true ); + } + +} diff --git a/www/wiki/vendor/param-processor/param-processor/tests/phpunit/Definitions/BoolParamTest.php b/www/wiki/vendor/param-processor/param-processor/tests/phpunit/Definitions/BoolParamTest.php deleted file mode 100644 index 875a05fb..00000000 --- a/www/wiki/vendor/param-processor/param-processor/tests/phpunit/Definitions/BoolParamTest.php +++ /dev/null @@ -1,72 +0,0 @@ - - */ -class BoolParamTest extends ParamDefinitionTest { - - /** - * @see ParamDefinitionTest::getDefinitions - * @return array - */ - public function getDefinitions() { - $params = parent::getDefinitions(); - - return $params; - } - - /** - * @see ParamDefinitionTest::valueProvider - * - * @param boolean $stringlyTyped - * - * @return array - */ - public function valueProvider( $stringlyTyped = true ) { - $values = [ - 'empty' => [ - [ 'yes', true, true ], - [ 'on', true, true ], - [ '1', true, true ], - [ 'no', true, false ], - [ 'off', true, false ], - [ '0', true, false ], - [ 'foobar', false ], - [ '2', false ], - [ [], false ], - [ 42, false ], - ], - 'values' => [], -// 'values' => array( -// array( '1', true, true ), -// array( 'yes', true, true ), -// array( 'no', false ), -// array( 'foobar', false ), -// ), - ]; - - if ( !$stringlyTyped ) { - foreach ( $values as &$set ) { - foreach ( $set as &$value ) { - if ( in_array( $value[0], [ 'yes', 'on', '1', '0', 'off', 'no' ], true ) ) { - $value[0] = in_array( $value[0], [ 'yes', 'on', '1' ], true ); - } - } - } - } - - return $values; - } - - /** - * @see ParamDefinitionTest::getType - * @return string - */ - public function getType() { - return 'boolean'; - } - -} diff --git a/www/wiki/vendor/param-processor/param-processor/tests/phpunit/Definitions/DimensionParamTest.php b/www/wiki/vendor/param-processor/param-processor/tests/phpunit/Definitions/DimensionParamTest.php deleted file mode 100644 index 52afad5d..00000000 --- a/www/wiki/vendor/param-processor/param-processor/tests/phpunit/Definitions/DimensionParamTest.php +++ /dev/null @@ -1,106 +0,0 @@ - - */ -class DimensionParamTest extends ParamDefinitionTest { - - /** - * @see ParamDefinitionTest::getDefinitions - * @return array - */ - public function getDefinitions() { - $params = parent::getDefinitions(); - - $params['auto'] = [ - 'allowauto' => true, - ]; - - $params['allunits'] = [ - 'units' => [ 'px', 'ex', 'em', '%', '' ], - ]; - - $params['bounds'] = [ - 'lowerbound' => 42, - 'upperbound' => 9000, - 'maxpercentage' => 34, - 'minpercentage' => 23, - 'units' => [ 'px', 'ex', '%', '' ], - ]; - - return $params; - } - - /** - * @see ParamDefinitionTest::valueProvider - * - * @param boolean $stringlyTyped - * - * @return array - */ - public function valueProvider( $stringlyTyped = true ) { - $values = [ - 'empty' => [ - [ '100px', true, '100px' ], - [ '100', true, '100px' ], - [ 42, true, '42px' ], - [ 42.5, true, '42.5px' ], - [ 'over9000', false ], - [ 'yes', false ], - [ 'auto', false ], - [ '100%', false ], - ], - 'values' => [ - [ 1, true, '1px' ], -// array( 2, false ), - [ 'yes', false ], - [ 'no', false ], - ], - 'auto' => [ - [ 'auto', true, 'auto' ], - ], - 'allunits' => [ - [ '100%', true, '100%' ], - [ '100em', true, '100em' ], - [ '100ex', true, '100ex' ], - [ '101%', false ], - ], - 'bounds' => [ - [ '30%', true, '30%' ], - [ '20%', false ], - [ '40%', false ], - [ '100px', true, '100px' ], - [ '100ex', true, '100ex' ], - [ '10px', false ], - [ '9001ex', false ], - ], - ]; - - if ( $stringlyTyped ) { - foreach ( $values as &$set ) { - foreach ( $set as &$value ) { - if ( is_int( $value[0] ) || is_float( $value[0] ) ) { - $value[0] = (string)$value[0]; - } - } - } - -// $values['empty'][] = array( 42, false ); -// $values['empty'][] = array( 42.5, false ); - } - - return $values; - } - - /** - * @see ParamDefinitionTest::getType - * @return string - */ - public function getType() { - return 'dimension'; - } - -} diff --git a/www/wiki/vendor/param-processor/param-processor/tests/phpunit/Definitions/FloatParamTest.php b/www/wiki/vendor/param-processor/param-processor/tests/phpunit/Definitions/FloatParamTest.php deleted file mode 100644 index b86fd77a..00000000 --- a/www/wiki/vendor/param-processor/param-processor/tests/phpunit/Definitions/FloatParamTest.php +++ /dev/null @@ -1,72 +0,0 @@ - - */ -class FloatParamTest extends NumericParamTest { - - /** - * @see ParamDefinitionTest::getDefinitions - * @return array - */ - public function getDefinitions() { - $params = parent::getDefinitions(); - - return $params; - } - - /** - * @see ParamDefinitionTest::valueProvider - * - * @param boolean $stringlyTyped - * - * @return array - */ - public function valueProvider( $stringlyTyped = true ) { - $values = [ - 'empty' => [ - [ 1, true, 1.0 ], - [ 1.0, true, 1.0 ], - [ 1.1, true, 1.1 ], - [ 0.2555, true, 0.2555 ], - [ '1.1.1', false ], - [ 'foobar', false ], - [ [], false ], - [ 'yes', false ], - [ false, false ], - ], - 'values' => [], -// 'values' => array( -// array( 1, true, 1 ), -// array( 'yes', false ), -// array( 'no', false ), -// array( 0.1, true, 0.1 ), -// array( 0.2555, false ), -// ), - ]; - - if ( $stringlyTyped ) { - foreach ( $values as &$set ) { - foreach ( $set as &$value ) { - if ( is_float( $value[0] ) || is_int( $value[0] ) ) { - $value[0] = (string)$value[0]; - } - } - } - } - - return $values; - } - - /** - * @see ParamDefinitionTest::getType - * @return string - */ - public function getType() { - return 'float'; - } - -} diff --git a/www/wiki/vendor/param-processor/param-processor/tests/phpunit/Definitions/IntParamTest.php b/www/wiki/vendor/param-processor/param-processor/tests/phpunit/Definitions/IntParamTest.php deleted file mode 100644 index 6a7f4e87..00000000 --- a/www/wiki/vendor/param-processor/param-processor/tests/phpunit/Definitions/IntParamTest.php +++ /dev/null @@ -1,98 +0,0 @@ - - */ -class IntParamTest extends NumericParamTest { - - /** - * @see ParamDefinitionTest::getDefinitions - * @return array - */ - public function getDefinitions() { - $params = parent::getDefinitions(); - - $params['count'] = [ - 'type' => 'integer', - ]; - - $params['amount'] = [ - 'type' => 'integer', - 'default' => 42, - 'upperbound' => 99, - ]; - - $params['number'] = [ - 'type' => 'integer', - 'upperbound' => 99, - ]; - - return $params; - } - - /** - * @see ParamDefinitionTest::valueProvider - * - * @param boolean $stringlyTyped - * - * @return array - */ - public function valueProvider( $stringlyTyped = true ) { - $values = [ - 'count' => [ - [ 42, true, 42 ], - [ 'foo', false ], - [ 4.2, false ], - [ [ 42 ], false ], - ], - 'amount' => [ - [ 0, true, 0 ], - [ 'foo', false, 42 ], - [ 100, false, 42 ], - [ 4.2, false, 42 ], - ], - 'number' => [ - [ 42, true, 42 ], - [ 'foo', false ], - [ 100, false ], - [ 4.2, false ], - ], - 'empty' => [ - [ 42, true, 42 ], - [ 4.2, false ], - [ [ 42 ], false ], - ], - 'values' => [ - [ 1, true, 1 ], - [ 'yes', false ], - [ true, false ], - [ 0.1, false ], - [ [], false ], - ], - ]; - - if ( $stringlyTyped ) { - foreach ( $values as &$set ) { - foreach ( $set as &$value ) { - if ( is_float( $value[0] ) || is_int( $value[0] ) ) { - $value[0] = (string)$value[0]; - } - } - } - } - - return $values; - } - - /** - * @see ParamDefinitionTest::getType - * @return string - */ - public function getType() { - return 'integer'; - } - -} diff --git a/www/wiki/vendor/param-processor/param-processor/tests/phpunit/Definitions/NumericParamTest.php b/www/wiki/vendor/param-processor/param-processor/tests/phpunit/Definitions/NumericParamTest.php deleted file mode 100644 index 4d40eeda..00000000 --- a/www/wiki/vendor/param-processor/param-processor/tests/phpunit/Definitions/NumericParamTest.php +++ /dev/null @@ -1,69 +0,0 @@ - - */ -abstract class NumericParamTest extends ParamDefinitionTest { - - public function lowerBoundProvider() { - return [ - [ 42, 42, true ], - [ 42, 41, false ], - [ 42, 43, true ], - [ false, 43, true ], - [ false, 0, true ], - [ false, -100, true ], - [ -100, -100, true ], - [ -99, -100, false ], - [ -101, -100, true ], - ]; - } - - /** - * @dataProvider lowerBoundProvider - */ - public function testSetLowerBound( $bound, $testValue, $validity ) { - $definition = $this->getEmptyInstance(); - $definition->setArrayValues( [ 'lowerbound' => $bound ] ); - - $this->validate( $definition, (string)$testValue, $validity ); - - $options = new Options(); - $options->setRawStringInputs( false ); - $this->validate( $definition, $testValue, $validity, $options ); - } - - public function upperBoundProvider() { - return [ - [ 42, 42, true ], - [ 42, 41, true ], - [ 42, 43, false ], - [ false, 43, true ], - [ false, 0, true ], - [ false, -100, true ], - [ -100, -100, true ], - [ -99, -100, true ], - [ -101, -100, false ], - ]; - } - - /** - * @dataProvider upperBoundProvider - */ - public function testSetUpperBound( $bound, $testValue, $validity ) { - $definition = $this->getEmptyInstance(); - $definition->setArrayValues( [ 'upperbound' => $bound ] ); - - $this->validate( $definition, (string)$testValue, $validity ); - - $options = new Options(); - $options->setRawStringInputs( false ); - $this->validate( $definition, $testValue, $validity, $options ); - } - -} diff --git a/www/wiki/vendor/param-processor/param-processor/tests/phpunit/Definitions/ParamDefinitionTest.php b/www/wiki/vendor/param-processor/param-processor/tests/phpunit/Definitions/ParamDefinitionTest.php deleted file mode 100644 index 08e22ce0..00000000 --- a/www/wiki/vendor/param-processor/param-processor/tests/phpunit/Definitions/ParamDefinitionTest.php +++ /dev/null @@ -1,159 +0,0 @@ - - */ -abstract class ParamDefinitionTest extends TestCase { - - /** - * Returns a list of arrays that hold values to test handling of. - * Each array holds the following unnamed elements: - * - value (mixed, required) - * - valid (boolean, required) - * - expected (mixed, optional) - * - * ie array( '42', true, 42 ) - * - * @since 0.1 - * - * @param boolean $stringlyTyped - * - * @return array - */ - public abstract function valueProvider( $stringlyTyped = true ); - - public abstract function getType(); - - public function getDefinitions() { - $params = []; - - $params['empty'] = []; - - $params['values'] = [ - 'values' => [ 'foo', '1', '0.1', 'yes', 1, 0.1 ] - ]; - - return $params; - } - - public function definitionProvider() { - $definitions = $this->getDefinitions(); - - foreach ( $definitions as &$definition ) { - $definition['type'] = $this->getType(); - } - - return $definitions; - } - - public function getEmptyInstance() { - return ParamDefinitionFactory::singleton()->newDefinitionFromArray( [ - 'name' => 'empty', - 'message' => 'test-empty', - 'type' => $this->getType(), - ] ); - } - - public function instanceProvider() { - $definitions = []; - - foreach ( $this->definitionProvider() as $name => $definition ) { - if ( !array_key_exists( 'message', $definition ) ) { - $definition['message'] = 'test-' . $name; - } - - $definition['name'] = $name; - $definitions[] = [ ParamDefinitionFactory::singleton()->newDefinitionFromArray( $definition ) ]; - } - - return $definitions; - } - - /** - * @dataProvider instanceProvider - */ - public function testGetType( IParamDefinition $definition ) { - $this->assertEquals( $this->getType(), $definition->getType() ); - } - - /** - * @dataProvider instanceProvider - */ - public function testValidate( IParamDefinition $definition ) { - foreach ( [ true, false ] as $stringlyTyped ) { - $values = $this->valueProvider( $stringlyTyped ); - $options = new Options(); - $options->setRawStringInputs( $stringlyTyped ); - - foreach ( $values[$definition->getName()] as $data ) { - list( $input, $valid, ) = $data; - - $param = new Param( $definition ); - $param->setUserValue( $definition->getName(), $input, $options ); - $definitions = []; - $param->process( $definitions, [], $options ); - - $this->assertEquals( - $valid, - $param->getErrors() === [], - 'The validation process should ' . ( $valid ? '' : 'not ' ) . 'pass' - ); - } - } - - $this->assertTrue( true ); - } - - /** - * @dataProvider instanceProvider - */ - public function testFormat( IParamDefinition $sourceDefinition ) { - $values = $this->valueProvider(); - $options = new Options(); - - foreach ( $values[$sourceDefinition->getName()] as $data ) { - $definition = clone $sourceDefinition; - - list( $input, $valid, ) = $data; - - $param = new Param( $definition ); - $param->setUserValue( $definition->getName(), $input, $options ); - - if ( $valid && array_key_exists( 2, $data ) ) { - $defs = []; - $param->process( $defs, [], $options ); - - $this->assertEquals( - $data[2], - $param->getValue() - ); - } - } - - $this->assertTrue( true ); - } - - protected function validate( ParamDefinition $definition, $testValue, $validity, Options $options = null ) { - $def = clone $definition; - $options = $options === null ? new Options() : $options; - - $param = new Param( $def ); - $param->setUserValue( $def->getName(), $testValue, $options ); - - $defs = []; - $param->process( $defs, [], $options ); - - $this->assertEquals( $validity, $param->getErrors() === [] ); - } - -} \ No newline at end of file diff --git a/www/wiki/vendor/param-processor/param-processor/tests/phpunit/Definitions/StringParamTest.php b/www/wiki/vendor/param-processor/param-processor/tests/phpunit/Definitions/StringParamTest.php deleted file mode 100644 index dea22920..00000000 --- a/www/wiki/vendor/param-processor/param-processor/tests/phpunit/Definitions/StringParamTest.php +++ /dev/null @@ -1,56 +0,0 @@ - - */ -class StringParamTest extends ParamDefinitionTest { - - /** - * @see ParamDefinitionTest::getDefinitions - */ - public function getDefinitions() { - $params = parent::getDefinitions(); - - - - return $params; - } - - /** - * @see ParamDefinitionTest::valueProvider - * - * @param boolean $stringlyTyped - * - * @return array - */ - public function valueProvider( $stringlyTyped = true ) { - return [ - 'empty' => [ - [ 'ohi there', true, 'ohi there' ], - [ 4.2, false ], - [ [ 42 ], false ], - ], - 'values' => [ - [ 'foo', true, 'foo' ], - [ '1', true, '1' ], - [ 'yes', true, 'yes' ], - [ 'bar', false ], - [ true, false ], - [ 0.1, false ], - [ [], false ], - ], - ]; - } - - /** - * @see ParamDefinitionTest::getType - * @return string - */ - public function getType() { - return 'string'; - } - -} diff --git a/www/wiki/vendor/param-processor/param-processor/tests/phpunit/OptionsTest.php b/www/wiki/vendor/param-processor/param-processor/tests/phpunit/OptionsTest.php deleted file mode 100644 index 044a4f59..00000000 --- a/www/wiki/vendor/param-processor/param-processor/tests/phpunit/OptionsTest.php +++ /dev/null @@ -1,50 +0,0 @@ - - */ -class OptionsTest extends TestCase { - - protected function getInstance() { - return new Options(); - } - - public function testBooleanSettersAndGetters() { - $methods = [ - 'setUnknownInvalid' => 'unknownIsInvalid', - 'setLowercaseNames' => 'lowercaseNames', - 'setRawStringInputs' => 'isStringlyTyped', - 'setTrimNames' => 'trimNames', - 'setTrimValues' => 'trimValues', - 'setLowercaseValues' => 'lowercaseValues', - ]; - - foreach ( $methods as $setter => $getter ) { - $options = $this->getInstance(); - - foreach ( [ false, true, false ] as $boolean ) { - call_user_func_array( [ $options, $setter ], [ $boolean ] ); - - $this->assertEquals( $boolean, call_user_func( [ $options, $getter ] ) ); - } - } - } - - public function testSetAndGetName() { - $options = $this->getInstance(); - - foreach ( [ 'foo', 'bar baz' ] as $name ) { - $options->setName( $name ); - $this->assertEquals( $name, $options->getName() ); - } - } - -} diff --git a/www/wiki/vendor/param-processor/param-processor/tests/phpunit/ParamDefinitionFactoryTest.php b/www/wiki/vendor/param-processor/param-processor/tests/phpunit/ParamDefinitionFactoryTest.php deleted file mode 100644 index c48e4bf0..00000000 --- a/www/wiki/vendor/param-processor/param-processor/tests/phpunit/ParamDefinitionFactoryTest.php +++ /dev/null @@ -1,23 +0,0 @@ - - */ -class ParamDefinitionFactoryTest extends TestCase { - - public function testCanConstruct() { - new ParamDefinitionFactory(); - $this->assertTrue( true ); - } - - // TODO: test other methods - -} diff --git a/www/wiki/vendor/param-processor/param-processor/tests/phpunit/ParamTest.php b/www/wiki/vendor/param-processor/param-processor/tests/phpunit/ParamTest.php deleted file mode 100644 index f25e4af3..00000000 --- a/www/wiki/vendor/param-processor/param-processor/tests/phpunit/ParamTest.php +++ /dev/null @@ -1,28 +0,0 @@ - - */ -class ParamTest extends TestCase { - - public function testListParamProcessingWithEmptyListAsDefault() { - $definition = new ParamDefinition( 'string', 'something', [] ); - $definitions = [ $definition ]; - - $param = new Param( $definition ); - $param->process( $definitions, [], new Options() ); - - $this->assertSame( [], $param->getValue() ); - } - -} diff --git a/www/wiki/vendor/param-processor/param-processor/tests/phpunit/ProcessingResultTest.php b/www/wiki/vendor/param-processor/param-processor/tests/phpunit/ProcessingResultTest.php deleted file mode 100644 index 078f86df..00000000 --- a/www/wiki/vendor/param-processor/param-processor/tests/phpunit/ProcessingResultTest.php +++ /dev/null @@ -1,79 +0,0 @@ - - */ -class ProcessingResultTest extends TestCase { - - public function testGetParameters() { - $processedParams = [ - $this->newMockParam() - ]; - - $result = new ProcessingResult( $processedParams ); - - $this->assertEquals( $processedParams, $result->getParameters() ); - } - - private function newMockParam() { - return $this->getMockBuilder( ProcessedParam::class ) - ->disableOriginalConstructor()->getMock(); - } - - public function testGetErrors() { - $errors = [ - $this->newMockError() - ]; - - $result = new ProcessingResult( [], $errors ); - - $this->assertEquals( $errors, $result->getErrors() ); - } - - private function newMockError() { - return $this->getMockBuilder( ProcessingError::class ) - ->disableOriginalConstructor()->getMock(); - } - - public function testGivenNoErrors_HasNoFatal() { - $this->assertNoFatalForErrors( [] ); - } - - private function assertNoFatalForErrors( array $errors ) { - $result = new ProcessingResult( [], $errors ); - - $this->assertFalse( $result->hasFatal() ); - } - - public function testGivenNonfatalErrors_HasNoFatal() { - $this->assertNoFatalForErrors( [ - new ProcessingError( '', ProcessingError::SEVERITY_HIGH ), - new ProcessingError( '', ProcessingError::SEVERITY_LOW ), - new ProcessingError( '', ProcessingError::SEVERITY_MINOR ), - new ProcessingError( '', ProcessingError::SEVERITY_NORMAL ), - ] ); - } - - public function testGivenFatalError_HasFatal() { - $result = new ProcessingResult( [], [ - new ProcessingError( '', ProcessingError::SEVERITY_HIGH ), - new ProcessingError( '', ProcessingError::SEVERITY_LOW ), - new ProcessingError( '', ProcessingError::SEVERITY_FATAL ), - new ProcessingError( '', ProcessingError::SEVERITY_MINOR ), - new ProcessingError( '', ProcessingError::SEVERITY_NORMAL ), - ] ); - - $this->assertTrue( $result->hasFatal() ); - } - -} \ No newline at end of file diff --git a/www/wiki/vendor/param-processor/param-processor/tests/phpunit/ProcessorTest.php b/www/wiki/vendor/param-processor/param-processor/tests/phpunit/ProcessorTest.php deleted file mode 100644 index 936c3118..00000000 --- a/www/wiki/vendor/param-processor/param-processor/tests/phpunit/ProcessorTest.php +++ /dev/null @@ -1,461 +0,0 @@ - - */ -class ProcessorTest extends TestCase { - - public function newFromOptionsProvider() { - $options = []; - - $option = new Options(); - - $options[] = clone $option; - - $option->setName( 'foobar' ); - $option->setLowercaseNames( false ); - - $options[] = clone $option; - - return $this->arrayWrap( $options ); - } - - private function arrayWrap( array $elements ) { - return array_map( - function( $element ) { - return [ $element ]; - }, - $elements - ); - } - - public function testNewFromOptions() { - $this->assertEquals( new Options(), Processor::newFromOptions( new Options() )->getOptions() ); - } - - /** - * Simple parameter definitions and values that should all pass. - * - * @return array - */ - private function getSimpleParams() { - $params = [ - 'awesome' => 'yes', - 'Howmuch ' => '9001', - 'FLOAT' => '4.2', - ' page' => 'Ohi there!', - ' text ' => 'foo bar baz o_O', - ]; - - $definitions = [ - 'awesome' => [ - 'type' => 'boolean', - ], - 'howmuch' => [ - 'type' => 'integer', - ], - 'float' => [ - 'type' => 'float', - ], - 'page' => [ - 'type' => 'string', - 'hastoexist' => false, - ], - 'text' => [], - ]; - - $options = new Options(); - - $expected = [ - 'awesome' => true, - 'howmuch' => 9001, - 'float' => 4.2, - 'page' => 'Ohi there!', - 'text' => 'foo bar baz o_O', - ]; - - return [ $params, $definitions, $options, $expected ]; - } - - /** - * Simple parameter definitions with defaults and values - * that are invalid or missing and therefore default. - * - * @return array - */ - private function getDefaultingParams() { - $params = [ - 'awesome' => 'omg!', - 'howmuch' => 'omg!', - 'float' => 'omg!', - 'page' => 42, - 'whot?' => 'O_o', - 'integerr' => ' 9001 ', - ]; - - $definitions = [ - 'awesome' => [ - 'type' => 'boolean', - 'default' => true, - ], - 'howmuch' => [ - 'type' => 'integer', - 'default' => 9001, - ], - 'float' => [ - 'type' => 'float', - 'default' => 4.2, - ], - 'page' => [ - 'type' => 'string', - 'hastoexist' => false, - 'default' => 'Ohi there!', - ], - 'text' => [ - 'default' => 'foo bar baz o_O', - ], - 'integerr' => [ - 'type' => 'integer', - 'default' => 42, - ], - ]; - - $options = new Options(); - $options->setTrimValues( false ); - - $expected = [ - 'awesome' => true, - 'howmuch' => 9001, - 'float' => 4.2, - 'page' => 'Ohi there!', - 'text' => 'foo bar baz o_O', - 'integerr' => 42, - ]; - - return [ $params, $definitions, $options, $expected ]; - } - - /** - * Values and definitions in-system parameter handling. - * Options set to expect non-raw values. - * - * @return array - */ - private function getTypedParams() { - $params = [ - 'awesome' => true, - 'howmuch' => '42', - 'float' => 4.2, - 'page' => 'Ohi there!', - 'Text' => 'foo bar baz o_O', - 'text1 ' => 'foo bar baz o_O', - ' text2' => 'foo bar baz o_O', - ]; - - $definitions = [ - 'awesome' => [ - 'type' => 'boolean', - ], - 'howmuch' => [ - 'type' => 'integer', - 'default' => 9001, - ], - 'float' => [ - 'type' => 'float', - 'lowerbound' => 9001, - 'default' => 9000.1 - ], - 'page' => [ - 'type' => 'string', - 'hastoexist' => false, - ], - 'text' => [ - 'default' => 'some text', - ], - 'text1' => [ - 'default' => 'some text', - ], - 'text2' => [ - 'default' => 'some text', - ], - ]; - - $options = new Options(); - $options->setRawStringInputs( false ); - $options->setLowercaseNames( false ); - $options->setTrimNames( false ); - - $expected = [ - 'awesome' => true, - 'howmuch' => 9001, - 'float' => 9000.1, - 'page' => 'Ohi there!', - 'text' => 'some text', - 'text1' => 'some text', - 'text2' => 'some text', - ]; - - return [ $params, $definitions, $options, $expected ]; - } - - /** - * Values with capitalization and preceding/tailing spaces to test - * of the clean options work. - * - * @return array - */ - private function getUncleanParams() { - $params = [ - 'awesome' => ' yes ', - 'text' => ' FOO bar ', - 'integerr' => ' 9001 ', - ]; - - $definitions = [ - 'awesome' => [ - 'type' => 'boolean', - ], - 'text' => [ - 'default' => 'bar', - ], - 'integerr' => [ - 'type' => 'integer', - 'default' => 42, - ], - ]; - - $options = new Options(); - $options->setLowercaseValues( true ); - $options->setTrimValues( true ); - - $expected = [ - 'awesome' => true, - 'text' => 'foo bar', - 'integerr' => 9001, - ]; - - return [ $params, $definitions, $options, $expected ]; - } - - /** - * List parameters to test if list handling works correctly. - * - * @return array - */ - private function getListParams() { - $params = [ - 'awesome' => ' yes, no, on, off ', - 'float' => ' 9001 ; 42 ; 4.2;0', - ]; - - $definitions = [ - 'awesome' => [ - 'type' => 'boolean', - 'islist' => true, - ], - 'text' => [ - 'default' => [ 'bar' ], - 'islist' => true, - ], - 'float' => [ - 'type' => 'float', - 'islist' => true, - 'delimiter' => ';' - ], - ]; - - $options = new Options(); - $options->setLowercaseValues( true ); - $options->setTrimValues( true ); - - $expected = [ - 'awesome' => [ true, false, true, false ], - 'text' => [ 'bar' ], - 'float' => [ 9001.0, 42.0, 4.2, 0.0 ], - ]; - - return [ $params, $definitions, $options, $expected ]; - } - - public function parameterProvider() { - // $params, $definitions [, $options] - $argLists = []; - - $argLists[] = $this->getSimpleParams(); - - $argLists[] = $this->getDefaultingParams(); - - $argLists[] = $this->getTypedParams(); - - $argLists[] = $this->getUncleanParams(); - - $argLists[] = $this->getListParams(); - - foreach ( $argLists as &$argList ) { - foreach ( $argList[1] as $key => &$definition ) { - $definition['message'] = 'test-' . $key; - } - - if ( !array_key_exists( 2, $argList ) ) { - $argList[2] = new Options(); - } - } - - return $argLists; - } - - /** - * @dataProvider parameterProvider - */ - public function testSetParameters( array $params, array $definitions, Options $options ) { - $validator = Processor::newFromOptions( $options ); - - $validator->setParameters( $params, $definitions ); - - $this->assertTrue( true ); // TODO - } - - /** - * @dataProvider parameterProvider - */ - public function testValidateParameters( array $params, array $definitions, Options $options, array $expected = [] ) { - $validator = Processor::newFromOptions( $options ); - - $validator->setParameters( $params, $definitions ); - - $processingResult = $validator->processParameters(); - - $actualValues = []; - - foreach ( $processingResult->getParameters() as $param ) { - $actualValues[$param->getName()] = $param->getValue(); - } - - $this->assertEquals( $expected, $actualValues ); - - - } - - public function testProcessParametersOnEmptyOptions() { - $processor = Processor::newDefault(); - - $this->assertInstanceOf( - ProcessingResult::class, - $processor->processParameters() - ); - } - - public function testErrorsCanBeRetrievedAfterProcessing() { - $processor = Processor::newDefault(); - - $this->processWithOneError( $processor ); - - $this->assertCount( 1, $processor->getErrors() ); - } - - private function processWithOneError( Processor $processor ) { - $processor->setParameters( - [], - [ - 'awesome' => [ - 'type' => 'boolean', - 'message' => 'test-awesome' - ], - ] - ); - - // There should be a single "missing required parameter" error. - $processor->processParameters(); - } - - public function testErrorsAreClearedBetweenProcessingRuns() { - $processor = Processor::newDefault(); - - $this->processWithOneError( $processor ); - $processor->setParameters( [], [] ); - $processor->processParameters(); - - $this->assertEmpty( $processor->getErrors() ); - } - - public function testInvalidListElementsAreOmitted() { - $processor = Processor::newDefault(); - - $processor->setFunctionParams( - [ - 'some-list=1,2,3, ,4,' - ], - [ - 'some-list' => [ - 'type' => 'integer', - 'message' => 'test', - 'islist' => true - ], - ] - ); - - $this->assertSame( - [ 1, 2, 3, 4 ], - $processor->processParameters()->getParameters()['some-list']->getValue() - ); - } - - public function testListParametersAreNotDefaultedWhenSomeElementsAreInvalid() { - $processor = Processor::newDefault(); - - $processor->setFunctionParams( - [ - 'some-list=1,nan' - ], - [ - 'some-list' => [ - 'type' => 'integer', - 'message' => 'test', - 'islist' => true, - 'default' => [] - ], - ] - ); - - $this->assertSame( - [ 1 ], - $processor->processParameters()->getParameters()['some-list']->getValue() - ); - } - - public function testListParametersAreDefaultedWhenAllElementsAreInvalid() { - $processor = Processor::newDefault(); - - $processor->setFunctionParams( - [ - 'some-list=such,nan' - ], - [ - 'some-list' => [ - 'type' => 'integer', - 'message' => 'test', - 'islist' => true, - 'default' => [ 42 ] - ], - ] - ); - - $this->assertSame( - [ 42 ], - $processor->processParameters()->getParameters()['some-list']->getValue() - ); - } - -} diff --git a/www/wiki/vendor/param-processor/param-processor/tests/phpunit/SettingsTest.php b/www/wiki/vendor/param-processor/param-processor/tests/phpunit/SettingsTest.php deleted file mode 100644 index c2491cf5..00000000 --- a/www/wiki/vendor/param-processor/param-processor/tests/phpunit/SettingsTest.php +++ /dev/null @@ -1,42 +0,0 @@ - - */ -class SettingsTest extends TestCase { - - public function constructorProvider() { - $settingArrays = [ - [ [] ], - [ [ 'foo' => 'bar' ] ], - [ [ 'foo' => 'bar', 'baz' => 'BAH' ] ], - [ [ '~[,,_,,]:3' => [ 9001, 4.2 ] ] ], - ]; - - return $settingArrays; - } - - /** - * @dataProvider constructorProvider - * - * @param array $settings - */ - public function testConstructor( array $settings ) { - $settingsObject = new Settings( $settings ); - - foreach ( $settings as $name => $value ) { - $this->assertEquals( $value, $settingsObject->get( $name ) ); - } - - $this->assertTrue( true ); - } - -} -- cgit v1.2.1