PageSourceSearch

https://www.selfloops.com/assets/selfloops-723c3a1a.js

js selfloops.com collected 2026-10-01 14:18:30 UTC 19,548 bytes, 709 lines download raw bytes

1var SL = {};
2
3SL.alert = function(message,callback){
4	if (callback != null){
5		bootbox.alert(message,callback);
6	} else {
7		bootbox.alert(message);
8	}
9};
10
11SL.measure = {
12      'metric' : {
13        'height': 'cm',
14        'weight': 'kg',
15        'speed': 'km/h',
16        'speedmeters': 'm/s',
17        'distance': 'km',
18        'distancemeters': 'm',
19        'rowmeters': 'm',
20        'altitude': 'm',
21        'pace': '/km',
22        'pacemeters': '/100m',
23        'pacerowmeters': '/500m',
24				'temperature': 'C'
25      },
26      'imperial': {
27        'height': 'in',
28        'weight': 'lb',
29        'speed': 'mi/h',
30        'speedmeters': 'yds/s',
31        'distance': 'mi',
32        'distancemeters': 'yds',
33        'rowmeters': 'm',
34        'altitude' : 'feet',
35        'pace': '/mi',
36        'pacemeters': '/100y',
37        'pacerowmeters': '/500m',
38				'temperature': 'F'
39      }
40};
41
42SL.sport_type = {
43	'running': 'running',
44	'swim': 'swim',
45	'rowing': 'rowing'
46};
47
48/*
49 * Convert a value from metric to imperial or viceversa
50 *  Input: source_u -> the source system used
51 *       source_m -> the metric example (height, weight, speed, distance)
52 *       source_val -> the value to convert
53 *      dest_u -> the destination system of return value
54 *  Output: an array of [value_converted,label of unit] example for speed at 30 mi/h --> [30, mi/h]
55*/
56SL.unit_conversion = function(source_u,source_m,source_val,dest_u) {
57	var conversion_coefficient = {
58		'metric': {
59			'height': 0.3937,
60			'weight': 2.2046,
61			'speed': 0.621371,
62			'speedmeters': 1.0936,
63			'distance': 0.621371,
64			'distancemeters': 1.0936,
65			'rowmeters': 1,
66			'altitude': 3.2808
67		},
68		'imperial': {
69			'height': 2.54,
70			'weight': 0.4536,
71			'speed': 1.609344,
72			'speedmeters': 0.9144,
73			'distance': 1.609344,
74			'distancemeters': 0.9144,
75			'rowmeters': 1,
76			'altitude': 0.3048
77		}
78	};
79	if (source_u == dest_u) {
80		return [source_val,SL.measure[dest_u][source_m]];
81	}
82	if (source_m == 'temperature') {
83		if (source_u == 'metric') {
84			return [(Math.round(10 * ((source_val * 9/5) + 32)) / 10),SL.measure[dest_u][source_m]];
85		} else {
86			return [(Math.round(10 * ((source_val - 32) * 5/9)) / 10),SL.measure[dest_u][source_m]];
87		}
88	} else {
89  	return [(Math.round(10 * (source_val * conversion_coefficient[source_u][source_m])) / 10),SL.measure[dest_u][source_m]];
90	}
91};
92
93/*
94 * Convert a number of seconds in duration value like hh:mm:ss. Use separator to format the output string.
95 * Input: s -> the seconds
96 * 		  separator -> the string separator used on output string.
97 * Output: string on format hh+separator+mm+separator+ss (hh -> hours, mm -> minutes, ss -> seconds)
98 */
99SL.seconds_format = function(s,separator) {
100	var s_i = parseInt(s+'');
101	var h,m,s = null;
102	if (s_i >= 3600) {
103		h = Math.floor(s_i / 3600);
104		s_i = s_i - (h * 3600);
105	} else {
106		h = 0;
107	}
108	h = ''+h;
109	if (h.length == 1) {
110		h = '0'+h;
111	}
112	if (s_i >= 60) {
113		m = Math.floor(s_i / 60);
114		s_i = s_i - (m * 60);
115	} else {
116		m = 0;
117	}
118	m = ''+m;
119	if (m.length == 1) {
120		m = '0'+m;
121	}
122	s = ''+s_i;
123	if (s.length == 1) {
124		s = '0'+s;
125	}
126	return h+separator+m+separator+s+'';
127};
128
129/*
130 * A shortcut to have only minutes not hour in seconds_format.
131 * Output: string on format mm+separator+ss (mm -> minutes, ss -> seconds)
132 */
133SL.minutes_format = function(s,separator) {
134	var all_format = SL.seconds_format(s,separator);
135	var splitted = all_format.split(separator);
136	var out = splitted[1]+separator+splitted[2];
137	if (parseInt(splitted[0]) > 0){
138		out = ""+((parseInt(splitted[0]) * 60)+parseInt(splitted[1]))+separator+splitted[2];
139	}
140	return out;
141};
142
143SL.show_loader = function() {
144	jQuery("#load-overlay").show();
145	jQuery("#load-overlay-content").show();
146};
147
148SL.hide_loader = function() {
149	jQuery("#load-overlay").hide();
150	jQuery("#load-overlay-content").hide();
151};
152
153SL.track_outbound_link = function(link,category,action,label) {
154	try {
155		_gaq.push(['_trackEvent', category , action, label]);
156	} catch(err){}
157
158	setTimeout(function() {
159		document.location.href = link.href;
160	}, 100);
161};
162
163SL.get_url_param = function(paramName) {
164	var searchString = window.location.search.substring(1),
165		i, val, params = searchString.split("&");
166
167	for (i=0;i<params.length;i++) {
168		val = params[i].split("=");
169		if (val[0] == paramName) {
170			return decodeURIComponent(val[1]);
171		}
172	}
173	return "";
174};
175
176/* Highchart time serie with time in millis */
177SL.hc_time_serie = function(input_serie) {
178    var o = [];
179    if (input_serie !== null && input_serie !== 'undefined') {
180        for(var i=0; i < input_serie.length; i++){
181            o.push([input_serie[i][0] * 1000, input_serie[i][1]]);
182        }
183    }
184    return o;
185};
186
187SL.training_stress_serie = function(train_const,train_start,load_serie) {
188	var o = new Array();
189	if (load_serie != null && train_const != null && train_const != 0 && train_start != null) {
190		for(var i=0;i<load_serie.length;i++){
191			if (i == 0) {
192				o.push([load_serie[i][0]*1000,train_start]);
193			} else {
194				o.push([load_serie[i][0]*1000,Math.round((o[i-1][1]*Math.exp(-1/train_const))+(load_serie[i][1]*(1-Math.exp(-1/train_const))))]);
195			}
196		}
197	}
198	return o;
199};
200
201SL.training_balance_serie = function(a,c) {
202	var b = new Array();
203	if (a != null && c != null && a.length == c.length && a.length > 0) {
204		for(var i=0;i<a.length;i++){
205			b.push([a[i][0],c[i][1]-a[i][1]]);
206		}
207	}
208	return b;
209};
210
211SL.aepf = function(p,c,cl) {
212	if (c > 0) {
213		return Math.round( (p*60) / (c*2*Math.PI*cl) );
214	}
215	return 0;
216};
217
218SL.force_cadence_serie = function(c,p,cl) {
219	var f = new Array();
220	var aepf = null;
221	if (c != null && p != null && cl != null && c.length == p.length && c.length > 0){
222		for(var i=0;i<c.length;i++){
223			aepf = SL.aepf(p[i],c[i],cl);
224			if (aepf > 0) {
225				f.push([c[i],aepf]);
226			}
227		}
228	}
229	return f;
230};
231
232SL.force_cadence_target = function(ftp,cs,cl) {
233	return SL.aepf(ftp,cs,cl);
234};
235
236SL.ftp_force_serie = function(c_min,c_max,ftp,cl) {
237	var f = new Array();
238	if (c_min != null && c_max != null && ftp != null && cl != null) {
239		for(var i=c_min;i<c_max;i++){
240			if (i > 0) {
241				f.push([i,SL.aepf(ftp,i,cl)]);
242			}
243		}
244	}
245	return f;
246};
247
248SL.min = function(a) {
249	var min = null;
250	if (a != null && a instanceof Array && a.length > 0){
251		min = a[0];
252		for(var i=1;i<a.length;i++){
253			if ((min != null) && (a[i] != null) && (a[i] < min)) {
254				min = a[i];
255			}
256		}
257	}
258	return min;
259};
260
261SL.max = function(a) {
262	var max = null;
263	if (a != null && a instanceof Array && a.length > 0){
264		max = a[0];
265		for(var i=1;i<a.length;i++){
266			if (a[i] > max) {
267				max = a[i];
268			}
269		}
270	}
271	return max;
272};
273
274SL.clean = function(o_id) {
275	if (jQuery(o_id).length > 0) {
276		jQuery(o_id).html('');
277	}
278};
279
280/*
281 * Slice an array from position x1 to x2.
282 */
283SL.slice = function(a,x1,x2) {
284	var start, end;
285	var out = new Array();
286	if (a!= null && a instanceof Array && a.length > 0){
287		start = 0;
288		end = a.length;
289		if ((x1 !== null) && (typeof x1 !== 'undefined') && (x1 >= 0) && (x1 < a.length)) {
290			start = x1;
291		} else {
292			return out;
293		}
294		if ((x2 !== null) && (typeof x2 !== 'undefined') && (x2 < a.length)){
295			end = x2;
296		} else {
297			return out;
298		}
299		out = a.slice(x1,x2);
300	}
301	return out;
302};
303
304
305/*
306 * Get the index of the first element in an ordered array where e > v.
307 */
308SL.ordered_indexOf = function(a,v) {
309	var idx = 0;
310	if (a!= null && a instanceof Array && a.length > 0 && v != null && typeof v != 'undefined'){
311		var i = 0;
312		v = v * 1; // convert to number
313		while ((i < a.length) && (a[i] < v)) {
314			i = i + 1;
315		}
316		if (i < a.length) {
317			idx = i;
318		} else {
319			idx = a.length - 1;
320		}
321	}
322	return idx;
323};
324
325/*
326 * Downsample multi-axes line series with a simple "each N point" alg.
327 */
328SL.downsample_simple = function(s,t){
329	var sampled = [];
330	var sampled_index;
331
332	var step = Math.floor(s[0].length / t) + 1;
333
334	for(var i=0;i<s.length;i++){
335		if ((s[i] !== null) && (typeof s[i] !== 'undefined') && (s[i].length > 0)){
336			sampled[i] = new Array();
337		}
338	}
339	var k;
340	for(var i=0;i<s.length;i++){
341		if ((s[i] !== null) && (typeof s[i] !== 'undefined') && (s[i].length > 0)){
342			k = 0;
343			sampled_index = 0;
344			while(k < s[i].length){
345				sampled[i][sampled_index] = s[i][k];
346				k = k + step;
347				sampled_index++;
348			}
349		}
350	}
351	return sampled;
352};
353
354/*
355 * Downsample multi-axes line series using Largest-Triangle-Three-Buckets alg extended with
356 * master serie approach. Downsampling occurs only on the master serie the other
357 * series choose the same point index. Master serie are the first array.
358 */
359SL.multi_downsample_lttb = function(s,t){
360	// the first series is the master
361	var threshold = t;
362	var data_length = s[0].length;
363
364    if (threshold >= data_length || threshold === 0) {
365        return s; // Nothing to do
366    }
367
368    var sampled = [],
369        sampled_index = 0;
370
371    for (var k=0;k<s.length;k++){
372    	if ((s[k] !== null) && (typeof s[k] !== 'undefined') && (s[k].length > 0)){
373    		sampled[k] = new Array();
374    	}
375    }
376
377    // Bucket size. Leave room for start and end data points
378    var every = (data_length - 2) / (threshold - 2);
379
380    var a = 0,  // Initially a is the first point in the triangle
381        max_area_point,
382        max_area,
383        area,
384        next_a;
385
386	// Always add the first point
387	sampled[0][sampled_index] = s[0][a];
388	sampled[1][sampled_index] = s[1][ a ];
389	for (var k=2;k<s.length;k++){
390		if ((s[k] !== null) && (typeof s[k] !== 'undefined') && (s[k].length > 0)){
391    		sampled[k][ sampled_index ] = s[k][ a ];
392    	}
393   	}
394    sampled_index++;
395
396    for (var i = 0; i < threshold - 2; i++) {
397
398        // Calculate point average for next bucket (containing c)
399        var avg_x = 0,
400            avg_y = 0,
401            avg_range_start  = Math.floor( ( i + 1 ) * every ) + 1,
402            avg_range_end    = Math.floor( ( i + 2 ) * every ) + 1;
403        avg_range_end = avg_range_end < data_length ? avg_range_end : data_length;
404
405        var avg_range_length = avg_range_end - avg_range_start;
406
407        for ( ; avg_range_start<avg_range_end; avg_range_start++ ) {
408          avg_x += s[ 0 ][ avg_range_start ] * 1; // * 1 enforces Number (value may be Date)
409          avg_y += s[ 1 ][ avg_range_start ] * 1;
410        }
411        avg_x /= avg_range_length;
412        avg_y /= avg_range_length;
413
414        // Get the range for this bucket
415        var range_offs = Math.floor( (i + 0) * every ) + 1,
416            range_to   = Math.floor( (i + 1) * every ) + 1;
417
418        // Point a
419        var point_a_x = s[0][ a ] * 1, // Enforce Number (value may be Date)
420            point_a_y = s[ 1 ][ a ] * 1;
421
422        max_area = area = -1;
423
424        for ( ; range_offs < range_to; range_offs++ ) {
425            // Calculate triangle area over three buckets
426            area = Math.abs( ( point_a_x - avg_x ) * ( s[1][ range_offs ] - point_a_y ) -
427                        ( point_a_x - s[ 0 ][ range_offs ] ) * ( avg_y - point_a_y )
428                      ) * 0.5;
429            if ( area > max_area ) {
430                max_area = area;
431                max_area_point = s[1][ range_offs ];
432                next_a = range_offs; // Next a is this b
433            }
434        }
435
436		sampled[0][ sampled_index ] = s[0][next_a];
437        sampled[1][ sampled_index ] = max_area_point; // Pick this point from the bucket
438        for (var k=2;k<s.length;k++){
439        	if ((s[k] !== null) && (typeof s[k] !== 'undefined') && (s[k].length > 0)){
440        		sampled[k][sampled_index] = s[k][next_a];
441        	}
442        }
443        sampled_index++;
444        a = next_a; // This a is the next a (chosen b)
445    }
446
447	// Always add last
448	sampled[0][sampled_index] = s[0][data_length - 1];
449	sampled[1][sampled_index] = s[1][data_length - 1];
450	for (var k=2;k<s.length;k++){
451		if ((s[k] !== null) && (typeof s[k] !== 'undefined') && (s[k].length > 0)){
452    		sampled[k][ sampled_index ] = s[k][ data_length - 1 ];
453    	}
454   	}
455
456	return sampled;
457};
458
459/*
460 * Simple moving average on a array of data.
461 * Invariant return the same sized array.
462 */
463SL.simple_moving_average = function(s,n){
464	var sma = null;
465	var sum = 0;
466	var mv_x = 1;
467	var output = new Array();
468
469	if ((s != null) && (s.length > 0)) {
470		for(var i=0;i<s.length;i++){
471			if (typeof s[i] !== 'undefined' && s[i] !== null) {
472				sum = sum + s[i];
473			}
474			if (mv_x <= n) {
475				sma = Math.ceil(sum / mv_x);
476				mv_x++;
477			} else {
478				sum = sum - s[i - n];
479				sma = Math.ceil(sum / n);
480			}
481			output.push(sma);
482		}
483	}
484	return output;
485};
486
487SL.remove_values = function(s,r){
488	var output = new Array();
489	if ((s != null) && (s.length > 0) && (r != null) && (r.length > 0)){
490		for(var i=0;i<s.length;i++){
491			if (r.indexOf(s[i]) >= 0) {
492				output.push(null);
493			} else {
494				output.push(s[i]);
495			}
496		}
497	}
498	return output;
499};
500
501SL.round = function(v,r){
502	var e = Math.pow(10,r);
503	return (Math.round(v * e) / e);
504};
505
506SL.append_url = function(b,e){
507	var out = b;
508	if (b !== null && typeof b !== 'undefined' &&
509			e !== null && typeof e !== 'undefined') {
510		var tmp_a = (""+b).split('?');
511		if (tmp_a.length > 0) {
512			out = tmp_a[0]+e;
513			if (tmp_a.length > 1) {
514				out = out + "?" + tmp_a[1];
515			}
516		}
517	}
518	return out;
519};
520
521SL.array_factor = function(a,f) {
522	var out = new Array();
523	if (a !== null && typeof a !== 'undefined' &&
524			f !== null){
525		for(var i=0; i<a.length; i++){
526			if (a[i] != null && typeof a[i] !== 'undefined'){
527				out.push(a[i] * f);
528			} else {
529				out.push(null);
530			}
531		}
532	}
533	return out;
534};
535
536SL.show_warning_box = function(c,m) {
537    var d = document.createElement("div");
538    d.setAttribute("class","alert alert-warning alert-dismissible");
539    d.setAttribute("role","alert");
540    d.innerHTML = "<button type=\"button\" class=\"close\" data-dismiss=\"alert\" aria-label=\"Close\"><span aria-hidden=\"true\">&times;</span></button>"+ m;
541    var cj = document.querySelector(c);
542    if (cj !== null && cj !== undefined) {
543        cj.insertBefore(d,cj.firstChild);
544    }
545};
546
547/**
548 * Decimal Adjust.
549 *
550 * @param {String}  type
551 * @param {Number}  value
552 * @param {Integer} exp
553 * @returns {Number}
554 */
555SL.decimal_adjust = function(type,value,exp) {
556    // If exp is undefined
557    if (typeof exp === 'undefined' || +exp === 0) {
558        return Math[type](value);
559    }
560    value = +value;
561    exp = +exp;
562    // If value is not a number
563    if (isNaN(value) || !(typeof exp === 'number' && exp % 1 === 0)) {
564        return NaN;
565    }
566    // If value is negative
567    if (value < 0) {
568        return -decimalAdjust(type, -value, exp);
569    }
570    // Shift
571    value = value.toString().split('e');
572    value = Math[type](+(value[0] + 'e' + (value[1] ? (+value[1] - exp) : -exp)));
573    // Shift back
574    value = value.toString().split('e');
575    return +(value[0] + 'e' + (value[1] ? (+value[1] + exp) : exp));
576};
577
578/*
579 * Clean null values from object properties.
580 * Return a new object, does not change the input.
581 * @param obj [Object] the input oject
582 * @return [Object] the input obj with null and undefined removed
583*/
584SL.clean_null = function(obj) {
585    var newObj = {};
586    Object.keys(obj).forEach(function(key) {
587        if (obj[key] && typeof obj[key] === 'object') newObj[key] = SL.clean_null(obj[key]);
588        else if (obj[key] != null) newObj[key] = obj[key]
589    });
590
591    return newObj;
592};
593
594/*
595 * Fetch content (proxy library between Fetch API or jQuery.ajax)
596 * @param input_url [String] the url
597 * @param input_params [Object] an object (key,value) for input data
598 * @param input_method [String] http method 'GET', 'POST', 'PUT', 'DELETE'
599 * @param response_type [String] the response type 'json' o 'html'
600 * @param onsuccess [Function] success handler onsuccess(data)
601 * @param onerror [Function] error handler onerror(error)
602 */
603SL.fetch_content = function(input_url, input_params, input_method, response_type, onsuccess, onerror) {
604    if (window.fetch) {
605        var ref_url = new URL(input_url);
606        var options = { method: input_method };
607        var cleaned_input_params = SL.clean_null(input_params);
608        if (input_method === 'GET') {
609            ref_url.search = (new URLSearchParams(cleaned_input_params)).toString();
610        } else {
611            //options['headers'] = {'Content-Type': 'application/json'};
612            //options['headers'] = {'Content-Type': 'application/x-www-form-urlencoded'};
613            var form_data = new FormData();
614            Object.keys(cleaned_input_params).forEach(function(key, index){
615                form_data.append(key, cleaned_input_params[key]);
616            });
617            options['body'] = form_data;
618        }
619				if (response_type == 'script') {
620					options['headers'] = {
621						'X-Requested-With': 'XMLHttpRequest',
622						'Accept': 'text/javascript, application/javascript, application/ecmascript, application/x-ecmascript, */*; q=0.01'
623					};
624				}
625        return fetch(ref_url, options).then(function(response) {
626            if (response.ok) {
627                if (response_type === 'json') {
628                    return response.json();
629                } else if (response_type === 'script') {
630                    if ("text/javascript".indexOf(response.headers.get('Content-Type') > -1) ||
631                        "application/javascript".indexOf(response.headers.get('Content-Type') > -1) ||
632                        "application/ecmascript".indexOf(response.headers.get('Content-Type') > -1) ||
633                        "application/x-ecmascript".indexOf(response.headers.get('Content-Type') > -1)) {
634                        return response.text();
635                    } else {
636                        throw new Error('Invalid response');
637                    }
638                } else {
639                    return response.text();
640                }
641            } else {
642                throw new Error(response.statusText);
643            }
644        }).then (function (contentHtml) {
645            if (response_type === 'script') {
646                var t_script = document.createElement( "script" );
647                t_script.text = contentHtml;
648                document.head.appendChild( t_script ).parentNode.removeChild( t_script );
649                onsuccess(null);
650            } else {
651                onsuccess(contentHtml);
652            }
653        }).catch (function (error) {
654            onerror(error.message);
655        });
656    } else if (window.jQuery) {
657        jQuery.ajax({
658            url: input_url,
659            method: input_method,
660            data: SL.clean_null(input_params),
661            dataType: response_type
662        }).done(function(data, t, e) {
663            onsuccess(data);
664        }).fail(function(j, t, e){
665            onerror(t)
666        });
667    } else {
668        onerror('No plugins available');
669    }
670};
671
672/*
673 * goFullScreen
674 * @param element_selector [String] the element that go fullscreen
675 *
676 */
677SL.goFullScreeen = function(element_selector) {
678  var o;
679  if (document.fullscreenEnabled || document.webkitFullscreenEnabled || document.mozFullScreenEnabled || document.msFullscreenEnabled) {
680    o = document.querySelector(element_selector);
681    if (o !== null && o !== 'undefined') {
682      if (o.requestFullscreen) {
683        o.requestFullscreen();
684      } else if (o.webkitRequestFullscreen) {
685        o.webkitRequestFullscreen();
686      } else if (i.mozRequestFullScreen) {
687        o.mozRequestFullScreen();
688      } else if (i.msRequestFullscreen) {
689        o.msRequestFullscreen();
690      }
691    }
692  }
693};
694
695/*
696 * exitFullScreen
697 *
698 */
699SL.exitFullScreen = function() {
700  if (document.exitFullscreen) {
701    document.exitFullscreen();
702  } else if (document.webkitExitFullscreen) {
703    document.webkitExitFullscreen();
704  } else if (document.mozCancelFullScreen) {
705    document.mozCancelFullScreen();
706  } else if (document.msExitFullscreen) {
707    document.msExitFullscreen();
708  }
709};

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.