{{#i18n 'label.properties'}}Properties{{/i18n}}
-| {{#i18n 'label.name'}}Name{{/i18n}} | -{{#i18n 'label.value'}}Value{{/i18n}} | -
|---|---|
| {{@key}} | -{{this}} | -
- {{#i18n 'property.all-types-permitted'}} - This property permits any simple type. - {{/i18n}} -
-- {{#i18n 'property.no-type-matched'}} - This value does not match the permitted type(s). - {{/i18n}} -
diff --git a/frontend/src/item-edit/linked-item-editor-view.ts b/frontend/src/item-edit/linked-item-editor-view.ts index 9d1366ef5..84e9f2eca 100644 --- a/frontend/src/item-edit/linked-item-editor-view.ts +++ b/frontend/src/item-edit/linked-item-editor-view.ts @@ -1,5 +1,5 @@ import { - find, intersection, extend, chain, isNumber, isBoolean, isString, isDate, + extend, chain } from 'lodash'; import Model from '../core/model'; @@ -7,53 +7,17 @@ import { CompositeView } from '../core/view'; import { asLD, Native } from '../common-rdf/conversion'; import Node from '../common-rdf/node'; import Graph from '../common-rdf/graph'; -import { rdfs, xsd } from '../common-rdf/ns'; +import { rdfs } from '../common-rdf/ns'; import Select2Picker from '../forms/select2-picker-view'; import RemoveButton from '../forms/remove-button-view'; import InputField from '../forms/input-field-view'; import { getRdfSuperProperties } from '../utilities/linked-data-utilities'; -import interpretText from '../utilities/interpret-text'; -import AllowedTypesListHelpText from './allowed-type-list-view'; -import DetectedTypeHelpText from './detected-type-help-view'; import linkedItemTemplate from './linked-item-editor-template'; +import TypeAwareHelpText from './type-aware-help-view'; // Selector of the control where the object picker is inserted. const objectControl = '.field.has-addons .control:nth-child(2)'; -// Selector of template element displaying "all types allowed" help text. -const allTypesAllowedHelp = 'p.help:first-of-type'; -// Selector of template element displaying "no matching type" help text. -const noMatchHelp = 'p.help.is-danger'; - -const semiCompatibleTypes: [(v: any) => boolean, string[]][] = [ - [isBoolean, [xsd.boolean]], - [isNumber, [ - xsd.double, xsd.float, xsd.byte, xsd.unsignedByte, xsd.short, - xsd.unsignedShort, xsd.int, xsd.unsignedInt, xsd.long, - xsd.unsignedLong, xsd.integer, xsd.nonNegativeInteger, - xsd.nonPositiveInteger, xsd.positiveInteger, xsd.negativeInteger, - xsd.decimal, - ]], - [isDate, [xsd.dateTime, xsd.date]], - [isString, [ - xsd.string, xsd.normalizedString, xsd.token, xsd.language, - xsd.base64Binary, - ]], -]; - -function findType(range: Graph, value: any): string { - const available = range.map(n => (n.id as string)); - let singleType; - if (range.length === 1) { - singleType = available[0]; - if (singleType !== rdfs.Literal) return singleType; - } - const matches = find(semiCompatibleTypes, ([check]) => check(value))[1]; - if (!range.length || singleType === rdfs.Literal) { - return matches[0]; - } - return intersection(matches, available)[0]; -} export default class LinkedItemEditor extends CompositeView { collection: Graph; @@ -61,19 +25,15 @@ export default class LinkedItemEditor extends CompositeView { predicatePicker: Select2Picker; removeButton: RemoveButton; literalField: InputField; - allowedTypesList: AllowedTypesListHelpText; - detectedTypeHelp: DetectedTypeHelpText; + typeAwareHelp: TypeAwareHelpText; initialize() { this.range = new Graph; this.predicatePicker = new Select2Picker({collection: this.collection}); this.literalField = new InputField(); this.removeButton = new RemoveButton().on('click', this.close, this); - this.allowedTypesList = new AllowedTypesListHelpText({ - collection: this.range, - }); - this.detectedTypeHelp = new DetectedTypeHelpText({ model: new Model }); - this.render().updateRange(); + this.typeAwareHelp = new TypeAwareHelpText({model: this.model.get('predicate')}); + this.render(); this.predicateFromModel(this.model).objectFromModel(this.model); this.literalField.on('keyup', this.updateObject, this); this.predicatePicker.on('change', this.updatePredicate, this); @@ -87,49 +47,12 @@ export default class LinkedItemEditor extends CompositeView { updatePredicate(picker: Select2Picker, id: string): void { const predicate = this.collection.get(id); this.model.set('predicate', predicate); - this.updateRange(); + this.typeAwareHelp.updateRange(predicate); this.updateObject(this.literalField, this.literalField.getValue()); } - updateRange(): this { - const predicate = this.model.get('predicate'); - this.$(allTypesAllowedHelp).hide(); - this.allowedTypesList.$el.hide(); - if (!predicate) { - this.range.reset(); - return this; - } - const allProperties = getRdfSuperProperties([predicate]); - this.range.set( - chain(allProperties) - .map(n => n.get(rdfs.range) as Node[]) - .flatten() - .compact() - .value() - ); - if (!this.range.length || this.range.get(rdfs.Literal)) { - this.$(allTypesAllowedHelp).show(); - } else { - this.allowedTypesList.$el.show(); - } - return this; - } - updateObject(labelField: InputField, val: string): void { - const interpretation = interpretText(val, this.range); - this.literalField.$el.removeClass('is-danger'); - this.$(noMatchHelp).hide(); - if (interpretation) { - this.detectedTypeHelp.model.set(interpretation); - this.detectedTypeHelp.$el.show(); - this.model.set('object', interpretation.jsonld); - } else { - if (val) { - this.$(noMatchHelp).show(); - this.literalField.$el.addClass('is-danger'); - } - this.detectedTypeHelp.$el.hide(); - } + this.typeAwareHelp.updateHelpText(val); } predicateFromModel(model: Model, selectedPredicate?: Node): this { @@ -143,15 +66,8 @@ export default class LinkedItemEditor extends CompositeView { setLiteral || (setLiteral = model.get('object')); if (setLiteral) { const jsonld = asLD(setLiteral); - this.literalField.setValue(jsonld['@value']); - if (!jsonld['@type']) { - jsonld['@type'] = findType(this.range, jsonld['@value']); - } - this.detectedTypeHelp.model.set({ jsonld }); - } else { - this.detectedTypeHelp.$el.hide(); + this.typeAwareHelp.setHelpText(jsonld); } - this.$(noMatchHelp).hide(); return this; } @@ -173,8 +89,8 @@ extend(LinkedItemEditor.prototype, { view: 'removeButton', selector: '.field.has-addons', }, { - view: 'allowedTypesList', - selector: noMatchHelp, - method: 'before', + view: 'typeAwareHelp', + selector: '.field.has-addons', + method: 'after', }, 'detectedTypeHelp'], }); diff --git a/frontend/src/item-edit/type-aware-help-template.hbs b/frontend/src/item-edit/type-aware-help-template.hbs new file mode 100644 index 000000000..23ec8f4f0 --- /dev/null +++ b/frontend/src/item-edit/type-aware-help-template.hbs @@ -0,0 +1,10 @@ ++ {{#i18n 'property.all-types-permitted'}} + This property permits any simple type. + {{/i18n}} +
++ {{#i18n 'property.no-type-matched'}} + This value does not match the permitted type(s). + {{/i18n}} +
diff --git a/frontend/src/item-edit/type-aware-help-view.ts b/frontend/src/item-edit/type-aware-help-view.ts new file mode 100644 index 000000000..3b134bca1 --- /dev/null +++ b/frontend/src/item-edit/type-aware-help-view.ts @@ -0,0 +1,138 @@ +import { + find, intersection, extend, isNumber, isBoolean, isString, isDate, chain, +} from 'lodash'; +import { CompositeView } from '../core/view'; +import { rdfs, xsd } from '../common-rdf/ns'; +import interpretText from '../utilities/interpret-text'; +import AllowedTypesListHelpText from './allowed-type-list-view'; +import DetectedTypeHelpText from './detected-type-help-view'; +import Graph from '../common-rdf/graph'; +import Node from '../common-rdf/node'; +import { FlatSingleValue } from '../common-rdf/json'; +import { asLD, Native } from '../common-rdf/conversion'; +import Model from '../core/model'; + +import typeAwareHelpTemplate from './type-aware-help-template'; +import { getRdfSuperProperties } from '../utilities/linked-data-utilities'; + +// Selector of template element displaying "all types allowed" help text. +const allTypesAllowedHelp = 'p.help:first-of-type'; +// Selector of template element displaying "no matching type" help text. +const noMatchHelp = 'p.help.is-danger'; + +const semiCompatibleTypes: [(v: any) => boolean, string[]][] = [ + [isBoolean, [xsd.boolean]], + [isNumber, [ + xsd.double, xsd.float, xsd.byte, xsd.unsignedByte, xsd.short, + xsd.unsignedShort, xsd.int, xsd.unsignedInt, xsd.long, + xsd.unsignedLong, xsd.integer, xsd.nonNegativeInteger, + xsd.nonPositiveInteger, xsd.positiveInteger, xsd.negativeInteger, + xsd.decimal, + ]], + [isDate, [xsd.dateTime, xsd.date]], + [isString, [ + xsd.string, xsd.normalizedString, xsd.token, xsd.language, + xsd.base64Binary, + ]], +]; + +function findType(range: Graph, value: any): string { + const available = range.map((n) => n.id as string); + let singleType; + if (range.length === 1) { + singleType = available[0]; + if (singleType !== rdfs.Literal) return singleType; + } + const matches = find(semiCompatibleTypes, ([check]) => check(value)); + const matchedTypes = matches ? matches[1] : []; + + if (!range.length || singleType === rdfs.Literal) { + return matchedTypes[0]; + } + return intersection(matchedTypes, available)[0]; +} + +export default class TypeAwareHelpText extends CompositeView{{#i18n 'label.properties'}}Properties{{/i18n}}
-| {{#i18n 'label.name'}}Name{{/i18n}} | -{{#i18n 'label.value'}}Value{{/i18n}} | -
|---|---|
| {{@key}} | -{{this}} | -
{{{additionalHelpText}}} +
+{{#i18n 'upload.only-txt-utf8-lf'}}Only txt files in UTF-8 encoding (LF for line endings) are supported.{{/i18n}}
++ {{#i18n 'upload.source-lang-select-help' + }}If the source contains multiple languages, please select 'Other'.{{/i18n}} +
+{{#i18n 'upload.source-type-select-help'}}When in doubt, choose 'Unknown'.{{/i18n}}
+{{#i18n 'upload.public-private-help'}}Provide access to everyone (public) or only to authenticated users (private).{{/i18n}}
+No file selected
-Note that only txt files in UTF-8 encoding (LF for line endings) are supported.
-Specify the language that the source text is in. If the source contains multiple languages, - please select 'Other'.
-Specify the type of the source. When in doubt, choose 'Other'
-Preview source content
-