UNPKG

labo-components

Version:
302 lines (270 loc) 10.5 kB
import DocumentAPI from '../api/DocumentAPI'; import CollectionUtil from '../util/CollectionUtil'; import IDUtil from '../util/IDUtil'; const AnnotationUtil = { /************************************************************************************* --------------------------- W3C BUSINESS LOGIC HERE --------------------------------- *************************************************************************************/ //called from components that want to create a new annotation with a proper target generateW3CEmptyAnnotation : function(user, project, collectionId, resourceId, mediaObject = null, segmentParams = null) { //Resource or MediaObject annotations ALL start with this as part of the target let resourceTarget = AnnotationUtil.generateResourceLevelTarget(collectionId, resourceId); let refinedTarget = null; //ALL THESE ARE MANDATORY TO BE ABLE TO PROPERLY ANNOTATE A MEDIA OBJECT! //TODO DELETE/UPDATE NON-COMPATIBLE ANNOTATIONS if(mediaObject && mediaObject.mimeType && mediaObject.assetId) { let refinedBy = null; //when selecting a piece of the target let mediaType = null; if(mediaObject.mimeType.indexOf('video') !== -1) { mediaType = 'Video'; refinedBy = AnnotationUtil.__generateSegmentSelector(segmentParams, 'temporal') } else if(mediaObject.mimeType.indexOf('audio') !== -1) { mediaType = 'Audio'; refinedBy = AnnotationUtil.__generateSegmentSelector(segmentParams, 'temporal') } else if(mediaObject.mimeType.indexOf('image') !== -1) { mediaType = 'Image'; refinedBy = AnnotationUtil.__generateSegmentSelector(segmentParams, 'spatial') } refinedTarget = AnnotationUtil.__generateMediaObjectTarget( resourceTarget, refinedBy, mediaType, mediaObject.assetId ) } return { id : null, //should be generated on the server? user : user.id, project : project ? project.id : null, //no suitable field found in W3C so far body : null, target : refinedTarget ? refinedTarget : resourceTarget } }, //currently only used for bookmarking lots of resources generateEmptyW3CMultiTargetAnnotation : function(user, project, collectionId, resourceIds, motivation='bookmarking') { const annotation = { id : null, user : user.id, project : project ? project.id : null, //no suitable field found in W3C so far motivation : motivation, target : resourceIds.map((rid) => AnnotationUtil.generateResourceLevelTarget(collectionId, rid)), body : null }; return annotation }, generateBookmarkGroupAnnotation : function(user, project, collectionId, groupLabel) { const annotation = AnnotationUtil.generateEmptyW3CMultiTargetAnnotation(user, project, collectionId, []); //empty target annotation.id = IDUtil.guid(); //Note: normally generated on the server, but used as guid in this component annotation.body = [{ "annotationType": "classification", "vocabulary": "clariahwp5-bookmark-group", "label": groupLabel, "user": user.id }]; return annotation; }, toUpdatedAnnotation(user, project, collectionId, resourceId, mediaObject, segmentParams, annotation) { if(!annotation) { annotation = AnnotationUtil.generateW3CEmptyAnnotation( user, project, collectionId, resourceId, mediaObject, segmentParams ); } else if(segmentParams && annotation.target.selector.refinedBy) { annotation.target.selector.refinedBy = segmentParams.rect ? this.__generateSegmentSelector(segmentParams, 'spatial') : this.__generateSegmentSelector(segmentParams, 'temporal') } return annotation; }, removeSourceUrlParams(url) { if(url.indexOf('?') !== -1 && url.indexOf('cgi?') === -1) { return url.substring(0, url.indexOf('?')); } return url }, generateResourceLevelTarget(collectionId, resourceId) { return { type : 'Resource', source : resourceId, selector : { type: 'NestedPIDSelector', value: [ { id: collectionId, type: ['Collection'], property: 'isPartOf' }, { id: resourceId, type: ['Resource'], property: 'isPartOf' } ] } } }, __generateSegmentSelector : function(params, segmentType) { if(!params) { return null } if(segmentType === 'temporal') { if(params.start && params.end && params.start !== -1 && params.end !== -1) { return { type: "FragmentSelector", conformsTo: "http://www.w3.org/TR/media-frags/", value: '#t=' + params.start + ',' + params.end, start: params.start, end: params.end } } } else if(segmentType === 'spatial') { if(params.rect && typeof(params.rect) === 'object') { return { type: "FragmentSelector", conformsTo: "http://www.w3.org/TR/media-frags/", value: '#xywh=' + params.rect.x + ',' + params.rect.y + ',' + params.rect.w + ',' + params.rect.h, rect : params.rect } } } return null }, //TODO make this suitable for resource annotations too (now it's currently only for mediaobject annotations) __generateMediaObjectTarget : function(basicTarget, refinedBy, mediaType, assetId) { let targetType = 'MediaObject'; if (refinedBy) { basicTarget.selector.refinedBy = refinedBy targetType = 'Segment' } const representationTypes = ['Representation', 'MediaObject', mediaType] if (refinedBy) { representationTypes.push('Segment') } basicTarget.selector.value.push({ id: assetId, type: representationTypes, property: 'isRepresentation' }); basicTarget.type = targetType; basicTarget.source = assetId; return basicTarget }, /************************************************************************************* ******************************* USED BY PLAYERS TO GET THE RIGHT SEGMENTS *********** *************************************************************************************/ //get the index of the segment within a list of annotations of a certain target getSegmentIndex(annotations, annotation) { if(annotations && annotation) { let i = 0; for(const a of annotations) { if(a.target.selector.refinedBy) { if(a.id === annotation.id) { return i; } i++; } } } return -1; }, //get the nth segment within a list of annotations of a certain target getSegment(annotations, index) { if(annotations) { index = index < 0 ? 0 : index; let i = 0; for(const a of annotations) { if(a.target.selector.refinedBy) { if(i === index) { return a; } i++; } } } return null; }, /************************************************************************************* ************************************* ANNOTATION BODY PRETTIFY ********************** *************************************************************************************/ annotationBodyToPrettyText(body) { if(body.annotationType === 'comment') { return body.text ? body.text.substring(0, 200) : ''; } else if(body.annotationType === 'classification') { return body.label; } else if(body.annotationType === 'metadata') { if(body.properties && body.properties.length > 0) { let text = body.properties[0].key; text += ': ' + (body.properties[0].value ? body.properties[0].value.substring(0,200) : ''); text += ' (+ ' + (body.properties.length -1) + ' other properties)'; return text } } return '-'; }, /************************************************************************************* ************************************* W3C MEDIA FRAGMENTS HELPERS *************** *************************************************************************************/ extractAnnotationTargetDetails : function(annotation) { //check if there is a temporal fragment in the annotation target let frag = AnnotationUtil.extractTemporalFragmentFromAnnotation(annotation.target); if(frag) { return { type : 'temporal', frag : frag, source : annotation.target.source } } else { //then see if there is a spatial fragment frag = AnnotationUtil.extractSpatialFragmentFromAnnotation(annotation); if(frag) { return { type : 'spatial', frag : frag, source : annotation.target.source} } } return {type : 'object', frag : null, source : annotation.target.source} }, extractTemporalFragmentFromAnnotation : function(target) { if(target && target.selector && target.selector.refinedBy && target.selector.refinedBy.start) { return { start : target.selector.refinedBy.start, end : target.selector.refinedBy.end } } return null; }, extractSpatialFragmentFromAnnotation : function(annotation) { if(annotation && annotation.target && annotation.target.selector && annotation.target.selector.refinedBy) { return { x: annotation.target.selector.refinedBy.x, y: annotation.target.selector.refinedBy.y, w: annotation.target.selector.refinedBy.w, h: annotation.target.selector.refinedBy.h } } return null; }, /************************************************************************************* *********************EXTRACT STUFF FROM CONTAINED ANNOTATION CARDS ****************** *************************************************************************************/ extractAnnotationCardTitle : function(annotation) { if(annotation && annotation.body) { const cards = annotation.body.filter((a) => { return a.annotationType === 'metadata' }); if(cards.length > 0) { const title = cards[0].properties.filter((p) => { return p.key === 'title' || p.key === 'titel'; }); return title.length > 0 ? title[0].value : null; } } return null; }, /************************************************************************************* ************************************* URL VALIDATION **************************** *************************************************************************************/ isValidURL(url) { const urlPattern =/^(?:(?:https?|ftp):\/\/)(?:\S+(?::\S*)?@)?(?:(?!10(?:\.\d{1,3}){3})(?!127(?:\.\d{1,3}){3})(?!169\.254(?:\.\d{1,3}){2})(?!192\.168(?:\.\d{1,3}){2})(?!172\.(?:1[6-9]|2\d|3[0-1])(?:\.\d{1,3}){2})(?:[1-9]\d?|1\d\d|2[01]\d|22[0-3])(?:\.(?:1?\d{1,2}|2[0-4]\d|25[0-5])){2}(?:\.(?:[1-9]\d?|1\d\d|2[0-4]\d|25[0-4]))|(?:(?:[a-z\u00a1-\uffff0-9]+-?)*[a-z\u00a1-\uffff0-9]+)(?:\.(?:[a-z\u00a1-\uffff0-9]+-?)*[a-z\u00a1-\uffff0-9]+)*(?:\.(?:[a-z\u00a1-\uffff]{2,})))(?::\d{2,5})?(?:\/[^\s]*)?$/i; return urlPattern.test(url); } }; export default AnnotationUtil;