source: OKFNAnnotator (for Zope)/annotator_files/lib/annotator.js @ 3:6356e78ccf5c

Last change on this file since 3:6356e78ccf5c was 3:6356e78ccf5c, checked in by casties, 12 years ago

new version contains Annotator JS files to be used with FilesystemSite?.

File size: 13.8 KB
Line 
1var Annotator, util, _Annotator,
2  __bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; },
3  __hasProp = Object.prototype.hasOwnProperty,
4  __extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor; child.__super__ = parent.prototype; return child; };
5
6util = {
7  uuid: (function() {
8    var counter;
9    counter = 0;
10    return function() {
11      return counter++;
12    };
13  })(),
14  getGlobal: function() {
15    return (function() {
16      return this;
17    })();
18  },
19  mousePosition: function(e, offsetEl) {
20    var offset;
21    offset = $(offsetEl).offset();
22    return {
23      top: e.pageY - offset.top,
24      left: e.pageX - offset.left
25    };
26  },
27  preventEventDefault: function(event) {
28    return event != null ? typeof event.preventDefault === "function" ? event.preventDefault() : void 0 : void 0;
29  }
30};
31
32_Annotator = this.Annotator;
33
34Annotator = (function(_super) {
35
36  __extends(Annotator, _super);
37
38  Annotator.prototype.events = {
39    ".annotator-adder button click": "onAdderClick",
40    ".annotator-adder button mousedown": "onAdderMousedown",
41    ".annotator-hl mouseover": "onHighlightMouseover",
42    ".annotator-hl mouseout": "startViewerHideTimer"
43  };
44
45  Annotator.prototype.html = {
46    hl: '<span class="annotator-hl"></span>',
47    adder: '<div class="annotator-adder"><button>' + _t('Annotate') + '</button></div>',
48    wrapper: '<div class="annotator-wrapper"></div>'
49  };
50
51  Annotator.prototype.options = {
52    readOnly: false
53  };
54
55  Annotator.prototype.plugins = {};
56
57  Annotator.prototype.editor = null;
58
59  Annotator.prototype.viewer = null;
60
61  Annotator.prototype.selectedRanges = null;
62
63  Annotator.prototype.mouseIsDown = false;
64
65  Annotator.prototype.ignoreMouseup = false;
66
67  Annotator.prototype.viewerHideTimer = null;
68
69  function Annotator(element, options) {
70    this.onDeleteAnnotation = __bind(this.onDeleteAnnotation, this);
71    this.onEditAnnotation = __bind(this.onEditAnnotation, this);
72    this.onAdderClick = __bind(this.onAdderClick, this);
73    this.onAdderMousedown = __bind(this.onAdderMousedown, this);
74    this.onHighlightMouseover = __bind(this.onHighlightMouseover, this);
75    this.checkForEndSelection = __bind(this.checkForEndSelection, this);
76    this.checkForStartSelection = __bind(this.checkForStartSelection, this);
77    this.clearViewerHideTimer = __bind(this.clearViewerHideTimer, this);
78    this.startViewerHideTimer = __bind(this.startViewerHideTimer, this);
79    this.showViewer = __bind(this.showViewer, this);
80    this.onEditorSubmit = __bind(this.onEditorSubmit, this);
81    this.onEditorHide = __bind(this.onEditorHide, this);
82    this.showEditor = __bind(this.showEditor, this);
83    var name, src, _ref;
84    Annotator.__super__.constructor.apply(this, arguments);
85    this.plugins = {};
86    if (!Annotator.supported()) return this;
87    if (!this.options.readOnly) this._setupDocumentEvents();
88    this._setupWrapper()._setupViewer()._setupEditor();
89    _ref = this.html;
90    for (name in _ref) {
91      src = _ref[name];
92      if (name !== 'wrapper') this[name] = $(src).appendTo(this.wrapper).hide();
93    }
94  }
95
96  Annotator.prototype._setupWrapper = function() {
97    this.wrapper = $(this.html.wrapper);
98    this.element.find('script').remove();
99    this.element.wrapInner(this.wrapper);
100    this.wrapper = this.element.find('.annotator-wrapper');
101    return this;
102  };
103
104  Annotator.prototype._setupViewer = function() {
105    var _this = this;
106    this.viewer = new Annotator.Viewer({
107      readOnly: this.options.readOnly
108    });
109    this.viewer.hide().on("edit", this.onEditAnnotation).on("delete", this.onDeleteAnnotation).addField({
110      load: function(field, annotation) {
111        if (annotation.text) {
112          $(field).escape(annotation.text);
113        } else {
114          $(field).html("<i>" + (_t('No Comment')) + "</i>");
115        }
116        return _this.publish('annotationViewerTextField', [field, annotation]);
117      }
118    }).element.appendTo(this.wrapper).bind({
119      "mouseover": this.clearViewerHideTimer,
120      "mouseout": this.startViewerHideTimer
121    });
122    return this;
123  };
124
125  Annotator.prototype._setupEditor = function() {
126    this.editor = new Annotator.Editor();
127    this.editor.hide().on('hide', this.onEditorHide).on('save', this.onEditorSubmit).addField({
128      type: 'textarea',
129      label: _t('Comments') + '\u2026',
130      load: function(field, annotation) {
131        return $(field).find('textarea').val(annotation.text || '');
132      },
133      submit: function(field, annotation) {
134        return annotation.text = $(field).find('textarea').val();
135      }
136    });
137    this.editor.element.appendTo(this.wrapper);
138    return this;
139  };
140
141  Annotator.prototype._setupDocumentEvents = function() {
142    $(document).bind({
143      "mouseup": this.checkForEndSelection,
144      "mousedown": this.checkForStartSelection
145    });
146    return this;
147  };
148
149  Annotator.prototype.getSelectedRanges = function() {
150    var browserRange, i, normedRange, r, ranges, rangesToIgnore, selection, _i, _len;
151    selection = util.getGlobal().getSelection();
152    ranges = [];
153    rangesToIgnore = [];
154    if (!selection.isCollapsed) {
155      ranges = (function() {
156        var _ref, _results;
157        _results = [];
158        for (i = 0, _ref = selection.rangeCount; 0 <= _ref ? i < _ref : i > _ref; 0 <= _ref ? i++ : i--) {
159          r = selection.getRangeAt(i);
160          browserRange = new Range.BrowserRange(r);
161          normedRange = browserRange.normalize().limit(this.wrapper[0]);
162          if (normedRange === null) rangesToIgnore.push(r);
163          _results.push(normedRange);
164        }
165        return _results;
166      }).call(this);
167      selection.removeAllRanges();
168    }
169    for (_i = 0, _len = rangesToIgnore.length; _i < _len; _i++) {
170      r = rangesToIgnore[_i];
171      selection.addRange(r);
172    }
173    return $.grep(ranges, function(range) {
174      if (range) selection.addRange(range.toRange());
175      return range;
176    });
177  };
178
179  Annotator.prototype.createAnnotation = function() {
180    var annotation;
181    annotation = {};
182    this.publish('beforeAnnotationCreated', [annotation]);
183    return annotation;
184  };
185
186  Annotator.prototype.setupAnnotation = function(annotation, fireEvents) {
187    var normed, normedRanges, r, sniffed, _i, _len;
188    if (fireEvents == null) fireEvents = true;
189    annotation.ranges || (annotation.ranges = this.selectedRanges);
190    normedRanges = (function() {
191      var _i, _len, _ref, _results;
192      _ref = annotation.ranges;
193      _results = [];
194      for (_i = 0, _len = _ref.length; _i < _len; _i++) {
195        r = _ref[_i];
196        if (!(r != null)) continue;
197        sniffed = Range.sniff(r);
198        _results.push(sniffed.normalize(this.wrapper[0]));
199      }
200      return _results;
201    }).call(this);
202    normedRanges = $.grep(normedRanges, function(range) {
203      return range !== null;
204    });
205    annotation.quote = [];
206    annotation.ranges = [];
207    annotation.highlights = [];
208    for (_i = 0, _len = normedRanges.length; _i < _len; _i++) {
209      normed = normedRanges[_i];
210      annotation.quote.push($.trim(normed.text()));
211      annotation.ranges.push(normed.serialize(this.wrapper[0], '.annotator-hl'));
212      $.merge(annotation.highlights, this.highlightRange(normed));
213    }
214    annotation.quote = annotation.quote.join(' / ');
215    $(annotation.highlights).data('annotation', annotation);
216    if (fireEvents) this.publish('annotationCreated', [annotation]);
217    return annotation;
218  };
219
220  Annotator.prototype.updateAnnotation = function(annotation) {
221    this.publish('beforeAnnotationUpdated', [annotation]);
222    this.publish('annotationUpdated', [annotation]);
223    return annotation;
224  };
225
226  Annotator.prototype.deleteAnnotation = function(annotation) {
227    var h, _i, _len, _ref;
228    _ref = annotation.highlights;
229    for (_i = 0, _len = _ref.length; _i < _len; _i++) {
230      h = _ref[_i];
231      $(h).replaceWith(h.childNodes);
232    }
233    this.publish('annotationDeleted', [annotation]);
234    return annotation;
235  };
236
237  Annotator.prototype.loadAnnotations = function(annotations) {
238    var clone, loader,
239      _this = this;
240    if (annotations == null) annotations = [];
241    loader = function(annList) {
242      var n, now, _i, _len;
243      if (annList == null) annList = [];
244      now = annList.splice(0, 10);
245      for (_i = 0, _len = now.length; _i < _len; _i++) {
246        n = now[_i];
247        _this.setupAnnotation(n, false);
248      }
249      if (annList.length > 0) {
250        return setTimeout((function() {
251          return loader(annList);
252        }), 1);
253      } else {
254        return _this.publish('annotationsLoaded', [clone]);
255      }
256    };
257    clone = annotations.slice();
258    if (annotations.length) loader(annotations);
259    return this;
260  };
261
262  Annotator.prototype.dumpAnnotations = function() {
263    if (this.plugins['Store']) {
264      return this.plugins['Store'].dumpAnnotations();
265    } else {
266      return console.warn(_t("Can't dump annotations without Store plugin."));
267    }
268  };
269
270  Annotator.prototype.highlightRange = function(normedRange) {
271    var node, white, _i, _len, _ref, _results;
272    white = /^\s*$/;
273    _ref = normedRange.textNodes();
274    _results = [];
275    for (_i = 0, _len = _ref.length; _i < _len; _i++) {
276      node = _ref[_i];
277      if (!white.test(node.nodeValue)) {
278        _results.push($(node).wrapAll(this.hl).parent().show()[0]);
279      }
280    }
281    return _results;
282  };
283
284  Annotator.prototype.addPlugin = function(name, options) {
285    var klass, _base;
286    if (this.plugins[name]) {
287      console.error(_t("You cannot have more than one instance of any plugin."));
288    } else {
289      klass = Annotator.Plugin[name];
290      if (typeof klass === 'function') {
291        this.plugins[name] = new klass(this.element[0], options);
292        this.plugins[name].annotator = this;
293        if (typeof (_base = this.plugins[name]).pluginInit === "function") {
294          _base.pluginInit();
295        }
296      } else {
297        console.error(_t("Could not load ") + name + _t(" plugin. Have you included the appropriate <script> tag?"));
298      }
299    }
300    return this;
301  };
302
303  Annotator.prototype.showEditor = function(annotation, location) {
304    this.editor.element.css(location);
305    this.editor.load(annotation);
306    return this;
307  };
308
309  Annotator.prototype.onEditorHide = function() {
310    this.publish('annotationEditorHidden', [this.editor]);
311    return this.ignoreMouseup = false;
312  };
313
314  Annotator.prototype.onEditorSubmit = function(annotation) {
315    this.publish('annotationEditorSubmit', [this.editor, annotation]);
316    if (annotation.ranges === void 0) {
317      return this.setupAnnotation(annotation);
318    } else {
319      return this.updateAnnotation(annotation);
320    }
321  };
322
323  Annotator.prototype.showViewer = function(annotations, location) {
324    this.viewer.element.css(location);
325    this.viewer.load(annotations);
326    return this.publish('annotationViewerShown', [this.viewer, annotations]);
327  };
328
329  Annotator.prototype.startViewerHideTimer = function() {
330    if (!this.viewerHideTimer) {
331      return this.viewerHideTimer = setTimeout(this.viewer.hide, 250);
332    }
333  };
334
335  Annotator.prototype.clearViewerHideTimer = function() {
336    clearTimeout(this.viewerHideTimer);
337    return this.viewerHideTimer = false;
338  };
339
340  Annotator.prototype.checkForStartSelection = function(event) {
341    if (!(event && this.isAnnotator(event.target))) {
342      this.startViewerHideTimer();
343      return this.mouseIsDown = true;
344    }
345  };
346
347  Annotator.prototype.checkForEndSelection = function(event) {
348    var container, range, _i, _len, _ref;
349    this.mouseIsDown = false;
350    if (this.ignoreMouseup) return;
351    this.selectedRanges = this.getSelectedRanges();
352    _ref = this.selectedRanges;
353    for (_i = 0, _len = _ref.length; _i < _len; _i++) {
354      range = _ref[_i];
355      container = range.commonAncestor;
356      if (this.isAnnotator(container)) return;
357    }
358    if (event && this.selectedRanges.length) {
359      return this.adder.css(util.mousePosition(event, this.wrapper[0])).show();
360    } else {
361      return this.adder.hide();
362    }
363  };
364
365  Annotator.prototype.isAnnotator = function(element) {
366    return !!$(element).parents().andSelf().filter('[class^=annotator-]').not(this.wrapper).length;
367  };
368
369  Annotator.prototype.onHighlightMouseover = function(event) {
370    var annotations;
371    this.clearViewerHideTimer();
372    if (this.mouseIsDown || this.viewer.isShown()) return false;
373    annotations = $(event.target).parents('.annotator-hl').andSelf().map(function() {
374      return $(this).data("annotation");
375    });
376    return this.showViewer($.makeArray(annotations), util.mousePosition(event, this.wrapper[0]));
377  };
378
379  Annotator.prototype.onAdderMousedown = function(event) {
380    if (event != null) event.preventDefault();
381    return this.ignoreMouseup = true;
382  };
383
384  Annotator.prototype.onAdderClick = function(event) {
385    var position;
386    if (event != null) event.preventDefault();
387    position = this.adder.position();
388    this.adder.hide();
389    return this.showEditor(this.createAnnotation(), position);
390  };
391
392  Annotator.prototype.onEditAnnotation = function(annotation) {
393    var offset;
394    offset = this.viewer.element.position();
395    this.viewer.hide();
396    return this.showEditor(annotation, offset);
397  };
398
399  Annotator.prototype.onDeleteAnnotation = function(annotation) {
400    this.viewer.hide();
401    return this.deleteAnnotation(annotation);
402  };
403
404  return Annotator;
405
406})(Delegator);
407
408Annotator.Plugin = (function(_super) {
409
410  __extends(Plugin, _super);
411
412  function Plugin(element, options) {
413    Plugin.__super__.constructor.apply(this, arguments);
414  }
415
416  Plugin.prototype.pluginInit = function() {};
417
418  return Plugin;
419
420})(Delegator);
421
422Annotator.$ = $;
423
424Annotator.Delegator = Delegator;
425
426Annotator.Range = Range;
427
428Annotator._t = _t;
429
430Annotator.supported = function() {
431  return (function() {
432    return !!this.getSelection;
433  })();
434};
435
436Annotator.noConflict = function() {
437  util.getGlobal().Annotator = _Annotator;
438  return this;
439};
440
441$.plugin('annotator', Annotator);
442
443this.Annotator = Annotator;
Note: See TracBrowser for help on using the repository browser.