timeline_canvas.js 5.0 KB
/* Copyright 2018 Onestein
 * License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). */

odoo.define('web_timeline.TimelineCanvas', function (require) {
    "use strict";
    var Widget = require('web.Widget');

    /**
     * Used to draw stuff on upon the timeline view.
     */
    var TimelineCanvas = Widget.extend({
        template: 'TimelineView.Canvas',

        /**
         * Clears all drawings (svg elements) from the canvas.
         */
        clear: function () {
            this.$el.find(' > :not(defs)').remove();
        },

        /**
         * Gets the path from one point to another.
         *
         * @param {Number} coordx1
         * @param {Number} coordy1
         * @param {Number} coordx2
         * @param {Number} coordy2
         * @param {Number} width1
         * @param {Number} height1
         * @param {Number} width2
         * @param {Number} height2
         * @param {Number} widthMarker The marker's width of the polyline
         * @param {Number} breakAt The space between the line turns
         * @returns {Array} Each item represents a coordinate
         */
        get_polyline_points: function (coordx1, coordy1, coordx2, coordy2,
                                       width1, height1, width2, height2,
                                       widthMarker, breakAt) {
            var halfHeight1 = height1 / 2;
            var halfHeight2 = height2 / 2;
            var x1 = coordx1 - widthMarker;
            var y1 = coordy1 + halfHeight1;
            var x2 = coordx2 + width2;
            var y2 = coordy2 + halfHeight2;
            var xDiff = x1 - x2;
            var yDiff = y1 - y2;
            var threshold = breakAt + widthMarker;
            var spaceY = halfHeight2 + 6;

            var points = [[x1, y1]];
            if (y1 !== y2) {
                if (xDiff > threshold) {
                    points.push([x1 - breakAt, y1]);
                    points.push([x1 - breakAt, y1 - yDiff]);
                } else if (xDiff <= threshold) {
                    var yDiffSpace = yDiff > 0 ? spaceY : -spaceY;
                    points.push([x1 - breakAt, y1]);
                    points.push([x1 - breakAt, y2 + yDiffSpace]);
                    points.push([x2 + breakAt, y2 + yDiffSpace]);
                    points.push([x2 + breakAt, y2]);
                }
            } else if(x1 < x2) {
                points.push([x1 - breakAt, y1]);
                points.push([x1 - breakAt, y1 + spaceY]);
                points.push([x2 + breakAt, y2 + spaceY]);
                points.push([x2 + breakAt, y2]);
            }
            points.push([x2, y2]);

            return points;
        },

        /**
         * Draws an arrow.
         *
         * @param {HTMLElement} from Element to draw the arrow from
         * @param {HTMLElement} to Element to draw the arrow to
         * @param {String} color Color of the line
         * @param {Number} width Width of the line
         * @returns {HTMLElement} The created SVG polyline
         */
        draw_arrow: function (from, to, color, width) {
            return this.draw_line(from, to, color, width, '#arrowhead', 10, 12);
        },

        /**
         * Draws a line.
         *
         * @param {HTMLElement} from Element to draw the line from
         * @param {HTMLElement} to Element to draw the line to
         * @param {String} color Color of the line
         * @param {Number} width Width of the line
         * @param {String} markerStart Start marker of the line
         * @param {Number} widthMarker The marker's width of the polyline
         * @param {Number} breakLineAt The space between the line turns
         * @returns {HTMLElement} The created SVG polyline
         */
        draw_line: function (from, to, color, width, markerStart, widthMarker, breakLineAt) {
            var x1 = from.offsetLeft,
                y1 = from.offsetTop + from.parentElement.offsetTop,
                x2 = to.offsetLeft,
                y2 = to.offsetTop + to.parentElement.offsetTop,
                width1 = from.clientWidth,
                height1 = from.clientHeight,
                width2 = to.clientWidth,
                height2 = to.clientHeight;

            var points = this.get_polyline_points(
                x1, y1, x2, y2, width1, height1, width2, height2, widthMarker, breakLineAt
            );

            var polyline_points = _.map(points, function(point) {
                return point.join(',');
            }).join();

            var line = document.createElementNS(
                'http://www.w3.org/2000/svg', 'polyline'
            );
            line.setAttribute('points', polyline_points);
            line.setAttribute('stroke', color || '#000');
            line.setAttribute('stroke-width', width || 1);
            line.setAttribute('fill', 'none');
            if (markerStart) {
                line.setAttribute('marker-start', 'url(' + markerStart + ')');
            }
            this.$el.append(line);
            return line;
        },
    });

    return TimelineCanvas;
});