Browse Source

store annotations in db and sync with peers

j 6 months ago
parent
commit
e0cba14d6a

+ 12
- 0
config.json View File

@@ -242,6 +242,18 @@
242 242
             "sort": true
243 243
         },
244 244
         {
245
+            "id": "quotes",
246
+            "title": "Quotes",
247
+            "find": true,
248
+            "type": "text"
249
+        },
250
+        {
251
+            "id": "notes",
252
+            "title": "Notes",
253
+            "find": true,
254
+            "type": "text"
255
+        },
256
+        {
245 257
             "id": "fulltext",
246 258
             "title": "Full Text",
247 259
             "find": true,

+ 48
- 0
oml/annotation/api.py View File

@@ -0,0 +1,48 @@
1
+# -*- coding: utf-8 -*-
2
+from oxtornado import actions
3
+from . import models
4
+import settings
5
+import state
6
+from changelog import add_record
7
+
8
+import logging
9
+logger = logging.getLogger(__name__)
10
+
11
+
12
+def getAnnotations(data):
13
+    response = {}
14
+    response['annotations'] = models.Annotation.get_by_item(data['id'])
15
+    return response
16
+actions.register(getAnnotations)
17
+
18
+
19
+def addAnnotation(data):
20
+    item_id = data.pop('item')
21
+    a = models.Annotation.create(item_id, settings.USER_ID, data)
22
+    a.add_record('addannotation')
23
+    response = a.json()
24
+    return response
25
+actions.register(addAnnotation)
26
+
27
+
28
+def editNote(data):
29
+    a = models.Annotation.get(state.user(), data['item'], data['annotation'])
30
+    if a:
31
+        a.data['notes'] = data['notes']
32
+        a.add_record('editannotation')
33
+        a.save()
34
+        response = a.json()
35
+    else:
36
+        response = {}
37
+    return response
38
+actions.register(editNote)
39
+
40
+
41
+def removeAnnotation(data):
42
+    a = models.Annotation.get(state.user(), data['item'], data['annotation'])
43
+    if a:
44
+        a.add_record('removeannotation')
45
+        a.delete()
46
+    response = {}
47
+    return response
48
+actions.register(removeAnnotation)

+ 57
- 11
oml/annotation/models.py View File

@@ -4,6 +4,7 @@ import json
4 4
 import logging
5 5
 import os
6 6
 import shutil
7
+import unicodedata
7 8
 
8 9
 from sqlalchemy.orm import load_only
9 10
 import ox
@@ -31,10 +32,15 @@ class Annotation(db.Model):
31 32
     created = sa.Column(sa.DateTime())
32 33
     modified = sa.Column(sa.DateTime())
33 34
 
35
+    user_id = sa.Column(sa.String(43), sa.ForeignKey('user.id'))
34 36
     user = sa.orm.relationship('User', backref=sa.orm.backref('annotations', lazy='dynamic'))
37
+    item_id = sa.Column(sa.String(43), sa.ForeignKey('item.id'))
35 38
     item = sa.orm.relationship('Item', backref=sa.orm.backref('items', lazy='dynamic'))
36 39
     data = sa.Column(MutableDict.as_mutable(sa.PickleType(pickler=json_pickler)))
37 40
 
41
+    findquotes = sa.Column(sa.Text(), index=True)
42
+    findnotes = sa.Column(sa.Text(), index=True)
43
+
38 44
     def __init__(self, item_id, user_id, data):
39 45
         self.created = datetime.utcnow()
40 46
         self.modified = datetime.utcnow()
@@ -43,28 +49,44 @@ class Annotation(db.Model):
43 49
         self.data = data
44 50
 
45 51
     @classmethod
46
-    def create(cls, **kwargs):
47
-        a = cls(**kwargs)
48
-        state.db.session.add(a)
52
+    def create(cls, item_id, user_id, data):
53
+        a = cls(item_id, user_id, data)
54
+        a.save()
55
+        return a
56
+
57
+    def delete(self):
58
+        state.db.session.delete(self)
49 59
         state.db.session.commit()
50 60
 
51 61
     @classmethod
52 62
     def get(cls, user, item_id, annotation_id):
53
-        for a in cls.query.filter_by(item_id=item_id, user=user, _id=annotation_id):
54
-            if a.data.get('id') == annotation_id:
55
-                return a
63
+        if isinstance(user, str):
64
+            qs = cls.query.filter_by(item_id=item_id, user_id=user, id=annotation_id)
65
+        else:
66
+            qs = cls.query.filter_by(item_id=item_id, user=user, id=annotation_id)
67
+        for a in qs:
68
+            return a
56 69
 
57 70
     @classmethod
58
-    def get_by_item(cls, user, item_id):
71
+    def get_by_item(cls, item_id):
59 72
         annotations = []
60 73
         for a in cls.query.filter_by(item_id=item_id):
61 74
             annotations.append(a.json())
62 75
         return annotations
63 76
 
64 77
     def save(self):
65
-        _id = self.data.get('id')
66
-        if _id:
67
-            self._id = _id
78
+        id = self.data.get('id')
79
+        if id:
80
+            self.id = id
81
+        self.findquotes = unicodedata.normalize('NFKD', self.data.get('text', '')).lower()
82
+        note = self.data.get('notes', {})
83
+        if isinstance(note, list) and note:
84
+            note = note[0]
85
+        if isinstance(note, dict):
86
+            note = note.get('value', '')
87
+        else:
88
+            note = ''
89
+        self.findnotes = unicodedata.normalize('NFKD', note).lower()
68 90
         state.db.session.add(self)
69 91
         state.db.session.commit()
70 92
 
@@ -73,6 +95,30 @@ class Annotation(db.Model):
73 95
         data['created'] = self.created
74 96
         data['modified'] = self.modified
75 97
         data['user'] = self.user_id
76
-        data['_id'] = ox.toAZ(self.id)
98
+        data['_id'] = ox.toAZ(self._id)
99
+        if isinstance(data.get('notes'), dict):
100
+            note = data['notes']
101
+            if self.user_id != settings.USER_ID:
102
+                note['user'] = self.user_id
103
+                if not note.get('id'):
104
+                    note['id'] = 'A'
105
+            data['notes'] = [data['notes']]
106
+        if 'notes' not in data:
107
+            data['notes'] = []
77 108
         return data
78 109
 
110
+    def add_record(self, action):
111
+        args = [self.item_id]
112
+        if action == 'addannotation':
113
+            args.append(self.data)
114
+        elif action == 'editannotation':
115
+            args.append(self.id)
116
+            args.append({
117
+                'notes': self.data['notes']
118
+            })
119
+        elif action == 'removeannotation':
120
+            args.append(self.id)
121
+        else:
122
+            raise Exception('unknown action')
123
+        add_record(action, *args)
124
+

+ 1
- 0
oml/api.py View File

@@ -12,6 +12,7 @@ from oxtornado import actions
12 12
 
13 13
 import item.api
14 14
 import user.api
15
+import annotation.api
15 16
 import update
16 17
 import utils
17 18
 

+ 26
- 1
oml/changelog.py View File

@@ -1,8 +1,8 @@
1 1
 # -*- coding: utf-8 -*-
2 2
 
3
-import os
4 3
 from datetime import datetime
5 4
 import json
5
+import os
6 6
 
7 7
 import sqlalchemy as sa
8 8
 from sqlalchemy.sql.expression import text
@@ -76,6 +76,10 @@ class Changelog(db.Model):
76 76
         addpeer               peerid peername
77 77
         removepeer            peerid peername
78 78
         editpeer              peerid {username: string, contact: string}
79
+
80
+        addannotation         itemid data
81
+        editannotation        itemid annotationid data
82
+        removeannotation      itemid annotationid
79 83
     '''
80 84
     __tablename__ = 'changelog'
81 85
     id = sa.Column(sa.Integer(), primary_key=True)
@@ -327,6 +331,27 @@ class Changelog(db.Model):
327 331
                 peer.save()
328 332
         return True
329 333
 
334
+    def action_addannotation(self, user, timestamp, itemid, data):
335
+        from annotation.models import Annotation
336
+        Annotation.create(item_id=itemid, user_id=user.id, data=data)
337
+        return True
338
+
339
+    def action_editannotation(self, user, timestamp, itemid, annotationid, data):
340
+        from annotation.models import Annotation
341
+        a = Annotation.get(user, itemid, annotationid)
342
+        if a:
343
+            for key in data:
344
+                a.data[key] = data[key]
345
+            a.save()
346
+        return True
347
+
348
+    def action_removeannotation(self, user, timestamp, itemid, annotationid):
349
+        from annotation.models import Annotation
350
+        a = Annotation.get(user, itemid, annotationid)
351
+        if a:
352
+            a.delete()
353
+        return True
354
+
330 355
     @classmethod
331 356
     def aggregated_changes(cls, since=None, user_id=None):
332 357
         from item.models import Item

+ 27
- 0
oml/library.py View File

@@ -190,6 +190,33 @@ class Peer(object):
190 190
             self.info['username'] = args[0]
191 191
         elif action == 'editcontact':
192 192
             self.info['contact'] = args[0]
193
+        elif action == 'addannotation':
194
+            from annotation.models import Annotation
195
+            if len(args) == 2:
196
+                itemid, data = args
197
+                Annotation.create(item_id=itemid, user_id=self.id, data=data)
198
+            else:
199
+                logger.error('invalid entry %s %s', action, args)
200
+        elif action == 'editannotation':
201
+            from annotation.models import Annotation
202
+            if len(args) == 3:
203
+                itemid, annotationid, data = args
204
+                a = Annotation.get(self.id, itemid, annotationid)
205
+                if a:
206
+                    for key in data:
207
+                        a.data[key] = data[key]
208
+                    a.save()
209
+            else:
210
+                logger.error('invalid entry %s %s', action, args)
211
+        elif action == 'removeannotation':
212
+            from annotation.models import Annotation
213
+            if len(args) == 2:
214
+                itemid, annotationid = args
215
+                a = Annotation.get(self.id, itemid, annotationid)
216
+                if a:
217
+                    a.delete()
218
+            else:
219
+                logger.error('invalid entry %s %s', action, args)
193 220
         else:
194 221
             logger.debug('UNKNOWN ACTION:', action)
195 222
         self.info['revision'] = revision

+ 2
- 2
oml/localnodes.py View File

@@ -152,8 +152,8 @@ class LocalNodes(dict):
152 152
             if state.tasks:
153 153
                 state.tasks.queue('removelocalinfo', id)
154 154
 
155
-    def get(self, user_id):
156
-        data = super().get(user_id)
155
+    def get_data(self, user_id):
156
+        data = self.get(user_id)
157 157
         if data and can_connect(data):
158 158
             return data
159 159
         return None

+ 5
- 2
oml/nodes.py View File

@@ -124,9 +124,12 @@ class Node(Thread):
124 124
             self.local = None
125 125
             self.port = 9851
126 126
 
127
+    def is_local(self):
128
+        return self._nodes and self.user_id in self._nodes.local
129
+
127 130
     def get_local(self):
128 131
         if self._nodes and self._nodes.local:
129
-            return self._nodes.local.get(self.user_id)
132
+            return self._nodes.local.get_data(self.user_id)
130 133
         return None
131 134
 
132 135
     def request(self, action, *args):
@@ -216,7 +219,7 @@ class Node(Thread):
216 219
         return False
217 220
 
218 221
     def is_online(self):
219
-        return self.online or self.get_local() is not None
222
+        return self.online or self.is_local()
220 223
 
221 224
     def send_response(self):
222 225
         self._q.put('send_response')

+ 12
- 1
oml/queryparser.py View File

@@ -116,7 +116,18 @@ class Parser(object):
116 116
         elif k == 'fulltext':
117 117
             ids = find_fulltext(v)
118 118
             return self.in_ids(ids, exclude)
119
-
119
+        elif k in ('notes', 'quotes'):
120
+            from annotation.models import Annotation
121
+            if isinstance(v, str):
122
+                v = unicodedata.normalize('NFKD', v).lower()
123
+            ids = set()
124
+            if k == 'notes':
125
+                qs = Annotation.query.filter(get_operator('=')(Annotation.findnotes, v))
126
+            elif k == 'quotes':
127
+                qs = Annotation.query.filter(get_operator('=')(Annotation.findquotes, v))
128
+            for a in qs:
129
+                ids.add(a.item_id)
130
+            return self.in_ids(ids, exclude)
120 131
         elif key_type in ("string", "text"):
121 132
             if isinstance(v, str):
122 133
                 v = unicodedata.normalize('NFKD', v).lower()

+ 1
- 1
oml/server.py View File

@@ -170,8 +170,8 @@ def run():
170 170
         import bandwidth
171 171
         state.bandwidth = bandwidth.Bandwidth()
172 172
         state.tor = tor.Tor()
173
-        state.node = node.server.start()
174 173
         state.nodes = nodes.Nodes()
174
+        state.node = node.server.start()
175 175
 
176 176
         def publish():
177 177
             if not state.tor.is_online():

+ 2
- 2
oml/settings.py View File

@@ -82,7 +82,7 @@ if 'modules' in release and 'openmedialibrary' in release['modules']:
82 82
 else:
83 83
     MINOR_VERSION = 'git'
84 84
 
85
-NODE_PROTOCOL = "0.8"
85
+NODE_PROTOCOL = "0.9"
86 86
 VERSION = "%s.%s" % (NODE_PROTOCOL, MINOR_VERSION)
87 87
 
88 88
 USER_AGENT = 'OpenMediaLibrary/%s' % VERSION
@@ -95,4 +95,4 @@ FULLTEXT_SUPPORT = fulltext.platform_supported()
95 95
 if not FULLTEXT_SUPPORT:
96 96
     config['itemKeys'] = [k for k in config['itemKeys'] if k['id'] != 'fulltext']
97 97
 
98
-DB_VERSION = 17
98
+DB_VERSION = 18

+ 16
- 0
oml/setup.py View File

@@ -151,6 +151,22 @@ CREATE TABLE listitem (
151 151
     FOREIGN KEY(item_id) REFERENCES item (id), 
152 152
     FOREIGN KEY(list_id) REFERENCES list (id)
153 153
 );
154
+CREATE TABLE annotation (
155
+    _id INTEGER NOT NULL,
156
+    id VARCHAR(43),
157
+    created DATETIME,
158
+    modified DATETIME,
159
+    user_id VARCHAR(43),
160
+    item_id VARCHAR(43),
161
+    data BLOB,
162
+    findquotes TEXT,
163
+    findnotes TEXT,
164
+    PRIMARY KEY (_id),
165
+    FOREIGN KEY(user_id) REFERENCES user (id),
166
+    FOREIGN KEY(item_id) REFERENCES item (id)
167
+);
168
+CREATE INDEX ix_annotation_findquotes ON annotation (findquotes);
169
+CREATE INDEX ix_annotation_findnotes ON annotation (findnotes);
154 170
 PRAGMA journal_mode=WAL
155 171
 '''
156 172
         for statement in sql.split(';'):

+ 24
- 0
oml/update.py View File

@@ -377,6 +377,8 @@ class Update(Thread):
377 377
                 db_version = migrate_16()
378 378
             if db_version < 17:
379 379
                 db_version = migrate_17()
380
+            if db_version < 18:
381
+                db_version = migrate_18()
380 382
             settings.server['db_version'] = db_version
381 383
 
382 384
     def run(self):
@@ -674,3 +676,25 @@ def migrate_17():
674 676
                 lists.append(l.name)
675 677
         add_record('orderlists', lists)
676 678
     return 17
679
+
680
+def migrate_18():
681
+    db.run_sql([
682
+        '''CREATE TABLE annotation (
683
+    _id INTEGER NOT NULL,
684
+    id VARCHAR(43),
685
+    created DATETIME,
686
+    modified DATETIME,
687
+    user_id VARCHAR(43),
688
+    item_id VARCHAR(43),
689
+    data BLOB,
690
+    findquotes TEXT,
691
+    findnotes TEXT,
692
+    PRIMARY KEY (_id),
693
+    FOREIGN KEY(user_id) REFERENCES user (id),
694
+    FOREIGN KEY(item_id) REFERENCES item (id)
695
+)'''])
696
+    db.run_sql([
697
+        'CREATE INDEX ix_annotation_findquotes ON annotation (findquotes)',
698
+        'CREATE INDEX ix_annotation_findnotes ON annotation (findnotes)'
699
+    ])
700
+    return 18

+ 1
- 1
oml/user/api.py View File

@@ -474,7 +474,7 @@ def removePeering(data):
474 474
     if len(data.get('id', '')) not in (16, 43):
475 475
         logger.debug('invalid user id')
476 476
         return {}
477
-    u = models.User.get(data['id'], for_udpate=True)
477
+    u = models.User.get(data['id'], for_update=True)
478 478
     if u:
479 479
         u.info['message'] = data.get('message', '')
480 480
         u.update_peering(False)

+ 3
- 0
oml/user/models.py View File

@@ -187,7 +187,9 @@ class User(db.Model):
187 187
 
188 188
     def cleanup(self):
189 189
         from item.models import user_items, Item
190
+        from annotation.models import Annotation
190 191
         List.query.filter_by(user_id=self.id).delete()
192
+        Annotation.query.filter_by(user_id=self.id).delete()
191 193
         c_user_id = user_items.columns['user_id']
192 194
         q = user_items.delete().where(c_user_id.is_(self.id))
193 195
         state.db.session.execute(q)
@@ -197,6 +199,7 @@ class User(db.Model):
197 199
             state.peers[self.id].remove()
198 200
             del state.peers[self.id]
199 201
 
202
+
200 203
     def update_name(self):
201 204
         if self.id == settings.USER_ID:
202 205
             name = settings.preferences.get('username', 'anonymous')

+ 10
- 2
static/js/annotation.js View File

@@ -46,14 +46,22 @@ oml.ui.annotation = function(annotation, $iframe) {
46 46
                 note.value = data.value
47 47
                 note.modified = (new Date).toISOString()
48 48
             } else {
49
-                annotation.notes.push({
49
+                annotation.notes.push(note = {
50 50
                     created: data.created || (new Date).toISOString(),
51 51
                     modified: (new Date).toISOString(),
52 52
                     id: data.id,
53
-                    user: '',
54 53
                     value: data.value
55 54
                 })
56 55
             }
56
+            oml.api.editNote({
57
+                item: oml.user.ui.item,
58
+                annotation: annotation.id,
59
+                notes: {
60
+                    created: note.created,
61
+                    modified: note.modified,
62
+                    value: note.value
63
+                }
64
+            })
57 65
             that.triggerEvent('change')
58 66
         }
59 67
     });

+ 17
- 0
static/js/annotationPanel.js View File

@@ -1,6 +1,7 @@
1 1
 'use strict';
2 2
 
3 3
 oml.ui.annotationPanel = function() {
4
+    var ui = oml.user.ui;
4 5
 
5 6
     var ui = oml.user.ui;
6 7
 
@@ -28,6 +29,7 @@ oml.ui.annotationPanel = function() {
28 29
         click: function() {
29 30
             var $annotation = oml.$ui.annotationFolder.find('.OMLAnnotation.selected')
30 31
             $annotation.length && $annotation.delete()
32
+            $deleteQuote.options({disabled: true})
31 33
         }
32 34
     }).appendTo($bar);
33 35
 
@@ -80,6 +82,21 @@ oml.ui.annotationPanel = function() {
80 82
                 }, function(result) {
81 83
                     oml.ui.exportAnnotationsDialog(result.data).open()
82 84
                 })
85
+            } else {
86
+                console.log('click', id, data)
87
+            }
88
+        },
89
+        change: function(data) {
90
+            var id = data.id;
91
+            console.log('change', data)
92
+            if (id == 'show') {
93
+                console.log('show', data)
94
+                oml.UI.set({annotationsShow: data.checked[0].id});
95
+            } else if (id == 'sort') {
96
+                console.log('sort', data)
97
+                oml.UI.set({annotationsSort: data.checked[0].id});
98
+            } else {
99
+                console.log('change', id, data)
83 100
             }
84 101
         }
85 102
     }).appendTo($bar);

+ 2
- 1
static/js/exportAnnotationsDialog.js View File

@@ -47,7 +47,8 @@ oml.ui.exportAnnotationsDialog = function(data) {
47 47
         var annotations = oml.$ui.viewer.getAnnotations()
48 48
         var text = 'Annotations for ' + data.title + ' (' + data.author.join(', ') + ')\n\n\n\n'
49 49
         text += annotations.map(function(annotation) {
50
-            var text = 'Quote:\n\n' + annotation.text
50
+            var page = annotation.pageLabel || annotation.page
51
+            var text = 'Quote' + (page ? ' Page ' + page : '' )+ ':\n\n' + annotation.text
51 52
             if (annotation.notes.length) {
52 53
                 text += '\n\nNotes:\n' + annotation.notes.map(function(note) {
53 54
                     return note.value

+ 101
- 35
static/js/viewer.js View File

@@ -15,6 +15,12 @@ oml.ui.viewer = function() {
15 15
                 },
16 16
                 oml_showannotations: function() {
17 17
                     panel.toggleElement(1);
18
+                },
19
+                oml_sortannotations: function(data) {
20
+                    that.renderAnnotations()
21
+                },
22
+                oml_annotationusers: function(data) {
23
+                    that.renderAnnotations()
18 24
                 }
19 25
             }),
20 26
         panel = Ox.SplitPanel({
@@ -40,38 +46,81 @@ oml.ui.viewer = function() {
40 46
         $iframe, item;
41 47
 
42 48
     function loadAnnotations(callback) {
43
-        annotations = JSON.parse(localStorage[item + '.annotations'] || '[]')
44
-        annotations.forEach(function(data) {
45
-            if (data.comments && !data.notes) {
46
-                data.notes = data.comments
47
-                delete data.comments
48
-            }
49
-            data.notes = data.notes || [];
50
-        })
51
-        callback && callback(annotations)
49
+        if (localStorage[item + '.annotations']) {
50
+            annotations = JSON.parse(localStorage[item + '.annotations'] || '[]')
51
+            var ids = []
52
+            annotations.forEach(function(data) {
53
+                if (data.comments && !data.notes) {
54
+                    data.notes = data.comments
55
+                    delete data.comments
56
+                }
57
+                if (!Ox.contains(ids, data.id)) {
58
+                    ids.push(data.id)
59
+                    var note
60
+                    if (data.notes && data.notes.length) {
61
+                        note = data.notes[0]
62
+                        delete data.notes
63
+                    }
64
+                    addAnnotation(data, false)
65
+                    if (note) {
66
+                        data.notes = [note]
67
+                    } else {
68
+                        data.notes = []
69
+                    }
70
+                    if (data.notes.length) {
71
+                        var note = data.notes[0]
72
+                        oml.api.editNote({
73
+                            item: ui.item,
74
+                            annotation: data.id,
75
+                            notes: {
76
+                                created: note.created,
77
+                                modified: note.modified,
78
+                                value: note.value
79
+                            }
80
+                        })
81
+                    }
82
+                } else {
83
+                    console.log('ignore second time', data)
84
+                }
85
+            })
86
+            localStorage[oml.user.ui.item + '.annotations_'] = localStorage[oml.user.ui.item + '.annotations']
87
+            delete localStorage[oml.user.ui.item + '.annotations']
88
+            callback && callback(annotations)
89
+        } else {
90
+            oml.api.getAnnotations({
91
+                id: ui.item
92
+            }, function(result) {
93
+                console.log(result)
94
+                annotations = result.data.annotations
95
+                callback && callback(annotations)
96
+            })
97
+        }
52 98
     }
53
-    function saveAnnotations(data) {
54
-        if (data) {
55
-            data.created = data.created || (new Date).toISOString();
56
-            if (data.comments && !data.notes) {
57
-                data.notes = data.comments
58
-                delete data.comments
59
-            }
60
-            data.notes = data.notes || [];
61
-            annotations.push(data);
99
+    function addAnnotation(data, save) {
100
+        var a = Ox.extend({}, data)
101
+        a.created = a.created || (new Date).toISOString();
102
+        a.item = ui.item
103
+        if (save !== false) {
104
+            oml.api.addAnnotation(a)
62 105
         }
63
-        localStorage[item + '.annotations'] = JSON.stringify(annotations)
106
+        data.notes = data.notes || [];
107
+        annotations.push(data);
64 108
     }
109
+
65 110
     function removeAnnotation(id) {
66
-        annotations = annotations.filter(function(annotation) {
67
-            return annotation.id != id
111
+        oml.api.removeAnnotation({
112
+            item: ui.item,
113
+            annotation: id
114
+        }, function(result) {
115
+            annotations = annotations.filter(function(annotation) {
116
+                return annotation.id != id
117
+            })
68 118
         })
69
-        saveAnnotations()
70 119
     }
71 120
 
72 121
     var annotationEvents = {
73
-        change: function() {
74
-            saveAnnotations()
122
+        change: function(data) {
123
+            // console.log('change', data)
75 124
         },
76 125
         'delete': function(data) {
77 126
             oml.$ui.annotationFolder.find('#a-' + data.id).remove()
@@ -95,14 +144,14 @@ oml.ui.viewer = function() {
95 144
                     }).onMessage(function(data, event) {
96 145
                         console.log('got', event, data)
97 146
                         if (event == 'addAnnotation') {
98
-                            saveAnnotations(data);
147
+                            addAnnotation(data);
99 148
                             var $annotation = oml.ui.annotation(data, $iframe).bindEvent(annotationEvents)
100 149
                             oml.$ui.annotationFolder.append($annotation);
101 150
                             $annotation.annotate();
102 151
                             oml.$ui.annotationPanel.updateSelection(false)
103 152
                         } else if (event == 'removeAnnotation') {
104 153
                             oml.$ui.annotationFolder.find('#a-' + data.id).remove()
105
-                            removeAnnotation(data.id)
154
+                            data.id && removeAnnotation(data.id)
106 155
                         } else if (event == 'selectAnnotation') {
107 156
                             if (data.id) {
108 157
                                 var $annotation = oml.$ui.annotationFolder.find('#a-' + data.id)
@@ -115,18 +164,13 @@ oml.ui.viewer = function() {
115 164
                         } else if (event == 'selectText') {
116 165
                             oml.$ui.annotationPanel.updateSelection(data)
117 166
                         } else {
167
+                            console.log('trigger unknwon event', event, data)
118 168
                             that.triggerEvent(event, data);
119 169
                         }
120 170
                     }).bindEvent({
121 171
                         init: function() {
122 172
                             loadAnnotations(function(annotations) {
123 173
                                 that.renderAnnotations()
124
-                                // fixme: trigger loaded event from reader instead?
125
-                                setTimeout(function() {
126
-                                    that.postMessage('addAnnotations', {
127
-                                        annotations: annotations
128
-                                    })
129
-                                }, 500)
130 174
                             })
131 175
                         }
132 176
                     }).appendTo(frame);
@@ -145,12 +189,34 @@ oml.ui.viewer = function() {
145 189
         return annotations;
146 190
     }
147 191
     that.renderAnnotations = function() {
192
+        var sortKey = ui.sortAnnotations
193
+        if (sortKey == 'date') {
194
+            sortKey = 'created'
195
+        }
196
+        if (sortKey == 'date') {
197
+            sortKey = 'created'
198
+        }
199
+        if (sortKey == 'quote') {
200
+            sortKey = 'text'
201
+        }
202
+        annotations = Ox.sortBy(annotations, sortKey)
148 203
         oml.$ui.annotationFolder.empty();
149
-        Ox.sortBy(annotations, ui.sortAnnotations);
204
+        var visibleAnnotations = [];
150 205
         annotations.forEach(function(data) {
151
-            var $annotation = oml.ui.annotation(data, $iframe).bindEvent(annotationEvents)
152
-            oml.$ui.annotationFolder.append($annotation);
206
+            //that.postMessage('removeAnnotation', {id: data.id})
207
+            if (ui.showAnnotationUsers == 'all' || data.user == oml.user.id) {
208
+                var $annotation = oml.ui.annotation(data, $iframe).bindEvent(annotationEvents)
209
+                oml.$ui.annotationFolder.append($annotation);
210
+                visibleAnnotations.push(data)
211
+            }
153 212
         })
213
+        // fixme: trigger loaded event from reader instead?
214
+        setTimeout(function() {
215
+            that.postMessage('addAnnotations', {
216
+                annotations: visibleAnnotations,
217
+                replace: true
218
+            })
219
+        }, 500)
154 220
     }
155 221
     return that.updateElement();
156 222
 };

+ 6
- 0
static/reader/epub.js View File

@@ -22,6 +22,12 @@ Ox.load({
22 22
         } else if (event == 'addAnnotation') {
23 23
             createAnnotation()
24 24
         } else if (event == 'addAnnotations') {
25
+            if (data.replace) {
26
+                annotations.forEach(function(a) {
27
+                    reader.rendition.annotations.remove(a.cfiRange)
28
+                })
29
+                annotations = []
30
+            }
25 31
             data.annotations.forEach(function(annotation) {
26 32
                 annotations.push(annotation)
27 33
                 renderAnnotation(annotation)

+ 11
- 3
static/reader/pdf.js View File

@@ -8,7 +8,6 @@ Ox.load({
8 8
     }
9 9
 }, function() {
10 10
     Ox.$parent.bindMessage(function(data, event) {
11
-        console.log('got', event, 'data', data)
12 11
         if (event == 'selectAnnotation') {
13 12
             var annotation = annotations.filter(function(a) { return a.id == data.id })[0]
14 13
             var delay = 0
@@ -20,7 +19,7 @@ Ox.load({
20 19
                 PDFViewerApplication.pdfViewer.currentPageNumber = annotation.page;
21 20
                 delay = 250
22 21
             }
23
-            setTimeout(function() {
22
+            annotation && setTimeout(function() {
24 23
                 var el = document.querySelector('.a' + annotation.id);
25 24
                 if (el && !isInView(el)) {
26 25
                     document.querySelector('#viewerContainer').scrollTop = el.offsetTop + el.parentElement.offsetTop - 64;
@@ -30,12 +29,20 @@ Ox.load({
30 29
         } else if (event == 'addAnnotation') {
31 30
             createAnnotation()
32 31
         } else if (event == 'addAnnotations') {
32
+            if (data.replace) {
33
+                document.querySelectorAll('.oml-annotation').forEach(function(a) {
34
+                    a.remove()
35
+                })
36
+                annotations = []
37
+            }
33 38
             data.annotations.forEach(function(annotation) {
34 39
                 annotations.push(annotation)
35 40
                 renderAnnotation(annotation)
36 41
             })
37 42
         } else if (event == 'removeAnnotation') {
38 43
             removeAnnotation(data.id)
44
+        } else {
45
+            console.log('got', event, 'data', data)
39 46
         }
40 47
     })
41 48
 })
@@ -96,6 +103,7 @@ function getHighlight() {
96 103
     var position = [pageNumber].concat(Ox.sort(selected.map(function(c) { return [c[1], c[0]]}))[0]);
97 104
     return {
98 105
         page: pageNumber,
106
+        pageLabel: PDFViewerApplication.pdfViewer.currentPageLabel,
99 107
         position: position,
100 108
         coords: selected,
101 109
         text: text,
@@ -176,7 +184,7 @@ function deselectAllAnnotations() {
176 184
         g.classList.remove('selected')
177 185
         g.style.backgroundColor = 'yellow'
178 186
         var id = $(g).parents('.oml-annotation').data('id')
179
-        console.log('deselect', g, id)
187
+        //console.log('deselect', g, id)
180 188
         if (!Ox.contains(ids, id)) {
181 189
             ids.push(id)
182 190
             Ox.$parent.postMessage('selectAnnotation', {id: null})