mirror of
				https://github.com/flynx/PortableMag.git
				synced 2025-10-30 19:40:12 +00:00 
			
		
		
		
	
		
			
	
	
		
			895 lines
		
	
	
		
			25 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
		
		
			
		
	
	
			895 lines
		
	
	
		
			25 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
|  | 
 | ||
|  | /* | ||
|  |  * Javascript EXIF Reader - jQuery plugin 0.1.3 | ||
|  |  * Copyright (c) 2008 Jacob Seidelin, cupboy@gmail.com, http://blog.nihilogic.dk/
 | ||
|  |  * Licensed under the MPL License [http://www.nihilogic.dk/licenses/mpl-license.txt]
 | ||
|  |  */ | ||
|  | 
 | ||
|  | (function() { | ||
|  | 
 | ||
|  | 
 | ||
|  | var BinaryFile = function(strData, iDataOffset, iDataLength) { | ||
|  | 	var data = strData; | ||
|  | 	var dataOffset = iDataOffset || 0; | ||
|  | 	var dataLength = 0; | ||
|  | 
 | ||
|  | 	this.getRawData = function() { | ||
|  | 		return data; | ||
|  | 	} | ||
|  | 
 | ||
|  | 	if (typeof strData == "string") { | ||
|  | 		dataLength = iDataLength || data.length; | ||
|  | 
 | ||
|  | 		this.getByteAt = function(iOffset) { | ||
|  | 			return data.charCodeAt(iOffset + dataOffset) & 0xFF; | ||
|  | 		} | ||
|  | 	} else if (typeof strData == "unknown") { | ||
|  | 		dataLength = iDataLength || IEBinary_getLength(data); | ||
|  | 
 | ||
|  | 		this.getByteAt = function(iOffset) { | ||
|  | 			return IEBinary_getByteAt(data, iOffset + dataOffset); | ||
|  | 		} | ||
|  | 	} | ||
|  | 
 | ||
|  | 	this.getLength = function() { | ||
|  | 		return dataLength; | ||
|  | 	} | ||
|  | 
 | ||
|  | 	this.getSByteAt = function(iOffset) { | ||
|  | 		var iByte = this.getByteAt(iOffset); | ||
|  | 		if (iByte > 127) | ||
|  | 			return iByte - 256; | ||
|  | 		else | ||
|  | 			return iByte; | ||
|  | 	} | ||
|  | 
 | ||
|  | 	this.getShortAt = function(iOffset, bBigEndian) { | ||
|  | 		var iShort = bBigEndian ?  | ||
|  | 			(this.getByteAt(iOffset) << 8) + this.getByteAt(iOffset + 1) | ||
|  | 			: (this.getByteAt(iOffset + 1) << 8) + this.getByteAt(iOffset) | ||
|  | 		if (iShort < 0) iShort += 65536; | ||
|  | 		return iShort; | ||
|  | 	} | ||
|  | 	this.getSShortAt = function(iOffset, bBigEndian) { | ||
|  | 		var iUShort = this.getShortAt(iOffset, bBigEndian); | ||
|  | 		if (iUShort > 32767) | ||
|  | 			return iUShort - 65536; | ||
|  | 		else | ||
|  | 			return iUShort; | ||
|  | 	} | ||
|  | 	this.getLongAt = function(iOffset, bBigEndian) { | ||
|  | 		var iByte1 = this.getByteAt(iOffset), | ||
|  | 			iByte2 = this.getByteAt(iOffset + 1), | ||
|  | 			iByte3 = this.getByteAt(iOffset + 2), | ||
|  | 			iByte4 = this.getByteAt(iOffset + 3); | ||
|  | 
 | ||
|  | 		var iLong = bBigEndian ?  | ||
|  | 			(((((iByte1 << 8) + iByte2) << 8) + iByte3) << 8) + iByte4 | ||
|  | 			: (((((iByte4 << 8) + iByte3) << 8) + iByte2) << 8) + iByte1; | ||
|  | 		if (iLong < 0) iLong += 4294967296; | ||
|  | 		return iLong; | ||
|  | 	} | ||
|  | 	this.getSLongAt = function(iOffset, bBigEndian) { | ||
|  | 		var iULong = this.getLongAt(iOffset, bBigEndian); | ||
|  | 		if (iULong > 2147483647) | ||
|  | 			return iULong - 4294967296; | ||
|  | 		else | ||
|  | 			return iULong; | ||
|  | 	} | ||
|  | 	this.getStringAt = function(iOffset, iLength) { | ||
|  | 		var aStr = []; | ||
|  | 		for (var i=iOffset,j=0;i<iOffset+iLength;i++,j++) { | ||
|  | 			aStr[j] = String.fromCharCode(this.getByteAt(i)); | ||
|  | 		} | ||
|  | 		return aStr.join(""); | ||
|  | 	} | ||
|  | 
 | ||
|  | 	this.getCharAt = function(iOffset) { | ||
|  | 		return String.fromCharCode(this.getByteAt(iOffset)); | ||
|  | 	} | ||
|  | 	this.toBase64 = function() { | ||
|  | 		return window.btoa(data); | ||
|  | 	} | ||
|  | 	this.fromBase64 = function(strBase64) { | ||
|  | 		data = window.atob(strBase64); | ||
|  | 	} | ||
|  | } | ||
|  | 
 | ||
|  | 
 | ||
|  | var BinaryAjax = (function() { | ||
|  | 
 | ||
|  | 	function createRequest() { | ||
|  | 		var oHTTP = null; | ||
|  | 		if (window.XMLHttpRequest) { | ||
|  | 			oHTTP = new XMLHttpRequest(); | ||
|  | 		} else if (window.ActiveXObject) { | ||
|  | 			oHTTP = new ActiveXObject("Microsoft.XMLHTTP"); | ||
|  | 		} | ||
|  | 		return oHTTP; | ||
|  | 	} | ||
|  | 
 | ||
|  | 	function getHead(strURL, fncCallback, fncError) { | ||
|  | 		var oHTTP = createRequest(); | ||
|  | 		if (oHTTP) { | ||
|  | 			if (fncCallback) { | ||
|  | 				if (typeof(oHTTP.onload) != "undefined") { | ||
|  | 					oHTTP.onload = function() { | ||
|  | 						if (oHTTP.status == "200") { | ||
|  | 							fncCallback(this); | ||
|  | 						} else { | ||
|  | 							if (fncError) fncError(); | ||
|  | 						} | ||
|  | 						oHTTP = null; | ||
|  | 					}; | ||
|  | 				} else { | ||
|  | 					oHTTP.onreadystatechange = function() { | ||
|  | 						if (oHTTP.readyState == 4) { | ||
|  | 							if (oHTTP.status == "200") { | ||
|  | 								fncCallback(this); | ||
|  | 							} else { | ||
|  | 								if (fncError) fncError(); | ||
|  | 							} | ||
|  | 							oHTTP = null; | ||
|  | 						} | ||
|  | 					}; | ||
|  | 				} | ||
|  | 			} | ||
|  | 			oHTTP.open("HEAD", strURL, true); | ||
|  | 			oHTTP.send(null); | ||
|  | 		} else { | ||
|  | 			if (fncError) fncError(); | ||
|  | 		} | ||
|  | 	} | ||
|  | 
 | ||
|  | 	function sendRequest(strURL, fncCallback, fncError, aRange, bAcceptRanges, iFileSize) { | ||
|  | 		var oHTTP = createRequest(); | ||
|  | 		if (oHTTP) { | ||
|  | 
 | ||
|  | 			var iDataOffset = 0; | ||
|  | 			if (aRange && !bAcceptRanges) { | ||
|  | 				iDataOffset = aRange[0]; | ||
|  | 			} | ||
|  | 			var iDataLen = 0; | ||
|  | 			if (aRange) { | ||
|  | 				iDataLen = aRange[1]-aRange[0]+1; | ||
|  | 			} | ||
|  | 
 | ||
|  | 			if (fncCallback) { | ||
|  | 				if (typeof(oHTTP.onload) != "undefined") { | ||
|  | 					oHTTP.onload = function() { | ||
|  | 
 | ||
|  | 						if (oHTTP.status == "200" || oHTTP.status == "206" || oHTTP.status == "0") { | ||
|  | 							this.binaryResponse = new BinaryFile(this.responseText, iDataOffset, iDataLen); | ||
|  | 							this.fileSize = iFileSize || this.getResponseHeader("Content-Length"); | ||
|  | 							fncCallback(this); | ||
|  | 						} else { | ||
|  | 							if (fncError) fncError(); | ||
|  | 						} | ||
|  | 						oHTTP = null; | ||
|  | 					}; | ||
|  | 				} else { | ||
|  | 					oHTTP.onreadystatechange = function() { | ||
|  | 						if (oHTTP.readyState == 4) { | ||
|  | 							if (oHTTP.status == "200" || oHTTP.status == "206" || oHTTP.status == "0") { | ||
|  | 								this.binaryResponse = new BinaryFile(oHTTP.responseBody, iDataOffset, iDataLen); | ||
|  | 								this.fileSize = iFileSize || this.getResponseHeader("Content-Length"); | ||
|  | 								fncCallback(this); | ||
|  | 							} else { | ||
|  | 								if (fncError) fncError(); | ||
|  | 							} | ||
|  | 							oHTTP = null; | ||
|  | 						} | ||
|  | 					}; | ||
|  | 				} | ||
|  | 			} | ||
|  | 			oHTTP.open("GET", strURL, true); | ||
|  | 
 | ||
|  | 			if (oHTTP.overrideMimeType) oHTTP.overrideMimeType('text/plain; charset=x-user-defined'); | ||
|  | 
 | ||
|  | 			if (aRange && bAcceptRanges) { | ||
|  | 				oHTTP.setRequestHeader("Range", "bytes=" + aRange[0] + "-" + aRange[1]); | ||
|  | 			} | ||
|  | 
 | ||
|  | 			oHTTP.setRequestHeader("If-Modified-Since", "Sat, 1 Jan 1970 00:00:00 GMT"); | ||
|  | 
 | ||
|  | 			oHTTP.send(null); | ||
|  | 		} else { | ||
|  | 			if (fncError) fncError(); | ||
|  | 		} | ||
|  | 	} | ||
|  | 
 | ||
|  | 	return function(strURL, fncCallback, fncError, aRange) { | ||
|  | 
 | ||
|  | 		if (aRange) { | ||
|  | 			getHead( | ||
|  | 				strURL,  | ||
|  | 				function(oHTTP) { | ||
|  | 					var iLength = parseInt(oHTTP.getResponseHeader("Content-Length"),10); | ||
|  | 					var strAcceptRanges = oHTTP.getResponseHeader("Accept-Ranges"); | ||
|  | 
 | ||
|  | 					var iStart, iEnd; | ||
|  | 					iStart = aRange[0]; | ||
|  | 					if (aRange[0] < 0)  | ||
|  | 						iStart += iLength; | ||
|  | 					iEnd = iStart + aRange[1] - 1; | ||
|  | 
 | ||
|  | 					sendRequest(strURL, fncCallback, fncError, [iStart, iEnd], (strAcceptRanges == "bytes"), iLength); | ||
|  | 				} | ||
|  | 			); | ||
|  | 
 | ||
|  | 		} else { | ||
|  | 			sendRequest(strURL, fncCallback, fncError); | ||
|  | 		} | ||
|  | 	} | ||
|  | 
 | ||
|  | }()); | ||
|  | 
 | ||
|  | 
 | ||
|  | document.write( | ||
|  | 	"<script type='text/vbscript'>\r\n" | ||
|  | 	+ "Function IEBinary_getByteAt(strBinary, iOffset)\r\n" | ||
|  | 	+ "	IEBinary_getByteAt = AscB(MidB(strBinary,iOffset+1,1))\r\n" | ||
|  | 	+ "End Function\r\n" | ||
|  | 	+ "Function IEBinary_getLength(strBinary)\r\n" | ||
|  | 	+ "	IEBinary_getLength = LenB(strBinary)\r\n" | ||
|  | 	+ "End Function\r\n" | ||
|  | 	+ "</script>\r\n" | ||
|  | ); | ||
|  | 
 | ||
|  | 
 | ||
|  | var EXIF = {}; | ||
|  | 
 | ||
|  | (function() { | ||
|  | 
 | ||
|  | var bDebug = false; | ||
|  | 
 | ||
|  | EXIF.Tags = { | ||
|  | 
 | ||
|  | 	// version tags
 | ||
|  | 	0x9000 : "ExifVersion",			// EXIF version
 | ||
|  | 	0xA000 : "FlashpixVersion",		// Flashpix format version
 | ||
|  | 
 | ||
|  | 	// colorspace tags
 | ||
|  | 	0xA001 : "ColorSpace",			// Color space information tag
 | ||
|  | 
 | ||
|  | 	// image configuration
 | ||
|  | 	0xA002 : "PixelXDimension",		// Valid width of meaningful image
 | ||
|  | 	0xA003 : "PixelYDimension",		// Valid height of meaningful image
 | ||
|  | 	0x9101 : "ComponentsConfiguration",	// Information about channels
 | ||
|  | 	0x9102 : "CompressedBitsPerPixel",	// Compressed bits per pixel
 | ||
|  | 
 | ||
|  | 	// user information
 | ||
|  | 	0x927C : "MakerNote",			// Any desired information written by the manufacturer
 | ||
|  | 	0x9286 : "UserComment",			// Comments by user
 | ||
|  | 
 | ||
|  | 	// related file
 | ||
|  | 	0xA004 : "RelatedSoundFile",		// Name of related sound file
 | ||
|  | 
 | ||
|  | 	// date and time
 | ||
|  | 	0x9003 : "DateTimeOriginal",		// Date and time when the original image was generated
 | ||
|  | 	0x9004 : "DateTimeDigitized",		// Date and time when the image was stored digitally
 | ||
|  | 	0x9290 : "SubsecTime",			// Fractions of seconds for DateTime
 | ||
|  | 	0x9291 : "SubsecTimeOriginal",		// Fractions of seconds for DateTimeOriginal
 | ||
|  | 	0x9292 : "SubsecTimeDigitized",		// Fractions of seconds for DateTimeDigitized
 | ||
|  | 
 | ||
|  | 	// picture-taking conditions
 | ||
|  | 	0x829A : "ExposureTime",		// Exposure time (in seconds)
 | ||
|  | 	0x829D : "FNumber",			// F number
 | ||
|  | 	0x8822 : "ExposureProgram",		// Exposure program
 | ||
|  | 	0x8824 : "SpectralSensitivity",		// Spectral sensitivity
 | ||
|  | 	0x8827 : "ISOSpeedRatings",		// ISO speed rating
 | ||
|  | 	0x8828 : "OECF",			// Optoelectric conversion factor
 | ||
|  | 	0x9201 : "ShutterSpeedValue",		// Shutter speed
 | ||
|  | 	0x9202 : "ApertureValue",		// Lens aperture
 | ||
|  | 	0x9203 : "BrightnessValue",		// Value of brightness
 | ||
|  | 	0x9204 : "ExposureBias",		// Exposure bias
 | ||
|  | 	0x9205 : "MaxApertureValue",		// Smallest F number of lens
 | ||
|  | 	0x9206 : "SubjectDistance",		// Distance to subject in meters
 | ||
|  | 	0x9207 : "MeteringMode", 		// Metering mode
 | ||
|  | 	0x9208 : "LightSource",			// Kind of light source
 | ||
|  | 	0x9209 : "Flash",			// Flash status
 | ||
|  | 	0x9214 : "SubjectArea",			// Location and area of main subject
 | ||
|  | 	0x920A : "FocalLength",			// Focal length of the lens in mm
 | ||
|  | 	0xA20B : "FlashEnergy",			// Strobe energy in BCPS
 | ||
|  | 	0xA20C : "SpatialFrequencyResponse",	// 
 | ||
|  | 	0xA20E : "FocalPlaneXResolution", 	// Number of pixels in width direction per FocalPlaneResolutionUnit
 | ||
|  | 	0xA20F : "FocalPlaneYResolution", 	// Number of pixels in height direction per FocalPlaneResolutionUnit
 | ||
|  | 	0xA210 : "FocalPlaneResolutionUnit", 	// Unit for measuring FocalPlaneXResolution and FocalPlaneYResolution
 | ||
|  | 	0xA214 : "SubjectLocation",		// Location of subject in image
 | ||
|  | 	0xA215 : "ExposureIndex",		// Exposure index selected on camera
 | ||
|  | 	0xA217 : "SensingMethod", 		// Image sensor type
 | ||
|  | 	0xA300 : "FileSource", 			// Image source (3 == DSC)
 | ||
|  | 	0xA301 : "SceneType", 			// Scene type (1 == directly photographed)
 | ||
|  | 	0xA302 : "CFAPattern",			// Color filter array geometric pattern
 | ||
|  | 	0xA401 : "CustomRendered",		// Special processing
 | ||
|  | 	0xA402 : "ExposureMode",		// Exposure mode
 | ||
|  | 	0xA403 : "WhiteBalance",		// 1 = auto white balance, 2 = manual
 | ||
|  | 	0xA404 : "DigitalZoomRation",		// Digital zoom ratio
 | ||
|  | 	0xA405 : "FocalLengthIn35mmFilm",	// Equivalent foacl length assuming 35mm film camera (in mm)
 | ||
|  | 	0xA406 : "SceneCaptureType",		// Type of scene
 | ||
|  | 	0xA407 : "GainControl",			// Degree of overall image gain adjustment
 | ||
|  | 	0xA408 : "Contrast",			// Direction of contrast processing applied by camera
 | ||
|  | 	0xA409 : "Saturation", 			// Direction of saturation processing applied by camera
 | ||
|  | 	0xA40A : "Sharpness",			// Direction of sharpness processing applied by camera
 | ||
|  | 	0xA40B : "DeviceSettingDescription",	// 
 | ||
|  | 	0xA40C : "SubjectDistanceRange",	// Distance to subject
 | ||
|  | 
 | ||
|  | 	// other tags
 | ||
|  | 	0xA005 : "InteroperabilityIFDPointer", | ||
|  | 	0xA420 : "ImageUniqueID"		// Identifier assigned uniquely to each image
 | ||
|  | }; | ||
|  | 
 | ||
|  | EXIF.TiffTags = { | ||
|  | 	0x0100 : "ImageWidth", | ||
|  | 	0x0101 : "ImageHeight", | ||
|  | 	0x8769 : "ExifIFDPointer", | ||
|  | 	0x8825 : "GPSInfoIFDPointer", | ||
|  | 	0xA005 : "InteroperabilityIFDPointer", | ||
|  | 	0x0102 : "BitsPerSample", | ||
|  | 	0x0103 : "Compression", | ||
|  | 	0x0106 : "PhotometricInterpretation", | ||
|  | 	0x0112 : "Orientation", | ||
|  | 	0x0115 : "SamplesPerPixel", | ||
|  | 	0x011C : "PlanarConfiguration", | ||
|  | 	0x0212 : "YCbCrSubSampling", | ||
|  | 	0x0213 : "YCbCrPositioning", | ||
|  | 	0x011A : "XResolution", | ||
|  | 	0x011B : "YResolution", | ||
|  | 	0x0128 : "ResolutionUnit", | ||
|  | 	0x0111 : "StripOffsets", | ||
|  | 	0x0116 : "RowsPerStrip", | ||
|  | 	0x0117 : "StripByteCounts", | ||
|  | 	0x0201 : "JPEGInterchangeFormat", | ||
|  | 	0x0202 : "JPEGInterchangeFormatLength", | ||
|  | 	0x012D : "TransferFunction", | ||
|  | 	0x013E : "WhitePoint", | ||
|  | 	0x013F : "PrimaryChromaticities", | ||
|  | 	0x0211 : "YCbCrCoefficients", | ||
|  | 	0x0214 : "ReferenceBlackWhite", | ||
|  | 	0x0132 : "DateTime", | ||
|  | 	0x010E : "ImageDescription", | ||
|  | 	0x010F : "Make", | ||
|  | 	0x0110 : "Model", | ||
|  | 	0x0131 : "Software", | ||
|  | 	0x013B : "Artist", | ||
|  | 	0x8298 : "Copyright" | ||
|  | } | ||
|  | 
 | ||
|  | EXIF.GPSTags = { | ||
|  | 	0x0000 : "GPSVersionID", | ||
|  | 	0x0001 : "GPSLatitudeRef", | ||
|  | 	0x0002 : "GPSLatitude", | ||
|  | 	0x0003 : "GPSLongitudeRef", | ||
|  | 	0x0004 : "GPSLongitude", | ||
|  | 	0x0005 : "GPSAltitudeRef", | ||
|  | 	0x0006 : "GPSAltitude", | ||
|  | 	0x0007 : "GPSTimeStamp", | ||
|  | 	0x0008 : "GPSSatellites", | ||
|  | 	0x0009 : "GPSStatus", | ||
|  | 	0x000A : "GPSMeasureMode", | ||
|  | 	0x000B : "GPSDOP", | ||
|  | 	0x000C : "GPSSpeedRef", | ||
|  | 	0x000D : "GPSSpeed", | ||
|  | 	0x000E : "GPSTrackRef", | ||
|  | 	0x000F : "GPSTrack", | ||
|  | 	0x0010 : "GPSImgDirectionRef", | ||
|  | 	0x0011 : "GPSImgDirection", | ||
|  | 	0x0012 : "GPSMapDatum", | ||
|  | 	0x0013 : "GPSDestLatitudeRef", | ||
|  | 	0x0014 : "GPSDestLatitude", | ||
|  | 	0x0015 : "GPSDestLongitudeRef", | ||
|  | 	0x0016 : "GPSDestLongitude", | ||
|  | 	0x0017 : "GPSDestBearingRef", | ||
|  | 	0x0018 : "GPSDestBearing", | ||
|  | 	0x0019 : "GPSDestDistanceRef", | ||
|  | 	0x001A : "GPSDestDistance", | ||
|  | 	0x001B : "GPSProcessingMethod", | ||
|  | 	0x001C : "GPSAreaInformation", | ||
|  | 	0x001D : "GPSDateStamp", | ||
|  | 	0x001E : "GPSDifferential" | ||
|  | } | ||
|  | 
 | ||
|  | EXIF.StringValues = { | ||
|  | 	ExposureProgram : { | ||
|  | 		0 : "Not defined", | ||
|  | 		1 : "Manual", | ||
|  | 		2 : "Normal program", | ||
|  | 		3 : "Aperture priority", | ||
|  | 		4 : "Shutter priority", | ||
|  | 		5 : "Creative program", | ||
|  | 		6 : "Action program", | ||
|  | 		7 : "Portrait mode", | ||
|  | 		8 : "Landscape mode" | ||
|  | 	}, | ||
|  | 	MeteringMode : { | ||
|  | 		0 : "Unknown", | ||
|  | 		1 : "Average", | ||
|  | 		2 : "CenterWeightedAverage", | ||
|  | 		3 : "Spot", | ||
|  | 		4 : "MultiSpot", | ||
|  | 		5 : "Pattern", | ||
|  | 		6 : "Partial", | ||
|  | 		255 : "Other" | ||
|  | 	}, | ||
|  | 	LightSource : { | ||
|  | 		0 : "Unknown", | ||
|  | 		1 : "Daylight", | ||
|  | 		2 : "Fluorescent", | ||
|  | 		3 : "Tungsten (incandescent light)", | ||
|  | 		4 : "Flash", | ||
|  | 		9 : "Fine weather", | ||
|  | 		10 : "Cloudy weather", | ||
|  | 		11 : "Shade", | ||
|  | 		12 : "Daylight fluorescent (D 5700 - 7100K)", | ||
|  | 		13 : "Day white fluorescent (N 4600 - 5400K)", | ||
|  | 		14 : "Cool white fluorescent (W 3900 - 4500K)", | ||
|  | 		15 : "White fluorescent (WW 3200 - 3700K)", | ||
|  | 		17 : "Standard light A", | ||
|  | 		18 : "Standard light B", | ||
|  | 		19 : "Standard light C", | ||
|  | 		20 : "D55", | ||
|  | 		21 : "D65", | ||
|  | 		22 : "D75", | ||
|  | 		23 : "D50", | ||
|  | 		24 : "ISO studio tungsten", | ||
|  | 		255 : "Other" | ||
|  | 	}, | ||
|  | 	Flash : { | ||
|  | 		0x0000 : "Flash did not fire", | ||
|  | 		0x0001 : "Flash fired", | ||
|  | 		0x0005 : "Strobe return light not detected", | ||
|  | 		0x0007 : "Strobe return light detected", | ||
|  | 		0x0009 : "Flash fired, compulsory flash mode", | ||
|  | 		0x000D : "Flash fired, compulsory flash mode, return light not detected", | ||
|  | 		0x000F : "Flash fired, compulsory flash mode, return light detected", | ||
|  | 		0x0010 : "Flash did not fire, compulsory flash mode", | ||
|  | 		0x0018 : "Flash did not fire, auto mode", | ||
|  | 		0x0019 : "Flash fired, auto mode", | ||
|  | 		0x001D : "Flash fired, auto mode, return light not detected", | ||
|  | 		0x001F : "Flash fired, auto mode, return light detected", | ||
|  | 		0x0020 : "No flash function", | ||
|  | 		0x0041 : "Flash fired, red-eye reduction mode", | ||
|  | 		0x0045 : "Flash fired, red-eye reduction mode, return light not detected", | ||
|  | 		0x0047 : "Flash fired, red-eye reduction mode, return light detected", | ||
|  | 		0x0049 : "Flash fired, compulsory flash mode, red-eye reduction mode", | ||
|  | 		0x004D : "Flash fired, compulsory flash mode, red-eye reduction mode, return light not detected", | ||
|  | 		0x004F : "Flash fired, compulsory flash mode, red-eye reduction mode, return light detected", | ||
|  | 		0x0059 : "Flash fired, auto mode, red-eye reduction mode", | ||
|  | 		0x005D : "Flash fired, auto mode, return light not detected, red-eye reduction mode", | ||
|  | 		0x005F : "Flash fired, auto mode, return light detected, red-eye reduction mode" | ||
|  | 	}, | ||
|  | 	SensingMethod : { | ||
|  | 		1 : "Not defined", | ||
|  | 		2 : "One-chip color area sensor", | ||
|  | 		3 : "Two-chip color area sensor", | ||
|  | 		4 : "Three-chip color area sensor", | ||
|  | 		5 : "Color sequential area sensor", | ||
|  | 		7 : "Trilinear sensor", | ||
|  | 		8 : "Color sequential linear sensor" | ||
|  | 	}, | ||
|  | 	SceneCaptureType : { | ||
|  | 		0 : "Standard", | ||
|  | 		1 : "Landscape", | ||
|  | 		2 : "Portrait", | ||
|  | 		3 : "Night scene" | ||
|  | 	}, | ||
|  | 	SceneType : { | ||
|  | 		1 : "Directly photographed" | ||
|  | 	}, | ||
|  | 	CustomRendered : { | ||
|  | 		0 : "Normal process", | ||
|  | 		1 : "Custom process" | ||
|  | 	}, | ||
|  | 	WhiteBalance : { | ||
|  | 		0 : "Auto white balance", | ||
|  | 		1 : "Manual white balance" | ||
|  | 	}, | ||
|  | 	GainControl : { | ||
|  | 		0 : "None", | ||
|  | 		1 : "Low gain up", | ||
|  | 		2 : "High gain up", | ||
|  | 		3 : "Low gain down", | ||
|  | 		4 : "High gain down" | ||
|  | 	}, | ||
|  | 	Contrast : { | ||
|  | 		0 : "Normal", | ||
|  | 		1 : "Soft", | ||
|  | 		2 : "Hard" | ||
|  | 	}, | ||
|  | 	Saturation : { | ||
|  | 		0 : "Normal", | ||
|  | 		1 : "Low saturation", | ||
|  | 		2 : "High saturation" | ||
|  | 	}, | ||
|  | 	Sharpness : { | ||
|  | 		0 : "Normal", | ||
|  | 		1 : "Soft", | ||
|  | 		2 : "Hard" | ||
|  | 	}, | ||
|  | 	SubjectDistanceRange : { | ||
|  | 		0 : "Unknown", | ||
|  | 		1 : "Macro", | ||
|  | 		2 : "Close view", | ||
|  | 		3 : "Distant view" | ||
|  | 	}, | ||
|  | 	FileSource : { | ||
|  | 		3 : "DSC" | ||
|  | 	}, | ||
|  | 
 | ||
|  | 	Components : { | ||
|  | 		0 : "", | ||
|  | 		1 : "Y", | ||
|  | 		2 : "Cb", | ||
|  | 		3 : "Cr", | ||
|  | 		4 : "R", | ||
|  | 		5 : "G", | ||
|  | 		6 : "B" | ||
|  | 	} | ||
|  | } | ||
|  | 
 | ||
|  | function addEvent(oElement, strEvent, fncHandler)  | ||
|  | { | ||
|  | 	if (oElement.addEventListener) {  | ||
|  | 		oElement.addEventListener(strEvent, fncHandler, false);  | ||
|  | 	} else if (oElement.attachEvent) {  | ||
|  | 		oElement.attachEvent("on" + strEvent, fncHandler);  | ||
|  | 	} | ||
|  | } | ||
|  | 
 | ||
|  | 
 | ||
|  | function imageHasData(oImg)  | ||
|  | { | ||
|  | 	return !!(oImg.exifdata); | ||
|  | } | ||
|  | 
 | ||
|  | function getImageData(oImg, fncCallback)  | ||
|  | { | ||
|  | 	BinaryAjax( | ||
|  | 		oImg.src, | ||
|  | 		function(oHTTP) { | ||
|  | 			var oEXIF = findEXIFinJPEG(oHTTP.binaryResponse); | ||
|  | 			oImg.exifdata = oEXIF || {}; | ||
|  | 			if (fncCallback) fncCallback(); | ||
|  | 		} | ||
|  | 	) | ||
|  | } | ||
|  | 
 | ||
|  | function findEXIFinJPEG(oFile) { | ||
|  | 	var aMarkers = []; | ||
|  | 
 | ||
|  | 	if (oFile.getByteAt(0) != 0xFF || oFile.getByteAt(1) != 0xD8) { | ||
|  | 		return false; // not a valid jpeg
 | ||
|  | 	} | ||
|  | 
 | ||
|  | 	var iOffset = 2; | ||
|  | 	var iLength = oFile.getLength(); | ||
|  | 	while (iOffset < iLength) { | ||
|  | 		if (oFile.getByteAt(iOffset) != 0xFF) { | ||
|  | 			if (bDebug) console.log("Not a valid marker at offset " + iOffset + ", found: " + oFile.getByteAt(iOffset)); | ||
|  | 			return false; // not a valid marker, something is wrong
 | ||
|  | 		} | ||
|  | 
 | ||
|  | 		var iMarker = oFile.getByteAt(iOffset+1); | ||
|  | 
 | ||
|  | 		// we could implement handling for other markers here, 
 | ||
|  | 		// but we're only looking for 0xFFE1 for EXIF data
 | ||
|  | 
 | ||
|  | 		if (iMarker == 22400) { | ||
|  | 			if (bDebug) console.log("Found 0xFFE1 marker"); | ||
|  | 			return readEXIFData(oFile, iOffset + 4, oFile.getShortAt(iOffset+2, true)-2); | ||
|  | 			iOffset += 2 + oFile.getShortAt(iOffset+2, true); | ||
|  | 
 | ||
|  | 		} else if (iMarker == 225) { | ||
|  | 			// 0xE1 = Application-specific 1 (for EXIF)
 | ||
|  | 			if (bDebug) console.log("Found 0xFFE1 marker"); | ||
|  | 			return readEXIFData(oFile, iOffset + 4, oFile.getShortAt(iOffset+2, true)-2); | ||
|  | 
 | ||
|  | 		} else { | ||
|  | 			iOffset += 2 + oFile.getShortAt(iOffset+2, true); | ||
|  | 		} | ||
|  | 
 | ||
|  | 	} | ||
|  | 
 | ||
|  | } | ||
|  | 
 | ||
|  | 
 | ||
|  | function readTags(oFile, iTIFFStart, iDirStart, oStrings, bBigEnd)  | ||
|  | { | ||
|  | 	var iEntries = oFile.getShortAt(iDirStart, bBigEnd); | ||
|  | 	var oTags = {}; | ||
|  | 	for (var i=0;i<iEntries;i++) { | ||
|  | 		var iEntryOffset = iDirStart + i*12 + 2; | ||
|  | 		var strTag = oStrings[oFile.getShortAt(iEntryOffset, bBigEnd)]; | ||
|  | 		if (!strTag && bDebug) console.log("Unknown tag: " + oFile.getShortAt(iEntryOffset, bBigEnd)); | ||
|  | 		oTags[strTag] = readTagValue(oFile, iEntryOffset, iTIFFStart, iDirStart, bBigEnd); | ||
|  | 	} | ||
|  | 	return oTags; | ||
|  | } | ||
|  | 
 | ||
|  | 
 | ||
|  | function readTagValue(oFile, iEntryOffset, iTIFFStart, iDirStart, bBigEnd) | ||
|  | { | ||
|  | 	var iType = oFile.getShortAt(iEntryOffset+2, bBigEnd); | ||
|  | 	var iNumValues = oFile.getLongAt(iEntryOffset+4, bBigEnd); | ||
|  | 	var iValueOffset = oFile.getLongAt(iEntryOffset+8, bBigEnd) + iTIFFStart; | ||
|  | 
 | ||
|  | 	switch (iType) { | ||
|  | 		case 1: // byte, 8-bit unsigned int
 | ||
|  | 		case 7: // undefined, 8-bit byte, value depending on field
 | ||
|  | 			if (iNumValues == 1) { | ||
|  | 				return oFile.getByteAt(iEntryOffset + 8, bBigEnd); | ||
|  | 			} else { | ||
|  | 				var iValOffset = iNumValues > 4 ? iValueOffset : (iEntryOffset + 8); | ||
|  | 				var aVals = []; | ||
|  | 				for (var n=0;n<iNumValues;n++) { | ||
|  | 					aVals[n] = oFile.getByteAt(iValOffset + n); | ||
|  | 				} | ||
|  | 				return aVals; | ||
|  | 			} | ||
|  | 			break; | ||
|  | 
 | ||
|  | 		case 2: // ascii, 8-bit byte
 | ||
|  | 			var iStringOffset = iNumValues > 4 ? iValueOffset : (iEntryOffset + 8); | ||
|  | 			return oFile.getStringAt(iStringOffset, iNumValues-1); | ||
|  | 			break; | ||
|  | 
 | ||
|  | 		case 3: // short, 16 bit int
 | ||
|  | 			if (iNumValues == 1) { | ||
|  | 				return oFile.getShortAt(iEntryOffset + 8, bBigEnd); | ||
|  | 			} else { | ||
|  | 				var iValOffset = iNumValues > 2 ? iValueOffset : (iEntryOffset + 8); | ||
|  | 				var aVals = []; | ||
|  | 				for (var n=0;n<iNumValues;n++) { | ||
|  | 					aVals[n] = oFile.getShortAt(iValOffset + 2*n, bBigEnd); | ||
|  | 				} | ||
|  | 				return aVals; | ||
|  | 			} | ||
|  | 			break; | ||
|  | 
 | ||
|  | 		case 4: // long, 32 bit int
 | ||
|  | 			if (iNumValues == 1) { | ||
|  | 				return oFile.getLongAt(iEntryOffset + 8, bBigEnd); | ||
|  | 			} else { | ||
|  | 				var aVals = []; | ||
|  | 				for (var n=0;n<iNumValues;n++) { | ||
|  | 					aVals[n] = oFile.getLongAt(iValueOffset + 4*n, bBigEnd); | ||
|  | 				} | ||
|  | 				return aVals; | ||
|  | 			} | ||
|  | 			break; | ||
|  | 		case 5:	// rational = two long values, first is numerator, second is denominator
 | ||
|  | 			if (iNumValues == 1) { | ||
|  | 				return oFile.getLongAt(iValueOffset, bBigEnd) / oFile.getLongAt(iValueOffset+4, bBigEnd); | ||
|  | 			} else { | ||
|  | 				var aVals = []; | ||
|  | 				for (var n=0;n<iNumValues;n++) { | ||
|  | 					aVals[n] = oFile.getLongAt(iValueOffset + 8*n, bBigEnd) / oFile.getLongAt(iValueOffset+4 + 8*n, bBigEnd); | ||
|  | 				} | ||
|  | 				return aVals; | ||
|  | 			} | ||
|  | 			break; | ||
|  | 		case 9: // slong, 32 bit signed int
 | ||
|  | 			if (iNumValues == 1) { | ||
|  | 				return oFile.getSLongAt(iEntryOffset + 8, bBigEnd); | ||
|  | 			} else { | ||
|  | 				var aVals = []; | ||
|  | 				for (var n=0;n<iNumValues;n++) { | ||
|  | 					aVals[n] = oFile.getSLongAt(iValueOffset + 4*n, bBigEnd); | ||
|  | 				} | ||
|  | 				return aVals; | ||
|  | 			} | ||
|  | 			break; | ||
|  | 		case 10: // signed rational, two slongs, first is numerator, second is denominator
 | ||
|  | 			if (iNumValues == 1) { | ||
|  | 				return oFile.getSLongAt(iValueOffset, bBigEnd) / oFile.getSLongAt(iValueOffset+4, bBigEnd); | ||
|  | 			} else { | ||
|  | 				var aVals = []; | ||
|  | 				for (var n=0;n<iNumValues;n++) { | ||
|  | 					aVals[n] = oFile.getSLongAt(iValueOffset + 8*n, bBigEnd) / oFile.getSLongAt(iValueOffset+4 + 8*n, bBigEnd); | ||
|  | 				} | ||
|  | 				return aVals; | ||
|  | 			} | ||
|  | 			break; | ||
|  | 	} | ||
|  | } | ||
|  | 
 | ||
|  | 
 | ||
|  | function readEXIFData(oFile, iStart, iLength)  | ||
|  | { | ||
|  | 	if (oFile.getStringAt(iStart, 4) != "Exif") { | ||
|  | 		if (bDebug) console.log("Not valid EXIF data! " + oFile.getStringAt(iStart, 4)); | ||
|  | 		return false; | ||
|  | 	} | ||
|  | 
 | ||
|  | 	var bBigEnd; | ||
|  | 
 | ||
|  | 	var iTIFFOffset = iStart + 6; | ||
|  | 
 | ||
|  | 	// test for TIFF validity and endianness
 | ||
|  | 	if (oFile.getShortAt(iTIFFOffset) == 0x4949) { | ||
|  | 		bBigEnd = false; | ||
|  | 	} else if (oFile.getShortAt(iTIFFOffset) == 0x4D4D) { | ||
|  | 		bBigEnd = true; | ||
|  | 	} else { | ||
|  | 		if (bDebug) console.log("Not valid TIFF data! (no 0x4949 or 0x4D4D)"); | ||
|  | 		return false; | ||
|  | 	} | ||
|  | 
 | ||
|  | 	if (oFile.getShortAt(iTIFFOffset+2, bBigEnd) != 0x002A) { | ||
|  | 		if (bDebug) console.log("Not valid TIFF data! (no 0x002A)"); | ||
|  | 		return false; | ||
|  | 	} | ||
|  | 
 | ||
|  | 	if (oFile.getLongAt(iTIFFOffset+4, bBigEnd) != 0x00000008) { | ||
|  | 		if (bDebug) console.log("Not valid TIFF data! (First offset not 8)", oFile.getShortAt(iTIFFOffset+4, bBigEnd)); | ||
|  | 		return false; | ||
|  | 	} | ||
|  | 
 | ||
|  | 	var oTags = readTags(oFile, iTIFFOffset, iTIFFOffset+8, EXIF.TiffTags, bBigEnd); | ||
|  | 
 | ||
|  | 	if (oTags.ExifIFDPointer) { | ||
|  | 		var oEXIFTags = readTags(oFile, iTIFFOffset, iTIFFOffset + oTags.ExifIFDPointer, EXIF.Tags, bBigEnd); | ||
|  | 		for (var strTag in oEXIFTags) { | ||
|  | 			switch (strTag) { | ||
|  | 				case "LightSource" : | ||
|  | 				case "Flash" : | ||
|  | 				case "MeteringMode" : | ||
|  | 				case "ExposureProgram" : | ||
|  | 				case "SensingMethod" : | ||
|  | 				case "SceneCaptureType" : | ||
|  | 				case "SceneType" : | ||
|  | 				case "CustomRendered" : | ||
|  | 				case "WhiteBalance" :  | ||
|  | 				case "GainControl" :  | ||
|  | 				case "Contrast" : | ||
|  | 				case "Saturation" : | ||
|  | 				case "Sharpness" :  | ||
|  | 				case "SubjectDistanceRange" : | ||
|  | 				case "FileSource" : | ||
|  | 					oEXIFTags[strTag] = EXIF.StringValues[strTag][oEXIFTags[strTag]]; | ||
|  | 					break; | ||
|  | 	 | ||
|  | 				case "ExifVersion" : | ||
|  | 				case "FlashpixVersion" : | ||
|  | 					oEXIFTags[strTag] = String.fromCharCode(oEXIFTags[strTag][0], oEXIFTags[strTag][1], oEXIFTags[strTag][2], oEXIFTags[strTag][3]); | ||
|  | 					break; | ||
|  | 	 | ||
|  | 				case "ComponentsConfiguration" :  | ||
|  | 					oEXIFTags[strTag] =  | ||
|  | 						EXIF.StringValues.Components[oEXIFTags[strTag][0]] | ||
|  | 						+ EXIF.StringValues.Components[oEXIFTags[strTag][1]] | ||
|  | 						+ EXIF.StringValues.Components[oEXIFTags[strTag][2]] | ||
|  | 						+ EXIF.StringValues.Components[oEXIFTags[strTag][3]]; | ||
|  | 					break; | ||
|  | 			} | ||
|  | 			oTags[strTag] = oEXIFTags[strTag]; | ||
|  | 		} | ||
|  | 	} | ||
|  | 
 | ||
|  | 	if (oTags.GPSInfoIFDPointer) { | ||
|  | 		var oGPSTags = readTags(oFile, iTIFFOffset, iTIFFOffset + oTags.GPSInfoIFDPointer, EXIF.GPSTags, bBigEnd); | ||
|  | 		for (var strTag in oGPSTags) { | ||
|  | 			switch (strTag) { | ||
|  | 				case "GPSVersionID" :  | ||
|  | 					oGPSTags[strTag] = oGPSTags[strTag][0]  | ||
|  | 						+ "." + oGPSTags[strTag][1]  | ||
|  | 						+ "." + oGPSTags[strTag][2]  | ||
|  | 						+ "." + oGPSTags[strTag][3]; | ||
|  | 					break; | ||
|  | 			} | ||
|  | 			oTags[strTag] = oGPSTags[strTag]; | ||
|  | 		} | ||
|  | 	} | ||
|  | 
 | ||
|  | 	return oTags; | ||
|  | } | ||
|  | 
 | ||
|  | 
 | ||
|  | EXIF.getData = function(oImg, fncCallback)  | ||
|  | { | ||
|  | 	if (!oImg.complete) return false; | ||
|  | 	if (!imageHasData(oImg)) { | ||
|  | 		getImageData(oImg, fncCallback); | ||
|  | 	} else { | ||
|  | 		if (fncCallback) fncCallback(); | ||
|  | 	} | ||
|  | 	return true; | ||
|  | } | ||
|  | 
 | ||
|  | EXIF.getTag = function(oImg, strTag)  | ||
|  | { | ||
|  | 	if (!imageHasData(oImg)) return; | ||
|  | 	return oImg.exifdata[strTag]; | ||
|  | } | ||
|  | 
 | ||
|  | EXIF.getAllTags = function(oImg)  | ||
|  | { | ||
|  | 	if (!imageHasData(oImg)) return {}; | ||
|  | 	var oData = oImg.exifdata; | ||
|  | 	var oAllTags = {}; | ||
|  | 	for (var a in oData) { | ||
|  | 		if (oData.hasOwnProperty(a)) { | ||
|  | 			oAllTags[a] = oData[a]; | ||
|  | 		} | ||
|  | 	} | ||
|  | 	return oAllTags; | ||
|  | } | ||
|  | 
 | ||
|  | EXIF.pretty = function(oImg)  | ||
|  | { | ||
|  | 	if (!imageHasData(oImg)) return ""; | ||
|  | 	var oData = oImg.exifdata; | ||
|  | 	var strPretty = ""; | ||
|  | 	for (var a in oData) { | ||
|  | 		if (oData.hasOwnProperty(a)) { | ||
|  | 			if (typeof oData[a] == "object") { | ||
|  | 				strPretty += a + " : [" + oData[a].length + " values]\r\n"; | ||
|  | 			} else { | ||
|  | 				strPretty += a + " : " + oData[a] + "\r\n"; | ||
|  | 			} | ||
|  | 		} | ||
|  | 	} | ||
|  | 	return strPretty; | ||
|  | } | ||
|  | 
 | ||
|  | EXIF.readFromBinaryFile = function(oFile) { | ||
|  | 	return findEXIFinJPEG(oFile); | ||
|  | } | ||
|  | 
 | ||
|  | function loadAllImages()  | ||
|  | { | ||
|  | 	var aImages = document.getElementsByTagName("img"); | ||
|  | 	for (var i=0;i<aImages.length;i++) { | ||
|  | 		if (aImages[i].getAttribute("exif") == "true") { | ||
|  | 			if (!aImages[i].complete) { | ||
|  | 				addEvent(aImages[i], "load",  | ||
|  | 					function() { | ||
|  | 						EXIF.getData(this); | ||
|  | 					} | ||
|  | 				);  | ||
|  | 			} else { | ||
|  | 				EXIF.getData(aImages[i]); | ||
|  | 			} | ||
|  | 		} | ||
|  | 	} | ||
|  | } | ||
|  | 
 | ||
|  | // automatically load exif data for all images with exif=true when doc is ready
 | ||
|  | jQuery(document).ready(loadAllImages); | ||
|  | 
 | ||
|  | // load data for images manually
 | ||
|  | jQuery.fn.exifLoad = function(fncCallback) { | ||
|  | 	return this.each(function() { | ||
|  | 		EXIF.getData(this, fncCallback) | ||
|  | 	}); | ||
|  | } | ||
|  | 
 | ||
|  | jQuery.fn.exif = function(strTag) { | ||
|  | 	var aStrings = []; | ||
|  | 	this.each(function() { | ||
|  | 		aStrings.push(EXIF.getTag(this, strTag)); | ||
|  | 	}); | ||
|  | 	return aStrings; | ||
|  | } | ||
|  | 
 | ||
|  | jQuery.fn.exifAll = function() { | ||
|  | 	var aStrings = []; | ||
|  | 	this.each(function() { | ||
|  | 		aStrings.push(EXIF.getAllTags(this)); | ||
|  | 	}); | ||
|  | 	return aStrings; | ||
|  | } | ||
|  | 
 | ||
|  | jQuery.fn.exifPretty = function() { | ||
|  | 	var aStrings = []; | ||
|  | 	this.each(function() { | ||
|  | 		aStrings.push(EXIF.pretty(this)); | ||
|  | 	}); | ||
|  | 	return aStrings; | ||
|  | } | ||
|  | 
 | ||
|  | 
 | ||
|  | })(); | ||
|  | 
 | ||
|  | 
 | ||
|  | })(); |