summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--plugins/sonar-core-plugin/src/main/resources/org/sonar/plugins/core/widgets/measures/measure_filter_bubble_chart.html.erb12
-rw-r--r--plugins/sonar-core-plugin/src/main/resources/org/sonar/plugins/core/widgets/measures/measure_filter_cloud.html.erb10
-rw-r--r--plugins/sonar-core-plugin/src/main/resources/org/sonar/plugins/core/widgets/measures/measure_filter_histogram.html.erb9
-rw-r--r--plugins/sonar-core-plugin/src/main/resources/org/sonar/plugins/core/widgets/measures/measure_filter_pie_chart.html.erb9
-rw-r--r--plugins/sonar-core-plugin/src/main/resources/org/sonar/plugins/core/widgets/measures/measure_filter_treemap.html.erb6
-rw-r--r--plugins/sonar-core-plugin/src/main/resources/org/sonar/plugins/core/widgets/size.html.erb25
-rw-r--r--plugins/sonar-cpd-plugin/pom.xml5
-rw-r--r--plugins/sonar-cpd-plugin/src/main/java/org/sonar/plugins/cpd/CpdEngine.java5
-rw-r--r--plugins/sonar-cpd-plugin/src/main/java/org/sonar/plugins/cpd/CpdMappings.java51
-rw-r--r--plugins/sonar-cpd-plugin/src/main/java/org/sonar/plugins/cpd/CpdPlugin.java7
-rw-r--r--plugins/sonar-cpd-plugin/src/main/java/org/sonar/plugins/cpd/CpdSensor.java26
-rw-r--r--plugins/sonar-cpd-plugin/src/main/java/org/sonar/plugins/cpd/DefaultCpdEngine.java (renamed from plugins/sonar-cpd-plugin/src/main/java/org/sonar/plugins/cpd/SonarBridgeEngine.java)70
-rw-r--r--plugins/sonar-cpd-plugin/src/main/java/org/sonar/plugins/cpd/JavaCpdEngine.java (renamed from plugins/sonar-cpd-plugin/src/main/java/org/sonar/plugins/cpd/SonarEngine.java)81
-rw-r--r--plugins/sonar-cpd-plugin/src/main/java/org/sonar/plugins/cpd/index/IndexFactory.java33
-rw-r--r--plugins/sonar-cpd-plugin/src/test/java/org/sonar/plugins/cpd/CpdPluginTest.java2
-rw-r--r--plugins/sonar-cpd-plugin/src/test/java/org/sonar/plugins/cpd/CpdSensorTest.java9
-rw-r--r--plugins/sonar-cpd-plugin/src/test/java/org/sonar/plugins/cpd/DefaultCpdEngineTest.java (renamed from plugins/sonar-cpd-plugin/src/test/java/org/sonar/plugins/cpd/SonarBridgeEngineTest.java)34
-rw-r--r--plugins/sonar-cpd-plugin/src/test/java/org/sonar/plugins/cpd/JavaCpdEngineTest.java147
-rw-r--r--plugins/sonar-cpd-plugin/src/test/java/org/sonar/plugins/cpd/SonarEngineTest.java160
-rw-r--r--plugins/sonar-cpd-plugin/src/test/java/org/sonar/plugins/cpd/index/IndexFactoryTest.java22
-rw-r--r--plugins/sonar-cpd-plugin/src/test/java/org/sonar/plugins/cpd/medium/CpdMediumTest.java119
-rw-r--r--plugins/sonar-xoo-plugin/src/main/java/org/sonar/xoo/XooConstants.java (renamed from sonar-batch/src/test/java/org/sonar/batch/mediumtest/xoo/plugin/base/XooConstants.java)2
-rw-r--r--plugins/sonar-xoo-plugin/src/main/java/org/sonar/xoo/XooPlugin.java22
-rw-r--r--plugins/sonar-xoo-plugin/src/main/java/org/sonar/xoo/lang/MeasureSensor.java (renamed from sonar-batch/src/test/java/org/sonar/batch/mediumtest/xoo/plugin/lang/MeasureSensor.java)6
-rw-r--r--plugins/sonar-xoo-plugin/src/main/java/org/sonar/xoo/lang/ScmActivitySensor.java (renamed from sonar-batch/src/test/java/org/sonar/batch/mediumtest/xoo/plugin/lang/ScmActivitySensor.java)11
-rw-r--r--plugins/sonar-xoo-plugin/src/main/java/org/sonar/xoo/lang/SymbolReferencesSensor.java (renamed from sonar-batch/src/test/java/org/sonar/batch/mediumtest/xoo/plugin/lang/SymbolReferencesSensor.java)8
-rw-r--r--plugins/sonar-xoo-plugin/src/main/java/org/sonar/xoo/lang/SyntaxHighlightingSensor.java (renamed from sonar-batch/src/test/java/org/sonar/batch/mediumtest/xoo/plugin/lang/SyntaxHighlightingSensor.java)6
-rw-r--r--plugins/sonar-xoo-plugin/src/main/java/org/sonar/xoo/lang/XooTokenizerSensor.java84
-rw-r--r--plugins/sonar-xoo-plugin/src/main/java/org/sonar/xoo/rule/CreateIssueByInternalKeySensor.java (renamed from sonar-batch/src/test/java/org/sonar/batch/mediumtest/xoo/plugin/rule/CreateIssueByInternalKeySensor.java)7
-rw-r--r--plugins/sonar-xoo-plugin/src/main/java/org/sonar/xoo/rule/OneIssueOnDirPerFileSensor.java (renamed from sonar-batch/src/test/java/org/sonar/batch/mediumtest/xoo/plugin/rule/OneIssueOnDirPerFileSensor.java)7
-rw-r--r--plugins/sonar-xoo-plugin/src/main/java/org/sonar/xoo/rule/OneIssuePerLineSensor.java (renamed from sonar-batch/src/test/java/org/sonar/batch/mediumtest/xoo/plugin/rule/OneIssuePerLineSensor.java)7
-rw-r--r--plugins/sonar-xoo-plugin/src/test/java/org/sonar/xoo/XooPluginTest.java2
-rw-r--r--pom.xml6
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/batch/ProjectReferentialsAction.java81
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/component/persistence/ComponentDao.java16
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/db/DatabaseChecker.java24
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/db/migrations/BaseDataChange.java4
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/db/migrations/UpsertImpl.java1
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/duplication/ws/DuplicationsJsonWriter.java69
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/issue/RulesAggregation.java17
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/measure/MeasureFilterFactory.java45
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/platform/ServerIdGenerator.java3
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/qualityprofile/QProfileFactory.java2
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/qualityprofile/QProfileProjectOperations.java10
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/rule/index/RuleIndex.java22
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/rule/ws/RuleMapping.java17
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/rule/ws/SearchAction.java3
-rw-r--r--server/sonar-server/src/test/java/org/sonar/server/batch/ProjectReferentialsActionTest.java209
-rw-r--r--server/sonar-server/src/test/java/org/sonar/server/component/persistence/ComponentDaoTest.java36
-rw-r--r--server/sonar-server/src/test/java/org/sonar/server/db/DatabaseCheckerTest.java4
-rw-r--r--server/sonar-server/src/test/java/org/sonar/server/issue/RulesAggregationTest.java17
-rw-r--r--server/sonar-server/src/test/java/org/sonar/server/measure/MeasureFilterFactoryTest.java46
-rw-r--r--server/sonar-server/src/test/java/org/sonar/server/rule/index/RuleIndexMediumTest.java86
-rw-r--r--server/sonar-server/src/test/java/org/sonar/server/tester/ServerTester.java1
-rw-r--r--server/sonar-server/src/test/resources/org/sonar/server/batch/ProjectReferentialsActionTest/return_project_settings.json (renamed from server/sonar-server/src/test/resources/org/sonar/server/batch/ProjectReferentialsActionTest/return_settings_by_modules_without_empty_module_settings.json)0
-rw-r--r--server/sonar-server/src/test/resources/org/sonar/server/batch/ProjectReferentialsActionTest/return_project_with_module_settings.json (renamed from server/sonar-server/src/test/resources/org/sonar/server/batch/ProjectReferentialsActionTest/return_settings_by_modules.json)2
-rw-r--r--server/sonar-server/src/test/resources/org/sonar/server/batch/ProjectReferentialsActionTest/return_project_with_module_settings_inherited_from_project.json22
-rw-r--r--server/sonar-server/src/test/resources/org/sonar/server/batch/ProjectReferentialsActionTest/return_project_with_module_with_sub_module.json28
-rw-r--r--server/sonar-server/src/test/resources/org/sonar/server/batch/ProjectReferentialsActionTest/return_quality_profiles_even_when_project_does_not_exists.json13
-rw-r--r--server/sonar-server/src/test/resources/org/sonar/server/batch/ProjectReferentialsActionTest/return_sub_module_settings.json19
-rw-r--r--server/sonar-server/src/test/resources/org/sonar/server/batch/ProjectReferentialsActionTest/return_sub_module_settings_including_settings_from_parent_modules.json19
-rw-r--r--server/sonar-server/src/test/resources/org/sonar/server/batch/ProjectReferentialsActionTest/return_sub_module_settings_inherited_from_project.json19
-rw-r--r--server/sonar-server/src/test/resources/org/sonar/server/batch/ProjectReferentialsActionTest/return_sub_module_settings_inherited_from_project_and_module.json19
-rw-r--r--server/sonar-server/src/test/resources/org/sonar/server/component/persistence/ComponentDaoTest/multi-modules.xml89
-rw-r--r--server/sonar-web/Gruntfile.coffee2
-rw-r--r--server/sonar-web/pom.xml1
-rw-r--r--server/sonar-web/src/main/coffee/coding-rules/views/coding-rules-facets-view.coffee3
-rw-r--r--server/sonar-web/src/main/coffee/coding-rules/views/coding-rules-list-item-view.coffee5
-rw-r--r--server/sonar-web/src/main/coffee/coding-rules/views/filters/tag-filter-view.coffee17
-rw-r--r--server/sonar-web/src/main/coffee/component-viewer/duplication-popup.coffee4
-rw-r--r--server/sonar-web/src/main/coffee/component-viewer/header/tests-header.coffee7
-rw-r--r--server/sonar-web/src/main/coffee/component-viewer/main.coffee2
-rw-r--r--server/sonar-web/src/main/coffee/component-viewer/source.coffee4
-rw-r--r--server/sonar-web/src/main/coffee/drilldown/app.coffee4
-rw-r--r--server/sonar-web/src/main/coffee/widgets/base.coffee3
-rw-r--r--server/sonar-web/src/main/coffee/widgets/treemap.coffee1
-rw-r--r--server/sonar-web/src/main/hbs/coding-rules/coding-rules-facets.hbs8
-rw-r--r--server/sonar-web/src/main/hbs/coding-rules/coding-rules-list-item.hbs21
-rw-r--r--server/sonar-web/src/main/hbs/coding-rules/coding-rules-profile-filter-detail.hbs12
-rw-r--r--server/sonar-web/src/main/hbs/coding-rules/coding-rules-repository-detail.hbs14
-rw-r--r--server/sonar-web/src/main/hbs/common/_markdown-tips.hbs2
-rw-r--r--server/sonar-web/src/main/hbs/component-viewer/cw-duplication-popup.hbs16
-rw-r--r--server/sonar-web/src/main/hbs/component-viewer/cw-more-actions.hbs10
-rw-r--r--server/sonar-web/src/main/hbs/component-viewer/header/cw-basic-header.hbs2
-rw-r--r--server/sonar-web/src/main/hbs/component-viewer/header/cw-coverage-header.hbs2
-rw-r--r--server/sonar-web/src/main/hbs/component-viewer/header/cw-scm-header.hbs2
-rw-r--r--server/sonar-web/src/main/hbs/component-viewer/header/cw-tests-header.hbs4
-rw-r--r--server/sonar-web/src/main/hbs/issue/issue.hbs4
-rw-r--r--server/sonar-web/src/main/hbs/navigator/choice-filter-item.hbs17
-rw-r--r--server/sonar-web/src/main/hbs/navigator/more-criteria-details-filter.hbs2
-rw-r--r--server/sonar-web/src/main/js/application.js2
-rw-r--r--server/sonar-web/src/main/js/common/handlebars-extensions.js21
-rw-r--r--server/sonar-web/src/main/js/issues/app.js26
-rw-r--r--server/sonar-web/src/main/js/navigator/filters/ajax-select-filters.js4
-rw-r--r--server/sonar-web/src/main/js/navigator/filters/base-filters.js1
-rw-r--r--server/sonar-web/src/main/js/navigator/filters/choice-filters.js12
-rw-r--r--server/sonar-web/src/main/js/navigator/filters/metric-filters.js36
-rw-r--r--server/sonar-web/src/main/js/navigator/filters/range-filters.js4
-rw-r--r--server/sonar-web/src/main/js/tests/e2e/tests/coding-rules-spec.js36
-rw-r--r--server/sonar-web/src/main/js/tests/e2e/tests/component-viewer-lines-filters-spec.js29
-rw-r--r--server/sonar-web/src/main/js/tests/e2e/tests/component-viewer-spec/resources-without-ncloc-data.json150
-rw-r--r--server/sonar-web/src/main/js/third-party/moment.js6417
-rw-r--r--server/sonar-web/src/main/js/widgets/bubble-chart.js70
-rw-r--r--server/sonar-web/src/main/js/widgets/stack-area.js6
-rw-r--r--server/sonar-web/src/main/js/widgets/timeline.js6
-rw-r--r--server/sonar-web/src/main/less/api-documentation.less6
-rw-r--r--server/sonar-web/src/main/less/coding-rules.less10
-rw-r--r--server/sonar-web/src/main/less/component-viewer-source-colorizer.less2
-rw-r--r--server/sonar-web/src/main/less/component-viewer.less14
-rw-r--r--server/sonar-web/src/main/less/dashboard.less20
-rw-r--r--server/sonar-web/src/main/less/icons.less17
-rw-r--r--server/sonar-web/src/main/less/layout.less24
-rw-r--r--server/sonar-web/src/main/less/mixins.less2
-rw-r--r--server/sonar-web/src/main/less/navigator.less4
-rw-r--r--server/sonar-web/src/main/less/navigator/filters.less19
-rw-r--r--server/sonar-web/src/main/less/quality-gates.less8
-rw-r--r--server/sonar-web/src/main/less/select2-sonar.less4
-rw-r--r--server/sonar-web/src/main/less/style.less51
-rw-r--r--server/sonar-web/src/main/less/ui.less46
-rw-r--r--server/sonar-web/src/main/webapp/WEB-INF/app/controllers/issues_controller.rb2
-rw-r--r--server/sonar-web/src/main/webapp/WEB-INF/app/controllers/measures_controller.rb2
-rw-r--r--server/sonar-web/src/main/webapp/WEB-INF/app/controllers/project_controller.rb6
-rw-r--r--server/sonar-web/src/main/webapp/WEB-INF/app/helpers/application_helper.rb4
-rw-r--r--server/sonar-web/src/main/webapp/WEB-INF/app/helpers/measures_helper.rb6
-rw-r--r--server/sonar-web/src/main/webapp/WEB-INF/app/models/measure_filter.rb7
-rw-r--r--server/sonar-web/src/main/webapp/WEB-INF/app/views/admin_dashboards/_list.html.erb4
-rw-r--r--server/sonar-web/src/main/webapp/WEB-INF/app/views/comparison/index.html.erb2
-rw-r--r--server/sonar-web/src/main/webapp/WEB-INF/app/views/components/_list_table_header.rhtml2
-rw-r--r--server/sonar-web/src/main/webapp/WEB-INF/app/views/components/index.html.erb2
-rw-r--r--server/sonar-web/src/main/webapp/WEB-INF/app/views/dashboard/_header.html.erb10
-rw-r--r--server/sonar-web/src/main/webapp/WEB-INF/app/views/dashboards/_available_dashboards.html.erb2
-rw-r--r--server/sonar-web/src/main/webapp/WEB-INF/app/views/dashboards/_my_dashboards.html.erb2
-rw-r--r--server/sonar-web/src/main/webapp/WEB-INF/app/views/drilldown/_severity.html.erb2
-rw-r--r--server/sonar-web/src/main/webapp/WEB-INF/app/views/drilldown/issues.html.erb9
-rw-r--r--server/sonar-web/src/main/webapp/WEB-INF/app/views/drilldown/measures.html.erb6
-rw-r--r--server/sonar-web/src/main/webapp/WEB-INF/app/views/groups/index.html.erb8
-rw-r--r--server/sonar-web/src/main/webapp/WEB-INF/app/views/issues/_filter_shared_form.html.erb4
-rw-r--r--server/sonar-web/src/main/webapp/WEB-INF/app/views/issues/search.html.erb1
-rw-r--r--server/sonar-web/src/main/webapp/WEB-INF/app/views/layouts/_footer.html.erb2
-rw-r--r--server/sonar-web/src/main/webapp/WEB-INF/app/views/layouts/_head.html.erb3
-rw-r--r--server/sonar-web/src/main/webapp/WEB-INF/app/views/measures/_display_list.html.erb4
-rw-r--r--server/sonar-web/src/main/webapp/WEB-INF/app/views/measures/search.html.erb4
-rw-r--r--server/sonar-web/src/main/webapp/WEB-INF/app/views/metrics/index.html.erb12
-rw-r--r--server/sonar-web/src/main/webapp/WEB-INF/app/views/permission_templates/index.html.erb14
-rw-r--r--server/sonar-web/src/main/webapp/WEB-INF/app/views/profiles/index.html.erb14
-rw-r--r--server/sonar-web/src/main/webapp/WEB-INF/app/views/project/widgets/issues/_issues_list.html.erb2
-rw-r--r--server/sonar-web/src/main/webapp/WEB-INF/app/views/project_roles/index.html.erb12
-rw-r--r--server/sonar-web/src/main/webapp/WEB-INF/app/views/roles/projects.html.erb14
-rw-r--r--server/sonar-web/src/main/webapp/WEB-INF/app/views/updatecenter/system_updates.html.erb39
-rw-r--r--server/sonar-web/src/main/webapp/WEB-INF/app/views/users/index.html.erb10
-rwxr-xr-xserver/sonar-web/src/main/webapp/fonts/sonar.eotbin18568 -> 18360 bytes
-rwxr-xr-xserver/sonar-web/src/main/webapp/fonts/sonar.svg1
-rwxr-xr-xserver/sonar-web/src/main/webapp/fonts/sonar.ttfbin18412 -> 18204 bytes
-rwxr-xr-xserver/sonar-web/src/main/webapp/fonts/sonar.woffbin18532 -> 18292 bytes
-rw-r--r--sonar-batch-protocol/src/main/java/org/sonar/batch/protocol/input/ActiveRule.java4
-rw-r--r--sonar-batch-protocol/src/main/java/org/sonar/batch/protocol/input/Metric.java3
-rw-r--r--sonar-batch-protocol/src/main/java/org/sonar/batch/protocol/input/ProjectReferentials.java7
-rw-r--r--sonar-batch/pom.xml5
-rw-r--r--sonar-batch/src/main/java/org/sonar/batch/bootstrap/BatchExtensionDictionnary.java10
-rw-r--r--sonar-batch/src/main/java/org/sonar/batch/duplication/BlockCache.java56
-rw-r--r--sonar-batch/src/main/java/org/sonar/batch/duplication/DefaultDuplicationBuilder.java74
-rw-r--r--sonar-batch/src/main/java/org/sonar/batch/duplication/DefaultTokenBuilder.java77
-rw-r--r--sonar-batch/src/main/java/org/sonar/batch/duplication/DuplicationBlockValueCoder.java43
-rw-r--r--sonar-batch/src/main/java/org/sonar/batch/duplication/DuplicationCache.java58
-rw-r--r--sonar-batch/src/main/java/org/sonar/batch/duplication/DuplicationGroup.java76
-rw-r--r--sonar-batch/src/main/java/org/sonar/batch/duplication/DuplicationGroupValueCoder.java54
-rw-r--r--sonar-batch/src/main/java/org/sonar/batch/duplication/FileBlocksValueCoder.java69
-rw-r--r--sonar-batch/src/main/java/org/sonar/batch/duplication/package-info.java (renamed from sonar-batch/src/test/java/org/sonar/batch/mediumtest/xoo/plugin/base/Xoo.java)23
-rw-r--r--sonar-batch/src/main/java/org/sonar/batch/highlighting/SyntaxHighlightingDataBuilder.java3
-rw-r--r--sonar-batch/src/main/java/org/sonar/batch/highlighting/SyntaxHighlightingRule.java3
-rw-r--r--sonar-batch/src/main/java/org/sonar/batch/index/Cache.java13
-rw-r--r--sonar-batch/src/main/java/org/sonar/batch/index/Caches.java3
-rw-r--r--sonar-batch/src/main/java/org/sonar/batch/index/MeasurePersister.java49
-rw-r--r--sonar-batch/src/main/java/org/sonar/batch/mediumtest/BatchMediumTester.java14
-rw-r--r--sonar-batch/src/main/java/org/sonar/batch/referential/ProjectReferentialsProvider.java12
-rw-r--r--sonar-batch/src/main/java/org/sonar/batch/scan/ProjectScanContainer.java6
-rw-r--r--sonar-batch/src/main/java/org/sonar/batch/scan/SensorContextAdaptor.java59
-rw-r--r--sonar-batch/src/main/java/org/sonar/batch/scan/SensorWrapper.java10
-rw-r--r--sonar-batch/src/main/java/org/sonar/batch/scan/filesystem/DefaultInputFileValueCoder.java76
-rw-r--r--sonar-batch/src/main/java/org/sonar/batch/scan/filesystem/InputPathCache.java2
-rw-r--r--sonar-batch/src/main/java/org/sonar/batch/scan/measure/MeasureValueCoder.java20
-rw-r--r--sonar-batch/src/main/java/org/sonar/batch/scan2/AnalyzerMeasureCache.java7
-rw-r--r--sonar-batch/src/main/java/org/sonar/batch/scan2/DefaultMeasureValueCoder.java64
-rw-r--r--sonar-batch/src/main/java/org/sonar/batch/scan2/DefaultSensorContext.java44
-rw-r--r--sonar-batch/src/main/java/org/sonar/batch/scan2/ProjectScanContainer.java6
-rw-r--r--sonar-batch/src/main/java/org/sonar/batch/scan2/SensorsExecutor.java2
-rw-r--r--sonar-batch/src/test/java/org/sonar/batch/duplication/DuplicationCacheTest.java82
-rw-r--r--sonar-batch/src/test/java/org/sonar/batch/index/MeasurePersisterTest.java9
-rw-r--r--sonar-batch/src/test/java/org/sonar/batch/mediumtest/fs/FileSystemMediumTest.java2
-rw-r--r--sonar-batch/src/test/java/org/sonar/batch/mediumtest/highlighting/HighlightingMediumTest.java2
-rw-r--r--sonar-batch/src/test/java/org/sonar/batch/mediumtest/issues/IssuesMediumTest.java2
-rw-r--r--sonar-batch/src/test/java/org/sonar/batch/mediumtest/issues/IssuesOnDirMediumTest.java2
-rw-r--r--sonar-batch/src/test/java/org/sonar/batch/mediumtest/measures/MeasuresMediumTest.java2
-rw-r--r--sonar-batch/src/test/java/org/sonar/batch/mediumtest/symbol/SymbolMediumTest.java2
-rw-r--r--sonar-batch/src/test/java/org/sonar/batch/mediumtest/xoo/plugin/XooPlugin.java53
-rw-r--r--sonar-batch/src/test/java/org/sonar/batch/scan/SensorContextAdapterTest.java5
-rw-r--r--sonar-batch/src/test/java/org/sonar/batch/scan/filesystem/InputPathCacheTest.java16
-rw-r--r--sonar-core/src/main/java/org/sonar/core/component/db/ComponentMapper.java7
-rw-r--r--sonar-core/src/main/java/org/sonar/core/properties/PropertiesDao.java2
-rw-r--r--sonar-core/src/main/java/org/sonar/core/source/SnapshotDataTypes.java1
-rw-r--r--sonar-core/src/main/resources/org/sonar/core/component/db/ComponentMapper.xml25
-rw-r--r--sonar-core/src/main/resources/org/sonar/l10n/core.properties6
-rw-r--r--sonar-deprecated/src/main/java/org/sonar/api/batch/BatchExtensionDictionnary.java5
-rw-r--r--sonar-duplications/src/main/java/org/sonar/duplications/block/ByteArray.java8
-rw-r--r--sonar-duplications/src/main/java/org/sonar/duplications/block/FileBlocks.java45
-rw-r--r--sonar-duplications/src/main/java/org/sonar/duplications/index/CloneGroup.java13
-rw-r--r--sonar-duplications/src/main/java/org/sonar/duplications/internal/pmd/PmdBlockChunker.java21
-rw-r--r--sonar-duplications/src/main/java/org/sonar/duplications/internal/pmd/TokenizerBridge.java2
-rw-r--r--sonar-duplications/src/main/java/org/sonar/duplications/internal/pmd/TokensLine.java3
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/CoreProperties.java4
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/internal/DeprecatedDefaultInputFile.java3
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/batch/sensor/SensorContext.java17
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/batch/sensor/duplication/DuplicationBuilder.java38
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/batch/sensor/duplication/TokenBuilder.java40
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/batch/sensor/duplication/package-info.java21
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/measures/Measure.java1
216 files changed, 9601 insertions, 1171 deletions
diff --git a/plugins/sonar-core-plugin/src/main/resources/org/sonar/plugins/core/widgets/measures/measure_filter_bubble_chart.html.erb b/plugins/sonar-core-plugin/src/main/resources/org/sonar/plugins/core/widgets/measures/measure_filter_bubble_chart.html.erb
index af427c247c6..dfb2fba1b93 100644
--- a/plugins/sonar-core-plugin/src/main/resources/org/sonar/plugins/core/widgets/measures/measure_filter_bubble_chart.html.erb
+++ b/plugins/sonar-core-plugin/src/main/resources/org/sonar/plugins/core/widgets/measures/measure_filter_bubble_chart.html.erb
@@ -11,7 +11,8 @@
maxItems = widget_properties["maxItems"].to_i
filter = MeasureFilter.find_by_id(filterId.to_i)
- @widget_title = link_to h(filter.name), {:controller => 'measures', :action => 'filter', :id => filter.id, :display => 'list'}
+ if filter
+ @widget_title = link_to h(filter.name), {:controller => 'measures', :action => 'filter', :id => filter.id, :display => 'list'}
%>
<div class="bubble-chart-widget" id="<%= containerId %>">
@@ -62,9 +63,6 @@
})();
</script>
<!--<![endif]-->
-
-
-
-
-
-
+<% else %>
+ <p><%= image_tag 'warning.png' %> <%= message 'measure_filter.widget.unknown_filter_warning' -%></p>
+<% end %>
diff --git a/plugins/sonar-core-plugin/src/main/resources/org/sonar/plugins/core/widgets/measures/measure_filter_cloud.html.erb b/plugins/sonar-core-plugin/src/main/resources/org/sonar/plugins/core/widgets/measures/measure_filter_cloud.html.erb
index 7d967faf8ec..d7de32d63c3 100644
--- a/plugins/sonar-core-plugin/src/main/resources/org/sonar/plugins/core/widgets/measures/measure_filter_cloud.html.erb
+++ b/plugins/sonar-core-plugin/src/main/resources/org/sonar/plugins/core/widgets/measures/measure_filter_cloud.html.erb
@@ -5,7 +5,8 @@
maxItems = widget_properties['maxItems'].to_i
filter = MeasureFilter.find_by_id(filterId.to_i)
- @widget_title = link_to h(filter.name), {:controller => 'measures', :action => 'filter', :id => filter.id, :display => 'list'}
+ if filter
+ @widget_title = link_to h(filter.name), {:controller => 'measures', :action => 'filter', :id => filter.id, :display => 'list'}
%>
<div class="histogram-widget" id="<%= containerId %>">
@@ -53,7 +54,6 @@
})();
</script>
<!--<![endif]-->
-
-
-
-
+<% else %>
+ <p><%= image_tag 'warning.png' %> <%= message 'measure_filter.widget.unknown_filter_warning' -%></p>
+<% end %>
diff --git a/plugins/sonar-core-plugin/src/main/resources/org/sonar/plugins/core/widgets/measures/measure_filter_histogram.html.erb b/plugins/sonar-core-plugin/src/main/resources/org/sonar/plugins/core/widgets/measures/measure_filter_histogram.html.erb
index 000ada35ae2..c18ce87276a 100644
--- a/plugins/sonar-core-plugin/src/main/resources/org/sonar/plugins/core/widgets/measures/measure_filter_histogram.html.erb
+++ b/plugins/sonar-core-plugin/src/main/resources/org/sonar/plugins/core/widgets/measures/measure_filter_histogram.html.erb
@@ -7,7 +7,8 @@
relativeScale = widget_properties["relativeScale"]
filter = MeasureFilter.find_by_id(filterId.to_i)
- @widget_title = link_to h(filter.name), {:controller => 'measures', :action => 'filter', :id => filter.id, :display => 'list'}
+ if filter
+ @widget_title = link_to h(filter.name), {:controller => 'measures', :action => 'filter', :id => filter.id, :display => 'list'}
%>
<div class="histogram-widget" id="<%= containerId %>">
@@ -56,6 +57,6 @@
})();
</script>
<!--<![endif]-->
-
-
-
+<% else %>
+ <p><%= image_tag 'warning.png' %> <%= message 'measure_filter.widget.unknown_filter_warning' -%></p>
+<% end %>
diff --git a/plugins/sonar-core-plugin/src/main/resources/org/sonar/plugins/core/widgets/measures/measure_filter_pie_chart.html.erb b/plugins/sonar-core-plugin/src/main/resources/org/sonar/plugins/core/widgets/measures/measure_filter_pie_chart.html.erb
index d7d2591b1ca..ca7af9c3d18 100644
--- a/plugins/sonar-core-plugin/src/main/resources/org/sonar/plugins/core/widgets/measures/measure_filter_pie_chart.html.erb
+++ b/plugins/sonar-core-plugin/src/main/resources/org/sonar/plugins/core/widgets/measures/measure_filter_pie_chart.html.erb
@@ -6,7 +6,8 @@
maxItems = widget_properties["maxItems"].to_i
filter = MeasureFilter.find_by_id(filterId.to_i)
- @widget_title = link_to h(filter.name), {:controller => 'measures', :action => 'filter', :id => filter.id, :display => 'list'}
+ if filter
+ @widget_title = link_to h(filter.name), {:controller => 'measures', :action => 'filter', :id => filter.id, :display => 'list'}
%>
<div class="pie-chart-widget" id="<%= containerId %>">
@@ -58,6 +59,6 @@
})();
</script>
<!--<![endif]-->
-
-
-
+<% else %>
+ <p><%= image_tag 'warning.png' %> <%= message 'measure_filter.widget.unknown_filter_warning' -%></p>
+<% end %>
diff --git a/plugins/sonar-core-plugin/src/main/resources/org/sonar/plugins/core/widgets/measures/measure_filter_treemap.html.erb b/plugins/sonar-core-plugin/src/main/resources/org/sonar/plugins/core/widgets/measures/measure_filter_treemap.html.erb
index 53911880335..771dc43a823 100644
--- a/plugins/sonar-core-plugin/src/main/resources/org/sonar/plugins/core/widgets/measures/measure_filter_treemap.html.erb
+++ b/plugins/sonar-core-plugin/src/main/resources/org/sonar/plugins/core/widgets/measures/measure_filter_treemap.html.erb
@@ -5,7 +5,8 @@
maxItems = widget_properties['maxItems'].to_i
filter = MeasureFilter.find_by_id(filterId.to_i)
- @widget_title = link_to h(filter.name), {:controller => 'measures', :action => 'filter', :id => filter.id, :display => 'list'}
+ if filter
+ @widget_title = link_to h(filter.name), {:controller => 'measures', :action => 'filter', :id => filter.id, :display => 'list'}
%>
<div class="treemap-widget" id="<%= containerId %>">
@@ -56,3 +57,6 @@
})();
</script>
<!--<![endif]-->
+<% else %>
+ <p><%= image_tag 'warning.png' %> <%= message 'measure_filter.widget.unknown_filter_warning' -%></p>
+<% end %>
diff --git a/plugins/sonar-core-plugin/src/main/resources/org/sonar/plugins/core/widgets/size.html.erb b/plugins/sonar-core-plugin/src/main/resources/org/sonar/plugins/core/widgets/size.html.erb
index 7ee4fb0e229..e68012f744b 100644
--- a/plugins/sonar-core-plugin/src/main/resources/org/sonar/plugins/core/widgets/size.html.erb
+++ b/plugins/sonar-core-plugin/src/main/resources/org/sonar/plugins/core/widgets/size.html.erb
@@ -32,13 +32,13 @@
<% if ncloc_language_dist_hash %>
<% if ncloc_language_dist_hash.size > 1 %>
- <table class="data widget-barchar">
+ <table class="data widget-barchar" id="size-widget-language-dist">
<%
- max = ncloc_language_dist_hash.max_by{|_k,v| v.to_i}[1].to_i
+ max = Math.sqrt(ncloc_language_dist_hash.max_by{|_k,v| v.to_i}[1].to_i)
- # Sort lines language distribution by language name
+ # Sort lines language distribution by lines count
languages_by_key = Hash[languages.collect { |l| [l.getKey(), l.getName] }]
- ncloc_language_dist_hash.sort {|v1,v2| (languages_by_key[v1[0]] ? languages_by_key[v1[0]].to_s : v1[0]) <=> (languages_by_key[v2[0]] ? languages_by_key[v2[0]].to_s : v2[0]) }.each do |language_key, language_ncloc|
+ ncloc_language_dist_hash.sort {|v1,v2| v2[1].to_i <=> v1[1].to_i }.each do |language_key, language_ncloc|
%>
<tr>
<td>
@@ -48,12 +48,23 @@
<td class="thin right nowrap">
<%= ncloc.format_numeric_value(language_ncloc) %>
</td>
- <td>
- <%= barchart(:width => 70, :percent => (40 * language_ncloc.to_i / max).to_i) %>
- </td>
</tr>
<% end %>
</table>
+ <script>
+ jQuery(function () {
+ var chart = jQuery('#size-widget-language-dist'),
+ count = chart.find('tr').length;
+ if (count > 3) {
+ chart.find('tr:gt(2)').hide();
+ var moreLinkBox = jQuery('<div></div>').addClass('widget-barchart-more').insertAfter(chart),
+ moreLink = jQuery('<a><%= message('more') -%></a>').appendTo(moreLinkBox).on('click', function () {
+ jQuery(this).hide();
+ chart.find('tr').show();
+ });
+ }
+ });
+ </script>
<% else %>
<%
language_key = ncloc_language_dist_hash.first[0]
diff --git a/plugins/sonar-cpd-plugin/pom.xml b/plugins/sonar-cpd-plugin/pom.xml
index 2903ae5a4be..334122c4b91 100644
--- a/plugins/sonar-cpd-plugin/pom.xml
+++ b/plugins/sonar-cpd-plugin/pom.xml
@@ -54,6 +54,11 @@
<artifactId>mockito-core</artifactId>
<scope>test</scope>
</dependency>
+ <dependency>
+ <groupId>org.codehaus.sonar.plugins</groupId>
+ <artifactId>sonar-xoo-plugin</artifactId>
+ <scope>test</scope>
+ </dependency>
</dependencies>
<build>
diff --git a/plugins/sonar-cpd-plugin/src/main/java/org/sonar/plugins/cpd/CpdEngine.java b/plugins/sonar-cpd-plugin/src/main/java/org/sonar/plugins/cpd/CpdEngine.java
index 7548282c87b..2f2a7633f40 100644
--- a/plugins/sonar-cpd-plugin/src/main/java/org/sonar/plugins/cpd/CpdEngine.java
+++ b/plugins/sonar-cpd-plugin/src/main/java/org/sonar/plugins/cpd/CpdEngine.java
@@ -21,14 +21,13 @@ package org.sonar.plugins.cpd;
import org.slf4j.Logger;
import org.sonar.api.BatchExtension;
-import org.sonar.api.batch.SensorContext;
-import org.sonar.api.resources.Project;
+import org.sonar.api.batch.sensor.SensorContext;
public abstract class CpdEngine implements BatchExtension {
abstract boolean isLanguageSupported(String language);
- abstract void analyse(Project project, String language, SensorContext context);
+ abstract void analyse(String language, SensorContext context);
protected void logExclusions(String[] exclusions, Logger logger) {
if (exclusions.length > 0) {
diff --git a/plugins/sonar-cpd-plugin/src/main/java/org/sonar/plugins/cpd/CpdMappings.java b/plugins/sonar-cpd-plugin/src/main/java/org/sonar/plugins/cpd/CpdMappings.java
new file mode 100644
index 00000000000..3f0f9d83b8c
--- /dev/null
+++ b/plugins/sonar-cpd-plugin/src/main/java/org/sonar/plugins/cpd/CpdMappings.java
@@ -0,0 +1,51 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.plugins.cpd;
+
+import org.sonar.api.BatchComponent;
+import org.sonar.api.batch.CpdMapping;
+
+import javax.annotation.CheckForNull;
+
+public class CpdMappings implements BatchComponent {
+
+ private final CpdMapping[] mappings;
+
+ public CpdMappings(CpdMapping[] mappings) {
+ this.mappings = mappings;
+ }
+
+ public CpdMappings() {
+ this(new CpdMapping[0]);
+ }
+
+ @CheckForNull
+ public CpdMapping getMapping(String language) {
+ if (mappings != null) {
+ for (CpdMapping cpdMapping : mappings) {
+ if (cpdMapping.getLanguage().getKey().equals(language)) {
+ return cpdMapping;
+ }
+ }
+ }
+ return null;
+ }
+
+}
diff --git a/plugins/sonar-cpd-plugin/src/main/java/org/sonar/plugins/cpd/CpdPlugin.java b/plugins/sonar-cpd-plugin/src/main/java/org/sonar/plugins/cpd/CpdPlugin.java
index 5f9df9621e1..defa9fe260e 100644
--- a/plugins/sonar-cpd-plugin/src/main/java/org/sonar/plugins/cpd/CpdPlugin.java
+++ b/plugins/sonar-cpd-plugin/src/main/java/org/sonar/plugins/cpd/CpdPlugin.java
@@ -35,7 +35,7 @@ public final class CpdPlugin extends SonarPlugin {
public List getExtensions() {
return ImmutableList.of(
- PropertyDefinition.builder(CoreProperties.CPD_CROSS_RPOJECT)
+ PropertyDefinition.builder(CoreProperties.CPD_CROSS_PROJECT)
.defaultValue(CoreProperties.CPD_CROSS_RPOJECT_DEFAULT_VALUE + "")
.name("Cross project duplication detection")
.description("SonarQube supports the detection of cross project duplications. Activating this property will slightly increase each SonarQube analysis time.")
@@ -65,11 +65,12 @@ public final class CpdPlugin extends SonarPlugin {
.build(),
CpdSensor.class,
+ CpdMappings.class,
SumDuplicationsDecorator.class,
DuplicationDensityDecorator.class,
IndexFactory.class,
- SonarEngine.class,
- SonarBridgeEngine.class);
+ JavaCpdEngine.class,
+ DefaultCpdEngine.class);
}
}
diff --git a/plugins/sonar-cpd-plugin/src/main/java/org/sonar/plugins/cpd/CpdSensor.java b/plugins/sonar-cpd-plugin/src/main/java/org/sonar/plugins/cpd/CpdSensor.java
index 53a45416cb7..dfeabd65635 100644
--- a/plugins/sonar-cpd-plugin/src/main/java/org/sonar/plugins/cpd/CpdSensor.java
+++ b/plugins/sonar-cpd-plugin/src/main/java/org/sonar/plugins/cpd/CpdSensor.java
@@ -23,12 +23,14 @@ import com.google.common.annotations.VisibleForTesting;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.sonar.api.CoreProperties;
-import org.sonar.api.batch.Sensor;
-import org.sonar.api.batch.SensorContext;
+import org.sonar.api.batch.Phase;
import org.sonar.api.batch.fs.FileSystem;
+import org.sonar.api.batch.sensor.Sensor;
+import org.sonar.api.batch.sensor.SensorContext;
+import org.sonar.api.batch.sensor.SensorDescriptor;
import org.sonar.api.config.Settings;
-import org.sonar.api.resources.Project;
+@Phase(name = Phase.Name.POST)
public class CpdSensor implements Sensor {
private static final Logger LOG = LoggerFactory.getLogger(CpdSensor.class);
@@ -38,15 +40,17 @@ public class CpdSensor implements Sensor {
private Settings settings;
private FileSystem fs;
- public CpdSensor(SonarEngine sonarEngine, SonarBridgeEngine sonarBridgeEngine, Settings settings, FileSystem fs) {
+ public CpdSensor(JavaCpdEngine sonarEngine, DefaultCpdEngine sonarBridgeEngine, Settings settings, FileSystem fs) {
this.sonarEngine = sonarEngine;
this.sonarBridgeEngine = sonarBridgeEngine;
this.settings = settings;
this.fs = fs;
}
- public boolean shouldExecuteOnProject(Project project) {
- return true;
+ @Override
+ public void describe(SensorDescriptor descriptor) {
+ descriptor.name("CPD Sensor");
+
}
@VisibleForTesting
@@ -66,7 +70,8 @@ public class CpdSensor implements Sensor {
return settings.getBoolean(CoreProperties.CPD_SKIP_PROPERTY);
}
- public void analyse(Project project, SensorContext context) {
+ @Override
+ public void execute(SensorContext context) {
for (String language : fs.languages()) {
if (isSkipped(language)) {
LOG.info("Detection of duplicated code is skipped for {}", language);
@@ -79,13 +84,8 @@ public class CpdSensor implements Sensor {
continue;
}
LOG.info("{} is used for {}", engine, language);
- engine.analyse(project, language, context);
+ engine.analyse(language, context);
}
}
- @Override
- public String toString() {
- return getClass().getSimpleName();
- }
-
}
diff --git a/plugins/sonar-cpd-plugin/src/main/java/org/sonar/plugins/cpd/SonarBridgeEngine.java b/plugins/sonar-cpd-plugin/src/main/java/org/sonar/plugins/cpd/DefaultCpdEngine.java
index df4bc036115..33558ec898a 100644
--- a/plugins/sonar-cpd-plugin/src/main/java/org/sonar/plugins/cpd/SonarBridgeEngine.java
+++ b/plugins/sonar-cpd-plugin/src/main/java/org/sonar/plugins/cpd/DefaultCpdEngine.java
@@ -27,22 +27,25 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.sonar.api.CoreProperties;
import org.sonar.api.batch.CpdMapping;
-import org.sonar.api.batch.SensorContext;
import org.sonar.api.batch.fs.FilePredicates;
import org.sonar.api.batch.fs.FileSystem;
import org.sonar.api.batch.fs.InputFile;
import org.sonar.api.batch.fs.internal.DeprecatedDefaultInputFile;
+import org.sonar.api.batch.sensor.SensorContext;
import org.sonar.api.config.Settings;
import org.sonar.api.resources.Project;
import org.sonar.api.utils.SonarException;
+import org.sonar.batch.duplication.BlockCache;
import org.sonar.duplications.DuplicationPredicates;
import org.sonar.duplications.block.Block;
+import org.sonar.duplications.block.FileBlocks;
import org.sonar.duplications.index.CloneGroup;
import org.sonar.duplications.internal.pmd.TokenizerBridge;
import org.sonar.plugins.cpd.index.IndexFactory;
import org.sonar.plugins.cpd.index.SonarDuplicationsIndex;
-import javax.annotation.CheckForNull;
+import javax.annotation.Nullable;
+
import java.util.Collection;
import java.util.List;
import java.util.concurrent.ExecutionException;
@@ -51,9 +54,9 @@ import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
-public class SonarBridgeEngine extends CpdEngine {
+public class DefaultCpdEngine extends CpdEngine {
- private static final Logger LOG = LoggerFactory.getLogger(SonarBridgeEngine.class);
+ private static final Logger LOG = LoggerFactory.getLogger(DefaultCpdEngine.class);
/**
* Limit of time to analyse one file (in seconds).
@@ -61,28 +64,32 @@ public class SonarBridgeEngine extends CpdEngine {
private static final int TIMEOUT = 5 * 60;
private final IndexFactory indexFactory;
- private final CpdMapping[] mappings;
+ private final CpdMappings mappings;
private final FileSystem fs;
private final Settings settings;
+ private final BlockCache duplicationCache;
+ private final Project project;
- public SonarBridgeEngine(IndexFactory indexFactory, CpdMapping[] mappings, FileSystem fs, Settings settings) {
+ public DefaultCpdEngine(@Nullable Project project, IndexFactory indexFactory, CpdMappings mappings, FileSystem fs, Settings settings, BlockCache duplicationCache) {
+ this.project = project;
this.indexFactory = indexFactory;
this.mappings = mappings;
this.fs = fs;
this.settings = settings;
+ this.duplicationCache = duplicationCache;
}
- public SonarBridgeEngine(IndexFactory indexFactory, FileSystem fs, Settings settings) {
- this(indexFactory, new CpdMapping[0], fs, settings);
+ public DefaultCpdEngine(IndexFactory indexFactory, CpdMappings mappings, FileSystem fs, Settings settings, BlockCache duplicationCache) {
+ this(null, indexFactory, mappings, fs, settings, duplicationCache);
}
@Override
public boolean isLanguageSupported(String language) {
- return getMapping(language) != null;
+ return true;
}
@Override
- public void analyse(Project project, String languageKey, SensorContext context) {
+ public void analyse(String languageKey, SensorContext context) {
String[] cpdExclusions = settings.getStringArray(CoreProperties.CPD_EXCLUSIONS);
logExclusions(cpdExclusions, LOG);
FilePredicates p = fs.predicates();
@@ -90,29 +97,34 @@ public class SonarBridgeEngine extends CpdEngine {
p.hasType(InputFile.Type.MAIN),
p.hasLanguage(languageKey),
p.doesNotMatchPathPatterns(cpdExclusions)
- )));
+ )));
if (sourceFiles.isEmpty()) {
return;
}
- CpdMapping mapping = getMapping(languageKey);
- if (mapping == null) {
- return;
- }
+ CpdMapping mapping = mappings.getMapping(languageKey);
// Create index
SonarDuplicationsIndex index = indexFactory.create(project, languageKey);
- TokenizerBridge bridge = new TokenizerBridge(mapping.getTokenizer(), fs.encoding().name(), getBlockSize(project, languageKey));
+ TokenizerBridge bridge = null;
+ if (mapping != null) {
+ bridge = new TokenizerBridge(mapping.getTokenizer(), fs.encoding().name(), getBlockSize(languageKey));
+ }
for (InputFile inputFile : sourceFiles) {
LOG.debug("Populating index from {}", inputFile);
String resourceEffectiveKey = ((DeprecatedDefaultInputFile) inputFile).key();
- List<Block> blocks = bridge.chunk(resourceEffectiveKey, inputFile.file());
- index.insert(inputFile, blocks);
+ FileBlocks fileBlocks = duplicationCache.byComponent(resourceEffectiveKey);
+ if (fileBlocks != null) {
+ index.insert(inputFile, fileBlocks.blocks());
+ } else if (bridge != null) {
+ List<Block> blocks2 = bridge.chunk(resourceEffectiveKey, inputFile.file());
+ index.insert(inputFile, blocks2);
+ }
}
// Detect
- Predicate<CloneGroup> minimumTokensPredicate = DuplicationPredicates.numberOfUnitsNotLessThan(getMinimumTokens(project, languageKey));
+ Predicate<CloneGroup> minimumTokensPredicate = DuplicationPredicates.numberOfUnitsNotLessThan(getMinimumTokens(languageKey));
ExecutorService executorService = Executors.newSingleThreadExecutor();
try {
@@ -123,7 +135,7 @@ public class SonarBridgeEngine extends CpdEngine {
Iterable<CloneGroup> filtered;
try {
- List<CloneGroup> duplications = executorService.submit(new SonarEngine.Task(index, fileBlocks)).get(TIMEOUT, TimeUnit.SECONDS);
+ List<CloneGroup> duplications = executorService.submit(new JavaCpdEngine.Task(index, fileBlocks)).get(TIMEOUT, TimeUnit.SECONDS);
filtered = Iterables.filter(duplications, minimumTokensPredicate);
} catch (TimeoutException e) {
filtered = null;
@@ -134,7 +146,7 @@ public class SonarBridgeEngine extends CpdEngine {
throw new SonarException("Fail during detection of duplication for " + inputFile, e);
}
- SonarEngine.save(context, inputFile, filtered);
+ JavaCpdEngine.save(context, inputFile, filtered);
}
} finally {
executorService.shutdown();
@@ -142,7 +154,7 @@ public class SonarBridgeEngine extends CpdEngine {
}
@VisibleForTesting
- int getBlockSize(Project project, String languageKey) {
+ int getBlockSize(String languageKey) {
int blockSize = settings.getInt("sonar.cpd." + languageKey + ".minimumLines");
if (blockSize == 0) {
blockSize = getDefaultBlockSize(languageKey);
@@ -162,7 +174,7 @@ public class SonarBridgeEngine extends CpdEngine {
}
@VisibleForTesting
- int getMinimumTokens(Project project, String languageKey) {
+ int getMinimumTokens(String languageKey) {
int minimumTokens = settings.getInt("sonar.cpd." + languageKey + ".minimumTokens");
if (minimumTokens == 0) {
minimumTokens = settings.getInt(CoreProperties.CPD_MINIMUM_TOKENS_PROPERTY);
@@ -174,16 +186,4 @@ public class SonarBridgeEngine extends CpdEngine {
return minimumTokens;
}
- @CheckForNull
- private CpdMapping getMapping(String language) {
- if (mappings != null) {
- for (CpdMapping cpdMapping : mappings) {
- if (cpdMapping.getLanguage().getKey().equals(language)) {
- return cpdMapping;
- }
- }
- }
- return null;
- }
-
}
diff --git a/plugins/sonar-cpd-plugin/src/main/java/org/sonar/plugins/cpd/SonarEngine.java b/plugins/sonar-cpd-plugin/src/main/java/org/sonar/plugins/cpd/JavaCpdEngine.java
index 130206427fc..2ea908dbb27 100644
--- a/plugins/sonar-cpd-plugin/src/main/java/org/sonar/plugins/cpd/SonarEngine.java
+++ b/plugins/sonar-cpd-plugin/src/main/java/org/sonar/plugins/cpd/JavaCpdEngine.java
@@ -23,21 +23,20 @@ package org.sonar.plugins.cpd;
import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;
import org.apache.commons.io.IOUtils;
-import org.apache.commons.lang.StringEscapeUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.sonar.api.CoreProperties;
-import org.sonar.api.batch.SensorContext;
import org.sonar.api.batch.fs.FilePredicates;
import org.sonar.api.batch.fs.FileSystem;
import org.sonar.api.batch.fs.InputFile;
import org.sonar.api.batch.fs.internal.DeprecatedDefaultInputFile;
+import org.sonar.api.batch.sensor.SensorContext;
+import org.sonar.api.batch.sensor.duplication.DuplicationBuilder;
import org.sonar.api.config.Settings;
import org.sonar.api.measures.CoreMetrics;
-import org.sonar.api.measures.Measure;
-import org.sonar.api.measures.PersistenceMode;
import org.sonar.api.resources.Project;
import org.sonar.api.utils.SonarException;
+import org.sonar.batch.duplication.DefaultDuplicationBuilder;
import org.sonar.duplications.block.Block;
import org.sonar.duplications.block.BlockChunker;
import org.sonar.duplications.detector.suffixtree.SuffixTreeCloneDetectionAlgorithm;
@@ -53,6 +52,7 @@ import org.sonar.plugins.cpd.index.IndexFactory;
import org.sonar.plugins.cpd.index.SonarDuplicationsIndex;
import javax.annotation.Nullable;
+
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.InputStreamReader;
@@ -61,11 +61,16 @@ import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
-import java.util.concurrent.*;
+import java.util.concurrent.Callable;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
-public class SonarEngine extends CpdEngine {
+public class JavaCpdEngine extends CpdEngine {
- private static final Logger LOG = LoggerFactory.getLogger(SonarEngine.class);
+ private static final Logger LOG = LoggerFactory.getLogger(JavaCpdEngine.class);
private static final int BLOCK_SIZE = 10;
@@ -77,20 +82,26 @@ public class SonarEngine extends CpdEngine {
private final IndexFactory indexFactory;
private final FileSystem fs;
private final Settings settings;
+ private final Project project;
- public SonarEngine(IndexFactory indexFactory, FileSystem fs, Settings settings) {
+ public JavaCpdEngine(@Nullable Project project, IndexFactory indexFactory, FileSystem fs, Settings settings) {
+ this.project = project;
this.indexFactory = indexFactory;
this.fs = fs;
this.settings = settings;
}
+ public JavaCpdEngine(IndexFactory indexFactory, FileSystem fs, Settings settings) {
+ this(null, indexFactory, fs, settings);
+ }
+
@Override
public boolean isLanguageSupported(String language) {
return "java".equals(language);
}
@Override
- public void analyse(Project project, String languageKey, SensorContext context) {
+ public void analyse(String languageKey, SensorContext context) {
String[] cpdExclusions = settings.getStringArray(CoreProperties.CPD_EXCLUSIONS);
logExclusions(cpdExclusions, LOG);
FilePredicates p = fs.predicates();
@@ -98,7 +109,7 @@ public class SonarEngine extends CpdEngine {
p.hasType(InputFile.Type.MAIN),
p.hasLanguage(languageKey),
p.doesNotMatchPathPatterns(cpdExclusions)
- )));
+ )));
if (sourceFiles.isEmpty()) {
return;
}
@@ -106,7 +117,7 @@ public class SonarEngine extends CpdEngine {
detect(index, context, sourceFiles);
}
- private SonarDuplicationsIndex createIndex(Project project, String language, Iterable<InputFile> sourceFiles) {
+ private SonarDuplicationsIndex createIndex(@Nullable Project project, String language, Iterable<InputFile> sourceFiles) {
final SonarDuplicationsIndex index = indexFactory.create(project, language);
TokenChunker tokenChunker = JavaTokenProducer.build();
@@ -136,7 +147,7 @@ public class SonarEngine extends CpdEngine {
return index;
}
- private void detect(SonarDuplicationsIndex index, SensorContext context, List<InputFile> sourceFiles) {
+ private void detect(SonarDuplicationsIndex index, org.sonar.api.batch.sensor.SensorContext context, List<InputFile> sourceFiles) {
ExecutorService executorService = Executors.newSingleThreadExecutor();
try {
for (InputFile inputFile : sourceFiles) {
@@ -178,13 +189,13 @@ public class SonarEngine extends CpdEngine {
}
}
- static void save(SensorContext context, InputFile inputFile, @Nullable Iterable<CloneGroup> duplications) {
+ static void save(org.sonar.api.batch.sensor.SensorContext context, InputFile inputFile, @Nullable Iterable<CloneGroup> duplications) {
if (duplications == null || Iterables.isEmpty(duplications)) {
return;
}
// Calculate number of lines and blocks
Set<Integer> duplicatedLines = new HashSet<Integer>();
- double duplicatedBlocks = 0;
+ int duplicatedBlocks = 0;
for (CloneGroup clone : duplications) {
ClonePart origin = clone.getOriginPart();
for (ClonePart part : clone.getCloneParts()) {
@@ -197,30 +208,32 @@ public class SonarEngine extends CpdEngine {
}
}
// Save
- context.saveMeasure(inputFile, CoreMetrics.DUPLICATED_FILES, 1.0);
- context.saveMeasure(inputFile, CoreMetrics.DUPLICATED_LINES, (double) duplicatedLines.size());
- context.saveMeasure(inputFile, CoreMetrics.DUPLICATED_BLOCKS, duplicatedBlocks);
-
- Measure data = new Measure(CoreMetrics.DUPLICATIONS_DATA, toXml(duplications))
- .setPersistenceMode(PersistenceMode.DATABASE);
- context.saveMeasure(inputFile, data);
- }
-
- private static String toXml(Iterable<CloneGroup> duplications) {
- StringBuilder xml = new StringBuilder();
- xml.append("<duplications>");
+ context.addMeasure(context.<Integer>measureBuilder()
+ .forMetric(CoreMetrics.DUPLICATED_FILES)
+ .onFile(inputFile)
+ .withValue(1)
+ .build());
+ context.addMeasure(context.<Integer>measureBuilder()
+ .forMetric(CoreMetrics.DUPLICATED_LINES)
+ .onFile(inputFile)
+ .withValue(duplicatedLines.size())
+ .build());
+ context.addMeasure(context.<Integer>measureBuilder()
+ .forMetric(CoreMetrics.DUPLICATED_BLOCKS)
+ .onFile(inputFile)
+ .withValue(duplicatedBlocks)
+ .build());
+
+ DuplicationBuilder builder = context.duplicationBuilder(inputFile);
for (CloneGroup duplication : duplications) {
- xml.append("<g>");
+ builder.originBlock(duplication.getOriginPart().getStartLine(), duplication.getOriginPart().getEndLine());
for (ClonePart part : duplication.getCloneParts()) {
- xml.append("<b s=\"").append(part.getStartLine())
- .append("\" l=\"").append(part.getLines())
- .append("\" r=\"").append(StringEscapeUtils.escapeXml(part.getResourceId()))
- .append("\"/>");
+ if (!part.equals(duplication.getOriginPart())) {
+ ((DefaultDuplicationBuilder) builder).isDuplicatedBy(part.getResourceId(), part.getStartLine(), part.getEndLine());
+ }
}
- xml.append("</g>");
}
- xml.append("</duplications>");
- return xml.toString();
+ builder.done();
}
}
diff --git a/plugins/sonar-cpd-plugin/src/main/java/org/sonar/plugins/cpd/index/IndexFactory.java b/plugins/sonar-cpd-plugin/src/main/java/org/sonar/plugins/cpd/index/IndexFactory.java
index f6f8981f6c1..6d5a7f1201d 100644
--- a/plugins/sonar-cpd-plugin/src/main/java/org/sonar/plugins/cpd/index/IndexFactory.java
+++ b/plugins/sonar-cpd-plugin/src/main/java/org/sonar/plugins/cpd/index/IndexFactory.java
@@ -23,29 +23,41 @@ import com.google.common.annotations.VisibleForTesting;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-import org.sonar.api.BatchExtension;
+import org.sonar.api.BatchComponent;
import org.sonar.api.CoreProperties;
import org.sonar.api.config.Settings;
import org.sonar.api.resources.Project;
+import org.sonar.batch.bootstrap.AnalysisMode;
import org.sonar.batch.index.ResourcePersister;
import org.sonar.core.duplication.DuplicationDao;
-public class IndexFactory implements BatchExtension {
+import javax.annotation.Nullable;
+
+public class IndexFactory implements BatchComponent {
private static final Logger LOG = LoggerFactory.getLogger(IndexFactory.class);
private final Settings settings;
private final ResourcePersister resourcePersister;
private final DuplicationDao dao;
+ private final AnalysisMode mode;
- public IndexFactory(Settings settings, ResourcePersister resourcePersister, DuplicationDao dao) {
+ public IndexFactory(AnalysisMode mode, Settings settings, @Nullable ResourcePersister resourcePersister, @Nullable DuplicationDao dao) {
+ this.mode = mode;
this.settings = settings;
this.resourcePersister = resourcePersister;
this.dao = dao;
}
- public SonarDuplicationsIndex create(Project project, String languageKey) {
- if (verifyCrossProject(project, LOG)) {
+ /**
+ * Used by new sensor mode
+ */
+ public IndexFactory(AnalysisMode mode, Settings settings) {
+ this(mode, settings, null, null);
+ }
+
+ public SonarDuplicationsIndex create(@Nullable Project project, String languageKey) {
+ if (verifyCrossProject(project, LOG) && dao != null && resourcePersister != null) {
return new SonarDuplicationsIndex(new DbDuplicationsIndex(resourcePersister, project, dao, languageKey));
}
return new SonarDuplicationsIndex();
@@ -55,11 +67,14 @@ public class IndexFactory implements BatchExtension {
boolean verifyCrossProject(Project project, Logger logger) {
boolean crossProject = false;
- if (settings.getBoolean(CoreProperties.CPD_CROSS_RPOJECT)) {
- if (settings.getBoolean(CoreProperties.DRY_RUN)) {
- logger.info("Cross-project analysis disabled. Not supported on dry runs.");
- } else if (StringUtils.isNotBlank(project.getBranch())) {
+ if (settings.getBoolean(CoreProperties.CPD_CROSS_PROJECT)) {
+ if (mode.isPreview()) {
+ logger.info("Cross-project analysis disabled. Not supported in preview mode.");
+ } else if (StringUtils.isNotBlank(settings.getString(CoreProperties.PROJECT_BRANCH_PROPERTY))) {
logger.info("Cross-project analysis disabled. Not supported on project branches.");
+ } else if (project == null) {
+ // New sensor mode
+ logger.info("Cross-project analysis disabled. Not supported in new sensor mode.");
} else {
logger.info("Cross-project analysis enabled");
crossProject = true;
diff --git a/plugins/sonar-cpd-plugin/src/test/java/org/sonar/plugins/cpd/CpdPluginTest.java b/plugins/sonar-cpd-plugin/src/test/java/org/sonar/plugins/cpd/CpdPluginTest.java
index 74cb7f5fa5b..7d1bbeb4a5c 100644
--- a/plugins/sonar-cpd-plugin/src/test/java/org/sonar/plugins/cpd/CpdPluginTest.java
+++ b/plugins/sonar-cpd-plugin/src/test/java/org/sonar/plugins/cpd/CpdPluginTest.java
@@ -27,6 +27,6 @@ public class CpdPluginTest {
@Test
public void getExtensions() {
- assertThat(new CpdPlugin().getExtensions()).hasSize(9);
+ assertThat(new CpdPlugin().getExtensions()).hasSize(10);
}
}
diff --git a/plugins/sonar-cpd-plugin/src/test/java/org/sonar/plugins/cpd/CpdSensorTest.java b/plugins/sonar-cpd-plugin/src/test/java/org/sonar/plugins/cpd/CpdSensorTest.java
index 89128b77a8a..7724fa6f725 100644
--- a/plugins/sonar-cpd-plugin/src/test/java/org/sonar/plugins/cpd/CpdSensorTest.java
+++ b/plugins/sonar-cpd-plugin/src/test/java/org/sonar/plugins/cpd/CpdSensorTest.java
@@ -25,6 +25,7 @@ import org.sonar.api.batch.fs.internal.DefaultFileSystem;
import org.sonar.api.config.PropertyDefinitions;
import org.sonar.api.config.Settings;
import org.sonar.api.resources.Java;
+import org.sonar.batch.duplication.BlockCache;
import org.sonar.plugins.cpd.index.IndexFactory;
import static org.fest.assertions.Assertions.assertThat;
@@ -32,16 +33,16 @@ import static org.mockito.Mockito.mock;
public class CpdSensorTest {
- SonarEngine sonarEngine;
- SonarBridgeEngine sonarBridgeEngine;
+ JavaCpdEngine sonarEngine;
+ DefaultCpdEngine sonarBridgeEngine;
CpdSensor sensor;
Settings settings;
@Before
public void setUp() {
IndexFactory indexFactory = mock(IndexFactory.class);
- sonarEngine = new SonarEngine(indexFactory, null, null);
- sonarBridgeEngine = new SonarBridgeEngine(indexFactory, null, null);
+ sonarEngine = new JavaCpdEngine(indexFactory, null, null);
+ sonarBridgeEngine = new DefaultCpdEngine(indexFactory, new CpdMappings(), null, null, mock(BlockCache.class));
settings = new Settings(new PropertyDefinitions(CpdPlugin.class));
DefaultFileSystem fs = new DefaultFileSystem();
diff --git a/plugins/sonar-cpd-plugin/src/test/java/org/sonar/plugins/cpd/SonarBridgeEngineTest.java b/plugins/sonar-cpd-plugin/src/test/java/org/sonar/plugins/cpd/DefaultCpdEngineTest.java
index 3923b53fe21..924aa9193c8 100644
--- a/plugins/sonar-cpd-plugin/src/test/java/org/sonar/plugins/cpd/SonarBridgeEngineTest.java
+++ b/plugins/sonar-cpd-plugin/src/test/java/org/sonar/plugins/cpd/DefaultCpdEngineTest.java
@@ -25,6 +25,7 @@ import org.slf4j.Logger;
import org.sonar.api.CoreProperties;
import org.sonar.api.config.Settings;
import org.sonar.api.resources.Project;
+import org.sonar.batch.duplication.BlockCache;
import static org.fest.assertions.Assertions.assertThat;
import static org.mockito.Matchers.anyString;
@@ -33,15 +34,15 @@ import static org.mockito.Mockito.never;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
-public class SonarBridgeEngineTest {
+public class DefaultCpdEngineTest {
- private SonarBridgeEngine engine;
+ private DefaultCpdEngine engine;
private Settings settings;
@Before
public void init() {
settings = new Settings();
- engine = new SonarBridgeEngine(null, null, null, settings);
+ engine = new DefaultCpdEngine(null, null, null, settings, mock(BlockCache.class));
}
@Test
@@ -61,53 +62,46 @@ public class SonarBridgeEngineTest {
@Test
public void shouldReturnDefaultBlockSize() {
- assertThat(SonarBridgeEngine.getDefaultBlockSize("cobol")).isEqualTo(30);
- assertThat(SonarBridgeEngine.getDefaultBlockSize("natur")).isEqualTo(20);
- assertThat(SonarBridgeEngine.getDefaultBlockSize("abap")).isEqualTo(20);
- assertThat(SonarBridgeEngine.getDefaultBlockSize("other")).isEqualTo(10);
+ assertThat(DefaultCpdEngine.getDefaultBlockSize("cobol")).isEqualTo(30);
+ assertThat(DefaultCpdEngine.getDefaultBlockSize("natur")).isEqualTo(20);
+ assertThat(DefaultCpdEngine.getDefaultBlockSize("abap")).isEqualTo(20);
+ assertThat(DefaultCpdEngine.getDefaultBlockSize("other")).isEqualTo(10);
}
@Test
public void defaultBlockSize() {
- Project project = newProject("foo");
- assertThat(engine.getBlockSize(project, "java")).isEqualTo(10);
+ assertThat(engine.getBlockSize("java")).isEqualTo(10);
}
@Test
public void blockSizeForCobol() {
- Project project = newProject("foo");
settings.setProperty("sonar.cpd.cobol.minimumLines", "42");
- assertThat(engine.getBlockSize(project, "cobol")).isEqualTo(42);
+ assertThat(engine.getBlockSize("cobol")).isEqualTo(42);
}
@Test
public void defaultMinimumTokens() {
- Project project = newProject("foo");
-
- assertThat(engine.getMinimumTokens(project, "java")).isEqualTo(CoreProperties.CPD_MINIMUM_TOKENS_DEFAULT_VALUE);
+ assertThat(engine.getMinimumTokens("java")).isEqualTo(CoreProperties.CPD_MINIMUM_TOKENS_DEFAULT_VALUE);
}
@Test
public void generalMinimumTokens() {
- Project project = newProject("foo");
settings.setProperty("sonar.cpd.minimumTokens", 33);
- assertThat(engine.getMinimumTokens(project, "java")).isEqualTo(33);
+ assertThat(engine.getMinimumTokens("java")).isEqualTo(33);
}
@Test
public void minimumTokensByLanguage() {
- Project javaProject = newProject("foo");
settings.setProperty("sonar.cpd.java.minimumTokens", "42");
settings.setProperty("sonar.cpd.php.minimumTokens", "33");
- assertThat(engine.getMinimumTokens(javaProject, "java")).isEqualTo(42);
+ assertThat(engine.getMinimumTokens("java")).isEqualTo(42);
- Project phpProject = newProject("foo");
settings.setProperty("sonar.cpd.java.minimumTokens", "42");
settings.setProperty("sonar.cpd.php.minimumTokens", "33");
- assertThat(engine.getMinimumTokens(phpProject, "php")).isEqualTo(33);
+ assertThat(engine.getMinimumTokens("php")).isEqualTo(33);
}
private static Project newProject(String key) {
diff --git a/plugins/sonar-cpd-plugin/src/test/java/org/sonar/plugins/cpd/JavaCpdEngineTest.java b/plugins/sonar-cpd-plugin/src/test/java/org/sonar/plugins/cpd/JavaCpdEngineTest.java
new file mode 100644
index 00000000000..770688d24a9
--- /dev/null
+++ b/plugins/sonar-cpd-plugin/src/test/java/org/sonar/plugins/cpd/JavaCpdEngineTest.java
@@ -0,0 +1,147 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.plugins.cpd;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.TemporaryFolder;
+import org.mockito.InOrder;
+import org.mockito.Mockito;
+import org.sonar.api.batch.fs.InputFile;
+import org.sonar.api.batch.fs.internal.DeprecatedDefaultInputFile;
+import org.sonar.api.batch.sensor.SensorContext;
+import org.sonar.api.batch.sensor.measure.internal.DefaultMeasureBuilder;
+import org.sonar.api.measures.CoreMetrics;
+import org.sonar.batch.duplication.DefaultDuplicationBuilder;
+import org.sonar.batch.duplication.DuplicationCache;
+import org.sonar.duplications.index.CloneGroup;
+import org.sonar.duplications.index.ClonePart;
+
+import java.io.IOException;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+
+import static org.mockito.Matchers.any;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyZeroInteractions;
+import static org.mockito.Mockito.when;
+
+public class JavaCpdEngineTest {
+
+ @Rule
+ public TemporaryFolder temp = new TemporaryFolder();
+
+ SensorContext context = mock(SensorContext.class);
+ DeprecatedDefaultInputFile inputFile;
+ private DefaultDuplicationBuilder duplicationBuilder;
+
+ @Before
+ public void before() throws IOException {
+ when(context.measureBuilder()).thenReturn(new DefaultMeasureBuilder());
+ inputFile = new DeprecatedDefaultInputFile("src/main/java/Foo.java");
+ DuplicationCache duplicationCache = mock(DuplicationCache.class);
+ duplicationBuilder = spy(new DefaultDuplicationBuilder(inputFile, duplicationCache));
+ when(context.duplicationBuilder(any(InputFile.class))).thenReturn(duplicationBuilder);
+ inputFile.setFile(temp.newFile("Foo.java"));
+ }
+
+ @SuppressWarnings("unchecked")
+ @Test
+ public void testNothingToSave() {
+ JavaCpdEngine.save(context, inputFile, null);
+ JavaCpdEngine.save(context, inputFile, Collections.EMPTY_LIST);
+
+ verifyZeroInteractions(context);
+ }
+
+ @Test
+ public void testOneSimpleDuplicationBetweenTwoFiles() {
+ List<CloneGroup> groups = Arrays.asList(newCloneGroup(new ClonePart("key1", 0, 5, 204), new ClonePart("key2", 0, 15, 214)));
+ JavaCpdEngine.save(context, inputFile, groups);
+
+ verify(context).addMeasure(new DefaultMeasureBuilder().forMetric(CoreMetrics.DUPLICATED_FILES).onFile(inputFile).withValue(1).build());
+ verify(context).addMeasure(new DefaultMeasureBuilder().forMetric(CoreMetrics.DUPLICATED_BLOCKS).onFile(inputFile).withValue(1).build());
+ verify(context).addMeasure(new DefaultMeasureBuilder().forMetric(CoreMetrics.DUPLICATED_LINES).onFile(inputFile).withValue(200).build());
+
+ InOrder inOrder = Mockito.inOrder(duplicationBuilder);
+ inOrder.verify(duplicationBuilder).originBlock(5, 204);
+ inOrder.verify(duplicationBuilder).isDuplicatedBy("key2", 15, 214);
+ inOrder.verify(duplicationBuilder).done();
+ }
+
+ @Test
+ public void testDuplicationOnSameFile() throws Exception {
+ List<CloneGroup> groups = Arrays.asList(newCloneGroup(new ClonePart("key1", 0, 5, 204), new ClonePart("key1", 0, 215, 414)));
+ JavaCpdEngine.save(context, inputFile, groups);
+
+ verify(context).addMeasure(new DefaultMeasureBuilder().forMetric(CoreMetrics.DUPLICATED_FILES).onFile(inputFile).withValue(1).build());
+ verify(context).addMeasure(new DefaultMeasureBuilder().forMetric(CoreMetrics.DUPLICATED_BLOCKS).onFile(inputFile).withValue(2).build());
+ verify(context).addMeasure(new DefaultMeasureBuilder().forMetric(CoreMetrics.DUPLICATED_LINES).onFile(inputFile).withValue(400).build());
+
+ InOrder inOrder = Mockito.inOrder(duplicationBuilder);
+ inOrder.verify(duplicationBuilder).originBlock(5, 204);
+ inOrder.verify(duplicationBuilder).isDuplicatedBy("key1", 215, 414);
+ inOrder.verify(duplicationBuilder).done();
+ }
+
+ @Test
+ public void testOneDuplicatedGroupInvolvingMoreThanTwoFiles() throws Exception {
+ List<CloneGroup> groups = Arrays.asList(newCloneGroup(new ClonePart("key1", 0, 5, 204), new ClonePart("key2", 0, 15, 214), new ClonePart("key3", 0, 25, 224)));
+ JavaCpdEngine.save(context, inputFile, groups);
+
+ verify(context).addMeasure(new DefaultMeasureBuilder().forMetric(CoreMetrics.DUPLICATED_FILES).onFile(inputFile).withValue(1).build());
+ verify(context).addMeasure(new DefaultMeasureBuilder().forMetric(CoreMetrics.DUPLICATED_BLOCKS).onFile(inputFile).withValue(1).build());
+ verify(context).addMeasure(new DefaultMeasureBuilder().forMetric(CoreMetrics.DUPLICATED_LINES).onFile(inputFile).withValue(200).build());
+
+ InOrder inOrder = Mockito.inOrder(duplicationBuilder);
+ inOrder.verify(duplicationBuilder).originBlock(5, 204);
+ inOrder.verify(duplicationBuilder).isDuplicatedBy("key2", 15, 214);
+ inOrder.verify(duplicationBuilder).isDuplicatedBy("key3", 25, 224);
+ inOrder.verify(duplicationBuilder).done();
+ }
+
+ @Test
+ public void testTwoDuplicatedGroupsInvolvingThreeFiles() throws Exception {
+ List<CloneGroup> groups = Arrays.asList(
+ newCloneGroup(new ClonePart("key1", 0, 5, 204), new ClonePart("key2", 0, 15, 214)),
+ newCloneGroup(new ClonePart("key1", 0, 15, 214), new ClonePart("key3", 0, 15, 214)));
+ JavaCpdEngine.save(context, inputFile, groups);
+
+ verify(context).addMeasure(new DefaultMeasureBuilder().forMetric(CoreMetrics.DUPLICATED_FILES).onFile(inputFile).withValue(1).build());
+ verify(context).addMeasure(new DefaultMeasureBuilder().forMetric(CoreMetrics.DUPLICATED_BLOCKS).onFile(inputFile).withValue(2).build());
+ verify(context).addMeasure(new DefaultMeasureBuilder().forMetric(CoreMetrics.DUPLICATED_LINES).onFile(inputFile).withValue(210).build());
+
+ InOrder inOrder = Mockito.inOrder(duplicationBuilder);
+ inOrder.verify(duplicationBuilder).originBlock(5, 204);
+ inOrder.verify(duplicationBuilder).isDuplicatedBy("key2", 15, 214);
+ inOrder.verify(duplicationBuilder).originBlock(15, 214);
+ inOrder.verify(duplicationBuilder).isDuplicatedBy("key3", 15, 214);
+ inOrder.verify(duplicationBuilder).done();
+ }
+
+ private CloneGroup newCloneGroup(ClonePart... parts) {
+ return CloneGroup.builder().setLength(0).setOrigin(parts[0]).setParts(Arrays.asList(parts)).build();
+ }
+
+}
diff --git a/plugins/sonar-cpd-plugin/src/test/java/org/sonar/plugins/cpd/SonarEngineTest.java b/plugins/sonar-cpd-plugin/src/test/java/org/sonar/plugins/cpd/SonarEngineTest.java
deleted file mode 100644
index c8551a74166..00000000000
--- a/plugins/sonar-cpd-plugin/src/test/java/org/sonar/plugins/cpd/SonarEngineTest.java
+++ /dev/null
@@ -1,160 +0,0 @@
-/*
- * SonarQube, open source software quality management tool.
- * Copyright (C) 2008-2014 SonarSource
- * mailto:contact AT sonarsource DOT com
- *
- * SonarQube is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * SonarQube is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- */
-package org.sonar.plugins.cpd;
-
-import org.junit.Before;
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.rules.TemporaryFolder;
-import org.sonar.api.batch.SensorContext;
-import org.sonar.api.batch.fs.InputFile;
-import org.sonar.api.batch.fs.internal.DeprecatedDefaultInputFile;
-import org.sonar.api.measures.CoreMetrics;
-import org.sonar.api.test.IsMeasure;
-import org.sonar.duplications.index.CloneGroup;
-import org.sonar.duplications.index.ClonePart;
-
-import java.io.IOException;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.List;
-
-import static org.mockito.Matchers.argThat;
-import static org.mockito.Matchers.eq;
-import static org.mockito.Mockito.*;
-
-public class SonarEngineTest {
-
- @Rule
- public TemporaryFolder temp = new TemporaryFolder();
-
- SensorContext context = mock(SensorContext.class);
- DeprecatedDefaultInputFile inputFile;
-
- @Before
- public void before() throws IOException {
- inputFile = new DeprecatedDefaultInputFile("src/main/java/Foo.java");
- inputFile.setFile(temp.newFile("Foo.java"));
- }
-
- @SuppressWarnings("unchecked")
- @Test
- public void testNothingToSave() {
- SonarEngine.save(context, inputFile, null);
- SonarEngine.save(context, inputFile, Collections.EMPTY_LIST);
-
- verifyZeroInteractions(context);
- }
-
- @Test
- public void testOneSimpleDuplicationBetweenTwoFiles() {
- List<CloneGroup> groups = Arrays.asList(newCloneGroup(new ClonePart("key1", 0, 5, 204), new ClonePart("key2", 0, 15, 214)));
- SonarEngine.save(context, inputFile, groups);
-
- verify(context).saveMeasure(inputFile, CoreMetrics.DUPLICATED_FILES, 1d);
- verify(context).saveMeasure(inputFile, CoreMetrics.DUPLICATED_BLOCKS, 1d);
- verify(context).saveMeasure(inputFile, CoreMetrics.DUPLICATED_LINES, 200d);
- verify(context).saveMeasure(
- eq(inputFile),
- argThat(new IsMeasure(CoreMetrics.DUPLICATIONS_DATA, "<duplications><g>"
- + "<b s=\"5\" l=\"200\" r=\"key1\"/>"
- + "<b s=\"15\" l=\"200\" r=\"key2\"/>"
- + "</g></duplications>")));
- }
-
- @Test
- public void testDuplicationOnSameFile() throws Exception {
- List<CloneGroup> groups = Arrays.asList(newCloneGroup(new ClonePart("key1", 0, 5, 204), new ClonePart("key1", 0, 215, 414)));
- SonarEngine.save(context, inputFile, groups);
-
- verify(context).saveMeasure(inputFile, CoreMetrics.DUPLICATED_FILES, 1d);
- verify(context).saveMeasure(inputFile, CoreMetrics.DUPLICATED_LINES, 400d);
- verify(context).saveMeasure(inputFile, CoreMetrics.DUPLICATED_BLOCKS, 2d);
- verify(context).saveMeasure(
- eq(inputFile),
- argThat(new IsMeasure(CoreMetrics.DUPLICATIONS_DATA, "<duplications><g>"
- + "<b s=\"5\" l=\"200\" r=\"key1\"/>"
- + "<b s=\"215\" l=\"200\" r=\"key1\"/>"
- + "</g></duplications>")));
- }
-
- @Test
- public void testOneDuplicatedGroupInvolvingMoreThanTwoFiles() throws Exception {
- List<CloneGroup> groups = Arrays.asList(newCloneGroup(new ClonePart("key1", 0, 5, 204), new ClonePart("key2", 0, 15, 214), new ClonePart("key3", 0, 25, 224)));
- SonarEngine.save(context, inputFile, groups);
-
- verify(context).saveMeasure(inputFile, CoreMetrics.DUPLICATED_FILES, 1d);
- verify(context).saveMeasure(inputFile, CoreMetrics.DUPLICATED_BLOCKS, 1d);
- verify(context).saveMeasure(inputFile, CoreMetrics.DUPLICATED_LINES, 200d);
- verify(context).saveMeasure(
- eq(inputFile),
- argThat(new IsMeasure(CoreMetrics.DUPLICATIONS_DATA, "<duplications><g>"
- + "<b s=\"5\" l=\"200\" r=\"key1\"/>"
- + "<b s=\"15\" l=\"200\" r=\"key2\"/>"
- + "<b s=\"25\" l=\"200\" r=\"key3\"/>"
- + "</g></duplications>")));
- }
-
- @Test
- public void testTwoDuplicatedGroupsInvolvingThreeFiles() throws Exception {
- List<CloneGroup> groups = Arrays.asList(
- newCloneGroup(new ClonePart("key1", 0, 5, 204), new ClonePart("key2", 0, 15, 214)),
- newCloneGroup(new ClonePart("key1", 0, 15, 214), new ClonePart("key3", 0, 15, 214)));
- SonarEngine.save(context, inputFile, groups);
-
- verify(context).saveMeasure(inputFile, CoreMetrics.DUPLICATED_FILES, 1d);
- verify(context).saveMeasure(inputFile, CoreMetrics.DUPLICATED_BLOCKS, 2d);
- verify(context).saveMeasure(inputFile, CoreMetrics.DUPLICATED_LINES, 210d);
- verify(context).saveMeasure(
- eq(inputFile),
- argThat(new IsMeasure(CoreMetrics.DUPLICATIONS_DATA, "<duplications>"
- + "<g>"
- + "<b s=\"5\" l=\"200\" r=\"key1\"/>"
- + "<b s=\"15\" l=\"200\" r=\"key2\"/>"
- + "</g>"
- + "<g>"
- + "<b s=\"15\" l=\"200\" r=\"key1\"/>"
- + "<b s=\"15\" l=\"200\" r=\"key3\"/>"
- + "</g>"
- + "</duplications>")));
- }
-
- @Test
- public void shouldEscapeXmlEntities() throws IOException {
- InputFile csharpFile = new DeprecatedDefaultInputFile("Loads/File Loads/Subs & Reds/SubsRedsDelivery.cs")
- .setFile(temp.newFile("SubsRedsDelivery.cs"));
- List<CloneGroup> groups = Arrays.asList(newCloneGroup(
- new ClonePart("Loads/File Loads/Subs & Reds/SubsRedsDelivery.cs", 0, 5, 204),
- new ClonePart("Loads/File Loads/Subs & Reds/SubsRedsDelivery2.cs", 0, 15, 214)));
- SonarEngine.save(context, csharpFile, groups);
-
- verify(context).saveMeasure(
- eq(csharpFile),
- argThat(new IsMeasure(CoreMetrics.DUPLICATIONS_DATA, "<duplications><g>"
- + "<b s=\"5\" l=\"200\" r=\"Loads/File Loads/Subs &amp; Reds/SubsRedsDelivery.cs\"/>"
- + "<b s=\"15\" l=\"200\" r=\"Loads/File Loads/Subs &amp; Reds/SubsRedsDelivery2.cs\"/>"
- + "</g></duplications>")));
- }
-
- private CloneGroup newCloneGroup(ClonePart... parts) {
- return CloneGroup.builder().setLength(0).setOrigin(parts[0]).setParts(Arrays.asList(parts)).build();
- }
-
-}
diff --git a/plugins/sonar-cpd-plugin/src/test/java/org/sonar/plugins/cpd/index/IndexFactoryTest.java b/plugins/sonar-cpd-plugin/src/test/java/org/sonar/plugins/cpd/index/IndexFactoryTest.java
index 45510dc40ac..7b9fa524fec 100644
--- a/plugins/sonar-cpd-plugin/src/test/java/org/sonar/plugins/cpd/index/IndexFactoryTest.java
+++ b/plugins/sonar-cpd-plugin/src/test/java/org/sonar/plugins/cpd/index/IndexFactoryTest.java
@@ -25,12 +25,14 @@ import org.slf4j.Logger;
import org.sonar.api.CoreProperties;
import org.sonar.api.config.Settings;
import org.sonar.api.resources.Project;
+import org.sonar.batch.bootstrap.AnalysisMode;
import org.sonar.batch.index.ResourcePersister;
import org.sonar.core.duplication.DuplicationDao;
import static org.fest.assertions.Assertions.assertThat;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
public class IndexFactoryTest {
@@ -38,41 +40,43 @@ public class IndexFactoryTest {
Settings settings;
IndexFactory factory;
Logger logger;
+ private AnalysisMode analysisMode;
@Before
public void setUp() {
project = new Project("foo");
settings = new Settings();
- factory = new IndexFactory(settings, mock(ResourcePersister.class), mock(DuplicationDao.class));
+ analysisMode = mock(AnalysisMode.class);
+ factory = new IndexFactory(analysisMode, settings, mock(ResourcePersister.class), mock(DuplicationDao.class));
logger = mock(Logger.class);
}
@Test
public void crossProjectEnabled() {
- settings.setProperty(CoreProperties.CPD_CROSS_RPOJECT, "true");
+ settings.setProperty(CoreProperties.CPD_CROSS_PROJECT, "true");
assertThat(factory.verifyCrossProject(project, logger)).isTrue();
verify(logger).info("Cross-project analysis enabled");
}
@Test
public void noCrossProjectWithBranch() {
- settings.setProperty(CoreProperties.CPD_CROSS_RPOJECT, "true");
- project.setBranch("branch");
+ settings.setProperty(CoreProperties.CPD_CROSS_PROJECT, "true");
+ settings.setProperty(CoreProperties.PROJECT_BRANCH_PROPERTY, "branch");
assertThat(factory.verifyCrossProject(project, logger)).isFalse();
verify(logger).info("Cross-project analysis disabled. Not supported on project branches.");
}
@Test
- public void cross_project_should_be_disabled_on_dry_run() {
- settings.setProperty(CoreProperties.CPD_CROSS_RPOJECT, "true");
- settings.setProperty(CoreProperties.DRY_RUN, "true");
+ public void cross_project_should_be_disabled_on_preview() {
+ when(analysisMode.isPreview()).thenReturn(true);
+ settings.setProperty(CoreProperties.CPD_CROSS_PROJECT, "true");
assertThat(factory.verifyCrossProject(project, logger)).isFalse();
- verify(logger).info("Cross-project analysis disabled. Not supported on dry runs.");
+ verify(logger).info("Cross-project analysis disabled. Not supported in preview mode.");
}
@Test
public void crossProjectDisabled() {
- settings.setProperty(CoreProperties.CPD_CROSS_RPOJECT, "false");
+ settings.setProperty(CoreProperties.CPD_CROSS_PROJECT, "false");
assertThat(factory.verifyCrossProject(project, logger)).isFalse();
verify(logger).info("Cross-project analysis disabled");
}
diff --git a/plugins/sonar-cpd-plugin/src/test/java/org/sonar/plugins/cpd/medium/CpdMediumTest.java b/plugins/sonar-cpd-plugin/src/test/java/org/sonar/plugins/cpd/medium/CpdMediumTest.java
new file mode 100644
index 00000000000..21876b8f3d5
--- /dev/null
+++ b/plugins/sonar-cpd-plugin/src/test/java/org/sonar/plugins/cpd/medium/CpdMediumTest.java
@@ -0,0 +1,119 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.plugins.cpd.medium;
+
+import com.google.common.collect.ImmutableMap;
+import org.apache.commons.io.FileUtils;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+import org.junit.rules.TemporaryFolder;
+import org.sonar.api.batch.fs.InputFile;
+import org.sonar.batch.duplication.DuplicationGroup;
+import org.sonar.batch.mediumtest.BatchMediumTester;
+import org.sonar.batch.mediumtest.BatchMediumTester.TaskResult;
+import org.sonar.plugins.cpd.CpdPlugin;
+import org.sonar.xoo.XooPlugin;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.List;
+
+import static org.fest.assertions.Assertions.assertThat;
+
+public class CpdMediumTest {
+
+ @Rule
+ public TemporaryFolder temp = new TemporaryFolder();
+
+ @Rule
+ public ExpectedException thrown = ExpectedException.none();
+
+ public BatchMediumTester tester = BatchMediumTester.builder()
+ .registerPlugin("xoo", new XooPlugin())
+ .registerPlugin("cpd", new CpdPlugin())
+ .addDefaultQProfile("xoo", "Sonar Way")
+ .bootstrapProperties(ImmutableMap.of("sonar.analysis.mode", "sensor"))
+ .build();
+
+ private File baseDir;
+
+ private ImmutableMap.Builder<String, String> builder;
+
+ @Before
+ public void prepare() throws IOException {
+ tester.start();
+
+ baseDir = temp.newFolder();
+
+ builder = ImmutableMap.<String, String>builder()
+ .put("sonar.task", "scan")
+ .put("sonar.projectBaseDir", baseDir.getAbsolutePath())
+ .put("sonar.projectKey", "com.foo.project")
+ .put("sonar.projectName", "Foo Project")
+ .put("sonar.projectVersion", "1.0-SNAPSHOT")
+ .put("sonar.projectDescription", "Description of Foo Project");
+ }
+
+ @After
+ public void stop() {
+ tester.stop();
+ }
+
+ @Test
+ public void testDuplications() throws IOException {
+ File srcDir = new File(baseDir, "src");
+ srcDir.mkdir();
+
+ String duplicatedStuff = "Sample xoo\ncontent\nfoo\nbar\ntoto\ntiti\nfoo\nbar\ntoto\ntiti\nbar\ntoto\ntiti\nfoo\nbar\ntoto\ntiti";
+
+ File xooFile1 = new File(srcDir, "sample1.xoo");
+ FileUtils.write(xooFile1, duplicatedStuff);
+
+ File xooFile2 = new File(srcDir, "sample2.xoo");
+ FileUtils.write(xooFile2, duplicatedStuff);
+
+ TaskResult result = tester.newTask()
+ .properties(builder
+ .put("sonar.sources", "src")
+ .put("sonar.cpd.xoo.minimumTokens", "10")
+ .put("sonar.verbose", "true")
+ .build())
+ .start();
+
+ assertThat(result.inputFiles()).hasSize(2);
+
+ // 4 measures per file
+ assertThat(result.measures()).hasSize(6);
+
+ InputFile inputFile = result.inputFiles().get(0);
+ // One clone group
+ List<DuplicationGroup> duplicationGroups = result.duplicationsFor(inputFile);
+ assertThat(duplicationGroups).hasSize(1);
+
+ DuplicationGroup cloneGroup = duplicationGroups.get(0);
+ assertThat(cloneGroup.duplicates()).hasSize(1);
+ assertThat(cloneGroup.originBlock().startLine()).isEqualTo(1);
+ assertThat(cloneGroup.originBlock().length()).isEqualTo(17);
+ }
+
+}
diff --git a/sonar-batch/src/test/java/org/sonar/batch/mediumtest/xoo/plugin/base/XooConstants.java b/plugins/sonar-xoo-plugin/src/main/java/org/sonar/xoo/XooConstants.java
index c59091fb0f6..62f7d2ba782 100644
--- a/sonar-batch/src/test/java/org/sonar/batch/mediumtest/xoo/plugin/base/XooConstants.java
+++ b/plugins/sonar-xoo-plugin/src/main/java/org/sonar/xoo/XooConstants.java
@@ -17,7 +17,7 @@
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
-package org.sonar.batch.mediumtest.xoo.plugin.base;
+package org.sonar.xoo;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
diff --git a/plugins/sonar-xoo-plugin/src/main/java/org/sonar/xoo/XooPlugin.java b/plugins/sonar-xoo-plugin/src/main/java/org/sonar/xoo/XooPlugin.java
index 95b65dd31a3..3477fab825c 100644
--- a/plugins/sonar-xoo-plugin/src/main/java/org/sonar/xoo/XooPlugin.java
+++ b/plugins/sonar-xoo-plugin/src/main/java/org/sonar/xoo/XooPlugin.java
@@ -20,6 +20,14 @@
package org.sonar.xoo;
import org.sonar.api.SonarPlugin;
+import org.sonar.xoo.lang.MeasureSensor;
+import org.sonar.xoo.lang.ScmActivitySensor;
+import org.sonar.xoo.lang.SymbolReferencesSensor;
+import org.sonar.xoo.lang.SyntaxHighlightingSensor;
+import org.sonar.xoo.lang.XooTokenizerSensor;
+import org.sonar.xoo.rule.CreateIssueByInternalKeySensor;
+import org.sonar.xoo.rule.OneIssueOnDirPerFileSensor;
+import org.sonar.xoo.rule.OneIssuePerLineSensor;
import org.sonar.xoo.rule.XooQualityProfile;
import org.sonar.xoo.rule.XooRulesDefinition;
@@ -39,7 +47,19 @@ public class XooPlugin extends SonarPlugin {
return Arrays.asList(
Xoo.class,
XooRulesDefinition.class,
- XooQualityProfile.class);
+ XooQualityProfile.class,
+
+ // sensors
+ MeasureSensor.class,
+ ScmActivitySensor.class,
+ SyntaxHighlightingSensor.class,
+ SymbolReferencesSensor.class,
+ XooTokenizerSensor.class,
+
+ OneIssuePerLineSensor.class,
+ OneIssueOnDirPerFileSensor.class,
+ CreateIssueByInternalKeySensor.class
+ );
}
}
diff --git a/sonar-batch/src/test/java/org/sonar/batch/mediumtest/xoo/plugin/lang/MeasureSensor.java b/plugins/sonar-xoo-plugin/src/main/java/org/sonar/xoo/lang/MeasureSensor.java
index c28ebd5a05c..56e013b3716 100644
--- a/sonar-batch/src/test/java/org/sonar/batch/mediumtest/xoo/plugin/lang/MeasureSensor.java
+++ b/plugins/sonar-xoo-plugin/src/main/java/org/sonar/xoo/lang/MeasureSensor.java
@@ -17,7 +17,7 @@
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
-package org.sonar.batch.mediumtest.xoo.plugin.lang;
+package org.sonar.xoo.lang;
import org.apache.commons.io.FileUtils;
import org.apache.commons.lang.StringUtils;
@@ -29,8 +29,8 @@ import org.sonar.api.batch.sensor.SensorDescriptor;
import org.sonar.api.batch.sensor.measure.Measure;
import org.sonar.api.batch.sensor.measure.MeasureBuilder;
import org.sonar.api.measures.CoreMetrics;
-import org.sonar.batch.mediumtest.xoo.plugin.base.Xoo;
-import org.sonar.batch.mediumtest.xoo.plugin.base.XooConstants;
+import org.sonar.xoo.Xoo;
+import org.sonar.xoo.XooConstants;
import java.io.File;
import java.io.IOException;
diff --git a/sonar-batch/src/test/java/org/sonar/batch/mediumtest/xoo/plugin/lang/ScmActivitySensor.java b/plugins/sonar-xoo-plugin/src/main/java/org/sonar/xoo/lang/ScmActivitySensor.java
index b3352dba62a..12663b0b697 100644
--- a/sonar-batch/src/test/java/org/sonar/batch/mediumtest/xoo/plugin/lang/ScmActivitySensor.java
+++ b/plugins/sonar-xoo-plugin/src/main/java/org/sonar/xoo/lang/ScmActivitySensor.java
@@ -17,11 +17,7 @@
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
-package org.sonar.batch.mediumtest.xoo.plugin.lang;
-
-import org.sonar.api.batch.sensor.Sensor;
-import org.sonar.api.batch.sensor.SensorContext;
-import org.sonar.api.batch.sensor.SensorDescriptor;
+package org.sonar.xoo.lang;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Charsets;
@@ -31,11 +27,14 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.sonar.api.batch.fs.FileSystem;
import org.sonar.api.batch.fs.InputFile;
+import org.sonar.api.batch.sensor.Sensor;
+import org.sonar.api.batch.sensor.SensorContext;
+import org.sonar.api.batch.sensor.SensorDescriptor;
import org.sonar.api.measures.CoreMetrics;
import org.sonar.api.measures.FileLinesContext;
import org.sonar.api.measures.FileLinesContextFactory;
import org.sonar.api.utils.DateUtils;
-import org.sonar.batch.mediumtest.xoo.plugin.base.Xoo;
+import org.sonar.xoo.Xoo;
import java.io.File;
import java.io.IOException;
diff --git a/sonar-batch/src/test/java/org/sonar/batch/mediumtest/xoo/plugin/lang/SymbolReferencesSensor.java b/plugins/sonar-xoo-plugin/src/main/java/org/sonar/xoo/lang/SymbolReferencesSensor.java
index 91fa61e5c78..c8cafc2d705 100644
--- a/sonar-batch/src/test/java/org/sonar/batch/mediumtest/xoo/plugin/lang/SymbolReferencesSensor.java
+++ b/plugins/sonar-xoo-plugin/src/main/java/org/sonar/xoo/lang/SymbolReferencesSensor.java
@@ -17,7 +17,7 @@
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
-package org.sonar.batch.mediumtest.xoo.plugin.lang;
+package org.sonar.xoo.lang;
import com.google.common.base.Splitter;
import org.apache.commons.io.FileUtils;
@@ -29,8 +29,8 @@ import org.sonar.api.batch.sensor.SensorDescriptor;
import org.sonar.api.batch.sensor.symbol.Symbol;
import org.sonar.api.batch.sensor.symbol.SymbolTableBuilder;
import org.sonar.api.measures.CoreMetrics;
-import org.sonar.batch.mediumtest.xoo.plugin.base.Xoo;
-import org.sonar.batch.mediumtest.xoo.plugin.base.XooConstants;
+import org.sonar.xoo.Xoo;
+import org.sonar.xoo.XooConstants;
import java.io.File;
import java.io.IOException;
@@ -83,7 +83,7 @@ public class SymbolReferencesSensor implements Sensor {
@Override
public void describe(SensorDescriptor descriptor) {
descriptor
- .name("Xoo Highlighting Sensor")
+ .name("Xoo Symbol Reference Sensor")
.provides(CoreMetrics.LINES)
.workOnLanguages(Xoo.KEY)
.workOnFileTypes(InputFile.Type.MAIN, InputFile.Type.TEST);
diff --git a/sonar-batch/src/test/java/org/sonar/batch/mediumtest/xoo/plugin/lang/SyntaxHighlightingSensor.java b/plugins/sonar-xoo-plugin/src/main/java/org/sonar/xoo/lang/SyntaxHighlightingSensor.java
index 5b78759dbb9..0ae23954442 100644
--- a/sonar-batch/src/test/java/org/sonar/batch/mediumtest/xoo/plugin/lang/SyntaxHighlightingSensor.java
+++ b/plugins/sonar-xoo-plugin/src/main/java/org/sonar/xoo/lang/SyntaxHighlightingSensor.java
@@ -17,7 +17,7 @@
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
-package org.sonar.batch.mediumtest.xoo.plugin.lang;
+package org.sonar.xoo.lang;
import com.google.common.base.Splitter;
import org.apache.commons.io.FileUtils;
@@ -28,8 +28,8 @@ import org.sonar.api.batch.sensor.SensorContext;
import org.sonar.api.batch.sensor.SensorDescriptor;
import org.sonar.api.batch.sensor.highlighting.HighlightingBuilder;
import org.sonar.api.measures.CoreMetrics;
-import org.sonar.batch.mediumtest.xoo.plugin.base.Xoo;
-import org.sonar.batch.mediumtest.xoo.plugin.base.XooConstants;
+import org.sonar.xoo.Xoo;
+import org.sonar.xoo.XooConstants;
import java.io.File;
import java.io.IOException;
diff --git a/plugins/sonar-xoo-plugin/src/main/java/org/sonar/xoo/lang/XooTokenizerSensor.java b/plugins/sonar-xoo-plugin/src/main/java/org/sonar/xoo/lang/XooTokenizerSensor.java
new file mode 100644
index 00000000000..1ee33e560ef
--- /dev/null
+++ b/plugins/sonar-xoo-plugin/src/main/java/org/sonar/xoo/lang/XooTokenizerSensor.java
@@ -0,0 +1,84 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.xoo.lang;
+
+import com.google.common.base.Splitter;
+import com.google.common.collect.Lists;
+import org.apache.commons.io.FileUtils;
+import org.sonar.api.CoreProperties;
+import org.sonar.api.batch.fs.FilePredicates;
+import org.sonar.api.batch.fs.InputFile;
+import org.sonar.api.batch.sensor.Sensor;
+import org.sonar.api.batch.sensor.SensorContext;
+import org.sonar.api.batch.sensor.SensorDescriptor;
+import org.sonar.api.batch.sensor.duplication.TokenBuilder;
+import org.sonar.xoo.Xoo;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.List;
+
+/**
+ * Parse files *.xoo.highlighting
+ */
+public class XooTokenizerSensor implements Sensor {
+
+ private void computeTokens(InputFile inputFile, SensorContext context) {
+ TokenBuilder tokenBuilder = context.tokenBuilder(inputFile);
+ File ioFile = inputFile.file();
+ int lineId = 0;
+ try {
+ for (String line : FileUtils.readLines(ioFile)) {
+ lineId++;
+ for (String token : Splitter.on(" ").split(line)) {
+ tokenBuilder.addToken(lineId, token);
+ }
+ }
+ tokenBuilder.done();
+ } catch (IOException e) {
+ throw new IllegalStateException("Unable to read file " + ioFile, e);
+ }
+ }
+
+ @Override
+ public void describe(SensorDescriptor descriptor) {
+ descriptor
+ .name("Xoo Tokenizer Sensor")
+ .workOnLanguages(Xoo.KEY)
+ .workOnFileTypes(InputFile.Type.MAIN);
+ }
+
+ @Override
+ public void execute(SensorContext context) {
+ String[] cpdExclusions = context.settings().getStringArray(CoreProperties.CPD_EXCLUSIONS);
+ FilePredicates p = context.fileSystem().predicates();
+ List<InputFile> sourceFiles = Lists.newArrayList(context.fileSystem().inputFiles(p.and(
+ p.hasType(InputFile.Type.MAIN),
+ p.hasLanguage(Xoo.KEY),
+ p.doesNotMatchPathPatterns(cpdExclusions)
+ )));
+ if (sourceFiles.isEmpty()) {
+ return;
+ }
+ for (InputFile file : sourceFiles) {
+ computeTokens(file, context);
+ }
+ }
+}
diff --git a/sonar-batch/src/test/java/org/sonar/batch/mediumtest/xoo/plugin/rule/CreateIssueByInternalKeySensor.java b/plugins/sonar-xoo-plugin/src/main/java/org/sonar/xoo/rule/CreateIssueByInternalKeySensor.java
index 1ee150a5c01..c3e7d2641e6 100644
--- a/sonar-batch/src/test/java/org/sonar/batch/mediumtest/xoo/plugin/rule/CreateIssueByInternalKeySensor.java
+++ b/plugins/sonar-xoo-plugin/src/main/java/org/sonar/xoo/rule/CreateIssueByInternalKeySensor.java
@@ -17,15 +17,15 @@
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
-package org.sonar.batch.mediumtest.xoo.plugin.rule;
+package org.sonar.xoo.rule;
import org.sonar.api.batch.fs.InputFile;
import org.sonar.api.batch.rule.ActiveRule;
import org.sonar.api.batch.sensor.Sensor;
import org.sonar.api.batch.sensor.SensorContext;
import org.sonar.api.batch.sensor.SensorDescriptor;
-import org.sonar.batch.mediumtest.xoo.plugin.base.Xoo;
-import org.sonar.batch.mediumtest.xoo.plugin.base.XooConstants;
+import org.sonar.xoo.Xoo;
+import org.sonar.xoo.XooConstants;
public class CreateIssueByInternalKeySensor implements Sensor {
@@ -36,6 +36,7 @@ public class CreateIssueByInternalKeySensor implements Sensor {
descriptor
.name("CreateIssueByInternalKeySensor")
.workOnLanguages(Xoo.KEY)
+ .createIssuesForRuleRepositories(XooConstants.REPOSITORY_KEY)
.workOnFileTypes(InputFile.Type.MAIN, InputFile.Type.TEST);
}
diff --git a/sonar-batch/src/test/java/org/sonar/batch/mediumtest/xoo/plugin/rule/OneIssueOnDirPerFileSensor.java b/plugins/sonar-xoo-plugin/src/main/java/org/sonar/xoo/rule/OneIssueOnDirPerFileSensor.java
index 2a02ebef9dd..c2478830975 100644
--- a/sonar-batch/src/test/java/org/sonar/batch/mediumtest/xoo/plugin/rule/OneIssueOnDirPerFileSensor.java
+++ b/plugins/sonar-xoo-plugin/src/main/java/org/sonar/xoo/rule/OneIssueOnDirPerFileSensor.java
@@ -17,7 +17,7 @@
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
-package org.sonar.batch.mediumtest.xoo.plugin.rule;
+package org.sonar.xoo.rule;
import org.sonar.api.batch.fs.InputDir;
import org.sonar.api.batch.fs.InputFile;
@@ -25,8 +25,8 @@ import org.sonar.api.batch.sensor.Sensor;
import org.sonar.api.batch.sensor.SensorContext;
import org.sonar.api.batch.sensor.SensorDescriptor;
import org.sonar.api.rule.RuleKey;
-import org.sonar.batch.mediumtest.xoo.plugin.base.Xoo;
-import org.sonar.batch.mediumtest.xoo.plugin.base.XooConstants;
+import org.sonar.xoo.Xoo;
+import org.sonar.xoo.XooConstants;
public class OneIssueOnDirPerFileSensor implements Sensor {
@@ -37,6 +37,7 @@ public class OneIssueOnDirPerFileSensor implements Sensor {
descriptor
.name("One Issue On Dir Per File")
.workOnLanguages(Xoo.KEY)
+ .createIssuesForRuleRepositories(XooConstants.REPOSITORY_KEY)
.workOnFileTypes(InputFile.Type.MAIN, InputFile.Type.TEST);
}
diff --git a/sonar-batch/src/test/java/org/sonar/batch/mediumtest/xoo/plugin/rule/OneIssuePerLineSensor.java b/plugins/sonar-xoo-plugin/src/main/java/org/sonar/xoo/rule/OneIssuePerLineSensor.java
index 2a811068e54..bc0697b64b4 100644
--- a/sonar-batch/src/test/java/org/sonar/batch/mediumtest/xoo/plugin/rule/OneIssuePerLineSensor.java
+++ b/plugins/sonar-xoo-plugin/src/main/java/org/sonar/xoo/rule/OneIssuePerLineSensor.java
@@ -17,7 +17,7 @@
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
-package org.sonar.batch.mediumtest.xoo.plugin.rule;
+package org.sonar.xoo.rule;
import org.slf4j.LoggerFactory;
import org.sonar.api.batch.fs.InputFile;
@@ -28,8 +28,8 @@ import org.sonar.api.batch.sensor.issue.IssueBuilder;
import org.sonar.api.batch.sensor.measure.Measure;
import org.sonar.api.measures.CoreMetrics;
import org.sonar.api.rule.RuleKey;
-import org.sonar.batch.mediumtest.xoo.plugin.base.Xoo;
-import org.sonar.batch.mediumtest.xoo.plugin.base.XooConstants;
+import org.sonar.xoo.Xoo;
+import org.sonar.xoo.XooConstants;
public class OneIssuePerLineSensor implements Sensor {
@@ -43,6 +43,7 @@ public class OneIssuePerLineSensor implements Sensor {
.name("One Issue Per Line")
.dependsOn(CoreMetrics.LINES)
.workOnLanguages(Xoo.KEY)
+ .createIssuesForRuleRepositories(XooConstants.REPOSITORY_KEY)
.workOnFileTypes(InputFile.Type.MAIN, InputFile.Type.TEST);
}
diff --git a/plugins/sonar-xoo-plugin/src/test/java/org/sonar/xoo/XooPluginTest.java b/plugins/sonar-xoo-plugin/src/test/java/org/sonar/xoo/XooPluginTest.java
index b65ad573daa..bfe1fd99afa 100644
--- a/plugins/sonar-xoo-plugin/src/test/java/org/sonar/xoo/XooPluginTest.java
+++ b/plugins/sonar-xoo-plugin/src/test/java/org/sonar/xoo/XooPluginTest.java
@@ -27,6 +27,6 @@ public class XooPluginTest {
@Test
public void provide_extensions() {
- assertThat(new XooPlugin().getExtensions()).hasSize(3);
+ assertThat(new XooPlugin().getExtensions()).hasSize(11);
}
}
diff --git a/pom.xml b/pom.xml
index aec42caa6a9..161ae471faa 100644
--- a/pom.xml
+++ b/pom.xml
@@ -576,6 +576,12 @@
<version>${project.version}</version>
</dependency>
<dependency>
+ <groupId>org.codehaus.sonar.plugins</groupId>
+ <artifactId>sonar-xoo-plugin</artifactId>
+ <version>${project.version}</version>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
<groupId>org.codehaus.sonar</groupId>
<artifactId>sonar-squid</artifactId>
<version>4.1</version>
diff --git a/server/sonar-server/src/main/java/org/sonar/server/batch/ProjectReferentialsAction.java b/server/sonar-server/src/main/java/org/sonar/server/batch/ProjectReferentialsAction.java
index c5b3c5632f8..be4f94f5381 100644
--- a/server/sonar-server/src/main/java/org/sonar/server/batch/ProjectReferentialsAction.java
+++ b/server/sonar-server/src/main/java/org/sonar/server/batch/ProjectReferentialsAction.java
@@ -20,9 +20,11 @@
package org.sonar.server.batch;
+import com.google.common.collect.Maps;
import org.apache.commons.io.IOUtils;
import org.sonar.api.resources.Language;
import org.sonar.api.resources.Languages;
+import org.sonar.api.resources.Qualifiers;
import org.sonar.api.server.ws.Request;
import org.sonar.api.server.ws.RequestHandler;
import org.sonar.api.server.ws.Response;
@@ -47,9 +49,11 @@ import org.sonar.server.user.UserSession;
import javax.annotation.Nullable;
+import java.util.Collections;
import java.util.List;
import java.util.Map;
+import static com.google.common.collect.Lists.newArrayList;
import static com.google.common.collect.Maps.newHashMap;
public class ProjectReferentialsAction implements RequestHandler {
@@ -84,7 +88,7 @@ public class ProjectReferentialsAction implements RequestHandler {
action
.createParam(PARAM_KEY)
.setRequired(true)
- .setDescription("Project key")
+ .setDescription("Project or module key")
.setExampleValue("org.codehaus.sonar:sonar");
action
@@ -100,10 +104,21 @@ public class ProjectReferentialsAction implements RequestHandler {
DbSession session = dbClient.openSession(false);
try {
- ProjectReferentials ref = new ProjectReferentials();
- String projectKey = request.mandatoryParam(PARAM_KEY);
+ String projectOrModuleKey = request.mandatoryParam(PARAM_KEY);
String profileName = request.param(PARAM_PROFILE);
- addSettings(ref, projectKey, hasScanPerm, session);
+ ProjectReferentials ref = new ProjectReferentials();
+
+ String projectKey = null;
+ ComponentDto module = dbClient.componentDao().getNullableByKey(session, projectOrModuleKey);
+ if (module != null) {
+ ComponentDto project = !module.qualifier().equals(Qualifiers.PROJECT) ? dbClient.componentDao().getRootProjectByKey(projectOrModuleKey, session) : module;
+ if (!project.key().equals(module.key())) {
+ addSettings(ref, module.getKey(), getSettingsFromParentModules(module.key(), hasScanPerm, session));
+ }
+ projectKey = project.key();
+ addSettingsToChildrenModules(ref, projectOrModuleKey, Maps.<String, String>newHashMap(), hasScanPerm, session);
+ }
+
addProfiles(ref, projectKey, profileName, session);
addActiveRules(ref);
@@ -114,14 +129,43 @@ public class ProjectReferentialsAction implements RequestHandler {
}
}
- private void addSettings(ProjectReferentials ref, String projectKey, boolean hasScanPerm, DbSession session) {
- addSettings(ref, projectKey, propertiesDao.selectProjectProperties(projectKey, session), hasScanPerm);
+ private Map<String, String> getSettingsFromParentModules(String moduleKey, boolean hasScanPerm, DbSession session) {
+ List<ComponentDto> parents = newArrayList();
+ aggregateParentModules(moduleKey, parents, session);
+ Collections.reverse(parents);
+
+ Map<String, String> parentProperties = newHashMap();
+ for (ComponentDto parent : parents) {
+ parentProperties.putAll(getPropertiesMap(propertiesDao.selectProjectProperties(parent.key(), session), hasScanPerm));
+ }
+ return parentProperties;
+ }
+
+ private void aggregateParentModules(String component, List<ComponentDto> parents, DbSession session){
+ ComponentDto parent = dbClient.componentDao().getParentModuleByKey(component, session);
+ if (parent != null) {
+ parents.add(parent);
+ aggregateParentModules(parent.key(), parents, session);
+ }
+ }
+
+ private void addSettingsToChildrenModules(ProjectReferentials ref, String projectKey, Map<String, String> parentProperties, boolean hasScanPerm, DbSession session) {
+ parentProperties.putAll(getPropertiesMap(propertiesDao.selectProjectProperties(projectKey, session), hasScanPerm));
+ addSettings(ref, projectKey, parentProperties);
+
for (ComponentDto module : dbClient.componentDao().findModulesByProject(projectKey, session)) {
- addSettings(ref, module.getKey(), propertiesDao.selectProjectProperties(module.getKey(), session), hasScanPerm);
+ addSettings(ref, module.key(), parentProperties);
+ addSettingsToChildrenModules(ref, module.key(), parentProperties, hasScanPerm, session);
+ }
+ }
+
+ private void addSettings(ProjectReferentials ref, String module, Map<String, String> properties) {
+ if (!properties.isEmpty()) {
+ ref.addSettings(module, properties);
}
}
- private void addSettings(ProjectReferentials ref, String projectOrModuleKey, List<PropertyDto> propertyDtos, boolean hasScanPerm) {
+ private Map<String, String> getPropertiesMap(List<PropertyDto> propertyDtos, boolean hasScanPerm) {
Map<String, String> properties = newHashMap();
for (PropertyDto propertyDto : propertyDtos) {
String key = propertyDto.getKey();
@@ -130,16 +174,14 @@ public class ProjectReferentialsAction implements RequestHandler {
properties.put(key, value);
}
}
- if (!properties.isEmpty()) {
- ref.addSettings(projectOrModuleKey, properties);
- }
+ return properties;
}
private static boolean isPropertyAllowed(String key, boolean hasScanPerm) {
return !key.contains(".secured") || hasScanPerm;
}
- private void addProfiles(ProjectReferentials ref, String projectKey, @Nullable String profileName, DbSession session) {
+ private void addProfiles(ProjectReferentials ref, @Nullable String projectKey, @Nullable String profileName, DbSession session) {
for (Language language : languages.all()) {
String languageKey = language.getKey();
QualityProfileDto qualityProfileDto = getProfile(languageKey, projectKey, profileName, session);
@@ -153,12 +195,16 @@ public class ProjectReferentialsAction implements RequestHandler {
/**
* First try to find a quality profile matching the given name (if provided) and current language
- * If null, try to find the quality profile set on the project
- * If null, try to find the default profile of the language
+ * If no profile found, try to find the quality profile set on the project (if provided)
+ * If still no profile found, try to find the default profile of the language
+ *
+ * Never return null because a default profile should always be set on ech language
*/
- private QualityProfileDto getProfile(String languageKey, String projectKey, @Nullable String profileName, DbSession session) {
+ private QualityProfileDto getProfile(String languageKey, @Nullable String projectKey, @Nullable String profileName, DbSession session) {
QualityProfileDto qualityProfileDto = profileName != null ? qProfileFactory.getByNameAndLanguage(session, profileName, languageKey) : null;
- qualityProfileDto = qualityProfileDto != null ? qualityProfileDto : qProfileFactory.getByProjectAndLanguage(session, projectKey, languageKey);
+ if (qualityProfileDto == null && projectKey != null) {
+ qualityProfileDto = qProfileFactory.getByProjectAndLanguage(session, projectKey, languageKey);
+ }
qualityProfileDto = qualityProfileDto != null ? qualityProfileDto : qProfileFactory.getDefault(session, languageKey);
if (qualityProfileDto != null) {
return qualityProfileDto;
@@ -177,8 +223,7 @@ public class ProjectReferentialsAction implements RequestHandler {
rule.name(),
activeRule.severity(),
rule.internalKey(),
- qProfile.language()
- );
+ qProfile.language());
for (Map.Entry<String, String> entry : activeRule.params().entrySet()) {
inputActiveRule.addParam(entry.getKey(), entry.getValue());
}
diff --git a/server/sonar-server/src/main/java/org/sonar/server/component/persistence/ComponentDao.java b/server/sonar-server/src/main/java/org/sonar/server/component/persistence/ComponentDao.java
index 39a23370fdf..657287999b7 100644
--- a/server/sonar-server/src/main/java/org/sonar/server/component/persistence/ComponentDao.java
+++ b/server/sonar-server/src/main/java/org/sonar/server/component/persistence/ComponentDao.java
@@ -26,6 +26,9 @@ import org.sonar.core.component.db.ComponentMapper;
import org.sonar.core.persistence.DaoComponent;
import org.sonar.core.persistence.DbSession;
import org.sonar.server.db.BaseDao;
+import org.sonar.server.exceptions.NotFoundException;
+
+import javax.annotation.CheckForNull;
import java.util.Date;
import java.util.List;
@@ -47,6 +50,19 @@ public class ComponentDao extends BaseDao<ComponentMapper, ComponentDto, String>
return mapper(session).countById(id) > 0;
}
+ public ComponentDto getRootProjectByKey(String componentKey, DbSession session) {
+ ComponentDto componentDto = mapper(session).selectRootProjectByKey(componentKey);
+ if (componentDto == null) {
+ throw new NotFoundException(String.format("Root project for project '%s' not found", componentKey));
+ }
+ return componentDto;
+ }
+
+ @CheckForNull
+ public ComponentDto getParentModuleByKey(String componentKey, DbSession session) {
+ return mapper(session).selectParentModuleByKey(componentKey);
+ }
+
public List<ComponentDto> findModulesByProject(String projectKey, DbSession session) {
return mapper(session).findModulesByProject(projectKey);
}
diff --git a/server/sonar-server/src/main/java/org/sonar/server/db/DatabaseChecker.java b/server/sonar-server/src/main/java/org/sonar/server/db/DatabaseChecker.java
index c37b192f919..1b3fc5aeedd 100644
--- a/server/sonar-server/src/main/java/org/sonar/server/db/DatabaseChecker.java
+++ b/server/sonar-server/src/main/java/org/sonar/server/db/DatabaseChecker.java
@@ -19,8 +19,10 @@
*/
package org.sonar.server.db;
+import com.google.common.base.Throwables;
import org.apache.commons.dbutils.DbUtils;
import org.apache.commons.lang.StringUtils;
+import org.picocontainer.Startable;
import org.slf4j.LoggerFactory;
import org.sonar.api.ServerComponent;
import org.sonar.core.persistence.Database;
@@ -30,7 +32,7 @@ import org.sonar.core.persistence.dialect.Oracle;
import java.sql.Connection;
import java.sql.SQLException;
-public class DatabaseChecker implements ServerComponent {
+public class DatabaseChecker implements ServerComponent, Startable {
private final Database db;
@@ -38,14 +40,24 @@ public class DatabaseChecker implements ServerComponent {
this.db = db;
}
- public void start() throws SQLException {
- if (H2.ID.equals(db.getDialect().getId())) {
- LoggerFactory.getLogger(DatabaseChecker.class).warn("H2 database should be used for evaluation purpose only");
- } else if (Oracle.ID.equals(db.getDialect().getId())) {
- checkOracleDriverVersion();
+ @Override
+ public void start() {
+ try {
+ if (H2.ID.equals(db.getDialect().getId())) {
+ LoggerFactory.getLogger(DatabaseChecker.class).warn("H2 database should be used for evaluation purpose only");
+ } else if (Oracle.ID.equals(db.getDialect().getId())) {
+ checkOracleDriverVersion();
+ }
+ } catch (Exception e) {
+ Throwables.propagate(e);
}
}
+ @Override
+ public void stop() {
+ // nothing to do
+ }
+
private void checkOracleDriverVersion() throws SQLException {
Connection connection = db.getDataSource().getConnection();
try {
diff --git a/server/sonar-server/src/main/java/org/sonar/server/db/migrations/BaseDataChange.java b/server/sonar-server/src/main/java/org/sonar/server/db/migrations/BaseDataChange.java
index 8d36fbe9b83..32ebbaf443e 100644
--- a/server/sonar-server/src/main/java/org/sonar/server/db/migrations/BaseDataChange.java
+++ b/server/sonar-server/src/main/java/org/sonar/server/db/migrations/BaseDataChange.java
@@ -39,6 +39,10 @@ public abstract class BaseDataChange implements DataChange, DatabaseMigration {
try {
readConnection = db.getDataSource().getConnection();
readConnection.setAutoCommit(false);
+ if (readConnection.getMetaData().supportsTransactionIsolationLevel(Connection.TRANSACTION_READ_UNCOMMITTED)) {
+ readConnection.setTransactionIsolation(Connection.TRANSACTION_READ_UNCOMMITTED);
+ }
+
writeConnection = db.getDataSource().getConnection();
writeConnection.setAutoCommit(false);
Context context = new Context(db, readConnection, writeConnection);
diff --git a/server/sonar-server/src/main/java/org/sonar/server/db/migrations/UpsertImpl.java b/server/sonar-server/src/main/java/org/sonar/server/db/migrations/UpsertImpl.java
index da1d3f4b8bf..cefaef18d30 100644
--- a/server/sonar-server/src/main/java/org/sonar/server/db/migrations/UpsertImpl.java
+++ b/server/sonar-server/src/main/java/org/sonar/server/db/migrations/UpsertImpl.java
@@ -36,6 +36,7 @@ public class UpsertImpl extends BaseSqlStatement<Upsert> implements Upsert {
@Override
public Upsert addBatch() throws SQLException {
pstmt.addBatch();
+ pstmt.clearParameters();
batchCount++;
if (batchCount % BatchSession.MAX_BATCH_SIZE == 0L) {
pstmt.executeBatch();
diff --git a/server/sonar-server/src/main/java/org/sonar/server/duplication/ws/DuplicationsJsonWriter.java b/server/sonar-server/src/main/java/org/sonar/server/duplication/ws/DuplicationsJsonWriter.java
index 0e9f286f265..ccc2afb7032 100644
--- a/server/sonar-server/src/main/java/org/sonar/server/duplication/ws/DuplicationsJsonWriter.java
+++ b/server/sonar-server/src/main/java/org/sonar/server/duplication/ws/DuplicationsJsonWriter.java
@@ -27,6 +27,8 @@ import org.sonar.core.component.ComponentDto;
import org.sonar.core.persistence.DbSession;
import org.sonar.server.component.persistence.ComponentDao;
+import javax.annotation.Nullable;
+
import java.util.List;
import java.util.Map;
@@ -90,42 +92,45 @@ public class DuplicationsJsonWriter implements ServerComponent {
ComponentDto file = componentDao.getNullableByKey(session, componentKey);
if (file != null) {
json.name(ref).beginObject();
- json.prop("key", file.key());
- json.prop("name", file.longName());
-
- Long projectId = file.projectId();
- ComponentDto project = projectsById.get(file.projectId());
- if (project == null && projectId != null) {
- project = componentDao.getById(projectId, session);
- if (project != null) {
- projectsById.put(projectId, project);
- }
- }
-
- Long subProjectId = file.subProjectId();
- ComponentDto subProject = subProjectsById.get(subProjectId);
- if (subProject == null && subProjectId != null) {
- subProject = componentDao.getById(subProjectId, session);
- if (subProject != null) {
- subProjectsById.put(subProject.getId(), subProject);
- }
- }
-
- if (project != null) {
- json.prop("project", project.key());
- json.prop("projectName", project.longName());
-
- // Do not return sub project if sub project and project are the same
- boolean displaySubProject = subProject != null && !subProject.getId().equals(project.getId());
- if (displaySubProject) {
- json.prop("subProject", subProject.key());
- json.prop("subProjectName", subProject.longName());
- }
- }
+
+ addFile(json, file);
+ ComponentDto project = getProject(file.projectId(), projectsById, session);
+ ComponentDto subProject = getProject(file.subProjectId(), subProjectsById, session);
+ addProject(json, project, subProject);
json.endObject();
}
}
}
+ private void addFile(JsonWriter json, ComponentDto file) {
+ json.prop("key", file.key());
+ json.prop("name", file.longName());
+ }
+
+ private void addProject(JsonWriter json, @Nullable ComponentDto project, @Nullable ComponentDto subProject) {
+ if (project != null) {
+ json.prop("project", project.key());
+ json.prop("projectName", project.longName());
+
+ // Do not return sub project if sub project and project are the same
+ boolean displaySubProject = subProject != null && !subProject.getId().equals(project.getId());
+ if (displaySubProject) {
+ json.prop("subProject", subProject.key());
+ json.prop("subProjectName", subProject.longName());
+ }
+ }
+ }
+
+ private ComponentDto getProject(@Nullable Long projectId, Map<Long, ComponentDto> projectsById, DbSession session) {
+ ComponentDto project = projectsById.get(projectId);
+ if (project == null && projectId != null) {
+ project = componentDao.getById(projectId, session);
+ if (project != null) {
+ projectsById.put(project.getId(), project);
+ }
+ }
+ return project;
+ }
+
}
diff --git a/server/sonar-server/src/main/java/org/sonar/server/issue/RulesAggregation.java b/server/sonar-server/src/main/java/org/sonar/server/issue/RulesAggregation.java
index f6f0fb7fade..920b84c60dc 100644
--- a/server/sonar-server/src/main/java/org/sonar/server/issue/RulesAggregation.java
+++ b/server/sonar-server/src/main/java/org/sonar/server/issue/RulesAggregation.java
@@ -36,7 +36,7 @@ public class RulesAggregation {
}
public RulesAggregation add(RuleDto ruleDto) {
- rules.add(new Rule().setRuleKey(ruleDto.getKey()).setName(ruleDto.getName()));
+ rules.add(new Rule(ruleDto.getKey(), ruleDto.getName()));
return this;
}
@@ -53,24 +53,19 @@ public class RulesAggregation {
private RuleKey ruleKey;
private String name;
- public RuleKey ruleKey() {
- return ruleKey;
+ public Rule(RuleKey ruleKey, String name) {
+ this.ruleKey = ruleKey;
+ this.name = name;
}
- public Rule setRuleKey(RuleKey ruleKey) {
- this.ruleKey = ruleKey;
- return this;
+ public RuleKey ruleKey() {
+ return ruleKey;
}
public String name() {
return name;
}
- public Rule setName(String name) {
- this.name = name;
- return this;
- }
-
@Override
public boolean equals(Object o) {
if (this == o) {
diff --git a/server/sonar-server/src/main/java/org/sonar/server/measure/MeasureFilterFactory.java b/server/sonar-server/src/main/java/org/sonar/server/measure/MeasureFilterFactory.java
index 06d5143a6d0..bb6dcdda810 100644
--- a/server/sonar-server/src/main/java/org/sonar/server/measure/MeasureFilterFactory.java
+++ b/server/sonar-server/src/main/java/org/sonar/server/measure/MeasureFilterFactory.java
@@ -22,6 +22,7 @@ package org.sonar.server.measure;
import com.google.common.base.Function;
import com.google.common.base.Joiner;
import com.google.common.base.Strings;
+import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;
import org.apache.commons.lang.StringUtils;
import org.sonar.api.ServerComponent;
@@ -33,11 +34,10 @@ import org.sonar.api.utils.System2;
import javax.annotation.CheckForNull;
import javax.annotation.Nullable;
-import java.util.Arrays;
-import java.util.Calendar;
-import java.util.Date;
-import java.util.List;
-import java.util.Map;
+
+import java.util.*;
+
+import static com.google.common.collect.Lists.newArrayList;
public class MeasureFilterFactory implements ServerComponent {
@@ -62,7 +62,7 @@ public class MeasureFilterFactory implements ServerComponent {
if (onBaseComponents != null) {
filter.setOnBaseResourceChildren(Boolean.valueOf(onBaseComponents));
}
- filter.setResourceName((String) properties.get("nameSearch"));
+ filter.setResourceName(toString(properties.get("nameSearch")));
filter.setResourceKey((String) properties.get("keySearch"));
String onFavourites = (String) properties.get("onFavourites");
if (onFavourites != null) {
@@ -111,7 +111,10 @@ public class MeasureFilterFactory implements ServerComponent {
}
}
} else {
- filter.setSortOn(MeasureFilterSort.Field.valueOf(s.toUpperCase()));
+ String sort = s.toUpperCase();
+ if (sortFieldLabels().contains(sort)) {
+ filter.setSortOn(MeasureFilterSort.Field.valueOf(sort));
+ }
}
}
@@ -120,6 +123,15 @@ public class MeasureFilterFactory implements ServerComponent {
}
}
+ private List<String> sortFieldLabels(){
+ return newArrayList(Iterables.transform(Arrays.asList(MeasureFilterSort.Field.values()), new Function<MeasureFilterSort.Field, String>() {
+ @Override
+ public String apply(@Nullable MeasureFilterSort.Field input) {
+ return input != null ? input.name() : null;
+ }
+ }));
+ }
+
@CheckForNull
private MeasureFilterCondition toCondition(Map<String, Object> props, int index) {
MeasureFilterCondition condition = null;
@@ -139,7 +151,7 @@ public class MeasureFilterFactory implements ServerComponent {
}
@CheckForNull
- private MeasureFilterCondition alertToCondition(List<String> alertLevels) {
+ private MeasureFilterCondition alertToCondition(@Nullable List<String> alertLevels) {
if (alertLevels == null || alertLevels.isEmpty()) {
return null;
}
@@ -148,7 +160,7 @@ public class MeasureFilterFactory implements ServerComponent {
String op = "in";
List<String> alertLevelsUppercase = Lists.transform(alertLevels, new Function<String, String>() {
@Override
- public String apply(String input) {
+ public String apply(@Nullable String input) {
return input != null ? input.toUpperCase() : "";
}
});
@@ -192,4 +204,19 @@ public class MeasureFilterFactory implements ServerComponent {
return null;
}
+ @CheckForNull
+ public static String toString(@Nullable Object o) {
+ if (o != null) {
+ if (o instanceof List) {
+ return Joiner.on(",").join((List) o);
+ } else if (o instanceof String[]) {
+ // assume that it contains only strings
+ return Joiner.on(",").join((String[]) o);
+ } else {
+ return o.toString();
+ }
+ }
+ return null;
+ }
+
}
diff --git a/server/sonar-server/src/main/java/org/sonar/server/platform/ServerIdGenerator.java b/server/sonar-server/src/main/java/org/sonar/server/platform/ServerIdGenerator.java
index b5fe7007e9b..7979bca8a8c 100644
--- a/server/sonar-server/src/main/java/org/sonar/server/platform/ServerIdGenerator.java
+++ b/server/sonar-server/src/main/java/org/sonar/server/platform/ServerIdGenerator.java
@@ -22,7 +22,6 @@ package org.sonar.server.platform;
import com.google.common.collect.Lists;
import org.apache.commons.codec.digest.DigestUtils;
import org.apache.commons.lang.StringUtils;
-import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.UnsupportedEncodingException;
@@ -41,8 +40,6 @@ public class ServerIdGenerator {
private static final Pattern ORGANIZATION_PATTERN = Pattern.compile("[a-zA-Z0-9]+[a-zA-Z0-9 ]*");
- private static final Logger LOG = LoggerFactory.getLogger(ServerIdGenerator.class);
-
/**
* Increment this version each time the algorithm is changed. Do not exceed 9.
*/
diff --git a/server/sonar-server/src/main/java/org/sonar/server/qualityprofile/QProfileFactory.java b/server/sonar-server/src/main/java/org/sonar/server/qualityprofile/QProfileFactory.java
index 4643ea4f641..65c0f46025f 100644
--- a/server/sonar-server/src/main/java/org/sonar/server/qualityprofile/QProfileFactory.java
+++ b/server/sonar-server/src/main/java/org/sonar/server/qualityprofile/QProfileFactory.java
@@ -177,6 +177,7 @@ public class QProfileFactory implements ServerComponent {
}
}
+ @CheckForNull
public QualityProfileDto getByProjectAndLanguage(DbSession session, String projectKey, String language) {
return db.qualityProfileDao().getByProjectAndLanguage(projectKey, language, PROFILE_PROPERTY_PREFIX + language, session);
}
@@ -190,6 +191,7 @@ public class QProfileFactory implements ServerComponent {
}
}
+ @CheckForNull
public QualityProfileDto getByNameAndLanguage(DbSession session, String name, String language) {
return db.qualityProfileDao().getByNameAndLanguage(name, language, session);
}
diff --git a/server/sonar-server/src/main/java/org/sonar/server/qualityprofile/QProfileProjectOperations.java b/server/sonar-server/src/main/java/org/sonar/server/qualityprofile/QProfileProjectOperations.java
index da24181f9fb..429b192a336 100644
--- a/server/sonar-server/src/main/java/org/sonar/server/qualityprofile/QProfileProjectOperations.java
+++ b/server/sonar-server/src/main/java/org/sonar/server/qualityprofile/QProfileProjectOperations.java
@@ -53,12 +53,12 @@ public class QProfileProjectOperations implements ServerComponent {
void addProject(int profileId, long projectId, UserSession userSession, DbSession session) {
checkPermission(userSession);
- ComponentDto project = (ComponentDto) findProjectNotNull(projectId, session);
- QualityProfileDto qualityProfile = findNotNull(profileId, session);
+ ComponentDto project = (ComponentDto) findProjectNotNull(projectId, session);
+ QualityProfileDto qualityProfile = findNotNull(profileId, session);
- db.propertiesDao().setProperty(new PropertyDto().setKey(
- QProfileProjectLookup.PROFILE_PROPERTY_PREFIX + qualityProfile.getLanguage()).setValue(qualityProfile.getName()).setResourceId(project.getId()), session);
- session.commit();
+ db.propertiesDao().setProperty(new PropertyDto().setKey(
+ QProfileProjectLookup.PROFILE_PROPERTY_PREFIX + qualityProfile.getLanguage()).setValue(qualityProfile.getName()).setResourceId(project.getId()), session);
+ session.commit();
}
public void removeProject(int profileId, long projectId, UserSession userSession) {
diff --git a/server/sonar-server/src/main/java/org/sonar/server/rule/index/RuleIndex.java b/server/sonar-server/src/main/java/org/sonar/server/rule/index/RuleIndex.java
index 2885b4c1dd8..ca4e42f7a9d 100644
--- a/server/sonar-server/src/main/java/org/sonar/server/rule/index/RuleIndex.java
+++ b/server/sonar-server/src/main/java/org/sonar/server/rule/index/RuleIndex.java
@@ -22,6 +22,7 @@ package org.sonar.server.rule.index;
import com.google.common.base.Preconditions;
import org.elasticsearch.action.search.SearchRequestBuilder;
import org.elasticsearch.action.search.SearchResponse;
+import org.elasticsearch.action.search.SearchScrollRequestBuilder;
import org.elasticsearch.action.search.SearchType;
import org.elasticsearch.common.settings.ImmutableSettings;
import org.elasticsearch.common.settings.Settings;
@@ -416,12 +417,27 @@ public class RuleIndex extends BaseIndex<Rule, RuleDto, RuleKey> {
public List<Rule> getByIds(Collection<Integer> ids) {
SearchRequestBuilder request = getClient().prepareSearch(this.getIndexName())
.setTypes(this.getIndexType())
+ .setSearchType(SearchType.SCAN)
+ .setScroll(TimeValue.timeValueSeconds(3L))
+ .setSize(100)
.setQuery(QueryBuilders.termsQuery(RuleNormalizer.RuleField.ID.field(), ids));
- SearchResponse response = node.execute(request);
+ SearchResponse scrollResp = node.execute(request);
List<Rule> rules = newArrayList();
- for (SearchHit hit : response.getHits()) {
- rules.add(toDoc(hit.getSource()));
+ while (true) {
+ SearchScrollRequestBuilder scrollRequest = getClient()
+ .prepareSearchScroll(scrollResp.getScrollId())
+ .setScroll(TimeValue.timeValueSeconds(3L));
+
+ scrollResp = node.execute(scrollRequest);
+
+ for (SearchHit hit : scrollResp.getHits()) {
+ rules.add(toDoc(hit.getSource()));
+ }
+ //Break condition: No hits are returned
+ if (scrollResp.getHits().getHits().length == 0) {
+ break;
+ }
}
return rules;
}
diff --git a/server/sonar-server/src/main/java/org/sonar/server/rule/ws/RuleMapping.java b/server/sonar-server/src/main/java/org/sonar/server/rule/ws/RuleMapping.java
index 3b9533951bb..a57469683ba 100644
--- a/server/sonar-server/src/main/java/org/sonar/server/rule/ws/RuleMapping.java
+++ b/server/sonar-server/src/main/java/org/sonar/server/rule/ws/RuleMapping.java
@@ -36,6 +36,7 @@ import org.sonar.server.text.MacroInterpreter;
import javax.annotation.CheckForNull;
import javax.annotation.Nullable;
+
import java.util.Collection;
import java.util.Map;
@@ -165,12 +166,18 @@ public class RuleMapping extends BaseMapping<RuleDoc, RuleMappingContext> {
public void write(Rule rule, JsonWriter json, @Nullable SearchOptions options) {
RuleMappingContext context = new RuleMappingContext();
- if (needDebtCharacteristicNames(options) && rule.debtCharacteristicKey() != null) {
- // load debt characteristics if requested
- context.add(debtModel.characteristicByKey(rule.debtCharacteristicKey()));
+ if (needDebtCharacteristicNames(options)) {
+ String debtCharacteristicKey = rule.debtCharacteristicKey();
+ if (debtCharacteristicKey != null) {
+ // load debt characteristics if requested
+ context.add(debtModel.characteristicByKey(debtCharacteristicKey));
+ }
}
- if (needDebtSubCharacteristicNames(options) && rule.debtSubCharacteristicKey() != null) {
- context.add(debtModel.characteristicByKey(rule.debtSubCharacteristicKey()));
+ if (needDebtSubCharacteristicNames(options)) {
+ String debtSubCharacteristicKey = rule.debtSubCharacteristicKey();
+ if (debtSubCharacteristicKey != null) {
+ context.add(debtModel.characteristicByKey(debtSubCharacteristicKey));
+ }
}
doWrite((RuleDoc) rule, context, json, options);
}
diff --git a/server/sonar-server/src/main/java/org/sonar/server/rule/ws/SearchAction.java b/server/sonar-server/src/main/java/org/sonar/server/rule/ws/SearchAction.java
index 8f34fe3015c..2d90ff52a2b 100644
--- a/server/sonar-server/src/main/java/org/sonar/server/rule/ws/SearchAction.java
+++ b/server/sonar-server/src/main/java/org/sonar/server/rule/ws/SearchAction.java
@@ -31,7 +31,6 @@ import org.sonar.api.utils.text.JsonWriter;
import org.sonar.server.qualityprofile.ActiveRule;
import org.sonar.server.rule.Rule;
import org.sonar.server.rule.RuleService;
-import org.sonar.server.rule.index.RuleDoc;
import org.sonar.server.rule.index.RuleNormalizer;
import org.sonar.server.rule.index.RuleQuery;
import org.sonar.server.search.FacetValue;
@@ -259,7 +258,7 @@ public class SearchAction implements RequestHandler {
private void writeRules(Result<Rule> result, JsonWriter json, SearchOptions options) {
json.name("rules").beginArray();
for (Rule rule : result.getHits()) {
- mapping.write((RuleDoc) rule, json, options);
+ mapping.write(rule, json, options);
}
json.endArray();
}
diff --git a/server/sonar-server/src/test/java/org/sonar/server/batch/ProjectReferentialsActionTest.java b/server/sonar-server/src/test/java/org/sonar/server/batch/ProjectReferentialsActionTest.java
index e6a635b149f..65e1995054f 100644
--- a/server/sonar-server/src/test/java/org/sonar/server/batch/ProjectReferentialsActionTest.java
+++ b/server/sonar-server/src/test/java/org/sonar/server/batch/ProjectReferentialsActionTest.java
@@ -28,6 +28,7 @@ import org.mockito.Mock;
import org.mockito.runners.MockitoJUnitRunner;
import org.sonar.api.resources.Language;
import org.sonar.api.resources.Languages;
+import org.sonar.api.resources.Qualifiers;
import org.sonar.api.rule.RuleKey;
import org.sonar.api.rule.Severity;
import org.sonar.core.component.ComponentDto;
@@ -47,6 +48,8 @@ import org.sonar.server.rule.RuleService;
import org.sonar.server.user.MockUserSession;
import org.sonar.server.ws.WsTester;
+import java.util.Collections;
+
import static com.google.common.collect.Lists.newArrayList;
import static org.fest.assertions.Assertions.assertThat;
import static org.mockito.Mockito.mock;
@@ -81,70 +84,194 @@ public class ProjectReferentialsActionTest {
WsTester tester;
+ ComponentDto project;
+ ComponentDto module;
+ ComponentDto subModule;
+
@Before
public void setUp() throws Exception {
DbClient dbClient = mock(DbClient.class);
when(dbClient.openSession(false)).thenReturn(session);
when(dbClient.componentDao()).thenReturn(componentDao);
+ project = new ComponentDto().setKey("org.codehaus.sonar:sonar").setQualifier(Qualifiers.PROJECT);
+ module = new ComponentDto().setKey("org.codehaus.sonar:sonar-server").setQualifier(Qualifiers.MODULE);
+ subModule = new ComponentDto().setKey("org.codehaus.sonar:sonar-server-dao").setQualifier(Qualifiers.MODULE);
+
+ when(componentDao.getNullableByKey(session, project.key())).thenReturn(project);
+ when(componentDao.getNullableByKey(session, module.key())).thenReturn(module);
+ when(componentDao.getNullableByKey(session, subModule.key())).thenReturn(subModule);
+
when(language.getKey()).thenReturn("java");
- when(languages.all()).thenReturn(new Language[]{language});
+ when(languages.all()).thenReturn(new Language[] {language});
+
+ when(qProfileFactory.getDefault(session, "java")).thenReturn(
+ QualityProfileDto.createFor("abcd").setName("Default").setLanguage("java").setRulesUpdatedAt("2014-01-14T14:00:00+0200")
+ );
tester = new WsTester(new BatchWs(mock(BatchIndex.class), mock(GlobalReferentialsAction.class),
new ProjectReferentialsAction(dbClient, propertiesDao, qProfileFactory, qProfileLoader, ruleService, languages)));
}
@Test
- public void return_settings_by_modules() throws Exception {
+ public void return_project_settings() throws Exception {
MockUserSession.set().setLogin("john").setGlobalPermissions(GlobalPermissions.SCAN_EXECUTION, GlobalPermissions.DRY_RUN_EXECUTION);
- String projectKey = "org.codehaus.sonar:sonar";
- String moduleKey = "org.codehaus.sonar:sonar-server";
- when(componentDao.findModulesByProject(projectKey, session)).thenReturn(newArrayList(
- new ComponentDto().setKey(projectKey),
- new ComponentDto().setKey(moduleKey)
+ // Project without modules
+ when(componentDao.findModulesByProject(project.key(), session)).thenReturn(Collections.<ComponentDto>emptyList());
+
+ when(propertiesDao.selectProjectProperties(project.key(), session)).thenReturn(newArrayList(
+ new PropertyDto().setKey("sonar.jira.project.key").setValue("SONAR"),
+ new PropertyDto().setKey("sonar.jira.login.secured").setValue("john")
));
- when(propertiesDao.selectProjectProperties(projectKey, session)).thenReturn(newArrayList(
+ WsTester.TestRequest request = tester.newGetRequest("batch", "project").setParam("key", project.key());
+ request.execute().assertJson(getClass(), "return_project_settings.json");
+ }
+
+ @Test
+ public void return_project_with_module_settings() throws Exception {
+ MockUserSession.set().setLogin("john").setGlobalPermissions(GlobalPermissions.SCAN_EXECUTION, GlobalPermissions.DRY_RUN_EXECUTION);
+
+ when(componentDao.findModulesByProject(project.key(), session)).thenReturn(newArrayList(module));
+
+ when(propertiesDao.selectProjectProperties(project.key(), session)).thenReturn(newArrayList(
new PropertyDto().setKey("sonar.jira.project.key").setValue("SONAR"),
new PropertyDto().setKey("sonar.jira.login.secured").setValue("john")
));
- when(propertiesDao.selectProjectProperties(moduleKey, session)).thenReturn(newArrayList(
+ when(propertiesDao.selectProjectProperties(module.key(), session)).thenReturn(newArrayList(
+ new PropertyDto().setKey("sonar.jira.project.key").setValue("SONAR-SERVER"),
new PropertyDto().setKey("sonar.coverage.exclusions").setValue("**/*.java")
- ));
+ ));
- when(qProfileFactory.getDefault(session, "java")).thenReturn(
- QualityProfileDto.createFor("abcd").setName("Default").setLanguage("java").setRulesUpdatedAt("2014-01-14T14:00:00+0200")
- );
+ WsTester.TestRequest request = tester.newGetRequest("batch", "project").setParam("key", project.key());
+ request.execute().assertJson(getClass(), "return_project_with_module_settings.json");
+ }
- WsTester.TestRequest request = tester.newGetRequest("batch", "project").setParam("key", projectKey);
- request.execute().assertJson(getClass(), "return_settings_by_modules.json");
+ @Test
+ public void return_project_with_module_settings_inherited_from_project() throws Exception {
+ MockUserSession.set().setLogin("john").setGlobalPermissions(GlobalPermissions.SCAN_EXECUTION, GlobalPermissions.DRY_RUN_EXECUTION);
+
+ when(componentDao.findModulesByProject(project.key(), session)).thenReturn(newArrayList(module));
+
+ when(propertiesDao.selectProjectProperties(project.key(), session)).thenReturn(newArrayList(
+ new PropertyDto().setKey("sonar.jira.project.key").setValue("SONAR"),
+ new PropertyDto().setKey("sonar.jira.login.secured").setValue("john")
+ ));
+ // No property on module -> should have the same than project
+
+ WsTester.TestRequest request = tester.newGetRequest("batch", "project").setParam("key", project.key());
+ request.execute().assertJson(getClass(), "return_project_with_module_settings_inherited_from_project.json");
}
@Test
- public void return_settings_by_modules_without_empty_module_settings() throws Exception {
+ public void return_project_with_module_with_sub_module() throws Exception {
MockUserSession.set().setLogin("john").setGlobalPermissions(GlobalPermissions.SCAN_EXECUTION, GlobalPermissions.DRY_RUN_EXECUTION);
- String projectKey = "org.codehaus.sonar:sonar";
- String moduleKey = "org.codehaus.sonar:sonar-server";
- when(componentDao.findModulesByProject(projectKey, session)).thenReturn(newArrayList(
- new ComponentDto().setKey(projectKey),
- new ComponentDto().setKey(moduleKey)
- ));
+ when(componentDao.findModulesByProject(project.key(), session)).thenReturn(newArrayList(module));
+ when(componentDao.findModulesByProject(module.key(), session)).thenReturn(newArrayList(subModule));
- when(propertiesDao.selectProjectProperties(projectKey, session)).thenReturn(newArrayList(
+ when(propertiesDao.selectProjectProperties(project.key(), session)).thenReturn(newArrayList(
new PropertyDto().setKey("sonar.jira.project.key").setValue("SONAR"),
new PropertyDto().setKey("sonar.jira.login.secured").setValue("john")
+ ));
+
+ when(propertiesDao.selectProjectProperties(module.key(), session)).thenReturn(newArrayList(
+ new PropertyDto().setKey("sonar.jira.project.key").setValue("SONAR-SERVER"),
+ new PropertyDto().setKey("sonar.coverage.exclusions").setValue("**/*.java")
+ ));
+
+ when(propertiesDao.selectProjectProperties(subModule.key(), session)).thenReturn(newArrayList(
+ new PropertyDto().setKey("sonar.jira.project.key").setValue("SONAR-SERVER-DAO")
+ ));
+
+ WsTester.TestRequest request = tester.newGetRequest("batch", "project").setParam("key", project.key());
+ request.execute().assertJson(getClass(), "return_project_with_module_with_sub_module.json");
+ }
+
+ @Test
+ public void return_sub_module_settings() throws Exception {
+ MockUserSession.set().setLogin("john").setGlobalPermissions(GlobalPermissions.SCAN_EXECUTION, GlobalPermissions.DRY_RUN_EXECUTION);
+
+ when(componentDao.getRootProjectByKey(subModule.key(), session)).thenReturn(project);
+ when(componentDao.getParentModuleByKey(module.key(), session)).thenReturn(project);
+ when(componentDao.getParentModuleByKey(subModule.key(), session)).thenReturn(module);
+
+ when(propertiesDao.selectProjectProperties(subModule.key(), session)).thenReturn(newArrayList(
+ new PropertyDto().setKey("sonar.jira.project.key").setValue("SONAR"),
+ new PropertyDto().setKey("sonar.jira.login.secured").setValue("john"),
+ new PropertyDto().setKey("sonar.coverage.exclusions").setValue("**/*.java")
));
- // No property on module
- when(qProfileFactory.getDefault(session, "java")).thenReturn(
- QualityProfileDto.createFor("abcd").setName("Default").setLanguage("java").setRulesUpdatedAt("2014-01-14T14:00:00+0200")
- );
+ WsTester.TestRequest request = tester.newGetRequest("batch", "project").setParam("key", subModule.key());
+ request.execute().assertJson(getClass(), "return_sub_module_settings.json");
+ }
- WsTester.TestRequest request = tester.newGetRequest("batch", "project").setParam("key", projectKey);
- request.execute().assertJson(getClass(), "return_settings_by_modules_without_empty_module_settings.json");
+ @Test
+ public void return_sub_module_settings_including_settings_from_parent_modules() throws Exception {
+ MockUserSession.set().setLogin("john").setGlobalPermissions(GlobalPermissions.SCAN_EXECUTION, GlobalPermissions.DRY_RUN_EXECUTION);
+
+ when(componentDao.getRootProjectByKey(subModule.key(), session)).thenReturn(project);
+ when(componentDao.getParentModuleByKey(module.key(), session)).thenReturn(project);
+ when(componentDao.getParentModuleByKey(subModule.key(), session)).thenReturn(module);
+
+ when(propertiesDao.selectProjectProperties(project.key(), session)).thenReturn(newArrayList(
+ new PropertyDto().setKey("sonar.jira.project.key").setValue("SONAR")
+ ));
+
+ when(propertiesDao.selectProjectProperties(module.key(), session)).thenReturn(newArrayList(
+ new PropertyDto().setKey("sonar.jira.login.secured").setValue("john")
+ ));
+
+ when(propertiesDao.selectProjectProperties(subModule.key(), session)).thenReturn(newArrayList(
+ new PropertyDto().setKey("sonar.coverage.exclusions").setValue("**/*.java")
+ ));
+
+ WsTester.TestRequest request = tester.newGetRequest("batch", "project").setParam("key", subModule.key());
+ request.execute().assertJson(getClass(), "return_sub_module_settings_including_settings_from_parent_modules.json");
+ }
+
+ @Test
+ public void return_sub_module_settings_only_inherited_from_project() throws Exception {
+ MockUserSession.set().setLogin("john").setGlobalPermissions(GlobalPermissions.SCAN_EXECUTION, GlobalPermissions.DRY_RUN_EXECUTION);
+
+ when(componentDao.getRootProjectByKey(subModule.key(), session)).thenReturn(project);
+ when(componentDao.getParentModuleByKey(module.key(), session)).thenReturn(project);
+ when(componentDao.getParentModuleByKey(subModule.key(), session)).thenReturn(module);
+
+ when(propertiesDao.selectProjectProperties(project.key(), session)).thenReturn(newArrayList(
+ new PropertyDto().setKey("sonar.jira.project.key").setValue("SONAR"),
+ new PropertyDto().setKey("sonar.jira.login.secured").setValue("john"),
+ new PropertyDto().setKey("sonar.coverage.exclusions").setValue("**/*.java")
+ ));
+ // No settings on module or sub module -> All setting should come from the project
+
+ WsTester.TestRequest request = tester.newGetRequest("batch", "project").setParam("key", subModule.key());
+ request.execute().assertJson(getClass(), "return_sub_module_settings_inherited_from_project.json");
+ }
+
+ @Test
+ public void return_sub_module_settings_inherited_from_project_and_module() throws Exception {
+ MockUserSession.set().setLogin("john").setGlobalPermissions(GlobalPermissions.SCAN_EXECUTION, GlobalPermissions.DRY_RUN_EXECUTION);
+
+ when(componentDao.getRootProjectByKey(subModule.key(), session)).thenReturn(project);
+ when(componentDao.getParentModuleByKey(module.key(), session)).thenReturn(project);
+ when(componentDao.getParentModuleByKey(subModule.key(), session)).thenReturn(module);
+
+ when(propertiesDao.selectProjectProperties(project.key(), session)).thenReturn(newArrayList(
+ new PropertyDto().setKey("sonar.jira.login.secured").setValue("john"),
+ new PropertyDto().setKey("sonar.coverage.exclusions").setValue("**/*.java")
+ ));
+
+ when(propertiesDao.selectProjectProperties(module.key(), session)).thenReturn(newArrayList(
+ new PropertyDto().setKey("sonar.jira.project.key").setValue("SONAR-SERVER")
+ ));
+
+ // No settings on sub module -> All setting should come from the project and the module
+
+ WsTester.TestRequest request = tester.newGetRequest("batch", "project").setParam("key", subModule.key());
+ request.execute().assertJson(getClass(), "return_sub_module_settings_inherited_from_project_and_module.json");
}
@Test
@@ -154,7 +281,7 @@ public class ProjectReferentialsActionTest {
when(qProfileFactory.getByProjectAndLanguage(session, projectKey, "java")).thenReturn(
QualityProfileDto.createFor("abcd").setName("Default").setLanguage("java").setRulesUpdatedAt("2014-01-14T14:00:00+0200")
- );
+ );
WsTester.TestRequest request = tester.newGetRequest("batch", "project").setParam("key", projectKey);
request.execute().assertJson(getClass(), "return_quality_profiles.json");
@@ -168,7 +295,7 @@ public class ProjectReferentialsActionTest {
try {
request.execute();
- } catch (Exception e){
+ } catch (Exception e) {
assertThat(e).isInstanceOf(IllegalStateException.class).hasMessage("No quality profile can been found on language 'java' for project 'org.codehaus.sonar:sonar'");
}
}
@@ -180,7 +307,7 @@ public class ProjectReferentialsActionTest {
when(qProfileFactory.getDefault(session, "java")).thenReturn(
QualityProfileDto.createFor("abcd").setName("Default").setLanguage("java").setRulesUpdatedAt("2014-01-14T14:00:00+0200")
- );
+ );
WsTester.TestRequest request = tester.newGetRequest("batch", "project").setParam("key", projectKey);
request.execute().assertJson(getClass(), "return_quality_profile_from_default_profile.json");
@@ -193,20 +320,34 @@ public class ProjectReferentialsActionTest {
when(qProfileFactory.getByNameAndLanguage(session, "Default", "java")).thenReturn(
QualityProfileDto.createFor("abcd").setName("Default").setLanguage("java").setRulesUpdatedAt("2014-01-14T14:00:00+0200")
- );
+ );
WsTester.TestRequest request = tester.newGetRequest("batch", "project").setParam("key", projectKey).setParam("profile", "Default");
request.execute().assertJson(getClass(), "return_quality_profile_from_given_profile_name.json");
}
@Test
+ public void return_quality_profiles_even_when_project_does_not_exists() throws Exception {
+ MockUserSession.set().setLogin("john").setGlobalPermissions(GlobalPermissions.SCAN_EXECUTION, GlobalPermissions.DRY_RUN_EXECUTION);
+ String projectKey = "org.codehaus.sonar:sonar";
+ when(componentDao.getNullableByKey(session, projectKey)).thenReturn(null);
+
+ when(qProfileFactory.getDefault(session, "java")).thenReturn(
+ QualityProfileDto.createFor("abcd").setName("Default").setLanguage("java").setRulesUpdatedAt("2014-01-14T14:00:00+0200")
+ );
+
+ WsTester.TestRequest request = tester.newGetRequest("batch", "project").setParam("key", projectKey);
+ request.execute().assertJson(getClass(), "return_quality_profiles_even_when_project_does_not_exists.json");
+ }
+
+ @Test
public void return_active_rules() throws Exception {
MockUserSession.set().setLogin("john").setGlobalPermissions(GlobalPermissions.SCAN_EXECUTION, GlobalPermissions.DRY_RUN_EXECUTION);
String projectKey = "org.codehaus.sonar:sonar";
when(qProfileFactory.getByProjectAndLanguage(session, projectKey, "java")).thenReturn(
QualityProfileDto.createFor("abcd").setName("Default").setLanguage("java").setRulesUpdatedAt("2014-01-14T14:00:00+0200")
- );
+ );
RuleKey ruleKey = RuleKey.of("squid", "AvoidCycle");
ActiveRule activeRule = mock(ActiveRule.class);
diff --git a/server/sonar-server/src/test/java/org/sonar/server/component/persistence/ComponentDaoTest.java b/server/sonar-server/src/test/java/org/sonar/server/component/persistence/ComponentDaoTest.java
index c5cb63a11b5..190127ecca3 100644
--- a/server/sonar-server/src/test/java/org/sonar/server/component/persistence/ComponentDaoTest.java
+++ b/server/sonar-server/src/test/java/org/sonar/server/component/persistence/ComponentDaoTest.java
@@ -27,6 +27,7 @@ import org.sonar.api.utils.System2;
import org.sonar.core.component.ComponentDto;
import org.sonar.core.persistence.AbstractDaoTestCase;
import org.sonar.core.persistence.DbSession;
+import org.sonar.server.exceptions.NotFoundException;
import java.util.Date;
import java.util.List;
@@ -109,16 +110,49 @@ public class ComponentDaoTest extends AbstractDaoTestCase {
@Test
public void find_modules_by_project() throws Exception {
- setupData("shared");
+ setupData("multi-modules");
List<ComponentDto> results = dao.findModulesByProject("org.struts:struts", session);
assertThat(results).hasSize(1);
assertThat(results.get(0).getKey()).isEqualTo("org.struts:struts-core");
+ results = dao.findModulesByProject("org.struts:struts-core", session);
+ assertThat(results).hasSize(1);
+ assertThat(results.get(0).getKey()).isEqualTo("org.struts:struts-data");
+
+ assertThat(dao.findModulesByProject("org.struts:struts-data", session)).isEmpty();
+
assertThat(dao.findModulesByProject("unknown", session)).isEmpty();
}
@Test
+ public void get_root_project_by_key() throws Exception {
+ setupData("multi-modules");
+
+ assertThat(dao.getRootProjectByKey("org.struts:struts-data", session).getKey()).isEqualTo("org.struts:struts");
+ assertThat(dao.getRootProjectByKey("org.struts:struts-core", session).getKey()).isEqualTo("org.struts:struts");
+
+ // Root project of a project is itself
+ assertThat(dao.getRootProjectByKey("org.struts:struts", session).getKey()).isEqualTo("org.struts:struts");
+ }
+
+ @Test(expected = NotFoundException.class)
+ public void get_root_project_by_key_on_unknown_project() throws Exception {
+ dao.getRootProjectByKey("unknown", session);
+ }
+
+ @Test
+ public void get_parent_module_by_key() throws Exception {
+ setupData("multi-modules");
+
+ assertThat(dao.getParentModuleByKey("org.struts:struts-data", session).getKey()).isEqualTo("org.struts:struts-core");
+ assertThat(dao.getParentModuleByKey("org.struts:struts-core", session).getKey()).isEqualTo("org.struts:struts");
+ assertThat(dao.getParentModuleByKey("org.struts:struts", session)).isNull();
+
+ assertThat(dao.getParentModuleByKey("unknown", session)).isNull();
+ }
+
+ @Test
public void insert() {
when(system2.now()).thenReturn(DateUtils.parseDate("2014-06-18").getTime());
setupData("empty");
diff --git a/server/sonar-server/src/test/java/org/sonar/server/db/DatabaseCheckerTest.java b/server/sonar-server/src/test/java/org/sonar/server/db/DatabaseCheckerTest.java
index 9baaac36dd3..c4c5844e68e 100644
--- a/server/sonar-server/src/test/java/org/sonar/server/db/DatabaseCheckerTest.java
+++ b/server/sonar-server/src/test/java/org/sonar/server/db/DatabaseCheckerTest.java
@@ -62,7 +62,9 @@ public class DatabaseCheckerTest {
@Test
public void log_warning_if_h2() throws Exception {
Database db = mockDb(new H2(), "13.4");
- new DatabaseChecker(db).start();
+ DatabaseChecker checker = new DatabaseChecker(db);
+ checker.start();
+ checker.stop();
// TODO test log
}
diff --git a/server/sonar-server/src/test/java/org/sonar/server/issue/RulesAggregationTest.java b/server/sonar-server/src/test/java/org/sonar/server/issue/RulesAggregationTest.java
index 31cb3fbc9e9..7ae8476846f 100644
--- a/server/sonar-server/src/test/java/org/sonar/server/issue/RulesAggregationTest.java
+++ b/server/sonar-server/src/test/java/org/sonar/server/issue/RulesAggregationTest.java
@@ -43,7 +43,7 @@ public class RulesAggregationTest {
rulesAggregation.add(ruleDto);
rulesAggregation.add(ruleDto);
- RulesAggregation.Rule rule = new RulesAggregation.Rule().setRuleKey(ruleKey).setName("Rule name");
+ RulesAggregation.Rule rule = new RulesAggregation.Rule(ruleKey, "Rule name");
assertThat(rulesAggregation.rules()).hasSize(1);
assertThat(rulesAggregation.rules().iterator().next().name()).isEqualTo("Rule name");
@@ -61,4 +61,19 @@ public class RulesAggregationTest {
assertThat(rulesAggregation.rules()).hasSize(2);
}
+
+ @Test
+ public void test_equals_and_hash_code() throws Exception {
+ RulesAggregation.Rule rule = new RulesAggregation.Rule(RuleKey.of("xoo", "S001"), "S001");
+ RulesAggregation.Rule ruleSameRuleKey = new RulesAggregation.Rule(RuleKey.of("xoo", "S001"), "S001");
+ RulesAggregation.Rule ruleWithDifferentRuleKey = new RulesAggregation.Rule(RuleKey.of("xoo", "S002"), "S002");
+
+ assertThat(rule).isEqualTo(rule);
+ assertThat(rule).isEqualTo(ruleSameRuleKey);
+ assertThat(rule).isNotEqualTo(ruleWithDifferentRuleKey);
+
+ assertThat(rule.hashCode()).isEqualTo(rule.hashCode());
+ assertThat(rule.hashCode()).isEqualTo(ruleSameRuleKey.hashCode());
+ assertThat(rule.hashCode()).isNotEqualTo(ruleWithDifferentRuleKey.hashCode());
+ }
}
diff --git a/server/sonar-server/src/test/java/org/sonar/server/measure/MeasureFilterFactoryTest.java b/server/sonar-server/src/test/java/org/sonar/server/measure/MeasureFilterFactoryTest.java
index bd6348e7a28..20cc5872bee 100644
--- a/server/sonar-server/src/test/java/org/sonar/server/measure/MeasureFilterFactoryTest.java
+++ b/server/sonar-server/src/test/java/org/sonar/server/measure/MeasureFilterFactoryTest.java
@@ -33,6 +33,7 @@ import java.util.Arrays;
import java.util.List;
import java.util.Map;
+import static com.google.common.collect.Lists.newArrayList;
import static org.fest.assertions.Assertions.assertThat;
import static org.mockito.Matchers.anyString;
import static org.mockito.Mockito.mock;
@@ -90,6 +91,18 @@ public class MeasureFilterFactoryTest {
}
@Test
+ public void fallback_on_name_sort_when_sort_is_unknown() {
+ MeasureFilterFactory factory = new MeasureFilterFactory(newMetricFinder(), system);
+ Map<String, Object> props = ImmutableMap.<String, Object>of("sort", "unknown");
+ MeasureFilter filter = factory.create(props);
+
+ assertThat(filter.sort().column()).isEqualTo("p.long_name");
+ assertThat(filter.sort().metric()).isNull();
+ assertThat(filter.sort().period()).isNull();
+ assertThat(filter.sort().isAsc()).isTrue();
+ }
+
+ @Test
public void descending_sort() {
MeasureFilterFactory factory = new MeasureFilterFactory(newMetricFinder(), system);
Map<String, Object> props = ImmutableMap.<String, Object>of("asc", "false");
@@ -196,6 +209,39 @@ public class MeasureFilterFactoryTest {
}
@Test
+ public void name_conditions() {
+ MeasureFilterFactory factory = new MeasureFilterFactory(newMetricFinder(), system);
+ Map<String, Object> props = ImmutableMap.<String, Object>of(
+ "nameSearch", "SonarQube"
+ );
+ MeasureFilter filter = factory.create(props);
+
+ assertThat(filter.getResourceName()).isEqualTo("SonarQube");
+ }
+
+ @Test
+ public void not_fail_when_name_conditions_contains_array() {
+ MeasureFilterFactory factory = new MeasureFilterFactory(newMetricFinder(), system);
+ Map<String, Object> props = ImmutableMap.<String, Object>of(
+ "nameSearch", new String[]{"sonar", "qube"}
+ );
+ MeasureFilter filter = factory.create(props);
+
+ assertThat(filter.getResourceName()).isEqualTo("sonar,qube");
+ }
+
+ @Test
+ public void not_fail_when_name_conditions_contains_list() {
+ MeasureFilterFactory factory = new MeasureFilterFactory(newMetricFinder(), system);
+ Map<String, Object> props = ImmutableMap.<String, Object>of(
+ "nameSearch", newArrayList("sonar", "qube")
+ );
+ MeasureFilter filter = factory.create(props);
+
+ assertThat(filter.getResourceName()).isEqualTo("sonar,qube");
+ }
+
+ @Test
public void ignore_partial_measure_condition() {
MeasureFilterFactory factory = new MeasureFilterFactory(newMetricFinder(), system);
Map<String, Object> props = ImmutableMap.<String, Object>of(
diff --git a/server/sonar-server/src/test/java/org/sonar/server/rule/index/RuleIndexMediumTest.java b/server/sonar-server/src/test/java/org/sonar/server/rule/index/RuleIndexMediumTest.java
index 3b7fdb0ed29..27e50982d8b 100644
--- a/server/sonar-server/src/test/java/org/sonar/server/rule/index/RuleIndexMediumTest.java
+++ b/server/sonar-server/src/test/java/org/sonar/server/rule/index/RuleIndexMediumTest.java
@@ -54,8 +54,10 @@ import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Date;
+import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
+import java.util.Set;
import static com.google.common.collect.Lists.newArrayList;
import static org.fest.assertions.Assertions.assertThat;
@@ -613,7 +615,7 @@ public class RuleIndexMediumTest {
// 4. get all active rules on profile
result = index.search(new RuleQuery().setActivation(true)
- .setQProfileKey(qualityProfileDto2.getKey()),
+ .setQProfileKey(qualityProfileDto2.getKey()),
new QueryOptions());
assertThat(result.getHits()).hasSize(1);
assertThat(result.getHits().get(0).name()).isEqualTo(rule1.getName());
@@ -621,7 +623,7 @@ public class RuleIndexMediumTest {
}
@Test
- public void search_by_profile_inheritance_and_active_severities() throws InterruptedException {
+ public void search_by_profile_and_inheritance() throws InterruptedException {
QualityProfileDto qualityProfileDto1 = QProfileTesting.newXooP1();
QualityProfileDto qualityProfileDto2 = QProfileTesting.newXooP2().setParentKee(QProfileTesting.XOO_P1_KEY);
db.qualityProfileDao().insert(dbSession, qualityProfileDto1, qualityProfileDto2);
@@ -650,7 +652,7 @@ public class RuleIndexMediumTest {
ActiveRuleDto.createFor(qualityProfileDto2, rule3)
.setSeverity("BLOCKER")
.setInheritance(ActiveRule.Inheritance.INHERITED.name())
- );
+ );
dbSession.commit();
@@ -672,77 +674,52 @@ public class RuleIndexMediumTest {
// 3. get Inherited Rules on profile1
result = index.search(new RuleQuery().setActivation(true)
- .setQProfileKey(qualityProfileDto1.getKey())
- .setInheritance(ImmutableSet.of(ActiveRule.Inheritance.INHERITED.name())),
+ .setQProfileKey(qualityProfileDto1.getKey())
+ .setInheritance(ImmutableSet.of(ActiveRule.Inheritance.INHERITED.name())),
new QueryOptions()
- );
+ );
assertThat(result.getHits()).hasSize(0);
// 4. get Inherited Rules on profile2
result = index.search(new RuleQuery().setActivation(true)
- .setQProfileKey(qualityProfileDto2.getKey())
- .setInheritance(ImmutableSet.of(ActiveRule.Inheritance.INHERITED.name())),
+ .setQProfileKey(qualityProfileDto2.getKey())
+ .setInheritance(ImmutableSet.of(ActiveRule.Inheritance.INHERITED.name())),
new QueryOptions()
- );
+ );
assertThat(result.getHits()).hasSize(2);
// 5. get Overridden Rules on profile1
result = index.search(new RuleQuery().setActivation(true)
- .setQProfileKey(qualityProfileDto1.getKey())
- .setInheritance(ImmutableSet.of(ActiveRule.Inheritance.OVERRIDES.name())),
+ .setQProfileKey(qualityProfileDto1.getKey())
+ .setInheritance(ImmutableSet.of(ActiveRule.Inheritance.OVERRIDES.name())),
new QueryOptions()
- );
+ );
assertThat(result.getHits()).hasSize(0);
// 6. get Overridden Rules on profile2
result = index.search(new RuleQuery().setActivation(true)
- .setQProfileKey(qualityProfileDto2.getKey())
- .setInheritance(ImmutableSet.of(ActiveRule.Inheritance.OVERRIDES.name())),
+ .setQProfileKey(qualityProfileDto2.getKey())
+ .setInheritance(ImmutableSet.of(ActiveRule.Inheritance.OVERRIDES.name())),
new QueryOptions()
- );
+ );
assertThat(result.getHits()).hasSize(1);
// 7. get Inherited AND Overridden Rules on profile1
result = index.search(new RuleQuery().setActivation(true)
- .setQProfileKey(qualityProfileDto1.getKey())
- .setInheritance(ImmutableSet.of(
- ActiveRule.Inheritance.INHERITED.name(), ActiveRule.Inheritance.OVERRIDES.name())),
+ .setQProfileKey(qualityProfileDto1.getKey())
+ .setInheritance(ImmutableSet.of(
+ ActiveRule.Inheritance.INHERITED.name(), ActiveRule.Inheritance.OVERRIDES.name())),
new QueryOptions()
- );
+ );
assertThat(result.getHits()).hasSize(0);
// 8. get Inherited AND Overridden Rules on profile2
result = index.search(new RuleQuery().setActivation(true)
- .setQProfileKey(qualityProfileDto2.getKey())
- .setInheritance(ImmutableSet.of(
- ActiveRule.Inheritance.INHERITED.name(), ActiveRule.Inheritance.OVERRIDES.name())),
+ .setQProfileKey(qualityProfileDto2.getKey())
+ .setInheritance(ImmutableSet.of(
+ ActiveRule.Inheritance.INHERITED.name(), ActiveRule.Inheritance.OVERRIDES.name())),
new QueryOptions()
- );
- assertThat(result.getHits()).hasSize(3);
-
- // 9. get rules active on profile1 with active severity BLOCKER
- result = index.search(new RuleQuery().setActivation(true)
- .setQProfileKey(qualityProfileDto1.getKey())
- .setActiveSeverities(ImmutableSet.of(
- Severity.BLOCKER.toString())),
- new QueryOptions()
- );
- assertThat(result.getHits()).hasSize(3);
-
- // 10. get rules active on profile2 with active severity MINOR, then BLOCKER + MINOR
- result = index.search(new RuleQuery().setActivation(true)
- .setQProfileKey(qualityProfileDto2.getKey())
- .setActiveSeverities(ImmutableSet.of(
- Severity.MINOR.toString())),
- new QueryOptions()
- );
- assertThat(result.getHits()).hasSize(1);
- result = index.search(new RuleQuery().setActivation(true)
- .setQProfileKey(qualityProfileDto2.getKey())
- .setActiveSeverities(ImmutableSet.of(
- Severity.BLOCKER.toString(), Severity.MINOR.toString())),
- new QueryOptions()
- );
+ );
assertThat(result.getHits()).hasSize(3);
}
@@ -956,6 +933,19 @@ public class RuleIndexMediumTest {
}
@Test
+ public void scroll_byIds() throws Exception {
+ Set<Integer> ids = new HashSet<Integer>();
+ for (int i = 0; i < 150; i++) {
+ RuleDto rule = RuleTesting.newDto(RuleKey.of("scroll", "r_" + i));
+ dao.insert(dbSession, rule);
+ dbSession.commit();
+ ids.add(rule.getId());
+ }
+ List<Rule> rules = index.getByIds(ids);
+ assertThat(rules).hasSize(ids.size());
+ }
+
+ @Test
public void search_protected_chars() throws InterruptedException {
String nameWithProtectedChars = "ja#va&sc\"r:ipt";
diff --git a/server/sonar-server/src/test/java/org/sonar/server/tester/ServerTester.java b/server/sonar-server/src/test/java/org/sonar/server/tester/ServerTester.java
index b1bef3250f1..480382fae23 100644
--- a/server/sonar-server/src/test/java/org/sonar/server/tester/ServerTester.java
+++ b/server/sonar-server/src/test/java/org/sonar/server/tester/ServerTester.java
@@ -24,7 +24,6 @@ import com.google.common.collect.Lists;
import org.apache.commons.io.FileUtils;
import org.apache.commons.lang.StringUtils;
import org.junit.rules.ExternalResource;
-import org.sonar.api.CoreProperties;
import org.sonar.api.database.DatabaseProperties;
import org.sonar.server.platform.Platform;
import org.sonar.server.search.IndexProperties;
diff --git a/server/sonar-server/src/test/resources/org/sonar/server/batch/ProjectReferentialsActionTest/return_settings_by_modules_without_empty_module_settings.json b/server/sonar-server/src/test/resources/org/sonar/server/batch/ProjectReferentialsActionTest/return_project_settings.json
index 4b562891c28..4b562891c28 100644
--- a/server/sonar-server/src/test/resources/org/sonar/server/batch/ProjectReferentialsActionTest/return_settings_by_modules_without_empty_module_settings.json
+++ b/server/sonar-server/src/test/resources/org/sonar/server/batch/ProjectReferentialsActionTest/return_project_settings.json
diff --git a/server/sonar-server/src/test/resources/org/sonar/server/batch/ProjectReferentialsActionTest/return_settings_by_modules.json b/server/sonar-server/src/test/resources/org/sonar/server/batch/ProjectReferentialsActionTest/return_project_with_module_settings.json
index a8e063a344b..d1b864aa7c4 100644
--- a/server/sonar-server/src/test/resources/org/sonar/server/batch/ProjectReferentialsActionTest/return_settings_by_modules.json
+++ b/server/sonar-server/src/test/resources/org/sonar/server/batch/ProjectReferentialsActionTest/return_project_with_module_settings.json
@@ -15,6 +15,8 @@
"sonar.jira.login.secured": "john"
},
"org.codehaus.sonar:sonar-server": {
+ "sonar.jira.project.key": "SONAR-SERVER",
+ "sonar.jira.login.secured": "john",
"sonar.coverage.exclusions": "**/*.java"
}
}
diff --git a/server/sonar-server/src/test/resources/org/sonar/server/batch/ProjectReferentialsActionTest/return_project_with_module_settings_inherited_from_project.json b/server/sonar-server/src/test/resources/org/sonar/server/batch/ProjectReferentialsActionTest/return_project_with_module_settings_inherited_from_project.json
new file mode 100644
index 00000000000..deaea79e1a3
--- /dev/null
+++ b/server/sonar-server/src/test/resources/org/sonar/server/batch/ProjectReferentialsActionTest/return_project_with_module_settings_inherited_from_project.json
@@ -0,0 +1,22 @@
+{
+ "timestamp": 0,
+ "qprofilesByLanguage": {
+ "java": {
+ "key": "abcd",
+ "name": "Default",
+ "language": "java",
+ "rulesUpdatedAt": "Jan 14, 2014 1:00:00 PM"
+ }
+ },
+ "activeRules": [],
+ "settingsByModule": {
+ "org.codehaus.sonar:sonar": {
+ "sonar.jira.project.key": "SONAR",
+ "sonar.jira.login.secured": "john"
+ },
+ "org.codehaus.sonar:sonar-server": {
+ "sonar.jira.project.key": "SONAR",
+ "sonar.jira.login.secured": "john"
+ }
+ }
+}
diff --git a/server/sonar-server/src/test/resources/org/sonar/server/batch/ProjectReferentialsActionTest/return_project_with_module_with_sub_module.json b/server/sonar-server/src/test/resources/org/sonar/server/batch/ProjectReferentialsActionTest/return_project_with_module_with_sub_module.json
new file mode 100644
index 00000000000..ef8ef13fa12
--- /dev/null
+++ b/server/sonar-server/src/test/resources/org/sonar/server/batch/ProjectReferentialsActionTest/return_project_with_module_with_sub_module.json
@@ -0,0 +1,28 @@
+{
+ "timestamp": 0,
+ "qprofilesByLanguage": {
+ "java": {
+ "key": "abcd",
+ "name": "Default",
+ "language": "java",
+ "rulesUpdatedAt": "Jan 14, 2014 1:00:00 PM"
+ }
+ },
+ "activeRules": [],
+ "settingsByModule": {
+ "org.codehaus.sonar:sonar": {
+ "sonar.jira.project.key": "SONAR",
+ "sonar.jira.login.secured": "john"
+ },
+ "org.codehaus.sonar:sonar-server": {
+ "sonar.jira.project.key": "SONAR-SERVER",
+ "sonar.jira.login.secured": "john",
+ "sonar.coverage.exclusions": "**/*.java"
+ },
+ "org.codehaus.sonar:sonar-server-dao": {
+ "sonar.jira.project.key": "SONAR-SERVER-DAO",
+ "sonar.jira.login.secured": "john",
+ "sonar.coverage.exclusions": "**/*.java"
+ }
+ }
+}
diff --git a/server/sonar-server/src/test/resources/org/sonar/server/batch/ProjectReferentialsActionTest/return_quality_profiles_even_when_project_does_not_exists.json b/server/sonar-server/src/test/resources/org/sonar/server/batch/ProjectReferentialsActionTest/return_quality_profiles_even_when_project_does_not_exists.json
new file mode 100644
index 00000000000..a1bb33bb01f
--- /dev/null
+++ b/server/sonar-server/src/test/resources/org/sonar/server/batch/ProjectReferentialsActionTest/return_quality_profiles_even_when_project_does_not_exists.json
@@ -0,0 +1,13 @@
+{
+ "timestamp": 0,
+ "qprofilesByLanguage": {
+ "java": {
+ "key": "abcd",
+ "name": "Default",
+ "language": "java",
+ "rulesUpdatedAt": "Jan 14, 2014 1:00:00 PM"
+ }
+ },
+ "activeRules": [],
+ "settingsByModule": {}
+}
diff --git a/server/sonar-server/src/test/resources/org/sonar/server/batch/ProjectReferentialsActionTest/return_sub_module_settings.json b/server/sonar-server/src/test/resources/org/sonar/server/batch/ProjectReferentialsActionTest/return_sub_module_settings.json
new file mode 100644
index 00000000000..f2a89a9895a
--- /dev/null
+++ b/server/sonar-server/src/test/resources/org/sonar/server/batch/ProjectReferentialsActionTest/return_sub_module_settings.json
@@ -0,0 +1,19 @@
+{
+ "timestamp": 0,
+ "qprofilesByLanguage": {
+ "java": {
+ "key": "abcd",
+ "name": "Default",
+ "language": "java",
+ "rulesUpdatedAt": "Jan 14, 2014 1:00:00 PM"
+ }
+ },
+ "activeRules": [],
+ "settingsByModule": {
+ "org.codehaus.sonar:sonar-server-dao": {
+ "sonar.jira.project.key": "SONAR",
+ "sonar.jira.login.secured": "john",
+ "sonar.coverage.exclusions": "**/*.java"
+ }
+ }
+}
diff --git a/server/sonar-server/src/test/resources/org/sonar/server/batch/ProjectReferentialsActionTest/return_sub_module_settings_including_settings_from_parent_modules.json b/server/sonar-server/src/test/resources/org/sonar/server/batch/ProjectReferentialsActionTest/return_sub_module_settings_including_settings_from_parent_modules.json
new file mode 100644
index 00000000000..f2a89a9895a
--- /dev/null
+++ b/server/sonar-server/src/test/resources/org/sonar/server/batch/ProjectReferentialsActionTest/return_sub_module_settings_including_settings_from_parent_modules.json
@@ -0,0 +1,19 @@
+{
+ "timestamp": 0,
+ "qprofilesByLanguage": {
+ "java": {
+ "key": "abcd",
+ "name": "Default",
+ "language": "java",
+ "rulesUpdatedAt": "Jan 14, 2014 1:00:00 PM"
+ }
+ },
+ "activeRules": [],
+ "settingsByModule": {
+ "org.codehaus.sonar:sonar-server-dao": {
+ "sonar.jira.project.key": "SONAR",
+ "sonar.jira.login.secured": "john",
+ "sonar.coverage.exclusions": "**/*.java"
+ }
+ }
+}
diff --git a/server/sonar-server/src/test/resources/org/sonar/server/batch/ProjectReferentialsActionTest/return_sub_module_settings_inherited_from_project.json b/server/sonar-server/src/test/resources/org/sonar/server/batch/ProjectReferentialsActionTest/return_sub_module_settings_inherited_from_project.json
new file mode 100644
index 00000000000..f2a89a9895a
--- /dev/null
+++ b/server/sonar-server/src/test/resources/org/sonar/server/batch/ProjectReferentialsActionTest/return_sub_module_settings_inherited_from_project.json
@@ -0,0 +1,19 @@
+{
+ "timestamp": 0,
+ "qprofilesByLanguage": {
+ "java": {
+ "key": "abcd",
+ "name": "Default",
+ "language": "java",
+ "rulesUpdatedAt": "Jan 14, 2014 1:00:00 PM"
+ }
+ },
+ "activeRules": [],
+ "settingsByModule": {
+ "org.codehaus.sonar:sonar-server-dao": {
+ "sonar.jira.project.key": "SONAR",
+ "sonar.jira.login.secured": "john",
+ "sonar.coverage.exclusions": "**/*.java"
+ }
+ }
+}
diff --git a/server/sonar-server/src/test/resources/org/sonar/server/batch/ProjectReferentialsActionTest/return_sub_module_settings_inherited_from_project_and_module.json b/server/sonar-server/src/test/resources/org/sonar/server/batch/ProjectReferentialsActionTest/return_sub_module_settings_inherited_from_project_and_module.json
new file mode 100644
index 00000000000..c99fd8e67db
--- /dev/null
+++ b/server/sonar-server/src/test/resources/org/sonar/server/batch/ProjectReferentialsActionTest/return_sub_module_settings_inherited_from_project_and_module.json
@@ -0,0 +1,19 @@
+{
+ "timestamp": 0,
+ "qprofilesByLanguage": {
+ "java": {
+ "key": "abcd",
+ "name": "Default",
+ "language": "java",
+ "rulesUpdatedAt": "Jan 14, 2014 1:00:00 PM"
+ }
+ },
+ "activeRules": [],
+ "settingsByModule": {
+ "org.codehaus.sonar:sonar-server-dao": {
+ "sonar.jira.project.key": "SONAR-SERVER",
+ "sonar.jira.login.secured": "john",
+ "sonar.coverage.exclusions": "**/*.java"
+ }
+ }
+}
diff --git a/server/sonar-server/src/test/resources/org/sonar/server/component/persistence/ComponentDaoTest/multi-modules.xml b/server/sonar-server/src/test/resources/org/sonar/server/component/persistence/ComponentDaoTest/multi-modules.xml
new file mode 100644
index 00000000000..decdab5279a
--- /dev/null
+++ b/server/sonar-server/src/test/resources/org/sonar/server/component/persistence/ComponentDaoTest/multi-modules.xml
@@ -0,0 +1,89 @@
+<dataset>
+
+ <!-- Struts projects is authorized for all user -->
+ <group_roles id="1" group_id="[null]" resource_id="1" role="user"/>
+
+
+ <!-- root project -->
+ <projects id="1" root_id="[null]" scope="PRJ" qualifier="TRK" kee="org.struts:struts" name="Struts"
+ description="the description" long_name="Apache Struts"
+ enabled="[true]" language="[null]" copy_resource_id="[null]" person_id="[null]" path="[null]"/>
+ <snapshots id="1" project_id="1" parent_snapshot_id="[null]" root_project_id="1" root_snapshot_id="[null]"
+ status="P" islast="[true]" purge_status="[null]"
+ period1_mode="[null]" period1_param="[null]" period1_date="[null]"
+ period2_mode="[null]" period2_param="[null]" period2_date="[null]"
+ period3_mode="[null]" period3_param="[null]" period3_date="[null]"
+ period4_mode="[null]" period4_param="[null]" period4_date="[null]"
+ period5_mode="[null]" period5_param="[null]" period5_date="[null]"
+ depth="[null]" scope="PRJ" qualifier="TRK" created_at="2008-12-02 13:58:00.00" build_date="2008-12-02 13:58:00.00"
+ version="[null]" path=""/>
+ <snapshots id="10" project_id="1" parent_snapshot_id="[null]" root_project_id="1" root_snapshot_id="[null]"
+ status="P" islast="[false]" purge_status="[null]"
+ period1_mode="[null]" period1_param="[null]" period1_date="[null]"
+ period2_mode="[null]" period2_param="[null]" period2_date="[null]"
+ period3_mode="[null]" period3_param="[null]" period3_date="[null]"
+ period4_mode="[null]" period4_param="[null]" period4_date="[null]"
+ period5_mode="[null]" period5_param="[null]" period5_date="[null]"
+ depth="[null]" scope="PRJ" qualifier="TRK" created_at="2008-12-01 13:58:00.00" build_date="2008-12-01 13:58:00.00"
+ version="[null]" path=""/>
+
+ <!-- module -->
+ <projects id="2" root_id="1" kee="org.struts:struts-core" name="Struts Core"
+ scope="PRJ" qualifier="BRC" long_name="Struts Core"
+ description="[null]" enabled="[true]" language="[null]" copy_resource_id="[null]" person_id="[null]"/>
+ <snapshots id="2" project_id="2" parent_snapshot_id="1" root_project_id="1" root_snapshot_id="1"
+ status="P" islast="[true]" purge_status="[null]"
+ period1_mode="[null]" period1_param="[null]" period1_date="[null]"
+ period2_mode="[null]" period2_param="[null]" period2_date="[null]"
+ period3_mode="[null]" period3_param="[null]" period3_date="[null]"
+ period4_mode="[null]" period4_param="[null]" period4_date="[null]"
+ period5_mode="[null]" period5_param="[null]" period5_date="[null]"
+ depth="[null]" scope="PRJ" qualifier="BRC" created_at="2008-12-02 13:58:00.00" build_date="2008-12-02 13:58:00.00"
+ version="[null]" path="1."/>
+
+ <!-- sub module -->
+ <projects id="3" root_id="1" kee="org.struts:struts-data" name="Struts Data"
+ scope="PRJ" qualifier="BRC" long_name="Struts Data"
+ description="[null]" enabled="[true]" language="[null]" copy_resource_id="[null]" person_id="[null]"/>
+ <snapshots id="3" project_id="3" parent_snapshot_id="2" root_project_id="1" root_snapshot_id="1"
+ status="P" islast="[true]" purge_status="[null]"
+ period1_mode="[null]" period1_param="[null]" period1_date="[null]"
+ period2_mode="[null]" period2_param="[null]" period2_date="[null]"
+ period3_mode="[null]" period3_param="[null]" period3_date="[null]"
+ period4_mode="[null]" period4_param="[null]" period4_date="[null]"
+ period5_mode="[null]" period5_param="[null]" period5_date="[null]"
+ depth="[null]" scope="PRJ" qualifier="BRC" created_at="2008-12-02 13:58:00.00" build_date="2008-12-02 13:58:00.00"
+ version="[null]" path="1.2."/>
+
+ <!-- directory -->
+ <projects long_name="org.struts" id="4" scope="DIR" qualifier="DIR" kee="org.struts:struts-core:src/org/struts"
+ name="src/org/struts" root_id="3"
+ description="[null]"
+ enabled="[true]" language="[null]" copy_resource_id="[null]" person_id="[null]" path="src/org/struts"/>
+ <snapshots id="4" project_id="4" parent_snapshot_id="3" root_project_id="1" root_snapshot_id="1"
+ status="P" islast="[true]" purge_status="[null]"
+ period1_mode="[null]" period1_param="[null]" period1_date="[null]"
+ period2_mode="[null]" period2_param="[null]" period2_date="[null]"
+ period3_mode="[null]" period3_param="[null]" period3_date="[null]"
+ period4_mode="[null]" period4_param="[null]" period4_date="[null]"
+ period5_mode="[null]" period5_param="[null]" period5_date="[null]"
+ depth="[null]" scope="DIR" qualifier="PAC" created_at="2008-12-02 13:58:00.00" build_date="2008-12-02 13:58:00.00"
+ version="[null]" path="1.2.3."/>
+
+ <!-- file -->
+ <projects long_name="org.struts.RequestContext" id="5" scope="FIL" qualifier="FIL" kee="org.struts:struts-core:src/org/struts/RequestContext.java"
+ name="RequestContext.java" root_id="3"
+ description="[null]"
+ enabled="[true]" language="java" copy_resource_id="[null]" person_id="[null]" path="src/org/struts/RequestContext.java"/>
+
+ <snapshots id="5" project_id="5" parent_snapshot_id="4" root_project_id="1" root_snapshot_id="1"
+ status="P" islast="[true]" purge_status="[null]"
+ period1_mode="[null]" period1_param="[null]" period1_date="[null]"
+ period2_mode="[null]" period2_param="[null]" period2_date="[null]"
+ period3_mode="[null]" period3_param="[null]" period3_date="[null]"
+ period4_mode="[null]" period4_param="[null]" period4_date="[null]"
+ period5_mode="[null]" period5_param="[null]" period5_date="[null]"
+ depth="[null]" scope="FIL" qualifier="CLA" created_at="2008-12-02 13:58:00.00" build_date="2008-12-02 13:58:00.00"
+ version="[null]" path="1.2.3.4."/>
+
+</dataset>
diff --git a/server/sonar-web/Gruntfile.coffee b/server/sonar-web/Gruntfile.coffee
index 9fd22feb5db..f905e6201af 100644
--- a/server/sonar-web/Gruntfile.coffee
+++ b/server/sonar-web/Gruntfile.coffee
@@ -81,6 +81,7 @@ module.exports = (grunt) ->
'<%= pkg.assets %>js/third-party/underscore.js'
'<%= pkg.assets %>js/third-party/select2.js'
'<%= pkg.assets %>js/third-party/keymaster.js'
+ '<%= pkg.assets %>js/third-party/moment.js'
'<%= pkg.assets %>js/select2-jquery-ui-fix.js'
'<%= pkg.assets %>js/widgets/base.js'
'<%= pkg.assets %>js/widgets/widget.js'
@@ -113,6 +114,7 @@ module.exports = (grunt) ->
'<%= pkg.assets %>js/third-party/underscore.js'
'<%= pkg.assets %>js/third-party/select2.js'
'<%= pkg.assets %>js/third-party/keymaster.js'
+ '<%= pkg.assets %>js/third-party/moment.js'
'<%= pkg.assets %>js/select2-jquery-ui-fix.js'
'<%= pkg.assets %>js/widgets/base.js'
'<%= pkg.assets %>js/widgets/widget.js'
diff --git a/server/sonar-web/pom.xml b/server/sonar-web/pom.xml
index 6aec8f63f33..7e51b5c2a76 100644
--- a/server/sonar-web/pom.xml
+++ b/server/sonar-web/pom.xml
@@ -351,7 +351,6 @@
<sonar.log.console>true</sonar.log.console>
<sonar.log.profilingLevel>BASIC</sonar.log.profilingLevel>
<sonar.web.context>/dev</sonar.web.context>
- <sonar.updatecenter.activate>false</sonar.updatecenter.activate>
</systemProperties>
</configuration>
</plugin>
diff --git a/server/sonar-web/src/main/coffee/coding-rules/views/coding-rules-facets-view.coffee b/server/sonar-web/src/main/coffee/coding-rules/views/coding-rules-facets-view.coffee
index 22204f2929a..b0bc3653b67 100644
--- a/server/sonar-web/src/main/coffee/coding-rules/views/coding-rules-facets-view.coffee
+++ b/server/sonar-web/src/main/coffee/coding-rules/views/coding-rules-facets-view.coffee
@@ -24,7 +24,8 @@ define [
that = @
@options.collection.each (facet) ->
property = facet.get 'property'
- facet.set 'property_message', 'coding_rules.facets.' + property
+ facet.set 'property_message', t 'coding_rules.facets.' + property
+ facet.set 'limitReached', facet.get('values').length >= 10
_.each(facet.get('values'), (value) ->
value.text = that.options.app.facetLabel(property, value.val)
)
diff --git a/server/sonar-web/src/main/coffee/coding-rules/views/coding-rules-list-item-view.coffee b/server/sonar-web/src/main/coffee/coding-rules/views/coding-rules-list-item-view.coffee
index 77efec04259..0c77a630538 100644
--- a/server/sonar-web/src/main/coffee/coding-rules/views/coding-rules-list-item-view.coffee
+++ b/server/sonar-web/src/main/coffee/coding-rules/views/coding-rules-list-item-view.coffee
@@ -24,5 +24,8 @@ define [
serializeData: ->
+ tags = _.union @model.get('sysTags'), @model.get('tags')
_.extend super,
- allTags: _.union @model.get('sysTags'), @model.get('tags')
+ manualRuleLabel: t 'coding_rules.manual_rule'
+ allTags: tags
+ showDetails: (@model.get('status') != 'READY') || (_.isArray(tags) && tags.length > 0)
diff --git a/server/sonar-web/src/main/coffee/coding-rules/views/filters/tag-filter-view.coffee b/server/sonar-web/src/main/coffee/coding-rules/views/filters/tag-filter-view.coffee
index a6b33b8a05e..01366e7f2e4 100644
--- a/server/sonar-web/src/main/coffee/coding-rules/views/filters/tag-filter-view.coffee
+++ b/server/sonar-web/src/main/coffee/coding-rules/views/filters/tag-filter-view.coffee
@@ -15,20 +15,21 @@ define [
loadTags: ->
tagsXHR = jQuery.ajax
url: "#{baseUrl}/api/rules/tags"
+ async: false
- that = @
- jQuery.when(tagsXHR).done (r) ->
- that.choices = new Backbone.Collection(
+ jQuery.when(tagsXHR).done (r) =>
+ @choices = new Backbone.Collection(
_.map(r.tags, (tag) ->
new Backbone.Model
id: tag
text: tag
- ))
+ ),
+ comparator: 'text')
- if that.tagToRestore
- that.restore(that.tagToRestore)
- that.tagToRestore = null
- that.render()
+ if @tagToRestore
+ @restore(@tagToRestore)
+ @tagToRestore = null
+ @render()
restore: (value) ->
unless @choices.isEmpty()
diff --git a/server/sonar-web/src/main/coffee/component-viewer/duplication-popup.coffee b/server/sonar-web/src/main/coffee/component-viewer/duplication-popup.coffee
index c9e6545fd24..09aa6aa7e0b 100644
--- a/server/sonar-web/src/main/coffee/component-viewer/duplication-popup.coffee
+++ b/server/sonar-web/src/main/coffee/component-viewer/duplication-popup.coffee
@@ -36,7 +36,7 @@ define [
key = $(e.currentTarget).data 'key'
line = $(e.currentTarget).data 'line'
files = @options.main.source.get('duplicationFiles')
- @options.main.addTransition 'duplication', @collection.map (item) ->
+ options = @collection.map (item) ->
file = files[item.get('_ref')]
x = utils.splitLongName file.name
key: file.key
@@ -46,6 +46,8 @@ define [
projectName: file.projectName
subProjectName: file.subProjectName
active: file.key == key
+ options = _.uniq options, (item) -> item.key
+ @options.main.addTransition 'duplication', options
if key == @options.main.component.get 'key'
@options.main.scrollToLine line
@options.main.workspaceView.render()
diff --git a/server/sonar-web/src/main/coffee/component-viewer/header/tests-header.coffee b/server/sonar-web/src/main/coffee/component-viewer/header/tests-header.coffee
index 76e196c66c7..667796b9bb6 100644
--- a/server/sonar-web/src/main/coffee/component-viewer/header/tests-header.coffee
+++ b/server/sonar-web/src/main/coffee/component-viewer/header/tests-header.coffee
@@ -36,7 +36,12 @@ define [
onRender: ->
@header.enableUnitTest = (testName) =>
- @ui.unitTests.filter("[data-name=#{testName}]").click()
+ test = @ui.unitTests.filter("[data-name=#{testName}]")
+ container = test.closest '.component-viewer-header-expanded-bar-section-list'
+ topOffset = test.offset().top - container.offset().top
+ if topOffset > container.height()
+ container.scrollTop topOffset
+ test.click()
@$(@activeSort).addClass 'active-link' if @activeSort
diff --git a/server/sonar-web/src/main/coffee/component-viewer/main.coffee b/server/sonar-web/src/main/coffee/component-viewer/main.coffee
index 6dd645d853b..9f65306e531 100644
--- a/server/sonar-web/src/main/coffee/component-viewer/main.coffee
+++ b/server/sonar-web/src/main/coffee/component-viewer/main.coffee
@@ -135,7 +135,7 @@ define [
availableHeight = height - @$(@headerRegion.$el).outerHeight(true)
source.removeClass 'overflow'
- source.width(availableWidth).css('max-height', availableHeight)
+ source.width(availableWidth).height availableHeight
source.addClass 'overflow'
workspace.removeClass 'overflow'
workspace.height availableHeight
diff --git a/server/sonar-web/src/main/coffee/component-viewer/source.coffee b/server/sonar-web/src/main/coffee/component-viewer/source.coffee
index f0c17c7a127..bad93f7bc93 100644
--- a/server/sonar-web/src/main/coffee/component-viewer/source.coffee
+++ b/server/sonar-web/src/main/coffee/component-viewer/source.coffee
@@ -158,6 +158,7 @@ define [
row = $(e.currentTarget).closest('.row')
highlighted = row.is ".#{HIGHLIGHTED_ROW_CLASS}"
@$(".#{HIGHLIGHTED_ROW_CLASS}").removeClass HIGHLIGHTED_ROW_CLASS
+ @highlightedLine = null
unless highlighted
row.addClass HIGHLIGHTED_ROW_CLASS
@highlightedLine = row.data 'line-number'
@@ -170,9 +171,10 @@ define [
highlightUsages: (e) ->
+ highlighted = $(e.currentTarget).is '.highlighted'
key = e.currentTarget.className.split(/\s+/)[0]
@$('.sym.highlighted').removeClass 'highlighted'
- @$(".sym.#{key}").addClass 'highlighted'
+ @$(".sym.#{key}").addClass 'highlighted' unless highlighted
toggleSettings: ->
diff --git a/server/sonar-web/src/main/coffee/drilldown/app.coffee b/server/sonar-web/src/main/coffee/drilldown/app.coffee
index c1179e3e05d..860b9b3d985 100644
--- a/server/sonar-web/src/main/coffee/drilldown/app.coffee
+++ b/server/sonar-web/src/main/coffee/drilldown/app.coffee
@@ -84,7 +84,9 @@ requirejs [
viewer.render()
else viewer.showAllLines()
- viewer.open(key).done ->
+ viewer.open key
+ viewer.on 'loaded', ->
+ viewer.off 'loaded'
if activeHeaderTab?
viewer.headerView.enableBar(activeHeaderTab).done -> f()
else f()
diff --git a/server/sonar-web/src/main/coffee/widgets/base.coffee b/server/sonar-web/src/main/coffee/widgets/base.coffee
index 49103c0e7ca..53b66bcba4d 100644
--- a/server/sonar-web/src/main/coffee/widgets/base.coffee
+++ b/server/sonar-web/src/main/coffee/widgets/base.coffee
@@ -1,3 +1,6 @@
+moment.lang window.pageLang
+
+
window.SonarWidgets ?= {}
class BaseWidget
diff --git a/server/sonar-web/src/main/coffee/widgets/treemap.coffee b/server/sonar-web/src/main/coffee/widgets/treemap.coffee
index f205fb1c486..72cff1254f3 100644
--- a/server/sonar-web/src/main/coffee/widgets/treemap.coffee
+++ b/server/sonar-web/src/main/coffee/widgets/treemap.coffee
@@ -101,7 +101,6 @@ class Treemap extends window.SonarWidgets.BaseWidget
breadcrumbsEnter.append('i').attr 'class', (d) ->
if d.qualifier? then "icon-qualifier-#{d.qualifier.toLowerCase()}" else ''
breadcrumbsEnterLinks = breadcrumbsEnter.append 'a'
- breadcrumbsEnterLinks.classed 'underlined-link', (d, i) -> i > 0
breadcrumbsEnterLinks.html (d) -> d.name
breadcrumbsEnterLinks.on 'click', (d) =>
@updateTreemap d.components, d.maxResultsReached
diff --git a/server/sonar-web/src/main/hbs/coding-rules/coding-rules-facets.hbs b/server/sonar-web/src/main/hbs/coding-rules/coding-rules-facets.hbs
index 2bb6a7593d4..d2ea9d4fd56 100644
--- a/server/sonar-web/src/main/hbs/coding-rules/coding-rules-facets.hbs
+++ b/server/sonar-web/src/main/hbs/coding-rules/coding-rules-facets.hbs
@@ -4,7 +4,13 @@
{{/unless}}
{{#each items}}
<div class="navigator-facets-list-item" data-property="{{property}}">
- <div class="navigator-facets-list-item-name">{{t property_message}}</div>
+ <div class="navigator-facets-list-item-name">
+ {{#if limitReached}}
+ {{tp 'coding_rules.facets.top' property_message}}
+ {{else}}
+ {{property_message}}
+ {{/if}}
+ </div>
<div class="navigator-facets-list-item-options">
{{#each values}}
{{#if count}}
diff --git a/server/sonar-web/src/main/hbs/coding-rules/coding-rules-list-item.hbs b/server/sonar-web/src/main/hbs/coding-rules/coding-rules-list-item.hbs
index ea2a2057d7e..afa53e8d33d 100644
--- a/server/sonar-web/src/main/hbs/coding-rules/coding-rules-list-item.hbs
+++ b/server/sonar-web/src/main/hbs/coding-rules/coding-rules-list-item.hbs
@@ -1,13 +1,18 @@
<div class="line line-small">
- <span class="coding-rules-detail-status">{{language}}</span>
+ <span class="coding-rules-detail-status">{{default language manualRuleLabel}}</span>
- <div class="line-right">
- <span class="coding-rules-list-tags">
- <i class="icon-tags"></i>
- <span>{{#if allTags}}{{join allTags ', '}}{{else}}{{t 'coding_rules.no_tags'}}{{/if}}</span>
- </span>
+ {{#if showDetails}}
+ <div class="line-right">
+ {{#if allTags}}
+ <span class="coding-rules-list-tags">
+ <i class="icon-tags"></i>
+ <span>{{join allTags ', '}}</span>
+ </span>
+ &nbsp;&nbsp;
+ {{/if}}
- {{#notEq status 'READY'}}| <span class="coding-rules-detail-not-ready">{{status}}</span>{{/notEq}}
- </div>
+ {{#notEq status 'READY'}}<span class="coding-rules-detail-not-ready">{{status}}</span>{{/notEq}}
+ </div>
+ {{/if}}
</div>
<div class="line" title="{{name}}" name="{{key}}">{{name}}</div>
diff --git a/server/sonar-web/src/main/hbs/coding-rules/coding-rules-profile-filter-detail.hbs b/server/sonar-web/src/main/hbs/coding-rules/coding-rules-profile-filter-detail.hbs
index ee479733dc0..3a3b44cff8b 100644
--- a/server/sonar-web/src/main/hbs/coding-rules/coding-rules-profile-filter-detail.hbs
+++ b/server/sonar-web/src/main/hbs/coding-rules/coding-rules-profile-filter-detail.hbs
@@ -1,10 +1,16 @@
<li>
<label title="{{id}}" data-id="{{id}}">
- <i class="icon-checkbox {{#if checked}}icon-checkbox-checked{{/if}} {{#unless multiple}}icon-checkbox-single{{/unless}}"></i>
<span>
+ <i class="icon-checkbox {{#if checked}}icon-checkbox-checked{{/if}} {{#unless multiple}}icon-checkbox-single{{/unless}}"></i>
{{text}}
- <br>
- <span class="subtitle">{{language}}</span>
</span>
+
+ {{#if language}}
+ <br>
+ <span>
+ <i class="icon-checkbox icon-checkbox-invisible"></i>
+ <span class="subtitle">{{language}}</span>
+ </span>
+ {{/if}}
</label>
</li>
diff --git a/server/sonar-web/src/main/hbs/coding-rules/coding-rules-repository-detail.hbs b/server/sonar-web/src/main/hbs/coding-rules/coding-rules-repository-detail.hbs
index 081660e3b5c..3a3b44cff8b 100644
--- a/server/sonar-web/src/main/hbs/coding-rules/coding-rules-repository-detail.hbs
+++ b/server/sonar-web/src/main/hbs/coding-rules/coding-rules-repository-detail.hbs
@@ -1,12 +1,16 @@
<li>
<label title="{{id}}" data-id="{{id}}">
- <i class="icon-checkbox {{#if checked}}icon-checkbox-checked{{/if}} {{#unless multiple}}icon-checkbox-single{{/unless}}"></i>
<span>
+ <i class="icon-checkbox {{#if checked}}icon-checkbox-checked{{/if}} {{#unless multiple}}icon-checkbox-single{{/unless}}"></i>
{{text}}
- {{#if language}}
- <br>
- <span class="subtitle">{{language}}</span>
- {{/if}}
</span>
+
+ {{#if language}}
+ <br>
+ <span>
+ <i class="icon-checkbox icon-checkbox-invisible"></i>
+ <span class="subtitle">{{language}}</span>
+ </span>
+ {{/if}}
</label>
</li>
diff --git a/server/sonar-web/src/main/hbs/common/_markdown-tips.hbs b/server/sonar-web/src/main/hbs/common/_markdown-tips.hbs
index fe5edc9fc35..51ebaa00ae5 100644
--- a/server/sonar-web/src/main/hbs/common/_markdown-tips.hbs
+++ b/server/sonar-web/src/main/hbs/common/_markdown-tips.hbs
@@ -1,4 +1,4 @@
<div class="markdown-tips">
- <a href="#" class="underlined-link" onclick="window.open(baseUrl + '/markdown/help','markdown','height=300,width=600,scrollbars=1,resizable=1');return false;">{{t 'markdown.helplink'}}</a> :
+ <a href="#" onclick="window.open(baseUrl + '/markdown/help','markdown','height=300,width=600,scrollbars=1,resizable=1');return false;">{{t 'markdown.helplink'}}</a> :
&nbsp; *{{t 'bold'}}* &nbsp;&nbsp; ``{{t 'code'}}`` &nbsp;&nbsp; * {{t 'bulleted_point'}}
</div>
diff --git a/server/sonar-web/src/main/hbs/component-viewer/cw-duplication-popup.hbs b/server/sonar-web/src/main/hbs/component-viewer/cw-duplication-popup.hbs
index 8ae13b172a6..bf134d7fd62 100644
--- a/server/sonar-web/src/main/hbs/component-viewer/cw-duplication-popup.hbs
+++ b/server/sonar-web/src/main/hbs/component-viewer/cw-duplication-popup.hbs
@@ -2,19 +2,11 @@
<div class="bubble-popup-title">{{t 'component_viewer.transition.duplication'}}</div>
{{#each duplications}}
<div class="bubble-popup-section">
- {{#notEq file.project ../component.project}}
- <div class="component-viewer-popup-label">
- <i class="icon-cross-project"></i>
- {{file.projectName}}
- {{#if file.subProjectName}}
- / {{file.subProjectName}}
- {{/if}}
+ {{#notEqComponents file ../component}}
+ <div class="component-viewer-popup-label" title="{{projectFullName file}}">
+ <i class="icon-qualifier-trk"></i> {{projectFullName file}}
</div>
- {{else}}
- {{#notEq file.subProject ../../component.subProject}}
- <div class="component-viewer-popup-label">{{file.projectName}} / {{file.subProjectName}}</div>
- {{/notEq}}
- {{/notEq}}
+ {{/notEqComponents}}
{{#notEq file.key ../component.key}}
<a class="link-action" data-key="{{file.key}}" title="{{file.name}}">
diff --git a/server/sonar-web/src/main/hbs/component-viewer/cw-more-actions.hbs b/server/sonar-web/src/main/hbs/component-viewer/cw-more-actions.hbs
index 354436d5cdb..4fe8ed7400c 100644
--- a/server/sonar-web/src/main/hbs/component-viewer/cw-more-actions.hbs
+++ b/server/sonar-web/src/main/hbs/component-viewer/cw-more-actions.hbs
@@ -1,10 +1,10 @@
-<a class="js-new-window underlined-link">{{t 'component_viewer.new_window'}}</a>
+<a class="js-new-window">{{t 'component_viewer.new_window'}}</a>
<br>
-<a class="js-full-source underlined-link">{{t 'component_viewer.show_full_source'}}</a>
+<a class="js-full-source">{{t 'component_viewer.show_full_source'}}</a>
<br>
-<a class="js-raw-source underlined-link">{{t 'component_viewer.show_raw_source'}}</a>
+<a class="js-raw-source">{{t 'component_viewer.show_raw_source'}}</a>
{{#each state.extensions}}
<br>
- <a class="js-extension underlined-link" data-key="{{this.[0]}}">{{this.[1]}}</a>
-{{/each}} \ No newline at end of file
+ <a class="js-extension" data-key="{{this.[0]}}">{{this.[1]}}</a>
+{{/each}}
diff --git a/server/sonar-web/src/main/hbs/component-viewer/header/cw-basic-header.hbs b/server/sonar-web/src/main/hbs/component-viewer/header/cw-basic-header.hbs
index 32cd2c1730f..b108d744d00 100644
--- a/server/sonar-web/src/main/hbs/component-viewer/header/cw-basic-header.hbs
+++ b/server/sonar-web/src/main/hbs/component-viewer/header/cw-basic-header.hbs
@@ -5,7 +5,7 @@
</div>
<ul class="component-viewer-header-expanded-bar-section-list">
{{{componentViewerHeaderLink lines 'lines' 'js-filter-lines'}}}
- {{#if 'ncloc_data'}}
+ {{#if ncloc_data}}
{{{componentViewerHeaderLink ncloc 'ncloc' 'js-filter-ncloc'}}}
{{else}}
{{{componentViewerHeaderItem ncloc 'ncloc'}}}
diff --git a/server/sonar-web/src/main/hbs/component-viewer/header/cw-coverage-header.hbs b/server/sonar-web/src/main/hbs/component-viewer/header/cw-coverage-header.hbs
index 8b656730ef7..9b0966ce4c3 100644
--- a/server/sonar-web/src/main/hbs/component-viewer/header/cw-coverage-header.hbs
+++ b/server/sonar-web/src/main/hbs/component-viewer/header/cw-coverage-header.hbs
@@ -1,6 +1,6 @@
{{#if state.hasSource}}
<div class="component-viewer-header-time-changes">
- <a class="highlighted-link js-coverage-time-changes">
+ <a class="js-coverage-time-changes">
{{#if period}}Δ {{period.label}}{{else}}<i class="icon-period"></i> {{t 'component_viewer.time_changes'}}{{/if}}
</a>
</div>
diff --git a/server/sonar-web/src/main/hbs/component-viewer/header/cw-scm-header.hbs b/server/sonar-web/src/main/hbs/component-viewer/header/cw-scm-header.hbs
index 599f9f71e33..2cf3da9e995 100644
--- a/server/sonar-web/src/main/hbs/component-viewer/header/cw-scm-header.hbs
+++ b/server/sonar-web/src/main/hbs/component-viewer/header/cw-scm-header.hbs
@@ -1,5 +1,5 @@
<div class="component-viewer-header-time-changes">
- <a class="highlighted-link js-scm-time-changes">
+ <a class="js-scm-time-changes">
{{#if period}}Δ {{period.label}}{{else}}<i class="icon-period"></i> {{t 'component_viewer.time_changes'}}{{/if}}
</a>
</div>
diff --git a/server/sonar-web/src/main/hbs/component-viewer/header/cw-tests-header.hbs b/server/sonar-web/src/main/hbs/component-viewer/header/cw-tests-header.hbs
index 19f904c1b3b..c11d69bc279 100644
--- a/server/sonar-web/src/main/hbs/component-viewer/header/cw-tests-header.hbs
+++ b/server/sonar-web/src/main/hbs/component-viewer/header/cw-tests-header.hbs
@@ -47,9 +47,9 @@
<span class="ib">
{{t 'component_viewer.measure_section.test_cases'}}
{{t 'component_viewer.tests.ordered_by'}}
- <a class="js-sort-tests-name underlined-link">{{t 'component_viewer.tests.test_name'}}</a>
+ <a class="js-sort-tests-name">{{t 'component_viewer.tests.test_name'}}</a>
/
- <a class="js-sort-tests-duration underlined-link">{{t 'component_viewer.tests.duration'}}</a>
+ <a class="js-sort-tests-duration">{{t 'component_viewer.tests.duration'}}</a>
</span>
{{#if hasCoveragePerTestData}}
<span class="ib">{{t 'component_viewer.covered_lines'}}</span>
diff --git a/server/sonar-web/src/main/hbs/issue/issue.hbs b/server/sonar-web/src/main/hbs/issue/issue.hbs
index ac3a80fd69e..de7379aa2c5 100644
--- a/server/sonar-web/src/main/hbs/issue/issue.hbs
+++ b/server/sonar-web/src/main/hbs/issue/issue.hbs
@@ -105,10 +105,10 @@
<div class="code-issue-details">
<ul class="code-issue-tabs">
<li>
- <a class="js-tab-link underlined-link" href="#tab-issue-rule">{{t 'rule'}}</a>
+ <a class="js-tab-link" href="#tab-issue-rule">{{t 'rule'}}</a>
</li>
<li>
- <a class="js-tab-link underlined-link" href="#tab-issue-changelog">{{t 'changelog'}}</a>
+ <a class="js-tab-link" href="#tab-issue-changelog">{{t 'changelog'}}</a>
</li>
</ul>
diff --git a/server/sonar-web/src/main/hbs/navigator/choice-filter-item.hbs b/server/sonar-web/src/main/hbs/navigator/choice-filter-item.hbs
index c8b4b596a90..d5fed7a743f 100644
--- a/server/sonar-web/src/main/hbs/navigator/choice-filter-item.hbs
+++ b/server/sonar-web/src/main/hbs/navigator/choice-filter-item.hbs
@@ -1,13 +1,16 @@
<li>
<label title="{{text}}" data-id="{{id}}">
- <i class="icon-checkbox {{#if checked}}icon-checkbox-checked{{/if}} {{#unless multiple}}icon-checkbox-single{{/unless}}"></i>
- {{#if icon}}<i class="icon-{{icon}}"></i>{{/if}}
<span>
- {{text}}
- {{#if category}}
- <br>
- <span class="subtitle">{{category}}</span>
- {{/if}}
+ <i class="icon-checkbox {{#if checked}}icon-checkbox-checked{{/if}} {{#unless multiple}}icon-checkbox-single{{/unless}}"></i>
+ {{#if icon}}<i class="icon-{{icon}}"></i>{{/if}}
+ {{text}}
+ </span>
+ {{#if category}}
+ <br>
+ <span>
+ <i class="icon-checkbox icon-checkbox-invisible"></i>
+ <span class="subtitle">{{category}}</span>
</span>
+ {{/if}}
</label>
</li>
diff --git a/server/sonar-web/src/main/hbs/navigator/more-criteria-details-filter.hbs b/server/sonar-web/src/main/hbs/navigator/more-criteria-details-filter.hbs
index 252cacc2476..c56b0e7023d 100644
--- a/server/sonar-web/src/main/hbs/navigator/more-criteria-details-filter.hbs
+++ b/server/sonar-web/src/main/hbs/navigator/more-criteria-details-filter.hbs
@@ -1,7 +1,7 @@
<ul class="navigator-filter-select-list">
{{#each filters}}
<li>
- <label data-id="{{id}}" {{#if inactive}}class="inactive"{{/if}} {{#if title}}title="{{title}}"{{/if}}>
+ <label data-id="{{id}}" data-property="{{property}}" {{#if inactive}}class="inactive"{{/if}} {{#if title}}title="{{title}}"{{/if}}>
{{name}}
</label>
</li>
diff --git a/server/sonar-web/src/main/js/application.js b/server/sonar-web/src/main/js/application.js
index 730edbfc0e0..c17aeadeaea 100644
--- a/server/sonar-web/src/main/js/application.js
+++ b/server/sonar-web/src/main/js/application.js
@@ -58,7 +58,7 @@ function resourceViewerOnBulkIssues() {
}
} else {
// No tab selected, see how to add tab parameter
- if (window.location.search.startsWith('?')) {
+ if (window.location.search.indexOf('?') === 0) {
window.location.search += ('&' + issuesTab);
} else {
window.location.search += ('?' + issuesTab);
diff --git a/server/sonar-web/src/main/js/common/handlebars-extensions.js b/server/sonar-web/src/main/js/common/handlebars-extensions.js
index cf16e5968ca..7846b606990 100644
--- a/server/sonar-web/src/main/js/common/handlebars-extensions.js
+++ b/server/sonar-web/src/main/js/common/handlebars-extensions.js
@@ -1,20 +1,16 @@
requirejs.config({
paths: {
- 'moment': 'third-party/moment',
'handlebars': 'third-party/handlebars'
},
shim: {
- 'moment': {
- exports: 'moment'
- },
'handlebars': {
exports: 'Handlebars'
}
}
});
-define(['handlebars', 'moment'], function (Handlebars, moment) {
+define(['handlebars'], function (Handlebars) {
/*
* Shortcut for templates retrieving
@@ -341,4 +337,19 @@ define(['handlebars', 'moment'], function (Handlebars, moment) {
}
});
+ Handlebars.registerHelper('eqComponents', function (a, b, options) {
+ var notEq = a && b && ((a.project !== b.project) || (a.subProject !== b.subProject));
+ return notEq ? options.inverse(this) : options.fn(this);
+ });
+
+ Handlebars.registerHelper('notEqComponents', function (a, b, options) {
+ var notEq = a && b && ((a.project !== b.project) || (a.subProject !== b.subProject));
+ return notEq ? options.fn(this) : options.inverse(this);
+ });
+
+ Handlebars.registerHelper('projectFullName', function (component) {
+ var name = component.projectName + (component.subProjectName ? (' / ' + component.subProjectName) : '');
+ return name;
+ });
+
});
diff --git a/server/sonar-web/src/main/js/issues/app.js b/server/sonar-web/src/main/js/issues/app.js
index 31715516a2d..e299af9b820 100644
--- a/server/sonar-web/src/main/js/issues/app.js
+++ b/server/sonar-web/src/main/js/issues/app.js
@@ -4,8 +4,7 @@ requirejs.config({
paths: {
'backbone': 'third-party/backbone',
'backbone.marionette': 'third-party/backbone.marionette',
- 'handlebars': 'third-party/handlebars',
- 'moment': 'third-party/moment'
+ 'handlebars': 'third-party/handlebars'
},
shim: {
@@ -18,9 +17,6 @@ requirejs.config({
},
'handlebars': {
exports: 'Handlebars'
- },
- 'moment': {
- exports: 'moment'
}
}
@@ -28,7 +24,7 @@ requirejs.config({
requirejs(
[
- 'backbone', 'backbone.marionette', 'handlebars', 'moment',
+ 'backbone', 'backbone.marionette', 'handlebars',
'issues/extra',
'navigator/filters/filter-bar',
'navigator/filters/base-filters',
@@ -44,7 +40,7 @@ requirejs(
'common/handlebars-extensions'
],
- function (Backbone, Marionette, Handlebars, moment, Extra, FilterBar, BaseFilters, CheckboxFilterView,
+ function (Backbone, Marionette, Handlebars, Extra, FilterBar, BaseFilters, CheckboxFilterView,
ChoiceFilters, AjaxSelectFilters, FavoriteFilters, RangeFilters, ContextFilterView,
ReadOnlyFilterView, ActionPlanFilterView, RuleFilterView) {
Handlebars.registerPartial('detailInnerTemplate', jQuery('#issue-detail-inner-template').html());
@@ -111,6 +107,15 @@ requirejs(
});
this.filters.add(projectFilter);
+ var assigneeChoices = {
+ '!assigned': window.SS.phrases.unassigned
+ },
+ reporterChoices = {};
+ if (window.SS.currentUser) {
+ assigneeChoices[window.SS.currentUser] = window.SS.currentUserName + ' (' + window.SS.currentUser + ')';
+ reporterChoices[window.SS.currentUser] = window.SS.currentUserName + ' (' + window.SS.currentUser + ')';
+ }
+
this.filters.add([
new BaseFilters.Filter({
name: window.SS.phrases.severity,
@@ -162,9 +167,7 @@ requirejs(
type: AjaxSelectFilters.AssigneeFilterView,
enabled: true,
optional: false,
- choices: {
- '!assigned': window.SS.phrases.unassigned
- }
+ choices: assigneeChoices
}),
new BaseFilters.Filter({
@@ -226,7 +229,8 @@ requirejs(
property: 'reporters',
type: AjaxSelectFilters.ReporterFilterView,
enabled: false,
- optional: true
+ optional: true,
+ choices: reporterChoices
}),
new BaseFilters.Filter({
diff --git a/server/sonar-web/src/main/js/navigator/filters/ajax-select-filters.js b/server/sonar-web/src/main/js/navigator/filters/ajax-select-filters.js
index d32cf80bdf6..4037f7172b9 100644
--- a/server/sonar-web/src/main/js/navigator/filters/ajax-select-filters.js
+++ b/server/sonar-web/src/main/js/navigator/filters/ajax-select-filters.js
@@ -141,7 +141,9 @@ define([
that.options.filterView.choices.unshift(item);
});
_.each(that.model.get('choices'), function(v, k) {
- that.options.filterView.choices.add(new Backbone.Model({ id: k, text: v }));
+ if (k[0] === '!') {
+ that.options.filterView.choices.add(new Backbone.Model({ id: k, text: v }));
+ }
});
that.updateLists();
that.$el.removeClass('fetching');
diff --git a/server/sonar-web/src/main/js/navigator/filters/base-filters.js b/server/sonar-web/src/main/js/navigator/filters/base-filters.js
index abd81862283..b73a08e0928 100644
--- a/server/sonar-web/src/main/js/navigator/filters/base-filters.js
+++ b/server/sonar-web/src/main/js/navigator/filters/base-filters.js
@@ -104,6 +104,7 @@ define([
var title = this.model.get('name') + ': ' + this.renderValue();
this.$el.prop('title', title);
+ this.$el.attr('data-property', this.model.get('property'));
},
diff --git a/server/sonar-web/src/main/js/navigator/filters/choice-filters.js b/server/sonar-web/src/main/js/navigator/filters/choice-filters.js
index b4553847159..c36e42ad2a7 100644
--- a/server/sonar-web/src/main/js/navigator/filters/choice-filters.js
+++ b/server/sonar-web/src/main/js/navigator/filters/choice-filters.js
@@ -57,7 +57,7 @@ define([
onCheck: function(e) {
var checkbox = jQuery(e.currentTarget),
id = checkbox.data('id'),
- checked = checkbox.children('.icon-checkbox-checked').length > 0;
+ checked = checkbox.find('.icon-checkbox-checked').length > 0;
if (this.model.get('multiple')) {
if (checkbox.closest('.opposite').length > 0) {
@@ -315,11 +315,19 @@ define([
item.set('checked', false);
});
+ var unknownValues = [];
+
_.each(value, function(v) {
var cModel = that.choices.findWhere({ id: v });
- cModel.set('checked', true);
+ if (cModel) {
+ cModel.set('checked', true);
+ } else {
+ unknownValues.push(v);
+ }
});
+ value = _.difference(value, unknownValues);
+
this.model.set({
value: value,
enabled: true
diff --git a/server/sonar-web/src/main/js/navigator/filters/metric-filters.js b/server/sonar-web/src/main/js/navigator/filters/metric-filters.js
index 0bb84819e0b..3a81da9a0c0 100644
--- a/server/sonar-web/src/main/js/navigator/filters/metric-filters.js
+++ b/server/sonar-web/src/main/js/navigator/filters/metric-filters.js
@@ -14,16 +14,37 @@ define([
inputChanged: function() {
- var value = {
- metric: this.$('[name=metric]').val(),
+ var metric = this.$('[name=metric]').val(),
+ isDifferentialMetric = metric.indexOf('new_') === 0,
+ periodSelect = this.$('[name=period]'),
+ period = periodSelect.val(),
+ optionZero = periodSelect.children('[value="0"]'),
+ value = {
+ metric: metric,
metricText: this.$('[name=metric] option:selected').text(),
- period: this.$('[name=period]').val(),
+ period: period,
periodText: this.$('[name=period] option:selected').text(),
op: this.$('[name=op]').val(),
opText: this.$('[name=op] option:selected').text(),
val: this.$('[name=val]').val(),
valText: this.$('[name=val]').originalVal()
};
+
+ if (isDifferentialMetric) {
+ optionZero.remove();
+ if (period === '0') {
+ period = '1';
+ }
+ } else {
+ if (optionZero.length === 0) {
+ periodSelect.prepend(this.periodZeroOption);
+ }
+ }
+ periodSelect.select2('destroy').val(period).select2({
+ width: '100%',
+ minimumResultsForSearch: 100
+ });
+
this.updateDataType(value);
this.model.set('value', value);
},
@@ -46,6 +67,9 @@ define([
onRender: function() {
+ var periodZeroLabel = this.$('[name=period]').children('[value="0"]').html();
+ this.periodZeroOption = '<option value="0">' + periodZeroLabel + '</option>';
+
var value = this.model.get('value') || {};
this.$('[name=metric]').val(value.metric).select2({
width: '100%',
@@ -68,7 +92,11 @@ define([
onShow: function() {
var select = this.$('[name=metric]');
- select.select2('open');
+ if (this.model.get('value')['metric'] === '') {
+ select.select2('open');
+ } else {
+ select.select2('focus');
+ }
}
});
diff --git a/server/sonar-web/src/main/js/navigator/filters/range-filters.js b/server/sonar-web/src/main/js/navigator/filters/range-filters.js
index 8457ee356d6..1627895022e 100644
--- a/server/sonar-web/src/main/js/navigator/filters/range-filters.js
+++ b/server/sonar-web/src/main/js/navigator/filters/range-filters.js
@@ -171,12 +171,14 @@ define([
render: function() {
RangeFilterView.prototype.render.apply(this, arguments);
this.detailsView.$('input')
- .prop('readonly', true)
.prop('placeholder', '1970-01-31')
.datepicker({
dateFormat: 'yy-mm-dd',
changeMonth: true,
changeYear: true
+ })
+ .on('change', function () {
+ jQuery(this).datepicker('setDate', jQuery(this).val());
});
},
diff --git a/server/sonar-web/src/main/js/tests/e2e/tests/coding-rules-spec.js b/server/sonar-web/src/main/js/tests/e2e/tests/coding-rules-spec.js
index 2553a3a3033..750da1da56a 100644
--- a/server/sonar-web/src/main/js/tests/e2e/tests/coding-rules-spec.js
+++ b/server/sonar-web/src/main/js/tests/e2e/tests/coding-rules-spec.js
@@ -32,8 +32,17 @@ casper.test.begin(testName('Readonly Tests'), function suite(test) {
casper.waitForSelector('.navigator-filters', function checkDefaultFilters() {
test.assertVisible('input[type="text"].query-filter-input');
test.assertElementCount('.navigator-filter', 15);
- test.assertElementCount('.navigator-filter-optional', 12 /* Only query, qProfile and 'More' are visible by default */);
+ test.assertElementCount('.navigator-filter-optional', 12 /* Only query, qProfile and 'More' are visible by default */);
test.assertVisible('button.navigator-filter-submit');
+
+
+ casper.click('.navigator-filter-more-criteria');
+ casper.waitUntilVisible('.navigator-filter-details.active', function checkTagsAreOrdered() {
+ casper.click('.navigator-filter-details.active label[data-property="tags"]');
+ test.assertSelectorHasText('.navigator-filter[data-property="tags"] option:nth-child(1)', 'brain-overload');
+ test.assertSelectorHasText('.navigator-filter[data-property="tags"] option:nth-child(11)', 'unused');
+ casper.click('.navigator-filter.active>.navigator-filter-disable');
+ });
});
@@ -260,3 +269,28 @@ casper.test.begin(testName('Activation Tests'), function suite(test) {
test.done();
});
});
+
+
+casper.test.begin(testName('Tag Navigation Test'), function suite(test) {
+
+ casper.start(lib.buildUrl('coding-rules#tags=polop,bug,pilip,unused,palap'), function() {
+ lib.clearRequestMocks();
+ lib.mockRequest('/api/l10n/index', '{}');
+ lib.mockRequestFromFile('/api/rules/app', 'app_admin.json');
+ lib.mockRequestFromFile('/api/rules/tags', 'tags.json');
+ lib.mockRequestFromFile('/api/rules/search', 'search_x1.json');
+ lib.mockRequestFromFile('/api/rules/show', 'show_x1.json');
+ });
+
+
+ casper.waitWhileSelector("div#coding-rules-page-loader", function checkTagFilterRestored() {
+ casper.waitForSelector('.navigator-filters', function checkDefaultFilters() {
+ test.assertElementCount('.navigator-filter-disabled', 11 /* Tag is enabled */);
+ test.assertSelectorHasText('.navigator-filter[data-property="tags"] .navigator-filter-value', 'bug, unused');
+ });
+ });
+
+ casper.run(function() {
+ test.done();
+ });
+});
diff --git a/server/sonar-web/src/main/js/tests/e2e/tests/component-viewer-lines-filters-spec.js b/server/sonar-web/src/main/js/tests/e2e/tests/component-viewer-lines-filters-spec.js
index d2470ccc8a9..2960f784d25 100644
--- a/server/sonar-web/src/main/js/tests/e2e/tests/component-viewer-lines-filters-spec.js
+++ b/server/sonar-web/src/main/js/tests/e2e/tests/component-viewer-lines-filters-spec.js
@@ -38,3 +38,32 @@ casper.test.begin(testName('Lines Filters'), function (test) {
test.done();
});
});
+
+
+casper.test.begin(testName('Do Not Show Ncloc Filter If No Data'), function (test) {
+ casper
+ .start(lib.buildUrl('component-viewer#component=component'), function () {
+ lib.setDefaultViewport();
+ lib.mockRequest('/api/l10n/index', '{}');
+ lib.mockRequestFromFile('/api/components/app', 'app.json');
+ lib.mockRequestFromFile('/api/sources/show', 'source.json');
+ lib.mockRequestFromFile('/api/resources', 'resources-without-ncloc-data.json');
+ })
+
+ .then(function () {
+ casper.waitForSelector('.component-viewer-source .row');
+ })
+
+ .then(function () {
+ casper.click('.js-header-tab-basic');
+ casper.waitForSelector('[data-metric="ncloc"]');
+ })
+
+ .then(function () {
+ test.assertDoesntExist('.js-filter-ncloc');
+ })
+
+ .run(function () {
+ test.done();
+ });
+});
diff --git a/server/sonar-web/src/main/js/tests/e2e/tests/component-viewer-spec/resources-without-ncloc-data.json b/server/sonar-web/src/main/js/tests/e2e/tests/component-viewer-spec/resources-without-ncloc-data.json
new file mode 100644
index 00000000000..dfbacdf7b28
--- /dev/null
+++ b/server/sonar-web/src/main/js/tests/e2e/tests/component-viewer-spec/resources-without-ncloc-data.json
@@ -0,0 +1,150 @@
+[
+ {
+ "id": 19983,
+ "key": "org.codehaus.sonar:sonar-batch:src/main/java/org/sonar/batch/index/Cache.java",
+ "name": "Cache.java",
+ "scope": "FIL",
+ "qualifier": "FIL",
+ "date": "2014-07-21T23:18:51+0200",
+ "creationDate": "2013-04-17T04:06:45+0200",
+ "lname": "src/main/java/org/sonar/batch/index/Cache.java",
+ "lang": "java",
+ "msr": [
+ {
+ "key": "lines",
+ "val": 519.0,
+ "frmt_val": "519"
+ },
+ {
+ "key": "ncloc",
+ "val": 379.0,
+ "frmt_val": "379"
+ },
+ {
+ "key": "classes",
+ "val": 6.0,
+ "frmt_val": "6"
+ },
+ {
+ "key": "functions",
+ "val": 56.0,
+ "frmt_val": "56"
+ },
+ {
+ "key": "accessors",
+ "val": 0.0,
+ "frmt_val": "0"
+ },
+ {
+ "key": "statements",
+ "val": 174.0,
+ "frmt_val": "174"
+ },
+ {
+ "key": "public_api",
+ "val": 33.0,
+ "frmt_val": "33"
+ },
+ {
+ "key": "comment_lines",
+ "val": 23.0,
+ "frmt_val": "23"
+ },
+ {
+ "key": "comment_lines_density",
+ "val": 5.7,
+ "frmt_val": "5.7%"
+ },
+ {
+ "key": "public_documented_api_density",
+ "val": 36.4,
+ "frmt_val": "36.4%"
+ },
+ {
+ "key": "public_undocumented_api",
+ "val": 21.0,
+ "frmt_val": "21"
+ },
+ {
+ "key": "complexity",
+ "val": 116.0,
+ "frmt_val": "116"
+ },
+ {
+ "key": "function_complexity",
+ "val": 2.1,
+ "frmt_val": "2.1"
+ },
+ {
+ "key": "coverage",
+ "val": 74.3,
+ "frmt_val": "74.3%"
+ },
+ {
+ "key": "lines_to_cover",
+ "val": 194.0,
+ "frmt_val": "194"
+ },
+ {
+ "key": "uncovered_lines",
+ "val": 50.0,
+ "frmt_val": "50"
+ },
+ {
+ "key": "line_coverage",
+ "val": 74.2,
+ "frmt_val": "74.2%"
+ },
+ {
+ "key": "conditions_to_cover",
+ "val": 16.0,
+ "frmt_val": "16"
+ },
+ {
+ "key": "uncovered_conditions",
+ "val": 4.0,
+ "frmt_val": "4"
+ },
+ {
+ "key": "branch_coverage",
+ "val": 75.0,
+ "frmt_val": "75.0%"
+ },
+ {
+ "key": "duplicated_lines",
+ "val": 30.0,
+ "frmt_val": "30"
+ },
+ {
+ "key": "duplicated_blocks",
+ "val": 2.0,
+ "frmt_val": "2"
+ },
+ {
+ "key": "duplicated_files",
+ "val": 1.0,
+ "frmt_val": "1"
+ },
+ {
+ "key": "duplicated_lines_density",
+ "val": 5.8,
+ "frmt_val": "5.8%"
+ },
+ {
+ "key": "major_violations",
+ "val": 1.0,
+ "frmt_val": "1"
+ },
+ {
+ "key": "minor_violations",
+ "val": 1.0,
+ "frmt_val": "1"
+ },
+ {
+ "key": "info_violations",
+ "val": 4.0,
+ "frmt_val": "4"
+ }
+ ]
+ }
+]
diff --git a/server/sonar-web/src/main/js/third-party/moment.js b/server/sonar-web/src/main/js/third-party/moment.js
index 0018faebf9f..4b6676d8aa0 100644
--- a/server/sonar-web/src/main/js/third-party/moment.js
+++ b/server/sonar-web/src/main/js/third-party/moment.js
@@ -1,5 +1,5 @@
//! moment.js
-//! version : 2.5.1
+//! version : 2.7.0
//! authors : Tim Wood, Iskren Chernev, Moment.js contributors
//! license : MIT
//! momentjs.com
@@ -11,8 +11,10 @@
************************************/
var moment,
- VERSION = "2.5.1",
- global = this,
+ VERSION = "2.7.0",
+ // the global-scope this is NOT the global object in Node.js
+ globalScope = typeof global !== 'undefined' ? global : this,
+ oldGlobalMoment,
round = Math.round,
i,
@@ -34,6 +36,7 @@
_f : null,
_l : null,
_strict : null,
+ _tzm : null,
_isUTC : null,
_offset : null, // optional. Combine with _isUTC
_pf : null,
@@ -41,7 +44,7 @@
},
// check for nodeJS
- hasModule = (typeof module !== 'undefined' && module.exports && typeof require !== 'undefined'),
+ hasModule = (typeof module !== 'undefined' && module.exports),
// ASP.NET json date format regex
aspNetJsonRegex = /^\/?Date\((\-?\d+)/i,
@@ -52,7 +55,7 @@
isoDurationRegex = /^(-)?P(?:(?:([0-9,.]*)Y)?(?:([0-9,.]*)M)?(?:([0-9,.]*)D)?(?:T(?:([0-9,.]*)H)?(?:([0-9,.]*)M)?(?:([0-9,.]*)S)?)?|([0-9,.]*)W)$/,
// format tokens
- formattingTokens = /(\[[^\[]*\])|(\\)?(Mo|MM?M?M?|Do|DDDo|DD?D?D?|ddd?d?|do?|w[o|w]?|W[o|W]?|YYYYYY|YYYYY|YYYY|YY|gg(ggg?)?|GG(GGG?)?|e|E|a|A|hh?|HH?|mm?|ss?|S{1,4}|X|zz?|ZZ?|.)/g,
+ formattingTokens = /(\[[^\[]*\])|(\\)?(Mo|MM?M?M?|Do|DDDo|DD?D?D?|ddd?d?|do?|w[o|w]?|W[o|W]?|Q|YYYYYY|YYYYY|YYYY|YY|gg(ggg?)?|GG(GGG?)?|e|E|a|A|hh?|HH?|mm?|ss?|S{1,4}|X|zz?|ZZ?|.)/g,
localFormattingTokens = /(\[[^\[]*\])|(\\)?(LT|LL?L?L?|l{1,4})/g,
// parsing token regexes
@@ -91,7 +94,7 @@
// iso time formats and regexes
isoTimes = [
- ['HH:mm:ss.SSSS', /(T| )\d\d:\d\d:\d\d\.\d{1,3}/],
+ ['HH:mm:ss.SSSS', /(T| )\d\d:\d\d:\d\d\.\d+/],
['HH:mm:ss', /(T| )\d\d:\d\d:\d\d/],
['HH:mm', /(T| )\d\d:\d\d/],
['HH', /(T| )\d\d/]
@@ -122,6 +125,7 @@
w : 'week',
W : 'isoWeek',
M : 'month',
+ Q : 'quarter',
y : 'year',
DDD : 'dayOfYear',
e : 'weekday',
@@ -141,6 +145,16 @@
// format function strings
formatFunctions = {},
+ // default relative time thresholds
+ relativeTimeThresholds = {
+ s: 45, //seconds to minutes
+ m: 45, //minutes to hours
+ h: 22, //hours to days
+ dd: 25, //days to month (month == 1)
+ dm: 45, //days to months (months > 1)
+ dy: 345 //days to year
+ },
+
// tokens to ordinalize and pad
ordinalizeTokens = 'DDD w W M D d'.split(' '),
paddedTokens = 'M D H h m s w W'.split(' '),
@@ -280,6 +294,16 @@
lists = ['months', 'monthsShort', 'weekdays', 'weekdaysShort', 'weekdaysMin'];
+ // Pick the first defined of two or three arguments. dfl comes from
+ // default.
+ function dfl(a, b, c) {
+ switch (arguments.length) {
+ case 2: return a != null ? a : b;
+ case 3: return a != null ? a : b != null ? b : c;
+ default: throw new Error("Implement me");
+ }
+ }
+
function defaultParsingFlags() {
// We need to deep clone this object, and es5 standard is not very
// helpful.
@@ -297,6 +321,23 @@
};
}
+ function deprecate(msg, fn) {
+ var firstTime = true;
+ function printMsg() {
+ if (moment.suppressDeprecationWarnings === false &&
+ typeof console !== 'undefined' && console.warn) {
+ console.warn("Deprecation warning: " + msg);
+ }
+ }
+ return extend(function () {
+ if (firstTime) {
+ printMsg();
+ firstTime = false;
+ }
+ return fn.apply(this, arguments);
+ }, fn);
+ }
+
function padToken(func, count) {
return function (a) {
return leftZeroFill(func.call(this, a), count);
@@ -337,6 +378,7 @@
function Duration(duration) {
var normalizedInput = normalizeObjectUnits(duration),
years = normalizedInput.year || 0,
+ quarters = normalizedInput.quarter || 0,
months = normalizedInput.month || 0,
weeks = normalizedInput.week || 0,
days = normalizedInput.day || 0,
@@ -358,6 +400,7 @@
// which months you are are talking about, so we have to store
// it separately.
this._months = +months +
+ quarters * 3 +
years * 12;
this._data = {};
@@ -420,35 +463,24 @@
}
// helper function for _.addTime and _.subtractTime
- function addOrSubtractDurationFromMoment(mom, duration, isAdding, ignoreUpdateOffset) {
+ function addOrSubtractDurationFromMoment(mom, duration, isAdding, updateOffset) {
var milliseconds = duration._milliseconds,
days = duration._days,
- months = duration._months,
- minutes,
- hours;
+ months = duration._months;
+ updateOffset = updateOffset == null ? true : updateOffset;
if (milliseconds) {
mom._d.setTime(+mom._d + milliseconds * isAdding);
}
- // store the minutes and hours so we can restore them
- if (days || months) {
- minutes = mom.minute();
- hours = mom.hour();
- }
if (days) {
- mom.date(mom.date() + days * isAdding);
+ rawSetter(mom, 'Date', rawGetter(mom, 'Date') + days * isAdding);
}
if (months) {
- mom.month(mom.month() + months * isAdding);
+ rawMonthSetter(mom, rawGetter(mom, 'Month') + months * isAdding);
}
- if (milliseconds && !ignoreUpdateOffset) {
+ if (updateOffset) {
moment.updateOffset(mom, days || months);
}
- // restore the minutes and hours after possibly changing dst
- if (days || months) {
- mom.minute(minutes);
- mom.hour(hours);
- }
}
// check if is an array
@@ -956,6 +988,8 @@
function getParseRegexForToken(token, config) {
var a, strict = config._strict;
switch (token) {
+ case 'Q':
+ return parseTokenOneDigit;
case 'DDDD':
return parseTokenThreeDigits;
case 'YYYY':
@@ -1047,6 +1081,12 @@
var a, datePartArray = config._a;
switch (token) {
+ // QUARTER
+ case 'Q':
+ if (input != null) {
+ datePartArray[MONTH] = (toInt(input) - 1) * 3;
+ }
+ break;
// MONTH
case 'M' : // fall through to MM
case 'MM' :
@@ -1086,7 +1126,7 @@
break;
// YEAR
case 'YY' :
- datePartArray[YEAR] = toInt(input) + (toInt(input) > 68 ? 1900 : 2000);
+ datePartArray[YEAR] = moment.parseTwoDigitYear(input);
break;
case 'YYYY' :
case 'YYYYY' :
@@ -1132,39 +1172,94 @@
config._useUTC = true;
config._tzm = timezoneMinutesFromString(input);
break;
+ // WEEKDAY - human
+ case 'dd':
+ case 'ddd':
+ case 'dddd':
+ a = getLangDefinition(config._l).weekdaysParse(input);
+ // if we didn't get a weekday name, mark the date as invalid
+ if (a != null) {
+ config._w = config._w || {};
+ config._w['d'] = a;
+ } else {
+ config._pf.invalidWeekday = input;
+ }
+ break;
+ // WEEK, WEEK DAY - numeric
case 'w':
case 'ww':
case 'W':
case 'WW':
case 'd':
- case 'dd':
- case 'ddd':
- case 'dddd':
case 'e':
case 'E':
token = token.substr(0, 1);
/* falls through */
- case 'gg':
case 'gggg':
- case 'GG':
case 'GGGG':
case 'GGGGG':
token = token.substr(0, 2);
if (input) {
config._w = config._w || {};
- config._w[token] = input;
+ config._w[token] = toInt(input);
}
break;
+ case 'gg':
+ case 'GG':
+ config._w = config._w || {};
+ config._w[token] = moment.parseTwoDigitYear(input);
}
}
+ function dayOfYearFromWeekInfo(config) {
+ var w, weekYear, week, weekday, dow, doy, temp, lang;
+
+ w = config._w;
+ if (w.GG != null || w.W != null || w.E != null) {
+ dow = 1;
+ doy = 4;
+
+ // TODO: We need to take the current isoWeekYear, but that depends on
+ // how we interpret now (local, utc, fixed offset). So create
+ // a now version of current config (take local/utc/offset flags, and
+ // create now).
+ weekYear = dfl(w.GG, config._a[YEAR], weekOfYear(moment(), 1, 4).year);
+ week = dfl(w.W, 1);
+ weekday = dfl(w.E, 1);
+ } else {
+ lang = getLangDefinition(config._l);
+ dow = lang._week.dow;
+ doy = lang._week.doy;
+
+ weekYear = dfl(w.gg, config._a[YEAR], weekOfYear(moment(), dow, doy).year);
+ week = dfl(w.w, 1);
+
+ if (w.d != null) {
+ // weekday -- low day numbers are considered next week
+ weekday = w.d;
+ if (weekday < dow) {
+ ++week;
+ }
+ } else if (w.e != null) {
+ // local weekday -- counting starts from begining of week
+ weekday = w.e + dow;
+ } else {
+ // default to begining of week
+ weekday = dow;
+ }
+ }
+ temp = dayOfYearFromWeeks(weekYear, week, weekday, doy, dow);
+
+ config._a[YEAR] = temp.year;
+ config._dayOfYear = temp.dayOfYear;
+ }
+
// convert an array to a date.
// the array should mirror the parameters below
// note: all values past the year are optional and will default to the lowest possible value.
// [year, month, day , hour, minute, second, millisecond]
function dateFromConfig(config) {
- var i, date, input = [], currentDate,
- yearToUse, fixYear, w, temp, lang, weekday, week;
+ var i, date, input = [], currentDate, yearToUse;
if (config._d) {
return;
@@ -1174,39 +1269,12 @@
//compute day of the year from weeks and weekdays
if (config._w && config._a[DATE] == null && config._a[MONTH] == null) {
- fixYear = function (val) {
- var int_val = parseInt(val, 10);
- return val ?
- (val.length < 3 ? (int_val > 68 ? 1900 + int_val : 2000 + int_val) : int_val) :
- (config._a[YEAR] == null ? moment().weekYear() : config._a[YEAR]);
- };
-
- w = config._w;
- if (w.GG != null || w.W != null || w.E != null) {
- temp = dayOfYearFromWeeks(fixYear(w.GG), w.W || 1, w.E, 4, 1);
- }
- else {
- lang = getLangDefinition(config._l);
- weekday = w.d != null ? parseWeekday(w.d, lang) :
- (w.e != null ? parseInt(w.e, 10) + lang._week.dow : 0);
-
- week = parseInt(w.w, 10) || 1;
-
- //if we're parsing 'd', then the low day numbers may be next week
- if (w.d != null && weekday < lang._week.dow) {
- week++;
- }
-
- temp = dayOfYearFromWeeks(fixYear(w.gg), week, weekday, lang._week.doy, lang._week.dow);
- }
-
- config._a[YEAR] = temp.year;
- config._dayOfYear = temp.dayOfYear;
+ dayOfYearFromWeekInfo(config);
}
//if the day of the year is set, figure out what it is
if (config._dayOfYear) {
- yearToUse = config._a[YEAR] == null ? currentDate[YEAR] : config._a[YEAR];
+ yearToUse = dfl(config._a[YEAR], currentDate[YEAR]);
if (config._dayOfYear > daysInYear(yearToUse)) {
config._pf._overflowDayOfYear = true;
@@ -1231,11 +1299,12 @@
config._a[i] = input[i] = (config._a[i] == null) ? (i === 2 ? 1 : 0) : config._a[i];
}
- // add the offsets to the time to be parsed so that we can have a clean array for checking isValid
- input[HOUR] += toInt((config._tzm || 0) / 60);
- input[MINUTE] += toInt((config._tzm || 0) % 60);
-
config._d = (config._useUTC ? makeUTCDate : makeDate).apply(null, input);
+ // Apply timezone offset from input. The actual zone can be changed
+ // with parseZone.
+ if (config._tzm != null) {
+ config._d.setUTCMinutes(config._d.getUTCMinutes() + config._tzm);
+ }
}
function dateFromObject(config) {
@@ -1275,6 +1344,11 @@
// date from string and format string
function makeDateFromStringAndFormat(config) {
+ if (config._f === moment.ISO_8601) {
+ parseISO(config);
+ return;
+ }
+
config._a = [];
config._pf.empty = true;
@@ -1387,7 +1461,7 @@
}
// date from iso format
- function makeDateFromString(config) {
+ function parseISO(config) {
var i, l,
string = config._i,
match = isoRegex.exec(string);
@@ -1411,9 +1485,17 @@
config._f += "Z";
}
makeDateFromStringAndFormat(config);
+ } else {
+ config._isValid = false;
}
- else {
- config._d = new Date(string);
+ }
+
+ // date from iso format or fallback
+ function makeDateFromString(config) {
+ parseISO(config);
+ if (config._isValid === false) {
+ delete config._isValid;
+ moment.createFromInputFallback(config);
}
}
@@ -1434,8 +1516,11 @@
config._d = new Date(+input);
} else if (typeof(input) === 'object') {
dateFromObject(config);
- } else {
+ } else if (typeof(input) === 'number') {
+ // from milliseconds
config._d = new Date(input);
+ } else {
+ moment.createFromInputFallback(config);
}
}
@@ -1490,15 +1575,15 @@
hours = round(minutes / 60),
days = round(hours / 24),
years = round(days / 365),
- args = seconds < 45 && ['s', seconds] ||
+ args = seconds < relativeTimeThresholds.s && ['s', seconds] ||
minutes === 1 && ['m'] ||
- minutes < 45 && ['mm', minutes] ||
+ minutes < relativeTimeThresholds.m && ['mm', minutes] ||
hours === 1 && ['h'] ||
- hours < 22 && ['hh', hours] ||
+ hours < relativeTimeThresholds.h && ['hh', hours] ||
days === 1 && ['d'] ||
- days <= 25 && ['dd', days] ||
- days <= 45 && ['M'] ||
- days < 345 && ['MM', round(days / 30)] ||
+ days <= relativeTimeThresholds.dd && ['dd', days] ||
+ days <= relativeTimeThresholds.dm && ['M'] ||
+ days < relativeTimeThresholds.dy && ['MM', round(days / 30)] ||
years === 1 && ['y'] || ['yy', years];
args[2] = withoutSuffix;
args[3] = milliseconds > 0;
@@ -1544,6 +1629,7 @@
function dayOfYearFromWeeks(year, week, weekday, firstDayOfWeekOfYear, firstDayOfWeek) {
var d = makeUTCDate(year, 0, 1).getUTCDay(), daysToAdd, dayOfYear;
+ d = d === 0 ? 7 : d;
weekday = weekday != null ? weekday : firstDayOfWeek;
daysToAdd = firstDayOfWeek - d + (d > firstDayOfWeekOfYear ? 7 : 0) - (d < firstDayOfWeek ? 7 : 0);
dayOfYear = 7 * (week - 1) + (weekday - firstDayOfWeek) + daysToAdd + 1;
@@ -1562,7 +1648,7 @@
var input = config._i,
format = config._f;
- if (input === null) {
+ if (input === null || (format === undefined && input === '')) {
return moment.invalid({nullInput: true});
}
@@ -1608,6 +1694,51 @@
return makeMoment(c);
};
+ moment.suppressDeprecationWarnings = false;
+
+ moment.createFromInputFallback = deprecate(
+ "moment construction falls back to js Date. This is " +
+ "discouraged and will be removed in upcoming major " +
+ "release. Please refer to " +
+ "https://github.com/moment/moment/issues/1407 for more info.",
+ function (config) {
+ config._d = new Date(config._i);
+ });
+
+ // Pick a moment m from moments so that m[fn](other) is true for all
+ // other. This relies on the function fn to be transitive.
+ //
+ // moments should either be an array of moment objects or an array, whose
+ // first element is an array of moment objects.
+ function pickBy(fn, moments) {
+ var res, i;
+ if (moments.length === 1 && isArray(moments[0])) {
+ moments = moments[0];
+ }
+ if (!moments.length) {
+ return moment();
+ }
+ res = moments[0];
+ for (i = 1; i < moments.length; ++i) {
+ if (moments[i][fn](res)) {
+ res = moments[i];
+ }
+ }
+ return res;
+ }
+
+ moment.min = function () {
+ var args = [].slice.call(arguments, 0);
+
+ return pickBy('isBefore', args);
+ };
+
+ moment.max = function () {
+ var args = [].slice.call(arguments, 0);
+
+ return pickBy('isAfter', args);
+ };
+
// creating with utc
moment.utc = function (input, format, lang, strict) {
var c;
@@ -1704,10 +1835,26 @@
// default format
moment.defaultFormat = isoFormat;
+ // constant that refers to the ISO standard
+ moment.ISO_8601 = function () {};
+
+ // Plugins that add properties should also add the key here (null value),
+ // so we can properly clone ourselves.
+ moment.momentProperties = momentProperties;
+
// This function will be called whenever a moment is mutated.
// It is intended to keep the offset in sync with the timezone.
moment.updateOffset = function () {};
+ // This function allows you to set a threshold for relative time strings
+ moment.relativeTimeThreshold = function(threshold, limit) {
+ if (relativeTimeThresholds[threshold] === undefined) {
+ return false;
+ }
+ relativeTimeThresholds[threshold] = limit;
+ return true;
+ };
+
// This function will load languages and then set the global language. If
// no arguments are passed in, it will simply return the current global
// language key.
@@ -1771,6 +1918,10 @@
return moment.apply(null, arguments).parseZone();
};
+ moment.parseTwoDigitYear = function (input) {
+ return toInt(input) + (toInt(input) > 68 ? 1900 : 2000);
+ };
+
/************************************
Moment Prototype
************************************/
@@ -1859,7 +2010,9 @@
add : function (input, val) {
var dur;
// switch args to support add('s', 1) and add(1, 's')
- if (typeof input === 'string') {
+ if (typeof input === 'string' && typeof val === 'string') {
+ dur = moment.duration(isNaN(+val) ? +input : +val, isNaN(+val) ? val : input);
+ } else if (typeof input === 'string') {
dur = moment.duration(+val, input);
} else {
dur = moment.duration(input, val);
@@ -1871,7 +2024,9 @@
subtract : function (input, val) {
var dur;
// switch args to support subtract('s', 1) and subtract(1, 's')
- if (typeof input === 'string') {
+ if (typeof input === 'string' && typeof val === 'string') {
+ dur = moment.duration(isNaN(+val) ? +input : +val, isNaN(+val) ? val : input);
+ } else if (typeof input === 'string') {
dur = moment.duration(+val, input);
} else {
dur = moment.duration(input, val);
@@ -1922,10 +2077,11 @@
return this.from(moment(), withoutSuffix);
},
- calendar : function () {
+ calendar : function (time) {
// We want to compare the start of today, vs this.
// Getting start-of-today depends on whether we're zone'd or not.
- var sod = makeAs(moment(), this).startOf('day'),
+ var now = time || moment(),
+ sod = makeAs(now, this).startOf('day'),
diff = this.diff(sod, 'days', true),
format = diff < -6 ? 'sameElse' :
diff < -1 ? 'lastWeek' :
@@ -1955,27 +2111,7 @@
}
},
- month : function (input) {
- var utc = this._isUTC ? 'UTC' : '',
- dayOfMonth;
-
- if (input != null) {
- if (typeof input === 'string') {
- input = this.lang().monthsParse(input);
- if (typeof input !== 'number') {
- return this;
- }
- }
-
- dayOfMonth = Math.min(this.date(),
- daysInMonth(this.year(), input));
- this._d['set' + utc + 'Month'](input, dayOfMonth);
- moment.updateOffset(this, true);
- return this;
- } else {
- return this._d['get' + utc + 'Month']();
- }
- },
+ month : makeAccessor('Month', true),
startOf: function (units) {
units = normalizeUnits(units);
@@ -1985,6 +2121,7 @@
case 'year':
this.month(0);
/* falls through */
+ case 'quarter':
case 'month':
this.date(1);
/* falls through */
@@ -2011,6 +2148,11 @@
this.isoWeekday(1);
}
+ // quarters are also special
+ if (units === 'quarter') {
+ this.month(Math.floor(this.month() / 3) * 3);
+ }
+
return this;
},
@@ -2034,18 +2176,33 @@
return +this.clone().startOf(units) === +makeAs(input, this).startOf(units);
},
- min: function (other) {
- other = moment.apply(null, arguments);
- return other < this ? this : other;
- },
-
- max: function (other) {
- other = moment.apply(null, arguments);
- return other > this ? this : other;
- },
-
- zone : function (input, adjust) {
- adjust = (adjust == null ? true : false);
+ min: deprecate(
+ "moment().min is deprecated, use moment.min instead. https://github.com/moment/moment/issues/1548",
+ function (other) {
+ other = moment.apply(null, arguments);
+ return other < this ? this : other;
+ }
+ ),
+
+ max: deprecate(
+ "moment().max is deprecated, use moment.max instead. https://github.com/moment/moment/issues/1548",
+ function (other) {
+ other = moment.apply(null, arguments);
+ return other > this ? this : other;
+ }
+ ),
+
+ // keepTime = true means only change the timezone, without affecting
+ // the local hour. So 5:31:26 +0300 --[zone(2, true)]--> 5:31:26 +0200
+ // It is possible that 5:31:26 doesn't exist int zone +0200, so we
+ // adjust the time as needed, to be valid.
+ //
+ // Keeping the time actually adds/subtracts (one hour)
+ // from the actual represented time. That is why we call updateOffset
+ // a second time. In case it wants us to change the offset again
+ // _changeInProgress == true case, then we have to adjust, because
+ // there is no such time in the given timezone.
+ zone : function (input, keepTime) {
var offset = this._offset || 0;
if (input != null) {
if (typeof input === "string") {
@@ -2056,8 +2213,15 @@
}
this._offset = input;
this._isUTC = true;
- if (offset !== input && adjust) {
- addOrSubtractDurationFromMoment(this, moment.duration(offset - input, 'm'), 1, true);
+ if (offset !== input) {
+ if (!keepTime || this._changeInProgress) {
+ addOrSubtractDurationFromMoment(this,
+ moment.duration(offset - input, 'm'), 1, false);
+ } else if (!this._changeInProgress) {
+ this._changeInProgress = true;
+ moment.updateOffset(this, true);
+ this._changeInProgress = null;
+ }
}
} else {
return this._isUTC ? offset : this._d.getTimezoneOffset();
@@ -2102,8 +2266,8 @@
return input == null ? dayOfYear : this.add("d", (input - dayOfYear));
},
- quarter : function () {
- return Math.ceil((this.month() + 1.0) / 3.0);
+ quarter : function (input) {
+ return input == null ? Math.ceil((this.month() + 1) / 3) : this.month((input - 1) * 3 + this.month() % 3);
},
weekYear : function (input) {
@@ -2173,41 +2337,68 @@
}
});
- // helper for adding shortcuts
- function makeGetterAndSetter(name, key) {
- // ignoreOffsetTransitions provides a hint to updateOffset to not
- // change hours/minutes when crossing a tz boundary. This is frequently
- // desirable when modifying part of an existing moment object directly.
- var defaultIgnoreOffsetTransitions = key === 'date' || key === 'month' || key === 'year';
- moment.fn[name] = moment.fn[name + 's'] = function (input, ignoreOffsetTransitions) {
- var utc = this._isUTC ? 'UTC' : '';
- if (ignoreOffsetTransitions == null) {
- ignoreOffsetTransitions = defaultIgnoreOffsetTransitions;
+ function rawMonthSetter(mom, value) {
+ var dayOfMonth;
+
+ // TODO: Move this out of here!
+ if (typeof value === 'string') {
+ value = mom.lang().monthsParse(value);
+ // TODO: Another silent failure?
+ if (typeof value !== 'number') {
+ return mom;
}
- if (input != null) {
- this._d['set' + utc + key](input);
- moment.updateOffset(this, ignoreOffsetTransitions);
+ }
+
+ dayOfMonth = Math.min(mom.date(),
+ daysInMonth(mom.year(), value));
+ mom._d['set' + (mom._isUTC ? 'UTC' : '') + 'Month'](value, dayOfMonth);
+ return mom;
+ }
+
+ function rawGetter(mom, unit) {
+ return mom._d['get' + (mom._isUTC ? 'UTC' : '') + unit]();
+ }
+
+ function rawSetter(mom, unit, value) {
+ if (unit === 'Month') {
+ return rawMonthSetter(mom, value);
+ } else {
+ return mom._d['set' + (mom._isUTC ? 'UTC' : '') + unit](value);
+ }
+ }
+
+ function makeAccessor(unit, keepTime) {
+ return function (value) {
+ if (value != null) {
+ rawSetter(this, unit, value);
+ moment.updateOffset(this, keepTime);
return this;
} else {
- return this._d['get' + utc + key]();
+ return rawGetter(this, unit);
}
};
}
- // loop through and add shortcuts (Date, Hours, Minutes, Seconds, Milliseconds)
- // Month has a custom getter/setter.
- for (i = 0; i < proxyGettersAndSetters.length; i ++) {
- makeGetterAndSetter(proxyGettersAndSetters[i].toLowerCase().replace(/s$/, ''), proxyGettersAndSetters[i]);
- }
-
- // add shortcut for year (uses different syntax than the getter/setter 'year' == 'FullYear')
- makeGetterAndSetter('year', 'FullYear');
+ moment.fn.millisecond = moment.fn.milliseconds = makeAccessor('Milliseconds', false);
+ moment.fn.second = moment.fn.seconds = makeAccessor('Seconds', false);
+ moment.fn.minute = moment.fn.minutes = makeAccessor('Minutes', false);
+ // Setting the hour should keep the time, because the user explicitly
+ // specified which hour he wants. So trying to maintain the same hour (in
+ // a new timezone) makes sense. Adding/subtracting hours does not follow
+ // this rule.
+ moment.fn.hour = moment.fn.hours = makeAccessor('Hours', true);
+ // moment.fn.month is defined separately
+ moment.fn.date = makeAccessor('Date', true);
+ moment.fn.dates = deprecate("dates accessor is deprecated. Use date instead.", makeAccessor('Date', true));
+ moment.fn.year = makeAccessor('FullYear', true);
+ moment.fn.years = deprecate("years accessor is deprecated. Use year instead.", makeAccessor('FullYear', true));
// add plural methods
moment.fn.days = moment.fn.day;
moment.fn.months = moment.fn.month;
moment.fn.weeks = moment.fn.week;
moment.fn.isoWeeks = moment.fn.isoWeek;
+ moment.fn.quarters = moment.fn.quarter;
// add aliased format methods
moment.fn.toJSON = moment.fn.toISOString;
@@ -2377,51 +2568,5953 @@
}
});
- /* EMBED_LANGUAGES */
+ // moment.js language configuration
+// language : Moroccan Arabic (ar-ma)
+// author : ElFadili Yassine : https://github.com/ElFadiliY
+// author : Abdel Said : https://github.com/abdelsaid
+
+(function (factory) {
+ factory(moment);
+}(function (moment) {
+ return moment.lang('ar-ma', {
+ months : "يناير_فبراير_مارس_أبريل_ماي_يونيو_يوليوز_غشت_شتنبر_أكتوبر_نونبر_دجنبر".split("_"),
+ monthsShort : "يناير_فبراير_مارس_أبريل_ماي_يونيو_يوليوز_غشت_شتنبر_أكتوبر_نونبر_دجنبر".split("_"),
+ weekdays : "الأحد_الإتنين_الثلاثاء_الأربعاء_الخميس_الجمعة_السبت".split("_"),
+ weekdaysShort : "احد_اتنين_ثلاثاء_اربعاء_خميس_جمعة_سبت".split("_"),
+ weekdaysMin : "ح_ن_ث_ر_خ_ج_س".split("_"),
+ longDateFormat : {
+ LT : "HH:mm",
+ L : "DD/MM/YYYY",
+ LL : "D MMMM YYYY",
+ LLL : "D MMMM YYYY LT",
+ LLLL : "dddd D MMMM YYYY LT"
+ },
+ calendar : {
+ sameDay: "[اليوم على الساعة] LT",
+ nextDay: '[غدا على الساعة] LT',
+ nextWeek: 'dddd [على الساعة] LT',
+ lastDay: '[أمس على الساعة] LT',
+ lastWeek: 'dddd [على الساعة] LT',
+ sameElse: 'L'
+ },
+ relativeTime : {
+ future : "في %s",
+ past : "منذ %s",
+ s : "ثوان",
+ m : "دقيقة",
+ mm : "%d دقائق",
+ h : "ساعة",
+ hh : "%d ساعات",
+ d : "يوم",
+ dd : "%d أيام",
+ M : "شهر",
+ MM : "%d أشهر",
+ y : "سنة",
+ yy : "%d سنوات"
+ },
+ week : {
+ dow : 6, // Saturday is the first day of the week.
+ doy : 12 // The week that contains Jan 1st is the first week of the year.
+ }
+ });
+}));
+// moment.js language configuration
+// language : Arabic Saudi Arabia (ar-sa)
+// author : Suhail Alkowaileet : https://github.com/xsoh
+
+(function (factory) {
+ factory(moment);
+}(function (moment) {
+ var symbolMap = {
+ '1': '١',
+ '2': '٢',
+ '3': '٣',
+ '4': '٤',
+ '5': '٥',
+ '6': '٦',
+ '7': '٧',
+ '8': '٨',
+ '9': '٩',
+ '0': '٠'
+ }, numberMap = {
+ '١': '1',
+ '٢': '2',
+ '٣': '3',
+ '٤': '4',
+ '٥': '5',
+ '٦': '6',
+ '٧': '7',
+ '٨': '8',
+ '٩': '9',
+ '٠': '0'
+ };
+
+ return moment.lang('ar-sa', {
+ months : "يناير_فبراير_مارس_أبريل_مايو_يونيو_يوليو_أغسطس_سبتمبر_أكتوبر_نوفمبر_ديسمبر".split("_"),
+ monthsShort : "يناير_فبراير_مارس_أبريل_مايو_يونيو_يوليو_أغسطس_سبتمبر_أكتوبر_نوفمبر_ديسمبر".split("_"),
+ weekdays : "الأحد_الإثنين_الثلاثاء_الأربعاء_الخميس_الجمعة_السبت".split("_"),
+ weekdaysShort : "أحد_إثنين_ثلاثاء_أربعاء_خميس_جمعة_سبت".split("_"),
+ weekdaysMin : "ح_ن_ث_ر_خ_ج_س".split("_"),
+ longDateFormat : {
+ LT : "HH:mm",
+ L : "DD/MM/YYYY",
+ LL : "D MMMM YYYY",
+ LLL : "D MMMM YYYY LT",
+ LLLL : "dddd D MMMM YYYY LT"
+ },
+ meridiem : function (hour, minute, isLower) {
+ if (hour < 12) {
+ return "ص";
+ } else {
+ return "م";
+ }
+ },
+ calendar : {
+ sameDay: "[اليوم على الساعة] LT",
+ nextDay: '[غدا على الساعة] LT',
+ nextWeek: 'dddd [على الساعة] LT',
+ lastDay: '[أمس على الساعة] LT',
+ lastWeek: 'dddd [على الساعة] LT',
+ sameElse: 'L'
+ },
+ relativeTime : {
+ future : "في %s",
+ past : "منذ %s",
+ s : "ثوان",
+ m : "دقيقة",
+ mm : "%d دقائق",
+ h : "ساعة",
+ hh : "%d ساعات",
+ d : "يوم",
+ dd : "%d أيام",
+ M : "شهر",
+ MM : "%d أشهر",
+ y : "سنة",
+ yy : "%d سنوات"
+ },
+ preparse: function (string) {
+ return string.replace(/[۰-۹]/g, function (match) {
+ return numberMap[match];
+ }).replace(/،/g, ',');
+ },
+ postformat: function (string) {
+ return string.replace(/\d/g, function (match) {
+ return symbolMap[match];
+ }).replace(/,/g, '،');
+ },
+ week : {
+ dow : 6, // Saturday is the first day of the week.
+ doy : 12 // The week that contains Jan 1st is the first week of the year.
+ }
+ });
+}));
+// moment.js language configuration
+// language : Arabic (ar)
+// author : Abdel Said : https://github.com/abdelsaid
+// changes in months, weekdays : Ahmed Elkhatib
+
+(function (factory) {
+ factory(moment);
+}(function (moment) {
+ var symbolMap = {
+ '1': '١',
+ '2': '٢',
+ '3': '٣',
+ '4': '٤',
+ '5': '٥',
+ '6': '٦',
+ '7': '٧',
+ '8': '٨',
+ '9': '٩',
+ '0': '٠'
+ }, numberMap = {
+ '١': '1',
+ '٢': '2',
+ '٣': '3',
+ '٤': '4',
+ '٥': '5',
+ '٦': '6',
+ '٧': '7',
+ '٨': '8',
+ '٩': '9',
+ '٠': '0'
+ };
+
+ return moment.lang('ar', {
+ months : "يناير/ كانون الثاني_فبراير/ شباط_مارس/ آذار_أبريل/ نيسان_مايو/ أيار_يونيو/ حزيران_يوليو/ تموز_أغسطس/ آب_سبتمبر/ أيلول_أكتوبر/ تشرين الأول_نوفمبر/ تشرين الثاني_ديسمبر/ كانون الأول".split("_"),
+ monthsShort : "يناير/ كانون الثاني_فبراير/ شباط_مارس/ آذار_أبريل/ نيسان_مايو/ أيار_يونيو/ حزيران_يوليو/ تموز_أغسطس/ آب_سبتمبر/ أيلول_أكتوبر/ تشرين الأول_نوفمبر/ تشرين الثاني_ديسمبر/ كانون الأول".split("_"),
+ weekdays : "الأحد_الإثنين_الثلاثاء_الأربعاء_الخميس_الجمعة_السبت".split("_"),
+ weekdaysShort : "أحد_إثنين_ثلاثاء_أربعاء_خميس_جمعة_سبت".split("_"),
+ weekdaysMin : "ح_ن_ث_ر_خ_ج_س".split("_"),
+ longDateFormat : {
+ LT : "HH:mm",
+ L : "DD/MM/YYYY",
+ LL : "D MMMM YYYY",
+ LLL : "D MMMM YYYY LT",
+ LLLL : "dddd D MMMM YYYY LT"
+ },
+ meridiem : function (hour, minute, isLower) {
+ if (hour < 12) {
+ return "ص";
+ } else {
+ return "م";
+ }
+ },
+ calendar : {
+ sameDay: "[اليوم على الساعة] LT",
+ nextDay: '[غدا على الساعة] LT',
+ nextWeek: 'dddd [على الساعة] LT',
+ lastDay: '[أمس على الساعة] LT',
+ lastWeek: 'dddd [على الساعة] LT',
+ sameElse: 'L'
+ },
+ relativeTime : {
+ future : "في %s",
+ past : "منذ %s",
+ s : "ثوان",
+ m : "دقيقة",
+ mm : "%d دقائق",
+ h : "ساعة",
+ hh : "%d ساعات",
+ d : "يوم",
+ dd : "%d أيام",
+ M : "شهر",
+ MM : "%d أشهر",
+ y : "سنة",
+ yy : "%d سنوات"
+ },
+ preparse: function (string) {
+ return string.replace(/[۰-۹]/g, function (match) {
+ return numberMap[match];
+ }).replace(/،/g, ',');
+ },
+ postformat: function (string) {
+ return string.replace(/\d/g, function (match) {
+ return symbolMap[match];
+ }).replace(/,/g, '،');
+ },
+ week : {
+ dow : 6, // Saturday is the first day of the week.
+ doy : 12 // The week that contains Jan 1st is the first week of the year.
+ }
+ });
+}));
+// moment.js language configuration
+// language : azerbaijani (az)
+// author : topchiyev : https://github.com/topchiyev
+
+(function (factory) {
+ factory(moment);
+}(function (moment) {
+
+ var suffixes = {
+ 1: "-inci",
+ 5: "-inci",
+ 8: "-inci",
+ 70: "-inci",
+ 80: "-inci",
+
+ 2: "-nci",
+ 7: "-nci",
+ 20: "-nci",
+ 50: "-nci",
+
+ 3: "-üncü",
+ 4: "-üncü",
+ 100: "-üncü",
+
+ 6: "-ncı",
+
+ 9: "-uncu",
+ 10: "-uncu",
+ 30: "-uncu",
+
+ 60: "-ıncı",
+ 90: "-ıncı"
+ };
+ return moment.lang('az', {
+ months : "yanvar_fevral_mart_aprel_may_iyun_iyul_avqust_sentyabr_oktyabr_noyabr_dekabr".split("_"),
+ monthsShort : "yan_fev_mar_apr_may_iyn_iyl_avq_sen_okt_noy_dek".split("_"),
+ weekdays : "Bazar_Bazar ertəsi_Çərşənbə axşamı_Çərşənbə_Cümə axşamı_Cümə_Şənbə".split("_"),
+ weekdaysShort : "Baz_BzE_ÇAx_Çər_CAx_Cüm_Şən".split("_"),
+ weekdaysMin : "Bz_BE_ÇA_Çə_CA_Cü_Şə".split("_"),
+ longDateFormat : {
+ LT : "HH:mm",
+ L : "DD.MM.YYYY",
+ LL : "D MMMM YYYY",
+ LLL : "D MMMM YYYY LT",
+ LLLL : "dddd, D MMMM YYYY LT"
+ },
+ calendar : {
+ sameDay : '[bugün saat] LT',
+ nextDay : '[sabah saat] LT',
+ nextWeek : '[gələn həftə] dddd [saat] LT',
+ lastDay : '[dünən] LT',
+ lastWeek : '[keçən həftə] dddd [saat] LT',
+ sameElse : 'L'
+ },
+ relativeTime : {
+ future : "%s sonra",
+ past : "%s əvvəl",
+ s : "birneçə saniyyə",
+ m : "bir dəqiqə",
+ mm : "%d dəqiqə",
+ h : "bir saat",
+ hh : "%d saat",
+ d : "bir gün",
+ dd : "%d gün",
+ M : "bir ay",
+ MM : "%d ay",
+ y : "bir il",
+ yy : "%d il"
+ },
+ meridiem : function (hour, minute, isLower) {
+ if (hour < 4) {
+ return "gecə";
+ } else if (hour < 12) {
+ return "səhər";
+ } else if (hour < 17) {
+ return "gündüz";
+ } else {
+ return "axşam";
+ }
+ },
+ ordinal : function (number) {
+ if (number === 0) { // special case for zero
+ return number + "-ıncı";
+ }
+ var a = number % 10,
+ b = number % 100 - a,
+ c = number >= 100 ? 100 : null;
+
+ return number + (suffixes[a] || suffixes[b] || suffixes[c]);
+ },
+ week : {
+ dow : 1, // Monday is the first day of the week.
+ doy : 7 // The week that contains Jan 1st is the first week of the year.
+ }
+ });
+}));
+// moment.js language configuration
+// language : bulgarian (bg)
+// author : Krasen Borisov : https://github.com/kraz
+
+(function (factory) {
+ factory(moment);
+}(function (moment) {
+ return moment.lang('bg', {
+ months : "януари_февруари_март_април_май_юни_юли_август_септември_октомври_ноември_декември".split("_"),
+ monthsShort : "янр_фев_мар_апр_май_юни_юли_авг_сеп_окт_ное_дек".split("_"),
+ weekdays : "неделя_понеделник_вторник_сряда_четвъртък_петък_събота".split("_"),
+ weekdaysShort : "нед_пон_вто_сря_чет_пет_съб".split("_"),
+ weekdaysMin : "нд_пн_вт_ср_чт_пт_сб".split("_"),
+ longDateFormat : {
+ LT : "H:mm",
+ L : "D.MM.YYYY",
+ LL : "D MMMM YYYY",
+ LLL : "D MMMM YYYY LT",
+ LLLL : "dddd, D MMMM YYYY LT"
+ },
+ calendar : {
+ sameDay : '[Днес в] LT',
+ nextDay : '[Утре в] LT',
+ nextWeek : 'dddd [в] LT',
+ lastDay : '[Вчера в] LT',
+ lastWeek : function () {
+ switch (this.day()) {
+ case 0:
+ case 3:
+ case 6:
+ return '[В изминалата] dddd [в] LT';
+ case 1:
+ case 2:
+ case 4:
+ case 5:
+ return '[В изминалия] dddd [в] LT';
+ }
+ },
+ sameElse : 'L'
+ },
+ relativeTime : {
+ future : "след %s",
+ past : "преди %s",
+ s : "няколко секунди",
+ m : "минута",
+ mm : "%d минути",
+ h : "час",
+ hh : "%d часа",
+ d : "ден",
+ dd : "%d дни",
+ M : "месец",
+ MM : "%d месеца",
+ y : "година",
+ yy : "%d години"
+ },
+ ordinal : function (number) {
+ var lastDigit = number % 10,
+ last2Digits = number % 100;
+ if (number === 0) {
+ return number + '-ев';
+ } else if (last2Digits === 0) {
+ return number + '-ен';
+ } else if (last2Digits > 10 && last2Digits < 20) {
+ return number + '-ти';
+ } else if (lastDigit === 1) {
+ return number + '-ви';
+ } else if (lastDigit === 2) {
+ return number + '-ри';
+ } else if (lastDigit === 7 || lastDigit === 8) {
+ return number + '-ми';
+ } else {
+ return number + '-ти';
+ }
+ },
+ week : {
+ dow : 1, // Monday is the first day of the week.
+ doy : 7 // The week that contains Jan 1st is the first week of the year.
+ }
+ });
+}));
+// moment.js language configuration
+// language : Bengali (bn)
+// author : Kaushik Gandhi : https://github.com/kaushikgandhi
+
+(function (factory) {
+ factory(moment);
+}(function (moment) {
+ var symbolMap = {
+ '1': '১',
+ '2': '২',
+ '3': '৩',
+ '4': '৪',
+ '5': '৫',
+ '6': '৬',
+ '7': '৭',
+ '8': '৮',
+ '9': '৯',
+ '0': '০'
+ },
+ numberMap = {
+ '১': '1',
+ '২': '2',
+ '৩': '3',
+ '৪': '4',
+ '৫': '5',
+ '৬': '6',
+ '৭': '7',
+ '৮': '8',
+ '৯': '9',
+ '০': '0'
+ };
+
+ return moment.lang('bn', {
+ months : 'জানুয়ারী_ফেবুয়ারী_মার্চ_এপ্রিল_মে_জুন_জুলাই_অগাস্ট_সেপ্টেম্বর_অক্টোবর_নভেম্বর_ডিসেম্বর'.split("_"),
+ monthsShort : 'জানু_ফেব_মার্চ_এপর_মে_জুন_জুল_অগ_সেপ্ট_অক্টো_নভ_ডিসেম্'.split("_"),
+ weekdays : 'রবিবার_সোমবার_মঙ্গলবার_বুধবার_বৃহস্পত্তিবার_শুক্রুবার_শনিবার'.split("_"),
+ weekdaysShort : 'রবি_সোম_মঙ্গল_বুধ_বৃহস্পত্তি_শুক্রু_শনি'.split("_"),
+ weekdaysMin : 'রব_সম_মঙ্গ_বু_ব্রিহ_শু_শনি'.split("_"),
+ longDateFormat : {
+ LT : "A h:mm সময়",
+ L : "DD/MM/YYYY",
+ LL : "D MMMM YYYY",
+ LLL : "D MMMM YYYY, LT",
+ LLLL : "dddd, D MMMM YYYY, LT"
+ },
+ calendar : {
+ sameDay : '[আজ] LT',
+ nextDay : '[আগামীকাল] LT',
+ nextWeek : 'dddd, LT',
+ lastDay : '[গতকাল] LT',
+ lastWeek : '[গত] dddd, LT',
+ sameElse : 'L'
+ },
+ relativeTime : {
+ future : "%s পরে",
+ past : "%s আগে",
+ s : "কএক সেকেন্ড",
+ m : "এক মিনিট",
+ mm : "%d মিনিট",
+ h : "এক ঘন্টা",
+ hh : "%d ঘন্টা",
+ d : "এক দিন",
+ dd : "%d দিন",
+ M : "এক মাস",
+ MM : "%d মাস",
+ y : "এক বছর",
+ yy : "%d বছর"
+ },
+ preparse: function (string) {
+ return string.replace(/[১২৩৪৫৬৭৮৯০]/g, function (match) {
+ return numberMap[match];
+ });
+ },
+ postformat: function (string) {
+ return string.replace(/\d/g, function (match) {
+ return symbolMap[match];
+ });
+ },
+ //Bengali is a vast language its spoken
+ //in different forms in various parts of the world.
+ //I have just generalized with most common one used
+ meridiem : function (hour, minute, isLower) {
+ if (hour < 4) {
+ return "রাত";
+ } else if (hour < 10) {
+ return "শকাল";
+ } else if (hour < 17) {
+ return "দুপুর";
+ } else if (hour < 20) {
+ return "বিকেল";
+ } else {
+ return "রাত";
+ }
+ },
+ week : {
+ dow : 0, // Sunday is the first day of the week.
+ doy : 6 // The week that contains Jan 1st is the first week of the year.
+ }
+ });
+}));
+// moment.js language configuration
+// language : breton (br)
+// author : Jean-Baptiste Le Duigou : https://github.com/jbleduigou
+
+(function (factory) {
+ factory(moment);
+}(function (moment) {
+ function relativeTimeWithMutation(number, withoutSuffix, key) {
+ var format = {
+ 'mm': "munutenn",
+ 'MM': "miz",
+ 'dd': "devezh"
+ };
+ return number + ' ' + mutation(format[key], number);
+ }
+
+ function specialMutationForYears(number) {
+ switch (lastNumber(number)) {
+ case 1:
+ case 3:
+ case 4:
+ case 5:
+ case 9:
+ return number + ' bloaz';
+ default:
+ return number + ' vloaz';
+ }
+ }
+
+ function lastNumber(number) {
+ if (number > 9) {
+ return lastNumber(number % 10);
+ }
+ return number;
+ }
+
+ function mutation(text, number) {
+ if (number === 2) {
+ return softMutation(text);
+ }
+ return text;
+ }
+
+ function softMutation(text) {
+ var mutationTable = {
+ 'm': 'v',
+ 'b': 'v',
+ 'd': 'z'
+ };
+ if (mutationTable[text.charAt(0)] === undefined) {
+ return text;
+ }
+ return mutationTable[text.charAt(0)] + text.substring(1);
+ }
+
+ return moment.lang('br', {
+ months : "Genver_C'hwevrer_Meurzh_Ebrel_Mae_Mezheven_Gouere_Eost_Gwengolo_Here_Du_Kerzu".split("_"),
+ monthsShort : "Gen_C'hwe_Meu_Ebr_Mae_Eve_Gou_Eos_Gwe_Her_Du_Ker".split("_"),
+ weekdays : "Sul_Lun_Meurzh_Merc'her_Yaou_Gwener_Sadorn".split("_"),
+ weekdaysShort : "Sul_Lun_Meu_Mer_Yao_Gwe_Sad".split("_"),
+ weekdaysMin : "Su_Lu_Me_Mer_Ya_Gw_Sa".split("_"),
+ longDateFormat : {
+ LT : "h[e]mm A",
+ L : "DD/MM/YYYY",
+ LL : "D [a viz] MMMM YYYY",
+ LLL : "D [a viz] MMMM YYYY LT",
+ LLLL : "dddd, D [a viz] MMMM YYYY LT"
+ },
+ calendar : {
+ sameDay : '[Hiziv da] LT',
+ nextDay : '[Warc\'hoazh da] LT',
+ nextWeek : 'dddd [da] LT',
+ lastDay : '[Dec\'h da] LT',
+ lastWeek : 'dddd [paset da] LT',
+ sameElse : 'L'
+ },
+ relativeTime : {
+ future : "a-benn %s",
+ past : "%s 'zo",
+ s : "un nebeud segondennoù",
+ m : "ur vunutenn",
+ mm : relativeTimeWithMutation,
+ h : "un eur",
+ hh : "%d eur",
+ d : "un devezh",
+ dd : relativeTimeWithMutation,
+ M : "ur miz",
+ MM : relativeTimeWithMutation,
+ y : "ur bloaz",
+ yy : specialMutationForYears
+ },
+ ordinal : function (number) {
+ var output = (number === 1) ? 'añ' : 'vet';
+ return number + output;
+ },
+ week : {
+ dow : 1, // Monday is the first day of the week.
+ doy : 4 // The week that contains Jan 4th is the first week of the year.
+ }
+ });
+}));
+// moment.js language configuration
+// language : bosnian (bs)
+// author : Nedim Cholich : https://github.com/frontyard
+// based on (hr) translation by Bojan Marković
+
+(function (factory) {
+ factory(moment);
+}(function (moment) {
+
+ function translate(number, withoutSuffix, key) {
+ var result = number + " ";
+ switch (key) {
+ case 'm':
+ return withoutSuffix ? 'jedna minuta' : 'jedne minute';
+ case 'mm':
+ if (number === 1) {
+ result += 'minuta';
+ } else if (number === 2 || number === 3 || number === 4) {
+ result += 'minute';
+ } else {
+ result += 'minuta';
+ }
+ return result;
+ case 'h':
+ return withoutSuffix ? 'jedan sat' : 'jednog sata';
+ case 'hh':
+ if (number === 1) {
+ result += 'sat';
+ } else if (number === 2 || number === 3 || number === 4) {
+ result += 'sata';
+ } else {
+ result += 'sati';
+ }
+ return result;
+ case 'dd':
+ if (number === 1) {
+ result += 'dan';
+ } else {
+ result += 'dana';
+ }
+ return result;
+ case 'MM':
+ if (number === 1) {
+ result += 'mjesec';
+ } else if (number === 2 || number === 3 || number === 4) {
+ result += 'mjeseca';
+ } else {
+ result += 'mjeseci';
+ }
+ return result;
+ case 'yy':
+ if (number === 1) {
+ result += 'godina';
+ } else if (number === 2 || number === 3 || number === 4) {
+ result += 'godine';
+ } else {
+ result += 'godina';
+ }
+ return result;
+ }
+ }
+
+ return moment.lang('bs', {
+ months : "januar_februar_mart_april_maj_juni_juli_avgust_septembar_oktobar_novembar_decembar".split("_"),
+ monthsShort : "jan._feb._mar._apr._maj._jun._jul._avg._sep._okt._nov._dec.".split("_"),
+ weekdays : "nedjelja_ponedjeljak_utorak_srijeda_četvrtak_petak_subota".split("_"),
+ weekdaysShort : "ned._pon._uto._sri._čet._pet._sub.".split("_"),
+ weekdaysMin : "ne_po_ut_sr_če_pe_su".split("_"),
+ longDateFormat : {
+ LT : "H:mm",
+ L : "DD. MM. YYYY",
+ LL : "D. MMMM YYYY",
+ LLL : "D. MMMM YYYY LT",
+ LLLL : "dddd, D. MMMM YYYY LT"
+ },
+ calendar : {
+ sameDay : '[danas u] LT',
+ nextDay : '[sutra u] LT',
+
+ nextWeek : function () {
+ switch (this.day()) {
+ case 0:
+ return '[u] [nedjelju] [u] LT';
+ case 3:
+ return '[u] [srijedu] [u] LT';
+ case 6:
+ return '[u] [subotu] [u] LT';
+ case 1:
+ case 2:
+ case 4:
+ case 5:
+ return '[u] dddd [u] LT';
+ }
+ },
+ lastDay : '[jučer u] LT',
+ lastWeek : function () {
+ switch (this.day()) {
+ case 0:
+ case 3:
+ return '[prošlu] dddd [u] LT';
+ case 6:
+ return '[prošle] [subote] [u] LT';
+ case 1:
+ case 2:
+ case 4:
+ case 5:
+ return '[prošli] dddd [u] LT';
+ }
+ },
+ sameElse : 'L'
+ },
+ relativeTime : {
+ future : "za %s",
+ past : "prije %s",
+ s : "par sekundi",
+ m : translate,
+ mm : translate,
+ h : translate,
+ hh : translate,
+ d : "dan",
+ dd : translate,
+ M : "mjesec",
+ MM : translate,
+ y : "godinu",
+ yy : translate
+ },
+ ordinal : '%d.',
+ week : {
+ dow : 1, // Monday is the first day of the week.
+ doy : 7 // The week that contains Jan 1st is the first week of the year.
+ }
+ });
+}));
+// moment.js language configuration
+// language : catalan (ca)
+// author : Juan G. Hurtado : https://github.com/juanghurtado
+
+(function (factory) {
+ factory(moment);
+}(function (moment) {
+ return moment.lang('ca', {
+ months : "gener_febrer_març_abril_maig_juny_juliol_agost_setembre_octubre_novembre_desembre".split("_"),
+ monthsShort : "gen._febr._mar._abr._mai._jun._jul._ag._set._oct._nov._des.".split("_"),
+ weekdays : "diumenge_dilluns_dimarts_dimecres_dijous_divendres_dissabte".split("_"),
+ weekdaysShort : "dg._dl._dt._dc._dj._dv._ds.".split("_"),
+ weekdaysMin : "Dg_Dl_Dt_Dc_Dj_Dv_Ds".split("_"),
+ longDateFormat : {
+ LT : "H:mm",
+ L : "DD/MM/YYYY",
+ LL : "D MMMM YYYY",
+ LLL : "D MMMM YYYY LT",
+ LLLL : "dddd D MMMM YYYY LT"
+ },
+ calendar : {
+ sameDay : function () {
+ return '[avui a ' + ((this.hours() !== 1) ? 'les' : 'la') + '] LT';
+ },
+ nextDay : function () {
+ return '[demà a ' + ((this.hours() !== 1) ? 'les' : 'la') + '] LT';
+ },
+ nextWeek : function () {
+ return 'dddd [a ' + ((this.hours() !== 1) ? 'les' : 'la') + '] LT';
+ },
+ lastDay : function () {
+ return '[ahir a ' + ((this.hours() !== 1) ? 'les' : 'la') + '] LT';
+ },
+ lastWeek : function () {
+ return '[el] dddd [passat a ' + ((this.hours() !== 1) ? 'les' : 'la') + '] LT';
+ },
+ sameElse : 'L'
+ },
+ relativeTime : {
+ future : "en %s",
+ past : "fa %s",
+ s : "uns segons",
+ m : "un minut",
+ mm : "%d minuts",
+ h : "una hora",
+ hh : "%d hores",
+ d : "un dia",
+ dd : "%d dies",
+ M : "un mes",
+ MM : "%d mesos",
+ y : "un any",
+ yy : "%d anys"
+ },
+ ordinal : '%dº',
+ week : {
+ dow : 1, // Monday is the first day of the week.
+ doy : 4 // The week that contains Jan 4th is the first week of the year.
+ }
+ });
+}));
+// moment.js language configuration
+// language : czech (cs)
+// author : petrbela : https://github.com/petrbela
+
+(function (factory) {
+ factory(moment);
+}(function (moment) {
+ var months = "leden_únor_březen_duben_květen_červen_červenec_srpen_září_říjen_listopad_prosinec".split("_"),
+ monthsShort = "led_úno_bře_dub_kvě_čvn_čvc_srp_zář_říj_lis_pro".split("_");
+
+ function plural(n) {
+ return (n > 1) && (n < 5) && (~~(n / 10) !== 1);
+ }
+
+ function translate(number, withoutSuffix, key, isFuture) {
+ var result = number + " ";
+ switch (key) {
+ case 's': // a few seconds / in a few seconds / a few seconds ago
+ return (withoutSuffix || isFuture) ? 'pár sekund' : 'pár sekundami';
+ case 'm': // a minute / in a minute / a minute ago
+ return withoutSuffix ? 'minuta' : (isFuture ? 'minutu' : 'minutou');
+ case 'mm': // 9 minutes / in 9 minutes / 9 minutes ago
+ if (withoutSuffix || isFuture) {
+ return result + (plural(number) ? 'minuty' : 'minut');
+ } else {
+ return result + 'minutami';
+ }
+ break;
+ case 'h': // an hour / in an hour / an hour ago
+ return withoutSuffix ? 'hodina' : (isFuture ? 'hodinu' : 'hodinou');
+ case 'hh': // 9 hours / in 9 hours / 9 hours ago
+ if (withoutSuffix || isFuture) {
+ return result + (plural(number) ? 'hodiny' : 'hodin');
+ } else {
+ return result + 'hodinami';
+ }
+ break;
+ case 'd': // a day / in a day / a day ago
+ return (withoutSuffix || isFuture) ? 'den' : 'dnem';
+ case 'dd': // 9 days / in 9 days / 9 days ago
+ if (withoutSuffix || isFuture) {
+ return result + (plural(number) ? 'dny' : 'dní');
+ } else {
+ return result + 'dny';
+ }
+ break;
+ case 'M': // a month / in a month / a month ago
+ return (withoutSuffix || isFuture) ? 'měsíc' : 'měsícem';
+ case 'MM': // 9 months / in 9 months / 9 months ago
+ if (withoutSuffix || isFuture) {
+ return result + (plural(number) ? 'měsíce' : 'měsíců');
+ } else {
+ return result + 'měsíci';
+ }
+ break;
+ case 'y': // a year / in a year / a year ago
+ return (withoutSuffix || isFuture) ? 'rok' : 'rokem';
+ case 'yy': // 9 years / in 9 years / 9 years ago
+ if (withoutSuffix || isFuture) {
+ return result + (plural(number) ? 'roky' : 'let');
+ } else {
+ return result + 'lety';
+ }
+ break;
+ }
+ }
+
+ return moment.lang('cs', {
+ months : months,
+ monthsShort : monthsShort,
+ monthsParse : (function (months, monthsShort) {
+ var i, _monthsParse = [];
+ for (i = 0; i < 12; i++) {
+ // use custom parser to solve problem with July (červenec)
+ _monthsParse[i] = new RegExp('^' + months[i] + '$|^' + monthsShort[i] + '$', 'i');
+ }
+ return _monthsParse;
+ }(months, monthsShort)),
+ weekdays : "neděle_pondělí_úterý_středa_čtvrtek_pátek_sobota".split("_"),
+ weekdaysShort : "ne_po_út_st_čt_pá_so".split("_"),
+ weekdaysMin : "ne_po_út_st_čt_pá_so".split("_"),
+ longDateFormat : {
+ LT: "H.mm",
+ L : "DD. MM. YYYY",
+ LL : "D. MMMM YYYY",
+ LLL : "D. MMMM YYYY LT",
+ LLLL : "dddd D. MMMM YYYY LT"
+ },
+ calendar : {
+ sameDay: "[dnes v] LT",
+ nextDay: '[zítra v] LT',
+ nextWeek: function () {
+ switch (this.day()) {
+ case 0:
+ return '[v neděli v] LT';
+ case 1:
+ case 2:
+ return '[v] dddd [v] LT';
+ case 3:
+ return '[ve středu v] LT';
+ case 4:
+ return '[ve čtvrtek v] LT';
+ case 5:
+ return '[v pátek v] LT';
+ case 6:
+ return '[v sobotu v] LT';
+ }
+ },
+ lastDay: '[včera v] LT',
+ lastWeek: function () {
+ switch (this.day()) {
+ case 0:
+ return '[minulou neděli v] LT';
+ case 1:
+ case 2:
+ return '[minulé] dddd [v] LT';
+ case 3:
+ return '[minulou středu v] LT';
+ case 4:
+ case 5:
+ return '[minulý] dddd [v] LT';
+ case 6:
+ return '[minulou sobotu v] LT';
+ }
+ },
+ sameElse: "L"
+ },
+ relativeTime : {
+ future : "za %s",
+ past : "před %s",
+ s : translate,
+ m : translate,
+ mm : translate,
+ h : translate,
+ hh : translate,
+ d : translate,
+ dd : translate,
+ M : translate,
+ MM : translate,
+ y : translate,
+ yy : translate
+ },
+ ordinal : '%d.',
+ week : {
+ dow : 1, // Monday is the first day of the week.
+ doy : 4 // The week that contains Jan 4th is the first week of the year.
+ }
+ });
+}));
+// moment.js language configuration
+// language : chuvash (cv)
+// author : Anatoly Mironov : https://github.com/mirontoli
+
+(function (factory) {
+ factory(moment);
+}(function (moment) {
+ return moment.lang('cv', {
+ months : "кăрлач_нарăс_пуш_ака_май_çĕртме_утă_çурла_авăн_юпа_чӳк_раштав".split("_"),
+ monthsShort : "кăр_нар_пуш_ака_май_çĕр_утă_çур_ав_юпа_чӳк_раш".split("_"),
+ weekdays : "вырсарникун_тунтикун_ытларикун_юнкун_кĕçнерникун_эрнекун_шăматкун".split("_"),
+ weekdaysShort : "выр_тун_ытл_юн_кĕç_эрн_шăм".split("_"),
+ weekdaysMin : "вр_тн_ыт_юн_кç_эр_шм".split("_"),
+ longDateFormat : {
+ LT : "HH:mm",
+ L : "DD-MM-YYYY",
+ LL : "YYYY [çулхи] MMMM [уйăхĕн] D[-мĕшĕ]",
+ LLL : "YYYY [çулхи] MMMM [уйăхĕн] D[-мĕшĕ], LT",
+ LLLL : "dddd, YYYY [çулхи] MMMM [уйăхĕн] D[-мĕшĕ], LT"
+ },
+ calendar : {
+ sameDay: '[Паян] LT [сехетре]',
+ nextDay: '[Ыран] LT [сехетре]',
+ lastDay: '[Ĕнер] LT [сехетре]',
+ nextWeek: '[Çитес] dddd LT [сехетре]',
+ lastWeek: '[Иртнĕ] dddd LT [сехетре]',
+ sameElse: 'L'
+ },
+ relativeTime : {
+ future : function (output) {
+ var affix = /сехет$/i.exec(output) ? "рен" : /çул$/i.exec(output) ? "тан" : "ран";
+ return output + affix;
+ },
+ past : "%s каялла",
+ s : "пĕр-ик çеккунт",
+ m : "пĕр минут",
+ mm : "%d минут",
+ h : "пĕр сехет",
+ hh : "%d сехет",
+ d : "пĕр кун",
+ dd : "%d кун",
+ M : "пĕр уйăх",
+ MM : "%d уйăх",
+ y : "пĕр çул",
+ yy : "%d çул"
+ },
+ ordinal : '%d-мĕш',
+ week : {
+ dow : 1, // Monday is the first day of the week.
+ doy : 7 // The week that contains Jan 1st is the first week of the year.
+ }
+ });
+}));
+// moment.js language configuration
+// language : Welsh (cy)
+// author : Robert Allen
+
+(function (factory) {
+ factory(moment);
+}(function (moment) {
+ return moment.lang("cy", {
+ months: "Ionawr_Chwefror_Mawrth_Ebrill_Mai_Mehefin_Gorffennaf_Awst_Medi_Hydref_Tachwedd_Rhagfyr".split("_"),
+ monthsShort: "Ion_Chwe_Maw_Ebr_Mai_Meh_Gor_Aws_Med_Hyd_Tach_Rhag".split("_"),
+ weekdays: "Dydd Sul_Dydd Llun_Dydd Mawrth_Dydd Mercher_Dydd Iau_Dydd Gwener_Dydd Sadwrn".split("_"),
+ weekdaysShort: "Sul_Llun_Maw_Mer_Iau_Gwe_Sad".split("_"),
+ weekdaysMin: "Su_Ll_Ma_Me_Ia_Gw_Sa".split("_"),
+ // time formats are the same as en-gb
+ longDateFormat: {
+ LT: "HH:mm",
+ L: "DD/MM/YYYY",
+ LL: "D MMMM YYYY",
+ LLL: "D MMMM YYYY LT",
+ LLLL: "dddd, D MMMM YYYY LT"
+ },
+ calendar: {
+ sameDay: '[Heddiw am] LT',
+ nextDay: '[Yfory am] LT',
+ nextWeek: 'dddd [am] LT',
+ lastDay: '[Ddoe am] LT',
+ lastWeek: 'dddd [diwethaf am] LT',
+ sameElse: 'L'
+ },
+ relativeTime: {
+ future: "mewn %s",
+ past: "%s yn ôl",
+ s: "ychydig eiliadau",
+ m: "munud",
+ mm: "%d munud",
+ h: "awr",
+ hh: "%d awr",
+ d: "diwrnod",
+ dd: "%d diwrnod",
+ M: "mis",
+ MM: "%d mis",
+ y: "blwyddyn",
+ yy: "%d flynedd"
+ },
+ // traditional ordinal numbers above 31 are not commonly used in colloquial Welsh
+ ordinal: function (number) {
+ var b = number,
+ output = '',
+ lookup = [
+ '', 'af', 'il', 'ydd', 'ydd', 'ed', 'ed', 'ed', 'fed', 'fed', 'fed', // 1af to 10fed
+ 'eg', 'fed', 'eg', 'eg', 'fed', 'eg', 'eg', 'fed', 'eg', 'fed' // 11eg to 20fed
+ ];
+
+ if (b > 20) {
+ if (b === 40 || b === 50 || b === 60 || b === 80 || b === 100) {
+ output = 'fed'; // not 30ain, 70ain or 90ain
+ } else {
+ output = 'ain';
+ }
+ } else if (b > 0) {
+ output = lookup[b];
+ }
+
+ return number + output;
+ },
+ week : {
+ dow : 1, // Monday is the first day of the week.
+ doy : 4 // The week that contains Jan 4th is the first week of the year.
+ }
+ });
+}));
+// moment.js language configuration
+// language : danish (da)
+// author : Ulrik Nielsen : https://github.com/mrbase
+
+(function (factory) {
+ factory(moment);
+}(function (moment) {
+ return moment.lang('da', {
+ months : "januar_februar_marts_april_maj_juni_juli_august_september_oktober_november_december".split("_"),
+ monthsShort : "jan_feb_mar_apr_maj_jun_jul_aug_sep_okt_nov_dec".split("_"),
+ weekdays : "søndag_mandag_tirsdag_onsdag_torsdag_fredag_lørdag".split("_"),
+ weekdaysShort : "søn_man_tir_ons_tor_fre_lør".split("_"),
+ weekdaysMin : "sø_ma_ti_on_to_fr_lø".split("_"),
+ longDateFormat : {
+ LT : "HH:mm",
+ L : "DD/MM/YYYY",
+ LL : "D. MMMM YYYY",
+ LLL : "D. MMMM YYYY LT",
+ LLLL : "dddd [d.] D. MMMM YYYY LT"
+ },
+ calendar : {
+ sameDay : '[I dag kl.] LT',
+ nextDay : '[I morgen kl.] LT',
+ nextWeek : 'dddd [kl.] LT',
+ lastDay : '[I går kl.] LT',
+ lastWeek : '[sidste] dddd [kl] LT',
+ sameElse : 'L'
+ },
+ relativeTime : {
+ future : "om %s",
+ past : "%s siden",
+ s : "få sekunder",
+ m : "et minut",
+ mm : "%d minutter",
+ h : "en time",
+ hh : "%d timer",
+ d : "en dag",
+ dd : "%d dage",
+ M : "en måned",
+ MM : "%d måneder",
+ y : "et år",
+ yy : "%d år"
+ },
+ ordinal : '%d.',
+ week : {
+ dow : 1, // Monday is the first day of the week.
+ doy : 4 // The week that contains Jan 4th is the first week of the year.
+ }
+ });
+}));
+// moment.js language configuration
+// language : austrian german (de-at)
+// author : lluchs : https://github.com/lluchs
+// author: Menelion Elensúle: https://github.com/Oire
+// author : Martin Groller : https://github.com/MadMG
+
+(function (factory) {
+ factory(moment);
+}(function (moment) {
+ function processRelativeTime(number, withoutSuffix, key, isFuture) {
+ var format = {
+ 'm': ['eine Minute', 'einer Minute'],
+ 'h': ['eine Stunde', 'einer Stunde'],
+ 'd': ['ein Tag', 'einem Tag'],
+ 'dd': [number + ' Tage', number + ' Tagen'],
+ 'M': ['ein Monat', 'einem Monat'],
+ 'MM': [number + ' Monate', number + ' Monaten'],
+ 'y': ['ein Jahr', 'einem Jahr'],
+ 'yy': [number + ' Jahre', number + ' Jahren']
+ };
+ return withoutSuffix ? format[key][0] : format[key][1];
+ }
+
+ return moment.lang('de-at', {
+ months : "Jänner_Februar_März_April_Mai_Juni_Juli_August_September_Oktober_November_Dezember".split("_"),
+ monthsShort : "Jän._Febr._Mrz._Apr._Mai_Jun._Jul._Aug._Sept._Okt._Nov._Dez.".split("_"),
+ weekdays : "Sonntag_Montag_Dienstag_Mittwoch_Donnerstag_Freitag_Samstag".split("_"),
+ weekdaysShort : "So._Mo._Di._Mi._Do._Fr._Sa.".split("_"),
+ weekdaysMin : "So_Mo_Di_Mi_Do_Fr_Sa".split("_"),
+ longDateFormat : {
+ LT: "HH:mm [Uhr]",
+ L : "DD.MM.YYYY",
+ LL : "D. MMMM YYYY",
+ LLL : "D. MMMM YYYY LT",
+ LLLL : "dddd, D. MMMM YYYY LT"
+ },
+ calendar : {
+ sameDay: "[Heute um] LT",
+ sameElse: "L",
+ nextDay: '[Morgen um] LT',
+ nextWeek: 'dddd [um] LT',
+ lastDay: '[Gestern um] LT',
+ lastWeek: '[letzten] dddd [um] LT'
+ },
+ relativeTime : {
+ future : "in %s",
+ past : "vor %s",
+ s : "ein paar Sekunden",
+ m : processRelativeTime,
+ mm : "%d Minuten",
+ h : processRelativeTime,
+ hh : "%d Stunden",
+ d : processRelativeTime,
+ dd : processRelativeTime,
+ M : processRelativeTime,
+ MM : processRelativeTime,
+ y : processRelativeTime,
+ yy : processRelativeTime
+ },
+ ordinal : '%d.',
+ week : {
+ dow : 1, // Monday is the first day of the week.
+ doy : 4 // The week that contains Jan 4th is the first week of the year.
+ }
+ });
+}));
+// moment.js language configuration
+// language : german (de)
+// author : lluchs : https://github.com/lluchs
+// author: Menelion Elensúle: https://github.com/Oire
+
+(function (factory) {
+ factory(moment);
+}(function (moment) {
+ function processRelativeTime(number, withoutSuffix, key, isFuture) {
+ var format = {
+ 'm': ['eine Minute', 'einer Minute'],
+ 'h': ['eine Stunde', 'einer Stunde'],
+ 'd': ['ein Tag', 'einem Tag'],
+ 'dd': [number + ' Tage', number + ' Tagen'],
+ 'M': ['ein Monat', 'einem Monat'],
+ 'MM': [number + ' Monate', number + ' Monaten'],
+ 'y': ['ein Jahr', 'einem Jahr'],
+ 'yy': [number + ' Jahre', number + ' Jahren']
+ };
+ return withoutSuffix ? format[key][0] : format[key][1];
+ }
+
+ return moment.lang('de', {
+ months : "Januar_Februar_März_April_Mai_Juni_Juli_August_September_Oktober_November_Dezember".split("_"),
+ monthsShort : "Jan._Febr._Mrz._Apr._Mai_Jun._Jul._Aug._Sept._Okt._Nov._Dez.".split("_"),
+ weekdays : "Sonntag_Montag_Dienstag_Mittwoch_Donnerstag_Freitag_Samstag".split("_"),
+ weekdaysShort : "So._Mo._Di._Mi._Do._Fr._Sa.".split("_"),
+ weekdaysMin : "So_Mo_Di_Mi_Do_Fr_Sa".split("_"),
+ longDateFormat : {
+ LT: "HH:mm [Uhr]",
+ L : "DD.MM.YYYY",
+ LL : "D. MMMM YYYY",
+ LLL : "D. MMMM YYYY LT",
+ LLLL : "dddd, D. MMMM YYYY LT"
+ },
+ calendar : {
+ sameDay: "[Heute um] LT",
+ sameElse: "L",
+ nextDay: '[Morgen um] LT',
+ nextWeek: 'dddd [um] LT',
+ lastDay: '[Gestern um] LT',
+ lastWeek: '[letzten] dddd [um] LT'
+ },
+ relativeTime : {
+ future : "in %s",
+ past : "vor %s",
+ s : "ein paar Sekunden",
+ m : processRelativeTime,
+ mm : "%d Minuten",
+ h : processRelativeTime,
+ hh : "%d Stunden",
+ d : processRelativeTime,
+ dd : processRelativeTime,
+ M : processRelativeTime,
+ MM : processRelativeTime,
+ y : processRelativeTime,
+ yy : processRelativeTime
+ },
+ ordinal : '%d.',
+ week : {
+ dow : 1, // Monday is the first day of the week.
+ doy : 4 // The week that contains Jan 4th is the first week of the year.
+ }
+ });
+}));
+// moment.js language configuration
+// language : modern greek (el)
+// author : Aggelos Karalias : https://github.com/mehiel
+
+(function (factory) {
+ factory(moment);
+}(function (moment) {
+ return moment.lang('el', {
+ monthsNominativeEl : "Ιανουάριος_Φεβρουάριος_Μάρτιος_Απρίλιος_Μάιος_Ιούνιος_Ιούλιος_Αύγουστος_Σεπτέμβριος_Οκτώβριος_Νοέμβριος_Δεκέμβριος".split("_"),
+ monthsGenitiveEl : "Ιανουαρίου_Φεβρουαρίου_Μαρτίου_Απριλίου_Μαΐου_Ιουνίου_Ιουλίου_Αυγούστου_Σεπτεμβρίου_Οκτωβρίου_Νοεμβρίου_Δεκεμβρίου".split("_"),
+ months : function (momentToFormat, format) {
+ if (/D/.test(format.substring(0, format.indexOf("MMMM")))) { // if there is a day number before 'MMMM'
+ return this._monthsGenitiveEl[momentToFormat.month()];
+ } else {
+ return this._monthsNominativeEl[momentToFormat.month()];
+ }
+ },
+ monthsShort : "Ιαν_Φεβ_Μαρ_Απρ_Μαϊ_Ιουν_Ιουλ_Αυγ_Σεπ_Οκτ_Νοε_Δεκ".split("_"),
+ weekdays : "Κυριακή_Δευτέρα_Τρίτη_Τετάρτη_Πέμπτη_Παρασκευή_Σάββατο".split("_"),
+ weekdaysShort : "Κυρ_Δευ_Τρι_Τετ_Πεμ_Παρ_Σαβ".split("_"),
+ weekdaysMin : "Κυ_Δε_Τρ_Τε_Πε_Πα_Σα".split("_"),
+ meridiem : function (hours, minutes, isLower) {
+ if (hours > 11) {
+ return isLower ? 'μμ' : 'ΜΜ';
+ } else {
+ return isLower ? 'πμ' : 'ΠΜ';
+ }
+ },
+ longDateFormat : {
+ LT : "h:mm A",
+ L : "DD/MM/YYYY",
+ LL : "D MMMM YYYY",
+ LLL : "D MMMM YYYY LT",
+ LLLL : "dddd, D MMMM YYYY LT"
+ },
+ calendarEl : {
+ sameDay : '[Σήμερα {}] LT',
+ nextDay : '[Αύριο {}] LT',
+ nextWeek : 'dddd [{}] LT',
+ lastDay : '[Χθες {}] LT',
+ lastWeek : function() {
+ switch (this.day()) {
+ case 6:
+ return '[το προηγούμενο] dddd [{}] LT';
+ default:
+ return '[την προηγούμενη] dddd [{}] LT';
+ }
+ },
+ sameElse : 'L'
+ },
+ calendar : function (key, mom) {
+ var output = this._calendarEl[key],
+ hours = mom && mom.hours();
+
+ if (typeof output === 'function') {
+ output = output.apply(mom);
+ }
+
+ return output.replace("{}", (hours % 12 === 1 ? "στη" : "στις"));
+ },
+ relativeTime : {
+ future : "σε %s",
+ past : "%s πριν",
+ s : "δευτερόλεπτα",
+ m : "ένα λεπτό",
+ mm : "%d λεπτά",
+ h : "μία ώρα",
+ hh : "%d ώρες",
+ d : "μία μέρα",
+ dd : "%d μέρες",
+ M : "ένας μήνας",
+ MM : "%d μήνες",
+ y : "ένας χρόνος",
+ yy : "%d χρόνια"
+ },
+ ordinal : function (number) {
+ return number + 'η';
+ },
+ week : {
+ dow : 1, // Monday is the first day of the week.
+ doy : 4 // The week that contains Jan 4st is the first week of the year.
+ }
+ });
+}));
+// moment.js language configuration
+// language : australian english (en-au)
+
+(function (factory) {
+ factory(moment);
+}(function (moment) {
+ return moment.lang('en-au', {
+ months : "January_February_March_April_May_June_July_August_September_October_November_December".split("_"),
+ monthsShort : "Jan_Feb_Mar_Apr_May_Jun_Jul_Aug_Sep_Oct_Nov_Dec".split("_"),
+ weekdays : "Sunday_Monday_Tuesday_Wednesday_Thursday_Friday_Saturday".split("_"),
+ weekdaysShort : "Sun_Mon_Tue_Wed_Thu_Fri_Sat".split("_"),
+ weekdaysMin : "Su_Mo_Tu_We_Th_Fr_Sa".split("_"),
+ longDateFormat : {
+ LT : "h:mm A",
+ L : "DD/MM/YYYY",
+ LL : "D MMMM YYYY",
+ LLL : "D MMMM YYYY LT",
+ LLLL : "dddd, D MMMM YYYY LT"
+ },
+ calendar : {
+ sameDay : '[Today at] LT',
+ nextDay : '[Tomorrow at] LT',
+ nextWeek : 'dddd [at] LT',
+ lastDay : '[Yesterday at] LT',
+ lastWeek : '[Last] dddd [at] LT',
+ sameElse : 'L'
+ },
+ relativeTime : {
+ future : "in %s",
+ past : "%s ago",
+ s : "a few seconds",
+ m : "a minute",
+ mm : "%d minutes",
+ h : "an hour",
+ hh : "%d hours",
+ d : "a day",
+ dd : "%d days",
+ M : "a month",
+ MM : "%d months",
+ y : "a year",
+ yy : "%d years"
+ },
+ ordinal : function (number) {
+ var b = number % 10,
+ output = (~~ (number % 100 / 10) === 1) ? 'th' :
+ (b === 1) ? 'st' :
+ (b === 2) ? 'nd' :
+ (b === 3) ? 'rd' : 'th';
+ return number + output;
+ },
+ week : {
+ dow : 1, // Monday is the first day of the week.
+ doy : 4 // The week that contains Jan 4th is the first week of the year.
+ }
+ });
+}));
+// moment.js language configuration
+// language : canadian english (en-ca)
+// author : Jonathan Abourbih : https://github.com/jonbca
+
+(function (factory) {
+ factory(moment);
+}(function (moment) {
+ return moment.lang('en-ca', {
+ months : "January_February_March_April_May_June_July_August_September_October_November_December".split("_"),
+ monthsShort : "Jan_Feb_Mar_Apr_May_Jun_Jul_Aug_Sep_Oct_Nov_Dec".split("_"),
+ weekdays : "Sunday_Monday_Tuesday_Wednesday_Thursday_Friday_Saturday".split("_"),
+ weekdaysShort : "Sun_Mon_Tue_Wed_Thu_Fri_Sat".split("_"),
+ weekdaysMin : "Su_Mo_Tu_We_Th_Fr_Sa".split("_"),
+ longDateFormat : {
+ LT : "h:mm A",
+ L : "YYYY-MM-DD",
+ LL : "D MMMM, YYYY",
+ LLL : "D MMMM, YYYY LT",
+ LLLL : "dddd, D MMMM, YYYY LT"
+ },
+ calendar : {
+ sameDay : '[Today at] LT',
+ nextDay : '[Tomorrow at] LT',
+ nextWeek : 'dddd [at] LT',
+ lastDay : '[Yesterday at] LT',
+ lastWeek : '[Last] dddd [at] LT',
+ sameElse : 'L'
+ },
+ relativeTime : {
+ future : "in %s",
+ past : "%s ago",
+ s : "a few seconds",
+ m : "a minute",
+ mm : "%d minutes",
+ h : "an hour",
+ hh : "%d hours",
+ d : "a day",
+ dd : "%d days",
+ M : "a month",
+ MM : "%d months",
+ y : "a year",
+ yy : "%d years"
+ },
+ ordinal : function (number) {
+ var b = number % 10,
+ output = (~~ (number % 100 / 10) === 1) ? 'th' :
+ (b === 1) ? 'st' :
+ (b === 2) ? 'nd' :
+ (b === 3) ? 'rd' : 'th';
+ return number + output;
+ }
+ });
+}));
+// moment.js language configuration
+// language : great britain english (en-gb)
+// author : Chris Gedrim : https://github.com/chrisgedrim
+
+(function (factory) {
+ factory(moment);
+}(function (moment) {
+ return moment.lang('en-gb', {
+ months : "January_February_March_April_May_June_July_August_September_October_November_December".split("_"),
+ monthsShort : "Jan_Feb_Mar_Apr_May_Jun_Jul_Aug_Sep_Oct_Nov_Dec".split("_"),
+ weekdays : "Sunday_Monday_Tuesday_Wednesday_Thursday_Friday_Saturday".split("_"),
+ weekdaysShort : "Sun_Mon_Tue_Wed_Thu_Fri_Sat".split("_"),
+ weekdaysMin : "Su_Mo_Tu_We_Th_Fr_Sa".split("_"),
+ longDateFormat : {
+ LT : "HH:mm",
+ L : "DD/MM/YYYY",
+ LL : "D MMMM YYYY",
+ LLL : "D MMMM YYYY LT",
+ LLLL : "dddd, D MMMM YYYY LT"
+ },
+ calendar : {
+ sameDay : '[Today at] LT',
+ nextDay : '[Tomorrow at] LT',
+ nextWeek : 'dddd [at] LT',
+ lastDay : '[Yesterday at] LT',
+ lastWeek : '[Last] dddd [at] LT',
+ sameElse : 'L'
+ },
+ relativeTime : {
+ future : "in %s",
+ past : "%s ago",
+ s : "a few seconds",
+ m : "a minute",
+ mm : "%d minutes",
+ h : "an hour",
+ hh : "%d hours",
+ d : "a day",
+ dd : "%d days",
+ M : "a month",
+ MM : "%d months",
+ y : "a year",
+ yy : "%d years"
+ },
+ ordinal : function (number) {
+ var b = number % 10,
+ output = (~~ (number % 100 / 10) === 1) ? 'th' :
+ (b === 1) ? 'st' :
+ (b === 2) ? 'nd' :
+ (b === 3) ? 'rd' : 'th';
+ return number + output;
+ },
+ week : {
+ dow : 1, // Monday is the first day of the week.
+ doy : 4 // The week that contains Jan 4th is the first week of the year.
+ }
+ });
+}));
+// moment.js language configuration
+// language : esperanto (eo)
+// author : Colin Dean : https://github.com/colindean
+// komento: Mi estas malcerta se mi korekte traktis akuzativojn en tiu traduko.
+// Se ne, bonvolu korekti kaj avizi min por ke mi povas lerni!
+
+(function (factory) {
+ factory(moment);
+}(function (moment) {
+ return moment.lang('eo', {
+ months : "januaro_februaro_marto_aprilo_majo_junio_julio_aŭgusto_septembro_oktobro_novembro_decembro".split("_"),
+ monthsShort : "jan_feb_mar_apr_maj_jun_jul_aŭg_sep_okt_nov_dec".split("_"),
+ weekdays : "Dimanĉo_Lundo_Mardo_Merkredo_Ĵaŭdo_Vendredo_Sabato".split("_"),
+ weekdaysShort : "Dim_Lun_Mard_Merk_Ĵaŭ_Ven_Sab".split("_"),
+ weekdaysMin : "Di_Lu_Ma_Me_Ĵa_Ve_Sa".split("_"),
+ longDateFormat : {
+ LT : "HH:mm",
+ L : "YYYY-MM-DD",
+ LL : "D[-an de] MMMM, YYYY",
+ LLL : "D[-an de] MMMM, YYYY LT",
+ LLLL : "dddd, [la] D[-an de] MMMM, YYYY LT"
+ },
+ meridiem : function (hours, minutes, isLower) {
+ if (hours > 11) {
+ return isLower ? 'p.t.m.' : 'P.T.M.';
+ } else {
+ return isLower ? 'a.t.m.' : 'A.T.M.';
+ }
+ },
+ calendar : {
+ sameDay : '[Hodiaŭ je] LT',
+ nextDay : '[Morgaŭ je] LT',
+ nextWeek : 'dddd [je] LT',
+ lastDay : '[Hieraŭ je] LT',
+ lastWeek : '[pasinta] dddd [je] LT',
+ sameElse : 'L'
+ },
+ relativeTime : {
+ future : "je %s",
+ past : "antaŭ %s",
+ s : "sekundoj",
+ m : "minuto",
+ mm : "%d minutoj",
+ h : "horo",
+ hh : "%d horoj",
+ d : "tago",//ne 'diurno', ĉar estas uzita por proksimumo
+ dd : "%d tagoj",
+ M : "monato",
+ MM : "%d monatoj",
+ y : "jaro",
+ yy : "%d jaroj"
+ },
+ ordinal : "%da",
+ week : {
+ dow : 1, // Monday is the first day of the week.
+ doy : 7 // The week that contains Jan 1st is the first week of the year.
+ }
+ });
+}));
+// moment.js language configuration
+// language : spanish (es)
+// author : Julio Napurí : https://github.com/julionc
+
+(function (factory) {
+ factory(moment);
+}(function (moment) {
+ var monthsShortDot = "ene._feb._mar._abr._may._jun._jul._ago._sep._oct._nov._dic.".split("_"),
+ monthsShort = "ene_feb_mar_abr_may_jun_jul_ago_sep_oct_nov_dic".split("_");
+
+ return moment.lang('es', {
+ months : "enero_febrero_marzo_abril_mayo_junio_julio_agosto_septiembre_octubre_noviembre_diciembre".split("_"),
+ monthsShort : function (m, format) {
+ if (/-MMM-/.test(format)) {
+ return monthsShort[m.month()];
+ } else {
+ return monthsShortDot[m.month()];
+ }
+ },
+ weekdays : "domingo_lunes_martes_miércoles_jueves_viernes_sábado".split("_"),
+ weekdaysShort : "dom._lun._mar._mié._jue._vie._sáb.".split("_"),
+ weekdaysMin : "Do_Lu_Ma_Mi_Ju_Vi_Sá".split("_"),
+ longDateFormat : {
+ LT : "H:mm",
+ L : "DD/MM/YYYY",
+ LL : "D [de] MMMM [del] YYYY",
+ LLL : "D [de] MMMM [del] YYYY LT",
+ LLLL : "dddd, D [de] MMMM [del] YYYY LT"
+ },
+ calendar : {
+ sameDay : function () {
+ return '[hoy a la' + ((this.hours() !== 1) ? 's' : '') + '] LT';
+ },
+ nextDay : function () {
+ return '[mañana a la' + ((this.hours() !== 1) ? 's' : '') + '] LT';
+ },
+ nextWeek : function () {
+ return 'dddd [a la' + ((this.hours() !== 1) ? 's' : '') + '] LT';
+ },
+ lastDay : function () {
+ return '[ayer a la' + ((this.hours() !== 1) ? 's' : '') + '] LT';
+ },
+ lastWeek : function () {
+ return '[el] dddd [pasado a la' + ((this.hours() !== 1) ? 's' : '') + '] LT';
+ },
+ sameElse : 'L'
+ },
+ relativeTime : {
+ future : "en %s",
+ past : "hace %s",
+ s : "unos segundos",
+ m : "un minuto",
+ mm : "%d minutos",
+ h : "una hora",
+ hh : "%d horas",
+ d : "un día",
+ dd : "%d días",
+ M : "un mes",
+ MM : "%d meses",
+ y : "un año",
+ yy : "%d años"
+ },
+ ordinal : '%dº',
+ week : {
+ dow : 1, // Monday is the first day of the week.
+ doy : 4 // The week that contains Jan 4th is the first week of the year.
+ }
+ });
+}));
+// moment.js language configuration
+// language : estonian (et)
+// author : Henry Kehlmann : https://github.com/madhenry
+// improvements : Illimar Tambek : https://github.com/ragulka
+
+(function (factory) {
+ factory(moment);
+}(function (moment) {
+ function processRelativeTime(number, withoutSuffix, key, isFuture) {
+ var format = {
+ 's' : ['mõne sekundi', 'mõni sekund', 'paar sekundit'],
+ 'm' : ['ühe minuti', 'üks minut'],
+ 'mm': [number + ' minuti', number + ' minutit'],
+ 'h' : ['ühe tunni', 'tund aega', 'üks tund'],
+ 'hh': [number + ' tunni', number + ' tundi'],
+ 'd' : ['ühe päeva', 'üks päev'],
+ 'M' : ['kuu aja', 'kuu aega', 'üks kuu'],
+ 'MM': [number + ' kuu', number + ' kuud'],
+ 'y' : ['ühe aasta', 'aasta', 'üks aasta'],
+ 'yy': [number + ' aasta', number + ' aastat']
+ };
+ if (withoutSuffix) {
+ return format[key][2] ? format[key][2] : format[key][1];
+ }
+ return isFuture ? format[key][0] : format[key][1];
+ }
+
+ return moment.lang('et', {
+ months : "jaanuar_veebruar_märts_aprill_mai_juuni_juuli_august_september_oktoober_november_detsember".split("_"),
+ monthsShort : "jaan_veebr_märts_apr_mai_juuni_juuli_aug_sept_okt_nov_dets".split("_"),
+ weekdays : "pühapäev_esmaspäev_teisipäev_kolmapäev_neljapäev_reede_laupäev".split("_"),
+ weekdaysShort : "P_E_T_K_N_R_L".split("_"),
+ weekdaysMin : "P_E_T_K_N_R_L".split("_"),
+ longDateFormat : {
+ LT : "H:mm",
+ L : "DD.MM.YYYY",
+ LL : "D. MMMM YYYY",
+ LLL : "D. MMMM YYYY LT",
+ LLLL : "dddd, D. MMMM YYYY LT"
+ },
+ calendar : {
+ sameDay : '[Täna,] LT',
+ nextDay : '[Homme,] LT',
+ nextWeek : '[Järgmine] dddd LT',
+ lastDay : '[Eile,] LT',
+ lastWeek : '[Eelmine] dddd LT',
+ sameElse : 'L'
+ },
+ relativeTime : {
+ future : "%s pärast",
+ past : "%s tagasi",
+ s : processRelativeTime,
+ m : processRelativeTime,
+ mm : processRelativeTime,
+ h : processRelativeTime,
+ hh : processRelativeTime,
+ d : processRelativeTime,
+ dd : '%d päeva',
+ M : processRelativeTime,
+ MM : processRelativeTime,
+ y : processRelativeTime,
+ yy : processRelativeTime
+ },
+ ordinal : '%d.',
+ week : {
+ dow : 1, // Monday is the first day of the week.
+ doy : 4 // The week that contains Jan 4th is the first week of the year.
+ }
+ });
+}));
+// moment.js language configuration
+// language : euskara (eu)
+// author : Eneko Illarramendi : https://github.com/eillarra
+
+(function (factory) {
+ factory(moment);
+}(function (moment) {
+ return moment.lang('eu', {
+ months : "urtarrila_otsaila_martxoa_apirila_maiatza_ekaina_uztaila_abuztua_iraila_urria_azaroa_abendua".split("_"),
+ monthsShort : "urt._ots._mar._api._mai._eka._uzt._abu._ira._urr._aza._abe.".split("_"),
+ weekdays : "igandea_astelehena_asteartea_asteazkena_osteguna_ostirala_larunbata".split("_"),
+ weekdaysShort : "ig._al._ar._az._og._ol._lr.".split("_"),
+ weekdaysMin : "ig_al_ar_az_og_ol_lr".split("_"),
+ longDateFormat : {
+ LT : "HH:mm",
+ L : "YYYY-MM-DD",
+ LL : "YYYY[ko] MMMM[ren] D[a]",
+ LLL : "YYYY[ko] MMMM[ren] D[a] LT",
+ LLLL : "dddd, YYYY[ko] MMMM[ren] D[a] LT",
+ l : "YYYY-M-D",
+ ll : "YYYY[ko] MMM D[a]",
+ lll : "YYYY[ko] MMM D[a] LT",
+ llll : "ddd, YYYY[ko] MMM D[a] LT"
+ },
+ calendar : {
+ sameDay : '[gaur] LT[etan]',
+ nextDay : '[bihar] LT[etan]',
+ nextWeek : 'dddd LT[etan]',
+ lastDay : '[atzo] LT[etan]',
+ lastWeek : '[aurreko] dddd LT[etan]',
+ sameElse : 'L'
+ },
+ relativeTime : {
+ future : "%s barru",
+ past : "duela %s",
+ s : "segundo batzuk",
+ m : "minutu bat",
+ mm : "%d minutu",
+ h : "ordu bat",
+ hh : "%d ordu",
+ d : "egun bat",
+ dd : "%d egun",
+ M : "hilabete bat",
+ MM : "%d hilabete",
+ y : "urte bat",
+ yy : "%d urte"
+ },
+ ordinal : '%d.',
+ week : {
+ dow : 1, // Monday is the first day of the week.
+ doy : 7 // The week that contains Jan 1st is the first week of the year.
+ }
+ });
+}));
+// moment.js language configuration
+// language : Persian Language
+// author : Ebrahim Byagowi : https://github.com/ebraminio
+
+(function (factory) {
+ factory(moment);
+}(function (moment) {
+ var symbolMap = {
+ '1': '۱',
+ '2': '۲',
+ '3': '۳',
+ '4': '۴',
+ '5': '۵',
+ '6': '۶',
+ '7': '۷',
+ '8': '۸',
+ '9': '۹',
+ '0': '۰'
+ }, numberMap = {
+ '۱': '1',
+ '۲': '2',
+ '۳': '3',
+ '۴': '4',
+ '۵': '5',
+ '۶': '6',
+ '۷': '7',
+ '۸': '8',
+ '۹': '9',
+ '۰': '0'
+ };
+
+ return moment.lang('fa', {
+ months : 'ژانویه_فوریه_مارس_آوریل_مه_ژوئن_ژوئیه_اوت_سپتامبر_اکتبر_نوامبر_دسامبر'.split('_'),
+ monthsShort : 'ژانویه_فوریه_مارس_آوریل_مه_ژوئن_ژوئیه_اوت_سپتامبر_اکتبر_نوامبر_دسامبر'.split('_'),
+ weekdays : 'یک\u200cشنبه_دوشنبه_سه\u200cشنبه_چهارشنبه_پنج\u200cشنبه_جمعه_شنبه'.split('_'),
+ weekdaysShort : 'یک\u200cشنبه_دوشنبه_سه\u200cشنبه_چهارشنبه_پنج\u200cشنبه_جمعه_شنبه'.split('_'),
+ weekdaysMin : 'ی_د_س_چ_پ_ج_ش'.split('_'),
+ longDateFormat : {
+ LT : 'HH:mm',
+ L : 'DD/MM/YYYY',
+ LL : 'D MMMM YYYY',
+ LLL : 'D MMMM YYYY LT',
+ LLLL : 'dddd, D MMMM YYYY LT'
+ },
+ meridiem : function (hour, minute, isLower) {
+ if (hour < 12) {
+ return "قبل از ظهر";
+ } else {
+ return "بعد از ظهر";
+ }
+ },
+ calendar : {
+ sameDay : '[امروز ساعت] LT',
+ nextDay : '[فردا ساعت] LT',
+ nextWeek : 'dddd [ساعت] LT',
+ lastDay : '[دیروز ساعت] LT',
+ lastWeek : 'dddd [پیش] [ساعت] LT',
+ sameElse : 'L'
+ },
+ relativeTime : {
+ future : 'در %s',
+ past : '%s پیش',
+ s : 'چندین ثانیه',
+ m : 'یک دقیقه',
+ mm : '%d دقیقه',
+ h : 'یک ساعت',
+ hh : '%d ساعت',
+ d : 'یک روز',
+ dd : '%d روز',
+ M : 'یک ماه',
+ MM : '%d ماه',
+ y : 'یک سال',
+ yy : '%d سال'
+ },
+ preparse: function (string) {
+ return string.replace(/[۰-۹]/g, function (match) {
+ return numberMap[match];
+ }).replace(/،/g, ',');
+ },
+ postformat: function (string) {
+ return string.replace(/\d/g, function (match) {
+ return symbolMap[match];
+ }).replace(/,/g, '،');
+ },
+ ordinal : '%dم',
+ week : {
+ dow : 6, // Saturday is the first day of the week.
+ doy : 12 // The week that contains Jan 1st is the first week of the year.
+ }
+ });
+}));
+// moment.js language configuration
+// language : finnish (fi)
+// author : Tarmo Aidantausta : https://github.com/bleadof
+
+(function (factory) {
+ factory(moment);
+}(function (moment) {
+ var numbersPast = 'nolla yksi kaksi kolme neljä viisi kuusi seitsemän kahdeksan yhdeksän'.split(' '),
+ numbersFuture = ['nolla', 'yhden', 'kahden', 'kolmen', 'neljän', 'viiden', 'kuuden',
+ numbersPast[7], numbersPast[8], numbersPast[9]];
+
+ function translate(number, withoutSuffix, key, isFuture) {
+ var result = "";
+ switch (key) {
+ case 's':
+ return isFuture ? 'muutaman sekunnin' : 'muutama sekunti';
+ case 'm':
+ return isFuture ? 'minuutin' : 'minuutti';
+ case 'mm':
+ result = isFuture ? 'minuutin' : 'minuuttia';
+ break;
+ case 'h':
+ return isFuture ? 'tunnin' : 'tunti';
+ case 'hh':
+ result = isFuture ? 'tunnin' : 'tuntia';
+ break;
+ case 'd':
+ return isFuture ? 'päivän' : 'päivä';
+ case 'dd':
+ result = isFuture ? 'päivän' : 'päivää';
+ break;
+ case 'M':
+ return isFuture ? 'kuukauden' : 'kuukausi';
+ case 'MM':
+ result = isFuture ? 'kuukauden' : 'kuukautta';
+ break;
+ case 'y':
+ return isFuture ? 'vuoden' : 'vuosi';
+ case 'yy':
+ result = isFuture ? 'vuoden' : 'vuotta';
+ break;
+ }
+ result = verbalNumber(number, isFuture) + " " + result;
+ return result;
+ }
+
+ function verbalNumber(number, isFuture) {
+ return number < 10 ? (isFuture ? numbersFuture[number] : numbersPast[number]) : number;
+ }
+
+ return moment.lang('fi', {
+ months : "tammikuu_helmikuu_maaliskuu_huhtikuu_toukokuu_kesäkuu_heinäkuu_elokuu_syyskuu_lokakuu_marraskuu_joulukuu".split("_"),
+ monthsShort : "tammi_helmi_maalis_huhti_touko_kesä_heinä_elo_syys_loka_marras_joulu".split("_"),
+ weekdays : "sunnuntai_maanantai_tiistai_keskiviikko_torstai_perjantai_lauantai".split("_"),
+ weekdaysShort : "su_ma_ti_ke_to_pe_la".split("_"),
+ weekdaysMin : "su_ma_ti_ke_to_pe_la".split("_"),
+ longDateFormat : {
+ LT : "HH.mm",
+ L : "DD.MM.YYYY",
+ LL : "Do MMMM[ta] YYYY",
+ LLL : "Do MMMM[ta] YYYY, [klo] LT",
+ LLLL : "dddd, Do MMMM[ta] YYYY, [klo] LT",
+ l : "D.M.YYYY",
+ ll : "Do MMM YYYY",
+ lll : "Do MMM YYYY, [klo] LT",
+ llll : "ddd, Do MMM YYYY, [klo] LT"
+ },
+ calendar : {
+ sameDay : '[tänään] [klo] LT',
+ nextDay : '[huomenna] [klo] LT',
+ nextWeek : 'dddd [klo] LT',
+ lastDay : '[eilen] [klo] LT',
+ lastWeek : '[viime] dddd[na] [klo] LT',
+ sameElse : 'L'
+ },
+ relativeTime : {
+ future : "%s päästä",
+ past : "%s sitten",
+ s : translate,
+ m : translate,
+ mm : translate,
+ h : translate,
+ hh : translate,
+ d : translate,
+ dd : translate,
+ M : translate,
+ MM : translate,
+ y : translate,
+ yy : translate
+ },
+ ordinal : "%d.",
+ week : {
+ dow : 1, // Monday is the first day of the week.
+ doy : 4 // The week that contains Jan 4th is the first week of the year.
+ }
+ });
+}));
+// moment.js language configuration
+// language : faroese (fo)
+// author : Ragnar Johannesen : https://github.com/ragnar123
+
+(function (factory) {
+ factory(moment);
+}(function (moment) {
+ return moment.lang('fo', {
+ months : "januar_februar_mars_apríl_mai_juni_juli_august_september_oktober_november_desember".split("_"),
+ monthsShort : "jan_feb_mar_apr_mai_jun_jul_aug_sep_okt_nov_des".split("_"),
+ weekdays : "sunnudagur_mánadagur_týsdagur_mikudagur_hósdagur_fríggjadagur_leygardagur".split("_"),
+ weekdaysShort : "sun_mán_týs_mik_hós_frí_ley".split("_"),
+ weekdaysMin : "su_má_tý_mi_hó_fr_le".split("_"),
+ longDateFormat : {
+ LT : "HH:mm",
+ L : "DD/MM/YYYY",
+ LL : "D MMMM YYYY",
+ LLL : "D MMMM YYYY LT",
+ LLLL : "dddd D. MMMM, YYYY LT"
+ },
+ calendar : {
+ sameDay : '[Í dag kl.] LT',
+ nextDay : '[Í morgin kl.] LT',
+ nextWeek : 'dddd [kl.] LT',
+ lastDay : '[Í gjár kl.] LT',
+ lastWeek : '[síðstu] dddd [kl] LT',
+ sameElse : 'L'
+ },
+ relativeTime : {
+ future : "um %s",
+ past : "%s síðani",
+ s : "fá sekund",
+ m : "ein minutt",
+ mm : "%d minuttir",
+ h : "ein tími",
+ hh : "%d tímar",
+ d : "ein dagur",
+ dd : "%d dagar",
+ M : "ein mánaði",
+ MM : "%d mánaðir",
+ y : "eitt ár",
+ yy : "%d ár"
+ },
+ ordinal : '%d.',
+ week : {
+ dow : 1, // Monday is the first day of the week.
+ doy : 4 // The week that contains Jan 4th is the first week of the year.
+ }
+ });
+}));
+// moment.js language configuration
+// language : canadian french (fr-ca)
+// author : Jonathan Abourbih : https://github.com/jonbca
+
+(function (factory) {
+ factory(moment);
+}(function (moment) {
+ return moment.lang('fr-ca', {
+ months : "janvier_février_mars_avril_mai_juin_juillet_août_septembre_octobre_novembre_décembre".split("_"),
+ monthsShort : "janv._févr._mars_avr._mai_juin_juil._août_sept._oct._nov._déc.".split("_"),
+ weekdays : "dimanche_lundi_mardi_mercredi_jeudi_vendredi_samedi".split("_"),
+ weekdaysShort : "dim._lun._mar._mer._jeu._ven._sam.".split("_"),
+ weekdaysMin : "Di_Lu_Ma_Me_Je_Ve_Sa".split("_"),
+ longDateFormat : {
+ LT : "HH:mm",
+ L : "YYYY-MM-DD",
+ LL : "D MMMM YYYY",
+ LLL : "D MMMM YYYY LT",
+ LLLL : "dddd D MMMM YYYY LT"
+ },
+ calendar : {
+ sameDay: "[Aujourd'hui à] LT",
+ nextDay: '[Demain à] LT',
+ nextWeek: 'dddd [à] LT',
+ lastDay: '[Hier à] LT',
+ lastWeek: 'dddd [dernier à] LT',
+ sameElse: 'L'
+ },
+ relativeTime : {
+ future : "dans %s",
+ past : "il y a %s",
+ s : "quelques secondes",
+ m : "une minute",
+ mm : "%d minutes",
+ h : "une heure",
+ hh : "%d heures",
+ d : "un jour",
+ dd : "%d jours",
+ M : "un mois",
+ MM : "%d mois",
+ y : "un an",
+ yy : "%d ans"
+ },
+ ordinal : function (number) {
+ return number + (number === 1 ? 'er' : '');
+ }
+ });
+}));
+// moment.js language configuration
+// language : french (fr)
+// author : John Fischer : https://github.com/jfroffice
+
+(function (factory) {
+ factory(moment);
+}(function (moment) {
+ return moment.lang('fr', {
+ months : "janvier_février_mars_avril_mai_juin_juillet_août_septembre_octobre_novembre_décembre".split("_"),
+ monthsShort : "janv._févr._mars_avr._mai_juin_juil._août_sept._oct._nov._déc.".split("_"),
+ weekdays : "dimanche_lundi_mardi_mercredi_jeudi_vendredi_samedi".split("_"),
+ weekdaysShort : "dim._lun._mar._mer._jeu._ven._sam.".split("_"),
+ weekdaysMin : "Di_Lu_Ma_Me_Je_Ve_Sa".split("_"),
+ longDateFormat : {
+ LT : "HH:mm",
+ L : "DD/MM/YYYY",
+ LL : "D MMMM YYYY",
+ LLL : "D MMMM YYYY LT",
+ LLLL : "dddd D MMMM YYYY LT"
+ },
+ calendar : {
+ sameDay: "[Aujourd'hui à] LT",
+ nextDay: '[Demain à] LT',
+ nextWeek: 'dddd [à] LT',
+ lastDay: '[Hier à] LT',
+ lastWeek: 'dddd [dernier à] LT',
+ sameElse: 'L'
+ },
+ relativeTime : {
+ future : "dans %s",
+ past : "il y a %s",
+ s : "quelques secondes",
+ m : "une minute",
+ mm : "%d minutes",
+ h : "une heure",
+ hh : "%d heures",
+ d : "un jour",
+ dd : "%d jours",
+ M : "un mois",
+ MM : "%d mois",
+ y : "un an",
+ yy : "%d ans"
+ },
+ ordinal : function (number) {
+ return number + (number === 1 ? 'er' : '');
+ },
+ week : {
+ dow : 1, // Monday is the first day of the week.
+ doy : 4 // The week that contains Jan 4th is the first week of the year.
+ }
+ });
+}));
+// moment.js language configuration
+// language : galician (gl)
+// author : Juan G. Hurtado : https://github.com/juanghurtado
+
+(function (factory) {
+ factory(moment);
+}(function (moment) {
+ return moment.lang('gl', {
+ months : "Xaneiro_Febreiro_Marzo_Abril_Maio_Xuño_Xullo_Agosto_Setembro_Outubro_Novembro_Decembro".split("_"),
+ monthsShort : "Xan._Feb._Mar._Abr._Mai._Xuñ._Xul._Ago._Set._Out._Nov._Dec.".split("_"),
+ weekdays : "Domingo_Luns_Martes_Mércores_Xoves_Venres_Sábado".split("_"),
+ weekdaysShort : "Dom._Lun._Mar._Mér._Xov._Ven._Sáb.".split("_"),
+ weekdaysMin : "Do_Lu_Ma_Mé_Xo_Ve_Sá".split("_"),
+ longDateFormat : {
+ LT : "H:mm",
+ L : "DD/MM/YYYY",
+ LL : "D MMMM YYYY",
+ LLL : "D MMMM YYYY LT",
+ LLLL : "dddd D MMMM YYYY LT"
+ },
+ calendar : {
+ sameDay : function () {
+ return '[hoxe ' + ((this.hours() !== 1) ? 'ás' : 'á') + '] LT';
+ },
+ nextDay : function () {
+ return '[mañá ' + ((this.hours() !== 1) ? 'ás' : 'á') + '] LT';
+ },
+ nextWeek : function () {
+ return 'dddd [' + ((this.hours() !== 1) ? 'ás' : 'a') + '] LT';
+ },
+ lastDay : function () {
+ return '[onte ' + ((this.hours() !== 1) ? 'á' : 'a') + '] LT';
+ },
+ lastWeek : function () {
+ return '[o] dddd [pasado ' + ((this.hours() !== 1) ? 'ás' : 'a') + '] LT';
+ },
+ sameElse : 'L'
+ },
+ relativeTime : {
+ future : function (str) {
+ if (str === "uns segundos") {
+ return "nuns segundos";
+ }
+ return "en " + str;
+ },
+ past : "hai %s",
+ s : "uns segundos",
+ m : "un minuto",
+ mm : "%d minutos",
+ h : "unha hora",
+ hh : "%d horas",
+ d : "un día",
+ dd : "%d días",
+ M : "un mes",
+ MM : "%d meses",
+ y : "un ano",
+ yy : "%d anos"
+ },
+ ordinal : '%dº',
+ week : {
+ dow : 1, // Monday is the first day of the week.
+ doy : 7 // The week that contains Jan 1st is the first week of the year.
+ }
+ });
+}));
+// moment.js language configuration
+// language : Hebrew (he)
+// author : Tomer Cohen : https://github.com/tomer
+// author : Moshe Simantov : https://github.com/DevelopmentIL
+// author : Tal Ater : https://github.com/TalAter
+
+(function (factory) {
+ factory(moment);
+}(function (moment) {
+ return moment.lang('he', {
+ months : "ינואר_פברואר_מרץ_אפריל_מאי_יוני_יולי_אוגוסט_ספטמבר_אוקטובר_נובמבר_דצמבר".split("_"),
+ monthsShort : "ינו׳_פבר׳_מרץ_אפר׳_מאי_יוני_יולי_אוג׳_ספט׳_אוק׳_נוב׳_דצמ׳".split("_"),
+ weekdays : "ראשון_שני_שלישי_רביעי_חמישי_שישי_שבת".split("_"),
+ weekdaysShort : "א׳_ב׳_ג׳_ד׳_ה׳_ו׳_ש׳".split("_"),
+ weekdaysMin : "א_ב_ג_ד_ה_ו_ש".split("_"),
+ longDateFormat : {
+ LT : "HH:mm",
+ L : "DD/MM/YYYY",
+ LL : "D [ב]MMMM YYYY",
+ LLL : "D [ב]MMMM YYYY LT",
+ LLLL : "dddd, D [ב]MMMM YYYY LT",
+ l : "D/M/YYYY",
+ ll : "D MMM YYYY",
+ lll : "D MMM YYYY LT",
+ llll : "ddd, D MMM YYYY LT"
+ },
+ calendar : {
+ sameDay : '[היום ב־]LT',
+ nextDay : '[מחר ב־]LT',
+ nextWeek : 'dddd [בשעה] LT',
+ lastDay : '[אתמול ב־]LT',
+ lastWeek : '[ביום] dddd [האחרון בשעה] LT',
+ sameElse : 'L'
+ },
+ relativeTime : {
+ future : "בעוד %s",
+ past : "לפני %s",
+ s : "מספר שניות",
+ m : "דקה",
+ mm : "%d דקות",
+ h : "שעה",
+ hh : function (number) {
+ if (number === 2) {
+ return "שעתיים";
+ }
+ return number + " שעות";
+ },
+ d : "יום",
+ dd : function (number) {
+ if (number === 2) {
+ return "יומיים";
+ }
+ return number + " ימים";
+ },
+ M : "חודש",
+ MM : function (number) {
+ if (number === 2) {
+ return "חודשיים";
+ }
+ return number + " חודשים";
+ },
+ y : "שנה",
+ yy : function (number) {
+ if (number === 2) {
+ return "שנתיים";
+ }
+ return number + " שנים";
+ }
+ }
+ });
+}));
+// moment.js language configuration
+// language : hindi (hi)
+// author : Mayank Singhal : https://github.com/mayanksinghal
+
+(function (factory) {
+ factory(moment);
+}(function (moment) {
+ var symbolMap = {
+ '1': '१',
+ '2': '२',
+ '3': '३',
+ '4': '४',
+ '5': '५',
+ '6': '६',
+ '7': '७',
+ '8': '८',
+ '9': '९',
+ '0': '०'
+ },
+ numberMap = {
+ '१': '1',
+ '२': '2',
+ '३': '3',
+ '४': '4',
+ '५': '5',
+ '६': '6',
+ '७': '7',
+ '८': '8',
+ '९': '9',
+ '०': '0'
+ };
+
+ return moment.lang('hi', {
+ months : 'जनवरी_फ़रवरी_मार्च_अप्रैल_मई_जून_जुलाई_अगस्त_सितम्बर_अक्टूबर_नवम्बर_दिसम्बर'.split("_"),
+ monthsShort : 'जन._फ़र._मार्च_अप्रै._मई_जून_जुल._अग._सित._अक्टू._नव._दिस.'.split("_"),
+ weekdays : 'रविवार_सोमवार_मंगलवार_बुधवार_गुरूवार_शुक्रवार_शनिवार'.split("_"),
+ weekdaysShort : 'रवि_सोम_मंगल_बुध_गुरू_शुक्र_शनि'.split("_"),
+ weekdaysMin : 'र_सो_मं_बु_गु_शु_श'.split("_"),
+ longDateFormat : {
+ LT : "A h:mm बजे",
+ L : "DD/MM/YYYY",
+ LL : "D MMMM YYYY",
+ LLL : "D MMMM YYYY, LT",
+ LLLL : "dddd, D MMMM YYYY, LT"
+ },
+ calendar : {
+ sameDay : '[आज] LT',
+ nextDay : '[कल] LT',
+ nextWeek : 'dddd, LT',
+ lastDay : '[कल] LT',
+ lastWeek : '[पिछले] dddd, LT',
+ sameElse : 'L'
+ },
+ relativeTime : {
+ future : "%s में",
+ past : "%s पहले",
+ s : "कुछ ही क्षण",
+ m : "एक मिनट",
+ mm : "%d मिनट",
+ h : "एक घंटा",
+ hh : "%d घंटे",
+ d : "एक दिन",
+ dd : "%d दिन",
+ M : "एक महीने",
+ MM : "%d महीने",
+ y : "एक वर्ष",
+ yy : "%d वर्ष"
+ },
+ preparse: function (string) {
+ return string.replace(/[१२३४५६७८९०]/g, function (match) {
+ return numberMap[match];
+ });
+ },
+ postformat: function (string) {
+ return string.replace(/\d/g, function (match) {
+ return symbolMap[match];
+ });
+ },
+ // Hindi notation for meridiems are quite fuzzy in practice. While there exists
+ // a rigid notion of a 'Pahar' it is not used as rigidly in modern Hindi.
+ meridiem : function (hour, minute, isLower) {
+ if (hour < 4) {
+ return "रात";
+ } else if (hour < 10) {
+ return "सुबह";
+ } else if (hour < 17) {
+ return "दोपहर";
+ } else if (hour < 20) {
+ return "शाम";
+ } else {
+ return "रात";
+ }
+ },
+ week : {
+ dow : 0, // Sunday is the first day of the week.
+ doy : 6 // The week that contains Jan 1st is the first week of the year.
+ }
+ });
+}));
+// moment.js language configuration
+// language : hrvatski (hr)
+// author : Bojan Marković : https://github.com/bmarkovic
+
+// based on (sl) translation by Robert Sedovšek
+
+(function (factory) {
+ factory(moment);
+}(function (moment) {
+
+ function translate(number, withoutSuffix, key) {
+ var result = number + " ";
+ switch (key) {
+ case 'm':
+ return withoutSuffix ? 'jedna minuta' : 'jedne minute';
+ case 'mm':
+ if (number === 1) {
+ result += 'minuta';
+ } else if (number === 2 || number === 3 || number === 4) {
+ result += 'minute';
+ } else {
+ result += 'minuta';
+ }
+ return result;
+ case 'h':
+ return withoutSuffix ? 'jedan sat' : 'jednog sata';
+ case 'hh':
+ if (number === 1) {
+ result += 'sat';
+ } else if (number === 2 || number === 3 || number === 4) {
+ result += 'sata';
+ } else {
+ result += 'sati';
+ }
+ return result;
+ case 'dd':
+ if (number === 1) {
+ result += 'dan';
+ } else {
+ result += 'dana';
+ }
+ return result;
+ case 'MM':
+ if (number === 1) {
+ result += 'mjesec';
+ } else if (number === 2 || number === 3 || number === 4) {
+ result += 'mjeseca';
+ } else {
+ result += 'mjeseci';
+ }
+ return result;
+ case 'yy':
+ if (number === 1) {
+ result += 'godina';
+ } else if (number === 2 || number === 3 || number === 4) {
+ result += 'godine';
+ } else {
+ result += 'godina';
+ }
+ return result;
+ }
+ }
+
+ return moment.lang('hr', {
+ months : "sječanj_veljača_ožujak_travanj_svibanj_lipanj_srpanj_kolovoz_rujan_listopad_studeni_prosinac".split("_"),
+ monthsShort : "sje._vel._ožu._tra._svi._lip._srp._kol._ruj._lis._stu._pro.".split("_"),
+ weekdays : "nedjelja_ponedjeljak_utorak_srijeda_četvrtak_petak_subota".split("_"),
+ weekdaysShort : "ned._pon._uto._sri._čet._pet._sub.".split("_"),
+ weekdaysMin : "ne_po_ut_sr_če_pe_su".split("_"),
+ longDateFormat : {
+ LT : "H:mm",
+ L : "DD. MM. YYYY",
+ LL : "D. MMMM YYYY",
+ LLL : "D. MMMM YYYY LT",
+ LLLL : "dddd, D. MMMM YYYY LT"
+ },
+ calendar : {
+ sameDay : '[danas u] LT',
+ nextDay : '[sutra u] LT',
+
+ nextWeek : function () {
+ switch (this.day()) {
+ case 0:
+ return '[u] [nedjelju] [u] LT';
+ case 3:
+ return '[u] [srijedu] [u] LT';
+ case 6:
+ return '[u] [subotu] [u] LT';
+ case 1:
+ case 2:
+ case 4:
+ case 5:
+ return '[u] dddd [u] LT';
+ }
+ },
+ lastDay : '[jučer u] LT',
+ lastWeek : function () {
+ switch (this.day()) {
+ case 0:
+ case 3:
+ return '[prošlu] dddd [u] LT';
+ case 6:
+ return '[prošle] [subote] [u] LT';
+ case 1:
+ case 2:
+ case 4:
+ case 5:
+ return '[prošli] dddd [u] LT';
+ }
+ },
+ sameElse : 'L'
+ },
+ relativeTime : {
+ future : "za %s",
+ past : "prije %s",
+ s : "par sekundi",
+ m : translate,
+ mm : translate,
+ h : translate,
+ hh : translate,
+ d : "dan",
+ dd : translate,
+ M : "mjesec",
+ MM : translate,
+ y : "godinu",
+ yy : translate
+ },
+ ordinal : '%d.',
+ week : {
+ dow : 1, // Monday is the first day of the week.
+ doy : 7 // The week that contains Jan 1st is the first week of the year.
+ }
+ });
+}));
+// moment.js language configuration
+// language : hungarian (hu)
+// author : Adam Brunner : https://github.com/adambrunner
+
+(function (factory) {
+ factory(moment);
+}(function (moment) {
+ var weekEndings = 'vasárnap hétfőn kedden szerdán csütörtökön pénteken szombaton'.split(' ');
+
+ function translate(number, withoutSuffix, key, isFuture) {
+ var num = number,
+ suffix;
+
+ switch (key) {
+ case 's':
+ return (isFuture || withoutSuffix) ? 'néhány másodperc' : 'néhány másodperce';
+ case 'm':
+ return 'egy' + (isFuture || withoutSuffix ? ' perc' : ' perce');
+ case 'mm':
+ return num + (isFuture || withoutSuffix ? ' perc' : ' perce');
+ case 'h':
+ return 'egy' + (isFuture || withoutSuffix ? ' óra' : ' órája');
+ case 'hh':
+ return num + (isFuture || withoutSuffix ? ' óra' : ' órája');
+ case 'd':
+ return 'egy' + (isFuture || withoutSuffix ? ' nap' : ' napja');
+ case 'dd':
+ return num + (isFuture || withoutSuffix ? ' nap' : ' napja');
+ case 'M':
+ return 'egy' + (isFuture || withoutSuffix ? ' hónap' : ' hónapja');
+ case 'MM':
+ return num + (isFuture || withoutSuffix ? ' hónap' : ' hónapja');
+ case 'y':
+ return 'egy' + (isFuture || withoutSuffix ? ' év' : ' éve');
+ case 'yy':
+ return num + (isFuture || withoutSuffix ? ' év' : ' éve');
+ }
+
+ return '';
+ }
+
+ function week(isFuture) {
+ return (isFuture ? '' : '[múlt] ') + '[' + weekEndings[this.day()] + '] LT[-kor]';
+ }
+
+ return moment.lang('hu', {
+ months : "január_február_március_április_május_június_július_augusztus_szeptember_október_november_december".split("_"),
+ monthsShort : "jan_feb_márc_ápr_máj_jún_júl_aug_szept_okt_nov_dec".split("_"),
+ weekdays : "vasárnap_hétfő_kedd_szerda_csütörtök_péntek_szombat".split("_"),
+ weekdaysShort : "vas_hét_kedd_sze_csüt_pén_szo".split("_"),
+ weekdaysMin : "v_h_k_sze_cs_p_szo".split("_"),
+ longDateFormat : {
+ LT : "H:mm",
+ L : "YYYY.MM.DD.",
+ LL : "YYYY. MMMM D.",
+ LLL : "YYYY. MMMM D., LT",
+ LLLL : "YYYY. MMMM D., dddd LT"
+ },
+ meridiem : function (hours, minutes, isLower) {
+ if (hours < 12) {
+ return isLower === true ? 'de' : 'DE';
+ } else {
+ return isLower === true ? 'du' : 'DU';
+ }
+ },
+ calendar : {
+ sameDay : '[ma] LT[-kor]',
+ nextDay : '[holnap] LT[-kor]',
+ nextWeek : function () {
+ return week.call(this, true);
+ },
+ lastDay : '[tegnap] LT[-kor]',
+ lastWeek : function () {
+ return week.call(this, false);
+ },
+ sameElse : 'L'
+ },
+ relativeTime : {
+ future : "%s múlva",
+ past : "%s",
+ s : translate,
+ m : translate,
+ mm : translate,
+ h : translate,
+ hh : translate,
+ d : translate,
+ dd : translate,
+ M : translate,
+ MM : translate,
+ y : translate,
+ yy : translate
+ },
+ ordinal : '%d.',
+ week : {
+ dow : 1, // Monday is the first day of the week.
+ doy : 7 // The week that contains Jan 1st is the first week of the year.
+ }
+ });
+}));
+// moment.js language configuration
+// language : Armenian (hy-am)
+// author : Armendarabyan : https://github.com/armendarabyan
+
+(function (factory) {
+ factory(moment);
+}(function (moment) {
+
+ function monthsCaseReplace(m, format) {
+ var months = {
+ 'nominative': 'հունվար_փետրվար_մարտ_ապրիլ_մայիս_հունիս_հուլիս_օգոստոս_սեպտեմբեր_հոկտեմբեր_նոյեմբեր_դեկտեմբեր'.split('_'),
+ 'accusative': 'հունվարի_փետրվարի_մարտի_ապրիլի_մայիսի_հունիսի_հուլիսի_օգոստոսի_սեպտեմբերի_հոկտեմբերի_նոյեմբերի_դեկտեմբերի'.split('_')
+ },
+
+ nounCase = (/D[oD]?(\[[^\[\]]*\]|\s+)+MMMM?/).test(format) ?
+ 'accusative' :
+ 'nominative';
+
+ return months[nounCase][m.month()];
+ }
+
+ function monthsShortCaseReplace(m, format) {
+ var monthsShort = 'հնվ_փտր_մրտ_ապր_մյս_հնս_հլս_օգս_սպտ_հկտ_նմբ_դկտ'.split('_');
+
+ return monthsShort[m.month()];
+ }
+
+ function weekdaysCaseReplace(m, format) {
+ var weekdays = 'կիրակի_երկուշաբթի_երեքշաբթի_չորեքշաբթի_հինգշաբթի_ուրբաթ_շաբաթ'.split('_');
+
+ return weekdays[m.day()];
+ }
+
+ return moment.lang('hy-am', {
+ months : monthsCaseReplace,
+ monthsShort : monthsShortCaseReplace,
+ weekdays : weekdaysCaseReplace,
+ weekdaysShort : "կրկ_երկ_երք_չրք_հնգ_ուրբ_շբթ".split("_"),
+ weekdaysMin : "կրկ_երկ_երք_չրք_հնգ_ուրբ_շբթ".split("_"),
+ longDateFormat : {
+ LT : "HH:mm",
+ L : "DD.MM.YYYY",
+ LL : "D MMMM YYYY թ.",
+ LLL : "D MMMM YYYY թ., LT",
+ LLLL : "dddd, D MMMM YYYY թ., LT"
+ },
+ calendar : {
+ sameDay: '[այսօր] LT',
+ nextDay: '[վաղը] LT',
+ lastDay: '[երեկ] LT',
+ nextWeek: function () {
+ return 'dddd [օրը ժամը] LT';
+ },
+ lastWeek: function () {
+ return '[անցած] dddd [օրը ժամը] LT';
+ },
+ sameElse: 'L'
+ },
+ relativeTime : {
+ future : "%s հետո",
+ past : "%s առաջ",
+ s : "մի քանի վայրկյան",
+ m : "րոպե",
+ mm : "%d րոպե",
+ h : "ժամ",
+ hh : "%d ժամ",
+ d : "օր",
+ dd : "%d օր",
+ M : "ամիս",
+ MM : "%d ամիս",
+ y : "տարի",
+ yy : "%d տարի"
+ },
+
+ meridiem : function (hour) {
+ if (hour < 4) {
+ return "գիշերվա";
+ } else if (hour < 12) {
+ return "առավոտվա";
+ } else if (hour < 17) {
+ return "ցերեկվա";
+ } else {
+ return "երեկոյան";
+ }
+ },
+
+ ordinal: function (number, period) {
+ switch (period) {
+ case 'DDD':
+ case 'w':
+ case 'W':
+ case 'DDDo':
+ if (number === 1) {
+ return number + '-ին';
+ }
+ return number + '-րդ';
+ default:
+ return number;
+ }
+ },
+
+ week : {
+ dow : 1, // Monday is the first day of the week.
+ doy : 7 // The week that contains Jan 1st is the first week of the year.
+ }
+ });
+}));
+// moment.js language configuration
+// language : Bahasa Indonesia (id)
+// author : Mohammad Satrio Utomo : https://github.com/tyok
+// reference: http://id.wikisource.org/wiki/Pedoman_Umum_Ejaan_Bahasa_Indonesia_yang_Disempurnakan
+
+(function (factory) {
+ factory(moment);
+}(function (moment) {
+ return moment.lang('id', {
+ months : "Januari_Februari_Maret_April_Mei_Juni_Juli_Agustus_September_Oktober_November_Desember".split("_"),
+ monthsShort : "Jan_Feb_Mar_Apr_Mei_Jun_Jul_Ags_Sep_Okt_Nov_Des".split("_"),
+ weekdays : "Minggu_Senin_Selasa_Rabu_Kamis_Jumat_Sabtu".split("_"),
+ weekdaysShort : "Min_Sen_Sel_Rab_Kam_Jum_Sab".split("_"),
+ weekdaysMin : "Mg_Sn_Sl_Rb_Km_Jm_Sb".split("_"),
+ longDateFormat : {
+ LT : "HH.mm",
+ L : "DD/MM/YYYY",
+ LL : "D MMMM YYYY",
+ LLL : "D MMMM YYYY [pukul] LT",
+ LLLL : "dddd, D MMMM YYYY [pukul] LT"
+ },
+ meridiem : function (hours, minutes, isLower) {
+ if (hours < 11) {
+ return 'pagi';
+ } else if (hours < 15) {
+ return 'siang';
+ } else if (hours < 19) {
+ return 'sore';
+ } else {
+ return 'malam';
+ }
+ },
+ calendar : {
+ sameDay : '[Hari ini pukul] LT',
+ nextDay : '[Besok pukul] LT',
+ nextWeek : 'dddd [pukul] LT',
+ lastDay : '[Kemarin pukul] LT',
+ lastWeek : 'dddd [lalu pukul] LT',
+ sameElse : 'L'
+ },
+ relativeTime : {
+ future : "dalam %s",
+ past : "%s yang lalu",
+ s : "beberapa detik",
+ m : "semenit",
+ mm : "%d menit",
+ h : "sejam",
+ hh : "%d jam",
+ d : "sehari",
+ dd : "%d hari",
+ M : "sebulan",
+ MM : "%d bulan",
+ y : "setahun",
+ yy : "%d tahun"
+ },
+ week : {
+ dow : 1, // Monday is the first day of the week.
+ doy : 7 // The week that contains Jan 1st is the first week of the year.
+ }
+ });
+}));
+// moment.js language configuration
+// language : icelandic (is)
+// author : Hinrik Örn Sigurðsson : https://github.com/hinrik
+
+(function (factory) {
+ factory(moment);
+}(function (moment) {
+ function plural(n) {
+ if (n % 100 === 11) {
+ return true;
+ } else if (n % 10 === 1) {
+ return false;
+ }
+ return true;
+ }
+
+ function translate(number, withoutSuffix, key, isFuture) {
+ var result = number + " ";
+ switch (key) {
+ case 's':
+ return withoutSuffix || isFuture ? 'nokkrar sekúndur' : 'nokkrum sekúndum';
+ case 'm':
+ return withoutSuffix ? 'mínúta' : 'mínútu';
+ case 'mm':
+ if (plural(number)) {
+ return result + (withoutSuffix || isFuture ? 'mínútur' : 'mínútum');
+ } else if (withoutSuffix) {
+ return result + 'mínúta';
+ }
+ return result + 'mínútu';
+ case 'hh':
+ if (plural(number)) {
+ return result + (withoutSuffix || isFuture ? 'klukkustundir' : 'klukkustundum');
+ }
+ return result + 'klukkustund';
+ case 'd':
+ if (withoutSuffix) {
+ return 'dagur';
+ }
+ return isFuture ? 'dag' : 'degi';
+ case 'dd':
+ if (plural(number)) {
+ if (withoutSuffix) {
+ return result + 'dagar';
+ }
+ return result + (isFuture ? 'daga' : 'dögum');
+ } else if (withoutSuffix) {
+ return result + 'dagur';
+ }
+ return result + (isFuture ? 'dag' : 'degi');
+ case 'M':
+ if (withoutSuffix) {
+ return 'mánuður';
+ }
+ return isFuture ? 'mánuð' : 'mánuði';
+ case 'MM':
+ if (plural(number)) {
+ if (withoutSuffix) {
+ return result + 'mánuðir';
+ }
+ return result + (isFuture ? 'mánuði' : 'mánuðum');
+ } else if (withoutSuffix) {
+ return result + 'mánuður';
+ }
+ return result + (isFuture ? 'mánuð' : 'mánuði');
+ case 'y':
+ return withoutSuffix || isFuture ? 'ár' : 'ári';
+ case 'yy':
+ if (plural(number)) {
+ return result + (withoutSuffix || isFuture ? 'ár' : 'árum');
+ }
+ return result + (withoutSuffix || isFuture ? 'ár' : 'ári');
+ }
+ }
+
+ return moment.lang('is', {
+ months : "janúar_febrúar_mars_apríl_maí_júní_júlí_ágúst_september_október_nóvember_desember".split("_"),
+ monthsShort : "jan_feb_mar_apr_maí_jún_júl_ágú_sep_okt_nóv_des".split("_"),
+ weekdays : "sunnudagur_mánudagur_þriðjudagur_miðvikudagur_fimmtudagur_föstudagur_laugardagur".split("_"),
+ weekdaysShort : "sun_mán_þri_mið_fim_fös_lau".split("_"),
+ weekdaysMin : "Su_Má_Þr_Mi_Fi_Fö_La".split("_"),
+ longDateFormat : {
+ LT : "H:mm",
+ L : "DD/MM/YYYY",
+ LL : "D. MMMM YYYY",
+ LLL : "D. MMMM YYYY [kl.] LT",
+ LLLL : "dddd, D. MMMM YYYY [kl.] LT"
+ },
+ calendar : {
+ sameDay : '[í dag kl.] LT',
+ nextDay : '[á morgun kl.] LT',
+ nextWeek : 'dddd [kl.] LT',
+ lastDay : '[í gær kl.] LT',
+ lastWeek : '[síðasta] dddd [kl.] LT',
+ sameElse : 'L'
+ },
+ relativeTime : {
+ future : "eftir %s",
+ past : "fyrir %s síðan",
+ s : translate,
+ m : translate,
+ mm : translate,
+ h : "klukkustund",
+ hh : translate,
+ d : translate,
+ dd : translate,
+ M : translate,
+ MM : translate,
+ y : translate,
+ yy : translate
+ },
+ ordinal : '%d.',
+ week : {
+ dow : 1, // Monday is the first day of the week.
+ doy : 4 // The week that contains Jan 4th is the first week of the year.
+ }
+ });
+}));
+// moment.js language configuration
+// language : italian (it)
+// author : Lorenzo : https://github.com/aliem
+// author: Mattia Larentis: https://github.com/nostalgiaz
+
+(function (factory) {
+ factory(moment);
+}(function (moment) {
+ return moment.lang('it', {
+ months : "gennaio_febbraio_marzo_aprile_maggio_giugno_luglio_agosto_settembre_ottobre_novembre_dicembre".split("_"),
+ monthsShort : "gen_feb_mar_apr_mag_giu_lug_ago_set_ott_nov_dic".split("_"),
+ weekdays : "Domenica_Lunedì_Martedì_Mercoledì_Giovedì_Venerdì_Sabato".split("_"),
+ weekdaysShort : "Dom_Lun_Mar_Mer_Gio_Ven_Sab".split("_"),
+ weekdaysMin : "D_L_Ma_Me_G_V_S".split("_"),
+ longDateFormat : {
+ LT : "HH:mm",
+ L : "DD/MM/YYYY",
+ LL : "D MMMM YYYY",
+ LLL : "D MMMM YYYY LT",
+ LLLL : "dddd, D MMMM YYYY LT"
+ },
+ calendar : {
+ sameDay: '[Oggi alle] LT',
+ nextDay: '[Domani alle] LT',
+ nextWeek: 'dddd [alle] LT',
+ lastDay: '[Ieri alle] LT',
+ lastWeek: '[lo scorso] dddd [alle] LT',
+ sameElse: 'L'
+ },
+ relativeTime : {
+ future : function (s) {
+ return ((/^[0-9].+$/).test(s) ? "tra" : "in") + " " + s;
+ },
+ past : "%s fa",
+ s : "alcuni secondi",
+ m : "un minuto",
+ mm : "%d minuti",
+ h : "un'ora",
+ hh : "%d ore",
+ d : "un giorno",
+ dd : "%d giorni",
+ M : "un mese",
+ MM : "%d mesi",
+ y : "un anno",
+ yy : "%d anni"
+ },
+ ordinal: '%dº',
+ week : {
+ dow : 1, // Monday is the first day of the week.
+ doy : 4 // The week that contains Jan 4th is the first week of the year.
+ }
+ });
+}));
+// moment.js language configuration
+// language : japanese (ja)
+// author : LI Long : https://github.com/baryon
+
+(function (factory) {
+ factory(moment);
+}(function (moment) {
+ return moment.lang('ja', {
+ months : "1月_2月_3月_4月_5月_6月_7月_8月_9月_10月_11月_12月".split("_"),
+ monthsShort : "1月_2月_3月_4月_5月_6月_7月_8月_9月_10月_11月_12月".split("_"),
+ weekdays : "日曜日_月曜日_火曜日_水曜日_木曜日_金曜日_土曜日".split("_"),
+ weekdaysShort : "日_月_火_水_木_金_土".split("_"),
+ weekdaysMin : "日_月_火_水_木_金_土".split("_"),
+ longDateFormat : {
+ LT : "Ah時m分",
+ L : "YYYY/MM/DD",
+ LL : "YYYY年M月D日",
+ LLL : "YYYY年M月D日LT",
+ LLLL : "YYYY年M月D日LT dddd"
+ },
+ meridiem : function (hour, minute, isLower) {
+ if (hour < 12) {
+ return "午前";
+ } else {
+ return "午後";
+ }
+ },
+ calendar : {
+ sameDay : '[今日] LT',
+ nextDay : '[明日] LT',
+ nextWeek : '[来週]dddd LT',
+ lastDay : '[昨日] LT',
+ lastWeek : '[前週]dddd LT',
+ sameElse : 'L'
+ },
+ relativeTime : {
+ future : "%s後",
+ past : "%s前",
+ s : "数秒",
+ m : "1分",
+ mm : "%d分",
+ h : "1時間",
+ hh : "%d時間",
+ d : "1日",
+ dd : "%d日",
+ M : "1ヶ月",
+ MM : "%dヶ月",
+ y : "1年",
+ yy : "%d年"
+ }
+ });
+}));
+// moment.js language configuration
+// language : Georgian (ka)
+// author : Irakli Janiashvili : https://github.com/irakli-janiashvili
+
+(function (factory) {
+ factory(moment);
+}(function (moment) {
+
+ function monthsCaseReplace(m, format) {
+ var months = {
+ 'nominative': 'იანვარი_თებერვალი_მარტი_აპრილი_მაისი_ივნისი_ივლისი_აგვისტო_სექტემბერი_ოქტომბერი_ნოემბერი_დეკემბერი'.split('_'),
+ 'accusative': 'იანვარს_თებერვალს_მარტს_აპრილის_მაისს_ივნისს_ივლისს_აგვისტს_სექტემბერს_ოქტომბერს_ნოემბერს_დეკემბერს'.split('_')
+ },
+
+ nounCase = (/D[oD] *MMMM?/).test(format) ?
+ 'accusative' :
+ 'nominative';
+
+ return months[nounCase][m.month()];
+ }
+
+ function weekdaysCaseReplace(m, format) {
+ var weekdays = {
+ 'nominative': 'კვირა_ორშაბათი_სამშაბათი_ოთხშაბათი_ხუთშაბათი_პარასკევი_შაბათი'.split('_'),
+ 'accusative': 'კვირას_ორშაბათს_სამშაბათს_ოთხშაბათს_ხუთშაბათს_პარასკევს_შაბათს'.split('_')
+ },
+
+ nounCase = (/(წინა|შემდეგ)/).test(format) ?
+ 'accusative' :
+ 'nominative';
+
+ return weekdays[nounCase][m.day()];
+ }
+
+ return moment.lang('ka', {
+ months : monthsCaseReplace,
+ monthsShort : "იან_თებ_მარ_აპრ_მაი_ივნ_ივლ_აგვ_სექ_ოქტ_ნოე_დეკ".split("_"),
+ weekdays : weekdaysCaseReplace,
+ weekdaysShort : "კვი_ორშ_სამ_ოთხ_ხუთ_პარ_შაბ".split("_"),
+ weekdaysMin : "კვ_ორ_სა_ოთ_ხუ_პა_შა".split("_"),
+ longDateFormat : {
+ LT : "h:mm A",
+ L : "DD/MM/YYYY",
+ LL : "D MMMM YYYY",
+ LLL : "D MMMM YYYY LT",
+ LLLL : "dddd, D MMMM YYYY LT"
+ },
+ calendar : {
+ sameDay : '[დღეს] LT[-ზე]',
+ nextDay : '[ხვალ] LT[-ზე]',
+ lastDay : '[გუშინ] LT[-ზე]',
+ nextWeek : '[შემდეგ] dddd LT[-ზე]',
+ lastWeek : '[წინა] dddd LT-ზე',
+ sameElse : 'L'
+ },
+ relativeTime : {
+ future : function (s) {
+ return (/(წამი|წუთი|საათი|წელი)/).test(s) ?
+ s.replace(/ი$/, "ში") :
+ s + "ში";
+ },
+ past : function (s) {
+ if ((/(წამი|წუთი|საათი|დღე|თვე)/).test(s)) {
+ return s.replace(/(ი|ე)$/, "ის წინ");
+ }
+ if ((/წელი/).test(s)) {
+ return s.replace(/წელი$/, "წლის წინ");
+ }
+ },
+ s : "რამდენიმე წამი",
+ m : "წუთი",
+ mm : "%d წუთი",
+ h : "საათი",
+ hh : "%d საათი",
+ d : "დღე",
+ dd : "%d დღე",
+ M : "თვე",
+ MM : "%d თვე",
+ y : "წელი",
+ yy : "%d წელი"
+ },
+ ordinal : function (number) {
+ if (number === 0) {
+ return number;
+ }
+
+ if (number === 1) {
+ return number + "-ლი";
+ }
+
+ if ((number < 20) || (number <= 100 && (number % 20 === 0)) || (number % 100 === 0)) {
+ return "მე-" + number;
+ }
+
+ return number + "-ე";
+ },
+ week : {
+ dow : 1,
+ doy : 7
+ }
+ });
+}));
+// moment.js language configuration
+// language : khmer (km)
+// author : Kruy Vanna : https://github.com/kruyvanna
+
+(function (factory) {
+ factory(moment);
+}(function (moment) {
+ return moment.lang('km', {
+ months: "មករា_កុម្ភៈ_មិនា_មេសា_ឧសភា_មិថុនា_កក្កដា_សីហា_កញ្ញា_តុលា_វិច្ឆិកា_ធ្នូ".split("_"),
+ monthsShort: "មករា_កុម្ភៈ_មិនា_មេសា_ឧសភា_មិថុនា_កក្កដា_សីហា_កញ្ញា_តុលា_វិច្ឆិកា_ធ្នូ".split("_"),
+ weekdays: "អាទិត្យ_ច័ន្ទ_អង្គារ_ពុធ_ព្រហស្បតិ៍_សុក្រ_សៅរ៍".split("_"),
+ weekdaysShort: "អាទិត្យ_ច័ន្ទ_អង្គារ_ពុធ_ព្រហស្បតិ៍_សុក្រ_សៅរ៍".split("_"),
+ weekdaysMin: "អាទិត្យ_ច័ន្ទ_អង្គារ_ពុធ_ព្រហស្បតិ៍_សុក្រ_សៅរ៍".split("_"),
+ longDateFormat: {
+ LT: "HH:mm",
+ L: "DD/MM/YYYY",
+ LL: "D MMMM YYYY",
+ LLL: "D MMMM YYYY LT",
+ LLLL: "dddd, D MMMM YYYY LT"
+ },
+ calendar: {
+ sameDay: '[ថ្ងៃនៈ ម៉ោង] LT',
+ nextDay: '[ស្អែក ម៉ោង] LT',
+ nextWeek: 'dddd [ម៉ោង] LT',
+ lastDay: '[ម្សិលមិញ ម៉ោង] LT',
+ lastWeek: 'dddd [សប្តាហ៍មុន] [ម៉ោង] LT',
+ sameElse: 'L'
+ },
+ relativeTime: {
+ future: "%sទៀត",
+ past: "%sមុន",
+ s: "ប៉ុន្មានវិនាទី",
+ m: "មួយនាទី",
+ mm: "%d នាទី",
+ h: "មួយម៉ោង",
+ hh: "%d ម៉ោង",
+ d: "មួយថ្ងៃ",
+ dd: "%d ថ្ងៃ",
+ M: "មួយខែ",
+ MM: "%d ខែ",
+ y: "មួយឆ្នាំ",
+ yy: "%d ឆ្នាំ"
+ },
+ week: {
+ dow: 1, // Monday is the first day of the week.
+ doy: 4 // The week that contains Jan 4th is the first week of the year.
+ }
+ });
+}));
+// moment.js language configuration
+// language : korean (ko)
+//
+// authors
+//
+// - Kyungwook, Park : https://github.com/kyungw00k
+// - Jeeeyul Lee <jeeeyul@gmail.com>
+(function (factory) {
+ factory(moment);
+}(function (moment) {
+ return moment.lang('ko', {
+ months : "1월_2월_3월_4월_5월_6월_7월_8월_9월_10월_11월_12월".split("_"),
+ monthsShort : "1월_2월_3월_4월_5월_6월_7월_8월_9월_10월_11월_12월".split("_"),
+ weekdays : "일요일_월요일_화요일_수요일_목요일_금요일_토요일".split("_"),
+ weekdaysShort : "일_월_화_수_목_금_토".split("_"),
+ weekdaysMin : "일_월_화_수_목_금_토".split("_"),
+ longDateFormat : {
+ LT : "A h시 mm분",
+ L : "YYYY.MM.DD",
+ LL : "YYYY년 MMMM D일",
+ LLL : "YYYY년 MMMM D일 LT",
+ LLLL : "YYYY년 MMMM D일 dddd LT"
+ },
+ meridiem : function (hour, minute, isUpper) {
+ return hour < 12 ? '오전' : '오후';
+ },
+ calendar : {
+ sameDay : '오늘 LT',
+ nextDay : '내일 LT',
+ nextWeek : 'dddd LT',
+ lastDay : '어제 LT',
+ lastWeek : '지난주 dddd LT',
+ sameElse : 'L'
+ },
+ relativeTime : {
+ future : "%s 후",
+ past : "%s 전",
+ s : "몇초",
+ ss : "%d초",
+ m : "일분",
+ mm : "%d분",
+ h : "한시간",
+ hh : "%d시간",
+ d : "하루",
+ dd : "%d일",
+ M : "한달",
+ MM : "%d달",
+ y : "일년",
+ yy : "%d년"
+ },
+ ordinal : '%d일',
+ meridiemParse : /(오전|오후)/,
+ isPM : function (token) {
+ return token === "오후";
+ }
+ });
+}));
+// moment.js language configuration
+// language : Luxembourgish (lb)
+// author : mweimerskirch : https://github.com/mweimerskirch
+
+// Note: Luxembourgish has a very particular phonological rule ("Eifeler Regel") that causes the
+// deletion of the final "n" in certain contexts. That's what the "eifelerRegelAppliesToWeekday"
+// and "eifelerRegelAppliesToNumber" methods are meant for
+
+(function (factory) {
+ factory(moment);
+}(function (moment) {
+ function processRelativeTime(number, withoutSuffix, key, isFuture) {
+ var format = {
+ 'm': ['eng Minutt', 'enger Minutt'],
+ 'h': ['eng Stonn', 'enger Stonn'],
+ 'd': ['een Dag', 'engem Dag'],
+ 'dd': [number + ' Deeg', number + ' Deeg'],
+ 'M': ['ee Mount', 'engem Mount'],
+ 'MM': [number + ' Méint', number + ' Méint'],
+ 'y': ['ee Joer', 'engem Joer'],
+ 'yy': [number + ' Joer', number + ' Joer']
+ };
+ return withoutSuffix ? format[key][0] : format[key][1];
+ }
+
+ function processFutureTime(string) {
+ var number = string.substr(0, string.indexOf(' '));
+ if (eifelerRegelAppliesToNumber(number)) {
+ return "a " + string;
+ }
+ return "an " + string;
+ }
+
+ function processPastTime(string) {
+ var number = string.substr(0, string.indexOf(' '));
+ if (eifelerRegelAppliesToNumber(number)) {
+ return "viru " + string;
+ }
+ return "virun " + string;
+ }
+
+ function processLastWeek(string1) {
+ var weekday = this.format('d');
+ if (eifelerRegelAppliesToWeekday(weekday)) {
+ return '[Leschte] dddd [um] LT';
+ }
+ return '[Leschten] dddd [um] LT';
+ }
+
+ /**
+ * Returns true if the word before the given week day loses the "-n" ending.
+ * e.g. "Leschten Dënschdeg" but "Leschte Méindeg"
+ *
+ * @param weekday {integer}
+ * @returns {boolean}
+ */
+ function eifelerRegelAppliesToWeekday(weekday) {
+ weekday = parseInt(weekday, 10);
+ switch (weekday) {
+ case 0: // Sonndeg
+ case 1: // Méindeg
+ case 3: // Mëttwoch
+ case 5: // Freideg
+ case 6: // Samschdeg
+ return true;
+ default: // 2 Dënschdeg, 4 Donneschdeg
+ return false;
+ }
+ }
+
+ /**
+ * Returns true if the word before the given number loses the "-n" ending.
+ * e.g. "an 10 Deeg" but "a 5 Deeg"
+ *
+ * @param number {integer}
+ * @returns {boolean}
+ */
+ function eifelerRegelAppliesToNumber(number) {
+ number = parseInt(number, 10);
+ if (isNaN(number)) {
+ return false;
+ }
+ if (number < 0) {
+ // Negative Number --> always true
+ return true;
+ } else if (number < 10) {
+ // Only 1 digit
+ if (4 <= number && number <= 7) {
+ return true;
+ }
+ return false;
+ } else if (number < 100) {
+ // 2 digits
+ var lastDigit = number % 10, firstDigit = number / 10;
+ if (lastDigit === 0) {
+ return eifelerRegelAppliesToNumber(firstDigit);
+ }
+ return eifelerRegelAppliesToNumber(lastDigit);
+ } else if (number < 10000) {
+ // 3 or 4 digits --> recursively check first digit
+ while (number >= 10) {
+ number = number / 10;
+ }
+ return eifelerRegelAppliesToNumber(number);
+ } else {
+ // Anything larger than 4 digits: recursively check first n-3 digits
+ number = number / 1000;
+ return eifelerRegelAppliesToNumber(number);
+ }
+ }
+
+ return moment.lang('lb', {
+ months: "Januar_Februar_Mäerz_Abrëll_Mee_Juni_Juli_August_September_Oktober_November_Dezember".split("_"),
+ monthsShort: "Jan._Febr._Mrz._Abr._Mee_Jun._Jul._Aug._Sept._Okt._Nov._Dez.".split("_"),
+ weekdays: "Sonndeg_Méindeg_Dënschdeg_Mëttwoch_Donneschdeg_Freideg_Samschdeg".split("_"),
+ weekdaysShort: "So._Mé._Dë._Më._Do._Fr._Sa.".split("_"),
+ weekdaysMin: "So_Mé_Dë_Më_Do_Fr_Sa".split("_"),
+ longDateFormat: {
+ LT: "H:mm [Auer]",
+ L: "DD.MM.YYYY",
+ LL: "D. MMMM YYYY",
+ LLL: "D. MMMM YYYY LT",
+ LLLL: "dddd, D. MMMM YYYY LT"
+ },
+ calendar: {
+ sameDay: "[Haut um] LT",
+ sameElse: "L",
+ nextDay: '[Muer um] LT',
+ nextWeek: 'dddd [um] LT',
+ lastDay: '[Gëschter um] LT',
+ lastWeek: processLastWeek
+ },
+ relativeTime: {
+ future: processFutureTime,
+ past: processPastTime,
+ s: "e puer Sekonnen",
+ m: processRelativeTime,
+ mm: "%d Minutten",
+ h: processRelativeTime,
+ hh: "%d Stonnen",
+ d: processRelativeTime,
+ dd: processRelativeTime,
+ M: processRelativeTime,
+ MM: processRelativeTime,
+ y: processRelativeTime,
+ yy: processRelativeTime
+ },
+ ordinal: '%d.',
+ week: {
+ dow: 1, // Monday is the first day of the week.
+ doy: 4 // The week that contains Jan 4th is the first week of the year.
+ }
+ });
+}));
+// moment.js language configuration
+// language : Lithuanian (lt)
+// author : Mindaugas Mozūras : https://github.com/mmozuras
+
+(function (factory) {
+ factory(moment);
+}(function (moment) {
+ var units = {
+ "m" : "minutė_minutės_minutę",
+ "mm": "minutės_minučių_minutes",
+ "h" : "valanda_valandos_valandą",
+ "hh": "valandos_valandų_valandas",
+ "d" : "diena_dienos_dieną",
+ "dd": "dienos_dienų_dienas",
+ "M" : "mėnuo_mėnesio_mėnesį",
+ "MM": "mėnesiai_mėnesių_mėnesius",
+ "y" : "metai_metų_metus",
+ "yy": "metai_metų_metus"
+ },
+ weekDays = "sekmadienis_pirmadienis_antradienis_trečiadienis_ketvirtadienis_penktadienis_šeštadienis".split("_");
+
+ function translateSeconds(number, withoutSuffix, key, isFuture) {
+ if (withoutSuffix) {
+ return "kelios sekundės";
+ } else {
+ return isFuture ? "kelių sekundžių" : "kelias sekundes";
+ }
+ }
+
+ function translateSingular(number, withoutSuffix, key, isFuture) {
+ return withoutSuffix ? forms(key)[0] : (isFuture ? forms(key)[1] : forms(key)[2]);
+ }
+
+ function special(number) {
+ return number % 10 === 0 || (number > 10 && number < 20);
+ }
+
+ function forms(key) {
+ return units[key].split("_");
+ }
+
+ function translate(number, withoutSuffix, key, isFuture) {
+ var result = number + " ";
+ if (number === 1) {
+ return result + translateSingular(number, withoutSuffix, key[0], isFuture);
+ } else if (withoutSuffix) {
+ return result + (special(number) ? forms(key)[1] : forms(key)[0]);
+ } else {
+ if (isFuture) {
+ return result + forms(key)[1];
+ } else {
+ return result + (special(number) ? forms(key)[1] : forms(key)[2]);
+ }
+ }
+ }
+
+ function relativeWeekDay(moment, format) {
+ var nominative = format.indexOf('dddd HH:mm') === -1,
+ weekDay = weekDays[moment.day()];
+
+ return nominative ? weekDay : weekDay.substring(0, weekDay.length - 2) + "į";
+ }
+
+ return moment.lang("lt", {
+ months : "sausio_vasario_kovo_balandžio_gegužės_biržėlio_liepos_rugpjūčio_rugsėjo_spalio_lapkričio_gruodžio".split("_"),
+ monthsShort : "sau_vas_kov_bal_geg_bir_lie_rgp_rgs_spa_lap_grd".split("_"),
+ weekdays : relativeWeekDay,
+ weekdaysShort : "Sek_Pir_Ant_Tre_Ket_Pen_Šeš".split("_"),
+ weekdaysMin : "S_P_A_T_K_Pn_Š".split("_"),
+ longDateFormat : {
+ LT : "HH:mm",
+ L : "YYYY-MM-DD",
+ LL : "YYYY [m.] MMMM D [d.]",
+ LLL : "YYYY [m.] MMMM D [d.], LT [val.]",
+ LLLL : "YYYY [m.] MMMM D [d.], dddd, LT [val.]",
+ l : "YYYY-MM-DD",
+ ll : "YYYY [m.] MMMM D [d.]",
+ lll : "YYYY [m.] MMMM D [d.], LT [val.]",
+ llll : "YYYY [m.] MMMM D [d.], ddd, LT [val.]"
+ },
+ calendar : {
+ sameDay : "[Šiandien] LT",
+ nextDay : "[Rytoj] LT",
+ nextWeek : "dddd LT",
+ lastDay : "[Vakar] LT",
+ lastWeek : "[Praėjusį] dddd LT",
+ sameElse : "L"
+ },
+ relativeTime : {
+ future : "po %s",
+ past : "prieš %s",
+ s : translateSeconds,
+ m : translateSingular,
+ mm : translate,
+ h : translateSingular,
+ hh : translate,
+ d : translateSingular,
+ dd : translate,
+ M : translateSingular,
+ MM : translate,
+ y : translateSingular,
+ yy : translate
+ },
+ ordinal : function (number) {
+ return number + '-oji';
+ },
+ week : {
+ dow : 1, // Monday is the first day of the week.
+ doy : 4 // The week that contains Jan 4th is the first week of the year.
+ }
+ });
+}));
+// moment.js language configuration
+// language : latvian (lv)
+// author : Kristaps Karlsons : https://github.com/skakri
+
+(function (factory) {
+ factory(moment);
+}(function (moment) {
+ var units = {
+ 'mm': 'minūti_minūtes_minūte_minūtes',
+ 'hh': 'stundu_stundas_stunda_stundas',
+ 'dd': 'dienu_dienas_diena_dienas',
+ 'MM': 'mēnesi_mēnešus_mēnesis_mēneši',
+ 'yy': 'gadu_gadus_gads_gadi'
+ };
+
+ function format(word, number, withoutSuffix) {
+ var forms = word.split('_');
+ if (withoutSuffix) {
+ return number % 10 === 1 && number !== 11 ? forms[2] : forms[3];
+ } else {
+ return number % 10 === 1 && number !== 11 ? forms[0] : forms[1];
+ }
+ }
+
+ function relativeTimeWithPlural(number, withoutSuffix, key) {
+ return number + ' ' + format(units[key], number, withoutSuffix);
+ }
+
+ return moment.lang('lv', {
+ months : "janvāris_februāris_marts_aprīlis_maijs_jūnijs_jūlijs_augusts_septembris_oktobris_novembris_decembris".split("_"),
+ monthsShort : "jan_feb_mar_apr_mai_jūn_jūl_aug_sep_okt_nov_dec".split("_"),
+ weekdays : "svētdiena_pirmdiena_otrdiena_trešdiena_ceturtdiena_piektdiena_sestdiena".split("_"),
+ weekdaysShort : "Sv_P_O_T_C_Pk_S".split("_"),
+ weekdaysMin : "Sv_P_O_T_C_Pk_S".split("_"),
+ longDateFormat : {
+ LT : "HH:mm",
+ L : "DD.MM.YYYY",
+ LL : "YYYY. [gada] D. MMMM",
+ LLL : "YYYY. [gada] D. MMMM, LT",
+ LLLL : "YYYY. [gada] D. MMMM, dddd, LT"
+ },
+ calendar : {
+ sameDay : '[Šodien pulksten] LT',
+ nextDay : '[Rīt pulksten] LT',
+ nextWeek : 'dddd [pulksten] LT',
+ lastDay : '[Vakar pulksten] LT',
+ lastWeek : '[Pagājušā] dddd [pulksten] LT',
+ sameElse : 'L'
+ },
+ relativeTime : {
+ future : "%s vēlāk",
+ past : "%s agrāk",
+ s : "dažas sekundes",
+ m : "minūti",
+ mm : relativeTimeWithPlural,
+ h : "stundu",
+ hh : relativeTimeWithPlural,
+ d : "dienu",
+ dd : relativeTimeWithPlural,
+ M : "mēnesi",
+ MM : relativeTimeWithPlural,
+ y : "gadu",
+ yy : relativeTimeWithPlural
+ },
+ ordinal : '%d.',
+ week : {
+ dow : 1, // Monday is the first day of the week.
+ doy : 4 // The week that contains Jan 4th is the first week of the year.
+ }
+ });
+}));
+// moment.js language configuration
+// language : macedonian (mk)
+// author : Borislav Mickov : https://github.com/B0k0
+
+(function (factory) {
+ factory(moment);
+}(function (moment) {
+ return moment.lang('mk', {
+ months : "јануари_февруари_март_април_мај_јуни_јули_август_септември_октомври_ноември_декември".split("_"),
+ monthsShort : "јан_фев_мар_апр_мај_јун_јул_авг_сеп_окт_ное_дек".split("_"),
+ weekdays : "недела_понеделник_вторник_среда_четврток_петок_сабота".split("_"),
+ weekdaysShort : "нед_пон_вто_сре_чет_пет_саб".split("_"),
+ weekdaysMin : "нe_пo_вт_ср_че_пе_сa".split("_"),
+ longDateFormat : {
+ LT : "H:mm",
+ L : "D.MM.YYYY",
+ LL : "D MMMM YYYY",
+ LLL : "D MMMM YYYY LT",
+ LLLL : "dddd, D MMMM YYYY LT"
+ },
+ calendar : {
+ sameDay : '[Денес во] LT',
+ nextDay : '[Утре во] LT',
+ nextWeek : 'dddd [во] LT',
+ lastDay : '[Вчера во] LT',
+ lastWeek : function () {
+ switch (this.day()) {
+ case 0:
+ case 3:
+ case 6:
+ return '[Во изминатата] dddd [во] LT';
+ case 1:
+ case 2:
+ case 4:
+ case 5:
+ return '[Во изминатиот] dddd [во] LT';
+ }
+ },
+ sameElse : 'L'
+ },
+ relativeTime : {
+ future : "после %s",
+ past : "пред %s",
+ s : "неколку секунди",
+ m : "минута",
+ mm : "%d минути",
+ h : "час",
+ hh : "%d часа",
+ d : "ден",
+ dd : "%d дена",
+ M : "месец",
+ MM : "%d месеци",
+ y : "година",
+ yy : "%d години"
+ },
+ ordinal : function (number) {
+ var lastDigit = number % 10,
+ last2Digits = number % 100;
+ if (number === 0) {
+ return number + '-ев';
+ } else if (last2Digits === 0) {
+ return number + '-ен';
+ } else if (last2Digits > 10 && last2Digits < 20) {
+ return number + '-ти';
+ } else if (lastDigit === 1) {
+ return number + '-ви';
+ } else if (lastDigit === 2) {
+ return number + '-ри';
+ } else if (lastDigit === 7 || lastDigit === 8) {
+ return number + '-ми';
+ } else {
+ return number + '-ти';
+ }
+ },
+ week : {
+ dow : 1, // Monday is the first day of the week.
+ doy : 7 // The week that contains Jan 1st is the first week of the year.
+ }
+ });
+}));
+// moment.js language configuration
+// language : malayalam (ml)
+// author : Floyd Pink : https://github.com/floydpink
+
+(function (factory) {
+ factory(moment);
+}(function (moment) {
+ return moment.lang('ml', {
+ months : 'ജനുവരി_ഫെബ്രുവരി_മാർച്ച്_ഏപ്രിൽ_മേയ്_ജൂൺ_ജൂലൈ_ഓഗസ്റ്റ്_സെപ്റ്റംബർ_ഒക്ടോബർ_നവംബർ_ഡിസംബർ'.split("_"),
+ monthsShort : 'ജനു._ഫെബ്രു._മാർ._ഏപ്രി._മേയ്_ജൂൺ_ജൂലൈ._ഓഗ._സെപ്റ്റ._ഒക്ടോ._നവം._ഡിസം.'.split("_"),
+ weekdays : 'ഞായറാഴ്ച_തിങ്കളാഴ്ച_ചൊവ്വാഴ്ച_ബുധനാഴ്ച_വ്യാഴാഴ്ച_വെള്ളിയാഴ്ച_ശനിയാഴ്ച'.split("_"),
+ weekdaysShort : 'ഞായർ_തിങ്കൾ_ചൊവ്വ_ബുധൻ_വ്യാഴം_വെള്ളി_ശനി'.split("_"),
+ weekdaysMin : 'ഞാ_തി_ചൊ_ബു_വ്യാ_വെ_ശ'.split("_"),
+ longDateFormat : {
+ LT : "A h:mm -നു",
+ L : "DD/MM/YYYY",
+ LL : "D MMMM YYYY",
+ LLL : "D MMMM YYYY, LT",
+ LLLL : "dddd, D MMMM YYYY, LT"
+ },
+ calendar : {
+ sameDay : '[ഇന്ന്] LT',
+ nextDay : '[നാളെ] LT',
+ nextWeek : 'dddd, LT',
+ lastDay : '[ഇന്നലെ] LT',
+ lastWeek : '[കഴിഞ്ഞ] dddd, LT',
+ sameElse : 'L'
+ },
+ relativeTime : {
+ future : "%s കഴിഞ്ഞ്",
+ past : "%s മുൻപ്",
+ s : "അൽപ നിമിഷങ്ങൾ",
+ m : "ഒരു മിനിറ്റ്",
+ mm : "%d മിനിറ്റ്",
+ h : "ഒരു മണിക്കൂർ",
+ hh : "%d മണിക്കൂർ",
+ d : "ഒരു ദിവസം",
+ dd : "%d ദിവസം",
+ M : "ഒരു മാസം",
+ MM : "%d മാസം",
+ y : "ഒരു വർഷം",
+ yy : "%d വർഷം"
+ },
+ meridiem : function (hour, minute, isLower) {
+ if (hour < 4) {
+ return "രാത്രി";
+ } else if (hour < 12) {
+ return "രാവിലെ";
+ } else if (hour < 17) {
+ return "ഉച്ച കഴിഞ്ഞ്";
+ } else if (hour < 20) {
+ return "വൈകുന്നേരം";
+ } else {
+ return "രാത്രി";
+ }
+ }
+ });
+}));
+// moment.js language configuration
+// language : Marathi (mr)
+// author : Harshad Kale : https://github.com/kalehv
+
+(function (factory) {
+ factory(moment);
+}(function (moment) {
+ var symbolMap = {
+ '1': '१',
+ '2': '२',
+ '3': '३',
+ '4': '४',
+ '5': '५',
+ '6': '६',
+ '7': '७',
+ '8': '८',
+ '9': '९',
+ '0': '०'
+ },
+ numberMap = {
+ '१': '1',
+ '२': '2',
+ '३': '3',
+ '४': '4',
+ '५': '5',
+ '६': '6',
+ '७': '7',
+ '८': '8',
+ '९': '9',
+ '०': '0'
+ };
+
+ return moment.lang('mr', {
+ months : 'जानेवारी_फेब्रुवारी_मार्च_एप्रिल_मे_जून_जुलै_ऑगस्ट_सप्टेंबर_ऑक्टोबर_नोव्हेंबर_डिसेंबर'.split("_"),
+ monthsShort: 'जाने._फेब्रु._मार्च._एप्रि._मे._जून._जुलै._ऑग._सप्टें._ऑक्टो._नोव्हें._डिसें.'.split("_"),
+ weekdays : 'रविवार_सोमवार_मंगळवार_बुधवार_गुरूवार_शुक्रवार_शनिवार'.split("_"),
+ weekdaysShort : 'रवि_सोम_मंगळ_बुध_गुरू_शुक्र_शनि'.split("_"),
+ weekdaysMin : 'र_सो_मं_बु_गु_शु_श'.split("_"),
+ longDateFormat : {
+ LT : "A h:mm वाजता",
+ L : "DD/MM/YYYY",
+ LL : "D MMMM YYYY",
+ LLL : "D MMMM YYYY, LT",
+ LLLL : "dddd, D MMMM YYYY, LT"
+ },
+ calendar : {
+ sameDay : '[आज] LT',
+ nextDay : '[उद्या] LT',
+ nextWeek : 'dddd, LT',
+ lastDay : '[काल] LT',
+ lastWeek: '[मागील] dddd, LT',
+ sameElse : 'L'
+ },
+ relativeTime : {
+ future : "%s नंतर",
+ past : "%s पूर्वी",
+ s : "सेकंद",
+ m: "एक मिनिट",
+ mm: "%d मिनिटे",
+ h : "एक तास",
+ hh : "%d तास",
+ d : "एक दिवस",
+ dd : "%d दिवस",
+ M : "एक महिना",
+ MM : "%d महिने",
+ y : "एक वर्ष",
+ yy : "%d वर्षे"
+ },
+ preparse: function (string) {
+ return string.replace(/[१२३४५६७८९०]/g, function (match) {
+ return numberMap[match];
+ });
+ },
+ postformat: function (string) {
+ return string.replace(/\d/g, function (match) {
+ return symbolMap[match];
+ });
+ },
+ meridiem: function (hour, minute, isLower)
+ {
+ if (hour < 4) {
+ return "रात्री";
+ } else if (hour < 10) {
+ return "सकाळी";
+ } else if (hour < 17) {
+ return "दुपारी";
+ } else if (hour < 20) {
+ return "सायंकाळी";
+ } else {
+ return "रात्री";
+ }
+ },
+ week : {
+ dow : 0, // Sunday is the first day of the week.
+ doy : 6 // The week that contains Jan 1st is the first week of the year.
+ }
+ });
+}));
+// moment.js language configuration
+// language : Bahasa Malaysia (ms-MY)
+// author : Weldan Jamili : https://github.com/weldan
+
+(function (factory) {
+ factory(moment);
+}(function (moment) {
+ return moment.lang('ms-my', {
+ months : "Januari_Februari_Mac_April_Mei_Jun_Julai_Ogos_September_Oktober_November_Disember".split("_"),
+ monthsShort : "Jan_Feb_Mac_Apr_Mei_Jun_Jul_Ogs_Sep_Okt_Nov_Dis".split("_"),
+ weekdays : "Ahad_Isnin_Selasa_Rabu_Khamis_Jumaat_Sabtu".split("_"),
+ weekdaysShort : "Ahd_Isn_Sel_Rab_Kha_Jum_Sab".split("_"),
+ weekdaysMin : "Ah_Is_Sl_Rb_Km_Jm_Sb".split("_"),
+ longDateFormat : {
+ LT : "HH.mm",
+ L : "DD/MM/YYYY",
+ LL : "D MMMM YYYY",
+ LLL : "D MMMM YYYY [pukul] LT",
+ LLLL : "dddd, D MMMM YYYY [pukul] LT"
+ },
+ meridiem : function (hours, minutes, isLower) {
+ if (hours < 11) {
+ return 'pagi';
+ } else if (hours < 15) {
+ return 'tengahari';
+ } else if (hours < 19) {
+ return 'petang';
+ } else {
+ return 'malam';
+ }
+ },
+ calendar : {
+ sameDay : '[Hari ini pukul] LT',
+ nextDay : '[Esok pukul] LT',
+ nextWeek : 'dddd [pukul] LT',
+ lastDay : '[Kelmarin pukul] LT',
+ lastWeek : 'dddd [lepas pukul] LT',
+ sameElse : 'L'
+ },
+ relativeTime : {
+ future : "dalam %s",
+ past : "%s yang lepas",
+ s : "beberapa saat",
+ m : "seminit",
+ mm : "%d minit",
+ h : "sejam",
+ hh : "%d jam",
+ d : "sehari",
+ dd : "%d hari",
+ M : "sebulan",
+ MM : "%d bulan",
+ y : "setahun",
+ yy : "%d tahun"
+ },
+ week : {
+ dow : 1, // Monday is the first day of the week.
+ doy : 7 // The week that contains Jan 1st is the first week of the year.
+ }
+ });
+}));
+// moment.js language configuration
+// language : norwegian bokmål (nb)
+// authors : Espen Hovlandsdal : https://github.com/rexxars
+// Sigurd Gartmann : https://github.com/sigurdga
+
+(function (factory) {
+ factory(moment);
+}(function (moment) {
+ return moment.lang('nb', {
+ months : "januar_februar_mars_april_mai_juni_juli_august_september_oktober_november_desember".split("_"),
+ monthsShort : "jan._feb._mars_april_mai_juni_juli_aug._sep._okt._nov._des.".split("_"),
+ weekdays : "søndag_mandag_tirsdag_onsdag_torsdag_fredag_lørdag".split("_"),
+ weekdaysShort : "sø._ma._ti._on._to._fr._lø.".split("_"),
+ weekdaysMin : "sø_ma_ti_on_to_fr_lø".split("_"),
+ longDateFormat : {
+ LT : "H.mm",
+ L : "DD.MM.YYYY",
+ LL : "D. MMMM YYYY",
+ LLL : "D. MMMM YYYY [kl.] LT",
+ LLLL : "dddd D. MMMM YYYY [kl.] LT"
+ },
+ calendar : {
+ sameDay: '[i dag kl.] LT',
+ nextDay: '[i morgen kl.] LT',
+ nextWeek: 'dddd [kl.] LT',
+ lastDay: '[i går kl.] LT',
+ lastWeek: '[forrige] dddd [kl.] LT',
+ sameElse: 'L'
+ },
+ relativeTime : {
+ future : "om %s",
+ past : "for %s siden",
+ s : "noen sekunder",
+ m : "ett minutt",
+ mm : "%d minutter",
+ h : "en time",
+ hh : "%d timer",
+ d : "en dag",
+ dd : "%d dager",
+ M : "en måned",
+ MM : "%d måneder",
+ y : "ett år",
+ yy : "%d år"
+ },
+ ordinal : '%d.',
+ week : {
+ dow : 1, // Monday is the first day of the week.
+ doy : 4 // The week that contains Jan 4th is the first week of the year.
+ }
+ });
+}));
+// moment.js language configuration
+// language : nepali/nepalese
+// author : suvash : https://github.com/suvash
+
+(function (factory) {
+ factory(moment);
+}(function (moment) {
+ var symbolMap = {
+ '1': '१',
+ '2': '२',
+ '3': '३',
+ '4': '४',
+ '5': '५',
+ '6': '६',
+ '7': '७',
+ '8': '८',
+ '9': '९',
+ '0': '०'
+ },
+ numberMap = {
+ '१': '1',
+ '२': '2',
+ '३': '3',
+ '४': '4',
+ '५': '5',
+ '६': '6',
+ '७': '7',
+ '८': '8',
+ '९': '9',
+ '०': '0'
+ };
+
+ return moment.lang('ne', {
+ months : 'जनवरी_फेब्रुवरी_मार्च_अप्रिल_मई_जुन_जुलाई_अगष्ट_सेप्टेम्बर_अक्टोबर_नोभेम्बर_डिसेम्बर'.split("_"),
+ monthsShort : 'जन._फेब्रु._मार्च_अप्रि._मई_जुन_जुलाई._अग._सेप्ट._अक्टो._नोभे._डिसे.'.split("_"),
+ weekdays : 'आइतबार_सोमबार_मङ्गलबार_बुधबार_बिहिबार_शुक्रबार_शनिबार'.split("_"),
+ weekdaysShort : 'आइत._सोम._मङ्गल._बुध._बिहि._शुक्र._शनि.'.split("_"),
+ weekdaysMin : 'आइ._सो._मङ्_बु._बि._शु._श.'.split("_"),
+ longDateFormat : {
+ LT : "Aको h:mm बजे",
+ L : "DD/MM/YYYY",
+ LL : "D MMMM YYYY",
+ LLL : "D MMMM YYYY, LT",
+ LLLL : "dddd, D MMMM YYYY, LT"
+ },
+ preparse: function (string) {
+ return string.replace(/[१२३४५६७८९०]/g, function (match) {
+ return numberMap[match];
+ });
+ },
+ postformat: function (string) {
+ return string.replace(/\d/g, function (match) {
+ return symbolMap[match];
+ });
+ },
+ meridiem : function (hour, minute, isLower) {
+ if (hour < 3) {
+ return "राती";
+ } else if (hour < 10) {
+ return "बिहान";
+ } else if (hour < 15) {
+ return "दिउँसो";
+ } else if (hour < 18) {
+ return "बेलुका";
+ } else if (hour < 20) {
+ return "साँझ";
+ } else {
+ return "राती";
+ }
+ },
+ calendar : {
+ sameDay : '[आज] LT',
+ nextDay : '[भोली] LT',
+ nextWeek : '[आउँदो] dddd[,] LT',
+ lastDay : '[हिजो] LT',
+ lastWeek : '[गएको] dddd[,] LT',
+ sameElse : 'L'
+ },
+ relativeTime : {
+ future : "%sमा",
+ past : "%s अगाडी",
+ s : "केही समय",
+ m : "एक मिनेट",
+ mm : "%d मिनेट",
+ h : "एक घण्टा",
+ hh : "%d घण्टा",
+ d : "एक दिन",
+ dd : "%d दिन",
+ M : "एक महिना",
+ MM : "%d महिना",
+ y : "एक बर्ष",
+ yy : "%d बर्ष"
+ },
+ week : {
+ dow : 1, // Monday is the first day of the week.
+ doy : 7 // The week that contains Jan 1st is the first week of the year.
+ }
+ });
+}));
+// moment.js language configuration
+// language : dutch (nl)
+// author : Joris Röling : https://github.com/jjupiter
+
+(function (factory) {
+ factory(moment);
+}(function (moment) {
+ var monthsShortWithDots = "jan._feb._mrt._apr._mei_jun._jul._aug._sep._okt._nov._dec.".split("_"),
+ monthsShortWithoutDots = "jan_feb_mrt_apr_mei_jun_jul_aug_sep_okt_nov_dec".split("_");
+
+ return moment.lang('nl', {
+ months : "januari_februari_maart_april_mei_juni_juli_augustus_september_oktober_november_december".split("_"),
+ monthsShort : function (m, format) {
+ if (/-MMM-/.test(format)) {
+ return monthsShortWithoutDots[m.month()];
+ } else {
+ return monthsShortWithDots[m.month()];
+ }
+ },
+ weekdays : "zondag_maandag_dinsdag_woensdag_donderdag_vrijdag_zaterdag".split("_"),
+ weekdaysShort : "zo._ma._di._wo._do._vr._za.".split("_"),
+ weekdaysMin : "Zo_Ma_Di_Wo_Do_Vr_Za".split("_"),
+ longDateFormat : {
+ LT : "HH:mm",
+ L : "DD-MM-YYYY",
+ LL : "D MMMM YYYY",
+ LLL : "D MMMM YYYY LT",
+ LLLL : "dddd D MMMM YYYY LT"
+ },
+ calendar : {
+ sameDay: '[vandaag om] LT',
+ nextDay: '[morgen om] LT',
+ nextWeek: 'dddd [om] LT',
+ lastDay: '[gisteren om] LT',
+ lastWeek: '[afgelopen] dddd [om] LT',
+ sameElse: 'L'
+ },
+ relativeTime : {
+ future : "over %s",
+ past : "%s geleden",
+ s : "een paar seconden",
+ m : "één minuut",
+ mm : "%d minuten",
+ h : "één uur",
+ hh : "%d uur",
+ d : "één dag",
+ dd : "%d dagen",
+ M : "één maand",
+ MM : "%d maanden",
+ y : "één jaar",
+ yy : "%d jaar"
+ },
+ ordinal : function (number) {
+ return number + ((number === 1 || number === 8 || number >= 20) ? 'ste' : 'de');
+ },
+ week : {
+ dow : 1, // Monday is the first day of the week.
+ doy : 4 // The week that contains Jan 4th is the first week of the year.
+ }
+ });
+}));
+// moment.js language configuration
+// language : norwegian nynorsk (nn)
+// author : https://github.com/mechuwind
+
+(function (factory) {
+ factory(moment);
+}(function (moment) {
+ return moment.lang('nn', {
+ months : "januar_februar_mars_april_mai_juni_juli_august_september_oktober_november_desember".split("_"),
+ monthsShort : "jan_feb_mar_apr_mai_jun_jul_aug_sep_okt_nov_des".split("_"),
+ weekdays : "sundag_måndag_tysdag_onsdag_torsdag_fredag_laurdag".split("_"),
+ weekdaysShort : "sun_mån_tys_ons_tor_fre_lau".split("_"),
+ weekdaysMin : "su_må_ty_on_to_fr_lø".split("_"),
+ longDateFormat : {
+ LT : "HH:mm",
+ L : "DD.MM.YYYY",
+ LL : "D MMMM YYYY",
+ LLL : "D MMMM YYYY LT",
+ LLLL : "dddd D MMMM YYYY LT"
+ },
+ calendar : {
+ sameDay: '[I dag klokka] LT',
+ nextDay: '[I morgon klokka] LT',
+ nextWeek: 'dddd [klokka] LT',
+ lastDay: '[I går klokka] LT',
+ lastWeek: '[Føregåande] dddd [klokka] LT',
+ sameElse: 'L'
+ },
+ relativeTime : {
+ future : "om %s",
+ past : "for %s sidan",
+ s : "nokre sekund",
+ m : "eit minutt",
+ mm : "%d minutt",
+ h : "ein time",
+ hh : "%d timar",
+ d : "ein dag",
+ dd : "%d dagar",
+ M : "ein månad",
+ MM : "%d månader",
+ y : "eit år",
+ yy : "%d år"
+ },
+ ordinal : '%d.',
+ week : {
+ dow : 1, // Monday is the first day of the week.
+ doy : 4 // The week that contains Jan 4th is the first week of the year.
+ }
+ });
+}));
+// moment.js language configuration
+// language : polish (pl)
+// author : Rafal Hirsz : https://github.com/evoL
+
+(function (factory) {
+ factory(moment);
+}(function (moment) {
+ var monthsNominative = "styczeń_luty_marzec_kwiecień_maj_czerwiec_lipiec_sierpień_wrzesień_październik_listopad_grudzień".split("_"),
+ monthsSubjective = "stycznia_lutego_marca_kwietnia_maja_czerwca_lipca_sierpnia_września_października_listopada_grudnia".split("_");
+
+ function plural(n) {
+ return (n % 10 < 5) && (n % 10 > 1) && ((~~(n / 10) % 10) !== 1);
+ }
+
+ function translate(number, withoutSuffix, key) {
+ var result = number + " ";
+ switch (key) {
+ case 'm':
+ return withoutSuffix ? 'minuta' : 'minutę';
+ case 'mm':
+ return result + (plural(number) ? 'minuty' : 'minut');
+ case 'h':
+ return withoutSuffix ? 'godzina' : 'godzinę';
+ case 'hh':
+ return result + (plural(number) ? 'godziny' : 'godzin');
+ case 'MM':
+ return result + (plural(number) ? 'miesiące' : 'miesięcy');
+ case 'yy':
+ return result + (plural(number) ? 'lata' : 'lat');
+ }
+ }
+
+ return moment.lang('pl', {
+ months : function (momentToFormat, format) {
+ if (/D MMMM/.test(format)) {
+ return monthsSubjective[momentToFormat.month()];
+ } else {
+ return monthsNominative[momentToFormat.month()];
+ }
+ },
+ monthsShort : "sty_lut_mar_kwi_maj_cze_lip_sie_wrz_paź_lis_gru".split("_"),
+ weekdays : "niedziela_poniedziałek_wtorek_środa_czwartek_piątek_sobota".split("_"),
+ weekdaysShort : "nie_pon_wt_śr_czw_pt_sb".split("_"),
+ weekdaysMin : "N_Pn_Wt_Śr_Cz_Pt_So".split("_"),
+ longDateFormat : {
+ LT : "HH:mm",
+ L : "DD.MM.YYYY",
+ LL : "D MMMM YYYY",
+ LLL : "D MMMM YYYY LT",
+ LLLL : "dddd, D MMMM YYYY LT"
+ },
+ calendar : {
+ sameDay: '[Dziś o] LT',
+ nextDay: '[Jutro o] LT',
+ nextWeek: '[W] dddd [o] LT',
+ lastDay: '[Wczoraj o] LT',
+ lastWeek: function () {
+ switch (this.day()) {
+ case 0:
+ return '[W zeszłą niedzielę o] LT';
+ case 3:
+ return '[W zeszłą środę o] LT';
+ case 6:
+ return '[W zeszłą sobotę o] LT';
+ default:
+ return '[W zeszły] dddd [o] LT';
+ }
+ },
+ sameElse: 'L'
+ },
+ relativeTime : {
+ future : "za %s",
+ past : "%s temu",
+ s : "kilka sekund",
+ m : translate,
+ mm : translate,
+ h : translate,
+ hh : translate,
+ d : "1 dzień",
+ dd : '%d dni',
+ M : "miesiąc",
+ MM : translate,
+ y : "rok",
+ yy : translate
+ },
+ ordinal : '%d.',
+ week : {
+ dow : 1, // Monday is the first day of the week.
+ doy : 4 // The week that contains Jan 4th is the first week of the year.
+ }
+ });
+}));
+// moment.js language configuration
+// language : brazilian portuguese (pt-br)
+// author : Caio Ribeiro Pereira : https://github.com/caio-ribeiro-pereira
+
+(function (factory) {
+ factory(moment);
+}(function (moment) {
+ return moment.lang('pt-br', {
+ months : "janeiro_fevereiro_março_abril_maio_junho_julho_agosto_setembro_outubro_novembro_dezembro".split("_"),
+ monthsShort : "jan_fev_mar_abr_mai_jun_jul_ago_set_out_nov_dez".split("_"),
+ weekdays : "domingo_segunda-feira_terça-feira_quarta-feira_quinta-feira_sexta-feira_sábado".split("_"),
+ weekdaysShort : "dom_seg_ter_qua_qui_sex_sáb".split("_"),
+ weekdaysMin : "dom_2ª_3ª_4ª_5ª_6ª_sáb".split("_"),
+ longDateFormat : {
+ LT : "HH:mm",
+ L : "DD/MM/YYYY",
+ LL : "D [de] MMMM [de] YYYY",
+ LLL : "D [de] MMMM [de] YYYY [às] LT",
+ LLLL : "dddd, D [de] MMMM [de] YYYY [às] LT"
+ },
+ calendar : {
+ sameDay: '[Hoje às] LT',
+ nextDay: '[Amanhã às] LT',
+ nextWeek: 'dddd [às] LT',
+ lastDay: '[Ontem às] LT',
+ lastWeek: function () {
+ return (this.day() === 0 || this.day() === 6) ?
+ '[Último] dddd [às] LT' : // Saturday + Sunday
+ '[Última] dddd [às] LT'; // Monday - Friday
+ },
+ sameElse: 'L'
+ },
+ relativeTime : {
+ future : "em %s",
+ past : "%s atrás",
+ s : "segundos",
+ m : "um minuto",
+ mm : "%d minutos",
+ h : "uma hora",
+ hh : "%d horas",
+ d : "um dia",
+ dd : "%d dias",
+ M : "um mês",
+ MM : "%d meses",
+ y : "um ano",
+ yy : "%d anos"
+ },
+ ordinal : '%dº'
+ });
+}));
+// moment.js language configuration
+// language : portuguese (pt)
+// author : Jefferson : https://github.com/jalex79
+
+(function (factory) {
+ factory(moment);
+}(function (moment) {
+ return moment.lang('pt', {
+ months : "janeiro_fevereiro_março_abril_maio_junho_julho_agosto_setembro_outubro_novembro_dezembro".split("_"),
+ monthsShort : "jan_fev_mar_abr_mai_jun_jul_ago_set_out_nov_dez".split("_"),
+ weekdays : "domingo_segunda-feira_terça-feira_quarta-feira_quinta-feira_sexta-feira_sábado".split("_"),
+ weekdaysShort : "dom_seg_ter_qua_qui_sex_sáb".split("_"),
+ weekdaysMin : "dom_2ª_3ª_4ª_5ª_6ª_sáb".split("_"),
+ longDateFormat : {
+ LT : "HH:mm",
+ L : "DD/MM/YYYY",
+ LL : "D [de] MMMM [de] YYYY",
+ LLL : "D [de] MMMM [de] YYYY LT",
+ LLLL : "dddd, D [de] MMMM [de] YYYY LT"
+ },
+ calendar : {
+ sameDay: '[Hoje às] LT',
+ nextDay: '[Amanhã às] LT',
+ nextWeek: 'dddd [às] LT',
+ lastDay: '[Ontem às] LT',
+ lastWeek: function () {
+ return (this.day() === 0 || this.day() === 6) ?
+ '[Último] dddd [às] LT' : // Saturday + Sunday
+ '[Última] dddd [às] LT'; // Monday - Friday
+ },
+ sameElse: 'L'
+ },
+ relativeTime : {
+ future : "em %s",
+ past : "há %s",
+ s : "segundos",
+ m : "um minuto",
+ mm : "%d minutos",
+ h : "uma hora",
+ hh : "%d horas",
+ d : "um dia",
+ dd : "%d dias",
+ M : "um mês",
+ MM : "%d meses",
+ y : "um ano",
+ yy : "%d anos"
+ },
+ ordinal : '%dº',
+ week : {
+ dow : 1, // Monday is the first day of the week.
+ doy : 4 // The week that contains Jan 4th is the first week of the year.
+ }
+ });
+}));
+// moment.js language configuration
+// language : romanian (ro)
+// author : Vlad Gurdiga : https://github.com/gurdiga
+// author : Valentin Agachi : https://github.com/avaly
+
+(function (factory) {
+ factory(moment);
+}(function (moment) {
+ function relativeTimeWithPlural(number, withoutSuffix, key) {
+ var format = {
+ 'mm': 'minute',
+ 'hh': 'ore',
+ 'dd': 'zile',
+ 'MM': 'luni',
+ 'yy': 'ani'
+ },
+ separator = ' ';
+ if (number % 100 >= 20 || (number >= 100 && number % 100 === 0)) {
+ separator = ' de ';
+ }
+
+ return number + separator + format[key];
+ }
+
+ return moment.lang('ro', {
+ months : "ianuarie_februarie_martie_aprilie_mai_iunie_iulie_august_septembrie_octombrie_noiembrie_decembrie".split("_"),
+ monthsShort : "ian._febr._mart._apr._mai_iun._iul._aug._sept._oct._nov._dec.".split("_"),
+ weekdays : "duminică_luni_marți_miercuri_joi_vineri_sâmbătă".split("_"),
+ weekdaysShort : "Dum_Lun_Mar_Mie_Joi_Vin_Sâm".split("_"),
+ weekdaysMin : "Du_Lu_Ma_Mi_Jo_Vi_Sâ".split("_"),
+ longDateFormat : {
+ LT : "H:mm",
+ L : "DD.MM.YYYY",
+ LL : "D MMMM YYYY",
+ LLL : "D MMMM YYYY H:mm",
+ LLLL : "dddd, D MMMM YYYY H:mm"
+ },
+ calendar : {
+ sameDay: "[azi la] LT",
+ nextDay: '[mâine la] LT',
+ nextWeek: 'dddd [la] LT',
+ lastDay: '[ieri la] LT',
+ lastWeek: '[fosta] dddd [la] LT',
+ sameElse: 'L'
+ },
+ relativeTime : {
+ future : "peste %s",
+ past : "%s în urmă",
+ s : "câteva secunde",
+ m : "un minut",
+ mm : relativeTimeWithPlural,
+ h : "o oră",
+ hh : relativeTimeWithPlural,
+ d : "o zi",
+ dd : relativeTimeWithPlural,
+ M : "o lună",
+ MM : relativeTimeWithPlural,
+ y : "un an",
+ yy : relativeTimeWithPlural
+ },
+ week : {
+ dow : 1, // Monday is the first day of the week.
+ doy : 7 // The week that contains Jan 1st is the first week of the year.
+ }
+ });
+}));
+// moment.js language configuration
+// language : russian (ru)
+// author : Viktorminator : https://github.com/Viktorminator
+// Author : Menelion Elensúle : https://github.com/Oire
+
+(function (factory) {
+ factory(moment);
+}(function (moment) {
+ function plural(word, num) {
+ var forms = word.split('_');
+ return num % 10 === 1 && num % 100 !== 11 ? forms[0] : (num % 10 >= 2 && num % 10 <= 4 && (num % 100 < 10 || num % 100 >= 20) ? forms[1] : forms[2]);
+ }
+
+ function relativeTimeWithPlural(number, withoutSuffix, key) {
+ var format = {
+ 'mm': withoutSuffix ? 'минута_минуты_минут' : 'минуту_минуты_минут',
+ 'hh': 'час_часа_часов',
+ 'dd': 'день_дня_дней',
+ 'MM': 'месяц_месяца_месяцев',
+ 'yy': 'год_года_лет'
+ };
+ if (key === 'm') {
+ return withoutSuffix ? 'минута' : 'минуту';
+ }
+ else {
+ return number + ' ' + plural(format[key], +number);
+ }
+ }
+
+ function monthsCaseReplace(m, format) {
+ var months = {
+ 'nominative': 'январь_февраль_март_апрель_май_июнь_июль_август_сентябрь_октябрь_ноябрь_декабрь'.split('_'),
+ 'accusative': 'января_февраля_марта_апреля_мая_июня_июля_августа_сентября_октября_ноября_декабря'.split('_')
+ },
+
+ nounCase = (/D[oD]?(\[[^\[\]]*\]|\s+)+MMMM?/).test(format) ?
+ 'accusative' :
+ 'nominative';
+
+ return months[nounCase][m.month()];
+ }
+
+ function monthsShortCaseReplace(m, format) {
+ var monthsShort = {
+ 'nominative': 'янв_фев_мар_апр_май_июнь_июль_авг_сен_окт_ноя_дек'.split('_'),
+ 'accusative': 'янв_фев_мар_апр_мая_июня_июля_авг_сен_окт_ноя_дек'.split('_')
+ },
+
+ nounCase = (/D[oD]?(\[[^\[\]]*\]|\s+)+MMMM?/).test(format) ?
+ 'accusative' :
+ 'nominative';
+
+ return monthsShort[nounCase][m.month()];
+ }
+
+ function weekdaysCaseReplace(m, format) {
+ var weekdays = {
+ 'nominative': 'воскресенье_понедельник_вторник_среда_четверг_пятница_суббота'.split('_'),
+ 'accusative': 'воскресенье_понедельник_вторник_среду_четверг_пятницу_субботу'.split('_')
+ },
+
+ nounCase = (/\[ ?[Вв] ?(?:прошлую|следующую)? ?\] ?dddd/).test(format) ?
+ 'accusative' :
+ 'nominative';
+
+ return weekdays[nounCase][m.day()];
+ }
+
+ return moment.lang('ru', {
+ months : monthsCaseReplace,
+ monthsShort : monthsShortCaseReplace,
+ weekdays : weekdaysCaseReplace,
+ weekdaysShort : "вс_пн_вт_ср_чт_пт_сб".split("_"),
+ weekdaysMin : "вс_пн_вт_ср_чт_пт_сб".split("_"),
+ monthsParse : [/^янв/i, /^фев/i, /^мар/i, /^апр/i, /^ма[й|я]/i, /^июн/i, /^июл/i, /^авг/i, /^сен/i, /^окт/i, /^ноя/i, /^дек/i],
+ longDateFormat : {
+ LT : "HH:mm",
+ L : "DD.MM.YYYY",
+ LL : "D MMMM YYYY г.",
+ LLL : "D MMMM YYYY г., LT",
+ LLLL : "dddd, D MMMM YYYY г., LT"
+ },
+ calendar : {
+ sameDay: '[Сегодня в] LT',
+ nextDay: '[Завтра в] LT',
+ lastDay: '[Вчера в] LT',
+ nextWeek: function () {
+ return this.day() === 2 ? '[Во] dddd [в] LT' : '[В] dddd [в] LT';
+ },
+ lastWeek: function () {
+ switch (this.day()) {
+ case 0:
+ return '[В прошлое] dddd [в] LT';
+ case 1:
+ case 2:
+ case 4:
+ return '[В прошлый] dddd [в] LT';
+ case 3:
+ case 5:
+ case 6:
+ return '[В прошлую] dddd [в] LT';
+ }
+ },
+ sameElse: 'L'
+ },
+ relativeTime : {
+ future : "через %s",
+ past : "%s назад",
+ s : "несколько секунд",
+ m : relativeTimeWithPlural,
+ mm : relativeTimeWithPlural,
+ h : "час",
+ hh : relativeTimeWithPlural,
+ d : "день",
+ dd : relativeTimeWithPlural,
+ M : "месяц",
+ MM : relativeTimeWithPlural,
+ y : "год",
+ yy : relativeTimeWithPlural
+ },
+
+ meridiemParse: /ночи|утра|дня|вечера/i,
+ isPM : function (input) {
+ return /^(дня|вечера)$/.test(input);
+ },
+
+ meridiem : function (hour, minute, isLower) {
+ if (hour < 4) {
+ return "ночи";
+ } else if (hour < 12) {
+ return "утра";
+ } else if (hour < 17) {
+ return "дня";
+ } else {
+ return "вечера";
+ }
+ },
+
+ ordinal: function (number, period) {
+ switch (period) {
+ case 'M':
+ case 'd':
+ case 'DDD':
+ return number + '-й';
+ case 'D':
+ return number + '-го';
+ case 'w':
+ case 'W':
+ return number + '-я';
+ default:
+ return number;
+ }
+ },
+
+ week : {
+ dow : 1, // Monday is the first day of the week.
+ doy : 7 // The week that contains Jan 1st is the first week of the year.
+ }
+ });
+}));
+// moment.js language configuration
+// language : slovak (sk)
+// author : Martin Minka : https://github.com/k2s
+// based on work of petrbela : https://github.com/petrbela
+
+(function (factory) {
+ factory(moment);
+}(function (moment) {
+ var months = "január_február_marec_apríl_máj_jún_júl_august_september_október_november_december".split("_"),
+ monthsShort = "jan_feb_mar_apr_máj_jún_júl_aug_sep_okt_nov_dec".split("_");
+
+ function plural(n) {
+ return (n > 1) && (n < 5);
+ }
+
+ function translate(number, withoutSuffix, key, isFuture) {
+ var result = number + " ";
+ switch (key) {
+ case 's': // a few seconds / in a few seconds / a few seconds ago
+ return (withoutSuffix || isFuture) ? 'pár sekúnd' : 'pár sekundami';
+ case 'm': // a minute / in a minute / a minute ago
+ return withoutSuffix ? 'minúta' : (isFuture ? 'minútu' : 'minútou');
+ case 'mm': // 9 minutes / in 9 minutes / 9 minutes ago
+ if (withoutSuffix || isFuture) {
+ return result + (plural(number) ? 'minúty' : 'minút');
+ } else {
+ return result + 'minútami';
+ }
+ break;
+ case 'h': // an hour / in an hour / an hour ago
+ return withoutSuffix ? 'hodina' : (isFuture ? 'hodinu' : 'hodinou');
+ case 'hh': // 9 hours / in 9 hours / 9 hours ago
+ if (withoutSuffix || isFuture) {
+ return result + (plural(number) ? 'hodiny' : 'hodín');
+ } else {
+ return result + 'hodinami';
+ }
+ break;
+ case 'd': // a day / in a day / a day ago
+ return (withoutSuffix || isFuture) ? 'deň' : 'dňom';
+ case 'dd': // 9 days / in 9 days / 9 days ago
+ if (withoutSuffix || isFuture) {
+ return result + (plural(number) ? 'dni' : 'dní');
+ } else {
+ return result + 'dňami';
+ }
+ break;
+ case 'M': // a month / in a month / a month ago
+ return (withoutSuffix || isFuture) ? 'mesiac' : 'mesiacom';
+ case 'MM': // 9 months / in 9 months / 9 months ago
+ if (withoutSuffix || isFuture) {
+ return result + (plural(number) ? 'mesiace' : 'mesiacov');
+ } else {
+ return result + 'mesiacmi';
+ }
+ break;
+ case 'y': // a year / in a year / a year ago
+ return (withoutSuffix || isFuture) ? 'rok' : 'rokom';
+ case 'yy': // 9 years / in 9 years / 9 years ago
+ if (withoutSuffix || isFuture) {
+ return result + (plural(number) ? 'roky' : 'rokov');
+ } else {
+ return result + 'rokmi';
+ }
+ break;
+ }
+ }
+
+ return moment.lang('sk', {
+ months : months,
+ monthsShort : monthsShort,
+ monthsParse : (function (months, monthsShort) {
+ var i, _monthsParse = [];
+ for (i = 0; i < 12; i++) {
+ // use custom parser to solve problem with July (červenec)
+ _monthsParse[i] = new RegExp('^' + months[i] + '$|^' + monthsShort[i] + '$', 'i');
+ }
+ return _monthsParse;
+ }(months, monthsShort)),
+ weekdays : "nedeľa_pondelok_utorok_streda_štvrtok_piatok_sobota".split("_"),
+ weekdaysShort : "ne_po_ut_st_št_pi_so".split("_"),
+ weekdaysMin : "ne_po_ut_st_št_pi_so".split("_"),
+ longDateFormat : {
+ LT: "H:mm",
+ L : "DD.MM.YYYY",
+ LL : "D. MMMM YYYY",
+ LLL : "D. MMMM YYYY LT",
+ LLLL : "dddd D. MMMM YYYY LT"
+ },
+ calendar : {
+ sameDay: "[dnes o] LT",
+ nextDay: '[zajtra o] LT',
+ nextWeek: function () {
+ switch (this.day()) {
+ case 0:
+ return '[v nedeľu o] LT';
+ case 1:
+ case 2:
+ return '[v] dddd [o] LT';
+ case 3:
+ return '[v stredu o] LT';
+ case 4:
+ return '[vo štvrtok o] LT';
+ case 5:
+ return '[v piatok o] LT';
+ case 6:
+ return '[v sobotu o] LT';
+ }
+ },
+ lastDay: '[včera o] LT',
+ lastWeek: function () {
+ switch (this.day()) {
+ case 0:
+ return '[minulú nedeľu o] LT';
+ case 1:
+ case 2:
+ return '[minulý] dddd [o] LT';
+ case 3:
+ return '[minulú stredu o] LT';
+ case 4:
+ case 5:
+ return '[minulý] dddd [o] LT';
+ case 6:
+ return '[minulú sobotu o] LT';
+ }
+ },
+ sameElse: "L"
+ },
+ relativeTime : {
+ future : "za %s",
+ past : "pred %s",
+ s : translate,
+ m : translate,
+ mm : translate,
+ h : translate,
+ hh : translate,
+ d : translate,
+ dd : translate,
+ M : translate,
+ MM : translate,
+ y : translate,
+ yy : translate
+ },
+ ordinal : '%d.',
+ week : {
+ dow : 1, // Monday is the first day of the week.
+ doy : 4 // The week that contains Jan 4th is the first week of the year.
+ }
+ });
+}));
+// moment.js language configuration
+// language : slovenian (sl)
+// author : Robert Sedovšek : https://github.com/sedovsek
+
+(function (factory) {
+ factory(moment);
+}(function (moment) {
+ function translate(number, withoutSuffix, key) {
+ var result = number + " ";
+ switch (key) {
+ case 'm':
+ return withoutSuffix ? 'ena minuta' : 'eno minuto';
+ case 'mm':
+ if (number === 1) {
+ result += 'minuta';
+ } else if (number === 2) {
+ result += 'minuti';
+ } else if (number === 3 || number === 4) {
+ result += 'minute';
+ } else {
+ result += 'minut';
+ }
+ return result;
+ case 'h':
+ return withoutSuffix ? 'ena ura' : 'eno uro';
+ case 'hh':
+ if (number === 1) {
+ result += 'ura';
+ } else if (number === 2) {
+ result += 'uri';
+ } else if (number === 3 || number === 4) {
+ result += 'ure';
+ } else {
+ result += 'ur';
+ }
+ return result;
+ case 'dd':
+ if (number === 1) {
+ result += 'dan';
+ } else {
+ result += 'dni';
+ }
+ return result;
+ case 'MM':
+ if (number === 1) {
+ result += 'mesec';
+ } else if (number === 2) {
+ result += 'meseca';
+ } else if (number === 3 || number === 4) {
+ result += 'mesece';
+ } else {
+ result += 'mesecev';
+ }
+ return result;
+ case 'yy':
+ if (number === 1) {
+ result += 'leto';
+ } else if (number === 2) {
+ result += 'leti';
+ } else if (number === 3 || number === 4) {
+ result += 'leta';
+ } else {
+ result += 'let';
+ }
+ return result;
+ }
+ }
+
+ return moment.lang('sl', {
+ months : "januar_februar_marec_april_maj_junij_julij_avgust_september_oktober_november_december".split("_"),
+ monthsShort : "jan._feb._mar._apr._maj._jun._jul._avg._sep._okt._nov._dec.".split("_"),
+ weekdays : "nedelja_ponedeljek_torek_sreda_četrtek_petek_sobota".split("_"),
+ weekdaysShort : "ned._pon._tor._sre._čet._pet._sob.".split("_"),
+ weekdaysMin : "ne_po_to_sr_če_pe_so".split("_"),
+ longDateFormat : {
+ LT : "H:mm",
+ L : "DD. MM. YYYY",
+ LL : "D. MMMM YYYY",
+ LLL : "D. MMMM YYYY LT",
+ LLLL : "dddd, D. MMMM YYYY LT"
+ },
+ calendar : {
+ sameDay : '[danes ob] LT',
+ nextDay : '[jutri ob] LT',
+
+ nextWeek : function () {
+ switch (this.day()) {
+ case 0:
+ return '[v] [nedeljo] [ob] LT';
+ case 3:
+ return '[v] [sredo] [ob] LT';
+ case 6:
+ return '[v] [soboto] [ob] LT';
+ case 1:
+ case 2:
+ case 4:
+ case 5:
+ return '[v] dddd [ob] LT';
+ }
+ },
+ lastDay : '[včeraj ob] LT',
+ lastWeek : function () {
+ switch (this.day()) {
+ case 0:
+ case 3:
+ case 6:
+ return '[prejšnja] dddd [ob] LT';
+ case 1:
+ case 2:
+ case 4:
+ case 5:
+ return '[prejšnji] dddd [ob] LT';
+ }
+ },
+ sameElse : 'L'
+ },
+ relativeTime : {
+ future : "čez %s",
+ past : "%s nazaj",
+ s : "nekaj sekund",
+ m : translate,
+ mm : translate,
+ h : translate,
+ hh : translate,
+ d : "en dan",
+ dd : translate,
+ M : "en mesec",
+ MM : translate,
+ y : "eno leto",
+ yy : translate
+ },
+ ordinal : '%d.',
+ week : {
+ dow : 1, // Monday is the first day of the week.
+ doy : 7 // The week that contains Jan 1st is the first week of the year.
+ }
+ });
+}));
+// moment.js language configuration
+// language : Albanian (sq)
+// author : Flakërim Ismani : https://github.com/flakerimi
+// author: Menelion Elensúle: https://github.com/Oire (tests)
+// author : Oerd Cukalla : https://github.com/oerd (fixes)
+
+(function (factory) {
+ factory(moment);
+}(function (moment) {
+ return moment.lang('sq', {
+ months : "Janar_Shkurt_Mars_Prill_Maj_Qershor_Korrik_Gusht_Shtator_Tetor_Nëntor_Dhjetor".split("_"),
+ monthsShort : "Jan_Shk_Mar_Pri_Maj_Qer_Kor_Gus_Sht_Tet_Nën_Dhj".split("_"),
+ weekdays : "E Diel_E Hënë_E Martë_E Mërkurë_E Enjte_E Premte_E Shtunë".split("_"),
+ weekdaysShort : "Die_Hën_Mar_Mër_Enj_Pre_Sht".split("_"),
+ weekdaysMin : "D_H_Ma_Më_E_P_Sh".split("_"),
+ meridiem : function (hours, minutes, isLower) {
+ return hours < 12 ? 'PD' : 'MD';
+ },
+ longDateFormat : {
+ LT : "HH:mm",
+ L : "DD/MM/YYYY",
+ LL : "D MMMM YYYY",
+ LLL : "D MMMM YYYY LT",
+ LLLL : "dddd, D MMMM YYYY LT"
+ },
+ calendar : {
+ sameDay : '[Sot në] LT',
+ nextDay : '[Nesër në] LT',
+ nextWeek : 'dddd [në] LT',
+ lastDay : '[Dje në] LT',
+ lastWeek : 'dddd [e kaluar në] LT',
+ sameElse : 'L'
+ },
+ relativeTime : {
+ future : "në %s",
+ past : "%s më parë",
+ s : "disa sekonda",
+ m : "një minutë",
+ mm : "%d minuta",
+ h : "një orë",
+ hh : "%d orë",
+ d : "një ditë",
+ dd : "%d ditë",
+ M : "një muaj",
+ MM : "%d muaj",
+ y : "një vit",
+ yy : "%d vite"
+ },
+ ordinal : '%d.',
+ week : {
+ dow : 1, // Monday is the first day of the week.
+ doy : 4 // The week that contains Jan 4th is the first week of the year.
+ }
+ });
+}));
+// moment.js language configuration
+// language : Serbian-cyrillic (sr-cyrl)
+// author : Milan Janačković<milanjanackovic@gmail.com> : https://github.com/milan-j
+
+(function (factory) {
+ factory(moment);
+}(function (moment) {
+
+ var translator = {
+ words: { //Different grammatical cases
+ m: ['један минут', 'једне минуте'],
+ mm: ['минут', 'минуте', 'минута'],
+ h: ['један сат', 'једног сата'],
+ hh: ['сат', 'сата', 'сати'],
+ dd: ['дан', 'дана', 'дана'],
+ MM: ['месец', 'месеца', 'месеци'],
+ yy: ['година', 'године', 'година']
+ },
+ correctGrammaticalCase: function (number, wordKey) {
+ return number === 1 ? wordKey[0] : (number >= 2 && number <= 4 ? wordKey[1] : wordKey[2]);
+ },
+ translate: function (number, withoutSuffix, key) {
+ var wordKey = translator.words[key];
+ if (key.length === 1) {
+ return withoutSuffix ? wordKey[0] : wordKey[1];
+ } else {
+ return number + ' ' + translator.correctGrammaticalCase(number, wordKey);
+ }
+ }
+ };
+
+ return moment.lang('sr-cyrl', {
+ months: ['јануар', 'фебруар', 'март', 'април', 'мај', 'јун', 'јул', 'август', 'септембар', 'октобар', 'новембар', 'децембар'],
+ monthsShort: ['јан.', 'феб.', 'мар.', 'апр.', 'мај', 'јун', 'јул', 'авг.', 'сеп.', 'окт.', 'нов.', 'дец.'],
+ weekdays: ['недеља', 'понедељак', 'уторак', 'среда', 'четвртак', 'петак', 'субота'],
+ weekdaysShort: ['нед.', 'пон.', 'уто.', 'сре.', 'чет.', 'пет.', 'суб.'],
+ weekdaysMin: ['не', 'по', 'ут', 'ср', 'че', 'пе', 'су'],
+ longDateFormat: {
+ LT: "H:mm",
+ L: "DD. MM. YYYY",
+ LL: "D. MMMM YYYY",
+ LLL: "D. MMMM YYYY LT",
+ LLLL: "dddd, D. MMMM YYYY LT"
+ },
+ calendar: {
+ sameDay: '[данас у] LT',
+ nextDay: '[сутра у] LT',
+
+ nextWeek: function () {
+ switch (this.day()) {
+ case 0:
+ return '[у] [недељу] [у] LT';
+ case 3:
+ return '[у] [среду] [у] LT';
+ case 6:
+ return '[у] [суботу] [у] LT';
+ case 1:
+ case 2:
+ case 4:
+ case 5:
+ return '[у] dddd [у] LT';
+ }
+ },
+ lastDay : '[јуче у] LT',
+ lastWeek : function () {
+ var lastWeekDays = [
+ '[прошле] [недеље] [у] LT',
+ '[прошлог] [понедељка] [у] LT',
+ '[прошлог] [уторка] [у] LT',
+ '[прошле] [среде] [у] LT',
+ '[прошлог] [четвртка] [у] LT',
+ '[прошлог] [петка] [у] LT',
+ '[прошле] [суботе] [у] LT'
+ ];
+ return lastWeekDays[this.day()];
+ },
+ sameElse : 'L'
+ },
+ relativeTime : {
+ future : "за %s",
+ past : "пре %s",
+ s : "неколико секунди",
+ m : translator.translate,
+ mm : translator.translate,
+ h : translator.translate,
+ hh : translator.translate,
+ d : "дан",
+ dd : translator.translate,
+ M : "месец",
+ MM : translator.translate,
+ y : "годину",
+ yy : translator.translate
+ },
+ ordinal : '%d.',
+ week : {
+ dow : 1, // Monday is the first day of the week.
+ doy : 7 // The week that contains Jan 1st is the first week of the year.
+ }
+ });
+}));
+// moment.js language configuration
+// language : Serbian-latin (sr)
+// author : Milan Janačković<milanjanackovic@gmail.com> : https://github.com/milan-j
+
+(function (factory) {
+ factory(moment);
+}(function (moment) {
+
+ var translator = {
+ words: { //Different grammatical cases
+ m: ['jedan minut', 'jedne minute'],
+ mm: ['minut', 'minute', 'minuta'],
+ h: ['jedan sat', 'jednog sata'],
+ hh: ['sat', 'sata', 'sati'],
+ dd: ['dan', 'dana', 'dana'],
+ MM: ['mesec', 'meseca', 'meseci'],
+ yy: ['godina', 'godine', 'godina']
+ },
+ correctGrammaticalCase: function (number, wordKey) {
+ return number === 1 ? wordKey[0] : (number >= 2 && number <= 4 ? wordKey[1] : wordKey[2]);
+ },
+ translate: function (number, withoutSuffix, key) {
+ var wordKey = translator.words[key];
+ if (key.length === 1) {
+ return withoutSuffix ? wordKey[0] : wordKey[1];
+ } else {
+ return number + ' ' + translator.correctGrammaticalCase(number, wordKey);
+ }
+ }
+ };
+
+ return moment.lang('sr', {
+ months: ['januar', 'februar', 'mart', 'april', 'maj', 'jun', 'jul', 'avgust', 'septembar', 'oktobar', 'novembar', 'decembar'],
+ monthsShort: ['jan.', 'feb.', 'mar.', 'apr.', 'maj', 'jun', 'jul', 'avg.', 'sep.', 'okt.', 'nov.', 'dec.'],
+ weekdays: ['nedelja', 'ponedeljak', 'utorak', 'sreda', 'četvrtak', 'petak', 'subota'],
+ weekdaysShort: ['ned.', 'pon.', 'uto.', 'sre.', 'čet.', 'pet.', 'sub.'],
+ weekdaysMin: ['ne', 'po', 'ut', 'sr', 'če', 'pe', 'su'],
+ longDateFormat: {
+ LT: "H:mm",
+ L: "DD. MM. YYYY",
+ LL: "D. MMMM YYYY",
+ LLL: "D. MMMM YYYY LT",
+ LLLL: "dddd, D. MMMM YYYY LT"
+ },
+ calendar: {
+ sameDay: '[danas u] LT',
+ nextDay: '[sutra u] LT',
+
+ nextWeek: function () {
+ switch (this.day()) {
+ case 0:
+ return '[u] [nedelju] [u] LT';
+ case 3:
+ return '[u] [sredu] [u] LT';
+ case 6:
+ return '[u] [subotu] [u] LT';
+ case 1:
+ case 2:
+ case 4:
+ case 5:
+ return '[u] dddd [u] LT';
+ }
+ },
+ lastDay : '[juče u] LT',
+ lastWeek : function () {
+ var lastWeekDays = [
+ '[prošle] [nedelje] [u] LT',
+ '[prošlog] [ponedeljka] [u] LT',
+ '[prošlog] [utorka] [u] LT',
+ '[prošle] [srede] [u] LT',
+ '[prošlog] [četvrtka] [u] LT',
+ '[prošlog] [petka] [u] LT',
+ '[prošle] [subote] [u] LT'
+ ];
+ return lastWeekDays[this.day()];
+ },
+ sameElse : 'L'
+ },
+ relativeTime : {
+ future : "za %s",
+ past : "pre %s",
+ s : "nekoliko sekundi",
+ m : translator.translate,
+ mm : translator.translate,
+ h : translator.translate,
+ hh : translator.translate,
+ d : "dan",
+ dd : translator.translate,
+ M : "mesec",
+ MM : translator.translate,
+ y : "godinu",
+ yy : translator.translate
+ },
+ ordinal : '%d.',
+ week : {
+ dow : 1, // Monday is the first day of the week.
+ doy : 7 // The week that contains Jan 1st is the first week of the year.
+ }
+ });
+}));
+// moment.js language configuration
+// language : swedish (sv)
+// author : Jens Alm : https://github.com/ulmus
+
+(function (factory) {
+ factory(moment);
+}(function (moment) {
+ return moment.lang('sv', {
+ months : "januari_februari_mars_april_maj_juni_juli_augusti_september_oktober_november_december".split("_"),
+ monthsShort : "jan_feb_mar_apr_maj_jun_jul_aug_sep_okt_nov_dec".split("_"),
+ weekdays : "söndag_måndag_tisdag_onsdag_torsdag_fredag_lördag".split("_"),
+ weekdaysShort : "sön_mån_tis_ons_tor_fre_lör".split("_"),
+ weekdaysMin : "sö_må_ti_on_to_fr_lö".split("_"),
+ longDateFormat : {
+ LT : "HH:mm",
+ L : "YYYY-MM-DD",
+ LL : "D MMMM YYYY",
+ LLL : "D MMMM YYYY LT",
+ LLLL : "dddd D MMMM YYYY LT"
+ },
+ calendar : {
+ sameDay: '[Idag] LT',
+ nextDay: '[Imorgon] LT',
+ lastDay: '[Igår] LT',
+ nextWeek: 'dddd LT',
+ lastWeek: '[Förra] dddd[en] LT',
+ sameElse: 'L'
+ },
+ relativeTime : {
+ future : "om %s",
+ past : "för %s sedan",
+ s : "några sekunder",
+ m : "en minut",
+ mm : "%d minuter",
+ h : "en timme",
+ hh : "%d timmar",
+ d : "en dag",
+ dd : "%d dagar",
+ M : "en månad",
+ MM : "%d månader",
+ y : "ett år",
+ yy : "%d år"
+ },
+ ordinal : function (number) {
+ var b = number % 10,
+ output = (~~ (number % 100 / 10) === 1) ? 'e' :
+ (b === 1) ? 'a' :
+ (b === 2) ? 'a' :
+ (b === 3) ? 'e' : 'e';
+ return number + output;
+ },
+ week : {
+ dow : 1, // Monday is the first day of the week.
+ doy : 4 // The week that contains Jan 4th is the first week of the year.
+ }
+ });
+}));
+// moment.js language configuration
+// language : tamil (ta)
+// author : Arjunkumar Krishnamoorthy : https://github.com/tk120404
+
+(function (factory) {
+ factory(moment);
+}(function (moment) {
+ /*var symbolMap = {
+ '1': '௧',
+ '2': '௨',
+ '3': '௩',
+ '4': '௪',
+ '5': '௫',
+ '6': '௬',
+ '7': '௭',
+ '8': '௮',
+ '9': '௯',
+ '0': '௦'
+ },
+ numberMap = {
+ '௧': '1',
+ '௨': '2',
+ '௩': '3',
+ '௪': '4',
+ '௫': '5',
+ '௬': '6',
+ '௭': '7',
+ '௮': '8',
+ '௯': '9',
+ '௦': '0'
+ }; */
+
+ return moment.lang('ta', {
+ months : 'ஜனவரி_பிப்ரவரி_மார்ச்_ஏப்ரல்_மே_ஜூன்_ஜூலை_ஆகஸ்ட்_செப்டெம்பர்_அக்டோபர்_நவம்பர்_டிசம்பர்'.split("_"),
+ monthsShort : 'ஜனவரி_பிப்ரவரி_மார்ச்_ஏப்ரல்_மே_ஜூன்_ஜூலை_ஆகஸ்ட்_செப்டெம்பர்_அக்டோபர்_நவம்பர்_டிசம்பர்'.split("_"),
+ weekdays : 'ஞாயிற்றுக்கிழமை_திங்கட்கிழமை_செவ்வாய்கிழமை_புதன்கிழமை_வியாழக்கிழமை_வெள்ளிக்கிழமை_சனிக்கிழமை'.split("_"),
+ weekdaysShort : 'ஞாயிறு_திங்கள்_செவ்வாய்_புதன்_வியாழன்_வெள்ளி_சனி'.split("_"),
+ weekdaysMin : 'ஞா_தி_செ_பு_வி_வெ_ச'.split("_"),
+ longDateFormat : {
+ LT : "HH:mm",
+ L : "DD/MM/YYYY",
+ LL : "D MMMM YYYY",
+ LLL : "D MMMM YYYY, LT",
+ LLLL : "dddd, D MMMM YYYY, LT"
+ },
+ calendar : {
+ sameDay : '[இன்று] LT',
+ nextDay : '[நாளை] LT',
+ nextWeek : 'dddd, LT',
+ lastDay : '[நேற்று] LT',
+ lastWeek : '[கடந்த வாரம்] dddd, LT',
+ sameElse : 'L'
+ },
+ relativeTime : {
+ future : "%s இல்",
+ past : "%s முன்",
+ s : "ஒரு சில விநாடிகள்",
+ m : "ஒரு நிமிடம்",
+ mm : "%d நிமிடங்கள்",
+ h : "ஒரு மணி நேரம்",
+ hh : "%d மணி நேரம்",
+ d : "ஒரு நாள்",
+ dd : "%d நாட்கள்",
+ M : "ஒரு மாதம்",
+ MM : "%d மாதங்கள்",
+ y : "ஒரு வருடம்",
+ yy : "%d ஆண்டுகள்"
+ },
+/* preparse: function (string) {
+ return string.replace(/[௧௨௩௪௫௬௭௮௯௦]/g, function (match) {
+ return numberMap[match];
+ });
+ },
+ postformat: function (string) {
+ return string.replace(/\d/g, function (match) {
+ return symbolMap[match];
+ });
+ },*/
+ ordinal : function (number) {
+ return number + 'வது';
+ },
+
+
+// refer http://ta.wikipedia.org/s/1er1
+
+ meridiem : function (hour, minute, isLower) {
+ if (hour >= 6 && hour <= 10) {
+ return " காலை";
+ } else if (hour >= 10 && hour <= 14) {
+ return " நண்பகல்";
+ } else if (hour >= 14 && hour <= 18) {
+ return " எற்பாடு";
+ } else if (hour >= 18 && hour <= 20) {
+ return " மாலை";
+ } else if (hour >= 20 && hour <= 24) {
+ return " இரவு";
+ } else if (hour >= 0 && hour <= 6) {
+ return " வைகறை";
+ }
+ },
+ week : {
+ dow : 0, // Sunday is the first day of the week.
+ doy : 6 // The week that contains Jan 1st is the first week of the year.
+ }
+ });
+}));
+// moment.js language configuration
+// language : thai (th)
+// author : Kridsada Thanabulpong : https://github.com/sirn
+
+(function (factory) {
+ factory(moment);
+}(function (moment) {
+ return moment.lang('th', {
+ months : "มกราคม_กุมภาพันธ์_มีนาคม_เมษายน_พฤษภาคม_มิถุนายน_กรกฎาคม_สิงหาคม_กันยายน_ตุลาคม_พฤศจิกายน_ธันวาคม".split("_"),
+ monthsShort : "มกรา_กุมภา_มีนา_เมษา_พฤษภา_มิถุนา_กรกฎา_สิงหา_กันยา_ตุลา_พฤศจิกา_ธันวา".split("_"),
+ weekdays : "อาทิตย์_จันทร์_อังคาร_พุธ_พฤหัสบดี_ศุกร์_เสาร์".split("_"),
+ weekdaysShort : "อาทิตย์_จันทร์_อังคาร_พุธ_พฤหัส_ศุกร์_เสาร์".split("_"), // yes, three characters difference
+ weekdaysMin : "อา._จ._อ._พ._พฤ._ศ._ส.".split("_"),
+ longDateFormat : {
+ LT : "H นาฬิกา m นาที",
+ L : "YYYY/MM/DD",
+ LL : "D MMMM YYYY",
+ LLL : "D MMMM YYYY เวลา LT",
+ LLLL : "วันddddที่ D MMMM YYYY เวลา LT"
+ },
+ meridiem : function (hour, minute, isLower) {
+ if (hour < 12) {
+ return "ก่อนเที่ยง";
+ } else {
+ return "หลังเที่ยง";
+ }
+ },
+ calendar : {
+ sameDay : '[วันนี้ เวลา] LT',
+ nextDay : '[พรุ่งนี้ เวลา] LT',
+ nextWeek : 'dddd[หน้า เวลา] LT',
+ lastDay : '[เมื่อวานนี้ เวลา] LT',
+ lastWeek : '[วัน]dddd[ที่แล้ว เวลา] LT',
+ sameElse : 'L'
+ },
+ relativeTime : {
+ future : "อีก %s",
+ past : "%sที่แล้ว",
+ s : "ไม่กี่วินาที",
+ m : "1 นาที",
+ mm : "%d นาที",
+ h : "1 ชั่วโมง",
+ hh : "%d ชั่วโมง",
+ d : "1 วัน",
+ dd : "%d วัน",
+ M : "1 เดือน",
+ MM : "%d เดือน",
+ y : "1 ปี",
+ yy : "%d ปี"
+ }
+ });
+}));
+// moment.js language configuration
+// language : Tagalog/Filipino (tl-ph)
+// author : Dan Hagman
+
+(function (factory) {
+ factory(moment);
+}(function (moment) {
+ return moment.lang('tl-ph', {
+ months : "Enero_Pebrero_Marso_Abril_Mayo_Hunyo_Hulyo_Agosto_Setyembre_Oktubre_Nobyembre_Disyembre".split("_"),
+ monthsShort : "Ene_Peb_Mar_Abr_May_Hun_Hul_Ago_Set_Okt_Nob_Dis".split("_"),
+ weekdays : "Linggo_Lunes_Martes_Miyerkules_Huwebes_Biyernes_Sabado".split("_"),
+ weekdaysShort : "Lin_Lun_Mar_Miy_Huw_Biy_Sab".split("_"),
+ weekdaysMin : "Li_Lu_Ma_Mi_Hu_Bi_Sab".split("_"),
+ longDateFormat : {
+ LT : "HH:mm",
+ L : "MM/D/YYYY",
+ LL : "MMMM D, YYYY",
+ LLL : "MMMM D, YYYY LT",
+ LLLL : "dddd, MMMM DD, YYYY LT"
+ },
+ calendar : {
+ sameDay: "[Ngayon sa] LT",
+ nextDay: '[Bukas sa] LT',
+ nextWeek: 'dddd [sa] LT',
+ lastDay: '[Kahapon sa] LT',
+ lastWeek: 'dddd [huling linggo] LT',
+ sameElse: 'L'
+ },
+ relativeTime : {
+ future : "sa loob ng %s",
+ past : "%s ang nakalipas",
+ s : "ilang segundo",
+ m : "isang minuto",
+ mm : "%d minuto",
+ h : "isang oras",
+ hh : "%d oras",
+ d : "isang araw",
+ dd : "%d araw",
+ M : "isang buwan",
+ MM : "%d buwan",
+ y : "isang taon",
+ yy : "%d taon"
+ },
+ ordinal : function (number) {
+ return number;
+ },
+ week : {
+ dow : 1, // Monday is the first day of the week.
+ doy : 4 // The week that contains Jan 4th is the first week of the year.
+ }
+ });
+}));
+// moment.js language configuration
+// language : turkish (tr)
+// authors : Erhan Gundogan : https://github.com/erhangundogan,
+// Burak Yiğit Kaya: https://github.com/BYK
+
+(function (factory) {
+ factory(moment);
+}(function (moment) {
+
+ var suffixes = {
+ 1: "'inci",
+ 5: "'inci",
+ 8: "'inci",
+ 70: "'inci",
+ 80: "'inci",
+
+ 2: "'nci",
+ 7: "'nci",
+ 20: "'nci",
+ 50: "'nci",
+
+ 3: "'üncü",
+ 4: "'üncü",
+ 100: "'üncü",
+
+ 6: "'ncı",
+
+ 9: "'uncu",
+ 10: "'uncu",
+ 30: "'uncu",
+
+ 60: "'ıncı",
+ 90: "'ıncı"
+ };
+
+ return moment.lang('tr', {
+ months : "Ocak_Şubat_Mart_Nisan_Mayıs_Haziran_Temmuz_Ağustos_Eylül_Ekim_Kasım_Aralık".split("_"),
+ monthsShort : "Oca_Şub_Mar_Nis_May_Haz_Tem_Ağu_Eyl_Eki_Kas_Ara".split("_"),
+ weekdays : "Pazar_Pazartesi_Salı_Çarşamba_Perşembe_Cuma_Cumartesi".split("_"),
+ weekdaysShort : "Paz_Pts_Sal_Çar_Per_Cum_Cts".split("_"),
+ weekdaysMin : "Pz_Pt_Sa_Ça_Pe_Cu_Ct".split("_"),
+ longDateFormat : {
+ LT : "HH:mm",
+ L : "DD.MM.YYYY",
+ LL : "D MMMM YYYY",
+ LLL : "D MMMM YYYY LT",
+ LLLL : "dddd, D MMMM YYYY LT"
+ },
+ calendar : {
+ sameDay : '[bugün saat] LT',
+ nextDay : '[yarın saat] LT',
+ nextWeek : '[haftaya] dddd [saat] LT',
+ lastDay : '[dün] LT',
+ lastWeek : '[geçen hafta] dddd [saat] LT',
+ sameElse : 'L'
+ },
+ relativeTime : {
+ future : "%s sonra",
+ past : "%s önce",
+ s : "birkaç saniye",
+ m : "bir dakika",
+ mm : "%d dakika",
+ h : "bir saat",
+ hh : "%d saat",
+ d : "bir gün",
+ dd : "%d gün",
+ M : "bir ay",
+ MM : "%d ay",
+ y : "bir yıl",
+ yy : "%d yıl"
+ },
+ ordinal : function (number) {
+ if (number === 0) { // special case for zero
+ return number + "'ıncı";
+ }
+ var a = number % 10,
+ b = number % 100 - a,
+ c = number >= 100 ? 100 : null;
+
+ return number + (suffixes[a] || suffixes[b] || suffixes[c]);
+ },
+ week : {
+ dow : 1, // Monday is the first day of the week.
+ doy : 7 // The week that contains Jan 1st is the first week of the year.
+ }
+ });
+}));
+// moment.js language configuration
+// language : Morocco Central Atlas Tamaziɣt in Latin (tzm-latn)
+// author : Abdel Said : https://github.com/abdelsaid
+
+(function (factory) {
+ factory(moment);
+}(function (moment) {
+ return moment.lang('tzm-latn', {
+ months : "innayr_brˤayrˤ_marˤsˤ_ibrir_mayyw_ywnyw_ywlywz_ɣwšt_šwtanbir_ktˤwbrˤ_nwwanbir_dwjnbir".split("_"),
+ monthsShort : "innayr_brˤayrˤ_marˤsˤ_ibrir_mayyw_ywnyw_ywlywz_ɣwšt_šwtanbir_ktˤwbrˤ_nwwanbir_dwjnbir".split("_"),
+ weekdays : "asamas_aynas_asinas_akras_akwas_asimwas_asiḍyas".split("_"),
+ weekdaysShort : "asamas_aynas_asinas_akras_akwas_asimwas_asiḍyas".split("_"),
+ weekdaysMin : "asamas_aynas_asinas_akras_akwas_asimwas_asiḍyas".split("_"),
+ longDateFormat : {
+ LT : "HH:mm",
+ L : "DD/MM/YYYY",
+ LL : "D MMMM YYYY",
+ LLL : "D MMMM YYYY LT",
+ LLLL : "dddd D MMMM YYYY LT"
+ },
+ calendar : {
+ sameDay: "[asdkh g] LT",
+ nextDay: '[aska g] LT',
+ nextWeek: 'dddd [g] LT',
+ lastDay: '[assant g] LT',
+ lastWeek: 'dddd [g] LT',
+ sameElse: 'L'
+ },
+ relativeTime : {
+ future : "dadkh s yan %s",
+ past : "yan %s",
+ s : "imik",
+ m : "minuḍ",
+ mm : "%d minuḍ",
+ h : "saɛa",
+ hh : "%d tassaɛin",
+ d : "ass",
+ dd : "%d ossan",
+ M : "ayowr",
+ MM : "%d iyyirn",
+ y : "asgas",
+ yy : "%d isgasn"
+ },
+ week : {
+ dow : 6, // Saturday is the first day of the week.
+ doy : 12 // The week that contains Jan 1st is the first week of the year.
+ }
+ });
+}));
+// moment.js language configuration
+// language : Morocco Central Atlas Tamaziɣt (tzm)
+// author : Abdel Said : https://github.com/abdelsaid
+
+(function (factory) {
+ factory(moment);
+}(function (moment) {
+ return moment.lang('tzm', {
+ months : "ⵉⵏⵏⴰⵢⵔ_ⴱⵕⴰⵢⵕ_ⵎⴰⵕⵚ_ⵉⴱⵔⵉⵔ_ⵎⴰⵢⵢⵓ_ⵢⵓⵏⵢⵓ_ⵢⵓⵍⵢⵓⵣ_ⵖⵓⵛⵜ_ⵛⵓⵜⴰⵏⴱⵉⵔ_ⴽⵟⵓⴱⵕ_ⵏⵓⵡⴰⵏⴱⵉⵔ_ⴷⵓⵊⵏⴱⵉⵔ".split("_"),
+ monthsShort : "ⵉⵏⵏⴰⵢⵔ_ⴱⵕⴰⵢⵕ_ⵎⴰⵕⵚ_ⵉⴱⵔⵉⵔ_ⵎⴰⵢⵢⵓ_ⵢⵓⵏⵢⵓ_ⵢⵓⵍⵢⵓⵣ_ⵖⵓⵛⵜ_ⵛⵓⵜⴰⵏⴱⵉⵔ_ⴽⵟⵓⴱⵕ_ⵏⵓⵡⴰⵏⴱⵉⵔ_ⴷⵓⵊⵏⴱⵉⵔ".split("_"),
+ weekdays : "ⴰⵙⴰⵎⴰⵙ_ⴰⵢⵏⴰⵙ_ⴰⵙⵉⵏⴰⵙ_ⴰⴽⵔⴰⵙ_ⴰⴽⵡⴰⵙ_ⴰⵙⵉⵎⵡⴰⵙ_ⴰⵙⵉⴹⵢⴰⵙ".split("_"),
+ weekdaysShort : "ⴰⵙⴰⵎⴰⵙ_ⴰⵢⵏⴰⵙ_ⴰⵙⵉⵏⴰⵙ_ⴰⴽⵔⴰⵙ_ⴰⴽⵡⴰⵙ_ⴰⵙⵉⵎⵡⴰⵙ_ⴰⵙⵉⴹⵢⴰⵙ".split("_"),
+ weekdaysMin : "ⴰⵙⴰⵎⴰⵙ_ⴰⵢⵏⴰⵙ_ⴰⵙⵉⵏⴰⵙ_ⴰⴽⵔⴰⵙ_ⴰⴽⵡⴰⵙ_ⴰⵙⵉⵎⵡⴰⵙ_ⴰⵙⵉⴹⵢⴰⵙ".split("_"),
+ longDateFormat : {
+ LT : "HH:mm",
+ L : "DD/MM/YYYY",
+ LL : "D MMMM YYYY",
+ LLL : "D MMMM YYYY LT",
+ LLLL : "dddd D MMMM YYYY LT"
+ },
+ calendar : {
+ sameDay: "[ⴰⵙⴷⵅ ⴴ] LT",
+ nextDay: '[ⴰⵙⴽⴰ ⴴ] LT',
+ nextWeek: 'dddd [ⴴ] LT',
+ lastDay: '[ⴰⵚⴰⵏⵜ ⴴ] LT',
+ lastWeek: 'dddd [ⴴ] LT',
+ sameElse: 'L'
+ },
+ relativeTime : {
+ future : "ⴷⴰⴷⵅ ⵙ ⵢⴰⵏ %s",
+ past : "ⵢⴰⵏ %s",
+ s : "ⵉⵎⵉⴽ",
+ m : "ⵎⵉⵏⵓⴺ",
+ mm : "%d ⵎⵉⵏⵓⴺ",
+ h : "ⵙⴰⵄⴰ",
+ hh : "%d ⵜⴰⵙⵙⴰⵄⵉⵏ",
+ d : "ⴰⵙⵙ",
+ dd : "%d oⵙⵙⴰⵏ",
+ M : "ⴰⵢoⵓⵔ",
+ MM : "%d ⵉⵢⵢⵉⵔⵏ",
+ y : "ⴰⵙⴳⴰⵙ",
+ yy : "%d ⵉⵙⴳⴰⵙⵏ"
+ },
+ week : {
+ dow : 6, // Saturday is the first day of the week.
+ doy : 12 // The week that contains Jan 1st is the first week of the year.
+ }
+ });
+}));
+// moment.js language configuration
+// language : ukrainian (uk)
+// author : zemlanin : https://github.com/zemlanin
+// Author : Menelion Elensúle : https://github.com/Oire
+
+(function (factory) {
+ factory(moment);
+}(function (moment) {
+ function plural(word, num) {
+ var forms = word.split('_');
+ return num % 10 === 1 && num % 100 !== 11 ? forms[0] : (num % 10 >= 2 && num % 10 <= 4 && (num % 100 < 10 || num % 100 >= 20) ? forms[1] : forms[2]);
+ }
+
+ function relativeTimeWithPlural(number, withoutSuffix, key) {
+ var format = {
+ 'mm': 'хвилина_хвилини_хвилин',
+ 'hh': 'година_години_годин',
+ 'dd': 'день_дні_днів',
+ 'MM': 'місяць_місяці_місяців',
+ 'yy': 'рік_роки_років'
+ };
+ if (key === 'm') {
+ return withoutSuffix ? 'хвилина' : 'хвилину';
+ }
+ else if (key === 'h') {
+ return withoutSuffix ? 'година' : 'годину';
+ }
+ else {
+ return number + ' ' + plural(format[key], +number);
+ }
+ }
+
+ function monthsCaseReplace(m, format) {
+ var months = {
+ 'nominative': 'січень_лютий_березень_квітень_травень_червень_липень_серпень_вересень_жовтень_листопад_грудень'.split('_'),
+ 'accusative': 'січня_лютого_березня_квітня_травня_червня_липня_серпня_вересня_жовтня_листопада_грудня'.split('_')
+ },
+
+ nounCase = (/D[oD]? *MMMM?/).test(format) ?
+ 'accusative' :
+ 'nominative';
+
+ return months[nounCase][m.month()];
+ }
+
+ function weekdaysCaseReplace(m, format) {
+ var weekdays = {
+ 'nominative': 'неділя_понеділок_вівторок_середа_четвер_п’ятниця_субота'.split('_'),
+ 'accusative': 'неділю_понеділок_вівторок_середу_четвер_п’ятницю_суботу'.split('_'),
+ 'genitive': 'неділі_понеділка_вівторка_середи_четверга_п’ятниці_суботи'.split('_')
+ },
+
+ nounCase = (/(\[[ВвУу]\]) ?dddd/).test(format) ?
+ 'accusative' :
+ ((/\[?(?:минулої|наступної)? ?\] ?dddd/).test(format) ?
+ 'genitive' :
+ 'nominative');
+
+ return weekdays[nounCase][m.day()];
+ }
+
+ function processHoursFunction(str) {
+ return function () {
+ return str + 'о' + (this.hours() === 11 ? 'б' : '') + '] LT';
+ };
+ }
+
+ return moment.lang('uk', {
+ months : monthsCaseReplace,
+ monthsShort : "січ_лют_бер_квіт_трав_черв_лип_серп_вер_жовт_лист_груд".split("_"),
+ weekdays : weekdaysCaseReplace,
+ weekdaysShort : "нд_пн_вт_ср_чт_пт_сб".split("_"),
+ weekdaysMin : "нд_пн_вт_ср_чт_пт_сб".split("_"),
+ longDateFormat : {
+ LT : "HH:mm",
+ L : "DD.MM.YYYY",
+ LL : "D MMMM YYYY р.",
+ LLL : "D MMMM YYYY р., LT",
+ LLLL : "dddd, D MMMM YYYY р., LT"
+ },
+ calendar : {
+ sameDay: processHoursFunction('[Сьогодні '),
+ nextDay: processHoursFunction('[Завтра '),
+ lastDay: processHoursFunction('[Вчора '),
+ nextWeek: processHoursFunction('[У] dddd ['),
+ lastWeek: function () {
+ switch (this.day()) {
+ case 0:
+ case 3:
+ case 5:
+ case 6:
+ return processHoursFunction('[Минулої] dddd [').call(this);
+ case 1:
+ case 2:
+ case 4:
+ return processHoursFunction('[Минулого] dddd [').call(this);
+ }
+ },
+ sameElse: 'L'
+ },
+ relativeTime : {
+ future : "за %s",
+ past : "%s тому",
+ s : "декілька секунд",
+ m : relativeTimeWithPlural,
+ mm : relativeTimeWithPlural,
+ h : "годину",
+ hh : relativeTimeWithPlural,
+ d : "день",
+ dd : relativeTimeWithPlural,
+ M : "місяць",
+ MM : relativeTimeWithPlural,
+ y : "рік",
+ yy : relativeTimeWithPlural
+ },
+
+ // M. E.: those two are virtually unused but a user might want to implement them for his/her website for some reason
+
+ meridiem : function (hour, minute, isLower) {
+ if (hour < 4) {
+ return "ночі";
+ } else if (hour < 12) {
+ return "ранку";
+ } else if (hour < 17) {
+ return "дня";
+ } else {
+ return "вечора";
+ }
+ },
+
+ ordinal: function (number, period) {
+ switch (period) {
+ case 'M':
+ case 'd':
+ case 'DDD':
+ case 'w':
+ case 'W':
+ return number + '-й';
+ case 'D':
+ return number + '-го';
+ default:
+ return number;
+ }
+ },
+
+ week : {
+ dow : 1, // Monday is the first day of the week.
+ doy : 7 // The week that contains Jan 1st is the first week of the year.
+ }
+ });
+}));
+// moment.js language configuration
+// language : uzbek
+// author : Sardor Muminov : https://github.com/muminoff
+
+(function (factory) {
+ factory(moment);
+}(function (moment) {
+ return moment.lang('uz', {
+ months : "январь_февраль_март_апрель_май_июнь_июль_август_сентябрь_октябрь_ноябрь_декабрь".split("_"),
+ monthsShort : "янв_фев_мар_апр_май_июн_июл_авг_сен_окт_ноя_дек".split("_"),
+ weekdays : "Якшанба_Душанба_Сешанба_Чоршанба_Пайшанба_Жума_Шанба".split("_"),
+ weekdaysShort : "Якш_Душ_Сеш_Чор_Пай_Жум_Шан".split("_"),
+ weekdaysMin : "Як_Ду_Се_Чо_Па_Жу_Ша".split("_"),
+ longDateFormat : {
+ LT : "HH:mm",
+ L : "DD/MM/YYYY",
+ LL : "D MMMM YYYY",
+ LLL : "D MMMM YYYY LT",
+ LLLL : "D MMMM YYYY, dddd LT"
+ },
+ calendar : {
+ sameDay : '[Бугун соат] LT [да]',
+ nextDay : '[Эртага] LT [да]',
+ nextWeek : 'dddd [куни соат] LT [да]',
+ lastDay : '[Кеча соат] LT [да]',
+ lastWeek : '[Утган] dddd [куни соат] LT [да]',
+ sameElse : 'L'
+ },
+ relativeTime : {
+ future : "Якин %s ичида",
+ past : "Бир неча %s олдин",
+ s : "фурсат",
+ m : "бир дакика",
+ mm : "%d дакика",
+ h : "бир соат",
+ hh : "%d соат",
+ d : "бир кун",
+ dd : "%d кун",
+ M : "бир ой",
+ MM : "%d ой",
+ y : "бир йил",
+ yy : "%d йил"
+ },
+ week : {
+ dow : 1, // Monday is the first day of the week.
+ doy : 7 // The week that contains Jan 4th is the first week of the year.
+ }
+ });
+}));
+// moment.js language configuration
+// language : vietnamese (vi)
+// author : Bang Nguyen : https://github.com/bangnk
+
+(function (factory) {
+ factory(moment);
+}(function (moment) {
+ return moment.lang('vi', {
+ months : "tháng 1_tháng 2_tháng 3_tháng 4_tháng 5_tháng 6_tháng 7_tháng 8_tháng 9_tháng 10_tháng 11_tháng 12".split("_"),
+ monthsShort : "Th01_Th02_Th03_Th04_Th05_Th06_Th07_Th08_Th09_Th10_Th11_Th12".split("_"),
+ weekdays : "chủ nhật_thứ hai_thứ ba_thứ tư_thứ năm_thứ sáu_thứ bảy".split("_"),
+ weekdaysShort : "CN_T2_T3_T4_T5_T6_T7".split("_"),
+ weekdaysMin : "CN_T2_T3_T4_T5_T6_T7".split("_"),
+ longDateFormat : {
+ LT : "HH:mm",
+ L : "DD/MM/YYYY",
+ LL : "D MMMM [năm] YYYY",
+ LLL : "D MMMM [năm] YYYY LT",
+ LLLL : "dddd, D MMMM [năm] YYYY LT",
+ l : "DD/M/YYYY",
+ ll : "D MMM YYYY",
+ lll : "D MMM YYYY LT",
+ llll : "ddd, D MMM YYYY LT"
+ },
+ calendar : {
+ sameDay: "[Hôm nay lúc] LT",
+ nextDay: '[Ngày mai lúc] LT',
+ nextWeek: 'dddd [tuần tới lúc] LT',
+ lastDay: '[Hôm qua lúc] LT',
+ lastWeek: 'dddd [tuần rồi lúc] LT',
+ sameElse: 'L'
+ },
+ relativeTime : {
+ future : "%s tới",
+ past : "%s trước",
+ s : "vài giây",
+ m : "một phút",
+ mm : "%d phút",
+ h : "một giờ",
+ hh : "%d giờ",
+ d : "một ngày",
+ dd : "%d ngày",
+ M : "một tháng",
+ MM : "%d tháng",
+ y : "một năm",
+ yy : "%d năm"
+ },
+ ordinal : function (number) {
+ return number;
+ },
+ week : {
+ dow : 1, // Monday is the first day of the week.
+ doy : 4 // The week that contains Jan 4th is the first week of the year.
+ }
+ });
+}));
+// moment.js language configuration
+// language : chinese
+// author : suupic : https://github.com/suupic
+// author : Zeno Zeng : https://github.com/zenozeng
+
+(function (factory) {
+ factory(moment);
+}(function (moment) {
+ return moment.lang('zh-cn', {
+ months : "一月_二月_三月_四月_五月_六月_七月_八月_九月_十月_十一月_十二月".split("_"),
+ monthsShort : "1月_2月_3月_4月_5月_6月_7月_8月_9月_10月_11月_12月".split("_"),
+ weekdays : "星期日_星期一_星期二_星期三_星期四_星期五_星期六".split("_"),
+ weekdaysShort : "周日_周一_周二_周三_周四_周五_周六".split("_"),
+ weekdaysMin : "日_一_二_三_四_五_六".split("_"),
+ longDateFormat : {
+ LT : "Ah点mm",
+ L : "YYYY-MM-DD",
+ LL : "YYYY年MMMD日",
+ LLL : "YYYY年MMMD日LT",
+ LLLL : "YYYY年MMMD日ddddLT",
+ l : "YYYY-MM-DD",
+ ll : "YYYY年MMMD日",
+ lll : "YYYY年MMMD日LT",
+ llll : "YYYY年MMMD日ddddLT"
+ },
+ meridiem : function (hour, minute, isLower) {
+ var hm = hour * 100 + minute;
+ if (hm < 600) {
+ return "凌晨";
+ } else if (hm < 900) {
+ return "早上";
+ } else if (hm < 1130) {
+ return "上午";
+ } else if (hm < 1230) {
+ return "中午";
+ } else if (hm < 1800) {
+ return "下午";
+ } else {
+ return "晚上";
+ }
+ },
+ calendar : {
+ sameDay : function () {
+ return this.minutes() === 0 ? "[今天]Ah[点整]" : "[今天]LT";
+ },
+ nextDay : function () {
+ return this.minutes() === 0 ? "[明天]Ah[点整]" : "[明天]LT";
+ },
+ lastDay : function () {
+ return this.minutes() === 0 ? "[昨天]Ah[点整]" : "[昨天]LT";
+ },
+ nextWeek : function () {
+ var startOfWeek, prefix;
+ startOfWeek = moment().startOf('week');
+ prefix = this.unix() - startOfWeek.unix() >= 7 * 24 * 3600 ? '[下]' : '[本]';
+ return this.minutes() === 0 ? prefix + "dddAh点整" : prefix + "dddAh点mm";
+ },
+ lastWeek : function () {
+ var startOfWeek, prefix;
+ startOfWeek = moment().startOf('week');
+ prefix = this.unix() < startOfWeek.unix() ? '[上]' : '[本]';
+ return this.minutes() === 0 ? prefix + "dddAh点整" : prefix + "dddAh点mm";
+ },
+ sameElse : 'LL'
+ },
+ ordinal : function (number, period) {
+ switch (period) {
+ case "d":
+ case "D":
+ case "DDD":
+ return number + "日";
+ case "M":
+ return number + "月";
+ case "w":
+ case "W":
+ return number + "周";
+ default:
+ return number;
+ }
+ },
+ relativeTime : {
+ future : "%s内",
+ past : "%s前",
+ s : "几秒",
+ m : "1分钟",
+ mm : "%d分钟",
+ h : "1小时",
+ hh : "%d小时",
+ d : "1天",
+ dd : "%d天",
+ M : "1个月",
+ MM : "%d个月",
+ y : "1年",
+ yy : "%d年"
+ },
+ week : {
+ // GB/T 7408-1994《数据元和交换格式·信息交换·日期和时间表示法》与ISO 8601:1988等效
+ dow : 1, // Monday is the first day of the week.
+ doy : 4 // The week that contains Jan 4th is the first week of the year.
+ }
+ });
+}));
+// moment.js language configuration
+// language : traditional chinese (zh-tw)
+// author : Ben : https://github.com/ben-lin
+
+(function (factory) {
+ factory(moment);
+}(function (moment) {
+ return moment.lang('zh-tw', {
+ months : "一月_二月_三月_四月_五月_六月_七月_八月_九月_十月_十一月_十二月".split("_"),
+ monthsShort : "1月_2月_3月_4月_5月_6月_7月_8月_9月_10月_11月_12月".split("_"),
+ weekdays : "星期日_星期一_星期二_星期三_星期四_星期五_星期六".split("_"),
+ weekdaysShort : "週日_週一_週二_週三_週四_週五_週六".split("_"),
+ weekdaysMin : "日_一_二_三_四_五_六".split("_"),
+ longDateFormat : {
+ LT : "Ah點mm",
+ L : "YYYY年MMMD日",
+ LL : "YYYY年MMMD日",
+ LLL : "YYYY年MMMD日LT",
+ LLLL : "YYYY年MMMD日ddddLT",
+ l : "YYYY年MMMD日",
+ ll : "YYYY年MMMD日",
+ lll : "YYYY年MMMD日LT",
+ llll : "YYYY年MMMD日ddddLT"
+ },
+ meridiem : function (hour, minute, isLower) {
+ var hm = hour * 100 + minute;
+ if (hm < 900) {
+ return "早上";
+ } else if (hm < 1130) {
+ return "上午";
+ } else if (hm < 1230) {
+ return "中午";
+ } else if (hm < 1800) {
+ return "下午";
+ } else {
+ return "晚上";
+ }
+ },
+ calendar : {
+ sameDay : '[今天]LT',
+ nextDay : '[明天]LT',
+ nextWeek : '[下]ddddLT',
+ lastDay : '[昨天]LT',
+ lastWeek : '[上]ddddLT',
+ sameElse : 'L'
+ },
+ ordinal : function (number, period) {
+ switch (period) {
+ case "d" :
+ case "D" :
+ case "DDD" :
+ return number + "日";
+ case "M" :
+ return number + "月";
+ case "w" :
+ case "W" :
+ return number + "週";
+ default :
+ return number;
+ }
+ },
+ relativeTime : {
+ future : "%s內",
+ past : "%s前",
+ s : "幾秒",
+ m : "一分鐘",
+ mm : "%d分鐘",
+ h : "一小時",
+ hh : "%d小時",
+ d : "一天",
+ dd : "%d天",
+ M : "一個月",
+ MM : "%d個月",
+ y : "一年",
+ yy : "%d年"
+ }
+ });
+}));
+
+ moment.lang('en');
+
/************************************
Exposing Moment
************************************/
- function makeGlobal(deprecate) {
- var warned = false, local_moment = moment;
+ function makeGlobal(shouldDeprecate) {
/*global ender:false */
if (typeof ender !== 'undefined') {
return;
}
- // here, `this` means `window` in the browser, or `global` on the server
- // add `moment` as a global object via a string identifier,
- // for Closure Compiler "advanced" mode
- if (deprecate) {
- global.moment = function () {
- if (!warned && console && console.warn) {
- warned = true;
- console.warn(
- "Accessing Moment through the global scope is " +
- "deprecated, and will be removed in an upcoming " +
- "release.");
- }
- return local_moment.apply(null, arguments);
- };
- extend(global.moment, local_moment);
+ oldGlobalMoment = globalScope.moment;
+ if (shouldDeprecate) {
+ globalScope.moment = deprecate(
+ "Accessing Moment through the global scope is " +
+ "deprecated, and will be removed in an upcoming " +
+ "release.",
+ moment);
} else {
- global['moment'] = moment;
+ globalScope.moment = moment;
}
}
// CommonJS module is defined
if (hasModule) {
module.exports = moment;
- makeGlobal(true);
} else if (typeof define === "function" && define.amd) {
define("moment", function (require, exports, module) {
- if (module.config && module.config() && module.config().noGlobal !== true) {
- // If user provided noGlobal, he is aware of global
- makeGlobal(module.config().noGlobal === undefined);
+ if (module.config && module.config() && module.config().noGlobal === true) {
+ // release the global variable
+ globalScope.moment = oldGlobalMoment;
}
return moment;
});
+ makeGlobal(true);
} else {
makeGlobal();
}
diff --git a/server/sonar-web/src/main/js/widgets/bubble-chart.js b/server/sonar-web/src/main/js/widgets/bubble-chart.js
index edafc4c6f66..de34819fdd5 100644
--- a/server/sonar-web/src/main/js/widgets/bubble-chart.js
+++ b/server/sonar-web/src/main/js/widgets/bubble-chart.js
@@ -128,23 +128,29 @@ window.SonarWidgets = window.SonarWidgets == null ? {} : window.SonarWidgets;
this.y = this.yLog() ? d3.scale.log() : d3.scale.linear();
this.size = d3.scale.linear();
- this.x
- .domain(d3.extent(this.components(), function (d) {
- return widget.getXMetric(d);
- }))
- .range([0, this.availableWidth]);
-
- this.y
- .domain(d3.extent(this.components(), function (d) {
- return widget.getYMetric(d);
- }))
- .range([this.availableHeight, 0]);
-
- this.size
- .domain(d3.extent(this.components(), function (d) {
- return widget.getSizeMetric(d);
- }))
- .range([5, 45]);
+ this.x.range([0, this.availableWidth]);
+ this.y.range([this.availableHeight, 0]);
+ this.size.range([5, 45]);
+
+ if (this.components().length > 1) {
+ this.x.domain(d3.extent(this.components(), function (d) {
+ return widget.getXMetric(d);
+ }));
+ this.y.domain(d3.extent(this.components(), function (d) {
+ return widget.getYMetric(d);
+ }));
+ this.size.domain(d3.extent(this.components(), function (d) {
+ return widget.getSizeMetric(d);
+ }));
+ } else {
+ var singleComponent = this.components()[0],
+ xm = this.getXMetric(singleComponent),
+ ym = this.getYMetric(singleComponent),
+ sm = this.getSizeMetric(singleComponent);
+ this.x.domain([xm * 0.8, xm * 1.2]);
+ this.y.domain([ym * 0.8, ym * 1.2]);
+ this.size.domain([sm * 0.8, sm * 1.2]);
+ }
// Create bubbles
@@ -318,17 +324,25 @@ window.SonarWidgets = window.SonarWidgets == null ? {} : window.SonarWidgets;
// Update scales
- this.x
- .domain(d3.extent(this.components(), function (d) {
- return widget.getXMetric(d);
- }))
- .range([0, this.availableWidth]);
-
- this.y
- .domain(d3.extent(this.components(), function (d) {
- return widget.getYMetric(d);
- }))
- .range([this.availableHeight, 0]);
+ this.x.range([0, this.availableWidth]);
+ this.y.range([this.availableHeight, 0]);
+
+ if (this.components().length > 1) {
+ this.x.domain(d3.extent(this.components(), function (d) {
+ return widget.getXMetric(d);
+ }));
+ this.y.domain(d3.extent(this.components(), function (d) {
+ return widget.getYMetric(d);
+ }))
+ } else {
+ var singleComponent = this.components()[0],
+ xm = this.getXMetric(singleComponent),
+ ym = this.getYMetric(singleComponent),
+ sm = this.getSizeMetric(singleComponent);
+ this.x.domain([xm * 0.8, xm * 1.2]);
+ this.y.domain([ym * 0.8, ym * 1.2]);
+ this.size.domain([sm * 0.8, sm * 1.2]);
+ }
if (this.x.domain()[0] === 0 && this.x.domain()[1] === 0) {
diff --git a/server/sonar-web/src/main/js/widgets/stack-area.js b/server/sonar-web/src/main/js/widgets/stack-area.js
index e5174e02462..5f625519bf1 100644
--- a/server/sonar-web/src/main/js/widgets/stack-area.js
+++ b/server/sonar-web/src/main/js/widgets/stack-area.js
@@ -152,7 +152,7 @@ window.SonarWidgets = window.SonarWidgets == null ? {} : window.SonarWidgets;
.attr('transform', trans(0, 54));
this.infoMetrics = [];
- var prevX = 110;
+ var prevX = 120;
this.metrics().forEach(function(d, i) {
var infoMetric = widget.infoWrap.append('g');
@@ -175,7 +175,7 @@ window.SonarWidgets = window.SonarWidgets == null ? {} : window.SonarWidgets;
widget.infoMetrics.push(infoMetric);
if (i % 3 === 2) {
- prevX += (infoMetricText.node().getComputedTextLength() + 60);
+ prevX += (infoMetricText.node().getComputedTextLength() + 70);
}
});
@@ -232,7 +232,7 @@ window.SonarWidgets = window.SonarWidgets == null ? {} : window.SonarWidgets;
// Update info
widget.infoDate
- .text(d3.time.format('%b %d, %Y')(widget.data()[0][cl].x));
+ .text(moment(widget.data()[0][cl].x).format('LL'));
var snapshotValue = this.snapshots()[snapshotIndex].fy,
totalValue = snapshotValue || (widget.stackDataTop[cl].y0 + widget.stackDataTop[cl].y);
diff --git a/server/sonar-web/src/main/js/widgets/timeline.js b/server/sonar-web/src/main/js/widgets/timeline.js
index 7825d853ca3..26faa583c46 100644
--- a/server/sonar-web/src/main/js/widgets/timeline.js
+++ b/server/sonar-web/src/main/js/widgets/timeline.js
@@ -240,7 +240,7 @@ window.SonarWidgets = window.SonarWidgets == null ? {} : window.SonarWidgets;
.attr('x2', sx);
widget.infoDate
- .text(d3.time.format('%b %d, %Y')(widget.data()[0][cl].x));
+ .text(moment(widget.data()[0][cl].x).format('LL'));
var metricsLines = widget.data().map(function(d, i) {
return widget.metrics()[i] + ': ' + d[cl].yl;
@@ -298,12 +298,12 @@ window.SonarWidgets = window.SonarWidgets == null ? {} : window.SonarWidgets;
// Update metric lines
var metricY = -1;
this.infoMetrics.forEach(function(metric, i) {
- var x = 110 + i * 170,
+ var x = 120 + i * 170,
x2 = x + 170;
if (x2 > widget.availableWidth) {
metricY += 18;
- x = 110;
+ x = 120;
}
metric
diff --git a/server/sonar-web/src/main/less/api-documentation.less b/server/sonar-web/src/main/less/api-documentation.less
index 98041a07289..d73a319aec8 100644
--- a/server/sonar-web/src/main/less/api-documentation.less
+++ b/server/sonar-web/src/main/less/api-documentation.less
@@ -1,6 +1,6 @@
-@import "variables";
-@import "mixins";
-@import "navigator/config";
+@import (reference) "variables";
+@import (reference) "mixins";
+@import (reference) "navigator/config";
@apiDocumentationSidebarWidth: 230px;
diff --git a/server/sonar-web/src/main/less/coding-rules.less b/server/sonar-web/src/main/less/coding-rules.less
index b58a9f94b7f..10551963557 100644
--- a/server/sonar-web/src/main/less/coding-rules.less
+++ b/server/sonar-web/src/main/less/coding-rules.less
@@ -1,6 +1,6 @@
-@import 'mixins';
-@import 'variables';
-@import 'navigator/config';
+@import (reference) 'mixins';
+@import (reference) 'variables';
+@import (reference) 'navigator/config';
@facetsHeight: 40px;
@@ -379,10 +379,6 @@ textarea.coding-rules-markdown-description {
display: none;
}
-.coding-rules-detail-not-ready {
- color: @orange;
-}
-
.coding-rules-list-tags {
display: inline-block;
vertical-align: top;
diff --git a/server/sonar-web/src/main/less/component-viewer-source-colorizer.less b/server/sonar-web/src/main/less/component-viewer-source-colorizer.less
index c3a56f66f40..f80ebe3fd83 100644
--- a/server/sonar-web/src/main/less/component-viewer-source-colorizer.less
+++ b/server/sonar-web/src/main/less/component-viewer-source-colorizer.less
@@ -1,4 +1,4 @@
-@import 'variables';
+@import (reference) 'variables';
.component-viewer-source {
diff --git a/server/sonar-web/src/main/less/component-viewer.less b/server/sonar-web/src/main/less/component-viewer.less
index 84ce9a28f63..b1268b39243 100644
--- a/server/sonar-web/src/main/less/component-viewer.less
+++ b/server/sonar-web/src/main/less/component-viewer.less
@@ -1,5 +1,6 @@
-@import "variables";
-@import "mixins";
+@import (reference) "variables";
+@import (reference) "mixins";
+@import (reference) "ui";
@headerHeight: 60px;
@workspaceWidth: 250px;
@@ -110,7 +111,6 @@
.component-viewer-source {
position: relative;
float: left;
- border: 1px solid @barBorderColor;
&.overflow {
.box-sizing(border-box);
@@ -119,6 +119,7 @@
.code {
width: 100%;
+ border: 1px solid @barBorderColor;
}
.code th {
@@ -354,6 +355,7 @@
position: relative;
top: -2px;
margin-left: 4px;
+ .link-no-underline;
}
.component-viewer-header-measures {
@@ -375,6 +377,7 @@
border-left: 1px solid @barBackgroundColor;
.box-sizing(border-box);
background-color: lighten(@blue, 30%);
+ .link-no-underline;
.transform-origin(0 0);
.trans;
@@ -399,6 +402,7 @@
display: block;
padding: 11px 20px;
white-space: nowrap;
+ .link-no-underline;
a& {
.trans(background);
@@ -481,6 +485,7 @@
display: block;
padding: 20px 10px;
font-size: @iconFontSize;
+ .link-no-underline;
}
.component-viewer-header-more-actions {
@@ -563,6 +568,7 @@
& > li > a.item {
color: @baseFontColor;
+ .link-no-underline;
.trans;
&:hover { background-color: @barBackgroundColor; }
@@ -587,6 +593,8 @@
.component-viewer-header-time-changes {
padding: 10px;
+
+ & > a { .link-no-underline; }
}
.component-viewer-header-sqale-rating {
diff --git a/server/sonar-web/src/main/less/dashboard.less b/server/sonar-web/src/main/less/dashboard.less
index aa181d7791e..65987d2f5ec 100644
--- a/server/sonar-web/src/main/less/dashboard.less
+++ b/server/sonar-web/src/main/less/dashboard.less
@@ -1,6 +1,6 @@
-@import "mixins";
-@import "variables";
-@import "ui";
+@import (reference) "mixins";
+@import (reference) "variables";
+@import (reference) "ui";
.dashboard-page {
background-color: @barBackgroundColor;
@@ -74,7 +74,7 @@
}
#dashboard .widget-title a {
- .highlighted-link;
+ .link-no-underline;
}
/*CONFIGURATION*/
@@ -390,17 +390,27 @@
// Widget Histogram
.widget-barchar {
+ max-width: 180px;
line-height: 1;
td {
vertical-align: middle !important;
}
- div.barchart div { height: 1em; }
+ div.barchart div {
+ min-width: 1px;
+ height: 1em;
+ }
}
.widget-measure-container .widget-barchar { margin-bottom: 10px; }
+.widget-measure-container .widget-barchart-more {
+ margin-top: -5px;
+ margin-bottom: 10px;
+ padding-left: 5px;
+}
+
// Description Widget
.description-widget-project {
diff --git a/server/sonar-web/src/main/less/icons.less b/server/sonar-web/src/main/less/icons.less
index 271a0045538..271349d43a7 100644
--- a/server/sonar-web/src/main/less/icons.less
+++ b/server/sonar-web/src/main/less/icons.less
@@ -1,5 +1,6 @@
-@import "variables";
-@import "mixins";
+@import (reference) "variables";
+@import (reference) "mixins";
+@import (reference) "ui";
@font-face {
font-family: 'sonar';
@@ -28,7 +29,7 @@
}
a[class^="icon-"], a[class*=" icon-"] {
- text-decoration: none !important;
+ .link-no-underline;
}
@@ -275,6 +276,11 @@ a[class^="icon-"], a[class*=" icon-"] {
&.icon-checkbox-single:after { content: "\e60e"; }
}
+// Used for align elements
+.icon-checkbox-invisible {
+ visibility: hidden;
+}
+
/*
* Common
@@ -509,11 +515,6 @@ a[class^="icon-"], a[class*=" icon-"] {
content: "\f016";
font-size: @iconFontSize;
}
-.icon-cross-project:before {
- content: "\f0ec";
- color: @purple;
- font-size: @iconFontSize;
-}
/*
diff --git a/server/sonar-web/src/main/less/layout.less b/server/sonar-web/src/main/less/layout.less
index 8f4c36156a8..75579aa28af 100644
--- a/server/sonar-web/src/main/less/layout.less
+++ b/server/sonar-web/src/main/less/layout.less
@@ -1,6 +1,6 @@
-@import "mixins";
-@import "variables";
-@import "ui";
+@import (reference) "mixins";
+@import (reference) "variables";
+@import (reference) "ui";
@media print {
/* ------------------- PRINT ------------------- */
@@ -70,8 +70,8 @@ body, a {
}
#hd a {
- .base-link;
- color: #e5e5e5;
+ color: #fff;
+ .link-no-underline;
&.selected { color: @blue; }
}
@@ -145,11 +145,7 @@ body, a {
}
#bc li a {
- text-decoration: none;
-}
-
-#bc li a:hover, #bc li a:focus {
- text-decoration: underline;
+ .link-no-underline;
}
#crumbs-ops {
@@ -163,11 +159,7 @@ body, a {
}
#crumbs-ops li a {
- text-decoration: none;
-}
-
-#crumbs-ops li a:hover, #crumbs-ops li a:focus {
- text-decoration: underline;
+ .link-no-underline;
}
#hd .dropdown-menu a {
@@ -224,7 +216,7 @@ ul.sidebar li {
}
ul.sidebar li > a {
- .highlighted-link;
+ .link-no-underline;
}
ul.sidebar li.active {
diff --git a/server/sonar-web/src/main/less/mixins.less b/server/sonar-web/src/main/less/mixins.less
index ef2dc10a7b5..66349f4b655 100644
--- a/server/sonar-web/src/main/less/mixins.less
+++ b/server/sonar-web/src/main/less/mixins.less
@@ -1,4 +1,4 @@
-@import "variables";
+@import (reference) "variables";
.clearfix() {
&:before, &:after { display: table; content: ""; line-height: 0; }
diff --git a/server/sonar-web/src/main/less/navigator.less b/server/sonar-web/src/main/less/navigator.less
index ab9401b0478..ddb1478deaa 100644
--- a/server/sonar-web/src/main/less/navigator.less
+++ b/server/sonar-web/src/main/less/navigator.less
@@ -1,5 +1,5 @@
-@import "mixins";
-@import "variables";
+@import (reference) "mixins";
+@import (reference) "variables";
@import "navigator/config";
@import "navigator/base";
diff --git a/server/sonar-web/src/main/less/navigator/filters.less b/server/sonar-web/src/main/less/navigator/filters.less
index e049c8ad87e..f56739867ef 100644
--- a/server/sonar-web/src/main/less/navigator/filters.less
+++ b/server/sonar-web/src/main/less/navigator/filters.less
@@ -209,10 +209,11 @@
label {
display: block;
padding: 5px @navigatorFilterPadding;
- transition: background 0.3s ease;
cursor: pointer;
white-space: nowrap;
overflow: hidden;
+ text-overflow: ellipsis;
+ .trans(background);
&:hover,
&.current {
@@ -228,14 +229,14 @@
vertical-align: text-bottom;
}
- & > span {
- display: inline-block;
- vertical-align: top;
- width: 86%;
- white-space: nowrap;
- overflow: hidden;
- text-overflow: ellipsis;
- }
+// & > span {
+// display: inline-block;
+// vertical-align: top;
+// width: 86%;
+// white-space: nowrap;
+// overflow: hidden;
+// text-overflow: ellipsis;
+// }
&.special {
font-style: italic;
diff --git a/server/sonar-web/src/main/less/quality-gates.less b/server/sonar-web/src/main/less/quality-gates.less
index fb56fbc7be7..070216d1b21 100644
--- a/server/sonar-web/src/main/less/quality-gates.less
+++ b/server/sonar-web/src/main/less/quality-gates.less
@@ -1,6 +1,6 @@
-@import "variables";
-@import "mixins";
-@import "navigator/config";
+@import (reference) "variables";
+@import (reference) "mixins";
+@import (reference) "navigator/config";
@qualityGateSidebarWidth: 230px;
@@ -125,4 +125,4 @@
border-left: none !important;
border-right: none !important;
}
-} \ No newline at end of file
+}
diff --git a/server/sonar-web/src/main/less/select2-sonar.less b/server/sonar-web/src/main/less/select2-sonar.less
index fed92898f3d..d6b89c29cc1 100644
--- a/server/sonar-web/src/main/less/select2-sonar.less
+++ b/server/sonar-web/src/main/less/select2-sonar.less
@@ -1,5 +1,5 @@
-@import "mixins";
-@import "variables";
+@import (reference) "mixins";
+@import (reference) "variables";
@imagesPath: '../images/select2.png';
@imagesPath2x: '../images/select2x2.png';
diff --git a/server/sonar-web/src/main/less/style.less b/server/sonar-web/src/main/less/style.less
index 8a607cfa136..db1329484a0 100644
--- a/server/sonar-web/src/main/less/style.less
+++ b/server/sonar-web/src/main/less/style.less
@@ -1,6 +1,6 @@
-@import "mixins";
-@import "variables";
-@import "ui";
+@import (reference) "mixins";
+@import (reference) "variables";
+@import (reference) "ui";
/*
* SonarQube, open source software quality management tool.
@@ -40,6 +40,7 @@
border-top: 1px solid #fff;
font-size: 85%;
padding: 10px 0;
+ line-height: 1.4;
}
.ie-warn {
@@ -53,10 +54,6 @@
text-decoration: underline;
}
-#ftlinks a {
- color: #444;
-}
-
/*
GENERAL
*/
@@ -64,6 +61,11 @@ a {
cursor: pointer;
}
+a.icon-favorite,
+a.icon-not-favorite {
+ .link-no-underline;
+}
+
.loading {
background: url("../images/loading.gif") no-repeat 4px 2px;
color: #444;
@@ -386,6 +388,7 @@ table.data > thead > tr > th {
vertical-align: top;
font-size: 93%;
padding: 4px 7px 4px 3px;
+ line-height: 1.4;
text-transform: uppercase;
& > .small {
@@ -471,7 +474,6 @@ table.sortable .sortcol {
padding-left: 15px;
background-repeat: no-repeat;
background-position: right center;
- text-decoration: underline;
}
table.sortable .sortasc {
@@ -1792,10 +1794,6 @@ ul.bullet li {
padding: 0;
}
-.tabs li a {
- outline: none;
-}
-
.tabs2 li a, .tabs li a {
float: left;
color: #777;
@@ -1803,6 +1801,7 @@ ul.bullet li {
height: 17px;
margin: 0 1px 0 0;
padding: 1px 5px;
+ .link-no-underline;
}
.tabs2 li a.selected, .tabs li a.selected, .tabs .ui-tabs-active a {
@@ -1864,6 +1863,14 @@ ul.bullet li {
color: #fff;
margin: 0;
padding: 0 3px;
+
+ a > & {
+ margin-bottom: -1px;
+ border-bottom: 1px solid @orange;
+ .trans;
+
+ &:hover { opacity: 0.8; }
+ }
}
.alert_ERROR {
@@ -1871,6 +1878,14 @@ ul.bullet li {
color: #fff;
margin: 0;
padding: 0 3px;
+
+ a > & {
+ margin-bottom: -1px;
+ border-bottom: 1px solid @red;
+ .trans;
+
+ &:hover { opacity: 0.8; }
+ }
}
#comparison span.best {
@@ -2076,11 +2091,7 @@ table.matrix tbody td.title {
}
a.nolink {
- text-decoration: none;
-}
-
-a.nolink:hover {
- text-decoration: underline;
+ .link-no-underline;
}
h1 strong, .dashbox .title, .gwt-SourcePanel .sources .msg li strong {
@@ -2222,11 +2233,6 @@ table.nowrap td.small, td.nowrap.small, th.nowrap.small {
background: #FFFBCC;
}
-.action {
- text-decoration: underline;
- cursor: pointer;
-}
-
/* Used on links which are located inside a dense text place or in tables */
/* in order to rapidly identify them */
.link-action {
@@ -2850,6 +2856,7 @@ div.rule-title {
line-height: @iconSmallFontSize;
color: #fff;
opacity: 0.5;
+ .link-no-underline;
&:hover { opacity: 1; }
}
diff --git a/server/sonar-web/src/main/less/ui.less b/server/sonar-web/src/main/less/ui.less
index 571f245e061..374bb140db2 100644
--- a/server/sonar-web/src/main/less/ui.less
+++ b/server/sonar-web/src/main/less/ui.less
@@ -1,5 +1,5 @@
-@import "variables";
-@import "mixins";
+@import (reference) "variables";
+@import (reference) "mixins";
@@ -107,12 +107,12 @@ select::-moz-focus-inner, input::-moz-focus-inner, button::-moz-focus-inner {
.link;
}
-.highlighted-link {
+.highlighted-link() {
color: @darkBlue;
.link;
}
-.underlined-link {
+.underlined-link() {
.highlighted-link;
border-bottom: 1px solid @lightBlue;
@@ -139,14 +139,19 @@ select::-moz-focus-inner, input::-moz-focus-inner, button::-moz-focus-inner {
}
}
-.active-link {
+.link-no-underline() {
border-bottom: none;
- font-weight: 500;
}
+
a {
cursor: pointer;
- .highlighted-link;
+ .underlined-link;
+
+ &.active-link {
+ border-bottom: none;
+ font-weight: 500;
+ }
}
@@ -238,6 +243,8 @@ input[type=button] {
}
}
+.button { line-height: 22px; }
+
.button-red {
&:hover, &:focus {
border-color: #900;
@@ -267,7 +274,8 @@ input[type=button] {
font-size: 0;
white-space: nowrap;
- & > button {
+ & > button,
+ & > .button {
position: relative;
z-index: 2;
display: inline-block;
@@ -283,11 +291,16 @@ input[type=button] {
}
}
- & > button + button {
+ & > .button { line-height: 16px; }
+
+ & > button + button,
+ & > button + .button,
+ & > .button + button,
+ & > .button + .button {
margin-left: -1px;
}
- & > a {
+ & > a:not(.button) {
vertical-align: middle;
margin: 0 8px;
font-size: @smallFontSize;
@@ -325,25 +338,38 @@ input[type=button] {
color: #fff;
font-weight: 300;
text-align: center;
+
+ a > & {
+ margin-bottom: -1px;
+ border-bottom: 1px solid;
+ .trans;
+
+ &:hover { opacity: 0.8; }
+ }
}
.rating-A {
background-color: #00AA00;
+ a & { border-bottom-color: #00AA00; }
}
.rating-B {
background-color: #80CC00;
+ a & { border-bottom-color: #80CC00; }
}
.rating-C {
background-color: #FFEE00;
color: @baseFontColor;
+ a & { border-bottom-color: #FFEE00; }
}
.rating-D {
background-color: #F77700;
+ a & { border-bottom-color: #F77700; }
}
.rating-E {
background-color: #EE0000;
+ a & { border-bottom-color: #EE0000; }
}
diff --git a/server/sonar-web/src/main/webapp/WEB-INF/app/controllers/issues_controller.rb b/server/sonar-web/src/main/webapp/WEB-INF/app/controllers/issues_controller.rb
index a675fd5f3a9..7690cea09be 100644
--- a/server/sonar-web/src/main/webapp/WEB-INF/app/controllers/issues_controller.rb
+++ b/server/sonar-web/src/main/webapp/WEB-INF/app/controllers/issues_controller.rb
@@ -109,6 +109,8 @@ class IssuesController < ApplicationController
def copy_form
require_parameters :id
@filter = find_filter(params[:id].to_i)
+ @filter.setUser(nil)
+ @filter.setShared(false)
render :partial => 'issues/filter_copy_form'
end
diff --git a/server/sonar-web/src/main/webapp/WEB-INF/app/controllers/measures_controller.rb b/server/sonar-web/src/main/webapp/WEB-INF/app/controllers/measures_controller.rb
index 2ec278531e9..7c955be664b 100644
--- a/server/sonar-web/src/main/webapp/WEB-INF/app/controllers/measures_controller.rb
+++ b/server/sonar-web/src/main/webapp/WEB-INF/app/controllers/measures_controller.rb
@@ -165,6 +165,8 @@ class MeasuresController < ApplicationController
def copy_form
require_parameters :id
@filter = find_filter(params[:id])
+ @filter.shared = false
+ @filter.user_id = nil
render :partial => 'measures/copy_form'
end
diff --git a/server/sonar-web/src/main/webapp/WEB-INF/app/controllers/project_controller.rb b/server/sonar-web/src/main/webapp/WEB-INF/app/controllers/project_controller.rb
index 0afc33579e6..150b37a3e7e 100644
--- a/server/sonar-web/src/main/webapp/WEB-INF/app/controllers/project_controller.rb
+++ b/server/sonar-web/src/main/webapp/WEB-INF/app/controllers/project_controller.rb
@@ -408,7 +408,11 @@ class ProjectController < ApplicationController
name = event.name
resource_id = event.resource_id
events = find_events(event)
- Event.delete(events.map { |e| e.id })
+ Event.transaction do
+ events.map { |e| e.id }.each_slice(999) do |safe_for_oracle_ids|
+ Event.delete(safe_for_oracle_ids)
+ end
+ end
flash[:notice] = message('project_history.event_deleted', :params => name)
redirect_to :action => 'history', :id => resource_id
diff --git a/server/sonar-web/src/main/webapp/WEB-INF/app/helpers/application_helper.rb b/server/sonar-web/src/main/webapp/WEB-INF/app/helpers/application_helper.rb
index 9a58a14981d..5ea88ea1f96 100644
--- a/server/sonar-web/src/main/webapp/WEB-INF/app/helpers/application_helper.rb
+++ b/server/sonar-web/src/main/webapp/WEB-INF/app/helpers/application_helper.rb
@@ -129,7 +129,7 @@ module ApplicationHelper
def boolean_icon(boolean_value, options={})
if boolean_value
- image_tag('tick.png', options)
+ "<i class='icon-check'></i>"
elsif options[:display_false]
image_tag('cross.png', options)
else
@@ -947,7 +947,7 @@ module ApplicationHelper
total = pagination.total.to_i
page_index = pagination.pageIndex() ? pagination.pageIndex().to_i : 1
pages = pagination.pages().to_i
- results_html = options[:url_results] ? message('x_results', :params => "<a class='underlined-link' href='#{options[:url_results]}'>#{total}</a>") : message('x_results', :params => [total])
+ results_html = options[:url_results] ? message('x_results', :params => "<a href='#{options[:url_results]}'>#{total}</a>") : message('x_results', :params => [total])
html = '<tfoot'
html += " id='#{options[:id]}'" if options[:id]
diff --git a/server/sonar-web/src/main/webapp/WEB-INF/app/helpers/measures_helper.rb b/server/sonar-web/src/main/webapp/WEB-INF/app/helpers/measures_helper.rb
index ca4472c524b..b316df15a2e 100644
--- a/server/sonar-web/src/main/webapp/WEB-INF/app/helpers/measures_helper.rb
+++ b/server/sonar-web/src/main/webapp/WEB-INF/app/helpers/measures_helper.rb
@@ -28,7 +28,7 @@ module MeasuresHelper
if column.period
html += "<br><span class='note'>#{Api::Utils.period_abbreviation(column.period)}</small>"
end
- if filter.sort_key==column.key
+ if column.sort? && filter.sort_key==column.key
html << (filter.sort_asc? ? ' <i class="icon-sort-asc"></i>' : ' <i class="icon-sort-desc"></i>')
end
"<th class='#{column.align} #{column.title_css}'>#{html}</th>"
@@ -50,9 +50,9 @@ module MeasuresHelper
end
elsif column.key=='name'
- "#{qualifier_icon(row.snapshot)} #{link_to(h(row.snapshot.resource.name(true)), {:controller => 'dashboard', :id => row.snapshot.resource_id}, {:title => h(row.snapshot.resource.key), :class => 'underlined-link'})}"
+ "#{qualifier_icon(row.snapshot)} #{link_to(h(row.snapshot.resource.name(true)), {:controller => 'dashboard', :id => row.snapshot.resource_id}, {:title => h(row.snapshot.resource.key)})}"
elsif column.key=='short_name'
- "#{qualifier_icon(row.snapshot)} #{link_to(h(row.snapshot.resource.name(false)), {:controller => 'dashboard', :id => row.snapshot.resource_id}, {:title => h(row.snapshot.resource.key), :class => 'highlighted-link'})}"
+ "#{qualifier_icon(row.snapshot)} #{link_to(h(row.snapshot.resource.name(false)), {:controller => 'dashboard', :id => row.snapshot.resource_id}, {:title => h(row.snapshot.resource.key)})}"
elsif column.key=='date'
human_short_date(row.snapshot.created_at)
elsif column.key=='project_creation_date'
diff --git a/server/sonar-web/src/main/webapp/WEB-INF/app/models/measure_filter.rb b/server/sonar-web/src/main/webapp/WEB-INF/app/models/measure_filter.rb
index a0a1a25fc0b..0ec73aabe27 100644
--- a/server/sonar-web/src/main/webapp/WEB-INF/app/models/measure_filter.rb
+++ b/server/sonar-web/src/main/webapp/WEB-INF/app/models/measure_filter.rb
@@ -158,8 +158,11 @@ class MeasureFilter < ActiveRecord::Base
@criteria = self.data.split(CRITERIA_SEPARATOR).inject(HashWithIndifferentAccess.new) do |h, s|
k, v=s.split('=')
if k && v
- # Empty values are removed
- v=v.split(CRITERIA_KEY_VALUE_SEPARATOR).select{|v| !v.empty?} if v.include?(CRITERIA_KEY_VALUE_SEPARATOR)
+ # nameSearch can contains comma, in this case we should not split the value
+ if k != 'nameSearch'
+ # Empty values are removed
+ v=v.split(CRITERIA_KEY_VALUE_SEPARATOR).select{|v| !v.empty?} if v.include?(CRITERIA_KEY_VALUE_SEPARATOR)
+ end
h[k]=v
end
h
diff --git a/server/sonar-web/src/main/webapp/WEB-INF/app/views/admin_dashboards/_list.html.erb b/server/sonar-web/src/main/webapp/WEB-INF/app/views/admin_dashboards/_list.html.erb
index 5490789acc7..4f474906a13 100644
--- a/server/sonar-web/src/main/webapp/WEB-INF/app/views/admin_dashboards/_list.html.erb
+++ b/server/sonar-web/src/main/webapp/WEB-INF/app/views/admin_dashboards/_list.html.erb
@@ -24,12 +24,12 @@
</td>
<td class="order">
<% if index > 0 %>
- <%= link_to image_tag('blue-up.png'), {:action => :up, :id => active.id}, :method => :post, :id => "up-#{u active.name}" %>
+ <%= link_to '', {:action => :up, :id => active.id}, :method => :post, :id => "up-#{u active.name}", :class => 'icon-move-up' %>
<% else %>
<%= image_tag('transparent_16.gif') %>
<% end %>
<% if index < active_dashboards.size-1 %>
- <%= link_to image_tag('blue-down.png'), {:action => :down, :id => active.id}, :method => :post, :id => "down-#{u active.name}" %>
+ <%= link_to '', {:action => :down, :id => active.id}, :method => :post, :id => "down-#{u active.name}", :class => 'icon-move-down' %>
<% else %>
<%= image_tag('transparent_16.gif') %>
<% end %>
diff --git a/server/sonar-web/src/main/webapp/WEB-INF/app/views/comparison/index.html.erb b/server/sonar-web/src/main/webapp/WEB-INF/app/views/comparison/index.html.erb
index 9a3b6e3d548..71a1b4463a1 100644
--- a/server/sonar-web/src/main/webapp/WEB-INF/app/views/comparison/index.html.erb
+++ b/server/sonar-web/src/main/webapp/WEB-INF/app/views/comparison/index.html.erb
@@ -196,7 +196,7 @@
%>
<th style="text-align: center; vertical-align: top; line-height: 1.5;">
<span class="no-transform">
- <a class="underlined-link" href="<%= ApplicationController.root_context -%>/dashboard/index/<%= s.resource.key -%>"><%= h s.resource.name(true) -%></a>
+ <a href="<%= ApplicationController.root_context -%>/dashboard/index/<%= s.resource.key -%>"><%= h s.resource.name(true) -%></a>
<br/>
<span class="note"><b><%= event ? event.name : message('comparison.version.latest') -%></b></span>
<br/>
diff --git a/server/sonar-web/src/main/webapp/WEB-INF/app/views/components/_list_table_header.rhtml b/server/sonar-web/src/main/webapp/WEB-INF/app/views/components/_list_table_header.rhtml
index 17838c3e097..5d74204a211 100644
--- a/server/sonar-web/src/main/webapp/WEB-INF/app/views/components/_list_table_header.rhtml
+++ b/server/sonar-web/src/main/webapp/WEB-INF/app/views/components/_list_table_header.rhtml
@@ -16,7 +16,7 @@
<th><% if logged_in? %><%= link_to_favourite(@snapshot.project) -%><% end %></th>
<th class="left text <%= 'sortfirstasc' if @components_configuration.sorted_by_project_name? -%>">
<%= qualifier_icon(@snapshot) -%>
- <a class="underlined-link no-transform" x="<%= u(@snapshot.project.name) -%>" href="<%= ApplicationController.root_context + "/dashboard/index/#{@snapshot.project.id}" -%>"><%= h @snapshot.project.name -%></a>
+ <a class="no-transform" x="<%= u(@snapshot.project.name) -%>" href="<%= ApplicationController.root_context + "/dashboard/index/#{@snapshot.project.id}" -%>"><%= h @snapshot.project.name -%></a>
</th>
<% @columns.each do |column| %>
<%= get_header_content(column, @snapshot) -%>
diff --git a/server/sonar-web/src/main/webapp/WEB-INF/app/views/components/index.html.erb b/server/sonar-web/src/main/webapp/WEB-INF/app/views/components/index.html.erb
index f85b44f48f2..f24a454aa46 100644
--- a/server/sonar-web/src/main/webapp/WEB-INF/app/views/components/index.html.erb
+++ b/server/sonar-web/src/main/webapp/WEB-INF/app/views/components/index.html.erb
@@ -53,7 +53,7 @@
<td class="left" x="<%= u(snapshot.project.name) -%>">
<%= qualifier_icon(snapshot) %>
<% if snapshot.display_dashboard? %>
- <%= link_to_resource(project, h(snapshot.project.name), :class => 'underlined-link') %>
+ <%= link_to_resource(project, h(snapshot.project.name)) %>
<% else %>
<%= h snapshot.project.name %>
<% end %>
diff --git a/server/sonar-web/src/main/webapp/WEB-INF/app/views/dashboard/_header.html.erb b/server/sonar-web/src/main/webapp/WEB-INF/app/views/dashboard/_header.html.erb
index 1dab108bcc4..900075c19f0 100644
--- a/server/sonar-web/src/main/webapp/WEB-INF/app/views/dashboard/_header.html.erb
+++ b/server/sonar-web/src/main/webapp/WEB-INF/app/views/dashboard/_header.html.erb
@@ -2,16 +2,16 @@
<div class="line-block">
<% if logged_in? %>
- <ul class="operations noprint">
+ <div class="operations noprint button-group">
<% if back %>
- <li><%= link_to message('dashboard.back_to_dashboard'), dashboard_action(:index) -%></li>
+ <%= link_to message('dashboard.back_to_dashboard'), dashboard_action(:index), :class => 'button' -%>
<% else %>
<% if @dashboard.editable_by?(current_user) %>
- <li><%= link_to message('dashboard.configure_widgets'), dashboard_action(:configure) -%></li>
+ <%= link_to message('dashboard.configure_widgets'), dashboard_action(:configure), :class => 'button' -%>
<% end %>
<% end %>
- <li class="last"><%= link_to message('dashboard.manage_dashboards'), {:controller => :dashboards, :action => :index, :resource => (@resource.id if @resource) } -%></li>
- </ul>
+ <%= link_to message('dashboard.manage_dashboards'), {:controller => :dashboards, :action => :index, :resource => (@resource.id if @resource) }, :class => 'button' -%>
+ </div>
<% end %>
<% if @snapshot %>
diff --git a/server/sonar-web/src/main/webapp/WEB-INF/app/views/dashboards/_available_dashboards.html.erb b/server/sonar-web/src/main/webapp/WEB-INF/app/views/dashboards/_available_dashboards.html.erb
index 073f91dc35a..5737650fb80 100644
--- a/server/sonar-web/src/main/webapp/WEB-INF/app/views/dashboards/_available_dashboards.html.erb
+++ b/server/sonar-web/src/main/webapp/WEB-INF/app/views/dashboards/_available_dashboards.html.erb
@@ -19,7 +19,7 @@
<tr id="dashboard-<%= dashboard.id -%>" class="<%= cycle('even', 'odd', :name => 'shared') -%>">
<td>
<%= link_to h(dashboard.name(true)), {:controller => :dashboard, :action => :index, :did => dashboard.id, :id => (resource_id unless dashboard.global?)},
- :id => "view-#{u dashboard.name}", :class => 'underlined-link' %>
+ :id => "view-#{u dashboard.name}" %>
<div class="description"><%= h dashboard.description -%></div>
</td>
<td class="shared">
diff --git a/server/sonar-web/src/main/webapp/WEB-INF/app/views/dashboards/_my_dashboards.html.erb b/server/sonar-web/src/main/webapp/WEB-INF/app/views/dashboards/_my_dashboards.html.erb
index 6a36cbe0706..68240dea729 100644
--- a/server/sonar-web/src/main/webapp/WEB-INF/app/views/dashboards/_my_dashboards.html.erb
+++ b/server/sonar-web/src/main/webapp/WEB-INF/app/views/dashboards/_my_dashboards.html.erb
@@ -19,7 +19,7 @@
<tr id="dashboard-<%= dashboard.id -%>" class="<%= cycle('even', 'odd', :name => 'dashboard') -%>">
<td>
<%= link_to h(dashboard.name(true)), {:controller => :dashboard, :action => :index, :did => dashboard.id, :id => (resource_id unless dashboard.global?)},
- :id => "view-#{u dashboard.name}", :class => 'underlined-link' %>
+ :id => "view-#{u dashboard.name}" %>
<div class="description"><%= h dashboard.description -%></div>
</td>
<td class="shared">
diff --git a/server/sonar-web/src/main/webapp/WEB-INF/app/views/drilldown/_severity.html.erb b/server/sonar-web/src/main/webapp/WEB-INF/app/views/drilldown/_severity.html.erb
index cb890b1cf7a..8625066e0c4 100644
--- a/server/sonar-web/src/main/webapp/WEB-INF/app/views/drilldown/_severity.html.erb
+++ b/server/sonar-web/src/main/webapp/WEB-INF/app/views/drilldown/_severity.html.erb
@@ -2,7 +2,7 @@
<tr class="<%= css -%> <%= 'selected' if selected -%>">
<td><i class="icon-severity-<%= severity.downcase -%>"></i></td>
<td>
- <%= link_to message("severity.#{severity}"), {:controller => :drilldown, :action => :issues, :id => @resource.id, :severity => (selected ? nil : severity), :period => @period}, :class => 'underlined-link' %>
+ <%= link_to message("severity.#{severity}"), {:controller => :drilldown, :action => :issues, :id => @resource.id, :severity => (selected ? nil : severity), :period => @period} %>
</td>
<td style="padding-left: 10px;" align="right" nowrap>
<%= @period ? format_variation(measure, :index => @period, :style => 'light') : format_measure(measure) -%>
diff --git a/server/sonar-web/src/main/webapp/WEB-INF/app/views/drilldown/issues.html.erb b/server/sonar-web/src/main/webapp/WEB-INF/app/views/drilldown/issues.html.erb
index 8aef0011a5e..7c588467e66 100644
--- a/server/sonar-web/src/main/webapp/WEB-INF/app/views/drilldown/issues.html.erb
+++ b/server/sonar-web/src/main/webapp/WEB-INF/app/views/drilldown/issues.html.erb
@@ -100,8 +100,7 @@
{:controller => :drilldown, :action => :issues, :id => @resource.id, :rule => (selected ? nil : rule.key),
:rule_sev => (selected ? nil : rule_measure.severity), :sid => nil, :severity => @severity, :period => @period,
:rids => (selected ? nil : @selected_rids)},
- :title => "#{rule.plugin_name}: #{rule.plugin_rule_key}",
- :class => 'underlined-link'
+ :title => "#{rule.plugin_name}: #{rule.plugin_rule_key}"
) -%>
</td>
<td class="right" nowrap="nowrap">
@@ -153,7 +152,7 @@
<a href="<%= url_for :controller => 'dashboard', :action => 'index', :id => resource.id, :period => @period, :metric => (@metric && @metric.key),
:rule => @rule ? @rule.id : @severity -%>"
onclick="window.open(this.href,'resource-<%= resource.key.parameterize -%>','scrollbars=1,resizable=1');return false;"
- id="popup-<%= resource.key.parameterize -%>"
+ id="popup-<%= resource.key.parameterize -%>" class="nolink"
target="_blank"><i class="icon-detach" title="<%= message('new_window') -%>"></i></a>
<% else %>
<%= link_to(image_tag('zoom.png'), {:id => resource.id}, {:class => 'nolink'}) %>
@@ -161,9 +160,9 @@
<%= qualifier_icon(resource) -%>&nbsp;
<% if resource.source_code? %>
<a href="#" alt="<%= h resource.name(true) -%>" title="<%= h resource.name(true) -%>" data-key="<%= resource.key -%>"
- class="js-drilldown-link underlined-link"><%= h resource.name(false) %></a>
+ class="js-drilldown-link"><%= h resource.name(false) %></a>
<% else %>
- <%= link_to(h(resource.name), {:only_path => true, :overwrite_params => {:rids => (selected ? rids-[resource.id] : rids+[resource.id])}}, :class => 'underlined-link') -%>
+ <%= link_to(h(resource.name), {:only_path => true, :overwrite_params => {:rids => (selected ? rids-[resource.id] : rids+[resource.id])}}) -%>
<% end %>
</td>
<td class="right" nowrap>
diff --git a/server/sonar-web/src/main/webapp/WEB-INF/app/views/drilldown/measures.html.erb b/server/sonar-web/src/main/webapp/WEB-INF/app/views/drilldown/measures.html.erb
index 0bea2a00709..453e9e8ba65 100644
--- a/server/sonar-web/src/main/webapp/WEB-INF/app/views/drilldown/measures.html.erb
+++ b/server/sonar-web/src/main/webapp/WEB-INF/app/views/drilldown/measures.html.erb
@@ -77,7 +77,7 @@
<% if resource.source_code? %>
<a href="<%= url_for :controller => 'dashboard', :action => 'index', :id => resource.id, :period => @period, :metric => (@metric && @metric.key), :rule => @rule ? @rule.id : @severity -%>"
onclick="window.open(this.href,'resource-<%= resource.key.parameterize -%>','scrollbars=1,resizable=1');return false;"
- id="popup-<%= resource.key.parameterize -%>"
+ id="popup-<%= resource.key.parameterize -%>" class="nolink"
target="_blank"><i class="icon-detach" title="<%= message('new_window') -%>"></i></a>
<% else %>
<%= link_to(image_tag('zoom.png'), {:id => resource.id, :metric => @metric.id}, {:class => 'nolink'}) -%>
@@ -85,9 +85,9 @@
<%= qualifier_icon(resource) -%>&nbsp;
<% if resource.source_code? %>
<a href="#" title="<%= h resource.name(true) -%>" data-key="<%= resource.key -%>"
- class="js-drilldown-link underlined-link"><%= h resource.name(false) %></a>
+ class="js-drilldown-link"><%= h resource.name(false) %></a>
<% else %>
- <%= link_to(h(resource.name), params.merge({:only_path => true, :rids => (selected ? rids-[resource.id] : rids+[resource.id])}), :class => 'underlined-link') -%>
+ <%= link_to(h(resource.name), params.merge({:only_path => true, :rids => (selected ? rids-[resource.id] : rids+[resource.id])})) -%>
<% end %>
</td>
<td class="right">
diff --git a/server/sonar-web/src/main/webapp/WEB-INF/app/views/groups/index.html.erb b/server/sonar-web/src/main/webapp/WEB-INF/app/views/groups/index.html.erb
index 671e29f1ab2..3d75fcbfdba 100644
--- a/server/sonar-web/src/main/webapp/WEB-INF/app/views/groups/index.html.erb
+++ b/server/sonar-web/src/main/webapp/WEB-INF/app/views/groups/index.html.erb
@@ -22,10 +22,10 @@
<table class="data width100 sortable" id="groups">
<thead>
<tr>
- <th class="left sortfirstasc">Name</th>
- <th class="left">Description</th>
- <th class="left">Members</th>
- <th class="operations nosort" nowrap>Operations</th>
+ <th class="left sortfirstasc"><a>Name</a></th>
+ <th class="left"><a>Description</a></th>
+ <th class="left"><a>Members</a></th>
+ <th class="operations nosort" nowrap><a>Operations</a></th>
</tr>
</thead>
<tbody>
diff --git a/server/sonar-web/src/main/webapp/WEB-INF/app/views/issues/_filter_shared_form.html.erb b/server/sonar-web/src/main/webapp/WEB-INF/app/views/issues/_filter_shared_form.html.erb
index d90b9660b73..24c3891792a 100644
--- a/server/sonar-web/src/main/webapp/WEB-INF/app/views/issues/_filter_shared_form.html.erb
+++ b/server/sonar-web/src/main/webapp/WEB-INF/app/views/issues/_filter_shared_form.html.erb
@@ -22,7 +22,7 @@
<% end %>
<% if Internal.issues.canUserShareIssueFilter() %>
<div class="modal-field">
- <% if !@filter || @filter.user == current_user.login %>
+ <% if !@filter || @filter.user.nil? || @filter.user == current_user.login %>
<label for="shared"><%= message('issue_filter.form.share') -%></label>
<input id="shared" name="shared" type="checkbox" value="true" <%= 'checked' if (@filter && @filter.shared) -%>/>
<% else %>
@@ -30,4 +30,4 @@
<% end %>
</div>
<% end %>
-</div> \ No newline at end of file
+</div>
diff --git a/server/sonar-web/src/main/webapp/WEB-INF/app/views/issues/search.html.erb b/server/sonar-web/src/main/webapp/WEB-INF/app/views/issues/search.html.erb
index c76a3a81826..fac32ece75d 100644
--- a/server/sonar-web/src/main/webapp/WEB-INF/app/views/issues/search.html.erb
+++ b/server/sonar-web/src/main/webapp/WEB-INF/app/views/issues/search.html.erb
@@ -27,6 +27,7 @@
_.extend(window.SS, {
currentUser: '<%= current_user.login if current_user -%>',
+ currentUserName: '<%= current_user.name if current_user -%>',
severities: <%= RulesConfigurationController::RULE_PRIORITIES.to_json.html_safe -%>,
statuses: <%= @options_for_statuses.to_json.html_safe -%>,
resolutions: <%= @options_for_resolutions.to_json.html_safe -%>,
diff --git a/server/sonar-web/src/main/webapp/WEB-INF/app/views/layouts/_footer.html.erb b/server/sonar-web/src/main/webapp/WEB-INF/app/views/layouts/_footer.html.erb
index 1512e0ea45a..e4985adb843 100644
--- a/server/sonar-web/src/main/webapp/WEB-INF/app/views/layouts/_footer.html.erb
+++ b/server/sonar-web/src/main/webapp/WEB-INF/app/views/layouts/_footer.html.erb
@@ -2,7 +2,7 @@
<div style="position:fixed;z-index:99999;top:0;bottom:0;left:0;right:0;background:#fff;">
<div style="margin-top:150px;text-align:center;line-height:1.4;color:#333;">
The web interface cannot be displayed because your browser is not supported.<br>
- Please switch to a <a class="underlined-link" target="_blank"
+ Please switch to a <a target="_blank"
href="http://docs.codehaus.org/x/zYHEBg">supported version or another supported browser</a>.
</div>
</div>
diff --git a/server/sonar-web/src/main/webapp/WEB-INF/app/views/layouts/_head.html.erb b/server/sonar-web/src/main/webapp/WEB-INF/app/views/layouts/_head.html.erb
index 25be7638c90..193a0a0096a 100644
--- a/server/sonar-web/src/main/webapp/WEB-INF/app/views/layouts/_head.html.erb
+++ b/server/sonar-web/src/main/webapp/WEB-INF/app/views/layouts/_head.html.erb
@@ -22,6 +22,9 @@
<link href="<%= ApplicationController.root_context -%>/css/sonar.css" rel="stylesheet" media="all">
<%= yield :style -%>
+ <script>
+ var pageLang = '<%= I18n.locale.to_s.gsub(/-/, '_') -%>';
+ </script>
<script src="<%= ApplicationController.root_context -%>/js/sonar.js"></script>
<script>
var baseUrl = '<%= ApplicationController.root_context -%>';
diff --git a/server/sonar-web/src/main/webapp/WEB-INF/app/views/measures/_display_list.html.erb b/server/sonar-web/src/main/webapp/WEB-INF/app/views/measures/_display_list.html.erb
index 8a63f3807dd..3dae12f9a0e 100644
--- a/server/sonar-web/src/main/webapp/WEB-INF/app/views/measures/_display_list.html.erb
+++ b/server/sonar-web/src/main/webapp/WEB-INF/app/views/measures/_display_list.html.erb
@@ -222,11 +222,11 @@
</tbody>
<% if widget_id %>
<%= table_pagination(filter.pagination, :colspan => colspan, :id => "measure_filter_foot#{widget_id}", :include_loading_icon => true) { |label, page_id|
- link_to_function label, "refreshList#{widget_id}('#{filter.criteria[:sort]}', #{filter.criteria[:asc]}, #{page_id})", :class => 'underlined-link'
+ link_to_function label, "refreshList#{widget_id}('#{filter.criteria[:sort]}', #{filter.criteria[:asc]}, #{page_id})"
} -%>
<% else %>
<%= table_pagination(filter.pagination, :colspan => colspan, :id => "measure_filter_foot#{widget_id}", :include_loading_icon => true) { |label, page_id|
- link_to(label, filter.criteria.merge({:page => page_id}), :class => 'underlined-link')
+ link_to(label, filter.criteria.merge({:page => page_id}))
} -%>
<% end %>
</table>
diff --git a/server/sonar-web/src/main/webapp/WEB-INF/app/views/measures/search.html.erb b/server/sonar-web/src/main/webapp/WEB-INF/app/views/measures/search.html.erb
index 13fbbdfdbf1..e2b250d32e8 100644
--- a/server/sonar-web/src/main/webapp/WEB-INF/app/views/measures/search.html.erb
+++ b/server/sonar-web/src/main/webapp/WEB-INF/app/views/measures/search.html.erb
@@ -127,6 +127,8 @@
<% end %>
<% end %>
- { key: 'nameSearch', value: '<%= h @filter.criteria['nameSearch'] -%>' }
+ <% puts "### " + @filter.criteria.inspect %>
+
+ { key: 'nameSearch', value: '<%= @filter.criteria['nameSearch'] -%>' }
];
</script>
diff --git a/server/sonar-web/src/main/webapp/WEB-INF/app/views/metrics/index.html.erb b/server/sonar-web/src/main/webapp/WEB-INF/app/views/metrics/index.html.erb
index 05f02a80d92..d308a1f9161 100644
--- a/server/sonar-web/src/main/webapp/WEB-INF/app/views/metrics/index.html.erb
+++ b/server/sonar-web/src/main/webapp/WEB-INF/app/views/metrics/index.html.erb
@@ -19,12 +19,12 @@
<table class="sortable data width100" id="metrics">
<thead>
<tr>
- <th class="left">Key</th>
- <th class="left sortfirstasc">Name</th>
- <th class="left">Description</th>
- <th class="left">Domain</th>
- <th class="left">Type</th>
- <th class="left nosort">Operations</th>
+ <th class="left"><a>Key</a></th>
+ <th class="left sortfirstasc"><a>Name</a></th>
+ <th class="left"><a>Description</a></th>
+ <th class="left"><a>Domain</a></th>
+ <th class="left"><a>Type</a></th>
+ <th class="left nosort"><a>Operations</a></th>
</tr>
</thead>
<tbody>
diff --git a/server/sonar-web/src/main/webapp/WEB-INF/app/views/permission_templates/index.html.erb b/server/sonar-web/src/main/webapp/WEB-INF/app/views/permission_templates/index.html.erb
index 1a4f834e45b..fa44944563a 100644
--- a/server/sonar-web/src/main/webapp/WEB-INF/app/views/permission_templates/index.html.erb
+++ b/server/sonar-web/src/main/webapp/WEB-INF/app/views/permission_templates/index.html.erb
@@ -8,15 +8,11 @@
<%= render :partial => 'roles/tabs', :locals => {:selected_tab=>'Permission templates'} %>
<br/>
<div class="line-block marginbottom10">
- <ul class="operations">
- <li>
- <%= link_to message('permission_template.set_default_templates'), {:action => :default_templates_form, :qualifiers => @root_qualifiers},
- :id => 'configure-defaults-permission-template', :class => 'open-modal link-action' %>
- </li>
- <li class="last">
- <%= link_to message('create'), {:action => :create_form}, :id => 'create-link-permission-template', :class => 'open-modal link-action' %>
- </li>
- </ul>
+ <div class="button-group operations">
+ <%= link_to message('permission_template.set_default_templates'), {:action => :default_templates_form, :qualifiers => @root_qualifiers},
+ :id => 'configure-defaults-permission-template', :class => 'open-modal link-action button' %>
+ <%= link_to message('create'), {:action => :create_form}, :id => 'create-link-permission-template', :class => 'open-modal link-action button' %>
+ </div>
</div>
<table class="data width100" id="permission-templates">
<thead>
diff --git a/server/sonar-web/src/main/webapp/WEB-INF/app/views/profiles/index.html.erb b/server/sonar-web/src/main/webapp/WEB-INF/app/views/profiles/index.html.erb
index e8a77e22cd9..536e6eb445e 100644
--- a/server/sonar-web/src/main/webapp/WEB-INF/app/views/profiles/index.html.erb
+++ b/server/sonar-web/src/main/webapp/WEB-INF/app/views/profiles/index.html.erb
@@ -1,17 +1,11 @@
<div class="page">
<div class="line-block marginbottom10">
- <ul style="float: right" class="operations">
- <li>
- <i class="icon-compare"></i>
- <a href="profiles/compare" id="compare-link"><%= message('quality_profiles.compare_profiles') -%></a>
- </li>
+ <div style="float: right" class="operations button-group">
+ <a href="profiles/compare" id="compare-link" class="button"><i class="icon-compare"></i> <%= message('quality_profiles.compare_profiles') -%></a>
<% if profiles_administrator? %>
- <li class="last">
- <i class="icon-restore"></i>
- <a href="profiles/restore_form" class="open-modal" id="restore-link"><%= message('quality_profiles.restore_profile') -%></a>
- </li>
+ <a href="profiles/restore_form" class="open-modal button" id="restore-link"><i class="icon-restore"></i> <%= message('quality_profiles.restore_profile') -%></a>
<% end %>
- </ul>
+ </div>
</div>
<%
diff --git a/server/sonar-web/src/main/webapp/WEB-INF/app/views/project/widgets/issues/_issues_list.html.erb b/server/sonar-web/src/main/webapp/WEB-INF/app/views/project/widgets/issues/_issues_list.html.erb
index 141d250d491..15719704de4 100644
--- a/server/sonar-web/src/main/webapp/WEB-INF/app/views/project/widgets/issues/_issues_list.html.erb
+++ b/server/sonar-web/src/main/webapp/WEB-INF/app/views/project/widgets/issues/_issues_list.html.erb
@@ -71,7 +71,7 @@
<%= paginate_java(paging, :colspan => 3, :id => "issue-filter-foot-#{widget_id}", :include_loading_icon => true,
:url_results => url_for_issues(search_options.except('pageSize', 'pageIndex', 'table_limit', 'widget_id')) ) { |label, page_id|
<<EOF
-<a class="underlined-link" href="#" onclick="$j.ajax({ url:'#{url_for(link_params.merge({:pageIndex => page_id}))}', type:'post', success:function(response){$j('#issues-widget-#{widget_id}').html(response);}}); return false;">#{label}</a>
+<a href="#" onclick="$j.ajax({ url:'#{url_for(link_params.merge({:pageIndex => page_id}))}', type:'post', success:function(response){$j('#issues-widget-#{widget_id}').html(response);}}); return false;">#{label}</a>
EOF
} -%>
</table>
diff --git a/server/sonar-web/src/main/webapp/WEB-INF/app/views/project_roles/index.html.erb b/server/sonar-web/src/main/webapp/WEB-INF/app/views/project_roles/index.html.erb
index 93a78829954..5aee7f30416 100644
--- a/server/sonar-web/src/main/webapp/WEB-INF/app/views/project_roles/index.html.erb
+++ b/server/sonar-web/src/main/webapp/WEB-INF/app/views/project_roles/index.html.erb
@@ -3,13 +3,11 @@
<% end %>
<div>
- <ul class="operations">
- <li class="last">
- <%= link_to message('projects_role.apply_template'), {:controller => :roles, :action => :apply_template_form, :components => [@project.key], :names => @project.name,
- :results_count => 1},
- :id => "apply-template-#{u @project.kee}", :class => 'open-modal link-action' %>
- </li>
- </ul>
+ <div class="operations button-group">
+ <%= link_to message('projects_role.apply_template'), {:controller => :roles, :action => :apply_template_form, :components => [@project.key], :names => @project.name,
+ :results_count => 1},
+ :id => "apply-template-#{u @project.kee}", :class => 'open-modal link-action button' %>
+ </div>
<h1 class="admin-page-title"><%= message('project_roles.page') -%></h1>
<p class="admin-page-description"><%= message('project_roles.page.description2') -%></p>
</div>
diff --git a/server/sonar-web/src/main/webapp/WEB-INF/app/views/roles/projects.html.erb b/server/sonar-web/src/main/webapp/WEB-INF/app/views/roles/projects.html.erb
index 200b74e46e4..64e2f4483fd 100644
--- a/server/sonar-web/src/main/webapp/WEB-INF/app/views/roles/projects.html.erb
+++ b/server/sonar-web/src/main/webapp/WEB-INF/app/views/roles/projects.html.erb
@@ -36,14 +36,12 @@
</div>
<div id="project-roles-operations" style="float: right;">
- <ul class="operations">
- <li class="last">
- <%= link_to message('projects_role.bulk_change'), {:action => :apply_template_form, :names => @components_names,
- :keys => @components_keys, :qualifiers => @components_qualifiers,
- :results_count => @query_result.paging.total},
- :id => 'apply-template-modal', :class => 'open-modal link-action' %>
- </li>
- </ul>
+ <div class="operations button-group">
+ <%= link_to message('projects_role.bulk_change'), {:action => :apply_template_form, :names => @components_names,
+ :keys => @components_keys, :qualifiers => @components_qualifiers,
+ :results_count => @query_result.paging.total},
+ :id => 'apply-template-modal', :class => 'open-modal link-action button' %>
+ </div>
</div>
<table class="data width100" id="projects">
diff --git a/server/sonar-web/src/main/webapp/WEB-INF/app/views/updatecenter/system_updates.html.erb b/server/sonar-web/src/main/webapp/WEB-INF/app/views/updatecenter/system_updates.html.erb
index 9399514f7bc..9b7c63199ce 100644
--- a/server/sonar-web/src/main/webapp/WEB-INF/app/views/updatecenter/system_updates.html.erb
+++ b/server/sonar-web/src/main/webapp/WEB-INF/app/views/updatecenter/system_updates.html.erb
@@ -69,34 +69,25 @@
Follow those steps to upgrade SonarQube from version <%= sonar_version -%> to
version <%= release.getVersion() -%> :
<ol class="bulletpoints">
+ <li>Stop SonarQube</li>
+ <li><%= link_to 'Download', release.getDownloadUrl(), :class => 'external' -%> and install
+ SonarQube <%= release.getVersion() -%> after having carefully read the <a href="http://docs.codehaus.org/display/SONAR/Upgrading" class="external">upgrade guide</a>.
+ </li>
<% update.getIncompatiblePlugins().each do |incompatible_plugin| %>
- <li>
- <form method="post"
- action="<%= ApplicationController.root_context -%>/updatecenter/uninstall?key=<%= incompatible_plugin.getKey() -%>&amp;from=system_updates"
- style="display: inline-block">
- <%= image_tag 'warning.png' -%>
- <input type="submit" value="Uninstall" class="red-button" onClick="return submitForm(this);"/>
- the plugin <%= incompatible_plugin.getName() -%> which is not compatible with
- SonarQube <%= release.getVersion() -%>.
- </form>
- </li>
+ <li>
+ Uninstall the plugin <%= incompatible_plugin.getName() -%> which is not compatible with
+ SonarQube <%= release.getVersion() -%>.
+ </form>
+ </li>
<% end %>
<% update.getPluginsToUpgrade().each do |plugin_to_upgrade| %>
- <li>
- <form method="post" id="upgrade-form-<%= plugin_to_upgrade.getArtifact().getKey() -%>"
- action="<%= ApplicationController.root_context -%>/updatecenter/install?key=<%= plugin_to_upgrade.getArtifact().getKey() -%>&amp;version=<%= plugin_to_upgrade.getVersion() -%>&amp;from=system_updates"
- style="display: inline-block">
- <input type="submit" id="upgrade-submit-<%= plugin_to_upgrade.getArtifact().getKey() -%>" value="Upgrade" onClick="return submitForm(this);"/>
- the plugin <%= plugin_to_upgrade.getArtifact().getName() -%> to
- version <%= plugin_to_upgrade.getVersion() -%>
- </form>
- </li>
- <% end %>
- <li>Stop Sonar</li>
- <li><%= link_to 'Download', release.getDownloadUrl(), :class => 'external' -%> and install
- SonarQube <%= release.getVersion() -%> after having carefully read the <a href="http://docs.codehaus.org/display/SONAR/Upgrading" class="external">upgrade guide</a>.
+ <li>
+ Replace current version of plugin <%= plugin_to_upgrade.getArtifact().getName() -%> by
+ version <%= plugin_to_upgrade.getVersion() -%>
+ </form>
</li>
- <li>Start Sonar</li>
+ <% end %>
+ <li>Start SonarQube</li>
</ol>
<% else %>
diff --git a/server/sonar-web/src/main/webapp/WEB-INF/app/views/users/index.html.erb b/server/sonar-web/src/main/webapp/WEB-INF/app/views/users/index.html.erb
index 2fd4e5045eb..131c2491982 100644
--- a/server/sonar-web/src/main/webapp/WEB-INF/app/views/users/index.html.erb
+++ b/server/sonar-web/src/main/webapp/WEB-INF/app/views/users/index.html.erb
@@ -20,11 +20,11 @@
<table class="data width100 sortable" id="users">
<thead>
<tr>
- <th class="left">Login</th>
- <th class="left sortfirstasc">Name</th>
- <th class="left">Email</th>
- <th class="left nosort">Groups</th>
- <th class="right nosort" nowrap>Operations</th>
+ <th class="left"><a>Login</a></th>
+ <th class="left sortfirstasc"><a>Name</a></th>
+ <th class="left"><a>Email</a></th>
+ <th class="left nosort"><a>Groups</a></th>
+ <th class="right nosort" nowrap><a>Operations</a></th>
</tr>
</thead>
<tbody >
diff --git a/server/sonar-web/src/main/webapp/fonts/sonar.eot b/server/sonar-web/src/main/webapp/fonts/sonar.eot
index ba3b9187a7f..6ca49648665 100755
--- a/server/sonar-web/src/main/webapp/fonts/sonar.eot
+++ b/server/sonar-web/src/main/webapp/fonts/sonar.eot
Binary files differ
diff --git a/server/sonar-web/src/main/webapp/fonts/sonar.svg b/server/sonar-web/src/main/webapp/fonts/sonar.svg
index 562f4b05bd8..9383951117a 100755
--- a/server/sonar-web/src/main/webapp/fonts/sonar.svg
+++ b/server/sonar-web/src/main/webapp/fonts/sonar.svg
@@ -67,7 +67,6 @@
<glyph unicode="&#xf0d8;" d="M585.143 265.143q0-14.857-10.857-25.714t-25.714-10.857h-512q-14.857 0-25.714 10.857t-10.857 25.714 10.857 25.714l256 256q10.857 10.857 25.714 10.857t25.714-10.857l256-256q10.857-10.857 10.857-25.714z" horiz-adv-x="585" />
<glyph unicode="&#xf0d9;" d="M365.714 704v-512q0-14.857-10.857-25.714t-25.714-10.857-25.714 10.857l-256 256q-10.857 10.857-10.857 25.714t10.857 25.714l256 256q10.857 10.857 25.714 10.857t25.714-10.857 10.857-25.714z" horiz-adv-x="366" />
<glyph unicode="&#xf0da;" d="M329.143 448q0-14.857-10.857-25.714l-256-256q-10.857-10.857-25.714-10.857t-25.714 10.857-10.857 25.714v512q0 14.857 10.857 25.714t25.714 10.857 25.714-10.857l256-256q10.857-10.857 10.857-25.714z" horiz-adv-x="366" />
-<glyph unicode="&#xf0ec;" d="M1024 283.429v-109.714q0-7.429-5.429-12.857t-12.857-5.429h-786.286v-109.714q0-7.429-5.429-12.857t-12.857-5.429q-6.857 0-13.714 5.714l-182.286 182.857q-5.143 5.143-5.143 12.571 0 8 5.143 13.143l182.857 182.857q5.143 5.143 13.143 5.143 7.429 0 12.857-5.429t5.429-12.857v-109.714h786.286q7.429 0 12.857-5.429t5.429-12.857zM1024 594.286q0-8-5.143-13.143l-182.857-182.857q-5.143-5.143-13.143-5.143-7.429 0-12.857 5.429t-5.429 12.857v109.714h-786.286q-7.429 0-12.857 5.429t-5.429 12.857v109.714q0 7.429 5.429 12.857t12.857 5.429h786.286v109.714q0 8 5.143 13.143t13.143 5.143q6.857 0 13.714-5.714l182.286-182.286q5.143-5.143 5.143-13.143z" />
<glyph unicode="&#xf0f6;" d="M731.643 283.429v-36.57c0-5.334-1.714-9.715-5.143-13.144s-7.81-5.143-13.143-5.143h-402.286c-5.333 0-9.714 1.714-13.143 5.143s-5.143 7.81-5.143 13.144v36.57c0 5.334 1.714 9.715 5.143 13.144s7.81 5.143 13.143 5.143h402.287c5.333 0 9.714-1.714 13.143-5.143s5.142-7.81 5.142-13.144zM731.643 429.714v-36.571c0-5.333-1.714-9.714-5.143-13.143s-7.81-5.143-13.143-5.143h-402.286c-5.333 0-9.714 1.714-13.143 5.143s-5.143 7.81-5.143 13.143v36.571c0 5.333 1.714 9.715 5.143 13.144s7.81 5.143 13.143 5.143h402.287c5.333 0 9.714-1.715 13.143-5.143s5.142-7.811 5.142-13.144zM219.643 82.286h585.143v438.857h-237.714c-15.238 0-28.19 5.334-38.857 16s-16 23.619-16 38.857v237.714h-292.571l-0.001-731.428zM585.357 594.286h214.856c-3.81 11.047-7.999 18.857-12.571 23.429l-178.856 178.857c-4.571 4.571-12.381 8.761-23.429 12.571v-214.857zM877.929 576v-512c0-15.238-5.333-28.19-16-38.857s-23.618-16-38.856-16h-621.715c-15.238 0-28.19 5.334-38.857 16s-16 23.619-16 38.857v768c0 15.238 5.333 28.19 16 38.857s23.619 16 38.857 16h365.714c15.237 0 32-3.81 50.286-11.429s32.762-16.762 43.429-27.429l178.286-178.286c10.667-10.667 19.81-25.143 27.429-43.429s11.429-35.048 11.429-50.286l-0.002 0.002z" horiz-adv-x="1019" />
<glyph unicode="&#xf100;" d="M358.286 173.714q0-7.429-5.714-13.143l-28.571-28.571q-5.714-5.714-13.143-5.714t-13.143 5.714l-266.286 266.286q-5.714 5.714-5.714 13.143t5.714 13.143l266.286 266.286q5.714 5.714 13.143 5.714t13.143-5.714l28.571-28.571q5.714-5.714 5.714-13.143t-5.714-13.143l-224.571-224.571 224.571-224.571q5.714-5.714 5.714-13.143zM577.714 173.714q0-7.429-5.714-13.143l-28.571-28.571q-5.714-5.714-13.143-5.714t-13.143 5.714l-266.286 266.286q-5.714 5.714-5.714 13.143t5.714 13.143l266.286 266.286q5.714 5.714 13.143 5.714t13.143-5.714l28.571-28.571q5.714-5.714 5.714-13.143t-5.714-13.143l-224.571-224.571 224.571-224.571q5.714-5.714 5.714-13.143z" horiz-adv-x="585" />
<glyph unicode="&#xf101;" d="M340 411.429q0-7.429-5.714-13.143l-266.286-266.286q-5.714-5.714-13.143-5.714t-13.143 5.714l-28.571 28.571q-5.714 5.714-5.714 13.143t5.714 13.143l224.571 224.571-224.571 224.571q-5.714 5.714-5.714 13.143t5.714 13.143l28.571 28.571q5.714 5.714 13.143 5.714t13.143-5.714l266.286-266.286q5.714-5.714 5.714-13.143zM559.429 411.429q0-7.429-5.714-13.143l-266.286-266.286q-5.714-5.714-13.143-5.714t-13.143 5.714l-28.571 28.571q-5.714 5.714-5.714 13.143t5.714 13.143l224.571 224.571-224.571 224.571q-5.714 5.714-5.714 13.143t5.714 13.143l28.571 28.571q5.714 5.714 13.143 5.714t13.143-5.714l266.286-266.286q5.714-5.714 5.714-13.143z" horiz-adv-x="585" />
diff --git a/server/sonar-web/src/main/webapp/fonts/sonar.ttf b/server/sonar-web/src/main/webapp/fonts/sonar.ttf
index cf9bd8fb9a2..6d672598d25 100755
--- a/server/sonar-web/src/main/webapp/fonts/sonar.ttf
+++ b/server/sonar-web/src/main/webapp/fonts/sonar.ttf
Binary files differ
diff --git a/server/sonar-web/src/main/webapp/fonts/sonar.woff b/server/sonar-web/src/main/webapp/fonts/sonar.woff
index b32c6ddbdba..dd58a26a975 100755
--- a/server/sonar-web/src/main/webapp/fonts/sonar.woff
+++ b/server/sonar-web/src/main/webapp/fonts/sonar.woff
Binary files differ
diff --git a/sonar-batch-protocol/src/main/java/org/sonar/batch/protocol/input/ActiveRule.java b/sonar-batch-protocol/src/main/java/org/sonar/batch/protocol/input/ActiveRule.java
index 6c1992ac1e2..4fda09f8910 100644
--- a/sonar-batch-protocol/src/main/java/org/sonar/batch/protocol/input/ActiveRule.java
+++ b/sonar-batch-protocol/src/main/java/org/sonar/batch/protocol/input/ActiveRule.java
@@ -20,6 +20,7 @@
package org.sonar.batch.protocol.input;
import javax.annotation.CheckForNull;
+import javax.annotation.Nullable;
import java.util.HashMap;
import java.util.Map;
@@ -29,7 +30,7 @@ public class ActiveRule {
private final String name, severity, internalKey, language;
private final Map<String, String> params = new HashMap<String, String>();
- public ActiveRule(String repositoryKey, String ruleKey, String name, String severity, String internalKey, String language) {
+ public ActiveRule(String repositoryKey, String ruleKey, String name, String severity, @Nullable String internalKey, String language) {
this.repositoryKey = repositoryKey;
this.ruleKey = ruleKey;
this.name = name;
@@ -72,6 +73,7 @@ public class ActiveRule {
return params;
}
+ @CheckForNull
public String internalKey() {
return internalKey;
}
diff --git a/sonar-batch-protocol/src/main/java/org/sonar/batch/protocol/input/Metric.java b/sonar-batch-protocol/src/main/java/org/sonar/batch/protocol/input/Metric.java
index f09f3b34e81..775acb52523 100644
--- a/sonar-batch-protocol/src/main/java/org/sonar/batch/protocol/input/Metric.java
+++ b/sonar-batch-protocol/src/main/java/org/sonar/batch/protocol/input/Metric.java
@@ -49,7 +49,7 @@ public class Metric {
public Metric(int id,
String key,
String valueType,
- String description,
+ @Nullable String description,
int direction,
String name,
boolean qualitative,
@@ -82,6 +82,7 @@ public class Metric {
return valueType;
}
+ @CheckForNull
public String description() {
return description;
}
diff --git a/sonar-batch-protocol/src/main/java/org/sonar/batch/protocol/input/ProjectReferentials.java b/sonar-batch-protocol/src/main/java/org/sonar/batch/protocol/input/ProjectReferentials.java
index a386a0eeac7..570fb3630e5 100644
--- a/sonar-batch-protocol/src/main/java/org/sonar/batch/protocol/input/ProjectReferentials.java
+++ b/sonar-batch-protocol/src/main/java/org/sonar/batch/protocol/input/ProjectReferentials.java
@@ -43,7 +43,12 @@ public class ProjectReferentials {
}
public ProjectReferentials addSettings(String projectKey, Map<String, String> settings) {
- settingsByModule.put(projectKey, settings);
+ Map<String, String> existingSettings = settingsByModule.get(projectKey);
+ if (existingSettings == null) {
+ existingSettings = new HashMap<String, String>();
+ }
+ existingSettings.putAll(settings);
+ settingsByModule.put(projectKey, existingSettings);
return this;
}
diff --git a/sonar-batch/pom.xml b/sonar-batch/pom.xml
index 1ba3a32d61f..89f7c9252a4 100644
--- a/sonar-batch/pom.xml
+++ b/sonar-batch/pom.xml
@@ -149,5 +149,10 @@
<artifactId>jsonassert</artifactId>
<scope>test</scope>
</dependency>
+ <dependency>
+ <groupId>org.codehaus.sonar.plugins</groupId>
+ <artifactId>sonar-xoo-plugin</artifactId>
+ <scope>test</scope>
+ </dependency>
</dependencies>
</project>
diff --git a/sonar-batch/src/main/java/org/sonar/batch/bootstrap/BatchExtensionDictionnary.java b/sonar-batch/src/main/java/org/sonar/batch/bootstrap/BatchExtensionDictionnary.java
index 7317c9afd08..32bebf6182e 100644
--- a/sonar-batch/src/main/java/org/sonar/batch/bootstrap/BatchExtensionDictionnary.java
+++ b/sonar-batch/src/main/java/org/sonar/batch/bootstrap/BatchExtensionDictionnary.java
@@ -22,6 +22,7 @@ package org.sonar.batch.bootstrap;
import com.google.common.collect.Lists;
import org.apache.commons.lang.ClassUtils;
import org.sonar.api.batch.CheckProject;
+import org.sonar.api.batch.Phase;
import org.sonar.api.batch.sensor.Sensor;
import org.sonar.api.batch.sensor.SensorContext;
import org.sonar.api.platform.ComponentContainer;
@@ -56,6 +57,15 @@ public class BatchExtensionDictionnary extends org.sonar.api.batch.BatchExtensio
return result;
}
+ @Override
+ protected Phase.Name evaluatePhase(Object extension) {
+ if (extension instanceof SensorWrapper) {
+ return super.evaluatePhase(((SensorWrapper) extension).wrappedSensor());
+ } else {
+ return super.evaluatePhase(extension);
+ }
+ }
+
private <T> List<T> getFilteredExtensions(Class<T> type, @Nullable Project project, @Nullable ExtensionMatcher matcher) {
List<T> result = Lists.newArrayList();
for (Object extension : getExtensions(type)) {
diff --git a/sonar-batch/src/main/java/org/sonar/batch/duplication/BlockCache.java b/sonar-batch/src/main/java/org/sonar/batch/duplication/BlockCache.java
new file mode 100644
index 00000000000..f6a4e3d18bf
--- /dev/null
+++ b/sonar-batch/src/main/java/org/sonar/batch/duplication/BlockCache.java
@@ -0,0 +1,56 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.batch.duplication;
+
+import org.sonar.api.BatchComponent;
+import org.sonar.batch.index.Cache;
+import org.sonar.batch.index.Cache.Entry;
+import org.sonar.batch.index.Caches;
+import org.sonar.duplications.block.FileBlocks;
+
+import javax.annotation.CheckForNull;
+
+/**
+ * Cache of duplication blocks. This cache is shared amongst all project modules.
+ */
+public class BlockCache implements BatchComponent {
+
+ private final Cache<FileBlocks> cache;
+
+ public BlockCache(Caches caches) {
+ caches.registerValueCoder(FileBlocks.class, new FileBlocksValueCoder());
+ cache = caches.createCache("blocks");
+ }
+
+ public Iterable<Entry<FileBlocks>> entries() {
+ return cache.entries();
+ }
+
+ @CheckForNull
+ public FileBlocks byComponent(String effectiveKey) {
+ return cache.get(effectiveKey);
+ }
+
+ public BlockCache put(String effectiveKey, FileBlocks blocks) {
+ cache.put(effectiveKey, blocks);
+ return this;
+ }
+
+}
diff --git a/sonar-batch/src/main/java/org/sonar/batch/duplication/DefaultDuplicationBuilder.java b/sonar-batch/src/main/java/org/sonar/batch/duplication/DefaultDuplicationBuilder.java
new file mode 100644
index 00000000000..d6dd18026a2
--- /dev/null
+++ b/sonar-batch/src/main/java/org/sonar/batch/duplication/DefaultDuplicationBuilder.java
@@ -0,0 +1,74 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.batch.duplication;
+
+import com.google.common.base.Preconditions;
+import org.sonar.api.batch.fs.InputFile;
+import org.sonar.api.batch.fs.internal.DefaultInputFile;
+import org.sonar.api.batch.sensor.duplication.DuplicationBuilder;
+
+import java.util.ArrayList;
+
+public class DefaultDuplicationBuilder implements DuplicationBuilder {
+
+ private final InputFile inputFile;
+ private final DuplicationCache duplicationCache;
+ private boolean done = false;
+ private DuplicationGroup current = null;
+ private ArrayList<DuplicationGroup> duplications;
+
+ public DefaultDuplicationBuilder(InputFile inputFile, DuplicationCache duplicationCache) {
+ this.inputFile = inputFile;
+ this.duplicationCache = duplicationCache;
+ duplications = new ArrayList<DuplicationGroup>();
+ }
+
+ @Override
+ public DuplicationBuilder originBlock(int startLine, int endLine) {
+ if (current != null) {
+ duplications.add(current);
+ }
+ current = new DuplicationGroup(new DuplicationGroup.Block(((DefaultInputFile) inputFile).key(), startLine, endLine - startLine + 1));
+ return this;
+ }
+
+ @Override
+ public DuplicationBuilder isDuplicatedBy(InputFile sameOrOtherFile, int startLine, int endLine) {
+ return isDuplicatedBy(((DefaultInputFile) sameOrOtherFile).key(), startLine, endLine);
+ }
+
+ /**
+ * For internal use. Global duplications are referencing files outside of current project so
+ * no way to manipulate an InputFile.
+ */
+ public DuplicationBuilder isDuplicatedBy(String fileKey, int startLine, int endLine) {
+ Preconditions.checkNotNull(current, "Call originBlock() first");
+ current.addDuplicate(new DuplicationGroup.Block(fileKey, startLine, endLine - startLine + 1));
+ return this;
+ }
+
+ @Override
+ public void done() {
+ Preconditions.checkState(!done, "done() already called");
+ Preconditions.checkNotNull(current, "Call originBlock() first");
+ duplications.add(current);
+ duplicationCache.put(((DefaultInputFile) inputFile).key(), duplications);
+ }
+}
diff --git a/sonar-batch/src/main/java/org/sonar/batch/duplication/DefaultTokenBuilder.java b/sonar-batch/src/main/java/org/sonar/batch/duplication/DefaultTokenBuilder.java
new file mode 100644
index 00000000000..6c7836d9e39
--- /dev/null
+++ b/sonar-batch/src/main/java/org/sonar/batch/duplication/DefaultTokenBuilder.java
@@ -0,0 +1,77 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.batch.duplication;
+
+import com.google.common.base.Preconditions;
+import net.sourceforge.pmd.cpd.TokenEntry;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.sonar.api.batch.fs.InputFile;
+import org.sonar.api.batch.fs.internal.DefaultInputFile;
+import org.sonar.api.batch.sensor.duplication.TokenBuilder;
+import org.sonar.duplications.block.Block;
+import org.sonar.duplications.block.FileBlocks;
+import org.sonar.duplications.internal.pmd.PmdBlockChunker;
+import org.sonar.duplications.internal.pmd.TokenizerBridge;
+import org.sonar.duplications.internal.pmd.TokensLine;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class DefaultTokenBuilder implements TokenBuilder {
+
+ private static final Logger LOG = LoggerFactory.getLogger(DefaultTokenBuilder.class);
+
+ private final BlockCache cache;
+ private final InputFile inputFile;
+ private final List<TokenEntry> tokens = new ArrayList<TokenEntry>();
+ private final PmdBlockChunker blockChunker;
+ private boolean done = false;
+ private int previousLine = 0;
+
+ public DefaultTokenBuilder(InputFile inputFile, BlockCache cache, PmdBlockChunker blockChunker) {
+ this.inputFile = inputFile;
+ this.cache = cache;
+ this.blockChunker = blockChunker;
+ TokenEntry.clearImages();
+ }
+
+ @Override
+ public DefaultTokenBuilder addToken(int line, String image) {
+ Preconditions.checkState(!done, "done() already called");
+ Preconditions.checkState(line >= previousLine, "Token should be created in order. Previous line was " + previousLine + " and you tried to create a token at line " + line);
+ TokenEntry cpdToken = new TokenEntry(image, inputFile.absolutePath(), line);
+ tokens.add(cpdToken);
+ previousLine = line;
+ return this;
+ }
+
+ @Override
+ public void done() {
+ Preconditions.checkState(!done, "done() already called");
+ tokens.add(TokenEntry.getEOF());
+ TokenEntry.clearImages();
+ List<TokensLine> tokensLines = TokenizerBridge.convert(tokens);
+ ArrayList<Block> blocks = blockChunker.chunk(((DefaultInputFile) inputFile).key(), tokensLines);
+
+ cache.put(((DefaultInputFile) inputFile).key(), new FileBlocks(((DefaultInputFile) inputFile).key(), blocks));
+ tokens.clear();
+ }
+}
diff --git a/sonar-batch/src/main/java/org/sonar/batch/duplication/DuplicationBlockValueCoder.java b/sonar-batch/src/main/java/org/sonar/batch/duplication/DuplicationBlockValueCoder.java
new file mode 100644
index 00000000000..ab1dad47009
--- /dev/null
+++ b/sonar-batch/src/main/java/org/sonar/batch/duplication/DuplicationBlockValueCoder.java
@@ -0,0 +1,43 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.batch.duplication;
+
+import com.persistit.Value;
+import com.persistit.encoding.CoderContext;
+import com.persistit.encoding.ValueCoder;
+
+class DuplicationBlockValueCoder implements ValueCoder {
+
+ @Override
+ public void put(Value value, Object object, CoderContext context) {
+ DuplicationGroup.Block b = (DuplicationGroup.Block) object;
+ value.putUTF(b.resourceKey());
+ value.put(b.startLine());
+ value.put(b.length());
+ }
+
+ @Override
+ public Object get(Value value, Class clazz, CoderContext context) {
+ String resourceKey = value.getString();
+ int startLine = value.getInt();
+ int length = value.getInt();
+ return new DuplicationGroup.Block(resourceKey, startLine, length);
+ }
+}
diff --git a/sonar-batch/src/main/java/org/sonar/batch/duplication/DuplicationCache.java b/sonar-batch/src/main/java/org/sonar/batch/duplication/DuplicationCache.java
new file mode 100644
index 00000000000..e0265215efc
--- /dev/null
+++ b/sonar-batch/src/main/java/org/sonar/batch/duplication/DuplicationCache.java
@@ -0,0 +1,58 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.batch.duplication;
+
+import org.sonar.api.BatchComponent;
+import org.sonar.batch.index.Cache;
+import org.sonar.batch.index.Cache.Entry;
+import org.sonar.batch.index.Caches;
+
+import javax.annotation.CheckForNull;
+
+import java.util.ArrayList;
+
+/**
+ * Cache of duplication blocks. This cache is shared amongst all project modules.
+ */
+public class DuplicationCache implements BatchComponent {
+
+ private final Cache<ArrayList<DuplicationGroup>> cache;
+
+ public DuplicationCache(Caches caches) {
+ caches.registerValueCoder(DuplicationGroup.class, new DuplicationGroupValueCoder());
+ caches.registerValueCoder(DuplicationGroup.Block.class, new DuplicationBlockValueCoder());
+ cache = caches.createCache("duplications");
+ }
+
+ public Iterable<Entry<ArrayList<DuplicationGroup>>> entries() {
+ return cache.entries();
+ }
+
+ @CheckForNull
+ public ArrayList<DuplicationGroup> byComponent(String effectiveKey) {
+ return cache.get(effectiveKey);
+ }
+
+ public DuplicationCache put(String effectiveKey, ArrayList<DuplicationGroup> blocks) {
+ cache.put(effectiveKey, blocks);
+ return this;
+ }
+
+}
diff --git a/sonar-batch/src/main/java/org/sonar/batch/duplication/DuplicationGroup.java b/sonar-batch/src/main/java/org/sonar/batch/duplication/DuplicationGroup.java
new file mode 100644
index 00000000000..dc9a7aa2602
--- /dev/null
+++ b/sonar-batch/src/main/java/org/sonar/batch/duplication/DuplicationGroup.java
@@ -0,0 +1,76 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.batch.duplication;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class DuplicationGroup {
+
+ public static class Block {
+ private final String resourceKey;
+ private final int startLine;
+ private final int length;
+
+ public Block(String resourceKey, int startLine, int length) {
+ this.resourceKey = resourceKey;
+ this.startLine = startLine;
+ this.length = length;
+ }
+
+ public String resourceKey() {
+ return resourceKey;
+ }
+
+ public int startLine() {
+ return startLine;
+ }
+
+ public int length() {
+ return length;
+ }
+ }
+
+ private final Block originBlock;
+
+ private List<Block> duplicates = new ArrayList<DuplicationGroup.Block>();
+
+ public DuplicationGroup(Block originBlock) {
+ this.originBlock = originBlock;
+ }
+
+ public void setDuplicates(List<Block> duplicates) {
+ this.duplicates = duplicates;
+ }
+
+ public DuplicationGroup addDuplicate(Block anotherBlock) {
+ this.duplicates.add(anotherBlock);
+ return this;
+ }
+
+ public Block originBlock() {
+ return originBlock;
+ }
+
+ public List<Block> duplicates() {
+ return duplicates;
+ }
+
+}
diff --git a/sonar-batch/src/main/java/org/sonar/batch/duplication/DuplicationGroupValueCoder.java b/sonar-batch/src/main/java/org/sonar/batch/duplication/DuplicationGroupValueCoder.java
new file mode 100644
index 00000000000..244cffccd5f
--- /dev/null
+++ b/sonar-batch/src/main/java/org/sonar/batch/duplication/DuplicationGroupValueCoder.java
@@ -0,0 +1,54 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.batch.duplication;
+
+import com.persistit.Value;
+import com.persistit.encoding.CoderContext;
+import com.persistit.encoding.ValueCoder;
+import org.sonar.batch.duplication.DuplicationGroup.Block;
+
+import java.util.ArrayList;
+
+class DuplicationGroupValueCoder implements ValueCoder {
+
+ private DuplicationBlockValueCoder blockCoder = new DuplicationBlockValueCoder();
+
+ @Override
+ public void put(Value value, Object object, CoderContext context) {
+ DuplicationGroup c = (DuplicationGroup) object;
+ value.put(c.originBlock());
+ value.put(c.duplicates().size());
+ for (DuplicationGroup.Block block : c.duplicates()) {
+ blockCoder.put(value, block, context);
+ }
+ }
+
+ @Override
+ public Object get(Value value, Class clazz, CoderContext context) {
+ DuplicationGroup g = new DuplicationGroup((DuplicationGroup.Block) value.get());
+ int count = value.getInt();
+ ArrayList<DuplicationGroup.Block> blocks = new ArrayList<DuplicationGroup.Block>(count);
+ for (int i = 0; i < count; i++) {
+ blocks.add((Block) blockCoder.get(value, DuplicationGroup.Block.class, context));
+ }
+ g.setDuplicates(blocks);
+ return g;
+ }
+}
diff --git a/sonar-batch/src/main/java/org/sonar/batch/duplication/FileBlocksValueCoder.java b/sonar-batch/src/main/java/org/sonar/batch/duplication/FileBlocksValueCoder.java
new file mode 100644
index 00000000000..99b3467c921
--- /dev/null
+++ b/sonar-batch/src/main/java/org/sonar/batch/duplication/FileBlocksValueCoder.java
@@ -0,0 +1,69 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.batch.duplication;
+
+import com.persistit.Value;
+import com.persistit.encoding.CoderContext;
+import com.persistit.encoding.ValueCoder;
+import org.sonar.duplications.block.Block;
+import org.sonar.duplications.block.ByteArray;
+import org.sonar.duplications.block.FileBlocks;
+
+import java.util.ArrayList;
+import java.util.List;
+
+class FileBlocksValueCoder implements ValueCoder {
+
+ @Override
+ public void put(Value value, Object object, CoderContext context) {
+ FileBlocks blocks = (FileBlocks) object;
+ value.putUTF(blocks.resourceId());
+ value.put(blocks.blocks().size());
+ for (Block b : blocks.blocks()) {
+ value.putByteArray(b.getBlockHash().getBytes());
+ value.put(b.getIndexInFile());
+ value.put(b.getStartLine());
+ value.put(b.getEndLine());
+ value.put(b.getStartUnit());
+ value.put(b.getEndUnit());
+ }
+ }
+
+ @Override
+ public Object get(Value value, Class clazz, CoderContext context) {
+ String resourceId = value.getString();
+ int count = value.getInt();
+ List<Block> blocks = new ArrayList<Block>(count);
+ for (int i = 0; i < count; i++) {
+ Block.Builder b = Block.builder();
+ b.setResourceId(resourceId);
+ b.setBlockHash(new ByteArray(value.getByteArray()));
+ b.setIndexInFile(value.getInt());
+ int startLine = value.getInt();
+ int endLine = value.getInt();
+ b.setLines(startLine, endLine);
+ int startUnit = value.getInt();
+ int endUnit = value.getInt();
+ b.setUnit(startUnit, endUnit);
+ blocks.add(b.build());
+ }
+ return new FileBlocks(resourceId, blocks);
+ }
+}
diff --git a/sonar-batch/src/test/java/org/sonar/batch/mediumtest/xoo/plugin/base/Xoo.java b/sonar-batch/src/main/java/org/sonar/batch/duplication/package-info.java
index 1922fd7aa35..103e90ab281 100644
--- a/sonar-batch/src/test/java/org/sonar/batch/mediumtest/xoo/plugin/base/Xoo.java
+++ b/sonar-batch/src/main/java/org/sonar/batch/duplication/package-info.java
@@ -17,24 +17,7 @@
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
-package org.sonar.batch.mediumtest.xoo.plugin.base;
+@ParametersAreNonnullByDefault
+package org.sonar.batch.duplication;
-import org.sonar.api.resources.AbstractLanguage;
-
-public class Xoo extends AbstractLanguage {
-
- public static final String KEY = "xoo";
- public static final String NAME = "Xoo";
-
- public Xoo() {
- super(KEY, NAME);
- }
-
- /**
- * ${@inheritDoc}
- */
- public String[] getFileSuffixes() {
- return XooConstants.FILE_SUFFIXES;
- }
-
-}
+import javax.annotation.ParametersAreNonnullByDefault;
diff --git a/sonar-batch/src/main/java/org/sonar/batch/highlighting/SyntaxHighlightingDataBuilder.java b/sonar-batch/src/main/java/org/sonar/batch/highlighting/SyntaxHighlightingDataBuilder.java
index 38ae875af98..3a74dbf4884 100644
--- a/sonar-batch/src/main/java/org/sonar/batch/highlighting/SyntaxHighlightingDataBuilder.java
+++ b/sonar-batch/src/main/java/org/sonar/batch/highlighting/SyntaxHighlightingDataBuilder.java
@@ -32,9 +32,6 @@ import java.util.List;
import static com.google.common.collect.Lists.newArrayList;
-/**
- * @since 3.6
- */
public class SyntaxHighlightingDataBuilder {
private List<SyntaxHighlightingRule> syntaxHighlightingRuleSet;
diff --git a/sonar-batch/src/main/java/org/sonar/batch/highlighting/SyntaxHighlightingRule.java b/sonar-batch/src/main/java/org/sonar/batch/highlighting/SyntaxHighlightingRule.java
index 08985752d60..e6faa7651b4 100644
--- a/sonar-batch/src/main/java/org/sonar/batch/highlighting/SyntaxHighlightingRule.java
+++ b/sonar-batch/src/main/java/org/sonar/batch/highlighting/SyntaxHighlightingRule.java
@@ -21,9 +21,6 @@ package org.sonar.batch.highlighting;
import java.io.Serializable;
-/**
- * @since 3.6
- */
public class SyntaxHighlightingRule implements Serializable {
private final int startPosition;
diff --git a/sonar-batch/src/main/java/org/sonar/batch/index/Cache.java b/sonar-batch/src/main/java/org/sonar/batch/index/Cache.java
index ff57e4c065d..c98b88773e6 100644
--- a/sonar-batch/src/main/java/org/sonar/batch/index/Cache.java
+++ b/sonar-batch/src/main/java/org/sonar/batch/index/Cache.java
@@ -28,7 +28,6 @@ import org.apache.commons.lang.builder.ToStringBuilder;
import javax.annotation.CheckForNull;
-import java.io.Serializable;
import java.util.Iterator;
import java.util.NoSuchElementException;
import java.util.Set;
@@ -38,7 +37,7 @@ import java.util.Set;
* This cache is not thread-safe, due to direct usage of {@link com.persistit.Exchange}
* </p>
*/
-public class Cache<V extends Serializable> {
+public class Cache<V> {
private final String name;
private final Exchange exchange;
@@ -383,7 +382,7 @@ public class Cache<V extends Serializable> {
// LAZY ITERATORS AND ITERABLES
//
- private static class ValueIterable<T extends Serializable> implements Iterable<T> {
+ private static class ValueIterable<T> implements Iterable<T> {
private final Iterator<T> iterator;
private ValueIterable(Exchange exchange, KeyFilter keyFilter) {
@@ -396,7 +395,7 @@ public class Cache<V extends Serializable> {
}
}
- private static class ValueIterator<T extends Serializable> implements Iterator<T> {
+ private static class ValueIterator<T> implements Iterator<T> {
private final Exchange exchange;
private final KeyFilter keyFilter;
@@ -434,7 +433,7 @@ public class Cache<V extends Serializable> {
}
}
- private static class EntryIterable<T extends Serializable> implements Iterable<Entry<T>> {
+ private static class EntryIterable<T> implements Iterable<Entry<T>> {
private final EntryIterator<T> it;
private EntryIterable(Exchange exchange, KeyFilter keyFilter) {
@@ -447,7 +446,7 @@ public class Cache<V extends Serializable> {
}
}
- private static class EntryIterator<T extends Serializable> implements Iterator<Entry<T>> {
+ private static class EntryIterator<T> implements Iterator<Entry<T>> {
private final Exchange exchange;
private final KeyFilter keyFilter;
@@ -491,7 +490,7 @@ public class Cache<V extends Serializable> {
}
}
- public static class Entry<V extends Serializable> {
+ public static class Entry<V> {
private final Object[] key;
private final V value;
diff --git a/sonar-batch/src/main/java/org/sonar/batch/index/Caches.java b/sonar-batch/src/main/java/org/sonar/batch/index/Caches.java
index 1e8c53d8853..ca2a33a5014 100644
--- a/sonar-batch/src/main/java/org/sonar/batch/index/Caches.java
+++ b/sonar-batch/src/main/java/org/sonar/batch/index/Caches.java
@@ -36,7 +36,6 @@ import org.sonar.api.BatchComponent;
import org.sonar.api.utils.TempFolder;
import java.io.File;
-import java.io.Serializable;
import java.util.Properties;
import java.util.Set;
@@ -85,7 +84,7 @@ public class Caches implements BatchComponent, Startable {
cm.registerValueCoder(clazz, coder);
}
- public <V extends Serializable> Cache<V> createCache(String cacheName) {
+ public <V> Cache<V> createCache(String cacheName) {
Preconditions.checkState(volume != null && volume.isOpened(), "Caches are not initialized");
Preconditions.checkState(!cacheNames.contains(cacheName), "Cache is already created: " + cacheName);
try {
diff --git a/sonar-batch/src/main/java/org/sonar/batch/index/MeasurePersister.java b/sonar-batch/src/main/java/org/sonar/batch/index/MeasurePersister.java
index 2a63d4b8b9e..0f7691728a1 100644
--- a/sonar-batch/src/main/java/org/sonar/batch/index/MeasurePersister.java
+++ b/sonar-batch/src/main/java/org/sonar/batch/index/MeasurePersister.java
@@ -20,10 +20,13 @@
package org.sonar.batch.index;
import com.google.common.annotations.VisibleForTesting;
+import org.apache.commons.lang.StringEscapeUtils;
import org.sonar.api.database.model.MeasureMapper;
import org.sonar.api.database.model.MeasureModel;
import org.sonar.api.database.model.Snapshot;
+import org.sonar.api.measures.CoreMetrics;
import org.sonar.api.measures.Measure;
+import org.sonar.api.measures.PersistenceMode;
import org.sonar.api.measures.RuleMeasure;
import org.sonar.api.resources.Resource;
import org.sonar.api.resources.ResourceUtils;
@@ -31,6 +34,8 @@ import org.sonar.api.rule.RuleKey;
import org.sonar.api.rules.Rule;
import org.sonar.api.rules.RuleFinder;
import org.sonar.api.technicaldebt.batch.Characteristic;
+import org.sonar.batch.duplication.DuplicationCache;
+import org.sonar.batch.duplication.DuplicationGroup;
import org.sonar.batch.index.Cache.Entry;
import org.sonar.batch.scan.measure.MeasureCache;
import org.sonar.core.persistence.DbSession;
@@ -38,20 +43,27 @@ import org.sonar.core.persistence.MyBatis;
import javax.annotation.Nullable;
+import java.util.ArrayList;
+
public final class MeasurePersister implements ScanPersister {
private final MyBatis mybatis;
private final RuleFinder ruleFinder;
private final MeasureCache measureCache;
private final SnapshotCache snapshotCache;
private final ResourceCache resourceCache;
+ private final DuplicationCache duplicationCache;
+ private final org.sonar.api.measures.MetricFinder metricFinder;
public MeasurePersister(MyBatis mybatis, RuleFinder ruleFinder,
- MeasureCache measureCache, SnapshotCache snapshotCache, ResourceCache resourceCache) {
+ MeasureCache measureCache, SnapshotCache snapshotCache, ResourceCache resourceCache,
+ DuplicationCache duplicationCache, org.sonar.api.measures.MetricFinder metricFinder) {
this.mybatis = mybatis;
this.ruleFinder = ruleFinder;
this.measureCache = measureCache;
this.snapshotCache = snapshotCache;
this.resourceCache = resourceCache;
+ this.duplicationCache = duplicationCache;
+ this.metricFinder = metricFinder;
}
@Override
@@ -72,6 +84,19 @@ public final class MeasurePersister implements ScanPersister {
}
}
+ org.sonar.api.measures.Metric duplicationMetricWithId = metricFinder.findByKey(CoreMetrics.DUPLICATIONS_DATA_KEY);
+ for (Entry<ArrayList<DuplicationGroup>> entry : duplicationCache.entries()) {
+ String effectiveKey = entry.key()[0].toString();
+ Measure measure = new Measure(duplicationMetricWithId, toXml(entry.value())).setPersistenceMode(PersistenceMode.DATABASE);
+ Resource resource = resourceCache.get(effectiveKey);
+
+ if (shouldPersistMeasure(resource, measure)) {
+ Snapshot snapshot = snapshotCache.get(effectiveKey);
+ MeasureModel measureModel = model(measure).setSnapshotId(snapshot.getId());
+ mapper.insert(measureModel);
+ }
+ }
+
session.commit();
} catch (Exception e) {
throw new IllegalStateException("Unable to save some measures", e);
@@ -80,6 +105,28 @@ public final class MeasurePersister implements ScanPersister {
}
}
+ private static String toXml(Iterable<DuplicationGroup> duplications) {
+ StringBuilder xml = new StringBuilder();
+ xml.append("<duplications>");
+ for (DuplicationGroup duplication : duplications) {
+ xml.append("<g>");
+ toXml(xml, duplication.originBlock());
+ for (DuplicationGroup.Block part : duplication.duplicates()) {
+ toXml(xml, part);
+ }
+ xml.append("</g>");
+ }
+ xml.append("</duplications>");
+ return xml.toString();
+ }
+
+ private static void toXml(StringBuilder xml, DuplicationGroup.Block part) {
+ xml.append("<b s=\"").append(part.startLine())
+ .append("\" l=\"").append(part.length())
+ .append("\" r=\"").append(StringEscapeUtils.escapeXml(part.resourceKey()))
+ .append("\"/>");
+ }
+
@VisibleForTesting
static boolean shouldPersistMeasure(@Nullable Resource resource, @Nullable Measure measure) {
if (resource == null || measure == null) {
diff --git a/sonar-batch/src/main/java/org/sonar/batch/mediumtest/BatchMediumTester.java b/sonar-batch/src/main/java/org/sonar/batch/mediumtest/BatchMediumTester.java
index fc7a89ebad8..58cc4f461af 100644
--- a/sonar-batch/src/main/java/org/sonar/batch/mediumtest/BatchMediumTester.java
+++ b/sonar-batch/src/main/java/org/sonar/batch/mediumtest/BatchMediumTester.java
@@ -41,8 +41,11 @@ import org.sonar.api.resources.Languages;
import org.sonar.batch.bootstrap.PluginsReferential;
import org.sonar.batch.bootstrapper.Batch;
import org.sonar.batch.bootstrapper.EnvironmentInformation;
+import org.sonar.batch.duplication.DuplicationCache;
+import org.sonar.batch.duplication.DuplicationGroup;
import org.sonar.batch.highlighting.SyntaxHighlightingData;
import org.sonar.batch.highlighting.SyntaxHighlightingRule;
+import org.sonar.batch.index.Cache.Entry;
import org.sonar.batch.index.ComponentDataCache;
import org.sonar.batch.protocol.input.ActiveRule;
import org.sonar.batch.protocol.input.GlobalReferentials;
@@ -222,6 +225,7 @@ public class BatchMediumTester {
private List<Issue> issues = new ArrayList<Issue>();
private List<Measure> measures = new ArrayList<Measure>();
+ private Map<String, List<DuplicationGroup>> duplications = new HashMap<String, List<DuplicationGroup>>();
private List<InputFile> inputFiles = new ArrayList<InputFile>();
private List<InputDir> inputDirs = new ArrayList<InputDir>();
private Map<InputFile, SyntaxHighlightingData> highlightingPerFile = new HashMap<InputFile, SyntaxHighlightingData>();
@@ -259,6 +263,12 @@ public class BatchMediumTester {
}
}
+ DuplicationCache duplicationCache = container.getComponentByType(DuplicationCache.class);
+ for (Entry<ArrayList<DuplicationGroup>> entry : duplicationCache.entries()) {
+ String effectiveKey = entry.key()[0].toString();
+ duplications.put(effectiveKey, entry.value());
+ }
+
}
public List<Issue> issues() {
@@ -277,6 +287,10 @@ public class BatchMediumTester {
return inputDirs;
}
+ public List<DuplicationGroup> duplicationsFor(InputFile inputFile) {
+ return duplications.get(((DefaultInputFile) inputFile).key());
+ }
+
/**
* Get highlighting type at a given position in an inputfile
* @param charIndex 0-based offset in file
diff --git a/sonar-batch/src/main/java/org/sonar/batch/referential/ProjectReferentialsProvider.java b/sonar-batch/src/main/java/org/sonar/batch/referential/ProjectReferentialsProvider.java
index 75a063eb119..ac08e40f5fa 100644
--- a/sonar-batch/src/main/java/org/sonar/batch/referential/ProjectReferentialsProvider.java
+++ b/sonar-batch/src/main/java/org/sonar/batch/referential/ProjectReferentialsProvider.java
@@ -20,14 +20,24 @@
package org.sonar.batch.referential;
import org.picocontainer.injectors.ProviderAdapter;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
import org.sonar.api.batch.bootstrap.ProjectReactor;
import org.sonar.api.config.Settings;
import org.sonar.api.resources.Languages;
+import org.sonar.api.utils.TimeProfiler;
import org.sonar.batch.protocol.input.ProjectReferentials;
public class ProjectReferentialsProvider extends ProviderAdapter {
+ private static final Logger LOG = LoggerFactory.getLogger(ProjectReferentialsProvider.class);
+
public ProjectReferentials provide(ProjectReferentialsLoader loader, ProjectReactor reactor, Settings settings, Languages languages) {
- return loader.load(reactor, settings, languages);
+ TimeProfiler profiler = new TimeProfiler(LOG).start("Load project referentials");
+ try {
+ return loader.load(reactor, settings, languages);
+ } finally {
+ profiler.stop();
+ }
}
}
diff --git a/sonar-batch/src/main/java/org/sonar/batch/scan/ProjectScanContainer.java b/sonar-batch/src/main/java/org/sonar/batch/scan/ProjectScanContainer.java
index c5e21b89a66..3a209b91961 100644
--- a/sonar-batch/src/main/java/org/sonar/batch/scan/ProjectScanContainer.java
+++ b/sonar-batch/src/main/java/org/sonar/batch/scan/ProjectScanContainer.java
@@ -42,6 +42,8 @@ import org.sonar.batch.bootstrap.MetricProvider;
import org.sonar.batch.components.PeriodsDefinition;
import org.sonar.batch.debt.DebtModelProvider;
import org.sonar.batch.debt.IssueChangelogDebtCalculator;
+import org.sonar.batch.duplication.BlockCache;
+import org.sonar.batch.duplication.DuplicationCache;
import org.sonar.batch.index.Caches;
import org.sonar.batch.index.ComponentDataCache;
import org.sonar.batch.index.ComponentDataPersister;
@@ -194,6 +196,10 @@ public class ProjectScanContainer extends ComponentContainer {
new RulesProvider(),
new DebtModelProvider(),
+ // Duplications
+ BlockCache.class,
+ DuplicationCache.class,
+
ProjectSettings.class);
}
diff --git a/sonar-batch/src/main/java/org/sonar/batch/scan/SensorContextAdaptor.java b/sonar-batch/src/main/java/org/sonar/batch/scan/SensorContextAdaptor.java
index 1de8fd7201c..d285a6c8646 100644
--- a/sonar-batch/src/main/java/org/sonar/batch/scan/SensorContextAdaptor.java
+++ b/sonar-batch/src/main/java/org/sonar/batch/scan/SensorContextAdaptor.java
@@ -27,6 +27,8 @@ import org.sonar.api.batch.fs.internal.DefaultInputFile;
import org.sonar.api.batch.measure.Metric;
import org.sonar.api.batch.rule.ActiveRules;
import org.sonar.api.batch.sensor.SensorContext;
+import org.sonar.api.batch.sensor.duplication.DuplicationBuilder;
+import org.sonar.api.batch.sensor.duplication.TokenBuilder;
import org.sonar.api.batch.sensor.highlighting.HighlightingBuilder;
import org.sonar.api.batch.sensor.issue.Issue;
import org.sonar.api.batch.sensor.issue.IssueBuilder;
@@ -49,9 +51,14 @@ import org.sonar.api.resources.Project;
import org.sonar.api.resources.Resource;
import org.sonar.api.resources.Scopes;
import org.sonar.api.rule.RuleKey;
+import org.sonar.batch.duplication.BlockCache;
+import org.sonar.batch.duplication.DefaultDuplicationBuilder;
+import org.sonar.batch.duplication.DefaultTokenBuilder;
+import org.sonar.batch.duplication.DuplicationCache;
import org.sonar.batch.highlighting.DefaultHighlightingBuilder;
import org.sonar.batch.index.ComponentDataCache;
import org.sonar.batch.symbol.DefaultSymbolTableBuilder;
+import org.sonar.duplications.internal.pmd.PmdBlockChunker;
import java.io.Serializable;
@@ -61,17 +68,20 @@ import java.io.Serializable;
*/
public class SensorContextAdaptor implements SensorContext {
- private org.sonar.api.batch.SensorContext sensorContext;
- private MetricFinder metricFinder;
- private Project project;
- private ResourcePerspectives perspectives;
- private Settings settings;
- private FileSystem fs;
- private ActiveRules activeRules;
- private ComponentDataCache componentDataCache;
+ private final org.sonar.api.batch.SensorContext sensorContext;
+ private final MetricFinder metricFinder;
+ private final Project project;
+ private final ResourcePerspectives perspectives;
+ private final Settings settings;
+ private final FileSystem fs;
+ private final ActiveRules activeRules;
+ private final ComponentDataCache componentDataCache;
+ private final BlockCache blockCache;
+ private final DuplicationCache duplicationCache;
public SensorContextAdaptor(org.sonar.api.batch.SensorContext sensorContext, MetricFinder metricFinder, Project project, ResourcePerspectives perspectives,
- Settings settings, FileSystem fs, ActiveRules activeRules, ComponentDataCache componentDataCache) {
+ Settings settings, FileSystem fs, ActiveRules activeRules, ComponentDataCache componentDataCache, BlockCache blockCache,
+ DuplicationCache duplicationCache) {
this.sensorContext = sensorContext;
this.metricFinder = metricFinder;
this.project = project;
@@ -80,6 +90,8 @@ public class SensorContextAdaptor implements SensorContext {
this.fs = fs;
this.activeRules = activeRules;
this.componentDataCache = componentDataCache;
+ this.blockCache = blockCache;
+ this.duplicationCache = duplicationCache;
}
@Override
@@ -254,4 +266,33 @@ public class SensorContextAdaptor implements SensorContext {
return new DefaultSymbolTableBuilder(((DefaultInputFile) inputFile).key(), componentDataCache);
}
+ @Override
+ public TokenBuilder tokenBuilder(InputFile inputFile) {
+ PmdBlockChunker blockChunker = new PmdBlockChunker(getBlockSize(inputFile.language()));
+ return new DefaultTokenBuilder(inputFile, blockCache, blockChunker);
+ }
+
+ @Override
+ public DuplicationBuilder duplicationBuilder(InputFile inputFile) {
+ return new DefaultDuplicationBuilder(inputFile, duplicationCache);
+ }
+
+ private int getBlockSize(String languageKey) {
+ int blockSize = settings.getInt("sonar.cpd." + languageKey + ".minimumLines");
+ if (blockSize == 0) {
+ blockSize = getDefaultBlockSize(languageKey);
+ }
+ return blockSize;
+ }
+
+ private static int getDefaultBlockSize(String languageKey) {
+ if ("cobol".equals(languageKey)) {
+ return 30;
+ } else if ("abap".equals(languageKey) || "natur".equals(languageKey)) {
+ return 20;
+ } else {
+ return 10;
+ }
+ }
+
}
diff --git a/sonar-batch/src/main/java/org/sonar/batch/scan/SensorWrapper.java b/sonar-batch/src/main/java/org/sonar/batch/scan/SensorWrapper.java
index b5929008269..32a49a16492 100644
--- a/sonar-batch/src/main/java/org/sonar/batch/scan/SensorWrapper.java
+++ b/sonar-batch/src/main/java/org/sonar/batch/scan/SensorWrapper.java
@@ -33,19 +33,23 @@ import java.util.List;
public class SensorWrapper implements org.sonar.api.batch.Sensor {
- private Sensor analyzer;
+ private Sensor wrappedSensor;
private SensorContext adaptor;
private DefaultSensorDescriptor descriptor;
private AnalyzerOptimizer optimizer;
public SensorWrapper(Sensor newSensor, SensorContext adaptor, AnalyzerOptimizer optimizer) {
- this.analyzer = newSensor;
+ this.wrappedSensor = newSensor;
this.optimizer = optimizer;
descriptor = new DefaultSensorDescriptor();
newSensor.describe(descriptor);
this.adaptor = adaptor;
}
+ public Sensor wrappedSensor() {
+ return wrappedSensor;
+ }
+
@DependedUpon
public List<Metric> provides() {
return Arrays.asList(descriptor.provides());
@@ -63,7 +67,7 @@ public class SensorWrapper implements org.sonar.api.batch.Sensor {
@Override
public void analyse(Project module, org.sonar.api.batch.SensorContext context) {
- analyzer.execute(adaptor);
+ wrappedSensor.execute(adaptor);
}
@Override
diff --git a/sonar-batch/src/main/java/org/sonar/batch/scan/filesystem/DefaultInputFileValueCoder.java b/sonar-batch/src/main/java/org/sonar/batch/scan/filesystem/DefaultInputFileValueCoder.java
new file mode 100644
index 00000000000..6dcfc9903b1
--- /dev/null
+++ b/sonar-batch/src/main/java/org/sonar/batch/scan/filesystem/DefaultInputFileValueCoder.java
@@ -0,0 +1,76 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.batch.scan.filesystem;
+
+import com.persistit.Value;
+import com.persistit.encoding.CoderContext;
+import com.persistit.encoding.ValueCoder;
+import org.sonar.api.batch.fs.InputFile;
+import org.sonar.api.batch.fs.internal.DeprecatedDefaultInputFile;
+
+import javax.annotation.Nullable;
+
+import java.io.File;
+
+class DefaultInputFileValueCoder implements ValueCoder {
+
+ @Override
+ public void put(Value value, Object object, CoderContext context) {
+ DeprecatedDefaultInputFile f = (DeprecatedDefaultInputFile) object;
+ putUTFOrNull(value, f.relativePath());
+ value.putString(f.getFileBaseDir().toString());
+ putUTFOrNull(value, f.deprecatedKey());
+ value.putString(f.sourceDirAbsolutePath());
+ putUTFOrNull(value, f.pathRelativeToSourceDir());
+ putUTFOrNull(value, f.absolutePath());
+ value.putString(f.language());
+ value.putString(f.type().name());
+ value.putString(f.status().name());
+ putUTFOrNull(value, f.hash());
+ value.put(f.lines());
+ putUTFOrNull(value, f.key());
+ }
+
+ private void putUTFOrNull(Value value, @Nullable String utfOrNull) {
+ if (utfOrNull != null) {
+ value.putUTF(utfOrNull);
+ } else {
+ value.putNull();
+ }
+ }
+
+ @Override
+ public Object get(Value value, Class clazz, CoderContext context) {
+ DeprecatedDefaultInputFile file = new DeprecatedDefaultInputFile(value.getString());
+ file.setBasedir(new File(value.getString()));
+ file.setDeprecatedKey(value.getString());
+ file.setSourceDirAbsolutePath(value.getString());
+ file.setPathRelativeToSourceDir(value.getString());
+ file.setAbsolutePath(value.getString());
+ file.setLanguage(value.getString());
+ file.setType(InputFile.Type.valueOf(value.getString()));
+ file.setStatus(InputFile.Status.valueOf(value.getString()));
+ file.setHash(value.getString());
+ file.setLines(value.getInt());
+ file.setKey(value.getString());
+ return file;
+ }
+
+}
diff --git a/sonar-batch/src/main/java/org/sonar/batch/scan/filesystem/InputPathCache.java b/sonar-batch/src/main/java/org/sonar/batch/scan/filesystem/InputPathCache.java
index 5d901a517ae..39672d7f7c5 100644
--- a/sonar-batch/src/main/java/org/sonar/batch/scan/filesystem/InputPathCache.java
+++ b/sonar-batch/src/main/java/org/sonar/batch/scan/filesystem/InputPathCache.java
@@ -23,6 +23,7 @@ import org.sonar.api.BatchComponent;
import org.sonar.api.batch.fs.InputDir;
import org.sonar.api.batch.fs.InputFile;
import org.sonar.api.batch.fs.InputPath;
+import org.sonar.api.batch.fs.internal.DeprecatedDefaultInputFile;
import org.sonar.batch.index.Cache;
import org.sonar.batch.index.Caches;
@@ -44,6 +45,7 @@ public class InputPathCache implements BatchComponent {
private final Cache<InputPath> cache;
public InputPathCache(Caches caches) {
+ caches.registerValueCoder(DeprecatedDefaultInputFile.class, new DefaultInputFileValueCoder());
cache = caches.createCache("inputFiles");
}
diff --git a/sonar-batch/src/main/java/org/sonar/batch/scan/measure/MeasureValueCoder.java b/sonar-batch/src/main/java/org/sonar/batch/scan/measure/MeasureValueCoder.java
index 7f010d97d0a..bd8cee930d8 100644
--- a/sonar-batch/src/main/java/org/sonar/batch/scan/measure/MeasureValueCoder.java
+++ b/sonar-batch/src/main/java/org/sonar/batch/scan/measure/MeasureValueCoder.java
@@ -30,6 +30,8 @@ import org.sonar.api.technicaldebt.batch.Characteristic;
import org.sonar.api.technicaldebt.batch.Requirement;
import org.sonar.api.technicaldebt.batch.TechnicalDebtModel;
+import javax.annotation.Nullable;
+
class MeasureValueCoder implements ValueCoder {
private final MetricFinder metricFinder;
@@ -42,12 +44,12 @@ class MeasureValueCoder implements ValueCoder {
public void put(Value value, Object object, CoderContext context) {
Measure<?> m = (Measure) object;
- value.putString(m.getMetricKey());
+ value.putUTF(m.getMetricKey());
value.put(m.getValue());
- value.putString(m.getData());
- value.putString(m.getDescription());
+ putUTFOrNull(value, m.getData());
+ putUTFOrNull(value, m.getDescription());
value.putString(m.getAlertStatus() != null ? m.getAlertStatus().name() : null);
- value.putString(m.getAlertText());
+ putUTFOrNull(value, m.getAlertText());
value.put(m.getTendency());
value.putDate(m.getDate());
value.put(m.getVariation1());
@@ -55,7 +57,7 @@ class MeasureValueCoder implements ValueCoder {
value.put(m.getVariation3());
value.put(m.getVariation4());
value.put(m.getVariation5());
- value.putString(m.getUrl());
+ putUTFOrNull(value, m.getUrl());
Characteristic characteristic = m.getCharacteristic();
value.put(characteristic != null ? characteristic.id() : null);
Requirement requirement = m.getRequirement();
@@ -66,6 +68,14 @@ class MeasureValueCoder implements ValueCoder {
value.putString(persistenceMode != null ? persistenceMode.name() : null);
}
+ private void putUTFOrNull(Value value, @Nullable String utfOrNull) {
+ if (utfOrNull != null) {
+ value.putUTF(utfOrNull);
+ } else {
+ value.putNull();
+ }
+ }
+
public Object get(Value value, Class clazz, CoderContext context) {
Measure<?> m = new Measure();
String metricKey = value.getString();
diff --git a/sonar-batch/src/main/java/org/sonar/batch/scan2/AnalyzerMeasureCache.java b/sonar-batch/src/main/java/org/sonar/batch/scan2/AnalyzerMeasureCache.java
index 75d0e31932c..801744de2c7 100644
--- a/sonar-batch/src/main/java/org/sonar/batch/scan2/AnalyzerMeasureCache.java
+++ b/sonar-batch/src/main/java/org/sonar/batch/scan2/AnalyzerMeasureCache.java
@@ -19,10 +19,10 @@
*/
package org.sonar.batch.scan2;
-import org.sonar.api.batch.sensor.measure.internal.DefaultMeasure;
-
import com.google.common.base.Preconditions;
import org.sonar.api.BatchComponent;
+import org.sonar.api.batch.measure.MetricFinder;
+import org.sonar.api.batch.sensor.measure.internal.DefaultMeasure;
import org.sonar.batch.index.Cache;
import org.sonar.batch.index.Cache.Entry;
import org.sonar.batch.index.Caches;
@@ -35,7 +35,8 @@ public class AnalyzerMeasureCache implements BatchComponent {
// project key -> component key -> metric key -> measure
private final Cache<DefaultMeasure> cache;
- public AnalyzerMeasureCache(Caches caches) {
+ public AnalyzerMeasureCache(Caches caches, MetricFinder metricFinder) {
+ caches.registerValueCoder(DefaultMeasure.class, new DefaultMeasureValueCoder(metricFinder));
cache = caches.createCache("measures");
}
diff --git a/sonar-batch/src/main/java/org/sonar/batch/scan2/DefaultMeasureValueCoder.java b/sonar-batch/src/main/java/org/sonar/batch/scan2/DefaultMeasureValueCoder.java
new file mode 100644
index 00000000000..8b82a684dd0
--- /dev/null
+++ b/sonar-batch/src/main/java/org/sonar/batch/scan2/DefaultMeasureValueCoder.java
@@ -0,0 +1,64 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.batch.scan2;
+
+import com.persistit.Value;
+import com.persistit.encoding.CoderContext;
+import com.persistit.encoding.ValueCoder;
+import org.sonar.api.batch.fs.InputFile;
+import org.sonar.api.batch.measure.Metric;
+import org.sonar.api.batch.measure.MetricFinder;
+import org.sonar.api.batch.sensor.measure.internal.DefaultMeasure;
+import org.sonar.api.batch.sensor.measure.internal.DefaultMeasureBuilder;
+
+import java.io.Serializable;
+
+class DefaultMeasureValueCoder implements ValueCoder {
+
+ private MetricFinder metricFinder;
+
+ public DefaultMeasureValueCoder(MetricFinder metricFinder) {
+ this.metricFinder = metricFinder;
+ }
+
+ @Override
+ public void put(Value value, Object object, CoderContext context) {
+ DefaultMeasure m = (DefaultMeasure) object;
+ value.put(m.inputFile());
+ value.putUTF(m.metric().key());
+ value.put(m.value());
+ }
+
+ @Override
+ public Object get(Value value, Class clazz, CoderContext context) {
+ DefaultMeasureBuilder builder = new DefaultMeasureBuilder();
+ InputFile f = (InputFile) value.get();
+ if (f != null) {
+ builder.onFile(f);
+ } else {
+ builder.onProject();
+ }
+ Metric m = metricFinder.findByKey(value.getString());
+ builder.forMetric(m);
+ builder.withValue((Serializable) value.get());
+ return builder.build();
+ }
+
+}
diff --git a/sonar-batch/src/main/java/org/sonar/batch/scan2/DefaultSensorContext.java b/sonar-batch/src/main/java/org/sonar/batch/scan2/DefaultSensorContext.java
index dcd6efd1ca4..73d6429bfcd 100644
--- a/sonar-batch/src/main/java/org/sonar/batch/scan2/DefaultSensorContext.java
+++ b/sonar-batch/src/main/java/org/sonar/batch/scan2/DefaultSensorContext.java
@@ -28,6 +28,8 @@ import org.sonar.api.batch.measure.Metric;
import org.sonar.api.batch.rule.ActiveRules;
import org.sonar.api.batch.rule.internal.DefaultActiveRule;
import org.sonar.api.batch.sensor.SensorContext;
+import org.sonar.api.batch.sensor.duplication.DuplicationBuilder;
+import org.sonar.api.batch.sensor.duplication.TokenBuilder;
import org.sonar.api.batch.sensor.highlighting.HighlightingBuilder;
import org.sonar.api.batch.sensor.issue.Issue;
import org.sonar.api.batch.sensor.issue.IssueBuilder;
@@ -41,12 +43,17 @@ import org.sonar.api.batch.sensor.symbol.SymbolTableBuilder;
import org.sonar.api.config.Settings;
import org.sonar.api.rule.RuleKey;
import org.sonar.api.utils.MessageException;
+import org.sonar.batch.duplication.BlockCache;
+import org.sonar.batch.duplication.DefaultDuplicationBuilder;
+import org.sonar.batch.duplication.DefaultTokenBuilder;
+import org.sonar.batch.duplication.DuplicationCache;
import org.sonar.batch.highlighting.DefaultHighlightingBuilder;
import org.sonar.batch.index.ComponentDataCache;
import org.sonar.batch.issue.IssueFilters;
import org.sonar.batch.scan.SensorContextAdaptor;
import org.sonar.batch.symbol.DefaultSymbolTableBuilder;
import org.sonar.core.component.ComponentKeys;
+import org.sonar.duplications.internal.pmd.PmdBlockChunker;
import java.io.Serializable;
@@ -60,9 +67,12 @@ public class DefaultSensorContext implements SensorContext {
private final ActiveRules activeRules;
private final IssueFilters issueFilters;
private final ComponentDataCache componentDataCache;
+ private final BlockCache blockCache;
+ private final DuplicationCache duplicationCache;
public DefaultSensorContext(ProjectDefinition def, AnalyzerMeasureCache measureCache, AnalyzerIssueCache issueCache,
- Settings settings, FileSystem fs, ActiveRules activeRules, IssueFilters issueFilters, ComponentDataCache componentDataCache) {
+ Settings settings, FileSystem fs, ActiveRules activeRules, IssueFilters issueFilters, ComponentDataCache componentDataCache,
+ BlockCache blockCache, DuplicationCache duplicationCache) {
this.def = def;
this.measureCache = measureCache;
this.issueCache = issueCache;
@@ -71,6 +81,8 @@ public class DefaultSensorContext implements SensorContext {
this.activeRules = activeRules;
this.issueFilters = issueFilters;
this.componentDataCache = componentDataCache;
+ this.blockCache = blockCache;
+ this.duplicationCache = duplicationCache;
}
@Override
@@ -175,4 +187,34 @@ public class DefaultSensorContext implements SensorContext {
return new DefaultSymbolTableBuilder(((DefaultInputFile) inputFile).key(), componentDataCache);
}
+ @Override
+ public TokenBuilder tokenBuilder(InputFile inputFile) {
+ PmdBlockChunker blockChunker = new PmdBlockChunker(getBlockSize(inputFile.language()));
+
+ return new DefaultTokenBuilder(inputFile, blockCache, blockChunker);
+ }
+
+ @Override
+ public DuplicationBuilder duplicationBuilder(InputFile inputFile) {
+ return new DefaultDuplicationBuilder(inputFile, duplicationCache);
+ }
+
+ private int getBlockSize(String languageKey) {
+ int blockSize = settings.getInt("sonar.cpd." + languageKey + ".minimumLines");
+ if (blockSize == 0) {
+ blockSize = getDefaultBlockSize(languageKey);
+ }
+ return blockSize;
+ }
+
+ private static int getDefaultBlockSize(String languageKey) {
+ if ("cobol".equals(languageKey)) {
+ return 30;
+ } else if ("abap".equals(languageKey) || "natur".equals(languageKey)) {
+ return 20;
+ } else {
+ return 10;
+ }
+ }
+
}
diff --git a/sonar-batch/src/main/java/org/sonar/batch/scan2/ProjectScanContainer.java b/sonar-batch/src/main/java/org/sonar/batch/scan2/ProjectScanContainer.java
index e13fca37d28..b0a2b3e0af5 100644
--- a/sonar-batch/src/main/java/org/sonar/batch/scan2/ProjectScanContainer.java
+++ b/sonar-batch/src/main/java/org/sonar/batch/scan2/ProjectScanContainer.java
@@ -33,6 +33,8 @@ import org.sonar.api.scan.filesystem.PathResolver;
import org.sonar.batch.bootstrap.ExtensionInstaller;
import org.sonar.batch.bootstrap.ExtensionMatcher;
import org.sonar.batch.bootstrap.ExtensionUtils;
+import org.sonar.batch.duplication.BlockCache;
+import org.sonar.batch.duplication.DuplicationCache;
import org.sonar.batch.index.Caches;
import org.sonar.batch.index.ComponentDataCache;
import org.sonar.batch.languages.DefaultLanguagesReferential;
@@ -107,6 +109,10 @@ public class ProjectScanContainer extends ComponentContainer {
ComponentDataCache.class,
+ // Duplications
+ BlockCache.class,
+ DuplicationCache.class,
+
ScanTaskObservers.class);
}
diff --git a/sonar-batch/src/main/java/org/sonar/batch/scan2/SensorsExecutor.java b/sonar-batch/src/main/java/org/sonar/batch/scan2/SensorsExecutor.java
index d437eadb6fd..ea872c4c1bf 100644
--- a/sonar-batch/src/main/java/org/sonar/batch/scan2/SensorsExecutor.java
+++ b/sonar-batch/src/main/java/org/sonar/batch/scan2/SensorsExecutor.java
@@ -53,7 +53,7 @@ public class SensorsExecutor implements BatchComponent {
continue;
}
- LOG.info("Execute analyzer: " + descriptor.name());
+ LOG.info("Execute sensor: " + descriptor.name());
executeSensor(context, analyzer);
}
diff --git a/sonar-batch/src/test/java/org/sonar/batch/duplication/DuplicationCacheTest.java b/sonar-batch/src/test/java/org/sonar/batch/duplication/DuplicationCacheTest.java
new file mode 100644
index 00000000000..ade701fce45
--- /dev/null
+++ b/sonar-batch/src/test/java/org/sonar/batch/duplication/DuplicationCacheTest.java
@@ -0,0 +1,82 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.batch.duplication;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+import org.junit.rules.TemporaryFolder;
+import org.sonar.batch.index.Caches;
+import org.sonar.batch.index.CachesTest;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+
+import static org.fest.assertions.Assertions.assertThat;
+
+public class DuplicationCacheTest {
+
+ @Rule
+ public TemporaryFolder temp = new TemporaryFolder();
+
+ @Rule
+ public ExpectedException thrown = ExpectedException.none();
+
+ Caches caches;
+
+ @Before
+ public void start() throws Exception {
+ caches = CachesTest.createCacheOnTemp(temp);
+ caches.start();
+ }
+
+ @After
+ public void stop() {
+ caches.stop();
+ }
+
+ @Test
+ public void should_add_clone_groups() throws Exception {
+ DuplicationCache cache = new DuplicationCache(caches);
+
+ DuplicationGroup group1 = new DuplicationGroup(new DuplicationGroup.Block("foo", 1, 2))
+ .addDuplicate(new DuplicationGroup.Block("foo", 1, 2))
+ .addDuplicate(new DuplicationGroup.Block("foo2", 12, 22))
+ .addDuplicate(new DuplicationGroup.Block("foo3", 13, 23));
+
+ DuplicationGroup group2 = new DuplicationGroup(new DuplicationGroup.Block("2foo", 1, 2))
+ .addDuplicate(new DuplicationGroup.Block("2foo", 1, 2))
+ .addDuplicate(new DuplicationGroup.Block("2foo2", 12, 22))
+ .addDuplicate(new DuplicationGroup.Block("2foo3", 13, 23));
+
+ assertThat(cache.entries()).hasSize(0);
+
+ cache.put("foo", new ArrayList<DuplicationGroup>(Arrays.asList(group1, group2)));
+
+ assertThat(cache.entries()).hasSize(1);
+
+ ArrayList<DuplicationGroup> entry = cache.byComponent("foo");
+ assertThat(entry.get(0).originBlock().resourceKey()).isEqualTo("foo");
+
+ }
+
+}
diff --git a/sonar-batch/src/test/java/org/sonar/batch/index/MeasurePersisterTest.java b/sonar-batch/src/test/java/org/sonar/batch/index/MeasurePersisterTest.java
index fe8a1a45a65..dfed349e696 100644
--- a/sonar-batch/src/test/java/org/sonar/batch/index/MeasurePersisterTest.java
+++ b/sonar-batch/src/test/java/org/sonar/batch/index/MeasurePersisterTest.java
@@ -27,6 +27,7 @@ import org.sonar.api.database.model.Snapshot;
import org.sonar.api.measures.CoreMetrics;
import org.sonar.api.measures.Measure;
import org.sonar.api.measures.Metric;
+import org.sonar.api.measures.MetricFinder;
import org.sonar.api.measures.PersistenceMode;
import org.sonar.api.measures.RuleMeasure;
import org.sonar.api.resources.Directory;
@@ -35,10 +36,14 @@ import org.sonar.api.resources.Project;
import org.sonar.api.rules.Rule;
import org.sonar.api.rules.RuleFinder;
import org.sonar.api.rules.RulePriority;
+import org.sonar.batch.duplication.DuplicationCache;
+import org.sonar.batch.duplication.DuplicationGroup;
import org.sonar.batch.scan.measure.MeasureCache;
import org.sonar.core.persistence.AbstractDaoTestCase;
+import java.util.ArrayList;
import java.util.Arrays;
+import java.util.Collections;
import static org.fest.assertions.Assertions.assertThat;
import static org.mockito.Mockito.mock;
@@ -70,6 +75,8 @@ public class MeasurePersisterTest extends AbstractDaoTestCase {
public void mockResourcePersister() {
snapshotCache = mock(SnapshotCache.class);
measureCache = mock(MeasureCache.class);
+ DuplicationCache duplicationCache = mock(DuplicationCache.class);
+ when(duplicationCache.entries()).thenReturn(Collections.<Cache.Entry<ArrayList<DuplicationGroup>>>emptyList());
ResourceCache resourceCache = mock(ResourceCache.class);
when(snapshotCache.get("foo")).thenReturn(projectSnapshot);
when(snapshotCache.get("foo:org/foo")).thenReturn(packageSnapshot);
@@ -77,7 +84,7 @@ public class MeasurePersisterTest extends AbstractDaoTestCase {
when(resourceCache.get("foo:org/foo/Bar.java")).thenReturn(aFile);
when(resourceCache.get("foo:org/foo")).thenReturn(aDirectory);
- measurePersister = new MeasurePersister(getMyBatis(), ruleFinder, measureCache, snapshotCache, resourceCache);
+ measurePersister = new MeasurePersister(getMyBatis(), ruleFinder, measureCache, snapshotCache, resourceCache, duplicationCache, mock(MetricFinder.class));
}
@Test
diff --git a/sonar-batch/src/test/java/org/sonar/batch/mediumtest/fs/FileSystemMediumTest.java b/sonar-batch/src/test/java/org/sonar/batch/mediumtest/fs/FileSystemMediumTest.java
index 674f472d0b5..19d7aea020a 100644
--- a/sonar-batch/src/test/java/org/sonar/batch/mediumtest/fs/FileSystemMediumTest.java
+++ b/sonar-batch/src/test/java/org/sonar/batch/mediumtest/fs/FileSystemMediumTest.java
@@ -31,8 +31,8 @@ import org.sonar.api.batch.fs.InputFile;
import org.sonar.api.utils.MessageException;
import org.sonar.batch.mediumtest.BatchMediumTester;
import org.sonar.batch.mediumtest.BatchMediumTester.TaskResult;
-import org.sonar.batch.mediumtest.xoo.plugin.XooPlugin;
import org.sonar.batch.protocol.input.ActiveRule;
+import org.sonar.xoo.XooPlugin;
import java.io.File;
import java.io.IOException;
diff --git a/sonar-batch/src/test/java/org/sonar/batch/mediumtest/highlighting/HighlightingMediumTest.java b/sonar-batch/src/test/java/org/sonar/batch/mediumtest/highlighting/HighlightingMediumTest.java
index 259b2ffcec0..b9fab7ba450 100644
--- a/sonar-batch/src/test/java/org/sonar/batch/mediumtest/highlighting/HighlightingMediumTest.java
+++ b/sonar-batch/src/test/java/org/sonar/batch/mediumtest/highlighting/HighlightingMediumTest.java
@@ -29,7 +29,7 @@ import org.sonar.api.batch.fs.InputFile;
import org.sonar.api.batch.sensor.highlighting.HighlightingBuilder;
import org.sonar.batch.mediumtest.BatchMediumTester;
import org.sonar.batch.mediumtest.BatchMediumTester.TaskResult;
-import org.sonar.batch.mediumtest.xoo.plugin.XooPlugin;
+import org.sonar.xoo.XooPlugin;
import java.io.File;
import java.io.IOException;
diff --git a/sonar-batch/src/test/java/org/sonar/batch/mediumtest/issues/IssuesMediumTest.java b/sonar-batch/src/test/java/org/sonar/batch/mediumtest/issues/IssuesMediumTest.java
index 875e7e6d510..f3eebf971e3 100644
--- a/sonar-batch/src/test/java/org/sonar/batch/mediumtest/issues/IssuesMediumTest.java
+++ b/sonar-batch/src/test/java/org/sonar/batch/mediumtest/issues/IssuesMediumTest.java
@@ -29,8 +29,8 @@ import org.sonar.api.batch.fs.internal.DefaultInputFile;
import org.sonar.api.batch.sensor.issue.Issue;
import org.sonar.batch.mediumtest.BatchMediumTester;
import org.sonar.batch.mediumtest.BatchMediumTester.TaskResult;
-import org.sonar.batch.mediumtest.xoo.plugin.XooPlugin;
import org.sonar.batch.protocol.input.ActiveRule;
+import org.sonar.xoo.XooPlugin;
import java.io.File;
import java.io.IOException;
diff --git a/sonar-batch/src/test/java/org/sonar/batch/mediumtest/issues/IssuesOnDirMediumTest.java b/sonar-batch/src/test/java/org/sonar/batch/mediumtest/issues/IssuesOnDirMediumTest.java
index fdf6b15e857..fb3e8d16edd 100644
--- a/sonar-batch/src/test/java/org/sonar/batch/mediumtest/issues/IssuesOnDirMediumTest.java
+++ b/sonar-batch/src/test/java/org/sonar/batch/mediumtest/issues/IssuesOnDirMediumTest.java
@@ -28,8 +28,8 @@ import org.junit.rules.TemporaryFolder;
import org.sonar.api.batch.fs.internal.DefaultInputDir;
import org.sonar.batch.mediumtest.BatchMediumTester;
import org.sonar.batch.mediumtest.BatchMediumTester.TaskResult;
-import org.sonar.batch.mediumtest.xoo.plugin.XooPlugin;
import org.sonar.batch.protocol.input.ActiveRule;
+import org.sonar.xoo.XooPlugin;
import java.io.File;
import java.io.IOException;
diff --git a/sonar-batch/src/test/java/org/sonar/batch/mediumtest/measures/MeasuresMediumTest.java b/sonar-batch/src/test/java/org/sonar/batch/mediumtest/measures/MeasuresMediumTest.java
index bb93ae4b7ba..146b89eb316 100644
--- a/sonar-batch/src/test/java/org/sonar/batch/mediumtest/measures/MeasuresMediumTest.java
+++ b/sonar-batch/src/test/java/org/sonar/batch/mediumtest/measures/MeasuresMediumTest.java
@@ -30,7 +30,7 @@ import org.sonar.api.batch.sensor.measure.internal.DefaultMeasureBuilder;
import org.sonar.api.measures.CoreMetrics;
import org.sonar.batch.mediumtest.BatchMediumTester;
import org.sonar.batch.mediumtest.BatchMediumTester.TaskResult;
-import org.sonar.batch.mediumtest.xoo.plugin.XooPlugin;
+import org.sonar.xoo.XooPlugin;
import java.io.File;
import java.io.IOException;
diff --git a/sonar-batch/src/test/java/org/sonar/batch/mediumtest/symbol/SymbolMediumTest.java b/sonar-batch/src/test/java/org/sonar/batch/mediumtest/symbol/SymbolMediumTest.java
index c3875c625f3..8772d371ba2 100644
--- a/sonar-batch/src/test/java/org/sonar/batch/mediumtest/symbol/SymbolMediumTest.java
+++ b/sonar-batch/src/test/java/org/sonar/batch/mediumtest/symbol/SymbolMediumTest.java
@@ -28,7 +28,7 @@ import org.junit.rules.TemporaryFolder;
import org.sonar.api.batch.fs.InputFile;
import org.sonar.batch.mediumtest.BatchMediumTester;
import org.sonar.batch.mediumtest.BatchMediumTester.TaskResult;
-import org.sonar.batch.mediumtest.xoo.plugin.XooPlugin;
+import org.sonar.xoo.XooPlugin;
import java.io.File;
import java.io.IOException;
diff --git a/sonar-batch/src/test/java/org/sonar/batch/mediumtest/xoo/plugin/XooPlugin.java b/sonar-batch/src/test/java/org/sonar/batch/mediumtest/xoo/plugin/XooPlugin.java
deleted file mode 100644
index 78718da3682..00000000000
--- a/sonar-batch/src/test/java/org/sonar/batch/mediumtest/xoo/plugin/XooPlugin.java
+++ /dev/null
@@ -1,53 +0,0 @@
-/*
- * SonarQube, open source software quality management tool.
- * Copyright (C) 2008-2014 SonarSource
- * mailto:contact AT sonarsource DOT com
- *
- * SonarQube is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * SonarQube is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- */
-package org.sonar.batch.mediumtest.xoo.plugin;
-
-import org.sonar.api.SonarPlugin;
-import org.sonar.batch.mediumtest.xoo.plugin.base.Xoo;
-import org.sonar.batch.mediumtest.xoo.plugin.lang.MeasureSensor;
-import org.sonar.batch.mediumtest.xoo.plugin.lang.ScmActivitySensor;
-import org.sonar.batch.mediumtest.xoo.plugin.lang.SymbolReferencesSensor;
-import org.sonar.batch.mediumtest.xoo.plugin.lang.SyntaxHighlightingSensor;
-import org.sonar.batch.mediumtest.xoo.plugin.rule.CreateIssueByInternalKeySensor;
-import org.sonar.batch.mediumtest.xoo.plugin.rule.OneIssueOnDirPerFileSensor;
-import org.sonar.batch.mediumtest.xoo.plugin.rule.OneIssuePerLineSensor;
-
-import java.util.Arrays;
-import java.util.List;
-
-public final class XooPlugin extends SonarPlugin {
-
- @Override
- public List getExtensions() {
- return Arrays.asList(
- // language
- MeasureSensor.class,
- ScmActivitySensor.class,
- SyntaxHighlightingSensor.class,
- SymbolReferencesSensor.class,
- Xoo.class,
-
- // sensors
- OneIssuePerLineSensor.class,
- OneIssueOnDirPerFileSensor.class,
- CreateIssueByInternalKeySensor.class
- );
- }
-}
diff --git a/sonar-batch/src/test/java/org/sonar/batch/scan/SensorContextAdapterTest.java b/sonar-batch/src/test/java/org/sonar/batch/scan/SensorContextAdapterTest.java
index 7b7eb21ee58..a93bf9bae83 100644
--- a/sonar-batch/src/test/java/org/sonar/batch/scan/SensorContextAdapterTest.java
+++ b/sonar-batch/src/test/java/org/sonar/batch/scan/SensorContextAdapterTest.java
@@ -42,6 +42,8 @@ import org.sonar.api.measures.MetricFinder;
import org.sonar.api.resources.File;
import org.sonar.api.resources.Project;
import org.sonar.api.rule.RuleKey;
+import org.sonar.batch.duplication.BlockCache;
+import org.sonar.batch.duplication.DuplicationCache;
import org.sonar.batch.index.ComponentDataCache;
import static org.fest.assertions.Assertions.assertThat;
@@ -71,8 +73,9 @@ public class SensorContextAdapterTest {
settings = new Settings();
resourcePerspectives = mock(ResourcePerspectives.class);
ComponentDataCache componentDataCache = mock(ComponentDataCache.class);
+ BlockCache blockCache = mock(BlockCache.class);
adaptor = new SensorContextAdaptor(sensorContext, metricFinder, new Project("myProject"),
- resourcePerspectives, settings, fs, activeRules, componentDataCache);
+ resourcePerspectives, settings, fs, activeRules, componentDataCache, blockCache, mock(DuplicationCache.class));
}
@Test
diff --git a/sonar-batch/src/test/java/org/sonar/batch/scan/filesystem/InputPathCacheTest.java b/sonar-batch/src/test/java/org/sonar/batch/scan/filesystem/InputPathCacheTest.java
index f1bf43f37e3..03dbec79af8 100644
--- a/sonar-batch/src/test/java/org/sonar/batch/scan/filesystem/InputPathCacheTest.java
+++ b/sonar-batch/src/test/java/org/sonar/batch/scan/filesystem/InputPathCacheTest.java
@@ -24,6 +24,8 @@ import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TemporaryFolder;
+import org.sonar.api.batch.fs.InputFile.Status;
+import org.sonar.api.batch.fs.InputFile.Type;
import org.sonar.api.batch.fs.InputPath;
import org.sonar.api.batch.fs.internal.DefaultInputFile;
import org.sonar.api.batch.fs.internal.DeprecatedDefaultInputFile;
@@ -55,7 +57,18 @@ public class InputPathCacheTest {
InputPathCache cache = new InputPathCache(caches);
DefaultInputFile fooFile = new DefaultInputFile("src/main/java/Foo.java").setFile(temp.newFile("Foo.java"));
cache.put("struts", fooFile);
- cache.put("struts-core", new DeprecatedDefaultInputFile("src/main/java/Bar.java").setFile(temp.newFile("Bar.java")));
+ cache.put("struts-core", new DeprecatedDefaultInputFile("src/main/java/Bar.java")
+ .setBasedir(temp.newFolder())
+ .setDeprecatedKey("foo")
+ .setSourceDirAbsolutePath("foo")
+ .setPathRelativeToSourceDir("foo")
+ .setLanguage("bla")
+ .setType(Type.MAIN)
+ .setStatus(Status.ADDED)
+ .setHash("xyz")
+ .setLines(1)
+ .setKey("foo")
+ .setFile(temp.newFile("Bar.java")));
assertThat(cache.getFile("struts", "src/main/java/Foo.java").relativePath())
.isEqualTo("src/main/java/Foo.java");
@@ -75,5 +88,4 @@ public class InputPathCacheTest {
assertThat(cache.filesByModule("struts-core")).hasSize(1);
assertThat(cache.all()).hasSize(1);
}
-
}
diff --git a/sonar-core/src/main/java/org/sonar/core/component/db/ComponentMapper.java b/sonar-core/src/main/java/org/sonar/core/component/db/ComponentMapper.java
index 9d74af46428..8e925d67ca0 100644
--- a/sonar-core/src/main/java/org/sonar/core/component/db/ComponentMapper.java
+++ b/sonar-core/src/main/java/org/sonar/core/component/db/ComponentMapper.java
@@ -33,6 +33,13 @@ public interface ComponentMapper {
ComponentDto selectById(long id);
+ ComponentDto selectRootProjectByKey(String key);
+
+ ComponentDto selectParentModuleByKey(String key);
+
+ /**
+ * Return direct modules from a project/module
+ */
List<ComponentDto> findModulesByProject(@Param("projectKey") String projectKey);
long countById(long id);
diff --git a/sonar-core/src/main/java/org/sonar/core/properties/PropertiesDao.java b/sonar-core/src/main/java/org/sonar/core/properties/PropertiesDao.java
index 4357258a1b4..e7a13b39f26 100644
--- a/sonar-core/src/main/java/org/sonar/core/properties/PropertiesDao.java
+++ b/sonar-core/src/main/java/org/sonar/core/properties/PropertiesDao.java
@@ -96,7 +96,7 @@ public class PropertiesDao implements BatchComponent, ServerComponent, DaoCompon
}
public List<PropertyDto> selectProjectProperties(String resourceKey, SqlSession session) {
- return session.getMapper(PropertiesMapper.class).selectProjectProperties(resourceKey);
+ return session.getMapper(PropertiesMapper.class).selectProjectProperties(resourceKey);
}
public List<PropertyDto> selectProjectProperties(String resourceKey) {
diff --git a/sonar-core/src/main/java/org/sonar/core/source/SnapshotDataTypes.java b/sonar-core/src/main/java/org/sonar/core/source/SnapshotDataTypes.java
index 745b21b8c97..cb7ff46ff60 100644
--- a/sonar-core/src/main/java/org/sonar/core/source/SnapshotDataTypes.java
+++ b/sonar-core/src/main/java/org/sonar/core/source/SnapshotDataTypes.java
@@ -24,6 +24,7 @@ public interface SnapshotDataTypes {
String SYNTAX_HIGHLIGHTING = "highlight_syntax";
String SYMBOL_HIGHLIGHTING = "symbol";
+ String TOKEN = "token";
/**
* Key-values [relative path, hash] of all files. Stored on modules.
diff --git a/sonar-core/src/main/resources/org/sonar/core/component/db/ComponentMapper.xml b/sonar-core/src/main/resources/org/sonar/core/component/db/ComponentMapper.xml
index f0dc353a03a..b0fd329b070 100644
--- a/sonar-core/src/main/resources/org/sonar/core/component/db/ComponentMapper.xml
+++ b/sonar-core/src/main/resources/org/sonar/core/component/db/ComponentMapper.xml
@@ -44,11 +44,34 @@
</where>
</select>
+ <select id="selectRootProjectByKey" parameterType="String" resultType="Component">
+ SELECT rootProject.*
+ FROM projects p
+ INNER JOIN snapshots s ON s.project_id=p.id AND s.islast=${_true}
+ INNER JOIN projects rootProject ON rootProject.id=s.root_project_id
+ <where>
+ AND p.kee=#{componentKey}
+ </where>
+ </select>
+
+ <select id="selectParentModuleByKey" parameterType="String" resultType="Component">
+ SELECT <include refid="componentColumns"/>
+ FROM projects p
+ INNER JOIN snapshots s ON s.project_id=p.id AND s.islast=${_true}
+ INNER JOIN snapshots child_snapshots ON child_snapshots.parent_snapshot_id=s.id AND s.islast=${_true}
+ INNER JOIN projects child ON child.id=child_snapshots.project_id AND child.enabled=${_true} AND child.kee=#{key}
+ <where>
+ AND p.enabled=${_true}
+ AND p.scope='PRJ'
+ </where>
+ </select>
+
<select id="findModulesByProject" parameterType="String" resultType="Component">
SELECT <include refid="componentColumns"/>
FROM projects p
- INNER JOIN projects root ON root.id=p.root_id AND root.enabled=${_true} AND root.kee=#{projectKey}
INNER JOIN snapshots s ON s.project_id=p.id AND s.islast=${_true}
+ INNER JOIN snapshots parent_snapshots ON parent_snapshots.id=s.parent_snapshot_id AND s.islast=${_true}
+ INNER JOIN projects parent ON parent.id=parent_snapshots.project_id AND parent.enabled=${_true} AND parent.kee=#{projectKey}
<where>
AND p.enabled=${_true}
AND p.scope='PRJ'
diff --git a/sonar-core/src/main/resources/org/sonar/l10n/core.properties b/sonar-core/src/main/resources/org/sonar/l10n/core.properties
index 892226ed05c..c887b3526ec 100644
--- a/sonar-core/src/main/resources/org/sonar/l10n/core.properties
+++ b/sonar-core/src/main/resources/org/sonar/l10n/core.properties
@@ -1741,6 +1741,7 @@ coding_rules.deactivate_in_all_quality_profiles=Deactivate In All {0} Profiles
coding_rules.found=Found
coding_rules.inherits="{0}" inherits from "{1}"
coding_rules.key=Key:
+coding_rules.manual_rule=Manual Rule
coding_rules.new_search=New Search
coding_rules.no_results=No Coding Rules
coding_rules.no_tags=No tags
@@ -1798,6 +1799,7 @@ coding_rules.filters.template.is_not_template=Hide Templates
coding_rules.facets.languages=Languages
coding_rules.facets.tags=Tags
coding_rules.facets.repositories=Repositories
+coding_rules.facets.top=Top {0}
coding_rules.sort.creation_date=Creation Date
coding_rules.sort.name=Name
@@ -2536,10 +2538,10 @@ metric.new_technical_debt.name=Technical Debt on new code
metric.new_technical_debt.description=Technical Debt on new code
metric.sqale_rating.name=SQALE Rating
-metric.sqale_rating.description=Density of technical debt computed by dividing the technical debt by the estimated effort to develop from scratch an application.
+metric.sqale_rating.description=Rating of the technical debt ratio based on the SQALE Governance Model.
metric.sqale_debt_ratio.name=Technical Debt Ratio
-metric.sqale_debt_ratio.description=Ratio of the technical debt compared to what it would cost to develop the whole source code from scratch.
+metric.sqale_debt_ratio.description=Ratio of the actual technical debt compared to the estimated cost to develop the whole source code from scratch.
#------------------------------------------------------------------------------
diff --git a/sonar-deprecated/src/main/java/org/sonar/api/batch/BatchExtensionDictionnary.java b/sonar-deprecated/src/main/java/org/sonar/api/batch/BatchExtensionDictionnary.java
index ffd90b34de2..34c5d6bbdbd 100644
--- a/sonar-deprecated/src/main/java/org/sonar/api/batch/BatchExtensionDictionnary.java
+++ b/sonar-deprecated/src/main/java/org/sonar/api/batch/BatchExtensionDictionnary.java
@@ -19,9 +19,6 @@
*/
package org.sonar.api.batch;
-import org.sonar.api.batch.sensor.Sensor;
-import org.sonar.api.batch.sensor.internal.DefaultSensorDescriptor;
-
import com.google.common.base.Predicates;
import com.google.common.collect.Collections2;
import com.google.common.collect.Lists;
@@ -29,6 +26,8 @@ import org.apache.commons.lang.ClassUtils;
import org.sonar.api.BatchExtension;
import org.sonar.api.batch.maven.DependsUponMavenPlugin;
import org.sonar.api.batch.maven.MavenPluginHandler;
+import org.sonar.api.batch.sensor.Sensor;
+import org.sonar.api.batch.sensor.internal.DefaultSensorDescriptor;
import org.sonar.api.platform.ComponentContainer;
import org.sonar.api.resources.Project;
import org.sonar.api.utils.AnnotationUtils;
diff --git a/sonar-duplications/src/main/java/org/sonar/duplications/block/ByteArray.java b/sonar-duplications/src/main/java/org/sonar/duplications/block/ByteArray.java
index aab2802c27b..a3deacff154 100644
--- a/sonar-duplications/src/main/java/org/sonar/duplications/block/ByteArray.java
+++ b/sonar-duplications/src/main/java/org/sonar/duplications/block/ByteArray.java
@@ -60,7 +60,7 @@ public final class ByteArray {
(byte) (value >>> 24),
(byte) (value >>> 16),
(byte) (value >>> 8),
- (byte) value };
+ (byte) value};
}
public ByteArray(int value) {
@@ -68,7 +68,7 @@ public final class ByteArray {
(byte) (value >>> 24),
(byte) (value >>> 16),
(byte) (value >>> 8),
- (byte) value };
+ (byte) value};
}
public ByteArray(int[] intArray) {
@@ -79,6 +79,10 @@ public final class ByteArray {
this.bytes = bb.array();
}
+ public byte[] getBytes() {
+ return bytes;
+ }
+
public int[] toIntArray() {
// Pad the size to multiple of 4
int size = (bytes.length / 4) + (bytes.length % 4 == 0 ? 0 : 1);
diff --git a/sonar-duplications/src/main/java/org/sonar/duplications/block/FileBlocks.java b/sonar-duplications/src/main/java/org/sonar/duplications/block/FileBlocks.java
new file mode 100644
index 00000000000..7b43988efc2
--- /dev/null
+++ b/sonar-duplications/src/main/java/org/sonar/duplications/block/FileBlocks.java
@@ -0,0 +1,45 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.duplications.block;
+
+import java.util.List;
+
+/**
+ * Represents all blocks in a file.
+ */
+public final class FileBlocks {
+
+ private final String resourceId;
+ private final List<Block> blocks;
+
+ public FileBlocks(String resourceId, List<Block> blocks) {
+ this.resourceId = resourceId;
+ this.blocks = blocks;
+ }
+
+ public String resourceId() {
+ return resourceId;
+ }
+
+ public List<Block> blocks() {
+ return blocks;
+ }
+
+}
diff --git a/sonar-duplications/src/main/java/org/sonar/duplications/index/CloneGroup.java b/sonar-duplications/src/main/java/org/sonar/duplications/index/CloneGroup.java
index 7c9ad2a8278..01b7273da1c 100644
--- a/sonar-duplications/src/main/java/org/sonar/duplications/index/CloneGroup.java
+++ b/sonar-duplications/src/main/java/org/sonar/duplications/index/CloneGroup.java
@@ -20,8 +20,10 @@
package org.sonar.duplications.index;
import com.google.common.annotations.Beta;
+import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableList;
+import java.util.ArrayList;
import java.util.List;
/**
@@ -32,6 +34,7 @@ public class CloneGroup {
private final ClonePart originPart;
private final int cloneLength;
private final List<ClonePart> parts;
+ private int length;
/**
* Cache for hash code.
@@ -52,7 +55,7 @@ public class CloneGroup {
private ClonePart origin;
private int length;
private int lengthInUnits;
- private List<ClonePart> parts;
+ private List<ClonePart> parts = new ArrayList<ClonePart>();
public Builder setLength(int length) {
this.length = length;
@@ -69,6 +72,12 @@ public class CloneGroup {
return this;
}
+ public Builder addPart(ClonePart part) {
+ Preconditions.checkNotNull(part);
+ this.parts.add(part);
+ return this;
+ }
+
public Builder setLengthInUnits(int length) {
this.lengthInUnits = length;
return this;
@@ -90,8 +99,6 @@ public class CloneGroup {
return originPart;
}
- private int length;
-
/**
* Length of duplication measured in original units, e.g. for token-based detection - in tokens.
*
diff --git a/sonar-duplications/src/main/java/org/sonar/duplications/internal/pmd/PmdBlockChunker.java b/sonar-duplications/src/main/java/org/sonar/duplications/internal/pmd/PmdBlockChunker.java
index 1c7c0ee4437..b0ae419332a 100644
--- a/sonar-duplications/src/main/java/org/sonar/duplications/internal/pmd/PmdBlockChunker.java
+++ b/sonar-duplications/src/main/java/org/sonar/duplications/internal/pmd/PmdBlockChunker.java
@@ -23,7 +23,7 @@ import com.google.common.collect.Lists;
import org.sonar.duplications.block.Block;
import org.sonar.duplications.block.ByteArray;
-import java.util.Collections;
+import java.util.ArrayList;
import java.util.List;
/**
@@ -48,7 +48,10 @@ public class PmdBlockChunker {
this.power = pow;
}
- public List<Block> chunk(String resourceId, List<TokensLine> fragments) {
+ /**
+ * @return ArrayList as we need a serializable object
+ */
+ public ArrayList<Block> chunk(String resourceId, List<TokensLine> fragments) {
List<TokensLine> filtered = Lists.newArrayList();
int i = 0;
while (i < fragments.size()) {
@@ -66,10 +69,10 @@ public class PmdBlockChunker {
fragments = filtered;
if (fragments.size() < blockSize) {
- return Collections.emptyList();
+ return Lists.newArrayList();
}
TokensLine[] fragmentsArr = fragments.toArray(new TokensLine[fragments.size()]);
- List<Block> blocks = Lists.newArrayListWithCapacity(fragmentsArr.length - blockSize + 1);
+ ArrayList<Block> blocks = Lists.newArrayListWithCapacity(fragmentsArr.length - blockSize + 1);
long hash = 0;
int first = 0;
int last = 0;
@@ -84,11 +87,11 @@ public class PmdBlockChunker {
hash = hash * PRIME_BASE + lastFragment.getHashCode();
// create block
Block block = blockBuilder
- .setBlockHash(new ByteArray(hash))
- .setIndexInFile(first)
- .setLines(firstFragment.getStartLine(), lastFragment.getEndLine())
- .setUnit(firstFragment.getStartUnit(), lastFragment.getEndUnit())
- .build();
+ .setBlockHash(new ByteArray(hash))
+ .setIndexInFile(first)
+ .setLines(firstFragment.getStartLine(), lastFragment.getEndLine())
+ .setUnit(firstFragment.getStartUnit(), lastFragment.getEndUnit())
+ .build();
blocks.add(block);
// remove first statement from hash
hash -= power * firstFragment.getHashCode();
diff --git a/sonar-duplications/src/main/java/org/sonar/duplications/internal/pmd/TokenizerBridge.java b/sonar-duplications/src/main/java/org/sonar/duplications/internal/pmd/TokenizerBridge.java
index e8dcc4bad76..80fc8691717 100644
--- a/sonar-duplications/src/main/java/org/sonar/duplications/internal/pmd/TokenizerBridge.java
+++ b/sonar-duplications/src/main/java/org/sonar/duplications/internal/pmd/TokenizerBridge.java
@@ -69,7 +69,7 @@ public class TokenizerBridge {
* We expect that implementation of {@link Tokenizer} is correct:
* tokens ordered by occurrence in source code and last token is EOF.
*/
- private static List<TokensLine> convert(List<TokenEntry> tokens) {
+ public static List<TokensLine> convert(List<TokenEntry> tokens) {
ImmutableList.Builder<TokensLine> result = ImmutableList.builder();
StringBuilder sb = new StringBuilder();
int startLine = Integer.MIN_VALUE;
diff --git a/sonar-duplications/src/main/java/org/sonar/duplications/internal/pmd/TokensLine.java b/sonar-duplications/src/main/java/org/sonar/duplications/internal/pmd/TokensLine.java
index 9c07b381240..d8f5a304af9 100644
--- a/sonar-duplications/src/main/java/org/sonar/duplications/internal/pmd/TokensLine.java
+++ b/sonar-duplications/src/main/java/org/sonar/duplications/internal/pmd/TokensLine.java
@@ -25,7 +25,7 @@ import org.sonar.duplications.CodeFragment;
/**
* Immutable code fragment, which formed from tokens of one line.
*/
-class TokensLine implements CodeFragment {
+public class TokensLine implements CodeFragment {
private final String value;
@@ -35,7 +35,6 @@ class TokensLine implements CodeFragment {
private final int startUnit;
private final int endUnit;
-
public TokensLine(int startUnit, int endUnit, int startLine, String value) {
Preconditions.checkArgument(startLine > 0);
// TODO do we have requirements for length and hashcode ?
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/CoreProperties.java b/sonar-plugin-api/src/main/java/org/sonar/api/CoreProperties.java
index aad1de5ea03..b73e0a0ecee 100644
--- a/sonar-plugin-api/src/main/java/org/sonar/api/CoreProperties.java
+++ b/sonar-plugin-api/src/main/java/org/sonar/api/CoreProperties.java
@@ -344,10 +344,10 @@ public interface CoreProperties {
/**
* @since 2.11
*/
- String CPD_CROSS_RPOJECT = "sonar.cpd.cross_project";
+ String CPD_CROSS_PROJECT = "sonar.cpd.cross_project";
/**
- * @see #CPD_CROSS_RPOJECT
+ * @see #CPD_CROSS_PROJECT
* @since 2.11
*/
boolean CPD_CROSS_RPOJECT_DEFAULT_VALUE = false;
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/internal/DeprecatedDefaultInputFile.java b/sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/internal/DeprecatedDefaultInputFile.java
index 156e140fa57..7c82aa501d0 100644
--- a/sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/internal/DeprecatedDefaultInputFile.java
+++ b/sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/internal/DeprecatedDefaultInputFile.java
@@ -50,8 +50,9 @@ public class DeprecatedDefaultInputFile extends DefaultInputFile implements org.
return new File(basedir);
}
- public void setBasedir(File basedir) {
+ public DeprecatedDefaultInputFile setBasedir(File basedir) {
this.basedir = PathUtils.sanitize(basedir.getAbsolutePath());
+ return this;
}
/**
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/batch/sensor/SensorContext.java b/sonar-plugin-api/src/main/java/org/sonar/api/batch/sensor/SensorContext.java
index 787d2fddb3d..647cab3f3f9 100644
--- a/sonar-plugin-api/src/main/java/org/sonar/api/batch/sensor/SensorContext.java
+++ b/sonar-plugin-api/src/main/java/org/sonar/api/batch/sensor/SensorContext.java
@@ -24,6 +24,8 @@ import org.sonar.api.batch.fs.FileSystem;
import org.sonar.api.batch.fs.InputFile;
import org.sonar.api.batch.measure.Metric;
import org.sonar.api.batch.rule.ActiveRules;
+import org.sonar.api.batch.sensor.duplication.DuplicationBuilder;
+import org.sonar.api.batch.sensor.duplication.TokenBuilder;
import org.sonar.api.batch.sensor.highlighting.HighlightingBuilder;
import org.sonar.api.batch.sensor.issue.Issue;
import org.sonar.api.batch.sensor.issue.IssueBuilder;
@@ -126,4 +128,19 @@ public interface SensorContext {
*/
SymbolTableBuilder symbolTableBuilder(InputFile inputFile);
+ // ------------ DUPLICATIONS ------------
+
+ /**
+ * Builder to define tokens in a file. Tokens are used to compute duplication by the core.
+ * @since 4.5
+ */
+ TokenBuilder tokenBuilder(InputFile inputFile);
+
+ /**
+ * Builder to manually define duplications in a file. When duplication are manually computed then
+ * no need to use {@link #tokenBuilder(InputFile)}.
+ * @since 4.5
+ */
+ DuplicationBuilder duplicationBuilder(InputFile inputFile);
+
}
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/batch/sensor/duplication/DuplicationBuilder.java b/sonar-plugin-api/src/main/java/org/sonar/api/batch/sensor/duplication/DuplicationBuilder.java
new file mode 100644
index 00000000000..2f37220b1a3
--- /dev/null
+++ b/sonar-plugin-api/src/main/java/org/sonar/api/batch/sensor/duplication/DuplicationBuilder.java
@@ -0,0 +1,38 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.api.batch.sensor.duplication;
+
+import org.sonar.api.batch.fs.InputFile;
+
+/**
+ * This builder is used to declare duplications on files of the project.
+ * @since 4.5
+ */
+public interface DuplicationBuilder {
+
+ DuplicationBuilder originBlock(int startLine, int endLine);
+
+ DuplicationBuilder isDuplicatedBy(InputFile sameOrOtherFile, int startLine, int endLine);
+
+ /**
+ * Call this method only once when your are done with defining all duplicates of origin block.
+ */
+ void done();
+}
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/batch/sensor/duplication/TokenBuilder.java b/sonar-plugin-api/src/main/java/org/sonar/api/batch/sensor/duplication/TokenBuilder.java
new file mode 100644
index 00000000000..f4de9fffc8c
--- /dev/null
+++ b/sonar-plugin-api/src/main/java/org/sonar/api/batch/sensor/duplication/TokenBuilder.java
@@ -0,0 +1,40 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.api.batch.sensor.duplication;
+
+/**
+ * This builder is used to define token on files. Tokens are later used to compute duplication.
+ * Tokens should be declared in sequential order.
+ * @since 4.5
+ */
+public interface TokenBuilder {
+
+ /**
+ * Call this method to register a new token.
+ * @param line Line number of the token. Line starts at 1.
+ * @param image Text of the token.
+ */
+ TokenBuilder addToken(int line, String image);
+
+ /**
+ * Call this method only once when your are done with defining tokens of the file.
+ */
+ void done();
+}
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/batch/sensor/duplication/package-info.java b/sonar-plugin-api/src/main/java/org/sonar/api/batch/sensor/duplication/package-info.java
new file mode 100644
index 00000000000..4973316830a
--- /dev/null
+++ b/sonar-plugin-api/src/main/java/org/sonar/api/batch/sensor/duplication/package-info.java
@@ -0,0 +1,21 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+@javax.annotation.ParametersAreNonnullByDefault
+package org.sonar.api.batch.sensor.duplication;
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/measures/Measure.java b/sonar-plugin-api/src/main/java/org/sonar/api/measures/Measure.java
index 7a86f7b145c..870a8519271 100644
--- a/sonar-plugin-api/src/main/java/org/sonar/api/measures/Measure.java
+++ b/sonar-plugin-api/src/main/java/org/sonar/api/measures/Measure.java
@@ -343,6 +343,7 @@ public class Measure<G extends Serializable> implements Serializable {
/**
* @return the data field of the measure
*/
+ @CheckForNull
public String getData() {
return data;
}