PageSourceSearch

https://hangar.link/releases/20260929083919-746fcbb/hangar-link-connect.js

js hangar.link collected 2026-10-01 23:45:58 UTC 23,347 bytes, 776 lines download raw bytes

1
2function parsePledgesPage(pageData, page) {
3  var parsed = {
4    isLastPage: false,
5    pledges: [],
6    errors: [],
7  };
8
9  // if mocking only fetch a single page.
10  if (page == -1) {
11    parsed.isLastPage = true;
12  }
13
14  var parser = new DOMParser();
15  var htmlDoc = parser.parseFromString(pageData, 'text/html');
16
17  var raquoRes = htmlDoc.getElementsByClassName('raquo')[0];
18  if (!raquoRes) {
19    parsed.isLastPage = true;
20  } 
21
22  var singleline = pageData.replace(/\r?\n|\r/g, " ").replace(/\s+/g, " ");
23  if (/Your hangar is empty/.test(singleline)) {
24    parsed.isLastPage = true;
25    return parsed;
26  }
27
28  var itemsUlBlock = htmlDoc.getElementsByClassName('list-items')[0];
29  if (!itemsUlBlock) {
30    parsed.errors.push("items block not found");
31    return parsed;
32  }
33
34  var items = itemsUlBlock.children;
35  for (var i = 0; i < items.length; i++) {
36    p = parseItem(items[i], page);
37    if (!!p) {
38      parsed.pledges.push(p);
39    } else {
40      console.log({errorParsingPlege: items[i]});
41    }
42  }
43
44  return parsed;
45}
46
47function parsePledgeItem(item) {
48  var itemCustomName = "";
49  {
50    var itemCustomNameNode = item.getElementsByClassName('custom-name-text');
51    if (itemCustomNameNode.length == 1) {
52      itemCustomName = itemCustomNameNode[0].innerText.trim();
53    }
54  }
55  
56  var itemTitle = "";
57  {
58    var itemTitleNode = item.getElementsByClassName('title');
59    if (itemTitleNode.length == 1) {
60      itemTitle = itemTitleNode[0].innerText.trim();
61    }
62  }
63    
64  var itemKind = "";
65  {
66    var itemKindNode = item.getElementsByClassName('kind');
67    if (itemKindNode.length == 1) {
68      itemKind = itemKindNode[0].innerText.trim();
69    }
70  }
71
72  var itemLiner = "";
73  {
74    var itemLinerNode = item.getElementsByClassName('liner');
75    if (itemLinerNode.length == 1) {
76      itemLiner = itemLinerNode[0].innerText.trim();
77    }
78  }
79  
80  return {
81    title: itemTitle,
82    kind: itemKind,
83    liner: itemLiner,
84    itemCustomName: itemCustomName,
85  };
86}
87
88function parseAlsoContainsItem(alsoContainsNode) {
89  var alsoContains = [];
90  {
91    var alsoContainsItemsNode = alsoContainsNode.getElementsByClassName('item');
92    for (var j = 0; j < alsoContainsItemsNode.length; j++) {true
93      var titleNodes = alsoContainsItemsNode[j].getElementsByClassName('title');
94      if (titleNodes.length == 1) {
95        alsoContains.push({"title": titleNodes[0].innerText.trim()});
96        }
97      }
98  }
99  return alsoContains;
100}
101
102function parseItem(item, page) {
103  var pledgeId = "";
104  {
105    var jsPledgeIdNode = item.getElementsByClassName('js-pledge-id');
106    if (jsPledgeIdNode.length == 1) {
107      pledgeId = jsPledgeIdNode[0].value.trim();
108    } else {
109      console.log({ error: "pledgeId not found" });
110      return null;
111    }
112  }
113
114
115  var pledgeName = "";
116  {
117    var jsPledgeNameNode = item.getElementsByClassName('js-pledge-name');
118    if (jsPledgeNameNode.length == 1) {
119      pledgeName = jsPledgeNameNode[0].value.trim();
120    } else {
121      console.log({ error: "pledgeName not found" });
122      return null;
123    }
124  }
125  //strip out coupon codes
126  pledgeName = pledgeName.split(":")[0];
127
128  var pledgeValue = "";
129  {
130    var jsPledgeValueNode = item.getElementsByClassName('js-pledge-value');
131    if (jsPledgeValueNode.length == 1) {
132      pledgeValue = jsPledgeValueNode[0].value.trim();
133    }
134  }
135
136  var pledgeConfigurationValue = "";
137  {
138    var jsPledgeConfigurationValueNode = item.getElementsByClassName('js-pledge-configuration-value');
139    if (jsPledgeConfigurationValueNode.length == 1) {
140      pledgeConfigurationValue = jsPledgeConfigurationValueNode[0].value.trim();
141    }
142  }
143
144  var date = "";
145  {
146    var dateNode = item.getElementsByClassName('date-col');
147    if (dateNode.length == 1) {
148      date = dateNode[0].innerText.trim().replace(/\r?\n|\r/g, "").replace(/Created\:/g, "");
149    } else {
150      console.log({ error: "dateNode not found" });
151      return null;
152    }
153  }
154
155  var contains = "";
156  {
157    var containsNode = item.getElementsByClassName('items-col');
158    if (containsNode.length == 1) {
159      contains = containsNode[0].innerText.trim().replace(/\r?\n|\r/g, "").replace(/Contains\:/g, "");;
160    }
161  }
162
163  var pledgeItems = [];
164  {
165    var itemsParentNode = item.getElementsByClassName('with-images');
166    if (itemsParentNode.length == 1) {
167      var pledgeItemsNode = itemsParentNode[0].getElementsByClassName('item');
168      for (var y = 0; y < pledgeItemsNode.length; y++) {
169        pledgeItem = parsePledgeItem(pledgeItemsNode[y]);
170        if (!!pledgeItem) {
171          pledgeItems.push(pledgeItem);
172        } else {
173          console.log({error: "error parsing pledge item"});
174          return null;
175        }
176      }
177    }
178  }
179
180  var pledgeAlsoContains = [];
181    {
182      var alsoContainsNode = item.getElementsByClassName('without-images');
183      if (alsoContainsNode.length == 1) {
184      pledgeAlsoContains = parseAlsoContainsItem(alsoContainsNode[0]);
185    }
186    }
187
188  var pledgeGiftable = false;
189  var pledgeGiftableNode = item.getElementsByClassName('js-gift')[0];
190  if (!!pledgeGiftableNode) {
191      pledgeGiftable = true;
192  }
193
194  var pledgeMeltable = false;
195  var pledgeMeltableNode = item.getElementsByClassName('js-reclaim')[0];
196  if (!!pledgeMeltableNode) {
197    pledgeMeltable = true;
198  }
199
200  return {
201    pledgeId: pledgeId,
202    pledgeName: pledgeName,
203    pledgeValue: pledgeValue,
204    pledgeConfigurationValue: pledgeConfigurationValue,
205    date: date,
206    contains: contains,
207    items: pledgeItems,
208    alsoContains: pledgeAlsoContains,
209    giftable: pledgeGiftable,
210    meltable: pledgeMeltable,
211    page: page,
212    handle: '',
213  };
214}
215
216function parseBuybacksPage(pageData, page) {
217  var parsed = {
218    isLastPage: false,
219    buybacks: [],
220    errors: [],
221  };
222
223  var parser = new DOMParser();
224  var htmlDoc = parser.parseFromString(pageData, 'text/html');
225  
226  var raquoRes = htmlDoc.getElementsByClassName('raquo')[0];
227  if (!raquoRes) {
228    parsed.isLastPage = true;
229  } 
230
231  var singleline = pageData.replace(/\r?\n|\r/g, " ").replace(/\s+/g, " ");
232  if (/No pledges available/.test(singleline)) {
233    parsed.isLastPage = true;
234    return parsed;
235  }
236
237  var articleNodes = htmlDoc.getElementsByTagName('article');
238  for (var s = 0; s < articleNodes.length; s++) {
239    buyback = parseArticle(articleNodes[s], page);
240    if (!!buyback) {
241      parsed.buybacks.push(buyback);
242    } else {
243      console.log({errorParsingArticle: articleNodes[i]});
244    }
245  }
246  return parsed; 
247}
248
249function parseArticle(articleNode, page) {
250  var name = "";
251  {
252    var nameNode = articleNode.getElementsByTagName('h1');
253    if (nameNode.length == 1) {
254      name = nameNode[0].innerText.replace(/\r?\n|\r/g, " ").replace(/\ \- upgraded/gi, " ").trim();
255    } else {
256      console.log({ error: "name not found" });
257      return null;
258    }
259  }
260
261  var lastModified = "";
262  var contained = "";
263  {
264    var dataNode = articleNode.getElementsByTagName('dd');
265    if (dataNode.length == 4) {
266      lastModified = dataNode[0].innerText.trim();
267      contained = dataNode[2].innerText.trim();
268    } else if (dataNode.length == 2) {
269      lastModified = dataNode[0].innerText.trim();
270      contained = dataNode[1].innerText.trim();
271    } else {
272      console.log({error: "dd not found"});
273      return null;
274    }
275  }
276
277  var pledgeId = "";
278  var fromShipId = "";
279  var toShipId = "";
280  var toSkuId = "";
281  var href = "";
282  {
283    var holosmallbtn = articleNode.getElementsByClassName('holosmallbtn')[0];
284    if (!!holosmallbtn) {
285      pledgeId = holosmallbtn.getAttribute('data-pledgeId');
286      fromShipId = holosmallbtn.getAttribute('data-fromShipId');
287      toShipId = holosmallbtn.getAttribute('data-toShipId');
288      toSkuId = holosmallbtn.getAttribute('data-toSkuId');
289      href = holosmallbtn.getAttribute('href');
290    } else {
291      console.log({ error: "holosmallbtn not found" });
292      return null;
293    }
294  }
295
296  return {
297    pledgeName: name || "",
298    date: lastModified || "",
299    contains: contained || "",
300    pledgeId: pledgeId || "",
301    fromShipId: fromShipId || "",
302    toShipId: toShipId || "",
303    toSkuId: toSkuId || "",
304    buybackHref: href || "",
305    buybackPrice: "",
306    page: page,
307    handle: '',
308  };
309}
310
311
312function parseBuybackPage(pageData, parsed) {
313  parsed = {
314    price: "",
315    currency: "",
316    errors: [],
317  }
318
319  var parser = new DOMParser();
320  var htmlDoc = parser.parseFromString(pageData, 'text/html');
321  
322
323  var singleline = pageData.replace(/\r?\n|\r/g, " ").replace(/\s+/g, " ");
324  if (/NAVIGATING UNCHARTED TERRITORY/.test(singleline)) {
325    parsed.errors.push("not found");
326    return parsed
327  }
328
329  var priceNode = htmlDoc.getElementsByClassName('final-price')[0];
330  if (!!priceNode) {
331    var price = parseInt(priceNode.getAttribute('data-value'));
332    var currency = priceNode.getAttribute('data-currency') || "USD";
333    if (!isNaN(price)) {
334      parsed.price = (price / 100).toFixed(2);
335      parsed.currency = currency;
336    } else {
337      parsed.errors.push("final-price data-value not a number");
338    }
339  } else {
340    parsed.errors.push("final-price data-value not found");
341  }
342  return parsed;
343}
344
345
346// function buildJson(handle, pledges, pledgesPagesize, pledgesLastPage, buybacks, buybacksPagesize, buybacksLastPage) {
347//   var data = {
348//     type: "HangarLinkConnect",
349//     version: 2,
350//     handle: handle,
351//     pledges: pledges,
352//     pledgesPagesize: pledgesPagesize,
353//     pledgesLastPage: pledgesLastPage,
354//     buybacks: buybacks,
355//     buybacksPagesize: buybacksPagesize,
356//     buybacksLastPage: buybacksLastPage,
357//   };
358//   return data;
359// }
360
361// function buildCsv(handle, pledges, pledgesPagesize, pledgesLastPage, buybacks, buybacksPagesize, buybacksLastPage) {
362//   var lines = [];
363//   lines.push([
364//     `"${handle} Type"`,
365//     '"PledgeId"',
366//     '"Name"',
367//     '"Value"',
368//     '"ConfigurationValue"',
369//     '"Date"',
370//     '"Page"',
371//     '"Pagesize"',
372//     '"Contains"',
373//     '"AlsoContains"',
374//     '"Items"',
375//     '"PageUrl"',
376//     '"BuybackUrl"',
377//   ].join(","));
378
379//   (pledges || []).map((pledge) => { 
380//     lines.push([
381//       '"PLEDGE"',
382//       pledge.pledgeId, 
383//       '"' + pledge.pledgeName + '"',
384//       '"' + pledge.pledgeValue + '"',
385//       '"' + pledge.pledgeConfigurationValue + '"',
386//       '"' + pledge.date + '"',
387//       pledge.page,
388//       pledgesPagesize,
389//       '"' + pledge.contains + '"',
390//       '"' + pledge.alsoContains.map((ac) => { return `[TITLE ${ac.title || ''}]` }).join(",") + '"', 
391//       '"' + pledge.items.map((it) => { return convertItem(it)}).join(",") + '"', 
392//       `"https://robertsspaceindustries.com/account/pledges?page=${(pledge.page || 0).toString()}&pagesize=${(pledgesPagesize || 10).toString()}"`,
393//       '',
394//     ].join(','))});
395
396//     (buybacks || []).map((buyback) => { 
397//       var pid = buyback.pledgeId
398//       if (!buyback.pledgeId || buyback.pledgeId.trim() == '') {
399//         parts = (buyback.buybackHref || "").split('/')
400//         pid = parts[parts.length-1];
401//       }
402  
403//       var buybackUrl = "";
404//       if (!!buyback.buybackHref && buyback.buybackHref != "") {
405//       buybackUrl = `"https://robertsspaceindustries.com${buyback.buybackHref}"`
406//       }
407      
408//       lines.push([
409//         '"BUYBACK"',
410//         pid, 
411//         '"' + buyback.pledgeName + '"',
412//         '',
413//         '',
414//         '"' + buyback.date + '"',
415//         buyback.page,
416//         buybacksPagesize,
417//         '"' + buyback.contains + '"',
418//         '',
419//         '',
420//         `"https://robertsspaceindustries.com/account/buy-back-pledges?page=${(buyback.page || 0).toString()}&pagesize=${(buybacksPagesize || 100).toString()}"`,
421//         buybackUrl,
422//       ].join(','))});
423
424//     return lines.join("\n");
425// }
426
427// function convertItem(it) {
428//   result = '';
429//   if (!!it.kind && it.kind != '') {
430//     result = `${result}[KIND ${it.kind}]`;
431//   }
432//   if (!!it.title && it.title != '') {
433//     result = `${result}[TITLE ${it.title}]`;
434//   }
435//   if (!!it.liner && it.liner != '') {
436//     result = `${result}[LINER ${it.liner}]`;
437//   }
438//   if (!!it.itemCustomName && it.itemCustomName != '') {
439//     result = `${result}[CUSTOMNAME ${it.itemCustomName}]`;
440//   }
441//   return result;
442// }
443
444var isChromium = window.chrome;
445var winNav = window.navigator;
446var vendorName = winNav.vendor;
447var isChrome = (isChromium !== null && typeof isChromium !== "undefined" && vendorName === "Google Inc.");
448var isFirefox = parseFloat(navigator.userAgent.split('Firefox/').pop(), 10) >= 93;
449var globalResponder = undefined;
450
451if (isFirefox || isChrome) {
452  window.addEventListener("message", function(event) {
453    if (event.source == window &&
454        event.data.direction &&
455        event.data.direction == "from-hangar-link-connect") {
456          if(!!globalResponder) {
457        globalResponder(event.data.message);
458        globalResponder = undefined;
459      }
460    }
461  });
462}
463
464var HangarLinkConnect = (function () {
465    function HangarLinkConnect() {}
466
467    HangarLinkConnect.prototype.platform = function (func) {
468      if (isFirefox) {
469        func('firefox');
470      } else if (isChrome) {
471        func('chrome');
472      } else {
473        func('unknown');
474      }
475    };
476
477    HangarLinkConnect.prototype.connect = function (id, func) {
478      var payload = JSON.stringify({action: "connect"});
479      var timeoutId;
480      var responder = function(response) {
481        if (!!timeoutId) {
482          clearTimeout(timeoutId)
483        }
484        if (!response) {
485          func(JSON.stringify({code: 101, error: "sendmessage error"}));
486        } else {
487          func(response);
488        }
489      };
490
491      if (isFirefox || isChrome) {
492        if (!!globalResponder) {
493          func(JSON.stringify({code: 103, error: "globalResponder not empty"}));
494        } else {
495          timeoutId = setTimeout(function(){
496            globalResponder = undefined;
497            func(JSON.stringify({code: 600, error: "timeout"}));
498          }, 2000);
499          globalResponder = responder;
500          window.postMessage({
501            direction: "from-hangar-link",
502            message: payload
503          }, "*");
504        }        
505      } else {
506        func(JSON.stringify({code: 100, version: 0}));
507      }
508    };
509
510    HangarLinkConnect.prototype.identify = function (id, func) {
511      var payload = JSON.stringify({action: "identify"});
512      var responder = function(response) {
513        if (!response) {
514          func(JSON.stringify({code: 101, error: "sendmessage error"}));
515        } else {
516          func(response);
517        }
518      };
519      if (isFirefox || isChrome) {
520        if (!!globalResponder) {
521          func(JSON.stringify({code: 103, error: "globalResponder not empty"}));
522        } else {
523          globalResponder = responder
524          window.postMessage({
525            direction: "from-hangar-link",
526            message: payload,
527          }, "*");
528        }        
529      } else {
530        func(JSON.stringify({code: 200, version: 0}));
531        }
532    };
533
534    HangarLinkConnect.prototype.getPledgesPage = function (id, page, mock, func) {
535      var payload = JSON.stringify({action: "getPledgesPage", page: page})
536      var responder = function(response) {
537        if (!response) {
538          func(JSON.stringify({code: 101, error: "sendmessage error"}));
539        } else {
540          var data = JSON.parse(response);
541          if (data.code == 200) {
542
543            var parsed = "";
544            if (mock != "") {
545              parsed = parsePledgesPage(mock, -1);
546            } else {
547              parsed = parsePledgesPage(data.payload, page);
548            }
549            func(JSON.stringify({code: 200, payload: parsed || {}}));
550          } else {
551            func(JSON.stringify({code: 500, error: data.payload || {}}));
552          }
553        }
554      };
555
556      if (isFirefox || isChrome) {
557        if (!!globalResponder) {
558          func(JSON.stringify({code: 103, error: "globalResponder not empty"}));
559        } else {
560          globalResponder = responder;
561          window.postMessage({
562            direction: "from-hangar-link",
563            message: payload,
564          }, "*");
565        }        
566      } else {
567        func(JSON.stringify({code: 100, version: 0}));
568      }
569    };    
570
571    HangarLinkConnect.prototype.getBuybacksPage = function (id, page, func) {
572      var payload = JSON.stringify({action: "getBuybacksPage", page: page});
573      var responder = function(response) {
574        if (!response) {
575          func(JSON.stringify({code: 101, error: "sendmessage error"}));
576        } else {
577          var data = JSON.parse(response);
578          if (data.code == 200) {
579            var parsed = parseBuybacksPage(data.payload, page);
580            func(JSON.stringify({code: 200, payload: parsed || {}}));
581          } else {
582            func(JSON.stringify({code: 500, error: data.payload || {}}));
583          }
584        }
585      };
586      if (isFirefox || isChrome) {
587        if (!!globalResponder) {
588          func(JSON.stringify({code: 103, error: "globalResponder not empty"}));
589        } else {
590          globalResponder = responder;
591          window.postMessage({
592            direction: "from-hangar-link",
593            message: payload,
594          }, "*");
595        }
596      } else {
597        func(JSON.stringify({code: 100, version: 0}));
598      }
599    };    
600
601    HangarLinkConnect.prototype.getBuybackPage = function (id, pledgeId, func) {
602      var payload = JSON.stringify({action: "getBuybackPage", pledgeId: pledgeId});
603      var responder = function(response) {
604        if (!response) {
605          func(JSON.stringify({code: 101, error: "sendmessage error"}));
606        } else {
607          var data = JSON.parse(response);
608          if (data.code == 200) {
609            var parsed = parseBuybackPage(data.payload);
610            func(JSON.stringify({code: 200, payload: parsed || {}}));
611          } else {
612            func(JSON.stringify({code: 500, error: data.payload || {}}));
613          }
614        }
615      };
616      if (isFirefox || isChrome) {
617        if (!!globalResponder) {
618          func(JSON.stringify({code: 103, error: "globalResponder not empty"}));
619        } else {
620          globalResponder = responder;
621          window.postMessage({
622            direction: "from-hangar-link",
623            message: payload,
624          }, "*");
625        }        
626      } else {
627        func(JSON.stringify({code: 100, version: 0}));
628      }
629    };        
630
631    HangarLinkConnect.prototype.setAuthToken = function (id, func) {
632      var payload = JSON.stringify({action: "setAuthToken"});
633      var responder = function(response) {
634        if (!response) {
635          func(JSON.stringify({code: 101, error: "sendmessage error"}));
636        } else {
637          func(response);
638        }
639      };
640      if (isFirefox || isChrome) {
641        if (!!globalResponder) {
642          func(JSON.stringify({code: 103, error: "globalResponder not empty"}));
643        } else {
644          globalResponder = responder;
645          window.postMessage({
646            direction: "from-hangar-link",
647            message: payload,
648          }, "*");
649        }        
650      } else {
651        func(JSON.stringify({code: 100, version: 0}));
652      }
653    };
654
655    HangarLinkConnect.prototype.setContextToken = function (id, func) {
656      var payload = JSON.stringify({action: "setContextToken"});
657      var responder = function(response) {
658        if (!response) {
659          func(JSON.stringify({code: 101, error: "sendmessage error"}));
660        } else {
661          func(response);
662        }
663      };
664      if (isFirefox || isChrome) {
665        if (!!globalResponder) {
666          func(JSON.stringify({code: 103, error: "globalResponder not empty"}));
667        } else {
668          globalResponder = responder;
669          window.postMessage({
670            direction: "from-hangar-link",
671            message: payload,
672          }, "*");
673        }        
674      } else {
675        func(JSON.stringify({code: 100, version: 0}));
676      }
677    };
678
679    HangarLinkConnect.prototype.setContextTokenBuyback = function (id, fromShipId, toShipId, toSkuId, pledgeId, func) {
680      var payload = JSON.stringify({action: "setContextTokenBuyback", fromShipId: fromShipId, toShipId: toShipId, toSkuId: toSkuId, pledgeId: pledgeId});
681      var responder = function(response) {
682        if (!response) {
683          func(JSON.stringify({code: 101, error: "sendmessage error"}));
684        } else {
685          func(response);
686        }
687      };
688      if (isFirefox || isChrome) {
689        if (!!globalResponder) {
690          func(JSON.stringify({code: 103, error: "globalResponder not empty"}));
691        } else {
692          globalResponder = responder;
693          window.postMessage({
694            direction: "from-hangar-link",
695            message: payload,
696          }, "*");
697        }        
698      } else {
699        func(JSON.stringify({code: 100, version: 0}));
700      }
701    };
702
703    HangarLinkConnect.prototype.getUpgradePrice = function (id, fromShipId, toSkuId, func) {
704      var payload = JSON.stringify({action: "getUpgradePrice", fromShipId: fromShipId, toSkuId: toSkuId});
705      var responder =  function(response) {
706        if (!response) {
707          func(JSON.stringify({code: 101, error: "sendmessage error"}));
708        } else {
709          func(response);
710        }
711      };
712      if (isFirefox || isChrome) {
713        if (!!globalResponder) {
714          func(JSON.stringify({code: 103, error: "globalResponder not empty"}));
715        } else {
716          globalResponder = responder;
717          window.postMessage({
718            direction: "from-hangar-link",
719            message: payload,
720          }, "*");
721        }
722      } else {
723        func(JSON.stringify({code: 100, version: 0}));
724      }
725    };
726
727    HangarLinkConnect.prototype.reclaimPledge = function (id, pledgeId, currentPassword, func) {
728      var payload = JSON.stringify({action: "reclaimPledge", pledgeId: pledgeId, currentPassword: currentPassword});
729      var responder =  function(response) {
730        if (!response) {
731          func(JSON.stringify({code: 101, error: "sendmessage error"}));
732        } else {
733          func(response);
734        }
735      };
736      if (isFirefox || isChrome) {
737        if (!!globalResponder) {
738          func(JSON.stringify({code: 103, error: "globalResponder not empty"}));
739        } else {
740          globalResponder = responder;
741          window.postMessage({
742            direction: "from-hangar-link",
743            message: payload,
744          }, "*");
745        }
746      } else {
747        func(JSON.stringify({code: 100, version: 0}));
748      }
749    };
750
751    HangarLinkConnect.prototype.addCartItem = function (id, skuId, quantity, func) {
752      var payload = JSON.stringify({action: "addCartItem", skuId: skuId, quantity: quantity});
753      var responder =  function(response) {
754        if (!response) {
755          func(JSON.stringify({code: 101, error: "sendmessage error"}));
756        } else {
757          func(response);
758        }
759      };
760      if (isFirefox || isChrome) {
761        if (!!globalResponder) {
762          func(JSON.stringify({code: 103, error: "globalResponder not empty"}));
763        } else {
764          globalResponder = responder;
765          window.postMessage({
766            direction: "from-hangar-link",
767            message: payload,
768          }, "*");
769        }
770      } else {
771        func(JSON.stringify({code: 100, version: 0}));
772      }
773    };    
774
775    return HangarLinkConnect;
776})();

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.