UNPKG

angular-annotorious

Version:

Angular directive for Annotorius to start drawing and commenting to images on your Web page.

480 lines (424 loc) 18.5 kB
/*global anno:false */ (function () { 'use strict'; angular.module('annotorious', []) .service('annotoriousService', annotoriousService) .directive('annotoriousAnnotate', annotoriousAnnotateDirective) .directive('annotorious', annotoriousDirective); function annotoriousService() { //Annotorious JavaScript API reference: // https://github.com/annotorious/annotorious/wiki/JavaScript-API var service = { getAnnotorious: getAnnotorious, createPlugin: createPlugin, activateSelector: activateSelector,//(opt_item_url_or_callback, opt_callback) addAnnotation: addAnnotation,//(annotation, opt_replace) setProperties: setProperties, //(opt_options) addHandler: addHandler, //(type, handler) addPlugin: addPlugin, //(pluginName, opt_config_options) destroy: destroy, //(opt_item_url) getAnnotations: getAnnotations, //(opt_item_url) hideAnnotations: hideAnnotations, //(opt_item_url) hideSelectionWidget: hideSelectionWidget, //(opt_item_url) highlightAnnotation: highlightAnnotation, //(annotation) makeAnnotatable: makeAnnotatable, //(item) removeAll: removeAll, //(opt_item_url) removeAnnotation: removeAnnotation, //(annotation) reset: reset, //() showAnnotations: showAnnotations, //(opt_item_url) showSelectionWidget: showSelectionWidget //(opt_item_url) }; return service; //////////// function getAnnotorious() { return anno; } /** * Sets the color properties for the annotation canvas. * NOTE: The text dialogs are out-of-scope and per CSS customizable. * The following properties can be changed: * * { * outline: '#00f', * stroke: '#ff0000', * fill: 'rgba(255, 0, 0, 0.3)', * hi_stroke: '#00ff00', * hi_fill: 'rgba(0, 255, 0, 0.3)' * } * * @param props */ function setProperties(props) { getAnnotorious().setProperties(props); } /** * Creates and registers a plugin. * * @param pluginId * @param opt_config_options * @param pluginFunc * @param initFunc * @param annotatorFunc */ function createPlugin(pluginId, opt_config_options, pluginFunc, initFunc, annotatorFunc) { if (!opt_config_options) { opt_config_options = {}; } if (!pluginFunc) { pluginFunc = function (opt_config_options) { }; } if (!initFunc) { initFunc = function (anno) { // Add initialization code here, if needed (or just skip this method if not) }; } if (!annotatorFunc) { annotatorFunc = function (annotator) { // A Field can be an HTML string or a function(annotation) that returns a string /*annotator.popup.addField(function(annotation) { return '<em>Hello World: ' + annotation.text.length + ' chars</em>' });*/ }; } // Set all plugin methods annotorious.plugin[pluginId] = pluginFunc; annotorious.plugin[pluginId].prototype.initPlugin = initFunc; annotorious.plugin[pluginId].prototype.onInitAnnotator = annotatorFunc; // Add the plugin addPlugin(pluginId, opt_config_options); } /** * NOTE: this method is currently only relevant for the OpenLayers module. * Feel free to ignore in case you are only using the standard image annotation features of Annotorious. * Manually actives the selector. The selector can be activated on a specific * item or globally, on all items (which serves mainly as a shortcut for pages where * there is only one annotatable item). The function can take a callback function as parameter, * which will be called when the selector is deactivated again. * * @param opt_item_url_or_callback * @param opt_callback * @returns {*} */ function activateSelector(opt_item_url_or_callback, opt_callback) { return getAnnotorious().activateSelector(opt_item_url_or_callback, opt_callback); } /** * Adds a new annotation, or replaces an existing annotation with a new annotation. * (In the latter case, the parameter opt_replace must be the existing annotation.) * * @param annotation * @param opt_replace * @returns {*} */ function addAnnotation(annotation, opt_replace) { return getAnnotorious().addAnnotation(annotation, opt_replace); } /** * Adds an event handler function. * Annotorious issues the following events: onMouseOverItem(event) - fired when the mouse enters an annotatable item onMouseOutOfItem(event) - fired when the mouse leaves an annotatable item onMouseOverAnnotation(event) - fired when the mouse enters an annotation onMouseOutOfAnnotation(event) - fired when the mouse leaves an annotation onSelectionStarted(event) - fired when the user starts a selection onSelectionCanceled(event) - fired when the user cancels a selection (not available on all selection tools) onSelectionCompleted(event) - fired when the user completes a selection onSelectionChanged(event) - fired when the user changed a selection beforePopupHide(popup) - fired just before the annotation info popup window hides beforeAnnotationRemoved(annotation) - fired before an annotation is removed (Note: it is possible to prevent annotation removal by returning false from the handler method!) onAnnotationRemoved(annotation) - fired when an annotation is removed from an imgae onAnnotationCreated(annotation) - fired when an annotation was created onAnnotationUpdated(annotation) - fired when an existing annotation was edited/updated * * @param type * @param handler * @returns {*} */ function addHandler(type, handler) { return getAnnotorious().addHandler(type, handler); } /** * Registers a plugin. For more information, * see the Plugins Wiki page https://github.com/annotorious/annotorious/wiki/Plugins. * * @param pluginName * @param opt_config_options * @returns {*} */ function addPlugin(pluginName, opt_config_options) { return getAnnotorious().addPlugin(pluginName, opt_config_options); } /** * Destroys annotation functionality on a specific item, or on all items on the page. * Note that this method differs from anno.reset() (see below) insofar as destroy does not * re-evaluate the annotatable CSS attributes. What is destroyed, stays destroyed. * (Until re-enabled through anno.makeAnnotatable()). * * @param opt_item_url * @returns {*} */ function destroy(opt_item_url) { return getAnnotorious().destroy(opt_item_url); } /** * Returns the current annotations. opt_item_url is optional. If omitted, * the method call will return all annotations, on all annotatable items on the page. * If set to a specific item URL, only the annotations on that item will be returned. * * @param opt_item_url * @returns {*} */ function getAnnotations(opt_item_url) { return getAnnotorious().getAnnotations(opt_item_url); } /** * Hides existing annotations on all, or a specific item. * * @param opt_item_url * @returns {*} */ function hideAnnotations(opt_item_url) { return getAnnotorious().hideAnnotations(opt_item_url); } /** * Disables the selection widget (the small tooltip in the upper left corner which * says "Click and Drag to Annotate"), thus preventing users from creating new annotations * altogether. The typical use case for this is 'read-only' annotated images. * I.e. if you want to add some pre-defined annotations using anno.addAnnotation without the * user being able to add or change anything. * * The selection widget can be hidden on a specific item or globally, on all annotatable items on the page. * * @param opt_item_url * @returns {*} */ function hideSelectionWidget(opt_item_url) { return getAnnotorious().hideSelectionWidget(opt_item_url); } /** * Highlights the specified annotation, just as if the mouse pointer was hovering over it. * The annotation will remain highlighted until one of these conditions is met: * * -The user moves the mouse into, and out of the annotation * -The user moves the mouse over another annotation * -The highlight is removed by calling this method with an empty parameter, e.g. anno.highlightAnnotation() or anno.highlightAnnotation(undefined) * -Another annotation is highlighted via anno.highlightAnnotation * * @param annotation * @returns {*} */ function highlightAnnotation(annotation) { return getAnnotorious().highlightAnnotation(annotation); } /** * Makes an item on the screen annotatable (if there is a module available supporting the item format). * You can use this method as an alternative to CSS-based activation. It works just the same way, * and is simply there for convenience, and to prepare for (future) item formats that technically * don't support CSS-based activation (such as Web maps). * * @param item * @returns {*} */ function makeAnnotatable(item) { return getAnnotorious().makeAnnotatable(item); } /** * Removes all annotations. If the optional parameter opt_item_url is set, only the annotations on the * specified item will be removed. Otherwise all annotations on all items on the page will be removed. * * @param opt_item_url * @returns {*} */ function removeAll(opt_item_url) { return getAnnotorious().removeAll(opt_item_url); } /** * Removes an annotation from the page. * * @param annotation * @returns {*} */ function removeAnnotation(annotation) { return getAnnotorious().removeAnnotation(annotation); } /** * Performs a 'hard reset' on Annotorious. This means all annotation features will be removed, * and the page will be re-scanned for items with the 'annotatable' CSS class. (Note: this method * could be handy in case you are working with JavaScript image carousels. Just make sure * the images have 'annotatable' set, then reset Annotorious after each page flip.) * * NOTE: Annotorious will destroy the current annotation canvas, and create a new one * * @returns {*} */ function reset() { return getAnnotorious().reset(); } /** * Shows existing annotations on all, or a specific item (if they were hidden using anno.hideAnnotations). * * @param opt_item_url * @returns {*} */ function showAnnotations(opt_item_url) { return getAnnotorious().showAnnotations(opt_item_url); } /** * Enables the selection widget (the small tooltip in the upper left corner which says * "Click and Drag to Annotate"), thus enabling users to creating new annotations. * (Per default, the selection widget is enabled.) * * @param opt_item_url * @returns {*} */ function showSelectionWidget(opt_item_url) { return getAnnotorious().showSelectionWidget(opt_item_url); } } annotoriousAnnotateDirective.$inject = ['annotoriousService', '$timeout']; function annotoriousAnnotateDirective(annotoriousService, $timeout) { var service = { restrict: 'A', link: annotateLink, priority: 100 // must lower priority than ngSrc (99) }; return service; //////////////////////////// link.$inject = ['$scope', '$element', '$attributes']; function annotateLink($scope, $element, $attributes) { if ($attributes.src) { annotoriousService.makeAnnotatable($element[0]); } else { $element.bind('load', function () { $scope.$apply(function () { annotoriousService.makeAnnotatable($element[0]); }); }); } } } annotoriousDirective.$inject = ['$compile', '$rootScope', '$http', '$parse', '$timeout', 'annotoriousService']; function annotoriousDirective($compile, $rootScope, $http, $parse, $timeout, annotoriousService) { var service = { restrict: 'E', scope: { open: '=', options: '=', onOpen: '&', onLoad: '&', onComplete: '&', onCleanup: '&', onClosed: '&' }, require: 'annotorious', link: link, controller: controller, controllerAs: 'vm' }; return service; //////////////////////////// controller.$inject = ['$scope']; function controller($scope) { } link.$inject = ['$scope', '$element', '$attributes']; function link($scope, $element, $attributes, controller) { var cb = null; $scope.$watch('open', function (newValue, oldValue) { //console.log("watch $scope.open(" + $scope.open + ") " + oldValue + "->" + newValue); if (oldValue !== newValue) { updateOpen(newValue); } }); $scope.$on('$destroy', function () { $element.remove(); }); init(); function updateOpen(newValue) { if (newValue) { init(newValue); } else { if (options.annotationsFor) { var annotatables = $(options.annotationsFor); annotatables.each(function (idx) { var item = this; annotoriousService.makeAnnotatable(item); }); } } } function init(open) { var options = { //href: $attributes.src, annotationsFor: $attributes.annotationsFor, onOpen: function () { if ($scope.onOpen && $scope.onOpen()) { $scope.onOpen()(); } }, onLoad: function () { if ($scope.onLoad && $scope.onLoad()) { $scope.onLoad()(); } }, onComplete: function () { onComplete(); if ($scope.onComplete && $scope.onComplete()) { $scope.onComplete()(); } }, onCleanup: function () { if ($scope.onCleanup && $scope.onCleanup()) { $scope.onCleanup()(); } }, onClosed: function () { $scope.$apply(function () { $scope.open = false; }); if ($scope.onClosed && $scope.onClosed()) { $scope.onClosed()(); } } }; //generic way that sets all (non-function) parameters of annotorious. if ($scope.options) { angular.extend(options, $scope.options); } //clean undefined for (var key in options) { if (options.hasOwnProperty(key)) { if (typeof(options[key]) === 'undefined') { delete options[key]; } } } if (typeof(open) !== 'undefined') { options.open = open; } //wait for the DOM view to be ready $timeout(function () { /*if your image is dynamically loaded, make sure to set the 'annotatable' class on the element and use anno.reset() after loading a new image if necessary. I had to do this because annotorious was loading a 0x0px canvas when dynamically setting the image src via an angular model.*/ if (options.annotationsFor) { $(options.annotationsFor).each(function (i) { var itemToAnnotate = this; annotoriousService.makeAnnotatable(itemToAnnotate); }); } }, 0); } function onComplete() { /*$rootScope.$apply(function () { var content = $('#cboxLoadedContent'); $compile(content)($rootScope); });*/ } } } }) ();