PageSourceSearch

https://openstreetmap.cz/theme/lib/exif.js?5ca6fbd3

js openstreetmap.cz collected 2026-10-01 11:52:55 UTC 30,092 bytes, 805 lines download raw bytes

vendor: 30,092 bytes, lines 1-805
1(function () {
2
3    var debug = false;
4
5    var root = this;
6
7    var EXIF = function (obj) {
8        if (obj instanceof EXIF) return obj;
9        if (!(this instanceof EXIF)) return new EXIF(obj);
10        this.EXIFwrapped = obj;
11    };
12
13    if (typeof exports !== 'undefined') {
14        if (typeof module !== 'undefined' && module.exports) {
15            exports = module.exports = EXIF;
16        }
17        exports.EXIF = EXIF;
18    } else {
19        root.EXIF = EXIF;
20    }
21
22    var ExifTags = EXIF.Tags = {
23
24        // version tags
25        0x9000: "ExifVersion",             // EXIF version
26        0xA000: "FlashpixVersion",         // Flashpix format version
27
28        // colorspace tags
29        0xA001: "ColorSpace",              // Color space information tag
30
31        // image configuration
32        0xA002: "PixelXDimension",         // Valid width of meaningful image
33        0xA003: "PixelYDimension",         // Valid height of meaningful image
34        0x9101: "ComponentsConfiguration", // Information about channels
35        0x9102: "CompressedBitsPerPixel",  // Compressed bits per pixel
36
37        // user information
38        0x927C: "MakerNote",               // Any desired information written by the manufacturer
39        0x9286: "UserComment",             // Comments by user
40
41        // related file
42        0xA004: "RelatedSoundFile",        // Name of related sound file
43
44        // date and time
45        0x9003: "DateTimeOriginal",        // Date and time when the original image was generated
46        0x9004: "DateTimeDigitized",       // Date and time when the image was stored digitally
47        0x9290: "SubsecTime",              // Fractions of seconds for DateTime
48        0x9291: "SubsecTimeOriginal",      // Fractions of seconds for DateTimeOriginal
49        0x9292: "SubsecTimeDigitized",     // Fractions of seconds for DateTimeDigitized
50
51        // picture-taking conditions
52        0x829A: "ExposureTime",            // Exposure time (in seconds)
53        0x829D: "FNumber",                 // F number
54        0x8822: "ExposureProgram",         // Exposure program
55        0x8824: "SpectralSensitivity",     // Spectral sensitivity
56        0x8827: "ISOSpeedRatings",         // ISO speed rating
57        0x8828: "OECF",                    // Optoelectric conversion factor
58        0x9201: "ShutterSpeedValue",       // Shutter speed
59        0x9202: "ApertureValue",           // Lens aperture
60        0x9203: "BrightnessValue",         // Value of brightness
61        0x9204: "ExposureBias",            // Exposure bias
62        0x9205: "MaxApertureValue",        // Smallest F number of lens
63        0x9206: "SubjectDistance",         // Distance to subject in meters
64        0x9207: "MeteringMode",            // Metering mode
65        0x9208: "LightSource",             // Kind of light source
66        0x9209: "Flash",                   // Flash status
67        0x9214: "SubjectArea",             // Location and area of main subject
68        0x920A: "FocalLength",             // Focal length of the lens in mm
69        0xA20B: "FlashEnergy",             // Strobe energy in BCPS
70        0xA20C: "SpatialFrequencyResponse",    //
71        0xA20E: "FocalPlaneXResolution",   // Number of pixels in width direction per FocalPlaneResolutionUnit
72        0xA20F: "FocalPlaneYResolution",   // Number of pixels in height direction per FocalPlaneResolutionUnit
73        0xA210: "FocalPlaneResolutionUnit",    // Unit for measuring FocalPlaneXResolution and FocalPlaneYResolution
74        0xA214: "SubjectLocation",         // Location of subject in image
75        0xA215: "ExposureIndex",           // Exposure index selected on camera
76        0xA217: "SensingMethod",           // Image sensor type
77        0xA300: "FileSource",              // Image source (3 == DSC)
78        0xA301: "SceneType",               // Scene type (1 == directly photographed)
79        0xA302: "CFAPattern",              // Color filter array geometric pattern
80        0xA401: "CustomRendered",          // Special processing
81        0xA402: "ExposureMode",            // Exposure mode
82        0xA403: "WhiteBalance",            // 1 = auto white balance, 2 = manual
83        0xA404: "DigitalZoomRation",       // Digital zoom ratio
84        0xA405: "FocalLengthIn35mmFilm",   // Equivalent foacl length assuming 35mm film camera (in mm)
85        0xA406: "SceneCaptureType",        // Type of scene
86        0xA407: "GainControl",             // Degree of overall image gain adjustment
87        0xA408: "Contrast",                // Direction of contrast processing applied by camera
88        0xA409: "Saturation",              // Direction of saturation processing applied by camera
89        0xA40A: "Sharpness",               // Direction of sharpness processing applied by camera
90        0xA40B: "DeviceSettingDescription",    //
91        0xA40C: "SubjectDistanceRange",    // Distance to subject
92
93        // other tags
94        0xA005: "InteroperabilityIFDPointer",
95        0xA420: "ImageUniqueID"            // Identifier assigned uniquely to each image
96    };
97
98    var TiffTags = EXIF.TiffTags = {
99        0x0100: "ImageWidth",
100        0x0101: "ImageHeight",
101        0x8769: "ExifIFDPointer",
102        0x8825: "GPSInfoIFDPointer",
103        0xA005: "InteroperabilityIFDPointer",
104        0x0102: "BitsPerSample",
105        0x0103: "Compression",
106        0x0106: "PhotometricInterpretation",
107        0x0112: "Orientation",
108        0x0115: "SamplesPerPixel",
109        0x011C: "PlanarConfiguration",
110        0x0212: "YCbCrSubSampling",
111        0x0213: "YCbCrPositioning",
112        0x011A: "XResolution",
113        0x011B: "YResolution",
114        0x0128: "ResolutionUnit",
115        0x0111: "StripOffsets",
116        0x0116: "RowsPerStrip",
117        0x0117: "StripByteCounts",
118        0x0201: "JPEGInterchangeFormat",
119        0x0202: "JPEGInterchangeFormatLength",
120        0x012D: "TransferFunction",
121        0x013E: "WhitePoint",
122        0x013F: "PrimaryChromaticities",
123        0x0211: "YCbCrCoefficients",
124        0x0214: "ReferenceBlackWhite",
125        0x0132: "DateTime",
126        0x010E: "ImageDescription",
127        0x010F: "Make",
128        0x0110: "Model",
129        0x0131: "Software",
130        0x013B: "Artist",
131        0x8298: "Copyright"
132    };
133
134    var GPSTags = EXIF.GPSTags = {
135        0x0000: "GPSVersionID",
136        0x0001: "GPSLatitudeRef",
137        0x0002: "GPSLatitude",
138        0x0003: "GPSLongitudeRef",
139        0x0004: "GPSLongitude",
140        0x0005: "GPSAltitudeRef",
141        0x0006: "GPSAltitude",
142        0x0007: "GPSTimeStamp",
143        0x0008: "GPSSatellites",
144        0x0009: "GPSStatus",
145        0x000A: "GPSMeasureMode",
146        0x000B: "GPSDOP",
147        0x000C: "GPSSpeedRef",
148        0x000D: "GPSSpeed",
149        0x000E: "GPSTrackRef",
150        0x000F: "GPSTrack",
151        0x0010: "GPSImgDirectionRef",
152        0x0011: "GPSImgDirection",
153        0x0012: "GPSMapDatum",
154        0x0013: "GPSDestLatitudeRef",
155        0x0014: "GPSDestLatitude",
156        0x0015: "GPSDestLongitudeRef",
157        0x0016: "GPSDestLongitude",
158        0x0017: "GPSDestBearingRef",
159        0x0018: "GPSDestBearing",
160        0x0019: "GPSDestDistanceRef",
161        0x001A: "GPSDestDistance",
162        0x001B: "GPSProcessingMethod",
163        0x001C: "GPSAreaInformation",
164        0x001D: "GPSDateStamp",
165        0x001E: "GPSDifferential"
166    };
167
168    var StringValues = EXIF.StringValues = {
169        ExposureProgram: {
170            0: "Not defined",
171            1: "Manual",
172            2: "Normal program",
173            3: "Aperture priority",
174            4: "Shutter priority",
175            5: "Creative program",
176            6: "Action program",
177            7: "Portrait mode",
178            8: "Landscape mode"
179        },
180        MeteringMode: {
181            0: "Unknown",
182            1: "Average",
183            2: "CenterWeightedAverage",
184            3: "Spot",
185            4: "MultiSpot",
186            5: "Pattern",
187            6: "Partial",
188            255: "Other"
189        },
190        LightSource: {
191            0: "Unknown",
192            1: "Daylight",
193            2: "Fluorescent",
194            3: "Tungsten (incandescent light)",
195            4: "Flash",
196            9: "Fine weather",
197            10: "Cloudy weather",
198            11: "Shade",
199            12: "Daylight fluorescent (D 5700 - 7100K)",
200            13: "Day white fluorescent (N 4600 - 5400K)",
201            14: "Cool white fluorescent (W 3900 - 4500K)",
202            15: "White fluorescent (WW 3200 - 3700K)",
203            17: "Standard light A",
204            18: "Standard light B",
205            19: "Standard light C",
206            20: "D55",
207            21: "D65",
208            22: "D75",
209            23: "D50",
210            24: "ISO studio tungsten",
211            255: "Other"
212        },
213        Flash: {
214            0x0000: "Flash did not fire",
215            0x0001: "Flash fired",
216            0x0005: "Strobe return light not detected",
217            0x0007: "Strobe return light detected",
218            0x0009: "Flash fired, compulsory flash mode",
219            0x000D: "Flash fired, compulsory flash mode, return light not detected",
220            0x000F: "Flash fired, compulsory flash mode, return light detected",
221            0x0010: "Flash did not fire, compulsory flash mode",
222            0x0018: "Flash did not fire, auto mode",
223            0x0019: "Flash fired, auto mode",
224            0x001D: "Flash fired, auto mode, return light not detected",
225            0x001F: "Flash fired, auto mode, return light detected",
226            0x0020: "No flash function",
227            0x0041: "Flash fired, red-eye reduction mode",
228            0x0045: "Flash fired, red-eye reduction mode, return light not detected",
229            0x0047: "Flash fired, red-eye reduction mode, return light detected",
230            0x0049: "Flash fired, compulsory flash mode, red-eye reduction mode",
231            0x004D: "Flash fired, compulsory flash mode, red-eye reduction mode, return light not detected",
232            0x004F: "Flash fired, compulsory flash mode, red-eye reduction mode, return light detected",
233            0x0059: "Flash fired, auto mode, red-eye reduction mode",
234            0x005D: "Flash fired, auto mode, return light not detected, red-eye reduction mode",
235            0x005F: "Flash fired, auto mode, return light detected, red-eye reduction mode"
236        },
237        SensingMethod: {
238            1: "Not defined",
239            2: "One-chip color area sensor",
240            3: "Two-chip color area sensor",
241            4: "Three-chip color area sensor",
242            5: "Color sequential area sensor",
243            7: "Trilinear sensor",
244            8: "Color sequential linear sensor"
245        },
246        SceneCaptureType: {
247            0: "Standard",
248            1: "Landscape",
249            2: "Portrait",
250            3: "Night scene"
251        },
252        SceneType: {
253            1: "Directly photographed"
254        },
255        CustomRendered: {
256            0: "Normal process",
257            1: "Custom process"
258        },
259        WhiteBalance: {
260            0: "Auto white balance",
261            1: "Manual white balance"
262        },
263        GainControl: {
264            0: "None",
265            1: "Low gain up",
266            2: "High gain up",
267            3: "Low gain down",
268            4: "High gain down"
269        },
270        Contrast: {
271            0: "Normal",
272            1: "Soft",
273            2: "Hard"
274        },
275        Saturation: {
276            0: "Normal",
277            1: "Low saturation",
278            2: "High saturation"
279        },
280        Sharpness: {
281            0: "Normal",
282            1: "Soft",
283            2: "Hard"
284        },
285        SubjectDistanceRange: {
286            0: "Unknown",
287            1: "Macro",
288            2: "Close view",
289            3: "Distant view"
290        },
291        FileSource: {
292            3: "DSC"
293        },
294
295        Components: {
296            0: "",
297            1: "Y",
298            2: "Cb",
299            3: "Cr",
300            4: "R",
301            5: "G",
302            6: "B"
303        }
304    };
305
306    function addEvent(element, event, handler) {
307        if (element.addEventListener) {
308            element.addEventListener(event, handler, false);
309        } else if (element.attachEvent) {
310            element.attachEvent("on" + event, handler);
311        }
312    }
313
314    function imageHasData(img) {
315        return !!(img.exifdata);
316    }
317
318
319    function base64ToArrayBuffer(base64, contentType) {
320        contentType = contentType || base64.match(/^data\:([^\;]+)\;base64,/mi)[1] || ''; // e.g. 'data:image/jpeg;base64,...' => 'image/jpeg'
321        base64 = base64.replace(/^data\:([^\;]+)\;base64,/gmi, '');
322        var binary = atob(base64);
323        var len = binary.length;
324        var buffer = new ArrayBuffer(len);
325        var view = new Uint8Array(buffer);
326        for (var i = 0; i < len; i++) {
327            view[i] = binary.charCodeAt(i);
328        }
329        return buffer;
330    }
331
332    function objectURLToBlob(url, callback) {
333        var http = new XMLHttpRequest();
334        http.open("GET", url, true);
335        http.responseType = "blob";
336        http.onload = function (e) {
337            if (this.status == 200 || this.status === 0) {
338                callback(this.response);
339            }
340        };
341        http.send();
342    }
343
344    function getImageData(img, callback) {
345        function handleBinaryFile(binFile) {
346            var data = findEXIFinJPEG(binFile);
347            var iptcdata = findIPTCinJPEG(binFile);
348            img.exifdata = data || {};
349            img.iptcdata = iptcdata || {};
350            if (callback) {
351                callback.call(img);
352            }
353        }
354
355        if (img.src) {
356            if (/^data\:/i.test(img.src)) { // Data URI
357                var arrayBuffer = base64ToArrayBuffer(img.src);
358                handleBinaryFile(arrayBuffer);
359
360            } else if (/^blob\:/i.test(img.src)) { // Object URL
361                var fileReader = new FileReader();
362                fileReader.onload = function (e) {
363                    handleBinaryFile(e.target.result);
364                };
365                objectURLToBlob(img.src, function (blob) {
366                    fileReader.readAsArrayBuffer(blob);
367                });
368            } else {
369                var http = new XMLHttpRequest();
370                http.onload = function () {
371                    if (this.status == 200 || this.status === 0) {
372                        handleBinaryFile(http.response);
373                    } else {
374                        throw "Could not load image";
375                    }
376                    http = null;
377                };
378                http.open("GET", img.src, true);
379                http.responseType = "arraybuffer";
380                http.send(null);
381            }
382        } else if (window.FileReader && (img instanceof window.Blob || img instanceof window.File)) {
383            var fileReader = new FileReader();
384            fileReader.onload = function (e) {
385                if (debug) console.log("Got file of length " + e.target.result.byteLength);
386                handleBinaryFile(e.target.result);
387            };
388
389            fileReader.readAsArrayBuffer(img);
390        }
391    }
392
393    function findEXIFinJPEG(file) {
394        var dataView = new DataView(file);
395
396        if (debug) console.log("Got file of length " + file.byteLength);
397        if ((dataView.getUint8(0) != 0xFF) || (dataView.getUint8(1) != 0xD8)) {
398            if (debug) console.log("Not a valid JPEG");
399            return false; // not a valid jpeg
400        }
401
402        var offset = 2,
403            length = file.byteLength,
404            marker;
405
406        while (offset < length) {
407            if (dataView.getUint8(offset) != 0xFF) {
408                if (debug) console.log("Not a valid marker at offset " + offset + ", found: " + dataView.getUint8(offset));
409                return false; // not a valid marker, something is wrong
410            }
411
412            marker = dataView.getUint8(offset + 1);
413            if (debug) console.log(marker);
414
415            // we could implement handling for other markers here,
416            // but we're only looking for 0xFFE1 for EXIF data
417
418            if (marker == 225) {
419                if (debug) console.log("Found 0xFFE1 marker");
420
421                return readEXIFData(dataView, offset + 4, dataView.getUint16(offset + 2) - 2);
422
423                // offset += 2 + file.getShortAt(offset+2, true);
424
425            } else {
426                offset += 2 + dataView.getUint16(offset + 2);
427            }
428
429        }
430
431    }
432
433    function findIPTCinJPEG(file) {
434        var dataView = new DataView(file);
435
436        if (debug) console.log("Got file of length " + file.byteLength);
437        if ((dataView.getUint8(0) != 0xFF) || (dataView.getUint8(1) != 0xD8)) {
438            if (debug) console.log("Not a valid JPEG");
439            return false; // not a valid jpeg
440        }
441
442        var offset = 2,
443            length = file.byteLength;
444
445
446        var isFieldSegmentStart = function (dataView, offset) {
447            return (
448                dataView.getUint8(offset) === 0x38 &&
449                dataView.getUint8(offset + 1) === 0x42 &&
450                dataView.getUint8(offset + 2) === 0x49 &&
451                dataView.getUint8(offset + 3) === 0x4D &&
452                dataView.getUint8(offset + 4) === 0x04 &&
453                dataView.getUint8(offset + 5) === 0x04
454            );
455        };
456
457        while (offset < length) {
458
459            if (isFieldSegmentStart(dataView, offset)) {
460
461                // Get the length of the name header (which is padded to an even number of bytes)
462                var nameHeaderLength = dataView.getUint8(offset + 7);
463                if (nameHeaderLength % 2 !== 0) nameHeaderLength += 1;
464                // Check for pre photoshop 6 format
465                if (nameHeaderLength === 0) {
466                    // Always 4
467                    nameHeaderLength = 4;
468                }
469
470                var startOffset = offset + 8 + nameHeaderLength;
471                var sectionLength = dataView.getUint16(offset + 6 + nameHeaderLength);
472
473                return readIPTCData(file, startOffset, sectionLength);
474
475                break;
476
477            }
478
479
480            // Not the marker, continue searching
481            offset++;
482
483        }
484
485    }
486
487    var IptcFieldMap = {
488        0x78: 'caption',
489        0x6E: 'credit',
490        0x19: 'keywords',
491        0x37: 'dateCreated',
492        0x50: 'byline',
493        0x55: 'bylineTitle',
494        0x7A: 'captionWriter',
495        0x69: 'headline',
496        0x74: 'copyright',
497        0x0F: 'category'
498    };
499
500    function readIPTCData(file, startOffset, sectionLength) {
501        var dataView = new DataView(file);
502        var data = {};
503        var fieldValue, fieldName, dataSize, segmentType, segmentSize;
504        var segmentStartPos = startOffset;
505        while (segmentStartPos < startOffset + sectionLength) {
506            if (dataView.getUint8(segmentStartPos) === 0x1C && dataView.getUint8(segmentStartPos + 1) === 0x02) {
507                segmentType = dataView.getUint8(segmentStartPos + 2);
508                if (segmentType in IptcFieldMap) {
509                    dataSize = dataView.getInt16(segmentStartPos + 3);
510                    segmentSize = dataSize + 5;
511                    fieldName = IptcFieldMap[segmentType];
512                    fieldValue = getStringFromDB(dataView, segmentStartPos + 5, dataSize);
513                    // Check if we already stored a value with this name
514                    if (data.hasOwnProperty(fieldName)) {
515                        // Value already stored with this name, create multivalue field
516                        if (data[fieldName] instanceof Array) {
517                            data[fieldName].push(fieldValue);
518                        }
519                        else {
520                            data[fieldName] = [data[fieldName], fieldValue];
521                        }
522                    }
523                    else {
524                        data[fieldName] = fieldValue;
525                    }
526                }
527
528            }
529            segmentStartPos++;
530        }
531        return data;
532    }
533
534
535    function readTags(file, tiffStart, dirStart, strings, bigEnd) {
536        var entries = file.getUint16(dirStart, !bigEnd),
537            tags = {},
538            entryOffset, tag,
539            i;
540
541        for (i = 0; i < entries; i++) {
542            entryOffset = dirStart + i * 12 + 2;
543            tag = strings[file.getUint16(entryOffset, !bigEnd)];
544            if (!tag && debug) console.log("Unknown tag: " + file.getUint16(entryOffset, !bigEnd));
545            tags[tag] = readTagValue(file, entryOffset, tiffStart, dirStart, bigEnd);
546        }
547        return tags;
548    }
549
550
551    function readTagValue(file, entryOffset, tiffStart, dirStart, bigEnd) {
552        var type = file.getUint16(entryOffset + 2, !bigEnd),
553            numValues = file.getUint32(entryOffset + 4, !bigEnd),
554            valueOffset = file.getUint32(entryOffset + 8, !bigEnd) + tiffStart,
555            offset,
556            vals, val, n,
557            numerator, denominator;
558
559        switch (type) {
560            case 1: // byte, 8-bit unsigned int
561            case 7: // undefined, 8-bit byte, value depending on field
562                if (numValues == 1) {
563                    return file.getUint8(entryOffset + 8, !bigEnd);
564                } else {
565                    offset = numValues > 4 ? valueOffset : (entryOffset + 8);
566                    vals = [];
567                    for (n = 0; n < numValues; n++) {
568                        vals[n] = file.getUint8(offset + n);
569                    }
570                    return vals;
571                }
572
573            case 2: // ascii, 8-bit byte
574                offset = numValues > 4 ? valueOffset : (entryOffset + 8);
575                return getStringFromDB(file, offset, numValues - 1);
576
577            case 3: // short, 16 bit int
578                if (numValues == 1) {
579                    return file.getUint16(entryOffset + 8, !bigEnd);
580                } else {
581                    offset = numValues > 2 ? valueOffset : (entryOffset + 8);
582                    vals = [];
583                    for (n = 0; n < numValues; n++) {
584                        vals[n] = file.getUint16(offset + 2 * n, !bigEnd);
585                    }
586                    return vals;
587                }
588
589            case 4: // long, 32 bit int
590                if (numValues == 1) {
591                    return file.getUint32(entryOffset + 8, !bigEnd);
592                } else {
593                    vals = [];
594                    for (n = 0; n < numValues; n++) {
595                        vals[n] = file.getUint32(valueOffset + 4 * n, !bigEnd);
596                    }
597                    return vals;
598                }
599
600            case 5:    // rational = two long values, first is numerator, second is denominator
601                if (numValues == 1) {
602                    numerator = file.getUint32(valueOffset, !bigEnd);
603                    denominator = file.getUint32(valueOffset + 4, !bigEnd);
604                    val = new Number(numerator / denominator);
605                    val.numerator = numerator;
606                    val.denominator = denominator;
607                    return val;
608                } else {
609                    vals = [];
610                    for (n = 0; n < numValues; n++) {
611                        numerator = file.getUint32(valueOffset + 8 * n, !bigEnd);
612                        denominator = file.getUint32(valueOffset + 4 + 8 * n, !bigEnd);
613                        vals[n] = new Number(numerator / denominator);
614                        vals[n].numerator = numerator;
615                        vals[n].denominator = denominator;
616                    }
617                    return vals;
618                }
619
620            case 9: // slong, 32 bit signed int
621                if (numValues == 1) {
622                    return file.getInt32(entryOffset + 8, !bigEnd);
623                } else {
624                    vals = [];
625                    for (n = 0; n < numValues; n++) {
626                        vals[n] = file.getInt32(valueOffset + 4 * n, !bigEnd);
627                    }
628                    return vals;
629                }
630
631            case 10: // signed rational, two slongs, first is numerator, second is denominator
632                if (numValues == 1) {
633                    return file.getInt32(valueOffset, !bigEnd) / file.getInt32(valueOffset + 4, !bigEnd);
634                } else {
635                    vals = [];
636                    for (n = 0; n < numValues; n++) {
637                        vals[n] = file.getInt32(valueOffset + 8 * n, !bigEnd) / file.getInt32(valueOffset + 4 + 8 * n, !bigEnd);
638                    }
639                    return vals;
640                }
641        }
642    }
643
644    function getStringFromDB(buffer, start, length) {
645        var outstr = "";
646        for (n = start; n < start + length; n++) {
647            outstr += String.fromCharCode(buffer.getUint8(n));
648        }
649        return outstr;
650    }
651
652    function readEXIFData(file, start) {
653        if (getStringFromDB(file, start, 4) != "Exif") {
654            if (debug) console.log("Not valid EXIF data! " + getStringFromDB(file, start, 4));
655            return false;
656        }
657
658        var bigEnd,
659            tags, tag,
660            exifData, gpsData,
661            tiffOffset = start + 6;
662
663        // test for TIFF validity and endianness
664        if (file.getUint16(tiffOffset) == 0x4949) {
665            bigEnd = false;
666        } else if (file.getUint16(tiffOffset) == 0x4D4D) {
667            bigEnd = true;
668        } else {
669            if (debug) console.log("Not valid TIFF data! (no 0x4949 or 0x4D4D)");
670            return false;
671        }
672
673        if (file.getUint16(tiffOffset + 2, !bigEnd) != 0x002A) {
674            if (debug) console.log("Not valid TIFF data! (no 0x002A)");
675            return false;
676        }
677
678        var firstIFDOffset = file.getUint32(tiffOffset + 4, !bigEnd);
679
680        if (firstIFDOffset < 0x00000008) {
681            if (debug) console.log("Not valid TIFF data! (First offset less than 8)", file.getUint32(tiffOffset + 4, !bigEnd));
682            return false;
683        }
684
685        tags = readTags(file, tiffOffset, tiffOffset + firstIFDOffset, TiffTags, bigEnd);
686
687        if (tags.ExifIFDPointer) {
688            exifData = readTags(file, tiffOffset, tiffOffset + tags.ExifIFDPointer, ExifTags, bigEnd);
689            for (tag in exifData) {
690                switch (tag) {
691                    case "LightSource" :
692                    case "Flash" :
693                    case "MeteringMode" :
694                    case "ExposureProgram" :
695                    case "SensingMethod" :
696                    case "SceneCaptureType" :
697                    case "SceneType" :
698                    case "CustomRendered" :
699                    case "WhiteBalance" :
700                    case "GainControl" :
701                    case "Contrast" :
702                    case "Saturation" :
703                    case "Sharpness" :
704                    case "SubjectDistanceRange" :
705                    case "FileSource" :
706                        exifData[tag] = StringValues[tag][exifData[tag]];
707                        break;
708
709                    case "ExifVersion" :
710                    case "FlashpixVersion" :
711                        exifData[tag] = String.fromCharCode(exifData[tag][0], exifData[tag][1], exifData[tag][2], exifData[tag][3]);
712                        break;
713
714                    case "ComponentsConfiguration" :
715                        exifData[tag] =
716                            StringValues.Components[exifData[tag][0]] +
717                            StringValues.Components[exifData[tag][1]] +
718                            StringValues.Components[exifData[tag][2]] +
719                            StringValues.Components[exifData[tag][3]];
720                        break;
721                }
722                tags[tag] = exifData[tag];
723            }
724        }
725
726        if (tags.GPSInfoIFDPointer) {
727            gpsData = readTags(file, tiffOffset, tiffOffset + tags.GPSInfoIFDPointer, GPSTags, bigEnd);
728            for (tag in gpsData) {
729                switch (tag) {
730                    case "GPSVersionID" :
731                        gpsData[tag] = gpsData[tag][0] +
732                            "." + gpsData[tag][1] +
733                            "." + gpsData[tag][2] +
734                            "." + gpsData[tag][3];
735                        break;
736                }
737                tags[tag] = gpsData[tag];
738            }
739        }
740
741        return tags;
742    }
743
744    EXIF.getData = function (img, callback) {
745        if ((img instanceof Image || img instanceof HTMLImageElement) && !img.complete) return false;
746
747        if (!imageHasData(img)) {
748            getImageData(img, callback);
749        } else {
750            if (callback) {
751                callback.call(img);
752            }
753        }
754        return true;
755    }
756
757    EXIF.getTag = function (img, tag) {
758        if (!imageHasData(img)) return;
759        return img.exifdata[tag];
760    }
761
762    EXIF.getAllTags = function (img) {
763        if (!imageHasData(img)) return {};
764        var a,
765            data = img.exifdata,
766            tags = {};
767        for (a in data) {
768            if (data.hasOwnProperty(a)) {
769                tags[a] = data[a];
770            }
771        }
772        return tags;
773    }
774
775    EXIF.pretty = function (img) {
776        if (!imageHasData(img)) return "";
777        var a,
778            data = img.exifdata,
779            strPretty = "";
780        for (a in data) {
781            if (data.hasOwnProperty(a)) {
782                if (typeof data[a] == "object") {
783                    if (data[a] instanceof Number) {
784                        strPretty += a + " : " + data[a] + " [" + data[a].numerator + "/" + data[a].denominator + "]\r\n";
785                    } else {
786                        strPretty += a + " : [" + data[a].length + " values]\r\n";
787                    }
788                } else {
789                    strPretty += a + " : " + data[a] + "\r\n";
790                }
791            }
792        }
793        return strPretty;
794    }
795
796    EXIF.readFromBinaryFile = function (file) {
797        return findEXIFinJPEG(file);
798    }
799
800    if (typeof define === 'function' && define.amd) {
801        define('exif-js', [], function () {
802            return EXIF;
803        });
804    }
805}.call(this));

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.