PageSourceSearch

https://avtonavix.ru/bitrix/templates/desktop//js/countdown/jlivetime.js

js avtonavix.ru collected 2026-09-25 03:09:50 UTC 28,055 bytes, 656 lines download raw bytes

1'use strict';
2/*
3* jLiveTime
4*   jQuery plugin for live timestamps, countdowns, time-ago, and timers.
5*/
6(function($){
7
8    var lt = $.livetime = $.livetime || {};
9    lt.version = '0.0.13';
10
11    lt.localTimeOffset = null;
12
13    var defaultOptions = {
14        datetimeSelector: '[datetime]',
15        datetimeAttribute: 'datetime',
16        durationAttribute: 'data-duration',
17        dateLabelSelector: '[data-time-label]',
18        triggerRefreshComplete: true,
19        serverTimeUrl: null,
20        autostart: false
21    };
22    var defaultFormats = {
23        _default: [
24                [-24*3600*30*11, 'yyyy MMMM d at H:mm'],
25                [-24*3600*30, 'in td_M months d_d days, MMMM d at H:mm'],
26                [-24*3600*7, 'in td_d days, MMMM d at H:mm'],
27                [-24*3600*2, 'next eeee, MMMM d at H:mm'],
28                [-2*3600, 'in td_h hours d_m minutes, at H:mm'],
29                [-60*5, 'in td_m minutes, at H:mm:ss'],
30                [-60, 'in td_m minutes d_s seconds, at H:mm:ss'],
31                [0, 'in td_s seconds, at H:mm:ss'],
32                [60, 'td_s seconds ago, at H:mm:ss'],
33                [60*5, 'td_m minutes d_s seconds ago, at H:mm:ss'],
34                [2*3600, 'td_m minutes ago, at H:mm:ss'],
35                [24*3600*2 , 'td_h hours d_m minutes ago, at H:mm'],
36                [24*3600*7 , 'last eeee, MMMM d at H:mm'],
37                [24*3600*30, 'MMMM d at H:mm'],
38                ['yyyy MMMM d at H:mm']
39            ],
40        seconds: [
41            [0, 'in td_hh:d_mm:d_ss'],
42            ['td_hh:d_mm:d_ss']
43        ],
44        milliseconds: [
45            [0, 'in td_hh:d_mm:d_ss.d_fff'],
46            ['td_hh:d_mm:d_ss.d_fff']
47        ],
48        humanized: [
49            [-360*24*3600, 'MMMM d, yyyy'],
50            ['daystart-'+7*24*3600, 'MMMM d at h:mm tt'],
51            ['daystart-'+24*3600, 'next eeee at h:mm tt'],
52            ['daystart', 'tomorrow at h:mm tt'],
53            [-7200, 'today at h:mm tt'],
54            [-3600, 'in about an hour'],
55            [-120, 'in td_m minutes'],
56            [-60, 'in about a minute'],
57            [0, 'in a few seconds'],
58            [60, 'a few seconds ago'],
59            [120, 'about a minute ago'],
60            [3600, 'td_m minutes ago'],
61            [7200, 'about an hour ago'],
62            [5*3600, 'td_h hours ago'],
63            ['dayend', 'today at h:mm tt'],
64            ['dayend+'+24*3600, 'yesterday at h:mm tt'],
65            ['dayend+'+7*24*3600, 'last eeee at h:mm tt'],
66            [360*24*3600, 'MMMM d at h:mm tt'],
67            ['MMMM d, yyyy']
68        ],
69        fulldate: 'eee MMM d yyyy at h:mm:ss tt',
70        shortdate: 'MMM d yyyy',
71        _default_tooltip: '#fulldate',
72        _in: [[0,'in'],['']],
73        remaining: [[0,'remaining'],['']],
74        ago: [[0,''],['ago']],
75        elapsed: [[0,''],['elapsed']]
76    };
77
78    var options = lt.options = $.extend(true, defaultOptions, lt.options);
79    options.formats = $.extend(defaultFormats, options.formats);
80
81    var padLeft = function(num, digits) {
82        if (digits < 2) {
83            return num;
84        }
85        var str = num+'';
86        while (str.length < digits) {
87            str = '0' + str;
88        }
89        return str;
90    };
91
92    var timeUnits = {
93        // years
94        y: 1000 * 3600 * 24 * 365.25,
95        // months
96        M: 1000 * 3600 * 24 * 30,
97        // weeks
98        w: 1000 * 3600 * 24 * 7,
99        // days (from month)
100        d: 1000 * 3600 * 24,
101        // days (from week)
102        e: 1000 * 3600 * 24,
103        // hours
104        h: 1000 * 3600,
105        // minutes
106        m: 1000 * 60,
107        // seconds
108        s: 1000,
109        // milliseconds
110        f: 1
111    };
112
113    var parentTimeUnits = {
114        M: 'y',
115        w: 'M',
116        d: 'M',
117        e: 'w',
118        h: 'd',
119        m: 'h',
120        s: 'm',
121        f: 's'
122    };
123
124    var unitNames = {
125        M: ['January','February','March','April','May','June','July','August','September','October','November','December'],
126        e: ['Sunday','Monday','Tuesday','Wednesday','Thursday','Friday','Saturday']
127    };
128
129    var log = (window.console && typeof window.console.log == 'function') ?
130            function(){ console.log.call(console, arguments); } :
131            function(){};
132
133    var datetimeRegex = /^(\d{4})\-(\d{2})\-(\d{2})(?:[ T](\d{2}):(\d{2}):(\d{2})(?:\.(\d+))?(.*))?$/;
134    var timezoneRegex = /^([+\-]?)(\d{2}):(\d{2})$/;
135
136    // alternative strin date parsers, the first supported by browser will be used
137    var dateStringParsers = [
138        // use native constructor (fastest)
139        function(str) {
140            return new Date(str);
141        },
142        // use native constructor, allowing space instead of T
143        function(str) {
144            return new Date(str.slice(10,11)!==' ' ? str : str.slice(0,10) + 'T' + str.slice(11));
145        },
146        // use regex (slowest, but safe)
147        function(str) {
148            var match = datetimeRegex.exec(str);
149            var timezoneOffset = 0;
150            if (match) {
151                // remove leading zeros
152                for (var i=1; i < 8; i++) {
153                    var matchString = match[i];
154                    if (typeof matchString == 'string' && matchString.length > 1 && matchString.slice(0,1) === '0') {
155                        match[i] = matchString.slice(0,-1).replace(/^0+/, '') + matchString.slice(-1);
156                    }
157                }
158                if (typeof match[4] == 'undefined' || match[4] === '' || match[4] === null) {
159                    return new Date(Date.UTC(parseInt(match[1]), parseInt(match[2])-1, parseInt(match[3])));
160                }
161                if (match[8] && match[8] !== 'Z' && match[8] !== 'GMT' && match[8] !== 'UTC') {
162                    var timezoneMatch = timezoneRegex.exec(match[8]);
163                    if (timezoneMatch) {
164                        timezoneOffset = (timezoneMatch[0] === '-' ? -1 : 1) * (parseInt(timezoneMatch[2]) * 60 + parseInt(timezoneMatch[3])) * 60000;
165                    } else {
166                        throw new Error('invalid datetime format');
167                    }
168                }
169                return new Date(Date.UTC(parseInt(match[1]), parseInt(match[2])-1, parseInt(match[3]),
170                    parseInt(match[4]), parseInt(match[5]), parseInt(match[6]), match[7] ? parseInt(match[7]) : 0) + timezoneOffset);
171            }
172            throw new Error('invalid datetime format');
173        }
174    ];
175    var validDateStringParser = dateStringParsers[dateStringParsers.length-1];
176
177    var detectValidDateParser = function(){
178        var valid = false;
179        var index = 0;
180        var tests = {};
181        tests['2012-12-04 23:59:59.999Z'] = Date.UTC(2012,11,4,23,59,59,999);
182        tests['2012-01-31T00:54:02'] = Date.UTC(2012,0,31,0,54,2);
183        tests['2012-12-22T18:30:00-03:00'] = Date.UTC(2012,11,22,21,30,0);
184        tests['1987-02-28'] = Date.UTC(1987,1,28);
185
186        while (!valid && index < dateStringParsers.length) {
187            try {
188                valid = true;
189                for (var str in tests) {
190                    if (tests[str] !== dateStringParsers[index](str).getTime()) {
191                        valid = false;
192                        break;
193                    }
194                }
195                if (valid) {
196                    validDateStringParser = dateStringParsers[index];
197                }
198            } catch(err) {
199                valid = false;
200            }
201            if (!valid) {
202                index++;
203            }
204        }
205    };
206
207    detectValidDateParser();
208
209    lt.parseDate = function(timestamp) {
210        if (typeof str == 'number' || /^[0-9]+$/.test(timestamp)) {
211            return new Date(parseInt(timestamp,10));
212        } else {
213            try {
214                return validDateStringParser(timestamp);
215            } catch(err) {
216                throw new Error('error parsing datetime "' + timestamp + '": ' + err);
217            }
218        }
219    };
220
221    lt.now = function(){
222        if (lt.localTimeOffset === null){
223            // local time offset unknown yet
224            lt.getLocalTimeOffset();
225        }
226        var localTimeOffset = lt.localTimeOffset || 0;
227        var now = new Date();
228        var nowUTC = now.getTime() - now.getTimezoneOffset()*60*1000 + localTimeOffset;
229        return nowUTC;
230    };
231
232    lt.getLocalTimeOffset = function(){
233        var cookieOffset, now, dateHeader, match, serverTime;
234
235        if (!options.serverTimeUrl) {
236            lt.localTimeOffset = 0;
237            return;
238        }
239
240        if (!lt.localTimeOffsetRequested && (typeof lt.localTimeOffset == 'undefined' || lt.localTimeOffset === null)) {
241            // get milliseconds of time diff between server and client. positive means client ahead
242            lt.localTimeOffsetRequested = true;
243            lt.localTimeOffset = 0;
244
245            var storedOffset = null;
246            if (window.sessionStorage) {
247                storedOffset = window.sessionStorage.getItem('jlivetime-localtimeoffset');
248            } else {
249                try {
250                    storedOffset = /localtimeoffset=([0-9]+)[^0-9]?/i.exec(document.cookie)[1];
251                } catch (err) {
252                    storedOffset = null;
253                }
254            }
255
256            if (typeof storedOffset !='undefined' && storedOffset !== null) {
257                lt.localTimeOffset = parseInt(storedOffset, 10);
258            } else {
259                // offset not obtained yet, calculate with an ajax request
260                $.ajax({
261                    url: options.serverTimeUrl,
262                    cache: false,
263                    success: function (data, status, req) {
264                        // local time
265                        now = new Date();
266                        // try to get server time from response Date header
267                        dateHeader = req.getResponseHeader('Date');
268                        var serverTime = new Date(dateHeader);
269                        // milliseconds of time diff between server and client.  positive means client ahead
270                        lt.localTimeOffset = now.getTime() - serverTime.getTime();
271                        // save offset for this browser session
272                        if (window.sessionStorage) {
273                            window.sessionStorage.setItem('jlivetime-localtimeoffset', lt.localTimeOffset);
274                        } else {
275                            document.cookie = 'localtimeoffset='+lt.localTimeOffset;
276                        }
277                        if (lt.localTimeOffset > 10000) {
278                            log('WARNING: local time offset is ' + Math.round(lt.localTimeOffset  / 1000) + 's');
279                        }
280                    }
281                });
282            }
283        }
284    };
285
286    lt.millisecondsFromNow = function(date){
287        var ts = date;
288        if (date instanceof Date){
289            ts = (date.getTime() + date.getTimezoneOffset()*60*1000);
290        }
291        return lt.now() - ts;
292    };
293
294    lt.refresh = function(element) {
295
296        var start = new Date().getTime();
297
298        var root = $(element);
299        root.addClass('jlivetime-active');
300
301        // if there's a pending refresh, cancel it
302        var timerTimeout = root.data('jlivetime-timeout');
303        if (typeof timerTimeout !== 'undefined'){
304            clearTimeout(timerTimeout);
305        }
306
307        var nextRefreshMs = 60000;
308        var timestamps = root.find(options.datetimeSelector);
309        if (root.is(options.datetimeSelector)) {
310            timestamps = timestamps.add(root);
311        }
312        timestamps.each(function(){
313            var tsNextRefreshMs = 60000;
314            var tsElem = $(this);
315            var tsString = tsElem.attr(options.datetimeAttribute);
316            if (tsString) {
317                var ts;
318                try {
319                    if (tsString.indexOf('-') > 0) {
320                        var date = lt.parseDate(tsString);
321                        ts = date.getTime() - date.getTimezoneOffset() * 60000;
322                    } else {
323                        ts = parseInt(tsString, 10);
324                    }
325                } catch (err) {
326                    // error parsing timestamp
327                    log('error parsing timestamp: '+err);
328                    ts = 0;
329                }
330                if (ts > 0) {
331                    var timeDiff = lt.millisecondsFromNow(ts);
332                    var duration = 0;
333                    try {
334                        var durationString = tsElem.attr(options.durationAttribute);
335                        if (durationString) {
336                            duration = parseInt(durationString, 10);
337                        }
338                    } catch (err) {
339                        // error parsing timestamp
340                        log('error parsing duration: '+err);
341                        duration = 0;
342                    }
343                    tsElem.data('time-diff', timeDiff);
344                    var labels = tsElem.find(options.dateLabelSelector);
345                    if (tsElem.is(options.dateLabelSelector)) {
346                        labels = labels.add(tsElem);
347                    }
348                    labels.each(function() {
349                        var label = $(this);
350                        var htmlChanged = false;
351                        var tooltipChanged = false;
352                        var formatResult;
353                        try {
354                            formatResult = lt.format(ts, timeDiff, duration, label.data('time-label') || '#_default');
355                        } catch (error) {
356                            log('error updating time label: ' + error);
357                            formatResult = { value: '-', nextRefreshMs: 60000 };
358                        }
359                        if (formatResult.value !== null && typeof formatResult.value !== 'undefined') {
360                            if (label.html()!==formatResult.value) {
361                                label.html(formatResult.value.replace(':', '').replace(':', ''));
362                                htmlChanged = true;
363                            }
364                        }
365                        if (formatResult.nextRefreshMs) {
366                            // next refresh detected when formatting (eg. when format rule will change)
367                            tsNextRefreshMs = Math.min(tsNextRefreshMs, formatResult.nextRefreshMs);
368                        }
369                        if (typeof label.data('time-tooltip') !== 'undefined') {
370                            formatResult = lt.format(ts, timeDiff, duration, label.data('time-tooltip') || '#_default_tooltip');
371                            if (formatResult.value !== null && typeof formatResult.value !== 'undefined') {
372                                if (label.attr('title') !== formatResult.value) {
373                                    label.attr('title', formatResult.value);
374                                    if (typeof label.attr('data-original-title') != 'undefined') {
375                                        label.attr('data-original-title', formatResult.value);
376                                    }
377                                    tooltipChanged = true;
378                                }
379                            }
380                            if (formatResult.nextRefreshMs) {
381                                // next refresh detected when formatting (eg. when format rule will change)
382                                tsNextRefreshMs = Math.min(tsNextRefreshMs, formatResult.nextRefreshMs);
383                            }
384                        }
385
386                        if ((htmlChanged || tooltipChanged) && options.triggerRefreshComplete) {
387                            label.trigger('refreshComplete', {
388                                nextRefreshMs: tsNextRefreshMs,
389                                htmlChanged: htmlChanged,
390                                tooltipChanged: tooltipChanged,
391                                refreshElapsedTime: new Date().getTime() - start
392                            });
393                        }
394                    });
395                    if (timeDiff < 0){
396                        // refresh when time comes
397                        tsNextRefreshMs = Math.min(tsNextRefreshMs, -timeDiff);
398                    }
399                    nextRefreshMs = Math.min(nextRefreshMs, tsNextRefreshMs);
400                }
401            }
402            tsElem.data('jlivetime-nextrefresh',new Date().getTime()+nextRefreshMs);
403        });
404        nextRefreshMs = Math.max(nextRefreshMs, 65);
405
406        // schedule the next refresh
407        root.data('jlivetime-timeout', setTimeout(function(){
408            lt.refresh(element);
409        }, nextRefreshMs));
410    };
411
412    lt.disable = function(element) {
413        var root = $(element);
414
415        // if there's a pending refresh, cancel it
416        var timerTimeout = root.data('jlivetime-timeout');
417        if (typeof timerTimeout !== 'undefined'){
418            clearTimeout(timerTimeout);
419        }
420        root.removeClass('jlivetime-active');
421    };
422
423    lt.formatPart = function(ts, timeDiff, format){
424        if (format==='[]'){
425            return {value: ''};
426        }
427        var match = /^(t?d_)?(([yMdewHhmsft])\3*)$/.exec(format);
428        if (match) {
429            if (!match[1]) {
430                // regular date format
431                var date = new Date(ts + new Date(ts).getTimezoneOffset()*60*1000);
432                switch (match[3]) {
433                    case 'y':
434                        return {
435                            value: padLeft(date.getFullYear(), match[2].length)
436                        };
437                    case 'M':
438                        // use name if length > 2
439                        var month =  match[2].length > 2 ? unitNames.M[date.getMonth()] : padLeft(date.getMonth() + 1, match[2].length);
440                        if (match[2].length === 3) {
441                            month = month.slice(0,3);
442                        }
443                        return {
444                            value: month
445                        };
446                    case 'w':
447                        return {
448                            value: padLeft(Math.floor(date.getDate() / 7) + 1, match[2].length)
449                        };
450                    case 'd':
451                        return {
452                            value: padLeft(date.getDate(), match[2].length)
453                        };
454                    case 'e':
455                        // use name if length > 1
456                        var weekday =  match[2].length > 1 ? unitNames.e[date.getDay()] : date.getDay() + 1;
457                        if (match[2].length > 1 && match[2].length <= 3) {
458                            weekday = weekday.slice(0,match[2].length);
459                        }
460                        return {
461                            value: weekday
462                        };
463                    case 'h':
464                        return {
465                            value: padLeft(date.getHours() > 12 ? date.getHours() - 12 :
466                                 (date.getHours() === 0 ? 12 : date.getHours()), match[2].length)
467                        };
468                    case 'H':
469                        return {
470                            value: padLeft(date.getHours(), match[2].length)
471                        };
472                    case 'm':
473                        return {
474                            value: padLeft(date.getMinutes(), match[2].length)
475                        };
476                    case 's':
477                        return {
478                            value: padLeft(date.getSeconds(), match[2].length)
479                        };
480                    case 'f':
481                        return {
482                            value: padLeft(date.getMilliseconds(), match[2].length)
483                        };
484                    case 't':
485                        var ampm = date.getHours() >= 12 ? 'pm' : 'am';
486                        return {
487                            value: match[2].length > 1 ? ampm : ampm.slice(0,1)
488                        };
489                }
490            } else {
491                var timeUnit = timeUnits[match[3]];
492                if (timeUnit) {
493                    var nextRefreshMs;
494                    // time difference format
495                    nextRefreshMs = Math.max(65, timeDiff > 0 ? timeUnit - timeDiff % timeUnit : -timeDiff % timeUnit);
496                    if (match[1] == 'td_') {
497                        // total in unit
498                        return {
499                            value: padLeft(Math.floor(Math.abs(timeDiff / timeUnit)), match[2].length),
500                            nextRefreshMs: nextRefreshMs
501                        };
502                    } else if (match[1] == 'd_') {
503                        var parentTimeUnit = timeUnits[parentTimeUnits[match[3]]];
504                        return {
505                            value: padLeft(Math.floor(Math.abs(parentTimeUnit ? timeDiff % parentTimeUnit : timeDiff) / timeUnit), match[2].length),
506                            nextRefreshMs: nextRefreshMs
507                        };
508                    }
509                }
510            }
511        }
512        return {
513            value: format
514        };
515    };
516
517    lt.getFormatExpression = function(ts, timeDiff, duration, format) {
518        var nextRefreshMs = 60000;
519        var format_expression = format;
520        if (format_expression === null || format_expression === 'null') {
521            format_expression = null;
522        } else {
523            if (typeof format_expression == 'string' && format_expression.slice(0,1) === '#') {
524                format_expression = options.formats[format_expression.slice(1)];
525                if (typeof format_expression == 'undefined') {
526                    throw new Error('time format not found: ' + format);
527                }
528            }
529            var localTs = ts + new Date().getTimezoneOffset() * 60 * 1000;
530            var dayStartLimit, dayEndLimit;
531            if (format_expression instanceof Array) {
532                var fmtLength = format_expression.length;
533                for (var i=0; i< fmtLength; i++) {
534                    if (format_expression[i].length === 1) {
535                        format_expression = format_expression[i][0];
536                        break;
537                    } else {
538                        var rangeLimit = format_expression[i][0];
539                        if (typeof rangeLimit == 'string') {
540                            var limitParameters = /^([\w_]+)([+\-]\d+)?/i.exec(rangeLimit);
541                            if (!limitParameters) {
542                                log('error parsing format range: ' + rangeLimit);
543                                return {
544                                    nextRefreshMs: 1000,
545                                    format_expression: '-'
546                                }
547                            }
548                            var limitKeyword = limitParameters[1];
549                            if (limitKeyword === 'end') {
550                                rangeLimit = duration;
551                            } else if (limitKeyword === 'daystart') {
552                                if (!dayStartLimit) {
553                                    dayStartLimit = new Date(localTs).setHours(0, 0, 0, 0) - localTs;
554                                }
555                                rangeLimit = dayStartLimit;
556                            }
556 else if (limitKeyword === 'dayend') {
557                                if (!dayEndLimit) {
558                                    dayEndLimit = new Date(localTs).setHours(23, 59, 59, 999) - localTs;
559                                }
560                                rangeLimit = dayEndLimit;
561                            } else {
562                                log('error parsing format range, unknown keyword: ' + limitKeyword);
563                                return {
564                                    nextRefreshMs: 1000,
565                                    format_expression: '-'
566                                }
567                            }
568                            if (limitParameters[2]) {
569                                rangeLimit += parseInt(limitParameters[2], 10) * 1000;
570                            }
571                        } else {
572                            rangeLimit = rangeLimit * 1000;
573                        }
574                        if (rangeLimit >= timeDiff) {
575                            // refresh when format range changes
576                            nextRefreshMs = Math.min(nextRefreshMs, rangeLimit - timeDiff);
577                            format_expression = format_expression[i][1];
578                            break;
579                        }
580                    }
581                }
582                if (format_expression instanceof Array) {
583                    format_expression = '<unknown>';
584                }
585            }
586            if (typeof format_expression == 'string' && format_expression.slice(0,1) === '#') {
587                return lt.getFormatExpression(ts, timeDiff, duration, format_expression);
588            }
589        }
590        return {
591            expression: format_expression,
592            nextRefreshMs: nextRefreshMs
593        };
594    };
595
596    lt.format = function(ts, timeDiff, duration, format){
597        var partRegex = /\[?([a-z_]+)\]?/gim;
598        var value = [];
599        var lastindex = 0;
600        var fmt = lt.getFormatExpression(ts, timeDiff, duration, format);
601
602        if (fmt.expression === null) {
603            return fmt;
604        }
605        var match = partRegex.exec(fmt.expression);
606        while (match) {
607            value.push(fmt.expression.slice(lastindex, match.index));
608            if (match[0].length != match[1].length){
609                value.push(match[1]);
610            }else{
611                var format_expression = match[0];
612                var ts_offset = 0;
613                if (format_expression.slice(0,4) === 'end_') {
614                    format_expression = format_expression.slice(4);
615                    ts_offset = duration;
616                }
617                var formatPartResult = lt.formatPart(ts + ts_offset, timeDiff - ts_offset, format_expression);
618                value.push(formatPartResult.value);
619                if (formatPartResult.nextRefreshMs){
620                    fmt.nextRefreshMs = Math.min(fmt.nextRefreshMs, formatPartResult.nextRefreshMs);
621                }
622            }
623            lastindex = match.index + match[0].length;
624            match = partRegex.exec(fmt.expression);
625        }
626        value.push(fmt.expression.slice(lastindex));
627
628        fmt.value = value.join('');
629        return fmt;
630    };
631
632    $.fn.livetime = function(enable){
633        this.each(function(){
634            var elem = $(this);
635            if (!elem.is('.jlivetime-active')) {
636                // if element is an active livetime container, call livetime() on that container
637                var activeParent = $(this).parent('.jlivetime-active');
638                if (activeParent.length) {
639                    elem = activeParent;
640                }
641            }
642            if (enable === false) {
643                lt.disable(elem.get(0));
644            } else {
645                lt.refresh(elem.get(0));
646            }
647        });
648    };
649
650    $(function(){
651        if (lt.options.autostart) {
652            $(lt.options.autostart.selector || 'body').livetime();
653        }
654    });
655
656})(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.