PageSourceSearch

https://xn--80aacyeau1asblh.xn--p1acf/theme/js/idm.js

js xn--80aacyeau1asblh.xn--p1acf collected 2026-10-09 00:00:17 UTC 27,517 bytes, 982 lines download raw bytes

1if (!$) throw new Error('InteractiveDeliveryMap: jQuery not found');
2if (!ymaps) throw new Error('InteractiveDeliveryMap: Yandex.Maps not found');
3
4/////////////////////////////////////////////////////////////////////////////////////////////
5// Конструктор экземпляра плагина
6/////////////////////////////////////////////////////////////////////////////////////////////
7function InteractiveDeliveryMap(options) {
8  var defaults = {
9    hash: '#map',
10    clear_hash_on_startup: true,
11    pickup_button: true,
12    cargo: [],
13    onPickupsReady: undefined,
14    onCalcDeliveryFinish: undefined,
15    onCalcDeliveryError: undefined,
16    onCalcDeliveryAddressFound: undefined,
17    onCalcPickupFinish: undefined,
18    onCalcPickupError: undefined,
19    onPickupSelect: undefined,
20    onPickupCity: undefined,
21    onPickupCityError: undefined,
22  };
23  this.options = $.extend({}, defaults, options);
24  this.mapDelivery = undefined;
25  this.address = undefined;
26  this.placemark = undefined;
27  this.delivery_areas = [];
28  this.loadDeliveryJSON();
29  this.lastAreaSign = undefined;
30  this.cityPolygon = undefined;
31  this.suburb_path = undefined;
32  this.pickup_first_run = true;
33  this.need_set_bounds = true;
34
35  if (this.options.clear_hash_on_startup && location.hash == this.options.hash)
36    history.replaceState(undefined, document.title, ' ');
37
38  // Создаем DOM
39  var template =
40    '<div class="idm-modal-background">' +
41    '  <div class="container idm-modal-container">' +
42    '	<div class="idm-modal idm-spinner">' +
43    '	  <div id="pickupMapContainer"></div>' +
44    '	  <div id="deliveryMapContainer"></div>' +
45    '	</div>' +
46    '	<div class="idm-modal-close idm-modal-close-desktop">&times;</div>' +
47    '  </div>' +
48    '</div>' +
49    '<div class="idm-modal-close idm-modal-close-mobile">&times;</div>';
50  this.element = $(template).appendTo('body');
51
52
53  $(window).on('hashchange', $.proxy(this.onHashChange, this));
54  $('.idm-modal-close').on('click', $.proxy(this.close, this));
55  $('.idm-modal-background').on('mousedown', $.proxy(this.onBackgroundClick, this));
56  $(document).on('keyup', $.proxy(this.onKeyPress, this));
57
58  $(this.element).find('.idm-modal > #pickupMapContainer').yadc({
59    site_id: 2,
60    height: '100%',
61    //map_сenter: [lat, lon],
62    auto_geolocation: true,
63    mark_center: true,
64    smart_zoom: false,
65    manual_pvz_adding: true,
66    geolocation_provider: 'auto',
67    auto_open_custom_pickup: false,
68    map_scale: 18,
69    pickup_button: this.options.pickup_button,
70    onReady: $.proxy(this.onYADCReady, this),
71    onPickupSelect: $.proxy(this.onPickupSelectClick, this),
72    cargo: this.options.cargo,
73  });
74
75}
76
77/////////////////////////////////////////////////////////////////////////////////////////////
78InteractiveDeliveryMap.prototype.onHashChange = function() {
79  if (location.hash == this.options.hash) {
80    $('.idm-modal-background').addClass('active');
81  } else if ($('.idm-modal-background').hasClass('active')) {
82    $('.idm-modal-background').removeClass('active');
83  }
84}
85
86/////////////////////////////////////////////////////////////////////////////////////////////
87InteractiveDeliveryMap.prototype.show = function() {
88  if (location.hash != this.options.hash) {
89    window.history.pushState({}, '', this.options.hash);
90    this.onHashChange();
91  }
92}
93
94/////////////////////////////////////////////////////////////////////////////////////////////
95InteractiveDeliveryMap.prototype.showDelivery = function(address, needCalc = true) {
96  var instance = this;
97  $(this.element).find('.idm-modal > div').hide();
98  this.show();
99  if (needCalc)
100    instance.calcDelivery(address);
101  else
102    instance.displayDeliveryMap();
103}
104
105/////////////////////////////////////////////////////////////////////////////////////////////
106InteractiveDeliveryMap.prototype.showPickup = function(pvz_id) {
107  var instance = this;
108  $(instance.element).find('.idm-modal > div').hide(0);
109  instance.show();
110  var el = $(instance.element).find('.idm-modal > #pickupMapContainer');
111  setTimeout(function() {
112    // Если первое открытие - добавляем ПВЗ в ObjectManager (долгая операция) пока в окне крутится спиннер
113    if (instance.pickup_first_run) {
114      el.yadc('addPVZToObjectManager');
115      instance.pickup_first_run = false;
116    }
117    // Отображаем yadc
118    el.show(0, function(){
119      el.yadc('onMapSizeChange');
120      if (instance.need_set_bounds) {
121        setTimeout(function(){
122          el.yadc('smartBounds');
123          instance.need_set_bounds = false;
124        }, 100);
125      } else
126        el.yadc('updateMap');
127      if (pvz_id)
128        el.yadc('openPVZBaloon', pvz_id);
129      el.yadc('updateBalloon');
130      el.yadc('updateList');
131    });
132  }, 100);
133}
134
135/////////////////////////////////////////////////////////////////////////////////////////////
136InteractiveDeliveryMap.prototype.displayDeliveryMap = function() {
137  $(this.element).find('.idm-modal > #deliveryMapContainer').show();
138  if (this.mapDelivery !== undefined)
139    this.mapDelivery.container.fitToViewport();
140}
141
142/////////////////////////////////////////////////////////////////////////////////////////////
143InteractiveDeliveryMap.prototype.close = function() {
144  if (location.hash == this.options.hash) {
145    window.history.go(-1);
146  }
147}
148
149/////////////////////////////////////////////////////////////////////////////////////////////
150InteractiveDeliveryMap.prototype.onBackgroundClick = function(e) {
151  if ($(e.target).hasClass('idm-modal-background') || $(e.target).hasClass('idm-modal-container') ) {
152    this.close();
153    e.stopPropagation();
154    e.preventDefault();
155  }
156}
157
158/////////////////////////////////////////////////////////////////////////////////////////////
159InteractiveDeliveryMap.prototype.onKeyPress = function(e) {
160  if ((e.key === "Escape") && ($('.idm-modal-background').hasClass('active')))
161    this.close();
162}
163
164/////////////////////////////////////////////////////////////////////////////////////////////
165InteractiveDeliveryMap.prototype.loadDeliveryJSON = function() {
166  var instance = this;
167  $.ajax({
168    url: '/products/delivery.json',
169    dataType: 'json',
170  }).done(function(data) {
171    instance.delivery_areas = data;
172  }).fail(function(jqXHR, textStatus, errorThrown){
173    console.log(errorThrown);
174  });
175}
176
177/////////////////////////////////////////////////////////////////////////////////////////////
178InteractiveDeliveryMap.prototype.setDeliveryMapCenter = function(coord) {
179  if (this.mapDelivery === undefined) {
180    var el = $(this.element).find('#deliveryMapContainer')[0];
181    this.mapDelivery = new ymaps.Map(el, {center: coord, zoom: 17}, {suppressMapOpenBlock: true});
182  } else
183    this.mapDelivery.setCenter(coord);
184}
185
186/////////////////////////////////////////////////////////////////////////////////////////////
187InteractiveDeliveryMap.prototype.setDeliveryPlacemark = function(coord, loc) {
188  var preset = (loc !== undefined) ? 'islands#blueGovernmentIcon' : 'islands#redStretchyIcon';
189  if (!this.placemark) {
190
191    this.placemark = new ymaps.Placemark(coord, {
192      hintContent: "Перетащите метку для уточнения адреса доставки",
193      iconContent: "Нет доставки",
194    }, {
195      preset: preset,
196      draggable: false
197    });
198
199    this.mapDelivery.geoObjects.add(this.placemark);
200
201  } else {
202    this.placemark.geometry.setCoordinates(coord);
203    this.placemark.options.set('preset', preset);
204  }
205}
206
207/////////////////////////////////////////////////////////////////////////////////////////////
208InteractiveDeliveryMap.prototype.getDeliveryGeoLocator = function(address) {
209  // COMMENT LINE BELOW!!!!
210  //return   ymaps.geocode([55.76, 37.64], {}); // Москва
211  //return   ymaps.geocode([55.91842865097746, 37.67366708755087], {}); // Мытищи
212  //return   ymaps.geocode([54.0503060853841, 58.02375854489996], {}); //Белорецк
213  //return   ymaps.geocode([47.228639, 39.715958], {}); // Ростов-на-Дону
214  //return   ymaps.geocode([54.0503060853841, 58.02375854489996], {}); // Белорецк
215  //return   ymaps.geocode([62.912986115138374, 73.85093749999923], {}); // Ханты-манты
216  //return   ymaps.geocode([63.80332624792426, 85.8040624999952], {}); // Красноярский край
217  //return   ymaps.geocode([47.122093, 39.729562], {}); // Ростовская обл.
218  //return   ymaps.geocode([59.797622, 30.639642], {}); // Ленинградская обл.
219  //
220  if (address === undefined || address === '')
221    return ymaps.geolocation.get({provider: 'auto',});
222  else
223    return ymaps.geocode(address);
224}
225
226/////////////////////////////////////////////////////////////////////////////////////////////
227InteractiveDeliveryMap.prototype.findDeliveryLocation = function(addr, coord) {
228  var instance = this;
229  for (var i = 0; i < instance.delivery_areas.length; i++) {
230    if (addr.toLowerCase().indexOf(instance.delivery_areas[i].sign.toLowerCase()) >= 0) {
231      return i;
232    }
233  };
234  return undefined;
235}
236
237/////////////////////////////////////////////////////////////////////////////////////////////
238//  Рассчёт стоимости доставки до адреса
239/////////////////////////////////////////////////////////////////////////////////////////////
240InteractiveDeliveryMap.prototype.calcDelivery = function(address, force = false) {
241
242  // Если адрес не поменялся, нечего делать
243  if (this.address !== undefined && address == this.address && !force) {
244    this.displayDeliveryMap();
245    this.calcDeliveryFinish();
246    return;
247  }
248
249  var instance = this;
250  $(instance.element).find('.idm-modal > #deliveryMapContainer').hide();
251
252  // Уничтожаем ранее пролаженный маршрут
253  if (instance.suburb_path) {
254    instance.mapDelivery.geoObjects.remove(instance.suburb_path);
255    instance.suburb_path = undefined;
256  }
257
258  instance.getDeliveryGeoLocator(address).then(function(result) {
259
260    if (result.geoObjects.getLength() == 0)
261      instance.calcDeliveryError("Could not geolocate '" + address + "'");
262
263    var coord = result.geoObjects.get(0).geometry.getCoordinates();
264    var addr = result.geoObjects.get(0).properties.get('text');
265    latitude = coord[0];
266    longitude = coord[1];
267    //getCity(coord[0], coord[1]);
268    //getDelivery(coord[0], coord[1], addr);
269
270    // Оповещаем о нахождении адреса, если геолоцировали без адреса
271    if (address === undefined || address == '') {
272      if (instance.options.onCalcDeliveryAddressFound) {
273        res = instance.options.onCalcDeliveryAddressFound(addr, coord);
274        // Нам вернули обработанный адрес?
275        if (res !== undefined && typeof res === 'string')
276          address = res;
277        else
278          address = addr;
279      } else {
280        address = addr;
281      }
282    }
283
284    instance.setDeliveryMapCenter(coord);
285    instance.displayDeliveryMap();
286
287    // Ищем по JSON-файлу регион доставки
288    var loc = instance.findDeliveryLocation(addr, coord);
289    if (loc === undefined) {
290      // Нет доставки
291      instance.calcDeliveryFinish(-1);
292    } else if (instance.delivery_areas[loc].city) {
293      // Доставка по городу
294      instance.calcDeliveryFinish(0);
295      instance.removeCityPolygon();
296    } else {
297      // Доставка за город
298      instance.calcSuburbDistance(coord, instance.delivery_areas[loc].city_center, instance.delivery_areas[loc].city_area, instance.delivery_areas[loc].sign);
299    }
300
301    instance.setDeliveryPlacemark(coord, loc);
302
303    instance.address = address;
304
305  }, function(err) {
306    // Ошибка геолокации
307    instance.calcDeliveryError(err.message);
308  });
309}
310
311/////////////////////////////////////////////////////////////////////////////////////////////
312InteractiveDeliveryMap.prototype.calcSuburbDistance = function(userCoord, cityCenterCoord, cityArea, areaSign, callback) {
313    var instance = this;
314    // Создаем полигон с границами города и наносим на карту.
315    // Без нанесения на карту многие функции работать не будут
316    if (instance.lastAreaSign !== areaSign) {
317      instance.removeCityPolygon();
318      instance.cityPolygon = new ymaps.Polygon([cityArea]);
319      instance.lastAreaSign = areaSign;
320      //cityPolygon.options.set('visible', false);
321      instance.mapDelivery.geoObjects.add(instance.cityPolygon);
322    }
323
324    // Строим маршрут от центра города до пользователя
325    ymaps.route([cityCenterCoord, userCoord], {avoidTrafficJams: false, multiRoute: true, results: 10, reverseGeocoding: false}).then(function (multiroute){
326
327      var dist, min_dist, route;
328      multiroute.getRoutes().each(function(r) {
329        dist = r.getPaths().get(0).properties.get("distance").value;
330        if (min_dist === undefined || min_dist > dist) {
331          min_dist = dist;
332          route = r;
333        }
334      });
335
336      //var coodinates = route.getPaths().get(0).geometry.getCoordinates();
337      var coodinates = route.getPaths().get(0).properties.get("coordinates");
338
339      var points = coodinates.filter(function (el, i, arr) {
340        return robustPointInPolygon(cityArea, el) > 0;
341      });
342
343      instance.suburb_path = new ymaps.Polyline(points, {}, {strokeColor: '#7ed83f', strokeWidth: 5});
344      instance.mapDelivery.geoObjects.add(instance.suburb_path);
345
346      dist = instance.suburb_path.geometry.getDistance();
347      instance.calcDeliveryFinish(1, dist);
348
349    }, function (error){
350      // Ошибка при построении маршрута
351      instance.calcDeliveryError(error.message);
352    });
353}
354
355/////////////////////////////////////////////////////////////////////////////////////////////
356InteractiveDeliveryMap.prototype.removeCityPolygon = function() {
357  if (this.cityPolygon !== undefined) {
358    this.mapDelivery.geoObjects.remove(this.cityPolygon);
359    this.cityPolygon = undefined;
360    this.lastAreaSign = undefined;
361  }
362}
363
364/////////////////////////////////////////////////////////////////////////////////////////////
365InteractiveDeliveryMap.prototype.calcDeliveryError = function(err) {
366  if (this.options.onCalcDeliveryError) {
367    this.options.onCalcDeliveryError(err);
368  }
369}
370
371/////////////////////////////////////////////////////////////////////////////////////////////
372InteractiveDeliveryMap.prototype.calcDeliveryFinish = function(delivery_location, distance) {
373  if (this.options.onCalcDeliveryFinish) {
374    this.options.onCalcDeliveryFinish(delivery_location, distance);
375  }
376}
377
378/////////////////////////////////////////////////////////////////////////////////////////////
379InteractiveDeliveryMap.prototype.onPickupSelectClick = function(pvz) {
380  if (this.options.onPickupSelect) {
381    this.options.onPickupSelect(pvz);
382  }
383}
384
385/////////////////////////////////////////////////////////////////////////////////////////////
386InteractiveDeliveryMap.prototype.setCargo = function(cargo) {
387  var instance = this;
388
389  instance.options.cargo = cargo;
390
391  el = $(instance.element).find('.idm-modal > #pickupMapContainer');
392  if ($.data(el[0], 'yadc'))
393    el.yadc('setCargo', cargo);
394}
395
396/////////////////////////////////////////////////////////////////////////////////////////////
397InteractiveDeliveryMap.prototype.calculatePickupPrice = function(pvz_id, cargo) {
398  var instance = this;
399  $.ajax({
400    url: '//api.yadc-js.ru/calculate.json',
401    type: 'POST',
402    cache: false,
403    data: {
404      'site_id': 2,
405      'pickup_id': pvz_id,
406      'cargo': JSON.stringify(cargo || instance.cargo),
407    },
408    dataType: 'json',
409  }).done(function(data) {
410    if (instance.options.onCalcPickupFinish) {
411      instance.options.onCalcPickupFinish(data.price);
412    }
413  }).fail(function (jqXHR, textStatus) {
414    console.log('Error calculatePickupPrice() - ' + textStatus);
415    if (instance.options.onCalcPickupError) {
416      instance.options.onCalcPickupError(textStatus);
417    }
418  });
419}
420
421/////////////////////////////////////////////////////////////////////////////////////////////
422InteractiveDeliveryMap.prototype.onYADCReady = function() {
423  if (this.options.onPickupsReady) {
424    this.options.onPickupsReady();
425  }
426}
427
428/////////////////////////////////////////////////////////////////////////////////////////////
429InteractiveDeliveryMap.prototype.getPickupCityByCoord = function(coord, cargo) {
430  var instance = this;
431  $.ajax({
432    url: '//api.yadc-js.ru/city.json',
433    type: 'POST',
434    cache: false,
435	dataType: 'json',
436    data: {
437      'site_id': 2,
438      'latitude': coord[0],
439      'longitude': coord[1],
440      'cargo': JSON.stringify([cargo || {}]),
441    },
442    }).done(function(data) {
443      if (instance.options.onPickupCity)
444        instance.options.onPickupCity(data);
445    }).fail(function(){
446      if (instance.options.onPickupCityError)
447        instance.options.onPickupCityError();
448    });
449}
450
451/////////////////////////////////////////////////////////////////////////////////////////////
452InteractiveDeliveryMap.prototype.getCityPickups = function(city, cargo) {
453  var instance = this;
454
455  // Определяем координаты города
456  instance.getDeliveryGeoLocator(city).then(function(result) {
457
458    if (result.geoObjects.getLength() == 0)
459      instance.calcDeliveryError("Could not geolocate '" + address + "'");
460
461    var coord = result.geoObjects.get(0).geometry.getCoordinates();
462    var el = $(instance.element).find('.idm-modal > #pickupMapContainer');
463    el.yadc('setMapCenter', coord, 18);
464    instance.need_set_bounds = true;
465  }, function(err) {
466    // Ошибка геолокации
467  });
468
469  // Запршиваем сколько ПВЗ в городе и стоимость выдачи
470  $.ajax({
471    url: '//api.yadc-js.ru/city.json',
472    type: 'POST',
473    cache: false,
474	dataType: 'json',
475    data: {
476      'site_id': 2,
477      'city': city,
478      'cargo': JSON.stringify([cargo || {}]),
479    },
480    }).done(function(data) {
481      if (instance.options.onPickupCity)
482        instance.options.onPickupCity(data);
483    }).fail(function(){
484      if (instance.options.onPickupCityError)
485        instance.options.onPickupCityError();
486    });
487}
488
489/////////////////////////////////////////////////////////////////////////////////////////////
490InteractiveDeliveryMap.prototype.getPickupCitiesList = function() {
491  var instance = this;
492  $.ajax({
493    url: '//api.yadc-js.ru/cities.json',
494    type: 'POST',
495    cache: false,
496	dataType: 'json',
497    data: {
498      site_id: 2,
499    },
500    }).done(function(data) {
501      if (instance.options.onPickupCitiesList)
502        instance.options.onPickupCitiesList(data);
503    }).fail(function(){
504      if (instance.options.onPickupCitiesListError)
505        instance.options.onPickupCitiesListtError();
506    });
507}
508
509/////////////////////////////////////////////////////////////////////////
510//   Taken from https://github.com/mikolalysenko/robust-point-in-polygon
511/////////////////////////////////////////////////////////////////////////
512function robustPointInPolygon(vs, point) {
513  var x = point[0]
514  var y = point[1]
515  var n = vs.length
516  var inside = 1
517  var lim = n
518  for(var i = 0, j = n-1; i<lim; j=i++) {
519    var a = vs[i]
520    var b = vs[j]
521    var yi = a[1]
522    var yj = b[1]
523    if(yj < yi) {
524      if(yj < y && y < yi) {
525        var s = orient(a, b, point)
526        if(s === 0) {
527          return 0
528        } else {
529          inside ^= (0 < s)|0
530        }
531      } else if(y === yi) {
532        var c = vs[(i+1)%n]
533        var yk = c[1]
534        if(yi < yk) {
535          var s = orient(a, b, point)
536          if(s === 0) {
537            return 0
538          } else {
539            inside ^= (0 < s)|0
540          }
541        }
542      }
543    } else if(yi < yj) {
544      if(yi < y && y < yj) {
545        var s = orient(a, b, point)
546        if(s === 0) {
547          return 0
548        } else {
549          inside ^= (s < 0)|0
550        }
551      } else if(y === yi) {
552        var c = vs[(i+1)%n]
553        var yk = c[1]
554        if(yk < yi) {
555          var s = orient(a, b, point)
556          if(s === 0) {
557            return 0
558          } else {
559            inside ^= (s < 0)|0
560          }
561        }
562      }
563    } else if(y === yi) {
564      var x0 = Math.min(a[0], b[0])
565      var x1 = Math.max(a[0], b[0])
566      if(i === 0) {
567        while(j>0) {
568          var k = (j+n-1)%n
569          var p = vs[k]
570          if(p[1] !== y) {
571            break
572          }
573          var px = p[0]
574          x0 = Math.min(x0, px)
575          x1 = Math.max(x1, px)
576          j = k
577        }
578        if(j === 0) {
579          if(x0 <= x && x <= x1) {
580            return 0
581          }
582          return 1
583        }
584        lim = j+1
585      }
586      var y0 = vs[(j+n-1)%n][1]
587      while(i+1<lim) {
588        var p = vs[i+1]
589        if(p[1] !== y) {
590          break
591        }
592        var px = p[0]
593        x0 = Math.min(x0, px)
594        x1 = Math.max(x1, px)
595        i += 1
596      }
597      if(x0 <= x && x <= x1) {
598        return 0
599      }
600      var y1 = vs[(i+1)%n][1]
601      if(x < x0 && (y0 < y !== y1 < y)) {
602        inside ^= 1
603      }
604    }
605  }
606  return 2 * inside - 1
607}
608
609/////////////////////////////////////////////////////////////////////////
610var EPSILON     = 1.1102230246251565e-16
611var ERRBOUND3   = (3.0 + 16.0 * EPSILON) * EPSILON
612
613function orient(a, b, c) {
614    var l = (a[1] - c[1]) * (b[0] - c[0])
615    var r = (a[0] - c[0]) * (b[1] - c[1])
616    var det = l - r
617    var s
618    if(l > 0) {
619      if(r <= 0) {
620        return det
621      } else {
622        s = l + r
623      }
624    } else if(l < 0) {
625      if(r >= 0) {
626        return det
627      } else {
628        s = -(l + r)
629      }
630    } else {
631      return det
632    }
633    var tol = ERRBOUND3 * s
634    if(det >= tol || det <= -tol) {
635      return det
636    }
637    return orientation3Exact(a, b, c)
638}
639
640/////////////////////////////////////////////////////////////////////////
641function orientation3Exact(m0, m1, m2) {
642    var p = linearExpansionSum(linearExpansionSum(twoProduct(m1[1], m2[0]), twoProduct(-m2[1], m1[0])), linearExpansionSum(twoProduct(m0[1], m1[0]), twoProduct(-m1[1], m0[0])))
643    var n = linearExpansionSum(twoProduct(m0[1], m2[0]), twoProduct(-m2[1], m0[0]))
644    var d = robustSubtract(p, n)
645    return d[d.length - 1]
646  }
647
648//Easy case: Add two scalars
649function scalarScalar(a, b) {
650  var x = a + b
651  var bv = x - a
652  var av = x - bv
653  var br = b - bv
654  var ar = a - av
655  var y = ar + br
656  if(y) {
657    return [y, x]
658  }
659  return [x]
660}
661
662function linearExpansionSum(e, f) {
663  var ne = e.length|0
664  var nf = f.length|0
665  if(ne === 1 && nf === 1) {
666    return scalarScalar(e[0], f[0])
667  }
668  var n = ne + nf
669  var g = new Array(n)
670  var count = 0
671  var eptr = 0
672  var fptr = 0
673  var abs = Math.abs
674  var ei = e[eptr]
675  var ea = abs(ei)
676  var fi = f[fptr]
677  var fa = abs(fi)
678  var a, b
679  if(ea < fa) {
680    b = ei
681    eptr += 1
682    if(eptr < ne) {
683      ei = e[eptr]
684      ea = abs(ei)
685    }
686  } else {
687    b = fi
688    fptr += 1
689    if(fptr < nf) {
690      fi = f[fptr]
691      fa = abs(fi)
692    }
693  }
694  if((eptr < ne && ea < fa) || (fptr >= nf)) {
695    a = ei
696    eptr += 1
697    if(eptr < ne) {
698      ei = e[eptr]
699      ea = abs(ei)
700    }
701  } else {
702    a = fi
703    fptr += 1
704    if(fptr < nf) {
705      fi = f[fptr]
706      fa = abs(fi)
707    }
708  }
709  var x = a + b
710  var bv = x - a
711  var y = b - bv
712  var q0 = y
713  var q1 = x
714  var _x, _bv, _av, _br, _ar
715  while(eptr < ne && fptr < nf) {
716    if(ea < fa) {
717      a = ei
718      eptr += 1
719      if(eptr < ne) {
720        ei = e[eptr]
721        ea = abs(ei)
722      }
723    } else {
724      a = fi
725      fptr += 1
726      if(fptr < nf) {
727        fi = f[fptr]
728        fa = abs(fi)
729      }
730    }
731    b = q0
732    x = a + b
733    bv = x - a
734    y = b - bv
735    if(y) {
736      g[count++] = y
737    }
738    _x = q1 + x
739    _bv = _x - q1
740    _av = _x - _bv
741    _br = x - _bv
742    _ar = q1 - _av
743    q0 = _ar + _br
744    q1 = _x
745  }
746  while(eptr < ne) {
747    a = ei
748    b = q0
749    x = a + b
750    bv = x - a
751    y = b - bv
752    if(y) {
753      g[count++] = y
754    }
755    _x = q1 + x
756    _bv = _x - q1
757    _av = _x - _bv
758    _br = x - _bv
759    _ar = q1 - _av
760    q0 = _ar + _br
761    q1 = _x
762    eptr += 1
763    if(eptr < ne) {
764      ei = e[eptr]
765    }
766  }
767  while(fptr < nf) {
768    a = fi
769    b = q0
770    x = a + b
771    bv = x - a
772    y = b - bv
773    if(y) {
774      g[count++] = y
775    }
776    _x = q1 + x
777    _bv = _x - q1
778    _av = _x - _bv
779    _br = x - _bv
780    _ar = q1 - _av
781    q0 = _ar + _br
782    q1 = _x
783    fptr += 1
784    if(fptr < nf) {
785      fi = f[fptr]
786    }
787  }
788  if(q0) {
789    g[count++] = q0
790  }
791  if(q1) {
792    g[count++] = q1
793  }
794  if(!count) {
795    g[count++] = 0.0
796  }
797  g.length = count
798  return g
799}
800
801var SPLITTER = +(Math.pow(2, 27) + 1.0)
802
803function twoProduct(a, b, result) {
804  var x = a * b
805
806  var c = SPLITTER * a
807  var abig = c - a
808  var ahi = c - abig
809  var alo = a - ahi
810
811  var d = SPLITTER * b
812  var bbig = d - b
813  var bhi = d - bbig
814  var blo = b - bhi
815
816  var err1 = x - (ahi * bhi)
817  var err2 = err1 - (alo * bhi)
818  var err3 = err2 - (ahi * blo)
819
820  var y = alo * blo - err3
821
822  if(result) {
823    result[0] = y
824    result[1] = x
825    return result
826  }
827
828  return [ y, x ]
829}
830
831//Easy case: Add two scalars
832function scalarScalar(a, b) {
833  var x = a + b
834  var bv = x - a
835  var av = x - bv
836  var br = b - bv
837  var ar = a - av
838  var y = ar + br
839  if(y) {
840    return [y, x]
841  }
842  return [x]
843}
844
845function robustSubtract(e, f) {
846  var ne = e.length|0
847  var nf = f.length|0
848  if(ne === 1 && nf === 1) {
849    return scalarScalar(e[0], -f[0])
850  }
851  var n = ne + nf
852  var g = new Array(n)
853  var count = 0
854  var eptr = 0
855  var fptr = 0
856  var abs = Math.abs
857  var ei = e[eptr]
858  var ea = abs(ei)
859  var fi = -f[fptr]
860  var fa = abs(fi)
861  var a, b
862  if(ea < fa) {
863    b = ei
864    eptr += 1
865    if(eptr < ne) {
866      ei = e[eptr]
867      ea = abs(ei)
868    }
869  } else {
870    b = fi
871    fptr += 1
872    if(fptr < nf) {
873      fi = -f[fptr]
874      fa = abs(fi)
875    }
876  }
877  if((eptr < ne && ea < fa) || (fptr >= nf)) {
878    a = ei
879    eptr += 1
880    if(eptr < ne) {
881      ei = e[eptr]
882      ea = abs(ei)
883    }
884  } else {
885    a = fi
886    fptr += 1
887    if(fptr < nf) {
888      fi = -f[fptr]
889      fa = abs(fi)
890    }
891  }
892  var x = a + b
893  var bv = x - a
894  var y = b - bv
895  var q0 = y
896  var q1 = x
897  var _x, _bv, _av, _br, _ar
898  while(eptr < ne && fptr < nf) {
899    if(ea < fa) {
900      a = ei
901      eptr += 1
902      if(eptr < ne) {
903        ei = e[eptr]
904        ea = abs(ei)
905      }
906    } else {
907      a = fi
908      fptr += 1
909      if(fptr < nf) {
910        fi = -f[fptr]
911        fa = abs(fi)
912      }
913    }
914    b = q0
915    x = a + b
916    bv = x - a
917    y = b - bv
918    if(y) {
919      g[count++] = y
920    }
921    _x = q1 + x
922    _bv = _x - q1
923    _av = _x - _bv
924    _br = x - _bv
925    _ar = q1 - _av
926    q0 = _ar + _br
927    q1 = _x
928  }
929  while(eptr < ne) {
930    a = ei
931    b = q0
932    x = a + b
933    bv = x - a
934    y = b - bv
935    if(y) {
936      g[count++] = y
937    }
938    _x = q1 + x
939    _bv = _x - q1
940    _av = _x - _bv
941    _br = x - _bv
942    _ar = q1 - _av
943    q0 = _ar + _br
944    q1 = _x
945    eptr += 1
946    if(eptr < ne) {
947      ei = e[eptr]
948    }
949  }
950  while(fptr < nf) {
951    a = fi
952    b = q0
953    x = a + b
954    bv = x - a
955    y = b - bv
956    if(y) {
957      g[count++] = y
958    }
959    _x = q1 + x
960    _bv = _x - q1
961    _av = _x - _bv
962    _br = x - _bv
963    _ar = q1 - _av
964    q0 = _ar + _br
965    q1 = _x
966    fptr += 1
967    if(fptr < nf) {
968      fi = -f[fptr]
969    }
970  }
971  if(q0) {
972    g[count++] = q0
973  }
974  if(q1) {
975    g[count++] = q1
976  }
977  if(!count) {
978    g[count++] = 0.0
979  }
980  g.length = count
981  return g
982}

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.