UNPKG

canvasimo

Version:

An HTML5 canvas drawing library, with 150+ useful methods, jQuery-like fluent interface, and cross-browser compatibility enhancements.

1,735 lines 70.6 kB
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
// tslint:disable-next-line:no-var-requires
var VERSION = require('../package.json').version;
var constants_1 = require("./constants");
var logger_1 = require("./logger");
var utils_1 = require("./utils");
var unsupportedMethodErrors = [];
var logUnsupportedMethodError = function (method) {
    if (unsupportedMethodErrors.indexOf(method) < 0) {
        unsupportedMethodErrors.push(method);
        logger_1.default.warn(method + " is not supported by this browser");
    }
};
var Canvasimo = /** @class */ (function () {
    function Canvasimo(element) {
        var _this = this;
        this.ctxType = constants_1.CONTEXT_TYPE;
        this.density = constants_1.DEFAULT_DENSITY;
        /**
         * @group Canvas element
         * @description A collection of methods for getting and setting various properties of the canvas element.
         */
        /**
         * Get the canvas element.
         * @alias getElement
         */
        this.getCanvas = function () { return _this.element; };
        this.getElement = function () { return _this.getCanvas(); };
        /**
         * Set the canvas pixel density.
         */
        this.setDensity = function (density) {
            if (_this.density !== density) {
                var _a = _this.getSize(), prevWidth = _a.width, prevHeight = _a.height;
                _this.saveContextValues();
                _this.density = density;
                _this.element.width = prevWidth * density;
                _this.element.height = prevHeight * density;
                _this.restoreContextValues();
            }
            return _this;
        };
        /**
         * Get the canvas pixel density.
         */
        this.getDensity = function () { return _this.density; };
        /**
         * Set the canvas dimensions.
         */
        this.setSize = function (width, height) {
            _this.saveContextValues();
            if (typeof width === 'object') {
                _this.element.width = width.width * _this.density;
                _this.element.height = width.height * _this.density;
            }
            else if (typeof height === 'number') {
                _this.element.width = width * _this.density;
                _this.element.height = height * _this.density;
            }
            _this.restoreContextValues();
            return _this;
        };
        /**
         * Get the canvas dimensions.
         */
        this.getSize = function () { return ({
            width: _this.element.width / _this.density,
            height: _this.element.height / _this.density,
        }); };
        /**
         * Set the canvas width.
         */
        this.setWidth = function (width) {
            _this.saveContextValues();
            _this.element.width = width * _this.density;
            _this.restoreContextValues();
            return _this;
        };
        /**
         * Get the canvas width.
         */
        this.getWidth = function () { return _this.element.width / _this.density; };
        /**
         * Set the canvas height.
         */
        this.setHeight = function (height) {
            _this.saveContextValues();
            _this.element.height = height * _this.density;
            _this.restoreContextValues();
            return _this;
        };
        /**
         * Get the canvas height.
         */
        this.getHeight = function () { return _this.element.height / _this.density; };
        /**
         * Get the canvas size & position on screen.
         */
        this.getBoundingClientRect = function () { return _this.element.getBoundingClientRect(); };
        /**
         * @group Context
         * @description 'A collection of methods for retrieving a canvas context or information about the context.
         */
        /**
         * Get the standard canvas context (used for drawing).
         */
        this.getContext = function (type, contextAttributes) {
            return _this.element.getContext(type, contextAttributes);
        };
        /**
         * Get canvas context used by Canvasimo (2d).
         */
        this.getCurrentContext = function () { return _this.ctx; };
        /**
         * Get the context type used by Canvasimo ('2d', 'webgl', etc).
         */
        this.getCurrentContextType = function () { return _this.ctxType; };
        /**
         * Get the context attributes used.
         */
        this.getContextAttributes = function () {
            if (typeof _this.ctx.getContextAttributes !== 'function') {
                logUnsupportedMethodError('getContextAttributes');
                return null;
            }
            return _this.ctx.getContextAttributes();
        };
        /**
         * @group Solid Shapes
         * @description A collection of methods for plotting or drawing solid shapes -
         * those that create a new shape when invoked, and are self closing.
         */
        // plotRect',
        /**
         * Plot a rectangle that can then have a fill or stroke applied to it.
         * @alias rect
         */
        this.plotRect = function (x, y, width, height) { return _this.rect(x, y, width, height); };
        this.rect = function (x, y, width, height) {
            _this.ctx.rect(x * _this.density, y * _this.density, width * _this.density, height * _this.density);
            return _this;
        };
        /**
         * Plot a rectangle and apply a stroke to it.
         */
        this.strokeRect = function (x, y, width, height, color) {
            if (typeof color !== 'undefined') {
                _this.setStroke(color);
            }
            _this.ctx.strokeRect(x * _this.density, y * _this.density, width * _this.density, height * _this.density);
            return _this;
        };
        /**
         *  Plot a rectangle and apply a fill to it.
         */
        this.fillRect = function (x, y, width, height, color) {
            if (typeof color !== 'undefined') {
                _this.setFill(color);
            }
            _this.ctx.fillRect(x * _this.density, y * _this.density, width * _this.density, height * _this.density);
            return _this;
        };
        /**
         *  Plot a rounded rectangle that can then have a fill or stroke applied to it.
         */
        this.plotRoundedRect = function (x, y, width, height, radius) {
            var minRadius = Math.min(width / 2, height / 2, radius);
            return _this
                .beginPath()
                .moveTo(x + minRadius, y)
                .lineTo(x + width - minRadius, y)
                .arcTo(x + width, y, x + width, y + minRadius, minRadius)
                .lineTo(x + width, y + height - minRadius)
                .arcTo(x + width, y + height, x + width - minRadius, y + height, minRadius)
                .lineTo(x + minRadius, y + height)
                .arcTo(x, y + height, x, y + height - minRadius, minRadius)
                .lineTo(x, y + minRadius)
                .arcTo(x, y, x + minRadius, y, minRadius)
                .closePath();
        };
        /**
         *  Plot a rounded rectangle and apply a stroke to it.
         */
        this.strokeRoundedRect = function (x, y, width, height, radius, color) {
            return _this
                .plotRoundedRect(x, y, width, height, radius)
                .stroke(color);
        };
        /**
         *  Plot a rounded rectangle and apply a fill to it.
         */
        this.fillRoundedRect = function (x, y, width, height, radius, color) {
            return _this
                .plotRoundedRect(x, y, width, height, radius)
                .fill(color);
        };
        /**
         * Plot a circle that can then have a stroke or fill applied to it.
         */
        this.plotCircle = function (x, y, radius, anticlockwise) {
            return _this
                .beginPath()
                .plotArc(x, y, radius, 0, Math.PI * 2, anticlockwise)
                .closePath();
        };
        /**
         * Plot a circle and apply a stroke to it.
         */
        this.strokeCircle = function (x, y, radius, anticlockwise, color) {
            return _this
                .plotCircle(x, y, radius, anticlockwise)
                .stroke(color);
        };
        /**
         * Plot a circle and apply a fill to it.
         */
        this.fillCircle = function (x, y, radius, anticlockwise, color) {
            return _this
                .plotCircle(x, y, radius, anticlockwise)
                .fill(color);
        };
        /**
         * Plot a polygon that can then have a stroke or fill applied to it.
         */
        this.plotPoly = function (x, y, radius, sides, anticlockwise) {
            sides = Math.round(sides);
            if (!sides || sides < 3) {
                return _this;
            }
            var direction = anticlockwise ? -1 : 1;
            var beforeEnd = function (i) { return anticlockwise ? i > -sides : i < sides; };
            _this
                .beginPath()
                .moveTo(x + radius, y);
            for (var i = 0; beforeEnd(i); i += direction) {
                var angle = Math.PI * 2 / sides * i;
                _this.lineTo(x + radius * Math.cos(angle), y + radius * Math.sin(angle));
            }
            return _this.closePath();
        };
        /**
         * Plot a polygon and apply a stoke to it.
         */
        this.strokePoly = function (x, y, radius, sides, anticlockwise, color) {
            sides = Math.round(sides);
            if (!sides || sides < 3) {
                return _this;
            }
            return _this
                .plotPoly(x, y, radius, sides, anticlockwise)
                .stroke(color);
        };
        /**
         * Plot a polygon and apply a fill to it.
         */
        this.fillPoly = function (x, y, radius, sides, anticlockwise, color) {
            sides = Math.round(sides);
            if (!sides || sides < 3) {
                return _this;
            }
            return _this
                .plotPoly(x, y, radius, sides, anticlockwise)
                .fill(color);
        };
        /**
         * Plot a star that can then have a stroke or fill applied to it.
         */
        this.plotStar = function (x, y, radius1, sides, anticlockwise) {
            sides = Math.round(sides);
            if (!sides || sides < 3) {
                return _this;
            }
            else if (sides === 3 || sides === 4) {
                return _this.plotPoly(x, y, radius1, sides);
            }
            sides = sides * 2;
            var direction = anticlockwise ? -1 : 1;
            var offset = Math.PI * 2 / sides;
            var cross = Math.cos(offset * 2) * radius1;
            var radius2 = cross / Math.cos(offset);
            var beforeEnd = function (i) { return anticlockwise ? i > -sides : i < sides; };
            _this
                .beginPath()
                .moveTo(x + radius1, y);
            for (var i = 0; beforeEnd(i); i += direction) {
                var angle = offset * i;
                var radius = i % 2 ? radius2 : radius1;
                _this.lineTo(x + radius * Math.cos(angle), y + radius * Math.sin(angle));
            }
            return _this.closePath();
        };
        /**
         * Plot a star and apply a stoke to it.
         */
        this.strokeStar = function (x, y, radius1, sides, anticlockwise, color) {
            sides = Math.round(sides);
            if (!sides || sides < 3) {
                return _this;
            }
            return _this
                .plotStar(x, y, radius1, sides, anticlockwise)
                .stroke(color);
        };
        /**
         * Plot a star and apply a fill to it.
         */
        this.fillStar = function (x, y, radius1, sides, anticlockwise, color) {
            sides = Math.round(sides);
            if (!sides || sides < 3) {
                return _this;
            }
            return _this
                .plotStar(x, y, radius1, sides, anticlockwise)
                .fill(color);
        };
        /**
         * Plot a burst that can then have a stroke or fill applied to it.
         */
        this.plotBurst = function (x, y, radius1, radius2, sides, anticlockwise) {
            sides = Math.round(sides);
            if (!sides || sides < 3) {
                return _this;
            }
            sides = sides * 2;
            var direction = anticlockwise ? -1 : 1;
            var offset = Math.PI * 2 / sides;
            var beforeEnd = function (i) { return anticlockwise ? i > -sides : i < sides; };
            _this
                .beginPath()
                .moveTo(x + radius1, y);
            for (var i = 0; beforeEnd(i); i += direction) {
                var angle = offset * i;
                var radius = i % 2 ? radius2 : radius1;
                _this.lineTo(x + radius * Math.cos(angle), y + radius * Math.sin(angle));
            }
            return _this
                .closePath();
        };
        /**
         * Plot a burst and apply a stoke to it.
         */
        this.strokeBurst = function (x, y, radius1, radius2, sides, anticlockwise, color) {
            sides = Math.round(sides);
            if (!sides || sides < 3) {
                return _this;
            }
            return _this
                .plotBurst(x, y, radius1, radius2, sides, anticlockwise)
                .stroke(color);
        };
        /**
         * Plot a burst and apply a fill to it.
         */
        this.fillBurst = function (x, y, radius1, radius2, sides, anticlockwise, color) {
            sides = Math.round(sides);
            if (!sides || sides < 3) {
                return _this;
            }
            return _this
                .plotBurst(x, y, radius1, radius2, sides, anticlockwise)
                .fill(color);
        };
        /**
         * Plot a single pixel that can then have a stroke or fill applied to it.
         */
        this.plotPixel = function (x, y) {
            return _this
                .plotRect(x, y, 1, 1);
        };
        /**
         * Plot a single pixel and apply a stroke to it.
         */
        this.strokePixel = function (x, y, color) {
            return _this
                .strokeRect(x, y, 1, 1, color);
        };
        /**
         * Plot a single pixel and apply a fill to it.
         */
        this.fillPixel = function (x, y, color) {
            return _this
                .fillRect(x, y, 1, 1, color);
        };
        /**
         * Plot a closed path that can then have a stroke or fill applied to it.
         */
        this.plotClosedPath = function (points) {
            return _this
                .beginPath()
                .plotPath(points)
                .closePath();
        };
        /**
         * Plot a closed path and apply a stroke to it.
         */
        this.strokeClosedPath = function (points, color) {
            return _this
                .plotClosedPath(points)
                .stroke(color);
        };
        /**
         * Plot a closed path and apply a fill to it.
         */
        this.fillClosedPath = function (points, color) {
            return _this
                .plotClosedPath(points)
                .fill(color);
        };
        /**
         * @group Open Shapes
         * @description A collection of methods for plotting or drawing open shapes -
         * those that create a new shape when invoked, but are not self closing.
         */
        /**
         *  Plot a line that can then have a stroke or fill applied to it.
         */
        this.plotLine = function (x1, y1, x2, y2) {
            return _this
                .moveTo(x1, y1)
                .lineTo(x2, y2);
        };
        /**
         *  Plot a line and apply a stroke to it.
         */
        this.strokeLine = function (x1, y1, x2, y2, color) {
            return _this
                .plotLine(x1, y1, x2, y2)
                .stroke(color);
        };
        /**
         *  Plot a line, by length & angle, that can then have a stroke or fill applied to it.
         */
        this.plotLength = function (x1, y1, length, angle) {
            var x2 = x1 + length * Math.cos(angle);
            var y2 = y1 + length * Math.sin(angle);
            return _this
                .moveTo(x1, y1)
                .lineTo(x2, y2);
        };
        /**
         *  Plot a line, by length & angle, and apply a stroke to it.
         */
        this.strokeLength = function (x1, y1, length, angle, color) {
            return _this
                .plotLength(x1, y1, length, angle)
                .stroke(color);
        };
        /**
         *  Plot a path, that is not self closing, that can have a stroke or fill applied to it.
         */
        this.plotPath = function (points) {
            utils_1.forPoints(points, function (x, y, i) {
                if (i === 0) {
                    _this.moveTo(x, y);
                }
                else {
                    _this.lineTo(x, y);
                }
            });
            return _this;
        };
        /**
         *  Plot a path, that is not self closing, and apply a stroke to it.
         */
        this.strokePath = function (points, color) {
            return _this
                .plotPath(points)
                .stroke(color);
        };
        /**
         *  Plot a path, that is not self closing, and apply a fill to it.
         */
        this.fillPath = function (points, color) {
            return _this
                .plotPath(points)
                .fill(color);
        };
        /**
         * @group Paths
         * @description A collection of methods for plotting or drawing paths -
         * shapes that can be connected to create more complex shapes.
         */
        /**
         * Plot an arc that can have a stroke or fill applied to it.
         * @alias arc
         */
        this.plotArc = function (x, y, radius, startAngle, endAngle, anticlockwise) {
            return _this.arc(x, y, radius, startAngle, endAngle, anticlockwise);
        };
        this.arc = function (x, y, radius, startAngle, endAngle, anticlockwise) {
            _this.ctx.arc(x * _this.density, y * _this.density, radius * _this.density, startAngle, endAngle, anticlockwise || false);
            return _this;
        };
        /**
         * Plot an arc and apply a stroke to it.
         */
        this.strokeArc = function (x, y, radius, startAngle, endAngle, anticlockwise, color) {
            return _this
                .plotArc(x, y, radius, startAngle, endAngle, anticlockwise)
                .stroke(color);
        };
        /**
         * Plot an arc and apply a fill to it.
         */
        this.fillArc = function (x, y, radius, startAngle, endAngle, anticlockwise, color) {
            return _this
                .plotArc(x, y, radius, startAngle, endAngle, anticlockwise)
                .fill(color);
        };
        /**
         * Plot an ellipse that can then have a stroke or fill applied to it.
         * @alias ellipse
         */
        this.plotEllipse = function (x, y, radiusX, radiusY, rotation, startAngle, endAngle, anticlockwise) {
            return _this.ellipse(x, y, radiusX, radiusY, rotation, startAngle, endAngle, anticlockwise);
        };
        this.ellipse = function (x, y, radiusX, radiusY, rotation, startAngle, endAngle, anticlockwise) {
            // tslint:disable-next-line:strict-type-predicates
            if (typeof _this.ctx.ellipse === 'function') {
                _this.ctx.ellipse(x * _this.density, y * _this.density, radiusX * _this.density, radiusY * _this.density, rotation, startAngle, endAngle, anticlockwise || false);
                return _this;
            }
            return _this
                .save()
                .translate(x, y)
                .rotate(rotation)
                .scale(1, radiusY / radiusX)
                .plotArc(0, 0, radiusX, startAngle, endAngle, anticlockwise)
                .restore();
        };
        /**
         * Plot an ellipse and apply a stroke to it.
         */
        this.strokeEllipse = function (x, y, radiusX, radiusY, rotation, startAngle, endAngle, anticlockwise, color) {
            return _this
                .plotEllipse(x, y, radiusX, radiusY, rotation, startAngle, endAngle, anticlockwise)
                .stroke(color);
        };
        /**
         * Plot an ellipse and apply a fill to it.
         */
        this.fillEllipse = function (x, y, radiusX, radiusY, rotation, startAngle, endAngle, anticlockwise, color) {
            return _this
                .plotEllipse(x, y, radiusX, radiusY, rotation, startAngle, endAngle, anticlockwise)
                .fill(color);
        };
        /**
         * @group Text
         * @description A collection of methods for drawing text,
         * and getting and setting properties related to text rendering.
         */
        /**
         * Draw a text with a stroke.
         */
        this.strokeText = function (text, x, y, maxWidth, color) {
            if (typeof color !== 'undefined') {
                _this.setStroke(color);
            }
            if (typeof maxWidth !== 'number') {
                _this.ctx.strokeText(text, x * _this.density, y * _this.density);
            }
            else {
                _this.ctx.strokeText(text, x * _this.density, y * _this.density, maxWidth * _this.density);
            }
            return _this;
        };
        /**
         * Draw a text with a fill.
         */
        this.fillText = function (text, x, y, maxWidth, color) {
            if (typeof color !== 'undefined') {
                _this.setFill(color);
            }
            // If max width is not a number (e.g. undefined) then iOS does not draw anything
            if (typeof maxWidth !== 'number') {
                _this.ctx.fillText(text, x * _this.density, y * _this.density);
            }
            else {
                _this.ctx.fillText(text, x * _this.density, y * _this.density, maxWidth * _this.density);
            }
            return _this;
        };
        /**
         * Draw text with a stroke, wrapped at newlines and automatically wrapped if the text exceeds the maxWidth.
         * If no maxWidth is specified text will only wrap at newlines (wordBreak is ignore).
         * Words will not break by default (normal) and therefore may overflow.
         * break-all will break words wherever possible, and break-word will only break words if there is not enough room.
         * The lineHeight parameter is a multiplier for the font size, and defaults to 1.
         */
        this.strokeTextMultiline = function (text, x, y, maxWidth, wordBreak, lineHeight, color) {
            return _this.textMultiline(_this.strokeText, text, x, y, maxWidth, wordBreak, lineHeight, color);
        };
        /**
         * Draw text with a fill, wrapped at newlines and automatically wrapped if the text exceeds the maxWidth.
         * If no maxWidth is specified text will only wrap at newlines (wordBreak is ignore).
         * Words will not break by default (normal) and therefore may overflow.
         * break-all will break words wherever possible, and break-word will only break words if there is not enough room.
         * The lineHeight parameter is a multiplier for the font size, and defaults to 1.
         */
        this.fillTextMultiline = function (text, x, y, maxWidth, wordBreak, lineHeight, color) {
            return _this.textMultiline(_this.fillText, text, x, y, maxWidth, wordBreak, lineHeight, color);
        };
        /**
         * Get information about the size text will be drawn.
         * @alias measureText
         */
        this.getTextSize = function (text) { return _this.measureText(text); };
        this.measureText = function (text) {
            var metrics = _this.ctx.measureText(text);
            return {
                width: (metrics.width || 0) / _this.density,
            };
        };
        /**
         * Set the horizontal text alignment.
         */
        this.setTextAlign = function (value) { return _this.setCanvasProperty('textAlign', value); };
        /**
         * Get the horizontal text alignment.
         */
        this.getTextAlign = function () { return _this.getCanvasProperty('textAlign'); };
        /**
         * Set the vertical text alignment.
         */
        this.setTextBaseline = function (value) { return _this.setCanvasProperty('textBaseline', value); };
        /**
         * Get the vertical text alignment.
         */
        this.getTextBaseline = function () { return _this.getCanvasProperty('textBaseline'); };
        /**
         * @group Fonts
         * @description A collection of methods for getting and setting font styles and variations.
         */
        /**
         * Set the font to use.
         */
        this.setFont = function (font) {
            _this.ctx.font = utils_1.formatFont(font, _this.density, false);
            return _this;
        };
        /**
         * Get the font that is being used.
         * This returns the exact CanvasRenderingContext2D.font string.
         */
        this.getFont = function () {
            return utils_1.formatFont(_this.ctx.font, _this.density, true);
        };
        /**
         * Set the font family to use.
         */
        this.setFontFamily = function (family) {
            var parts = utils_1.getFontParts(_this.ctx.font, _this.density, true);
            if (parts.length < 5) {
                return _this.setFont('');
            }
            parts[4] = family || constants_1.DEFAULT_FONT_PARTS[4];
            _this.ctx.font = utils_1.formatFont(parts.join(' '), _this.density, false);
            return _this;
        };
        /**
         * Get the font that is being used.
         */
        this.getFontFamily = function () {
            var parts = utils_1.getFontParts(_this.ctx.font, _this.density, true);
            if (parts.length < 5) {
                return null;
            }
            return parts[4];
        };
        /**
         * Set the font size to use.
         */
        this.setFontSize = function (size) {
            var parts = utils_1.getFontParts(_this.ctx.font, _this.density, true);
            if (parts.length < 5) {
                return _this.setFont('');
            }
            parts[3] = (typeof size === 'number' ? size + 'px' : size) || constants_1.DEFAULT_FONT_PARTS[3];
            _this.ctx.font = utils_1.formatFont(parts.join(' '), _this.density, false);
            return _this;
        };
        /**
         * Get the font size that is being used.
         * Returns null if using a special font e.g. caption, icon, menu.
         */
        this.getFontSize = function () {
            var parts = utils_1.getFontParts(_this.ctx.font, _this.density, true);
            if (parts.length < 5) {
                return null;
            }
            return parseFloat(parts[3]);
        };
        /**
         * Set the font style to use.
         */
        this.setFontStyle = function (style) {
            var parts = utils_1.getFontParts(_this.ctx.font, _this.density, true);
            if (parts.length < 5) {
                return _this.setFont('');
            }
            parts[0] = style || constants_1.DEFAULT_FONT_PARTS[0];
            _this.ctx.font = utils_1.formatFont(parts.join(' '), _this.density, false);
            return _this;
        };
        /**
         * Get the font style that is being used.
         * Returns null if using a special font e.g. caption, icon, menu.
         */
        this.getFontStyle = function () {
            var parts = utils_1.getFontParts(_this.ctx.font, _this.density, true);
            if (parts.length < 5) {
                return null;
            }
            return parts[0];
        };
        /**
         * Set the font variant to use.
         */
        this.setFontVariant = function (variant) {
            var parts = utils_1.getFontParts(_this.ctx.font, _this.density, true);
            if (parts.length < 5) {
                return _this.setFont('');
            }
            parts[1] = variant || constants_1.DEFAULT_FONT_PARTS[1];
            _this.ctx.font = utils_1.formatFont(parts.join(' '), _this.density, false);
            return _this;
        };
        /**
         * Get the font variant that is being used.
         * Returns null if using a special font e.g. caption, icon, menu.
         */
        this.getFontVariant = function () {
            var parts = utils_1.getFontParts(_this.ctx.font, _this.density, true);
            if (parts.length < 5) {
                return null;
            }
            return parts[1];
        };
        /**
         * Set the font weight to use.
         */
        this.setFontWeight = function (weight) {
            var parts = utils_1.getFontParts(_this.ctx.font, _this.density, true);
            if (parts.length < 5) {
                return _this.setFont('');
            }
            parts[2] = weight.toString() || constants_1.DEFAULT_FONT_PARTS[2];
            _this.ctx.font = utils_1.formatFont(parts.join(' '), _this.density, false);
            return _this;
        };
        /**
         * Get the font weight that is being used.
         * Returns null if using a special font e.g. caption, icon, menu.
         */
        this.getFontWeight = function () {
            var parts = utils_1.getFontParts(_this.ctx.font, _this.density, true);
            if (parts.length < 5) {
                return null;
            }
            return parts[2];
        };
        /**
         * @group Stroke Styles
         * @description A collection of methods for getting and setting stroke styles,
         * and applying strokes to existing shapes.
         */
        /**
         * Apply a stroke to the current shape.
         */
        this.stroke = function (color, path) {
            if (typeof color === 'string') {
                _this.setStroke(color);
                // tslint:disable-next-line:strict-type-predicates
                if (path && typeof path === 'object') {
                    _this.ctx.stroke(path);
                }
                else {
                    _this.ctx.stroke();
                }
                // tslint:disable-next-line:strict-type-predicates
            }
            else if (color && typeof color === 'object') {
                _this.ctx.stroke(color);
                // tslint:disable-next-line:strict-type-predicates
            }
            else if (path && typeof path === 'object') {
                _this.ctx.stroke(path);
            }
            else {
                _this.ctx.stroke();
            }
            return _this;
        };
        /**
         * Set the stroke style to use.
         * @alias setStrokeStyle
         */
        this.setStroke = function (value) { return _this.setStrokeStyle(value); };
        this.setStrokeStyle = function (value) { return _this.setCanvasProperty('strokeStyle', value); };
        /**
         * Get the stroke style that is being used.
         * @alias getStrokeStyle
         */
        this.getStroke = function () { return _this.getStrokeStyle(); };
        this.getStrokeStyle = function () { return _this.getCanvasProperty('strokeStyle'); };
        /**
         * Set the stroke cap to use.
         * @alias setLineCap
         */
        this.setStrokeCap = function (value) { return _this.setLineCap(value); };
        this.setLineCap = function (value) { return _this.setCanvasProperty('lineCap', value); };
        /**
         * Get the stroke cap that is being used.
         * @alias getLineCap
         */
        this.getStrokeCap = function () { return _this.getLineCap(); };
        this.getLineCap = function () { return _this.getCanvasProperty('lineCap'); };
        /**
         * Set the stroke dash to use.
         * @alias setLineDash
         */
        this.setStrokeDash = function (segments) { return _this.setLineDash(segments); };
        this.setLineDash = function (segments) {
            // tslint:disable-next-line:strict-type-predicates
            if (typeof _this.ctx.setLineDash !== 'function') {
                logUnsupportedMethodError('setLineDash');
                return _this;
            }
            _this.ctx.setLineDash(segments.map(function (segment) { return segment * _this.density; }));
            return _this;
        };
        /**
         * Get the stroke dash that is being used.
         * @alias getLineDash
         */
        this.getStrokeDash = function () { return _this.getLineDash(); };
        this.getLineDash = function () {
            // tslint:disable-next-line:strict-type-predicates
            if (typeof _this.ctx.getLineDash !== 'function') {
                logUnsupportedMethodError('getLineDash');
                return [];
            }
            return (_this.ctx.getLineDash() || []).map(function (value) { return value / _this.density; });
        };
        /**
         * Set the stroke dash offset to use.
         * @alias setLineDashOffset
         */
        this.setStrokeDashOffset = function (value) { return _this.setLineDashOffset(value); };
        this.setLineDashOffset = function (value) {
            return _this.setCanvasProperty('lineDashOffset', value * _this.density);
        };
        /**
         * Get the stroke dash offset that is being used.
         * @alias getLineDashOffset
         */
        this.getStrokeDashOffset = function () { return _this.getLineDashOffset(); };
        this.getLineDashOffset = function () { return _this.getCanvasProperty('lineDashOffset') / _this.density; };
        /**
         * Set the stroke join to use.
         * @alias setLineJoin
         */
        this.setStrokeJoin = function (value) { return _this.setLineJoin(value); };
        this.setLineJoin = function (value) { return _this.setCanvasProperty('lineJoin', value); };
        /**
         * Get the stroke join that is being used.
         * @alias getLineJoin
         */
        this.getStrokeJoin = function () { return _this.getLineJoin(); };
        this.getLineJoin = function () { return _this.getCanvasProperty('lineJoin'); };
        /**
         * Set the stroke width to use.
         * @alias setLineWidth
         */
        this.setStrokeWidth = function (value) { return _this.setLineWidth(value); };
        this.setLineWidth = function (value) { return _this.setCanvasProperty('lineWidth', value * _this.density); };
        /**
         * Get the stroke width that is being used.
         * @alias getLineWidth
         */
        this.getStrokeWidth = function () { return _this.getLineWidth(); };
        this.getLineWidth = function () { return _this.getCanvasProperty('lineWidth') / _this.density; };
        /**
         * Set the miter limit to use.
         */
        this.setMiterLimit = function (value) { return _this.setCanvasProperty('miterLimit', value * _this.density); };
        /**
         * Get the miter limit that is being used.
         */
        this.getMiterLimit = function () { return _this.getCanvasProperty('miterLimit') / _this.density; };
        /**
         * @group Fill styles
         * @description A collection of methods for getting and setting fill styles,
         * and applying fills to existing shapes.
         */
        /**
         * Apply a fill to the current shape.
         */
        this.fill = function (color, fillRule) {
            if (utils_1.isFillRule(color)) {
                _this.ctx.fill(color);
            }
            else if (typeof color === 'string') {
                _this.setFill(color);
                if (fillRule) {
                    _this.ctx.fill(fillRule);
                }
                else {
                    _this.ctx.fill();
                }
            }
            else {
                _this.ctx.fill(fillRule);
            }
            return _this;
        };
        /**
         * Apply a fill to the entire canvas area.
         */
        this.fillCanvas = function (color) {
            return _this
                .resetTransform()
                .fillRect(0, 0, _this.getWidth(), _this.getHeight(), color);
        };
        /**
         * Clear the entire canvas area
         */
        this.clearCanvas = function () {
            return _this.setWidth(_this.getWidth());
        };
        /**
         * Clear a rectangular area of the canvas.
         */
        this.clearRect = function (x, y, width, height) {
            _this.ctx.clearRect(x * _this.density, y * _this.density, width * _this.density, height * _this.density);
            return _this;
        };
        /**
         * Set the fill to use.
         * @alias setFillStyle
         */
        this.setFill = function (value) { return _this.setFillStyle(value); };
        this.setFillStyle = function (value) { return _this.setCanvasProperty('fillStyle', value); };
        /**
         * Get the fill that is being used.
         * @alias getFillStyle
         */
        this.getFill = function () { return _this.getFillStyle(); };
        this.getFillStyle = function () { return _this.getCanvasProperty('fillStyle'); };
        /**
         * Create a linear gradient to use as a fill.
         */
        this.createLinearGradient = function (x0, y0, x1, y1) {
            return _this.ctx.createLinearGradient(x0 * _this.density, y0 * _this.density, x1 * _this.density, y1 * _this.density);
        };
        /**
         * Create a radial gradient to use as a fill.
         */
        this.createRadialGradient = function (x0, y0, r0, x1, y1, r1) {
            return _this.ctx.createRadialGradient(x0 * _this.density, y0 * _this.density, r0 * _this.density, x1 * _this.density, y1 * _this.density, r1 * _this.density);
        };
        /**
         * Create a pattern to be used as a fill.
         */
        this.createPattern = function (image, repetition) {
            return _this.ctx.createPattern(image, repetition);
        };
        /**
         * Draw an image to the canvas.
         * If the second position / size arguments are supplied, the first will be used for cropping the image,
         * and the second for the position and size it will be drawn.
         */
        this.drawImage = function (image, srcX, srcY, srcW, srcH, dstX, dstY, dstW, dstH) {
            if (typeof srcW !== 'undefined' &&
                typeof srcH !== 'undefined') {
                if (typeof dstX !== 'undefined' &&
                    typeof dstY !== 'undefined' &&
                    typeof dstW !== 'undefined' &&
                    typeof dstH !== 'undefined') {
                    _this.ctx.drawImage(image, srcX * _this.density, srcY * _this.density, srcW * _this.density, srcH * _this.density, dstX * _this.density, dstY * _this.density, dstW * _this.density, dstH * _this.density);
                }
                else {
                    _this.ctx.drawImage(image, srcX * _this.density, srcY * _this.density, srcW * _this.density, srcH * _this.density);
                }
            }
            else {
                _this.ctx.drawImage(image, srcX * _this.density, srcY * _this.density);
            }
            return _this;
        };
        /**
         * @group Image Data
         * @description A collection of methods for creating, putting, or getting image data about the canvas.
         */
        /**
         * Get a data URL of the current canvas state.
         */
        this.getDataURL = function (type) {
            var args = [];
            for (var _i = 1; _i < arguments.length; _i++) {
                args[_i - 1] = arguments[_i];
            }
            var _a;
            return (_a = _this.element).toDataURL.apply(_a, [type].concat(args));
        };
        /**
         * Create image data with either the width and height specified,
         * or with the width and height of a the image data supplied.
         */
        this.createImageData = function (width, height) {
            if (typeof width !== 'number') {
                return _this.ctx.createImageData(width);
            }
            return _this.ctx.createImageData(width * _this.density, height || 0 * _this.density);
        };
        /**
         * Get the image data from an area of the canvas.
         */
        this.getImageData = function (sx, sy, sw, sh) {
            return _this.ctx.getImageData(sx * _this.density, sy * _this.density, sw * _this.density, sh * _this.density);
        };
        /**
         * Draw image data onto the canvas.
         */
        this.putImageData = function (imagedata, dx, dy, dirtyX, dirtyY, dirtyWidth, dirtyHeight) {
            if (typeof dirtyX === 'undefined' ||
                typeof dirtyY === 'undefined' ||
                typeof dirtyWidth === 'undefined' ||
                typeof dirtyHeight === 'undefined') {
                _this.ctx.putImageData(imagedata, dx * _this.density, dy * _this.density);
            }
            else {
                _this.ctx.putImageData(imagedata, dx * _this.density, dy * _this.density, dirtyX * _this.density, dirtyY * _this.density, dirtyWidth * _this.density, dirtyHeight * _this.density);
            }
            return _this;
        };
        /**
         * Get image data about a specific pixel.
         */
        this.getPixelData = function (x, y) {
            return _this.getImageData(x, y, 1, 1).data;
        };
        /**
         * Get the color of a specific pixel.
         */
        this.getPixelColor = function (x, y) {
            var data = _this.getImageData(x, y, 1, 1).data;
            return _this.createRGBA(data[0], data[1], data[2], data[3]);
        };
        /**
         * @group Color Helpers
         * @description A collection of methods to help with creating color strings.
         */
        /**
         * Create an HSL color string from the given values.
         */
        this.createHSL = function (h, s, l) {
            return 'hsl(' + h + ',' + s + '%,' + l + '%)';
        };
        /**
         * Create an HSLA color string from the given values.
         */
        this.createHSLA = function (h, s, l, a) {
            return 'hsla(' + h + ',' + s + '%,' + l + '%,' + a + ')';
        };
        /**
         * Create an RGB color string from the given values.
         */
        this.createRGB = function (r, g, b) {
            return 'rgb(' + r + ',' + g + ',' + b + ')';
        };
        /**
         * Create an RGBA color string from the given values.
         */
        this.createRGBA = function (r, g, b, a) {
            return 'rgba(' + r + ',' + g + ',' + b + ',' + a + ')';
        };
        /**
         * Return an HSL color string from the given HSLA color string.
         */
        this.getHSLFromHSLA = function (color) { return _this.getRGBFromRGBA(color); };
        /**
         * Return an RGB color string from the given RGBA color string.
         */
        this.getRGBFromRGBA = function (color) {
            var lastCommaIndex = color.lastIndexOf(',');
            return color.replace(/^(\w{3})a/, '$1').substring(0, lastCommaIndex - 1) + ')';
        };
        /**
         * @group Converting Sizes
         * @description A collection of methods to help with calculating and converting sizes, and distances.
         */
        /**
         * Get a fraction from the provided percent value e.g. 80 returns 0.8.
         */
        this.getFractionFromPercent = function (percent) {
            return (percent / 100);
        };
        /**
         * Get a percent from the provided fraction value e.g. 0.7 returns 70.
         */
        this.getPercentFromFraction = function (fraction) {
            return (fraction * 100);
        };
        /**
         * Returns the actual value of a fraction of the canvas width e.g.
         * a canvas with a width of 200 returns 100 if the provided value is 0.5.
         */
        this.getFractionOfWidth = function (fraction) {
            return _this.getWidth() * fraction;
        };
        /**
         * Returns the actual value of a fraction of the canvas height e.g.
         * a canvas with a height of 100 returns 20 if the provided value is 0.2.
         */
        this.getFractionOfHeight = function (fraction) {
            return _this.getHeight() * fraction;
        };
        /**
         * Returns the actual value of a percentage of the canvas width e.g.
         * a canvas with a width of 200 returns 100 if the provided value is 50.
         */
        this.getPercentOfWidth = function (percent) {
            return _this.getWidth() / 100 * percent;
        };
        /**
         * Returns the actual value of a percentage of the canvas height e.g.
         * a canvas with a height of 100 returns 20 if the provided value is 20.
         */
        this.getPercentOfHeight = function (percent) {
            return _this.getHeight() / 100 * percent;
        };
        /**
         * Returns the distance between 2 points.
         */
        this.getDistance = function (x1, y1, x2, y2) {
            return Math.sqrt(Math.pow(x1 - x2, 2) + Math.pow(y1 - y2, 2));
        };
        /**
         * @group Converting Angles
         * @description A collection of methods to help with calculating and converting angles.
         */
        /**
         * Get a radian value from the provided degrees e.g. 90 returns 1.5708.
         */
        this.getRadiansFromDegrees = function (degrees) {
            return degrees * Math.PI / 180;
        };
        /**
         * Get a degree value from the provided radians e.g. 3.14159 returns 180.
         */
        this.getDegreesFromRadians = function (radians) {
            return radians * 180 / Math.PI;
        };
        /**
         * Get the angle (in radians) between 2 or 3 points.
         */
        this.getAngle = function () {
            var args = [];
            for (var _i = 0; _i < arguments.length; _i++) {
                args[_i] = arguments[_i];
            }
            if (!args.length || !(args.length === 4 || args.length === 6)) {
                throw new Error(constants_1.INCORRECT_GET_ANGLE_ARGUMENTS);
            }
            var x1 = args[0];
            var y1 = args[1];
            var x2 = args[2];
            var y2 = args[3];
            if (args.length === 4) {
                return Math.atan2(y2 - y1, x2 - x1);
            }
            var x3 = args[4];
            var y3 = args[5];
            var a = _this.getAngle(x1, y1, x2, y2);
            var b = _this.getAngle(x2, y2, x3, y3);
            var c = b - a;
            if (c >= 0) {
                return Math.PI - c;
            }
            return -Math.PI - c;
        };
        /**
         * @group Path Plotting
         * @description A collection of methods for path drawing.
         */
        /**
         * Begin a new path (shape).
         */
        this.beginPath = function () {
            _this.ctx.beginPath();
            return _this;
        };
        /**
         * Close the current path (shape).
         */
        this.closePath = function () {
            _this.ctx.closePath();
            return _this;
        };
        /**
         * Move the starting point of a the next sub-path.
         */
        this.moveTo = function (x, y) {
            _this.ctx.moveTo(x * _this.density, y * _this.density);
            return _this;
        };
        /**
         * Connect the last point to the provided coordinates.
         */
        this.lineTo = function (x, y) {
            _this.ctx.lineTo(x * _this.density, y * _this.density);
            return _this;
        };
        /**
         * Arc from one point to another.
         */
        this.arcTo = function (x1, y1, x2, y2, radius) {
            _this.ctx.arcTo(x1 * _this.density, y1 * _this.density, x2 * _this.density, y2 * _this.density, radius * _this.density);
            return _this;
        };
        /**
         * Connect the last point to the provided coordinates with a bezier curve (2 control points).
         */
        this.bezierCurveTo = function (cp1x, cp1y, cp2x, cp2y, x, y) {
            _this.ctx.bezierCurveTo(cp1x * _this.density, cp1y * _this.density, cp2x * _this.density, cp2y * _this.density, x * _this.density, y * _this.density);
            return _this;
        };
        /**
         * Connect the last point to the provided coordinates with a quadratic curve (1 control point).
         */
        this.quadraticCurveTo = function (cpx, cpy, x, y) {
            _this.ctx.quadraticCurveTo(cpx * _this.density, cpy * _this.density, x * _this.density, y * _this.density);
            return _this;
        };
        /**
         * @group Canvas State
         * @description A collection of methods to save, restore, or transform the canvas state.
         */
        /**
         * Push the current state of the canvas into a stack that can later be restored.
         */
        this.save = function () {
            _this.ctx.save();
            return _this;
        };
        /**
         * Restore the most recent state of the canvas that was saved.
         */
        this.restore = function () {
            _this.ctx.restore();
            return _this;
        };
        /**
         * Add rotation (in radians) to the transform matrix so that shapes can be drawn at an angle.
         */
        this.rotate = function (angle) {
            _this.ctx.rotate(angle);
            return _this;
        };
        /**
         * Scale the transform matrix so that shapes can be drawn at the provided scale.
         */
        this.scale = function (x, y) {
            _this.ctx.scale(x, y);
            return _this;
        };
        /**
         * Move the canvas origin.
         */
        this.translate = function (x, y) {
            _this.ctx.translate(x * _this.density, y * _this.density);
            return _this;
        };
        /**
         * Multiply the current transformation with the provided matrix.
         */
        this.transform = function (m11, m12, m21, m22, dx, dy) {
            _this.ctx.transform(m11, m12, m21, m22, dx, dy);
            return _this;
        };
        /**
         * Replace the current transformation with the provided matrix.
         */
        this.setTransform = function (m11, m12, m21, m22, dx, dy) {
            _this.ctx.setTransform(m11, m12, m21, m22, dx, dy);
            return _this;
        };
        /**
         * Replace the current transformation with the default matrix: [1, 0, 0, 1, 0, 0].
         */
        this.resetTransform = function () {
            if (typeof _this.ctx.resetTransform === 'function') {
                _this.ctx.resetTransform();
                return _this;
            }
            return _this.setTransform(1, 0, 0, 1, 0, 0);
        };
        /**
         * Use the current path as a clipping path.
         */
        this.clip = function (fillRule) {
            _this.ctx.clip(fillRule);
            return _this;
        };
        /**
         * Set the opacity to use for drawing.
         * @alias setGlobalAlpha
         */
        this.setOpacity = function (value) { return _this.setGlobalAlpha(value); };
        this.setGlobalAlpha = function (value) { return _this.setCanvasProperty('globalAlpha', value); };
        /**
         * Get the opacity that is being used.
         * @alias getGlobalAlpha
         */
        this.getOpacity = function () { return _this.getGlobalAlpha(); };
        this.getGlobalAlpha = function () { return _this.getCanvasProperty('globalAlpha'); };
        /**
         * Set the composite operation to use for drawing.
         * @alias setGlobalCompositeOperation
         */
        this.setCompositeOperation = function (value) {
            return _this.setGlobalCompositeOperation(value);
        };
        this.setGlobalCompositeOperation = function (value) {
            return _this.setCanvasProperty('globalCompositeOperation', value);
        };
        /**
         * Get the composite operation that is being used.
         * @alias getGlobalCompositeOperation
         */
        this.getCompositeOperation = function () { return _this.getGlobalCompositeOperation(); };
        this.getGlobalCompositeOperation = function () {
            return _this.getCanvasProperty('globalCompositeOperation');
        };
        /**
         * Set whether image smoothing should be used.
         */
        this.setImageSmoothingEnabled = function (value) {
            for (var _i = 0, IMAGE_SMOOTHING_KEYS_1 = constants_1.IMAGE_SMOOTHING_KEYS; _i < IMAGE_SMOOTHING_KEYS_1.length; _i++) {
                var key = IMAGE_SMOOTHING_KEYS_1[_i];
                if (key in _this.ctx) {
                    _this.ctx[key] = value || false;
                    return _this;
                }
            }
            return _this;
        };
        /**
         * Get whether image smoothing is being used.
         */
        this.getImageSmoothingEnabled = function () {
            for (var _i = 0, IMAGE_SMOOTHING_KEYS_2 = constants_1.IMAGE_SMOOTHING_KEYS; _i < IMAGE_SMOOTHING_KEYS_2.length; _i++) {
                var key = IMAGE_SMOOTHING_KEYS_2[_i];
                if (key in _this.ctx) {
                    return _this.ctx[key];
                }
            }
            return false;
        };
        /**
         * Set the image smoothing quality.
         */
        this.setImageSmoothingQuality = function (value) {
            for (var _i = 0, IMAGE_SMOOTHING_QUALITY_KEYS_1 = constants_1.IMAGE_SMOOTHING_QUALITY_KEYS; _i < IMAGE_SMOOTHING_QUALITY_KEYS_1.length; _i++) {
                var key = IMAGE_SMOOTHING_QUALITY_KEYS_1[_i];
                if (key in _this.ctx) {
                    _this.ctx[key] = value;
                    return _this;
                }
            }
            return _this;
        };
        /**
         * Get the current image smoothing quality.
         */
        this.getImageSmoothingQuality = function () {
            for (var _i = 0, IMAGE_SMOOTHING_QUALITY_KEYS_2 = constants_1.IMAGE_SMOOTHING_QUALITY_KEYS; _i < IMAGE_SMOOTHING_QUALITY_KEYS_2.length; _i++) {
                var key = IMAGE_SMOOTHING_QUALITY_KEYS_2[_i];
                if (key in _this.ctx) {
                    return _this.ctx[key];
                }
            }
            return constants_1.DEFAULT_IMAGE_SMOOTHING_VALUES.imageSmoothingQuality;
        };
        /**
         * Set how blurry shadows are.
         */
        this.setShadowBlur = function (value) { return _this.setCanvasProperty('shadowBlur', value * _this.density); };
        /**
         * Get the value of how blurry shadows are.
         */
        this.getShadowBlur = function () { return _this.getCanvasProperty('shadowBlur') / _this.density; };
        /**
         * Set the color to be used for shadows.
         */
        this.setShadowColor = function (value) { return _this.setCanvasProperty('shadowColor', value); };
        /**
         * Get the color being used for shadows.
         */
        this.getShadowColor = function () { return _this.getCanvasProperty('shadowColor'); };
        /**
         * Set how horizontally offset shadows should be.
         */
        this.setShadowOffsetX = function (value) { return _this.setCanvasProperty('shadowOffsetX', value * _this.density); };
        /**
         * Get the value of how horizontally offset shadows should be.
         */
        this.getShadowOffsetX = function () { return _this.getCanvasProperty('shadowOffsetX') / _this.density; };
        /**
         * Set how vertically offset shadows should be.
         */
        this.setShadowOffsetY = function (value) { return _this.setCanvasProperty('shadowOffsetY', value * _this.density); };
        /**
         * Get the value of how vertically offset shadows should be.
         */
        this.getShadowOffsetY = function () { return _this.getCanvasProperty('shadowOffsetY') / _this.density; };
        /**
         * @group Misc
         * @description Miscellaneous methods.
         */
        /**
         * Break out of the method chain and execute a callback.
         */
        this.tap = function (callback) {
            callback.call(_this);
            return _this;
        };
        /**
         * Break out of the method chain and execute a callback with values between start and end,
         * increasing / decreasing by step (start defaults to 0, step defaults to 1).
         * You may return false from the callback at any point to stop at the current iteration.
         */
        this.repeat = function () {
            var args = [];
            for (var _i = 0; _i < arguments.length; _i++) {
                args[_i] = arguments[_i];
            }
            var start;
            var end;
            var step;
            var callback;
            var first = args[0], second = args[1], third = args[2], fourth = args[3];
            switch (args.length) {
                case 2:
                    start = 0;
                    end = (typeof first === 'number' ? first || 0 : 0);
                    step = 1;
                    callback = typeof second === 'function' ? second : undefined;
                    break;
                case 3:
                    start = (typeof first === 'number' ? first || 0 : 0);
                    end = (typeof second === 'number' ? second || 0 : 0);
                    step = 1;
                    callback = typeof third === 'function' ? third : undefined;
                    break;
                case 4:
                    start = (typeof first === 'number' ? first || 0 : 0);
                    end = (typeof second === 'number' ? second || 0 : 0);
                    step = Math.abs(typeof third === 'number' ? third || 0 : 0);
                    callback = typeof fourth === 'function' ? fourth : undefined;
                    break;
                default:
                    throw new Error('Incorrect number of arguments supplied for repeat. ' +
                        'Arguments must be [end, callback], [start, end, callback], ' +
                        'or [start, end, step, callback].');
            }
            if (step === 0) {
                return _this;
            }
            if (typeof callback !== 'function') {
                return _this;
            }
            var positive = end > start;
            step = positive ? step : -step;
            for (var i = start; (positive ? i < end : i > end); i += step) {
                if (callback(i) === false) {
                    return _this;
                }
            }
            return _this;
        };
        /**
         * Break out of the method chain and loop over the given array, object or string,
         * calling the callback with the value & key / index.
         * You may return false from the callback at any point to stop at the current iteration.
         */
        this.forEach = function (obj, callback) {
            if (Array.isArray(obj) || typeof obj === 'string') {
                for (var i = 0; i < obj.length; i += 1) {
                    if (callback(obj[i], i) === false) {
                        return _this;
                    }
                }
            }
            else {
                for (var key in obj) {
                    if (Object.prototype.hasOwnProperty.call(obj, key)) {
                        if (callback(obj[key], key) === false) {
                            return _this;
                        }
                    }
                }
            }
            return _this;
        };
        /**
         * Constrain a number between a minimum and maximum value.
         */
        this.constrain = function (value, min, max) {
            if (min > max) {
                var temp = min;
                min = max;
                max = temp;
            }
            return Math.max(Math.min(value, max), min);
        };
        /**
         * Map a value from one range to another e.g. mapping 0.5 from 0-1 to 0-10 returns 5.
         */
        this.map = function (value, fromStart, fromEnd, toStart, toEnd) {
            var fromDiff = fromEnd - fromStart;
            var toDiff = toEnd - toStart;
            return toStart + toDiff * (value - fromStart) / fromDiff;
        };
        /**
         * Draw a focus ring around the current path, or the path supplied,
         * if the element supplied has focus.
         */
        this.drawFocusIfNeeded = function (element) {
            _this.ctx.drawFocusIfNeeded(element);
            return _this;
        };
        /**
         * Returns whether the given point is within the current or given path.
         */
        this.isPointInPath = function (x, y, fillRule) {
            if (fillRule) {
                return _this.ctx.isPointInPath(x * _this.density, y * _this.density, fillRule);
            }
            return _this.ctx.isPointInPath(x * _this.density, y * _this.density);
        };
        /**
         * Returns whether the given point is within the area contained by applying
         * a stroke to the current or given path.
         */
        this.isPointInStroke = function () {
            if (typeof _this.ctx.isPointInStroke !== 'function') {
                logUnsupportedMethodError('isPointInStroke');
                return null;
            }
            return _this.ctx.isPointInStroke();
        };
        /**
         * Return the current version of Canvasimo (and log to console if logInfo parameter is true)
         * @alias version
         */
        this.getVersion = function (logInfo) {
            if (logInfo) {
                logger_1.default.info("Using version " + VERSION);
            }
            return VERSION;
        };
        this.version = function (logInfo) { return _this.getVersion(logInfo); };
        // Set and get context properties
        this.setCanvasProperty = function (attribute, value) {
            _this.ctx[attribute] = value;
            return _this;
        };
        this.getCanvasProperty = function (attribute) { return _this.ctx[attribute]; };
        this.drawTextWithLineBreaks = function (method, text, x, y, lineHeight, color) {
            var height = typeof lineHeight === 'number' ? lineHeight : 1;
            var definedFontSize = _this.getFontSize();
            var fontSize = typeof definedFontSize === 'number' ? definedFontSize : 10;
            var lines = text.split('\n');
            lines.forEach(function (line, index) {
                var offset = fontSize * height;
                method(line, x, y + offset * index, undefined, color);
            });
            return _this;
        };
        this.wrapBreakAll = function (text, maxWidth, lines) {
            if (lines === void 0) { lines = ['']; }
            var letters = text.split('');
            var lineIndex = lines.length - 1;
            letters.forEach(function (letter) {
                var line = lines[lineIndex];
                var newLineWidth = _this.getTextSize(line + letter).width;
                if (newLineWidth < maxWidth || line.length === 0) {
                    lines[lineIndex] += letter;
                }
                else {
                    lines.push('');
                    lineIndex = lines.length - 1;
                    if (!constants_1.MATCHES_ALL_WHITESPACE.test(letter)) {
                        lines[lineIndex] += letter;
                    }
                }
            });
            return lines;
        };
        this.wrapBreakWord = function (text, maxWidth, lines) {
            if (lines === void 0) { lines = ['']; }
            var words = text.split(constants_1.MATCHES_WORD_BREAKS);
            var lineIndex = 0;
            words.forEach(function (word) {
                var line = lines[lineIndex];
                var newLineWidth = _this.getTextSize(line + word).width;
                if (newLineWidth < maxWidth) {
                    lines[lineIndex] += word;
                }
                else {
                    if (!constants_1.MATCHES_ALL_WHITESPACE.test(word)) {
                        if (line.length > 0) {
                            lines.push('');
                        }
                        lines = _this.wrapBreakAll(word, maxWidth, lines);
                    }
                    lineIndex = lines.length - 1;
                }
            });
            return lines;
        };
        this.wrapNormal = function (text, maxWidth, lines) {
            if (lines === void 0) { lines = ['']; }
            var words = text.split(constants_1.MATCHES_WORD_BREAKS);
            var lineIndex = 0;
            words.forEach(function (word) {
                var line = lines[lineIndex];
                var newLineWidth = _this.getTextSize(line + word).width;
                if (newLineWidth < maxWidth || line.length === 0) {
                    lines[lineIndex] += word;
                }
                else {
                    if (!constants_1.MATCHES_ALL_WHITESPACE.test(word)) {
                        lines.push(word);
                    }
                    else {
                        lines.push('');
                    }
                    lineIndex = lines.length - 1;
                }
            });
            return lines;
        };
        this.textMultiline = function (method, text, x, y, maxWidth, wordBreak, lineHeight, color) {
            if (typeof maxWidth === 'undefined' || maxWidth === null) {
                return _this.drawTextWithLineBreaks(method, text, x, y, lineHeight, color);
            }
            else if (wordBreak === 'break-all') {
                var lines = text.split('\n').map(function (subText) { return _this.wrapBreakAll(subText, maxWidth).join('\n'); });
                return _this.drawTextWithLineBreaks(method, lines.join('\n'), x, y, lineHeight, color);
            }
            else if (wordBreak === 'break-word') {
                var lines = text.split('\n').map(function (subText) { return _this.wrapBreakWord(subText, maxWidth).join('\n'); });
                return _this.drawTextWithLineBreaks(method, lines.join('\n'), x, y, lineHeight, color);
            }
            else {
                var lines = text.split('\n').map(function (subText) { return _this.wrapNormal(subText, maxWidth).join('\n'); });
                return _this.drawTextWithLineBreaks(method, lines.join('\n'), x, y, lineHeight, color);
            }
        };
        this.setDefaultContextValues = function () {
            for (var key in constants_1.DEFAULT_CONTEXT_VALUES) {
                if (key in _this.ctx) {
                    _this.setCanvasProperty(key, constants_1.DEFAULT_CONTEXT_VALUES[key]);
                }
            }
            _this.setImageSmoothingEnabled(constants_1.DEFAULT_IMAGE_SMOOTHING_VALUES.imageSmoothingEnabled);
            _this.setImageSmoothingQuality(constants_1.DEFAULT_IMAGE_SMOOTHING_VALUES.imageSmoothingQuality);
            _this.setLineDash(constants_1.DEFAULT_LINE_DASH);
            return _this;
        };
        this.saveContextValues = function () {
            _this.storedContextValues = {
                globalAlpha: _this.getGlobalAlpha(),
                globalCompositeOperation: _this.getCompositeOperation(),
                strokeStyle: _this.getStroke(),
                fillStyle: _this.getFill(),
                shadowOffsetX: _this.getShadowOffsetX(),
                shadowOffsetY: _this.getShadowOffsetY(),
                shadowBlur: _this.getShadowBlur(),
                shadowColor: _this.getShadowColor(),
                lineWidth: _this.getLineWidth(),
                lineCap: _this.getLineCap(),
                lineJoin: _this.getLineJoin(),
                miterLimit: _this.getMiterLimit(),
                lineDashOffset: _this.getLineDashOffset(),
                font: _this.getFont(),
                textAlign: _this.getTextAlign(),
                textBaseline: _this.getTextBaseline(),
                imageSmoothingEnabled: _this.getImageSmoothingEnabled(),
                imageSmoothingQuality: _this.getImageSmoothingQuality(),
                lineDash: _this.getLineDash(),
            };
            return _this.storedContextValues;
        };
        this.restoreContextValues = function () {
            var _a = _this.storedContextValues, globalAlpha = _a.globalAlpha, globalCompositeOperation = _a.globalCompositeOperation, strokeStyle = _a.strokeStyle, fillStyle = _a.fillStyle, shadowOffsetX = _a.shadowOffsetX, shadowOffsetY = _a.shadowOffsetY, shadowBlur = _a.shadowBlur, shadowColor = _a.shadowColor, lineWidth = _a.lineWidth, lineCap = _a.lineCap, lineJoin = _a.lineJoin, miterLimit = _a.miterLimit, lineDashOffset = _a.lineDashOffset, font = _a.font, textAlign = _a.textAlign, textBaseline = _a.textBaseline, imageSmoothingEnabled = _a.imageSmoothingEnabled, imageSmoothingQuality = _a.imageSmoothingQuality, lineDash = _a.lineDash;
            return _this
                .setGlobalAlpha(globalAlpha)
                .setCompositeOperation(globalCompositeOperation)
                .setStroke(strokeStyle)
                .setFill(fillStyle)
                .setShadowOffsetX(shadowOffsetX)
                .setShadowOffsetY(shadowOffsetY)
                .setShadowBlur(shadowBlur)
                .setShadowColor(shadowColor)
                .setLineWidth(lineWidth)
                .setLineCap(lineCap)
                .setLineJoin(lineJoin)
                .setMiterLimit(miterLimit)
                .setLineDashOffset(lineDashOffset)
                .setFont(font)
                .setTextAlign(textAlign)
                .setTextBaseline(textBaseline)
                .setImageSmoothingEnabled(imageSmoothingEnabled)
                .setImageSmoothingQuality(imageSmoothingQuality)
                .setLineDash(lineDash);
        };
        this.element = element;
        var ctx = this.element.getContext(constants_1.CONTEXT_TYPE);
        if (!ctx) {
            throw new Error('Could not get a CanvasRenderingContext from the provided element');
        }
        this.ctx = ctx;
        this.setDefaultContextValues();
        this.storedContextValues = this.saveContextValues();
    }
    return Canvasimo;
}());
exports.Canvasimo = Canvasimo;
exports.default = Canvasimo;
//# sourceMappingURL=index.js.map