PageSourceSearch

https://www.eastcoastcars.net/v9/media/js/jquerytools/v1.2.4/validator.js

js eastcoastcars.net collected 2026-09-24 12:20:03 UTC 22,394 bytes, 754 lines download raw bytes

1/**
2 * @license
3 * jQuery Tools Validator 1.2.4 - HTML5 is here. Now use it.
4 *
5 * NO COPYRIGHTS OR LICENSES. DO WHAT YOU LIKE.
6 *
7 * http://flowplayer.org/tools/form/validator/
8 *
9 * Since: Mar 2010
10 * Date:    Sun Aug 15 08:16:31 2010 +0000
11 **/
12/*jslint evil: true */
13/*
14* DDCShanonP Note: This plugin has been edited multiple times by DDC for various fixes as the plug-in is no longer
15* under active development. Probably a good candidate for replacement in the future.
16*
17* dttimothyn : I modified the invalidate() method to where you can add custom errors in the message stack.
18*			   Prior to this change, you had to clone and extend the validator object
19*
20* Miguel.Rosas: Added a new rule that will validate comment section (textarea) by limiting to a max of 5,000 chars.
21*				Also did the same thing for name and lastname filed with a 50 char limit.
22* usage:
23* 	validator.invalidate({"myFieldName" : "my custom error message"});
24*	validator.checkValidity(); // attaches the error to myFieldName on your form
25*
26*/
27(function($) {
28
29	$.tools = $.tools || {version: '1.2.4'};
30
31	// globals
32	var typeRe = /\[type=([a-z]+)\]/,
33		regexList,
34		v;
35
36	v = $.tools.validator = {
37
38		conf: {
39			grouped: false, 			// show all error messages at once inside the container
40			effect: 'default',			// show/hide effect for error message. only 'default' is built-in
41			errorClass: 'invalid',		// input field class name in case of validation error
42
43			// Remove or set to false to disable
44			// US809696: Added validation for char limit.
45			charLimit: {
46				"textarea": 5000,
47				"input[name*='contact.firstName']": 50,
48				"input[name*='contact.lastName']": 50,
49				"input[name*='contact.address.postalCode']": 10,
50				"input[name*='contact.email']": 100,
51				"input[name*='contact.phone']": 100
52			},
53
54			// when to check for validity?
55			inputEvent: null,			// change, blur, keyup, null
56			errorInputEvent: 'keyup',  // change, blur, keyup, null
57			formEvent: 'submit',       // submit, null
58			lang: 'en',					// default language for error messages
59			message: '<div/>',
60			messageAttr: 'data-message', // name of the attribute for overridden error message
61			messageClass: 'error',		// error message element's class name
62			offset: [0, 0],
63			position: 'center right',
64			singleError: false, 		// validate all inputs at once
65			speed: 'normal',				// message's fade-in speed
66			// Dictionary of regex
67			// Validator should be set to ascii by default (standard RegExp)
68			// If form sets validator to utf8 via charset attr on form (XRegExp)
69			isAscii: true,
70			regexList: {
71				email: {
72					ascii: /^([a-z0-9_\.\-\+]+)@([\da-zA-Z\.\-]+)\.([a-zA-Z\.]{2,6})$/i,
73					utf8: '^([\\pL\\pN_\\.\\-\\+]+)@([\\pN\\pL\\.\\-]+)\\.([a-zA-Z\\.]{2,6})$'
74				},
75				url: {
76					ascii: /^(https?:\/\/)?[\da-z\.\-]+\.[a-z\.]{2,6}[#\?\/\w \.\-=]*$/i,
77					utf8: '^(https?:\/\/)?[\\d\\pL\\.\\-]+\.[a-z\\.]{2,6}[#\?\/\w \\.\\-=]*$'
78				},
79				containsUrl: {
80					ascii: /(https?:\/\/)?[\da-z\.\-]+\.[a-z\.]{2,6}/i,
81					utf8: '(https?:\\/\\/)?[\\da-z\\.\\-]+\\.[a-z\\.]{2,6}'
82				},
83				number: {
84					ascii: /^-?[0-9]*(\.[0-9]+)?$/,
85					utf8: '^-?[0-9]*(\\.[0-9]+)?$'
86				}
87			}
88		},
89
90		/* The Error Messages */
91		messages: {
92			"*": { en: DDC.i18n.getLabel('PLEASE_CORRECT_THIS_VALUE') }
93		},
94
95		localize: function(lang, messages) {
96			$.each(messages, function(key, msg) {
97				v.messages[key] = v.messages[key] || {};
98				v.messages[key][lang] = msg;
99			});
100		},
101
102		localizeFn: function(key, messages) {
103			v.messages[key] = v.messages[key] || {};
104			$.extend(v.messages[key], messages);
105		},
106
107		/**
108		 * Adds a new validator
109		 */
110		fn: function(matcher, msg, fn) {
111
112			// no message supplied
113			if ($.isFunction(msg)) {
114				fn = msg;
115
116			// message(s) on second argument
117			} else {
118				if (typeof msg == 'string') { msg = {en: msg}; }
119				this.messages[matcher.key || matcher] = msg;
120			}
121
122			// add validator to the arsenal
123			fns.push([matcher, fn]);
124		},
125
126		/* Add new show/hide effect */
127		addEffect: function(name, showFn, closeFn) {
128			effects[name] = [showFn, closeFn];
129		}
130
131	};
132
133	/* calculate error message position relative to the input */
134	function getPosition(trigger, el, conf) {
135
136		// get origin top/left position
137		var top = trigger.offset().top,
138			 left = trigger.offset().left,
139			 pos = conf.position.split(/,?\s+/),
140			 y = pos[0],
141			 x = pos[1];
142
143		top -= el.outerHeight() - conf.offset[0];
144		left += trigger.outerWidth() + conf.offset[1];
145
146		// adjust Y
147		var height = el.outerHeight() + trigger.outerHeight();
148		if (y == 'center') { top += height / 2; }
149		if (y == 'bottom') { top += height; }
150
151		// adjust X
152		var width = trigger.outerWidth();
153		if (x == 'center') { left -= (width + el.outerWidth()) / 2; }
154		if (x == 'left') { left -= width; }
155
156		return {top: top, left: left};
157	}
158
159	var fns = [], effects = {
160
161		'default' : [
162
163			// show errors function
164			function(errs) {
165
166				var conf = this.getConf();
167
168				// loop errors
169				$.each(errs, function(i, err) {
170
171					// add error class
172					var input = err.input;
173					input.addClass(conf.errorClass);
174
175					// get handle to the error container
176					var msg = input.data("msg.el");
177
178					// create it if not present
179					if (!msg) {
180						msg = $(conf.message).addClass(conf.messageClass).appendTo(document.body);
181						input.data("msg.el", msg);
182					}
183
184					// clear the container
185					msg.css({visibility: 'hidden', display: 'none'}).find("span").remove();
186
187					// populate messages
188					$.each(err.messages, function(i, m) {
189						$("<span/>").html(m).appendTo(msg);
190					});
191
192					// make sure the width is sane (not the body's width)
193					if (msg.outerWidth() == msg.parent().width()) {
194						msg.add(msg.find("p")).css({display: 'inline'});
195					}
196
197					// insert into correct position (relative to the field)
198					var pos = getPosition(input, msg, conf);
199
200					msg.css({ visibility: 'visible', position: 'absolute', top: pos.top, left: pos.left })
201						.fadeIn(conf.speed);
202				});
203
204			// hide errors function
205			}, function(inputs) {
206
207				var conf = this.getConf();
208				inputs.removeClass(conf.errorClass).each(function() {
209					var msg = $(this).data("msg.el");
210					if (msg) { msg.css({visibility: 'hidden'}); }
211				});
212			}
213		]
214	};
215
216	/* sperial selectors */
217	$.each("email,url,number".split(","), function(i, key) {
218		$.expr[':'][key] = function(el) {
219			return el.getAttribute("type") === key;
220		};
221	});
222
223	/*
224		oninvalid() jQuery plugin.
225		Usage: $("input:eq(2)").oninvalid(function() { ... });
226	*/
227	$.fn.oninvalid = function( fn ){
228		return this[fn ? "bind" : "trigger"]("OI", fn);
229	};
230
231	/******* built-in HTML5 standard validators *********/
232
233	v.fn(":email", DDC.i18n.getLabel('PLEASE_ENTER_A_VALID_EMAIL_ADDRESS'), function(el, v) {
234		return !v || this.getRegex('email', v);
235	});
236
237	v.fn(":url", DDC.i18n.getLabel('PLEASE_ENTER_A_VALID_URL'), function(el, v) {
238		return !v || this.getRegex('url', v);
239	});
240
241	v.fn('[data-urls-blocked="true"]', DDC.i18n.getLabel('PLEASE_DO_NOT_USE_URL'), function(el, v) {
242		return !v || !this.getRegex('containsUrl', v.replace(/\n/g, ' '));
243	});
244
245	v.fn(":number", DDC.i18n.getLabel('PLEASE_ENTER_A_NUMERIC_VALUE'), function(el, v) {
246		return this.getRegex('number', v);
247	});
248
249	v.fn("[max]", DDC.i18n.getLabel('PLEASE_ENTER_A_VALUE_SMALLER_THAN_ONE_DOLLAR'), function(el, v) {
250		// skip empty values and dateinputs
251		if (v === '' || $(el).hasClass("date")) { return true; }
252
253		var max = el.attr("max");
254		return parseFloat(v) <= parseFloat(max) ? true : [max];
255	});
256
257	v.fn("[min]", DDC.i18n.getLabel('PLEASE_ENTER_A_VALUE_LARGER_THAN_ONE_DOLLAR'), function(el, v) {
258		// skip empty values and dateinputs
259		if (v === '' || $(el).hasClass("date")) { return true; }
260
261		var min = el.attr("min");
262		return parseFloat(v) >= parseFloat(min) ? true : [min];
263	});
264
265	// US1004597: field name included on the validation message
266	v.fn(
267		"[required]:is(input[type=checkbox]",
268		DDC.i18n.getLabel("OOPS_EXCLAMATION_YOU_MISSED_THIS_ONE"),
269		function (el, v) {
270			if (el.is(":checkbox")) {
271				return el.is(":checked");
272			}
273			return !!v;
274		}
275	);
276
277	v.fn("[required]:not(input[type=checkbox])", function (el) {
278		if (!el.val()) {
279			v.messages["[required]:not(input[type=checkbox])"] = {
280				en:
281					el.context.labels[0].innerText.replace("*", " ") +
282					DDC.i18n.getLabel("FORM_FIELD_IS_REQUIRED"),
283			};
284
285			return false;
286		}
287		return true;
288	});
289	
290	v.fn("[consent]", DDC.i18n.getLabel('TCPA_CONSENT_ERROR_VERBIAGE'), function(el, v){
291		if (el.is(":checkbox")) { return el.is(":checked"); }
292		return !!v;
293	});
294
295	// US809696: char limit validation
296	Object.entries(v.conf.charLimit).map(function(e, i) {
297		const elem = e[0], limit = e[1];
298		v.fn(elem, function(el) {
299			if(limit && typeof limit === 'number')
300				if(!!(el.val().length > limit)){
301					v.messages[elem] = { en: "Oops! Character limit exceeded. " + limit + " max.<br>Current count: " + el.val().length + " characters." }
302					return false;
303				}
304			return true;
305		});	
306	});
307
308	v.fn("[pattern]", function(el) {
309		var p = new RegExp("^" + el.attr("pattern") + "$");
310		return p.test(el.val());
311	});
312
313	function Validator(inputs, form, conf) {
314
315		// private variables
316		var self = this,
317			fire = form.add(self),
318			invalidatedErrors = [];
319
320		// make sure there are input fields available
321		inputs = inputs.not(":button, :image, :reset, :submit");
322
323		// utility function
324		function pushMessage(to, matcher, returnValue) {
325
326			// only one message allowed
327			if (!conf.grouped && to.length) { return; }
328
329			// the error message
330			var msg;
331
332			// substitutions are returned
333			if (returnValue === false || $.isArray(returnValue)) {
334				msg = v.messages[matcher.key || matcher] || v.messages["*"];
335				msg = msg[conf.lang] || v.messages["*"].en;
336
337				// substitution
338				var matches = msg.match(/\$\d/g);
339
340				if (matches && $.isArray(returnValue)) {
341					$.each(matches, function(i) {
342						msg = msg.replace(this, returnValue[i]);
343					});
344				}
345
346			// error message is returned directly
347			} else {
348				msg = returnValue[conf.lang] || returnValue;
349			}
350
351			to.push(msg);
352		}
353
354		// API methods
355		$.extend(self, {
356
357			getConf: function() {
358				return conf;
359			},
360
361			getForm: function() {
362				return form;
363			},
364
365			getInputs: function() {
366				return inputs;
367			},
368
369			reflow: function() {
370				inputs.each(function() {
371					var input = $(this),
372						 msg = input.data("msg.el");
373
374					if (msg) {
375						var pos = getPosition(input, msg, conf);
376						msg.css({ top: pos.top, left: pos.left });
377					}
378				});
379				return self;
380			},
381
382			/** @param e - for internal use only */
383			invalidate: function(errs, e) {
384
385				// errors are given manually: { fieldName1: 'message1', fieldName2: 'message2' }
386				if (!e) {
387					var errors = [];
388					$.each(errs, function(key, val) {
389						var input = inputs.filter("[name='" + key + "']");
390						if (input.length) {
391
392							// trigger HTML5 ininvalid event
393							input.trigger("OI", [val]);
394
395							errors.push({ input: input, messages: [val]});
396						}
397					});
398
399					errs = errors;
400					this.invalidatedErrors = errors;
401					e = $.Event();
402				}
403
404				// onFail callback
405				e.type = "onFail";
406				fire.trigger(e, [errs]);
407
408
409				// call the effect
410				if (!e.isDefaultPrevented()) {
411					effects[conf.effect][0].call(self, errs, e);
412				}
413
414				//DDCShanonp
415				//Hack to fix JIRA ticket JIRA-27191 as the plugin does not know how to handle
416				//checkbox groups and there is no event to target externally that happens after all validation
417				//is complete AND displayed in the DOM
418				//If left to function normally, all checkboxes in the required group will get a validation message
419				//even if one is selected and will pass the validation
420				var $checkboxGroupContainer = $('span.group-required'),
421					checkboxGroupErrors = $checkboxGroupContainer.find('strong.errors'),
422					isSingleCheckboxChecked = $checkboxGroupContainer.find('input[type="checkbox"]:checked').length > 0,
423					$validationMessageContainer = $('label[checkbox-group-validation-error]');
424
425				//Clear all of the individual checkbox validation messages
426				checkboxGroupErrors.hide();
427
428				//if none are checked, add a single validation message to the top of the group
429				if (!isSingleCheckboxChecked) {
430					//grab one already placed there by the plug in and make it visible
431					var oneError = $(checkboxGroupErrors[0]).show();
432					//place it in the container
433					$validationMessageContainer.empty().append(oneError);
434				} else {
435					//All good, remove the single validator message
436					$validationMessageContainer.empty();
437				}
438				//End plug in hack
439
440				return self;
441			},
442
443			reset: function(els) {
444				els = els || inputs;
445				els.removeClass(conf.errorClass).each(function() {
446					var msg = $(this).data("msg.el");
447					if (msg) {
448						msg.remove();
449						$(this).data("msg.el", null);
450					}
451				}).unbind(conf.errorInputEvent || '');
452				this.invalidatedErrors = [];
453				return self;
454			},
455
456			destroy: function() {
457				form.unbind(conf.formEvent).unbind("reset.V");
458				inputs.unbind(conf.inputEvent || '').unbind("change.V");
459				return self.reset();
460			},
461
462			// UTF8 support
463			getRegex: function(type, value) {
464				var regex,
465						hasXreg = false,
466						conf = this.getConf(),
467						regexList = conf.regexList,
468						result = false;
469
470				if(typeof(window.XRegExp) === 'function')
471					hasXreg = true;
472
473				if(hasXreg && !conf.isAscii){
474					regex = regexList[type]['utf8'];
475					result = XRegExp(regex, 'i').test(value);
476				} else {
477					regex = regexList[type]['ascii'];
478					result = regex.test(value);
479				}
480
481				return result;
482			},
483
484//{{{  checkValidity() - flesh and bone of this tool
485
486			/** @returns boolean */
487			checkValidity: function(els, e) {
488
489				els = els || inputs;
490				els = els.not(":disabled");
491				if (!els.length) { return true; }
492
493				e = e || $.Event();
494
495				// DE736383 / US1550942: capture the original event type before fire.trigger
496				// mutates e.type. Used below to decide whether to scroll-to-first-error.
497				var originalEventType = e.type;
498
499				// onBeforeValidate
500				// DE736383: when the passed-in event is an upstream form event (e.g. submit)
501				// that another handler already preventDefault'd (recaptcha-loader on toyota
502				// minimal layouts does this), reusing the same event for onBeforeValidate
503				// makes isDefaultPrevented return true and bail before validation runs —
504				// no scroll-to-first-error animate, no field shown to the user. Use a fresh
505				// jQuery event so onBeforeValidate's prevented state reflects only handlers
506				// of this validation cycle.
507				if (originalEventType && originalEventType !== 'onBeforeValidate') {
508					e = $.Event('onBeforeValidate');
509				} else {
510					e.type = "onBeforeValidate";
511				}
512				fire.trigger(e, [els]);
513				if (e.isDefaultPrevented()) { return e.result; }
514
515				var errs = this.invalidatedErrors || [],
516						event = conf.errorInputEvent + ".v";
517
518				// loop trough the inputs
519				els.not(":radio:not(:checked)").each(function() {
520
521						// field and it's error message container
522						var msgs = [],
523								 el = $(this).unbind(event).data("messages", msgs);
524
525						// loop all validator functions
526						$.each(fns, function() {
527								var fn = this, match = fn[0];
528
529								// match found
530								if (el.filter(match).length) {
531
532										// execute a validator function
533										var returnValue = fn[1].call(self, el, el.val());
534
535										// validation failed. multiple substitutions can be returned with an array
536										if (returnValue !== true) {
537
538												// onBeforeFail
539												e.type = "onBeforeFail";
540												fire.trigger(e, [el, match]);
541												if (e.isDefaultPrevented()) { return false; }
542
543												// overridden custom message
544												var msg = el.attr(conf.messageAttr);
545												if (msg) {
546													msgs = [msg];
547													return false;
548												} else if (el.filter("[name*=tcpaConsent]").length > 0){
549													pushMessage(msgs, "[consent]", returnValue)
550												} else {
551													pushMessage(msgs, match, returnValue);
552												}
553										} 
554									}
555								
556						});
557
558						if (msgs.length) {
559
560								errs.push({input: el, messages: msgs});
561
562								// trigger HTML5 ininvalid event
563								el.trigger("OI", [msgs]);
564
565								// begin validating upon error event type (such as keyup or change)
566								if (conf.errorInputEvent) {
567									el.bind(event, function(e) {
568										self.checkValidity(el, e);
569									});
570								}
571						}
572						
573						if (conf.singleError && errs.length) { return false; }
574
575
576				});
577
578				// validation done. now check that we have a proper effect at hand
579				var eff = effects[conf.effect];
580				if (!eff) { throw "Validator: cannot find effect \"" + conf.effect + "\""; }
581
582				// errors found
583				if (errs.length) {
584
585						scrollInput = $(errs[0]['input']);
586
587						// Scroll to first error if not in a dialog: JIRA-10524
588						// But provide a behavior class override: SPECTRE-26 ddcorsonb
589						//
590						// DE736383 / US1550942: when checkValidity is called from a per-field
591						// event (such as input/change as the user fixes a field), running this
592						// scroll-to-first-error animate yanks the page around on every keystroke
593						// — the field the user is typing in gets pulled under the sticky header.
594						// It also interferes with submit clicks (layout shift between mousedown
595						// and mouseup makes the click miss the button). Limit the auto-scroll to
596						// form-submit-time validation, where it's actually useful.
597						// Only auto-scroll for an actual form submit. Other call sites
598					// (per-field input/change handlers, programmatic form.checkValidity()
599					// calls from tracking widgets, etc.) firing animate too cause competing
600					// scrolls — the page jitters before settling.
601					if (originalEventType === 'submit' && scrollInput.parents('.ui-dialog').size() === 0 && !scrollInput.parents('form').hasClass('validate-no-scroll')) {
602								// DE736383: account for the sticky header so the field lands
603								// below it, not underneath it. Without this the page scrolls
604								// the field to viewport top and any anchor / sticky-header
605								// watcher then re-corrects, producing a janky pop.
606								var headerAdj = (window.DDC && typeof DDC.calculateHeaderOffset === 'function')
607									? DDC.calculateHeaderOffset() : 0;
608								if (!headerAdj) { headerAdj = 10; }
609								// US1971730: scroll to the field's group (label + input) instead of the
610								// bare input. On the adaptive mobile layout the label stacks ABOVE the
611								// input, so landing the input just below the header tucked the label under
612								// it. Targeting .form-group lands the label below the header; desktop is
613								// unaffected (the label sits beside the input there).
614								var scrollTarget = scrollInput.closest('.form-group, .control-group');
615								if (!scrollTarget.length) { scrollTarget = scrollInput; }
616								offset = scrollTarget.offset();
617								offset = (offset.top - headerAdj);
618								if (offset < 0) {
619										offset = 0;
620								}
621								// DE736383: rapid repeat clicks on submit queue animates back-to-back
622								// because jQuery's default animate queue is FIFO. Each click adds a new
623								// animate to the queue. By the time the user clicks again, target is
624								// usually unchanged so the queued animate has nothing to scroll to once
625								// the first one completes. User perceives this as "second click did
626								// nothing" — has to wait for the queue to drain. Calling .stop(true, false)
627								// before .animate clears any queued animations and stops the in-flight
628								// one, so each click starts fresh from current scroll position.
629								$('html, body').stop(true, false).animate({scrollTop: offset}, 500);
630						}
631
632						this.invalidatedErrors = [];
633						self.invalidate(errs, e);
634						return false;
635
636				// no errors
637				} else {
638
639						// call the effect
640						eff[1].call(self, els, e);
641
642						// onSuccess callback
643						e.type = "onSuccess";
644						fire.trigger(e, [els]);
645
646						els.unbind(event);
647				}
648
649				return true;
650			}
651		});
652
653		// callbacks
654		$.each("onBeforeValidate,onBeforeFail,onFail,onSuccess".split(","), function(i, name) {
655
656			// configuration
657			if ($.isFunction(conf[name])) {
658				$(self).bind(name, conf[name]);
659			}
660
661			// API methods
662			self[name] = function(fn) {
663				$(self).bind(name, fn);
664				return self;
665			};
666		});
667
668		// form validation
669		if (conf.formEvent) {
670			form.bind(conf.formEvent, function(e) {
671				// submit happens here
672				if (!self.checkValidity(null, e)) {
673					return e.preventDefault();
674				}
675				// passed all validation
676				form.trigger("onPost");
677			});
678		}
679
680		// form reset
681		form.bind("reset.V", function() {
682			self.reset();
683		});
684
685		// disable browser's default validation mechanism
686		if (inputs[0] && inputs[0].validity) {
687			inputs.each(function() {
688				this.oninvalid = function() {
689					return false;
690				};
691			});
692		}
693
694		// Web Forms 2.0 compatibility
695		if (form[0]) {
696			form[0].checkValidity = self.checkValidity;
697		}
698
699		// input validation
700		if (conf.inputEvent) {
701			inputs.bind(conf.inputEvent, function(e) {
702				self.checkValidity($(this), e);
703			});
704		}
705
706		// checkboxes, selects and radios are checked separately
707		inputs.filter(":checkbox, select").filter("[required]").bind("change.V", function(e) {
708			var el = $(this);
709			if (this.checked || (el.is("select") && $(this).val())) {
710				effects[conf.effect][1].call(self, el, e);
711			}
712		});
713
714		var radios = inputs.filter(":radio").change(function(e) {
715			self.checkValidity(radios, e);
716		});
717
718		// reposition tooltips when window is resized
719		$(window).resize(function() {
720			self.reflow();
721		});
722	}
723
724	// jQuery plugin initialization
725	$.fn.validator = function(conf) {
726		var instance = this.data("validator");
727
728		// destroy existing instance
729		if (instance) {
730			instance.destroy();
731			this.removeData("validator");
732		}
733
734		// configuration
735		conf = $.extend(true, {}, v.conf, conf);
736
737		// selector is a form
738		if (this.is("form")) {
739
740			return this.each(function() {
741				var form = $(this);
742				// form charset for regex
743				if (form.attr('charset') && form.attr('charset') === 'utf8')
744					conf.isAscii = false;
745
746				instance = new Validator(form.find(":input"), form, conf);
747				form.data("validator", instance);
748			});
749		} else {
750			instance = new Validator(this, this.eq(0).closest("form"), conf);
751			return this.data("validator", instance);
752		}
753	};
754})(jQuery);

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.