Mercurial > hg > OKFNAnnotator
diff annotator_files/lib/annotator.js @ 3:6356e78ccf5c
new version contains Annotator JS files to be used with FilesystemSite.
author | casties |
---|---|
date | Thu, 05 Apr 2012 19:37:27 +0200 |
parents | |
children | 6979313586cf |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/annotator_files/lib/annotator.js Thu Apr 05 19:37:27 2012 +0200 @@ -0,0 +1,443 @@ +var Annotator, util, _Annotator, + __bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; }, + __hasProp = Object.prototype.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; }; + +util = { + uuid: (function() { + var counter; + counter = 0; + return function() { + return counter++; + }; + })(), + getGlobal: function() { + return (function() { + return this; + })(); + }, + 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; + } +}; + +_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 = { + hl: '<span class="annotator-hl"></span>', + adder: '<div class="annotator-adder"><button>' + _t('Annotate') + '</button></div>', + wrapper: '<div class="annotator-wrapper"></div>' + }; + + 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; + + 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); + this.showViewer = __bind(this.showViewer, this); + this.onEditorSubmit = __bind(this.onEditorSubmit, this); + this.onEditorHide = __bind(this.onEditorHide, this); + this.showEditor = __bind(this.showEditor, this); + var name, src, _ref; + Annotator.__super__.constructor.apply(this, arguments); + this.plugins = {}; + if (!Annotator.supported()) return this; + if (!this.options.readOnly) this._setupDocumentEvents(); + this._setupWrapper()._setupViewer()._setupEditor(); + _ref = this.html; + for (name in _ref) { + src = _ref[name]; + if (name !== 'wrapper') this[name] = $(src).appendTo(this.wrapper).hide(); + } + } + + 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() { + 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).escape(annotation.text); + } else { + $(field).html("<i>" + (_t('No Comment')) + "</i>"); + } + return _this.publish('annotationViewerTextField', [field, annotation]); + } + }).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.getSelectedRanges = function() { + var browserRange, i, normedRange, r, ranges, rangesToIgnore, selection, _i, _len; + selection = util.getGlobal().getSelection(); + ranges = []; + rangesToIgnore = []; + if (!selection.isCollapsed) { + ranges = (function() { + var _ref, _results; + _results = []; + for (i = 0, _ref = selection.rangeCount; 0 <= _ref ? i < _ref : i > _ref; 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); + } + 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; + }); + }; + + Annotator.prototype.createAnnotation = function() { + var annotation; + annotation = {}; + this.publish('beforeAnnotationCreated', [annotation]); + return annotation; + }; + + Annotator.prototype.setupAnnotation = function(annotation, fireEvents) { + var normed, normedRanges, r, sniffed, _i, _len; + if (fireEvents == null) fireEvents = true; + annotation.ranges || (annotation.ranges = this.selectedRanges); + normedRanges = (function() { + var _i, _len, _ref, _results; + _ref = annotation.ranges; + _results = []; + for (_i = 0, _len = _ref.length; _i < _len; _i++) { + r = _ref[_i]; + if (!(r != null)) continue; + sniffed = Range.sniff(r); + _results.push(sniffed.normalize(this.wrapper[0])); + } + return _results; + }).call(this); + normedRanges = $.grep(normedRanges, function(range) { + return range !== null; + }); + annotation.quote = []; + annotation.ranges = []; + annotation.highlights = []; + for (_i = 0, _len = normedRanges.length; _i < _len; _i++) { + normed = normedRanges[_i]; + 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.updateAnnotation = function(annotation) { + this.publish('beforeAnnotationUpdated', [annotation]); + this.publish('annotationUpdated', [annotation]); + return annotation; + }; + + Annotator.prototype.deleteAnnotation = function(annotation) { + var h, _i, _len, _ref; + _ref = annotation.highlights; + for (_i = 0, _len = _ref.length; _i < _len; _i++) { + h = _ref[_i]; + $(h).replaceWith(h.childNodes); + } + this.publish('annotationDeleted', [annotation]); + return annotation; + }; + + Annotator.prototype.loadAnnotations = function(annotations) { + var clone, loader, + _this = this; + if (annotations == null) annotations = []; + loader = function(annList) { + var n, now, _i, _len; + if (annList == null) annList = []; + now = annList.splice(0, 10); + for (_i = 0, _len = now.length; _i < _len; _i++) { + n = now[_i]; + _this.setupAnnotation(n, false); + } + if (annList.length > 0) { + return setTimeout((function() { + return loader(annList); + }), 1); + } else { + return _this.publish('annotationsLoaded', [clone]); + } + }; + 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) { + var node, white, _i, _len, _ref, _results; + white = /^\s*$/; + _ref = normedRange.textNodes(); + _results = []; + for (_i = 0, _len = _ref.length; _i < _len; _i++) { + node = _ref[_i]; + if (!white.test(node.nodeValue)) { + _results.push($(node).wrapAll(this.hl).parent().show()[0]); + } + } + return _results; + }; + + 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 <script> tag?")); + } + } + return this; + }; + + Annotator.prototype.showEditor = function(annotation, location) { + this.editor.element.css(location); + this.editor.load(annotation); + return this; + }; + + Annotator.prototype.onEditorHide = function() { + this.publish('annotationEditorHidden', [this.editor]); + return this.ignoreMouseup = false; + }; + + Annotator.prototype.onEditorSubmit = function(annotation) { + this.publish('annotationEditorSubmit', [this.editor, annotation]); + if (annotation.ranges === void 0) { + return this.setupAnnotation(annotation); + } else { + return this.updateAnnotation(annotation); + } + }; + + Annotator.prototype.showViewer = function(annotations, location) { + this.viewer.element.css(location); + this.viewer.load(annotations); + return this.publish('annotationViewerShown', [this.viewer, annotations]); + }; + + Annotator.prototype.startViewerHideTimer = function() { + if (!this.viewerHideTimer) { + return this.viewerHideTimer = setTimeout(this.viewer.hide, 250); + } + }; + + Annotator.prototype.clearViewerHideTimer = function() { + clearTimeout(this.viewerHideTimer); + return this.viewerHideTimer = false; + }; + + Annotator.prototype.checkForStartSelection = function(event) { + if (!(event && this.isAnnotator(event.target))) { + this.startViewerHideTimer(); + return this.mouseIsDown = true; + } + }; + + Annotator.prototype.checkForEndSelection = function(event) { + var container, range, _i, _len, _ref; + this.mouseIsDown = false; + if (this.ignoreMouseup) return; + this.selectedRanges = this.getSelectedRanges(); + _ref = this.selectedRanges; + for (_i = 0, _len = _ref.length; _i < _len; _i++) { + range = _ref[_i]; + container = range.commonAncestor; + if (this.isAnnotator(container)) return; + } + if (event && this.selectedRanges.length) { + return this.adder.css(util.mousePosition(event, this.wrapper[0])).show(); + } else { + return this.adder.hide(); + } + }; + + Annotator.prototype.isAnnotator = function(element) { + return !!$(element).parents().andSelf().filter('[class^=annotator-]').not(this.wrapper).length; + }; + + Annotator.prototype.onHighlightMouseover = function(event) { + var annotations; + this.clearViewerHideTimer(); + if (this.mouseIsDown || this.viewer.isShown()) return false; + annotations = $(event.target).parents('.annotator-hl').andSelf().map(function() { + return $(this).data("annotation"); + }); + return this.showViewer($.makeArray(annotations), util.mousePosition(event, this.wrapper[0])); + }; + + Annotator.prototype.onAdderMousedown = function(event) { + if (event != null) event.preventDefault(); + return this.ignoreMouseup = true; + }; + + Annotator.prototype.onAdderClick = function(event) { + var position; + if (event != null) event.preventDefault(); + position = this.adder.position(); + this.adder.hide(); + return this.showEditor(this.createAnnotation(), position); + }; + + Annotator.prototype.onEditAnnotation = function(annotation) { + var offset; + offset = this.viewer.element.position(); + this.viewer.hide(); + return this.showEditor(annotation, offset); + }; + + Annotator.prototype.onDeleteAnnotation = function(annotation) { + this.viewer.hide(); + return this.deleteAnnotation(annotation); + }; + + return Annotator; + +})(Delegator); + +Annotator.Plugin = (function(_super) { + + __extends(Plugin, _super); + + function Plugin(element, options) { + Plugin.__super__.constructor.apply(this, arguments); + } + + Plugin.prototype.pluginInit = function() {}; + + return Plugin; + +})(Delegator); + +Annotator.$ = $; + +Annotator.Delegator = Delegator; + +Annotator.Range = Range; + +Annotator._t = _t; + +Annotator.supported = function() { + return (function() { + return !!this.getSelection; + })(); +}; + +Annotator.noConflict = function() { + util.getGlobal().Annotator = _Annotator; + return this; +}; + +$.plugin('annotator', Annotator); + +this.Annotator = Annotator;