// source --> https://gardenscene.je/wp-content/plugins/search-in-place/js/codepeople_shearch_in_place.min.js?ver=1.5.2 
var codepeople_search_in_place_generator=function(){function t(){try{return document.createEvent("TouchEvent"),!0}catch(b){return!1}}var e=jQuery;if("undefined"==typeof codepeople_search_in_place_generator_flag){codepeople_search_in_place_generator_flag=!0;var v=!1,C,D,y=e(".search-in-place-results-container"),E=function(b){let a=e(this).find("a").attr("href"),d,h;if(a)switch("URL"in window&&(d=new URL(a),h=d.searchParams.get("highlight"))&&(d.searchParams.delete("highlight"),a=d.toString(),sessionStorage.setItem("highlight",
h)),b.which){case 2:window.open(a,"_blank").trigger("blur");window.focus();break;case 3:break;default:e(this).find("a").removeAttr("href"),document.location.href=a}},w=function(b){v=v||!1;if(b||v&&e('input[name="s"]:focus').length==0&&e(".search-in-place:hover").length==0&&e(".search-in-place :focus").length==0)e(".search-in-place-close-icon").remove(),e(".search-in-place").hide(),v=!1},F=function(b){if(b.is(":hidden")||b.closest(".e-n-accordion-item").length||b.closest(".wp-block-themeisle-blocks-accordion-item:not(.open)").length){var a=
b.closest(".wp-block-kadence-tab");if(a.length&&a.is(":hidden")&&"KBTabs"in window&&(a=a.attr("class").match(/kt\-inner\-tab\-(\d+)/))){a=a[1];var d=b.closest(".kt-tabs-wrap");if(d.length){KBTabs.setActiveTab(d[0],a);return}}a=b.closest(".e-n-accordion-item");if(a.length)a.attr("open")==void 0&&a.attr("open","open");else if(a=b.closest(".elementor-accordion-item"),a.length&&a.find("ul:eq(0)").is(":hidden"))a.find(".elementor-accordion-title:eq(0)").trigger("click");else{a=b.closest(".elementor-toggle-item");
if(a.length&&(a=a.find('[role="button"]:eq(0)'),a.length)){a.trigger("click");return}a=b.closest(".elementor-tab-content");if(a.length)for(;a.length&&a.is(":hidden");)a.hasClass("elementor-active")||(d=e("#"+a.attr("id").replace("content","title")),d.length?d.trigger("click"):a.attr("data-tab")&&(d=e('.elementor-tab-title[data-tab="'+a.attr("data-tab")+'"]'),d.length&&d.trigger("click"))),b=a.parent(),a=b.closest(".elementor-tab-content");else{a=b.closest(".et_pb_toggle_content");if(a.length&&(d=
a.siblings(".et_pb_toggle_title"),d.length)){d.trigger("click");return}if(b.closest(".et-learn-more").length&&(d=b.closest(".et-learn-more").find(".heading-more"),d.length)){d.trigger("click");return}a=b.closest(".sow-accordion-panel-content");if(a.length&&(d=a.siblings(".sow-accordion-panel-header-container"),d.length)){d.find(".sow-accordion-open-button").trigger("click");return}a=b.closest(".answer");if(a.length&&(d=a.siblings(".title"),d.length)){d.trigger("click");return}a=b.closest('.ei-faq,[class$="_faq"]');
if(a.length&&(d=a.find(".collapsed"),d.length)){d.trigger("click");return}a=b.closest(".accordion__item");if(a.length&&b.find(".accordion__body:hidden")&&(d=a.find(".accordion__header"),d.length)){d.trigger("click");return}a=b.closest(".vc_tta-panel");a.length?a.addClass("active vc_active"):(a=b.closest(".wp-block-themeisle-blocks-accordion-item"),a.length&&a.find(".wp-block-themeisle-blocks-accordion-item__title").trigger("click"))}}}},A=function(b){e(".search-in-place-mark-active").removeClass("search-in-place-mark-active");
e(b).addClass("search-in-place-mark-active");F(b);var a=b.offset().top-window.innerHeight/2;setTimeout(function(){window.scrollTo({top:a,left:0,behavior:"smooth"})},100)},q=function(){e("[data-disable-enter-key]").on("keypress",function(b){if(b.keyCode===13)return b.preventDefault(),b.stopPropagation(),!1});e("[data-search-in-page]").data("enter-counter",0).on("click",function(){e(this).data("enter-counter",0)}).closest("form").on("submit",function(b){b.preventDefault();b.stopPropagation();b=e("[data-search-in-page]",
this);var a=b.data("enter-counter"),d=e(".search-in-place-mark:eq("+a+")");d.length?(A(d),b.data("enter-counter",a+1)):b.data("enter-counter",0);w(!0);return!1});e(document).on("mouseover mouseout",".search-in-place>.item",function(){e(this).toggleClass("active")}).on("mousedown",".search-in-place>.item",E).on("mousedown",".search-in-place>.label.more",E)};q.prototype={active:null,search:"",source:"",config:{min:codepeople_search_in_place.char_number,image_width:50,image_height:50,colors:"highlight_colors"in
codepeople_search_in_place?codepeople_search_in_place.highlight_colors:"#B5DCE1 #F4E0E9 #D7E0B1 #F4D9D0 #D6CDC8 #F4E3C9 #CFDAF0 #F4EFEC".split(" "),areas:"areas"in codepeople_search_in_place?codepeople_search_in_place.areas:"div.hentry #content #main div.content #middle #container #wrapper article .elementor body".split(" ")},autohide:function(){function b(h){var g=e(h).next('[name="cpsp-autocomplete"]');g.length&&(clearTimeout(D),D=setTimeout(function(){g.show();g[0].scrollLeft=h.scrollLeft},10),
g.hide())}var a=this,d='input[name="s"]';"own_only"in codepeople_search_in_place&&codepeople_search_in_place.own_only*1&&(d+="[data-search-in-place]");e(document).on("keydown",d,function(h){if(h.keyCode==9&&e(".search-in-place:visible").length)return h.preventDefault(),h.stopPropagation(),e(".search-in-place:visible a:first").focus(),!1});e(document).on("keydown input keyup focus",d,function(h){b(h.target)});e(document).on("input keyup focus",d,function(h){var g=e(this),p=g.val();if(!("key"in h&&
h.key=="Escape")&&(e(h.currentTarget).data("search-in-page")!=1||h.type!="focus"&&h.keyCode!==13)){if(e(".search-in-place-close-icon").length==0&&!g.data("search-in-page")&&t()){var c=e('<span class="search-in-place-close-icon"></span>');g.after(c);var f=g.offset(),l=g.outerWidth()-5,k=g.outerHeight(),m=Math.min(c.height(),k-5),n=Math.min(c.width(),m);c.height(m);c.width(n);c.offset({top:f.top+(k-m)/2,left:f.left+(l-n)})}g.attr("autocomplete","off");a.checkString(p)?(setTimeout(function(){a.getResults(g)},
500),v=!0):(a.clearAutocomplete(g),e(".search-in-place").hide(),v=!1);h.type=="keyup"&&h.keyCode==39&&a.fromAutocomplete(g)}});e(document).on("click",".search-in-place-close-icon",function(h){h.stopPropagation();h.preventDefault();w(!0)});e(document).on("mouseover",":not(.search-in-place, .search-in-place *)",function(){t()||setTimeout(w,150)});e(document).on("blur",'input[name="s"]',function(){var h=e(this),g=h.data("background-color");typeof g!="undefined"&&h.css("background-color",g);e('[name="cpsp-autocomplete"]').remove()});
e(document).on("click",':not(input[name="s"])',function(){setTimeout(w,150)});e(document).on("keyup",function(h){h.key=="Escape"&&w(!0)})},checkString:function(b){return this.config.min<=b.length},getResults:function(b){function a(l){"object"==typeof l&&("result"in l&&d.displayResult(l.result,c,h),"autocomplete"in l&&l.autocomplete.length?d.autocomplete(l.autocomplete[0],b):d.clearAutocomplete(b))}var d=this,h=b.data("search-in-page")?"source":"website",g=b.data("search-in-sections")?b.data("search-in-sections").split(",").map(l=>
l.trim()).filter(Boolean):d.config.areas;if(h=="website"&&!y.length||h=="source"&&b.data("no-popup")==void 0){var p=b.offset(),c=e(".search-in-place"),f=b.outerWidth();p={left:p.left,top:parseInt(p.top)+b.outerHeight()+5};if(d.search==b.val()&&c.length&&d.source==h&&(h!="source"||!c.data("search-in-sections")||c.data("search-in-sections")==b.data("search-in-sections"))){c.show().width(f).offset(p);return}c.remove();c=e('<div class="search-in-place"></div>');c.appendTo("body");h=="source"&&b.data("search-in-sections")&&
c.data("search-in-sections",b.data("search-in-sections"));c.width(f).offset(p);d.displayLoading(c)}d.search=b.val();d.source=h;h=="source"?(d.exclude_hidden=b.data("exclude-hidden")||0,f=d.search.replace(/^\s+/,"").replace(/\s+$/,"").replace(/\s+/g," "),f="operator"in codepeople_search_in_place&&codepeople_search_in_place.operator!="or"?[f]:f.split(" "),e(".search-in-place-mark").each(function(){var l=e(this).parent();e(this).contents().filter(function(){return this.nodeType===3}).unwrap();l[0]=l[0].normalize()}),
f=function(l){const k=JSON.parse(JSON.stringify(l));for(let m in l){if(typeof l[m]!=="string"||!/['"]/.test(l[m]))continue;const n=[l[m].replace(/(?<!\w)"/g,"\u201c"),l[m].replace(/"(?<!\w)/g,"\u201d"),l[m].replace(/(?<!\w)'/g,"\u2018"),l[m].replace(/'/g,"\u2019"),l[m].replace(/(?<!\w)"/g,"\u201c").replace(/"(?<!\w)/g,"\u201d").replace(/(?<!\w)'/g,"\u2018").replace(/'/g,"\u2019")];for(let r in n)n[r]!==l[m]&&k.indexOf(n[r])==-1&&k.push(n[r])}return k}(f),g=B.highlightTerms(f,g),b.data("no-popup")==
void 0&&(a(g),e(".search-in-place-more").remove(),e(".search-in-place .title a").on("mousedown",function(l){l.preventDefault();l.stopPropagation();b.trigger("blur");A(e(e(l.target).attr("href")));w(!0)}))):(g={s:d.search,action:"search_in_place"},"lang"in codepeople_search_in_place&&(g.lang=codepeople_search_in_place.lang),d.active&&d.active.abort(),d.active=jQuery.ajax({url:atob(codepeople_search_in_place.root)+"admin-ajax.php",data:g,cache:!0,dataType:"json",success:function(l){a(l)}}))},autocomplete:function(b,
a){var d=a.data("background-color")||a.css("background-color"),h=function(c){if(c){if(c.toLowerCase()==="transparent")return[0,0,0,0];if(c[0]==="#")return c.length<7&&(c="#"+c[1]+c[1]+c[2]+c[2]+c[3]+c[3]+(c.length>4?c[4]+c[4]:"")),[parseInt(c.substr(1,2),16),parseInt(c.substr(3,2),16),parseInt(c.substr(5,2),16),c.length>7?parseInt(c.substr(7,2),16)/255:1];if(c.indexOf("rgb")===-1){var f=document.body.appendChild(document.createElement("fictum"));f.style.color="rgb(1, 2, 3)";if(f.style.color!=="rgb(1, 2, 3)")return;
f.style.color=c;if(f.style.color==="rgb(1, 2, 3)"||f.style.color==="")return;c=getComputedStyle(f).color;document.body.removeChild(f)}if(c.indexOf("rgb")===0)return c.indexOf("rgba")===-1&&(c+=",1"),c.match(/[\.\d]+/g).map(function(l){return+l})}}(a.css("color")),g=a.clone(),p={position:"absolute",background:d,"border-color":"transparent","box-shadow":"none",zIndex:1};g.removeAttr("placeholder").removeAttr("required");a.css("zIndex")=="auto"&&a.css("zIndex",10);a.css("position")=="static"&&a.css("position",
"relative");a.data("background-color",d);h&&(h[3]=.5,p.color="rgba("+h.join(",")+")");e('[name="cpsp-autocomplete"]').remove();a.css("backgroundColor","transparent").after(g.attr("name","cpsp-autocomplete").val(b).css(p));g.width(a.width());g.height(a.height());g.offset(a.offset());g[0].scrollLeft=a[0].scrollLeft},fromAutocomplete:function(b){var a=b.next('[name="cpsp-autocomplete"]');a.length&&a.val().length&&b.val(a.val())},clearAutocomplete:function(b){b=b.next('[name="cpsp-autocomplete"]');b.length&&
b.val("")},displayResult:function(b,a,d){var h=this,g="",p=0,c;for(c in b){g+='<div class="label">'+(c!="source"?c:"")+"</div>";var f=b[c],l=f.length;p+=l;for(var k=0;k<l;k++)g+='<div class="item '+(k+1==l?"last":"")+'">',g=f[k].thumbnail?g+('<div class="thumbnail"><img src="'+f[k].thumbnail+'" style="visibility:hidden;float:left;position:absolute;" /></div><div class="data" style="margin-left:'+(h.config.image_width+5)+"px;min-height:"+h.config.image_height+'px;">'):g+'<div class="data">',g+='<span class="title"><a href="'+
f[k].link+'">'+(d=="source"&&f[k].resume?f[k].resume:f[k].title)+"</a></span>",d=="source"&&f[k].resume?g+='<span class="resume">'+f[k].title+"</span>":d!="source"&&f[k].resume&&(g+='<span class="resume">'+f[k].resume+"</span>"),f[k].author&&(g+='<span class="author">'+f[k].author+"</span>"),f[k].date&&(g+='<span class="date">'+f[k].date+"</span>"),g+='</div><div style="clear:both;"></div></div>'}g+='<div class="label more">';p?codepeople_search_in_place.result_number*1<=p&&(b=codepeople_search_in_place.home,
b+=b.indexOf("?")==-1?"?":"&","lang"in codepeople_search_in_place&&(b+="lang="+codepeople_search_in_place.lang+"&"),g+='<a class="search-in-place-more" href="'+b+"s="+this.search+'&submit=Search">'+codepeople_search_in_place.more+" &gt;</a>"):g+=codepeople_search_in_place.empty;g+="</div>";if(a)a.html(g).find(".thumbnail img").on("load",function(){var m=h.imgSize(this);e(this).width(m.w).height(m.h).css("visibility","visible")});else y.length&&(y.html(g),y.find(".thumbnail img").css("visibility",
"visible"));clearTimeout(C);C=setTimeout(function(){if("codepeople_search_in_place"in window&&"screen_reader_alert"in codepeople_search_in_place)try{let m=e(a||y);if(m.find(".search-in-place-alert").length==0){let n=m.find(".item").length,r=codepeople_search_in_place.screen_reader_alert+" "+n+" "+(n==1?codepeople_search_in_place.screen_reader_alert_result_single:codepeople_search_in_place.screen_reader_alert_result_multiple);m.find(".item").length&&(r+=". "+codepeople_search_in_place.screen_reader_alert_instructions);
m.prepend('<label class="search-in-place-alert" role="alert">'+r+"</label>")}}catch(m){}},1200)},imgSize:function(b){b=e(b);var a=b.width();b=b.height();if(a>this.config.image_width){var d=this.config.image_width;var h=d/a*b;a=d;b=h}b>this.config.image_height&&(h=this.config.image_height,a*=h/b,b=h);return{w:a,h:b}},displayLoading:function(b){b.append('<div class="label"><div class="loading"></div></div>')},highlightTerms:function(b,a){var d=this,h,g=0,p={result:{source:[]},autocomplete:[]};innerHighlight=
function(c,f,l){var k=0;if(3==f.nodeType){var m=f.data,n=c.toUpperCase(),r=replaceTildes(m).toUpperCase().indexOf(n),z=c.length,x=(Math.max("codepeople_search_in_place"in window&&"summary_length"in codepeople_search_in_place?codepeople_search_in_place.summary_length*1:20,z)-z)/2;n=r;var u=m.length-(r+z);if(0<=r&&e(f).closest(".search-in-place-mark").length==0&&(f=replaceNodeContent(f,c,r),f!==!1)){n<u?u=n<x?2*x-n:n=x:u<x?n=2*x-u:u=n=x;n=Math.max(0,r-("search_in_place_characters_before"in window?search_in_place_characters_before:
n));u=Math.min(r+z+("search_in_place_characters_after"in window?search_in_place_characters_after:u),m.length);for(;n&&/[^\W]/.test(m.charAt(n));)n--;for(;u<m.length&&/[^\W]/.test(m.charAt(u));)u++;p.autocomplete.length||(k=e('[name="s"][data-autocomplete="1"]:focus'),k.length&&(k=k.val(),k=new RegExp(k.replace(/[.*+?^${}()|[\]\\]/g,"\\$&")+"\\s*[^\\s]*","i"),k=m.match(k),null!=k&&p.autocomplete.push(k[0])));p.result.source.push({link:"#"+f,title:l||c,resume:m.substring(n,u)+'<span class="ellipsis">[...]</span>'});
k=1}}else possibleTextNode(f)&&lookupTextNodes(f,c,l);return k};replaceTildes=function(c){return/[\u0600-\u06ff]|[\u0750-\u077f]|[\ufb50-\ufc3f]|[\ufe70-\ufefc]|[\u0200]|[\u00A0]/g.test(c)||/[\u3040-\u30ff\u3400-\u4dbf\u4e00-\u9fff\uf900-\ufaff\uff66-\uff9f\u3131-\uD79D]/g.test(c)||/[\u0400-\u04FF]/g.test(c)?c:c.normalize("NFD").replace(/[\u0300-\u036f]/g,"")};replaceNodeContent=function(c,f,l){try{if(d.exclude_hidden&&e(c).parent().is(":hidden"))return!1;var k=document.createElement("mark"),m=c.splitText(l);
m.splitText(f.length);var n=m.cloneNode(!0);g++;var r="search-in-page-"+g;k.setAttribute("id",r);k.setAttribute("style","background-color:"+h);k.setAttribute("class","search-in-place-mark");k.appendChild(n);m.parentNode.replaceChild(k,m);return r}catch(z){return!1}};possibleTextNode=function(c){return 1==c.nodeType&&c.childNodes&&!/(script|style)/i.test(c.tagName)};lookupTextNodes=function(c,f,l){for(var k=0;k<c.childNodes.length;k++)k+=innerHighlight(f,c.childNodes[k],l)};e.each(a??d.config.areas,
function(c,f){f=e(f);f.length&&f.each(function(){var l=this;e.each(b,function(k,m){m.length>=codepeople_search_in_place.char_number&&(h=d.config.colors[k%d.config.colors.length],innerHighlight(replaceTildes(m),l,m))})})});return p}};var B=new q;q=null;if(codepeople_search_in_place.highlight*1||codepeople_search_in_place.highlight_resulting_page*1){if(codepeople_search_in_place.terms&&0<codepeople_search_in_place.terms.length)q=codepeople_search_in_place.terms;else try{q=(new URLSearchParams(window.location.search)).get("highlight"),
!q&&"sessionStorage"in window&&(q=sessionStorage.getItem("highlight")),q&&(q=String(q).trim())&&(q=[q])}catch(b){}q&&(B.highlightTerms(q),q=e(".search-in-place-mark:eq(0)"),q.length&&A(q))}codepeople_search_in_place.identify_post_type*1&&(e(".type-post").prepend('<div class="search-in-place-type-post">'+codepeople_search_in_place.post_title+"</div>"),e(".type-page").prepend('<div class="search-in-place-type-page">'+codepeople_search_in_place.page_title+"</div>"));B.autohide()}};jQuery(codepeople_search_in_place_generator);
jQuery(window).on("load",codepeople_search_in_place_generator);
jQuery(window).on("load",function(){setTimeout(function(){try{let t=(new URLSearchParams(window.location.search)).get("highlight");!t&&"sessionStorage"in window&&(t=sessionStorage.getItem("highlight"),sessionStorage.removeItem("highlight"));if(!jQuery(".search-in-place-mark.search-in-place-mark-active:visible").length&&t&&(t=String(t).trim(),!window.find(t))){t=t.replace(/\s+/g," ").split(" ");for(let e in t)if(window.find(t[e]))break}}catch(t){console.log(t)}},1E3)});
// source --> https://gardenscene.je/wp-content/plugins/photo-gallery/booster/assets/js/circle-progress.js?ver=1.2.2 
/**
 * jquery-circle-progress - jQuery Plugin to draw animated circular progress bars:
 * {@link http://kottenator.github.io/jquery-circle-progress/}
 *
 * @author Rostyslav Bryzgunov <kottenator@gmail.com>
 * @version 1.2.2
 * @licence MIT
 * @preserve
 */
// UMD factory - https://github.com/umdjs/umd/blob/d31bb6ee7098715e019f52bdfe27b3e4bfd2b97e/templates/jqueryPlugin.js
// Uses AMD, CommonJS or browser globals to create a jQuery plugin.
(function(factory) {
  if (typeof define === 'function' && define.amd) {
    // AMD - register as an anonymous module
    define(['jquery'], factory);
  } else if (typeof module === 'object' && module.exports) {
    // Node/CommonJS
    var $ = require('jquery');
    factory($);
    module.exports = $;
  } else {
    // Browser globals
    factory(jQuery);
  }
})(function($) {
  /**
   * Inner implementation of the circle progress bar.
   * The class is not exposed _yet_ but you can create an instance through jQuery method call.
   *
   * @param {object} config - You can customize any class member (property or method).
   * @class
   * @alias CircleProgress
   */
  function CircleProgress(config) {
    this.init(config);
  }

  CircleProgress.prototype = {
    //--------------------------------------- public options ---------------------------------------
    /**
     * This is the only required option. It should be from `0.0` to `1.0`.
     * @type {number}
     * @default 0.0
     */
    value: 0.0,

    /**
     * Size of the canvas in pixels.
     * It's a square so we need only one dimension.
     * @type {number}
     * @default 100.0
     */
    size: 100.0,

    /**
     * Initial angle for `0.0` value in radians.
     * @type {number}
     * @default -Math.PI
     */
    startAngle: -Math.PI,

    /**
     * Width of the arc in pixels.
     * If it's `'auto'` - the value is calculated as `[this.size]{@link CircleProgress#size} / 14`.
     * @type {number|string}
     * @default 'auto'
     */
    thickness: 'auto',

    /**
     * Fill of the arc. You may set it to:
     *
     *   - solid color:
     *     - `'#3aeabb'`
     *     - `{ color: '#3aeabb' }`
     *     - `{ color: 'rgba(255, 255, 255, .3)' }`
     *   - linear gradient _(left to right)_:
     *     - `{ gradient: ['#3aeabb', '#fdd250'], gradientAngle: Math.PI / 4 }`
     *     - `{ gradient: ['red', 'green', 'blue'], gradientDirection: [x0, y0, x1, y1] }`
     *     - `{ gradient: [["red", .2], ["green", .3], ["blue", .8]] }`
     *   - image:
     *     - `{ image: 'http://i.imgur.com/pT0i89v.png' }`
     *     - `{ image: imageObject }`
     *     - `{ color: 'lime', image: 'http://i.imgur.com/pT0i89v.png' }` -
     *       color displayed until the image is loaded
     *
     * @default {gradient: ['#3aeabb', '#fdd250']}
     */
    fill: {
      gradient: ['#3aeabb', '#fdd250']
    },

    /**
     * Color of the "empty" arc. Only a color fill supported by now.
     * @type {string}
     * @default 'rgba(0, 0, 0, .1)'
     */
    emptyFill: 'rgba(0, 0, 0, .1)',

    /**
     * jQuery Animation config.
     * You can pass `false` to disable the animation.
     * @see http://api.jquery.com/animate/
     * @type {object|boolean}
     * @default {duration: 1200, easing: 'circleProgressEasing'}
     */
    animation: {
      duration: 1200,
      easing: 'circleProgressEasing'
    },

    /**
     * Default animation starts at `0.0` and ends at specified `value`. Let's call this _direct animation_.
     * If you want to make _reversed animation_ - set `animationStartValue: 1.0`.
     * Also you may specify any other value from `0.0` to `1.0`.
     * @type {number}
     * @default 0.0
     */
    animationStartValue: 0.0,

    /**
     * Reverse animation and arc draw.
     * By default, the arc is filled from `0.0` to `value`, _clockwise_.
     * With `reverse: true` the arc is filled from `1.0` to `value`, _counter-clockwise_.
     * @type {boolean}
     * @default false
     */
    reverse: false,

    /**
     * Arc line cap: `'butt'`, `'round'` or `'square'` -
     * [read more]{@link https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D.lineCap}.
     * @type {string}
     * @default 'butt'
     */
    lineCap: 'butt',

    /**
     * Canvas insertion mode: append or prepend it into the parent element?
     * @type {string}
     * @default 'prepend'
     */
    insertMode: 'prepend',

    //------------------------------ protected properties and methods ------------------------------
    /**
     * Link to {@link CircleProgress} constructor.
     * @protected
     */
    constructor: CircleProgress,

    /**
     * Container element. Should be passed into constructor config.
     * @protected
     * @type {jQuery}
     */
    el: null,

    /**
     * Canvas element. Automatically generated and prepended to [this.el]{@link CircleProgress#el}.
     * @protected
     * @type {HTMLCanvasElement}
     */
    canvas: null,

    /**
     * 2D-context of [this.canvas]{@link CircleProgress#canvas}.
     * @protected
     * @type {CanvasRenderingContext2D}
     */
    ctx: null,

    /**
     * Radius of the outer circle. Automatically calculated as `[this.size]{@link CircleProgress#size} / 2`.
     * @protected
     * @type {number}
     */
    radius: 0.0,

    /**
     * Fill of the main arc. Automatically calculated, depending on [this.fill]{@link CircleProgress#fill} option.
     * @protected
     * @type {string|CanvasGradient|CanvasPattern}
     */
    arcFill: null,

    /**
     * Last rendered frame value.
     * @protected
     * @type {number}
     */
    lastFrameValue: 0.0,

    /**
     * Init/re-init the widget.
     *
     * Throws a jQuery event:
     *
     * - `circle-inited(jqEvent)`
     *
     * @param {object} config - You can customize any class member (property or method).
     */
    init: function(config) {
      $.extend(this, config);
      this.radius = this.size / 2;
      this.initWidget();
      this.initFill();
      this.draw();
      this.el.trigger('circle-inited');
    },

    /**
     * Initialize `<canvas>`.
     * @protected
     */
    initWidget: function() {
      if (!this.canvas)
        this.canvas = $('<canvas>')[this.insertMode == 'prepend' ? 'prependTo' : 'appendTo'](this.el)[0];

      var canvas = this.canvas;
      canvas.width = this.size;
      canvas.height = this.size;
      this.ctx = canvas.getContext('2d');

      if (window.devicePixelRatio > 1) {
        var scaleBy = window.devicePixelRatio;
        canvas.style.width = canvas.style.height = this.size + 'px';
        canvas.width = canvas.height = this.size * scaleBy;
        this.ctx.scale(scaleBy, scaleBy);
      }
    },

    /**
     * This method sets [this.arcFill]{@link CircleProgress#arcFill}.
     * It could do this async (on image load).
     * @protected
     */
    initFill: function() {
      var self = this,
        fill = this.fill,
        ctx = this.ctx,
        size = this.size;

      if (!fill)
        throw Error("The fill is not specified!");

      if (typeof fill == 'string')
        fill = {color: fill};

      if (fill.color)
        this.arcFill = fill.color;

      if (fill.gradient) {
        var gr = fill.gradient;

        if (gr.length == 1) {
          this.arcFill = gr[0];
        } else if (gr.length > 1) {
          var ga = fill.gradientAngle || 0, // gradient direction angle; 0 by default
            gd = fill.gradientDirection || [
                size / 2 * (1 - Math.cos(ga)), // x0
                size / 2 * (1 + Math.sin(ga)), // y0
                size / 2 * (1 + Math.cos(ga)), // x1
                size / 2 * (1 - Math.sin(ga))  // y1
              ];

          var lg = ctx.createLinearGradient.apply(ctx, gd);

          for (var i = 0; i < gr.length; i++) {
            var color = gr[i],
              pos = i / (gr.length - 1);

            if ($.isArray(color)) {
              pos = color[1];
              color = color[0];
            }

            lg.addColorStop(pos, color);
          }

          this.arcFill = lg;
        }
      }

      if (fill.image) {
        var img;

        if (fill.image instanceof Image) {
          img = fill.image;
        } else {
          img = new Image();
          img.src = fill.image;
        }

        if (img.complete)
          setImageFill();
        else
          img.onload = setImageFill;
      }

      function setImageFill() {
        var bg = $('<canvas>')[0];
        bg.width = self.size;
        bg.height = self.size;
        bg.getContext('2d').drawImage(img, 0, 0, size, size);
        self.arcFill = self.ctx.createPattern(bg, 'no-repeat');
        self.drawFrame(self.lastFrameValue);
      }
    },

    /**
     * Draw the circle.
     * @protected
     */
    draw: function() {
      if (this.animation)
        this.drawAnimated(this.value);
      else
        this.drawFrame(this.value);
    },

    /**
     * Draw a single animation frame.
     * @protected
     * @param {number} v - Frame value.
     */
    drawFrame: function(v) {
      this.lastFrameValue = v;
      this.ctx.clearRect(0, 0, this.size, this.size);
      this.drawEmptyArc(v);
      this.drawArc(v);
    },

    /**
     * Draw the arc (part of the circle).
     * @protected
     * @param {number} v - Frame value.
     */
    drawArc: function(v) {
      if (v === 0)
        return;

      var ctx = this.ctx,
        r = this.radius,
        t = this.getThickness(),
        a = this.startAngle;

      ctx.save();
      ctx.beginPath();

      if (!this.reverse) {
        ctx.arc(r, r, r - t / 2, a, a + Math.PI * 2 * v);
      } else {
        ctx.arc(r, r, r - t / 2, a - Math.PI * 2 * v, a);
      }

      ctx.lineWidth = t;
      ctx.lineCap = this.lineCap;
      ctx.strokeStyle = this.arcFill;
      ctx.stroke();
      ctx.restore();
    },

    /**
     * Draw the _empty (background)_ arc (part of the circle).
     * @protected
     * @param {number} v - Frame value.
     */
    drawEmptyArc: function(v) {
      var ctx = this.ctx,
        r = this.radius,
        t = this.getThickness(),
        a = this.startAngle;

      if (v < 1) {
        ctx.save();
        ctx.beginPath();

        if (v <= 0) {
          ctx.arc(r, r, r - t / 2, 0, Math.PI * 2);
        } else {
          if (!this.reverse) {
            ctx.arc(r, r, r - t / 2, a + Math.PI * 2 * v, a);
          } else {
            ctx.arc(r, r, r - t / 2, a, a - Math.PI * 2 * v);
          }
        }

        ctx.lineWidth = t;
        ctx.strokeStyle = this.emptyFill;
        ctx.stroke();
        ctx.restore();
      }
    },

    /**
     * Animate the progress bar.
     *
     * Throws 3 jQuery events:
     *
     * - `circle-animation-start(jqEvent)`
     * - `circle-animation-progress(jqEvent, animationProgress, stepValue)` - multiple event
     *   animationProgress: from `0.0` to `1.0`; stepValue: from `0.0` to `value`
     * - `circle-animation-end(jqEvent)`
     *
     * @protected
     * @param {number} v - Final value.
     */
    drawAnimated: function(v) {
      var self = this,
        el = this.el,
        canvas = $(this.canvas);

      // stop previous animation before new "start" event is triggered
      canvas.stop(true, false);
      el.trigger('circle-animation-start');

      canvas
        .css({animationProgress: 0})
        .animate({animationProgress: 1}, $.extend({}, this.animation, {
          step: function(animationProgress) {
            var stepValue = self.animationStartValue * (1 - animationProgress) + v * animationProgress;
            self.drawFrame(stepValue);
            el.trigger('circle-animation-progress', [animationProgress, stepValue]);
          }
        }))
        .promise()
        .always(function() {
          // trigger on both successful & failure animation end
          el.trigger('circle-animation-end');
        });
    },

    /**
     * Get the circle thickness.
     * @see CircleProgress#thickness
     * @protected
     * @returns {number}
     */
    getThickness: function() {
      return $.isNumeric(this.thickness) ? this.thickness : this.size / 14;
    },

    /**
     * Get current value.
     * @protected
     * @return {number}
     */
    getValue: function() {
      return this.value;
    },

    /**
     * Set current value (with smooth animation transition).
     * @protected
     * @param {number} newValue
     */
    setValue: function(newValue) {
      if (this.animation)
        this.animationStartValue = this.lastFrameValue;
      this.value = newValue;
      this.draw();
    }
  };

  //----------------------------------- Initiating jQuery plugin -----------------------------------
  $.circleProgress = {
    // Default options (you may override them)
    defaults: CircleProgress.prototype
  };

  // ease-in-out-cubic
  $.easing.circleProgressEasing = function(x) {
    if (x < 0.5) {
      x = 2 * x;
      return 0.5 * x * x * x;
    } else {
      x = 2 - 2 * x;
      return 1 - 0.5 * x * x * x;
    }
  };

  /**
   * Creates an instance of {@link CircleProgress}.
   * Produces [init event]{@link CircleProgress#init} and [animation events]{@link CircleProgress#drawAnimated}.
   *
   * @param {object} [configOrCommand] - Config object or command name.
   *
   * Config example (you can specify any {@link CircleProgress} property):
   *
   * ```js
   * { value: 0.75, size: 50, animation: false }
   * ```
   *
   * Commands:
   *
   * ```js
   * el.circleProgress('widget'); // get the <canvas>
   * el.circleProgress('value'); // get the value
   * el.circleProgress('value', newValue); // update the value
   * el.circleProgress('redraw'); // redraw the circle
   * el.circleProgress(); // the same as 'redraw'
   * ```
   *
   * @param {string} [commandArgument] - Some commands (like `'value'`) may require an argument.
   * @see CircleProgress
   * @alias "$(...).circleProgress"
   */
  $.fn.circleProgress = function(configOrCommand, commandArgument) {
    var dataName = 'circle-progress',
      firstInstance = this.data(dataName);

    if (configOrCommand == 'widget') {
      if (!firstInstance)
        throw Error('Calling "widget" method on not initialized instance is forbidden');
      return firstInstance.canvas;
    }

    if (configOrCommand == 'value') {
      if (!firstInstance)
        throw Error('Calling "value" method on not initialized instance is forbidden');
      if (typeof commandArgument == 'undefined') {
        return firstInstance.getValue();
      } else {
        var newValue = arguments[1];
        return this.each(function() {
          $(this).data(dataName).setValue(newValue);
        });
      }
    }

    return this.each(function() {
      var el = $(this),
        instance = el.data(dataName),
        config = $.isPlainObject(configOrCommand) ? configOrCommand : {};

      if (instance) {
        instance.init(config);
      } else {
        var initialConfig = $.extend({}, el.data());
        if (typeof initialConfig.fill == 'string')
          initialConfig.fill = JSON.parse(initialConfig.fill);
        if (typeof initialConfig.animation == 'string')
          initialConfig.animation = JSON.parse(initialConfig.animation);
        config = $.extend(initialConfig, config);
        config.el = el;
        instance = new CircleProgress(config);
        el.data(dataName, instance);
      }
    });
  };
});