UNPKG

canvasimo

Version:

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

2,045 lines 82.1 kB
(function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.canvasimo = f()}})(function(){var define,module,exports;return (function(){function r(e,n,t){function o(i,f){if(!n[i]){if(!e[i]){var c="function"==typeof require&&require;if(!f&&c)return c(i,!0);if(u)return u(i,!0);var a=new Error("Cannot find module '"+i+"'");throw a.code="MODULE_NOT_FOUND",a}var p=n[i]={exports:{}};e[i][0].call(p.exports,function(r){var n=e[i][1][r];return o(n||r)},p,p.exports,r,e,n,t)}return n[i].exports}for(var u="function"==typeof require&&require,i=0;i<t.length;i++)o(t[i]);return o}return r})()({1:[function(_dereq_,module,exports){
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.PROPERTY_MAP = {
    globalAlpha: 'opacity',
    globalCompositeOperation: 'compositeOperation',
    fillStyle: 'fill',
    strokeStyle: 'stroke',
    lineWidth: 'strokeWidth',
    lineCap: 'strokeCap',
    lineJoin: 'strokeJoin',
    lineDashOffset: 'strokeDashOffset',
    miterLimit: 'miterLimit',
    shadowColor: 'shadowColor',
    shadowBlur: 'shadowBlur',
    shadowOffsetX: 'shadowOffsetX',
    shadowOffsetY: 'shadowOffsetY',
    textAlign: 'textAlign',
    textBaseline: 'textBaseline',
};
exports.IMAGE_SMOOTHING_KEYS = [
    'imageSmoothingEnabled',
    'msImageSmoothingEnabled',
    'mozImageSmoothingEnabled',
    'webkitImageSmoothingEnabled',
];
exports.IMAGE_SMOOTHING_QUALITY_KEYS = [
    'imageSmoothingQuality',
    'msImageSmoothingQuality',
    'mozImageSmoothingQuality',
    'webkitImageSmoothingQuality',
];
exports.CONTEXT_TYPE = '2d';
exports.INCORRECT_POINT_FORMAT = "Path points must be an array of:\n\n numbers [x, y, x, y], pairs [[x, y], [x, y]], or objects [{x, y}, {x, y}].";
exports.INCORRECT_GET_ANGLE_ARGUMENTS = 'Incorrect number of arguments supplied for getAngle. ' +
    'Arguments must be [x1, y1, x2, y2] or [x1, y1, x2, y2, x3, y3].';
exports.DEFAULT_FONT_PARTS = ['normal', 'normal', 'normal', '10px', 'sans-serif'];
exports.DEFAULT_FONT = exports.DEFAULT_FONT_PARTS.join(' ');
exports.DEFAULT_DENSITY = 1;
exports.MATCHES_SPECIAL_FILL = /^(nonzero|evenodd)$/i;
exports.MATCHES_NORMAL = /^(normal)$/i;
exports.MATCHES_FONT_STYLE = /^(italic|oblique)$/i;
exports.MATCHES_FONT_VARIANT = /^(small-caps)$/i;
exports.MATCHES_FONT_WEIGHT = /^(bold|bolder|lighter|\d00)$/i;
exports.MATCHES_SPECIAL_FONT = /^(caption|icon|menu|message-box|small-caption|status-bar)$/i;
exports.MATCHES_WHITESPACE = /\s+/g;
exports.MATCHES_ALL_WHITESPACE = /^\s*$/;
exports.MATCHES_FONT_SIZE = /(^|\s+)(\d*\.?\d+)([a-z]+|%)(\/\d*\.?\d+(?:[a-z]+|%)?)?\s/i;
exports.MATCHES_WORD_BREAKS = /(?![^\w\s])\b/g;
exports.DEFAULT_CONTEXT_VALUES = {
    globalAlpha: 1,
    globalCompositeOperation: 'source-over',
    strokeStyle: '#000000',
    fillStyle: '#000000',
    shadowOffsetX: 0,
    shadowOffsetY: 0,
    shadowBlur: 0,
    shadowColor: 'rgba(0, 0, 0, 0)',
    lineWidth: 1,
    lineCap: 'butt',
    lineJoin: 'miter',
    miterLimit: 10,
    lineDashOffset: 0,
    font: exports.DEFAULT_FONT,
    textAlign: 'start',
    textBaseline: 'alphabetic',
};
exports.DEFAULT_IMAGE_SMOOTHING_VALUES = {
    imageSmoothingEnabled: true,
    imageSmoothingQuality: 'low',
};
exports.DEFAULT_LINE_DASH = [];

},{}],2:[function(_dereq_,module,exports){
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
// tslint:disable-next-line:no-var-requires
var VERSION = _dereq_('../package.json').version;
var constants_1 = _dereq_("./constants");
var logger_1 = _dereq_("./logger");
var utils_1 = _dereq_("./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;

},{"../package.json":5,"./constants":1,"./logger":3,"./utils":4}],3:[function(_dereq_,module,exports){
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
var consoleExists = Boolean(window.console);
var logger = {
    info: function (message) {
        if (consoleExists) {
            window.console.info("Canvasimo: " + message);
        }
    },
    warn: function (message) {
        if (consoleExists) {
            window.console.warn("Canvasimo: " + message);
        }
    },
};
exports.default = logger;

},{}],4:[function(_dereq_,module,exports){
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
var constants_1 = _dereq_("./constants");
var logger_1 = _dereq_("./logger");
exports.isPoint = function (point) {
    return typeof point === 'object' && 'x' in point && 'y' in point;
};
exports.isTuplePoint = function (point) {
    return typeof point === 'object' && Array.isArray(point) && point.length === 2;
};
var warnedAboutLineHeight = false;
exports.getFontParts = function (input, density, getter) {
    if (!input) {
        return constants_1.DEFAULT_FONT_PARTS;
    }
    var font = input.trim();
    if (constants_1.MATCHES_SPECIAL_FONT.test(font)) {
        return [font];
    }
    var matchFontSize = constants_1.MATCHES_FONT_SIZE.exec(font);
    if (!matchFontSize) {
        return constants_1.DEFAULT_FONT_PARTS;
    }
    var fontString = matchFontSize[0].trim();
    var leadingSpaces = matchFontSize[1].length;
    var size = matchFontSize[2];
    var unit = matchFontSize[3];
    var lineHeight = matchFontSize[4];
    if (lineHeight && !warnedAboutLineHeight) {
        logger_1.default.warn("Attempted to set the font line height with \"" + fontString + "\", " +
            'but this is not supported by canvas. ' +
            'Use the Canvasimo TextMultiline methods with the lineHeight parameter instead.');
        warnedAboutLineHeight = true;
    }
    var fontSize = (unit !== '%' ? (getter ? parseFloat(size) / density : parseFloat(size) * density) : size) + unit;
    var parts = font.substring(matchFontSize.index + leadingSpaces).split(constants_1.MATCHES_WHITESPACE);
    var fontFamily = parts[parts.length - 1];
    var optional = font.substring(0, matchFontSize.index);
    var optionalParts = optional ? optional.split(constants_1.MATCHES_WHITESPACE) : null;
    var fontStyle;
    var fontVariant;
    var fontWeight;
    if (optionalParts) {
        while (optionalParts.length) {
            if (constants_1.MATCHES_FONT_STYLE.test(optionalParts[0])) {
                fontStyle = optionalParts.splice(0, 1)[0];
            }
            else if (constants_1.MATCHES_FONT_VARIANT.test(optionalParts[0])) {
                fontVariant = optionalParts.splice(0, 1)[0];
            }
            else if (constants_1.MATCHES_FONT_WEIGHT.test(optionalParts[0])) {
                fontWeight = optionalParts.splice(0, 1)[0];
            }
            else if (constants_1.MATCHES_NORMAL.test(optionalParts[0])) {
                optionalParts.splice(0, 1);
            }
            else {
                return constants_1.DEFAULT_FONT_PARTS;
            }
        }
    }
    return [
        fontStyle || constants_1.DEFAULT_FONT_PARTS[0],
        fontVariant || constants_1.DEFAULT_FONT_PARTS[1],
        fontWeight || constants_1.DEFAULT_FONT_PARTS[2],
        fontSize,
        fontFamily,
    ];
};
exports.formatFont = function (input, density, getter) {
    return exports.getFontParts(input, density, getter).join(' ');
};
exports.forPoints = function (points, callback) {
    if (!Array.isArray(points) || (typeof points[0] === 'number' && (points.length % 2) !== 0)) {
        throw new Error(constants_1.INCORRECT_POINT_FORMAT);
    }
    if (!points.length || points.length === 1 || (typeof points[0] === 'number' && points.length < 4)) {
        return;
    }
    var firstPoint = points[0];
    var secondPoint = points[1];
    if (exports.isPoint(firstPoint)) {
        points.forEach(function (point, index) {
            if (!exports.isPoint(point)) {
                throw new Error("Expected point with format {x, y} but got " + point);
            }
            callback(point.x, point.y, index);
        });
    }
    else if (exports.isTuplePoint(firstPoint)) {
        points.forEach(function (point, index) {
            if (!exports.isTuplePoint(point)) {
                throw new Error("Expected point with format [x, y] but got " + point);
            }
            callback(point[0], point[1], index);
        });
    }
    else if (typeof secondPoint === 'number') {
        // tslint:disable-next-line:prefer-for-of
        for (var i = 0; i < points.length; i += 2) {
            var pointX = points[i];
            var pointY = points[i + 1];
            if (typeof pointX !== 'number' || typeof pointY !== 'number') {
                throw new Error("Expected points to be an array of numbers but got " + pointX + ", " + pointY);
            }
            callback(pointX, pointY, i / 2);
        }
    }
    else {
        throw new Error(constants_1.INCORRECT_POINT_FORMAT);
    }
};
exports.isFillRule = function (value) { return typeof value === 'string' &&
    constants_1.MATCHES_SPECIAL_FILL.test(value); };

},{"./constants":1,"./logger":3}],5:[function(_dereq_,module,exports){
module.exports={
  "name": "canvasimo",
  "version": "0.8.0",
  "description": "An HTML5 canvas drawing library, with 150+ useful methods, jQuery-like fluent interface, and cross-browser compatibility enhancements.",
  "main": "dist/index.js",
  "module": "build/index.js",
  "types": "build/index.d.ts",
  "directories": {
    "example": "examples"
  },
  "scripts": {
    "prepublishOnly": "npm run dist",
    "dist": "./scripts/dist",
    "start": "./scripts/start",
    "build": "NODE_ENV=production ts-node scripts/generate-docs.tsx",
    "typecheck": "tsc --project tsconfig.json --noEmit",
    "lint": "tslint --project tsconfig.json '{docs/src,src,tests,types,scripts}/**/*.@(ts|tsx)'",
    "tests": "jest",
    "test": "npm run lint && npm run typecheck && npm run tests -- --coverage --runInBand && if grep -rn FIXME src; then exit 1; fi;"
  },
  "repository": {
    "type": "git",
    "url": "git+https://github.com/jakesidsmith/canvasimo.git"
  },
  "keywords": [
    "sensible",
    "simple",
    "fluent",
    "canvas",
    "api",
    "interface",
    "html5",
    "chain",
    "canvasimo"
  ],
  "author": "Jake 'Sid' Smith",
  "license": "MIT",
  "bugs": {
    "url": "https://github.com/jakesidsmith/canvasimo/issues"
  },
  "homepage": "https://github.com/jakesidsmith/canvasimo#readme",
  "dependencies": {},
  "devDependencies": {
    "@types/browserify": "^12.0.33",
    "@types/chokidar": "^1.7.4",
    "@types/glob": "^5.0.35",
    "@types/jest": "^24.0.11",
    "@types/mkdirp": "^0.5.2",
    "@types/node": "^9.4.0",
    "@types/react": "^16.0.36",
    "@types/react-dom": "^16.0.3",
    "@types/rimraf": "^2.0.2",
    "brfs": "^1.4.4",
    "browserify": "^16.2.3",
    "chokidar": "^2.0.0",
    "concurrently": "^3.5.1",
    "core-js": "^2.5.3",
    "derequire": "^2.0.6",
    "exorcist": "^1.0.0",
    "glob": "^7.1.2",
    "http-server": "^0.11.1",
    "jest": "^24.7.1",
    "minifyify": "^7.3.5",
    "mkdirp": "^0.5.1",
    "raf": "^3.4.0",
    "react": "^16.5.2",
    "react-dom": "^16.5.2",
    "rimraf": "^2.5.4",
    "ts-jest": "^24.0.2",
    "ts-node": "^4.1.0",
    "tsify": "^3.0.4",
    "tslint": "^5.9.1",
    "tslint-eslint-rules": "^4.1.1",
    "typescript": "^3.1.6"
  },
  "jest": {
    "testURL": "http://localhost",
    "collectCoverageFrom": [
      "src/**/*.{ts,tsx}"
    ],
    "testRegex": "(/tests/.*|\\.(test|spec))\\.(ts|tsx|js|jsx)$",
    "testPathIgnorePatterns": [
      "tests/helpers/",
      "tests/mocks/"
    ],
    "moduleFileExtensions": [
      "ts",
      "tsx",
      "js",
      "jsx"
    ],
    "transform": {
      "^.+\\.tsx?$": "ts-jest"
    }
  }
}

},{}]},{},[2])(2)
});
//# sourceMappingURL=index.js.map