PageSourceSearch

https://eelos.ru/bitrix/templates/eelos_copy/js/progress.js

js eelos.ru collected 2026-10-04 23:57:13 UTC 15,920 bytes, 487 lines download raw bytes

vendor: 15,920 bytes, lines 1-487
1(function(factory) {
2    if (typeof define === 'function' && define.amd) {
3        // AMD - register as an anonymous module
4        define(['jquery'], factory);
5    } else if (typeof module === 'object' && module.exports) {
6        // Node/CommonJS
7        var $ = require('jquery');
8        factory($);
9        module.exports = $;
10    } else {
11        // Browser globals
12        factory(jQuery);
13    }
14})(function($) {
15    /**
16     * Inner implementation of the circle progress bar.
17     * The class is not exposed _yet_ but you can create an instance through jQuery method call.
18     *
19     * @param {object} config - You can customize any class member (property or method).
20     * @class
21     * @alias CircleProgress
22     */
23    function CircleProgress(config) {
24        this.init(config);
25    }
26
27    CircleProgress.prototype = {
28        //--------------------------------------- public options ---------------------------------------
29        /**
30         * This is the only required option. It should be from `0.0` to `1.0`.
31         * @type {number}
32         * @default 0.0
33         */
34        value: 0.0,
35
36        /**
37         * Size of the canvas in pixels.
38         * It's a square so we need only one dimension.
39         * @type {number}
40         * @default 100.0
41         */
42        size: 100.0,
43
44        /**
45         * Initial angle for `0.0` value in radians.
46         * @type {number}
47         * @default -Math.PI
48         */
49        startAngle: -Math.PI,
50
51        /**
52         * Width of the arc in pixels.
53         * If it's `'auto'` - the value is calculated as `[this.size]{@link CircleProgress#size} / 14`.
54         * @type {number|string}
55         * @default 'auto'
56         */
57        thickness: 'auto',
58
59        /**
60         * Fill of the arc. You may set it to:
61         *
62         *   - solid color:
63         *     - `'#3aeabb'`
64         *     - `{ color: '#3aeabb' }`
65         *     - `{ color: 'rgba(255, 255, 255, .3)' }`
66         *   - linear gradient _(left to right)_:
67         *     - `{ gradient: ['#3aeabb', '#fdd250'], gradientAngle: Math.PI / 4 }`
68         *     - `{ gradient: ['red', 'green', 'blue'], gradientDirection: [x0, y0, x1, y1] }`
69         *     - `{ gradient: [["red", .2], ["green", .3], ["blue", .8]] }`
70         *   - image:
71         *     - `{ image: 'http://i.imgur.com/pT0i89v.png' }`
72         *     - `{ image: imageObject }`
73         *     - `{ color: 'lime', image: 'http://i.imgur.com/pT0i89v.png' }` -
74         *       color displayed until the image is loaded
75         *
76         * @default {gradient: ['#3aeabb', '#fdd250']}
77         */
78        fill: {
79            gradient: ['#3aeabb', '#fdd250']
80        },
81
82        /**
83         * Color of the "empty" arc. Only a color fill supported by now.
84         * @type {string}
85         * @default 'rgba(0, 0, 0, .1)'
86         */
87        emptyFill: 'rgba(0, 0, 0, .1)',
88
89        /**
90         * jQuery Animation config.
91         * You can pass `false` to disable the animation.
92         * @see http://api.jquery.com/animate/
93         * @type {object|boolean}
94         * @default {duration: 1200, easing: 'circleProgressEasing'}
95         */
96        animation: {
97            duration: 0,
98
99        },
100
101        /**
102         * Default animation starts at `0.0` and ends at specified `value`. Let's call this _direct animation_.
103         * If you want to make _reversed animation_ - set `animationStartValue: 1.0`.
104         * Also you may specify any other value from `0.0` to `1.0`.
105         * @type {number}
106         * @default 0.0
107         */
108        animationStartValue: 0.0,
109
110        /**
111         * Reverse animation and arc draw.
112         * By default, the arc is filled from `0.0` to `value`, _clockwise_.
113         * With `reverse: true` the arc is filled from `1.0` to `value`, _counter-clockwise_.
114         * @type {boolean}
115         * @default false
116         */
117        reverse: true,
118
119        /**
120         * Arc line cap: `'butt'`, `'round'` or `'square'` -
121         * [read more]{@link https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D.lineCap}.
122         * @type {string}
123         * @default 'butt'
124         */
125        lineCap: 'butt',
126
127        /**
128         * Canvas insertion mode: append or prepend it into the parent element?
129         * @type {string}
130         * @default 'prepend'
131         */
132        insertMode: 'prepend',
133
134        //------------------------------ protected properties and methods ------------------------------
135        /**
136         * Link to {@link CircleProgress} constructor.
137         * @protected
138         */
139        constructor: CircleProgress,
140
141        /**
142         * Container element. Should be passed into constructor config.
143         * @protected
144         * @type {jQuery}
145         */
146        el: null,
147
148        /**
149         * Canvas element. Automatically generated and prepended to [this.el]{@link CircleProgress#el}.
150         * @protected
151         * @type {HTMLCanvasElement}
152         */
153        canvas: null,
154
155        /**
156         * 2D-context of [this.canvas]{@link CircleProgress#canvas}.
157         * @protected
158         * @type {CanvasRenderingContext2D}
159         */
160        ctx: null,
161
162        /**
163         * Radius of the outer circle. Automatically calculated as `[this.size]{@link CircleProgress#size} / 2`.
164         * @protected
165         * @type {number}
166         */
167        radius: 0.0,
168
169        /**
170         * Fill of the main arc. Automatically calculated, depending on [this.fill]{@link CircleProgress#fill} option.
171         * @protected
172         * @type {string|CanvasGradient|CanvasPattern}
173         */
174        arcFill: null,
175
176        /**
177         * Last rendered frame value.
178         * @protected
179         * @type {number}
180         */
181        lastFrameValue: 0.0,
182
183        /**
184         * Init/re-init the widget.
185         *
186         * Throws a jQuery event:
187         *
188         * - `circle-inited(jqEvent)`
189         *
190         * @param {object} config - You can customize any class member (property or method).
191         */
192        init: function(config) {
193            $.extend(this, config);
194            this.radius = this.size / 2;
195            this.initWidget();
196            this.initFill();
197            this.draw();
198            this.el.trigger('circle-inited');
199        },
200
201        /**
202         * Initialize `<canvas>`.
203         * @protected
204         */
205        initWidget: function() {
206            if (!this.canvas)
207                this.canvas = $('<canvas>')[this.insertMode == 'prepend' ? 'prependTo' : 'appendTo'](this.el)[0];
208
209            var canvas = this.canvas;
210            canvas.width = this.size;
211            canvas.height = this.size;
212            this.ctx = canvas.getContext('2d');
213
214            if (window.devicePixelRatio > 1) {
215                var scaleBy = window.devicePixelRatio;
216                canvas.style.width = canvas.style.height = this.size + 'px';
217                canvas.width = canvas.height = this.size * scaleBy;
218                this.ctx.scale(scaleBy, scaleBy);
219            }
220        },
221
222        /**
223         * This method sets [this.arcFill]{@link CircleProgress#arcFill}.
224         * It could do this async (on image load).
225         * @protected
226         */
227        initFill: function() {
228            var self = this,
229                fill = this.fill,
230                ctx = this.ctx,
231                size = this.size;
232
233            if (!fill)
234                throw Error("The fill is not specified!");
235
236            if (typeof fill == 'string')
237                fill = {color: fill};
238
239            if (fill.color)
240                this.arcFill = fill.color;
241
242            if (fill.gradient) {
243                var gr = fill.gradient;
244
245                if (gr.length == 1) {
246                    this.arcFill = gr[0];
247                } else if (gr.length > 1) {
248                    var ga = fill.gradientAngle || 0, // gradient direction angle; 0 by default
249                        gd = fill.gradientDirection || [
250                            size / 2 * (1 - Math.cos(ga)), // x0
251                            size / 2 * (1 + Math.sin(ga)), // y0
252                            size / 2 * (1 + Math.cos(ga)), // x1
253                            size / 2 * (1 - Math.sin(ga))  // y1
254                        ];
255
256                    var lg = ctx.createLinearGradient.apply(ctx, gd);
257
258                    for (var i = 0; i < gr.length; i++) {
259                        var color = gr[i],
260                            pos = i / (gr.length - 1);
261
262                        if ($.isArray(color)) {
263                            pos = color[1];
264                            color = color[0];
265                        }
266
267                        lg.addColorStop(pos, color);
268                    }
269
270                    this.arcFill = lg;
271                }
272            }
273
274            if (fill.image) {
275                var img;
276
277                if (fill.image instanceof Image) {
278                    img = fill.image;
279                } else {
280                    img = new Image();
281                    img.src = fill.image;
282                }
283
284                if (img.complete)
285                    setImageFill();
286                else
287                    img.onload = setImageFill;
288            }
289
290            function setImageFill() {
291                var bg = $('<canvas>')[0];
292                bg.width = self.size;
293                bg.height = self.size;
294                bg.getContext('2d').drawImage(img, 0, 0, size, size);
295                self.arcFill = self.ctx.createPattern(bg, 'no-repeat');
296                self.drawFrame(self.lastFrameValue);
297            }
298        },
299
300        /**
301         * Draw the circle.
302         * @protected
303         */
304        draw: function() {
305            if (this.animation)
306                this.drawAnimated(this.value);
307            else
308                this.drawFrame(this.value);
309        },
310
311        /**
312         * Draw a single animation frame.
313         * @protected
314         * @param {number} v - Frame value.
315         */
316        drawFrame: function(v) {
317            this.lastFrameValue = v;
318            this.ctx.clearRect(0, 0, this.size, this.size);
319            this.drawEmptyArc(v);
320            this.drawArc(v);
321        },
322
323        /**
324         * Draw the arc (part of the circle).
325         * @protected
326         * @param {number} v - Frame value.
327         */
328        drawArc: function(v) {
329            if (v === 0)
330                return;
331
332            var ctx = this.ctx,
333                r = this.radius,
334                t = this.getThickness(),
335                a = this.startAngle;
336
337            ctx.save();
338            ctx.beginPath();
339
340            if (!this.reverse) {
341                ctx.arc(r, r, r - t / 2, a, a + Math.PI * 2 * v);
342            } else {
343                ctx.arc(r, r, r - t / 2, a - Math.PI * 2 * v, a);
344            }
345
346            ctx.lineWidth = t;
347            ctx.lineCap = this.lineCap;
348            ctx.strokeStyle = this.arcFill;
349            ctx.stroke();
350            ctx.restore();
351        },
352
353
354        drawEmptyArc: function(v) {
355            var ctx = this.ctx,
356                r = this.radius,
357                t = this.getThickness(),
358                a = this.startAngle;
359
360            if (v < 1) {
361                ctx.save();
362                ctx.beginPath();
363
364                if (v <= 0) {
365                    ctx.arc(r, r, r - t / 2, 0, Math.PI * 2);
366                } else {
367                    if (!this.reverse) {
368                        ctx.arc(r, r, r - t / 2, a + Math.PI * 2 * v, a);
369                    } else {
370                        ctx.arc(r, r, r - t / 2, a, a - Math.PI * 2 * v);
371                    }
372                }
373
374                ctx.lineWidth = t;
375                ctx.strokeStyle = this.emptyFill;
376                ctx.stroke();
377                ctx.restore();
378            }
379        },
380
381
382        drawAnimated: function(v) {
383            var self = this,
384                el = this.el,
385                canvas = $(this.canvas);
386
387            // stop previous animation before new "start" event is triggered
388            canvas.stop(true, false);
389            el.trigger('circle-animation-start');
390
391            canvas
392                .css({animationProgress: 0})
393                .animate({animationProgress: 1}, $.extend({}, this.animation, {
394                    step: function(animationProgress) {
395                        var stepValue = self.animationStartValue * (1 - animationProgress) + v * animationProgress;
396                        self.drawFrame(stepValue);
397                        el.trigger('circle-animation-progress', [animationProgress, stepValue]);
398                    }
399                }))
400                .promise()
401                .always(function() {
402                    // trigger on both successful & failure animation end
403                    el.trigger('circle-animation-end');
404                });
405        },
406
407
408        getThickness: function() {
409            return $.isNumeric(this.thickness) ? this.thickness : this.size / 14;
410        },
411
412
413        getValue: function() {
414            return this.value;
415        },
416
417
418        setValue: function(newValue) {
419            if (this.animation)
420                this.animationStartValue = this.lastFrameValue;
421            this.value = newValue;
422            this.draw();
423        }
424    };
425
426    //----------------------------------- Initiating jQuery plugin -----------------------------------
427    $.circleProgress = {
428        // Default options (you may override them)
429        defaults: CircleProgress.prototype
430    };
431
432    // ease-in-out-cubic
433    $.easing.circleProgressEasing = function(x) {
434        if (x < 0.5) {
435            x = 2 * x;
436            return 0.5 * x * x * x;
437        } else {
438            x = 2 - 2 * x;
439            return 1 - 0.5 * x * x * x;
440        }
441    };
442
443
444    $.fn.circleProgress = function(configOrCommand, commandArgument) {
445        var dataName = 'circle-progress',
446            firstInstance = this.data(dataName);
447
448        if (configOrCommand == 'widget') {
449            if (!firstInstance)
450                throw Error('Calling "widget" method on not initialized instance is forbidden');
451            return firstInstance.canvas;
452        }
453
454        if (configOrCommand == 'value') {
455            if (!firstInstance)
456                throw Error('Calling "value" method on not initialized instance is forbidden');
457            if (typeof commandArgument == 'undefined') {
458                return firstInstance.getValue();
459            } else {
460                var newValue = arguments[1];
461                return this.each(function() {
462                    $(this).data(dataName).setValue(newValue);
463                });
464            }
465        }
466
467        return this.each(function() {
468            var el = $(this),
469                instance = el.data(dataName),
470                config = $.isPlainObject(configOrCommand) ? configOrCommand : {};
471
472            if (instance) {
473                instance.init(config);
474            } else {
475                var initialConfig = $.extend({}, el.data());
476                if (typeof initialConfig.fill == 'string')
477                    initialConfig.fill = JSON.parse(initialConfig.fill);
478                if (typeof initialConfig.animation == 'string')
479                    initialConfig.animation = JSON.parse(initialConfig.animation);
480                config = $.extend(initialConfig, config);
481                config.el = el;
482                instance = new CircleProgress(config);
483                el.data(dataName, instance);
484            }
485        });
486    };
487});

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.