Mercurial > hg > digilib-old
comparison webapp/src/main/webapp/jquery/annotator-dl.js @ 1146:94a9c2379ebb
new shape annotations work now.
author | robcast |
---|---|
date | Fri, 23 Nov 2012 11:33:08 +0100 |
parents | |
children |
comparison
equal
deleted
inserted
replaced
1145:dc66ab520dae | 1146:94a9c2379ebb |
---|---|
1 /* | |
2 ** Annotator 1.2.5-dev-a4cd304 | |
3 ** https://github.com/okfn/annotator/ | |
4 ** | |
5 ** Copyright 2012 Aron Carroll, Rufus Pollock, and Nick Stenning. | |
6 ** Dual licensed under the MIT and GPLv3 licenses. | |
7 ** https://github.com/okfn/annotator/blob/master/LICENSE | |
8 ** | |
9 ** Built at: 2012-11-23 09:46:08Z | |
10 */ | |
11 | |
12 | |
13 (function() { | |
14 var $, Annotator, Delegator, LinkParser, Range, base64Decode, base64UrlDecode, createDateFromISO8601, fn, functions, g, gettext, parseToken, util, _Annotator, _gettext, _i, _j, _len, _len1, _ref, _ref1, _t, | |
15 __slice = [].slice, | |
16 __hasProp = {}.hasOwnProperty, | |
17 __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; }, | |
18 __bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; }, | |
19 __indexOf = [].indexOf || function(item) { for (var i = 0, l = this.length; i < l; i++) { if (i in this && this[i] === item) return i; } return -1; }; | |
20 | |
21 gettext = null; | |
22 | |
23 if (typeof Gettext !== "undefined" && Gettext !== null) { | |
24 _gettext = new Gettext({ | |
25 domain: "annotator" | |
26 }); | |
27 gettext = function(msgid) { | |
28 return _gettext.gettext(msgid); | |
29 }; | |
30 } else { | |
31 gettext = function(msgid) { | |
32 return msgid; | |
33 }; | |
34 } | |
35 | |
36 _t = function(msgid) { | |
37 return gettext(msgid); | |
38 }; | |
39 | |
40 if (!(typeof jQuery !== "undefined" && jQuery !== null ? (_ref = jQuery.fn) != null ? _ref.jquery : void 0 : void 0)) { | |
41 console.error(_t("Annotator requires jQuery: have you included lib/vendor/jquery.js?")); | |
42 } | |
43 | |
44 if (!(JSON && JSON.parse && JSON.stringify)) { | |
45 console.error(_t("Annotator requires a JSON implementation: have you included lib/vendor/json2.js?")); | |
46 } | |
47 | |
48 $ = jQuery.sub(); | |
49 | |
50 $.flatten = function(array) { | |
51 var flatten; | |
52 flatten = function(ary) { | |
53 var el, flat, _i, _len; | |
54 flat = []; | |
55 for (_i = 0, _len = ary.length; _i < _len; _i++) { | |
56 el = ary[_i]; | |
57 flat = flat.concat(el && $.isArray(el) ? flatten(el) : el); | |
58 } | |
59 return flat; | |
60 }; | |
61 return flatten(array); | |
62 }; | |
63 | |
64 $.plugin = function(name, object) { | |
65 return jQuery.fn[name] = function(options) { | |
66 var args; | |
67 args = Array.prototype.slice.call(arguments, 1); | |
68 return this.each(function() { | |
69 var instance; | |
70 instance = $.data(this, name); | |
71 if (instance) { | |
72 return options && instance[options].apply(instance, args); | |
73 } else { | |
74 instance = new object(this, options); | |
75 return $.data(this, name, instance); | |
76 } | |
77 }); | |
78 }; | |
79 }; | |
80 | |
81 $.fn.textNodes = function() { | |
82 var getTextNodes; | |
83 getTextNodes = function(node) { | |
84 var nodes; | |
85 if (node && node.nodeType !== 3) { | |
86 nodes = []; | |
87 if (node.nodeType !== 8) { | |
88 node = node.lastChild; | |
89 while (node) { | |
90 nodes.push(getTextNodes(node)); | |
91 node = node.previousSibling; | |
92 } | |
93 } | |
94 return nodes.reverse(); | |
95 } else { | |
96 return node; | |
97 } | |
98 }; | |
99 return this.map(function() { | |
100 return $.flatten(getTextNodes(this)); | |
101 }); | |
102 }; | |
103 | |
104 $.fn.xpath = function(relativeRoot) { | |
105 var jq; | |
106 jq = this.map(function() { | |
107 var elem, idx, path; | |
108 path = ''; | |
109 elem = this; | |
110 while (elem && elem.nodeType === 1 && elem !== relativeRoot) { | |
111 idx = $(elem.parentNode).children(elem.tagName).index(elem) + 1; | |
112 idx = "[" + idx + "]"; | |
113 path = "/" + elem.tagName.toLowerCase() + idx + path; | |
114 elem = elem.parentNode; | |
115 } | |
116 return path; | |
117 }); | |
118 return jq.get(); | |
119 }; | |
120 | |
121 $.escape = function(html) { | |
122 return html.replace(/&(?!\w+;)/g, '&').replace(/</g, '<').replace(/>/g, '>').replace(/"/g, '"'); | |
123 }; | |
124 | |
125 $.fn.escape = function(html) { | |
126 if (arguments.length) { | |
127 return this.html($.escape(html)); | |
128 } | |
129 return this.html(); | |
130 }; | |
131 | |
132 $.fn.reverse = []._reverse || [].reverse; | |
133 | |
134 functions = ["log", "debug", "info", "warn", "exception", "assert", "dir", "dirxml", "trace", "group", "groupEnd", "groupCollapsed", "time", "timeEnd", "profile", "profileEnd", "count", "clear", "table", "error", "notifyFirebug", "firebug", "userObjects"]; | |
135 | |
136 if (typeof console !== "undefined" && console !== null) { | |
137 if (!(console.group != null)) { | |
138 console.group = function(name) { | |
139 return console.log("GROUP: ", name); | |
140 }; | |
141 } | |
142 if (!(console.groupCollapsed != null)) { | |
143 console.groupCollapsed = console.group; | |
144 } | |
145 for (_i = 0, _len = functions.length; _i < _len; _i++) { | |
146 fn = functions[_i]; | |
147 if (!(console[fn] != null)) { | |
148 console[fn] = function() { | |
149 return console.log(_t("Not implemented:") + (" console." + name)); | |
150 }; | |
151 } | |
152 } | |
153 } else { | |
154 this.console = {}; | |
155 for (_j = 0, _len1 = functions.length; _j < _len1; _j++) { | |
156 fn = functions[_j]; | |
157 this.console[fn] = function() {}; | |
158 } | |
159 this.console['error'] = function() { | |
160 var args; | |
161 args = 1 <= arguments.length ? __slice.call(arguments, 0) : []; | |
162 return alert("ERROR: " + (args.join(', '))); | |
163 }; | |
164 this.console['warn'] = function() { | |
165 var args; | |
166 args = 1 <= arguments.length ? __slice.call(arguments, 0) : []; | |
167 return alert("WARNING: " + (args.join(', '))); | |
168 }; | |
169 } | |
170 | |
171 Delegator = (function() { | |
172 | |
173 Delegator.prototype.events = {}; | |
174 | |
175 Delegator.prototype.options = {}; | |
176 | |
177 Delegator.prototype.element = null; | |
178 | |
179 function Delegator(element, options) { | |
180 this.options = $.extend(true, {}, this.options, options); | |
181 this.element = $(element); | |
182 this.on = this.subscribe; | |
183 this.addEvents(); | |
184 } | |
185 | |
186 Delegator.prototype.addEvents = function() { | |
187 var event, functionName, sel, selector, _k, _ref1, _ref2, _results; | |
188 _ref1 = this.events; | |
189 _results = []; | |
190 for (sel in _ref1) { | |
191 functionName = _ref1[sel]; | |
192 _ref2 = sel.split(' '), selector = 2 <= _ref2.length ? __slice.call(_ref2, 0, _k = _ref2.length - 1) : (_k = 0, []), event = _ref2[_k++]; | |
193 _results.push(this.addEvent(selector.join(' '), event, functionName)); | |
194 } | |
195 return _results; | |
196 }; | |
197 | |
198 Delegator.prototype.addEvent = function(bindTo, event, functionName) { | |
199 var closure, isBlankSelector, | |
200 _this = this; | |
201 closure = function() { | |
202 return _this[functionName].apply(_this, arguments); | |
203 }; | |
204 isBlankSelector = typeof bindTo === 'string' && bindTo.replace(/\s+/g, '') === ''; | |
205 if (isBlankSelector) { | |
206 bindTo = this.element; | |
207 } | |
208 if (typeof bindTo === 'string') { | |
209 this.element.delegate(bindTo, event, closure); | |
210 } else { | |
211 if (this.isCustomEvent(event)) { | |
212 this.subscribe(event, closure); | |
213 } else { | |
214 $(bindTo).bind(event, closure); | |
215 } | |
216 } | |
217 return this; | |
218 }; | |
219 | |
220 Delegator.prototype.isCustomEvent = function(event) { | |
221 event = event.split('.')[0]; | |
222 return $.inArray(event, Delegator.natives) === -1; | |
223 }; | |
224 | |
225 Delegator.prototype.publish = function() { | |
226 this.element.triggerHandler.apply(this.element, arguments); | |
227 return this; | |
228 }; | |
229 | |
230 Delegator.prototype.subscribe = function(event, callback) { | |
231 var closure; | |
232 closure = function() { | |
233 return callback.apply(this, [].slice.call(arguments, 1)); | |
234 }; | |
235 closure.guid = callback.guid = ($.guid += 1); | |
236 this.element.bind(event, closure); | |
237 return this; | |
238 }; | |
239 | |
240 Delegator.prototype.unsubscribe = function() { | |
241 this.element.unbind.apply(this.element, arguments); | |
242 return this; | |
243 }; | |
244 | |
245 return Delegator; | |
246 | |
247 })(); | |
248 | |
249 Delegator.natives = (function() { | |
250 var key, specials, val; | |
251 specials = (function() { | |
252 var _ref1, _results; | |
253 _ref1 = jQuery.event.special; | |
254 _results = []; | |
255 for (key in _ref1) { | |
256 if (!__hasProp.call(_ref1, key)) continue; | |
257 val = _ref1[key]; | |
258 _results.push(key); | |
259 } | |
260 return _results; | |
261 })(); | |
262 return "blur focus focusin focusout load resize scroll unload click dblclick\nmousedown mouseup mousemove mouseover mouseout mouseenter mouseleave\nchange select submit keydown keypress keyup error".split(/[^a-z]+/).concat(specials); | |
263 })(); | |
264 | |
265 Range = {}; | |
266 | |
267 Range.sniff = function(r) { | |
268 if (r.commonAncestorContainer != null) { | |
269 return new Range.BrowserRange(r); | |
270 } else if (typeof r.start === "string") { | |
271 return new Range.SerializedRange(r); | |
272 } else if (r.start && typeof r.start === "object") { | |
273 return new Range.NormalizedRange(r); | |
274 } else { | |
275 console.error(_t("Could not sniff range type")); | |
276 return false; | |
277 } | |
278 }; | |
279 | |
280 Range.nodeFromXPath = function(xpath, root) { | |
281 var customResolver, evaluateXPath, namespace, node, segment; | |
282 if (root == null) { | |
283 root = document; | |
284 } | |
285 evaluateXPath = function(xp, nsResolver) { | |
286 if (nsResolver == null) { | |
287 nsResolver = null; | |
288 } | |
289 return document.evaluate('.' + xp, root, nsResolver, XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue; | |
290 }; | |
291 if (!$.isXMLDoc(document.documentElement)) { | |
292 return evaluateXPath(xpath); | |
293 } else { | |
294 customResolver = document.createNSResolver(document.ownerDocument === null ? document.documentElement : document.ownerDocument.documentElement); | |
295 node = evaluateXPath(xpath, customResolver); | |
296 if (!node) { | |
297 xpath = ((function() { | |
298 var _k, _len2, _ref1, _results; | |
299 _ref1 = xpath.split('/'); | |
300 _results = []; | |
301 for (_k = 0, _len2 = _ref1.length; _k < _len2; _k++) { | |
302 segment = _ref1[_k]; | |
303 if (segment && segment.indexOf(':') === -1) { | |
304 _results.push(segment.replace(/^([a-z]+)/, 'xhtml:$1')); | |
305 } else { | |
306 _results.push(segment); | |
307 } | |
308 } | |
309 return _results; | |
310 })()).join('/'); | |
311 namespace = document.lookupNamespaceURI(null); | |
312 customResolver = function(ns) { | |
313 if (ns === 'xhtml') { | |
314 return namespace; | |
315 } else { | |
316 return document.documentElement.getAttribute('xmlns:' + ns); | |
317 } | |
318 }; | |
319 node = evaluateXPath(xpath, customResolver); | |
320 } | |
321 return node; | |
322 } | |
323 }; | |
324 | |
325 Range.RangeError = (function(_super) { | |
326 | |
327 __extends(RangeError, _super); | |
328 | |
329 function RangeError(type, message, parent) { | |
330 this.type = type; | |
331 this.message = message; | |
332 this.parent = parent != null ? parent : null; | |
333 RangeError.__super__.constructor.call(this, this.message); | |
334 } | |
335 | |
336 return RangeError; | |
337 | |
338 })(Error); | |
339 | |
340 Range.BrowserRange = (function() { | |
341 | |
342 function BrowserRange(obj) { | |
343 this.commonAncestorContainer = obj.commonAncestorContainer; | |
344 this.startContainer = obj.startContainer; | |
345 this.startOffset = obj.startOffset; | |
346 this.endContainer = obj.endContainer; | |
347 this.endOffset = obj.endOffset; | |
348 } | |
349 | |
350 BrowserRange.prototype.normalize = function(root) { | |
351 var it, node, nr, offset, p, r, _k, _len2, _ref1; | |
352 if (this.tainted) { | |
353 console.error(_t("You may only call normalize() once on a BrowserRange!")); | |
354 return false; | |
355 } else { | |
356 this.tainted = true; | |
357 } | |
358 r = {}; | |
359 nr = {}; | |
360 _ref1 = ['start', 'end']; | |
361 for (_k = 0, _len2 = _ref1.length; _k < _len2; _k++) { | |
362 p = _ref1[_k]; | |
363 node = this[p + 'Container']; | |
364 offset = this[p + 'Offset']; | |
365 if (!((node != null) && (offset != null))) { | |
366 return false; | |
367 } | |
368 if (node.nodeType === 1) { | |
369 it = node.childNodes[offset]; | |
370 node = it || node.childNodes[offset - 1]; | |
371 if (node.nodeType === 1 && !node.firstChild) { | |
372 it = null; | |
373 node = node.previousSibling; | |
374 } | |
375 while (node.nodeType !== 3) { | |
376 node = node.firstChild; | |
377 } | |
378 offset = it ? 0 : node.nodeValue.length; | |
379 } | |
380 r[p] = node; | |
381 r[p + 'Offset'] = offset; | |
382 } | |
383 nr.start = r.startOffset > 0 ? r.start.splitText(r.startOffset) : r.start; | |
384 if (r.start === r.end) { | |
385 if ((r.endOffset - r.startOffset) < nr.start.nodeValue.length) { | |
386 nr.start.splitText(r.endOffset - r.startOffset); | |
387 } | |
388 nr.end = nr.start; | |
389 } else { | |
390 if (r.endOffset < r.end.nodeValue.length) { | |
391 r.end.splitText(r.endOffset); | |
392 } | |
393 nr.end = r.end; | |
394 } | |
395 nr.commonAncestor = this.commonAncestorContainer; | |
396 while (nr.commonAncestor.nodeType !== 1) { | |
397 nr.commonAncestor = nr.commonAncestor.parentNode; | |
398 } | |
399 return new Range.NormalizedRange(nr); | |
400 }; | |
401 | |
402 BrowserRange.prototype.serialize = function(root, ignoreSelector) { | |
403 return this.normalize(root).serialize(root, ignoreSelector); | |
404 }; | |
405 | |
406 return BrowserRange; | |
407 | |
408 })(); | |
409 | |
410 Range.NormalizedRange = (function() { | |
411 | |
412 function NormalizedRange(obj) { | |
413 this.commonAncestor = obj.commonAncestor; | |
414 this.start = obj.start; | |
415 this.end = obj.end; | |
416 } | |
417 | |
418 NormalizedRange.prototype.normalize = function(root) { | |
419 return this; | |
420 }; | |
421 | |
422 NormalizedRange.prototype.limit = function(bounds) { | |
423 var nodes, parent, startParents, _k, _len2, _ref1; | |
424 nodes = $.grep(this.textNodes(), function(node) { | |
425 return node.parentNode === bounds || $.contains(bounds, node.parentNode); | |
426 }); | |
427 if (!nodes.length) { | |
428 return null; | |
429 } | |
430 this.start = nodes[0]; | |
431 this.end = nodes[nodes.length - 1]; | |
432 startParents = $(this.start).parents(); | |
433 _ref1 = $(this.end).parents(); | |
434 for (_k = 0, _len2 = _ref1.length; _k < _len2; _k++) { | |
435 parent = _ref1[_k]; | |
436 if (startParents.index(parent) !== -1) { | |
437 this.commonAncestor = parent; | |
438 break; | |
439 } | |
440 } | |
441 return this; | |
442 }; | |
443 | |
444 NormalizedRange.prototype.serialize = function(root, ignoreSelector) { | |
445 var end, serialization, start; | |
446 serialization = function(node, isEnd) { | |
447 var n, nodes, offset, origParent, textNodes, xpath, _k, _len2; | |
448 if (ignoreSelector) { | |
449 origParent = $(node).parents(":not(" + ignoreSelector + ")").eq(0); | |
450 } else { | |
451 origParent = $(node).parent(); | |
452 } | |
453 xpath = origParent.xpath(root)[0]; | |
454 textNodes = origParent.textNodes(); | |
455 nodes = textNodes.slice(0, textNodes.index(node)); | |
456 offset = 0; | |
457 for (_k = 0, _len2 = nodes.length; _k < _len2; _k++) { | |
458 n = nodes[_k]; | |
459 offset += n.nodeValue.length; | |
460 } | |
461 if (isEnd) { | |
462 return [xpath, offset + node.nodeValue.length]; | |
463 } else { | |
464 return [xpath, offset]; | |
465 } | |
466 }; | |
467 start = serialization(this.start); | |
468 end = serialization(this.end, true); | |
469 return new Range.SerializedRange({ | |
470 start: start[0], | |
471 end: end[0], | |
472 startOffset: start[1], | |
473 endOffset: end[1] | |
474 }); | |
475 }; | |
476 | |
477 NormalizedRange.prototype.text = function() { | |
478 var node; | |
479 return ((function() { | |
480 var _k, _len2, _ref1, _results; | |
481 _ref1 = this.textNodes(); | |
482 _results = []; | |
483 for (_k = 0, _len2 = _ref1.length; _k < _len2; _k++) { | |
484 node = _ref1[_k]; | |
485 _results.push(node.nodeValue); | |
486 } | |
487 return _results; | |
488 }).call(this)).join(''); | |
489 }; | |
490 | |
491 NormalizedRange.prototype.textNodes = function() { | |
492 var end, start, textNodes, _ref1; | |
493 textNodes = $(this.commonAncestor).textNodes(); | |
494 _ref1 = [textNodes.index(this.start), textNodes.index(this.end)], start = _ref1[0], end = _ref1[1]; | |
495 return $.makeArray(textNodes.slice(start, end + 1 || 9e9)); | |
496 }; | |
497 | |
498 NormalizedRange.prototype.toRange = function() { | |
499 var range; | |
500 range = document.createRange(); | |
501 range.setStartBefore(this.start); | |
502 range.setEndAfter(this.end); | |
503 return range; | |
504 }; | |
505 | |
506 return NormalizedRange; | |
507 | |
508 })(); | |
509 | |
510 Range.SerializedRange = (function() { | |
511 | |
512 function SerializedRange(obj) { | |
513 this.start = obj.start; | |
514 this.startOffset = obj.startOffset; | |
515 this.end = obj.end; | |
516 this.endOffset = obj.endOffset; | |
517 } | |
518 | |
519 SerializedRange.prototype.normalize = function(root) { | |
520 var contains, length, node, p, range, tn, _k, _l, _len2, _len3, _ref1, _ref2; | |
521 range = {}; | |
522 _ref1 = ['start', 'end']; | |
523 for (_k = 0, _len2 = _ref1.length; _k < _len2; _k++) { | |
524 p = _ref1[_k]; | |
525 try { | |
526 node = Range.nodeFromXPath(this[p], root); | |
527 } catch (e) { | |
528 throw new Range.RangeError(p, ("Error while finding " + p + " node: " + this[p] + ": ") + e, e); | |
529 } | |
530 if (!node) { | |
531 throw new Range.RangeError(p, "Couldn't find " + p + " node: " + this[p]); | |
532 } | |
533 length = 0; | |
534 _ref2 = $(node).textNodes(); | |
535 for (_l = 0, _len3 = _ref2.length; _l < _len3; _l++) { | |
536 tn = _ref2[_l]; | |
537 if (length + tn.nodeValue.length >= this[p + 'Offset']) { | |
538 range[p + 'Container'] = tn; | |
539 range[p + 'Offset'] = this[p + 'Offset'] - length; | |
540 break; | |
541 } else { | |
542 length += tn.nodeValue.length; | |
543 } | |
544 } | |
545 if (!(range[p + 'Offset'] != null)) { | |
546 throw new Range.RangeError("" + p + "offset", "Couldn't find offset " + this[p + 'Offset'] + " in element " + this[p]); | |
547 } | |
548 } | |
549 contains = !(document.compareDocumentPosition != null) ? function(a, b) { | |
550 return a.contains(b); | |
551 } : function(a, b) { | |
552 return a.compareDocumentPosition(b) & 16; | |
553 }; | |
554 $(range.startContainer).parents().reverse().each(function() { | |
555 if (contains(this, range.endContainer)) { | |
556 range.commonAncestorContainer = this; | |
557 return false; | |
558 } | |
559 }); | |
560 return new Range.BrowserRange(range).normalize(root); | |
561 }; | |
562 | |
563 SerializedRange.prototype.serialize = function(root, ignoreSelector) { | |
564 return this.normalize(root).serialize(root, ignoreSelector); | |
565 }; | |
566 | |
567 SerializedRange.prototype.toObject = function() { | |
568 return { | |
569 start: this.start, | |
570 startOffset: this.startOffset, | |
571 end: this.end, | |
572 endOffset: this.endOffset | |
573 }; | |
574 }; | |
575 | |
576 return SerializedRange; | |
577 | |
578 })(); | |
579 | |
580 util = { | |
581 uuid: (function() { | |
582 var counter; | |
583 counter = 0; | |
584 return function() { | |
585 return counter++; | |
586 }; | |
587 })(), | |
588 getGlobal: function() { | |
589 return (function() { | |
590 return this; | |
591 })(); | |
592 }, | |
593 maxZIndex: function($elements) { | |
594 var all, el; | |
595 all = (function() { | |
596 var _k, _len2, _results; | |
597 _results = []; | |
598 for (_k = 0, _len2 = $elements.length; _k < _len2; _k++) { | |
599 el = $elements[_k]; | |
600 if ($(el).css('position') === 'static') { | |
601 _results.push(-1); | |
602 } else { | |
603 _results.push(parseInt($(el).css('z-index'), 10) || -1); | |
604 } | |
605 } | |
606 return _results; | |
607 })(); | |
608 return Math.max.apply(Math, all); | |
609 }, | |
610 mousePosition: function(e, offsetEl) { | |
611 var offset; | |
612 offset = $(offsetEl).offset(); | |
613 return { | |
614 top: e.pageY - offset.top, | |
615 left: e.pageX - offset.left | |
616 }; | |
617 }, | |
618 preventEventDefault: function(event) { | |
619 return event != null ? typeof event.preventDefault === "function" ? event.preventDefault() : void 0 : void 0; | |
620 } | |
621 }; | |
622 | |
623 _Annotator = this.Annotator; | |
624 | |
625 Annotator = (function(_super) { | |
626 | |
627 __extends(Annotator, _super); | |
628 | |
629 Annotator.prototype.events = { | |
630 ".annotator-adder button click": "onAdderClick", | |
631 ".annotator-adder button mousedown": "onAdderMousedown", | |
632 ".annotator-hl mouseover": "onHighlightMouseover", | |
633 ".annotator-hl mouseout": "startViewerHideTimer" | |
634 }; | |
635 | |
636 Annotator.prototype.html = { | |
637 adder: '<div class="annotator-adder"><button>' + _t('Annotate') + '</button></div>', | |
638 wrapper: '<div class="annotator-wrapper"></div>' | |
639 }; | |
640 | |
641 Annotator.prototype.options = { | |
642 readOnly: false | |
643 }; | |
644 | |
645 Annotator.prototype.plugins = {}; | |
646 | |
647 Annotator.prototype.editor = null; | |
648 | |
649 Annotator.prototype.viewer = null; | |
650 | |
651 Annotator.prototype.selectedRanges = null; | |
652 | |
653 Annotator.prototype.mouseIsDown = false; | |
654 | |
655 Annotator.prototype.ignoreMouseup = false; | |
656 | |
657 Annotator.prototype.viewerHideTimer = null; | |
658 | |
659 function Annotator(element, options) { | |
660 this.onDeleteAnnotation = __bind(this.onDeleteAnnotation, this); | |
661 | |
662 this.onEditAnnotation = __bind(this.onEditAnnotation, this); | |
663 | |
664 this.onAdderClick = __bind(this.onAdderClick, this); | |
665 | |
666 this.onAdderMousedown = __bind(this.onAdderMousedown, this); | |
667 | |
668 this.onHighlightMouseover = __bind(this.onHighlightMouseover, this); | |
669 | |
670 this.checkForEndSelection = __bind(this.checkForEndSelection, this); | |
671 | |
672 this.checkForStartSelection = __bind(this.checkForStartSelection, this); | |
673 | |
674 this.clearViewerHideTimer = __bind(this.clearViewerHideTimer, this); | |
675 | |
676 this.startViewerHideTimer = __bind(this.startViewerHideTimer, this); | |
677 | |
678 this.showViewer = __bind(this.showViewer, this); | |
679 | |
680 this.onEditorSubmit = __bind(this.onEditorSubmit, this); | |
681 | |
682 this.onEditorHide = __bind(this.onEditorHide, this); | |
683 | |
684 this.showEditor = __bind(this.showEditor, this); | |
685 Annotator.__super__.constructor.apply(this, arguments); | |
686 this.plugins = {}; | |
687 if (!Annotator.supported()) { | |
688 return this; | |
689 } | |
690 if (!this.options.readOnly) { | |
691 this._setupDocumentEvents(); | |
692 } | |
693 this._setupWrapper()._setupViewer()._setupEditor(); | |
694 this._setupDynamicStyle(); | |
695 this.adder = $(this.html.adder).appendTo(this.wrapper).hide(); | |
696 } | |
697 | |
698 Annotator.prototype._setupWrapper = function() { | |
699 this.wrapper = $(this.html.wrapper); | |
700 this.element.find('script').remove(); | |
701 this.element.wrapInner(this.wrapper); | |
702 this.wrapper = this.element.find('.annotator-wrapper'); | |
703 return this; | |
704 }; | |
705 | |
706 Annotator.prototype._setupViewer = function() { | |
707 var _this = this; | |
708 this.viewer = new Annotator.Viewer({ | |
709 readOnly: this.options.readOnly | |
710 }); | |
711 this.viewer.hide().on("edit", this.onEditAnnotation).on("delete", this.onDeleteAnnotation).addField({ | |
712 load: function(field, annotation) { | |
713 if (annotation.text) { | |
714 $(field).escape(annotation.text); | |
715 } else { | |
716 $(field).html("<i>" + (_t('No Comment')) + "</i>"); | |
717 } | |
718 return _this.publish('annotationViewerTextField', [field, annotation]); | |
719 } | |
720 }).element.appendTo(this.wrapper).bind({ | |
721 "mouseover": this.clearViewerHideTimer, | |
722 "mouseout": this.startViewerHideTimer | |
723 }); | |
724 return this; | |
725 }; | |
726 | |
727 Annotator.prototype._setupEditor = function() { | |
728 this.editor = new Annotator.Editor(); | |
729 this.editor.hide().on('hide', this.onEditorHide).on('save', this.onEditorSubmit).addField({ | |
730 type: 'textarea', | |
731 label: _t('Comments') + '\u2026', | |
732 load: function(field, annotation) { | |
733 return $(field).find('textarea').val(annotation.text || ''); | |
734 }, | |
735 submit: function(field, annotation) { | |
736 return annotation.text = $(field).find('textarea').val(); | |
737 } | |
738 }); | |
739 this.editor.element.appendTo(this.wrapper); | |
740 return this; | |
741 }; | |
742 | |
743 Annotator.prototype._setupDocumentEvents = function() { | |
744 $(document).bind({ | |
745 "mouseup": this.checkForEndSelection, | |
746 "mousedown": this.checkForStartSelection | |
747 }); | |
748 return this; | |
749 }; | |
750 | |
751 Annotator.prototype._setupDynamicStyle = function() { | |
752 var max, sel, style, x; | |
753 style = $('#annotator-dynamic-style'); | |
754 if (!style.length) { | |
755 style = $('<style id="annotator-dynamic-style"></style>').appendTo(document.head); | |
756 } | |
757 sel = '*' + ((function() { | |
758 var _k, _len2, _ref1, _results; | |
759 _ref1 = ['adder', 'outer', 'notice', 'filter']; | |
760 _results = []; | |
761 for (_k = 0, _len2 = _ref1.length; _k < _len2; _k++) { | |
762 x = _ref1[_k]; | |
763 _results.push(":not(.annotator-" + x + ")"); | |
764 } | |
765 return _results; | |
766 })()).join(''); | |
767 max = util.maxZIndex($(document.body).find(sel)); | |
768 max = Math.max(max, 1000); | |
769 style.text([".annotator-adder, .annotator-outer, .annotator-notice {", " z-index: " + (max + 20) + ";", "}", ".annotator-filter {", " z-index: " + (max + 10) + ";", "}"].join("\n")); | |
770 return this; | |
771 }; | |
772 | |
773 Annotator.prototype.getSelectedRanges = function() { | |
774 var browserRange, i, normedRange, r, ranges, rangesToIgnore, selection, _k, _len2; | |
775 selection = util.getGlobal().getSelection(); | |
776 ranges = []; | |
777 rangesToIgnore = []; | |
778 if (!selection.isCollapsed) { | |
779 ranges = (function() { | |
780 var _k, _ref1, _results; | |
781 _results = []; | |
782 for (i = _k = 0, _ref1 = selection.rangeCount; 0 <= _ref1 ? _k < _ref1 : _k > _ref1; i = 0 <= _ref1 ? ++_k : --_k) { | |
783 r = selection.getRangeAt(i); | |
784 browserRange = new Range.BrowserRange(r); | |
785 normedRange = browserRange.normalize().limit(this.wrapper[0]); | |
786 if (normedRange === null) { | |
787 rangesToIgnore.push(r); | |
788 } | |
789 _results.push(normedRange); | |
790 } | |
791 return _results; | |
792 }).call(this); | |
793 selection.removeAllRanges(); | |
794 } | |
795 for (_k = 0, _len2 = rangesToIgnore.length; _k < _len2; _k++) { | |
796 r = rangesToIgnore[_k]; | |
797 selection.addRange(r); | |
798 } | |
799 return $.grep(ranges, function(range) { | |
800 if (range) { | |
801 selection.addRange(range.toRange()); | |
802 } | |
803 return range; | |
804 }); | |
805 }; | |
806 | |
807 Annotator.prototype.createAnnotation = function() { | |
808 var annotation; | |
809 annotation = {}; | |
810 this.publish('beforeAnnotationCreated', [annotation]); | |
811 return annotation; | |
812 }; | |
813 | |
814 Annotator.prototype.setupAnnotation = function(annotation, fireEvents) { | |
815 var normed, normedRanges, r, root, _k, _l, _len2, _len3, _ref1; | |
816 if (fireEvents == null) { | |
817 fireEvents = true; | |
818 } | |
819 root = this.wrapper[0]; | |
820 annotation.ranges || (annotation.ranges = this.selectedRanges); | |
821 normedRanges = []; | |
822 _ref1 = annotation.ranges; | |
823 for (_k = 0, _len2 = _ref1.length; _k < _len2; _k++) { | |
824 r = _ref1[_k]; | |
825 try { | |
826 normedRanges.push(Range.sniff(r).normalize(root)); | |
827 } catch (e) { | |
828 if (e instanceof Range.RangeError) { | |
829 this.publish('rangeNormalizeFail', [annotation, r, e]); | |
830 } else { | |
831 throw e; | |
832 } | |
833 } | |
834 } | |
835 annotation.quote = []; | |
836 annotation.ranges = []; | |
837 annotation.highlights = []; | |
838 for (_l = 0, _len3 = normedRanges.length; _l < _len3; _l++) { | |
839 normed = normedRanges[_l]; | |
840 annotation.quote.push($.trim(normed.text())); | |
841 annotation.ranges.push(normed.serialize(this.wrapper[0], '.annotator-hl')); | |
842 $.merge(annotation.highlights, this.highlightRange(normed)); | |
843 } | |
844 annotation.quote = annotation.quote.join(' / '); | |
845 $(annotation.highlights).data('annotation', annotation); | |
846 if (fireEvents) { | |
847 this.publish('annotationCreated', [annotation]); | |
848 } | |
849 return annotation; | |
850 }; | |
851 | |
852 Annotator.prototype.updateAnnotation = function(annotation) { | |
853 this.publish('beforeAnnotationUpdated', [annotation]); | |
854 this.publish('annotationUpdated', [annotation]); | |
855 return annotation; | |
856 }; | |
857 | |
858 Annotator.prototype.deleteAnnotation = function(annotation) { | |
859 var h, _k, _len2, _ref1; | |
860 _ref1 = annotation.highlights; | |
861 for (_k = 0, _len2 = _ref1.length; _k < _len2; _k++) { | |
862 h = _ref1[_k]; | |
863 $(h).replaceWith(h.childNodes); | |
864 } | |
865 this.publish('annotationDeleted', [annotation]); | |
866 return annotation; | |
867 }; | |
868 | |
869 Annotator.prototype.loadAnnotations = function(annotations) { | |
870 var clone, loader, | |
871 _this = this; | |
872 if (annotations == null) { | |
873 annotations = []; | |
874 } | |
875 loader = function(annList) { | |
876 var n, now, _k, _len2; | |
877 if (annList == null) { | |
878 annList = []; | |
879 } | |
880 now = annList.splice(0, 10); | |
881 for (_k = 0, _len2 = now.length; _k < _len2; _k++) { | |
882 n = now[_k]; | |
883 _this.setupAnnotation(n, false); | |
884 } | |
885 if (annList.length > 0) { | |
886 return setTimeout((function() { | |
887 return loader(annList); | |
888 }), 10); | |
889 } else { | |
890 return _this.publish('annotationsLoaded', [clone]); | |
891 } | |
892 }; | |
893 clone = annotations.slice(); | |
894 if (annotations.length) { | |
895 loader(annotations); | |
896 } | |
897 return this; | |
898 }; | |
899 | |
900 Annotator.prototype.dumpAnnotations = function() { | |
901 if (this.plugins['Store']) { | |
902 return this.plugins['Store'].dumpAnnotations(); | |
903 } else { | |
904 return console.warn(_t("Can't dump annotations without Store plugin.")); | |
905 } | |
906 }; | |
907 | |
908 Annotator.prototype.highlightRange = function(normedRange, cssClass) { | |
909 var hl, node, white, _k, _len2, _ref1, _results; | |
910 if (cssClass == null) { | |
911 cssClass = 'annotator-hl'; | |
912 } | |
913 white = /^\s*$/; | |
914 hl = $("<span class='" + cssClass + "'></span>"); | |
915 _ref1 = normedRange.textNodes(); | |
916 _results = []; | |
917 for (_k = 0, _len2 = _ref1.length; _k < _len2; _k++) { | |
918 node = _ref1[_k]; | |
919 if (!white.test(node.nodeValue)) { | |
920 _results.push($(node).wrapAll(hl).parent().show()[0]); | |
921 } | |
922 } | |
923 return _results; | |
924 }; | |
925 | |
926 Annotator.prototype.highlightRanges = function(normedRanges, cssClass) { | |
927 var highlights, r, _k, _len2; | |
928 if (cssClass == null) { | |
929 cssClass = 'annotator-hl'; | |
930 } | |
931 highlights = []; | |
932 for (_k = 0, _len2 = normedRanges.length; _k < _len2; _k++) { | |
933 r = normedRanges[_k]; | |
934 $.merge(highlights, this.highlightRange(r, cssClass)); | |
935 } | |
936 return highlights; | |
937 }; | |
938 | |
939 Annotator.prototype.addPlugin = function(name, options) { | |
940 var klass, _base; | |
941 if (this.plugins[name]) { | |
942 console.error(_t("You cannot have more than one instance of any plugin.")); | |
943 } else { | |
944 klass = Annotator.Plugin[name]; | |
945 if (typeof klass === 'function') { | |
946 this.plugins[name] = new klass(this.element[0], options); | |
947 this.plugins[name].annotator = this; | |
948 if (typeof (_base = this.plugins[name]).pluginInit === "function") { | |
949 _base.pluginInit(); | |
950 } | |
951 } else { | |
952 console.error(_t("Could not load ") + name + _t(" plugin. Have you included the appropriate <script> tag?")); | |
953 } | |
954 } | |
955 return this; | |
956 }; | |
957 | |
958 Annotator.prototype.showEditor = function(annotation, location) { | |
959 this.editor.element.css(location); | |
960 this.editor.load(annotation); | |
961 this.publish('annotationEditorShown', [this.editor, annotation]); | |
962 return this; | |
963 }; | |
964 | |
965 Annotator.prototype.onEditorHide = function() { | |
966 this.publish('annotationEditorHidden', [this.editor]); | |
967 return this.ignoreMouseup = false; | |
968 }; | |
969 | |
970 Annotator.prototype.onEditorSubmit = function(annotation) { | |
971 this.publish('annotationEditorSubmit', [this.editor, annotation]); | |
972 if (annotation.ranges === void 0) { | |
973 return this.setupAnnotation(annotation); | |
974 } else { | |
975 return this.updateAnnotation(annotation); | |
976 } | |
977 }; | |
978 | |
979 Annotator.prototype.showViewer = function(annotations, location) { | |
980 this.viewer.element.css(location); | |
981 this.viewer.load(annotations); | |
982 return this.publish('annotationViewerShown', [this.viewer, annotations]); | |
983 }; | |
984 | |
985 Annotator.prototype.startViewerHideTimer = function() { | |
986 if (!this.viewerHideTimer) { | |
987 return this.viewerHideTimer = setTimeout(this.viewer.hide, 250); | |
988 } | |
989 }; | |
990 | |
991 Annotator.prototype.clearViewerHideTimer = function() { | |
992 clearTimeout(this.viewerHideTimer); | |
993 return this.viewerHideTimer = false; | |
994 }; | |
995 | |
996 Annotator.prototype.checkForStartSelection = function(event) { | |
997 if (!(event && this.isAnnotator(event.target))) { | |
998 this.startViewerHideTimer(); | |
999 return this.mouseIsDown = true; | |
1000 } | |
1001 }; | |
1002 | |
1003 Annotator.prototype.checkForEndSelection = function(event) { | |
1004 var container, range, _k, _len2, _ref1; | |
1005 this.mouseIsDown = false; | |
1006 if (this.ignoreMouseup) { | |
1007 return; | |
1008 } | |
1009 this.selectedRanges = this.getSelectedRanges(); | |
1010 _ref1 = this.selectedRanges; | |
1011 for (_k = 0, _len2 = _ref1.length; _k < _len2; _k++) { | |
1012 range = _ref1[_k]; | |
1013 container = range.commonAncestor; | |
1014 if ($(container).hasClass('annotator-hl')) { | |
1015 container = $(container).parents('[class^=annotator-hl]')[0]; | |
1016 } | |
1017 if (this.isAnnotator(container)) { | |
1018 return; | |
1019 } | |
1020 } | |
1021 if (event && this.selectedRanges.length) { | |
1022 return this.adder.css(util.mousePosition(event, this.wrapper[0])).show(); | |
1023 } else { | |
1024 return this.adder.hide(); | |
1025 } | |
1026 }; | |
1027 | |
1028 Annotator.prototype.isAnnotator = function(element) { | |
1029 return !!$(element).parents().andSelf().filter('[class^=annotator-]').not(this.wrapper).length; | |
1030 }; | |
1031 | |
1032 Annotator.prototype.onHighlightMouseover = function(event) { | |
1033 var annotations; | |
1034 this.clearViewerHideTimer(); | |
1035 if (this.mouseIsDown || this.viewer.isShown()) { | |
1036 return false; | |
1037 } | |
1038 annotations = $(event.target).parents('.annotator-hl').andSelf().map(function() { | |
1039 return $(this).data("annotation"); | |
1040 }); | |
1041 return this.showViewer($.makeArray(annotations), util.mousePosition(event, this.wrapper[0])); | |
1042 }; | |
1043 | |
1044 Annotator.prototype.onAdderMousedown = function(event) { | |
1045 if (event != null) { | |
1046 event.preventDefault(); | |
1047 } | |
1048 return this.ignoreMouseup = true; | |
1049 }; | |
1050 | |
1051 Annotator.prototype.onAdderClick = function(event) { | |
1052 var highlights, position, r, ranges; | |
1053 if (event != null) { | |
1054 event.preventDefault(); | |
1055 } | |
1056 position = this.adder.position(); | |
1057 this.adder.hide(); | |
1058 if (this.selectedRanges && this.selectedRanges.length) { | |
1059 ranges = (function() { | |
1060 var _k, _len2, _ref1, _results; | |
1061 _ref1 = this.selectedRanges; | |
1062 _results = []; | |
1063 for (_k = 0, _len2 = _ref1.length; _k < _len2; _k++) { | |
1064 r = _ref1[_k]; | |
1065 _results.push(Range.sniff(r).normalize()); | |
1066 } | |
1067 return _results; | |
1068 }).call(this); | |
1069 highlights = this.highlightRanges(ranges, 'annotator-hl annotator-hl-temporary'); | |
1070 this.editor.element.one('hide', function() { | |
1071 var h, _k, _len2, _results; | |
1072 _results = []; | |
1073 for (_k = 0, _len2 = highlights.length; _k < _len2; _k++) { | |
1074 h = highlights[_k]; | |
1075 _results.push($(h).replaceWith(h.childNodes)); | |
1076 } | |
1077 return _results; | |
1078 }); | |
1079 } | |
1080 return this.showEditor(this.createAnnotation(), position); | |
1081 }; | |
1082 | |
1083 Annotator.prototype.onEditAnnotation = function(annotation) { | |
1084 var offset; | |
1085 offset = this.viewer.element.position(); | |
1086 this.viewer.hide(); | |
1087 return this.showEditor(annotation, offset); | |
1088 }; | |
1089 | |
1090 Annotator.prototype.onDeleteAnnotation = function(annotation) { | |
1091 this.viewer.hide(); | |
1092 return this.deleteAnnotation(annotation); | |
1093 }; | |
1094 | |
1095 return Annotator; | |
1096 | |
1097 })(Delegator); | |
1098 | |
1099 Annotator.Plugin = (function(_super) { | |
1100 | |
1101 __extends(Plugin, _super); | |
1102 | |
1103 function Plugin(element, options) { | |
1104 Plugin.__super__.constructor.apply(this, arguments); | |
1105 } | |
1106 | |
1107 Plugin.prototype.pluginInit = function() {}; | |
1108 | |
1109 return Plugin; | |
1110 | |
1111 })(Delegator); | |
1112 | |
1113 g = util.getGlobal(); | |
1114 | |
1115 if (!(((_ref1 = g.document) != null ? _ref1.evaluate : void 0) != null)) { | |
1116 $.getScript('http://assets.annotateit.org/vendor/xpath.min.js'); | |
1117 } | |
1118 | |
1119 if (!(g.getSelection != null)) { | |
1120 $.getScript('http://assets.annotateit.org/vendor/ierange.min.js'); | |
1121 } | |
1122 | |
1123 if (!(g.JSON != null)) { | |
1124 $.getScript('http://assets.annotateit.org/vendor/json2.min.js'); | |
1125 } | |
1126 | |
1127 Annotator.$ = $; | |
1128 | |
1129 Annotator.Delegator = Delegator; | |
1130 | |
1131 Annotator.Range = Range; | |
1132 | |
1133 Annotator._t = _t; | |
1134 | |
1135 Annotator.supported = function() { | |
1136 return (function() { | |
1137 return !!this.getSelection; | |
1138 })(); | |
1139 }; | |
1140 | |
1141 Annotator.noConflict = function() { | |
1142 util.getGlobal().Annotator = _Annotator; | |
1143 return this; | |
1144 }; | |
1145 | |
1146 $.plugin('annotator', Annotator); | |
1147 | |
1148 this.Annotator = Annotator; | |
1149 | |
1150 Annotator.Widget = (function(_super) { | |
1151 | |
1152 __extends(Widget, _super); | |
1153 | |
1154 Widget.prototype.classes = { | |
1155 hide: 'annotator-hide', | |
1156 invert: { | |
1157 x: 'annotator-invert-x', | |
1158 y: 'annotator-invert-y' | |
1159 } | |
1160 }; | |
1161 | |
1162 function Widget(element, options) { | |
1163 Widget.__super__.constructor.apply(this, arguments); | |
1164 this.classes = $.extend({}, Annotator.Widget.prototype.classes, this.classes); | |
1165 } | |
1166 | |
1167 Widget.prototype.checkOrientation = function() { | |
1168 var current, offset, viewport, widget, window; | |
1169 this.resetOrientation(); | |
1170 window = $(util.getGlobal()); | |
1171 widget = this.element.children(":first"); | |
1172 offset = widget.offset(); | |
1173 viewport = { | |
1174 top: window.scrollTop(), | |
1175 right: window.width() + window.scrollLeft() | |
1176 }; | |
1177 current = { | |
1178 top: offset.top, | |
1179 right: offset.left + widget.width() | |
1180 }; | |
1181 if ((current.top - viewport.top) < 0) { | |
1182 this.invertY(); | |
1183 } | |
1184 if ((current.right - viewport.right) > 0) { | |
1185 this.invertX(); | |
1186 } | |
1187 return this; | |
1188 }; | |
1189 | |
1190 Widget.prototype.resetOrientation = function() { | |
1191 this.element.removeClass(this.classes.invert.x).removeClass(this.classes.invert.y); | |
1192 return this; | |
1193 }; | |
1194 | |
1195 Widget.prototype.invertX = function() { | |
1196 this.element.addClass(this.classes.invert.x); | |
1197 return this; | |
1198 }; | |
1199 | |
1200 Widget.prototype.invertY = function() { | |
1201 this.element.addClass(this.classes.invert.y); | |
1202 return this; | |
1203 }; | |
1204 | |
1205 Widget.prototype.isInvertedY = function() { | |
1206 return this.element.hasClass(this.classes.invert.y); | |
1207 }; | |
1208 | |
1209 Widget.prototype.isInvertedX = function() { | |
1210 return this.element.hasClass(this.classes.invert.x); | |
1211 }; | |
1212 | |
1213 return Widget; | |
1214 | |
1215 })(Delegator); | |
1216 | |
1217 Annotator.Editor = (function(_super) { | |
1218 | |
1219 __extends(Editor, _super); | |
1220 | |
1221 Editor.prototype.events = { | |
1222 "form submit": "submit", | |
1223 ".annotator-save click": "submit", | |
1224 ".annotator-cancel click": "hide", | |
1225 ".annotator-cancel mouseover": "onCancelButtonMouseover", | |
1226 "textarea keydown": "processKeypress" | |
1227 }; | |
1228 | |
1229 Editor.prototype.classes = { | |
1230 hide: 'annotator-hide', | |
1231 focus: 'annotator-focus' | |
1232 }; | |
1233 | |
1234 Editor.prototype.html = "<div class=\"annotator-outer annotator-editor\">\n <form class=\"annotator-widget\">\n <ul class=\"annotator-listing\"></ul>\n <div class=\"annotator-controls\">\n <a href=\"#cancel\" class=\"annotator-cancel\">" + _t('Cancel') + "</a>\n<a href=\"#save\" class=\"annotator-save annotator-focus\">" + _t('Save') + "</a>\n </div>\n </form>\n</div>"; | |
1235 | |
1236 Editor.prototype.options = {}; | |
1237 | |
1238 function Editor(options) { | |
1239 this.onCancelButtonMouseover = __bind(this.onCancelButtonMouseover, this); | |
1240 | |
1241 this.processKeypress = __bind(this.processKeypress, this); | |
1242 | |
1243 this.submit = __bind(this.submit, this); | |
1244 | |
1245 this.load = __bind(this.load, this); | |
1246 | |
1247 this.hide = __bind(this.hide, this); | |
1248 | |
1249 this.show = __bind(this.show, this); | |
1250 Editor.__super__.constructor.call(this, $(this.html)[0], options); | |
1251 this.fields = []; | |
1252 this.annotation = {}; | |
1253 } | |
1254 | |
1255 Editor.prototype.show = function(event) { | |
1256 util.preventEventDefault(event); | |
1257 this.element.removeClass(this.classes.hide); | |
1258 this.element.find('.annotator-save').addClass(this.classes.focus); | |
1259 this.checkOrientation(); | |
1260 this.element.find(":input:first").focus(); | |
1261 this.setupDraggables(); | |
1262 return this.publish('show'); | |
1263 }; | |
1264 | |
1265 Editor.prototype.hide = function(event) { | |
1266 util.preventEventDefault(event); | |
1267 this.element.addClass(this.classes.hide); | |
1268 return this.publish('hide'); | |
1269 }; | |
1270 | |
1271 Editor.prototype.load = function(annotation) { | |
1272 var field, _k, _len2, _ref2; | |
1273 this.annotation = annotation; | |
1274 this.publish('load', [this.annotation]); | |
1275 _ref2 = this.fields; | |
1276 for (_k = 0, _len2 = _ref2.length; _k < _len2; _k++) { | |
1277 field = _ref2[_k]; | |
1278 field.load(field.element, this.annotation); | |
1279 } | |
1280 return this.show(); | |
1281 }; | |
1282 | |
1283 Editor.prototype.submit = function(event) { | |
1284 var field, _k, _len2, _ref2; | |
1285 util.preventEventDefault(event); | |
1286 _ref2 = this.fields; | |
1287 for (_k = 0, _len2 = _ref2.length; _k < _len2; _k++) { | |
1288 field = _ref2[_k]; | |
1289 field.submit(field.element, this.annotation); | |
1290 } | |
1291 this.publish('save', [this.annotation]); | |
1292 return this.hide(); | |
1293 }; | |
1294 | |
1295 Editor.prototype.addField = function(options) { | |
1296 var element, field, input; | |
1297 field = $.extend({ | |
1298 id: 'annotator-field-' + util.uuid(), | |
1299 type: 'input', | |
1300 label: '', | |
1301 load: function() {}, | |
1302 submit: function() {} | |
1303 }, options); | |
1304 input = null; | |
1305 element = $('<li class="annotator-item" />'); | |
1306 field.element = element[0]; | |
1307 switch (field.type) { | |
1308 case 'textarea': | |
1309 input = $('<textarea />'); | |
1310 break; | |
1311 case 'input': | |
1312 case 'checkbox': | |
1313 input = $('<input />'); | |
1314 } | |
1315 element.append(input); | |
1316 input.attr({ | |
1317 id: field.id, | |
1318 placeholder: field.label | |
1319 }); | |
1320 if (field.type === 'checkbox') { | |
1321 input[0].type = 'checkbox'; | |
1322 element.addClass('annotator-checkbox'); | |
1323 element.append($('<label />', { | |
1324 "for": field.id, | |
1325 html: field.label | |
1326 })); | |
1327 } | |
1328 this.element.find('ul:first').append(element); | |
1329 this.fields.push(field); | |
1330 return field.element; | |
1331 }; | |
1332 | |
1333 Editor.prototype.checkOrientation = function() { | |
1334 var controls, list; | |
1335 Editor.__super__.checkOrientation.apply(this, arguments); | |
1336 list = this.element.find('ul'); | |
1337 controls = this.element.find('.annotator-controls'); | |
1338 if (this.element.hasClass(this.classes.invert.y)) { | |
1339 controls.insertBefore(list); | |
1340 } else if (controls.is(':first-child')) { | |
1341 controls.insertAfter(list); | |
1342 } | |
1343 return this; | |
1344 }; | |
1345 | |
1346 Editor.prototype.processKeypress = function(event) { | |
1347 if (event.keyCode === 27) { | |
1348 return this.hide(); | |
1349 } else if (event.keyCode === 13 && !event.shiftKey) { | |
1350 return this.submit(); | |
1351 } | |
1352 }; | |
1353 | |
1354 Editor.prototype.onCancelButtonMouseover = function() { | |
1355 return this.element.find('.' + this.classes.focus).removeClass(this.classes.focus); | |
1356 }; | |
1357 | |
1358 Editor.prototype.setupDraggables = function() { | |
1359 var classes, controls, cornerItem, editor, mousedown, onMousedown, onMousemove, onMouseup, resize, textarea, throttle, | |
1360 _this = this; | |
1361 this.element.find('.annotator-resize').remove(); | |
1362 if (this.element.hasClass(this.classes.invert.y)) { | |
1363 cornerItem = this.element.find('.annotator-item:last'); | |
1364 } else { | |
1365 cornerItem = this.element.find('.annotator-item:first'); | |
1366 } | |
1367 if (cornerItem) { | |
1368 $('<span class="annotator-resize"></span>').appendTo(cornerItem); | |
1369 } | |
1370 mousedown = null; | |
1371 classes = this.classes; | |
1372 editor = this.element; | |
1373 textarea = null; | |
1374 resize = editor.find('.annotator-resize'); | |
1375 controls = editor.find('.annotator-controls'); | |
1376 throttle = false; | |
1377 onMousedown = function(event) { | |
1378 if (event.target === this) { | |
1379 mousedown = { | |
1380 element: this, | |
1381 top: event.pageY, | |
1382 left: event.pageX | |
1383 }; | |
1384 textarea = editor.find('textarea:first'); | |
1385 $(window).bind({ | |
1386 'mouseup.annotator-editor-resize': onMouseup, | |
1387 'mousemove.annotator-editor-resize': onMousemove | |
1388 }); | |
1389 return event.preventDefault(); | |
1390 } | |
1391 }; | |
1392 onMouseup = function() { | |
1393 mousedown = null; | |
1394 return $(window).unbind('.annotator-editor-resize'); | |
1395 }; | |
1396 onMousemove = function(event) { | |
1397 var diff, directionX, directionY, height, width; | |
1398 if (mousedown && throttle === false) { | |
1399 diff = { | |
1400 top: event.pageY - mousedown.top, | |
1401 left: event.pageX - mousedown.left | |
1402 }; | |
1403 if (mousedown.element === resize[0]) { | |
1404 height = textarea.outerHeight(); | |
1405 width = textarea.outerWidth(); | |
1406 directionX = editor.hasClass(classes.invert.x) ? -1 : 1; | |
1407 directionY = editor.hasClass(classes.invert.y) ? 1 : -1; | |
1408 textarea.height(height + (diff.top * directionY)); | |
1409 textarea.width(width + (diff.left * directionX)); | |
1410 if (textarea.outerHeight() !== height) { | |
1411 mousedown.top = event.pageY; | |
1412 } | |
1413 if (textarea.outerWidth() !== width) { | |
1414 mousedown.left = event.pageX; | |
1415 } | |
1416 } else if (mousedown.element === controls[0]) { | |
1417 editor.css({ | |
1418 top: parseInt(editor.css('top'), 10) + diff.top, | |
1419 left: parseInt(editor.css('left'), 10) + diff.left | |
1420 }); | |
1421 mousedown.top = event.pageY; | |
1422 mousedown.left = event.pageX; | |
1423 } | |
1424 throttle = true; | |
1425 return setTimeout(function() { | |
1426 return throttle = false; | |
1427 }, 1000 / 60); | |
1428 } | |
1429 }; | |
1430 resize.bind('mousedown', onMousedown); | |
1431 return controls.bind('mousedown', onMousedown); | |
1432 }; | |
1433 | |
1434 return Editor; | |
1435 | |
1436 })(Annotator.Widget); | |
1437 | |
1438 Annotator.Viewer = (function(_super) { | |
1439 | |
1440 __extends(Viewer, _super); | |
1441 | |
1442 Viewer.prototype.events = { | |
1443 ".annotator-edit click": "onEditClick", | |
1444 ".annotator-delete click": "onDeleteClick" | |
1445 }; | |
1446 | |
1447 Viewer.prototype.classes = { | |
1448 hide: 'annotator-hide', | |
1449 showControls: 'annotator-visible' | |
1450 }; | |
1451 | |
1452 Viewer.prototype.html = { | |
1453 element: "<div class=\"annotator-outer annotator-viewer\">\n <ul class=\"annotator-widget annotator-listing\"></ul>\n</div>", | |
1454 item: "<li class=\"annotator-annotation annotator-item\">\n <span class=\"annotator-controls\">\n <a href=\"#\" title=\"View as webpage\" class=\"annotator-link\">View as webpage</a>\n <button title=\"Edit\" class=\"annotator-edit\">Edit</button>\n <button title=\"Delete\" class=\"annotator-delete\">Delete</button>\n </span>\n</li>" | |
1455 }; | |
1456 | |
1457 Viewer.prototype.options = { | |
1458 readOnly: false | |
1459 }; | |
1460 | |
1461 function Viewer(options) { | |
1462 this.onDeleteClick = __bind(this.onDeleteClick, this); | |
1463 | |
1464 this.onEditClick = __bind(this.onEditClick, this); | |
1465 | |
1466 this.load = __bind(this.load, this); | |
1467 | |
1468 this.hide = __bind(this.hide, this); | |
1469 | |
1470 this.show = __bind(this.show, this); | |
1471 Viewer.__super__.constructor.call(this, $(this.html.element)[0], options); | |
1472 this.item = $(this.html.item)[0]; | |
1473 this.fields = []; | |
1474 this.annotations = []; | |
1475 } | |
1476 | |
1477 Viewer.prototype.show = function(event) { | |
1478 var controls, | |
1479 _this = this; | |
1480 util.preventEventDefault(event); | |
1481 controls = this.element.find('.annotator-controls').addClass(this.classes.showControls); | |
1482 setTimeout((function() { | |
1483 return controls.removeClass(_this.classes.showControls); | |
1484 }), 500); | |
1485 this.element.removeClass(this.classes.hide); | |
1486 return this.checkOrientation().publish('show'); | |
1487 }; | |
1488 | |
1489 Viewer.prototype.isShown = function() { | |
1490 return !this.element.hasClass(this.classes.hide); | |
1491 }; | |
1492 | |
1493 Viewer.prototype.hide = function(event) { | |
1494 util.preventEventDefault(event); | |
1495 this.element.addClass(this.classes.hide); | |
1496 return this.publish('hide'); | |
1497 }; | |
1498 | |
1499 Viewer.prototype.load = function(annotations) { | |
1500 var annotation, controller, controls, del, edit, element, field, item, link, links, list, _k, _l, _len2, _len3, _ref2, _ref3; | |
1501 this.annotations = annotations || []; | |
1502 list = this.element.find('ul:first').empty(); | |
1503 _ref2 = this.annotations; | |
1504 for (_k = 0, _len2 = _ref2.length; _k < _len2; _k++) { | |
1505 annotation = _ref2[_k]; | |
1506 item = $(this.item).clone().appendTo(list).data('annotation', annotation); | |
1507 controls = item.find('.annotator-controls'); | |
1508 link = controls.find('.annotator-link'); | |
1509 edit = controls.find('.annotator-edit'); | |
1510 del = controls.find('.annotator-delete'); | |
1511 links = new LinkParser(annotation.links || []).get('alternate', { | |
1512 'type': 'text/html' | |
1513 }); | |
1514 if (links.length === 0 || !(links[0].href != null)) { | |
1515 link.remove(); | |
1516 } else { | |
1517 link.attr('href', links[0].href); | |
1518 } | |
1519 if (this.options.readOnly) { | |
1520 edit.remove(); | |
1521 del.remove(); | |
1522 } else { | |
1523 controller = { | |
1524 showEdit: function() { | |
1525 return edit.removeAttr('disabled'); | |
1526 }, | |
1527 hideEdit: function() { | |
1528 return edit.attr('disabled', 'disabled'); | |
1529 }, | |
1530 showDelete: function() { | |
1531 return del.removeAttr('disabled'); | |
1532 }, | |
1533 hideDelete: function() { | |
1534 return del.attr('disabled', 'disabled'); | |
1535 } | |
1536 }; | |
1537 } | |
1538 _ref3 = this.fields; | |
1539 for (_l = 0, _len3 = _ref3.length; _l < _len3; _l++) { | |
1540 field = _ref3[_l]; | |
1541 element = $(field.element).clone().appendTo(item)[0]; | |
1542 field.load(element, annotation, controller); | |
1543 } | |
1544 } | |
1545 this.publish('load', [this.annotations]); | |
1546 return this.show(); | |
1547 }; | |
1548 | |
1549 Viewer.prototype.addField = function(options) { | |
1550 var field; | |
1551 field = $.extend({ | |
1552 load: function() {} | |
1553 }, options); | |
1554 field.element = $('<div />')[0]; | |
1555 this.fields.push(field); | |
1556 field.element; | |
1557 return this; | |
1558 }; | |
1559 | |
1560 Viewer.prototype.onEditClick = function(event) { | |
1561 return this.onButtonClick(event, 'edit'); | |
1562 }; | |
1563 | |
1564 Viewer.prototype.onDeleteClick = function(event) { | |
1565 return this.onButtonClick(event, 'delete'); | |
1566 }; | |
1567 | |
1568 Viewer.prototype.onButtonClick = function(event, type) { | |
1569 var item; | |
1570 item = $(event.target).parents('.annotator-annotation'); | |
1571 return this.publish(type, [item.data('annotation')]); | |
1572 }; | |
1573 | |
1574 return Viewer; | |
1575 | |
1576 })(Annotator.Widget); | |
1577 | |
1578 LinkParser = (function() { | |
1579 | |
1580 function LinkParser(data) { | |
1581 this.data = data; | |
1582 } | |
1583 | |
1584 LinkParser.prototype.get = function(rel, cond) { | |
1585 var d, k, keys, match, v, _k, _len2, _ref2, _results; | |
1586 if (cond == null) { | |
1587 cond = {}; | |
1588 } | |
1589 cond = $.extend({}, cond, { | |
1590 rel: rel | |
1591 }); | |
1592 keys = (function() { | |
1593 var _results; | |
1594 _results = []; | |
1595 for (k in cond) { | |
1596 if (!__hasProp.call(cond, k)) continue; | |
1597 v = cond[k]; | |
1598 _results.push(k); | |
1599 } | |
1600 return _results; | |
1601 })(); | |
1602 _ref2 = this.data; | |
1603 _results = []; | |
1604 for (_k = 0, _len2 = _ref2.length; _k < _len2; _k++) { | |
1605 d = _ref2[_k]; | |
1606 match = keys.reduce((function(m, k) { | |
1607 return m && (d[k] === cond[k]); | |
1608 }), true); | |
1609 if (match) { | |
1610 _results.push(d); | |
1611 } else { | |
1612 continue; | |
1613 } | |
1614 } | |
1615 return _results; | |
1616 }; | |
1617 | |
1618 return LinkParser; | |
1619 | |
1620 })(); | |
1621 | |
1622 Annotator = Annotator || {}; | |
1623 | |
1624 Annotator.Notification = (function(_super) { | |
1625 | |
1626 __extends(Notification, _super); | |
1627 | |
1628 Notification.prototype.events = { | |
1629 "click": "hide" | |
1630 }; | |
1631 | |
1632 Notification.prototype.options = { | |
1633 html: "<div class='annotator-notice'></div>", | |
1634 classes: { | |
1635 show: "annotator-notice-show", | |
1636 info: "annotator-notice-info", | |
1637 success: "annotator-notice-success", | |
1638 error: "annotator-notice-error" | |
1639 } | |
1640 }; | |
1641 | |
1642 function Notification(options) { | |
1643 this.hide = __bind(this.hide, this); | |
1644 | |
1645 this.show = __bind(this.show, this); | |
1646 Notification.__super__.constructor.call(this, $(this.options.html).appendTo(document.body)[0], options); | |
1647 } | |
1648 | |
1649 Notification.prototype.show = function(message, status) { | |
1650 if (status == null) { | |
1651 status = Annotator.Notification.INFO; | |
1652 } | |
1653 $(this.element).addClass(this.options.classes.show).addClass(this.options.classes[status]).escape(message || ""); | |
1654 setTimeout(this.hide, 5000); | |
1655 return this; | |
1656 }; | |
1657 | |
1658 Notification.prototype.hide = function() { | |
1659 $(this.element).removeClass(this.options.classes.show); | |
1660 return this; | |
1661 }; | |
1662 | |
1663 return Notification; | |
1664 | |
1665 })(Delegator); | |
1666 | |
1667 Annotator.Notification.INFO = 'show'; | |
1668 | |
1669 Annotator.Notification.SUCCESS = 'success'; | |
1670 | |
1671 Annotator.Notification.ERROR = 'error'; | |
1672 | |
1673 $(function() { | |
1674 var notification; | |
1675 notification = new Annotator.Notification; | |
1676 Annotator.showNotification = notification.show; | |
1677 return Annotator.hideNotification = notification.hide; | |
1678 }); | |
1679 | |
1680 Annotator.Plugin.Unsupported = (function(_super) { | |
1681 | |
1682 __extends(Unsupported, _super); | |
1683 | |
1684 function Unsupported() { | |
1685 return Unsupported.__super__.constructor.apply(this, arguments); | |
1686 } | |
1687 | |
1688 Unsupported.prototype.options = { | |
1689 message: Annotator._t("Sorry your current browser does not support the Annotator") | |
1690 }; | |
1691 | |
1692 Unsupported.prototype.pluginInit = function() { | |
1693 var _this = this; | |
1694 if (!Annotator.supported()) { | |
1695 return $(function() { | |
1696 Annotator.showNotification(_this.options.message); | |
1697 if ((window.XMLHttpRequest === void 0) && (ActiveXObject !== void 0)) { | |
1698 return $('html').addClass('ie6'); | |
1699 } | |
1700 }); | |
1701 } | |
1702 }; | |
1703 | |
1704 return Unsupported; | |
1705 | |
1706 })(Annotator.Plugin); | |
1707 | |
1708 createDateFromISO8601 = function(string) { | |
1709 var d, date, offset, regexp, time, _ref2; | |
1710 regexp = "([0-9]{4})(-([0-9]{2})(-([0-9]{2})" + "(T([0-9]{2}):([0-9]{2})(:([0-9]{2})(\.([0-9]+))?)?" + "(Z|(([-+])([0-9]{2}):([0-9]{2})))?)?)?)?"; | |
1711 d = string.match(new RegExp(regexp)); | |
1712 offset = 0; | |
1713 date = new Date(d[1], 0, 1); | |
1714 if (d[3]) { | |
1715 date.setMonth(d[3] - 1); | |
1716 } | |
1717 if (d[5]) { | |
1718 date.setDate(d[5]); | |
1719 } | |
1720 if (d[7]) { | |
1721 date.setHours(d[7]); | |
1722 } | |
1723 if (d[8]) { | |
1724 date.setMinutes(d[8]); | |
1725 } | |
1726 if (d[10]) { | |
1727 date.setSeconds(d[10]); | |
1728 } | |
1729 if (d[12]) { | |
1730 date.setMilliseconds(Number("0." + d[12]) * 1000); | |
1731 } | |
1732 if (d[14]) { | |
1733 offset = (Number(d[16]) * 60) + Number(d[17]); | |
1734 offset *= (_ref2 = d[15] === '-') != null ? _ref2 : { | |
1735 1: -1 | |
1736 }; | |
1737 } | |
1738 offset -= date.getTimezoneOffset(); | |
1739 time = Number(date) + (offset * 60 * 1000); | |
1740 date.setTime(Number(time)); | |
1741 return date; | |
1742 }; | |
1743 | |
1744 base64Decode = function(data) { | |
1745 var ac, b64, bits, dec, h1, h2, h3, h4, i, o1, o2, o3, tmp_arr; | |
1746 if (typeof atob !== "undefined" && atob !== null) { | |
1747 return atob(data); | |
1748 } else { | |
1749 b64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/="; | |
1750 i = 0; | |
1751 ac = 0; | |
1752 dec = ""; | |
1753 tmp_arr = []; | |
1754 if (!data) { | |
1755 return data; | |
1756 } | |
1757 data += ''; | |
1758 while (i < data.length) { | |
1759 h1 = b64.indexOf(data.charAt(i++)); | |
1760 h2 = b64.indexOf(data.charAt(i++)); | |
1761 h3 = b64.indexOf(data.charAt(i++)); | |
1762 h4 = b64.indexOf(data.charAt(i++)); | |
1763 bits = h1 << 18 | h2 << 12 | h3 << 6 | h4; | |
1764 o1 = bits >> 16 & 0xff; | |
1765 o2 = bits >> 8 & 0xff; | |
1766 o3 = bits & 0xff; | |
1767 if (h3 === 64) { | |
1768 tmp_arr[ac++] = String.fromCharCode(o1); | |
1769 } else if (h4 === 64) { | |
1770 tmp_arr[ac++] = String.fromCharCode(o1, o2); | |
1771 } else { | |
1772 tmp_arr[ac++] = String.fromCharCode(o1, o2, o3); | |
1773 } | |
1774 } | |
1775 return tmp_arr.join(''); | |
1776 } | |
1777 }; | |
1778 | |
1779 base64UrlDecode = function(data) { | |
1780 var i, m, _k, _ref2; | |
1781 m = data.length % 4; | |
1782 if (m !== 0) { | |
1783 for (i = _k = 0, _ref2 = 4 - m; 0 <= _ref2 ? _k < _ref2 : _k > _ref2; i = 0 <= _ref2 ? ++_k : --_k) { | |
1784 data += '='; | |
1785 } | |
1786 } | |
1787 data = data.replace(/-/g, '+'); | |
1788 data = data.replace(/_/g, '/'); | |
1789 return base64Decode(data); | |
1790 }; | |
1791 | |
1792 parseToken = function(token) { | |
1793 var head, payload, sig, _ref2; | |
1794 _ref2 = token.split('.'), head = _ref2[0], payload = _ref2[1], sig = _ref2[2]; | |
1795 return JSON.parse(base64UrlDecode(payload)); | |
1796 }; | |
1797 | |
1798 Annotator.Plugin.Auth = (function(_super) { | |
1799 | |
1800 __extends(Auth, _super); | |
1801 | |
1802 Auth.prototype.options = { | |
1803 token: null, | |
1804 tokenUrl: '/auth/token', | |
1805 autoFetch: true, | |
1806 requestMethod: 'GET', | |
1807 requestData: null, | |
1808 unauthorizedCallback: null | |
1809 }; | |
1810 | |
1811 function Auth(element, options) { | |
1812 Auth.__super__.constructor.apply(this, arguments); | |
1813 this.waitingForToken = []; | |
1814 if (this.options.token) { | |
1815 this.setToken(this.options.token); | |
1816 } else { | |
1817 this.requestToken(); | |
1818 } | |
1819 } | |
1820 | |
1821 Auth.prototype.requestToken = function() { | |
1822 var _this = this; | |
1823 this.requestInProgress = true; | |
1824 return $.ajax({ | |
1825 url: this.options.tokenUrl, | |
1826 dataType: 'text', | |
1827 data: this.options.requestData, | |
1828 type: this.options.requestMethod, | |
1829 xhrFields: { | |
1830 withCredentials: true | |
1831 } | |
1832 }).done(function(data, status, xhr) { | |
1833 return _this.setToken(data); | |
1834 }).fail(function(xhr, status, err) { | |
1835 var callback, msg; | |
1836 if (xhr.status === 401) { | |
1837 callback = _this.options.unauthorizedCallback; | |
1838 if ((callback != null) && callback(_this)) { | |
1839 _this.retryTimeout = setTimeout((function() { | |
1840 return _this.requestToken(); | |
1841 }), 1000); | |
1842 return; | |
1843 } | |
1844 } | |
1845 msg = Annotator._t("Couldn't get auth token:"); | |
1846 console.error("" + msg + " " + err, xhr); | |
1847 return Annotator.showNotification("" + msg + " " + xhr.responseText, Annotator.Notification.ERROR); | |
1848 }).always(function() { | |
1849 return _this.requestInProgress = false; | |
1850 }); | |
1851 }; | |
1852 | |
1853 Auth.prototype.setToken = function(token) { | |
1854 var _results, | |
1855 _this = this; | |
1856 this.token = token; | |
1857 this._unsafeToken = parseToken(token); | |
1858 if (this.haveValidToken()) { | |
1859 if (this.options.autoFetch) { | |
1860 this.refreshTimeout = setTimeout((function() { | |
1861 return _this.requestToken(); | |
1862 }), (this.timeToExpiry() - 2) * 1000); | |
1863 } | |
1864 this.updateHeaders(); | |
1865 _results = []; | |
1866 while (this.waitingForToken.length > 0) { | |
1867 _results.push(this.waitingForToken.pop()(this._unsafeToken)); | |
1868 } | |
1869 return _results; | |
1870 } else { | |
1871 console.warn(Annotator._t("Didn't get a valid token.")); | |
1872 if (this.options.autoFetch) { | |
1873 console.warn(Annotator._t("Getting a new token in 10s.")); | |
1874 return setTimeout((function() { | |
1875 return _this.requestToken(); | |
1876 }), 10 * 1000); | |
1877 } | |
1878 } | |
1879 }; | |
1880 | |
1881 Auth.prototype.haveValidToken = function() { | |
1882 var allFields; | |
1883 allFields = this._unsafeToken && this._unsafeToken.issuedAt && this._unsafeToken.ttl && this._unsafeToken.consumerKey; | |
1884 return allFields && this.timeToExpiry() > 0; | |
1885 }; | |
1886 | |
1887 Auth.prototype.timeToExpiry = function() { | |
1888 var expiry, issue, now, timeToExpiry; | |
1889 now = new Date().getTime() / 1000; | |
1890 issue = createDateFromISO8601(this._unsafeToken.issuedAt).getTime() / 1000; | |
1891 expiry = issue + this._unsafeToken.ttl; | |
1892 timeToExpiry = expiry - now; | |
1893 if (timeToExpiry > 0) { | |
1894 return timeToExpiry; | |
1895 } else { | |
1896 return 0; | |
1897 } | |
1898 }; | |
1899 | |
1900 Auth.prototype.updateHeaders = function() { | |
1901 var current; | |
1902 current = this.element.data('annotator:headers'); | |
1903 return this.element.data('annotator:headers', $.extend(current, { | |
1904 'x-annotator-auth-token': this.token | |
1905 })); | |
1906 }; | |
1907 | |
1908 Auth.prototype.withToken = function(callback) { | |
1909 if (!(callback != null)) { | |
1910 return; | |
1911 } | |
1912 if (this.haveValidToken()) { | |
1913 return callback(this._unsafeToken); | |
1914 } else { | |
1915 this.waitingForToken.push(callback); | |
1916 if (!this.requestInProgress) { | |
1917 return this.requestToken(); | |
1918 } | |
1919 } | |
1920 }; | |
1921 | |
1922 return Auth; | |
1923 | |
1924 })(Annotator.Plugin); | |
1925 | |
1926 Annotator.Plugin.Store = (function(_super) { | |
1927 | |
1928 __extends(Store, _super); | |
1929 | |
1930 Store.prototype.events = { | |
1931 'annotationCreated': 'annotationCreated', | |
1932 'annotationDeleted': 'annotationDeleted', | |
1933 'annotationUpdated': 'annotationUpdated' | |
1934 }; | |
1935 | |
1936 Store.prototype.options = { | |
1937 prefix: '/store', | |
1938 autoFetch: true, | |
1939 annotationData: {}, | |
1940 loadFromSearch: false, | |
1941 urls: { | |
1942 create: '/annotations', | |
1943 read: '/annotations/:id', | |
1944 update: '/annotations/:id', | |
1945 destroy: '/annotations/:id', | |
1946 search: '/search' | |
1947 } | |
1948 }; | |
1949 | |
1950 function Store(element, options) { | |
1951 this._onError = __bind(this._onError, this); | |
1952 | |
1953 this._onLoadAnnotationsFromSearch = __bind(this._onLoadAnnotationsFromSearch, this); | |
1954 | |
1955 this._onLoadAnnotations = __bind(this._onLoadAnnotations, this); | |
1956 | |
1957 this._getAnnotations = __bind(this._getAnnotations, this); | |
1958 Store.__super__.constructor.apply(this, arguments); | |
1959 this.annotations = []; | |
1960 } | |
1961 | |
1962 Store.prototype.pluginInit = function() { | |
1963 if (!Annotator.supported()) { | |
1964 return; | |
1965 } | |
1966 if (this.annotator.plugins.Auth) { | |
1967 return this.annotator.plugins.Auth.withToken(this._getAnnotations); | |
1968 } else { | |
1969 return this._getAnnotations(); | |
1970 } | |
1971 }; | |
1972 | |
1973 Store.prototype._getAnnotations = function() { | |
1974 if (this.options.loadFromSearch) { | |
1975 return this.loadAnnotationsFromSearch(this.options.loadFromSearch); | |
1976 } else { | |
1977 return this.loadAnnotations(); | |
1978 } | |
1979 }; | |
1980 | |
1981 Store.prototype.annotationCreated = function(annotation) { | |
1982 var _this = this; | |
1983 if (__indexOf.call(this.annotations, annotation) < 0) { | |
1984 this.registerAnnotation(annotation); | |
1985 return this._apiRequest('create', annotation, function(data) { | |
1986 if (!(data.id != null)) { | |
1987 console.warn(Annotator._t("Warning: No ID returned from server for annotation "), annotation); | |
1988 } | |
1989 return _this.updateAnnotation(annotation, data); | |
1990 }); | |
1991 } else { | |
1992 return this.updateAnnotation(annotation, {}); | |
1993 } | |
1994 }; | |
1995 | |
1996 Store.prototype.annotationUpdated = function(annotation) { | |
1997 var _this = this; | |
1998 if (__indexOf.call(this.annotations, annotation) >= 0) { | |
1999 return this._apiRequest('update', annotation, (function(data) { | |
2000 return _this.updateAnnotation(annotation, data); | |
2001 })); | |
2002 } | |
2003 }; | |
2004 | |
2005 Store.prototype.annotationDeleted = function(annotation) { | |
2006 var _this = this; | |
2007 if (__indexOf.call(this.annotations, annotation) >= 0) { | |
2008 return this._apiRequest('destroy', annotation, (function() { | |
2009 return _this.unregisterAnnotation(annotation); | |
2010 })); | |
2011 } | |
2012 }; | |
2013 | |
2014 Store.prototype.registerAnnotation = function(annotation) { | |
2015 return this.annotations.push(annotation); | |
2016 }; | |
2017 | |
2018 Store.prototype.unregisterAnnotation = function(annotation) { | |
2019 return this.annotations.splice(this.annotations.indexOf(annotation), 1); | |
2020 }; | |
2021 | |
2022 Store.prototype.updateAnnotation = function(annotation, data) { | |
2023 if (__indexOf.call(this.annotations, annotation) < 0) { | |
2024 console.error(Annotator._t("Trying to update unregistered annotation!")); | |
2025 } else { | |
2026 $.extend(annotation, data); | |
2027 } | |
2028 return $(annotation.highlights).data('annotation', annotation); | |
2029 }; | |
2030 | |
2031 Store.prototype.loadAnnotations = function() { | |
2032 return this._apiRequest('read', null, this._onLoadAnnotations); | |
2033 }; | |
2034 | |
2035 Store.prototype._onLoadAnnotations = function(data) { | |
2036 if (data == null) { | |
2037 data = []; | |
2038 } | |
2039 this.annotations = data; | |
2040 return this.annotator.loadAnnotations(data.slice()); | |
2041 }; | |
2042 | |
2043 Store.prototype.loadAnnotationsFromSearch = function(searchOptions) { | |
2044 return this._apiRequest('search', searchOptions, this._onLoadAnnotationsFromSearch); | |
2045 }; | |
2046 | |
2047 Store.prototype._onLoadAnnotationsFromSearch = function(data) { | |
2048 if (data == null) { | |
2049 data = {}; | |
2050 } | |
2051 return this._onLoadAnnotations(data.rows || []); | |
2052 }; | |
2053 | |
2054 Store.prototype.dumpAnnotations = function() { | |
2055 var ann, _k, _len2, _ref2, _results; | |
2056 _ref2 = this.annotations; | |
2057 _results = []; | |
2058 for (_k = 0, _len2 = _ref2.length; _k < _len2; _k++) { | |
2059 ann = _ref2[_k]; | |
2060 _results.push(JSON.parse(this._dataFor(ann))); | |
2061 } | |
2062 return _results; | |
2063 }; | |
2064 | |
2065 Store.prototype._apiRequest = function(action, obj, onSuccess) { | |
2066 var id, options, request, url; | |
2067 id = obj && obj.id; | |
2068 url = this._urlFor(action, id); | |
2069 options = this._apiRequestOptions(action, obj, onSuccess); | |
2070 request = $.ajax(url, options); | |
2071 request._id = id; | |
2072 request._action = action; | |
2073 return request; | |
2074 }; | |
2075 | |
2076 Store.prototype._apiRequestOptions = function(action, obj, onSuccess) { | |
2077 var opts; | |
2078 opts = { | |
2079 type: this._methodFor(action), | |
2080 headers: this.element.data('annotator:headers'), | |
2081 dataType: "json", | |
2082 success: onSuccess || function() {}, | |
2083 error: this._onError | |
2084 }; | |
2085 if (action === "search") { | |
2086 opts = $.extend(opts, { | |
2087 data: obj | |
2088 }); | |
2089 } else { | |
2090 opts = $.extend(opts, { | |
2091 data: obj && this._dataFor(obj), | |
2092 contentType: "application/json; charset=utf-8" | |
2093 }); | |
2094 } | |
2095 return opts; | |
2096 }; | |
2097 | |
2098 Store.prototype._urlFor = function(action, id) { | |
2099 var replaceWith, url; | |
2100 replaceWith = id != null ? '/' + id : ''; | |
2101 url = this.options.prefix || '/'; | |
2102 url += this.options.urls[action]; | |
2103 url = url.replace(/\/:id/, replaceWith); | |
2104 return url; | |
2105 }; | |
2106 | |
2107 Store.prototype._methodFor = function(action) { | |
2108 var table; | |
2109 table = { | |
2110 'create': 'POST', | |
2111 'read': 'GET', | |
2112 'update': 'PUT', | |
2113 'destroy': 'DELETE', | |
2114 'search': 'GET' | |
2115 }; | |
2116 return table[action]; | |
2117 }; | |
2118 | |
2119 Store.prototype._dataFor = function(annotation) { | |
2120 var data, highlights; | |
2121 highlights = annotation.highlights; | |
2122 delete annotation.highlights; | |
2123 $.extend(annotation, this.options.annotationData); | |
2124 data = JSON.stringify(annotation); | |
2125 if (highlights) { | |
2126 annotation.highlights = highlights; | |
2127 } | |
2128 return data; | |
2129 }; | |
2130 | |
2131 Store.prototype._onError = function(xhr) { | |
2132 var action, message; | |
2133 action = xhr._action; | |
2134 message = Annotator._t("Sorry we could not ") + action + Annotator._t(" this annotation"); | |
2135 if (xhr._action === 'search') { | |
2136 message = Annotator._t("Sorry we could not search the store for annotations"); | |
2137 } else if (xhr._action === 'read' && !xhr._id) { | |
2138 message = Annotator._t("Sorry we could not ") + action + Annotator._t(" the annotations from the store"); | |
2139 } | |
2140 switch (xhr.status) { | |
2141 case 401: | |
2142 message = Annotator._t("Sorry you are not allowed to ") + action + Annotator._t(" this annotation"); | |
2143 break; | |
2144 case 404: | |
2145 message = Annotator._t("Sorry we could not connect to the annotations store"); | |
2146 break; | |
2147 case 500: | |
2148 message = Annotator._t("Sorry something went wrong with the annotation store"); | |
2149 } | |
2150 Annotator.showNotification(message, Annotator.Notification.ERROR); | |
2151 return console.error(Annotator._t("API request failed:") + (" '" + xhr.status + "'")); | |
2152 }; | |
2153 | |
2154 return Store; | |
2155 | |
2156 })(Annotator.Plugin); | |
2157 | |
2158 Annotator.Plugin.Permissions = (function(_super) { | |
2159 | |
2160 __extends(Permissions, _super); | |
2161 | |
2162 Permissions.prototype.events = { | |
2163 'beforeAnnotationCreated': 'addFieldsToAnnotation' | |
2164 }; | |
2165 | |
2166 Permissions.prototype.options = { | |
2167 showViewPermissionsCheckbox: true, | |
2168 showEditPermissionsCheckbox: true, | |
2169 userId: function(user) { | |
2170 return user; | |
2171 }, | |
2172 userString: function(user) { | |
2173 return user; | |
2174 }, | |
2175 userAuthorize: function(action, annotation, user) { | |
2176 var token, tokens, _k, _len2; | |
2177 if (annotation.permissions) { | |
2178 tokens = annotation.permissions[action] || []; | |
2179 if (tokens.length === 0) { | |
2180 return true; | |
2181 } | |
2182 for (_k = 0, _len2 = tokens.length; _k < _len2; _k++) { | |
2183 token = tokens[_k]; | |
2184 if (this.userId(user) === token) { | |
2185 return true; | |
2186 } | |
2187 } | |
2188 return false; | |
2189 } else if (annotation.user) { | |
2190 return user && this.userId(user) === this.userId(annotation.user); | |
2191 } | |
2192 return true; | |
2193 }, | |
2194 user: '', | |
2195 permissions: { | |
2196 'read': [], | |
2197 'update': [], | |
2198 'delete': [], | |
2199 'admin': [] | |
2200 } | |
2201 }; | |
2202 | |
2203 function Permissions(element, options) { | |
2204 this._setAuthFromToken = __bind(this._setAuthFromToken, this); | |
2205 | |
2206 this.updateViewer = __bind(this.updateViewer, this); | |
2207 | |
2208 this.updateAnnotationPermissions = __bind(this.updateAnnotationPermissions, this); | |
2209 | |
2210 this.updatePermissionsField = __bind(this.updatePermissionsField, this); | |
2211 | |
2212 this.addFieldsToAnnotation = __bind(this.addFieldsToAnnotation, this); | |
2213 Permissions.__super__.constructor.apply(this, arguments); | |
2214 if (this.options.user) { | |
2215 this.setUser(this.options.user); | |
2216 delete this.options.user; | |
2217 } | |
2218 } | |
2219 | |
2220 Permissions.prototype.pluginInit = function() { | |
2221 var createCallback, self, | |
2222 _this = this; | |
2223 if (!Annotator.supported()) { | |
2224 return; | |
2225 } | |
2226 self = this; | |
2227 createCallback = function(method, type) { | |
2228 return function(field, annotation) { | |
2229 return self[method].call(self, type, field, annotation); | |
2230 }; | |
2231 }; | |
2232 if (!this.user && this.annotator.plugins.Auth) { | |
2233 this.annotator.plugins.Auth.withToken(this._setAuthFromToken); | |
2234 } | |
2235 if (this.options.showViewPermissionsCheckbox === true) { | |
2236 this.annotator.editor.addField({ | |
2237 type: 'checkbox', | |
2238 label: Annotator._t('Allow anyone to <strong>view</strong> this annotation'), | |
2239 load: createCallback('updatePermissionsField', 'read'), | |
2240 submit: createCallback('updateAnnotationPermissions', 'read') | |
2241 }); | |
2242 } | |
2243 if (this.options.showEditPermissionsCheckbox === true) { | |
2244 this.annotator.editor.addField({ | |
2245 type: 'checkbox', | |
2246 label: Annotator._t('Allow anyone to <strong>edit</strong> this annotation'), | |
2247 load: createCallback('updatePermissionsField', 'update'), | |
2248 submit: createCallback('updateAnnotationPermissions', 'update') | |
2249 }); | |
2250 } | |
2251 this.annotator.viewer.addField({ | |
2252 load: this.updateViewer | |
2253 }); | |
2254 if (this.annotator.plugins.Filter) { | |
2255 return this.annotator.plugins.Filter.addFilter({ | |
2256 label: Annotator._t('User'), | |
2257 property: 'user', | |
2258 isFiltered: function(input, user) { | |
2259 var keyword, _k, _len2, _ref2; | |
2260 user = _this.options.userString(user); | |
2261 if (!(input && user)) { | |
2262 return false; | |
2263 } | |
2264 _ref2 = input.split(/\s*/); | |
2265 for (_k = 0, _len2 = _ref2.length; _k < _len2; _k++) { | |
2266 keyword = _ref2[_k]; | |
2267 if (user.indexOf(keyword) === -1) { | |
2268 return false; | |
2269 } | |
2270 } | |
2271 return true; | |
2272 } | |
2273 }); | |
2274 } | |
2275 }; | |
2276 | |
2277 Permissions.prototype.setUser = function(user) { | |
2278 return this.user = user; | |
2279 }; | |
2280 | |
2281 Permissions.prototype.addFieldsToAnnotation = function(annotation) { | |
2282 if (annotation) { | |
2283 annotation.permissions = this.options.permissions; | |
2284 if (this.user) { | |
2285 return annotation.user = this.user; | |
2286 } | |
2287 } | |
2288 }; | |
2289 | |
2290 Permissions.prototype.authorize = function(action, annotation, user) { | |
2291 if (user === void 0) { | |
2292 user = this.user; | |
2293 } | |
2294 if (this.options.userAuthorize) { | |
2295 return this.options.userAuthorize.call(this.options, action, annotation, user); | |
2296 } else { | |
2297 return true; | |
2298 } | |
2299 }; | |
2300 | |
2301 Permissions.prototype.updatePermissionsField = function(action, field, annotation) { | |
2302 var input; | |
2303 field = $(field).show(); | |
2304 input = field.find('input').removeAttr('disabled'); | |
2305 if (!this.authorize('admin', annotation)) { | |
2306 field.hide(); | |
2307 } | |
2308 if (this.authorize(action, annotation || {}, null)) { | |
2309 return input.attr('checked', 'checked'); | |
2310 } else { | |
2311 return input.removeAttr('checked'); | |
2312 } | |
2313 }; | |
2314 | |
2315 Permissions.prototype.updateAnnotationPermissions = function(type, field, annotation) { | |
2316 var dataKey; | |
2317 if (!annotation.permissions) { | |
2318 annotation.permissions = this.options.permissions; | |
2319 } | |
2320 dataKey = type + '-permissions'; | |
2321 if ($(field).find('input').is(':checked')) { | |
2322 return annotation.permissions[type] = []; | |
2323 } else { | |
2324 return annotation.permissions[type] = [this.user]; | |
2325 } | |
2326 }; | |
2327 | |
2328 Permissions.prototype.updateViewer = function(field, annotation, controls) { | |
2329 var user, username; | |
2330 field = $(field); | |
2331 username = this.options.userString(annotation.user); | |
2332 if (annotation.user && username && typeof username === 'string') { | |
2333 user = Annotator.$.escape(this.options.userString(annotation.user)); | |
2334 field.html(user).addClass('annotator-user'); | |
2335 } else { | |
2336 field.remove(); | |
2337 } | |
2338 if (controls) { | |
2339 if (!this.authorize('update', annotation)) { | |
2340 controls.hideEdit(); | |
2341 } | |
2342 if (!this.authorize('delete', annotation)) { | |
2343 return controls.hideDelete(); | |
2344 } | |
2345 } | |
2346 }; | |
2347 | |
2348 Permissions.prototype._setAuthFromToken = function(token) { | |
2349 return this.setUser(token.userId); | |
2350 }; | |
2351 | |
2352 return Permissions; | |
2353 | |
2354 })(Annotator.Plugin); | |
2355 | |
2356 Annotator.Plugin.Filter = (function(_super) { | |
2357 | |
2358 __extends(Filter, _super); | |
2359 | |
2360 Filter.prototype.events = { | |
2361 ".annotator-filter-property input focus": "_onFilterFocus", | |
2362 ".annotator-filter-property input blur": "_onFilterBlur", | |
2363 ".annotator-filter-property input keyup": "_onFilterKeyup", | |
2364 ".annotator-filter-previous click": "_onPreviousClick", | |
2365 ".annotator-filter-next click": "_onNextClick", | |
2366 ".annotator-filter-clear click": "_onClearClick" | |
2367 }; | |
2368 | |
2369 Filter.prototype.classes = { | |
2370 active: 'annotator-filter-active', | |
2371 hl: { | |
2372 hide: 'annotator-hl-filtered', | |
2373 active: 'annotator-hl-active' | |
2374 } | |
2375 }; | |
2376 | |
2377 Filter.prototype.html = { | |
2378 element: "<div class=\"annotator-filter\">\n <strong>" + Annotator._t('Navigate:') + "</strong>\n<span class=\"annotator-filter-navigation\">\n <button class=\"annotator-filter-previous\">" + Annotator._t('Previous') + "</button>\n<button class=\"annotator-filter-next\">" + Annotator._t('Next') + "</button>\n</span>\n<strong>" + Annotator._t('Filter by:') + "</strong>\n</div>", | |
2379 filter: "<span class=\"annotator-filter-property\">\n <label></label>\n <input/>\n <button class=\"annotator-filter-clear\">" + Annotator._t('Clear') + "</button>\n</span>" | |
2380 }; | |
2381 | |
2382 Filter.prototype.options = { | |
2383 appendTo: 'body', | |
2384 filters: [], | |
2385 addAnnotationFilter: true, | |
2386 isFiltered: function(input, property) { | |
2387 var keyword, _k, _len2, _ref2; | |
2388 if (!(input && property)) { | |
2389 return false; | |
2390 } | |
2391 _ref2 = input.split(/\s*/); | |
2392 for (_k = 0, _len2 = _ref2.length; _k < _len2; _k++) { | |
2393 keyword = _ref2[_k]; | |
2394 if (property.indexOf(keyword) === -1) { | |
2395 return false; | |
2396 } | |
2397 } | |
2398 return true; | |
2399 } | |
2400 }; | |
2401 | |
2402 function Filter(element, options) { | |
2403 this._onPreviousClick = __bind(this._onPreviousClick, this); | |
2404 | |
2405 this._onNextClick = __bind(this._onNextClick, this); | |
2406 | |
2407 this._onFilterKeyup = __bind(this._onFilterKeyup, this); | |
2408 | |
2409 this._onFilterBlur = __bind(this._onFilterBlur, this); | |
2410 | |
2411 this._onFilterFocus = __bind(this._onFilterFocus, this); | |
2412 | |
2413 this.updateHighlights = __bind(this.updateHighlights, this); | |
2414 | |
2415 var _base; | |
2416 element = $(this.html.element).appendTo((options != null ? options.appendTo : void 0) || this.options.appendTo); | |
2417 Filter.__super__.constructor.call(this, element, options); | |
2418 (_base = this.options).filters || (_base.filters = []); | |
2419 this.filter = $(this.html.filter); | |
2420 this.filters = []; | |
2421 this.current = 0; | |
2422 } | |
2423 | |
2424 Filter.prototype.pluginInit = function() { | |
2425 var filter, _k, _len2, _ref2; | |
2426 _ref2 = this.options.filters; | |
2427 for (_k = 0, _len2 = _ref2.length; _k < _len2; _k++) { | |
2428 filter = _ref2[_k]; | |
2429 this.addFilter(filter); | |
2430 } | |
2431 this.updateHighlights(); | |
2432 this._setupListeners()._insertSpacer(); | |
2433 if (this.options.addAnnotationFilter === true) { | |
2434 return this.addFilter({ | |
2435 label: Annotator._t('Annotation'), | |
2436 property: 'text' | |
2437 }); | |
2438 } | |
2439 }; | |
2440 | |
2441 Filter.prototype._insertSpacer = function() { | |
2442 var currentMargin, html; | |
2443 html = $('html'); | |
2444 currentMargin = parseInt(html.css('padding-top'), 10) || 0; | |
2445 html.css('padding-top', currentMargin + this.element.outerHeight()); | |
2446 return this; | |
2447 }; | |
2448 | |
2449 Filter.prototype._setupListeners = function() { | |
2450 var event, events, _k, _len2; | |
2451 events = ['annotationsLoaded', 'annotationCreated', 'annotationUpdated', 'annotationDeleted']; | |
2452 for (_k = 0, _len2 = events.length; _k < _len2; _k++) { | |
2453 event = events[_k]; | |
2454 this.annotator.subscribe(event, this.updateHighlights); | |
2455 } | |
2456 return this; | |
2457 }; | |
2458 | |
2459 Filter.prototype.addFilter = function(options) { | |
2460 var f, filter; | |
2461 filter = $.extend({ | |
2462 label: '', | |
2463 property: '', | |
2464 isFiltered: this.options.isFiltered | |
2465 }, options); | |
2466 if (!((function() { | |
2467 var _k, _len2, _ref2, _results; | |
2468 _ref2 = this.filters; | |
2469 _results = []; | |
2470 for (_k = 0, _len2 = _ref2.length; _k < _len2; _k++) { | |
2471 f = _ref2[_k]; | |
2472 if (f.property === filter.property) { | |
2473 _results.push(f); | |
2474 } | |
2475 } | |
2476 return _results; | |
2477 }).call(this)).length) { | |
2478 filter.id = 'annotator-filter-' + filter.property; | |
2479 filter.annotations = []; | |
2480 filter.element = this.filter.clone().appendTo(this.element); | |
2481 filter.element.find('label').html(filter.label).attr('for', filter.id); | |
2482 filter.element.find('input').attr({ | |
2483 id: filter.id, | |
2484 placeholder: Annotator._t('Filter by ') + filter.label + '\u2026' | |
2485 }); | |
2486 filter.element.find('button').hide(); | |
2487 filter.element.data('filter', filter); | |
2488 this.filters.push(filter); | |
2489 } | |
2490 return this; | |
2491 }; | |
2492 | |
2493 Filter.prototype.updateFilter = function(filter) { | |
2494 var annotation, annotations, input, property, _k, _len2, _ref2; | |
2495 filter.annotations = []; | |
2496 this.updateHighlights(); | |
2497 this.resetHighlights(); | |
2498 input = $.trim(filter.element.find('input').val()); | |
2499 if (input) { | |
2500 annotations = this.highlights.map(function() { | |
2501 return $(this).data('annotation'); | |
2502 }); | |
2503 _ref2 = $.makeArray(annotations); | |
2504 for (_k = 0, _len2 = _ref2.length; _k < _len2; _k++) { | |
2505 annotation = _ref2[_k]; | |
2506 property = annotation[filter.property]; | |
2507 if (filter.isFiltered(input, property)) { | |
2508 filter.annotations.push(annotation); | |
2509 } | |
2510 } | |
2511 return this.filterHighlights(); | |
2512 } | |
2513 }; | |
2514 | |
2515 Filter.prototype.updateHighlights = function() { | |
2516 this.highlights = this.annotator.element.find('.annotator-hl:visible'); | |
2517 return this.filtered = this.highlights.not(this.classes.hl.hide); | |
2518 }; | |
2519 | |
2520 Filter.prototype.filterHighlights = function() { | |
2521 var activeFilters, annotation, annotations, filtered, highlights, index, uniques, _k, _len2, _ref2; | |
2522 activeFilters = $.grep(this.filters, function(filter) { | |
2523 return !!filter.annotations.length; | |
2524 }); | |
2525 filtered = ((_ref2 = activeFilters[0]) != null ? _ref2.annotations : void 0) || []; | |
2526 if (activeFilters.length > 1) { | |
2527 annotations = []; | |
2528 $.each(activeFilters, function() { | |
2529 return $.merge(annotations, this.annotations); | |
2530 }); | |
2531 uniques = []; | |
2532 filtered = []; | |
2533 $.each(annotations, function() { | |
2534 if ($.inArray(this, uniques) === -1) { | |
2535 return uniques.push(this); | |
2536 } else { | |
2537 return filtered.push(this); | |
2538 } | |
2539 }); | |
2540 } | |
2541 highlights = this.highlights; | |
2542 for (index = _k = 0, _len2 = filtered.length; _k < _len2; index = ++_k) { | |
2543 annotation = filtered[index]; | |
2544 highlights = highlights.not(annotation.highlights); | |
2545 } | |
2546 highlights.addClass(this.classes.hl.hide); | |
2547 this.filtered = this.highlights.not(this.classes.hl.hide); | |
2548 return this; | |
2549 }; | |
2550 | |
2551 Filter.prototype.resetHighlights = function() { | |
2552 this.highlights.removeClass(this.classes.hl.hide); | |
2553 this.filtered = this.highlights; | |
2554 return this; | |
2555 }; | |
2556 | |
2557 Filter.prototype._onFilterFocus = function(event) { | |
2558 var input; | |
2559 input = $(event.target); | |
2560 input.parent().addClass(this.classes.active); | |
2561 return input.next('button').show(); | |
2562 }; | |
2563 | |
2564 Filter.prototype._onFilterBlur = function(event) { | |
2565 var input; | |
2566 if (!event.target.value) { | |
2567 input = $(event.target); | |
2568 input.parent().removeClass(this.classes.active); | |
2569 return input.next('button').hide(); | |
2570 } | |
2571 }; | |
2572 | |
2573 Filter.prototype._onFilterKeyup = function(event) { | |
2574 var filter; | |
2575 filter = $(event.target).parent().data('filter'); | |
2576 if (filter) { | |
2577 return this.updateFilter(filter); | |
2578 } | |
2579 }; | |
2580 | |
2581 Filter.prototype._findNextHighlight = function(previous) { | |
2582 var active, annotation, current, index, next, offset, operator, resetOffset; | |
2583 if (!this.highlights.length) { | |
2584 return this; | |
2585 } | |
2586 offset = previous ? 0 : -1; | |
2587 resetOffset = previous ? -1 : 0; | |
2588 operator = previous ? 'lt' : 'gt'; | |
2589 active = this.highlights.not('.' + this.classes.hl.hide); | |
2590 current = active.filter('.' + this.classes.hl.active); | |
2591 if (!current.length) { | |
2592 current = active.eq(offset); | |
2593 } | |
2594 annotation = current.data('annotation'); | |
2595 index = active.index(current[0]); | |
2596 next = active.filter(":" + operator + "(" + index + ")").not(annotation.highlights).eq(resetOffset); | |
2597 if (!next.length) { | |
2598 next = active.eq(resetOffset); | |
2599 } | |
2600 return this._scrollToHighlight(next.data('annotation').highlights); | |
2601 }; | |
2602 | |
2603 Filter.prototype._onNextClick = function(event) { | |
2604 return this._findNextHighlight(); | |
2605 }; | |
2606 | |
2607 Filter.prototype._onPreviousClick = function(event) { | |
2608 return this._findNextHighlight(true); | |
2609 }; | |
2610 | |
2611 Filter.prototype._scrollToHighlight = function(highlight) { | |
2612 highlight = $(highlight); | |
2613 this.highlights.removeClass(this.classes.hl.active); | |
2614 highlight.addClass(this.classes.hl.active); | |
2615 return $('html, body').animate({ | |
2616 scrollTop: highlight.offset().top - (this.element.height() + 20) | |
2617 }, 150); | |
2618 }; | |
2619 | |
2620 Filter.prototype._onClearClick = function(event) { | |
2621 return $(event.target).prev('input').val('').keyup().blur(); | |
2622 }; | |
2623 | |
2624 return Filter; | |
2625 | |
2626 })(Annotator.Plugin); | |
2627 | |
2628 Annotator.Plugin.Markdown = (function(_super) { | |
2629 | |
2630 __extends(Markdown, _super); | |
2631 | |
2632 Markdown.prototype.events = { | |
2633 'annotationViewerTextField': 'updateTextField' | |
2634 }; | |
2635 | |
2636 function Markdown(element, options) { | |
2637 this.updateTextField = __bind(this.updateTextField, this); | |
2638 if ((typeof Showdown !== "undefined" && Showdown !== null ? Showdown.converter : void 0) != null) { | |
2639 Markdown.__super__.constructor.apply(this, arguments); | |
2640 this.converter = new Showdown.converter(); | |
2641 } else { | |
2642 console.error(Annotator._t("To use the Markdown plugin, you must include Showdown into the page first.")); | |
2643 } | |
2644 } | |
2645 | |
2646 Markdown.prototype.updateTextField = function(field, annotation) { | |
2647 var text; | |
2648 text = Annotator.$.escape(annotation.text || ''); | |
2649 return $(field).html(this.convert(text)); | |
2650 }; | |
2651 | |
2652 Markdown.prototype.convert = function(text) { | |
2653 return this.converter.makeHtml(text); | |
2654 }; | |
2655 | |
2656 return Markdown; | |
2657 | |
2658 })(Annotator.Plugin); | |
2659 | |
2660 Annotator.Plugin.Tags = (function(_super) { | |
2661 | |
2662 __extends(Tags, _super); | |
2663 | |
2664 function Tags() { | |
2665 this.setAnnotationTags = __bind(this.setAnnotationTags, this); | |
2666 | |
2667 this.updateField = __bind(this.updateField, this); | |
2668 return Tags.__super__.constructor.apply(this, arguments); | |
2669 } | |
2670 | |
2671 Tags.prototype.options = { | |
2672 parseTags: function(string) { | |
2673 var tags; | |
2674 string = $.trim(string); | |
2675 tags = []; | |
2676 if (string) { | |
2677 tags = string.split(/\s+/); | |
2678 } | |
2679 return tags; | |
2680 }, | |
2681 stringifyTags: function(array) { | |
2682 return array.join(" "); | |
2683 } | |
2684 }; | |
2685 | |
2686 Tags.prototype.field = null; | |
2687 | |
2688 Tags.prototype.input = null; | |
2689 | |
2690 Tags.prototype.pluginInit = function() { | |
2691 if (!Annotator.supported()) { | |
2692 return; | |
2693 } | |
2694 this.field = this.annotator.editor.addField({ | |
2695 label: Annotator._t('Add some tags here') + '\u2026', | |
2696 load: this.updateField, | |
2697 submit: this.setAnnotationTags | |
2698 }); | |
2699 this.annotator.viewer.addField({ | |
2700 load: this.updateViewer | |
2701 }); | |
2702 if (this.annotator.plugins.Filter) { | |
2703 this.annotator.plugins.Filter.addFilter({ | |
2704 label: Annotator._t('Tag'), | |
2705 property: 'tags', | |
2706 isFiltered: Annotator.Plugin.Tags.filterCallback | |
2707 }); | |
2708 } | |
2709 return this.input = $(this.field).find(':input'); | |
2710 }; | |
2711 | |
2712 Tags.prototype.parseTags = function(string) { | |
2713 return this.options.parseTags(string); | |
2714 }; | |
2715 | |
2716 Tags.prototype.stringifyTags = function(array) { | |
2717 return this.options.stringifyTags(array); | |
2718 }; | |
2719 | |
2720 Tags.prototype.updateField = function(field, annotation) { | |
2721 var value; | |
2722 value = ''; | |
2723 if (annotation.tags) { | |
2724 value = this.stringifyTags(annotation.tags); | |
2725 } | |
2726 return this.input.val(value); | |
2727 }; | |
2728 | |
2729 Tags.prototype.setAnnotationTags = function(field, annotation) { | |
2730 return annotation.tags = this.parseTags(this.input.val()); | |
2731 }; | |
2732 | |
2733 Tags.prototype.updateViewer = function(field, annotation) { | |
2734 field = $(field); | |
2735 if (annotation.tags && $.isArray(annotation.tags) && annotation.tags.length) { | |
2736 return field.addClass('annotator-tags').html(function() { | |
2737 var string; | |
2738 return string = $.map(annotation.tags, function(tag) { | |
2739 return '<span class="annotator-tag">' + Annotator.$.escape(tag) + '</span>'; | |
2740 }).join(' '); | |
2741 }); | |
2742 } else { | |
2743 return field.remove(); | |
2744 } | |
2745 }; | |
2746 | |
2747 return Tags; | |
2748 | |
2749 })(Annotator.Plugin); | |
2750 | |
2751 Annotator.Plugin.Tags.filterCallback = function(input, tags) { | |
2752 var keyword, keywords, matches, tag, _k, _l, _len2, _len3; | |
2753 if (tags == null) { | |
2754 tags = []; | |
2755 } | |
2756 matches = 0; | |
2757 keywords = []; | |
2758 if (input) { | |
2759 keywords = input.split(/\s+/g); | |
2760 for (_k = 0, _len2 = keywords.length; _k < _len2; _k++) { | |
2761 keyword = keywords[_k]; | |
2762 if (tags.length) { | |
2763 for (_l = 0, _len3 = tags.length; _l < _len3; _l++) { | |
2764 tag = tags[_l]; | |
2765 if (tag.indexOf(keyword) !== -1) { | |
2766 matches += 1; | |
2767 } | |
2768 } | |
2769 } | |
2770 } | |
2771 } | |
2772 return matches === keywords.length; | |
2773 }; | |
2774 | |
2775 Annotator.Plugin.DigilibIntegrator = (function(_super) { | |
2776 | |
2777 __extends(DigilibIntegrator, _super); | |
2778 | |
2779 function DigilibIntegrator() { | |
2780 return DigilibIntegrator.__super__.constructor.apply(this, arguments); | |
2781 } | |
2782 | |
2783 DigilibIntegrator.prototype.events = { | |
2784 'annotationDeleted': 'annotationDeleted' | |
2785 }; | |
2786 | |
2787 DigilibIntegrator.prototype.options = { | |
2788 hooks: null | |
2789 }; | |
2790 | |
2791 DigilibIntegrator.prototype.pluginInit = function() { | |
2792 this.annotator.digilib = this.options.hooks; | |
2793 this.annotator.setupRangeAnnotation = this.annotator.setupAnnotation; | |
2794 this.annotator.setupAnnotation = this._setupAnnotation; | |
2795 return this; | |
2796 }; | |
2797 | |
2798 DigilibIntegrator.prototype._setupAnnotation = function(annotation, fireEvents) { | |
2799 if (fireEvents == null) { | |
2800 fireEvents = true; | |
2801 } | |
2802 if (this.selectedShapes || (annotation.shapes != null) || (this.selectedAreas != null) || (annotation.areas != null)) { | |
2803 annotation.shapes || (annotation.shapes = this.selectedShapes); | |
2804 annotation.areas || (annotation.areas = this.selectedAreas); | |
2805 annotation.highlights = []; | |
2806 annotation.ranges = []; | |
2807 this.digilib.setupAnnotation(annotation); | |
2808 if (fireEvents) { | |
2809 this.publish('annotationCreated', [annotation]); | |
2810 } | |
2811 return annotation; | |
2812 } else { | |
2813 return this.setupRangeAnnotation.apply(this, arguments); | |
2814 } | |
2815 }; | |
2816 | |
2817 DigilibIntegrator.prototype.annotationDeleted = function(annotation) { | |
2818 return this.options.hooks.annotationDeleted(annotation); | |
2819 }; | |
2820 | |
2821 return DigilibIntegrator; | |
2822 | |
2823 })(Annotator.Plugin); | |
2824 | |
2825 }).call(this); |