source: OKFNAnnotator (for Zope)/annotator_files/lib/annotator.js @ 4:6979313586cf

Last change on this file since 4:6979313586cf was 4:6979313586cf, checked in by casties, 12 years ago

new version of annotator.

File size: 17.1 KB
Line 
1// Generated by CoffeeScript 1.3.3
2var Annotator, g, util, _Annotator, _ref,
3  __bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; },
4  __hasProp = {}.hasOwnProperty,
5  __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; };
6
7util = {
8  uuid: (function() {
9    var counter;
10    counter = 0;
11    return function() {
12      return counter++;
13    };
14  })(),
15  getGlobal: function() {
16    return (function() {
17      return this;
18    })();
19  },
20  maxZIndex: function($elements) {
21    var all, el;
22    all = (function() {
23      var _i, _len, _results;
24      _results = [];
25      for (_i = 0, _len = $elements.length; _i < _len; _i++) {
26        el = $elements[_i];
27        if ($(el).css('position') === 'static') {
28          _results.push(-1);
29        } else {
30          _results.push(parseInt($(el).css('z-index'), 10) || -1);
31        }
32      }
33      return _results;
34    })();
35    return Math.max.apply(Math, all);
36  },
37  mousePosition: function(e, offsetEl) {
38    var offset;
39    offset = $(offsetEl).offset();
40    return {
41      top: e.pageY - offset.top,
42      left: e.pageX - offset.left
43    };
44  },
45  preventEventDefault: function(event) {
46    return event != null ? typeof event.preventDefault === "function" ? event.preventDefault() : void 0 : void 0;
47  }
48};
49
50_Annotator = this.Annotator;
51
52Annotator = (function(_super) {
53
54  __extends(Annotator, _super);
55
56  Annotator.prototype.events = {
57    ".annotator-adder button click": "onAdderClick",
58    ".annotator-adder button mousedown": "onAdderMousedown",
59    ".annotator-hl mouseover": "onHighlightMouseover",
60    ".annotator-hl mouseout": "startViewerHideTimer"
61  };
62
63  Annotator.prototype.html = {
64    adder: '<div class="annotator-adder"><button>' + _t('Annotate') + '</button></div>',
65    wrapper: '<div class="annotator-wrapper"></div>'
66  };
67
68  Annotator.prototype.options = {
69    readOnly: false
70  };
71
72  Annotator.prototype.plugins = {};
73
74  Annotator.prototype.editor = null;
75
76  Annotator.prototype.viewer = null;
77
78  Annotator.prototype.selectedRanges = null;
79
80  Annotator.prototype.mouseIsDown = false;
81
82  Annotator.prototype.ignoreMouseup = false;
83
84  Annotator.prototype.viewerHideTimer = null;
85
86  function Annotator(element, options) {
87    this.onDeleteAnnotation = __bind(this.onDeleteAnnotation, this);
88
89    this.onEditAnnotation = __bind(this.onEditAnnotation, this);
90
91    this.onAdderClick = __bind(this.onAdderClick, this);
92
93    this.onAdderMousedown = __bind(this.onAdderMousedown, this);
94
95    this.onHighlightMouseover = __bind(this.onHighlightMouseover, this);
96
97    this.checkForEndSelection = __bind(this.checkForEndSelection, this);
98
99    this.checkForStartSelection = __bind(this.checkForStartSelection, this);
100
101    this.clearViewerHideTimer = __bind(this.clearViewerHideTimer, this);
102
103    this.startViewerHideTimer = __bind(this.startViewerHideTimer, this);
104
105    this.showViewer = __bind(this.showViewer, this);
106
107    this.onEditorSubmit = __bind(this.onEditorSubmit, this);
108
109    this.onEditorHide = __bind(this.onEditorHide, this);
110
111    this.showEditor = __bind(this.showEditor, this);
112    Annotator.__super__.constructor.apply(this, arguments);
113    this.plugins = {};
114    if (!Annotator.supported()) {
115      return this;
116    }
117    if (!this.options.readOnly) {
118      this._setupDocumentEvents();
119    }
120    this._setupWrapper()._setupViewer()._setupEditor();
121    this._setupDynamicStyle();
122    this.adder = $(this.html.adder).appendTo(this.wrapper).hide();
123  }
124
125  Annotator.prototype._setupWrapper = function() {
126    this.wrapper = $(this.html.wrapper);
127    this.element.find('script').remove();
128    this.element.wrapInner(this.wrapper);
129    this.wrapper = this.element.find('.annotator-wrapper');
130    return this;
131  };
132
133  Annotator.prototype._setupViewer = function() {
134    var _this = this;
135    this.viewer = new Annotator.Viewer({
136      readOnly: this.options.readOnly
137    });
138    this.viewer.hide().on("edit", this.onEditAnnotation).on("delete", this.onDeleteAnnotation).addField({
139      load: function(field, annotation) {
140        if (annotation.text) {
141          $(field).escape(annotation.text);
142        } else {
143          $(field).html("<i>" + (_t('No Comment')) + "</i>");
144        }
145        return _this.publish('annotationViewerTextField', [field, annotation]);
146      }
147    }).element.appendTo(this.wrapper).bind({
148      "mouseover": this.clearViewerHideTimer,
149      "mouseout": this.startViewerHideTimer
150    });
151    return this;
152  };
153
154  Annotator.prototype._setupEditor = function() {
155    this.editor = new Annotator.Editor();
156    this.editor.hide().on('hide', this.onEditorHide).on('save', this.onEditorSubmit).addField({
157      type: 'textarea',
158      label: _t('Comments') + '\u2026',
159      load: function(field, annotation) {
160        return $(field).find('textarea').val(annotation.text || '');
161      },
162      submit: function(field, annotation) {
163        return annotation.text = $(field).find('textarea').val();
164      }
165    });
166    this.editor.element.appendTo(this.wrapper);
167    return this;
168  };
169
170  Annotator.prototype._setupDocumentEvents = function() {
171    $(document).bind({
172      "mouseup": this.checkForEndSelection,
173      "mousedown": this.checkForStartSelection
174    });
175    return this;
176  };
177
178  Annotator.prototype._setupDynamicStyle = function() {
179    var max, sel, style, x;
180    style = $('#annotator-dynamic-style');
181    if (!style.length) {
182      style = $('<style id="annotator-dynamic-style"></style>').appendTo(document.head);
183    }
184    sel = '*' + ((function() {
185      var _i, _len, _ref, _results;
186      _ref = ['adder', 'outer', 'notice', 'filter'];
187      _results = [];
188      for (_i = 0, _len = _ref.length; _i < _len; _i++) {
189        x = _ref[_i];
190        _results.push(":not(.annotator-" + x + ")");
191      }
192      return _results;
193    })()).join('');
194    max = util.maxZIndex($(document.body).find(sel));
195    max = Math.max(max, 1000);
196    style.text([".annotator-adder, .annotator-outer, .annotator-notice {", "  z-index: " + (max + 20) + ";", "}", ".annotator-filter {", "  z-index: " + (max + 10) + ";", "}"].join("\n"));
197    return this;
198  };
199
200  Annotator.prototype.getSelectedRanges = function() {
201    var browserRange, i, normedRange, r, ranges, rangesToIgnore, selection, _i, _len;
202    selection = util.getGlobal().getSelection();
203    ranges = [];
204    rangesToIgnore = [];
205    if (!selection.isCollapsed) {
206      ranges = (function() {
207        var _i, _ref, _results;
208        _results = [];
209        for (i = _i = 0, _ref = selection.rangeCount; 0 <= _ref ? _i < _ref : _i > _ref; i = 0 <= _ref ? ++_i : --_i) {
210          r = selection.getRangeAt(i);
211          browserRange = new Range.BrowserRange(r);
212          normedRange = browserRange.normalize().limit(this.wrapper[0]);
213          if (normedRange === null) {
214            rangesToIgnore.push(r);
215          }
216          _results.push(normedRange);
217        }
218        return _results;
219      }).call(this);
220      selection.removeAllRanges();
221    }
222    for (_i = 0, _len = rangesToIgnore.length; _i < _len; _i++) {
223      r = rangesToIgnore[_i];
224      selection.addRange(r);
225    }
226    return $.grep(ranges, function(range) {
227      if (range) {
228        selection.addRange(range.toRange());
229      }
230      return range;
231    });
232  };
233
234  Annotator.prototype.createAnnotation = function() {
235    var annotation;
236    annotation = {};
237    this.publish('beforeAnnotationCreated', [annotation]);
238    return annotation;
239  };
240
241  Annotator.prototype.setupAnnotation = function(annotation, fireEvents) {
242    var normed, normedRanges, r, root, _i, _j, _len, _len1, _ref;
243    if (fireEvents == null) {
244      fireEvents = true;
245    }
246    root = this.wrapper[0];
247    annotation.ranges || (annotation.ranges = this.selectedRanges);
248    normedRanges = [];
249    _ref = annotation.ranges;
250    for (_i = 0, _len = _ref.length; _i < _len; _i++) {
251      r = _ref[_i];
252      try {
253        normedRanges.push(Range.sniff(r).normalize(root));
254      } catch (e) {
255        if (e instanceof Range.RangeError) {
256          this.publish('rangeNormalizeFail', [annotation, r, e]);
257        } else {
258          throw e;
259        }
260      }
261    }
262    annotation.quote = [];
263    annotation.ranges = [];
264    annotation.highlights = [];
265    for (_j = 0, _len1 = normedRanges.length; _j < _len1; _j++) {
266      normed = normedRanges[_j];
267      annotation.quote.push($.trim(normed.text()));
268      annotation.ranges.push(normed.serialize(this.wrapper[0], '.annotator-hl'));
269      $.merge(annotation.highlights, this.highlightRange(normed));
270    }
271    annotation.quote = annotation.quote.join(' / ');
272    $(annotation.highlights).data('annotation', annotation);
273    if (fireEvents) {
274      this.publish('annotationCreated', [annotation]);
275    }
276    return annotation;
277  };
278
279  Annotator.prototype.updateAnnotation = function(annotation) {
280    this.publish('beforeAnnotationUpdated', [annotation]);
281    this.publish('annotationUpdated', [annotation]);
282    return annotation;
283  };
284
285  Annotator.prototype.deleteAnnotation = function(annotation) {
286    var h, _i, _len, _ref;
287    _ref = annotation.highlights;
288    for (_i = 0, _len = _ref.length; _i < _len; _i++) {
289      h = _ref[_i];
290      $(h).replaceWith(h.childNodes);
291    }
292    this.publish('annotationDeleted', [annotation]);
293    return annotation;
294  };
295
296  Annotator.prototype.loadAnnotations = function(annotations) {
297    var clone, loader,
298      _this = this;
299    if (annotations == null) {
300      annotations = [];
301    }
302    loader = function(annList) {
303      var n, now, _i, _len;
304      if (annList == null) {
305        annList = [];
306      }
307      now = annList.splice(0, 10);
308      for (_i = 0, _len = now.length; _i < _len; _i++) {
309        n = now[_i];
310        _this.setupAnnotation(n, false);
311      }
312      if (annList.length > 0) {
313        return setTimeout((function() {
314          return loader(annList);
315        }), 10);
316      } else {
317        return _this.publish('annotationsLoaded', [clone]);
318      }
319    };
320    clone = annotations.slice();
321    if (annotations.length) {
322      loader(annotations);
323    }
324    return this;
325  };
326
327  Annotator.prototype.dumpAnnotations = function() {
328    if (this.plugins['Store']) {
329      return this.plugins['Store'].dumpAnnotations();
330    } else {
331      return console.warn(_t("Can't dump annotations without Store plugin."));
332    }
333  };
334
335  Annotator.prototype.highlightRange = function(normedRange, cssClass) {
336    var hl, node, white, _i, _len, _ref, _results;
337    if (cssClass == null) {
338      cssClass = 'annotator-hl';
339    }
340    white = /^\s*$/;
341    hl = $("<span class='" + cssClass + "'></span>");
342    _ref = normedRange.textNodes();
343    _results = [];
344    for (_i = 0, _len = _ref.length; _i < _len; _i++) {
345      node = _ref[_i];
346      if (!white.test(node.nodeValue)) {
347        _results.push($(node).wrapAll(hl).parent().show()[0]);
348      }
349    }
350    return _results;
351  };
352
353  Annotator.prototype.highlightRanges = function(normedRanges, cssClass) {
354    var highlights, r, _i, _len;
355    if (cssClass == null) {
356      cssClass = 'annotator-hl';
357    }
358    highlights = [];
359    for (_i = 0, _len = normedRanges.length; _i < _len; _i++) {
360      r = normedRanges[_i];
361      $.merge(highlights, this.highlightRange(r, cssClass));
362    }
363    return highlights;
364  };
365
366  Annotator.prototype.addPlugin = function(name, options) {
367    var klass, _base;
368    if (this.plugins[name]) {
369      console.error(_t("You cannot have more than one instance of any plugin."));
370    } else {
371      klass = Annotator.Plugin[name];
372      if (typeof klass === 'function') {
373        this.plugins[name] = new klass(this.element[0], options);
374        this.plugins[name].annotator = this;
375        if (typeof (_base = this.plugins[name]).pluginInit === "function") {
376          _base.pluginInit();
377        }
378      } else {
379        console.error(_t("Could not load ") + name + _t(" plugin. Have you included the appropriate <script> tag?"));
380      }
381    }
382    return this;
383  };
384
385  Annotator.prototype.showEditor = function(annotation, location) {
386    this.editor.element.css(location);
387    this.editor.load(annotation);
388    this.publish('annotationEditorShown', [this.editor, annotation]);
389    return this;
390  };
391
392  Annotator.prototype.onEditorHide = function() {
393    this.publish('annotationEditorHidden', [this.editor]);
394    return this.ignoreMouseup = false;
395  };
396
397  Annotator.prototype.onEditorSubmit = function(annotation) {
398    this.publish('annotationEditorSubmit', [this.editor, annotation]);
399    if (annotation.ranges === void 0) {
400      return this.setupAnnotation(annotation);
401    } else {
402      return this.updateAnnotation(annotation);
403    }
404  };
405
406  Annotator.prototype.showViewer = function(annotations, location) {
407    this.viewer.element.css(location);
408    this.viewer.load(annotations);
409    return this.publish('annotationViewerShown', [this.viewer, annotations]);
410  };
411
412  Annotator.prototype.startViewerHideTimer = function() {
413    if (!this.viewerHideTimer) {
414      return this.viewerHideTimer = setTimeout(this.viewer.hide, 250);
415    }
416  };
417
418  Annotator.prototype.clearViewerHideTimer = function() {
419    clearTimeout(this.viewerHideTimer);
420    return this.viewerHideTimer = false;
421  };
422
423  Annotator.prototype.checkForStartSelection = function(event) {
424    if (!(event && this.isAnnotator(event.target))) {
425      this.startViewerHideTimer();
426      return this.mouseIsDown = true;
427    }
428  };
429
430  Annotator.prototype.checkForEndSelection = function(event) {
431    var container, range, _i, _len, _ref;
432    this.mouseIsDown = false;
433    if (this.ignoreMouseup) {
434      return;
435    }
436    this.selectedRanges = this.getSelectedRanges();
437    _ref = this.selectedRanges;
438    for (_i = 0, _len = _ref.length; _i < _len; _i++) {
439      range = _ref[_i];
440      container = range.commonAncestor;
441      if ($(container).hasClass('annotator-hl')) {
442        container = $(container).parents('[class^=annotator-hl]')[0];
443      }
444      if (this.isAnnotator(container)) {
445        return;
446      }
447    }
448    if (event && this.selectedRanges.length) {
449      return this.adder.css(util.mousePosition(event, this.wrapper[0])).show();
450    } else {
451      return this.adder.hide();
452    }
453  };
454
455  Annotator.prototype.isAnnotator = function(element) {
456    return !!$(element).parents().andSelf().filter('[class^=annotator-]').not(this.wrapper).length;
457  };
458
459  Annotator.prototype.onHighlightMouseover = function(event) {
460    var annotations;
461    this.clearViewerHideTimer();
462    if (this.mouseIsDown || this.viewer.isShown()) {
463      return false;
464    }
465    annotations = $(event.target).parents('.annotator-hl').andSelf().map(function() {
466      return $(this).data("annotation");
467    });
468    return this.showViewer($.makeArray(annotations), util.mousePosition(event, this.wrapper[0]));
469  };
470
471  Annotator.prototype.onAdderMousedown = function(event) {
472    if (event != null) {
473      event.preventDefault();
474    }
475    return this.ignoreMouseup = true;
476  };
477
478  Annotator.prototype.onAdderClick = function(event) {
479    var highlights, position, r, ranges;
480    if (event != null) {
481      event.preventDefault();
482    }
483    position = this.adder.position();
484    this.adder.hide();
485    if (this.selectedRanges && this.selectedRanges.length) {
486      ranges = (function() {
487        var _i, _len, _ref, _results;
488        _ref = this.selectedRanges;
489        _results = [];
490        for (_i = 0, _len = _ref.length; _i < _len; _i++) {
491          r = _ref[_i];
492          _results.push(Range.sniff(r).normalize());
493        }
494        return _results;
495      }).call(this);
496      highlights = this.highlightRanges(ranges, 'annotator-hl annotator-hl-temporary');
497      this.editor.element.one('hide', function() {
498        var h, _i, _len, _results;
499        _results = [];
500        for (_i = 0, _len = highlights.length; _i < _len; _i++) {
501          h = highlights[_i];
502          _results.push($(h).replaceWith(h.childNodes));
503        }
504        return _results;
505      });
506    }
507    return this.showEditor(this.createAnnotation(), position);
508  };
509
510  Annotator.prototype.onEditAnnotation = function(annotation) {
511    var offset;
512    offset = this.viewer.element.position();
513    this.viewer.hide();
514    return this.showEditor(annotation, offset);
515  };
516
517  Annotator.prototype.onDeleteAnnotation = function(annotation) {
518    this.viewer.hide();
519    return this.deleteAnnotation(annotation);
520  };
521
522  return Annotator;
523
524})(Delegator);
525
526Annotator.Plugin = (function(_super) {
527
528  __extends(Plugin, _super);
529
530  function Plugin(element, options) {
531    Plugin.__super__.constructor.apply(this, arguments);
532  }
533
534  Plugin.prototype.pluginInit = function() {};
535
536  return Plugin;
537
538})(Delegator);
539
540g = util.getGlobal();
541
542if (!(((_ref = g.document) != null ? _ref.evaluate : void 0) != null)) {
543  $.getScript('http://assets.annotateit.org/vendor/xpath.min.js');
544}
545
546if (!(g.getSelection != null)) {
547  $.getScript('http://assets.annotateit.org/vendor/ierange.min.js');
548}
549
550if (!(g.JSON != null)) {
551  $.getScript('http://assets.annotateit.org/vendor/json2.min.js');
552}
553
554Annotator.$ = $;
555
556Annotator.Delegator = Delegator;
557
558Annotator.Range = Range;
559
560Annotator._t = _t;
561
562Annotator.supported = function() {
563  return (function() {
564    return !!this.getSelection;
565  })();
566};
567
568Annotator.noConflict = function() {
569  util.getGlobal().Annotator = _Annotator;
570  return this;
571};
572
573$.plugin('annotator', Annotator);
574
575this.Annotator = Annotator;
Note: See TracBrowser for help on using the repository browser.