diff --git a/client/src/js/controllers/core.js b/client/src/js/controllers/core.js index 1388d9e..12799aa 100644 --- a/client/src/js/controllers/core.js +++ b/client/src/js/controllers/core.js @@ -848,17 +848,44 @@ angular.module('histograph') // } }); - modalInstance.result.then(function(){ + modalInstance.result.then(function (result) { if(options && typeof options.discard == "function") { - options.discard(); + options.submit(options.annotator, result); } }, function(){ if(options && typeof options.discard == "function") { - options.discard(); + options.discard(options.annotator); } }); - } + }; + + /* + loadAnnotations + --- + + Load notes attached to the target id + */ + $scope.loadAnnotations = function(item, next) { + next([ + { + "id": "39fc339cf058bd22176771b3e3187329", // unique id (added by backend) + "annotator_schema_version": "v1.0", // schema version: default v1.0 + "created": "2011-05-24T18:52:08.036814", // created datetime in iso8601 format (added by backend) + "updated": "2011-05-26T12:17:05.012544", // updated datetime in iso8601 format (added by backend) + "text": "A note I wrote", // content of annotation + "quote": "the text that was annotated", // the annotated text (added by frontend) + "uri": "http://example.com", + "ranges": [{ + end: "/blockquote[1]/p[1]", + endOffset: 222, + start: "/blockquote[1]/p[1]", + startOffset: 208, + } + ] + } + ]); + }; // $scope.contribute({id: 25723}) // $scope.inspect([26414])//27329]); /* @@ -1024,10 +1051,7 @@ angular.module('histograph') $scope.q = options.query; } $scope.ok = function () { - if(options && typeof options.discard == "function") { - options.discard(); - } - $uibModalInstance.close(); + $log.log('ContributeModalCtrl -> ok()', 'saving...'); var entities = [].concat($scope.persons, $scope.locations, $scope.organizations) for(var i in entities) @@ -1037,14 +1061,16 @@ angular.module('histograph') model: 'resource' }, {}, function(res) { $log.log('ContributeModalCtrl -> ok()', 'saved', res) + $uibModalInstance.close(res.result.item); + }) }; $scope.cancel = function () { - if(options && typeof options.discard == "function") { - options.discard(); - } + // if(options && typeof options.discard == "function") { + // options.discard(); + // } $uibModalInstance.dismiss('cancel'); }; diff --git a/client/src/js/directives/annotator.js b/client/src/js/directives/annotator.js index 4389dad..5d72980 100644 --- a/client/src/js/directives/annotator.js +++ b/client/src/js/directives/annotator.js @@ -186,10 +186,12 @@ angular.module('histograph') Annotator.Plugin.HelloWorld = function (element) { return { pluginInit: function () { - var editor; + var editor, + annotator = this.annotator, + link; this.annotator.subscribe("annotationCreated", function (annotation) { - console.log("The annotation: %o has just been created!", annotation) + console.log("The annotation: %o has just been created!", annotation, link) }) .subscribe("annotationUpdated", function (annotation) { console.log("The annotation: %o has just been updated!", annotation) @@ -201,12 +203,17 @@ angular.module('histograph') }) .subscribe("annotationEditorShown", function (editor, annotation) { editor = editor; - console.log("The annotation: has just been annotationEditorShown!", arguments); + console.log("The annotation: has just been annotationEditorShown!", arguments, annotator); scope.contribute(scope.item, "entity", { query: annotation.quote, - discard: function() { - editor.hide() + annotator: annotator, + submit: function(annotator, result) { + link = result; + annotator.editor.submit(); + }, + discard: function(annotator) { + annotator.editor.hide(); } }); scope.$apply(); @@ -220,24 +227,19 @@ angular.module('histograph') } var annotator = angular.element(element).annotator().data('annotator'); - annotator.addPlugin('Unsupported'); - annotator.addPlugin('HelloWorld');//' /*, any other options */); - // element..annotator()bind('mouseup', function(e){ - // var selection; - - // if (window.getSelection) { - // selection = window.getSelection(); - // } else if (document.selection) { - // selection = document.selection.createRange(); - // } - // if (selection.toString() !== '') { - // var s = selection.toString(); - // $log.log('::annotator -> ',selection) - - // } - // }); + annotator.addPlugin('Unsupported'); + annotator.addPlugin('HelloWorld'); + + // lazyload annotation for this specific goddamn element + // setTimeout(function(){ + // attrs.target && scope.loadAnnotations({ + // id: attrs.target + // }, function (annotations) {debugger + // annotator.loadAnnotations(annotations); + // }); + // }, 20) } } }); diff --git a/client/src/js/templates.js b/client/src/js/templates.js index 29ec7e4..b18b9ae 100644 --- a/client/src/js/templates.js +++ b/client/src/js/templates.js @@ -30,7 +30,7 @@ $templateCache.put("templates/partials/neighbors.html","\n
\n {{user.username}} {{user.type}} {{user.last_modification_time*1000|date:\'longDate\'}}\n
\n
\n
\n
\n \n
\n
\n
\n
{{relatedItem.props.ecmd|ecmd}} {{$index + 1}} out of {{totalItems}}
\n

\n \n \n \n

\n \n
\n no date found\n {{relatedItem.props|guessInterval}}\n persons in the document:\n {{per.props.name||per.name}}\n \n
\n \n
\n
\n
\n \n \n"); $templateCache.put("templates/partials/resource-toolbar.html","
\n \n \n
\n \n \n \n
\n \n \n \n
\n \n \n \n {{filter.props.name}} {{!$last? \', \': \'\'}}\n \n \n \n \n
\n \n \n
\n
\n \n
"); -$templateCache.put("templates/partials/resource.html","
\n \n \n \n \n \n
\n {{item.props.type}}\n \n \n \n \n —\n \n \n
\n

\n \n \n \n \n

\n \n
\n \n {{location.name}}-\n \n \n without any date add\n \n {{item.props|guessInterval}}edit\n \n \n
\n \n
\n

themes:\n {{the.props|lookup:\"name\":language}}{{$last? \'\': \', \'}} — \n

\n

persons mentioned:\n none.\n \n add\n

\n

groups:\n {{per.props.name}} {{$last? \'\': \', \'}} \n

\n

organizations:\n none.\n \n add \n

\n

location mentioned:\n none.\n \n add\n

\n

available in:\n {{lang}} \n

\n \n\n
\n
\n
\n \n \n \n \n \n \n \n \n
\n
\n \n \n \n
\n
\n
\n
\n
\n
\n \n \n
\n
\n
1\" class=\"btn-group\" dropdown is-open=\"status.isopen\" tooltip=\"view a different version\">\n \n \n
\n
\n \n
\n
\n \n \n
\n
\n
\n
\n
\n \n \n \n \n \n
\n \n
\n\n \n
0\">\n
\n \n {{com.user.username}}\n \n {{com.props.creation_date | date : \'longDate\'}} — {{com.props.content}}\n {{tag}}\n
\n \n
\n \n
"); +$templateCache.put("templates/partials/resource.html","
\n \n \n \n \n \n
\n {{item.props.type}}\n \n \n \n \n —\n \n \n
\n

\n \n \n \n \n

\n \n
\n \n {{location.name}}-\n \n \n without any date add\n \n {{item.props|guessInterval}}edit\n \n \n
\n \n
\n

themes:\n {{the.props|lookup:\"name\":language}}{{$last? \'\': \', \'}} — \n

\n

persons mentioned:\n none.\n \n add\n

\n

groups:\n {{per.props.name}} {{$last? \'\': \', \'}} \n

\n

organizations:\n none.\n \n add \n

\n

location mentioned:\n none.\n \n add\n

\n

available in:\n {{lang}} \n

\n \n\n
\n
\n
\n \n \n \n \n \n \n \n \n
\n
\n \n \n \n
\n
\n
\n \n
\n
\n
\n
\n \n \n
\n
\n
1\" class=\"btn-group\" dropdown is-open=\"status.isopen\" tooltip=\"view a different version\">\n \n \n
\n
\n \n
\n
\n \n \n
\n
\n
\n
\n
\n \n \n \n \n \n
\n \n
\n\n \n
0\">\n
\n \n {{com.user.username}}\n \n {{com.props.creation_date | date : \'longDate\'}} — {{com.props.content}}\n {{tag}}\n
\n \n
\n \n
"); $templateCache.put("templates/partials/resources-masonry.html","
\r\n \r\n
\r\n There are no related resources.\r\n
\r\n
\r\n \r\n
\r\n \r\n
\r\n
0 && offset + limit < totalItems\'>\r\n \r\n
\r\n
"); $templateCache.put("templates/partials/resources.html","
\n
\n There are no related resources.\n
\n
\n
\n \n
\n
"); $templateCache.put("templates/partials/user-lite.html","\n
\n
\n
\n \n \n
\n
\n \n
{{relatedItem.username}}\n
favourited — {{relatedItem.proposes.length}} inquiries proposed
\n
\n
\n
\n \n {{inquiry.props.name}} — {{inquiry.props.creation_date | date : \'longDate\'}}\n \n
\n
\n
"); diff --git a/client/src/templates/partials/resource.html b/client/src/templates/partials/resource.html index 6126412..8876229 100644 --- a/client/src/templates/partials/resource.html +++ b/client/src/templates/partials/resource.html @@ -97,8 +97,10 @@

+
+
diff --git a/controllers/entity.js b/controllers/entity.js index aae20e0..0011905 100644 --- a/controllers/entity.js +++ b/controllers/entity.js @@ -114,6 +114,9 @@ module.exports = function(io){ createRelatedResource: function(req, res) { var form = validator.request(req); + // check if it contains a silly annotation.... + if(!form.isValid) + return helpers.formError(form.errors, res); Entity.createRelatedResource({ id: +form.params.entity_id diff --git a/parser.js b/parser.js index 8e852e1..6af7534 100644 --- a/parser.js +++ b/parser.js @@ -232,6 +232,9 @@ module.exports = { return YAML.parse(yaml) }, + toYaml: function(item) { + return YAML.stringify(item, 2) + }, /* get the chunks from a YAML content for the specific ids only, then annotate them. diff --git a/test/controllers.entity.js b/test/controllers.entity.js index 00bfd63..f8633bc 100644 --- a/test/controllers.entity.js +++ b/test/controllers.entity.js @@ -213,7 +213,53 @@ describe('controller:entity related items', function() { }); }) + it('should create a manual connection with ANNOTATION!, with frequence = 1 resource B', function(done) { + var parser = require('../parser'); + session + .post('/api/entity/' + __entity.id +'/related/resource/'+ __resourceB.id) + .send({ + annotation: parser.toYaml([{ + text: "A note I wrote", // content of annotation + quote: "the text that was annotated", // the annotated text (added by frontend) + ranges: [{ + end: "/blockquote[1]/p[1]", + endOffset: 222, + start: "/blockquote[1]/p[1]", + startOffset: 208, + } + ] + }]) + }) + .expect('Content-Type', /json/) + .expect(200) + .end(function (err, res) { + should.exist(res.body.result.item.rel) + should.exist(res.body.result.item.related.action.props) + should.exist(res.body.result.item.rel.created_by) + should.not.exists(err); + + // should remove the action + Action.remove(res.body.result.item.related.action, function(){ + should.not.exist(err) + done(); + }); + }); + }); + + it('should delete the manual connection', function(done) { + session + .delete('/api/entity/' + __entity.id +'/related/resource/'+ __resourceB.id) + .expect('Content-Type', /json/) + .expect(200) + .end(function (err, res) { + should.equal(res.body.result.item.id, __entity.id) + should.equal(res.body.result.item.related.resource.id, __resourceB.id) + // console.log('DELETE', res.body) + should.not.exists(err); + done(); + }); + }) }); describe('controller:entity related issues', function() { diff --git a/test/parser.js b/test/parser.js index 3b4fdf8..215e634 100644 --- a/test/parser.js +++ b/test/parser.js @@ -37,6 +37,24 @@ describe('parser:lucene', function() { }) }); +describe('parser:annotation', function(){ + it('should correctly parse a annotatrjs annotation', function (done) { + var q = parser.toYaml({ + text: "A note I wrote", // content of annotation + quote: "the text that was annotated", // the annotated text (added by frontend) + ranges: [{ + end: "/blockquote[1]/p[1]", + endOffset: 222, + start: "/blockquote[1]/p[1]", + startOffset: 208, + } + ] + }); + var _q = parser.yaml(q); + should.equal(_q.ranges[0].endOffset, 222); + done(); + }) +}) describe('parser:lucene real use case', function() { it('understand "jacques delors" (@todo use case)', function (done) { diff --git a/validator.js b/validator.js index 3a0727a..1ec9826 100644 --- a/validator.js +++ b/validator.js @@ -21,6 +21,16 @@ validator.extend('includedIn', function (str, choices) { return _.difference(str.split(','), choices).length === 0; }); +validator.extend('isAnnotatorjs', function (str) { + var q = {}; + try{ + q = YAML.parse(str); + return true; + } catch(e) { + return false; + } +}) + /* Verify that for each field in form, everything looks good. @params form - req.body and/or req.params @@ -142,6 +152,14 @@ module.exports = { ], error: 'id not valid' }, + { + field: 'annotation', + check: 'isAnnotatorjs', + args: [ + 0 + ], + error: 'annotation not valid, should be annotatorjs compatible.' + }, { field: 'center', check: 'isInt',