Source: jquery-progresspiesvg-checkComplete.js

/**
 * @license 
 * Copyright (c) 2018, Immo Schulz-Gerlach, www.isg-software.de 
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without modification, are 
 * permitted provided that the following conditions are met:
 *
 * 1. Redistributions of source code must retain the above copyright notice, this list of
 * conditions and the following disclaimer.
 *
 * 2. Redistributions in binary form must reproduce the above copyright notice, this list
 * of conditions and the following disclaimer in the documentation and/or other materials
 * provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY 
 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT 
 * SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED 
 * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; 
 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY
 * WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */


( function($) {

	function iconRad(opts, contentRad) {
		var r = contentRad;
		var factor = opts.iconSizeFactor;
		if (typeof factor !== "number") {
			factor = typeof opts.pieOpts.ringWidth !== "undefined" && !opts.backgroundColor ? opts.iconSizeFactorRing : opts.iconSizeFactorPie;
		}
		r *= factor;
		if (opts.lineCap !== "none") {
			r -= opts.strokeWidth / 2; //Radius of lineCap is half the strokeWidth
		}
		return r;
	}

	/**
	 * SVG Content Plug-in for jquery-progresspiesvg: Does nothing for values less than 100%, but draws a check mark / tick onto the 
	 * fully filled pie resp. inside the full ring for a value of 100%.
	 * Use by adding the option <code>contentPlugin: "checkComplete"</code> (or <code>contentPlugin: $.fn.progressPie.contentPlugin.checkComplete</code>)
	 * to your call of the progresspie plug-in.
	 * <p>Additional arguments may be supplied by adding the option <code>contentPluginOptions</code> to the progressPie plugin options.
	 * This is to be an object which may hold the following properties:</p>
	 * <ul>
	 * <li><code>strokeWidth</code>: Defaults to 2. Width of the stroke for the check mark (not equal to the strokeWidth option of the pie chart (outer circle)</li>
	 * <li><code>lineCap</code>: Defaults to "round", may take any value allowed for the SVG line-cap style, like "square".</li>
	 * <li><code>color</code>: draw the check mark in a specific color (defaults to the color of the surrounding ring chart resp. to white on a pie chart).</li>
	 * <li><code>backgroundColor</code>: Defaults to <code>undefined</code>. If undefined, the check icon is drawn directly onto the fully filled pie resp.
	 * onto the blank space inside a fully filled ring. Especially if combined with a ring, you may optionally set this option do draw a filled circle inside the ring
	 * and to draw the check mark onto this circle.</li>
	 * <li><code>fullSize</code>: Only for progress rings (and only meant for combination with a <code>backgroundColor</code>): Setting this to true
	 * causes the filled background circle of the check icon to fully cover the whole ring chart instead of being drawn inside the free space of the ring.
	 * Defaults to false.</li>
	 * <li><code>inBackground</code>: boolean, defaults to false. If set to true, the icon will be drawn behind the chart
	 * instead of on top of it. In that case, the chart has to provide some kind of transparency in order for the check icon
	 * to be at least partly visible, e.g. by using a foreground color with alpha channel (rgba) or by drawing a ring chart
	 * with free/transparent room in the middle.</li>
	 * <li><code>margin</code>: number, defaults to undefined: Only used if the <code>backgroundColor</code> option is set. In that case, it defines the margin
	 * in pixels left free around the filled background circle. For a progress <em>pie</em> or if the <code>fullSize</code> option is truthy, this value (if the property is
	 * not set) defaults to zero, which means the background completely covers the pie graph. Increasing the value will reduce the icon in size, leaving some of
	 * the pie chart visible around it.<br>
	 * For a progress <em>ring</em> without <code>fullSize</code> option, the default
	 * margin value (if the property is not set) is 1, meaning a gap of 1 pixel width is left free between the ring and the filled background. Set this to zero
	 * in order for the background to "touch" the ring, or to a negative value in order to (partially) overlap the ring.</li>
	 * <li><code>iconSizeFactor</code>: Defines the ration between the background radius and the radius of the circumcircle of the check's stroke. I.e. if set to 1.0,
	 * the check's stroke ends will touch the edge of the background circle, smaller values will leave a margin between the background and the check.
	 * This defaults to 0.6 if the check is drawn onto a pie or if the <code>backgroundColor</code> option is set. If the check is drawn directly into the inner space
	 * of a ring graph without background color, this defaults to 0.8.</li>
	 * <li><code>animate</code>: boolean or string with duration (number and time unit): If true or string, an animation drawing the check (from left to right) will be added.
	 * If the value is a string, it has to be a valid duration value defining the speed of the animation. If "true", the default duration (1s) will be applied.</li>
	 * <li><code>contentPlugin</code> and <code>contentPluginOptions</code>: These options are ignored vor a value of 100%, i.e. in case the check mark gets drawn as
	 * content for the progress pie. But if set, this content plug-in will delegate to the alternative content plug-in stated here-in for any percent value less than 100%.
	 * I.e.: This plug-in will decide if the percent value is 100 or less, in the first case drawing the check mark as content, while in the second case, i.e. for any percent
	 * value in 0..99, the content of this "secondary" plug-in will be added to the pie/ring chart.</li>
	 * </ul>
	 * <p>Please note: This function is called <em>internally</em> by the progressPie jQuery plug-in! Don't call this function directly,
	 * but use it as desrcibed above!</p>
	 * @function checkComplete
	 * @param {object} args object holding several arguments provided by the progressPie plug-in, including any option you specified in
	 * the object <code>contentPluginOptions</code>.
	 * @memberof jQuery.fn.progressPie.contentPlugin
	 * @requires jquery-progresspiesvg-min.js
	 */
	$.fn.progressPie.contentPlugin.checkComplete = {
		draw: function(args) {
			if (args.percentValue === 100) {
				var opts = $.extend({}, $.fn.progressPie.contentPlugin.checkCompleteDefaults, args);
				var r = opts.getBackgroundRadius(!opts.backgroundColor);
				opts.addBackground(r, opts.cssClassBackgroundCircle);
				var r2 = iconRad(opts, r);
				var offset = r2 / Math.sqrt(2); //see errorIcons plug-in
				var innerOffset = offset / 22;
				var start = "M -" + offset + ",0 ";
				var line1 = "L -" + innerOffset + "," + offset + " ";
				var line2 = "L " + offset + ", -" + offset;
				var check = args.newSvgElement("path");
				check.setAttribute("d", start + line1 + line2);
				//Color styles
				const pieMode = typeof args.pieOpts.ringWidth === "undefined";
				//Filling for a check mark never makes sense, so always (even in CSS mode)
				//add style fill:none, which can only be overridden by !important directive:
				check.style.fill = "none";
				//Now for the stroke color, depending on the modes:
				if (!opts.isCssMode()) { //Not CSS mode, apply normal inline CSS
					var color = pieMode ? "white" : opts.color;
					check.style.stroke = color;
					check.style.strokeWidth = opts.strokeWidth;
					check.style.strokeLinecap = opts.lineCap;
				} else {
					check.setAttribute("stroke-width", opts.strokeWidth);
					check.setAttribute("stroke-linecap", opts.lineCap);
					//In CSS Mode, normally add no stroke style at all, except in pie mode: Then, the check
					//should still default to white color, but not set as inline CSS style but
					//as SVG attribute in order to enable CSS override without "!important" directive.
					if (pieMode) {
						check.setAttribute("stroke", "white");
					}
				}
				check.setAttribute("class", opts.cssClass);
				if (opts.animate) {
					var anim = args.newSvgSubelement(check, "animate");
					anim.setAttribute("attributeName", "d");
					anim.setAttribute("dur", typeof opts.animate === "string" ? opts.animate : "1s");
					anim.setAttribute("repeatCount", "1");
					anim.setAttribute("values", start + "l0,0 l0,0; " + start + line1 + "l0,0; " + start + line1 + line2);
					anim.setAttribute("calcMode", "spline");
					anim.setAttribute("keyTimes", "0; .25; 1");
					anim.setAttribute("keySplines", ".5 0 .3 1; .3 0 0 1");
				}
			} else if (typeof args.contentPlugin !== "undefined") {
				var f = args.getContentPlugin(args.contentPlugin);
				var cpArgs = typeof args.contentPluginOptions === "object" ? $.extend({}, args, args.contentPluginOptions) : args;
				f(cpArgs);
			}
		},
		hidesChartIfFullSize: function(args) {
			var opts = $.extend({}, $.fn.progressPie.contentPlugin.checkCompleteDefaults, args);
			return args.percentValue === 100 && typeof opts.backgroundColor === 'string' && opts.backgroundColor.substr(0,4) !== 'rgba' && !opts.margin &&
					 !this.inBackground(args);
		},
		inBackground: function(args) {
			var opts = $.extend({}, $.fn.progressPie.contentPlugin.checkCompleteDefaults, args);
			return opts.inBackground;
		}
	};
	
	/**
	 * Default Options.
	 * This is a public (static) object in order to allow users to globally modify the defaults
	 * before using the <code>checkComplete</code> content plug-in.
	 * @member checkCompleteDefaults
	 * @memberof jQuery.fn.progressPie.contentPlugin
	 * @property {number} strokeWidth - Width of the stroke the check mark is drawn width, defaults to 2.
	 * @property {string} lineCap - Value for SVG style property "line-cap" defining the look of the line ends of the check mark. Defaults to "round".
	 * @property {number} iconSizeFactorPie - Defines the size of the check icon for a pie graph (i.e. when the <code>ringWidth</code> option is not set) and also for the ring, if the <code>backgroundColor</code> plug-in option is set (it's undefined by default):
	 * If r is the total radius of the pie chart, the check mark is fit into an inner circle with radius r * iconSizeFactorPie.
	 * Defaults to 0.6 (i.e. filling 60% of the pie).
	 * This is ignored, if the iconSizeFactor option is defined! It's just the default value for iconSizeFactor for pie graphs and for filled circular backgrounds inside a ring graph.
	 * @property {number} iconSizeFactorRing - Defines the size of the check icon for a ring graph (i.e. if the ringWidth option is set) if no <code>backgroundColor</code> option is set (i.e. if the check is drawn directly onto the blank / transparent space inside the ring):
	 * If r is the radius of the <em>free space inside the ring</em>, then the check mark is fit into an inner circle with 
	 * radius r * iconSizeFactorRing. Defaults to 0.8 (i.e. filling 80% of the free space inside the ring). (If set to 1.0, the
	 * check mark would touch the ring.)
	 * This is ignored, if the iconSizeFactor option is defined! It's just the default value for iconSizeFactor for ring graphs.
	 * If a user wants to set an individual size factor in the <code>contentPluginOptions</code> object, he does not have to
	 * overwrite one of these two values, but may specify simply a <code>iconSizeFactor</code> property. Only if the latter is
	 * undefined, the plug-in will evaluate <code>iconSizeFactorPie</code> or <code>iconSizeFactorRing</code>, depending
	 * on the <code>ringWidth</code> option.
	 * @property {boolean} fullSize - If true and if the plug-in gets called with a ring chart, this causes the icon to be drawn full-size onto the whole
	 * chart instead of being fitted into the blank space inside the ring. Should only be combined with the <code>backgroundColor</code> option. Defaults to false.
	 * @property {boolean} inBackground - If false, the check icon is placed on top of the chart (into the foreground),
	 * if true, the check will be drawn as background with the chart on top. Defaults to false.
	 * @property {string} cssClass – The content of the <code>class</code> attribute to be added to the check stroke,
	 *  defaults to "progresspie-check". Allows selection of the check icon for CSS formatting.
	 * @property {string} cssClassBackgroundCircle – Optional, default is undefined. If defined, a background circle
	 *  (behind the actual check mark) will be added to the chart regardless of the presence of the backgroundColor option.
	 *  Will add a class attribute with this value to the circle element.
	 */
	$.fn.progressPie.contentPlugin.checkCompleteDefaults = {
		strokeWidth: 2,
		lineCap: "round",
		iconSizeFactorPie: 0.6,
		iconSizeFactorRing: 0.8,
		fullSize: false,
		inBackground: false,
		cssClass: "progresspie-check", 
		cssClassBackgroundCircle: undefined
	};

} (jQuery));