PageSourceSearch

https://bitsbox.com/lib/bitsboxnavbar/bitsboxnavbar.js

js bitsbox.com collected 2026-10-01 12:09:51 UTC 22,334 bytes, 699 lines download raw bytes

1//  Copyright: Copyright 2016 Bitsbox.
2//  License: All Rights Reserved.
3
4/**
5 * @fileoverview  Provide the BitsboxNavBar class.
6 */
7
8/**
9 * A handy namespace to hang things off of.
10 * @type {Object}
11 */
12var codepops = codepops || {};
13
14
15/**
16 * A nested datastructure describing everything on the navbar.
17 * @type {Object}
18 */
19codepops.navItems = {
20    /*'For Home': {
21        'Overview': { href: '/#overview-panel',
22         id: 'navbar-link-home-overview' },
23        'Who it\'s for': { href: '/#who-panel', id: 'navbar-link-home-who' },
24        'How it works': { href: '/#how-panel', id: 'navbar-link-home-how' },
25        'Why Bitsbox?': { href: '/#why-panel', id: 'navbar-link-home-why' },
26        'Pricing': { href: 'https://subscribe.bitsbox.com/',
27          id: 'navbar-link-home-store' },
28        href: '/',
29        id: 'navbar-link-home-home'
30        },
31
32    'Schools': {
33        'Overview': { href: '/teachers/#bitsbox-school-products',
34          id: 'navbar-link-schools-overview' },
35        'FAQ': { href: '/teachers/#faq',
36          id: 'navbar-link-schools-started' },
37        'School Products': { href: 'https://subscribe.bitsbox.com/details/school-products.html',
38          id: 'navbar-link-schools-shopp2' },
39        href: '/teachers',
40        id: 'navbar-link-schools-home'
41        },
42        
43    'Homeschool': {
44        'Overview': { href: '/homeschool',
45          id: 'navbar-link-homeschool-overview' },
46        'Subscriptions': { href: 'https://subscribe.bitsbox.com',
47          id: 'navbar-link-homeschool-store' },
48        href: '/homeschool',
49        id: 'navbar-link-homeschool-home'
50        },
51    */
52    'Home': {
53        /*'Subscriptions': { href: 'https://subscribe.bitsbox.com/',
54          id: 'navbar-link-store-subscriptions' },
55        'School Products': { href: 'https://subscribe.bitsbox.com/details/school-products.html',
56          id: 'navbar-link-store-shopp2' },
57        'Gift Set': { href: 'https://subscribe.bitsbox.com/shop/product/6170909776_b%27bitsbox-gift-set%27',
58          id: 'navbar-link-store-gift-set' },
59        'Complete Your Set': { href: 'https://subscribe.bitsbox.com/details/completeyourset.html',
60          id: 'navbar-link-store-shopp3' },
61        'Other': { href: 'https://subscribe.bitsbox.com/details/otherproducts.html',
62          id: 'navbar-link-store-shopp1' },*/
63        href: 'https://bitsbox.com/',
64        id: 'navbar-link-store-home'
65        },
66        
67    'Store': {
68        /*'Subscriptions': { href: 'https://subscribe.bitsbox.com/',
69          id: 'navbar-link-store-subscriptions' },
70        'School Products': { href: 'https://subscribe.bitsbox.com/details/school-products.html',
71          id: 'navbar-link-store-shopp2' },
72        'Gift Set': { href: 'https://subscribe.bitsbox.com/shop/product/6170909776_b%27bitsbox-gift-set%27',
73          id: 'navbar-link-store-gift-set' },
74        'Complete Your Set': { href: 'https://subscribe.bitsbox.com/details/completeyourset.html',
75          id: 'navbar-link-store-shopp3' },
76        'Other': { href: 'https://subscribe.bitsbox.com/details/otherproducts.html',
77          id: 'navbar-link-store-shopp1' },*/
78        href: 'https://subscribe.bitsbox.com/',
79        id: 'navbar-link-store-home'
80        },
81
82    'Reviews': {
83        /*'Parent Reviews': { href: '/reviews/#parent-review',
84          id: 'navbar-link-reviews-parent' },
85        'Teacher Reviews': { href: '/reviews/#teacher-review',
86          id: 'navbar-link-reviews-teacher' },
87        'Kid Reviews': { href: '/reviews/#kid-review',
88          id: 'navbar-link-reviews-kids' },*/
89        href: '/reviews/',
90        id: 'navbar-link-reviews-home'
91        },
92
93    'Blog': {
94        /*'Articles': { href: '/blog/#top', id: 'navbar-link-blog-articles' },
95        'Contributors': { href: '/blog/#contributors',
96          id: 'navbar-link-blog-contributors' },
97        'Join Mailing List': { href: '/blog/#join',
98          id: 'navbar-link-blog-email' },*/
99        href: '/blog/',
100        id: 'navbar-link-blog-home'
101        },
102
103    'Help': {
104        'Get Help': { href: '/support.html#help',
105          id: 'navbar-link-support-help' },
106        /*'About Us': { href: '/about.html',
107          id: 'navbar-link-support-about' },
108        'Jobs': { href: '/about.html',
109          id: 'navbar-link-support-jobs' },*/
110        'Contact Us': { href: '/support.html#contact',
111          id: 'navbar-link-support-contact' },
112        'FAQ': { href: '/faq.html', id: 'navbar-link-support-faq' },
113        'Terms': { href: '/terms.html', id: 'navbar-link-support-terms' },
114        href: '/support.html',
115        id: 'navbar-link-support-home'
116        }
117    };
118
119
120/**
121 * An object that creates a consistent navigation bar across the
122 * various Bitsbox websites.
123 *
124 * @param {Object=} opt_settings A hash of optional settings.
125 * @constructor
126 */
127codepops.BitsboxNavBar = function(opt_settings) {
128  var settings = opt_settings || {};
129
130  /**
131   * Contains a reference to the window. Useful for dependency injection.
132   * @type {Window}
133   * @private
134   */
135  this.window_ = settings['window'] || window;
136
137  /**
138   * Contains a reference to the window. Useful for dependency injection.
139   * @type {Document}
140   * @private
141   */
142  this.document_ = settings['document'] || this.window_.document;
143
144  /**
145   * Contains the hostname the navbar believes it is running under, such as
146   * 'subscribe.bitsbox.com' or 'bitsbox.com' or 'localhost' or
147   * 'bitsboxstaging.firebaseapp.com'. Useful for dependency injection.
148   * @type {string}
149   * @private
150   */
151  this.hostname_ = settings['hostname'] || this.window_.location.hostname;
152
153  // Time to generate the actual markup.
154  var navPanel = this.document_.createElement('div');
155  navPanel.className = 'bitsbox-navbar-panel';
156  
157  /**
158   * A pointer to the popdown navigation bar that becomes visible on mobile.
159   * @type {Element}
160   */
161  this.popdownPanel = this.document_.createElement('div');
162  this.popdownPanel.className = 'bitsbox-popdown hidden';
163  
164  // Hide the navigation panel until the CSS loads to override it.
165  navPanel.style.visibility = 'hidden';
166
167  // Load the CSS we need to make the navbar pretty.
168  var css = this.document_.createElement('link');
169  css.type = 'text/css';
170  css.rel = 'stylesheet';
171  if (this.isRunningOnCrateJoy()) {
172    css.href = 'https://bitsbox.com/lib/bitsboxnavbar/bitsboxnavbar.css';
173  } else {
174    css.href = '/lib/bitsboxnavbar/bitsboxnavbar.css';
175  }
176  this.document_.head.appendChild(css);
177
178  // Make a categories DIV with one SPAN per top-level nav item.
179  var categoriesDiv = this.document_.createElement('div');
180  categoriesDiv.id = 'bitsbox-categories-panel';
181
182  // Append our top-level category links.
183  if (this.shouldShowNavBar()) {
184    for (var key in codepops.navItems) {
185      var item = codepops.navItems[key];
186      this.addCategory(categoriesDiv, key, item);
187    }
188  }
189  navPanel.appendChild(categoriesDiv);
190
191  // Append our logo.
192  var logo = this.document_.createElement('a');
193  logo.id = 'bitsbox-navbar-logo';
194  logo.title = 'Bitsbox Logo';
195  if (this.shouldShowNavBar()) {
196    logo.href = this.fixHref('/');
197  } else {
198    logo.href = '/#';
199    logo.style.cursor = 'default';
200  }
201  logo.setAttribute('id','navbar-image-logo');
202  logo.className = 'bitsbox-logo';
203  navPanel.appendChild(logo);
204
205  // Add a dividing line in the mobile menu, to separate the
206  // top level nav links from the "corner" links.
207  var hr = this.document_.createElement('hr');
208  this.popdownPanel.appendChild(hr);
209
210  // Append the links that appear in the top-right corner.
211  var cornerPanel = this.document_.createElement('div');
212  cornerPanel.id = 'corner-panel';
213  navPanel.appendChild(cornerPanel);
214
215  if (this.shouldShowNavBar()) {
216    this.addCornerLink(cornerPanel, 'Kids Sign In',
217        '/code.html#signin', 'navbar-link-signin', 'navbar-link-signin-mobile');
218    this.addCornerJSLink(cornerPanel, 'Account',
219        'https://subscribe.bitsbox.com/customer/account', 'navbar-link-account',
220        'navbar-link-account-mobile');
221  
222    if (this.customerIsLoggedInToCratejoy()) {
223      this.addCornerLink(cornerPanel, 'Sign Out',
224        'https://subscribe.bitsbox.com/customer/logout', 'navbar-link-signout',
225        'navbar-link-signout-mobile');
226    }
227    this.addCornerLink(cornerPanel, 'Cart',
228      'https://subscribe.bitsbox.com/checkout/', 'navbar-link-cart',
229      'navbar-link-cart-mobile');
230    this.addMobileMenu(cornerPanel);
231  }
232  
233
234  // TODO(scott): We should probably create a bind() function in this
235  //     object so we can do proper binding.
236  var win = this.window_;
237  var doc = this.document_;
238  var target = this;
239  this.window_.onscroll = function() {
240  
241    target.hideAllLinkPanels();
242    target.deselectAllCategories();
243    
244    var linksPanelTop = '75px';
245    if (win.scrollY < 32) {
246      navPanel.className = 'bitsbox-navbar-panel';
247    } else {
248      navPanel.className = 'bitsbox-navbar-panel ' +
249          'scrolled';
250      linksPanelTop = '42px';
251    }
252
253    var linkPanels = doc.getElementsByClassName('bitsbox-links-panel');
254    for (var i = 0; i < linkPanels.length; i++) {
255      linkPanels[i].style.top = linksPanelTop;
256    }
257  };
258  
259  this.document_.body.appendChild(navPanel);
260  this.document_.body.appendChild(this.popdownPanel);
261  
262  // Show everything after a timeout. This avoids "popping" during load.
263  this.window_.setTimeout(codepops.bind(function() {
264    navPanel.style.visibility = 'visible';
265    
266    var linkPanels =
267      this.document_.getElementsByClassName('bitsbox-links-panel');
268    for (var i = 0; i < linkPanels.length; i++) {
269      linkPanels[i].style.visibility = 'visible';
270    }
271    
272  }, this), 350);
273};
274
275
276/**
277 * Adds a top-level category nav item.
278 * @param {Element} parent The div to add the category to.
279 * @param {string} title The title of the link.
280 * @param {Object} item A hash of sub-links to handle.
281 */
282codepops.BitsboxNavBar.prototype.addCategory = function(parent, title, item) {
283  var categoryLink = this.document_.createElement('a');
284  categoryLink.innerHTML = title;
285  categoryLink.href = this.fixHref(item.href || '#');
286  categoryLink.className = 'bitsbox-category';
287  if (item.id) {
288     categoryLink.setAttribute('id',item.id);
289  }
290
291  var linksPanel = this.document_.createElement('div');
292  linksPanel.className = 'bitsbox-links-panel';
293  
294  // Hide the panels until the CSS loads to override it.
295  linksPanel.style.visibility = 'hidden';
296
297  categoryLink.onmouseover = codepops.bind(function() {
298    this.hideAllLinkPanels();
299    linksPanel.className = 'bitsbox-links-panel visible';
300    this.deselectAllCategories();
301    categoryLink.className = 'bitsbox-category selected';
302  }, this);
303  parent.appendChild(categoryLink);
304
305  linksPanel.onmouseout = codepops.bind(this.hideAllLinkPanels, this);
306
307  for (var key in item) {
308    if ((key != 'href') && (key != 'id')) {
309      var subItem = item[key];
310      this.addLink(linksPanel, key, subItem);
311    }
312  }
313  this.document_.body.appendChild(linksPanel);
314  
315  // Mobile navigation items.
316  var mobileLink = this.document_.createElement('a');
317  mobileLink.innerHTML = title;
318  mobileLink.href = this.fixHref(item.href || '#');
319  if (item.id) {
320    mobileLink.setAttribute('id',item.id + '-mobile');
321  }
322  this.popdownPanel.appendChild(mobileLink);
323};
324
325
326/**
327 * Adds a second-level category nav link.
328 * @param {Element} parent The div to add the category to.
329 * @param {string} title The title of the link.
330 * @param {Object} subItem A hash of metadata about this link.
331 */
332codepops.BitsboxNavBar.prototype.addLink = function(parent, title, subItem) {
333  var link = this.document_.createElement('a');
334  link.innerHTML = title;
335  link.href = this.fixHref(subItem.href);
336  if (subItem.id) {
337    link.id = subItem.id;
338  }
339  parent.appendChild(link);
340  link.onmouseover = function() {
341    parent.className = 'bitsbox-links-panel visible';
342  };
343};
344
345
346/**
347 * Adds a link to the top right corner.
348 * @param {Element} parent The div to add the link to.
349 * @param {string} title The title of the link.
350 * @param {string} href Link to go to.
351 */
352codepops.BitsboxNavBar.prototype.addCornerLink = function(parent, title, href, 
353  id, mobileId) {
354
355  var fixedHref = this.fixHref(href || '#');
356  
357  var link = this.document_.createElement('a');
358  link.title = title;
359  link.innerHTML = title;
360  link.className = title.toLowerCase().replace(/\s+/gi, '-');
361
362  // If the href contains logout, we want to add some additional properties
363  // so we can send the user to our home page instead of the store page.
364  if (href.indexOf('logout') > -1)  {
365    link.href = 'https://bitsbox.com/';
366    link.onclick = this.logoutOfStoreAccount;
367  } else {
368    link.href = fixedHref;
369  }
370  if (id) {
371    link.setAttribute('id',id);
372  }
373  parent.appendChild(link);
374
375  // Mobile navigation items.
376  var mobileLink = this.document_.createElement('a');
377  mobileLink.innerHTML = title;
378
379  if (href.indexOf('logout') > -1)  {
380    mobileLink.href = 'https://bitsbox.com/';
381    mobileLink.onclick = this.logoutOfStoreAccount;
382  } else {
383    mobileLink.href = fixedHref;
384  }
385  if (mobileId) {
386    mobileLink.setAttribute('id',mobileId);
387  }
388  this.popdownPanel.appendChild(mobileLink);
389};
390
391
392/**
393 * Function that logs a user out of their store account and sends them to
394 * the bitsbox homepage.
395 */
396codepops.BitsboxNavBar.prototype.logoutOfStoreAccount = function() {
397
398  // Code taken from support.cratejoy.com to be able to logout, but still
399  // send the user where we want.
400  $.ajax({
401    url: '/customer/logout',
402    type: 'GET',
403    success: function(response){;}
404  });
405};
406
407
408/**
409 * Adds a link to the top right corner, but instead of it being an <a> tag,
410 * build it with and <em> tag with an onclick handler. Useful for keeping
411 * the Account link from being decorated with Google Analytics query parameters
412 * which cause the "blank screen" bug on the Cratejoy side.
413 * @param {Element} parent The div to add the link to.
414 * @param {string} title The title of the link.
415 * @param {string} href Link to go to.
416 */
417codepops.BitsboxNavBar.prototype.addCornerJSLink = function(parent, title, href, 
418  id, mobileId) {
419
420  var fixedHref = this.fixHref(href || '#');
421  fixedHref = fixedHref.replace(/"/gi, '\"');
422  
423  var link = this.document_.createElement('em');
424  link.title = title;
425  link.innerHTML = title;
426  link.className = title.toLowerCase().replace(/\s+/gi, '-');
427  link.onclick = function() {
428    window.location = fixedHref;
429  };
430  if (id) {
431    link.setAttribute('id', id);
432  }
433  parent.appendChild(link);
434
435  // Mobile navigation items.
436  var mobileLink = this.document_.createElement('em');
437  mobileLink.innerHTML = title;
438  mobileLink.onclick = function() {
439    window.location = fixedHref;
440  };
441  if (mobileId) {
442    mobileLink.setAttribute('id', mobileId);
443  }
444  this.popdownPanel.appendChild(mobileLink);
445};
446
447
448/**
449 * Adds a link to the top right corner.
450 * @param {Element} parent The div to add the link to.
451 * @param {string} title The title of the link.
452 * @param {string} href Link to go to.
453 */
454codepops.BitsboxNavBar.prototype.addMobileMenu = function(parent) {
455  var menu = this.document_.createElement('span');
456  menu.innerHTML = '=';
457  menu.className = 'bitsbox-mobile-menu';
458  parent.appendChild(menu);
459  menu.onclick = codepops.bind(this.onMobileMenuClick, this);
460};
461
462
463/**
464 * Handles clicks or taps on the mobile nav menu.
465 */
466codepops.BitsboxNavBar.prototype.onMobileMenuClick = function(parent) {
467  if (this.popdownPanel.className == 'bitsbox-popdown visible') {
468    this.popdownPanel.className = 'bitsbox-popdown hidden';
469  } else {
470    this.popdownPanel.className = 'bitsbox-popdown visible';
471  }
472};
473
474
475/**
476 * Hides all of the link panels in the navigation bar.
477 */
478codepops.BitsboxNavBar.prototype.hideAllLinkPanels = function() {
479  var linkPanels = this.document_.getElementsByClassName('bitsbox-links-panel');
480  for (var i = 0; i < linkPanels.length; i++) {
481    linkPanels[i].className = 'bitsbox-links-panel';
482  }
483};
484
485
486/**
487 * Deselects all of the top-level category links.
488 */
489codepops.BitsboxNavBar.prototype.deselectAllCategories = function() {
490  var categories = document.getElementsByClassName('bitsbox-category');
491  for (var i = 0; i < categories.length; i++) {
492    categories[i].className = 'bitsbox-category';
493  }
494};
495
496
497/**
498 * Gets the host the navbar believes it's running under.
499 * @return {string} The hostname
500 */
501codepops.BitsboxNavBar.prototype.getHost = function() {
502  return this.host_;
503};
504
505
506/**
507 * Returns whether this file is being loaded from Cratejoy.com. This will drive
508 * logic for figuring out relative links.
509 * @return {boolean} Whether we are running on a vendor site.
510 */
511codepops.BitsboxNavBar.prototype.isRunningOnCrateJoy = function() {
512  if (this.window_.location.href.indexOf('/subscribe.bitsbox.com') > -1 ||
513      this.window_.location.href.indexOf('/cratejoy.com') > -1) {
514    return true;
515  }
516  return false;
517};
518
519
520/**
521 * Returns whether this file is being loaded from a page where the navbar
522 * should be shown. Most pages hosted on Cratejoy should not show the navigation
523 * bar, as A/B tests show that navigation lowers our conversion rate.
524 * @return {boolean} Whether we should show the nav bar.
525 */
526codepops.BitsboxNavBar.prototype.shouldShowNavBar = function() {
527  
528  // Made the decision on August 9th, 2022 to start showing the navigation
529  // bar again. For now returning true here to make it easy to revert back
530  // just in case we need to.
531  return true;
532
533  if (this.isRunningOnCrateJoy() === false) {
534    return true;
535  } else {
536
537    // We show the navbar on anything that begins with /customer.
538    if (this.window_.location.href.indexOf('/customer') > -1) {
539      return true;
540    }
541
542    // And we show it on the school products and other products landing pages.
543    if (this.window_.location.href.indexOf('/details/school-products') > -1) {
544      return true;
545    }
546    if (this.window_.location.href.indexOf('details/otherproducts') > -1) {
547      return true;
548    }
549
550    // Otherwise, we hide it.
551    return false;
552  }
553};
554
555
556/**
557 * Takes a relative or absolute URL as input, and returns a new URL corrected
558 * to be either absolute or relative based on which site (bitsbox.com vs.
559 * Cratejoy) the nav bar is being viewed on.
560 * @param {string} href The URL to fix.
561 * @return {string} The fixes URL.
562 */
563codepops.BitsboxNavBar.prototype.fixHref = function(href) {
564
565  // If we're running on Cratejoy and the href begins with a forward slash,
566  // then make sure we send the user to a path on the main site.
567  if (this.isRunningOnCrateJoy() && href.substr(0, 1) == '/') {
568    return 'https://bitsbox.com' + href;
569  }
570  return href;
571};
572
573
574/**
575 * Hides the shopping cart bar on Cratejoy site. Note that this relies on JQuery
576 * being loaded, which it always is on Cratejoy sites.
577 */
578codepops.BitsboxNavBar.prototype.hideShoppingCart = function () {
579  $('.row-offcanvas').one('webkitTransitionEnd otransitionend oTransitionEnd ' +
580                          'msTransitionEnd transitionend', function(e) {
581    if(!$('.row-offcanvas').hasClass('active')) {
582      $('.row-offcanvas').removeClass('active-position');
583    }
584  });
585  $('.row-offcanvas').removeClass('active');
586  $(document).unbind('mouseup touchend');
587};
588
589
590/**
591 * Shows the shopping cart bar on Cratejoy site. Note that this relies on JQuery
592 * being loaded, which it always is on Cratejoy sites.
593 */
594codepops.BitsboxNavBar.prototype.showShoppingCart = function () {
595  $('.row-offcanvas').addClass('active-position');
596  $('.row-offcanvas').addClass('active');
597  var boundHideCart = codepops.bind(this.hideShoppingCart, this);
598  
599  $(document).bind('mouseup touchend', function (e) {
600    var $container = $('.sidebar-offcanvas');
601    if (!$container.is(e.target) && $container.has(e.target).length === 0) {
602      boundHideCart();
603    }
604  });
605};
606
607
608
609/**
610 * Toggles shopping cart bar on Cratejoy site.
611 */
612codepops.BitsboxNavBar.prototype.toggleShoppingCart = function () {
613
614  // Hide the mobile popdown menu so it does not cover up the cart.
615  this.popdownPanel.className = 'bitsbox-popdown hidden';
616
617  if (this.shoppingCartIsVisible()) {
618    this.hideShoppingCart();
619  } else {
620    this.showShoppingCart();
621  }
622};
623
624
625/**
626 * Returns whether the Cratejoy shopping cart bar is currently visible.
627 * @return {boolean} Whether the shopping cart bar is visible.
628 */
629codepops.BitsboxNavBar.prototype.shoppingCartIsVisible = function () {
630  if ($('.row-offcanvas') &&
631      $('.row-offcanvas')[0]
632      && $('.row-offcanvas')[0].className.indexOf('active-position') > -1) {
633    return true;
634  } else {
635    return false;
636  }
637};
638
639
640/**
641 * Returns whether the user is currently logged in to a Cratejoy account.
642 * @return {boolean} Whether they are logged in.
643 */
644codepops.BitsboxNavBar.prototype.customerIsLoggedInToCratejoy = function () {
645  var crateJoyMenu = this.document_.getElementsByClassName('dropdown-menu')[0];
646  if (crateJoyMenu && crateJoyMenu.innerHTML.indexOf('Store Sign Out') > -1) {
647    return true;
648  } else {
649    return false;
650  }
651};
652
653
654/**
655 * Handy bind function.
656 * @param {Function} fn The function to be bound.
657 * @param {Object} selfObj The object to be bound as 'this'.
658 * @param {Array} var_args The arguments to be handed in to the bound function.
659 * @return {Function} The newly bound function.
660 */
661codepops.bind = function(fn, selfObj, var_args) {
662  var boundArgs = fn.boundArgs_;
663  if (arguments.length > 2) {
664    var args = Array.prototype.slice.call(arguments, 2);
665    if (boundArgs)args.unshift.apply(args, boundArgs);
666    boundArgs = args;
667  }
668  selfObj = fn.boundSelf_ || selfObj;
669  fn = fn.boundFn_ || fn;
670  var newfn;
671  var context = selfObj || goog.global;
672  if (boundArgs)newfn = function() {
673    var args = Array.prototype.slice.call(arguments);
674    args.unshift.apply(args, boundArgs);
675    return fn.apply(context, args);
676  };
677  else newfn = function() {
678    return fn.apply(context, arguments);
679  };
680  newfn.boundArgs_ = boundArgs;
681  newfn.boundSelf_ = selfObj;
682  newfn.boundFn_ = fn;
683  return newfn;
684};
685
686
687
688// If this file is being loaded from anywhere but the test harness, go ahead
689// and create an instance of our object. (In a test situation, we want to
690// be able to create our own instances of the object using mock documents.)
691if (window.location.href.indexOf('/tests/') == -1) {
692
693  /**
694   * Instance of our navigation bar.
695   * @type {BitsboxNavBar}
696   */
697  codepops.bitsboxnavbar = new codepops.BitsboxNavBar();
698
699}

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.