comparison annotator_files/lib/annotator.js @ 3:6356e78ccf5c

new version contains Annotator JS files to be used with FilesystemSite.
author casties
date Thu, 05 Apr 2012 19:37:27 +0200
parents
children 6979313586cf
comparison
equal deleted inserted replaced
2:4c6c8835fc5c 3:6356e78ccf5c
1 var 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
6 util = {
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
34 Annotator = (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
408 Annotator.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
422 Annotator.$ = $;
423
424 Annotator.Delegator = Delegator;
425
426 Annotator.Range = Range;
427
428 Annotator._t = _t;
429
430 Annotator.supported = function() {
431 return (function() {
432 return !!this.getSelection;
433 })();
434 };
435
436 Annotator.noConflict = function() {
437 util.getGlobal().Annotator = _Annotator;
438 return this;
439 };
440
441 $.plugin('annotator', Annotator);
442
443 this.Annotator = Annotator;