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\">×</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.