From b461d36f8684a8583c98e04ab9561bb3f8f42a22 Mon Sep 17 00:00:00 2001 From: Elia Bellavista Date: Wed, 22 May 2024 14:42:47 +0200 Subject: [PATCH] aggiungo plugin mancanti da ckan 2.6. form danno ancora errore 400 - bad request --- .env | 3 +- .gitignore | 1 + .../templates/package/search.html | 39 +- .../templates/snippets/facet_list.html | 187 +- .../templates2.6/group/activity_stream.html | 15 - .../templates2.6/group/changes.html | 64 - .../templates2.6/group/read_base.html | 36 +- .../snippets/group_form.html | 0 .../snippets/group_item.html | 0 .../snippets/group_list.html | 0 .../snippets/group_list_simple.html | 0 .../snippets/group_tree.html | 0 .../group/snippets/item_group.html | 10 - .../templates2.6/group2.6/read_base.html | 36 - .../ckanext/multilingual/__init__.py | 0 .../ckanext/multilingual/plugin.py | 413 + .../ckanext/multilingual/solr/dutch_stop.txt | 117 + .../multilingual/solr/english_stop.txt | 317 + .../ckanext/multilingual/solr/fr_elision.txt | 8 + .../ckanext/multilingual/solr/french_stop.txt | 183 + .../ckanext/multilingual/solr/german_stop.txt | 292 + .../multilingual/solr/greek_stopwords.txt | 76 + .../multilingual/solr/italian_stop.txt | 301 + .../ckanext/multilingual/solr/polish_stop.txt | 186 + .../multilingual/solr/portuguese_stop.txt | 251 + .../multilingual/solr/romanian_stop.txt | 233 + .../ckanext/multilingual/solr/schema.xml | 486 + .../multilingual/solr/spanish_stop.txt | 354 + .../tests/test_multilingual_plugin.py | 239 + .../ckanext/reclineview/__init__.py | 0 .../reclineview/config_declaration.yaml | 14 + .../ckanext/reclineview/plugin.py | 306 + .../ckanext/reclineview/tests/__init__.py | 0 .../ckanext/reclineview/tests/test_view.py | 152 + .../reclineview/theme/public/css/recline.css | 380 + .../theme/public/css/recline.min.css | 1 + .../theme/public/img/ajaxload-circle.gif | Bin 0 -> 4176 bytes .../reclineview/theme/public/recline_view.js | 247 + .../theme/public/recline_view.min.js | 15 + .../reclineview/theme/public/resource.config | 15 + .../public/vendor/backbone/1.0.0/backbone.js | 1571 +++ .../bootstrap/3.2.0/css/bootstrap-theme.css | 442 + .../vendor/bootstrap/3.2.0/css/bootstrap.css | 6203 ++++++++++ .../fonts/glyphicons-halflings-regular.eot | Bin 0 -> 20335 bytes .../fonts/glyphicons-halflings-regular.svg | 229 + .../fonts/glyphicons-halflings-regular.ttf | Bin 0 -> 41280 bytes .../fonts/glyphicons-halflings-regular.woff | Bin 0 -> 23320 bytes .../vendor/bootstrap/3.2.0/js/bootstrap.js | 2114 ++++ .../theme/public/vendor/ckan.js/ckan.js | 262 + .../theme/public/vendor/flot/excanvas.js | 1428 +++ .../theme/public/vendor/flot/excanvas.min.js | 1 + .../theme/public/vendor/flot/jquery.flot.js | 3061 +++++ .../public/vendor/flot/jquery.flot.time.js | 431 + .../theme/public/vendor/flotr2/flotr2.js | 6128 ++++++++++ .../theme/public/vendor/flotr2/flotr2.min.js | 489 + .../public/vendor/jquery/1.7.1/jquery.js | 9266 ++++++++++++++ .../public/vendor/jquery/1.7.1/jquery.min.js | 4 + .../theme/public/vendor/json/json2.js | 486 + .../theme/public/vendor/json/json2.min.js | 25 + .../MarkerCluster.Default.css | 60 + .../leaflet.markercluster/MarkerCluster.css | 6 + .../leaflet.markercluster-src.js | 2163 ++++ .../leaflet.markercluster.js | 6 + .../vendor/leaflet/0.7.7/images/layers-2x.png | Bin 0 -> 1585 bytes .../vendor/leaflet/0.7.7/images/layers.png | Bin 0 -> 913 bytes .../leaflet/0.7.7/images/marker-icon-2x.png | Bin 0 -> 4032 bytes .../leaflet/0.7.7/images/marker-icon.png | Bin 0 -> 1747 bytes .../leaflet/0.7.7/images/marker-shadow.png | Bin 0 -> 681 bytes .../vendor/leaflet/0.7.7/leaflet-src.js | 9168 ++++++++++++++ .../public/vendor/leaflet/0.7.7/leaflet.css | 479 + .../public/vendor/leaflet/0.7.7/leaflet.js | 9 + .../public/vendor/moment/2.0.0/moment.js | 1400 +++ .../vendor/mustache/0.5.0-dev/mustache.js | 536 + .../vendor/mustache/0.5.0-dev/mustache.min.js | 36 + .../theme/public/vendor/recline/flot.css | 26 + .../theme/public/vendor/recline/map.css | 28 + .../theme/public/vendor/recline/recline.css | 644 + .../public/vendor/recline/recline.dataset.js | 896 ++ .../theme/public/vendor/recline/recline.js | 4463 +++++++ .../theme/public/vendor/recline/slickgrid.css | 188 + .../vendor/showdown/20120615/showdown.js | 1341 +++ .../vendor/showdown/20120615/showdown.min.js | 48 + .../vendor/slickgrid/2.2/MIT-LICENSE.txt | 20 + .../public/vendor/slickgrid/2.2/README.md | 25 + .../2.2/controls/slick.columnpicker.css | 31 + .../2.2/controls/slick.columnpicker.js | 152 + .../slickgrid/2.2/controls/slick.pager.css | 41 + .../slickgrid/2.2/controls/slick.pager.js | 154 + .../images/ui-bg_flat_0_aaaaaa_40x100.png | Bin 0 -> 86 bytes .../images/ui-bg_flat_75_ffffff_40x100.png | Bin 0 -> 74 bytes .../images/ui-bg_glass_55_fbf9ee_1x400.png | Bin 0 -> 111 bytes .../images/ui-bg_glass_65_ffffff_1x400.png | Bin 0 -> 90 bytes .../images/ui-bg_glass_75_dadada_1x400.png | Bin 0 -> 102 bytes .../images/ui-bg_glass_75_e6e6e6_1x400.png | Bin 0 -> 102 bytes .../images/ui-bg_glass_95_fef1ec_1x400.png | Bin 0 -> 115 bytes .../ui-bg_highlight-soft_75_cccccc_1x100.png | Bin 0 -> 86 bytes .../images/ui-icons_222222_256x240.png | Bin 0 -> 3687 bytes .../images/ui-icons_2e83ff_256x240.png | Bin 0 -> 3687 bytes .../images/ui-icons_454545_256x240.png | Bin 0 -> 3687 bytes .../images/ui-icons_888888_256x240.png | Bin 0 -> 3687 bytes .../images/ui-icons_cd0a0a_256x240.png | Bin 0 -> 3687 bytes .../smoothness/jquery-ui-1.8.16.custom.css | 409 + .../vendor/slickgrid/2.2/images/actions.gif | Bin 0 -> 170 bytes .../2.2/images/ajax-loader-small.gif | Bin 0 -> 1849 bytes .../slickgrid/2.2/images/arrow_redo.png | Bin 0 -> 550 bytes .../2.2/images/arrow_right_peppermint.png | Bin 0 -> 126 bytes .../2.2/images/arrow_right_spearmint.png | Bin 0 -> 126 bytes .../slickgrid/2.2/images/arrow_undo.png | Bin 0 -> 555 bytes .../slickgrid/2.2/images/bullet_blue.png | Bin 0 -> 231 bytes .../slickgrid/2.2/images/bullet_star.png | Bin 0 -> 273 bytes .../2.2/images/bullet_toggle_minus.png | Bin 0 -> 154 bytes .../2.2/images/bullet_toggle_plus.png | Bin 0 -> 156 bytes .../vendor/slickgrid/2.2/images/calendar.gif | Bin 0 -> 1035 bytes .../vendor/slickgrid/2.2/images/collapse.gif | Bin 0 -> 846 bytes .../slickgrid/2.2/images/comment_yellow.gif | Bin 0 -> 257 bytes .../vendor/slickgrid/2.2/images/down.gif | Bin 0 -> 59 bytes .../slickgrid/2.2/images/drag-handle.png | Bin 0 -> 98 bytes .../slickgrid/2.2/images/editor-helper-bg.gif | Bin 0 -> 1164 bytes .../vendor/slickgrid/2.2/images/expand.gif | Bin 0 -> 851 bytes .../vendor/slickgrid/2.2/images/header-bg.gif | Bin 0 -> 872 bytes .../2.2/images/header-columns-bg.gif | Bin 0 -> 836 bytes .../2.2/images/header-columns-over-bg.gif | Bin 0 -> 823 bytes .../vendor/slickgrid/2.2/images/help.png | Bin 0 -> 328 bytes .../vendor/slickgrid/2.2/images/info.gif | Bin 0 -> 80 bytes .../vendor/slickgrid/2.2/images/listview.gif | Bin 0 -> 2380 bytes .../vendor/slickgrid/2.2/images/pencil.gif | Bin 0 -> 914 bytes .../slickgrid/2.2/images/row-over-bg.gif | Bin 0 -> 823 bytes .../vendor/slickgrid/2.2/images/sort-asc.gif | Bin 0 -> 830 bytes .../vendor/slickgrid/2.2/images/sort-asc.png | Bin 0 -> 104 bytes .../vendor/slickgrid/2.2/images/sort-desc.gif | Bin 0 -> 833 bytes .../vendor/slickgrid/2.2/images/sort-desc.png | Bin 0 -> 106 bytes .../vendor/slickgrid/2.2/images/stripes.png | Bin 0 -> 94 bytes .../vendor/slickgrid/2.2/images/tag_red.png | Bin 0 -> 529 bytes .../vendor/slickgrid/2.2/images/tick.png | Bin 0 -> 465 bytes .../slickgrid/2.2/images/user_identity.gif | Bin 0 -> 905 bytes .../2.2/images/user_identity_plus.gif | Bin 0 -> 546 bytes .../vendor/slickgrid/2.2/jquery-1.7.min.js | 4 + .../slickgrid/2.2/jquery-ui-1.8.16.custom.js | 611 + .../slickgrid/2.2/jquery.event.drag-2.2.js | 402 + .../slickgrid/2.2/jquery.event.drop-2.2.js | 302 + .../2.2/plugins/slick.autotooltips.js | 83 + .../2.2/plugins/slick.cellcopymanager.js | 86 + .../2.2/plugins/slick.cellrangedecorator.js | 66 + .../2.2/plugins/slick.cellrangeselector.js | 113 + .../2.2/plugins/slick.cellselectionmodel.js | 154 + .../2.2/plugins/slick.checkboxselectcolumn.js | 153 + .../2.2/plugins/slick.headerbuttons.css | 39 + .../2.2/plugins/slick.headerbuttons.js | 177 + .../2.2/plugins/slick.headermenu.css | 59 + .../slickgrid/2.2/plugins/slick.headermenu.js | 275 + .../2.2/plugins/slick.rowmovemanager.js | 138 + .../2.2/plugins/slick.rowselectionmodel.js | 187 + .../slickgrid/2.2/slick-default-theme.css | 118 + .../public/vendor/slickgrid/2.2/slick.core.js | 467 + .../vendor/slickgrid/2.2/slick.dataview.js | 1126 ++ .../vendor/slickgrid/2.2/slick.editors.js | 512 + .../vendor/slickgrid/2.2/slick.formatters.js | 59 + .../vendor/slickgrid/2.2/slick.grid.css | 157 + .../public/vendor/slickgrid/2.2/slick.grid.js | 3422 ++++++ .../2.2/slick.groupitemmetadataprovider.js | 158 + .../vendor/slickgrid/2.2/slick.remotemodel.js | 173 + .../theme/public/vendor/timeline/LICENSE | 365 + .../theme/public/vendor/timeline/README | 1 + .../public/vendor/timeline/css/loading.gif | Bin 0 -> 6909 bytes .../public/vendor/timeline/css/timeline.css | 284 + .../public/vendor/timeline/css/timeline.png | Bin 0 -> 14048 bytes .../vendor/timeline/css/timeline@2x.png | Bin 0 -> 36430 bytes .../public/vendor/timeline/js/timeline.js | 10015 ++++++++++++++++ .../0.4.0/underscore.deferred.js | 445 + .../0.4.0/underscore.deferred.min.js | 17 + .../vendor/underscore/1.4.4/underscore.js | 1227 ++ .../reclineview/theme/public/webassets.yml | 47 + .../theme/public/widget.recordcount.js | 34 + .../theme/public/widget.recordcount.min.js | 3 + .../theme/templates/recline_graph_form.html | 8 + .../theme/templates/recline_map_form.html | 11 + .../theme/templates/recline_view.html | 24 + .../ckanext/stats/__init__.py | 3 + .../ckanext/stats/blueprint.py | 55 + .../ckanext/stats/plugin.py | 24 + .../ckanext/stats/public/.gitignore | 2 + .../ckanext/stats/public/__init__.py | 9 + .../ckanext/stats/public/ckanext/__init__.py | 9 + .../stats/public/ckanext/stats/__init__.py | 9 + .../stats/public/ckanext/stats/css/stats.css | 16 + .../ckanext/stats/javascript/modules/plot.js | 209 + .../stats/javascript/modules/stats-nav.js | 39 + .../public/ckanext/stats/resource.config | 12 + .../ckanext/stats/test/fixtures/table.html | 30 + .../public/ckanext/stats/test/index.html | 59 + .../stats/test/spec/modules/plot.spec.js | 136 + .../stats/test/spec/modules/stats-nav.spec.js | 44 + .../public/ckanext/stats/vendor/excanvas.js | 1427 +++ .../ckanext/stats/vendor/jquery.flot.js | 2599 ++++ .../stats/public/ckanext/stats/webassets.yml | 16 + .../ckanext/stats/stats.py | 405 + .../stats/templates/ckanext/stats/index.html | 178 + .../ckanext/stats/tests/__init__.py | 0 .../ckanext/stats/tests/test_stats_lib.py | 207 + .../ckanext/stats/tests/test_stats_plugin.py | 11 + .../ckanext/videoview/__init__.py | 0 .../ckanext/videoview/plugin.py | 52 + .../ckanext/videoview/tests/__init__.py | 0 .../ckanext/videoview/tests/test_view.py | 29 + .../videoview/theme/templates/video_form.html | 4 + .../videoview/theme/templates/video_view.html | 12 + .../SOURCES.txt | 34 +- 207 files changed, 86356 insertions(+), 242 deletions(-) delete mode 100644 src/ckanext-d4science_theme/ckanext/d4science_theme/templates2.6/group/activity_stream.html delete mode 100644 src/ckanext-d4science_theme/ckanext/d4science_theme/templates2.6/group/changes.html rename src/ckanext-d4science_theme/ckanext/d4science_theme/templates2.6/{group2.6 => group}/snippets/group_form.html (100%) rename src/ckanext-d4science_theme/ckanext/d4science_theme/templates2.6/{group2.6 => group}/snippets/group_item.html (100%) rename src/ckanext-d4science_theme/ckanext/d4science_theme/templates2.6/{group2.6 => group}/snippets/group_list.html (100%) rename src/ckanext-d4science_theme/ckanext/d4science_theme/templates2.6/{group2.6 => group}/snippets/group_list_simple.html (100%) rename src/ckanext-d4science_theme/ckanext/d4science_theme/templates2.6/{group2.6 => group}/snippets/group_tree.html (100%) delete mode 100644 src/ckanext-d4science_theme/ckanext/d4science_theme/templates2.6/group/snippets/item_group.html delete mode 100644 src/ckanext-d4science_theme/ckanext/d4science_theme/templates2.6/group2.6/read_base.html create mode 100644 src/ckanext-d4science_theme/ckanext/multilingual/__init__.py create mode 100644 src/ckanext-d4science_theme/ckanext/multilingual/plugin.py create mode 100644 src/ckanext-d4science_theme/ckanext/multilingual/solr/dutch_stop.txt create mode 100644 src/ckanext-d4science_theme/ckanext/multilingual/solr/english_stop.txt create mode 100644 src/ckanext-d4science_theme/ckanext/multilingual/solr/fr_elision.txt create mode 100644 src/ckanext-d4science_theme/ckanext/multilingual/solr/french_stop.txt create mode 100644 src/ckanext-d4science_theme/ckanext/multilingual/solr/german_stop.txt create mode 100644 src/ckanext-d4science_theme/ckanext/multilingual/solr/greek_stopwords.txt create mode 100644 src/ckanext-d4science_theme/ckanext/multilingual/solr/italian_stop.txt create mode 100644 src/ckanext-d4science_theme/ckanext/multilingual/solr/polish_stop.txt create mode 100644 src/ckanext-d4science_theme/ckanext/multilingual/solr/portuguese_stop.txt create mode 100644 src/ckanext-d4science_theme/ckanext/multilingual/solr/romanian_stop.txt create mode 100644 src/ckanext-d4science_theme/ckanext/multilingual/solr/schema.xml create mode 100644 src/ckanext-d4science_theme/ckanext/multilingual/solr/spanish_stop.txt create mode 100644 src/ckanext-d4science_theme/ckanext/multilingual/tests/test_multilingual_plugin.py create mode 100644 src/ckanext-d4science_theme/ckanext/reclineview/__init__.py create mode 100644 src/ckanext-d4science_theme/ckanext/reclineview/config_declaration.yaml create mode 100644 src/ckanext-d4science_theme/ckanext/reclineview/plugin.py create mode 100644 src/ckanext-d4science_theme/ckanext/reclineview/tests/__init__.py create mode 100644 src/ckanext-d4science_theme/ckanext/reclineview/tests/test_view.py create mode 100644 src/ckanext-d4science_theme/ckanext/reclineview/theme/public/css/recline.css create mode 100644 src/ckanext-d4science_theme/ckanext/reclineview/theme/public/css/recline.min.css create mode 100644 src/ckanext-d4science_theme/ckanext/reclineview/theme/public/img/ajaxload-circle.gif create mode 100644 src/ckanext-d4science_theme/ckanext/reclineview/theme/public/recline_view.js create mode 100644 src/ckanext-d4science_theme/ckanext/reclineview/theme/public/recline_view.min.js create mode 100644 src/ckanext-d4science_theme/ckanext/reclineview/theme/public/resource.config create mode 100644 src/ckanext-d4science_theme/ckanext/reclineview/theme/public/vendor/backbone/1.0.0/backbone.js create mode 100644 src/ckanext-d4science_theme/ckanext/reclineview/theme/public/vendor/bootstrap/3.2.0/css/bootstrap-theme.css create mode 100644 src/ckanext-d4science_theme/ckanext/reclineview/theme/public/vendor/bootstrap/3.2.0/css/bootstrap.css create mode 100644 src/ckanext-d4science_theme/ckanext/reclineview/theme/public/vendor/bootstrap/3.2.0/fonts/glyphicons-halflings-regular.eot create mode 100644 src/ckanext-d4science_theme/ckanext/reclineview/theme/public/vendor/bootstrap/3.2.0/fonts/glyphicons-halflings-regular.svg create mode 100644 src/ckanext-d4science_theme/ckanext/reclineview/theme/public/vendor/bootstrap/3.2.0/fonts/glyphicons-halflings-regular.ttf create mode 100644 src/ckanext-d4science_theme/ckanext/reclineview/theme/public/vendor/bootstrap/3.2.0/fonts/glyphicons-halflings-regular.woff create mode 100644 src/ckanext-d4science_theme/ckanext/reclineview/theme/public/vendor/bootstrap/3.2.0/js/bootstrap.js create mode 100644 src/ckanext-d4science_theme/ckanext/reclineview/theme/public/vendor/ckan.js/ckan.js create mode 100644 src/ckanext-d4science_theme/ckanext/reclineview/theme/public/vendor/flot/excanvas.js create mode 100644 src/ckanext-d4science_theme/ckanext/reclineview/theme/public/vendor/flot/excanvas.min.js create mode 100644 src/ckanext-d4science_theme/ckanext/reclineview/theme/public/vendor/flot/jquery.flot.js create mode 100644 src/ckanext-d4science_theme/ckanext/reclineview/theme/public/vendor/flot/jquery.flot.time.js create mode 100644 src/ckanext-d4science_theme/ckanext/reclineview/theme/public/vendor/flotr2/flotr2.js create mode 100644 src/ckanext-d4science_theme/ckanext/reclineview/theme/public/vendor/flotr2/flotr2.min.js create mode 100644 src/ckanext-d4science_theme/ckanext/reclineview/theme/public/vendor/jquery/1.7.1/jquery.js create mode 100644 src/ckanext-d4science_theme/ckanext/reclineview/theme/public/vendor/jquery/1.7.1/jquery.min.js create mode 100644 src/ckanext-d4science_theme/ckanext/reclineview/theme/public/vendor/json/json2.js create mode 100644 src/ckanext-d4science_theme/ckanext/reclineview/theme/public/vendor/json/json2.min.js create mode 100644 src/ckanext-d4science_theme/ckanext/reclineview/theme/public/vendor/leaflet.markercluster/MarkerCluster.Default.css create mode 100644 src/ckanext-d4science_theme/ckanext/reclineview/theme/public/vendor/leaflet.markercluster/MarkerCluster.css create mode 100644 src/ckanext-d4science_theme/ckanext/reclineview/theme/public/vendor/leaflet.markercluster/leaflet.markercluster-src.js create mode 100644 src/ckanext-d4science_theme/ckanext/reclineview/theme/public/vendor/leaflet.markercluster/leaflet.markercluster.js create mode 100644 src/ckanext-d4science_theme/ckanext/reclineview/theme/public/vendor/leaflet/0.7.7/images/layers-2x.png create mode 100644 src/ckanext-d4science_theme/ckanext/reclineview/theme/public/vendor/leaflet/0.7.7/images/layers.png create mode 100644 src/ckanext-d4science_theme/ckanext/reclineview/theme/public/vendor/leaflet/0.7.7/images/marker-icon-2x.png create mode 100644 src/ckanext-d4science_theme/ckanext/reclineview/theme/public/vendor/leaflet/0.7.7/images/marker-icon.png create mode 100644 src/ckanext-d4science_theme/ckanext/reclineview/theme/public/vendor/leaflet/0.7.7/images/marker-shadow.png create mode 100644 src/ckanext-d4science_theme/ckanext/reclineview/theme/public/vendor/leaflet/0.7.7/leaflet-src.js create mode 100644 src/ckanext-d4science_theme/ckanext/reclineview/theme/public/vendor/leaflet/0.7.7/leaflet.css create mode 100644 src/ckanext-d4science_theme/ckanext/reclineview/theme/public/vendor/leaflet/0.7.7/leaflet.js create mode 100644 src/ckanext-d4science_theme/ckanext/reclineview/theme/public/vendor/moment/2.0.0/moment.js create mode 100644 src/ckanext-d4science_theme/ckanext/reclineview/theme/public/vendor/mustache/0.5.0-dev/mustache.js create mode 100644 src/ckanext-d4science_theme/ckanext/reclineview/theme/public/vendor/mustache/0.5.0-dev/mustache.min.js create mode 100644 src/ckanext-d4science_theme/ckanext/reclineview/theme/public/vendor/recline/flot.css create mode 100644 src/ckanext-d4science_theme/ckanext/reclineview/theme/public/vendor/recline/map.css create mode 100644 src/ckanext-d4science_theme/ckanext/reclineview/theme/public/vendor/recline/recline.css create mode 100644 src/ckanext-d4science_theme/ckanext/reclineview/theme/public/vendor/recline/recline.dataset.js create mode 100644 src/ckanext-d4science_theme/ckanext/reclineview/theme/public/vendor/recline/recline.js create mode 100644 src/ckanext-d4science_theme/ckanext/reclineview/theme/public/vendor/recline/slickgrid.css create mode 100644 src/ckanext-d4science_theme/ckanext/reclineview/theme/public/vendor/showdown/20120615/showdown.js create mode 100644 src/ckanext-d4science_theme/ckanext/reclineview/theme/public/vendor/showdown/20120615/showdown.min.js create mode 100644 src/ckanext-d4science_theme/ckanext/reclineview/theme/public/vendor/slickgrid/2.2/MIT-LICENSE.txt create mode 100644 src/ckanext-d4science_theme/ckanext/reclineview/theme/public/vendor/slickgrid/2.2/README.md create mode 100644 src/ckanext-d4science_theme/ckanext/reclineview/theme/public/vendor/slickgrid/2.2/controls/slick.columnpicker.css create mode 100644 src/ckanext-d4science_theme/ckanext/reclineview/theme/public/vendor/slickgrid/2.2/controls/slick.columnpicker.js create mode 100644 src/ckanext-d4science_theme/ckanext/reclineview/theme/public/vendor/slickgrid/2.2/controls/slick.pager.css create mode 100644 src/ckanext-d4science_theme/ckanext/reclineview/theme/public/vendor/slickgrid/2.2/controls/slick.pager.js create mode 100644 src/ckanext-d4science_theme/ckanext/reclineview/theme/public/vendor/slickgrid/2.2/css/smoothness/images/ui-bg_flat_0_aaaaaa_40x100.png create mode 100644 src/ckanext-d4science_theme/ckanext/reclineview/theme/public/vendor/slickgrid/2.2/css/smoothness/images/ui-bg_flat_75_ffffff_40x100.png create mode 100644 src/ckanext-d4science_theme/ckanext/reclineview/theme/public/vendor/slickgrid/2.2/css/smoothness/images/ui-bg_glass_55_fbf9ee_1x400.png create mode 100644 src/ckanext-d4science_theme/ckanext/reclineview/theme/public/vendor/slickgrid/2.2/css/smoothness/images/ui-bg_glass_65_ffffff_1x400.png create mode 100644 src/ckanext-d4science_theme/ckanext/reclineview/theme/public/vendor/slickgrid/2.2/css/smoothness/images/ui-bg_glass_75_dadada_1x400.png create mode 100644 src/ckanext-d4science_theme/ckanext/reclineview/theme/public/vendor/slickgrid/2.2/css/smoothness/images/ui-bg_glass_75_e6e6e6_1x400.png create mode 100644 src/ckanext-d4science_theme/ckanext/reclineview/theme/public/vendor/slickgrid/2.2/css/smoothness/images/ui-bg_glass_95_fef1ec_1x400.png create mode 100644 src/ckanext-d4science_theme/ckanext/reclineview/theme/public/vendor/slickgrid/2.2/css/smoothness/images/ui-bg_highlight-soft_75_cccccc_1x100.png create mode 100644 src/ckanext-d4science_theme/ckanext/reclineview/theme/public/vendor/slickgrid/2.2/css/smoothness/images/ui-icons_222222_256x240.png create mode 100644 src/ckanext-d4science_theme/ckanext/reclineview/theme/public/vendor/slickgrid/2.2/css/smoothness/images/ui-icons_2e83ff_256x240.png create mode 100644 src/ckanext-d4science_theme/ckanext/reclineview/theme/public/vendor/slickgrid/2.2/css/smoothness/images/ui-icons_454545_256x240.png create mode 100644 src/ckanext-d4science_theme/ckanext/reclineview/theme/public/vendor/slickgrid/2.2/css/smoothness/images/ui-icons_888888_256x240.png create mode 100644 src/ckanext-d4science_theme/ckanext/reclineview/theme/public/vendor/slickgrid/2.2/css/smoothness/images/ui-icons_cd0a0a_256x240.png create mode 100644 src/ckanext-d4science_theme/ckanext/reclineview/theme/public/vendor/slickgrid/2.2/css/smoothness/jquery-ui-1.8.16.custom.css create mode 100644 src/ckanext-d4science_theme/ckanext/reclineview/theme/public/vendor/slickgrid/2.2/images/actions.gif create mode 100644 src/ckanext-d4science_theme/ckanext/reclineview/theme/public/vendor/slickgrid/2.2/images/ajax-loader-small.gif create mode 100644 src/ckanext-d4science_theme/ckanext/reclineview/theme/public/vendor/slickgrid/2.2/images/arrow_redo.png create mode 100644 src/ckanext-d4science_theme/ckanext/reclineview/theme/public/vendor/slickgrid/2.2/images/arrow_right_peppermint.png create mode 100644 src/ckanext-d4science_theme/ckanext/reclineview/theme/public/vendor/slickgrid/2.2/images/arrow_right_spearmint.png create mode 100644 src/ckanext-d4science_theme/ckanext/reclineview/theme/public/vendor/slickgrid/2.2/images/arrow_undo.png create mode 100644 src/ckanext-d4science_theme/ckanext/reclineview/theme/public/vendor/slickgrid/2.2/images/bullet_blue.png create mode 100644 src/ckanext-d4science_theme/ckanext/reclineview/theme/public/vendor/slickgrid/2.2/images/bullet_star.png create mode 100644 src/ckanext-d4science_theme/ckanext/reclineview/theme/public/vendor/slickgrid/2.2/images/bullet_toggle_minus.png create mode 100644 src/ckanext-d4science_theme/ckanext/reclineview/theme/public/vendor/slickgrid/2.2/images/bullet_toggle_plus.png create mode 100644 src/ckanext-d4science_theme/ckanext/reclineview/theme/public/vendor/slickgrid/2.2/images/calendar.gif create mode 100644 src/ckanext-d4science_theme/ckanext/reclineview/theme/public/vendor/slickgrid/2.2/images/collapse.gif create mode 100644 src/ckanext-d4science_theme/ckanext/reclineview/theme/public/vendor/slickgrid/2.2/images/comment_yellow.gif create mode 100644 src/ckanext-d4science_theme/ckanext/reclineview/theme/public/vendor/slickgrid/2.2/images/down.gif create mode 100644 src/ckanext-d4science_theme/ckanext/reclineview/theme/public/vendor/slickgrid/2.2/images/drag-handle.png create mode 100644 src/ckanext-d4science_theme/ckanext/reclineview/theme/public/vendor/slickgrid/2.2/images/editor-helper-bg.gif create mode 100644 src/ckanext-d4science_theme/ckanext/reclineview/theme/public/vendor/slickgrid/2.2/images/expand.gif create mode 100644 src/ckanext-d4science_theme/ckanext/reclineview/theme/public/vendor/slickgrid/2.2/images/header-bg.gif create mode 100644 src/ckanext-d4science_theme/ckanext/reclineview/theme/public/vendor/slickgrid/2.2/images/header-columns-bg.gif create mode 100644 src/ckanext-d4science_theme/ckanext/reclineview/theme/public/vendor/slickgrid/2.2/images/header-columns-over-bg.gif create mode 100644 src/ckanext-d4science_theme/ckanext/reclineview/theme/public/vendor/slickgrid/2.2/images/help.png create mode 100644 src/ckanext-d4science_theme/ckanext/reclineview/theme/public/vendor/slickgrid/2.2/images/info.gif create mode 100644 src/ckanext-d4science_theme/ckanext/reclineview/theme/public/vendor/slickgrid/2.2/images/listview.gif create mode 100644 src/ckanext-d4science_theme/ckanext/reclineview/theme/public/vendor/slickgrid/2.2/images/pencil.gif create mode 100644 src/ckanext-d4science_theme/ckanext/reclineview/theme/public/vendor/slickgrid/2.2/images/row-over-bg.gif create mode 100644 src/ckanext-d4science_theme/ckanext/reclineview/theme/public/vendor/slickgrid/2.2/images/sort-asc.gif create mode 100644 src/ckanext-d4science_theme/ckanext/reclineview/theme/public/vendor/slickgrid/2.2/images/sort-asc.png create mode 100644 src/ckanext-d4science_theme/ckanext/reclineview/theme/public/vendor/slickgrid/2.2/images/sort-desc.gif create mode 100644 src/ckanext-d4science_theme/ckanext/reclineview/theme/public/vendor/slickgrid/2.2/images/sort-desc.png create mode 100644 src/ckanext-d4science_theme/ckanext/reclineview/theme/public/vendor/slickgrid/2.2/images/stripes.png create mode 100644 src/ckanext-d4science_theme/ckanext/reclineview/theme/public/vendor/slickgrid/2.2/images/tag_red.png create mode 100644 src/ckanext-d4science_theme/ckanext/reclineview/theme/public/vendor/slickgrid/2.2/images/tick.png create mode 100644 src/ckanext-d4science_theme/ckanext/reclineview/theme/public/vendor/slickgrid/2.2/images/user_identity.gif create mode 100644 src/ckanext-d4science_theme/ckanext/reclineview/theme/public/vendor/slickgrid/2.2/images/user_identity_plus.gif create mode 100644 src/ckanext-d4science_theme/ckanext/reclineview/theme/public/vendor/slickgrid/2.2/jquery-1.7.min.js create mode 100644 src/ckanext-d4science_theme/ckanext/reclineview/theme/public/vendor/slickgrid/2.2/jquery-ui-1.8.16.custom.js create mode 100644 src/ckanext-d4science_theme/ckanext/reclineview/theme/public/vendor/slickgrid/2.2/jquery.event.drag-2.2.js create mode 100644 src/ckanext-d4science_theme/ckanext/reclineview/theme/public/vendor/slickgrid/2.2/jquery.event.drop-2.2.js create mode 100644 src/ckanext-d4science_theme/ckanext/reclineview/theme/public/vendor/slickgrid/2.2/plugins/slick.autotooltips.js create mode 100644 src/ckanext-d4science_theme/ckanext/reclineview/theme/public/vendor/slickgrid/2.2/plugins/slick.cellcopymanager.js create mode 100644 src/ckanext-d4science_theme/ckanext/reclineview/theme/public/vendor/slickgrid/2.2/plugins/slick.cellrangedecorator.js create mode 100644 src/ckanext-d4science_theme/ckanext/reclineview/theme/public/vendor/slickgrid/2.2/plugins/slick.cellrangeselector.js create mode 100644 src/ckanext-d4science_theme/ckanext/reclineview/theme/public/vendor/slickgrid/2.2/plugins/slick.cellselectionmodel.js create mode 100644 src/ckanext-d4science_theme/ckanext/reclineview/theme/public/vendor/slickgrid/2.2/plugins/slick.checkboxselectcolumn.js create mode 100644 src/ckanext-d4science_theme/ckanext/reclineview/theme/public/vendor/slickgrid/2.2/plugins/slick.headerbuttons.css create mode 100644 src/ckanext-d4science_theme/ckanext/reclineview/theme/public/vendor/slickgrid/2.2/plugins/slick.headerbuttons.js create mode 100644 src/ckanext-d4science_theme/ckanext/reclineview/theme/public/vendor/slickgrid/2.2/plugins/slick.headermenu.css create mode 100644 src/ckanext-d4science_theme/ckanext/reclineview/theme/public/vendor/slickgrid/2.2/plugins/slick.headermenu.js create mode 100644 src/ckanext-d4science_theme/ckanext/reclineview/theme/public/vendor/slickgrid/2.2/plugins/slick.rowmovemanager.js create mode 100644 src/ckanext-d4science_theme/ckanext/reclineview/theme/public/vendor/slickgrid/2.2/plugins/slick.rowselectionmodel.js create mode 100644 src/ckanext-d4science_theme/ckanext/reclineview/theme/public/vendor/slickgrid/2.2/slick-default-theme.css create mode 100644 src/ckanext-d4science_theme/ckanext/reclineview/theme/public/vendor/slickgrid/2.2/slick.core.js create mode 100644 src/ckanext-d4science_theme/ckanext/reclineview/theme/public/vendor/slickgrid/2.2/slick.dataview.js create mode 100644 src/ckanext-d4science_theme/ckanext/reclineview/theme/public/vendor/slickgrid/2.2/slick.editors.js create mode 100644 src/ckanext-d4science_theme/ckanext/reclineview/theme/public/vendor/slickgrid/2.2/slick.formatters.js create mode 100644 src/ckanext-d4science_theme/ckanext/reclineview/theme/public/vendor/slickgrid/2.2/slick.grid.css create mode 100644 src/ckanext-d4science_theme/ckanext/reclineview/theme/public/vendor/slickgrid/2.2/slick.grid.js create mode 100644 src/ckanext-d4science_theme/ckanext/reclineview/theme/public/vendor/slickgrid/2.2/slick.groupitemmetadataprovider.js create mode 100644 src/ckanext-d4science_theme/ckanext/reclineview/theme/public/vendor/slickgrid/2.2/slick.remotemodel.js create mode 100644 src/ckanext-d4science_theme/ckanext/reclineview/theme/public/vendor/timeline/LICENSE create mode 100644 src/ckanext-d4science_theme/ckanext/reclineview/theme/public/vendor/timeline/README create mode 100644 src/ckanext-d4science_theme/ckanext/reclineview/theme/public/vendor/timeline/css/loading.gif create mode 100644 src/ckanext-d4science_theme/ckanext/reclineview/theme/public/vendor/timeline/css/timeline.css create mode 100644 src/ckanext-d4science_theme/ckanext/reclineview/theme/public/vendor/timeline/css/timeline.png create mode 100644 src/ckanext-d4science_theme/ckanext/reclineview/theme/public/vendor/timeline/css/timeline@2x.png create mode 100644 src/ckanext-d4science_theme/ckanext/reclineview/theme/public/vendor/timeline/js/timeline.js create mode 100644 src/ckanext-d4science_theme/ckanext/reclineview/theme/public/vendor/underscore.deferred/0.4.0/underscore.deferred.js create mode 100644 src/ckanext-d4science_theme/ckanext/reclineview/theme/public/vendor/underscore.deferred/0.4.0/underscore.deferred.min.js create mode 100644 src/ckanext-d4science_theme/ckanext/reclineview/theme/public/vendor/underscore/1.4.4/underscore.js create mode 100644 src/ckanext-d4science_theme/ckanext/reclineview/theme/public/webassets.yml create mode 100644 src/ckanext-d4science_theme/ckanext/reclineview/theme/public/widget.recordcount.js create mode 100644 src/ckanext-d4science_theme/ckanext/reclineview/theme/public/widget.recordcount.min.js create mode 100644 src/ckanext-d4science_theme/ckanext/reclineview/theme/templates/recline_graph_form.html create mode 100644 src/ckanext-d4science_theme/ckanext/reclineview/theme/templates/recline_map_form.html create mode 100644 src/ckanext-d4science_theme/ckanext/reclineview/theme/templates/recline_view.html create mode 100644 src/ckanext-d4science_theme/ckanext/stats/__init__.py create mode 100644 src/ckanext-d4science_theme/ckanext/stats/blueprint.py create mode 100644 src/ckanext-d4science_theme/ckanext/stats/plugin.py create mode 100644 src/ckanext-d4science_theme/ckanext/stats/public/.gitignore create mode 100644 src/ckanext-d4science_theme/ckanext/stats/public/__init__.py create mode 100644 src/ckanext-d4science_theme/ckanext/stats/public/ckanext/__init__.py create mode 100644 src/ckanext-d4science_theme/ckanext/stats/public/ckanext/stats/__init__.py create mode 100644 src/ckanext-d4science_theme/ckanext/stats/public/ckanext/stats/css/stats.css create mode 100644 src/ckanext-d4science_theme/ckanext/stats/public/ckanext/stats/javascript/modules/plot.js create mode 100644 src/ckanext-d4science_theme/ckanext/stats/public/ckanext/stats/javascript/modules/stats-nav.js create mode 100644 src/ckanext-d4science_theme/ckanext/stats/public/ckanext/stats/resource.config create mode 100644 src/ckanext-d4science_theme/ckanext/stats/public/ckanext/stats/test/fixtures/table.html create mode 100644 src/ckanext-d4science_theme/ckanext/stats/public/ckanext/stats/test/index.html create mode 100644 src/ckanext-d4science_theme/ckanext/stats/public/ckanext/stats/test/spec/modules/plot.spec.js create mode 100644 src/ckanext-d4science_theme/ckanext/stats/public/ckanext/stats/test/spec/modules/stats-nav.spec.js create mode 100644 src/ckanext-d4science_theme/ckanext/stats/public/ckanext/stats/vendor/excanvas.js create mode 100644 src/ckanext-d4science_theme/ckanext/stats/public/ckanext/stats/vendor/jquery.flot.js create mode 100644 src/ckanext-d4science_theme/ckanext/stats/public/ckanext/stats/webassets.yml create mode 100644 src/ckanext-d4science_theme/ckanext/stats/stats.py create mode 100644 src/ckanext-d4science_theme/ckanext/stats/templates/ckanext/stats/index.html create mode 100644 src/ckanext-d4science_theme/ckanext/stats/tests/__init__.py create mode 100644 src/ckanext-d4science_theme/ckanext/stats/tests/test_stats_lib.py create mode 100644 src/ckanext-d4science_theme/ckanext/stats/tests/test_stats_plugin.py create mode 100644 src/ckanext-d4science_theme/ckanext/videoview/__init__.py create mode 100644 src/ckanext-d4science_theme/ckanext/videoview/plugin.py create mode 100644 src/ckanext-d4science_theme/ckanext/videoview/tests/__init__.py create mode 100644 src/ckanext-d4science_theme/ckanext/videoview/tests/test_view.py create mode 100644 src/ckanext-d4science_theme/ckanext/videoview/theme/templates/video_form.html create mode 100644 src/ckanext-d4science_theme/ckanext/videoview/theme/templates/video_view.html diff --git a/.env b/.env index 11a0ba3..be2b660 100644 --- a/.env +++ b/.env @@ -78,7 +78,8 @@ NGINX_PORT=80 NGINX_SSLPORT=443 # Extensions -CKAN__PLUGINS="envvars image_view text_view recline_view datastore datapusher d4science_theme activity dcat dcat_rdf_harvester dcat_json_harvester dcat_json_interface structured_data harvest ckan_harvester spatial_metadata spatial_query" +CKAN__PLUGINS="envvars image_view text_view recline_view datastore datapusher d4science_theme dcat dcat_rdf_harvester dcat_json_harvester dcat_json_interface structured_data harvest ckan_harvester spatial_metadata spatial_query activity audio_view chained_functions datatables_view multilingual_dataset multilingual_group multilingual_tag multilingual_resource recline_grid recline_graph recline_map resource_proxy stats video_view webpage_view " +# plugin rimossi in dev: expire_api_token CKAN__HARVEST__MQ__TYPE=redis CKAN__HARVEST__MQ__HOSTNAME=redis CKAN__HARVEST__MQ__PORT=6379 diff --git a/.gitignore b/.gitignore index 2da5ce3..940fe28 100755 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,7 @@ # generic .DS_Store .vagrant +*.pyc # code & config _service-provider/* diff --git a/src/ckanext-d4science_theme/ckanext/d4science_theme/templates/package/search.html b/src/ckanext-d4science_theme/ckanext/d4science_theme/templates/package/search.html index 51e9939..0809ef2 100644 --- a/src/ckanext-d4science_theme/ckanext/d4science_theme/templates/package/search.html +++ b/src/ckanext-d4science_theme/ckanext/d4science_theme/templates/package/search.html @@ -12,9 +12,12 @@
{% block page_primary_action %} {% if h.check_access('package_create') %} -
- {{ h.snippet ('snippets/add_dataset.html', dataset_type=dataset_type) }} -
+ {# ADDED BY FRANCESCO MANGIACRAPA, 'ADD DATASET' IS SHOWN ONLY TO USER SYSADMIN' #7188 #} + {% if c.userobj and c.userobj.sysadmin %} +
+ {{ h.snippet ('snippets/add_dataset.html', dataset_type=dataset_type) }} +
+ {% endif %} {% endif %} {% endblock %} {% block form %} @@ -49,18 +52,21 @@
{% block package_search_results_api_inner %} - {% set api_link = h.link_to(_('API'), h.url_for('api.get_api', ver=3)) %} - {% set api_doc_link = h.link_to(_('API Docs'), 'http://docs.ckan.org/en/{0}/api/'.format(g.ckan_doc_version)) %} - {% if g.dumps_url -%} - {% set dump_link = h.link_to(_('full {format} dump').format(format=g.dumps_format), g.dumps_url) %} - {% trans %} - You can also access this registry using the {{ api_link }} (see {{ api_doc_link }}) or download a {{ dump_link }}. - {% endtrans %} - {% else %} - {% trans %} - You can also access this registry using the {{ api_link }} (see {{ api_doc_link}}). - {% endtrans %} - {%- endif %} + {# Commented by Francesco, see Support #18126 #} + {# + {% set api_link = h.link_to(_('API'), h.url_for('api.get_api', ver=3)) %} + {% set api_doc_link = h.link_to(_('API Docs'), 'http://docs.ckan.org/en/{0}/api/'.format(g.ckan_doc_version)) %} + {% if g.dumps_url -%} + {% set dump_link = h.link_to(_('full {format} dump').format(format=g.dumps_format), g.dumps_url) %} + {% trans %} + You can also access this registry using the {{ api_link }} (see {{ api_doc_link }}) or download a {{ dump_link }}. + {% endtrans %} + {% else %} + {% trans %} + You can also access this registry using the {{ api_link }} (see {{ api_doc_link}}). + {% endtrans %} + {%- endif %} + #} {% endblock %}
@@ -72,6 +78,9 @@ {% block secondary_content %}
+ + {% snippet "spatial/snippets/spatial_query.html" %} + {% for facet in facet_titles %} {{ h.snippet('snippets/facet_list.html', title=facet_titles[facet], name=facet, search_facets=search_facets) }} {% endfor %} diff --git a/src/ckanext-d4science_theme/ckanext/d4science_theme/templates/snippets/facet_list.html b/src/ckanext-d4science_theme/ckanext/d4science_theme/templates/snippets/facet_list.html index 127fe99..fad6fc1 100644 --- a/src/ckanext-d4science_theme/ckanext/d4science_theme/templates/snippets/facet_list.html +++ b/src/ckanext-d4science_theme/ckanext/d4science_theme/templates/snippets/facet_list.html @@ -1,91 +1,100 @@ {# -Construct a facet module populated with links to filtered results. - -name -The field name identifying the facet field, eg. "tags" - -title -The title of the facet, eg. "Tags", or "Tag Cloud" - -label_function -Renders the human-readable label for each facet value. -If defined, this should be a callable that accepts a `facet_item`. -eg. lambda facet_item: facet_item.display_name.upper() -By default it displays the facet item's display name, which should -usually be good enough - -if_empty -A string, which if defined, and the list of possible facet items is empty, -is displayed in lieu of an empty list. - -count_label -A callable which accepts an integer, and returns a string. This controls -how a facet-item's count is displayed. - -extras -Extra info passed into the add/remove params to make the url - -alternative_url -URL to use when building the necessary URLs, instead of the default -ones returned by url_for. Useful eg for dataset types. - -hide_empty -Do not show facet if there are none, Default: false. - -search_facets -Dictionary with search facets - -#} -{% block facet_list %} - {% set hide_empty = hide_empty or false %} - {% with items = items or h.get_facet_items_dict(name, search_facets) %} - {% if items or not hide_empty %} - {% block facet_list_item %} -
- {% block facet_list_heading %} -

- - {{ title }} -

- {% endblock %} - {% block facet_list_items %} - {% with items = items or h.get_facet_items_dict(name, search_facets) %} - {% if items %} - - -
+ {% block facet_list_heading %} +

+ + {% set title = title or h.get_facet_title(name) %} + {{ title }} +

+ {% endblock %} + {% block facet_list_items %} + {% with items = items or h.get_facet_items_dict(name) %} + {% if items %} + + + - {% else %} -

{{ _('There are no {facet_type} that match this search').format(facet_type=title) }}

- {% endif %} - {% endwith %} - {% endblock %} -
- {% endblock %} - {% endif %} - {% endwith %} -{% endblock %} +

+ {% else %} +

{{ _('There are no {facet_type} that match this search').format(facet_type=title) }}

+ {% endif %} + {% endwith %} + {% endblock %} +
+ {% endblock %} + {% endif %} + {% endwith %} + {% endblock %} + \ No newline at end of file diff --git a/src/ckanext-d4science_theme/ckanext/d4science_theme/templates2.6/group/activity_stream.html b/src/ckanext-d4science_theme/ckanext/d4science_theme/templates2.6/group/activity_stream.html deleted file mode 100644 index 0a7484e..0000000 --- a/src/ckanext-d4science_theme/ckanext/d4science_theme/templates2.6/group/activity_stream.html +++ /dev/null @@ -1,15 +0,0 @@ -{% extends "group/read_base.html" %} - -{% block subtitle %}{{ _('Activity Stream') }} {{ g.template_title_delimiter }} {{ super() }}{% endblock %} - -{% block primary_content_inner %} - - {% if activity_types is defined %} - {% snippet 'snippets/activity_type_selector.html', id=id, activity_type=activity_type, activity_types=activity_types, blueprint='activity.group_activity' %} - {% endif %} - - {% snippet 'snippets/stream.html', activity_stream=activity_stream, id=id, object_type='group' %} - - {% snippet 'snippets/pagination.html', newer_activities_url=newer_activities_url, older_activities_url=older_activities_url %} - -{% endblock %} diff --git a/src/ckanext-d4science_theme/ckanext/d4science_theme/templates2.6/group/changes.html b/src/ckanext-d4science_theme/ckanext/d4science_theme/templates2.6/group/changes.html deleted file mode 100644 index 00b7307..0000000 --- a/src/ckanext-d4science_theme/ckanext/d4science_theme/templates2.6/group/changes.html +++ /dev/null @@ -1,64 +0,0 @@ -{% extends "group/read_base.html" %} - -{% block subtitle %}{{ group_dict.name }} {{ g.template_title_delimiter }} Changes {{ g.template_title_delimiter }} {{ super() }}{% endblock %} - -{% block breadcrumb_content_selected %}{% endblock %} - -{% block breadcrumb_content %} - {{ super() }} -
  • {% link_for _('Changes'), named_route='activity.group_activity', id=group_dict.name %}
  • -
  • {% link_for activity_diffs[0].activities[1].id|truncate(30), named_route='activity.group_changes', id=activity_diffs[0].activities[1].id %}
  • -{% endblock %} - -{% block primary %} -
    -
    - {% block group_changes_header %} -

    {{ _('Changes') }}

    - {% endblock %} - - {% set select_list1 = h.activity_list_select(group_activity_list, activity_diffs[-1].activities[0].id) %} - {% set select_list2 = h.activity_list_select(group_activity_list, activity_diffs[0].activities[1].id) %} -
    - - - View changes from - to - -
    - -
    - - {# iterate through the list of activity diffs #} -
    - {% for i in range(activity_diffs|length) %} - {% snippet "group/snippets/item_group.html", activity_diff=activity_diffs[i], group_dict=group_dict %} - - {# TODO: display metadata for more than most recent change #} - {% if i == 0 %} - {# button to show JSON metadata diff for the most recent change - not shown by default #} - - - {% endif %} - -
    - {% endfor %} -
    -
    -{% endblock %} - -{% block secondary %}{% endblock %} diff --git a/src/ckanext-d4science_theme/ckanext/d4science_theme/templates2.6/group/read_base.html b/src/ckanext-d4science_theme/ckanext/d4science_theme/templates2.6/group/read_base.html index 03ce862..23014cd 100644 --- a/src/ckanext-d4science_theme/ckanext/d4science_theme/templates2.6/group/read_base.html +++ b/src/ckanext-d4science_theme/ckanext/d4science_theme/templates2.6/group/read_base.html @@ -1,6 +1,36 @@ -{% ckan_extends %} +{% extends "page.html" %} + +{% block subtitle %}{{ c.group_dict.display_name }} - {{ _('Groups') }}{% endblock %} + +{% block breadcrumb_content %} +
  • {% link_for _('Groups'), controller='group', action='index' %}
  • +
  • {% link_for c.group_dict.display_name|truncate(35), controller='group', action='read', id=c.group_dict.name %}
  • +{% endblock %} + +{% block content_action %} + {% if h.check_access('group_update', {'id': c.group_dict.id}) %} + {% link_for _('Manage'), controller='group', action='edit', id=c.group_dict.name, class_='btn', icon='wrench' %} + {% endif %} +{% endblock %} {% block content_primary_nav %} + {{ h.build_nav_icon('group.read', _('Datasets'), id=c.group_dict.name) }} + + {% if h.check_access('group_update', {'id': c.group_dict.id}) %} + {% set role_for_user = h.d4science_theme_get_user_role_for_group_or_org(c.group_dict.id, c.userobj.name) %} + {% if (role_for_user and role_for_user == 'admin') or c.userobj.sysadmin %} + + {#{{ h.build_nav_icon('group.activity', _('Activity Stream'), id=c.group_dict.name, offset=0) }}#} + {% endif %} + {% endif %} + {{ h.build_nav_icon('group.about', _('About'), id=c.group_dict.name) }} +{% endblock %} + +{% block secondary_content %} + {% snippet "group/snippets/info.html", group=c.group_dict, show_nums=true %} +{% endblock %} + +{% block links %} {{ super() }} - {{ h.build_nav_icon('activity.group_activity', _('Activity Stream'), id=group_dict.name, offset=0, icon='clock') }} -{% endblock content_primary_nav %} + {% include "group/snippets/feeds.html" %} +{% endblock %} diff --git a/src/ckanext-d4science_theme/ckanext/d4science_theme/templates2.6/group2.6/snippets/group_form.html b/src/ckanext-d4science_theme/ckanext/d4science_theme/templates2.6/group/snippets/group_form.html similarity index 100% rename from src/ckanext-d4science_theme/ckanext/d4science_theme/templates2.6/group2.6/snippets/group_form.html rename to src/ckanext-d4science_theme/ckanext/d4science_theme/templates2.6/group/snippets/group_form.html diff --git a/src/ckanext-d4science_theme/ckanext/d4science_theme/templates2.6/group2.6/snippets/group_item.html b/src/ckanext-d4science_theme/ckanext/d4science_theme/templates2.6/group/snippets/group_item.html similarity index 100% rename from src/ckanext-d4science_theme/ckanext/d4science_theme/templates2.6/group2.6/snippets/group_item.html rename to src/ckanext-d4science_theme/ckanext/d4science_theme/templates2.6/group/snippets/group_item.html diff --git a/src/ckanext-d4science_theme/ckanext/d4science_theme/templates2.6/group2.6/snippets/group_list.html b/src/ckanext-d4science_theme/ckanext/d4science_theme/templates2.6/group/snippets/group_list.html similarity index 100% rename from src/ckanext-d4science_theme/ckanext/d4science_theme/templates2.6/group2.6/snippets/group_list.html rename to src/ckanext-d4science_theme/ckanext/d4science_theme/templates2.6/group/snippets/group_list.html diff --git a/src/ckanext-d4science_theme/ckanext/d4science_theme/templates2.6/group2.6/snippets/group_list_simple.html b/src/ckanext-d4science_theme/ckanext/d4science_theme/templates2.6/group/snippets/group_list_simple.html similarity index 100% rename from src/ckanext-d4science_theme/ckanext/d4science_theme/templates2.6/group2.6/snippets/group_list_simple.html rename to src/ckanext-d4science_theme/ckanext/d4science_theme/templates2.6/group/snippets/group_list_simple.html diff --git a/src/ckanext-d4science_theme/ckanext/d4science_theme/templates2.6/group2.6/snippets/group_tree.html b/src/ckanext-d4science_theme/ckanext/d4science_theme/templates2.6/group/snippets/group_tree.html similarity index 100% rename from src/ckanext-d4science_theme/ckanext/d4science_theme/templates2.6/group2.6/snippets/group_tree.html rename to src/ckanext-d4science_theme/ckanext/d4science_theme/templates2.6/group/snippets/group_tree.html diff --git a/src/ckanext-d4science_theme/ckanext/d4science_theme/templates2.6/group/snippets/item_group.html b/src/ckanext-d4science_theme/ckanext/d4science_theme/templates2.6/group/snippets/item_group.html deleted file mode 100644 index 915fabe..0000000 --- a/src/ckanext-d4science_theme/ckanext/d4science_theme/templates2.6/group/snippets/item_group.html +++ /dev/null @@ -1,10 +0,0 @@ -{{ gettext('On %(timestamp)s, %(username)s:', timestamp=h.render_datetime(activity_diff.activities[1].timestamp, with_hours=True, with_seconds=True), username=h.linked_user(activity_diff.activities[1].user_id)) }} - -{% set changes = h.compare_group_dicts(activity_diff.activities[0].data.group, activity_diff.activities[1].data.group, activity_diff.activities[0].id) %} -
      - {% for change in changes %} - {% snippet "snippets/group_changes/{}.html".format( - change.type), change=change %} -
      - {% endfor %} -
    diff --git a/src/ckanext-d4science_theme/ckanext/d4science_theme/templates2.6/group2.6/read_base.html b/src/ckanext-d4science_theme/ckanext/d4science_theme/templates2.6/group2.6/read_base.html deleted file mode 100644 index 23014cd..0000000 --- a/src/ckanext-d4science_theme/ckanext/d4science_theme/templates2.6/group2.6/read_base.html +++ /dev/null @@ -1,36 +0,0 @@ -{% extends "page.html" %} - -{% block subtitle %}{{ c.group_dict.display_name }} - {{ _('Groups') }}{% endblock %} - -{% block breadcrumb_content %} -
  • {% link_for _('Groups'), controller='group', action='index' %}
  • -
  • {% link_for c.group_dict.display_name|truncate(35), controller='group', action='read', id=c.group_dict.name %}
  • -{% endblock %} - -{% block content_action %} - {% if h.check_access('group_update', {'id': c.group_dict.id}) %} - {% link_for _('Manage'), controller='group', action='edit', id=c.group_dict.name, class_='btn', icon='wrench' %} - {% endif %} -{% endblock %} - -{% block content_primary_nav %} - {{ h.build_nav_icon('group.read', _('Datasets'), id=c.group_dict.name) }} - - {% if h.check_access('group_update', {'id': c.group_dict.id}) %} - {% set role_for_user = h.d4science_theme_get_user_role_for_group_or_org(c.group_dict.id, c.userobj.name) %} - {% if (role_for_user and role_for_user == 'admin') or c.userobj.sysadmin %} - - {#{{ h.build_nav_icon('group.activity', _('Activity Stream'), id=c.group_dict.name, offset=0) }}#} - {% endif %} - {% endif %} - {{ h.build_nav_icon('group.about', _('About'), id=c.group_dict.name) }} -{% endblock %} - -{% block secondary_content %} - {% snippet "group/snippets/info.html", group=c.group_dict, show_nums=true %} -{% endblock %} - -{% block links %} - {{ super() }} - {% include "group/snippets/feeds.html" %} -{% endblock %} diff --git a/src/ckanext-d4science_theme/ckanext/multilingual/__init__.py b/src/ckanext-d4science_theme/ckanext/multilingual/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/src/ckanext-d4science_theme/ckanext/multilingual/plugin.py b/src/ckanext-d4science_theme/ckanext/multilingual/plugin.py new file mode 100644 index 0000000..8e0589a --- /dev/null +++ b/src/ckanext-d4science_theme/ckanext/multilingual/plugin.py @@ -0,0 +1,413 @@ +# encoding: utf-8 +from __future__ import annotations + +from ckan.types import Context +from typing import Any, cast + +import ckan +import ckan.lib.navl.dictization_functions +import ckan.model + +import ckan.plugins as plugins + +from ckan.common import request, config, g +from ckan.logic import get_action + + +def translate_data_dict(data_dict: dict[str, Any]): + '''Return the given dict (e.g. a dataset dict) with as many of its fields + as possible translated into the desired or the fallback language. + + ''' + desired_lang_code = request.environ['CKAN_LANG'] + fallback_lang_code = config.get('ckan.locale_default') + + # Get a flattened copy of data_dict to do the translation on. + flattened = ckan.lib.navl.dictization_functions.flatten_dict( + data_dict) + + # Get a simple flat list of all the terms to be translated, from the + # flattened data dict. + terms = set() + for (key, value) in flattened.items(): + if value in (None, True, False): + continue + elif isinstance(value, str): + terms.add(value) + elif isinstance(value, int): + continue + else: + for item in value: + if isinstance(value, dict): + if key == (u'organization',) and item == 'description': + terms.add(value[item]) + else: + terms.add(item) + else: + terms.add(item) + + # Get the translations of all the terms (as a list of dictionaries). + translations = get_action('term_translation_show')( + cast(Context, {'model': ckan.model}), + {'terms': terms, + 'lang_codes': (desired_lang_code, fallback_lang_code)}) + + # Transform the translations into a more convenient structure. + desired_translations = {} + fallback_translations = {} + for translation in translations: + if translation['lang_code'] == desired_lang_code: + desired_translations[translation['term']] = ( + translation['term_translation']) + else: + assert translation['lang_code'] == fallback_lang_code + fallback_translations[translation['term']] = ( + translation['term_translation']) + + # Make a copy of the flattened data dict with all the terms replaced by + # their translations, where available. + translated_flattened = {} + for (key, value) in flattened.items(): + + # Don't translate names that are used for form URLs. + if key == ('name',): + translated_flattened[key] = value + elif (key[0] in ('tags', 'groups') and len(key) == 3 + and key[2] == 'name'): + translated_flattened[key] = value + + elif value in (None, True, False): + # Don't try to translate values that aren't strings. + translated_flattened[key] = value + + elif isinstance(value, str): + if value in desired_translations: + translated_flattened[key] = desired_translations[value] + else: + translated_flattened[key] = fallback_translations.get( + value, value) + + elif isinstance(value, (int, dict)): + if key == (u'organization',): + assert isinstance(value, dict) + translated_flattened[key] = translate_data_dict(value); + else: + translated_flattened[key] = value + + else: + translated_value = [] + for item in value: + if item in desired_translations: + translated_value.append(desired_translations[item]) + else: + translated_value.append( + fallback_translations.get(item, item) + ) + translated_flattened[key] = translated_value + + # Finally unflatten and return the translated data dict. + translated_data_dict = (ckan.lib.navl.dictization_functions + .unflatten(translated_flattened)) + return translated_data_dict + +def translate_resource_data_dict(data_dict: dict[str, Any]): + '''Return the given dict with as many of its fields + as possible translated into the desired or the fallback language. + + ''' + + desired_lang_code = request.environ['CKAN_LANG'] + fallback_lang_code = config.get('ckan.locale_default') + + # Get a flattened copy of data_dict to do the translation on. + flattened = ckan.lib.navl.dictization_functions.flatten_dict( + data_dict) + + # Get a simple flat list of all the terms to be translated, from the + # flattened data dict. + terms = set() + for (key, value) in flattened.items(): + if value in (None, True, False): + continue + elif isinstance(value, str): + terms.add(value) + elif isinstance(value, int): + continue + else: + for item in value: + terms.add(item) + + # Get the translations of all the terms (as a list of dictionaries). + translations = get_action('term_translation_show')( + cast(Context, {'model': ckan.model}), + {'terms': terms, + 'lang_codes': (desired_lang_code, fallback_lang_code)}) + # Transform the translations into a more convenient structure. + desired_translations = {} + fallback_translations = {} + for translation in translations: + if translation['lang_code'] == desired_lang_code: + desired_translations[translation['term']] = ( + translation['term_translation']) + else: + assert translation['lang_code'] == fallback_lang_code + fallback_translations[translation['term']] = ( + translation['term_translation']) + + # Make a copy of the flattened data dict with all the terms replaced by + # their translations, where available. + translated_flattened = {} + for (key, value) in flattened.items(): + + # Don't translate names that are used for form URLs. + if key == ('name',): + if value in desired_translations: + translated_flattened[key] = desired_translations[value] + elif value in fallback_translations: + translated_flattened[key] = fallback_translations.get( + value, value) + else: + translated_flattened[key] = value + + elif value in (None, True, False): + # Don't try to translate values that aren't strings. + translated_flattened[key] = value + + elif isinstance(value, str): + if value in desired_translations: + translated_flattened[key] = desired_translations[value] + else: + translated_flattened[key] = fallback_translations.get( + value, value) + + elif isinstance(value, (int, dict)): + translated_flattened[key] = value + + else: + translated_value = [] + for item in value: + if item in desired_translations: + translated_value.append(desired_translations[item]) + else: + translated_value.append( + fallback_translations.get(item, item) + ) + translated_flattened[key] = translated_value + # Finally unflatten and return the translated data dict. + translated_data_dict = (ckan.lib.navl.dictization_functions + .unflatten(translated_flattened)) + return translated_data_dict + +KEYS_TO_IGNORE = ['state', 'revision_id', 'id', #title done seperately + 'metadata_created', 'metadata_modified', 'site_id'] + + +class MultilingualDataset(plugins.SingletonPlugin): + plugins.implements(plugins.IPackageController, inherit=True) + LANGS = config.get('ckan.locale_order') or ["en"] + + def before_dataset_index(self, search_data: dict[str, Any]): + + default_lang = search_data.get( + 'lang_code', + config.get('ckan.locale_default') + ) + + ## translate title + title = search_data.get('title') + search_data['title_' + default_lang] = title + title_translations = get_action('term_translation_show')( + cast(Context, {'model': ckan.model}), + {'terms': [title], + 'lang_codes': self.LANGS}) + + for translation in title_translations: + title_field = 'title_' + translation['lang_code'] + search_data[title_field] = translation['term_translation'] + + ## translate rest + all_terms = [] + for key, value in sorted(search_data.items()): + if key in KEYS_TO_IGNORE or key.startswith('title'): + continue + if not isinstance(value, list): + value = [value] + for item in value: + if isinstance(item, str): + all_terms.append(item) + + field_translations = get_action('term_translation_show')( + cast(Context, {'model': ckan.model}), + {'terms': all_terms, + 'lang_codes': self.LANGS}) + + text_field_items = dict(('text_' + lang, []) for lang in self.LANGS) + + text_field_items['text_' + default_lang].extend(all_terms) + + for translation in sorted( + field_translations, + key=lambda tr: all_terms.index(tr['term'])): + lang_field = 'text_' + translation['lang_code'] + text_field_items[lang_field].append(translation['term_translation']) + + for key, value in text_field_items.items(): + search_data[key] = ' '.join(value) + + return search_data + + def before_dataset_search(self, search_params: dict[str, Any]): + lang_set = set(self.LANGS) + + try: + current_lang: str = request.environ['CKAN_LANG'] + except TypeError as err: + if str(err) == ('No object (name: request) has been registered ' + 'for this thread'): + # This happens when this code gets called as part of a paster + # command rather then as part of an HTTP request. + current_lang = config.get('ckan.locale_default') + else: + raise + except KeyError: + current_lang = config.get('ckan.locale_default') + + # fallback to default locale if locale not in suported langs + if not current_lang in lang_set: + current_lang = config.get('ckan.locale_default') + # fallback to english if default locale is not supported + if not current_lang in lang_set: + current_lang = 'en' + # treat current lang differenly so remove from set + lang_set.remove(current_lang) + + # weight current lang more highly + query_fields = 'title_%s^8 text_%s^4' % (current_lang, current_lang) + + for lang in lang_set: + query_fields += ' title_%s^2 text_%s' % (lang, lang) + + search_params['qf'] = query_fields + + return search_params + + def after_dataset_search(self, search_results: dict[str, Any], + search_params: dict[str, Any]): + + # Translate the unselected search facets. + facets = search_results.get('search_facets') + if not facets: + return search_results + + desired_lang_code = request.environ['CKAN_LANG'] + fallback_lang_code = config.get('ckan.locale_default') + + # Look up translations for all of the facets in one db query. + terms = set() + for facet in facets.values(): + for item in facet['items']: + terms.add(item['display_name']) + translations = get_action('term_translation_show')( + cast(Context, {'model': ckan.model}), + {'terms': terms, + 'lang_codes': (desired_lang_code, fallback_lang_code)}) + + # Replace facet display names with translated ones. + for facet in facets.values(): + for item in facet['items']: + matching_translations = [translation for + translation in translations + if translation['term'] == item['display_name'] + and translation['lang_code'] == desired_lang_code] + if not matching_translations: + matching_translations = [translation for + translation in translations + if translation['term'] == item['display_name'] + and translation['lang_code'] == fallback_lang_code] + if matching_translations: + assert len(matching_translations) == 1 + item['display_name'] = ( + matching_translations[0]['term_translation']) + + return search_results + + def before_dataset_view(self, dataset_dict: dict[str, Any]): + + # Translate any selected search facets (e.g. if we are rendering a + # group read page or the dataset index page): lookup translations of + # all the terms in c.fields (c.fields contains the selected facets) + # and save them in c.translated_fields where the templates can + # retrieve them later. + desired_lang_code = request.environ['CKAN_LANG'] + fallback_lang_code = config.get('ckan.locale_default') + try: + fields = g.fields + except AttributeError: + return translate_data_dict(dataset_dict) + terms = [value for _param, value in fields] + translations = get_action('term_translation_show')( + cast(Context, {'model': ckan.model}), + {'terms': terms, + 'lang_codes': (desired_lang_code, fallback_lang_code)}) + g.translated_fields = {} + for param, value in fields: + matching_translations = [translation for translation in + translations if translation['term'] == value and + translation['lang_code'] == desired_lang_code] + if not matching_translations: + matching_translations = [translation for translation in + translations if translation['term'] == value and + translation['lang_code'] == fallback_lang_code] + if matching_translations: + assert len(matching_translations) == 1 + translation = matching_translations[0]['term_translation'] + g.translated_fields[(param, value)] = translation + + # Now translate the fields of the dataset itself. + return translate_data_dict(dataset_dict) + +class MultilingualGroup(plugins.SingletonPlugin): + '''The MultilingualGroup plugin translates group names and other group + fields on group read pages and on the group index page. + + For example on the page /de/group/david the title "Dave's Books" at the + top of the page might be translated to "Dave's Bucher". + + Datasets are also shown on group pages, but these are translated by the + MultilingualDataset plugin. + + ''' + plugins.implements(plugins.IGroupController, inherit=True) + plugins.implements(plugins.IOrganizationController, inherit=True) + + def before_view(self, data_dict: dict[str, Any]): + translated_data_dict = translate_data_dict(data_dict) + return translated_data_dict + +class MultilingualTag(plugins.SingletonPlugin): + '''The MultilingualTag plugin translates tag names on tag read pages and + on the tag index page. + + For example on the page /de/tag/tolstoy the title "Tag: tolstoy" at the + top of the page might be translated to "Tag: Tolstoi". + + Datasets are also shown on tag pages, but these are translated by the + MultilingualDataset plugin. + + ''' + plugins.implements(plugins.ITagController, inherit=True) + + def before_view(self, data_dict: dict[str, Any]): + translated_data_dict = translate_data_dict(data_dict) + return translated_data_dict + +class MultilingualResource(plugins.SingletonPlugin): + '''The MultilinguaResource plugin translate the selected resource name and + description on resource preview page. + + ''' + plugins.implements(plugins.IResourceController, inherit=True) + + def before_resource_show(self, data_dict: dict[str, Any]): + translated_data_dict = translate_resource_data_dict(data_dict) + return translated_data_dict diff --git a/src/ckanext-d4science_theme/ckanext/multilingual/solr/dutch_stop.txt b/src/ckanext-d4science_theme/ckanext/multilingual/solr/dutch_stop.txt new file mode 100644 index 0000000..f4d61f5 --- /dev/null +++ b/src/ckanext-d4science_theme/ckanext/multilingual/solr/dutch_stop.txt @@ -0,0 +1,117 @@ + | From svn.tartarus.org/snowball/trunk/website/algorithms/dutch/stop.txt + | This file is distributed under the BSD License. + | See http://snowball.tartarus.org/license.php + | Also see http://www.opensource.org/licenses/bsd-license.html + | - Encoding was converted to UTF-8. + | - This notice was added. + + | A Dutch stop word list. Comments begin with vertical bar. Each stop + | word is at the start of a line. + + | This is a ranked list (commonest to rarest) of stopwords derived from + | a large sample of Dutch text. + + | Dutch stop words frequently exhibit homonym clashes. These are indicated + | clearly below. + +de | the +en | and +van | of, from +ik | I, the ego +te | (1) chez, at etc, (2) to, (3) too +dat | that, which +die | that, those, who, which +in | in, inside +een | a, an, one +hij | he +het | the, it +niet | not, nothing, naught +zijn | (1) to be, being, (2) his, one's, its +is | is +was | (1) was, past tense of all persons sing. of 'zijn' (to be) (2) wax, (3) the washing, (4) rise of river +op | on, upon, at, in, up, used up +aan | on, upon, to (as dative) +met | with, by +als | like, such as, when +voor | (1) before, in front of, (2) furrow +had | had, past tense all persons sing. of 'hebben' (have) +er | there +maar | but, only +om | round, about, for etc +hem | him +dan | then +zou | should/would, past tense all persons sing. of 'zullen' +of | or, whether, if +wat | what, something, anything +mijn | possessive and noun 'mine' +men | people, 'one' +dit | this +zo | so, thus, in this way +door | through by +over | over, across +ze | she, her, they, them +zich | oneself +bij | (1) a bee, (2) by, near, at +ook | also, too +tot | till, until +je | you +mij | me +uit | out of, from +der | Old Dutch form of 'van der' still found in surnames +daar | (1) there, (2) because +haar | (1) her, their, them, (2) hair +naar | (1) unpleasant, unwell etc, (2) towards, (3) as +heb | present first person sing. of 'to have' +hoe | how, why +heeft | present third person sing. of 'to have' +hebben | 'to have' and various parts thereof +deze | this +u | you +want | (1) for, (2) mitten, (3) rigging +nog | yet, still +zal | 'shall', first and third person sing. of verb 'zullen' (will) +me | me +zij | she, they +nu | now +ge | 'thou', still used in Belgium and south Netherlands +geen | none +omdat | because +iets | something, somewhat +worden | to become, grow, get +toch | yet, still +al | all, every, each +waren | (1) 'were' (2) to wander, (3) wares, (3) +veel | much, many +meer | (1) more, (2) lake +doen | to do, to make +toen | then, when +moet | noun 'spot/mote' and present form of 'to must' +ben | (1) am, (2) 'are' in interrogative second person singular of 'to be' +zonder | without +kan | noun 'can' and present form of 'to be able' +hun | their, them +dus | so, consequently +alles | all, everything, anything +onder | under, beneath +ja | yes, of course +eens | once, one day +hier | here +wie | who +werd | imperfect third person sing. of 'become' +altijd | always +doch | yet, but etc +wordt | present third person sing. of 'become' +wezen | (1) to be, (2) 'been' as in 'been fishing', (3) orphans +kunnen | to be able +ons | us/our +zelf | self +tegen | against, towards, at +na | after, near +reeds | already +wil | (1) present tense of 'want', (2) 'will', noun, (3) fender +kon | could; past tense of 'to be able' +niets | nothing +uw | your +iemand | somebody +geweest | been; past participle of 'be' +andere | other diff --git a/src/ckanext-d4science_theme/ckanext/multilingual/solr/english_stop.txt b/src/ckanext-d4science_theme/ckanext/multilingual/solr/english_stop.txt new file mode 100644 index 0000000..821c03e --- /dev/null +++ b/src/ckanext-d4science_theme/ckanext/multilingual/solr/english_stop.txt @@ -0,0 +1,317 @@ + | From svn.tartarus.org/snowball/trunk/website/algorithms/english/stop.txt + | This file is distributed under the BSD License. + | See http://snowball.tartarus.org/license.php + | Also see http://www.opensource.org/licenses/bsd-license.html + | - Encoding was converted to UTF-8. + | - This notice was added. + + | An English stop word list. Comments begin with vertical bar. Each stop + | word is at the start of a line. + + | Many of the forms below are quite rare (e.g. "yourselves") but included for + | completeness. + + | PRONOUNS FORMS + | 1st person sing + +i | subject, always in upper case of course + +me | object +my | possessive adjective + | the possessive pronoun `mine' is best suppressed, because of the + | sense of coal-mine etc. +myself | reflexive + | 1st person plural +we | subject + +| us | object + | care is required here because US = United States. It is usually + | safe to remove it if it is in lower case. +our | possessive adjective +ours | possessive pronoun +ourselves | reflexive + | second person (archaic `thou' forms not included) +you | subject and object +your | possessive adjective +yours | possessive pronoun +yourself | reflexive (singular) +yourselves | reflexive (plural) + | third person singular +he | subject +him | object +his | possessive adjective and pronoun +himself | reflexive + +she | subject +her | object and possessive adjective +hers | possessive pronoun +herself | reflexive + +it | subject and object +its | possessive adjective +itself | reflexive + | third person plural +they | subject +them | object +their | possessive adjective +theirs | possessive pronoun +themselves | reflexive + | other forms (demonstratives, interrogatives) +what +which +who +whom +this +that +these +those + + | VERB FORMS (using F.R. Palmer's nomenclature) + | BE +am | 1st person, present +is | -s form (3rd person, present) +are | present +was | 1st person, past +were | past +be | infinitive +been | past participle +being | -ing form + | HAVE +have | simple +has | -s form +had | past +having | -ing form + | DO +do | simple +does | -s form +did | past +doing | -ing form + + | The forms below are, I believe, best omitted, because of the significant + | homonym forms: + + | He made a WILL + | old tin CAN + | merry month of MAY + | a smell of MUST + | fight the good fight with all thy MIGHT + + | would, could, should, ought might however be included + + | | AUXILIARIES + | | WILL + |will + +would + + | | SHALL + |shall + +should + + | | CAN + |can + +could + + | | MAY + |may + |might + | | MUST + |must + | | OUGHT + +ought + + | COMPOUND FORMS, increasingly encountered nowadays in 'formal' writing + | pronoun + verb + +i'm +you're +he's +she's +it's +we're +they're +i've +you've +we've +they've +i'd +you'd +he'd +she'd +we'd +they'd +i'll +you'll +he'll +she'll +we'll +they'll + + | verb + negation + +isn't +aren't +wasn't +weren't +hasn't +haven't +hadn't +doesn't +don't +didn't + + | auxiliary + negation + +won't +wouldn't +shan't +shouldn't +can't +cannot +couldn't +mustn't + + | miscellaneous forms + +let's +that's +who's +what's +here's +there's +when's +where's +why's +how's + + | rarer forms + + | daren't needn't + + | doubtful forms + + | oughtn't mightn't + + | ARTICLES +a +an +the + + | THE REST (Overlap among prepositions, conjunctions, adverbs etc is so + | high, that classification is pointless.) +and +but +if +or +because +as +until +while + +of +at +by +for +with +about +against +between +into +through +during +before +after +above +below +to +from +up +down +in +out +on +off +over +under + +again +further +then +once + +here +there +when +where +why +how + +all +any +both +each +few +more +most +other +some +such + +no +nor +not +only +own +same +so +than +too +very + + | Just for the record, the following words are among the commonest in English + + | one + | every + | least + | less + | many + | now + | ever + | never + | say + | says + | said + | also + | get + | go + | goes + | just + | made + | make + | put + | see + | seen + | whether + | like + | well + | back + | even + | still + | way + | take + | since + | another + | however + | two + | three + | four + | five + | first + | second + | new + | old + | high + | long diff --git a/src/ckanext-d4science_theme/ckanext/multilingual/solr/fr_elision.txt b/src/ckanext-d4science_theme/ckanext/multilingual/solr/fr_elision.txt new file mode 100644 index 0000000..4e855c1 --- /dev/null +++ b/src/ckanext-d4science_theme/ckanext/multilingual/solr/fr_elision.txt @@ -0,0 +1,8 @@ +c +d +j +l +m +n +s +t diff --git a/src/ckanext-d4science_theme/ckanext/multilingual/solr/french_stop.txt b/src/ckanext-d4science_theme/ckanext/multilingual/solr/french_stop.txt new file mode 100644 index 0000000..c00837e --- /dev/null +++ b/src/ckanext-d4science_theme/ckanext/multilingual/solr/french_stop.txt @@ -0,0 +1,183 @@ + | From svn.tartarus.org/snowball/trunk/website/algorithms/french/stop.txt + | This file is distributed under the BSD License. + | See http://snowball.tartarus.org/license.php + | Also see http://www.opensource.org/licenses/bsd-license.html + | - Encoding was converted to UTF-8. + | - This notice was added. + + | A French stop word list. Comments begin with vertical bar. Each stop + | word is at the start of a line. + +au | a + le +aux | a + les +avec | with +ce | this +ces | these +dans | with +de | of +des | de + les +du | de + le +elle | she +en | `of them' etc +et | and +eux | them +il | he +je | I +la | the +le | the +leur | their +lui | him +ma | my (fem) +mais | but +me | me +même | same; as in moi-même (myself) etc +mes | me (pl) +moi | me +mon | my (masc) +ne | not +nos | our (pl) +notre | our +nous | we +on | one +ou | where +par | by +pas | not +pour | for +qu | que before vowel +que | that +qui | who +sa | his, her (fem) +se | oneself +ses | his (pl) +son | his, her (masc) +sur | on +ta | thy (fem) +te | thee +tes | thy (pl) +toi | thee +ton | thy (masc) +tu | thou +un | a +une | a +vos | your (pl) +votre | your +vous | you + + | single letter forms + +c | c' +d | d' +j | j' +l | l' +à | to, at +m | m' +n | n' +s | s' +t | t' +y | there + + | forms of être (not including the infinitive): +été +étée +étées +étés +étant +suis +es +est +sommes +êtes +sont +serai +seras +sera +serons +serez +seront +serais +serait +serions +seriez +seraient +étais +était +étions +étiez +étaient +fus +fut +fûmes +fûtes +furent +sois +soit +soyons +soyez +soient +fusse +fusses +fût +fussions +fussiez +fussent + + | forms of avoir (not including the infinitive): +ayant +eu +eue +eues +eus +ai +as +avons +avez +ont +aurai +auras +aura +aurons +aurez +auront +aurais +aurait +aurions +auriez +auraient +avais +avait +avions +aviez +avaient +eut +eûmes +eûtes +eurent +aie +aies +ait +ayons +ayez +aient +eusse +eusses +eût +eussions +eussiez +eussent + + | Later additions (from Jean-Christophe Deschamps) +ceci | this +celà  | that +cet | this +cette | this +ici | here +ils | they +les | the (pl) +leurs | their (pl) +quel | which +quels | which +quelle | which +quelles | which +sans | without +soi | oneself + diff --git a/src/ckanext-d4science_theme/ckanext/multilingual/solr/german_stop.txt b/src/ckanext-d4science_theme/ckanext/multilingual/solr/german_stop.txt new file mode 100644 index 0000000..f770384 --- /dev/null +++ b/src/ckanext-d4science_theme/ckanext/multilingual/solr/german_stop.txt @@ -0,0 +1,292 @@ + | From svn.tartarus.org/snowball/trunk/website/algorithms/german/stop.txt + | This file is distributed under the BSD License. + | See http://snowball.tartarus.org/license.php + | Also see http://www.opensource.org/licenses/bsd-license.html + | - Encoding was converted to UTF-8. + | - This notice was added. + + | A German stop word list. Comments begin with vertical bar. Each stop + | word is at the start of a line. + + | The number of forms in this list is reduced significantly by passing it + | through the German stemmer. + + +aber | but + +alle | all +allem +allen +aller +alles + +als | than, as +also | so +am | an + dem +an | at + +ander | other +andere +anderem +anderen +anderer +anderes +anderm +andern +anderr +anders + +auch | also +auf | on +aus | out of +bei | by +bin | am +bis | until +bist | art +da | there +damit | with it +dann | then + +der | the +den +des +dem +die +das + +daß | that + +derselbe | the same +derselben +denselben +desselben +demselben +dieselbe +dieselben +dasselbe + +dazu | to that + +dein | thy +deine +deinem +deinen +deiner +deines + +denn | because + +derer | of those +dessen | of him + +dich | thee +dir | to thee +du | thou + +dies | this +diese +diesem +diesen +dieser +dieses + + +doch | (several meanings) +dort | (over) there + + +durch | through + +ein | a +eine +einem +einen +einer +eines + +einig | some +einige +einigem +einigen +einiger +einiges + +einmal | once + +er | he +ihn | him +ihm | to him + +es | it +etwas | something + +euer | your +eure +eurem +euren +eurer +eures + +für | for +gegen | towards +gewesen | p.p. of sein +hab | have +habe | have +haben | have +hat | has +hatte | had +hatten | had +hier | here +hin | there +hinter | behind + +ich | I +mich | me +mir | to me + + +ihr | you, to her +ihre +ihrem +ihren +ihrer +ihres +euch | to you + +im | in + dem +in | in +indem | while +ins | in + das +ist | is + +jede | each, every +jedem +jeden +jeder +jedes + +jene | that +jenem +jenen +jener +jenes + +jetzt | now +kann | can + +kein | no +keine +keinem +keinen +keiner +keines + +können | can +könnte | could +machen | do +man | one + +manche | some, many a +manchem +manchen +mancher +manches + +mein | my +meine +meinem +meinen +meiner +meines + +mit | with +muss | must +musste | had to +nach | to(wards) +nicht | not +nichts | nothing +noch | still, yet +nun | now +nur | only +ob | whether +oder | or +ohne | without +sehr | very + +sein | his +seine +seinem +seinen +seiner +seines + +selbst | self +sich | herself + +sie | they, she +ihnen | to them + +sind | are +so | so + +solche | such +solchem +solchen +solcher +solches + +soll | shall +sollte | should +sondern | but +sonst | else +über | over +um | about, around +und | and + +uns | us +unse +unsem +unsen +unser +unses + +unter | under +viel | much +vom | von + dem +von | from +vor | before +während | while +war | was +waren | were +warst | wast +was | what +weg | away, off +weil | because +weiter | further + +welche | which +welchem +welchen +welcher +welches + +wenn | when +werde | will +werden | will +wie | how +wieder | again +will | want +wir | we +wird | will +wirst | willst +wo | where +wollen | want +wollte | wanted +würde | would +würden | would +zu | to +zum | zu + dem +zur | zu + der +zwar | indeed +zwischen | between + diff --git a/src/ckanext-d4science_theme/ckanext/multilingual/solr/greek_stopwords.txt b/src/ckanext-d4science_theme/ckanext/multilingual/solr/greek_stopwords.txt new file mode 100644 index 0000000..1a08d31 --- /dev/null +++ b/src/ckanext-d4science_theme/ckanext/multilingual/solr/greek_stopwords.txt @@ -0,0 +1,76 @@ +# Lucene Greek Stopwords list +ο +η +το +οι +τα +του +τησ +των +τον +την +και +κι +κ +ειμαι +εισαι +ειναι +ειμαστε +ειστε +στο +στον +στη +στην +μα +αλλα +απο +για +προσ +με +σε +ωσ +παρα +αντι +κατα +μετα +θα +να +δε +δεν +μη +μην +επι +ενω +εαν +αν +τοτε +που +πωσ +ποιοσ +ποια +ποιο +ποιοι +ποιεσ +ποιων +ποιουσ +αυτοσ +αυτη +αυτο +αυτοι +αυτων +αυτουσ +αυτεσ +αυτα +εκεινοσ +εκεινη +εκεινο +εκεινοι +εκεινεσ +εκεινα +εκεινων +εκεινουσ +οπωσ +ομωσ +ισωσ +οσο +οτι diff --git a/src/ckanext-d4science_theme/ckanext/multilingual/solr/italian_stop.txt b/src/ckanext-d4science_theme/ckanext/multilingual/solr/italian_stop.txt new file mode 100644 index 0000000..4cb5b08 --- /dev/null +++ b/src/ckanext-d4science_theme/ckanext/multilingual/solr/italian_stop.txt @@ -0,0 +1,301 @@ + | From svn.tartarus.org/snowball/trunk/website/algorithms/italian/stop.txt + | This file is distributed under the BSD License. + | See http://snowball.tartarus.org/license.php + | Also see http://www.opensource.org/licenses/bsd-license.html + | - Encoding was converted to UTF-8. + | - This notice was added. + + | An Italian stop word list. Comments begin with vertical bar. Each stop + | word is at the start of a line. + +ad | a (to) before vowel +al | a + il +allo | a + lo +ai | a + i +agli | a + gli +all | a + l' +agl | a + gl' +alla | a + la +alle | a + le +con | with +col | con + il +coi | con + i (forms collo, cogli etc are now very rare) +da | from +dal | da + il +dallo | da + lo +dai | da + i +dagli | da + gli +dall | da + l' +dagl | da + gll' +dalla | da + la +dalle | da + le +di | of +del | di + il +dello | di + lo +dei | di + i +degli | di + gli +dell | di + l' +degl | di + gl' +della | di + la +delle | di + le +in | in +nel | in + el +nello | in + lo +nei | in + i +negli | in + gli +nell | in + l' +negl | in + gl' +nella | in + la +nelle | in + le +su | on +sul | su + il +sullo | su + lo +sui | su + i +sugli | su + gli +sull | su + l' +sugl | su + gl' +sulla | su + la +sulle | su + le +per | through, by +tra | among +contro | against +io | I +tu | thou +lui | he +lei | she +noi | we +voi | you +loro | they +mio | my +mia | +miei | +mie | +tuo | +tua | +tuoi | thy +tue | +suo | +sua | +suoi | his, her +sue | +nostro | our +nostra | +nostri | +nostre | +vostro | your +vostra | +vostri | +vostre | +mi | me +ti | thee +ci | us, there +vi | you, there +lo | him, the +la | her, the +li | them +le | them, the +gli | to him, the +ne | from there etc +il | the +un | a +uno | a +una | a +ma | but +ed | and +se | if +perché | why, because +anche | also +come | how +dov | where (as dov') +dove | where +che | who, that +chi | who +cui | whom +non | not +più | more +quale | who, that +quanto | how much +quanti | +quanta | +quante | +quello | that +quelli | +quella | +quelle | +questo | this +questi | +questa | +queste | +si | yes +tutto | all +tutti | all + + | single letter forms: + +a | at +c | as c' for ce or ci +e | and +i | the +l | as l' +o | or + + | forms of avere, to have (not including the infinitive): + +ho +hai +ha +abbiamo +avete +hanno +abbia +abbiate +abbiano +avrò +avrai +avrà +avremo +avrete +avranno +avrei +avresti +avrebbe +avremmo +avreste +avrebbero +avevo +avevi +aveva +avevamo +avevate +avevano +ebbi +avesti +ebbe +avemmo +aveste +ebbero +avessi +avesse +avessimo +avessero +avendo +avuto +avuta +avuti +avute + + | forms of essere, to be (not including the infinitive): +sono +sei +è +siamo +siete +sia +siate +siano +sarò +sarai +sarà +saremo +sarete +saranno +sarei +saresti +sarebbe +saremmo +sareste +sarebbero +ero +eri +era +eravamo +eravate +erano +fui +fosti +fu +fummo +foste +furono +fossi +fosse +fossimo +fossero +essendo + + | forms of fare, to do (not including the infinitive, fa, fat-): +faccio +fai +facciamo +fanno +faccia +facciate +facciano +farò +farai +farà +faremo +farete +faranno +farei +faresti +farebbe +faremmo +fareste +farebbero +facevo +facevi +faceva +facevamo +facevate +facevano +feci +facesti +fece +facemmo +faceste +fecero +facessi +facesse +facessimo +facessero +facendo + + | forms of stare, to be (not including the infinitive): +sto +stai +sta +stiamo +stanno +stia +stiate +stiano +starò +starai +starà +staremo +starete +staranno +starei +staresti +starebbe +staremmo +stareste +starebbero +stavo +stavi +stava +stavamo +stavate +stavano +stetti +stesti +stette +stemmo +steste +stettero +stessi +stesse +stessimo +stessero +stando diff --git a/src/ckanext-d4science_theme/ckanext/multilingual/solr/polish_stop.txt b/src/ckanext-d4science_theme/ckanext/multilingual/solr/polish_stop.txt new file mode 100644 index 0000000..167e9e0 --- /dev/null +++ b/src/ckanext-d4science_theme/ckanext/multilingual/solr/polish_stop.txt @@ -0,0 +1,186 @@ +# This file was created from the carrot2 project and is distributed under the BSD license. +# See http://project.carrot2.org/license.html +# Also see http://www.opensource.org/licenses/bsd-license.html +# From trunk/core/carrot2-util-text/src-resources/stopwords.pl +vol +o.o. +mgr +godz +zł +www +pl +ul +tel +hab +prof +inż +dr +i +u +aby +albo +ale +ani +aż +bardzo +bez +bo +bowiem +by +byli +bym +był +była +było +były +być +będzie +będą +chce +choć +co +coraz +coś +czy +czyli +często +dla +do +gdy +gdyby +gdyż +gdzie +go +ich +im +inne +iż +ja +jak +jakie +jako +je +jednak +jednym +jedynie +jego +jej +jest +jeszcze +jeśli +jeżeli +już +ją +kiedy +kilku +kto +która +które +którego +której +który +których +którym +którzy +lat +lecz +lub +ma +mają +mamy +mi +miał +mimo +mnie +mogą +może +można +mu +musi +na +nad +nam +nas +nawet +nic +nich +nie +niej +nim +niż +no +nowe +np +nr +o +od +ok +on +one +oraz +pan +po +pod +ponad +ponieważ +poza +przed +przede +przez +przy +raz +razie +roku +również +się +sobie +sposób +swoje +są +ta +tak +takich +takie +także +tam +te +tego +tej +temu +ten +teraz +też +to +trzeba +tu +tych +tylko +tym +tys +tzw +tę +w +we +wie +więc +wszystko +wśród +właśnie +z +za +zaś +ze +że +żeby +ii +iii +iv +vi +vii +viii +ix +xi +xii +xiii +xiv +xv diff --git a/src/ckanext-d4science_theme/ckanext/multilingual/solr/portuguese_stop.txt b/src/ckanext-d4science_theme/ckanext/multilingual/solr/portuguese_stop.txt new file mode 100644 index 0000000..276c1b4 --- /dev/null +++ b/src/ckanext-d4science_theme/ckanext/multilingual/solr/portuguese_stop.txt @@ -0,0 +1,251 @@ + | From svn.tartarus.org/snowball/trunk/website/algorithms/portuguese/stop.txt + | This file is distributed under the BSD License. + | See http://snowball.tartarus.org/license.php + | Also see http://www.opensource.org/licenses/bsd-license.html + | - Encoding was converted to UTF-8. + | - This notice was added. + + | A Portuguese stop word list. Comments begin with vertical bar. Each stop + | word is at the start of a line. + + + | The following is a ranked list (commonest to rarest) of stopwords + | deriving from a large sample of text. + + | Extra words have been added at the end. + +de | of, from +a | the; to, at; her +o | the; him +que | who, that +e | and +do | de + o +da | de + a +em | in +um | a +para | for + | é from SER +com | with +não | not, no +uma | a +os | the; them +no | em + o +se | himself etc +na | em + a +por | for +mais | more +as | the; them +dos | de + os +como | as, like +mas | but + | foi from SER +ao | a + o +ele | he +das | de + as + | tem from TER +à | a + a +seu | his +sua | her +ou | or + | ser from SER +quando | when +muito | much + | há from HAV +nos | em + os; us +já | already, now + | está from EST +eu | I +também | also +só | only, just +pelo | per + o +pela | per + a +até | up to +isso | that +ela | he +entre | between + | era from SER +depois | after +sem | without +mesmo | same +aos | a + os + | ter from TER +seus | his +quem | whom +nas | em + as +me | me +esse | that +eles | they + | estão from EST +você | you + | tinha from TER + | foram from SER +essa | that +num | em + um +nem | nor +suas | her +meu | my +às | a + as +minha | my + | têm from TER +numa | em + uma +pelos | per + os +elas | they + | havia from HAV + | seja from SER +qual | which + | será from SER +nós | we + | tenho from TER +lhe | to him, her +deles | of them +essas | those +esses | those +pelas | per + as +este | this + | fosse from SER +dele | of him + + | other words. There are many contractions such as naquele = em+aquele, + | mo = me+o, but they are rare. + | Indefinite article plural forms are also rare. + +tu | thou +te | thee +vocês | you (plural) +vos | you +lhes | to them +meus | my +minhas +teu | thy +tua +teus +tuas +nosso | our +nossa +nossos +nossas + +dela | of her +delas | of them + +esta | this +estes | these +estas | these +aquele | that +aquela | that +aqueles | those +aquelas | those +isto | this +aquilo | that + + | forms of estar, to be (not including the infinitive): +estou +está +estamos +estão +estive +esteve +estivemos +estiveram +estava +estávamos +estavam +estivera +estivéramos +esteja +estejamos +estejam +estivesse +estivéssemos +estivessem +estiver +estivermos +estiverem + + | forms of haver, to have (not including the infinitive): +hei +há +havemos +hão +houve +houvemos +houveram +houvera +houvéramos +haja +hajamos +hajam +houvesse +houvéssemos +houvessem +houver +houvermos +houverem +houverei +houverá +houveremos +houverão +houveria +houveríamos +houveriam + + | forms of ser, to be (not including the infinitive): +sou +somos +são +era +éramos +eram +fui +foi +fomos +foram +fora +fôramos +seja +sejamos +sejam +fosse +fôssemos +fossem +for +formos +forem +serei +será +seremos +serão +seria +seríamos +seriam + + | forms of ter, to have (not including the infinitive): +tenho +tem +temos +tém +tinha +tínhamos +tinham +tive +teve +tivemos +tiveram +tivera +tivéramos +tenha +tenhamos +tenham +tivesse +tivéssemos +tivessem +tiver +tivermos +tiverem +terei +terá +teremos +terão +teria +teríamos +teriam diff --git a/src/ckanext-d4science_theme/ckanext/multilingual/solr/romanian_stop.txt b/src/ckanext-d4science_theme/ckanext/multilingual/solr/romanian_stop.txt new file mode 100644 index 0000000..4fdee90 --- /dev/null +++ b/src/ckanext-d4science_theme/ckanext/multilingual/solr/romanian_stop.txt @@ -0,0 +1,233 @@ +# This file was created by Jacques Savoy and is distributed under the BSD license. +# See http://members.unine.ch/jacques.savoy/clef/index.html. +# Also see http://www.opensource.org/licenses/bsd-license.html +acea +aceasta +această +aceea +acei +aceia +acel +acela +acele +acelea +acest +acesta +aceste +acestea +aceşti +aceştia +acolo +acum +ai +aia +aibă +aici +al +ăla +ale +alea +ălea +altceva +altcineva +am +ar +are +aş +aşadar +asemenea +asta +ăsta +astăzi +astea +ăstea +ăştia +asupra +aţi +au +avea +avem +aveţi +azi +bine +bucur +bună +ca +că +căci +când +care +cărei +căror +cărui +cât +câte +câţi +către +câtva +ce +cel +ceva +chiar +cînd +cine +cineva +cît +cîte +cîţi +cîtva +contra +cu +cum +cumva +curând +curînd +da +dă +dacă +dar +datorită +de +deci +deja +deoarece +departe +deşi +din +dinaintea +dintr +dintre +drept +după +ea +ei +el +ele +eram +este +eşti +eu +face +fără +fi +fie +fiecare +fii +fim +fiţi +iar +ieri +îi +îl +îmi +împotriva +în +înainte +înaintea +încât +încît +încotro +între +întrucât +întrucît +îţi +la +lângă +le +li +lîngă +lor +lui +mă +mâine +mea +mei +mele +mereu +meu +mi +mine +mult +multă +mulţi +ne +nicăieri +nici +nimeni +nişte +noastră +noastre +noi +noştri +nostru +nu +ori +oricând +oricare +oricât +orice +oricînd +oricine +oricît +oricum +oriunde +până +pe +pentru +peste +pînă +poate +pot +prea +prima +primul +prin +printr +sa +să +săi +sale +sau +său +se +şi +sînt +sîntem +sînteţi +spre +sub +sunt +suntem +sunteţi +ta +tăi +tale +tău +te +ţi +ţie +tine +toată +toate +tot +toţi +totuşi +tu +un +una +unde +undeva +unei +unele +uneori +unor +vă +vi +voastră +voastre +voi +voştri +vostru +vouă +vreo +vreun diff --git a/src/ckanext-d4science_theme/ckanext/multilingual/solr/schema.xml b/src/ckanext-d4science_theme/ckanext/multilingual/solr/schema.xml new file mode 100644 index 0000000..aaef892 --- /dev/null +++ b/src/ckanext-d4science_theme/ckanext/multilingual/solr/schema.xml @@ -0,0 +1,486 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +index_id +text + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/ckanext-d4science_theme/ckanext/multilingual/solr/spanish_stop.txt b/src/ckanext-d4science_theme/ckanext/multilingual/solr/spanish_stop.txt new file mode 100644 index 0000000..2db1476 --- /dev/null +++ b/src/ckanext-d4science_theme/ckanext/multilingual/solr/spanish_stop.txt @@ -0,0 +1,354 @@ + | From svn.tartarus.org/snowball/trunk/website/algorithms/spanish/stop.txt + | This file is distributed under the BSD License. + | See http://snowball.tartarus.org/license.php + | Also see http://www.opensource.org/licenses/bsd-license.html + | - Encoding was converted to UTF-8. + | - This notice was added. + + | A Spanish stop word list. Comments begin with vertical bar. Each stop + | word is at the start of a line. + + + | The following is a ranked list (commonest to rarest) of stopwords + | deriving from a large sample of text. + + | Extra words have been added at the end. + +de | from, of +la | the, her +que | who, that +el | the +en | in +y | and +a | to +los | the, them +del | de + el +se | himself, from him etc +las | the, them +por | for, by, etc +un | a +para | for +con | with +no | no +una | a +su | his, her +al | a + el + | es from SER +lo | him +como | how +más | more +pero | pero +sus | su plural +le | to him, her +ya | already +o | or + | fue from SER +este | this + | ha from HABER +sí | himself etc +porque | because +esta | this + | son from SER +entre | between + | está from ESTAR +cuando | when +muy | very +sin | without +sobre | on + | ser from SER + | tiene from TENER +también | also +me | me +hasta | until +hay | there is/are +donde | where + | han from HABER +quien | whom, that + | están from ESTAR + | estado from ESTAR +desde | from +todo | all +nos | us +durante | during + | estados from ESTAR +todos | all +uno | a +les | to them +ni | nor +contra | against +otros | other + | fueron from SER +ese | that +eso | that + | había from HABER +ante | before +ellos | they +e | and (variant of y) +esto | this +mí | me +antes | before +algunos | some +qué | what? +unos | a +yo | I +otro | other +otras | other +otra | other +él | he +tanto | so much, many +esa | that +estos | these +mucho | much, many +quienes | who +nada | nothing +muchos | many +cual | who + | sea from SER +poco | few +ella | she +estar | to be + | haber from HABER +estas | these + | estaba from ESTAR + | estamos from ESTAR +algunas | some +algo | something +nosotros | we + + | other forms + +mi | me +mis | mi plural +tú | thou +te | thee +ti | thee +tu | thy +tus | tu plural +ellas | they +nosotras | we +vosotros | you +vosotras | you +os | you +mío | mine +mía | +míos | +mías | +tuyo | thine +tuya | +tuyos | +tuyas | +suyo | his, hers, theirs +suya | +suyos | +suyas | +nuestro | ours +nuestra | +nuestros | +nuestras | +vuestro | yours +vuestra | +vuestros | +vuestras | +esos | those +esas | those + + | forms of estar, to be (not including the infinitive): +estoy +estás +está +estamos +estáis +están +esté +estés +estemos +estéis +estén +estaré +estarás +estará +estaremos +estaréis +estarán +estaría +estarías +estaríamos +estaríais +estarían +estaba +estabas +estábamos +estabais +estaban +estuve +estuviste +estuvo +estuvimos +estuvisteis +estuvieron +estuviera +estuvieras +estuviéramos +estuvierais +estuvieran +estuviese +estuvieses +estuviésemos +estuvieseis +estuviesen +estando +estado +estada +estados +estadas +estad + + | forms of haber, to have (not including the infinitive): +he +has +ha +hemos +habéis +han +haya +hayas +hayamos +hayáis +hayan +habré +habrás +habrá +habremos +habréis +habrán +habría +habrías +habríamos +habríais +habrían +había +habías +habíamos +habíais +habían +hube +hubiste +hubo +hubimos +hubisteis +hubieron +hubiera +hubieras +hubiéramos +hubierais +hubieran +hubiese +hubieses +hubiésemos +hubieseis +hubiesen +habiendo +habido +habida +habidos +habidas + + | forms of ser, to be (not including the infinitive): +soy +eres +es +somos +sois +son +sea +seas +seamos +seáis +sean +seré +serás +será +seremos +seréis +serán +sería +serías +seríamos +seríais +serían +era +eras +éramos +erais +eran +fui +fuiste +fue +fuimos +fuisteis +fueron +fuera +fueras +fuéramos +fuerais +fueran +fuese +fueses +fuésemos +fueseis +fuesen +siendo +sido + | sed also means 'thirst' + + | forms of tener, to have (not including the infinitive): +tengo +tienes +tiene +tenemos +tenéis +tienen +tenga +tengas +tengamos +tengáis +tengan +tendré +tendrás +tendrá +tendremos +tendréis +tendrán +tendría +tendrías +tendríamos +tendríais +tendrían +tenía +tenías +teníamos +teníais +tenían +tuve +tuviste +tuvo +tuvimos +tuvisteis +tuvieron +tuviera +tuvieras +tuviéramos +tuvierais +tuvieran +tuviese +tuvieses +tuviésemos +tuvieseis +tuviesen +teniendo +tenido +tenida +tenidos +tenidas +tened + diff --git a/src/ckanext-d4science_theme/ckanext/multilingual/tests/test_multilingual_plugin.py b/src/ckanext-d4science_theme/ckanext/multilingual/tests/test_multilingual_plugin.py new file mode 100644 index 0000000..c781a7e --- /dev/null +++ b/src/ckanext-d4science_theme/ckanext/multilingual/tests/test_multilingual_plugin.py @@ -0,0 +1,239 @@ +# encoding: utf-8 + +import pytest + +import ckan.plugins +import ckanext.multilingual.plugin as mulilingual_plugin +import ckan.lib.helpers as h +import ckan.lib.create_test_data +import ckan.model as model +from ckan.tests.helpers import body_contains, call_action + +_create_test_data = ckan.lib.create_test_data +ORG_NAME = "test_org" + + +@pytest.fixture +def prepare(clean_db, clean_index, with_request_context): + _create_test_data.CreateTestData.create_translations_test_data() + + sysadmin_user = model.User.get("testsysadmin") + org = { + "name": ORG_NAME, + "title": "russian", + "description": "Roger likes these books.", + } + context = {"user": sysadmin_user.name} + call_action("organization_create", context, **org) + dataset = { + "name": "test_org_dataset", + "title": "A Novel By Tolstoy", + "owner_org": org["name"], + } + context = {"user": sysadmin_user.name} + call_action("package_create", context, **dataset) + + # Add translation terms that match a couple of group names and package + # names. Group names and package names should _not_ get translated even + # if there are terms matching them, because they are used to form URLs. + for term in ("roger", "david", "annakarenina", "warandpeace"): + for lang_code in ("en", "de", "fr"): + data_dict = { + "term": term, + "term_translation": "this should not be rendered", + "lang_code": lang_code, + } + call_action("term_translation_update", **data_dict) + + +@pytest.mark.usefixtures("prepare", "with_plugins") +@pytest.mark.ckan_config( + "ckan.plugins", "multilingual_dataset multilingual_group multilingual_tag" +) +class TestDatasetTermTranslation: + "Test the translation of datasets by the multilingual_dataset plugin." + + def test_user_read_translation(self, app): + """Test the translation of datasets on user view pages by the + multilingual_dataset plugin. + + """ + + # It is testsysadmin who created the dataset, so testsysadmin whom + # we'd expect to see the datasets for. + for user_name in ("testsysadmin",): + offset = str(h.url_for("user.read", id=user_name)) + for (lang_code, translations) in ( + ("de", _create_test_data.german_translations), + ("fr", _create_test_data.french_translations), + ("en", _create_test_data.english_translations), + ("pl", {}), + ): + response = app.get( + offset, + status=200, + extra_environ={ + "CKAN_LANG": lang_code, + "CKAN_CURRENT_URL": offset, + }, + ) + terms = "A Novel By Tolstoy" + for term in terms: + if term in translations: + assert body_contains(response, translations[term]) + elif term in _create_test_data.english_translations: + assert body_contains( + response, + _create_test_data.english_translations[term], + ) + else: + assert body_contains(response, term) + assert not body_contains( + response, "this should not be rendered" + ) + + def test_org_read_translation(self, app): + for (lang_code, translations) in ( + ("de", _create_test_data.german_translations), + ("fr", _create_test_data.french_translations), + ("en", _create_test_data.english_translations), + ("pl", {}), + ): + offset = "/{0}/organization/{1}".format(lang_code, ORG_NAME) + response = app.get(offset, status=200) + terms = ( + "A Novel By Tolstoy", + "russian", + "Roger likes these books.", + ) + for term in terms: + if term in translations: + assert body_contains(response, translations[term]) + elif term in _create_test_data.english_translations: + assert body_contains( + response, _create_test_data.english_translations[term] + ) + else: + assert body_contains(response, term) + assert not body_contains(response, "this should not be rendered") + + def test_org_index_translation(self, app): + for (lang_code, translations) in ( + ("de", _create_test_data.german_translations), + ("fr", _create_test_data.french_translations), + ("en", _create_test_data.english_translations), + ("pl", {}), + ): + offset = "/{0}/organization".format(lang_code) + response = app.get(offset, status=200) + for term in ("russian", "Roger likes these books."): + if term in translations: + assert body_contains(response, translations[term]) + elif term in _create_test_data.english_translations: + assert body_contains( + response, _create_test_data.english_translations[term] + ) + else: + assert body_contains(response, term) + assert body_contains( + response, "/{0}/organization/{1}".format(lang_code, ORG_NAME) + ) + assert not body_contains(response, "this should not be rendered") + + +@pytest.mark.ckan_config( + "ckan.plugins", "multilingual_dataset multilingual_group multilingual_tag" +) +@pytest.mark.usefixtures("clean_db", "clean_index", "with_plugins") +class TestDatasetSearchIndex(object): + def test_translate_terms(self): + data_dicts = [ + { + "term": "moo", + "term_translation": "french_moo", + "lang_code": "fr", + }, + { + "term": "moo", + "term_translation": "this should not be rendered", + "lang_code": "fsdas", + }, + { + "term": "an interesting note", + "term_translation": "french note", + "lang_code": "fr", + }, + { + "term": "moon", + "term_translation": "french moon", + "lang_code": "fr", + }, + { + "term": "boon", + "term_translation": "french boon", + "lang_code": "fr", + }, + { + "term": "boon", + "term_translation": "italian boon", + "lang_code": "it", + }, + { + "term": "david", + "term_translation": "french david", + "lang_code": "fr", + }, + { + "term": "david", + "term_translation": "italian david", + "lang_code": "it", + }, + ] + + for data_dict in data_dicts: + call_action("term_translation_update", **data_dict) + + sample_index_data = { + "download_url": u"moo", + "notes": u"an interesting note", + "tags": [u"moon", "boon"], + "title": u"david", + } + + result = mulilingual_plugin.MultilingualDataset().before_dataset_index( + sample_index_data + ) + assert result == { + "text_sr@latin": "", + "text_fi": "", + "text_de": "", + "text_pt_BR": "", + u"title_fr": u"french david", + "text_fr": u"french_moo french note french moon french boon", + "text_ja": "", + "text_sr": "", + "title": u"david", + "text_ca": "", + "download_url": u"moo", + "text_hu": "", + "text_sa": "", + "text_cs_CZ": "", + "text_nl": "", + "text_no": "", + "text_ko_KR": "", + "text_sk": "", + "text_bg": "", + "text_sv": "", + "tags": [u"moon", "boon"], + "text_el": "", + "title_en": u"david", + "text_en": u"moo an interesting note moon boon", + "text_es": "", + "text_sl": "", + "text_pl": "", + "notes": u"an interesting note", + "text_lv": "", + "text_it": u"italian boon", + u"title_it": u"italian david", + "text_ru": "", + }, result diff --git a/src/ckanext-d4science_theme/ckanext/reclineview/__init__.py b/src/ckanext-d4science_theme/ckanext/reclineview/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/src/ckanext-d4science_theme/ckanext/reclineview/config_declaration.yaml b/src/ckanext-d4science_theme/ckanext/reclineview/config_declaration.yaml new file mode 100644 index 0000000..6222a58 --- /dev/null +++ b/src/ckanext-d4science_theme/ckanext/reclineview/config_declaration.yaml @@ -0,0 +1,14 @@ +version: 1 +groups: +- annotation: recline_view settings + options: + - key: ckan.recline.dataproxy_url + default: //jsonpdataproxy.appspot.com + example: https://mydataproxy.example.com + description: > + Custom URL to a self-hosted DataProxy instance. The DataProxy is an + external service currently used to stream data in JSON format to the + Recline-based views when data is not on the DataStore. The main instance + is deprecated and will be eventually shut down, so users that require it + can host an instance themselves and use this configuration option to + point Recline to it. diff --git a/src/ckanext-d4science_theme/ckanext/reclineview/plugin.py b/src/ckanext-d4science_theme/ckanext/reclineview/plugin.py new file mode 100644 index 0000000..57e9aa9 --- /dev/null +++ b/src/ckanext-d4science_theme/ckanext/reclineview/plugin.py @@ -0,0 +1,306 @@ +# encoding: utf-8 +from __future__ import annotations +import os + +import yaml + +from ckan.types import Context, Validator +from logging import getLogger +from typing import Any, Callable, Container + +from ckan.common import CKANConfig, json, config + +import ckan.plugins as p +import ckan.plugins.toolkit as toolkit +from ckan.plugins.toolkit import _ + +log = getLogger(__name__) +ignore_empty = p.toolkit.get_validator('ignore_empty') +natural_number_validator = p.toolkit.get_validator('natural_number_validator') +Invalid = p.toolkit.Invalid + + +def get_mapview_config() -> dict[str, Any]: + ''' + Extracts and returns map view configuration of the reclineview extension. + ''' + namespace = 'ckanext.spatial.common_map.' + return {k.replace(namespace, ''): v + for k, v in config.items() + if k.startswith(namespace)} + + +def get_dataproxy_url() -> str: + ''' + Returns the value of the ckan.recline.dataproxy_url config option + ''' + return config.get('ckan.recline.dataproxy_url') + + +def in_list(list_possible_values: Callable[[], Container[Any]]) -> Validator: + ''' + Validator that checks that the input value is one of the given + possible values. + + :param list_possible_values: function that returns list of possible values + for validated field + :type possible_values: function + ''' + def validate(value: Any): + if value not in list_possible_values(): + raise Invalid('"{0}" is not a valid parameter'.format(value)) + return validate + + +def datastore_fields(resource: dict[str, Any], + valid_field_types: Container[str]): + ''' + Return a list of all datastore fields for a given resource, as long as + the datastore field type is in valid_field_types. + + :param resource: resource dict + :type resource: dict + :param valid_field_types: field types to include in returned list + :type valid_field_types: list of strings + ''' + data = {'resource_id': resource['id'], 'limit': 0} + fields = toolkit.get_action('datastore_search')({}, data)['fields'] + return [{'value': f['id'], 'text': f['id']} for f in fields + if f['type'] in valid_field_types] + + +def _load_declaration(declaration: Any): + filename = os.path.join( + os.path.dirname(__file__), + "config_declaration.yaml" + ) + with open(filename) as src: + data = yaml.safe_load(src) + + try: + declaration.load_dict(data) + except ValueError: + # we a loading two recline plugins that are share config declaration. + pass + + +class ReclineViewBase(p.SingletonPlugin): + ''' + This base class for the Recline view extensions. + ''' + p.implements(p.IConfigurer, inherit=True) + p.implements(p.IResourceView, inherit=True) + p.implements(p.ITemplateHelpers, inherit=True) + + def update_config(self, config: CKANConfig): + ''' + Set up the resource library, public directory and + template directory for the view + ''' + toolkit.add_public_directory(config, 'theme/public') + toolkit.add_template_directory(config, 'theme/templates') + toolkit.add_resource('theme/public', 'ckanext-reclineview') + + log.warning( + "The Recline-based views are deprecated and" + "will be removed in future versions" + ) + + def can_view(self, data_dict: dict[str, Any]): + resource = data_dict['resource'] + return (resource.get('datastore_active') or + '_datastore_only_resource' in resource.get('url', '')) + + def setup_template_variables(self, context: Context, + data_dict: dict[str, Any]): + return {'resource_json': json.dumps(data_dict['resource']), + 'resource_view_json': json.dumps(data_dict['resource_view'])} + + def view_template(self, context: Context, data_dict: dict[str, Any]): + return 'recline_view.html' + + def get_helpers(self) -> dict[str, Callable[..., Any]]: + return { + 'get_map_config': get_mapview_config, + 'get_dataproxy_url': get_dataproxy_url, + } + + +class ReclineView(ReclineViewBase): + ''' + This extension views resources using a Recline MultiView. + ''' + p.implements(p.IConfigDeclaration) + + def declare_config_options(self, declaration: Any, key: Any): + _load_declaration(declaration) + + def info(self) -> dict[str, Any]: + return {'name': 'recline_view', + 'title': _('Data Explorer'), + 'filterable': True, + 'icon': 'table', + 'requires_datastore': False, + 'default_title': p.toolkit._('Data Explorer'), + } + + def can_view(self, data_dict: dict[str, Any]): + resource = data_dict['resource'] + + if (resource.get('datastore_active') or + '_datastore_only_resource' in resource.get('url', '')): + return True + resource_format = resource.get('format', None) + if resource_format: + return resource_format.lower() in [ + 'csv', 'xls', 'xlsx', 'ods', 'tsv' + ] + else: + return False + + +class ReclineGridView(ReclineViewBase): + ''' + This extension views resources using a Recline grid. + ''' + p.implements(p.IConfigDeclaration) + + def declare_config_options(self, declaration: Any, key: Any): + _load_declaration(declaration) + + def info(self) -> dict[str, Any]: + return {'name': 'recline_grid_view', + 'title': _('Grid'), + 'filterable': True, + 'icon': 'table', + 'requires_datastore': True, + 'default_title': p.toolkit._('Table'), + } + + +class ReclineGraphView(ReclineViewBase): + ''' + This extension views resources using a Recline graph. + ''' + p.implements(p.IConfigDeclaration) + + def declare_config_options(self, declaration: Any, key: Any): + _load_declaration(declaration) + + graph_types = [{'value': 'lines-and-points', + 'text': 'Lines and points'}, + {'value': 'lines', 'text': 'Lines'}, + {'value': 'points', 'text': 'Points'}, + {'value': 'bars', 'text': 'Bars'}, + {'value': 'columns', 'text': 'Columns'}] + + datastore_fields = [] + + datastore_field_types = ['numeric', 'int4', 'timestamp'] + + def list_graph_types(self): + return [t['value'] for t in self.graph_types] + + def list_datastore_fields(self): + return [t['value'] for t in self.datastore_fields] + + def info(self) -> dict[str, Any]: + # in_list validator here is passed functions because this + # method does not know what the possible values of the + # datastore fields are (requires a datastore search) + schema = { + 'offset': [ignore_empty, natural_number_validator], + 'limit': [ignore_empty, natural_number_validator], + 'graph_type': [ignore_empty, in_list(self.list_graph_types)], + 'group': [ignore_empty, in_list(self.list_datastore_fields)], + 'series': [ignore_empty, in_list(self.list_datastore_fields)] + } + return {'name': 'recline_graph_view', + 'title': _('Graph'), + 'filterable': True, + 'icon': 'chart-bar', + 'requires_datastore': True, + 'schema': schema, + 'default_title': p.toolkit._('Graph'), + } + + def setup_template_variables(self, context: Context, + data_dict: dict[str, Any]): + self.datastore_fields = datastore_fields(data_dict['resource'], + self.datastore_field_types) + vars: dict[str, Any] = ReclineViewBase.setup_template_variables( + self, context, data_dict) + vars.update({'graph_types': self.graph_types, + 'graph_fields': self.datastore_fields}) + return vars + + def form_template(self, context: Context, data_dict: dict[str, Any]): + return 'recline_graph_form.html' + + +class ReclineMapView(ReclineViewBase): + ''' + This extension views resources using a Recline map. + ''' + + map_field_types = [{'value': 'lat_long', + 'text': 'Latitude / Longitude fields'}, + {'value': 'geojson', 'text': 'GeoJSON'}] + + datastore_fields = [] + + datastore_field_latlon_types = ['numeric', 'text'] + + datastore_field_geojson_types = ['text'] + + def list_map_field_types(self): + return [t['value'] for t in self.map_field_types] + + def list_datastore_fields(self): + return [t['value'] for t in self.datastore_fields] + + def info(self) -> dict[str, Any]: + # in_list validator here is passed functions because this + # method does not know what the possible values of the + # datastore fields are (requires a datastore search) + schema = { + 'offset': [ignore_empty, natural_number_validator], + 'limit': [ignore_empty, natural_number_validator], + 'map_field_type': [ignore_empty, + in_list(self.list_map_field_types)], + 'latitude_field': [ignore_empty, + in_list(self.list_datastore_fields)], + 'longitude_field': [ignore_empty, + in_list(self.list_datastore_fields)], + 'geojson_field': [ignore_empty, + in_list(self.list_datastore_fields)], + 'auto_zoom': [ignore_empty], + 'cluster_markers': [ignore_empty] + } + return {'name': 'recline_map_view', + 'title': _('Map'), + 'schema': schema, + 'filterable': True, + 'icon': 'map-marker', + 'default_title': p.toolkit._('Map'), + } + + def setup_template_variables(self, context: Context, + data_dict: dict[str, Any]): + map_latlon_fields = datastore_fields( + data_dict['resource'], self.datastore_field_latlon_types) + map_geojson_fields = datastore_fields( + data_dict['resource'], self.datastore_field_geojson_types) + + self.datastore_fields = map_latlon_fields + map_geojson_fields + + vars: dict[str, Any] = ReclineViewBase.setup_template_variables( + self, context, data_dict) + vars.update({'map_field_types': self.map_field_types, + 'map_latlon_fields': map_latlon_fields, + 'map_geojson_fields': map_geojson_fields + }) + return vars + + def form_template(self, context: Context, data_dict: dict[str, Any]): + return 'recline_map_form.html' diff --git a/src/ckanext-d4science_theme/ckanext/reclineview/tests/__init__.py b/src/ckanext-d4science_theme/ckanext/reclineview/tests/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/src/ckanext-d4science_theme/ckanext/reclineview/tests/test_view.py b/src/ckanext-d4science_theme/ckanext/reclineview/tests/test_view.py new file mode 100644 index 0000000..916771e --- /dev/null +++ b/src/ckanext-d4science_theme/ckanext/reclineview/tests/test_view.py @@ -0,0 +1,152 @@ +# encoding: utf-8 + +import pytest + +import ckan.model as model +import ckan.plugins as p +import ckan.lib.helpers as h +import ckanext.reclineview.plugin as plugin +import ckan.lib.create_test_data as create_test_data + +from ckan.tests import helpers, factories + + +@pytest.mark.usefixtures("with_plugins", "with_request_context") +class BaseTestReclineViewBase(object): + + @pytest.fixture(autouse=True) + def initial_data(self, clean_db, with_request_context): + create_test_data.CreateTestData.create() + self.p = self.view_class() + self.resource_view, self.package, self.resource_id = \ + _create_test_view(self.view_type) + + def test_can_view(self): + data_dict = {'resource': {'datastore_active': True}} + assert self.p.can_view(data_dict) + + data_dict = {'resource': {'datastore_active': False}} + assert not self.p.can_view(data_dict) + + def test_title_description_iframe_shown(self, app): + url = h.url_for('{}_resource.read'.format(self.package.type), + id=self.package.name, resource_id=self.resource_id) + result = app.get(url) + assert self.resource_view['title'] in result + assert self.resource_view['description'] in result + assert 'data-module="data-viewer"' in result.body + + +@pytest.mark.ckan_config('ckan.plugins', 'recline_view') +class TestReclineView(BaseTestReclineViewBase): + view_type = 'recline_view' + view_class = plugin.ReclineView + + def test_it_has_no_schema(self): + schema = self.p.info().get('schema') + assert schema is None, schema + + def test_can_view_format_no_datastore(self): + ''' + Test can_view with acceptable formats when datastore_active is False + (DataProxy in use). + ''' + formats = ['CSV', 'XLS', 'TSV', 'csv', 'xls', 'tsv'] + for resource_format in formats: + data_dict = {'resource': {'datastore_active': False, + 'format': resource_format}} + assert self.p.can_view(data_dict) + + def test_can_view_bad_format_no_datastore(self): + ''' + Test can_view with incorrect formats when datastore_active is False. + ''' + formats = ['TXT', 'txt', 'doc', 'JSON'] + for resource_format in formats: + data_dict = {'resource': {'datastore_active': False, + 'format': resource_format}} + assert not self.p.can_view(data_dict) + + +@pytest.mark.ckan_config('ckan.plugins', 'recline_view datastore') +@pytest.mark.ckan_config('ckan.views.default_views', 'recline_view') +@pytest.mark.usefixtures("clean_db", "with_plugins") +class TestReclineViewDatastoreOnly(object): + + def test_create_datastore_only_view(self, app): + dataset = factories.Dataset() + data = { + 'resource': {'package_id': dataset['id']}, + 'fields': [{'id': 'a'}, {'id': 'b'}], + 'records': [{'a': 1, 'b': 'xyz'}, {'a': 2, 'b': 'zzz'}] + } + result = helpers.call_action('datastore_create', **data) + + resource_id = result['resource_id'] + + url = h.url_for('{}_resource.read'.format(dataset['type']), + id=dataset['id'], resource_id=resource_id) + + result = app.get(url) + + assert 'data-module="data-viewer"' in result.body + + +@pytest.mark.ckan_config('ckan.plugins', 'recline_grid_view') +class TestReclineGridView(BaseTestReclineViewBase): + view_type = 'recline_grid_view' + view_class = plugin.ReclineGridView + + def test_it_has_no_schema(self): + schema = self.p.info().get('schema') + assert schema is None, schema + + +@pytest.mark.ckan_config('ckan.plugins', 'recline_graph_view') +class TestReclineGraphView(BaseTestReclineViewBase): + view_type = 'recline_graph_view' + view_class = plugin.ReclineGraphView + + def test_it_has_the_correct_schema_keys(self): + schema = self.p.info().get('schema') + expected_keys = ['offset', 'limit', 'graph_type', 'group', 'series'] + _assert_schema_exists_and_has_keys(schema, expected_keys) + + +@pytest.mark.ckan_config('ckan.plugins', 'recline_map_view') +class TestReclineMapView(BaseTestReclineViewBase): + view_type = 'recline_map_view' + view_class = plugin.ReclineMapView + + def test_it_has_the_correct_schema_keys(self): + schema = self.p.info().get('schema') + expected_keys = ['offset', 'limit', 'map_field_type', + 'latitude_field', 'longitude_field', 'geojson_field', + 'auto_zoom', 'cluster_markers'] + _assert_schema_exists_and_has_keys(schema, expected_keys) + + +def _create_test_view(view_type): + context = {'model': model, + 'session': model.Session, + 'user': model.User.get('testsysadmin').name} + + package = model.Package.get('annakarenina') + resource_id = package.resources[1].id + resource_view = {'resource_id': resource_id, + 'view_type': view_type, + 'title': u'Test View', + 'description': u'A nice test view'} + resource_view = p.toolkit.get_action('resource_view_create')( + context, resource_view) + return resource_view, package, resource_id + + +def _assert_schema_exists_and_has_keys(schema, expected_keys): + assert schema is not None, schema + + keys = list(schema.keys()) + keys.sort() + expected_keys.sort() + + assert keys == expected_keys, '%s != %s' % (keys, expected_keys) diff --git a/src/ckanext-d4science_theme/ckanext/reclineview/theme/public/css/recline.css b/src/ckanext-d4science_theme/ckanext/reclineview/theme/public/css/recline.css new file mode 100644 index 0000000..0a8aea0 --- /dev/null +++ b/src/ckanext-d4science_theme/ckanext/reclineview/theme/public/css/recline.css @@ -0,0 +1,380 @@ +body { + background-color: #fff; +} + +.recline-data-explorer { + position: relative; + overflow: auto; +} + +.recline-record-count { + float: left; + margin-left: 10px; + line-height: 26px; +} + +.loading-dialog { + width: 120px; +} + +.recline-slickgrid { + height: 600px; +} +/* +.recline-timeline .vmm-timeline { + height: 600px; +} + +.recline-pager .pagination { + margin: 0; + float: left; +} + +.recline-pager .pagination a { + line-height: 26px; +} + +.recline-pager .pagination input { + width: 30px; + margin: 0; + padding: 2px 4px; +} +*/ +/* +.recline-query-editor { + float: right; +} +*/ +.loading-spinner { + float: right; + background-image: url('../img/ajaxload-circle.gif'); + width: 32px; + height: 32px; +} + +.recline-data-explorer .data-view-sidebar { + float: right; + margin-left: 8px; + width: 220px; +} + +.recline-data-explorer .header .navigation { + margin-bottom: 8px; +} + +.recline-data-explorer .header .navigation, +.recline-data-explorer .header .pagination, +.recline-data-explorer .header .pagination form +{ + display: inline; +} + +.recline-data-explorer .header .navigation { + float: left; +} + +.recline-data-explorer .header .menu-right { + float: right; + margin-left: 5px; + padding-left: 5px; +} + +.recline-results-info { + line-height: 35px; + margin-left: 20px; + float: left; +} + +.recline-data-explorer .data-view-sidebar > div { + margin-top: 5px; + margin-bottom: 10px; +} + +.recline-data-explorer .radio, +.recline-data-explorer .checkbox { + padding-left: 20px; +} + +.recline-data-explorer .editor-update-map { + margin: 30px 0px 20px 0px; +} + +.recline-data-explorer label { + font-weight: normal; +} + +/********************************************************** + * Query Editor + *********************************************************/ + +.recline-query-editor { + float: right; + height: 35px; + padding-right: 5px; + margin-right: 5px; + border-right: solid 2px #ddd; +} + +.header .input-prepend { + margin-bottom: auto; +} + +.header .add-on { + float: left; +} + +/* needed for Chrome but not FF */ +.header .add-on { + margin-left: -27px; +} + +/* needed for FF but not chrome */ +.header .input-prepend { + vertical-align: top; +} + +.recline-query-editor form button { + vertical-align: top; +} + +/* label for screen reader */ +.recline-query-editor .form-inline label { + position: absolute; + top:0; + left:-9999px +} + +/********************************************************** + * Pager + *********************************************************/ + +.recline-pager { + float: left; + margin: auto; + display: block; + margin-left: 20px; +} + +.recline-pager .pagination li { + display: inline-block; +} + +.recline-pager .pagination label { + display:none; +} + +.recline-pager .pagination input { + width: 40px; + height: 25px; + padding: 2px 4px; + margin: 0; + margin-top: -2px; + + border: 1px solid #cccccc; + box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); + transition: border linear 0.2s, box-shadow linear 0.2s; + border-radius: 4px; + + -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); + -webkit-transition: border linear 0.2s, box-shadow linear 0.2s; + -webkit-border-radius: 4px; + + -moz-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); + -moz-transition: border linear 0.2s, box-shadow linear 0.2s; + -moz-border-radius: 4px; + + -o-transition: border linear 0.2s, box-shadow linear 0.2s; +} + +.recline-pager .pagination a { + float: none; + margin-left: -5px; + color: #555; +} + +.recline-pager .pagination .page-range { + height: 34px; + padding: 5px 8px; + margin-left: -5px; + border: 1px solid #ddd; +} + +.recline-pager .pagination .page-range a { + padding: 0px 12px; + border: none; +} + +.recline-pager .pagination .page-range a:hover { + background-color: #ffffff; +} + +.recline-pager .pagination > li:first-child > a { + border-bottom-left-radius: 4px; + border-top-left-radius: 4px; + border-bottom-right-radius: 0px; + border-top-right-radius: 0px; + height: 34px; +} + +.recline-pager .pagination > li:last-child > a { + border-bottom-right-radius: 4px; + border-top-right-radius: 4px; + border-bottom-left-radius: 0px; + border-top-left-radius: 0px; + height: 34px; +} + +/********************************************************** + * Filter Editor + *********************************************************/ + +.recline-filter-editor { + padding: 8px; + display: none; +} + +.recline-filter-editor .filters { + margin: 20px 0px; +} + +.recline-filter-editor h3 { + margin-top: 4px; +} + +.recline-filter-editor .filter { + margin-top: 20px; +} + +.recline-filter-editor .filter .form-group { + margin-bottom: 0px; +} + +.recline-filter-editor .filter input, +.recline-filter-editor .filter label { + margin: 0px; +} + +.recline-filter-editor .js-edit button { + margin: 25px 0px 0px 0px; +} + +.recline-filter-editor .filter-term a { + font-size: 18px; +} + +.recline-filter-editor input, +.recline-filter-editor select +{ + width: 175px; +} + +.recline-filter-editor input { + margin-top: 0.5em; + margin-bottom: 10px; + -webkit-border-radius: 4px; + -moz-border-radius: 4px; + border-radius: 4px; + border: 1px solid #cccccc; +} + +.recline-filter-editor label { + font-weight: normal; + display: block; +} + +.recline-filter-editor legend { + margin-bottom: 5px; +} + +.recline-filter-editor .add-filter { + margin-top: 1em; + margin-bottom: 2em; +} + +.recline-filter-editor .update-filter { + margin-top: 1em; +} + +/********************************************************** + * Fields Widget + *********************************************************/ + +.recline-fields-view { + display: none; +} + +.recline-fields-view .fields-list { + padding: 0; +} + +.recline-fields-view .panel { + background-color: #f5f5f5; + border: 1px solid #e5e5e5; +} + +.recline-fields-view .panel-group h3 { + padding-left: 10px; +} + +.recline-fields-view .fields-list .panel-heading { + padding: 2px 5px; + margin: 1px 0px 1px 5px; +} + +.recline-fields-view .panel a, +.recline-fields-view .panel h4 { + display: inline; +} + +.recline-fields-view .panel a { + padding: 0; +} + +.recline-fields-view .panel h4 { + word-wrap: break-word +} + +.recline-fields-view .clear { + clear: both; +} + +.recline-fields-view .facet-items { + list-style-type: none; + margin-left: 0; +} + +.recline-fields-view .facet-item .term { + font-weight: bold; +} + +.recline-fields-view .facet-item .count { +} + +/********************************************************** + * Notifications + *********************************************************/ + +.recline-data-explorer .notification-loader { + width: 18px; + margin-left: 5px; + background-image: url(data:image/gif;base64,R0lGODlhEAAQAPQAAP///wAAAPDw8IqKiuDg4EZGRnp6egAAAFhYWCQkJKysrL6+vhQUFJycnAQEBDY2NmhoaAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACH/C05FVFNDQVBFMi4wAwEAAAAh/hpDcmVhdGVkIHdpdGggYWpheGxvYWQuaW5mbwAh+QQJCgAAACwAAAAAEAAQAAAFdyAgAgIJIeWoAkRCCMdBkKtIHIngyMKsErPBYbADpkSCwhDmQCBethRB6Vj4kFCkQPG4IlWDgrNRIwnO4UKBXDufzQvDMaoSDBgFb886MiQadgNABAokfCwzBA8LCg0Egl8jAggGAA1kBIA1BAYzlyILczULC2UhACH5BAkKAAAALAAAAAAQABAAAAV2ICACAmlAZTmOREEIyUEQjLKKxPHADhEvqxlgcGgkGI1DYSVAIAWMx+lwSKkICJ0QsHi9RgKBwnVTiRQQgwF4I4UFDQQEwi6/3YSGWRRmjhEETAJfIgMFCnAKM0KDV4EEEAQLiF18TAYNXDaSe3x6mjidN1s3IQAh+QQJCgAAACwAAAAAEAAQAAAFeCAgAgLZDGU5jgRECEUiCI+yioSDwDJyLKsXoHFQxBSHAoAAFBhqtMJg8DgQBgfrEsJAEAg4YhZIEiwgKtHiMBgtpg3wbUZXGO7kOb1MUKRFMysCChAoggJCIg0GC2aNe4gqQldfL4l/Ag1AXySJgn5LcoE3QXI3IQAh+QQJCgAAACwAAAAAEAAQAAAFdiAgAgLZNGU5joQhCEjxIssqEo8bC9BRjy9Ag7GILQ4QEoE0gBAEBcOpcBA0DoxSK/e8LRIHn+i1cK0IyKdg0VAoljYIg+GgnRrwVS/8IAkICyosBIQpBAMoKy9dImxPhS+GKkFrkX+TigtLlIyKXUF+NjagNiEAIfkECQoAAAAsAAAAABAAEAAABWwgIAICaRhlOY4EIgjH8R7LKhKHGwsMvb4AAy3WODBIBBKCsYA9TjuhDNDKEVSERezQEL0WrhXucRUQGuik7bFlngzqVW9LMl9XWvLdjFaJtDFqZ1cEZUB0dUgvL3dgP4WJZn4jkomWNpSTIyEAIfkECQoAAAAsAAAAABAAEAAABX4gIAICuSxlOY6CIgiD8RrEKgqGOwxwUrMlAoSwIzAGpJpgoSDAGifDY5kopBYDlEpAQBwevxfBtRIUGi8xwWkDNBCIwmC9Vq0aiQQDQuK+VgQPDXV9hCJjBwcFYU5pLwwHXQcMKSmNLQcIAExlbH8JBwttaX0ABAcNbWVbKyEAIfkECQoAAAAsAAAAABAAEAAABXkgIAICSRBlOY7CIghN8zbEKsKoIjdFzZaEgUBHKChMJtRwcWpAWoWnifm6ESAMhO8lQK0EEAV3rFopIBCEcGwDKAqPh4HUrY4ICHH1dSoTFgcHUiZjBhAJB2AHDykpKAwHAwdzf19KkASIPl9cDgcnDkdtNwiMJCshACH5BAkKAAAALAAAAAAQABAAAAV3ICACAkkQZTmOAiosiyAoxCq+KPxCNVsSMRgBsiClWrLTSWFoIQZHl6pleBh6suxKMIhlvzbAwkBWfFWrBQTxNLq2RG2yhSUkDs2b63AYDAoJXAcFRwADeAkJDX0AQCsEfAQMDAIPBz0rCgcxky0JRWE1AmwpKyEAIfkECQoAAAAsAAAAABAAEAAABXkgIAICKZzkqJ4nQZxLqZKv4NqNLKK2/Q4Ek4lFXChsg5ypJjs1II3gEDUSRInEGYAw6B6zM4JhrDAtEosVkLUtHA7RHaHAGJQEjsODcEg0FBAFVgkQJQ1pAwcDDw8KcFtSInwJAowCCA6RIwqZAgkPNgVpWndjdyohACH5BAkKAAAALAAAAAAQABAAAAV5ICACAimc5KieLEuUKvm2xAKLqDCfC2GaO9eL0LABWTiBYmA06W6kHgvCqEJiAIJiu3gcvgUsscHUERm+kaCxyxa+zRPk0SgJEgfIvbAdIAQLCAYlCj4DBw0IBQsMCjIqBAcPAooCBg9pKgsJLwUFOhCZKyQDA3YqIQAh+QQJCgAAACwAAAAAEAAQAAAFdSAgAgIpnOSonmxbqiThCrJKEHFbo8JxDDOZYFFb+A41E4H4OhkOipXwBElYITDAckFEOBgMQ3arkMkUBdxIUGZpEb7kaQBRlASPg0FQQHAbEEMGDSVEAA1QBhAED1E0NgwFAooCDWljaQIQCE5qMHcNhCkjIQAh+QQJCgAAACwAAAAAEAAQAAAFeSAgAgIpnOSoLgxxvqgKLEcCC65KEAByKK8cSpA4DAiHQ/DkKhGKh4ZCtCyZGo6F6iYYPAqFgYy02xkSaLEMV34tELyRYNEsCQyHlvWkGCzsPgMCEAY7Cg04Uk48LAsDhRA8MVQPEF0GAgqYYwSRlycNcWskCkApIyEAOwAAAAAAAAAAAA%3D%3D); + display: inline-block; + background-repeat: no-repeat; +} + +.recline-data-explorer .alert-loader { + position: absolute; + width: 200px; + left: 50%; + margin-left: -100px; + z-index: 10000; + padding: 40px 0px 40px 0px; + margin-top: -10px; + text-align: center; + font-size: 16px; + font-weight: bold; + -webkit-border-radius: 0px; + -moz-border-radius: 0px; + border-radius: 0px; + border-top: none; +} diff --git a/src/ckanext-d4science_theme/ckanext/reclineview/theme/public/css/recline.min.css b/src/ckanext-d4science_theme/ckanext/reclineview/theme/public/css/recline.min.css new file mode 100644 index 0000000..44fd9cb --- /dev/null +++ b/src/ckanext-d4science_theme/ckanext/reclineview/theme/public/css/recline.min.css @@ -0,0 +1 @@ +body{background-color:#fff}.recline-data-explorer{position:relative;overflow:auto}.recline-record-count{float:left;margin-left:10px;line-height:26px}.loading-dialog{width:120px}.recline-slickgrid{height:600px}.loading-spinner{float:right;background-image:url('../img/ajaxload-circle.gif');width:32px;height:32px}.recline-data-explorer .data-view-sidebar{float:right;margin-left:8px;width:220px}.recline-data-explorer .header .navigation{margin-bottom:8px}.recline-data-explorer .header .navigation,.recline-data-explorer .header .pagination,.recline-data-explorer .header .pagination form{display:inline}.recline-data-explorer .header .navigation{float:left}.recline-data-explorer .header .menu-right{float:right;margin-left:5px;padding-left:5px}.recline-results-info{line-height:35px;margin-left:20px;float:left}.recline-data-explorer .data-view-sidebar>div{margin-top:5px;margin-bottom:10px}.recline-data-explorer .radio,.recline-data-explorer .checkbox{padding-left:20px}.recline-data-explorer .editor-update-map{margin:30px 0px 20px 0px}.recline-data-explorer label{font-weight:normal}.recline-query-editor{float:right;height:35px;padding-right:5px;margin-right:5px;border-right:solid 2px #ddd}.header .input-prepend{margin-bottom:auto}.header .add-on{float:left}.header .add-on{margin-left:-27px}.header .input-prepend{vertical-align:top}.recline-query-editor form button{vertical-align:top}.recline-query-editor .form-inline label{position:absolute;top:0;left:-9999px}.recline-pager{float:left;margin:auto;display:block;margin-left:20px}.recline-pager .pagination li{display:inline-block}.recline-pager .pagination label{display:none}.recline-pager .pagination input{width:40px;height:25px;padding:2px 4px;margin:0;margin-top:-2px;border:1px solid #cccccc;box-shadow:inset 0 1px 1px rgba(0,0,0,0.075);transition:border linear 0.2s,box-shadow linear 0.2s;border-radius:4px;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075);-webkit-transition:border linear 0.2s,box-shadow linear 0.2s;-webkit-border-radius:4px;-moz-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075);-moz-transition:border linear 0.2s,box-shadow linear 0.2s;-moz-border-radius:4px;-o-transition:border linear 0.2s,box-shadow linear 0.2s}.recline-pager .pagination a{float:none;margin-left:-5px;color:#555}.recline-pager .pagination .page-range{height:34px;padding:5px 8px;margin-left:-5px;border:1px solid #ddd}.recline-pager .pagination .page-range a{padding:0px 12px;border:none}.recline-pager .pagination .page-range a:hover{background-color:#ffffff}.recline-pager .pagination>li:first-child>a{border-bottom-left-radius:4px;border-top-left-radius:4px;border-bottom-right-radius:0px;border-top-right-radius:0px;height:34px}.recline-pager .pagination>li:last-child>a{border-bottom-right-radius:4px;border-top-right-radius:4px;border-bottom-left-radius:0px;border-top-left-radius:0px;height:34px}.recline-filter-editor{padding:8px;display:none}.recline-filter-editor .filters{margin:20px 0px}.recline-filter-editor h3{margin-top:4px}.recline-filter-editor .filter{margin-top:20px}.recline-filter-editor .filter .form-group{margin-bottom:0px}.recline-filter-editor .filter input,.recline-filter-editor .filter label{margin:0px}.recline-filter-editor .js-edit button{margin:25px 0px 0px 0px}.recline-filter-editor .filter-term a{font-size:18px}.recline-filter-editor input,.recline-filter-editor select{width:175px}.recline-filter-editor input{margin-top:0.5em;margin-bottom:10px;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;border:1px solid #cccccc}.recline-filter-editor label{font-weight:normal;display:block}.recline-filter-editor legend{margin-bottom:5px}.recline-filter-editor .add-filter{margin-top:1em;margin-bottom:2em}.recline-filter-editor .update-filter{margin-top:1em}.recline-fields-view{display:none}.recline-fields-view .fields-list{padding:0}.recline-fields-view .panel{background-color:#f5f5f5;border:1px solid #e5e5e5}.recline-fields-view .panel-group h3{padding-left:10px}.recline-fields-view .fields-list .panel-heading{padding:2px 5px;margin:1px 0px 1px 5px}.recline-fields-view .panel a,.recline-fields-view .panel h4{display:inline}.recline-fields-view .panel a{padding:0}.recline-fields-view .panel h4{word-wrap:break-word}.recline-fields-view .clear{clear:both}.recline-fields-view .facet-items{list-style-type:none;margin-left:0}.recline-fields-view .facet-item .term{font-weight:bold}.recline-fields-view .facet-item .count{}.recline-data-explorer .notification-loader{width:18px;margin-left:5px;background-image:url(data:image/gif;base64,R0lGODlhEAAQAPQAAP///wAAAPDw8IqKiuDg4EZGRnp6egAAAFhYWCQkJKysrL6+vhQUFJycnAQEBDY2NmhoaAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACH/C05FVFNDQVBFMi4wAwEAAAAh/hpDcmVhdGVkIHdpdGggYWpheGxvYWQuaW5mbwAh+QQJCgAAACwAAAAAEAAQAAAFdyAgAgIJIeWoAkRCCMdBkKtIHIngyMKsErPBYbADpkSCwhDmQCBethRB6Vj4kFCkQPG4IlWDgrNRIwnO4UKBXDufzQvDMaoSDBgFb886MiQadgNABAokfCwzBA8LCg0Egl8jAggGAA1kBIA1BAYzlyILczULC2UhACH5BAkKAAAALAAAAAAQABAAAAV2ICACAmlAZTmOREEIyUEQjLKKxPHADhEvqxlgcGgkGI1DYSVAIAWMx+lwSKkICJ0QsHi9RgKBwnVTiRQQgwF4I4UFDQQEwi6/3YSGWRRmjhEETAJfIgMFCnAKM0KDV4EEEAQLiF18TAYNXDaSe3x6mjidN1s3IQAh+QQJCgAAACwAAAAAEAAQAAAFeCAgAgLZDGU5jgRECEUiCI+yioSDwDJyLKsXoHFQxBSHAoAAFBhqtMJg8DgQBgfrEsJAEAg4YhZIEiwgKtHiMBgtpg3wbUZXGO7kOb1MUKRFMysCChAoggJCIg0GC2aNe4gqQldfL4l/Ag1AXySJgn5LcoE3QXI3IQAh+QQJCgAAACwAAAAAEAAQAAAFdiAgAgLZNGU5joQhCEjxIssqEo8bC9BRjy9Ag7GILQ4QEoE0gBAEBcOpcBA0DoxSK/e8LRIHn+i1cK0IyKdg0VAoljYIg+GgnRrwVS/8IAkICyosBIQpBAMoKy9dImxPhS+GKkFrkX+TigtLlIyKXUF+NjagNiEAIfkECQoAAAAsAAAAABAAEAAABWwgIAICaRhlOY4EIgjH8R7LKhKHGwsMvb4AAy3WODBIBBKCsYA9TjuhDNDKEVSERezQEL0WrhXucRUQGuik7bFlngzqVW9LMl9XWvLdjFaJtDFqZ1cEZUB0dUgvL3dgP4WJZn4jkomWNpSTIyEAIfkECQoAAAAsAAAAABAAEAAABX4gIAICuSxlOY6CIgiD8RrEKgqGOwxwUrMlAoSwIzAGpJpgoSDAGifDY5kopBYDlEpAQBwevxfBtRIUGi8xwWkDNBCIwmC9Vq0aiQQDQuK+VgQPDXV9hCJjBwcFYU5pLwwHXQcMKSmNLQcIAExlbH8JBwttaX0ABAcNbWVbKyEAIfkECQoAAAAsAAAAABAAEAAABXkgIAICSRBlOY7CIghN8zbEKsKoIjdFzZaEgUBHKChMJtRwcWpAWoWnifm6ESAMhO8lQK0EEAV3rFopIBCEcGwDKAqPh4HUrY4ICHH1dSoTFgcHUiZjBhAJB2AHDykpKAwHAwdzf19KkASIPl9cDgcnDkdtNwiMJCshACH5BAkKAAAALAAAAAAQABAAAAV3ICACAkkQZTmOAiosiyAoxCq+KPxCNVsSMRgBsiClWrLTSWFoIQZHl6pleBh6suxKMIhlvzbAwkBWfFWrBQTxNLq2RG2yhSUkDs2b63AYDAoJXAcFRwADeAkJDX0AQCsEfAQMDAIPBz0rCgcxky0JRWE1AmwpKyEAIfkECQoAAAAsAAAAABAAEAAABXkgIAICKZzkqJ4nQZxLqZKv4NqNLKK2/Q4Ek4lFXChsg5ypJjs1II3gEDUSRInEGYAw6B6zM4JhrDAtEosVkLUtHA7RHaHAGJQEjsODcEg0FBAFVgkQJQ1pAwcDDw8KcFtSInwJAowCCA6RIwqZAgkPNgVpWndjdyohACH5BAkKAAAALAAAAAAQABAAAAV5ICACAimc5KieLEuUKvm2xAKLqDCfC2GaO9eL0LABWTiBYmA06W6kHgvCqEJiAIJiu3gcvgUsscHUERm+kaCxyxa+zRPk0SgJEgfIvbAdIAQLCAYlCj4DBw0IBQsMCjIqBAcPAooCBg9pKgsJLwUFOhCZKyQDA3YqIQAh+QQJCgAAACwAAAAAEAAQAAAFdSAgAgIpnOSonmxbqiThCrJKEHFbo8JxDDOZYFFb+A41E4H4OhkOipXwBElYITDAckFEOBgMQ3arkMkUBdxIUGZpEb7kaQBRlASPg0FQQHAbEEMGDSVEAA1QBhAED1E0NgwFAooCDWljaQIQCE5qMHcNhCkjIQAh+QQJCgAAACwAAAAAEAAQAAAFeSAgAgIpnOSoLgxxvqgKLEcCC65KEAByKK8cSpA4DAiHQ/DkKhGKh4ZCtCyZGo6F6iYYPAqFgYy02xkSaLEMV34tELyRYNEsCQyHlvWkGCzsPgMCEAY7Cg04Uk48LAsDhRA8MVQPEF0GAgqYYwSRlycNcWskCkApIyEAOwAAAAAAAAAAAA%3D%3D);display:inline-block}.recline-data-explorer .alert-loader{position:absolute;width:200px;left:50%;margin-left:-100px;z-index:10000;padding:40px 0px 40px 0px;margin-top:-10px;text-align:center;font-size:16px;font-weight:bold;-webkit-border-radius:0px;-moz-border-radius:0px;border-radius:0px;border-top:none} \ No newline at end of file diff --git a/src/ckanext-d4science_theme/ckanext/reclineview/theme/public/img/ajaxload-circle.gif b/src/ckanext-d4science_theme/ckanext/reclineview/theme/public/img/ajaxload-circle.gif new file mode 100644 index 0000000000000000000000000000000000000000..e15844396f9b8c4d915f5cf595e2a22014a24426 GIT binary patch literal 4176 zcmb`KYgAK*md8&{PI6984tX4MLVzSBA%TD-1Qd!GFefCDmzZGG)JGc-y3jHfEn2R% zy?K*FpaCNlZnXgcAMI7Ns8}Bp5GkUy$fDLdy)J5P2OaCs4!2{wy6V_j_v`I^nf-aM zz4qF__1}B{SCp9YmR3ms3GfpDJbU&G0Dxb9`Q`ro`_t3YGcz+!o;>;W*I$47>8Ii0 z;rjY|gTYWyQQ`OdZ{NPXu&^*bK3-m44ndGuES{a6ee~$jp+koT1_plo@yB2=n2?Z= zo16Rb#~0fL;fF$@aAIPD&*!5knv#;@@pwi@M+*xJaU5@KZ0zXh2!%odfuNMOh5e){z3!NI|%rly-WZ@OGAo6VM;ojo=-_Ta&Tr%#{G z&(DAN-FNBf>DATMFTM2A+}vD4L&Nil{eOk@8MDG@U0p<)joE4qmGZo<^tXhf9ko@B zwKbAAwlvmBs_gJPHeVXFgqG$8g^Qv69&tLBgT;^`WT}qP<06t4;X^4&1PrsZgqIMp10X{(G@i%^ z0p~qwWu^+m&*n?V&+w<4p+p5r_akw#FK}sUxS9CumLG<&j4v?5wsJvsZrUv%0nU%8* zGAFgAie1oDC51EJ3>?V)*zM4dL!9NgR~6OH@yeZYyMUT4S2ri9b8CubC`N3v)TAtO zkcq2tMV+z7b=2j$F{FD6NFqpwJatF4nc^;Cq#2mIW+I0_X`y`F5>?*10uH}n+A8SE zJ~QQp78ly)8g%brn@w6If3~NDa@`MEkA~RJQ1VczAr*RoNBP@D)vqhdy2mj^%9nAt z-ErZt?w`YQ!-UnE276oE+Sr^DrLruO{Z3aaxWtrz;|V4X*gXIcWwDV+YzcA1OJ_j@ zNSew&jF`w%E|?Xg#B3`KrVF^@(!T;!`3HEuvZ7=BSC!xlrxSC-zq33+rfZ|$NTc6~@jbG-&^qowT+xH?whc8z1U{NCPF zOr|V$^lVPXXg&@@AGy7JpGT9IbsF%}HfWe40p0^0jY*Y^>wyuF5@f$^UqpiYK4O{l zN(XJ0($>Fn*>1ngZO{U6_d+;YUBv7;!fDJZ??!+ROiCf_!Hj&?0buM$ zPj+6XFnE7HBF=m8X6;BY*>V~YheyExSCO>F9I6=QDl0^y!Rf<>))eCKV7Ej5`q)|f ziUXl&ZD|UYXV5rLNyuYVng>anI_vI0$}Vpm=Nc!8OMtdG>y!*7^u*S#e1{XP7}V}| zJQ~@2`kGXta|)_2Z}tkMubV|Jmx|keUVvfnp9Av(urCk{z=UxV$=DvgSOg}U-SJU@vlF!1wIS>Rql1-b#%5~_{E{T3*7gDfQx z&3rj9#8fEqF~&;nP4%icu3}h?S_AZ${Na?PWcrG}3~ohy%1xXpT~DcKTo3-of|N8|q4rf~;gy#?08V zhdPwa%HJ+tc{i3$U0=q|*8$h?qz+}jWLA;W!?G-i0?7F(w=gGcJ^)ow(_+oli%NR{ zlSj7CRqwRi|K@Kn&&R?Wj41C`P@EU&_|swlOYW6Rbu+0YrHB%GxNKr%IC<&44^pfS zGLW1%ckX=&ePcxYCYA3ZcX<@W>m`3c5(Z)K^F8UJL#tQv0)0J0KgVtlbSX9!OyIlebD=fHuxcH@*26)Clxsov(o4PwC> zv%S|7P(p&77tNF5K>Tk!QB%+8L`TYHe$3Q#vJVm{^ZGV4PLbZOAe1*z<#B>ivT}Cg z8KfwrUe}Out)VuWsu9t#GmU`DC03UMce&? z`o-W|hAGN}$#P0seJp4)IQn_2VuX=4Yos!zG#c!UjJj9Rt-`?6xBvY;$+nrPre|yP z6gbnrPSWt3%4Yu~lD}U(KtLKucCg*c)>^ozR?yJ_(@Yv%V==<+{=XpXBXD6Nep06e z0YqdZy8|#Qj&BITnOp^~z&3$&28vmdiGavfkZs}g6U%kgOg@1WZOHFot_?L8<(w?w z|2rY%ubdJr&pwGST0G^04Oj5QiuYn0QHN8w;>r1@Y`}stm9iys=0Of9W|nHwDEeac z+k}5gV7_IhF|l039nx~k+0hJHo2tC9lOnKh1H0^>;7o|a+kO6Zj*G7>ujBMMqQ_uE zhV9F9AGx{JC(CP!DfV~4EQuNmLp`5dsy5{}dIxnbCb4ni4Sf5~lLlV8ODjJ%Vh>Io zr1rMFsi2prDWW|WHRkYISNcZ1qqs6MOQGa*MH&vIXDXMkhq^vLzyZOfy%Dp0guFTJ zb=kQ;pyQx)J6kk2KU($*#Lv~7PYnWFibRDOI{ldtMdkia=irWu9H%OlceEf}U9zw) zXTX|j{d%;u+yh;t-~9GNR&pO+@U-(cnwcaIEeu-pMbKCM1weajKVk-07C}#x@#*(+ z$6Krel};+nJa5?o9tHo+C)_1K{y?U`!*#*zmxJ##3o`Yqd0^-OyvbCcTS`3;Vw+QN z#RdcHhL4Bfcin7$_J9dG4)Zb%#9#gNP3y=((D;f3;}ZZ3l`^@D$;)1lu|ZNIb|b9S zoJ&Aj@erP<7x)D+u{g7E#>+#W%YM=N>&=LYTY#R!*VLJVw76REq;2j;9T!9@b7!J@ z;C2rc(b&ot98p5dO7KmNJsNShCUd38j&H}9R1eF3)FDv-6i#o$!*(p?BUJy&5^ns~ z=)PS8W*x_4N6`2rU8~&Dd+3rlmCiMDD8b8Sve#A%y5Mu*roOgvDdcAfWY-ECmAubB zr?UCIhA%CB1D}#0{i)zqFOvci<^e57e#Ir0Y}4$qo-?D&#K$E_d(8=*82S|iR@=XD zWYT)0QyJ3H(BuovnhCQn-kXFCJItX-$(q~D?&vEmHK>nDXkI;Je=yogbXOUqkvUz^ zm-pvaJKKAV`;+ri48tK=7!c>!${fzCBOwvmQT{VpqT7d*+y3JK`H{XEeb~qI;OaU) z6;W~i9tW_ORCySRDBwa5sRY-V@rIr;nn<~}`2Ujb^Cb)tqBOW=pNkfc@`-Q@!cR<4 zaytGJI4DyvmmCHJJRZA6P&Md=N=)W%7s0PYE7F4Wb?dbPH^QS4&nLGD)2K~`PyLW- z1xg0l$xw5p&Mljj!|HJAD;d=*Wq$H z0ZGMiiM%wNBwkz40FYk{z!i>YXpjEwU_&7gLY!_v>rT!PsTUkfCN4&SR*< zO*LzJgvY3vy$c*?@71g#${Pv0fv;Dv#6>hlWOnly0j9<=I1Y)C=5aqdW{cJqg%?%b zV)rXMBAc(H#k<4EkrAYIk0-rFQ|Tt#SRD40j(cLeUr4YVKUDGnki|81-myzR&1tYvZF#JB=sC)?QM5xpF+KjI1F+%{~Xyu%mI{br&Jrwh&rfOH<292LR zA-NQx!O7~7 zAj3F|PV|{?QneX_>$X*LG{rh*zkP(CI1`C->Qr+7Tk9I8oR%Q;M^Do~EC~R=47YXw zY^YGwzHcwNN@*s#_ZpPaB&vr^XScr7^_=Xg=ST@vj4BcYMTOK8U>a}81r}i`7y}8? z`5RPYexza}o*4H*fW2~=;YI+apkyS5x%db8muCD2N2F^+F7V;lb4B69>J*LS6sh)Hl7?5Q9L5x{PD(Dj^4ZIy?Hn zs1{li@-;Oh#;oO-waz{|0C85za^-fz3I<0n)(+!6o)Q7dKV6z7LxULW$xdD6W)O?V z28v&`I*Z?B&CWI*G&_x6S`sZ?S=;#8lpCd0?74%}VCN+-WSxt=z;(r0+QNizP8V+tDI8ic|b!Wg(ANO~_O93Qi3awQoi+bCm(?xG64ACMjXtYIfUBifj Y#`Dx6nd>%kIx0NRp)dR0fBtX$58}9UkpKVy literal 0 HcmV?d00001 diff --git a/src/ckanext-d4science_theme/ckanext/reclineview/theme/public/recline_view.js b/src/ckanext-d4science_theme/ckanext/reclineview/theme/public/recline_view.js new file mode 100644 index 0000000..65a539d --- /dev/null +++ b/src/ckanext-d4science_theme/ckanext/reclineview/theme/public/recline_view.js @@ -0,0 +1,247 @@ +this.ckan.module('recline_view', function (jQuery) { + return { + options: { + site_url: "", + controlsClassName: "controls", + dataproxyUrl: "//jsonpdataproxy.appspot.com" + }, + + initialize: function () { + jQuery.proxyAll(this, /_on/); + this.options.resource = JSON.parse(this.options.resource); + this.options.resourceView = JSON.parse(this.options.resourceView); + this.el.ready(this._onReady); + // hack to make leaflet use a particular location to look for images + L.Icon.Default.imagePath = this.options.site_url + 'vendor/leaflet/0.7.7/images'; + }, + + _onReady: function() { + var resourceData = this.options.resource, + resourceView = this.options.resourceView; + + this.loadView(resourceData, resourceView); + }, + + loadView: function (resourceData, reclineView) { + var self = this; + + function showError(msg){ + msg = msg || self._('error loading view'); + window.parent.ckan.pubsub.publish('data-viewer-error', msg); + } + + if (resourceData.formatNormalized === '') { + var tmp = resourceData.url.split('/'); + tmp = tmp[tmp.length - 1]; + tmp = tmp.split('?'); // query strings + tmp = tmp[0]; + var ext = tmp.split('.'); + if (ext.length > 1) { + resourceData.formatNormalized = ext[ext.length-1]; + } + } + + var errorMsg, dataset, map_config; + + if (!resourceData.datastore_active) { + recline.Backend.DataProxy.timeout = 10000; + + recline.Backend.DataProxy.dataproxy_url = this.options.dataproxyUrl; + + resourceData.backend = 'dataproxy'; + } else { + resourceData.backend = 'ckan'; + resourceData.endpoint = jQuery('body').data('site-root') + 'api'; + } + + dataset = new recline.Model.Dataset(resourceData); + + map_config = this.options.map_config; + + var query = new recline.Model.Query(); + query.set({ size: reclineView.limit || 100 }); + query.set({ from: reclineView.offset || 0 }); + + var urlFilters = {}; + try { + if (window.parent.ckan.views && window.parent.ckan.views.filters) { + urlFilters = window.parent.ckan.views.filters.get(); + } + } catch(e) {} + var defaultFilters = reclineView.filters || {}, + filters = jQuery.extend({}, defaultFilters, urlFilters); + jQuery.each(filters, function (field,values) { + query.addFilter({type: 'term', field: field, term: values}); + }); + + dataset.queryState.set(query.toJSON(), {silent: true}); + + errorMsg = this._('Could not load view') + ': '; + if (resourceData.backend == 'ckan') { + errorMsg += this._('DataStore returned an error'); + } else if (resourceData.backend == 'dataproxy'){ + errorMsg += this._('DataProxy returned an error'); + } + dataset.fetch() + .done(function(dataset){ + self.initializeView(dataset, reclineView); + }) + .fail(function(error){ + if (error.message) errorMsg += ' (' + error.message + ')'; + showError(errorMsg); + }); + }, + + initializeView: function (dataset, reclineView) { + var view, + state, + controls = []; + + if(reclineView.view_type === "recline_graph_view") { + state = { + "graphType": reclineView.graph_type, + "group": reclineView.group, + "series": [reclineView.series] + }; + view = new recline.View.Graph({model: dataset, state: state}); + } else if(reclineView.view_type === "recline_map_view") { + state = { + geomField: null, + latField: null, + lonField: null, + autoZoom: Boolean(reclineView.auto_zoom), + cluster: Boolean(reclineView.cluster_markers) + }; + + if(reclineView.map_field_type === "geojson") { + state.geomField = reclineView.geojson_field; + } else { + state.latField = reclineView.latitude_field; + state.lonField = reclineView.longitude_field; + } + + view = new recline.View.Map($.extend(this._reclineMapViewOptions(dataset, this.options.map_config), {state:state})); + } else if(reclineView.view_type === "recline_view") { + view = this._newDataExplorer(dataset, this.options.map_config); + } else { + // default to Grid + view = new recline.View.SlickGrid({model: dataset}); + controls = [ + new recline.View.Pager({model: view.model}), + new recline.View.RecordCount({model: dataset}), + new recline.View.QueryEditor({model: view.model.queryState}) + ]; + } + + // recline_view automatically adds itself to the DOM, so we don't + // need to bother with it. + if(reclineView.view_type !== 'recline_view') { + var newElements = jQuery('
    '); + this._renderControls(newElements, controls, this.options.controlsClassName); + newElements.append(view.el); + jQuery(this.el).html(newElements); + view.visible = true; + view.render(); + } + + if(reclineView.view_type === "recline_graph_view") { + view.redraw(); + } + }, + + _reclineMapViewOptions: function(dataset, map_config) { + var tile_url, attribution, subdomains; + tile_url = attribution = subdomains = ''; + + if (map_config.type == 'mapbox') { + // MapBox base map + if (!map_config['mapbox.map_id'] || !map_config['mapbox.access_token']) { + throw '[CKAN Map Widgets] You need to provide a map ID ' + + '([account].[handle]) and an access token when using a ' + + 'MapBox layer. See ' + + 'http://www.mapbox.com/developers/api-overview/ for details'; + } + tile_url = '//{s}.tiles.mapbox.com/v4/' + + map_config['mapbox.map_id'] + + '/{z}/{x}/{y}.png?access_token=' + + map_config['mapbox.access_token']; + handle = map_config['mapbox.map_id']; + subdomains = map_config.subdomains || 'abcd'; + attribution = map_config.attribution || + 'Data: OpenStreetMap, Design: MapBox'; + } else if (map_config.type == 'custom') { + // Custom XYZ layer + tile_url = map_config['custom.url'] || ''; + attribution = map_config.attribution || ''; + subdomains = map_config.subdomains || ''; + + if (map_config['custom.tms']) + var tms = map_config['custom.tms']; + } + + return { + model: dataset, + mapTilesURL: tile_url, + mapTilesAttribution: attribution, + mapTilesSubdomains: subdomains + }; + }, + + _newDataExplorer: function (dataset, map_config) { + var views = [ + { + id: 'grid', + label: this._('Grid'), + view: new recline.View.SlickGrid({ + model: dataset + }) + }, + { + id: 'graph', + label: this._('Graph'), + view: new recline.View.Graph({ + model: dataset + }) + }, + { + id: 'map', + label: this._('Map'), + view: new recline.View.Map(this._reclineMapViewOptions(dataset, map_config)) + } + ]; + + var sidebarViews = [ + { + id: 'valueFilter', + label: this._('Filters'), + view: new recline.View.ValueFilter({ + model: dataset + }) + } + ]; + + var dataExplorer = new recline.View.MultiView({ + el: this.el, + model: dataset, + views: views, + sidebarViews: sidebarViews, + config: { + readOnly: true + } + }); + + return dataExplorer; + }, + + _renderControls: function (el, controls, className) { + var controlsEl = jQuery("
    "); + for (var i = 0; i < controls.length; i++) { + controlsEl.append(controls[i].el); + } + jQuery(el).append(controlsEl); + } + }; +}); diff --git a/src/ckanext-d4science_theme/ckanext/reclineview/theme/public/recline_view.min.js b/src/ckanext-d4science_theme/ckanext/reclineview/theme/public/recline_view.min.js new file mode 100644 index 0000000..b66728e --- /dev/null +++ b/src/ckanext-d4science_theme/ckanext/reclineview/theme/public/recline_view.min.js @@ -0,0 +1,15 @@ +this.ckan.module('recline_view',function(jQuery){return{options:{site_url:"",controlsClassName:"controls"},initialize:function(){jQuery.proxyAll(this,/_on/);this.options.resource=JSON.parse(this.options.resource);this.options.resourceView=JSON.parse(this.options.resourceView);this.el.ready(this._onReady);L.Icon.Default.imagePath=this.options.site_url+'vendor/leaflet/0.7.7/images';},_onReady:function(){var resourceData=this.options.resource,resourceView=this.options.resourceView;this.loadView(resourceData,resourceView);},loadView:function(resourceData,reclineView){var self=this;function showError(msg){msg=msg||self._('error loading view');window.parent.ckan.pubsub.publish('data-viewer-error',msg);} +if(resourceData.formatNormalized===''){var tmp=resourceData.url.split('/');tmp=tmp[tmp.length-1];tmp=tmp.split('?');tmp=tmp[0];var ext=tmp.split('.');if(ext.length>1){resourceData.formatNormalized=ext[ext.length-1];}} +var errorMsg,dataset,map_config;if(!resourceData.datastore_active){recline.Backend.DataProxy.timeout=10000;resourceData.backend='dataproxy';}else{resourceData.backend='ckan';resourceData.endpoint=jQuery('body').data('site-root')+'api';} +dataset=new recline.Model.Dataset(resourceData);map_config=this.options.map_config;var query=new recline.Model.Query();query.set({size:reclineView.limit||100});query.set({from:reclineView.offset||0});var urlFilters={};try{if(window.parent.ckan.views&&window.parent.ckan.views.filters){urlFilters=window.parent.ckan.views.filters.get();}}catch(e){} +var defaultFilters=reclineView.filters||{},filters=jQuery.extend({},defaultFilters,urlFilters);jQuery.each(filters,function(field,values){query.addFilter({type:'term',field:field,term:values});});dataset.queryState.set(query.toJSON(),{silent:true});errorMsg=this._('Could not load view')+': ';if(resourceData.backend=='ckan'){errorMsg+=this._('DataStore returned an error');}else if(resourceData.backend=='dataproxy'){errorMsg+=this._('DataProxy returned an error');} +dataset.fetch().done(function(dataset){self.initializeView(dataset,reclineView);}).fail(function(error){if(error.message)errorMsg+=' ('+error.message+')';showError(errorMsg);});},initializeView:function(dataset,reclineView){var view,state,controls=[];if(reclineView.view_type==="recline_graph_view"){state={"graphType":reclineView.graph_type,"group":reclineView.group,"series":[reclineView.series]};view=new recline.View.Graph({model:dataset,state:state});}else if(reclineView.view_type==="recline_map_view"){state={geomField:null,latField:null,lonField:null,autoZoom:Boolean(reclineView.auto_zoom),cluster:Boolean(reclineView.cluster_markers)};if(reclineView.map_field_type==="geojson"){state.geomField=reclineView.geojson_field;}else{state.latField=reclineView.latitude_field;state.lonField=reclineView.longitude_field;} +view=new recline.View.Map(this._reclineMapViewOptions(dataset,this.options.map_config));}else if(reclineView.view_type==="recline_view"){view=this._newDataExplorer(dataset,this.options.map_config);}else{view=new recline.View.SlickGrid({model:dataset});controls=[new recline.View.Pager({model:view.model}),new recline.View.RecordCount({model:dataset}),new recline.View.QueryEditor({model:view.model.queryState})];} +if(reclineView.view_type!=='recline_view'){var newElements=jQuery('
    ');this._renderControls(newElements,controls,this.options.controlsClassName);newElements.append(view.el);jQuery(this.el).html(newElements);view.visible=true;view.render();} +if(reclineView.view_type==="recline_graph_view"){view.redraw();}},_reclineMapViewOptions:function(dataset,map_config){var tile_url,attribution,subdomains;tile_url=attribution=subdomains='';if(map_config.type=='mapbox'){if(!map_config['mapbox.map_id']||!map_config['mapbox.access_token']){throw'[CKAN Map Widgets] You need to provide a map ID '+'([account].[handle]) and an access token when using a '+'MapBox layer. See '+'http://www.mapbox.com/developers/api-overview/ for details';} +tile_url='//{s}.tiles.mapbox.com/v4/'+ +map_config['mapbox.map_id']+'/{z}/{x}/{y}.png?access_token='+ +map_config['mapbox.access_token'];handle=map_config['mapbox.map_id'];subdomains=map_config.subdomains||'abcd';attribution=map_config.attribution||'Data: OpenStreetMap, Design: MapBox';}else if(map_config.type=='custom'){tile_url=map_config['custom.url']||'';attribution=map_config.attribution||'';subdomains=map_config.subdomains||'';if(map_config['custom.tms']) +var tms=map_config['custom.tms'];} +return{model:dataset,mapTilesURL:tile_url,mapTilesAttribution:attribution,mapTilesSubdomains:subdomains};},_newDataExplorer:function(dataset,map_config){var views=[{id:'grid',label:this._('Grid'),view:new recline.View.SlickGrid({model:dataset})},{id:'graph',label:this._('Graph'),view:new recline.View.Graph({model:dataset})},{id:'map',label:this._('Map'),view:new recline.View.Map(this._reclineMapViewOptions(dataset,map_config))}];var sidebarViews=[{id:'valueFilter',label:this._('Filters'),view:new recline.View.ValueFilter({model:dataset})}];var dataExplorer=new recline.View.MultiView({el:this.el,model:dataset,views:views,sidebarViews:sidebarViews,config:{readOnly:true}});return dataExplorer;},_renderControls:function(el,controls,className){var controlsEl=jQuery("
    ");for(var i=0;i').attr(attrs); + this.setElement($el, false); + } else { + this.setElement(_.result(this, 'el'), false); + } + } + + }); + + // Backbone.sync + // ------------- + + // Override this function to change the manner in which Backbone persists + // models to the server. You will be passed the type of request, and the + // model in question. By default, makes a RESTful Ajax request + // to the model's `url()`. Some possible customizations could be: + // + // * Use `setTimeout` to batch rapid-fire updates into a single request. + // * Send up the models as XML instead of JSON. + // * Persist models via WebSockets instead of Ajax. + // + // Turn on `Backbone.emulateHTTP` in order to send `PUT` and `DELETE` requests + // as `POST`, with a `_method` parameter containing the true HTTP method, + // as well as all requests with the body as `application/x-www-form-urlencoded` + // instead of `application/json` with the model in a param named `model`. + // Useful when interfacing with server-side languages like **PHP** that make + // it difficult to read the body of `PUT` requests. + Backbone.sync = function(method, model, options) { + var type = methodMap[method]; + + // Default options, unless specified. + _.defaults(options || (options = {}), { + emulateHTTP: Backbone.emulateHTTP, + emulateJSON: Backbone.emulateJSON + }); + + // Default JSON-request options. + var params = {type: type, dataType: 'json'}; + + // Ensure that we have a URL. + if (!options.url) { + params.url = _.result(model, 'url') || urlError(); + } + + // Ensure that we have the appropriate request data. + if (options.data == null && model && (method === 'create' || method === 'update' || method === 'patch')) { + params.contentType = 'application/json'; + params.data = JSON.stringify(options.attrs || model.toJSON(options)); + } + + // For older servers, emulate JSON by encoding the request into an HTML-form. + if (options.emulateJSON) { + params.contentType = 'application/x-www-form-urlencoded'; + params.data = params.data ? {model: params.data} : {}; + } + + // For older servers, emulate HTTP by mimicking the HTTP method with `_method` + // And an `X-HTTP-Method-Override` header. + if (options.emulateHTTP && (type === 'PUT' || type === 'DELETE' || type === 'PATCH')) { + params.type = 'POST'; + if (options.emulateJSON) params.data._method = type; + var beforeSend = options.beforeSend; + options.beforeSend = function(xhr) { + xhr.setRequestHeader('X-HTTP-Method-Override', type); + if (beforeSend) return beforeSend.apply(this, arguments); + }; + } + + // Don't process data on a non-GET request. + if (params.type !== 'GET' && !options.emulateJSON) { + params.processData = false; + } + + // If we're sending a `PATCH` request, and we're in an old Internet Explorer + // that still has ActiveX enabled by default, override jQuery to use that + // for XHR instead. Remove this line when jQuery supports `PATCH` on IE8. + if (params.type === 'PATCH' && window.ActiveXObject && + !(window.external && window.external.msActiveXFilteringEnabled)) { + params.xhr = function() { + return new ActiveXObject("Microsoft.XMLHTTP"); + }; + } + + // Make the request, allowing the user to override any Ajax options. + var xhr = options.xhr = Backbone.ajax(_.extend(params, options)); + model.trigger('request', model, xhr, options); + return xhr; + }; + + // Map from CRUD to HTTP for our default `Backbone.sync` implementation. + var methodMap = { + 'create': 'POST', + 'update': 'PUT', + 'patch': 'PATCH', + 'delete': 'DELETE', + 'read': 'GET' + }; + + // Set the default implementation of `Backbone.ajax` to proxy through to `$`. + // Override this if you'd like to use a different library. + Backbone.ajax = function() { + return Backbone.$.ajax.apply(Backbone.$, arguments); + }; + + // Backbone.Router + // --------------- + + // Routers map faux-URLs to actions, and fire events when routes are + // matched. Creating a new one sets its `routes` hash, if not set statically. + var Router = Backbone.Router = function(options) { + options || (options = {}); + if (options.routes) this.routes = options.routes; + this._bindRoutes(); + this.initialize.apply(this, arguments); + }; + + // Cached regular expressions for matching named param parts and splatted + // parts of route strings. + var optionalParam = /\((.*?)\)/g; + var namedParam = /(\(\?)?:\w+/g; + var splatParam = /\*\w+/g; + var escapeRegExp = /[\-{}\[\]+?.,\\\^$|#\s]/g; + + // Set up all inheritable **Backbone.Router** properties and methods. + _.extend(Router.prototype, Events, { + + // Initialize is an empty function by default. Override it with your own + // initialization logic. + initialize: function(){}, + + // Manually bind a single named route to a callback. For example: + // + // this.route('search/:query/p:num', 'search', function(query, num) { + // ... + // }); + // + route: function(route, name, callback) { + if (!_.isRegExp(route)) route = this._routeToRegExp(route); + if (_.isFunction(name)) { + callback = name; + name = ''; + } + if (!callback) callback = this[name]; + var router = this; + Backbone.history.route(route, function(fragment) { + var args = router._extractParameters(route, fragment); + callback && callback.apply(router, args); + router.trigger.apply(router, ['route:' + name].concat(args)); + router.trigger('route', name, args); + Backbone.history.trigger('route', router, name, args); + }); + return this; + }, + + // Simple proxy to `Backbone.history` to save a fragment into the history. + navigate: function(fragment, options) { + Backbone.history.navigate(fragment, options); + return this; + }, + + // Bind all defined routes to `Backbone.history`. We have to reverse the + // order of the routes here to support behavior where the most general + // routes can be defined at the bottom of the route map. + _bindRoutes: function() { + if (!this.routes) return; + this.routes = _.result(this, 'routes'); + var route, routes = _.keys(this.routes); + while ((route = routes.pop()) != null) { + this.route(route, this.routes[route]); + } + }, + + // Convert a route string into a regular expression, suitable for matching + // against the current location hash. + _routeToRegExp: function(route) { + route = route.replace(escapeRegExp, '\\$&') + .replace(optionalParam, '(?:$1)?') + .replace(namedParam, function(match, optional){ + return optional ? match : '([^\/]+)'; + }) + .replace(splatParam, '(.*?)'); + return new RegExp('^' + route + '$'); + }, + + // Given a route, and a URL fragment that it matches, return the array of + // extracted decoded parameters. Empty or unmatched parameters will be + // treated as `null` to normalize cross-browser behavior. + _extractParameters: function(route, fragment) { + var params = route.exec(fragment).slice(1); + return _.map(params, function(param) { + return param ? decodeURIComponent(param) : null; + }); + } + + }); + + // Backbone.History + // ---------------- + + // Handles cross-browser history management, based on either + // [pushState](http://diveintohtml5.info/history.html) and real URLs, or + // [onhashchange](https://developer.mozilla.org/en-US/docs/DOM/window.onhashchange) + // and URL fragments. If the browser supports neither (old IE, natch), + // falls back to polling. + var History = Backbone.History = function() { + this.handlers = []; + _.bindAll(this, 'checkUrl'); + + // Ensure that `History` can be used outside of the browser. + if (typeof window !== 'undefined') { + this.location = window.location; + this.history = window.history; + } + }; + + // Cached regex for stripping a leading hash/slash and trailing space. + var routeStripper = /^[#\/]|\s+$/g; + + // Cached regex for stripping leading and trailing slashes. + var rootStripper = /^\/+|\/+$/g; + + // Cached regex for detecting MSIE. + var isExplorer = /msie [\w.]+/; + + // Cached regex for removing a trailing slash. + var trailingSlash = /\/$/; + + // Has the history handling already been started? + History.started = false; + + // Set up all inheritable **Backbone.History** properties and methods. + _.extend(History.prototype, Events, { + + // The default interval to poll for hash changes, if necessary, is + // twenty times a second. + interval: 50, + + // Gets the true hash value. Cannot use location.hash directly due to bug + // in Firefox where location.hash will always be decoded. + getHash: function(window) { + var match = (window || this).location.href.match(/#(.*)$/); + return match ? match[1] : ''; + }, + + // Get the cross-browser normalized URL fragment, either from the URL, + // the hash, or the override. + getFragment: function(fragment, forcePushState) { + if (fragment == null) { + if (this._hasPushState || !this._wantsHashChange || forcePushState) { + fragment = this.location.pathname; + var root = this.root.replace(trailingSlash, ''); + if (!fragment.indexOf(root)) fragment = fragment.substr(root.length); + } else { + fragment = this.getHash(); + } + } + return fragment.replace(routeStripper, ''); + }, + + // Start the hash change handling, returning `true` if the current URL matches + // an existing route, and `false` otherwise. + start: function(options) { + if (History.started) throw new Error("Backbone.history has already been started"); + History.started = true; + + // Figure out the initial configuration. Do we need an iframe? + // Is pushState desired ... is it available? + this.options = _.extend({}, {root: '/'}, this.options, options); + this.root = this.options.root; + this._wantsHashChange = this.options.hashChange !== false; + this._wantsPushState = !!this.options.pushState; + this._hasPushState = !!(this.options.pushState && this.history && this.history.pushState); + var fragment = this.getFragment(); + var docMode = document.documentMode; + var oldIE = (isExplorer.exec(navigator.userAgent.toLowerCase()) && (!docMode || docMode <= 7)); + + // Normalize root to always include a leading and trailing slash. + this.root = ('/' + this.root + '/').replace(rootStripper, '/'); + + if (oldIE && this._wantsHashChange) { + this.iframe = Backbone.$('': +"");a._keyEvent=false;return w},_generateMonthYearHeader:function(a,b,c,e,f,h,i,g){var j=this._get(a,"changeMonth"),l=this._get(a,"changeYear"),u=this._get(a,"showMonthAfterYear"),k='
    ',o="";if(h||!j)o+=''+i[b]+"";else{i=e&&e.getFullYear()==c;var m=f&&f.getFullYear()==c;o+='"}u||(k+=o+(h||!(j&&l)?" ":""));if(!a.yearshtml){a.yearshtml="";if(h||!l)k+=''+c+"";else{g=this._get(a,"yearRange").split(":");var s=(new Date).getFullYear();i=function(q){q=q.match(/c[+-].*/)?c+parseInt(q.substring(1),10):q.match(/[+-].*/)?s+parseInt(q,10):parseInt(q,10);return isNaN(q)?s:q};b=i(g[0]);g=Math.max(b,i(g[1]||""));b=e?Math.max(b, +e.getFullYear()):b;g=f?Math.min(g,f.getFullYear()):g;for(a.yearshtml+='";k+=a.yearshtml;a.yearshtml=null}}k+=this._get(a,"yearSuffix");if(u)k+=(h||!(j&&l)?" ":"")+o;k+="
    ";return k},_adjustInstDate:function(a,b,c){var e=a.drawYear+(c=="Y"?b:0),f=a.drawMonth+ +(c=="M"?b:0);b=Math.min(a.selectedDay,this._getDaysInMonth(e,f))+(c=="D"?b:0);e=this._restrictMinMax(a,this._daylightSavingAdjust(new Date(e,f,b)));a.selectedDay=e.getDate();a.drawMonth=a.selectedMonth=e.getMonth();a.drawYear=a.selectedYear=e.getFullYear();if(c=="M"||c=="Y")this._notifyChange(a)},_restrictMinMax:function(a,b){var c=this._getMinMaxDate(a,"min");a=this._getMinMaxDate(a,"max");b=c&&ba?a:b},_notifyChange:function(a){var b=this._get(a,"onChangeMonthYear");if(b)b.apply(a.input? +a.input[0]:null,[a.selectedYear,a.selectedMonth+1,a])},_getNumberOfMonths:function(a){a=this._get(a,"numberOfMonths");return a==null?[1,1]:typeof a=="number"?[1,a]:a},_getMinMaxDate:function(a,b){return this._determineDate(a,this._get(a,b+"Date"),null)},_getDaysInMonth:function(a,b){return 32-this._daylightSavingAdjust(new Date(a,b,32)).getDate()},_getFirstDayOfMonth:function(a,b){return(new Date(a,b,1)).getDay()},_canAdjustMonth:function(a,b,c,e){var f=this._getNumberOfMonths(a);c=this._daylightSavingAdjust(new Date(c, +e+(b<0?b:f[0]*f[1]),1));b<0&&c.setDate(this._getDaysInMonth(c.getFullYear(),c.getMonth()));return this._isInRange(a,c)},_isInRange:function(a,b){var c=this._getMinMaxDate(a,"min");a=this._getMinMaxDate(a,"max");return(!c||b.getTime()>=c.getTime())&&(!a||b.getTime()<=a.getTime())},_getFormatConfig:function(a){var b=this._get(a,"shortYearCutoff");b=typeof b!="string"?b:(new Date).getFullYear()%100+parseInt(b,10);return{shortYearCutoff:b,dayNamesShort:this._get(a,"dayNamesShort"),dayNames:this._get(a, +"dayNames"),monthNamesShort:this._get(a,"monthNamesShort"),monthNames:this._get(a,"monthNames")}},_formatDate:function(a,b,c,e){if(!b){a.currentDay=a.selectedDay;a.currentMonth=a.selectedMonth;a.currentYear=a.selectedYear}b=b?typeof b=="object"?b:this._daylightSavingAdjust(new Date(e,c,b)):this._daylightSavingAdjust(new Date(a.currentYear,a.currentMonth,a.currentDay));return this.formatDate(this._get(a,"dateFormat"),b,this._getFormatConfig(a))}});d.fn.datepicker=function(a){if(!this.length)return this; +if(!d.datepicker.initialized){d(document).mousedown(d.datepicker._checkExternalClick).find("body").append(d.datepicker.dpDiv);d.datepicker.initialized=true}var b=Array.prototype.slice.call(arguments,1);if(typeof a=="string"&&(a=="isDisabled"||a=="getDate"||a=="widget"))return d.datepicker["_"+a+"Datepicker"].apply(d.datepicker,[this[0]].concat(b));if(a=="option"&&arguments.length==2&&typeof arguments[1]=="string")return d.datepicker["_"+a+"Datepicker"].apply(d.datepicker,[this[0]].concat(b));return this.each(function(){typeof a== +"string"?d.datepicker["_"+a+"Datepicker"].apply(d.datepicker,[this].concat(b)):d.datepicker._attachDatepicker(this,a)})};d.datepicker=new M;d.datepicker.initialized=false;d.datepicker.uuid=(new Date).getTime();d.datepicker.version="1.8.16";window["DP_jQuery_"+B]=d})(jQuery); +;/* + * jQuery UI Effects 1.8.16 + * + * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about) + * Dual licensed under the MIT or GPL Version 2 licenses. + * http://jquery.org/license + * + * http://docs.jquery.com/UI/Effects/ + */ +jQuery.effects||function(f,j){function m(c){var a;if(c&&c.constructor==Array&&c.length==3)return c;if(a=/rgb\(\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*\)/.exec(c))return[parseInt(a[1],10),parseInt(a[2],10),parseInt(a[3],10)];if(a=/rgb\(\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\%\s*\)/.exec(c))return[parseFloat(a[1])*2.55,parseFloat(a[2])*2.55,parseFloat(a[3])*2.55];if(a=/#([a-fA-F0-9]{2})([a-fA-F0-9]{2})([a-fA-F0-9]{2})/.exec(c))return[parseInt(a[1], +16),parseInt(a[2],16),parseInt(a[3],16)];if(a=/#([a-fA-F0-9])([a-fA-F0-9])([a-fA-F0-9])/.exec(c))return[parseInt(a[1]+a[1],16),parseInt(a[2]+a[2],16),parseInt(a[3]+a[3],16)];if(/rgba\(0, 0, 0, 0\)/.exec(c))return n.transparent;return n[f.trim(c).toLowerCase()]}function s(c,a){var b;do{b=f.curCSS(c,a);if(b!=""&&b!="transparent"||f.nodeName(c,"body"))break;a="backgroundColor"}while(c=c.parentNode);return m(b)}function o(){var c=document.defaultView?document.defaultView.getComputedStyle(this,null):this.currentStyle, +a={},b,d;if(c&&c.length&&c[0]&&c[c[0]])for(var e=c.length;e--;){b=c[e];if(typeof c[b]=="string"){d=b.replace(/\-(\w)/g,function(g,h){return h.toUpperCase()});a[d]=c[b]}}else for(b in c)if(typeof c[b]==="string")a[b]=c[b];return a}function p(c){var a,b;for(a in c){b=c[a];if(b==null||f.isFunction(b)||a in t||/scrollbar/.test(a)||!/color/i.test(a)&&isNaN(parseFloat(b)))delete c[a]}return c}function u(c,a){var b={_:0},d;for(d in a)if(c[d]!=a[d])b[d]=a[d];return b}function k(c,a,b,d){if(typeof c=="object"){d= +a;b=null;a=c;c=a.effect}if(f.isFunction(a)){d=a;b=null;a={}}if(typeof a=="number"||f.fx.speeds[a]){d=b;b=a;a={}}if(f.isFunction(b)){d=b;b=null}a=a||{};b=b||a.duration;b=f.fx.off?0:typeof b=="number"?b:b in f.fx.speeds?f.fx.speeds[b]:f.fx.speeds._default;d=d||a.complete;return[c,a,b,d]}function l(c){if(!c||typeof c==="number"||f.fx.speeds[c])return true;if(typeof c==="string"&&!f.effects[c])return true;return false}f.effects={};f.each(["backgroundColor","borderBottomColor","borderLeftColor","borderRightColor", +"borderTopColor","borderColor","color","outlineColor"],function(c,a){f.fx.step[a]=function(b){if(!b.colorInit){b.start=s(b.elem,a);b.end=m(b.end);b.colorInit=true}b.elem.style[a]="rgb("+Math.max(Math.min(parseInt(b.pos*(b.end[0]-b.start[0])+b.start[0],10),255),0)+","+Math.max(Math.min(parseInt(b.pos*(b.end[1]-b.start[1])+b.start[1],10),255),0)+","+Math.max(Math.min(parseInt(b.pos*(b.end[2]-b.start[2])+b.start[2],10),255),0)+")"}});var n={aqua:[0,255,255],azure:[240,255,255],beige:[245,245,220],black:[0, +0,0],blue:[0,0,255],brown:[165,42,42],cyan:[0,255,255],darkblue:[0,0,139],darkcyan:[0,139,139],darkgrey:[169,169,169],darkgreen:[0,100,0],darkkhaki:[189,183,107],darkmagenta:[139,0,139],darkolivegreen:[85,107,47],darkorange:[255,140,0],darkorchid:[153,50,204],darkred:[139,0,0],darksalmon:[233,150,122],darkviolet:[148,0,211],fuchsia:[255,0,255],gold:[255,215,0],green:[0,128,0],indigo:[75,0,130],khaki:[240,230,140],lightblue:[173,216,230],lightcyan:[224,255,255],lightgreen:[144,238,144],lightgrey:[211, +211,211],lightpink:[255,182,193],lightyellow:[255,255,224],lime:[0,255,0],magenta:[255,0,255],maroon:[128,0,0],navy:[0,0,128],olive:[128,128,0],orange:[255,165,0],pink:[255,192,203],purple:[128,0,128],violet:[128,0,128],red:[255,0,0],silver:[192,192,192],white:[255,255,255],yellow:[255,255,0],transparent:[255,255,255]},q=["add","remove","toggle"],t={border:1,borderBottom:1,borderColor:1,borderLeft:1,borderRight:1,borderTop:1,borderWidth:1,margin:1,padding:1};f.effects.animateClass=function(c,a,b, +d){if(f.isFunction(b)){d=b;b=null}return this.queue(function(){var e=f(this),g=e.attr("style")||" ",h=p(o.call(this)),r,v=e.attr("class");f.each(q,function(w,i){c[i]&&e[i+"Class"](c[i])});r=p(o.call(this));e.attr("class",v);e.animate(u(h,r),{queue:false,duration:a,easing:b,complete:function(){f.each(q,function(w,i){c[i]&&e[i+"Class"](c[i])});if(typeof e.attr("style")=="object"){e.attr("style").cssText="";e.attr("style").cssText=g}else e.attr("style",g);d&&d.apply(this,arguments);f.dequeue(this)}})})}; +f.fn.extend({_addClass:f.fn.addClass,addClass:function(c,a,b,d){return a?f.effects.animateClass.apply(this,[{add:c},a,b,d]):this._addClass(c)},_removeClass:f.fn.removeClass,removeClass:function(c,a,b,d){return a?f.effects.animateClass.apply(this,[{remove:c},a,b,d]):this._removeClass(c)},_toggleClass:f.fn.toggleClass,toggleClass:function(c,a,b,d,e){return typeof a=="boolean"||a===j?b?f.effects.animateClass.apply(this,[a?{add:c}:{remove:c},b,d,e]):this._toggleClass(c,a):f.effects.animateClass.apply(this, +[{toggle:c},a,b,d])},switchClass:function(c,a,b,d,e){return f.effects.animateClass.apply(this,[{add:a,remove:c},b,d,e])}});f.extend(f.effects,{version:"1.8.16",save:function(c,a){for(var b=0;b
    ").addClass("ui-effects-wrapper").css({fontSize:"100%",background:"transparent",border:"none",margin:0,padding:0}), +d=document.activeElement;c.wrap(b);if(c[0]===d||f.contains(c[0],d))f(d).focus();b=c.parent();if(c.css("position")=="static"){b.css({position:"relative"});c.css({position:"relative"})}else{f.extend(a,{position:c.css("position"),zIndex:c.css("z-index")});f.each(["top","left","bottom","right"],function(e,g){a[g]=c.css(g);if(isNaN(parseInt(a[g],10)))a[g]="auto"});c.css({position:"relative",top:0,left:0,right:"auto",bottom:"auto"})}return b.css(a).show()},removeWrapper:function(c){var a,b=document.activeElement; +if(c.parent().is(".ui-effects-wrapper")){a=c.parent().replaceWith(c);if(c[0]===b||f.contains(c[0],b))f(b).focus();return a}return c},setTransition:function(c,a,b,d){d=d||{};f.each(a,function(e,g){unit=c.cssUnit(g);if(unit[0]>0)d[g]=unit[0]*b+unit[1]});return d}});f.fn.extend({effect:function(c){var a=k.apply(this,arguments),b={options:a[1],duration:a[2],callback:a[3]};a=b.options.mode;var d=f.effects[c];if(f.fx.off||!d)return a?this[a](b.duration,b.callback):this.each(function(){b.callback&&b.callback.call(this)}); +return d.call(this,b)},_show:f.fn.show,show:function(c){if(l(c))return this._show.apply(this,arguments);else{var a=k.apply(this,arguments);a[1].mode="show";return this.effect.apply(this,a)}},_hide:f.fn.hide,hide:function(c){if(l(c))return this._hide.apply(this,arguments);else{var a=k.apply(this,arguments);a[1].mode="hide";return this.effect.apply(this,a)}},__toggle:f.fn.toggle,toggle:function(c){if(l(c)||typeof c==="boolean"||f.isFunction(c))return this.__toggle.apply(this,arguments);else{var a=k.apply(this, +arguments);a[1].mode="toggle";return this.effect.apply(this,a)}},cssUnit:function(c){var a=this.css(c),b=[];f.each(["em","px","%","pt"],function(d,e){if(a.indexOf(e)>0)b=[parseFloat(a),e]});return b}});f.easing.jswing=f.easing.swing;f.extend(f.easing,{def:"easeOutQuad",swing:function(c,a,b,d,e){return f.easing[f.easing.def](c,a,b,d,e)},easeInQuad:function(c,a,b,d,e){return d*(a/=e)*a+b},easeOutQuad:function(c,a,b,d,e){return-d*(a/=e)*(a-2)+b},easeInOutQuad:function(c,a,b,d,e){if((a/=e/2)<1)return d/ +2*a*a+b;return-d/2*(--a*(a-2)-1)+b},easeInCubic:function(c,a,b,d,e){return d*(a/=e)*a*a+b},easeOutCubic:function(c,a,b,d,e){return d*((a=a/e-1)*a*a+1)+b},easeInOutCubic:function(c,a,b,d,e){if((a/=e/2)<1)return d/2*a*a*a+b;return d/2*((a-=2)*a*a+2)+b},easeInQuart:function(c,a,b,d,e){return d*(a/=e)*a*a*a+b},easeOutQuart:function(c,a,b,d,e){return-d*((a=a/e-1)*a*a*a-1)+b},easeInOutQuart:function(c,a,b,d,e){if((a/=e/2)<1)return d/2*a*a*a*a+b;return-d/2*((a-=2)*a*a*a-2)+b},easeInQuint:function(c,a,b, +d,e){return d*(a/=e)*a*a*a*a+b},easeOutQuint:function(c,a,b,d,e){return d*((a=a/e-1)*a*a*a*a+1)+b},easeInOutQuint:function(c,a,b,d,e){if((a/=e/2)<1)return d/2*a*a*a*a*a+b;return d/2*((a-=2)*a*a*a*a+2)+b},easeInSine:function(c,a,b,d,e){return-d*Math.cos(a/e*(Math.PI/2))+d+b},easeOutSine:function(c,a,b,d,e){return d*Math.sin(a/e*(Math.PI/2))+b},easeInOutSine:function(c,a,b,d,e){return-d/2*(Math.cos(Math.PI*a/e)-1)+b},easeInExpo:function(c,a,b,d,e){return a==0?b:d*Math.pow(2,10*(a/e-1))+b},easeOutExpo:function(c, +a,b,d,e){return a==e?b+d:d*(-Math.pow(2,-10*a/e)+1)+b},easeInOutExpo:function(c,a,b,d,e){if(a==0)return b;if(a==e)return b+d;if((a/=e/2)<1)return d/2*Math.pow(2,10*(a-1))+b;return d/2*(-Math.pow(2,-10*--a)+2)+b},easeInCirc:function(c,a,b,d,e){return-d*(Math.sqrt(1-(a/=e)*a)-1)+b},easeOutCirc:function(c,a,b,d,e){return d*Math.sqrt(1-(a=a/e-1)*a)+b},easeInOutCirc:function(c,a,b,d,e){if((a/=e/2)<1)return-d/2*(Math.sqrt(1-a*a)-1)+b;return d/2*(Math.sqrt(1-(a-=2)*a)+1)+b},easeInElastic:function(c,a,b, +d,e){c=1.70158;var g=0,h=d;if(a==0)return b;if((a/=e)==1)return b+d;g||(g=e*0.3);if(h
    ").css({position:"absolute",visibility:"visible",left:-f*(h/d),top:-e*(i/c)}).parent().addClass("ui-effects-explode").css({position:"absolute",overflow:"hidden",width:h/d,height:i/c,left:g.left+f*(h/d)+(a.options.mode=="show"?(f-Math.floor(d/2))*(h/d):0),top:g.top+e*(i/c)+(a.options.mode=="show"?(e-Math.floor(c/2))*(i/c):0),opacity:a.options.mode=="show"?0:1}).animate({left:g.left+f*(h/d)+(a.options.mode=="show"?0:(f-Math.floor(d/2))*(h/d)),top:g.top+ +e*(i/c)+(a.options.mode=="show"?0:(e-Math.floor(c/2))*(i/c)),opacity:a.options.mode=="show"?1:0},a.duration||500);setTimeout(function(){a.options.mode=="show"?b.css({visibility:"visible"}):b.css({visibility:"visible"}).hide();a.callback&&a.callback.apply(b[0]);b.dequeue();j("div.ui-effects-explode").remove()},a.duration||500)})}})(jQuery); +;/* + * jQuery UI Effects Fade 1.8.16 + * + * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about) + * Dual licensed under the MIT or GPL Version 2 licenses. + * http://jquery.org/license + * + * http://docs.jquery.com/UI/Effects/Fade + * + * Depends: + * jquery.effects.core.js + */ +(function(b){b.effects.fade=function(a){return this.queue(function(){var c=b(this),d=b.effects.setMode(c,a.options.mode||"hide");c.animate({opacity:d},{queue:false,duration:a.duration,easing:a.options.easing,complete:function(){a.callback&&a.callback.apply(this,arguments);c.dequeue()}})})}})(jQuery); +;/* + * jQuery UI Effects Fold 1.8.16 + * + * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about) + * Dual licensed under the MIT or GPL Version 2 licenses. + * http://jquery.org/license + * + * http://docs.jquery.com/UI/Effects/Fold + * + * Depends: + * jquery.effects.core.js + */ +(function(c){c.effects.fold=function(a){return this.queue(function(){var b=c(this),j=["position","top","bottom","left","right"],d=c.effects.setMode(b,a.options.mode||"hide"),g=a.options.size||15,h=!!a.options.horizFirst,k=a.duration?a.duration/2:c.fx.speeds._default/2;c.effects.save(b,j);b.show();var e=c.effects.createWrapper(b).css({overflow:"hidden"}),f=d=="show"!=h,l=f?["width","height"]:["height","width"];f=f?[e.width(),e.height()]:[e.height(),e.width()];var i=/([0-9]+)%/.exec(g);if(i)g=parseInt(i[1], +10)/100*f[d=="hide"?0:1];if(d=="show")e.css(h?{height:0,width:g}:{height:g,width:0});h={};i={};h[l[0]]=d=="show"?f[0]:g;i[l[1]]=d=="show"?f[1]:0;e.animate(h,k,a.options.easing).animate(i,k,a.options.easing,function(){d=="hide"&&b.hide();c.effects.restore(b,j);c.effects.removeWrapper(b);a.callback&&a.callback.apply(b[0],arguments);b.dequeue()})})}})(jQuery); +;/* + * jQuery UI Effects Highlight 1.8.16 + * + * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about) + * Dual licensed under the MIT or GPL Version 2 licenses. + * http://jquery.org/license + * + * http://docs.jquery.com/UI/Effects/Highlight + * + * Depends: + * jquery.effects.core.js + */ +(function(b){b.effects.highlight=function(c){return this.queue(function(){var a=b(this),e=["backgroundImage","backgroundColor","opacity"],d=b.effects.setMode(a,c.options.mode||"show"),f={backgroundColor:a.css("backgroundColor")};if(d=="hide")f.opacity=0;b.effects.save(a,e);a.show().css({backgroundImage:"none",backgroundColor:c.options.color||"#ffff99"}).animate(f,{queue:false,duration:c.duration,easing:c.options.easing,complete:function(){d=="hide"&&a.hide();b.effects.restore(a,e);d=="show"&&!b.support.opacity&& +this.style.removeAttribute("filter");c.callback&&c.callback.apply(this,arguments);a.dequeue()}})})}})(jQuery); +;/* + * jQuery UI Effects Pulsate 1.8.16 + * + * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about) + * Dual licensed under the MIT or GPL Version 2 licenses. + * http://jquery.org/license + * + * http://docs.jquery.com/UI/Effects/Pulsate + * + * Depends: + * jquery.effects.core.js + */ +(function(d){d.effects.pulsate=function(a){return this.queue(function(){var b=d(this),c=d.effects.setMode(b,a.options.mode||"show");times=(a.options.times||5)*2-1;duration=a.duration?a.duration/2:d.fx.speeds._default/2;isVisible=b.is(":visible");animateTo=0;if(!isVisible){b.css("opacity",0).show();animateTo=1}if(c=="hide"&&isVisible||c=="show"&&!isVisible)times--;for(c=0;c
    ').appendTo(document.body).addClass(a.options.className).css({top:d.top,left:d.left,height:b.innerHeight(),width:b.innerWidth(),position:"absolute"}).animate(c,a.duration,a.options.easing,function(){f.remove();a.callback&&a.callback.apply(b[0],arguments); +b.dequeue()})})}})(jQuery); +; \ No newline at end of file diff --git a/src/ckanext-d4science_theme/ckanext/reclineview/theme/public/vendor/slickgrid/2.2/jquery.event.drag-2.2.js b/src/ckanext-d4science_theme/ckanext/reclineview/theme/public/vendor/slickgrid/2.2/jquery.event.drag-2.2.js new file mode 100644 index 0000000..f2c1d57 --- /dev/null +++ b/src/ckanext-d4science_theme/ckanext/reclineview/theme/public/vendor/slickgrid/2.2/jquery.event.drag-2.2.js @@ -0,0 +1,402 @@ +/*! + * jquery.event.drag - v 2.2 + * Copyright (c) 2010 Three Dub Media - http://threedubmedia.com + * Open Source MIT License - http://threedubmedia.com/code/license + */ +// Created: 2008-06-04 +// Updated: 2012-05-21 +// REQUIRES: jquery 1.7.x + +;(function( $ ){ + +// add the jquery instance method +$.fn.drag = function( str, arg, opts ){ + // figure out the event type + var type = typeof str == "string" ? str : "", + // figure out the event handler... + fn = $.isFunction( str ) ? str : $.isFunction( arg ) ? arg : null; + // fix the event type + if ( type.indexOf("drag") !== 0 ) + type = "drag"+ type; + // were options passed + opts = ( str == fn ? arg : opts ) || {}; + // trigger or bind event handler + return fn ? this.bind( type, opts, fn ) : this.trigger( type ); +}; + +// local refs (increase compression) +var $event = $.event, +$special = $event.special, +// configure the drag special event +drag = $special.drag = { + + // these are the default settings + defaults: { + which: 1, // mouse button pressed to start drag sequence + distance: 0, // distance dragged before dragstart + not: ':input', // selector to suppress dragging on target elements + handle: null, // selector to match handle target elements + relative: false, // true to use "position", false to use "offset" + drop: true, // false to suppress drop events, true or selector to allow + click: false // false to suppress click events after dragend (no proxy) + }, + + // the key name for stored drag data + datakey: "dragdata", + + // prevent bubbling for better performance + noBubble: true, + + // count bound related events + add: function( obj ){ + // read the interaction data + var data = $.data( this, drag.datakey ), + // read any passed options + opts = obj.data || {}; + // count another realted event + data.related += 1; + // extend data options bound with this event + // don't iterate "opts" in case it is a node + $.each( drag.defaults, function( key, def ){ + if ( opts[ key ] !== undefined ) + data[ key ] = opts[ key ]; + }); + }, + + // forget unbound related events + remove: function(){ + $.data( this, drag.datakey ).related -= 1; + }, + + // configure interaction, capture settings + setup: function(){ + // check for related events + if ( $.data( this, drag.datakey ) ) + return; + // initialize the drag data with copied defaults + var data = $.extend({ related:0 }, drag.defaults ); + // store the interaction data + $.data( this, drag.datakey, data ); + // bind the mousedown event, which starts drag interactions + $event.add( this, "touchstart mousedown", drag.init, data ); + // prevent image dragging in IE... + if ( this.attachEvent ) + this.attachEvent("ondragstart", drag.dontstart ); + }, + + // destroy configured interaction + teardown: function(){ + var data = $.data( this, drag.datakey ) || {}; + // check for related events + if ( data.related ) + return; + // remove the stored data + $.removeData( this, drag.datakey ); + // remove the mousedown event + $event.remove( this, "touchstart mousedown", drag.init ); + // enable text selection + drag.textselect( true ); + // un-prevent image dragging in IE... + if ( this.detachEvent ) + this.detachEvent("ondragstart", drag.dontstart ); + }, + + // initialize the interaction + init: function( event ){ + // sorry, only one touch at a time + if ( drag.touched ) + return; + // the drag/drop interaction data + var dd = event.data, results; + // check the which directive + if ( event.which != 0 && dd.which > 0 && event.which != dd.which ) + return; + // check for suppressed selector + if ( $( event.target ).is( dd.not ) ) + return; + // check for handle selector + if ( dd.handle && !$( event.target ).closest( dd.handle, event.currentTarget ).length ) + return; + + drag.touched = event.type == 'touchstart' ? this : null; + dd.propagates = 1; + dd.mousedown = this; + dd.interactions = [ drag.interaction( this, dd ) ]; + dd.target = event.target; + dd.pageX = event.pageX; + dd.pageY = event.pageY; + dd.dragging = null; + // handle draginit event... + results = drag.hijack( event, "draginit", dd ); + // early cancel + if ( !dd.propagates ) + return; + // flatten the result set + results = drag.flatten( results ); + // insert new interaction elements + if ( results && results.length ){ + dd.interactions = []; + $.each( results, function(){ + dd.interactions.push( drag.interaction( this, dd ) ); + }); + } + // remember how many interactions are propagating + dd.propagates = dd.interactions.length; + // locate and init the drop targets + if ( dd.drop !== false && $special.drop ) + $special.drop.handler( event, dd ); + // disable text selection + drag.textselect( false ); + // bind additional events... + if ( drag.touched ) + $event.add( drag.touched, "touchmove touchend", drag.handler, dd ); + else + $event.add( document, "mousemove mouseup", drag.handler, dd ); + // helps prevent text selection or scrolling + if ( !drag.touched || dd.live ) + return false; + }, + + // returns an interaction object + interaction: function( elem, dd ){ + var offset = $( elem )[ dd.relative ? "position" : "offset" ]() || { top:0, left:0 }; + return { + drag: elem, + callback: new drag.callback(), + droppable: [], + offset: offset + }; + }, + + // handle drag-releatd DOM events + handler: function( event ){ + // read the data before hijacking anything + var dd = event.data; + // handle various events + switch ( event.type ){ + // mousemove, check distance, start dragging + case !dd.dragging && 'touchmove': + event.preventDefault(); + case !dd.dragging && 'mousemove': + // drag tolerance, x≤ + y≤ = distance≤ + if ( Math.pow( event.pageX-dd.pageX, 2 ) + Math.pow( event.pageY-dd.pageY, 2 ) < Math.pow( dd.distance, 2 ) ) + break; // distance tolerance not reached + event.target = dd.target; // force target from "mousedown" event (fix distance issue) + drag.hijack( event, "dragstart", dd ); // trigger "dragstart" + if ( dd.propagates ) // "dragstart" not rejected + dd.dragging = true; // activate interaction + // mousemove, dragging + case 'touchmove': + event.preventDefault(); + case 'mousemove': + if ( dd.dragging ){ + // trigger "drag" + drag.hijack( event, "drag", dd ); + if ( dd.propagates ){ + // manage drop events + if ( dd.drop !== false && $special.drop ) + $special.drop.handler( event, dd ); // "dropstart", "dropend" + break; // "drag" not rejected, stop + } + event.type = "mouseup"; // helps "drop" handler behave + } + // mouseup, stop dragging + case 'touchend': + case 'mouseup': + default: + if ( drag.touched ) + $event.remove( drag.touched, "touchmove touchend", drag.handler ); // remove touch events + else + $event.remove( document, "mousemove mouseup", drag.handler ); // remove page events + if ( dd.dragging ){ + if ( dd.drop !== false && $special.drop ) + $special.drop.handler( event, dd ); // "drop" + drag.hijack( event, "dragend", dd ); // trigger "dragend" + } + drag.textselect( true ); // enable text selection + // if suppressing click events... + if ( dd.click === false && dd.dragging ) + $.data( dd.mousedown, "suppress.click", new Date().getTime() + 5 ); + dd.dragging = drag.touched = false; // deactivate element + break; + } + }, + + // re-use event object for custom events + hijack: function( event, type, dd, x, elem ){ + // not configured + if ( !dd ) + return; + // remember the original event and type + var orig = { event:event.originalEvent, type:event.type }, + // is the event drag related or drog related? + mode = type.indexOf("drop") ? "drag" : "drop", + // iteration vars + result, i = x || 0, ia, $elems, callback, + len = !isNaN( x ) ? x : dd.interactions.length; + // modify the event type + event.type = type; + // remove the original event + event.originalEvent = null; + // initialize the results + dd.results = []; + // handle each interacted element + do if ( ia = dd.interactions[ i ] ){ + // validate the interaction + if ( type !== "dragend" && ia.cancelled ) + continue; + // set the dragdrop properties on the event object + callback = drag.properties( event, dd, ia ); + // prepare for more results + ia.results = []; + // handle each element + $( elem || ia[ mode ] || dd.droppable ).each(function( p, subject ){ + // identify drag or drop targets individually + callback.target = subject; + // force propagtion of the custom event + event.isPropagationStopped = function(){ return false; }; + // handle the event + result = subject ? $event.dispatch.call( subject, event, callback ) : null; + // stop the drag interaction for this element + if ( result === false ){ + if ( mode == "drag" ){ + ia.cancelled = true; + dd.propagates -= 1; + } + if ( type == "drop" ){ + ia[ mode ][p] = null; + } + } + // assign any dropinit elements + else if ( type == "dropinit" ) + ia.droppable.push( drag.element( result ) || subject ); + // accept a returned proxy element + if ( type == "dragstart" ) + ia.proxy = $( drag.element( result ) || ia.drag )[0]; + // remember this result + ia.results.push( result ); + // forget the event result, for recycling + delete event.result; + // break on cancelled handler + if ( type !== "dropinit" ) + return result; + }); + // flatten the results + dd.results[ i ] = drag.flatten( ia.results ); + // accept a set of valid drop targets + if ( type == "dropinit" ) + ia.droppable = drag.flatten( ia.droppable ); + // locate drop targets + if ( type == "dragstart" && !ia.cancelled ) + callback.update(); + } + while ( ++i < len ) + // restore the original event & type + event.type = orig.type; + event.originalEvent = orig.event; + // return all handler results + return drag.flatten( dd.results ); + }, + + // extend the callback object with drag/drop properties... + properties: function( event, dd, ia ){ + var obj = ia.callback; + // elements + obj.drag = ia.drag; + obj.proxy = ia.proxy || ia.drag; + // starting mouse position + obj.startX = dd.pageX; + obj.startY = dd.pageY; + // current distance dragged + obj.deltaX = event.pageX - dd.pageX; + obj.deltaY = event.pageY - dd.pageY; + // original element position + obj.originalX = ia.offset.left; + obj.originalY = ia.offset.top; + // adjusted element position + obj.offsetX = obj.originalX + obj.deltaX; + obj.offsetY = obj.originalY + obj.deltaY; + // assign the drop targets information + obj.drop = drag.flatten( ( ia.drop || [] ).slice() ); + obj.available = drag.flatten( ( ia.droppable || [] ).slice() ); + return obj; + }, + + // determine is the argument is an element or jquery instance + element: function( arg ){ + if ( arg && ( arg.jquery || arg.nodeType == 1 ) ) + return arg; + }, + + // flatten nested jquery objects and arrays into a single dimension array + flatten: function( arr ){ + return $.map( arr, function( member ){ + return member && member.jquery ? $.makeArray( member ) : + member && member.length ? drag.flatten( member ) : member; + }); + }, + + // toggles text selection attributes ON (true) or OFF (false) + textselect: function( bool ){ + $( document )[ bool ? "unbind" : "bind" ]("selectstart", drag.dontstart ) + .css("MozUserSelect", bool ? "" : "none" ); + // .attr("unselectable", bool ? "off" : "on" ) + document.unselectable = bool ? "off" : "on"; + }, + + // suppress "selectstart" and "ondragstart" events + dontstart: function(){ + return false; + }, + + // a callback instance contructor + callback: function(){} + +}; + +// callback methods +drag.callback.prototype = { + update: function(){ + if ( $special.drop && this.available.length ) + $.each( this.available, function( i ){ + $special.drop.locate( this, i ); + }); + } +}; + +// patch $.event.$dispatch to allow suppressing clicks +var $dispatch = $event.dispatch; +$event.dispatch = function( event ){ + if ( $.data( this, "suppress."+ event.type ) - new Date().getTime() > 0 ){ + $.removeData( this, "suppress."+ event.type ); + return; + } + return $dispatch.apply( this, arguments ); +}; + +// event fix hooks for touch events... +var touchHooks = +$event.fixHooks.touchstart = +$event.fixHooks.touchmove = +$event.fixHooks.touchend = +$event.fixHooks.touchcancel = { + props: "clientX clientY pageX pageY screenX screenY".split( " " ), + filter: function( event, orig ) { + if ( orig ){ + var touched = ( orig.touches && orig.touches[0] ) + || ( orig.changedTouches && orig.changedTouches[0] ) + || null; + // iOS webkit: touchstart, touchmove, touchend + if ( touched ) + $.each( touchHooks.props, function( i, prop ){ + event[ prop ] = touched[ prop ]; + }); + } + return event; + } +}; + +// share the same special event configuration with related events... +$special.draginit = $special.dragstart = $special.dragend = drag; + +})( jQuery ); \ No newline at end of file diff --git a/src/ckanext-d4science_theme/ckanext/reclineview/theme/public/vendor/slickgrid/2.2/jquery.event.drop-2.2.js b/src/ckanext-d4science_theme/ckanext/reclineview/theme/public/vendor/slickgrid/2.2/jquery.event.drop-2.2.js new file mode 100644 index 0000000..7599ef9 --- /dev/null +++ b/src/ckanext-d4science_theme/ckanext/reclineview/theme/public/vendor/slickgrid/2.2/jquery.event.drop-2.2.js @@ -0,0 +1,302 @@ +/*! + * jquery.event.drop - v 2.2 + * Copyright (c) 2010 Three Dub Media - http://threedubmedia.com + * Open Source MIT License - http://threedubmedia.com/code/license + */ +// Created: 2008-06-04 +// Updated: 2012-05-21 +// REQUIRES: jquery 1.7.x, event.drag 2.2 + +;(function($){ // secure $ jQuery alias + +// Events: drop, dropstart, dropend + +// add the jquery instance method +$.fn.drop = function( str, arg, opts ){ + // figure out the event type + var type = typeof str == "string" ? str : "", + // figure out the event handler... + fn = $.isFunction( str ) ? str : $.isFunction( arg ) ? arg : null; + // fix the event type + if ( type.indexOf("drop") !== 0 ) + type = "drop"+ type; + // were options passed + opts = ( str == fn ? arg : opts ) || {}; + // trigger or bind event handler + return fn ? this.bind( type, opts, fn ) : this.trigger( type ); +}; + +// DROP MANAGEMENT UTILITY +// returns filtered drop target elements, caches their positions +$.drop = function( opts ){ + opts = opts || {}; + // safely set new options... + drop.multi = opts.multi === true ? Infinity : + opts.multi === false ? 1 : !isNaN( opts.multi ) ? opts.multi : drop.multi; + drop.delay = opts.delay || drop.delay; + drop.tolerance = $.isFunction( opts.tolerance ) ? opts.tolerance : + opts.tolerance === null ? null : drop.tolerance; + drop.mode = opts.mode || drop.mode || 'intersect'; +}; + +// local refs (increase compression) +var $event = $.event, +$special = $event.special, +// configure the drop special event +drop = $.event.special.drop = { + + // these are the default settings + multi: 1, // allow multiple drop winners per dragged element + delay: 20, // async timeout delay + mode: 'overlap', // drop tolerance mode + + // internal cache + targets: [], + + // the key name for stored drop data + datakey: "dropdata", + + // prevent bubbling for better performance + noBubble: true, + + // count bound related events + add: function( obj ){ + // read the interaction data + var data = $.data( this, drop.datakey ); + // count another realted event + data.related += 1; + }, + + // forget unbound related events + remove: function(){ + $.data( this, drop.datakey ).related -= 1; + }, + + // configure the interactions + setup: function(){ + // check for related events + if ( $.data( this, drop.datakey ) ) + return; + // initialize the drop element data + var data = { + related: 0, + active: [], + anyactive: 0, + winner: 0, + location: {} + }; + // store the drop data on the element + $.data( this, drop.datakey, data ); + // store the drop target in internal cache + drop.targets.push( this ); + }, + + // destroy the configure interaction + teardown: function(){ + var data = $.data( this, drop.datakey ) || {}; + // check for related events + if ( data.related ) + return; + // remove the stored data + $.removeData( this, drop.datakey ); + // reference the targeted element + var element = this; + // remove from the internal cache + drop.targets = $.grep( drop.targets, function( target ){ + return ( target !== element ); + }); + }, + + // shared event handler + handler: function( event, dd ){ + // local vars + var results, $targets; + // make sure the right data is available + if ( !dd ) + return; + // handle various events + switch ( event.type ){ + // draginit, from $.event.special.drag + case 'mousedown': // DROPINIT >> + case 'touchstart': // DROPINIT >> + // collect and assign the drop targets + $targets = $( drop.targets ); + if ( typeof dd.drop == "string" ) + $targets = $targets.filter( dd.drop ); + // reset drop data winner properties + $targets.each(function(){ + var data = $.data( this, drop.datakey ); + data.active = []; + data.anyactive = 0; + data.winner = 0; + }); + // set available target elements + dd.droppable = $targets; + // activate drop targets for the initial element being dragged + $special.drag.hijack( event, "dropinit", dd ); + break; + // drag, from $.event.special.drag + case 'mousemove': // TOLERATE >> + case 'touchmove': // TOLERATE >> + drop.event = event; // store the mousemove event + if ( !drop.timer ) + // monitor drop targets + drop.tolerate( dd ); + break; + // dragend, from $.event.special.drag + case 'mouseup': // DROP >> DROPEND >> + case 'touchend': // DROP >> DROPEND >> + drop.timer = clearTimeout( drop.timer ); // delete timer + if ( dd.propagates ){ + $special.drag.hijack( event, "drop", dd ); + $special.drag.hijack( event, "dropend", dd ); + } + break; + + } + }, + + // returns the location positions of an element + locate: function( elem, index ){ + var data = $.data( elem, drop.datakey ), + $elem = $( elem ), + posi = $elem.offset() || {}, + height = $elem.outerHeight(), + width = $elem.outerWidth(), + location = { + elem: elem, + width: width, + height: height, + top: posi.top, + left: posi.left, + right: posi.left + width, + bottom: posi.top + height + }; + // drag elements might not have dropdata + if ( data ){ + data.location = location; + data.index = index; + data.elem = elem; + } + return location; + }, + + // test the location positions of an element against another OR an X,Y coord + contains: function( target, test ){ // target { location } contains test [x,y] or { location } + return ( ( test[0] || test.left ) >= target.left && ( test[0] || test.right ) <= target.right + && ( test[1] || test.top ) >= target.top && ( test[1] || test.bottom ) <= target.bottom ); + }, + + // stored tolerance modes + modes: { // fn scope: "$.event.special.drop" object + // target with mouse wins, else target with most overlap wins + 'intersect': function( event, proxy, target ){ + return this.contains( target, [ event.pageX, event.pageY ] ) ? // check cursor + 1e9 : this.modes.overlap.apply( this, arguments ); // check overlap + }, + // target with most overlap wins + 'overlap': function( event, proxy, target ){ + // calculate the area of overlap... + return Math.max( 0, Math.min( target.bottom, proxy.bottom ) - Math.max( target.top, proxy.top ) ) + * Math.max( 0, Math.min( target.right, proxy.right ) - Math.max( target.left, proxy.left ) ); + }, + // proxy is completely contained within target bounds + 'fit': function( event, proxy, target ){ + return this.contains( target, proxy ) ? 1 : 0; + }, + // center of the proxy is contained within target bounds + 'middle': function( event, proxy, target ){ + return this.contains( target, [ proxy.left + proxy.width * .5, proxy.top + proxy.height * .5 ] ) ? 1 : 0; + } + }, + + // sort drop target cache by by winner (dsc), then index (asc) + sort: function( a, b ){ + return ( b.winner - a.winner ) || ( a.index - b.index ); + }, + + // async, recursive tolerance execution + tolerate: function( dd ){ + // declare local refs + var i, drp, drg, data, arr, len, elem, + // interaction iteration variables + x = 0, ia, end = dd.interactions.length, + // determine the mouse coords + xy = [ drop.event.pageX, drop.event.pageY ], + // custom or stored tolerance fn + tolerance = drop.tolerance || drop.modes[ drop.mode ]; + // go through each passed interaction... + do if ( ia = dd.interactions[x] ){ + // check valid interaction + if ( !ia ) + return; + // initialize or clear the drop data + ia.drop = []; + // holds the drop elements + arr = []; + len = ia.droppable.length; + // determine the proxy location, if needed + if ( tolerance ) + drg = drop.locate( ia.proxy ); + // reset the loop + i = 0; + // loop each stored drop target + do if ( elem = ia.droppable[i] ){ + data = $.data( elem, drop.datakey ); + drp = data.location; + if ( !drp ) continue; + // find a winner: tolerance function is defined, call it + data.winner = tolerance ? tolerance.call( drop, drop.event, drg, drp ) + // mouse position is always the fallback + : drop.contains( drp, xy ) ? 1 : 0; + arr.push( data ); + } while ( ++i < len ); // loop + // sort the drop targets + arr.sort( drop.sort ); + // reset the loop + i = 0; + // loop through all of the targets again + do if ( data = arr[ i ] ){ + // winners... + if ( data.winner && ia.drop.length < drop.multi ){ + // new winner... dropstart + if ( !data.active[x] && !data.anyactive ){ + // check to make sure that this is not prevented + if ( $special.drag.hijack( drop.event, "dropstart", dd, x, data.elem )[0] !== false ){ + data.active[x] = 1; + data.anyactive += 1; + } + // if false, it is not a winner + else + data.winner = 0; + } + // if it is still a winner + if ( data.winner ) + ia.drop.push( data.elem ); + } + // losers... + else if ( data.active[x] && data.anyactive == 1 ){ + // former winner... dropend + $special.drag.hijack( drop.event, "dropend", dd, x, data.elem ); + data.active[x] = 0; + data.anyactive -= 1; + } + } while ( ++i < len ); // loop + } while ( ++x < end ) // loop + // check if the mouse is still moving or is idle + if ( drop.last && xy[0] == drop.last.pageX && xy[1] == drop.last.pageY ) + delete drop.timer; // idle, don't recurse + else // recurse + drop.timer = setTimeout(function(){ + drop.tolerate( dd ); + }, drop.delay ); + // remember event, to compare idleness + drop.last = drop.event; + } + +}; + +// share the same special event configuration with related events... +$special.dropinit = $special.dropstart = $special.dropend = drop; + +})(jQuery); // confine scope \ No newline at end of file diff --git a/src/ckanext-d4science_theme/ckanext/reclineview/theme/public/vendor/slickgrid/2.2/plugins/slick.autotooltips.js b/src/ckanext-d4science_theme/ckanext/reclineview/theme/public/vendor/slickgrid/2.2/plugins/slick.autotooltips.js new file mode 100644 index 0000000..955684f --- /dev/null +++ b/src/ckanext-d4science_theme/ckanext/reclineview/theme/public/vendor/slickgrid/2.2/plugins/slick.autotooltips.js @@ -0,0 +1,83 @@ +(function ($) { + // Register namespace + $.extend(true, window, { + "Slick": { + "AutoTooltips": AutoTooltips + } + }); + + /** + * AutoTooltips plugin to show/hide tooltips when columns are too narrow to fit content. + * @constructor + * @param {boolean} [options.enableForCells=true] - Enable tooltip for grid cells + * @param {boolean} [options.enableForHeaderCells=false] - Enable tooltip for header cells + * @param {number} [options.maxToolTipLength=null] - The maximum length for a tooltip + */ + function AutoTooltips(options) { + var _grid; + var _self = this; + var _defaults = { + enableForCells: true, + enableForHeaderCells: false, + maxToolTipLength: null + }; + + /** + * Initialize plugin. + */ + function init(grid) { + options = $.extend(true, {}, _defaults, options); + _grid = grid; + if (options.enableForCells) _grid.onMouseEnter.subscribe(handleMouseEnter); + if (options.enableForHeaderCells) _grid.onHeaderMouseEnter.subscribe(handleHeaderMouseEnter); + } + + /** + * Destroy plugin. + */ + function destroy() { + if (options.enableForCells) _grid.onMouseEnter.unsubscribe(handleMouseEnter); + if (options.enableForHeaderCells) _grid.onHeaderMouseEnter.unsubscribe(handleHeaderMouseEnter); + } + + /** + * Handle mouse entering grid cell to add/remove tooltip. + * @param {jQuery.Event} e - The event + */ + function handleMouseEnter(e) { + var cell = _grid.getCellFromEvent(e); + if (cell) { + var $node = $(_grid.getCellNode(cell.row, cell.cell)); + var text; + if ($node.innerWidth() < $node[0].scrollWidth) { + text = $.trim($node.text()); + if (options.maxToolTipLength && text.length > options.maxToolTipLength) { + text = text.substr(0, options.maxToolTipLength - 3) + "..."; + } + } else { + text = ""; + } + $node.attr("title", text); + } + } + + /** + * Handle mouse entering header cell to add/remove tooltip. + * @param {jQuery.Event} e - The event + * @param {object} args.column - The column definition + */ + function handleHeaderMouseEnter(e, args) { + var column = args.column, + $node = $(e.target).closest(".slick-header-column"); + if (!column.toolTip) { + $node.attr("title", ($node.innerWidth() < $node[0].scrollWidth) ? column.name : ""); + } + } + + // Public API + $.extend(this, { + "init": init, + "destroy": destroy + }); + } +})(jQuery); \ No newline at end of file diff --git a/src/ckanext-d4science_theme/ckanext/reclineview/theme/public/vendor/slickgrid/2.2/plugins/slick.cellcopymanager.js b/src/ckanext-d4science_theme/ckanext/reclineview/theme/public/vendor/slickgrid/2.2/plugins/slick.cellcopymanager.js new file mode 100644 index 0000000..c74018d --- /dev/null +++ b/src/ckanext-d4science_theme/ckanext/reclineview/theme/public/vendor/slickgrid/2.2/plugins/slick.cellcopymanager.js @@ -0,0 +1,86 @@ +(function ($) { + // register namespace + $.extend(true, window, { + "Slick": { + "CellCopyManager": CellCopyManager + } + }); + + + function CellCopyManager() { + var _grid; + var _self = this; + var _copiedRanges; + + function init(grid) { + _grid = grid; + _grid.onKeyDown.subscribe(handleKeyDown); + } + + function destroy() { + _grid.onKeyDown.unsubscribe(handleKeyDown); + } + + function handleKeyDown(e, args) { + var ranges; + if (!_grid.getEditorLock().isActive()) { + if (e.which == $.ui.keyCode.ESCAPE) { + if (_copiedRanges) { + e.preventDefault(); + clearCopySelection(); + _self.onCopyCancelled.notify({ranges: _copiedRanges}); + _copiedRanges = null; + } + } + + if (e.which == 67 && (e.ctrlKey || e.metaKey)) { + ranges = _grid.getSelectionModel().getSelectedRanges(); + if (ranges.length != 0) { + e.preventDefault(); + _copiedRanges = ranges; + markCopySelection(ranges); + _self.onCopyCells.notify({ranges: ranges}); + } + } + + if (e.which == 86 && (e.ctrlKey || e.metaKey)) { + if (_copiedRanges) { + e.preventDefault(); + clearCopySelection(); + ranges = _grid.getSelectionModel().getSelectedRanges(); + _self.onPasteCells.notify({from: _copiedRanges, to: ranges}); + _copiedRanges = null; + } + } + } + } + + function markCopySelection(ranges) { + var columns = _grid.getColumns(); + var hash = {}; + for (var i = 0; i < ranges.length; i++) { + for (var j = ranges[i].fromRow; j <= ranges[i].toRow; j++) { + hash[j] = {}; + for (var k = ranges[i].fromCell; k <= ranges[i].toCell; k++) { + hash[j][columns[k].id] = "copied"; + } + } + } + _grid.setCellCssStyles("copy-manager", hash); + } + + function clearCopySelection() { + _grid.removeCellCssStyles("copy-manager"); + } + + $.extend(this, { + "init": init, + "destroy": destroy, + "clearCopySelection": clearCopySelection, + + "onCopyCells": new Slick.Event(), + "onCopyCancelled": new Slick.Event(), + "onPasteCells": new Slick.Event() + }); + } +})(jQuery); \ No newline at end of file diff --git a/src/ckanext-d4science_theme/ckanext/reclineview/theme/public/vendor/slickgrid/2.2/plugins/slick.cellrangedecorator.js b/src/ckanext-d4science_theme/ckanext/reclineview/theme/public/vendor/slickgrid/2.2/plugins/slick.cellrangedecorator.js new file mode 100644 index 0000000..0cbe71d --- /dev/null +++ b/src/ckanext-d4science_theme/ckanext/reclineview/theme/public/vendor/slickgrid/2.2/plugins/slick.cellrangedecorator.js @@ -0,0 +1,66 @@ +(function ($) { + // register namespace + $.extend(true, window, { + "Slick": { + "CellRangeDecorator": CellRangeDecorator + } + }); + + /*** + * Displays an overlay on top of a given cell range. + * + * TODO: + * Currently, it blocks mouse events to DOM nodes behind it. + * Use FF and WebKit-specific "pointer-events" CSS style, or some kind of event forwarding. + * Could also construct the borders separately using 4 individual DIVs. + * + * @param {Grid} grid + * @param {Object} options + */ + function CellRangeDecorator(grid, options) { + var _elem; + var _defaults = { + selectionCssClass: 'slick-range-decorator', + selectionCss: { + "zIndex": "9999", + "border": "2px dashed red" + } + }; + + options = $.extend(true, {}, _defaults, options); + + + function show(range) { + if (!_elem) { + _elem = $("
    ", {css: options.selectionCss}) + .addClass(options.selectionCssClass) + .css("position", "absolute") + .appendTo(grid.getCanvasNode()); + } + + var from = grid.getCellNodeBox(range.fromRow, range.fromCell); + var to = grid.getCellNodeBox(range.toRow, range.toCell); + + _elem.css({ + top: from.top - 1, + left: from.left - 1, + height: to.bottom - from.top - 2, + width: to.right - from.left - 2 + }); + + return _elem; + } + + function hide() { + if (_elem) { + _elem.remove(); + _elem = null; + } + } + + $.extend(this, { + "show": show, + "hide": hide + }); + } +})(jQuery); diff --git a/src/ckanext-d4science_theme/ckanext/reclineview/theme/public/vendor/slickgrid/2.2/plugins/slick.cellrangeselector.js b/src/ckanext-d4science_theme/ckanext/reclineview/theme/public/vendor/slickgrid/2.2/plugins/slick.cellrangeselector.js new file mode 100644 index 0000000..520b17f --- /dev/null +++ b/src/ckanext-d4science_theme/ckanext/reclineview/theme/public/vendor/slickgrid/2.2/plugins/slick.cellrangeselector.js @@ -0,0 +1,113 @@ +(function ($) { + // register namespace + $.extend(true, window, { + "Slick": { + "CellRangeSelector": CellRangeSelector + } + }); + + + function CellRangeSelector(options) { + var _grid; + var _canvas; + var _dragging; + var _decorator; + var _self = this; + var _handler = new Slick.EventHandler(); + var _defaults = { + selectionCss: { + "border": "2px dashed blue" + } + }; + + + function init(grid) { + options = $.extend(true, {}, _defaults, options); + _decorator = new Slick.CellRangeDecorator(grid, options); + _grid = grid; + _canvas = _grid.getCanvasNode(); + _handler + .subscribe(_grid.onDragInit, handleDragInit) + .subscribe(_grid.onDragStart, handleDragStart) + .subscribe(_grid.onDrag, handleDrag) + .subscribe(_grid.onDragEnd, handleDragEnd); + } + + function destroy() { + _handler.unsubscribeAll(); + } + + function handleDragInit(e, dd) { + // prevent the grid from cancelling drag'n'drop by default + e.stopImmediatePropagation(); + } + + function handleDragStart(e, dd) { + var cell = _grid.getCellFromEvent(e); + if (_self.onBeforeCellRangeSelected.notify(cell) !== false) { + if (_grid.canCellBeSelected(cell.row, cell.cell)) { + _dragging = true; + e.stopImmediatePropagation(); + } + } + if (!_dragging) { + return; + } + + _grid.focus(); + + var start = _grid.getCellFromPoint( + dd.startX - $(_canvas).offset().left, + dd.startY - $(_canvas).offset().top); + + dd.range = {start: start, end: {}}; + + return _decorator.show(new Slick.Range(start.row, start.cell)); + } + + function handleDrag(e, dd) { + if (!_dragging) { + return; + } + e.stopImmediatePropagation(); + + var end = _grid.getCellFromPoint( + e.pageX - $(_canvas).offset().left, + e.pageY - $(_canvas).offset().top); + + if (!_grid.canCellBeSelected(end.row, end.cell)) { + return; + } + + dd.range.end = end; + _decorator.show(new Slick.Range(dd.range.start.row, dd.range.start.cell, end.row, end.cell)); + } + + function handleDragEnd(e, dd) { + if (!_dragging) { + return; + } + + _dragging = false; + e.stopImmediatePropagation(); + + _decorator.hide(); + _self.onCellRangeSelected.notify({ + range: new Slick.Range( + dd.range.start.row, + dd.range.start.cell, + dd.range.end.row, + dd.range.end.cell + ) + }); + } + + $.extend(this, { + "init": init, + "destroy": destroy, + + "onBeforeCellRangeSelected": new Slick.Event(), + "onCellRangeSelected": new Slick.Event() + }); + } +})(jQuery); \ No newline at end of file diff --git a/src/ckanext-d4science_theme/ckanext/reclineview/theme/public/vendor/slickgrid/2.2/plugins/slick.cellselectionmodel.js b/src/ckanext-d4science_theme/ckanext/reclineview/theme/public/vendor/slickgrid/2.2/plugins/slick.cellselectionmodel.js new file mode 100644 index 0000000..74bc3eb --- /dev/null +++ b/src/ckanext-d4science_theme/ckanext/reclineview/theme/public/vendor/slickgrid/2.2/plugins/slick.cellselectionmodel.js @@ -0,0 +1,154 @@ +(function ($) { + // register namespace + $.extend(true, window, { + "Slick": { + "CellSelectionModel": CellSelectionModel + } + }); + + + function CellSelectionModel(options) { + var _grid; + var _canvas; + var _ranges = []; + var _self = this; + var _selector = new Slick.CellRangeSelector({ + "selectionCss": { + "border": "2px solid black" + } + }); + var _options; + var _defaults = { + selectActiveCell: true + }; + + + function init(grid) { + _options = $.extend(true, {}, _defaults, options); + _grid = grid; + _canvas = _grid.getCanvasNode(); + _grid.onActiveCellChanged.subscribe(handleActiveCellChange); + _grid.onKeyDown.subscribe(handleKeyDown); + grid.registerPlugin(_selector); + _selector.onCellRangeSelected.subscribe(handleCellRangeSelected); + _selector.onBeforeCellRangeSelected.subscribe(handleBeforeCellRangeSelected); + } + + function destroy() { + _grid.onActiveCellChanged.unsubscribe(handleActiveCellChange); + _grid.onKeyDown.unsubscribe(handleKeyDown); + _selector.onCellRangeSelected.unsubscribe(handleCellRangeSelected); + _selector.onBeforeCellRangeSelected.unsubscribe(handleBeforeCellRangeSelected); + _grid.unregisterPlugin(_selector); + } + + function removeInvalidRanges(ranges) { + var result = []; + + for (var i = 0; i < ranges.length; i++) { + var r = ranges[i]; + if (_grid.canCellBeSelected(r.fromRow, r.fromCell) && _grid.canCellBeSelected(r.toRow, r.toCell)) { + result.push(r); + } + } + + return result; + } + + function setSelectedRanges(ranges) { + _ranges = removeInvalidRanges(ranges); + _self.onSelectedRangesChanged.notify(_ranges); + } + + function getSelectedRanges() { + return _ranges; + } + + function handleBeforeCellRangeSelected(e, args) { + if (_grid.getEditorLock().isActive()) { + e.stopPropagation(); + return false; + } + } + + function handleCellRangeSelected(e, args) { + setSelectedRanges([args.range]); + } + + function handleActiveCellChange(e, args) { + if (_options.selectActiveCell && args.row != null && args.cell != null) { + setSelectedRanges([new Slick.Range(args.row, args.cell)]); + } + } + + function handleKeyDown(e) { + /*** + * Кey codes + * 37 left + * 38 up + * 39 right + * 40 down + */ + var ranges, last; + var active = _grid.getActiveCell(); + + if ( active && e.shiftKey && !e.ctrlKey && !e.altKey && + (e.which == 37 || e.which == 39 || e.which == 38 || e.which == 40) ) { + + ranges = getSelectedRanges(); + if (!ranges.length) + ranges.push(new Slick.Range(active.row, active.cell)); + + // keyboard can work with last range only + last = ranges.pop(); + + // can't handle selection out of active cell + if (!last.contains(active.row, active.cell)) + last = new Slick.Range(active.row, active.cell); + + var dRow = last.toRow - last.fromRow, + dCell = last.toCell - last.fromCell, + // walking direction + dirRow = active.row == last.fromRow ? 1 : -1, + dirCell = active.cell == last.fromCell ? 1 : -1; + + if (e.which == 37) { + dCell -= dirCell; + } else if (e.which == 39) { + dCell += dirCell ; + } else if (e.which == 38) { + dRow -= dirRow; + } else if (e.which == 40) { + dRow += dirRow; + } + + // define new selection range + var new_last = new Slick.Range(active.row, active.cell, active.row + dirRow*dRow, active.cell + dirCell*dCell); + if (removeInvalidRanges([new_last]).length) { + ranges.push(new_last); + var viewRow = dirRow > 0 ? new_last.toRow : new_last.fromRow; + var viewCell = dirCell > 0 ? new_last.toCell : new_last.fromCell; + _grid.scrollRowIntoView(viewRow); + _grid.scrollCellIntoView(viewRow, viewCell); + } + else + ranges.push(last); + + setSelectedRanges(ranges); + + e.preventDefault(); + e.stopPropagation(); + } + } + + $.extend(this, { + "getSelectedRanges": getSelectedRanges, + "setSelectedRanges": setSelectedRanges, + + "init": init, + "destroy": destroy, + + "onSelectedRangesChanged": new Slick.Event() + }); + } +})(jQuery); diff --git a/src/ckanext-d4science_theme/ckanext/reclineview/theme/public/vendor/slickgrid/2.2/plugins/slick.checkboxselectcolumn.js b/src/ckanext-d4science_theme/ckanext/reclineview/theme/public/vendor/slickgrid/2.2/plugins/slick.checkboxselectcolumn.js new file mode 100644 index 0000000..83d8d50 --- /dev/null +++ b/src/ckanext-d4science_theme/ckanext/reclineview/theme/public/vendor/slickgrid/2.2/plugins/slick.checkboxselectcolumn.js @@ -0,0 +1,153 @@ +(function ($) { + // register namespace + $.extend(true, window, { + "Slick": { + "CheckboxSelectColumn": CheckboxSelectColumn + } + }); + + + function CheckboxSelectColumn(options) { + var _grid; + var _self = this; + var _handler = new Slick.EventHandler(); + var _selectedRowsLookup = {}; + var _defaults = { + columnId: "_checkbox_selector", + cssClass: null, + toolTip: "Select/Deselect All", + width: 30 + }; + + var _options = $.extend(true, {}, _defaults, options); + + function init(grid) { + _grid = grid; + _handler + .subscribe(_grid.onSelectedRowsChanged, handleSelectedRowsChanged) + .subscribe(_grid.onClick, handleClick) + .subscribe(_grid.onHeaderClick, handleHeaderClick) + .subscribe(_grid.onKeyDown, handleKeyDown); + } + + function destroy() { + _handler.unsubscribeAll(); + } + + function handleSelectedRowsChanged(e, args) { + var selectedRows = _grid.getSelectedRows(); + var lookup = {}, row, i; + for (i = 0; i < selectedRows.length; i++) { + row = selectedRows[i]; + lookup[row] = true; + if (lookup[row] !== _selectedRowsLookup[row]) { + _grid.invalidateRow(row); + delete _selectedRowsLookup[row]; + } + } + for (i in _selectedRowsLookup) { + _grid.invalidateRow(i); + } + _selectedRowsLookup = lookup; + _grid.render(); + + if (selectedRows.length && selectedRows.length == _grid.getDataLength()) { + _grid.updateColumnHeader(_options.columnId, "", _options.toolTip); + } else { + _grid.updateColumnHeader(_options.columnId, "", _options.toolTip); + } + } + + function handleKeyDown(e, args) { + if (e.which == 32) { + if (_grid.getColumns()[args.cell].id === _options.columnId) { + // if editing, try to commit + if (!_grid.getEditorLock().isActive() || _grid.getEditorLock().commitCurrentEdit()) { + toggleRowSelection(args.row); + } + e.preventDefault(); + e.stopImmediatePropagation(); + } + } + } + + function handleClick(e, args) { + // clicking on a row select checkbox + if (_grid.getColumns()[args.cell].id === _options.columnId && $(e.target).is(":checkbox")) { + // if editing, try to commit + if (_grid.getEditorLock().isActive() && !_grid.getEditorLock().commitCurrentEdit()) { + e.preventDefault(); + e.stopImmediatePropagation(); + return; + } + + toggleRowSelection(args.row); + e.stopPropagation(); + e.stopImmediatePropagation(); + } + } + + function toggleRowSelection(row) { + if (_selectedRowsLookup[row]) { + _grid.setSelectedRows($.grep(_grid.getSelectedRows(), function (n) { + return n != row + })); + } else { + _grid.setSelectedRows(_grid.getSelectedRows().concat(row)); + } + } + + function handleHeaderClick(e, args) { + if (args.column.id == _options.columnId && $(e.target).is(":checkbox")) { + // if editing, try to commit + if (_grid.getEditorLock().isActive() && !_grid.getEditorLock().commitCurrentEdit()) { + e.preventDefault(); + e.stopImmediatePropagation(); + return; + } + + if ($(e.target).is(":checked")) { + var rows = []; + for (var i = 0; i < _grid.getDataLength(); i++) { + rows.push(i); + } + _grid.setSelectedRows(rows); + } else { + _grid.setSelectedRows([]); + } + e.stopPropagation(); + e.stopImmediatePropagation(); + } + } + + function getColumnDefinition() { + return { + id: _options.columnId, + name: "", + toolTip: _options.toolTip, + field: "sel", + width: _options.width, + resizable: false, + sortable: false, + cssClass: _options.cssClass, + formatter: checkboxSelectionFormatter + }; + } + + function checkboxSelectionFormatter(row, cell, value, columnDef, dataContext) { + if (dataContext) { + return _selectedRowsLookup[row] + ? "" + : ""; + } + return null; + } + + $.extend(this, { + "init": init, + "destroy": destroy, + + "getColumnDefinition": getColumnDefinition + }); + } +})(jQuery); \ No newline at end of file diff --git a/src/ckanext-d4science_theme/ckanext/reclineview/theme/public/vendor/slickgrid/2.2/plugins/slick.headerbuttons.css b/src/ckanext-d4science_theme/ckanext/reclineview/theme/public/vendor/slickgrid/2.2/plugins/slick.headerbuttons.css new file mode 100644 index 0000000..0ba79ea --- /dev/null +++ b/src/ckanext-d4science_theme/ckanext/reclineview/theme/public/vendor/slickgrid/2.2/plugins/slick.headerbuttons.css @@ -0,0 +1,39 @@ +.slick-column-name, +.slick-sort-indicator { + /** + * This makes all "float:right" elements after it that spill over to the next line + * display way below the lower boundary of the column thus hiding them. + */ + display: inline-block; + float: left; + margin-bottom: 100px; +} + +.slick-header-button { + display: inline-block; + float: right; + vertical-align: top; + margin: 1px; + /** + * This makes all "float:right" elements after it that spill over to the next line + * display way below the lower boundary of the column thus hiding them. + */ + margin-bottom: 100px; + height: 15px; + width: 15px; + background-repeat: no-repeat; + background-position: center center; + cursor: pointer; +} + +.slick-header-button-hidden { + width: 0; + + -webkit-transition: 0.2s width; + -ms-transition: 0.2s width; + transition: 0.2s width; +} + +.slick-header-column:hover > .slick-header-button { + width: 15px; +} \ No newline at end of file diff --git a/src/ckanext-d4science_theme/ckanext/reclineview/theme/public/vendor/slickgrid/2.2/plugins/slick.headerbuttons.js b/src/ckanext-d4science_theme/ckanext/reclineview/theme/public/vendor/slickgrid/2.2/plugins/slick.headerbuttons.js new file mode 100644 index 0000000..8e61273 --- /dev/null +++ b/src/ckanext-d4science_theme/ckanext/reclineview/theme/public/vendor/slickgrid/2.2/plugins/slick.headerbuttons.js @@ -0,0 +1,177 @@ +(function ($) { + // register namespace + $.extend(true, window, { + "Slick": { + "Plugins": { + "HeaderButtons": HeaderButtons + } + } + }); + + + /*** + * A plugin to add custom buttons to column headers. + * + * USAGE: + * + * Add the plugin .js & .css files and register it with the grid. + * + * To specify a custom button in a column header, extend the column definition like so: + * + * var columns = [ + * { + * id: 'myColumn', + * name: 'My column', + * + * // This is the relevant part + * header: { + * buttons: [ + * { + * // button options + * }, + * { + * // button options + * } + * ] + * } + * } + * ]; + * + * Available button options: + * cssClass: CSS class to add to the button. + * image: Relative button image path. + * tooltip: Button tooltip. + * showOnHover: Only show the button on hover. + * handler: Button click handler. + * command: A command identifier to be passed to the onCommand event handlers. + * + * The plugin exposes the following events: + * onCommand: Fired on button click for buttons with 'command' specified. + * Event args: + * grid: Reference to the grid. + * column: Column definition. + * command: Button command identified. + * button: Button options. Note that you can change the button options in your + * event handler, and the column header will be automatically updated to + * reflect them. This is useful if you want to implement something like a + * toggle button. + * + * + * @param options {Object} Options: + * buttonCssClass: a CSS class to use for buttons (default 'slick-header-button') + * @class Slick.Plugins.HeaderButtons + * @constructor + */ + function HeaderButtons(options) { + var _grid; + var _self = this; + var _handler = new Slick.EventHandler(); + var _defaults = { + buttonCssClass: "slick-header-button" + }; + + + function init(grid) { + options = $.extend(true, {}, _defaults, options); + _grid = grid; + _handler + .subscribe(_grid.onHeaderCellRendered, handleHeaderCellRendered) + .subscribe(_grid.onBeforeHeaderCellDestroy, handleBeforeHeaderCellDestroy); + + // Force the grid to re-render the header now that the events are hooked up. + _grid.setColumns(_grid.getColumns()); + } + + + function destroy() { + _handler.unsubscribeAll(); + } + + + function handleHeaderCellRendered(e, args) { + var column = args.column; + + if (column.header && column.header.buttons) { + // Append buttons in reverse order since they are floated to the right. + var i = column.header.buttons.length; + while (i--) { + var button = column.header.buttons[i]; + var btn = $("
    ") + .addClass(options.buttonCssClass) + .data("column", column) + .data("button", button); + + if (button.showOnHover) { + btn.addClass("slick-header-button-hidden"); + } + + if (button.image) { + btn.css("backgroundImage", "url(" + button.image + ")"); + } + + if (button.cssClass) { + btn.addClass(button.cssClass); + } + + if (button.tooltip) { + btn.attr("title", button.tooltip); + } + + if (button.command) { + btn.data("command", button.command); + } + + if (button.handler) { + btn.bind("click", button.handler); + } + + btn + .bind("click", handleButtonClick) + .appendTo(args.node); + } + } + } + + + function handleBeforeHeaderCellDestroy(e, args) { + var column = args.column; + + if (column.header && column.header.buttons) { + // Removing buttons via jQuery will also clean up any event handlers and data. + // NOTE: If you attach event handlers directly or using a different framework, + // you must also clean them up here to avoid memory leaks. + $(args.node).find("." + options.buttonCssClass).remove(); + } + } + + + function handleButtonClick(e) { + var command = $(this).data("command"); + var columnDef = $(this).data("column"); + var button = $(this).data("button"); + + if (command != null) { + _self.onCommand.notify({ + "grid": _grid, + "column": columnDef, + "command": command, + "button": button + }, e, _self); + + // Update the header in case the user updated the button definition in the handler. + _grid.updateColumnHeader(columnDef.id); + } + + // Stop propagation so that it doesn't register as a header click event. + e.preventDefault(); + e.stopPropagation(); + } + + $.extend(this, { + "init": init, + "destroy": destroy, + + "onCommand": new Slick.Event() + }); + } +})(jQuery); \ No newline at end of file diff --git a/src/ckanext-d4science_theme/ckanext/reclineview/theme/public/vendor/slickgrid/2.2/plugins/slick.headermenu.css b/src/ckanext-d4science_theme/ckanext/reclineview/theme/public/vendor/slickgrid/2.2/plugins/slick.headermenu.css new file mode 100644 index 0000000..8b0b6a9 --- /dev/null +++ b/src/ckanext-d4science_theme/ckanext/reclineview/theme/public/vendor/slickgrid/2.2/plugins/slick.headermenu.css @@ -0,0 +1,59 @@ +/* Menu button */ +.slick-header-menubutton { + position: absolute; + right: 0; + top: 0; + bottom: 0; + width: 14px; + background-repeat: no-repeat; + background-position: left center; + background-image: url(../images/down.gif); + cursor: pointer; + + display: none; + border-left: thin ridge silver; +} + +.slick-header-column:hover > .slick-header-menubutton, +.slick-header-column-active .slick-header-menubutton { + display: inline-block; +} + +/* Menu */ +.slick-header-menu { + position: absolute; + display: inline-block; + margin: 0; + padding: 2px; + cursor: default; +} + + +/* Menu items */ +.slick-header-menuitem { + list-style: none; + margin: 0; + padding: 0; + cursor: pointer; +} + +.slick-header-menuicon { + display: inline-block; + width: 16px; + height: 16px; + vertical-align: middle; + margin-right: 4px; + background-repeat: no-repeat; + background-position: center center; +} + +.slick-header-menucontent { + display: inline-block; + vertical-align: middle; +} + + +/* Disabled */ +.slick-header-menuitem-disabled { + color: silver; +} diff --git a/src/ckanext-d4science_theme/ckanext/reclineview/theme/public/vendor/slickgrid/2.2/plugins/slick.headermenu.js b/src/ckanext-d4science_theme/ckanext/reclineview/theme/public/vendor/slickgrid/2.2/plugins/slick.headermenu.js new file mode 100644 index 0000000..ec8244d --- /dev/null +++ b/src/ckanext-d4science_theme/ckanext/reclineview/theme/public/vendor/slickgrid/2.2/plugins/slick.headermenu.js @@ -0,0 +1,275 @@ +(function ($) { + // register namespace + $.extend(true, window, { + "Slick": { + "Plugins": { + "HeaderMenu": HeaderMenu + } + } + }); + + + /*** + * A plugin to add drop-down menus to column headers. + * + * USAGE: + * + * Add the plugin .js & .css files and register it with the grid. + * + * To specify a menu in a column header, extend the column definition like so: + * + * var columns = [ + * { + * id: 'myColumn', + * name: 'My column', + * + * // This is the relevant part + * header: { + * menu: { + * items: [ + * { + * // menu item options + * }, + * { + * // menu item options + * } + * ] + * } + * } + * } + * ]; + * + * + * Available menu options: + * tooltip: Menu button tooltip. + * + * + * Available menu item options: + * title: Menu item text. + * disabled: Whether the item is disabled. + * tooltip: Item tooltip. + * command: A command identifier to be passed to the onCommand event handlers. + * iconCssClass: A CSS class to be added to the menu item icon. + * iconImage: A url to the icon image. + * + * + * The plugin exposes the following events: + * onBeforeMenuShow: Fired before the menu is shown. You can customize the menu or dismiss it by returning false. + * Event args: + * grid: Reference to the grid. + * column: Column definition. + * menu: Menu options. Note that you can change the menu items here. + * + * onCommand: Fired on menu item click for buttons with 'command' specified. + * Event args: + * grid: Reference to the grid. + * column: Column definition. + * command: Button command identified. + * button: Button options. Note that you can change the button options in your + * event handler, and the column header will be automatically updated to + * reflect them. This is useful if you want to implement something like a + * toggle button. + * + * + * @param options {Object} Options: + * buttonCssClass: an extra CSS class to add to the menu button + * buttonImage: a url to the menu button image (default '../images/down.gif') + * @class Slick.Plugins.HeaderButtons + * @constructor + */ + function HeaderMenu(options) { + var _grid; + var _self = this; + var _handler = new Slick.EventHandler(); + var _defaults = { + buttonCssClass: null, + buttonImage: null + }; + var $menu; + var $activeHeaderColumn; + + + function init(grid) { + options = $.extend(true, {}, _defaults, options); + _grid = grid; + _handler + .subscribe(_grid.onHeaderCellRendered, handleHeaderCellRendered) + .subscribe(_grid.onBeforeHeaderCellDestroy, handleBeforeHeaderCellDestroy); + + // Force the grid to re-render the header now that the events are hooked up. + _grid.setColumns(_grid.getColumns()); + + // Hide the menu on outside click. + $(document.body).bind("mousedown", handleBodyMouseDown); + } + + + function destroy() { + _handler.unsubscribeAll(); + $(document.body).unbind("mousedown", handleBodyMouseDown); + } + + + function handleBodyMouseDown(e) { + if ($menu && $menu[0] != e.target && !$.contains($menu[0], e.target)) { + hideMenu(); + } + } + + + function hideMenu() { + if ($menu) { + $menu.remove(); + $menu = null; + $activeHeaderColumn + .removeClass("slick-header-column-active"); + } + } + + function handleHeaderCellRendered(e, args) { + var column = args.column; + var menu = column.header && column.header.menu; + + if (menu) { + var $el = $("
    ") + .addClass("slick-header-menubutton") + .data("column", column) + .data("menu", menu); + + if (options.buttonCssClass) { + $el.addClass(options.buttonCssClass); + } + + if (options.buttonImage) { + $el.css("background-image", "url(" + options.buttonImage + ")"); + } + + if (menu.tooltip) { + $el.attr("title", menu.tooltip); + } + + $el + .bind("click", showMenu) + .appendTo(args.node); + } + } + + + function handleBeforeHeaderCellDestroy(e, args) { + var column = args.column; + + if (column.header && column.header.menu) { + $(args.node).find(".slick-header-menubutton").remove(); + } + } + + + function showMenu(e) { + var $menuButton = $(this); + var menu = $menuButton.data("menu"); + var columnDef = $menuButton.data("column"); + + // Let the user modify the menu or cancel altogether, + // or provide alternative menu implementation. + if (_self.onBeforeMenuShow.notify({ + "grid": _grid, + "column": columnDef, + "menu": menu + }, e, _self) == false) { + return; + } + + + if (!$menu) { + $menu = $("
    ") + .appendTo(_grid.getContainerNode()); + } + $menu.empty(); + + + // Construct the menu items. + for (var i = 0; i < menu.items.length; i++) { + var item = menu.items[i]; + + var $li = $("
    ") + .data("command", item.command || '') + .data("column", columnDef) + .data("item", item) + .bind("click", handleMenuItemClick) + .appendTo($menu); + + if (item.disabled) { + $li.addClass("slick-header-menuitem-disabled"); + } + + if (item.tooltip) { + $li.attr("title", item.tooltip); + } + + var $icon = $("
    ") + .appendTo($li); + + if (item.iconCssClass) { + $icon.addClass(item.iconCssClass); + } + + if (item.iconImage) { + $icon.css("background-image", "url(" + item.iconImage + ")"); + } + + $("") + .text(item.title) + .appendTo($li); + } + + + // Position the menu. + $menu + .offset({ top: $(this).offset().top + $(this).height(), left: $(this).offset().left }); + + + // Mark the header as active to keep the highlighting. + $activeHeaderColumn = $menuButton.closest(".slick-header-column"); + $activeHeaderColumn + .addClass("slick-header-column-active"); + + // Stop propagation so that it doesn't register as a header click event. + e.preventDefault(); + e.stopPropagation(); + } + + + function handleMenuItemClick(e) { + var command = $(this).data("command"); + var columnDef = $(this).data("column"); + var item = $(this).data("item"); + + if (item.disabled) { + return; + } + + hideMenu(); + + if (command != null && command != '') { + _self.onCommand.notify({ + "grid": _grid, + "column": columnDef, + "command": command, + "item": item + }, e, _self); + } + + // Stop propagation so that it doesn't register as a header click event. + e.preventDefault(); + e.stopPropagation(); + } + + $.extend(this, { + "init": init, + "destroy": destroy, + + "onBeforeMenuShow": new Slick.Event(), + "onCommand": new Slick.Event() + }); + } +})(jQuery); diff --git a/src/ckanext-d4science_theme/ckanext/reclineview/theme/public/vendor/slickgrid/2.2/plugins/slick.rowmovemanager.js b/src/ckanext-d4science_theme/ckanext/reclineview/theme/public/vendor/slickgrid/2.2/plugins/slick.rowmovemanager.js new file mode 100644 index 0000000..5f87a1e --- /dev/null +++ b/src/ckanext-d4science_theme/ckanext/reclineview/theme/public/vendor/slickgrid/2.2/plugins/slick.rowmovemanager.js @@ -0,0 +1,138 @@ +(function ($) { + // register namespace + $.extend(true, window, { + "Slick": { + "RowMoveManager": RowMoveManager + } + }); + + function RowMoveManager(options) { + var _grid; + var _canvas; + var _dragging; + var _self = this; + var _handler = new Slick.EventHandler(); + var _defaults = { + cancelEditOnDrag: false + }; + + function init(grid) { + options = $.extend(true, {}, _defaults, options); + _grid = grid; + _canvas = _grid.getCanvasNode(); + _handler + .subscribe(_grid.onDragInit, handleDragInit) + .subscribe(_grid.onDragStart, handleDragStart) + .subscribe(_grid.onDrag, handleDrag) + .subscribe(_grid.onDragEnd, handleDragEnd); + } + + function destroy() { + _handler.unsubscribeAll(); + } + + function handleDragInit(e, dd) { + // prevent the grid from cancelling drag'n'drop by default + e.stopImmediatePropagation(); + } + + function handleDragStart(e, dd) { + var cell = _grid.getCellFromEvent(e); + + if (options.cancelEditOnDrag && _grid.getEditorLock().isActive()) { + _grid.getEditorLock().cancelCurrentEdit(); + } + + if (_grid.getEditorLock().isActive() || !/move|selectAndMove/.test(_grid.getColumns()[cell.cell].behavior)) { + return false; + } + + _dragging = true; + e.stopImmediatePropagation(); + + var selectedRows = _grid.getSelectedRows(); + + if (selectedRows.length == 0 || $.inArray(cell.row, selectedRows) == -1) { + selectedRows = [cell.row]; + _grid.setSelectedRows(selectedRows); + } + + var rowHeight = _grid.getOptions().rowHeight; + + dd.selectedRows = selectedRows; + + dd.selectionProxy = $("
    ") + .css("position", "absolute") + .css("zIndex", "99999") + .css("width", $(_canvas).innerWidth()) + .css("height", rowHeight * selectedRows.length) + .appendTo(_canvas); + + dd.guide = $("
    ") + .css("position", "absolute") + .css("zIndex", "99998") + .css("width", $(_canvas).innerWidth()) + .css("top", -1000) + .appendTo(_canvas); + + dd.insertBefore = -1; + } + + function handleDrag(e, dd) { + if (!_dragging) { + return; + } + + e.stopImmediatePropagation(); + + var top = e.pageY - $(_canvas).offset().top; + dd.selectionProxy.css("top", top - 5); + + var insertBefore = Math.max(0, Math.min(Math.round(top / _grid.getOptions().rowHeight), _grid.getDataLength())); + if (insertBefore !== dd.insertBefore) { + var eventData = { + "rows": dd.selectedRows, + "insertBefore": insertBefore + }; + + if (_self.onBeforeMoveRows.notify(eventData) === false) { + dd.guide.css("top", -1000); + dd.canMove = false; + } else { + dd.guide.css("top", insertBefore * _grid.getOptions().rowHeight); + dd.canMove = true; + } + + dd.insertBefore = insertBefore; + } + } + + function handleDragEnd(e, dd) { + if (!_dragging) { + return; + } + _dragging = false; + e.stopImmediatePropagation(); + + dd.guide.remove(); + dd.selectionProxy.remove(); + + if (dd.canMove) { + var eventData = { + "rows": dd.selectedRows, + "insertBefore": dd.insertBefore + }; + // TODO: _grid.remapCellCssClasses ? + _self.onMoveRows.notify(eventData); + } + } + + $.extend(this, { + "onBeforeMoveRows": new Slick.Event(), + "onMoveRows": new Slick.Event(), + + "init": init, + "destroy": destroy + }); + } +})(jQuery); \ No newline at end of file diff --git a/src/ckanext-d4science_theme/ckanext/reclineview/theme/public/vendor/slickgrid/2.2/plugins/slick.rowselectionmodel.js b/src/ckanext-d4science_theme/ckanext/reclineview/theme/public/vendor/slickgrid/2.2/plugins/slick.rowselectionmodel.js new file mode 100644 index 0000000..0de8dd3 --- /dev/null +++ b/src/ckanext-d4science_theme/ckanext/reclineview/theme/public/vendor/slickgrid/2.2/plugins/slick.rowselectionmodel.js @@ -0,0 +1,187 @@ +(function ($) { + // register namespace + $.extend(true, window, { + "Slick": { + "RowSelectionModel": RowSelectionModel + } + }); + + function RowSelectionModel(options) { + var _grid; + var _ranges = []; + var _self = this; + var _handler = new Slick.EventHandler(); + var _inHandler; + var _options; + var _defaults = { + selectActiveRow: true + }; + + function init(grid) { + _options = $.extend(true, {}, _defaults, options); + _grid = grid; + _handler.subscribe(_grid.onActiveCellChanged, + wrapHandler(handleActiveCellChange)); + _handler.subscribe(_grid.onKeyDown, + wrapHandler(handleKeyDown)); + _handler.subscribe(_grid.onClick, + wrapHandler(handleClick)); + } + + function destroy() { + _handler.unsubscribeAll(); + } + + function wrapHandler(handler) { + return function () { + if (!_inHandler) { + _inHandler = true; + handler.apply(this, arguments); + _inHandler = false; + } + }; + } + + function rangesToRows(ranges) { + var rows = []; + for (var i = 0; i < ranges.length; i++) { + for (var j = ranges[i].fromRow; j <= ranges[i].toRow; j++) { + rows.push(j); + } + } + return rows; + } + + function rowsToRanges(rows) { + var ranges = []; + var lastCell = _grid.getColumns().length - 1; + for (var i = 0; i < rows.length; i++) { + ranges.push(new Slick.Range(rows[i], 0, rows[i], lastCell)); + } + return ranges; + } + + function getRowsRange(from, to) { + var i, rows = []; + for (i = from; i <= to; i++) { + rows.push(i); + } + for (i = to; i < from; i++) { + rows.push(i); + } + return rows; + } + + function getSelectedRows() { + return rangesToRows(_ranges); + } + + function setSelectedRows(rows) { + setSelectedRanges(rowsToRanges(rows)); + } + + function setSelectedRanges(ranges) { + _ranges = ranges; + _self.onSelectedRangesChanged.notify(_ranges); + } + + function getSelectedRanges() { + return _ranges; + } + + function handleActiveCellChange(e, data) { + if (_options.selectActiveRow && data.row != null) { + setSelectedRanges([new Slick.Range(data.row, 0, data.row, _grid.getColumns().length - 1)]); + } + } + + function handleKeyDown(e) { + var activeRow = _grid.getActiveCell(); + if (activeRow && e.shiftKey && !e.ctrlKey && !e.altKey && !e.metaKey && (e.which == 38 || e.which == 40)) { + var selectedRows = getSelectedRows(); + selectedRows.sort(function (x, y) { + return x - y + }); + + if (!selectedRows.length) { + selectedRows = [activeRow.row]; + } + + var top = selectedRows[0]; + var bottom = selectedRows[selectedRows.length - 1]; + var active; + + if (e.which == 40) { + active = activeRow.row < bottom || top == bottom ? ++bottom : ++top; + } else { + active = activeRow.row < bottom ? --bottom : --top; + } + + if (active >= 0 && active < _grid.getDataLength()) { + _grid.scrollRowIntoView(active); + _ranges = rowsToRanges(getRowsRange(top, bottom)); + setSelectedRanges(_ranges); + } + + e.preventDefault(); + e.stopPropagation(); + } + } + + function handleClick(e) { + var cell = _grid.getCellFromEvent(e); + if (!cell || !_grid.canCellBeActive(cell.row, cell.cell)) { + return false; + } + + if (!_grid.getOptions().multiSelect || ( + !e.ctrlKey && !e.shiftKey && !e.metaKey)) { + return false; + } + + var selection = rangesToRows(_ranges); + var idx = $.inArray(cell.row, selection); + + if (idx === -1 && (e.ctrlKey || e.metaKey)) { + selection.push(cell.row); + _grid.setActiveCell(cell.row, cell.cell); + } else if (idx !== -1 && (e.ctrlKey || e.metaKey)) { + selection = $.grep(selection, function (o, i) { + return (o !== cell.row); + }); + _grid.setActiveCell(cell.row, cell.cell); + } else if (selection.length && e.shiftKey) { + var last = selection.pop(); + var from = Math.min(cell.row, last); + var to = Math.max(cell.row, last); + selection = []; + for (var i = from; i <= to; i++) { + if (i !== last) { + selection.push(i); + } + } + selection.push(last); + _grid.setActiveCell(cell.row, cell.cell); + } + + _ranges = rowsToRanges(selection); + setSelectedRanges(_ranges); + e.stopImmediatePropagation(); + + return true; + } + + $.extend(this, { + "getSelectedRows": getSelectedRows, + "setSelectedRows": setSelectedRows, + + "getSelectedRanges": getSelectedRanges, + "setSelectedRanges": setSelectedRanges, + + "init": init, + "destroy": destroy, + + "onSelectedRangesChanged": new Slick.Event() + }); + } +})(jQuery); \ No newline at end of file diff --git a/src/ckanext-d4science_theme/ckanext/reclineview/theme/public/vendor/slickgrid/2.2/slick-default-theme.css b/src/ckanext-d4science_theme/ckanext/reclineview/theme/public/vendor/slickgrid/2.2/slick-default-theme.css new file mode 100644 index 0000000..efc7415 --- /dev/null +++ b/src/ckanext-d4science_theme/ckanext/reclineview/theme/public/vendor/slickgrid/2.2/slick-default-theme.css @@ -0,0 +1,118 @@ +/* +IMPORTANT: +In order to preserve the uniform grid appearance, all cell styles need to have padding, margin and border sizes. +No built-in (selected, editable, highlight, flashing, invalid, loading, :focus) or user-specified CSS +classes should alter those! +*/ + +.slick-header-columns { + background: url('images/header-columns-bg.gif') repeat-x center bottom; + border-bottom: 1px solid silver; +} + +.slick-header-column { + background: url('images/header-columns-bg.gif') repeat-x center bottom; + border-right: 1px solid silver; +} + +.slick-header-column:hover, .slick-header-column-active { + background: white url('images/header-columns-over-bg.gif') repeat-x center bottom; +} + +.slick-headerrow { + background: #fafafa; +} + +.slick-headerrow-column { + background: #fafafa; + border-bottom: 0; + height: 100%; +} + +.slick-row.ui-state-active { + background: #F5F7D7; +} + +.slick-row { + position: absolute; + background: white; + border: 0px; + line-height: 20px; +} + +.slick-row.selected { + z-index: 10; + background: #DFE8F6; +} + +.slick-cell { + padding-left: 4px; + padding-right: 4px; +} + +.slick-group { + border-bottom: 2px solid silver; +} + +.slick-group-toggle { + width: 9px; + height: 9px; + margin-right: 5px; +} + +.slick-group-toggle.expanded { + background: url(images/collapse.gif) no-repeat center center; +} + +.slick-group-toggle.collapsed { + background: url(images/expand.gif) no-repeat center center; +} + +.slick-group-totals { + color: gray; + background: white; +} + +.slick-cell.selected { + background-color: beige; +} + +.slick-cell.active { + border-color: gray; + border-style: solid; +} + +.slick-sortable-placeholder { + background: silver !important; +} + +.slick-row.odd { + background: #fafafa; +} + +.slick-row.ui-state-active { + background: #F5F7D7; +} + +.slick-row.loading { + opacity: 0.5; + filter: alpha(opacity = 50); +} + +.slick-cell.invalid { + border-color: red; + -moz-animation-duration: 0.2s; + -webkit-animation-duration: 0.2s; + -moz-animation-name: slickgrid-invalid-hilite; + -webkit-animation-name: slickgrid-invalid-hilite; +} + +@-moz-keyframes slickgrid-invalid-hilite { + from { box-shadow: 0 0 6px red; } + to { box-shadow: none; } +} + +@-webkit-keyframes slickgrid-invalid-hilite { + from { box-shadow: 0 0 6px red; } + to { box-shadow: none; } +} \ No newline at end of file diff --git a/src/ckanext-d4science_theme/ckanext/reclineview/theme/public/vendor/slickgrid/2.2/slick.core.js b/src/ckanext-d4science_theme/ckanext/reclineview/theme/public/vendor/slickgrid/2.2/slick.core.js new file mode 100644 index 0000000..2f097b1 --- /dev/null +++ b/src/ckanext-d4science_theme/ckanext/reclineview/theme/public/vendor/slickgrid/2.2/slick.core.js @@ -0,0 +1,467 @@ +/*** + * Contains core SlickGrid classes. + * @module Core + * @namespace Slick + */ + +(function ($) { + // register namespace + $.extend(true, window, { + "Slick": { + "Event": Event, + "EventData": EventData, + "EventHandler": EventHandler, + "Range": Range, + "NonDataRow": NonDataItem, + "Group": Group, + "GroupTotals": GroupTotals, + "EditorLock": EditorLock, + + /*** + * A global singleton editor lock. + * @class GlobalEditorLock + * @static + * @constructor + */ + "GlobalEditorLock": new EditorLock() + } + }); + + /*** + * An event object for passing data to event handlers and letting them control propagation. + *

    This is pretty much identical to how W3C and jQuery implement events.

    + * @class EventData + * @constructor + */ + function EventData() { + var isPropagationStopped = false; + var isImmediatePropagationStopped = false; + + /*** + * Stops event from propagating up the DOM tree. + * @method stopPropagation + */ + this.stopPropagation = function () { + isPropagationStopped = true; + }; + + /*** + * Returns whether stopPropagation was called on this event object. + * @method isPropagationStopped + * @return {Boolean} + */ + this.isPropagationStopped = function () { + return isPropagationStopped; + }; + + /*** + * Prevents the rest of the handlers from being executed. + * @method stopImmediatePropagation + */ + this.stopImmediatePropagation = function () { + isImmediatePropagationStopped = true; + }; + + /*** + * Returns whether stopImmediatePropagation was called on this event object.\ + * @method isImmediatePropagationStopped + * @return {Boolean} + */ + this.isImmediatePropagationStopped = function () { + return isImmediatePropagationStopped; + } + } + + /*** + * A simple publisher-subscriber implementation. + * @class Event + * @constructor + */ + function Event() { + var handlers = []; + + /*** + * Adds an event handler to be called when the event is fired. + *

    Event handler will receive two arguments - an EventData and the data + * object the event was fired with.

    + * @method subscribe + * @param fn {Function} Event handler. + */ + this.subscribe = function (fn) { + handlers.push(fn); + }; + + /*** + * Removes an event handler added with subscribe(fn). + * @method unsubscribe + * @param fn {Function} Event handler to be removed. + */ + this.unsubscribe = function (fn) { + for (var i = handlers.length - 1; i >= 0; i--) { + if (handlers[i] === fn) { + handlers.splice(i, 1); + } + } + }; + + /*** + * Fires an event notifying all subscribers. + * @method notify + * @param args {Object} Additional data object to be passed to all handlers. + * @param e {EventData} + * Optional. + * An EventData object to be passed to all handlers. + * For DOM events, an existing W3C/jQuery event object can be passed in. + * @param scope {Object} + * Optional. + * The scope ("this") within which the handler will be executed. + * If not specified, the scope will be set to the Event instance. + */ + this.notify = function (args, e, scope) { + e = e || new EventData(); + scope = scope || this; + + var returnValue; + for (var i = 0; i < handlers.length && !(e.isPropagationStopped() || e.isImmediatePropagationStopped()); i++) { + returnValue = handlers[i].call(scope, e, args); + } + + return returnValue; + }; + } + + function EventHandler() { + var handlers = []; + + this.subscribe = function (event, handler) { + handlers.push({ + event: event, + handler: handler + }); + event.subscribe(handler); + + return this; // allow chaining + }; + + this.unsubscribe = function (event, handler) { + var i = handlers.length; + while (i--) { + if (handlers[i].event === event && + handlers[i].handler === handler) { + handlers.splice(i, 1); + event.unsubscribe(handler); + return; + } + } + + return this; // allow chaining + }; + + this.unsubscribeAll = function () { + var i = handlers.length; + while (i--) { + handlers[i].event.unsubscribe(handlers[i].handler); + } + handlers = []; + + return this; // allow chaining + } + } + + /*** + * A structure containing a range of cells. + * @class Range + * @constructor + * @param fromRow {Integer} Starting row. + * @param fromCell {Integer} Starting cell. + * @param toRow {Integer} Optional. Ending row. Defaults to fromRow. + * @param toCell {Integer} Optional. Ending cell. Defaults to fromCell. + */ + function Range(fromRow, fromCell, toRow, toCell) { + if (toRow === undefined && toCell === undefined) { + toRow = fromRow; + toCell = fromCell; + } + + /*** + * @property fromRow + * @type {Integer} + */ + this.fromRow = Math.min(fromRow, toRow); + + /*** + * @property fromCell + * @type {Integer} + */ + this.fromCell = Math.min(fromCell, toCell); + + /*** + * @property toRow + * @type {Integer} + */ + this.toRow = Math.max(fromRow, toRow); + + /*** + * @property toCell + * @type {Integer} + */ + this.toCell = Math.max(fromCell, toCell); + + /*** + * Returns whether a range represents a single row. + * @method isSingleRow + * @return {Boolean} + */ + this.isSingleRow = function () { + return this.fromRow == this.toRow; + }; + + /*** + * Returns whether a range represents a single cell. + * @method isSingleCell + * @return {Boolean} + */ + this.isSingleCell = function () { + return this.fromRow == this.toRow && this.fromCell == this.toCell; + }; + + /*** + * Returns whether a range contains a given cell. + * @method contains + * @param row {Integer} + * @param cell {Integer} + * @return {Boolean} + */ + this.contains = function (row, cell) { + return row >= this.fromRow && row <= this.toRow && + cell >= this.fromCell && cell <= this.toCell; + }; + + /*** + * Returns a readable representation of a range. + * @method toString + * @return {String} + */ + this.toString = function () { + if (this.isSingleCell()) { + return "(" + this.fromRow + ":" + this.fromCell + ")"; + } + else { + return "(" + this.fromRow + ":" + this.fromCell + " - " + this.toRow + ":" + this.toCell + ")"; + } + } + } + + + /*** + * A base class that all special / non-data rows (like Group and GroupTotals) derive from. + * @class NonDataItem + * @constructor + */ + function NonDataItem() { + this.__nonDataRow = true; + } + + + /*** + * Information about a group of rows. + * @class Group + * @extends Slick.NonDataItem + * @constructor + */ + function Group() { + this.__group = true; + + /** + * Grouping level, starting with 0. + * @property level + * @type {Number} + */ + this.level = 0; + + /*** + * Number of rows in the group. + * @property count + * @type {Integer} + */ + this.count = 0; + + /*** + * Grouping value. + * @property value + * @type {Object} + */ + this.value = null; + + /*** + * Formatted display value of the group. + * @property title + * @type {String} + */ + this.title = null; + + /*** + * Whether a group is collapsed. + * @property collapsed + * @type {Boolean} + */ + this.collapsed = false; + + /*** + * GroupTotals, if any. + * @property totals + * @type {GroupTotals} + */ + this.totals = null; + + /** + * Rows that are part of the group. + * @property rows + * @type {Array} + */ + this.rows = []; + + /** + * Sub-groups that are part of the group. + * @property groups + * @type {Array} + */ + this.groups = null; + + /** + * A unique key used to identify the group. This key can be used in calls to DataView + * collapseGroup() or expandGroup(). + * @property groupingKey + * @type {Object} + */ + this.groupingKey = null; + } + + Group.prototype = new NonDataItem(); + + /*** + * Compares two Group instances. + * @method equals + * @return {Boolean} + * @param group {Group} Group instance to compare to. + */ + Group.prototype.equals = function (group) { + return this.value === group.value && + this.count === group.count && + this.collapsed === group.collapsed && + this.title === group.title; + }; + + /*** + * Information about group totals. + * An instance of GroupTotals will be created for each totals row and passed to the aggregators + * so that they can store arbitrary data in it. That data can later be accessed by group totals + * formatters during the display. + * @class GroupTotals + * @extends Slick.NonDataItem + * @constructor + */ + function GroupTotals() { + this.__groupTotals = true; + + /*** + * Parent Group. + * @param group + * @type {Group} + */ + this.group = null; + + /*** + * Whether the totals have been fully initialized / calculated. + * Will be set to false for lazy-calculated group totals. + * @param initialized + * @type {Boolean} + */ + this.initialized = false; + } + + GroupTotals.prototype = new NonDataItem(); + + /*** + * A locking helper to track the active edit controller and ensure that only a single controller + * can be active at a time. This prevents a whole class of state and validation synchronization + * issues. An edit controller (such as SlickGrid) can query if an active edit is in progress + * and attempt a commit or cancel before proceeding. + * @class EditorLock + * @constructor + */ + function EditorLock() { + var activeEditController = null; + + /*** + * Returns true if a specified edit controller is active (has the edit lock). + * If the parameter is not specified, returns true if any edit controller is active. + * @method isActive + * @param editController {EditController} + * @return {Boolean} + */ + this.isActive = function (editController) { + return (editController ? activeEditController === editController : activeEditController !== null); + }; + + /*** + * Sets the specified edit controller as the active edit controller (acquire edit lock). + * If another edit controller is already active, and exception will be thrown. + * @method activate + * @param editController {EditController} edit controller acquiring the lock + */ + this.activate = function (editController) { + if (editController === activeEditController) { // already activated? + return; + } + if (activeEditController !== null) { + throw "SlickGrid.EditorLock.activate: an editController is still active, can't activate another editController"; + } + if (!editController.commitCurrentEdit) { + throw "SlickGrid.EditorLock.activate: editController must implement .commitCurrentEdit()"; + } + if (!editController.cancelCurrentEdit) { + throw "SlickGrid.EditorLock.activate: editController must implement .cancelCurrentEdit()"; + } + activeEditController = editController; + }; + + /*** + * Unsets the specified edit controller as the active edit controller (release edit lock). + * If the specified edit controller is not the active one, an exception will be thrown. + * @method deactivate + * @param editController {EditController} edit controller releasing the lock + */ + this.deactivate = function (editController) { + if (activeEditController !== editController) { + throw "SlickGrid.EditorLock.deactivate: specified editController is not the currently active one"; + } + activeEditController = null; + }; + + /*** + * Attempts to commit the current edit by calling "commitCurrentEdit" method on the active edit + * controller and returns whether the commit attempt was successful (commit may fail due to validation + * errors, etc.). Edit controller's "commitCurrentEdit" must return true if the commit has succeeded + * and false otherwise. If no edit controller is active, returns true. + * @method commitCurrentEdit + * @return {Boolean} + */ + this.commitCurrentEdit = function () { + return (activeEditController ? activeEditController.commitCurrentEdit() : true); + }; + + /*** + * Attempts to cancel the current edit by calling "cancelCurrentEdit" method on the active edit + * controller and returns whether the edit was successfully cancelled. If no edit controller is + * active, returns true. + * @method cancelCurrentEdit + * @return {Boolean} + */ + this.cancelCurrentEdit = function cancelCurrentEdit() { + return (activeEditController ? activeEditController.cancelCurrentEdit() : true); + }; + } +})(jQuery); + + diff --git a/src/ckanext-d4science_theme/ckanext/reclineview/theme/public/vendor/slickgrid/2.2/slick.dataview.js b/src/ckanext-d4science_theme/ckanext/reclineview/theme/public/vendor/slickgrid/2.2/slick.dataview.js new file mode 100644 index 0000000..f1c1b5e --- /dev/null +++ b/src/ckanext-d4science_theme/ckanext/reclineview/theme/public/vendor/slickgrid/2.2/slick.dataview.js @@ -0,0 +1,1126 @@ +(function ($) { + $.extend(true, window, { + Slick: { + Data: { + DataView: DataView, + Aggregators: { + Avg: AvgAggregator, + Min: MinAggregator, + Max: MaxAggregator, + Sum: SumAggregator + } + } + } + }); + + + /*** + * A sample Model implementation. + * Provides a filtered view of the underlying data. + * + * Relies on the data item having an "id" property uniquely identifying it. + */ + function DataView(options) { + var self = this; + + var defaults = { + groupItemMetadataProvider: null, + inlineFilters: false + }; + + + // private + var idProperty = "id"; // property holding a unique row id + var items = []; // data by index + var rows = []; // data by row + var idxById = {}; // indexes by id + var rowsById = null; // rows by id; lazy-calculated + var filter = null; // filter function + var updated = null; // updated item ids + var suspend = false; // suspends the recalculation + var sortAsc = true; + var fastSortField; + var sortComparer; + var refreshHints = {}; + var prevRefreshHints = {}; + var filterArgs; + var filteredItems = []; + var compiledFilter; + var compiledFilterWithCaching; + var filterCache = []; + + // grouping + var groupingInfoDefaults = { + getter: null, + formatter: null, + comparer: function(a, b) { return a.value - b.value; }, + predefinedValues: [], + aggregators: [], + aggregateEmpty: false, + aggregateCollapsed: false, + aggregateChildGroups: false, + collapsed: false, + displayTotalsRow: true, + lazyTotalsCalculation: false + }; + var groupingInfos = []; + var groups = []; + var toggledGroupsByLevel = []; + var groupingDelimiter = ':|:'; + + var pagesize = 0; + var pagenum = 0; + var totalRows = 0; + + // events + var onRowCountChanged = new Slick.Event(); + var onRowsChanged = new Slick.Event(); + var onPagingInfoChanged = new Slick.Event(); + + options = $.extend(true, {}, defaults, options); + + + function beginUpdate() { + suspend = true; + } + + function endUpdate() { + suspend = false; + refresh(); + } + + function setRefreshHints(hints) { + refreshHints = hints; + } + + function setFilterArgs(args) { + filterArgs = args; + } + + function updateIdxById(startingIndex) { + startingIndex = startingIndex || 0; + var id; + for (var i = startingIndex, l = items.length; i < l; i++) { + id = items[i][idProperty]; + if (id === undefined) { + throw "Each data element must implement a unique 'id' property"; + } + idxById[id] = i; + } + } + + function ensureIdUniqueness() { + var id; + for (var i = 0, l = items.length; i < l; i++) { + id = items[i][idProperty]; + if (id === undefined || idxById[id] !== i) { + throw "Each data element must implement a unique 'id' property"; + } + } + } + + function getItems() { + return items; + } + + function setItems(data, objectIdProperty) { + if (objectIdProperty !== undefined) { + idProperty = objectIdProperty; + } + items = filteredItems = data; + idxById = {}; + updateIdxById(); + ensureIdUniqueness(); + refresh(); + } + + function setPagingOptions(args) { + if (args.pageSize != undefined) { + pagesize = args.pageSize; + pagenum = pagesize ? Math.min(pagenum, Math.max(0, Math.ceil(totalRows / pagesize) - 1)) : 0; + } + + if (args.pageNum != undefined) { + pagenum = Math.min(args.pageNum, Math.max(0, Math.ceil(totalRows / pagesize) - 1)); + } + + onPagingInfoChanged.notify(getPagingInfo(), null, self); + + refresh(); + } + + function getPagingInfo() { + var totalPages = pagesize ? Math.max(1, Math.ceil(totalRows / pagesize)) : 1; + return {pageSize: pagesize, pageNum: pagenum, totalRows: totalRows, totalPages: totalPages}; + } + + function sort(comparer, ascending) { + sortAsc = ascending; + sortComparer = comparer; + fastSortField = null; + if (ascending === false) { + items.reverse(); + } + items.sort(comparer); + if (ascending === false) { + items.reverse(); + } + idxById = {}; + updateIdxById(); + refresh(); + } + + /*** + * Provides a workaround for the extremely slow sorting in IE. + * Does a [lexicographic] sort on a give column by temporarily overriding Object.prototype.toString + * to return the value of that field and then doing a native Array.sort(). + */ + function fastSort(field, ascending) { + sortAsc = ascending; + fastSortField = field; + sortComparer = null; + var oldToString = Object.prototype.toString; + Object.prototype.toString = (typeof field == "function") ? field : function () { + return this[field] + }; + // an extra reversal for descending sort keeps the sort stable + // (assuming a stable native sort implementation, which isn't true in some cases) + if (ascending === false) { + items.reverse(); + } + items.sort(); + Object.prototype.toString = oldToString; + if (ascending === false) { + items.reverse(); + } + idxById = {}; + updateIdxById(); + refresh(); + } + + function reSort() { + if (sortComparer) { + sort(sortComparer, sortAsc); + } else if (fastSortField) { + fastSort(fastSortField, sortAsc); + } + } + + function setFilter(filterFn) { + filter = filterFn; + if (options.inlineFilters) { + compiledFilter = compileFilter(); + compiledFilterWithCaching = compileFilterWithCaching(); + } + refresh(); + } + + function getGrouping() { + return groupingInfos; + } + + function setGrouping(groupingInfo) { + if (!options.groupItemMetadataProvider) { + options.groupItemMetadataProvider = new Slick.Data.GroupItemMetadataProvider(); + } + + groups = []; + toggledGroupsByLevel = []; + groupingInfo = groupingInfo || []; + groupingInfos = (groupingInfo instanceof Array) ? groupingInfo : [groupingInfo]; + + for (var i = 0; i < groupingInfos.length; i++) { + var gi = groupingInfos[i] = $.extend(true, {}, groupingInfoDefaults, groupingInfos[i]); + gi.getterIsAFn = typeof gi.getter === "function"; + + // pre-compile accumulator loops + gi.compiledAccumulators = []; + var idx = gi.aggregators.length; + while (idx--) { + gi.compiledAccumulators[idx] = compileAccumulatorLoop(gi.aggregators[idx]); + } + + toggledGroupsByLevel[i] = {}; + } + + refresh(); + } + + /** + * @deprecated Please use {@link setGrouping}. + */ + function groupBy(valueGetter, valueFormatter, sortComparer) { + if (valueGetter == null) { + setGrouping([]); + return; + } + + setGrouping({ + getter: valueGetter, + formatter: valueFormatter, + comparer: sortComparer + }); + } + + /** + * @deprecated Please use {@link setGrouping}. + */ + function setAggregators(groupAggregators, includeCollapsed) { + if (!groupingInfos.length) { + throw new Error("At least one grouping must be specified before calling setAggregators()."); + } + + groupingInfos[0].aggregators = groupAggregators; + groupingInfos[0].aggregateCollapsed = includeCollapsed; + + setGrouping(groupingInfos); + } + + function getItemByIdx(i) { + return items[i]; + } + + function getIdxById(id) { + return idxById[id]; + } + + function ensureRowsByIdCache() { + if (!rowsById) { + rowsById = {}; + for (var i = 0, l = rows.length; i < l; i++) { + rowsById[rows[i][idProperty]] = i; + } + } + } + + function getRowById(id) { + ensureRowsByIdCache(); + return rowsById[id]; + } + + function getItemById(id) { + return items[idxById[id]]; + } + + function mapIdsToRows(idArray) { + var rows = []; + ensureRowsByIdCache(); + for (var i = 0, l = idArray.length; i < l; i++) { + var row = rowsById[idArray[i]]; + if (row != null) { + rows[rows.length] = row; + } + } + return rows; + } + + function mapRowsToIds(rowArray) { + var ids = []; + for (var i = 0, l = rowArray.length; i < l; i++) { + if (rowArray[i] < rows.length) { + ids[ids.length] = rows[rowArray[i]][idProperty]; + } + } + return ids; + } + + function updateItem(id, item) { + if (idxById[id] === undefined || id !== item[idProperty]) { + throw "Invalid or non-matching id"; + } + items[idxById[id]] = item; + if (!updated) { + updated = {}; + } + updated[id] = true; + refresh(); + } + + function insertItem(insertBefore, item) { + items.splice(insertBefore, 0, item); + updateIdxById(insertBefore); + refresh(); + } + + function addItem(item) { + items.push(item); + updateIdxById(items.length - 1); + refresh(); + } + + function deleteItem(id) { + var idx = idxById[id]; + if (idx === undefined) { + throw "Invalid id"; + } + delete idxById[id]; + items.splice(idx, 1); + updateIdxById(idx); + refresh(); + } + + function getLength() { + return rows.length; + } + + function getItem(i) { + var item = rows[i]; + + // if this is a group row, make sure totals are calculated and update the title + if (item && item.__group && item.totals && !item.totals.initialized) { + var gi = groupingInfos[item.level]; + if (!gi.displayTotalsRow) { + calculateTotals(item.totals); + item.title = gi.formatter ? gi.formatter(item) : item.value; + } + } + // if this is a totals row, make sure it's calculated + else if (item && item.__groupTotals && !item.initialized) { + calculateTotals(item); + } + + return item; + } + + function getItemMetadata(i) { + var item = rows[i]; + if (item === undefined) { + return null; + } + + // overrides for grouping rows + if (item.__group) { + return options.groupItemMetadataProvider.getGroupRowMetadata(item); + } + + // overrides for totals rows + if (item.__groupTotals) { + return options.groupItemMetadataProvider.getTotalsRowMetadata(item); + } + + return null; + } + + function expandCollapseAllGroups(level, collapse) { + if (level == null) { + for (var i = 0; i < groupingInfos.length; i++) { + toggledGroupsByLevel[i] = {}; + groupingInfos[i].collapsed = collapse; + } + } else { + toggledGroupsByLevel[level] = {}; + groupingInfos[level].collapsed = collapse; + } + refresh(); + } + + /** + * @param level {Number} Optional level to collapse. If not specified, applies to all levels. + */ + function collapseAllGroups(level) { + expandCollapseAllGroups(level, true); + } + + /** + * @param level {Number} Optional level to expand. If not specified, applies to all levels. + */ + function expandAllGroups(level) { + expandCollapseAllGroups(level, false); + } + + function expandCollapseGroup(level, groupingKey, collapse) { + toggledGroupsByLevel[level][groupingKey] = groupingInfos[level].collapsed ^ collapse; + refresh(); + } + + /** + * @param varArgs Either a Slick.Group's "groupingKey" property, or a + * variable argument list of grouping values denoting a unique path to the row. For + * example, calling collapseGroup('high', '10%') will collapse the '10%' subgroup of + * the 'high' group. + */ + function collapseGroup(varArgs) { + var args = Array.prototype.slice.call(arguments); + var arg0 = args[0]; + if (args.length == 1 && arg0.indexOf(groupingDelimiter) != -1) { + expandCollapseGroup(arg0.split(groupingDelimiter).length - 1, arg0, true); + } else { + expandCollapseGroup(args.length - 1, args.join(groupingDelimiter), true); + } + } + + /** + * @param varArgs Either a Slick.Group's "groupingKey" property, or a + * variable argument list of grouping values denoting a unique path to the row. For + * example, calling expandGroup('high', '10%') will expand the '10%' subgroup of + * the 'high' group. + */ + function expandGroup(varArgs) { + var args = Array.prototype.slice.call(arguments); + var arg0 = args[0]; + if (args.length == 1 && arg0.indexOf(groupingDelimiter) != -1) { + expandCollapseGroup(arg0.split(groupingDelimiter).length - 1, arg0, false); + } else { + expandCollapseGroup(args.length - 1, args.join(groupingDelimiter), false); + } + } + + function getGroups() { + return groups; + } + + function extractGroups(rows, parentGroup) { + var group; + var val; + var groups = []; + var groupsByVal = {}; + var r; + var level = parentGroup ? parentGroup.level + 1 : 0; + var gi = groupingInfos[level]; + + for (var i = 0, l = gi.predefinedValues.length; i < l; i++) { + val = gi.predefinedValues[i]; + group = groupsByVal[val]; + if (!group) { + group = new Slick.Group(); + group.value = val; + group.level = level; + group.groupingKey = (parentGroup ? parentGroup.groupingKey + groupingDelimiter : '') + val; + groups[groups.length] = group; + groupsByVal[val] = group; + } + } + + for (var i = 0, l = rows.length; i < l; i++) { + r = rows[i]; + val = gi.getterIsAFn ? gi.getter(r) : r[gi.getter]; + group = groupsByVal[val]; + if (!group) { + group = new Slick.Group(); + group.value = val; + group.level = level; + group.groupingKey = (parentGroup ? parentGroup.groupingKey + groupingDelimiter : '') + val; + groups[groups.length] = group; + groupsByVal[val] = group; + } + + group.rows[group.count++] = r; + } + + if (level < groupingInfos.length - 1) { + for (var i = 0; i < groups.length; i++) { + group = groups[i]; + group.groups = extractGroups(group.rows, group); + } + } + + groups.sort(groupingInfos[level].comparer); + + return groups; + } + + function calculateTotals(totals) { + var group = totals.group; + var gi = groupingInfos[group.level]; + var isLeafLevel = (group.level == groupingInfos.length); + var agg, idx = gi.aggregators.length; + + if (!isLeafLevel && gi.aggregateChildGroups) { + // make sure all the subgroups are calculated + var i = group.groups.length; + while (i--) { + if (!group.groups[i].initialized) { + calculateTotals(group.groups[i]); + } + } + } + + while (idx--) { + agg = gi.aggregators[idx]; + agg.init(); + if (!isLeafLevel && gi.aggregateChildGroups) { + gi.compiledAccumulators[idx].call(agg, group.groups); + } else { + gi.compiledAccumulators[idx].call(agg, group.rows); + } + agg.storeResult(totals); + } + totals.initialized = true; + } + + function addGroupTotals(group) { + var gi = groupingInfos[group.level]; + var totals = new Slick.GroupTotals(); + totals.group = group; + group.totals = totals; + if (!gi.lazyTotalsCalculation) { + calculateTotals(totals); + } + } + + function addTotals(groups, level) { + level = level || 0; + var gi = groupingInfos[level]; + var groupCollapsed = gi.collapsed; + var toggledGroups = toggledGroupsByLevel[level]; + var idx = groups.length, g; + while (idx--) { + g = groups[idx]; + + if (g.collapsed && !gi.aggregateCollapsed) { + continue; + } + + // Do a depth-first aggregation so that parent group aggregators can access subgroup totals. + if (g.groups) { + addTotals(g.groups, level + 1); + } + + if (gi.aggregators.length && ( + gi.aggregateEmpty || g.rows.length || (g.groups && g.groups.length))) { + addGroupTotals(g); + } + + g.collapsed = groupCollapsed ^ toggledGroups[g.groupingKey]; + g.title = gi.formatter ? gi.formatter(g) : g.value; + } + } + + function flattenGroupedRows(groups, level) { + level = level || 0; + var gi = groupingInfos[level]; + var groupedRows = [], rows, gl = 0, g; + for (var i = 0, l = groups.length; i < l; i++) { + g = groups[i]; + groupedRows[gl++] = g; + + if (!g.collapsed) { + rows = g.groups ? flattenGroupedRows(g.groups, level + 1) : g.rows; + for (var j = 0, jj = rows.length; j < jj; j++) { + groupedRows[gl++] = rows[j]; + } + } + + if (g.totals && gi.displayTotalsRow && (!g.collapsed || gi.aggregateCollapsed)) { + groupedRows[gl++] = g.totals; + } + } + return groupedRows; + } + + function getFunctionInfo(fn) { + var fnRegex = /^function[^(]*\(([^)]*)\)\s*{([\s\S]*)}$/; + var matches = fn.toString().match(fnRegex); + return { + params: matches[1].split(","), + body: matches[2] + }; + } + + function compileAccumulatorLoop(aggregator) { + var accumulatorInfo = getFunctionInfo(aggregator.accumulate); + var fn = new Function( + "_items", + "for (var " + accumulatorInfo.params[0] + ", _i=0, _il=_items.length; _i<_il; _i++) {" + + accumulatorInfo.params[0] + " = _items[_i]; " + + accumulatorInfo.body + + "}" + ); + fn.displayName = fn.name = "compiledAccumulatorLoop"; + return fn; + } + + function compileFilter() { + var filterInfo = getFunctionInfo(filter); + + var filterBody = filterInfo.body + .replace(/return false\s*([;}]|$)/gi, "{ continue _coreloop; }$1") + .replace(/return true\s*([;}]|$)/gi, "{ _retval[_idx++] = $item$; continue _coreloop; }$1") + .replace(/return ([^;}]+?)\s*([;}]|$)/gi, + "{ if ($1) { _retval[_idx++] = $item$; }; continue _coreloop; }$2"); + + // This preserves the function template code after JS compression, + // so that replace() commands still work as expected. + var tpl = [ + //"function(_items, _args) { ", + "var _retval = [], _idx = 0; ", + "var $item$, $args$ = _args; ", + "_coreloop: ", + "for (var _i = 0, _il = _items.length; _i < _il; _i++) { ", + "$item$ = _items[_i]; ", + "$filter$; ", + "} ", + "return _retval; " + //"}" + ].join(""); + tpl = tpl.replace(/\$filter\$/gi, filterBody); + tpl = tpl.replace(/\$item\$/gi, filterInfo.params[0]); + tpl = tpl.replace(/\$args\$/gi, filterInfo.params[1]); + + var fn = new Function("_items,_args", tpl); + fn.displayName = fn.name = "compiledFilter"; + return fn; + } + + function compileFilterWithCaching() { + var filterInfo = getFunctionInfo(filter); + + var filterBody = filterInfo.body + .replace(/return false\s*([;}]|$)/gi, "{ continue _coreloop; }$1") + .replace(/return true\s*([;}]|$)/gi, "{ _cache[_i] = true;_retval[_idx++] = $item$; continue _coreloop; }$1") + .replace(/return ([^;}]+?)\s*([;}]|$)/gi, + "{ if ((_cache[_i] = $1)) { _retval[_idx++] = $item$; }; continue _coreloop; }$2"); + + // This preserves the function template code after JS compression, + // so that replace() commands still work as expected. + var tpl = [ + //"function(_items, _args, _cache) { ", + "var _retval = [], _idx = 0; ", + "var $item$, $args$ = _args; ", + "_coreloop: ", + "for (var _i = 0, _il = _items.length; _i < _il; _i++) { ", + "$item$ = _items[_i]; ", + "if (_cache[_i]) { ", + "_retval[_idx++] = $item$; ", + "continue _coreloop; ", + "} ", + "$filter$; ", + "} ", + "return _retval; " + //"}" + ].join(""); + tpl = tpl.replace(/\$filter\$/gi, filterBody); + tpl = tpl.replace(/\$item\$/gi, filterInfo.params[0]); + tpl = tpl.replace(/\$args\$/gi, filterInfo.params[1]); + + var fn = new Function("_items,_args,_cache", tpl); + fn.displayName = fn.name = "compiledFilterWithCaching"; + return fn; + } + + function uncompiledFilter(items, args) { + var retval = [], idx = 0; + + for (var i = 0, ii = items.length; i < ii; i++) { + if (filter(items[i], args)) { + retval[idx++] = items[i]; + } + } + + return retval; + } + + function uncompiledFilterWithCaching(items, args, cache) { + var retval = [], idx = 0, item; + + for (var i = 0, ii = items.length; i < ii; i++) { + item = items[i]; + if (cache[i]) { + retval[idx++] = item; + } else if (filter(item, args)) { + retval[idx++] = item; + cache[i] = true; + } + } + + return retval; + } + + function getFilteredAndPagedItems(items) { + if (filter) { + var batchFilter = options.inlineFilters ? compiledFilter : uncompiledFilter; + var batchFilterWithCaching = options.inlineFilters ? compiledFilterWithCaching : uncompiledFilterWithCaching; + + if (refreshHints.isFilterNarrowing) { + filteredItems = batchFilter(filteredItems, filterArgs); + } else if (refreshHints.isFilterExpanding) { + filteredItems = batchFilterWithCaching(items, filterArgs, filterCache); + } else if (!refreshHints.isFilterUnchanged) { + filteredItems = batchFilter(items, filterArgs); + } + } else { + // special case: if not filtering and not paging, the resulting + // rows collection needs to be a copy so that changes due to sort + // can be caught + filteredItems = pagesize ? items : items.concat(); + } + + // get the current page + var paged; + if (pagesize) { + if (filteredItems.length < pagenum * pagesize) { + pagenum = Math.floor(filteredItems.length / pagesize); + } + paged = filteredItems.slice(pagesize * pagenum, pagesize * pagenum + pagesize); + } else { + paged = filteredItems; + } + + return {totalRows: filteredItems.length, rows: paged}; + } + + function getRowDiffs(rows, newRows) { + var item, r, eitherIsNonData, diff = []; + var from = 0, to = newRows.length; + + if (refreshHints && refreshHints.ignoreDiffsBefore) { + from = Math.max(0, + Math.min(newRows.length, refreshHints.ignoreDiffsBefore)); + } + + if (refreshHints && refreshHints.ignoreDiffsAfter) { + to = Math.min(newRows.length, + Math.max(0, refreshHints.ignoreDiffsAfter)); + } + + for (var i = from, rl = rows.length; i < to; i++) { + if (i >= rl) { + diff[diff.length] = i; + } else { + item = newRows[i]; + r = rows[i]; + + if ((groupingInfos.length && (eitherIsNonData = (item.__nonDataRow) || (r.__nonDataRow)) && + item.__group !== r.__group || + item.__group && !item.equals(r)) + || (eitherIsNonData && + // no good way to compare totals since they are arbitrary DTOs + // deep object comparison is pretty expensive + // always considering them 'dirty' seems easier for the time being + (item.__groupTotals || r.__groupTotals)) + || item[idProperty] != r[idProperty] + || (updated && updated[item[idProperty]]) + ) { + diff[diff.length] = i; + } + } + } + return diff; + } + + function recalc(_items) { + rowsById = null; + + if (refreshHints.isFilterNarrowing != prevRefreshHints.isFilterNarrowing || + refreshHints.isFilterExpanding != prevRefreshHints.isFilterExpanding) { + filterCache = []; + } + + var filteredItems = getFilteredAndPagedItems(_items); + totalRows = filteredItems.totalRows; + var newRows = filteredItems.rows; + + groups = []; + if (groupingInfos.length) { + groups = extractGroups(newRows); + if (groups.length) { + addTotals(groups); + newRows = flattenGroupedRows(groups); + } + } + + var diff = getRowDiffs(rows, newRows); + + rows = newRows; + + return diff; + } + + function refresh() { + if (suspend) { + return; + } + + var countBefore = rows.length; + var totalRowsBefore = totalRows; + + var diff = recalc(items, filter); // pass as direct refs to avoid closure perf hit + + // if the current page is no longer valid, go to last page and recalc + // we suffer a performance penalty here, but the main loop (recalc) remains highly optimized + if (pagesize && totalRows < pagenum * pagesize) { + pagenum = Math.max(0, Math.ceil(totalRows / pagesize) - 1); + diff = recalc(items, filter); + } + + updated = null; + prevRefreshHints = refreshHints; + refreshHints = {}; + + if (totalRowsBefore != totalRows) { + onPagingInfoChanged.notify(getPagingInfo(), null, self); + } + if (countBefore != rows.length) { + onRowCountChanged.notify({previous: countBefore, current: rows.length}, null, self); + } + if (diff.length > 0) { + onRowsChanged.notify({rows: diff}, null, self); + } + } + + /*** + * Wires the grid and the DataView together to keep row selection tied to item ids. + * This is useful since, without it, the grid only knows about rows, so if the items + * move around, the same rows stay selected instead of the selection moving along + * with the items. + * + * NOTE: This doesn't work with cell selection model. + * + * @param grid {Slick.Grid} The grid to sync selection with. + * @param preserveHidden {Boolean} Whether to keep selected items that go out of the + * view due to them getting filtered out. + * @param preserveHiddenOnSelectionChange {Boolean} Whether to keep selected items + * that are currently out of the view (see preserveHidden) as selected when selection + * changes. + * @return {Slick.Event} An event that notifies when an internal list of selected row ids + * changes. This is useful since, in combination with the above two options, it allows + * access to the full list selected row ids, and not just the ones visible to the grid. + * @method syncGridSelection + */ + function syncGridSelection(grid, preserveHidden, preserveHiddenOnSelectionChange) { + var self = this; + var inHandler; + var selectedRowIds = self.mapRowsToIds(grid.getSelectedRows()); + var onSelectedRowIdsChanged = new Slick.Event(); + + function setSelectedRowIds(rowIds) { + if (selectedRowIds.join(",") == rowIds.join(",")) { + return; + } + + selectedRowIds = rowIds; + + onSelectedRowIdsChanged.notify({ + "grid": grid, + "ids": selectedRowIds + }, new Slick.EventData(), self); + } + + function update() { + if (selectedRowIds.length > 0) { + inHandler = true; + var selectedRows = self.mapIdsToRows(selectedRowIds); + if (!preserveHidden) { + setSelectedRowIds(self.mapRowsToIds(selectedRows)); + } + grid.setSelectedRows(selectedRows); + inHandler = false; + } + } + + grid.onSelectedRowsChanged.subscribe(function(e, args) { + if (inHandler) { return; } + var newSelectedRowIds = self.mapRowsToIds(grid.getSelectedRows()); + if (!preserveHiddenOnSelectionChange || !grid.getOptions().multiSelect) { + setSelectedRowIds(newSelectedRowIds); + } else { + // keep the ones that are hidden + var existing = $.grep(selectedRowIds, function(id) { return self.getRowById(id) === undefined; }); + // add the newly selected ones + setSelectedRowIds(existing.concat(newSelectedRowIds)); + } + }); + + this.onRowsChanged.subscribe(update); + + this.onRowCountChanged.subscribe(update); + + return onSelectedRowIdsChanged; + } + + function syncGridCellCssStyles(grid, key) { + var hashById; + var inHandler; + + // since this method can be called after the cell styles have been set, + // get the existing ones right away + storeCellCssStyles(grid.getCellCssStyles(key)); + + function storeCellCssStyles(hash) { + hashById = {}; + for (var row in hash) { + var id = rows[row][idProperty]; + hashById[id] = hash[row]; + } + } + + function update() { + if (hashById) { + inHandler = true; + ensureRowsByIdCache(); + var newHash = {}; + for (var id in hashById) { + var row = rowsById[id]; + if (row != undefined) { + newHash[row] = hashById[id]; + } + } + grid.setCellCssStyles(key, newHash); + inHandler = false; + } + } + + grid.onCellCssStylesChanged.subscribe(function(e, args) { + if (inHandler) { return; } + if (key != args.key) { return; } + if (args.hash) { + storeCellCssStyles(args.hash); + } + }); + + this.onRowsChanged.subscribe(update); + + this.onRowCountChanged.subscribe(update); + } + + $.extend(this, { + // methods + "beginUpdate": beginUpdate, + "endUpdate": endUpdate, + "setPagingOptions": setPagingOptions, + "getPagingInfo": getPagingInfo, + "getItems": getItems, + "setItems": setItems, + "setFilter": setFilter, + "sort": sort, + "fastSort": fastSort, + "reSort": reSort, + "setGrouping": setGrouping, + "getGrouping": getGrouping, + "groupBy": groupBy, + "setAggregators": setAggregators, + "collapseAllGroups": collapseAllGroups, + "expandAllGroups": expandAllGroups, + "collapseGroup": collapseGroup, + "expandGroup": expandGroup, + "getGroups": getGroups, + "getIdxById": getIdxById, + "getRowById": getRowById, + "getItemById": getItemById, + "getItemByIdx": getItemByIdx, + "mapRowsToIds": mapRowsToIds, + "mapIdsToRows": mapIdsToRows, + "setRefreshHints": setRefreshHints, + "setFilterArgs": setFilterArgs, + "refresh": refresh, + "updateItem": updateItem, + "insertItem": insertItem, + "addItem": addItem, + "deleteItem": deleteItem, + "syncGridSelection": syncGridSelection, + "syncGridCellCssStyles": syncGridCellCssStyles, + + // data provider methods + "getLength": getLength, + "getItem": getItem, + "getItemMetadata": getItemMetadata, + + // events + "onRowCountChanged": onRowCountChanged, + "onRowsChanged": onRowsChanged, + "onPagingInfoChanged": onPagingInfoChanged + }); + } + + function AvgAggregator(field) { + this.field_ = field; + + this.init = function () { + this.count_ = 0; + this.nonNullCount_ = 0; + this.sum_ = 0; + }; + + this.accumulate = function (item) { + var val = item[this.field_]; + this.count_++; + if (val != null && val !== "" && val !== NaN) { + this.nonNullCount_++; + this.sum_ += parseFloat(val); + } + }; + + this.storeResult = function (groupTotals) { + if (!groupTotals.avg) { + groupTotals.avg = {}; + } + if (this.nonNullCount_ != 0) { + groupTotals.avg[this.field_] = this.sum_ / this.nonNullCount_; + } + }; + } + + function MinAggregator(field) { + this.field_ = field; + + this.init = function () { + this.min_ = null; + }; + + this.accumulate = function (item) { + var val = item[this.field_]; + if (val != null && val !== "" && val !== NaN) { + if (this.min_ == null || val < this.min_) { + this.min_ = val; + } + } + }; + + this.storeResult = function (groupTotals) { + if (!groupTotals.min) { + groupTotals.min = {}; + } + groupTotals.min[this.field_] = this.min_; + } + } + + function MaxAggregator(field) { + this.field_ = field; + + this.init = function () { + this.max_ = null; + }; + + this.accumulate = function (item) { + var val = item[this.field_]; + if (val != null && val !== "" && val !== NaN) { + if (this.max_ == null || val > this.max_) { + this.max_ = val; + } + } + }; + + this.storeResult = function (groupTotals) { + if (!groupTotals.max) { + groupTotals.max = {}; + } + groupTotals.max[this.field_] = this.max_; + } + } + + function SumAggregator(field) { + this.field_ = field; + + this.init = function () { + this.sum_ = null; + }; + + this.accumulate = function (item) { + var val = item[this.field_]; + if (val != null && val !== "" && val !== NaN) { + this.sum_ += parseFloat(val); + } + }; + + this.storeResult = function (groupTotals) { + if (!groupTotals.sum) { + groupTotals.sum = {}; + } + groupTotals.sum[this.field_] = this.sum_; + } + } + + // TODO: add more built-in aggregators + // TODO: merge common aggregators in one to prevent needles iterating + +})(jQuery); diff --git a/src/ckanext-d4science_theme/ckanext/reclineview/theme/public/vendor/slickgrid/2.2/slick.editors.js b/src/ckanext-d4science_theme/ckanext/reclineview/theme/public/vendor/slickgrid/2.2/slick.editors.js new file mode 100644 index 0000000..04b20d2 --- /dev/null +++ b/src/ckanext-d4science_theme/ckanext/reclineview/theme/public/vendor/slickgrid/2.2/slick.editors.js @@ -0,0 +1,512 @@ +/*** + * Contains basic SlickGrid editors. + * @module Editors + * @namespace Slick + */ + +(function ($) { + // register namespace + $.extend(true, window, { + "Slick": { + "Editors": { + "Text": TextEditor, + "Integer": IntegerEditor, + "Date": DateEditor, + "YesNoSelect": YesNoSelectEditor, + "Checkbox": CheckboxEditor, + "PercentComplete": PercentCompleteEditor, + "LongText": LongTextEditor + } + } + }); + + function TextEditor(args) { + var $input; + var defaultValue; + var scope = this; + + this.init = function () { + $input = $("") + .appendTo(args.container) + .bind("keydown.nav", function (e) { + if (e.keyCode === $.ui.keyCode.LEFT || e.keyCode === $.ui.keyCode.RIGHT) { + e.stopImmediatePropagation(); + } + }) + .focus() + .select(); + }; + + this.destroy = function () { + $input.remove(); + }; + + this.focus = function () { + $input.focus(); + }; + + this.getValue = function () { + return $input.val(); + }; + + this.setValue = function (val) { + $input.val(val); + }; + + this.loadValue = function (item) { + defaultValue = item[args.column.field] || ""; + $input.val(defaultValue); + $input[0].defaultValue = defaultValue; + $input.select(); + }; + + this.serializeValue = function () { + return $input.val(); + }; + + this.applyValue = function (item, state) { + item[args.column.field] = state; + }; + + this.isValueChanged = function () { + return (!($input.val() == "" && defaultValue == null)) && ($input.val() != defaultValue); + }; + + this.validate = function () { + if (args.column.validator) { + var validationResults = args.column.validator($input.val()); + if (!validationResults.valid) { + return validationResults; + } + } + + return { + valid: true, + msg: null + }; + }; + + this.init(); + } + + function IntegerEditor(args) { + var $input; + var defaultValue; + var scope = this; + + this.init = function () { + $input = $(""); + + $input.bind("keydown.nav", function (e) { + if (e.keyCode === $.ui.keyCode.LEFT || e.keyCode === $.ui.keyCode.RIGHT) { + e.stopImmediatePropagation(); + } + }); + + $input.appendTo(args.container); + $input.focus().select(); + }; + + this.destroy = function () { + $input.remove(); + }; + + this.focus = function () { + $input.focus(); + }; + + this.loadValue = function (item) { + defaultValue = item[args.column.field]; + $input.val(defaultValue); + $input[0].defaultValue = defaultValue; + $input.select(); + }; + + this.serializeValue = function () { + return parseInt($input.val(), 10) || 0; + }; + + this.applyValue = function (item, state) { + item[args.column.field] = state; + }; + + this.isValueChanged = function () { + return (!($input.val() == "" && defaultValue == null)) && ($input.val() != defaultValue); + }; + + this.validate = function () { + if (isNaN($input.val())) { + return { + valid: false, + msg: "Please enter a valid integer" + }; + } + + return { + valid: true, + msg: null + }; + }; + + this.init(); + } + + function DateEditor(args) { + var $input; + var defaultValue; + var scope = this; + var calendarOpen = false; + + this.init = function () { + $input = $(""); + $input.appendTo(args.container); + $input.focus().select(); + $input.datepicker({ + showOn: "button", + buttonImageOnly: true, + buttonImage: "../images/calendar.gif", + beforeShow: function () { + calendarOpen = true + }, + onClose: function () { + calendarOpen = false + } + }); + $input.width($input.width() - 18); + }; + + this.destroy = function () { + $.datepicker.dpDiv.stop(true, true); + $input.datepicker("hide"); + $input.datepicker("destroy"); + $input.remove(); + }; + + this.show = function () { + if (calendarOpen) { + $.datepicker.dpDiv.stop(true, true).show(); + } + }; + + this.hide = function () { + if (calendarOpen) { + $.datepicker.dpDiv.stop(true, true).hide(); + } + }; + + this.position = function (position) { + if (!calendarOpen) { + return; + } + $.datepicker.dpDiv + .css("top", position.top + 30) + .css("left", position.left); + }; + + this.focus = function () { + $input.focus(); + }; + + this.loadValue = function (item) { + defaultValue = item[args.column.field]; + $input.val(defaultValue); + $input[0].defaultValue = defaultValue; + $input.select(); + }; + + this.serializeValue = function () { + return $input.val(); + }; + + this.applyValue = function (item, state) { + item[args.column.field] = state; + }; + + this.isValueChanged = function () { + return (!($input.val() == "" && defaultValue == null)) && ($input.val() != defaultValue); + }; + + this.validate = function () { + return { + valid: true, + msg: null + }; + }; + + this.init(); + } + + function YesNoSelectEditor(args) { + var $select; + var defaultValue; + var scope = this; + + this.init = function () { + $select = $(""); + $select.appendTo(args.container); + $select.focus(); + }; + + this.destroy = function () { + $select.remove(); + }; + + this.focus = function () { + $select.focus(); + }; + + this.loadValue = function (item) { + $select.val((defaultValue = item[args.column.field]) ? "yes" : "no"); + $select.select(); + }; + + this.serializeValue = function () { + return ($select.val() == "yes"); + }; + + this.applyValue = function (item, state) { + item[args.column.field] = state; + }; + + this.isValueChanged = function () { + return ($select.val() != defaultValue); + }; + + this.validate = function () { + return { + valid: true, + msg: null + }; + }; + + this.init(); + } + + function CheckboxEditor(args) { + var $select; + var defaultValue; + var scope = this; + + this.init = function () { + $select = $(""); + $select.appendTo(args.container); + $select.focus(); + }; + + this.destroy = function () { + $select.remove(); + }; + + this.focus = function () { + $select.focus(); + }; + + this.loadValue = function (item) { + defaultValue = !!item[args.column.field]; + if (defaultValue) { + $select.prop('checked', true); + } else { + $select.prop('checked', false); + } + }; + + this.serializeValue = function () { + return $select.prop('checked'); + }; + + this.applyValue = function (item, state) { + item[args.column.field] = state; + }; + + this.isValueChanged = function () { + return (this.serializeValue() !== defaultValue); + }; + + this.validate = function () { + return { + valid: true, + msg: null + }; + }; + + this.init(); + } + + function PercentCompleteEditor(args) { + var $input, $picker; + var defaultValue; + var scope = this; + + this.init = function () { + $input = $(""); + $input.width($(args.container).innerWidth() - 25); + $input.appendTo(args.container); + + $picker = $("

    ").appendTo(args.container); + $picker.append("
    "); + + $picker.find(".editor-percentcomplete-buttons").append("

    "); + + $input.focus().select(); + + $picker.find(".editor-percentcomplete-slider").slider({ + orientation: "vertical", + range: "min", + value: defaultValue, + slide: function (event, ui) { + $input.val(ui.value) + } + }); + + $picker.find(".editor-percentcomplete-buttons button").bind("click", function (e) { + $input.val($(this).attr("val")); + $picker.find(".editor-percentcomplete-slider").slider("value", $(this).attr("val")); + }) + }; + + this.destroy = function () { + $input.remove(); + $picker.remove(); + }; + + this.focus = function () { + $input.focus(); + }; + + this.loadValue = function (item) { + $input.val(defaultValue = item[args.column.field]); + $input.select(); + }; + + this.serializeValue = function () { + return parseInt($input.val(), 10) || 0; + }; + + this.applyValue = function (item, state) { + item[args.column.field] = state; + }; + + this.isValueChanged = function () { + return (!($input.val() == "" && defaultValue == null)) && ((parseInt($input.val(), 10) || 0) != defaultValue); + }; + + this.validate = function () { + if (isNaN(parseInt($input.val(), 10))) { + return { + valid: false, + msg: "Please enter a valid positive number" + }; + } + + return { + valid: true, + msg: null + }; + }; + + this.init(); + } + + /* + * An example of a "detached" editor. + * The UI is added onto document BODY and .position(), .show() and .hide() are implemented. + * KeyDown events are also handled to provide handling for Tab, Shift-Tab, Esc and Ctrl-Enter. + */ + function LongTextEditor(args) { + var $input, $wrapper; + var defaultValue; + var scope = this; + + this.init = function () { + var $container = $("body"); + + $wrapper = $("
    ") + .appendTo($container); + + $input = $("