Source: directives/annotation.js

/**
 * The directive handles the logic behind a single annotation.
 * The annotation must be implemented to the element as a directive declaration,
 * because IE does not support custom elements reliably. In example, use:
 * `<span annotation="">...</span>` instead of `<annotation>...</annotation>`.
 * 
 * @module annotation
 * @author Joonas Lattu
 * @author Petteri Palojärvi
 * @author Seppo Tarvainen
 * @licence MIT
 * @copyright 2016 Timber project members
 */

/*
 var angular;
 var timApp = angular.module('timApp');
 */

/** Directive for a single annotation.
 * @lends module:reviewController
 */
timApp.directive("annotation",['$window', function ($window, $timeout) {
    "use strict";
    var console = $window.console;
    return {
        templateUrl: "/static/templates/annotation.html",
        transclude: true,
        scope: {
            show: '=',
            points: '=',
            visibleto: '=',
            comments: '=',
            aid: '=',
            ismargin: '=',
            annotator: '@',
            editaccess: '=',
            //email: '@',
            timesince: '@',
            creationtime: '@',
            velp: '@',
            newannotation: '@',
            showHidden: '@'
        },

        link: function (scope, element) {
            scope.newComment = "";
            scope.velpElement = null;
            scope.ctrlDown = false;
            scope.ctrlKey = 17;
            scope.visible_options = {
                "type": "select",
                "value": scope.visibleto,
                "values": [1, 2, 3, 4],
                "names": ["Just me", "Document owner", "Teachers", "Everyone"]
            };
            scope.newannotation = false;
            scope.marginonly = false;


            // Original visibility, or visibility in session
            // TODO: origin visibility
            scope.original = {
                points: scope.points,
                velp: scope.velp,
                visible_to: scope.visibleto,
                comment: scope.newComment,
                annotation_id: scope.aid
            };

            /**
             * Toggles the visibility of the annotation.
             * @method toggleAnnotation
             */
            scope.toggleAnnotation = function () {
                if (scope.velpElement === null) {
                    scope.velpElement = element[0].getElementsByClassName("annotation-info")[0];
                }
                var elementName = scope.velpElement.parentNode.offsetParent.className;
                var annotationParents = document.querySelectorAll('[aid="{0}"]'.replace('{0}', scope.aid));
                if ( elementName === "notes" &&  annotationParents.length > 1 ){
                    if (scope.aid > 0)
                    {
                        if (true) {// todo: detect is scope.$$prevSibling visible $(scope.$$prevSibling).is(':visible')
                            scope.$$prevSibling.show = !scope.$$prevSibling.show;
                            scope.$$prevSibling.updateVelpZIndex();
                        } else {
                             scope.show = !scope.show;
                            if (scope.show) {
                                scope.updateVelpZIndex();
                            }
                        }
                    } else {
                        scope.$$nextSibling.show = !scope.$$nextSibling.show;
                        scope.$$nextSibling.updateVelpZIndex();
                        //scope.show = !scope.show;
                        //scope.velpElement.parentNode. = !scope.velpElement.parentNode.$$prevSibling.show;
                        //annotationParents.show = !annotationParents[0].show;
                        //scope.show = !scope.show;
                    }

                    //scope.isolateScope.show = !scope.isolateScope.show;
                } else {
                    scope.show = !scope.show;
                    if (scope.show) {
                        scope.updateVelpZIndex();
                    } else {
                     console.log(scope.$parent.annotations);
                    }
                }
            };


            /**
             * Updates the z-index attribute of the annotation.
             * @method updateVelpZIndex
             */
            scope.updateVelpZIndex = function () {
                if (scope.velpElement === null) {
                    scope.velpElement = element[0].getElementsByClassName("annotation-info")[0];
                    scope.velpElement.style.zIndex = scope.$parent.zIndex.toString();
                    scope.$parent.zIndex++;
                } else {
                    scope.velpElement.style.zIndex = scope.$parent.zIndex.toString();
                    scope.$parent.zIndex++;
                }
            };

            /**
             * Shows the annotation.
             * @method showAnnotation
             */
            scope.showAnnotation = function () {
                scope.showHidden = false;
                scope.newannotation = false;
                scope.show = true;

                scope.updateVelpZIndex();
            };

            /**
             * Focuses on the comment field of the annotation.
             * @method focusTextarea
             */
            scope.focusTextarea =function(){
               return true;
            };



            /**
             * Deletes the selected annotation. Queries parent scope and deletes
             * the corresponding annotation from there.
             * @method deleteAnnotation
             */
            scope.deleteAnnotation = function () {

                if (scope.comments.length < 2) {
                    if (!$window.confirm("Delete - are you sure?")) {
                        return;
                    }
                    scope.$parent.deleteAnnotation(scope.aid, scope.ismargin);
                    scope.toggleAnnotation();
                }
            };

            /**
             * Updates the annotation and toggles its visibility in the margin.
             * @method updateAnnotation
             */
            scope.updateAnnotation = function () {
                var margin = false;
                if (scope.velpElement.parentNode.offsetParent.className ==="notes"){
                    margin = true;
                }
                scope.$parent.updateAnnotation(scope.aid, margin);
            };

            /**
             * Changes points of the selected annotation. Qeries parent scope
             * and changes the points of the corresponding annotation.
             * @method changePoints
             */
            scope.changePoints = function () {
                console.log(scope.points);
                scope.$parent.changeAnnotationPoints(scope.aid, scope.points);
            };

            /**
             * Saves the changes made to the annotation. Queries parent scope
             * and updates the changes of the corresponding annotation.
             * @method saveChanges
             */
            scope.saveChanges = function () {
                var id = scope.$parent.getRealAnnotationId(scope.aid);

                // Add comment
                if (scope.newComment.length > 0) {
                    var comment = scope.newComment;

                    var data = {annotation_id: id, content: scope.newComment};
                    scope.$parent.makePostRequest("/add_annotation_comment", data, function (json) {
                        scope.comments.push({
                            commenter_username: json.data.name,
                            content: comment,
                            comment_time: "now",
                            comment_relative_time: "just now"
                        });
                        scope.$parent.addComment(scope.aid, json.data.name, comment);
                        scope.updateAnnotation();
                    });

                } else {
                    scope.updateAnnotation();
                }
                scope.newComment = "";
                if (scope.visible_options.value !== scope.original.visible_to) {
                    scope.$parent.changeVisibility(scope.aid, scope.visible_options.value);
                }
                scope.original = {
                    points: scope.points,
                    annotation_id: id,
                    visible_to: scope.visible_options.value,
                    velp: scope.velp,
                    comment: scope.newComment,
                    doc_id: scope.$parent.docId
                };

                scope.$parent.makePostRequest("/update_annotation", scope.original, function (json) {
                    console.log(json);
                });
            };

            /**
             * Checks if the user has rights to edit the annotation.
             * @method checkRights
             * @returns {boolean} Whether the user has rights or not
             */
            scope.checkRights = function () {
                return scope.editaccess !== 1;
            };

            /**
             * Detect user right to annotation to document.
             * @param points - Points given in velp or annotation
             * @returns {boolean} Whether user has rights to make annotations
             */

            scope.notAnnotationRights = function (points) {
                if (scope.$parent.rights.teacher) {
                    return false;
                } else {
                    if (points === null) {
                        return false;
                    } else {
                        return true;
                    }
                }
            };

            /**
             * Return true if user has teacher rights.
             * @returns {boolean} Whether the user has teacher rights or not
             */
            scope.allowChangePoints = function () {
                return scope.$parent.rights.teacher;
            };



            /**
             * Checks if the annotation is changed compared to its last saved state.
             * @method checkIfChanged
             * @returns {boolean} Whether any modifications were made or not
             */
            scope.checkIfChanged = function () {
                if (scope.original.points !== scope.points)
                    return true;
                if (scope.original.comment !== scope.newComment)
                    return true;
                if (scope.original.visible_to !== scope.visible_options.value)
                    return true;
                if (scope.original.velp !== scope.velp)
                    return true;
                return false;
            };

            /**
             * Detects the `Ctrl + S` and `Ctrl+Enter` key strokes on the text area.
             * @method keyDownFunc
             * @param event - Current event
             */
            scope.keyDownFunc = function (event) {
                 if (event.keyCode === scope.ctrlKey) {
                     scope.ctrlDown = true;
                 }
                if (scope.ctrlDown && (String.fromCharCode(event.which).toLowerCase() === 's' || event.keyCode === 13 )) {
                    event.preventDefault();
                    scope.ctrlDown = false;
                    if (scope.checkIfChanged()){
                        scope.saveChanges();
                    } else {
                        scope.toggleAnnotation();
                    }
                }
		    };

            /**
             * Detects if `Ctrl`-key is released.
             * @method keyUpFunc
             * @param event - Current event
             */
            scope.keyUpFunc = function (event) {
                 if (event.keyCode === scope.ctrlKey) {
                     scope.ctrlDown = false;
                 }
		    };

           scope.$watch('newannotation', function(value) {
                if(value) {
                    element.find('textarea').focus();
                    scope.newannotation = false;
                    console.log("focus on new annotation");
                }
            });

            setTimeout(function(){
                if (scope.show) scope.updateVelpZIndex();
            }, 0);
        }
    };
}]);