iqvoc 4.2.0 → 4.3.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +15 -0
- data/Gemfile +15 -12
- data/Gemfile.lock +124 -90
- data/README.md +17 -1
- data/app/assets/images/iqvoc_logo.svg +41 -0
- data/app/assets/javascripts/framework.js +1 -1
- data/app/assets/javascripts/iqvoc/concept_mapper.js +62 -0
- data/app/assets/javascripts/iqvoc/concept_mapping_manager.js +137 -0
- data/app/assets/javascripts/iqvoc/entityselect.js.erb +51 -19
- data/app/assets/javascripts/iqvoc/federated_concept_mapper.js +75 -0
- data/app/assets/javascripts/iqvoc/iqvoc.js +16 -4
- data/app/assets/javascripts/iqvoc/label_resolver.js +23 -0
- data/app/assets/javascripts/iqvoc/manifest.js +4 -0
- data/app/assets/javascripts/iqvoc/onebox.js.erb +4 -4
- data/app/assets/javascripts/iqvoc/quicksearch.js +1 -1
- data/app/assets/stylesheets/framework.css.scss +2 -1
- data/app/assets/stylesheets/iqvoc/_settings.css.scss +12 -0
- data/app/assets/stylesheets/iqvoc/components/_components.css.scss +237 -0
- data/app/assets/stylesheets/iqvoc/{entity_select.css.scss → components/_entity_select.css.scss} +0 -2
- data/app/assets/stylesheets/iqvoc/{forms.css.scss → components/_forms.css.scss} +0 -0
- data/app/assets/stylesheets/iqvoc/{note.css.scss → components/_notes.css.scss} +2 -10
- data/app/assets/stylesheets/iqvoc/{panel.css.scss → components/_panels.css.scss} +3 -5
- data/app/assets/stylesheets/iqvoc/{sidebar.css.scss → components/_sidebars.css.scss} +0 -0
- data/app/assets/stylesheets/iqvoc/{visualization.css.scss → components/_visualization.css.scss} +0 -0
- data/app/assets/stylesheets/iqvoc/{bootstrap_extensions.css.scss → hacks/_bootstrap_extensions.css.scss} +0 -0
- data/app/assets/stylesheets/iqvoc/{hacks.css.scss → hacks/_hacks.css.scss} +0 -0
- data/app/assets/stylesheets/iqvoc/{jquery-ui_extensions.css.scss → hacks/_jquery-ui_extensions.css.scss} +0 -0
- data/app/assets/stylesheets/iqvoc/manifest.css.scss +10 -10
- data/app/controllers/concepts/alphabetical_controller.rb +23 -9
- data/app/controllers/concepts/scheme_controller.rb +19 -0
- data/app/controllers/concepts_controller.rb +13 -1
- data/app/controllers/concerns/dataset_initialization.rb +18 -0
- data/app/controllers/dashboard_controller.rb +17 -0
- data/app/controllers/hierarchy_controller.rb +65 -6
- data/app/controllers/{import_controller.rb → imports_controller.rb} +14 -11
- data/app/controllers/rdf_controller.rb +6 -0
- data/app/controllers/remote_labels_controller.rb +31 -0
- data/app/controllers/search_results_controller.rb +95 -14
- data/app/helpers/application_helper.rb +6 -4
- data/app/helpers/dashboard_helper.rb +3 -3
- data/app/helpers/link_helper.rb +14 -0
- data/app/helpers/search_results_helper.rb +5 -1
- data/app/jobs/import_job.rb +19 -0
- data/app/models/concept/base.rb +2 -2
- data/app/models/dataset/adaptors/iqvoc/alphabetical_search_adaptor.rb +38 -0
- data/app/models/dataset/adaptors/iqvoc/http_adaptor.rb +39 -0
- data/app/models/dataset/adaptors/iqvoc/label_adaptor.rb +15 -0
- data/app/models/dataset/adaptors/iqvoc/search_adaptor.rb +60 -0
- data/app/models/dataset/iqvoc_dataset.rb +39 -0
- data/app/models/import.rb +16 -0
- data/app/models/labeling/skos/base.rb +2 -1
- data/app/models/note/base.rb +2 -2
- data/app/models/note/skos/change_note.rb +3 -2
- data/app/presenters/alphabetical_search_result.rb +33 -0
- data/app/presenters/alphabetical_search_result_remote.rb +37 -0
- data/app/presenters/search_result.rb +33 -0
- data/app/presenters/search_result_collection.rb +8 -0
- data/app/presenters/search_result_remote.rb +52 -0
- data/app/views/collections/_sidebar.html.erb +3 -3
- data/app/views/concepts/_form.html.erb +1 -1
- data/app/views/concepts/alphabetical/_search_result.html.erb +16 -0
- data/app/views/concepts/alphabetical/_search_result_remote.html.erb +14 -0
- data/app/views/concepts/alphabetical/index.html.erb +5 -3
- data/app/views/concepts/expired/index.html.erb +5 -2
- data/app/views/concepts/scheme/_sidebar.html.erb +3 -3
- data/app/views/concepts/sidebars/_singular.html.erb +4 -4
- data/app/views/dashboard/_sidebar.html.erb +7 -4
- data/app/views/dashboard/index.html.erb +1 -1
- data/app/views/dashboard/reset.html.erb +6 -0
- data/app/views/imports/index.html.erb +44 -0
- data/app/views/imports/show.html.erb +7 -0
- data/app/views/layouts/application.html.erb +7 -1
- data/app/views/partials/labeling/skos/_search_result.html.erb +12 -16
- data/app/views/partials/match/_edit_base.html.erb +1 -1
- data/app/views/partials/note/_search_result.html.erb +8 -5
- data/app/views/partials/note/skos/change_note/_search_result.html.erb +7 -10
- data/app/views/partials/note/skos/definition/_search_result.html.erb +5 -5
- data/app/views/rdf/dataset.iqrdf +9 -0
- data/app/views/search_results/_search_result_remote.html.erb +13 -0
- data/app/views/search_results/_sidebar.html.erb +3 -3
- data/app/views/search_results/index.html.erb +4 -4
- data/app/views/search_results/index.iqrdf +7 -7
- data/app/views/search_results/sections/_datasets.html.erb +17 -0
- data/app/views/search_results/sections/_klass.html.erb +2 -2
- data/app/views/search_results/sections/_languages.html.erb +3 -3
- data/app/views/users/_sidebar.html.erb +1 -1
- data/config/application.rb +0 -2
- data/config/engine.rb +5 -0
- data/config/environments/heroku.rb +62 -16
- data/config/initializers/apipie.rb +17 -0
- data/config/initializers/secret_token.rb +25 -9
- data/config/locales/activerecord.de.yml +5 -0
- data/config/locales/activerecord.en.yml +5 -0
- data/config/locales/de.yml +14 -0
- data/config/locales/en.yml +14 -0
- data/config/locales/pt.yml +1 -0
- data/config/routes.rb +7 -4
- data/db/migrate/20131220134356_create_delayed_jobs.rb +22 -0
- data/db/migrate/20131220144601_create_imports.rb +11 -0
- data/db/schema.rb +26 -1
- data/iqvoc.gemspec +12 -3
- data/lib/iqvoc/ability.rb +2 -0
- data/lib/iqvoc/configuration/core.rb +36 -9
- data/lib/iqvoc/controller_extensions.rb +4 -0
- data/lib/iqvoc/environments/production.rb +1 -1
- data/lib/iqvoc/skos_importer.rb +31 -19
- data/lib/iqvoc/version.rb +1 -1
- data/lib/tasks/importer.rake +10 -2
- data/lib/tasks/release.rake +1 -1
- data/public/404.html +43 -11
- data/public/422.html +43 -11
- data/public/500.html +43 -12
- data/test/functional/{rdf_test.rb → rdf_rendering_test.rb} +1 -1
- data/test/integration/alphabetical_test.rb +5 -5
- data/test/integration/search_test.rb +16 -16
- data/test/unit/skos_import_test.rb +9 -9
- data/vendor/assets/images/animated-overlay.gif +0 -0
- data/vendor/assets/images/ui-bg_flat_0_aaaaaa_40x100.png +0 -0
- data/vendor/assets/images/ui-bg_flat_55_fbec88_40x100.png +0 -0
- data/vendor/assets/images/ui-bg_glass_75_d0e5f5_1x400.png +0 -0
- data/vendor/assets/images/ui-bg_glass_85_dfeffc_1x400.png +0 -0
- data/vendor/assets/images/ui-bg_glass_95_fef1ec_1x400.png +0 -0
- data/vendor/assets/images/ui-bg_gloss-wave_55_5c9ccc_500x100.png +0 -0
- data/vendor/assets/images/ui-bg_inset-hard_100_f5f8f9_1x100.png +0 -0
- data/vendor/assets/images/ui-bg_inset-hard_100_fcfdfd_1x100.png +0 -0
- data/vendor/assets/images/ui-icons_217bc0_256x240.png +0 -0
- data/vendor/assets/images/ui-icons_2e83ff_256x240.png +0 -0
- data/vendor/assets/images/ui-icons_469bdd_256x240.png +0 -0
- data/vendor/assets/images/ui-icons_6da8d5_256x240.png +0 -0
- data/vendor/assets/images/ui-icons_cd0a0a_256x240.png +0 -0
- data/vendor/assets/images/ui-icons_d8e7f3_256x240.png +0 -0
- data/vendor/assets/images/ui-icons_f9bd01_256x240.png +0 -0
- data/vendor/assets/javascripts/jquery-ui.custom.js +4538 -0
- data/vendor/assets/stylesheets/jquery-ui-1.10.3.custom.css.scss +724 -0
- metadata +214 -64
- data/app/assets/images/arrow_down.gif +0 -0
- data/app/assets/images/arrow_up.gif +0 -0
- data/app/assets/images/iqvoc_logo.png +0 -0
- data/app/assets/images/spinner_16x16.gif +0 -0
- data/app/assets/images/spinner_24x24.gif +0 -0
- data/app/assets/stylesheets/iqvoc/components.css.scss +0 -155
- data/app/assets/stylesheets/iqvoc/layout.css.scss +0 -0
- data/app/views/concepts/alphabetical/_pref_labeling.html.erb +0 -19
- data/app/views/import/import.html.erb +0 -7
- data/app/views/import/index.html.erb +0 -17
- data/vendor/assets/javascripts/jquery-ui-1.8.23.custom.js +0 -3399
- data/vendor/assets/stylesheets/jquery-ui-1.8.23.custom.css.scss +0 -405
data/README.md
CHANGED
@@ -58,9 +58,25 @@ Running the cloned source code is possible but any modifications would require a
|
|
58
58
|
8. Log in with "admin@iqvoc" / "admin" or "demo@iqvoc" / "cooluri" (cf. step #5)
|
59
59
|
9. Visit the Users section and change the default passwords
|
60
60
|
|
61
|
+
## Background Jobs
|
62
|
+
|
63
|
+
Note that some features like "Import" exposed in the Web UI store their workload
|
64
|
+
as jobs. You can either issue a job worker that runs continuously and watches
|
65
|
+
for new jobs via
|
66
|
+
|
67
|
+
```
|
68
|
+
$ rake jobs:work
|
69
|
+
```
|
70
|
+
|
71
|
+
or process jobs in a one-off way (in development or via cron):
|
72
|
+
|
73
|
+
```
|
74
|
+
$ rake jobs:workoff
|
75
|
+
```
|
76
|
+
|
61
77
|
## Compatibility
|
62
78
|
|
63
|
-
iQvoc is fully compatible with Ruby 1.9, 2.0 and JRuby 1.7.
|
79
|
+
iQvoc is fully compatible with Ruby 1.9, 2.0, 2.1 and JRuby 1.7.
|
64
80
|
|
65
81
|
## Customization
|
66
82
|
|
@@ -0,0 +1,41 @@
|
|
1
|
+
<?xml version="1.0" encoding="utf-8"?>
|
2
|
+
<!-- Generator: Adobe Illustrator 17.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
3
|
+
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
4
|
+
<svg version="1.1" id="Ebene_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" width="132.8px" height="51.1px" viewBox="0 0 132.8 51.1" enable-background="new 0 0 132.8 51.1" xml:space="preserve">
|
5
|
+
<g>
|
6
|
+
<g>
|
7
|
+
<path fill="#063D63" d="M63.4,50.5c0,0-1,0.6-3,0.6c-2.6,0-5-1.6-6.2-2.4c-1.6-1.1-4.2-2.6-6.9-3.4c-5.1,0-8.4-1.4-11-4.8
|
8
|
+
c-2.6-3.3-3.9-7.6-3.9-12.6c0-7.3,2.8-13.2,7.6-15.8c2-1.1,4.5-1.7,7-1.7c9,0,14.6,6.6,14.6,17.2c0,7.4-2.6,12.9-6.7,15.5
|
9
|
+
c0.9,0.3,2.2,1.2,3,1.7c1.2,0.7,2.7,1.4,4.2,1.4c1.5,0,3-0.2,3-0.2L63.4,50.5z M52.9,20c-0.5-1.7-2.3-4.3-5.9-4.3
|
10
|
+
c-2.3,0-4.3,1.2-5.3,3C40.6,20.9,40,23.9,40,28c0,5.8,1,9.5,3,11c1.1,0.8,2.4,1.2,4,1.2c4.7,0,6.7-3.6,6.7-12.2
|
11
|
+
C53.8,24.4,53.5,21.9,52.9,20z"/>
|
12
|
+
<path fill="#063D63" d="M77.6,44.8h-5.4l-9.1-24.5l6.7-0.8l3.6,11.2c0.5,1.7,1.3,4.4,1.6,6c0.2-1.5,0.7-3.8,1.5-6.3l3.4-10.5h6.8
|
13
|
+
L77.6,44.8z"/>
|
14
|
+
<path fill="#063D63" d="M106.8,41.8c-1.9,2.1-4.4,3.5-8.2,3.5c-6.8,0-11.2-5.1-11.2-12.9c0-7.8,4.5-13,11.2-13
|
15
|
+
c3.6,0,6.3,1.2,8.5,3.7c2,2.3,2.9,5.1,2.9,9.1C109.9,36.5,109,39.4,106.8,41.8z M101.5,25.5c-0.6-0.8-1.7-1.3-2.8-1.3
|
16
|
+
c-1.6,0-2.8,1-3.4,2.5c-0.4,1.3-0.7,3.1-0.7,5.6c0,2.9,0.3,5,0.8,6.3c0.6,1.4,2,2,3.3,2c2.8,0,4-2.5,4-8.4
|
17
|
+
C102.8,28.8,102.4,26.6,101.5,25.5z"/>
|
18
|
+
<path fill="#063D63" d="M130.5,43.8c-1.8,1-3.7,1.6-6.1,1.6c-6.9,0-10.9-4.8-10.9-12.9c0-5.7,2.2-9.5,5.1-11.5
|
19
|
+
c1.7-1.1,4.1-1.9,6.2-1.9c1.7,0,3.5,0.4,4.8,1.1c0.9,0.5,1.4,0.8,2.4,1.8l-3.2,4.3c-1.3-1.2-2.7-1.9-3.9-1.9
|
20
|
+
c-2.9,0-4.2,2.5-4.2,8.4c0,3.3,0.4,5.2,1.3,6.3c0.7,0.9,1.8,1.4,2.9,1.4c1.5,0,2.9-0.6,4.5-2l0.4-0.3l3,3.9
|
21
|
+
C131.9,42.9,131.4,43.2,130.5,43.8z"/>
|
22
|
+
</g>
|
23
|
+
<path fill="#5DAB31" d="M1.7,8.5l16.6-8.3c0,0,11.3,8.3,19.1-0.2c0,0-9.1,15.1-17.9,17L1.7,8.5z"/>
|
24
|
+
<g>
|
25
|
+
<path fill="#063D63" d="M21.6,45.7V21.2l6.5-3.7V43L21.6,45.7z"/>
|
26
|
+
</g>
|
27
|
+
<polygon fill="#EC6408" points="0,12.4 17.6,21.4 17.6,45.5 0,35.4"/>
|
28
|
+
</g>
|
29
|
+
<g>
|
30
|
+
</g>
|
31
|
+
<g>
|
32
|
+
</g>
|
33
|
+
<g>
|
34
|
+
</g>
|
35
|
+
<g>
|
36
|
+
</g>
|
37
|
+
<g>
|
38
|
+
</g>
|
39
|
+
<g>
|
40
|
+
</g>
|
41
|
+
</svg>
|
@@ -0,0 +1,62 @@
|
|
1
|
+
/*jslint vars: true, white: true */
|
2
|
+
/*global jQuery, IQVOC */
|
3
|
+
|
4
|
+
IQVOC.ConceptMapper = (function($) {
|
5
|
+
|
6
|
+
"use strict";
|
7
|
+
|
8
|
+
// `selector` is either a jQuery object, a DOM node or a string
|
9
|
+
function ConceptMapper(selector) {
|
10
|
+
if(arguments.length === 0) { // subclassing
|
11
|
+
return;
|
12
|
+
}
|
13
|
+
|
14
|
+
this.root = selector.jquery ? selector : $(selector);
|
15
|
+
this.matchTypes = this.determineMatchTypes();
|
16
|
+
|
17
|
+
var matchOptions = $.map(this.matchTypes, function(desc, id) {
|
18
|
+
return $("<option />").val(id).text(desc)[0];
|
19
|
+
});
|
20
|
+
|
21
|
+
// spawn UI elements
|
22
|
+
|
23
|
+
this.container = $("<div />").addClass("concept-mapper control-group");
|
24
|
+
|
25
|
+
this.input = $("<input />").attr("type", "text").prependTo(this.container);
|
26
|
+
$("<button />").addClass("btn fa fa-plus").insertAfter(this.input).
|
27
|
+
click($.proxy(this, "onConfirm"));
|
28
|
+
this.matchType = $("<select />").append(matchOptions).insertAfter(this.input);
|
29
|
+
|
30
|
+
this.container.prependTo(this.root);
|
31
|
+
}
|
32
|
+
ConceptMapper.prototype.delimiter = ", ";
|
33
|
+
ConceptMapper.prototype.onConfirm = function(ev) {
|
34
|
+
ev.preventDefault();
|
35
|
+
|
36
|
+
var textAreaName = this.matchType.val();
|
37
|
+
var textArea = document.getElementsByName(textAreaName)[0];
|
38
|
+
textArea = $(textArea);
|
39
|
+
|
40
|
+
var newURI = this.input.val();
|
41
|
+
var newValue = $.trim(textArea.val() + this.delimiter + newURI);
|
42
|
+
|
43
|
+
textArea.val(newValue);
|
44
|
+
this.input.val("");
|
45
|
+
this.root.trigger("concept-mapped", {
|
46
|
+
uri: newURI,
|
47
|
+
matchType: this.matchTypes[textAreaName]
|
48
|
+
});
|
49
|
+
};
|
50
|
+
ConceptMapper.prototype.determineMatchTypes = function() {
|
51
|
+
var data = {};
|
52
|
+
$("label", this.root).each(function(i, node) {
|
53
|
+
var el = $(node);
|
54
|
+
var fieldName = el.attr("for");
|
55
|
+
data[fieldName] = el.text();
|
56
|
+
});
|
57
|
+
return data;
|
58
|
+
};
|
59
|
+
|
60
|
+
return ConceptMapper;
|
61
|
+
|
62
|
+
}(jQuery));
|
@@ -0,0 +1,137 @@
|
|
1
|
+
/*jslint vars: true, white: true */
|
2
|
+
/*global jQuery, IQVOC */
|
3
|
+
|
4
|
+
IQVOC.ConceptMappingManager = (function($) {
|
5
|
+
|
6
|
+
"use strict";
|
7
|
+
|
8
|
+
function ConceptMappingManager(selector, editable) {
|
9
|
+
this.root = selector.jquery ? selector : $(selector);
|
10
|
+
this.editable = editable === true;
|
11
|
+
this.conceptMappings = this.determineConceptMappings();
|
12
|
+
this.datasets = $(document.body).data("datasets");
|
13
|
+
|
14
|
+
this.list = $('<ul class="concept-mappings" />').appendTo(this.root);
|
15
|
+
this.render();
|
16
|
+
|
17
|
+
this.root.on("concept-mapped", $.proxy(this, "onUpdate"));
|
18
|
+
}
|
19
|
+
ConceptMappingManager.prototype.onDelete = function(ev, instance) {
|
20
|
+
var btn = $(this);
|
21
|
+
var item = btn.closest("li");
|
22
|
+
|
23
|
+
var matchType = $(".concept-mapping-match-type", item).text();
|
24
|
+
var uri = $(".concept-mapping-link", item).attr("href");
|
25
|
+
|
26
|
+
var data = instance.conceptMappings[matchType];
|
27
|
+
var values = data.values;
|
28
|
+
// XXX: hacky
|
29
|
+
var hits = $.map(values, function(value, i) {
|
30
|
+
if(value.uri === uri) {
|
31
|
+
item.fadeOut(function() {
|
32
|
+
item.remove();
|
33
|
+
});
|
34
|
+
return i;
|
35
|
+
}
|
36
|
+
return null;
|
37
|
+
});
|
38
|
+
values.splice(hits[0], 1);
|
39
|
+
|
40
|
+
var uris = $.map(data.values, function(value) { return value.uri; });
|
41
|
+
data.el.val(uris.join(", "));
|
42
|
+
};
|
43
|
+
ConceptMappingManager.prototype.render = function() {
|
44
|
+
var self = this;
|
45
|
+
|
46
|
+
var items = [];
|
47
|
+
$.each(this.conceptMappings, function(label, category) {
|
48
|
+
$.each(category.values, function(i, item) {
|
49
|
+
item = self.renderBubble(item, label);
|
50
|
+
items.push(item[0]);
|
51
|
+
});
|
52
|
+
});
|
53
|
+
|
54
|
+
this.list.empty().append(items);
|
55
|
+
};
|
56
|
+
ConceptMappingManager.prototype.renderBubble = function(item, categoryLabel) {
|
57
|
+
var category = $('<span class="concept-mapping-match-type">').
|
58
|
+
text(categoryLabel);
|
59
|
+
var dataset = this.determineDataset(item.uri) || "";
|
60
|
+
dataset = $('<span class="concept-mapping-dataset" />').text(dataset.name);
|
61
|
+
if(this.editable) {
|
62
|
+
var self = this;
|
63
|
+
var btn = $("<i class='fa fa-times concept-mapping-remove' />").click(function(ev) {
|
64
|
+
// inject instance
|
65
|
+
var args = Array.prototype.slice.apply(arguments);
|
66
|
+
args.push(self);
|
67
|
+
// ev.preventDefault();
|
68
|
+
return self.onDelete.apply(this, args);
|
69
|
+
});
|
70
|
+
}
|
71
|
+
var link = $('<a class="concept-mapping-link unlabeled" />').attr("href", item.uri).
|
72
|
+
text(item.uri);
|
73
|
+
return $('<li class="concept-mapping" />').append(link).append(dataset).
|
74
|
+
append(category).append(btn);
|
75
|
+
};
|
76
|
+
|
77
|
+
// [{ el: jQuery Element, values: ["http://uri.de"], label: "Foo" }]
|
78
|
+
ConceptMappingManager.prototype.determineConceptMappings = function() {
|
79
|
+
return this.editable ? this.readFromTextArea() : this.readFromLinks();
|
80
|
+
};
|
81
|
+
ConceptMappingManager.prototype.readFromLinks = function() { // TODO: rename
|
82
|
+
var urisByMatchType = {};
|
83
|
+
$(".relation.panel", this.root).each(function(i, node) { // match-type panels
|
84
|
+
var container = $(node);
|
85
|
+
var matchType = container.find("h2").text();
|
86
|
+
var mappings = container.find(".entity_list a");
|
87
|
+
urisByMatchType[matchType] = {
|
88
|
+
values: $.map(mappings, function(node) {
|
89
|
+
return { uri: $(node).attr("href") };
|
90
|
+
})
|
91
|
+
};
|
92
|
+
}).remove(); // XXX: does not belong here
|
93
|
+
return urisByMatchType;
|
94
|
+
};
|
95
|
+
ConceptMappingManager.prototype.readFromTextArea = function() { // TODO: rename
|
96
|
+
var textAreas = this.root.find("textarea");
|
97
|
+
|
98
|
+
var labels = {};
|
99
|
+
this.root.find("label").each(function(i, node) {
|
100
|
+
var el = $(node);
|
101
|
+
labels[el.attr("for")] = el.text();
|
102
|
+
});
|
103
|
+
|
104
|
+
var urisByLabel = {};
|
105
|
+
textAreas.each(function(i, node) {
|
106
|
+
var el = $(node);
|
107
|
+
var label = labels[el.attr("name")];
|
108
|
+
var values = $.map($(node).val().split(","), function(item, i) {
|
109
|
+
item = $.trim(item);
|
110
|
+
return item ? { uri: item } : null;
|
111
|
+
});
|
112
|
+
|
113
|
+
urisByLabel[label] = { el: el, values: values };
|
114
|
+
}).closest("div:has(label)").hide(); // XXX: does not belong here
|
115
|
+
|
116
|
+
return urisByLabel;
|
117
|
+
};
|
118
|
+
ConceptMappingManager.prototype.onUpdate = function(ev, data) {
|
119
|
+
this.conceptMappings[data.matchType].values.push({ uri: data.uri });
|
120
|
+
this.render();
|
121
|
+
|
122
|
+
$(document.body).trigger("concept-label", this.list);
|
123
|
+
};
|
124
|
+
ConceptMappingManager.prototype.determineDataset = function(uri) {
|
125
|
+
var result = null;
|
126
|
+
$.each(this.datasets, function(url, name) {
|
127
|
+
if(uri.indexOf(url) === 0) {
|
128
|
+
result = { name: name, url: url };
|
129
|
+
return false;
|
130
|
+
}
|
131
|
+
});
|
132
|
+
return result;
|
133
|
+
};
|
134
|
+
|
135
|
+
return ConceptMappingManager;
|
136
|
+
|
137
|
+
}(jQuery));
|
@@ -11,6 +11,7 @@ var EntitySelector = function(node) {
|
|
11
11
|
}
|
12
12
|
this.el = $(node).hide(); // XXX: rename
|
13
13
|
this.container = $('<div class="entity_select" />').data("widget", this);
|
14
|
+
this.indicator = $('<i class="spinner fa fa-refresh fa-spin hidden" />');
|
14
15
|
this.delimiter = ",";
|
15
16
|
this.singular = this.el.data("singular") || false;
|
16
17
|
this.entities = this.getSelection();
|
@@ -23,26 +24,10 @@ var EntitySelector = function(node) {
|
|
23
24
|
});
|
24
25
|
selection = $('<ul class="entity_list" />').append(selection);
|
25
26
|
|
26
|
-
var exclude = this.el.data("exclude") || null,
|
27
|
-
img = $('<img class="spinner hidden" />').
|
28
|
-
attr("src", "<%= asset_path('spinner_16x16.gif') %>");
|
29
27
|
this.input = $("<input />").autocomplete({
|
30
28
|
minLength: 3,
|
31
|
-
source:
|
32
|
-
|
33
|
-
$.getJSON(uri, { query: req.term }, function(data, status, xhr) { // TODO: error handling
|
34
|
-
var excludes = self.getSelection().
|
35
|
-
concat(exclude ? [exclude] : []);
|
36
|
-
data = $.map(data, function(entity, i) {
|
37
|
-
return $.inArray(entity.id, excludes) !== -1 ? null :
|
38
|
-
{ value: entity.id, label: entity.name };
|
39
|
-
});
|
40
|
-
self.input.autocomplete("option", "autoFocus", data.length === 1);
|
41
|
-
callback(data);
|
42
|
-
img.addClass("hidden");
|
43
|
-
});
|
44
|
-
},
|
45
|
-
search: function(ev, ui) { img.removeClass("hidden"); },
|
29
|
+
source: $.proxy(this, "onInput"), // XXX: discards original `this` context
|
30
|
+
search: function(ev, ui) { self.indicator.removeClass("hidden"); },
|
46
31
|
focus: function(ev, ui) { return false; },
|
47
32
|
select: this.onSelect
|
48
33
|
});
|
@@ -51,14 +36,55 @@ var EntitySelector = function(node) {
|
|
51
36
|
// Bootstrap expects it to be there
|
52
37
|
this.input.attr('type', 'text');
|
53
38
|
|
54
|
-
this.container.append(this.input).append(
|
39
|
+
this.container.append(this.input).append(this.indicator).append(selection).
|
55
40
|
insertAfter(node).prepend(node);
|
56
41
|
|
57
42
|
if(this.singular && this.entities.length) {
|
58
43
|
this.input.hide();
|
59
44
|
}
|
60
45
|
};
|
46
|
+
// data transformations; target format is an array of objects with members
|
47
|
+
// `value` and `label`
|
48
|
+
// optional second argument `excludes` is an array of item IDs to exclude
|
49
|
+
EntitySelector.preprocessors = {
|
50
|
+
// converts an array of objects with members `id` and `name`
|
51
|
+
"default": function(data, excludes) {
|
52
|
+
return $.map(data, function(entity, i) {
|
53
|
+
return $.inArray(entity.id, excludes) !== -1 ? null :
|
54
|
+
{ value: entity.id, label: entity.name };
|
55
|
+
});
|
56
|
+
}
|
57
|
+
};
|
58
|
+
EntitySelector.sourceSelectors = {
|
59
|
+
"default": function(callback) {
|
60
|
+
var uri = this.el.data("query-url");
|
61
|
+
callback(uri);
|
62
|
+
},
|
63
|
+
"dummy-prompt": function(callback) {
|
64
|
+
var uri = this.el.data("query-url");
|
65
|
+
var sources = { iQvoc: "hello", GEMET: "world" };
|
66
|
+
var params = $.map(sources, function(id, name) {
|
67
|
+
return confirm("include " + name) ? 'source=' + id : null;
|
68
|
+
}).join("&");
|
69
|
+
callback(uri + "?" + params);
|
70
|
+
}
|
71
|
+
};
|
61
72
|
$.extend(EntitySelector.prototype, {
|
73
|
+
onInput: function(req, callback) {
|
74
|
+
var self = this;
|
75
|
+
|
76
|
+
var responder = function(data, status, xhr) { // TODO: rename, move elsewhere
|
77
|
+
data = self.processResponse(data);
|
78
|
+
self.input.autocomplete("option", "autoFocus", data.length === 1);
|
79
|
+
callback(data);
|
80
|
+
self.indicator.addClass("hidden");
|
81
|
+
};
|
82
|
+
|
83
|
+
var sourceSelector = this.el.data("source-selector") || "default";
|
84
|
+
EntitySelector.sourceSelectors[sourceSelector].call(this, function(uri) { // XXX: direct `EntitySelector` reference limits subclassing
|
85
|
+
$.getJSON(uri, { query: req.term }, responder); // TODO: error handling
|
86
|
+
});
|
87
|
+
},
|
62
88
|
onSelect: function(ev, ui) {
|
63
89
|
var el = $(this).val(""),
|
64
90
|
widget = el.closest(".entity_select").data("widget");
|
@@ -83,6 +109,12 @@ $.extend(EntitySelector.prototype, {
|
|
83
109
|
}
|
84
110
|
ev.preventDefault();
|
85
111
|
},
|
112
|
+
processResponse: function(data) { // TODO: rename
|
113
|
+
var preprocessor = this.el.data("preprocessor") || "default";
|
114
|
+
var exclude = this.el.data("exclude") || null;
|
115
|
+
var excludes = this.getSelection().concat(exclude ? [exclude] : []);
|
116
|
+
return EntitySelector.preprocessors[preprocessor](data, excludes); // XXX: direct `EntitySelector` reference limits subclassing
|
117
|
+
},
|
86
118
|
createEntity: function(entity) {
|
87
119
|
var el;
|
88
120
|
if(this.uriTemplate) {
|
@@ -0,0 +1,75 @@
|
|
1
|
+
/*jslint vars: true, white: true */
|
2
|
+
/*global jQuery, IQVOC */
|
3
|
+
|
4
|
+
IQVOC.FederatedConceptMapper = (function($) {
|
5
|
+
|
6
|
+
"use strict";
|
7
|
+
|
8
|
+
var baseClass = IQVOC.ConceptMapper;
|
9
|
+
|
10
|
+
// `selector` is either a jQuery object, a DOM node or a string
|
11
|
+
function FederatedConceptMapper(selector) {
|
12
|
+
baseClass.apply(this, arguments);
|
13
|
+
|
14
|
+
var sources = this.root.data("datasets");
|
15
|
+
if(!sources) { // fall back to non-federated base class only
|
16
|
+
return;
|
17
|
+
}
|
18
|
+
sources["_custom"] = this.root.data("translation-other");
|
19
|
+
|
20
|
+
sources = $.map(sources, function(name, url) {
|
21
|
+
return $("<option />").val(url).text(name)[0];
|
22
|
+
});
|
23
|
+
this.source = $("<select />").append(sources).insertBefore(this.input);
|
24
|
+
|
25
|
+
this.indicator = $("<i />").
|
26
|
+
addClass("concept-mapper-indicator hidden fa fa-refresh fa-spin").
|
27
|
+
insertAfter(this.input);
|
28
|
+
|
29
|
+
var self = this;
|
30
|
+
this.input.autocomplete({ // TODO: extract autocomplete extension into subclass
|
31
|
+
source: $.proxy(this, "onChange"),
|
32
|
+
search: function(ev, ui) {
|
33
|
+
self.indicator.removeClass("hidden");
|
34
|
+
if(self.source.val() === "_custom") {
|
35
|
+
return false;
|
36
|
+
}
|
37
|
+
},
|
38
|
+
response: function(ev, ui) {
|
39
|
+
self.indicator.addClass("hidden");
|
40
|
+
},
|
41
|
+
minLength: 2
|
42
|
+
});
|
43
|
+
}
|
44
|
+
FederatedConceptMapper.prototype = new baseClass();
|
45
|
+
FederatedConceptMapper.prototype.onChange = function(req, callback) {
|
46
|
+
var self = this;
|
47
|
+
$.ajax({
|
48
|
+
type: "GET",
|
49
|
+
url: this.root.data("remote-proxy-url"),
|
50
|
+
data: {
|
51
|
+
prefix: encodeURIComponent(req.term), // FIXME: (double-)encoding should not be necessary
|
52
|
+
dataset: this.source.find("option:selected").text(),
|
53
|
+
layout: 0
|
54
|
+
},
|
55
|
+
success: function() {
|
56
|
+
// inject callback
|
57
|
+
var args = Array.prototype.slice.apply(arguments);
|
58
|
+
args.push(callback);
|
59
|
+
return self.onResults.apply(self, args);
|
60
|
+
}
|
61
|
+
});
|
62
|
+
};
|
63
|
+
FederatedConceptMapper.prototype.onResults = function(html, status, xhr, callback) {
|
64
|
+
var doc = $("<div />").append(html);
|
65
|
+
var concepts = doc.find(".concept-item-link");
|
66
|
+
var items = $.map(concepts, function(node, i) {
|
67
|
+
var el = $(node);
|
68
|
+
return { label: el.text(), value: el.data("resource-url") };
|
69
|
+
});
|
70
|
+
callback(items);
|
71
|
+
};
|
72
|
+
|
73
|
+
return FederatedConceptMapper;
|
74
|
+
|
75
|
+
}(jQuery));
|