# HG changeset patch
# User robcast
# Date 1421428815 -3600
# Node ID 49d643f0d6587cad5b9d027b5ffed994c7894f4c
# Parent 7d9132a513ab4c2e8a1cf4505197c6bd88ad8236
reverted back to stable branch Annotator.js-1.2.5.
diff -r 7d9132a513ab -r 49d643f0d658 webapp/src/main/webapp/jquery/annotator-dl.js
--- a/webapp/src/main/webapp/jquery/annotator-dl.js Mon Dec 15 18:49:42 2014 +0100
+++ b/webapp/src/main/webapp/jquery/annotator-dl.js Fri Jan 16 18:20:15 2015 +0100
@@ -1,747 +1,886 @@
/*
-** Annotator v2.0.0-dev-e25ce72
+** Annotator 1.2.5-dev-a4cd304
** https://github.com/okfn/annotator/
**
-** Copyright 2014, the Annotator project contributors.
+** Copyright 2012 Aron Carroll, Rufus Pollock, and Nick Stenning.
** Dual licensed under the MIT and GPLv3 licenses.
** https://github.com/okfn/annotator/blob/master/LICENSE
**
-** Built at: 2014-03-14 15:39:01Z
+** Built at: 2012-11-23 09:46:08Z
*/
-!function(e){if("object"==typeof exports)module.exports=e();else if("function"==typeof define&&define.amd)define(e);else{var n;"undefined"!=typeof window?n=window:"undefined"!=typeof global?n=global:"undefined"!=typeof self&&(n=self),n.Annotator=e()}}(function(){var define,module,exports;return (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);throw new Error("Cannot find module '"+o+"'")}var f=n[o]={exports:{}};t[o][0].call(f.exports,function(e){var n=t[o][1][e];return s(n?n:e)},f,f.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o/g, '>').replace(/"/g, '"');
+ };
- // Bind an event to only be triggered a single time. After the first time
- // the callback is invoked, it will be removed.
- once: function(name, callback, context) {
- if (!eventsApi(this, 'once', name, [callback, context]) || !callback) return this;
- var self = this;
- var once = _.once(function() {
- self.off(name, once);
- callback.apply(this, arguments);
- });
- once._callback = callback;
- return this.on(name, once, context);
- },
+ $.fn.escape = function(html) {
+ if (arguments.length) {
+ return this.html($.escape(html));
+ }
+ return this.html();
+ };
+
+ $.fn.reverse = []._reverse || [].reverse;
+
+ functions = ["log", "debug", "info", "warn", "exception", "assert", "dir", "dirxml", "trace", "group", "groupEnd", "groupCollapsed", "time", "timeEnd", "profile", "profileEnd", "count", "clear", "table", "error", "notifyFirebug", "firebug", "userObjects"];
- // Remove one or many callbacks. If `context` is null, removes all
- // callbacks with that function. If `callback` is null, removes all
- // callbacks for the event. If `name` is null, removes all bound
- // callbacks for all events.
- off: function(name, callback, context) {
- var retain, ev, events, names, i, l, j, k;
- if (!this._events || !eventsApi(this, 'off', name, [callback, context])) return this;
- if (!name && !callback && !context) {
- this._events = {};
- return this;
+ if (typeof console !== "undefined" && console !== null) {
+ if (!(console.group != null)) {
+ console.group = function(name) {
+ return console.log("GROUP: ", name);
+ };
+ }
+ if (!(console.groupCollapsed != null)) {
+ console.groupCollapsed = console.group;
+ }
+ for (_i = 0, _len = functions.length; _i < _len; _i++) {
+ fn = functions[_i];
+ if (!(console[fn] != null)) {
+ console[fn] = function() {
+ return console.log(_t("Not implemented:") + (" console." + name));
+ };
}
+ }
+ } else {
+ this.console = {};
+ for (_j = 0, _len1 = functions.length; _j < _len1; _j++) {
+ fn = functions[_j];
+ this.console[fn] = function() {};
+ }
+ this.console['error'] = function() {
+ var args;
+ args = 1 <= arguments.length ? __slice.call(arguments, 0) : [];
+ return alert("ERROR: " + (args.join(', ')));
+ };
+ this.console['warn'] = function() {
+ var args;
+ args = 1 <= arguments.length ? __slice.call(arguments, 0) : [];
+ return alert("WARNING: " + (args.join(', ')));
+ };
+ }
+
+ Delegator = (function() {
+
+ Delegator.prototype.events = {};
- names = name ? [name] : _.keys(this._events);
- for (i = 0, l = names.length; i < l; i++) {
- name = names[i];
- if (events = this._events[name]) {
- this._events[name] = retain = [];
- if (callback || context) {
- for (j = 0, k = events.length; j < k; j++) {
- ev = events[j];
- if ((callback && callback !== ev.callback && callback !== ev.callback._callback) ||
- (context && context !== ev.context)) {
- retain.push(ev);
- }
- }
- }
- if (!retain.length) delete this._events[name];
+ Delegator.prototype.options = {};
+
+ Delegator.prototype.element = null;
+
+ function Delegator(element, options) {
+ this.options = $.extend(true, {}, this.options, options);
+ this.element = $(element);
+ this.on = this.subscribe;
+ this.addEvents();
+ }
+
+ Delegator.prototype.addEvents = function() {
+ var event, functionName, sel, selector, _k, _ref1, _ref2, _results;
+ _ref1 = this.events;
+ _results = [];
+ for (sel in _ref1) {
+ functionName = _ref1[sel];
+ _ref2 = sel.split(' '), selector = 2 <= _ref2.length ? __slice.call(_ref2, 0, _k = _ref2.length - 1) : (_k = 0, []), event = _ref2[_k++];
+ _results.push(this.addEvent(selector.join(' '), event, functionName));
+ }
+ return _results;
+ };
+
+ Delegator.prototype.addEvent = function(bindTo, event, functionName) {
+ var closure, isBlankSelector,
+ _this = this;
+ closure = function() {
+ return _this[functionName].apply(_this, arguments);
+ };
+ isBlankSelector = typeof bindTo === 'string' && bindTo.replace(/\s+/g, '') === '';
+ if (isBlankSelector) {
+ bindTo = this.element;
+ }
+ if (typeof bindTo === 'string') {
+ this.element.delegate(bindTo, event, closure);
+ } else {
+ if (this.isCustomEvent(event)) {
+ this.subscribe(event, closure);
+ } else {
+ $(bindTo).bind(event, closure);
}
}
-
return this;
- },
+ };
+
+ Delegator.prototype.isCustomEvent = function(event) {
+ event = event.split('.')[0];
+ return $.inArray(event, Delegator.natives) === -1;
+ };
+
+ Delegator.prototype.publish = function() {
+ this.element.triggerHandler.apply(this.element, arguments);
+ return this;
+ };
- // Trigger one or many events, firing all bound callbacks. Callbacks are
- // passed the same arguments as `trigger` is, apart from the event name
- // (unless you're listening on `"all"`, which will cause your callback to
- // receive the true name of the event as the first argument).
- trigger: function(name) {
- if (!this._events) return this;
- var args = slice.call(arguments, 1);
- if (!eventsApi(this, 'trigger', name, args)) return this;
- var events = this._events[name];
- var allEvents = this._events.all;
- if (events) triggerEvents(events, args);
- if (allEvents) triggerEvents(allEvents, arguments);
+ Delegator.prototype.subscribe = function(event, callback) {
+ var closure;
+ closure = function() {
+ return callback.apply(this, [].slice.call(arguments, 1));
+ };
+ closure.guid = callback.guid = ($.guid += 1);
+ this.element.bind(event, closure);
+ return this;
+ };
+
+ Delegator.prototype.unsubscribe = function() {
+ this.element.unbind.apply(this.element, arguments);
return this;
- },
+ };
+
+ return Delegator;
+
+ })();
- // Tell this object to stop listening to either specific events ... or
- // to every object it's currently listening to.
- stopListening: function(obj, name, callback) {
- var listeners = this._listeners;
- if (!listeners) return this;
- var deleteListener = !name && !callback;
- if (typeof name === 'object') callback = this;
- if (obj) (listeners = {})[obj._listenerId] = obj;
- for (var id in listeners) {
- listeners[id].off(name, callback, this);
- if (deleteListener) delete this._listeners[id];
+ Delegator.natives = (function() {
+ var key, specials, val;
+ specials = (function() {
+ var _ref1, _results;
+ _ref1 = jQuery.event.special;
+ _results = [];
+ for (key in _ref1) {
+ if (!__hasProp.call(_ref1, key)) continue;
+ val = _ref1[key];
+ _results.push(key);
}
- return this;
+ return _results;
+ })();
+ 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);
+ })();
+
+ Range = {};
+
+ Range.sniff = function(r) {
+ if (r.commonAncestorContainer != null) {
+ return new Range.BrowserRange(r);
+ } else if (typeof r.start === "string") {
+ return new Range.SerializedRange(r);
+ } else if (r.start && typeof r.start === "object") {
+ return new Range.NormalizedRange(r);
+ } else {
+ console.error(_t("Could not sniff range type"));
+ return false;
}
-
};
- // Regular expression used to split event strings.
- var eventSplitter = /\s+/;
-
- // Implement fancy features of the Events API such as multiple event
- // names `"change blur"` and jQuery-style event maps `{change: action}`
- // in terms of the existing API.
- var eventsApi = function(obj, action, name, rest) {
- if (!name) return true;
-
- // Handle event maps.
- if (typeof name === 'object') {
- for (var key in name) {
- obj[action].apply(obj, [key, name[key]].concat(rest));
- }
- return false;
+ Range.nodeFromXPath = function(xpath, root) {
+ var customResolver, evaluateXPath, namespace, node, segment;
+ if (root == null) {
+ root = document;
}
-
- // Handle space separated event names.
- if (eventSplitter.test(name)) {
- var names = name.split(eventSplitter);
- for (var i = 0, l = names.length; i < l; i++) {
- obj[action].apply(obj, [names[i]].concat(rest));
+ evaluateXPath = function(xp, nsResolver) {
+ if (nsResolver == null) {
+ nsResolver = null;
}
- return false;
- }
-
- return true;
- };
-
- // A difficult-to-believe, but optimized internal dispatch function for
- // triggering events. Tries to keep the usual cases speedy (most internal
- // Backbone events have 3 arguments).
- var triggerEvents = function(events, args) {
- var ev, i = -1, l = events.length, a1 = args[0], a2 = args[1], a3 = args[2];
- switch (args.length) {
- case 0: while (++i < l) (ev = events[i]).callback.call(ev.ctx); return;
- case 1: while (++i < l) (ev = events[i]).callback.call(ev.ctx, a1); return;
- case 2: while (++i < l) (ev = events[i]).callback.call(ev.ctx, a1, a2); return;
- case 3: while (++i < l) (ev = events[i]).callback.call(ev.ctx, a1, a2, a3); return;
- default: while (++i < l) (ev = events[i]).callback.apply(ev.ctx, args);
+ return document.evaluate('.' + xp, root, nsResolver, XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue;
+ };
+ if (!$.isXMLDoc(document.documentElement)) {
+ return evaluateXPath(xpath);
+ } else {
+ customResolver = document.createNSResolver(document.ownerDocument === null ? document.documentElement : document.ownerDocument.documentElement);
+ node = evaluateXPath(xpath, customResolver);
+ if (!node) {
+ xpath = ((function() {
+ var _k, _len2, _ref1, _results;
+ _ref1 = xpath.split('/');
+ _results = [];
+ for (_k = 0, _len2 = _ref1.length; _k < _len2; _k++) {
+ segment = _ref1[_k];
+ if (segment && segment.indexOf(':') === -1) {
+ _results.push(segment.replace(/^([a-z]+)/, 'xhtml:$1'));
+ } else {
+ _results.push(segment);
+ }
+ }
+ return _results;
+ })()).join('/');
+ namespace = document.lookupNamespaceURI(null);
+ customResolver = function(ns) {
+ if (ns === 'xhtml') {
+ return namespace;
+ } else {
+ return document.documentElement.getAttribute('xmlns:' + ns);
+ }
+ };
+ node = evaluateXPath(xpath, customResolver);
+ }
+ return node;
}
};
- var listenMethods = {listenTo: 'on', listenToOnce: 'once'};
+ Range.RangeError = (function(_super) {
+
+ __extends(RangeError, _super);
+
+ function RangeError(type, message, parent) {
+ this.type = type;
+ this.message = message;
+ this.parent = parent != null ? parent : null;
+ RangeError.__super__.constructor.call(this, this.message);
+ }
+
+ return RangeError;
+
+ })(Error);
+
+ Range.BrowserRange = (function() {
+
+ function BrowserRange(obj) {
+ this.commonAncestorContainer = obj.commonAncestorContainer;
+ this.startContainer = obj.startContainer;
+ this.startOffset = obj.startOffset;
+ this.endContainer = obj.endContainer;
+ this.endOffset = obj.endOffset;
+ }
- // Inversion-of-control versions of `on` and `once`. Tell *this* object to
- // listen to an event in another object ... keeping track of what it's
- // listening to.
- _.each(listenMethods, function(implementation, method) {
- Events[method] = function(obj, name, callback) {
- var listeners = this._listeners || (this._listeners = {});
- var id = obj._listenerId || (obj._listenerId = _.uniqueId('l'));
- listeners[id] = obj;
- if (typeof name === 'object') callback = this;
- obj[implementation](name, callback, this);
+ BrowserRange.prototype.normalize = function(root) {
+ var it, node, nr, offset, p, r, _k, _len2, _ref1;
+ if (this.tainted) {
+ console.error(_t("You may only call normalize() once on a BrowserRange!"));
+ return false;
+ } else {
+ this.tainted = true;
+ }
+ r = {};
+ nr = {};
+ _ref1 = ['start', 'end'];
+ for (_k = 0, _len2 = _ref1.length; _k < _len2; _k++) {
+ p = _ref1[_k];
+ node = this[p + 'Container'];
+ offset = this[p + 'Offset'];
+ if (!((node != null) && (offset != null))) {
+ return false;
+ }
+ if (node.nodeType === 1) {
+ it = node.childNodes[offset];
+ node = it || node.childNodes[offset - 1];
+ if (node.nodeType === 1 && !node.firstChild) {
+ it = null;
+ node = node.previousSibling;
+ }
+ while (node.nodeType !== 3) {
+ node = node.firstChild;
+ }
+ offset = it ? 0 : node.nodeValue.length;
+ }
+ r[p] = node;
+ r[p + 'Offset'] = offset;
+ }
+ nr.start = r.startOffset > 0 ? r.start.splitText(r.startOffset) : r.start;
+ if (r.start === r.end) {
+ if ((r.endOffset - r.startOffset) < nr.start.nodeValue.length) {
+ nr.start.splitText(r.endOffset - r.startOffset);
+ }
+ nr.end = nr.start;
+ } else {
+ if (r.endOffset < r.end.nodeValue.length) {
+ r.end.splitText(r.endOffset);
+ }
+ nr.end = r.end;
+ }
+ nr.commonAncestor = this.commonAncestorContainer;
+ while (nr.commonAncestor.nodeType !== 1) {
+ nr.commonAncestor = nr.commonAncestor.parentNode;
+ }
+ return new Range.NormalizedRange(nr);
+ };
+
+ BrowserRange.prototype.serialize = function(root, ignoreSelector) {
+ return this.normalize(root).serialize(root, ignoreSelector);
+ };
+
+ return BrowserRange;
+
+ })();
+
+ Range.NormalizedRange = (function() {
+
+ function NormalizedRange(obj) {
+ this.commonAncestor = obj.commonAncestor;
+ this.start = obj.start;
+ this.end = obj.end;
+ }
+
+ NormalizedRange.prototype.normalize = function(root) {
+ return this;
+ };
+
+ NormalizedRange.prototype.limit = function(bounds) {
+ var nodes, parent, startParents, _k, _len2, _ref1;
+ nodes = $.grep(this.textNodes(), function(node) {
+ return node.parentNode === bounds || $.contains(bounds, node.parentNode);
+ });
+ if (!nodes.length) {
+ return null;
+ }
+ this.start = nodes[0];
+ this.end = nodes[nodes.length - 1];
+ startParents = $(this.start).parents();
+ _ref1 = $(this.end).parents();
+ for (_k = 0, _len2 = _ref1.length; _k < _len2; _k++) {
+ parent = _ref1[_k];
+ if (startParents.index(parent) !== -1) {
+ this.commonAncestor = parent;
+ break;
+ }
+ }
return this;
};
- });
- // Aliases for backwards compatibility.
- Events.bind = Events.on;
- Events.unbind = Events.off;
+ NormalizedRange.prototype.serialize = function(root, ignoreSelector) {
+ var end, serialization, start;
+ serialization = function(node, isEnd) {
+ var n, nodes, offset, origParent, textNodes, xpath, _k, _len2;
+ if (ignoreSelector) {
+ origParent = $(node).parents(":not(" + ignoreSelector + ")").eq(0);
+ } else {
+ origParent = $(node).parent();
+ }
+ xpath = origParent.xpath(root)[0];
+ textNodes = origParent.textNodes();
+ nodes = textNodes.slice(0, textNodes.index(node));
+ offset = 0;
+ for (_k = 0, _len2 = nodes.length; _k < _len2; _k++) {
+ n = nodes[_k];
+ offset += n.nodeValue.length;
+ }
+ if (isEnd) {
+ return [xpath, offset + node.nodeValue.length];
+ } else {
+ return [xpath, offset];
+ }
+ };
+ start = serialization(this.start);
+ end = serialization(this.end, true);
+ return new Range.SerializedRange({
+ start: start[0],
+ end: end[0],
+ startOffset: start[1],
+ endOffset: end[1]
+ });
+ };
- // Mixin utility
- Events.mixin = function(proto) {
- var exports = ['on', 'once', 'off', 'trigger', 'stopListening', 'listenTo',
- 'listenToOnce', 'bind', 'unbind'];
- _.each(exports, function(name) {
- proto[name] = this[name];
- }, this);
- return proto;
- };
+ NormalizedRange.prototype.text = function() {
+ var node;
+ return ((function() {
+ var _k, _len2, _ref1, _results;
+ _ref1 = this.textNodes();
+ _results = [];
+ for (_k = 0, _len2 = _ref1.length; _k < _len2; _k++) {
+ node = _ref1[_k];
+ _results.push(node.nodeValue);
+ }
+ return _results;
+ }).call(this)).join('');
+ };
- // Export Events as BackboneEvents depending on current context
- if (typeof define === "function") {
- define(function() {
- return Events;
- });
- } else if (typeof exports !== 'undefined') {
- if (typeof module !== 'undefined' && module.exports) {
- exports = module.exports = Events;
+ NormalizedRange.prototype.textNodes = function() {
+ var end, start, textNodes, _ref1;
+ textNodes = $(this.commonAncestor).textNodes();
+ _ref1 = [textNodes.index(this.start), textNodes.index(this.end)], start = _ref1[0], end = _ref1[1];
+ return $.makeArray(textNodes.slice(start, end + 1 || 9e9));
+ };
+
+ NormalizedRange.prototype.toRange = function() {
+ var range;
+ range = document.createRange();
+ range.setStartBefore(this.start);
+ range.setEndAfter(this.end);
+ return range;
+ };
+
+ return NormalizedRange;
+
+ })();
+
+ Range.SerializedRange = (function() {
+
+ function SerializedRange(obj) {
+ this.start = obj.start;
+ this.startOffset = obj.startOffset;
+ this.end = obj.end;
+ this.endOffset = obj.endOffset;
}
- exports.BackboneEvents = Events;
- } else {
- root.BackboneEvents = Events;
- }
-})(this);
-},{}],2:[function(_dereq_,module,exports){
-module.exports = _dereq_('./backbone-events-standalone');
-
-},{"./backbone-events-standalone":1}],3:[function(_dereq_,module,exports){
-(function (definition) {
- if (typeof exports === "object") {
- module.exports = definition();
- }
- else if (typeof define === 'function' && define.amd) {
- define(definition);
- }
- else {
- window.BackboneExtend = definition();
- }
-})(function () {
- "use strict";
-
- // mini-underscore
- var _ = {
- has: function (obj, key) {
- return Object.prototype.hasOwnProperty.call(obj, key);
- },
-
- extend: function(obj) {
- for (var i=1; i= this[p + 'Offset']) {
+ range[p + 'Container'] = tn;
+ range[p + 'Offset'] = this[p + 'Offset'] - length;
+ break;
+ } else {
+ length += tn.nodeValue.length;
}
}
+ if (!(range[p + 'Offset'] != null)) {
+ throw new Range.RangeError("" + p + "offset", "Couldn't find offset " + this[p + 'Offset'] + " in element " + this[p]);
+ }
}
- return obj;
+ contains = !(document.compareDocumentPosition != null) ? function(a, b) {
+ return a.contains(b);
+ } : function(a, b) {
+ return a.compareDocumentPosition(b) & 16;
+ };
+ $(range.startContainer).parents().reverse().each(function() {
+ if (contains(this, range.endContainer)) {
+ range.commonAncestorContainer = this;
+ return false;
+ }
+ });
+ return new Range.BrowserRange(range).normalize(root);
+ };
+
+ SerializedRange.prototype.serialize = function(root, ignoreSelector) {
+ return this.normalize(root).serialize(root, ignoreSelector);
+ };
+
+ SerializedRange.prototype.toObject = function() {
+ return {
+ start: this.start,
+ startOffset: this.startOffset,
+ end: this.end,
+ endOffset: this.endOffset
+ };
+ };
+
+ return SerializedRange;
+
+ })();
+
+ util = {
+ uuid: (function() {
+ var counter;
+ counter = 0;
+ return function() {
+ return counter++;
+ };
+ })(),
+ getGlobal: function() {
+ return (function() {
+ return this;
+ })();
+ },
+ maxZIndex: function($elements) {
+ var all, el;
+ all = (function() {
+ var _k, _len2, _results;
+ _results = [];
+ for (_k = 0, _len2 = $elements.length; _k < _len2; _k++) {
+ el = $elements[_k];
+ if ($(el).css('position') === 'static') {
+ _results.push(-1);
+ } else {
+ _results.push(parseInt($(el).css('z-index'), 10) || -1);
+ }
+ }
+ return _results;
+ })();
+ return Math.max.apply(Math, all);
+ },
+ mousePosition: function(e, offsetEl) {
+ var offset;
+ offset = $(offsetEl).offset();
+ return {
+ top: e.pageY - offset.top,
+ left: e.pageX - offset.left
+ };
+ },
+ preventEventDefault: function(event) {
+ return event != null ? typeof event.preventDefault === "function" ? event.preventDefault() : void 0 : void 0;
}
};
- /// Following code is pasted from Backbone.js ///
+ _Annotator = this.Annotator;
+
+ Annotator = (function(_super) {
+
+ __extends(Annotator, _super);
+
+ Annotator.prototype.events = {
+ ".annotator-adder button click": "onAdderClick",
+ ".annotator-adder button mousedown": "onAdderMousedown",
+ ".annotator-hl mouseover": "onHighlightMouseover",
+ ".annotator-hl mouseout": "startViewerHideTimer"
+ };
+
+ Annotator.prototype.html = {
+ adder: '',
+ wrapper: ''
+ };
+
+ Annotator.prototype.options = {
+ readOnly: false
+ };
+
+ Annotator.prototype.plugins = {};
+
+ Annotator.prototype.editor = null;
+
+ Annotator.prototype.viewer = null;
+
+ Annotator.prototype.selectedRanges = null;
+
+ Annotator.prototype.mouseIsDown = false;
+
+ Annotator.prototype.ignoreMouseup = false;
+
+ Annotator.prototype.viewerHideTimer = null;
- // Helper function to correctly set up the prototype chain, for subclasses.
- // Similar to `goog.inherits`, but uses a hash of prototype properties and
- // class properties to be extended.
- var extend = function(protoProps, staticProps) {
- var parent = this;
- var child;
+ function Annotator(element, options) {
+ this.onDeleteAnnotation = __bind(this.onDeleteAnnotation, this);
+
+ this.onEditAnnotation = __bind(this.onEditAnnotation, this);
+
+ this.onAdderClick = __bind(this.onAdderClick, this);
+
+ this.onAdderMousedown = __bind(this.onAdderMousedown, this);
+
+ this.onHighlightMouseover = __bind(this.onHighlightMouseover, this);
+
+ this.checkForEndSelection = __bind(this.checkForEndSelection, this);
+
+ this.checkForStartSelection = __bind(this.checkForStartSelection, this);
+
+ this.clearViewerHideTimer = __bind(this.clearViewerHideTimer, this);
+
+ this.startViewerHideTimer = __bind(this.startViewerHideTimer, this);
- // The constructor function for the new subclass is either defined by you
- // (the "constructor" property in your `extend` definition), or defaulted
- // by us to simply call the parent's constructor.
- if (protoProps && _.has(protoProps, 'constructor')) {
- child = protoProps.constructor;
- } else {
- child = function(){ return parent.apply(this, arguments); };
+ this.showViewer = __bind(this.showViewer, this);
+
+ this.onEditorSubmit = __bind(this.onEditorSubmit, this);
+
+ this.onEditorHide = __bind(this.onEditorHide, this);
+
+ this.showEditor = __bind(this.showEditor, this);
+ Annotator.__super__.constructor.apply(this, arguments);
+ this.plugins = {};
+ if (!Annotator.supported()) {
+ return this;
+ }
+ if (!this.options.readOnly) {
+ this._setupDocumentEvents();
+ }
+ this._setupWrapper()._setupViewer()._setupEditor();
+ this._setupDynamicStyle();
+ this.adder = $(this.html.adder).appendTo(this.wrapper).hide();
}
- // Add static properties to the constructor function, if supplied.
- _.extend(child, parent, staticProps);
-
- // Set the prototype chain to inherit from `parent`, without calling
- // `parent`'s constructor function.
- var Surrogate = function(){ this.constructor = child; };
- Surrogate.prototype = parent.prototype;
- child.prototype = new Surrogate();
-
- // Add prototype properties (instance properties) to the subclass,
- // if supplied.
- if (protoProps) _.extend(child.prototype, protoProps);
-
- // Set a convenience property in case the parent's prototype is needed
- // later.
- child.__super__ = parent.prototype;
-
- return child;
- };
-
- // Expose the extend function
- return extend;
-});
-
-},{}],4:[function(_dereq_,module,exports){
-var AnnotationProvider, StorageProvider,
- __hasProp = {}.hasOwnProperty;
-
-StorageProvider = _dereq_('./storage');
-
-AnnotationProvider = (function() {
- AnnotationProvider.configure = function(registry) {
- if (registry['annotations'] == null) {
- registry['annotations'] = new this(registry);
- }
- return registry.include(StorageProvider);
- };
-
- function AnnotationProvider(registry) {
- this.registry = registry;
- }
-
- AnnotationProvider.prototype.create = function(obj) {
- if (obj == null) {
- obj = {};
- }
- return this._cycle(obj, 'create');
- };
-
- AnnotationProvider.prototype.update = function(obj) {
- if (obj.id == null) {
- throw new TypeError("annotation must have an id for update()");
- }
- return this._cycle(obj, 'update');
- };
-
- AnnotationProvider.prototype["delete"] = function(obj) {
- if (obj.id == null) {
- throw new TypeError("annotation must have an id for delete()");
- }
- return this._cycle(obj, 'delete');
- };
-
- AnnotationProvider.prototype.query = function(query) {
- return this.registry['store'].query(query);
- };
-
- AnnotationProvider.prototype.load = function(query) {
- return this.query(query);
- };
-
- AnnotationProvider.prototype._cycle = function(obj, storeFunc) {
- var safeCopy;
- safeCopy = $.extend(true, {}, obj);
- delete safeCopy._local;
- return this.registry['store'][storeFunc](safeCopy).then((function(_this) {
- return function(ret) {
- var k, v;
- for (k in obj) {
- if (!__hasProp.call(obj, k)) continue;
- v = obj[k];
- if (k !== '_local') {
- delete obj[k];
- }
- }
- $.extend(obj, ret);
- return obj;
- };
- })(this));
- };
-
- return AnnotationProvider;
-
-})();
-
-module.exports = AnnotationProvider;
-
-
-},{"./storage":26}],"annotator":[function(_dereq_,module,exports){
-module.exports=_dereq_('haW+cw');
-},{}],"haW+cw":[function(_dereq_,module,exports){
-var AnnotationProvider, Annotator, Delegator, Editor, Notification, Range, Registry, Util, Viewer, Widget, extend, g, handleError, notification, _Annotator, _ref, _t,
- __bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; },
- __hasProp = {}.hasOwnProperty,
- __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; };
-
-extend = _dereq_('backbone-extend-standalone');
-
-Delegator = _dereq_('./class');
+ Annotator.prototype._setupWrapper = function() {
+ this.wrapper = $(this.html.wrapper);
+ this.element.find('script').remove();
+ this.element.wrapInner(this.wrapper);
+ this.wrapper = this.element.find('.annotator-wrapper');
+ return this;
+ };
-Range = _dereq_('./range');
-
-Util = _dereq_('./util');
-
-Widget = _dereq_('./widget');
-
-Viewer = _dereq_('./viewer');
-
-Editor = _dereq_('./editor');
-
-Notification = _dereq_('./notification');
-
-Registry = _dereq_('./registry');
-
-AnnotationProvider = _dereq_('./annotations');
-
-_t = Util.TranslationString;
-
-_Annotator = this.Annotator;
-
-handleError = function() {
- return console.error.apply(console, arguments);
-};
-
-Annotator = (function(_super) {
- __extends(Annotator, _super);
-
- Annotator.prototype.events = {
- ".annotator-adder button click": "onAdderClick",
- ".annotator-adder button mousedown": "onAdderMousedown",
- ".annotator-hl mouseover": "onHighlightMouseover",
- ".annotator-hl mouseout": "startViewerHideTimer"
- };
-
- Annotator.prototype.html = {
- adder: '',
- wrapper: ''
- };
-
- Annotator.prototype.options = {
- store: null,
- readOnly: false,
- loadQuery: {}
- };
-
- Annotator.prototype.plugins = {};
-
- Annotator.prototype.editor = null;
-
- Annotator.prototype.viewer = null;
-
- Annotator.prototype.selectedRanges = null;
-
- Annotator.prototype.mouseIsDown = false;
-
- Annotator.prototype.ignoreMouseup = false;
-
- Annotator.prototype.viewerHideTimer = null;
-
- function Annotator(element, options) {
- this.onEditAnnotation = __bind(this.onEditAnnotation, this);
- this.onAdderClick = __bind(this.onAdderClick, this);
- this.onAdderMousedown = __bind(this.onAdderMousedown, this);
- this.onHighlightMouseover = __bind(this.onHighlightMouseover, this);
- this.checkForEndSelection = __bind(this.checkForEndSelection, this);
- this.checkForStartSelection = __bind(this.checkForStartSelection, this);
- this.clearViewerHideTimer = __bind(this.clearViewerHideTimer, this);
- this.startViewerHideTimer = __bind(this.startViewerHideTimer, this);
- this.showViewer = __bind(this.showViewer, this);
- this.onEditorSubmit = __bind(this.onEditorSubmit, this);
- this.onEditorHide = __bind(this.onEditorHide, this);
- this.showEditor = __bind(this.showEditor, this);
- Annotator.__super__.constructor.apply(this, arguments);
- this.plugins = {};
- Annotator._instances.push(this);
- if (!Annotator.supported()) {
- return this;
- }
- Registry.createApp(this, options);
- }
-
- Annotator.extend = extend;
-
- Annotator.prototype._setupWrapper = function() {
- this.wrapper = $(this.html.wrapper);
- this.element.find('script').remove();
- this.element.wrapInner(this.wrapper);
- this.wrapper = this.element.find('.annotator-wrapper');
- return this;
- };
-
- Annotator.prototype._setupViewer = function() {
- this.viewer = new Annotator.Viewer({
- readOnly: this.options.readOnly
- });
- this.viewer.hide().on("edit", this.onEditAnnotation).on("delete", (function(_this) {
- return function(annotation) {
- _this.viewer.hide();
- _this.publish('beforeAnnotationDeleted', [annotation]);
- _this.cleanupAnnotation(annotation);
- return _this.annotations["delete"](annotation).done(function() {
- return _this.publish('annotationDeleted', [annotation]);
- });
- };
- })(this)).addField({
- load: (function(_this) {
- return function(field, annotation) {
+ Annotator.prototype._setupViewer = function() {
+ var _this = this;
+ this.viewer = new Annotator.Viewer({
+ readOnly: this.options.readOnly
+ });
+ this.viewer.hide().on("edit", this.onEditAnnotation).on("delete", this.onDeleteAnnotation).addField({
+ load: function(field, annotation) {
if (annotation.text) {
- $(field).html(Util.escape(annotation.text));
+ $(field).escape(annotation.text);
} else {
$(field).html("" + (_t('No Comment')) + "");
}
return _this.publish('annotationViewerTextField', [field, annotation]);
- };
- })(this)
- }).element.appendTo(this.wrapper).bind({
- "mouseover": this.clearViewerHideTimer,
- "mouseout": this.startViewerHideTimer
- });
- return this;
- };
-
- Annotator.prototype._setupEditor = function() {
- this.editor = new Annotator.Editor();
- this.editor.hide().on('hide', this.onEditorHide).on('save', this.onEditorSubmit).addField({
- type: 'textarea',
- label: _t('Comments') + '\u2026',
- load: function(field, annotation) {
- return $(field).find('textarea').val(annotation.text || '');
- },
- submit: function(field, annotation) {
- return annotation.text = $(field).find('textarea').val();
- }
- });
- this.editor.element.appendTo(this.wrapper);
- return this;
- };
-
- Annotator.prototype._setupDocumentEvents = function() {
- $(document).bind({
- "mouseup": this.checkForEndSelection,
- "mousedown": this.checkForStartSelection
- });
- return this;
- };
-
- Annotator.prototype._setupDynamicStyle = function() {
- var max, sel, style, x;
- style = $('#annotator-dynamic-style');
- if (!style.length) {
- style = $('').appendTo(document.head);
- }
- sel = '*' + ((function() {
- var _i, _len, _ref, _results;
- _ref = ['adder', 'outer', 'notice', 'filter'];
- _results = [];
- for (_i = 0, _len = _ref.length; _i < _len; _i++) {
- x = _ref[_i];
- _results.push(":not(.annotator-" + x + ")");
- }
- return _results;
- })()).join('');
- max = Util.maxZIndex($(document.body).find(sel));
- max = Math.max(max, 1000);
- style.text([".annotator-adder, .annotator-outer, .annotator-notice {", " z-index: " + (max + 20) + ";", "}", ".annotator-filter {", " z-index: " + (max + 10) + ";", "}"].join("\n"));
- return this;
- };
+ }
+ }).element.appendTo(this.wrapper).bind({
+ "mouseover": this.clearViewerHideTimer,
+ "mouseout": this.startViewerHideTimer
+ });
+ return this;
+ };
- Annotator.prototype.load = function(query) {
- return this.annotations.load(query).then((function(_this) {
- return function(annotations, meta) {
- return _this.loadAnnotations(annotations);
- };
- })(this));
- };
+ Annotator.prototype._setupEditor = function() {
+ this.editor = new Annotator.Editor();
+ this.editor.hide().on('hide', this.onEditorHide).on('save', this.onEditorSubmit).addField({
+ type: 'textarea',
+ label: _t('Comments') + '\u2026',
+ load: function(field, annotation) {
+ return $(field).find('textarea').val(annotation.text || '');
+ },
+ submit: function(field, annotation) {
+ return annotation.text = $(field).find('textarea').val();
+ }
+ });
+ this.editor.element.appendTo(this.wrapper);
+ return this;
+ };
- Annotator.prototype.destroy = function() {
- var idx, name, plugin, _ref;
- $(document).unbind({
- "mouseup": this.checkForEndSelection,
- "mousedown": this.checkForStartSelection
- });
- $('#annotator-dynamic-style').remove();
- this.adder.remove();
- this.viewer.destroy();
- this.editor.destroy();
- this.wrapper.find('.annotator-hl').each(function() {
- $(this).contents().insertBefore(this);
- return $(this).remove();
- });
- this.wrapper.contents().insertBefore(this.wrapper);
- this.wrapper.remove();
- this.element.data('annotator', null);
- _ref = this.plugins;
- for (name in _ref) {
- plugin = _ref[name];
- this.plugins[name].destroy();
- }
- this.removeEvents();
- idx = Annotator._instances.indexOf(this);
- if (idx !== -1) {
- return Annotator._instances.splice(idx, 1);
- }
- };
+ Annotator.prototype._setupDocumentEvents = function() {
+ $(document).bind({
+ "mouseup": this.checkForEndSelection,
+ "mousedown": this.checkForStartSelection
+ });
+ return this;
+ };
- Annotator.prototype.getSelectedRanges = function() {
- var browserRange, i, normedRange, r, ranges, rangesToIgnore, selection, _i, _len;
- selection = Util.getGlobal().getSelection();
- ranges = [];
- rangesToIgnore = [];
- if (!selection.isCollapsed) {
- ranges = (function() {
- var _i, _ref, _results;
+ Annotator.prototype._setupDynamicStyle = function() {
+ var max, sel, style, x;
+ style = $('#annotator-dynamic-style');
+ if (!style.length) {
+ style = $('').appendTo(document.head);
+ }
+ sel = '*' + ((function() {
+ var _k, _len2, _ref1, _results;
+ _ref1 = ['adder', 'outer', 'notice', 'filter'];
_results = [];
- for (i = _i = 0, _ref = selection.rangeCount; 0 <= _ref ? _i < _ref : _i > _ref; i = 0 <= _ref ? ++_i : --_i) {
- r = selection.getRangeAt(i);
- browserRange = new Range.BrowserRange(r);
- normedRange = browserRange.normalize().limit(this.wrapper[0]);
- if (normedRange === null) {
- rangesToIgnore.push(r);
- }
- _results.push(normedRange);
+ for (_k = 0, _len2 = _ref1.length; _k < _len2; _k++) {
+ x = _ref1[_k];
+ _results.push(":not(.annotator-" + x + ")");
}
return _results;
- }).call(this);
- selection.removeAllRanges();
- }
- for (_i = 0, _len = rangesToIgnore.length; _i < _len; _i++) {
- r = rangesToIgnore[_i];
- selection.addRange(r);
- }
- return $.grep(ranges, function(range) {
- if (range) {
- selection.addRange(range.toRange());
- }
- return range;
- });
- };
+ })()).join('');
+ max = util.maxZIndex($(document.body).find(sel));
+ max = Math.max(max, 1000);
+ style.text([".annotator-adder, .annotator-outer, .annotator-notice {", " z-index: " + (max + 20) + ";", "}", ".annotator-filter {", " z-index: " + (max + 10) + ";", "}"].join("\n"));
+ return this;
+ };
- Annotator.prototype.setupAnnotation = function(annotation) {
- var e, normed, normedRanges, r, root, _i, _j, _len, _len1, _ref;
- root = this.wrapper[0];
- normedRanges = [];
- _ref = annotation.ranges;
- for (_i = 0, _len = _ref.length; _i < _len; _i++) {
- r = _ref[_i];
- try {
- normedRanges.push(Range.sniff(r).normalize(root));
- } catch (_error) {
- e = _error;
- if (e instanceof Range.RangeError) {
- this.publish('rangeNormalizeFail', [annotation, r, e]);
- } else {
- throw e;
+ Annotator.prototype.getSelectedRanges = function() {
+ var browserRange, i, normedRange, r, ranges, rangesToIgnore, selection, _k, _len2;
+ selection = util.getGlobal().getSelection();
+ ranges = [];
+ rangesToIgnore = [];
+ if (!selection.isCollapsed) {
+ ranges = (function() {
+ var _k, _ref1, _results;
+ _results = [];
+ for (i = _k = 0, _ref1 = selection.rangeCount; 0 <= _ref1 ? _k < _ref1 : _k > _ref1; i = 0 <= _ref1 ? ++_k : --_k) {
+ r = selection.getRangeAt(i);
+ browserRange = new Range.BrowserRange(r);
+ normedRange = browserRange.normalize().limit(this.wrapper[0]);
+ if (normedRange === null) {
+ rangesToIgnore.push(r);
+ }
+ _results.push(normedRange);
+ }
+ return _results;
+ }).call(this);
+ selection.removeAllRanges();
+ }
+ for (_k = 0, _len2 = rangesToIgnore.length; _k < _len2; _k++) {
+ r = rangesToIgnore[_k];
+ selection.addRange(r);
+ }
+ return $.grep(ranges, function(range) {
+ if (range) {
+ selection.addRange(range.toRange());
+ }
+ return range;
+ });
+ };
+
+ Annotator.prototype.createAnnotation = function() {
+ var annotation;
+ annotation = {};
+ this.publish('beforeAnnotationCreated', [annotation]);
+ return annotation;
+ };
+
+ Annotator.prototype.setupAnnotation = function(annotation, fireEvents) {
+ var normed, normedRanges, r, root, _k, _l, _len2, _len3, _ref1;
+ if (fireEvents == null) {
+ fireEvents = true;
+ }
+ root = this.wrapper[0];
+ annotation.ranges || (annotation.ranges = this.selectedRanges);
+ normedRanges = [];
+ _ref1 = annotation.ranges;
+ for (_k = 0, _len2 = _ref1.length; _k < _len2; _k++) {
+ r = _ref1[_k];
+ try {
+ normedRanges.push(Range.sniff(r).normalize(root));
+ } catch (e) {
+ if (e instanceof Range.RangeError) {
+ this.publish('rangeNormalizeFail', [annotation, r, e]);
+ } else {
+ throw e;
+ }
}
}
- }
- annotation.quote = [];
- annotation.ranges = [];
- annotation._local = {};
- annotation._local.highlights = [];
- for (_j = 0, _len1 = normedRanges.length; _j < _len1; _j++) {
- normed = normedRanges[_j];
- annotation.quote.push($.trim(normed.text()));
- annotation.ranges.push(normed.serialize(this.wrapper[0], '.annotator-hl'));
- $.merge(annotation._local.highlights, this.highlightRange(normed));
- }
- annotation.quote = annotation.quote.join(' / ');
- $(annotation._local.highlights).data('annotation', annotation);
- return annotation;
- };
+ annotation.quote = [];
+ annotation.ranges = [];
+ annotation.highlights = [];
+ for (_l = 0, _len3 = normedRanges.length; _l < _len3; _l++) {
+ normed = normedRanges[_l];
+ annotation.quote.push($.trim(normed.text()));
+ annotation.ranges.push(normed.serialize(this.wrapper[0], '.annotator-hl'));
+ $.merge(annotation.highlights, this.highlightRange(normed));
+ }
+ annotation.quote = annotation.quote.join(' / ');
+ $(annotation.highlights).data('annotation', annotation);
+ if (fireEvents) {
+ this.publish('annotationCreated', [annotation]);
+ }
+ return annotation;
+ };
- Annotator.prototype.cleanupAnnotation = function(annotation) {
- var h, _i, _len, _ref, _ref1;
- if (((_ref = annotation._local) != null ? _ref.highlights : void 0) != null) {
- _ref1 = annotation._local.highlights;
- for (_i = 0, _len = _ref1.length; _i < _len; _i++) {
- h = _ref1[_i];
- if (h.parentNode != null) {
- $(h).replaceWith(h.childNodes);
- }
+ Annotator.prototype.updateAnnotation = function(annotation) {
+ this.publish('beforeAnnotationUpdated', [annotation]);
+ this.publish('annotationUpdated', [annotation]);
+ return annotation;
+ };
+
+ Annotator.prototype.deleteAnnotation = function(annotation) {
+ var h, _k, _len2, _ref1;
+ _ref1 = annotation.highlights;
+ for (_k = 0, _len2 = _ref1.length; _k < _len2; _k++) {
+ h = _ref1[_k];
+ $(h).replaceWith(h.childNodes);
}
- delete annotation._local.highlights;
- }
- return annotation;
- };
+ this.publish('annotationDeleted', [annotation]);
+ return annotation;
+ };
- Annotator.prototype.loadAnnotations = function(annotations) {
- var clone, loader;
- if (annotations == null) {
- annotations = [];
- }
- loader = (function(_this) {
- return function(annList) {
- var n, now, _i, _len;
+ Annotator.prototype.loadAnnotations = function(annotations) {
+ var clone, loader,
+ _this = this;
+ if (annotations == null) {
+ annotations = [];
+ }
+ loader = function(annList) {
+ var n, now, _k, _len2;
if (annList == null) {
annList = [];
}
now = annList.splice(0, 10);
- for (_i = 0, _len = now.length; _i < _len; _i++) {
- n = now[_i];
- _this.setupAnnotation(n);
+ for (_k = 0, _len2 = now.length; _k < _len2; _k++) {
+ n = now[_k];
+ _this.setupAnnotation(n, false);
}
if (annList.length > 0) {
return setTimeout((function() {
@@ -751,700 +890,510 @@
return _this.publish('annotationsLoaded', [clone]);
}
};
- })(this);
- clone = annotations.slice();
- loader(annotations);
- return this;
- };
+ clone = annotations.slice();
+ if (annotations.length) {
+ loader(annotations);
+ }
+ return this;
+ };
+
+ Annotator.prototype.dumpAnnotations = function() {
+ if (this.plugins['Store']) {
+ return this.plugins['Store'].dumpAnnotations();
+ } else {
+ return console.warn(_t("Can't dump annotations without Store plugin."));
+ }
+ };
+
+ Annotator.prototype.highlightRange = function(normedRange, cssClass) {
+ var hl, node, white, _k, _len2, _ref1, _results;
+ if (cssClass == null) {
+ cssClass = 'annotator-hl';
+ }
+ white = /^\s*$/;
+ hl = $("");
+ _ref1 = normedRange.textNodes();
+ _results = [];
+ for (_k = 0, _len2 = _ref1.length; _k < _len2; _k++) {
+ node = _ref1[_k];
+ if (!white.test(node.nodeValue)) {
+ _results.push($(node).wrapAll(hl).parent().show()[0]);
+ }
+ }
+ return _results;
+ };
+
+ Annotator.prototype.highlightRanges = function(normedRanges, cssClass) {
+ var highlights, r, _k, _len2;
+ if (cssClass == null) {
+ cssClass = 'annotator-hl';
+ }
+ highlights = [];
+ for (_k = 0, _len2 = normedRanges.length; _k < _len2; _k++) {
+ r = normedRanges[_k];
+ $.merge(highlights, this.highlightRange(r, cssClass));
+ }
+ return highlights;
+ };
- Annotator.prototype.dumpAnnotations = function() {
- if (this.plugins['Store']) {
- return this.plugins['Store'].dumpAnnotations();
- } else {
- console.warn(_t("Can't dump annotations without Store plugin."));
- return false;
- }
- };
+ Annotator.prototype.addPlugin = function(name, options) {
+ var klass, _base;
+ if (this.plugins[name]) {
+ console.error(_t("You cannot have more than one instance of any plugin."));
+ } else {
+ klass = Annotator.Plugin[name];
+ if (typeof klass === 'function') {
+ this.plugins[name] = new klass(this.element[0], options);
+ this.plugins[name].annotator = this;
+ if (typeof (_base = this.plugins[name]).pluginInit === "function") {
+ _base.pluginInit();
+ }
+ } else {
+ console.error(_t("Could not load ") + name + _t(" plugin. Have you included the appropriate
+
@@ -43,7 +44,8 @@
// URL of authentication token server
//'annotationTokenUrl' : 'http://localhost:8080/test/annotator/token/getLoginToken',
// list of Annotator plugins
- 'annotatorPlugins' : ['Auth', 'Permissions', 'Markdown']
+ //'annotatorPlugins' : ['Auth', 'Permissions', 'Store', 'Markdown', 'DigilibIntegrator']
+ 'annotatorPlugins' : ['Store', 'Markdown', 'DigilibIntegrator']
};
var $div = $('div#digilib');
$div.digilib(opts);
diff -r 7d9132a513ab -r 49d643f0d658 webapp/src/main/webapp/jquery/jquery.digilib.annotator-new.js
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/webapp/src/main/webapp/jquery/jquery.digilib.annotator-new.js Fri Jan 16 18:20:15 2015 +0100
@@ -0,0 +1,785 @@
+/*
+ * #%L
+ * digilib plugin for annotations.
+ * %%
+ * Copyright (C) 2012 - 2013 MPIWG Berlin
+ * %%
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Lesser Public License for more details.
+ *
+ * You should have received a copy of the GNU General Lesser Public
+ * License along with this program. If not, see
+ * .
+ * #L%
+ * Authors: Robert Casties, Martin Raspe
+ */
+
+/**
+ * digilib plugin for annotations.
+ *
+ * Currently supported are point-like annotations (like marks) and rectangular region annotations.
+ *
+ * Annotations are displayed using code from the Annotator (http://annotatorjs.org) project
+ * and stored on a Annotator-API compatible server.
+ */
+(function($) {
+
+ // affine geometry
+ var geom = null;
+ // plugin object with digilib data
+ var digilib = null;
+ // the functions made available by digilib
+ var fn = {};
+ // the normal zoom area
+ var FULL_AREA = null;
+
+ var buttons = {
+ annotations : {
+ onclick : "toggleAnnotations",
+ tooltip : "show or hide annotations",
+ icon : "annotations.png"
+ },
+ annotationuser : {
+ onclick : "setAnnotationUser",
+ tooltip : "set user account for annotations",
+ icon : "annotation-user.png"
+ },
+ annotationmark : {
+ onclick : "setAnnotationMark",
+ tooltip : "create an annotation for a point",
+ icon : "annotation-mark.png"
+ },
+ annotationregion : {
+ onclick : "setAnnotationRegion",
+ tooltip : "create an annotation for a region",
+ icon : "annotation-region.png"
+ }
+ };
+
+ // for defaults see below (we need to define the functions used in annotatorPluginSettings first)
+
+ var actions = {
+ /**
+ * show/hide annotations
+ */
+ toggleAnnotations : function (data) {
+ var show = !data.settings.isAnnotationsVisible;
+ data.settings.isAnnotationsVisible = show;
+ fn.highlightButtons(data, 'annotations', show);
+ renderAnnotations(data);
+ },
+
+ /**
+ * set user account for annotations
+ */
+ setAnnotationUser : function (data, user, password) {
+ var annotator = data.annotator;
+ var auth = annotator.plugins.Auth;
+ if (auth == null) {
+ console.error("setAnnotationUser: No Auth plugin!!");
+ return;
+ }
+ // set new user in digilib and Annotator Auth plugin
+ setAnnotationUser(data, auth, user, password);
+ // get new token
+ auth.token = null;
+ auth._unsafeToken = null;
+ auth.requestToken();
+ // save new token in cookie
+ auth.withToken(function (tkn) {
+ data.dlOpts.annotationToken = auth.token;
+ fn.storeOptions(data);
+ // reload annotations
+ reloadAnnotations(data);
+ });
+ },
+
+ /**
+ * set a mark-annotation by clicking (or giving a position and a text)
+ *
+ * @param data
+ * @param mpos
+ * @param text
+ */
+ setAnnotationMark : function (data, mpos, text) {
+ if (mpos == null) {
+ // interactive
+ setAnnotationMark(data);
+ } else {
+ // use position and text (and user-id)
+ console.error("Sorry, currently only interactive annotations!");
+ }
+ },
+
+ /**
+ * set a region-annotation by clicking (or giving a position and a text)
+ *
+ * @param data
+ * @param rect
+ * @param text
+ */
+ setAnnotationRegion : function (data, rect, text) {
+ if (rect == null) {
+ // interactive
+ setAnnotationRegion(data);
+ } else {
+ // use position and text (and user-id)
+ console.error("Sorry, currently only interactive annotations!");
+ }
+ }
+ };
+
+ /**
+ * install additional buttons
+ */
+ var installButtons = function(data, buttonSet) {
+ var settings = data.settings;
+ var mode = settings.interactionMode;
+ var buttonSettings = settings.buttonSettings[mode];
+ // set annotationSet to [] or '' for no buttons (when showing annotations only)
+ if (buttonSet.length && buttonSet.length > 0) {
+ buttonSettings.annotationSet = buttonSet;
+ buttonSettings.buttonSets.push('annotationSet');
+ }
+ };
+
+
+ /**
+ * returns an annotatable uri to this digilib image
+ */
+ var getAnnotationPageUri = function(data) {
+ var settings = data.settings;
+ var uri = settings.annotationPageUri;
+ if (uri == null) {
+ // default uri with digilibBaseUrl
+ uri = settings.digilibBaseUrl + settings.digilibFrontendPath;
+ uri += '?' + fn.getParamString(data.settings, ['fn', 'pn'], digilib.defaults);
+ } else if (typeof uri === 'function') {
+ // call function
+ uri = uri(data);
+ }
+ return uri;
+ };
+
+ /**
+ * Set annotation user and password in digilib and Annotator.Auth plugin.
+ *
+ * @param auth Auth plugin instance.
+ * @param user user name (optional)
+ * @param password password (optional)
+ */
+ var setAnnotationUser = function (data, auth, user, password) {
+ if (user == null) {
+ // user name entered in JS-prompt
+ user = window.prompt("Please authenticate (Cancel to log out): User name", data.settings.annotationUser);
+ if (user != null && user != 'anonymous') {
+ // password entered in JS-prompt
+ password = window.prompt("Please authenticate: Password", '');
+ // set params for Auth plugin
+ auth.options.requestData.password = password;
+ // try to use the safe url for the password
+ if (data.settings.annotationSafeTokenUrl != null) {
+ auth.options.tokenUrl = data.settings.annotationSafeTokenUrl;
+ } else {
+ console.warn("Sending token password over standard-URL!");
+ }
+ } else {
+ // use anonymous user
+ user = 'anonymous';
+ delete auth.options.requestData.password;
+ if (data.settings.annotationSafeTokenUrl != null) {
+ // reset url to unsafe
+ auth.options.tokenUrl = data.settings.annotationTokenUrl;
+ }
+ }
+ }
+ // set user in digilib
+ data.settings.annotationUser = user;
+ data.dlOpts.annotationUser = user;
+ fn.storeOptions(data);
+ // set params for Auth plugin
+ auth.options.requestData.user = user;
+ // set params for Permissions plugin
+ var perms = data.annotator.plugins.Permissions;
+ if (perms != null) {
+ perms.setUser(user);
+ }
+ };
+
+
+ /**
+ * add a mark-annotation where clicked.
+ */
+ var setAnnotationMark = function(data) {
+ var $scaler = data.$scaler;
+ // start event capturing
+ $scaler.one('mousedown.dlSetAnnotationMark', function (evt) {
+ // event handler adding a new mark
+ console.log("setAnnotationMark at=", evt);
+ var mpos = geom.position(evt);
+ var pos = data.imgTrafo.invtransform(mpos);
+ // mark selection shape
+ var shape = {'type' : 'point', 'units' : 'fraction', 'geometry' : geom.position(pos)};
+ // create and edit new annotation
+ createAnnotation(data, shape, mpos.getAsCss());
+ return false;
+ });
+ };
+
+ /**
+ * Add a region-annotation where clicked.
+ */
+ var setAnnotationRegion = function (data) {
+ fn.defineArea(data, function (data, rect) {
+ if (rect == null) return;
+ // event handler adding a new mark
+ console.log("setAnnotationRegion at=", rect);
+ // mark selection shape
+ var shape = {'type' : 'rectangle', 'units' : 'fraction', 'geometry' : rect};
+ var pos = rect.getPt1();
+ var mpos = data.imgTrafo.transform(pos);
+ // create and edit new annotation
+ createAnnotation(data, shape, mpos.getAsCss());
+ });
+ };
+
+ /**
+ * create an empty annotation with the given shape, show the editor at the given position,
+ * and store the annotation using Annotator.
+ *
+ * @param shape shape object
+ * @param editorPos css position object
+ * @returns promise
+ */
+ var createAnnotation = function (data, shape, editorPos) {
+ var annotator = data.annotator;
+ var annotation = {'shapes' : [shape]};
+ annotator.publish('beforeAnnotationCreated', [annotation]);
+ annotator.setupAnnotation(annotation);
+ // edit the annotation (returns a promise)
+ var dfd = annotator.editAnnotation(annotation, editorPos);
+ dfd.then(function (annotation) {
+ // store annotation (returns deferred)
+ return annotator.annotations.create(annotation)
+ // handle storage errors
+ .fail(function () {
+ console.error("Error storing annotation!");
+ // TODO: more error handling?
+ });
+ });
+ dfd.done(function (annotation) {
+ annotator.publish('annotationCreated', [annotation]);
+ });
+ // clean up (if, for example, editing was cancelled, or storage failed)
+ dfd.fail(function (annotation) {
+ console.warn("Editing annotation cancelled!");
+ deleteAnnotation(data, annotation);
+ });
+ return dfd;
+ };
+
+ /**
+ * Render all annotations on the image.
+ */
+ var renderAnnotations = function (data) {
+ if (data.annotations == null || data.annotator == null || data.$img == null || data.imgTrafo == null)
+ return;
+ var annotations = data.annotations;
+ var cssPrefix = data.settings.cssPrefix;
+ var $elem = data.$elem;
+ // show annotation user state
+ $elem.find('div#'+cssPrefix+'button-annotationuser').attr('title', 'annotation user: '+data.settings.annotationUser);
+ // clear annotations
+ $elem.find('div.'+cssPrefix+'annotationmark,div.'+cssPrefix+'annotationregion').remove();
+ if (!data.settings.isAnnotationsVisible) return;
+ // re-render
+ for (var i = 0; i < annotations.length; i++) {
+ renderAnnotation(data, annotations[i]);
+ }
+ };
+
+ /**
+ * Render a single annotation on the image.
+ *
+ * @param annot annotation wrapper object
+ */
+ var renderAnnotation = function (data, annot) {
+ if (annot == null || annot.annotation == null || data.$img == null || data.imgTrafo == null)
+ return;
+ if (!data.settings.isAnnotationsVisible) return;
+ var cssPrefix = data.settings.cssPrefix;
+ var $elem = data.$elem;
+ var annotator = data.annotator;
+ var annotation = annot.annotation;
+ var idx = '';
+ if (data.settings.showAnnotationNumbers) {
+ // show annotation number
+ idx = annot.idx ? annot.idx : '?';
+ }
+ var shape = null;
+ var area = null;
+ var type = null;
+ if (annotation.shapes != null) {
+ // annotation shape
+ shape = annotation.shapes[0];
+ type = shape.type;
+ if (type === "point") {
+ area = geom.position(shape.geometry);
+ } else if (type === "rectangle") {
+ area = geom.rectangle(shape.geometry);
+ } else {
+ console.error("Unsupported shape type="+type);
+ return;
+ }
+ } else if (annotation.areas != null) {
+ // legacy annotation areas
+ shape = annotation.areas[0];
+ area = geom.rectangle(shape);
+ if (area.isRectangle()) {
+ type = 'rectangle';
+ } else {
+ type = 'point';
+ }
+ } else {
+ console.error("Unable to render this annotation!");
+ return;
+ }
+ var screenRect = null;
+ var $annotation = null;
+ if (type === 'rectangle') {
+ // render rectangle
+ var clippedArea = data.zoomArea.intersect(area);
+ if (clippedArea == null) return;
+ screenRect = data.imgTrafo.transform(clippedArea);
+ $annotation = $(''+idx+'
');
+ } else {
+ // render point
+ if (!data.zoomArea.containsPosition(area)) return;
+ screenRect = data.imgTrafo.transform(area);
+ // create annotation
+ var html = ''+idx+'
';
+ $annotation = $(html);
+ }
+ // save annotation in data for Annotator
+ $annotation.data('annotation', annotation);
+ $annotation.data('rect', area);
+ // add shared css class from annotations collection
+ if (annotation.cssclass != null) {
+ $annotation.addClass(annotation.cssclass);
+ }
+ // add individual css class from this annotation
+ if (shape.cssclass != null) {
+ $annotation.addClass(shape.cssclass);
+ }
+ // save reference to div
+ annot.$div = $annotation;
+ $elem.append($annotation);
+ // hook up Annotator events
+ $annotation.on("mouseover", annotator.onHighlightMouseover);
+ $annotation.on("mouseout", annotator.startViewerHideTimer);
+ $annotation.on('click.dlAnnotation', function(event) {
+ $(data).trigger('annotationClick', [$annotation]);
+ });
+ screenRect.adjustDiv($annotation);
+ };
+
+
+ /**
+ * Delete annotation from digilib.
+ *
+ * Finds the corresponding digilib annotation wrapper, removes any elements from screen,
+ * and deletes the wrapper from the list.
+ *
+ * @param annotation the annotation object to delete.
+ */
+ var deleteAnnotation = function (data, annotation) {
+ // remove annotation mark
+ var annots = data.annotations;
+ for (var i = 0; i < annots.length; ++i) {
+ var annot = annots[i];
+ if (annot.annotation === annotation) {
+ // this is the right wrapper
+ if (annot.$div != null) {
+ // remove from screen
+ annot.$div.remove();
+ }
+ // remove from list
+ annots.splice(i, 1);
+ break;
+ }
+ }
+ };
+
+
+ /**
+ * Reload annotations for current page.
+ */
+ var reloadAnnotations = function (data) {
+ // clear annotations
+ data.annotations = [];
+ renderAnnotations(data);
+ // reload annotations
+ data.annotator.load(data.annotatorLoadQuery);
+ };
+
+
+ /**
+ * Our modified version of Annotator.
+ */
+ var DigilibAnnotator = Annotator.extend({
+ /**
+ * Set digilib data object in Annotator
+ */
+ 'setDigilibData' : function (data) {
+ // set digilib data in options
+ this.options.digilibData = data;
+ },
+ /**
+ * Initialises an annotation from an object representation.
+ * Overwrites Annotator.setupAnnotation().
+ *
+ * Checks for image annotations, creates a wrapper, adds wrapper to list,
+ * and renders the annotation.
+ */
+ 'setupAnnotation' : function (annotation) {
+ // digilibData has to be set in the options
+ var data = this.options.digilibData;
+ // is this a digilib image annotation?
+ if (annotation.shapes != null || annotation.areas != null) {
+ // create annotation wrapper
+ var ann = {
+ 'annotation' : annotation,
+ 'idx' : data.annotations.length+1
+ };
+ // add to list
+ data.annotations.push(ann);
+ // render this annotation
+ renderAnnotation(data, ann);
+ } else {
+ // Invoke the built-in implementation
+ Annotator.prototype.setupAnnotation.call(this, annotation);
+ }
+ return annotation;
+ },
+ /**
+ * Handler for annotationDeleted event for digilib annotations.
+ */
+ 'onDigilibAnnotationDeleted' : function (annotation) {
+ // remove digilib annotation
+ var data = this.options.digilibData;
+ deleteAnnotation(data, annotation);
+ }
+ });
+
+
+ /**
+ * returns unauthorizedCallback function for Annotator authlogin plugin.
+ */
+ var getHandleUnauthorized = function (data) {
+ return function (auth) {
+ // prompt for user name and set user
+ setAnnotationUser(data, auth);
+ // then try again
+ return true;
+ };
+ };
+
+ /**
+ * returns the annotation server URL.
+ */
+ var getAnnotationServerUrl = function (data) {
+ return data.settings.annotationServerUrl;
+ };
+
+ /**
+ * returns the annotation token URL.
+ */
+ var getAnnotationTokenUrl = function (data) {
+ return data.settings.annotationTokenUrl;
+ };
+
+ /**
+ * returns the cached annotation token.
+ */
+ var getAnnotationToken = function (data) {
+ return data.dlOpts.annotationToken;
+ };
+
+ /**
+ * returns the annotation user.
+ */
+ var getAnnotationUser = function (data) {
+ return data.settings.annotationUser;
+ };
+
+
+ /**
+ * zoom in and display the annotation in the middle of the screen.
+ */
+ var zoomToAnnotation = function (data, $div) {
+ var settings = data.settings;
+ var rect = $div.data('rect');
+ var za = geom.rectangle(rect);
+ var w = settings.annotationAutoWidth;
+ if (za.width == null || za.width == 0) za.width = w;
+ if (za.height == null || za.height == 0) za.height = w;
+ var factor = settings.annotationAutoZoomFactor;
+ za.width *= factor;
+ za.height *= factor;
+ za.setProportion(1, true); // avoid extreme zoomArea proportions
+ if (rect.width != null) {
+ za.setCenter(rect.getCenter()).stayInside(FULL_AREA);
+ }
+ fn.setZoomArea(data, za);
+ fn.redisplay(data);
+ };
+
+
+ /**
+ * event handler, gets called when a annotationClick event is triggered
+ */
+ var handleAnnotationClick = function (evt, $div) {
+ var data = this;
+ var settings = data.settings;
+ console.debug("annotations: handleAnnotationClick", $div);
+ if (typeof settings.annotationOnClick === 'function') {
+ // execute callback
+ return settings.annotationOnClick(data, $div);
+ }
+ if (typeof settings.annotationOnClick === 'string') {
+ // execute action
+ return actions[settings.annotationOnClick](data, $div);
+ }
+ };
+
+
+ var defaults = {
+ // are annotations active?
+ 'isAnnotationsVisible' : true,
+ // buttonset of this plugin
+ 'annotationSet' : ['annotations', 'annotationuser', 'annotationmark', 'annotationregion', 'lessoptions'],
+ 'annotationReadOnlySet' : ['annotations', 'lessoptions'],
+ // URL of annotation server .e.g. 'http://tuxserve03.mpiwg-berlin.mpg.de/AnnotationManager/annotator'
+ 'annotationServerUrl' : null,
+ // show numbers in rectangle annotations
+ 'showAnnotationNumbers' : true,
+ // default width for annotation when only point is given
+ 'annotationAutoWidth' : 0.005,
+ // zoomfactor for displaying larger area around region (for autoZoomOnClick)
+ 'annotationAutoZoomFactor' : 3,
+ // zoom in and center on click on the annotation area
+ 'annotationOnClick' : zoomToAnnotation,
+ // are the annotations read-only
+ 'annotationsReadOnly' : false,
+ // URL of authentication token server e.g. 'http://libcoll.mpiwg-berlin.mpg.de/libviewa/template/token'
+ 'annotationTokenUrl' : null,
+ // URL of safe authentication token server e.g. 'https://libcoll.mpiwg-berlin.mpg.de/libviewa/template/token'
+ 'annotationSafeTokenUrl' : null,
+ // annotation user name
+ 'annotationUser' : 'anonymous',
+ // string or function that returns the uri of the page being annotated
+ 'annotationPageUri' : null,
+ // list of Annotator plugins
+ 'annotatorPlugins' : ['Auth', 'Permissions'],
+ // Annotator plugin settings (values that are functions are replaced by fn(data))
+ 'annotatorPluginSettings' : {
+ 'Auth' : {
+ 'token' : getAnnotationToken,
+ 'tokenUrl' : getAnnotationTokenUrl,
+ 'autoFetch' : true,
+ 'requestMethod' : 'POST',
+ 'requestData' : {
+ 'user': getAnnotationUser
+ },
+ 'unauthorizedCallback' : getHandleUnauthorized
+ },
+ 'Permissions' : {
+ 'user' : getAnnotationUser,
+ // userString and userId have to remain functions after evaluation
+ 'userString' : function (data) {
+ return function(user) {
+ if (user && user.name) {
+ return user.name;
+ }
+ return user;
+ };
+ },
+ 'userId' : function (data) {
+ return function(user) {
+ if (user && user.id) {
+ return user.id;
+ }
+ return user;
+ };
+ }
+ }
+ }
+ };
+
+ /**
+ * plugin installation. called by digilib on plugin object.
+ */
+ var install = function(plugin) {
+ digilib = plugin;
+ console.debug('installing annotator plugin. digilib:', digilib);
+ // import digilib functions
+ $.extend(fn, digilib.fn);
+ // import geometry classes
+ geom = fn.geometry;
+ // add defaults, actions, buttons
+ $.extend(digilib.defaults, defaults);
+ $.extend(digilib.actions, actions);
+ $.extend(digilib.buttons, buttons);
+ };
+
+ /** plugin initialization */
+ var init = function(data) {
+ console.debug('initialising annotator plugin. data:', data);
+ var $data = $(data);
+ var settings = data.settings;
+ FULL_AREA = geom.rectangle(0, 0, 1, 1);
+ // set up list of annotation wrappers
+ data.annotations = [];
+ // set up buttons
+ if (digilib.plugins.buttons != null) {
+ if (settings.annotationsReadOnly) {
+ installButtons(data, settings.annotationReadOnlySet);
+ } else {
+ installButtons(data, settings.annotationSet);
+ }
+ }
+ if (data.dlOpts.annotationUser != null) {
+ // get annotation user from cookie
+ settings.annotationUser = data.dlOpts.annotationUser;
+ }
+ // install event handler
+ $data.bind('setup', handleSetup);
+ $data.bind('redisplay', handleRedisplay);
+ $data.bind('update', handleUpdate);
+ $data.on('annotationClick', handleAnnotationClick);
+ };
+
+ /**
+ * setup creates Annotator object (after HTML setup by digilib).
+ */
+ var handleSetup = function(evt) {
+ console.debug("annotations: handleSetup");
+ var data = this;
+ var settings = data.settings;
+ // set up annotator (after html has been set up)
+ var uri = getAnnotationPageUri(data);
+ var elem = data.$elem.get(0);
+ var opts = {
+ 'store' : {
+ 'type' : Annotator.Plugin.Store,
+ 'prefix' : getAnnotationServerUrl(data),
+ 'annotationData' : {'uri' : uri}
+ },
+ 'loadQuery' : null,
+ 'readOnly' : data.settings.annotationsReadOnly,
+ };
+ console.debug("creating annotator.");
+ var annotator = new DigilibAnnotator(elem, opts);
+ annotator.setDigilibData(data);
+ // save annotator reference
+ data.annotator = annotator;
+ // set plugin parameters
+ var pluginParams = {};
+ // merge settings
+ // (deep copy of defaults from plugin and options from HTML)
+ $.extend(true, pluginParams, defaults.annotatorPluginSettings, data.options.annotatorPluginSettings);
+ // function to evaluate plugin settings
+ var evalParams = function (params) {
+ if (params == null) return;
+ // eval functions in params
+ $.each(params, function (idx, param) {
+ if (typeof param === 'function') {
+ // replace function by value
+ params[idx] = param(data);
+ } else if (param == null) {
+ // delete value null
+ delete params[idx];
+ } else if (typeof param === 'object') {
+ // evaluate sub-objects
+ evalParams(param);
+ }
+ });
+ };
+ // add plugins
+ $.each(settings.annotatorPlugins, function (idx, name) {
+ // ignore Store plugin (added by Annotator constructor)
+ if (name === 'Store') return;
+ var params = pluginParams[name];
+ evalParams(params);
+ console.debug("plugin:", name, params);
+ annotator.addPlugin(name, params);
+ });
+ // subscribe annotation delete event
+ annotator.subscribe("annotationDeleted", annotator.onDigilibAnnotationDeleted);
+ // save annotation token in cookie
+ var auth = annotator.plugins.Auth;
+ if (auth != null) {
+ auth.withToken(function (tkn) {
+ data.dlOpts.annotationToken = auth.token;
+ fn.storeOptions(data);
+ });
+ }
+ // load annotations
+ var query = {'uri' : uri};
+ annotator.load(query);
+ data.annotatorLoadQuery = query;
+ };
+
+
+ /**
+ * redisplay checks if the page has changes and reloads all annotations.
+ */
+ var handleRedisplay = function(evt) {
+ console.debug("annotations: handleRedisplay");
+ var data = this;
+ var uri = getAnnotationPageUri(data);
+ if (uri != data.annotatorLoadQuery.uri) {
+ // uri changed
+ data.annotatorLoadQuery.uri = uri;
+ reloadAnnotations(data);
+ }
+ };
+
+
+ /**
+ * update renders all annotations.
+ */
+ var handleUpdate = function(evt) {
+ console.debug("annotations: handleUpdate");
+ var data = this;
+ renderAnnotations(data);
+ };
+
+ // plugin object with name and init
+ // shared objects filled by digilib on registration
+ var plugin = {
+ name : 'annotator',
+ install : install,
+ init : init,
+ buttons : {},
+ actions : {},
+ fn : {},
+ plugins : {}
+ };
+
+ if (Annotator == null) {
+ $.error("Annotator.js Javascript not found!");
+ }
+ if ($.fn.digilib == null) {
+ $.error("jquery.digilib.annotator must be loaded after jquery.digilib!");
+ } else {
+ $.fn.digilib('plugin', plugin);
+ }
+})(jQuery);
diff -r 7d9132a513ab -r 49d643f0d658 webapp/src/main/webapp/jquery/jquery.digilib.annotator.js
--- a/webapp/src/main/webapp/jquery/jquery.digilib.annotator.js Mon Dec 15 18:49:42 2014 +0100
+++ b/webapp/src/main/webapp/jquery/jquery.digilib.annotator.js Fri Jan 16 18:20:15 2015 +0100
@@ -30,6 +30,8 @@
* and stored on a Annotator-API compatible server.
*/
(function($) {
+ // version of this plugin
+ var version = 'jquery.digilib.annotator.js 1.2.0';
// affine geometry
var geom = null;
@@ -96,8 +98,11 @@
auth.withToken(function (tkn) {
data.dlOpts.annotationToken = auth.token;
fn.storeOptions(data);
- // reload annotations
- reloadAnnotations(data);
+ // clear annotations
+ data.annotations = [];
+ renderAnnotations(data);
+ // reload annotations
+ annotator.plugins.Store.pluginInit();
});
},
@@ -223,12 +228,15 @@
$scaler.one('mousedown.dlSetAnnotationMark', function (evt) {
// event handler adding a new mark
console.log("setAnnotationMark at=", evt);
+ var annotator = data.annotator;
var mpos = geom.position(evt);
var pos = data.imgTrafo.invtransform(mpos);
// mark selection shape
var shape = {'type' : 'point', 'units' : 'fraction', 'geometry' : geom.position(pos)};
+ annotator.selectedShapes = [shape];
// create and edit new annotation
- createAnnotation(data, shape, mpos.getAsCss());
+ var annotation = annotator.createAnnotation();
+ annotator.showEditor(annotation, mpos.getAsCss());
return false;
});
};
@@ -236,57 +244,25 @@
/**
* Add a region-annotation where clicked.
*/
- var setAnnotationRegion = function (data) {
+ var setAnnotationRegion = function(data) {
+ var annotator = data.annotator;
fn.defineArea(data, function (data, rect) {
if (rect == null) return;
// event handler adding a new mark
console.log("setAnnotationRegion at=", rect);
// mark selection shape
var shape = {'type' : 'rectangle', 'units' : 'fraction', 'geometry' : rect};
+ annotator.selectedShapes = [shape];
+ // create and edit new annotation
var pos = rect.getPt1();
var mpos = data.imgTrafo.transform(pos);
- // create and edit new annotation
- createAnnotation(data, shape, mpos.getAsCss());
+ var annotation = annotator.createAnnotation();
+ annotator.showEditor(annotation, mpos.getAsCss());
});
};
/**
- * create an empty annotation with the given shape, show the editor at the given position,
- * and store the annotation using Annotator.
- *
- * @param shape shape object
- * @param editorPos css position object
- * @returns promise
- */
- var createAnnotation = function (data, shape, editorPos) {
- var annotator = data.annotator;
- var annotation = {'shapes' : [shape]};
- annotator.publish('beforeAnnotationCreated', [annotation]);
- annotator.setupAnnotation(annotation);
- // edit the annotation (returns a promise)
- var dfd = annotator.editAnnotation(annotation, editorPos);
- dfd.then(function (annotation) {
- // store annotation (returns deferred)
- return annotator.annotations.create(annotation)
- // handle storage errors
- .fail(function () {
- console.error("Error storing annotation!");
- // TODO: more error handling?
- });
- });
- dfd.done(function (annotation) {
- annotator.publish('annotationCreated', [annotation]);
- });
- // clean up (if, for example, editing was cancelled, or storage failed)
- dfd.fail(function (annotation) {
- console.warn("Editing annotation cancelled!");
- deleteAnnotation(data, annotation);
- });
- return dfd;
- };
-
- /**
- * Render all annotations on the image.
+ * Render all annotations on the image
*/
var renderAnnotations = function (data) {
if (data.annotations == null || data.annotator == null || data.$img == null || data.imgTrafo == null)
@@ -390,95 +366,46 @@
screenRect.adjustDiv($annotation);
};
-
- /**
- * Delete annotation from digilib.
- *
- * Finds the corresponding digilib annotation wrapper, removes any elements from screen,
- * and deletes the wrapper from the list.
- *
- * @param annotation the annotation object to delete.
- */
- var deleteAnnotation = function (data, annotation) {
- // remove annotation mark
- var annots = data.annotations;
- for (var i = 0; i < annots.length; ++i) {
- var annot = annots[i];
- if (annot.annotation === annotation) {
- // this is the right wrapper
- if (annot.$div != null) {
- // remove from screen
- annot.$div.remove();
- }
- // remove from list
- annots.splice(i, 1);
- break;
- }
- }
- };
-
-
- /**
- * Reload annotations for current page.
- */
- var reloadAnnotations = function (data) {
- // clear annotations
- data.annotations = [];
- renderAnnotations(data);
- // reload annotations
- data.annotator.load(data.annotatorLoadQuery);
- };
+ /**
+ * returns setupAnnotation function using the given data.
+ */
+ var getSetupAnnotation = function(data) {
+ return function (annotation) {
+ // create annotation wrapper
+ var ann = {
+ 'annotation' : annotation,
+ 'idx' : data.annotations.length+1
+ };
+ // add to list
+ data.annotations.push(ann);
+ // render this annotation
+ renderAnnotation(data, ann);
+ };
+ };
-
- /**
- * Our modified version of Annotator.
- */
- var DigilibAnnotator = Annotator.extend({
- /**
- * Set digilib data object in Annotator
- */
- 'setDigilibData' : function (data) {
- // set digilib data in options
- this.options.digilibData = data;
- },
- /**
- * Initialises an annotation from an object representation.
- * Overwrites Annotator.setupAnnotation().
- *
- * Checks for image annotations, creates a wrapper, adds wrapper to list,
- * and renders the annotation.
- */
- 'setupAnnotation' : function (annotation) {
- // digilibData has to be set in the options
- var data = this.options.digilibData;
- // is this a digilib image annotation?
- if (annotation.shapes != null || annotation.areas != null) {
- // create annotation wrapper
- var ann = {
- 'annotation' : annotation,
- 'idx' : data.annotations.length+1
- };
- // add to list
- data.annotations.push(ann);
- // render this annotation
- renderAnnotation(data, ann);
- } else {
- // Invoke the built-in implementation
- Annotator.prototype.setupAnnotation.call(this, annotation);
- }
- return annotation;
- },
- /**
- * Handler for annotationDeleted event for digilib annotations.
- */
- 'onDigilibAnnotationDeleted' : function (annotation) {
- // remove digilib annotation
- var data = this.options.digilibData;
- deleteAnnotation(data, annotation);
- }
- });
-
-
+ /**
+ * returns annotationDeleted function using the given data.
+ */
+ var getAnnotationDeleted = function(data) {
+ return function (annotation) {
+ // remove annotation mark
+ var annots = data.annotations;
+ for (var i = 0; i < annots.length; ++i) {
+ var annot = annots[i];
+ if (annot.annotation === annotation) {
+ // this is the right wrapper
+ if (annot.$div != null) {
+ // remove from screen
+ annot.$div.remove();
+ }
+ // remove from list
+ delete annots[i];
+ break;
+ }
+ }
+ };
+ };
+
/**
* returns unauthorizedCallback function for Annotator authlogin plugin.
*/
@@ -587,7 +514,7 @@
// string or function that returns the uri of the page being annotated
'annotationPageUri' : null,
// list of Annotator plugins
- 'annotatorPlugins' : ['Auth', 'Permissions'],
+ 'annotatorPlugins' : ['Auth', 'Permissions', 'Store', 'DigilibIntegrator'],
// Annotator plugin settings (values that are functions are replaced by fn(data))
'annotatorPluginSettings' : {
'Auth' : {
@@ -619,6 +546,21 @@
return user;
};
}
+ },
+ 'Store' : {
+ 'prefix' : getAnnotationServerUrl,
+ 'annotationData': {
+ 'uri': getAnnotationPageUri
+ },
+ 'loadFromSearch': {
+ 'uri': getAnnotationPageUri
+ }
+ },
+ 'DigilibIntegrator' : {
+ 'hooks' : {
+ 'setupAnnotation' : getSetupAnnotation,
+ 'annotationDeleted' : getAnnotationDeleted
+ }
}
}
};
@@ -661,7 +603,6 @@
}
// install event handler
$data.bind('setup', handleSetup);
- $data.bind('redisplay', handleRedisplay);
$data.bind('update', handleUpdate);
$data.on('annotationClick', handleAnnotationClick);
};
@@ -676,21 +617,10 @@
// set up annotator (after html has been set up)
var uri = getAnnotationPageUri(data);
var elem = data.$elem.get(0);
- var opts = {
- 'store' : {
- 'type' : Annotator.Plugin.Store,
- 'prefix' : getAnnotationServerUrl(data),
- 'annotationData' : {'uri' : uri}
- },
- 'loadQuery' : null,
- 'readOnly' : data.settings.annotationsReadOnly,
- };
- console.debug("creating annotator.");
- var annotator = new DigilibAnnotator(elem, opts);
- annotator.setDigilibData(data);
- // save annotator reference
- data.annotator = annotator;
+ var opts = {'readOnly' : data.settings.annotationsReadOnly};
+ var annotator = new Annotator(elem, opts);
// set plugin parameters
+ var def = defaults.annotatorPluginSettings;
var pluginParams = {};
// merge settings
// (deep copy of defaults from plugin and options from HTML)
@@ -714,15 +644,13 @@
};
// add plugins
$.each(settings.annotatorPlugins, function (idx, name) {
- // ignore Store plugin (added by Annotator constructor)
- if (name === 'Store') return;
var params = pluginParams[name];
evalParams(params);
console.debug("plugin:", name, params);
annotator.addPlugin(name, params);
});
- // subscribe annotation delete event
- annotator.subscribe("annotationDeleted", annotator.onDigilibAnnotationDeleted);
+ // save annotator reference
+ data.annotator = annotator;
// save annotation token in cookie
var auth = annotator.plugins.Auth;
if (auth != null) {
@@ -731,28 +659,8 @@
fn.storeOptions(data);
});
}
- // load annotations
- var query = {'uri' : uri};
- annotator.load(query);
- data.annotatorLoadQuery = query;
};
-
- /**
- * redisplay checks if the page has changes and reloads all annotations.
- */
- var handleRedisplay = function(evt) {
- console.debug("annotations: handleRedisplay");
- var data = this;
- var uri = getAnnotationPageUri(data);
- if (uri != data.annotatorLoadQuery.uri) {
- // uri changed
- data.annotatorLoadQuery.uri = uri;
- reloadAnnotations(data);
- }
- };
-
-
/**
* update renders all annotations.
*/