Splitting a Single JavaScript Program File into Classes

The code included below, when executed, will accept a text file containing a JavaScript program as input and output each root-level function of that script to its own separate text file. The idea is to split a single, consolidated source code file containing many “classes” and split those classes into separate files for better maintainability.

To see the code in action, copy it into an .html file and open that file in a web browser that runs JavaScript.

In order for the “deconcatenation” to work correctly, a very specific program format is assumed. As an example, the text file containing the program listing could actually be used as input to an executing instance of itself. Alternately, almost any other post on this site that includes a JavaScript program can also be used as a model.

UPDATE 2016/06/14 – I have modified this code slightly to make it work better. A couple of the files it generates (mainly _Master.html) still need some manual editing, and I had to reset my browser settings to just save all the files to a designated directory rather than prompting for a new download location for each file. But I was able to use this utility to break the code from another post from a single file to about 70 class files, which I uploaded to github.com as an experiment. So that’s good enough for me.

UPDATE 2017/02/21 – That’s not good enough for me anymore. I have updated this program so that it not only requires no manual processing for the output code to work, but it also downloads a single .TAR file rather than many (potentially very many) individual .js and .html files. I’m also finally going to publish this thing, even though the code is a little messy and nobody but myself is likely to find much use from it.


<html>
<body>

	<label>File to deconcatenate:</label>
	<input id="inputFileToLoad" type="file" onchange="inputFileToLoad_OnChange()"></input>

<script type="text/javascript">

// user interface events

function inputFileToLoad_OnChange()
{
	var inputFileToLoad = document.getElementById("inputFileToLoad");
	var fileToLoad = inputFileToLoad.files[0];
	new Deconcatenator().deconcatenate(fileToLoad);
}

// extensions

function StringExtensions()
{
	// extension class
}
{
	String.prototype.padLeft = function(lengthToPadTo, characterToPadWith)
	{
		var result = this;
		
		if (characterToPadWith == null)
		{
			characterToPadWith = " ";
		}
	
		while (result.length < lengthToPadTo)
		{
			result = characterToPadWith + result;
		}
		
		return result;
	}

	String.prototype.padRight = function(lengthToPadTo, characterToPadWith)
	{
		var result = this;
		
		if (characterToPadWith == null)
		{
			characterToPadWith = " ";
		}
	
		while (result.length < lengthToPadTo)
		{
			result += characterToPadWith;
		}
		
		return result;
	}
}

// classes

function ByteHelper()
{
	// static class
}
{
	ByteHelper.stringToBytes = function(stringToConvert)
	{
		var returnValues = [];

		for (var i = 0; i < stringToConvert.length; i++)
		{
			var charAsBytes = stringToConvert.charCodeAt(i);
			returnValues.push(charAsBytes);
		}

		return returnValues;
	}
}

function ByteStream(bytes)
{
	this.bytes = bytes;  

	this.byteIndexCurrent = 0;
}
{
	// constants

	ByteStream.BitsPerByte = 8;
	ByteStream.BitsPerByteTimesTwo = ByteStream.BitsPerByte * 2;
	ByteStream.BitsPerByteTimesThree = ByteStream.BitsPerByte * 3;

	// instance methods

	ByteStream.prototype.hasMoreBytes = function()
	{
		return (this.byteIndexCurrent < this.bytes.length);
	}
	
	ByteStream.prototype.readBytes = function(numberOfBytesToRead)
	{
		var returnValue = [];

		for (var b = 0; b < numberOfBytesToRead; b++)
		{
			returnValue[b] = this.readByte();
		}

		return returnValue;
	}

	ByteStream.prototype.readByte = function()
	{
		var returnValue = this.bytes[this.byteIndexCurrent];

		this.byteIndexCurrent++;

		return returnValue;
	}

	ByteStream.prototype.readString = function(lengthOfString)
	{
		var returnValue = "";

		for (var i = 0; i < lengthOfString; i++)
		{
			var byte = this.readByte();

			if (byte != 0)
			{
				var byteAsChar = String.fromCharCode(byte);
				returnValue += byteAsChar;
			}
		}

		return returnValue;
	}

	ByteStream.prototype.writeBytes = function(bytesToWrite)
	{
		for (var b = 0; b < bytesToWrite.length; b++)
		{
			this.bytes.push(bytesToWrite[b]);
		}

		this.byteIndexCurrent = this.bytes.length;
	}

	ByteStream.prototype.writeByte = function(byteToWrite)
	{
		this.bytes.push(byteToWrite);

		this.byteIndexCurrent++;
	}

	ByteStream.prototype.writeString = function(stringToWrite, lengthPadded)
	{	
		for (var i = 0; i < stringToWrite.length; i++)
		{
			this.writeByte(stringToWrite.charCodeAt(i));
		}
		
		var numberOfPaddingChars = lengthPadded - stringToWrite.length;
		for (var i = 0; i < numberOfPaddingChars; i++)
		{
			this.writeByte(0);
		}
	}
}

function Deconcatenator()
{
	// do nothing
}
{
	// constants

	Deconcatenator.LineFeed = "\n";
	Deconcatenator.Newline = "\r\n";
	Deconcatenator.TokenForEndOfFunction = "}";
	Deconcatenator.TokenForStartOfFunction = "function ";
	Deconcatenator.TokenForScriptTagOpen = "<script";
	Deconcatenator.TokenForScriptTagClose = "</script";


	// methods

	Deconcatenator.prototype.deconcatenate = function(fileToLoad)
	{
		var fileReader = new FileReader();
		fileReader.onload = this.handleEventFileReaderOnload.bind(this);
		fileReader.readAsText(fileToLoad, "UTF-8");
	}

	Deconcatenator.prototype.handleEventFileReaderOnload = function(fileLoadedEvent) 
	{
		var textFromFile = fileLoadedEvent.target.result;
		this.processCode(textFromFile);
	}

	Deconcatenator.prototype.processCode = function(textFromFile)
	{
		this.filesSoFar = [];
		this.textForFileMaster = "";
		this.nameOfFunction = null;
		this.blocksForFunction = [];
		this.textForBlockCurrent = "";

		var textFromFileAsLines = textFromFile.split
		(
			Deconcatenator.LineFeed
		);

		var numberOfLines = textFromFileAsLines.length;

		for (var i = 0; i < numberOfLines; i++)
		{
			var lineFromFile = textFromFileAsLines[i];

			if (lineFromFile.startsWith(Deconcatenator.TokenForScriptTagClose))
			{
				break;
			}

			if (lineFromFile.startsWith(Deconcatenator.TokenForStartOfFunction))
			{
				this.processStartOfFunction(lineFromFile);
			}

			if (lineFromFile.startsWith(Deconcatenator.TokenForScriptTagOpen) == false)
			{
				this.textForBlockCurrent += 
					lineFromFile 
					+ Deconcatenator.LineFeed;
			}
			
			if (lineFromFile.startsWith(Deconcatenator.TokenForEndOfFunction))
			{
				this.blocksForFunction.push
				(
					this.textForBlockCurrent
				);
				this.textForBlockCurrent = "";
			}
		}

		var textForFileFunction = "";
		for (var b = 0; b < this.blocksForFunction.length; b++)
		{
			textForFileFunction += this.blocksForFunction[b];
		}
		var fileNameToSaveAs = this.nameOfFunction + ".js";
		var fileForFunction = new FileWrapper
		(
			fileNameToSaveAs, false, textForFileFunction
		);
		this.filesSoFar.push(fileForFunction);

		this.textForFileMaster += this.textForBlockCurrent;
		var fileForMaster = new FileWrapper
		(
			"_Master.html", false, this.textForFileMaster
		);
		this.filesSoFar.push(fileForMaster);

		var tarFile = TarFile.new();

		for (var i = 0; i < this.filesSoFar.length; i++)
		{
			var file = this.filesSoFar[i];

			var fileContentsAsBytes = ByteHelper.stringToBytes
			(
				file.contents
			);

			var fileAsTarEntry = TarFileEntry.fileNew
			(
				file.name,
				fileContentsAsBytes
			);

			tarFile.entries.push(fileAsTarEntry);
		}

		var tarFileAsBytes = tarFile.toBytes();

		FileHelper.saveBytesAsFile
		(
			tarFileAsBytes,
			"Program.tar"
		);
	}

	Deconcatenator.prototype.processStartOfFunction = function(lineFromFile)
	{
		if (this.nameOfFunction == null)
		{
			this.textForFileMaster += this.textForBlockCurrent;
			this.textForBlockCurrent = "";
		}
		else if (this.textForBlockCurrent.length > 0)
		{
			var fileNameToSaveAs = this.nameOfFunction + ".js"

			var textForFileFunction = this.blocksForFunction.join
			(
				Deconcatenator.LineFeed
			);
			
			var file = new FileWrapper
			(
				fileNameToSaveAs, false, textForFileFunction
			);

			this.filesSoFar.push(file);
		}

		var indexAtEndOfFunctionName = lineFromFile.indexOf("(");
		if (indexAtEndOfFunctionName == -1)
		{
			indexAtEndOfFunctionName = lineFromFile.length - 1;
		}
				
		this.nameOfFunction = lineFromFile.substring
		(
			Deconcatenator.TokenForStartOfFunction.length,
			indexAtEndOfFunctionName
		);

		this.blocksForFunction = [];

		this.textForFileMaster += 
			"<script type=\"text/javascript\" src=\"" 
			+ this.nameOfFunction 
			+ ".js\">"
			+ "<" + "/script>" 
			+ Deconcatenator.Newline;

		this.textForFileFunction = "";
	}
}

function FileWrapper(name, isTextRatherThanBinary, contents)
{
	this.name = name;
	this.isTextRatherThanBinary = isTextRatherThanBinary;
	this.contents = contents;
}

function FileHelper()
{}
{
	FileHelper.destroyClickedElement = function(event)
	{
		document.body.removeChild(event.target);
	}

	FileHelper.loadFileAsBinaryString = function(fileToLoad, contextForCallback, callback)
	{	
		var fileReader = new FileReader();
		fileReader.onloadend = function(fileLoadedEvent)
		{
			var returnValue = null;

			if (fileLoadedEvent.target.readyState == FileReader.DONE)
			{
				returnValue = fileLoadedEvent.target.result;
			}

			callback.call
			(
				contextForCallback, 
				fileToLoad,
				returnValue
			);
		}

		fileReader.readAsBinaryString(fileToLoad);
	}

	FileHelper.saveBytesAsFile = function(bytesToWrite, fileNameToSaveAs)
	{
		var bytesToWriteAsArrayBuffer = new ArrayBuffer(bytesToWrite.length);
		var bytesToWriteAsUIntArray = new Uint8Array(bytesToWriteAsArrayBuffer);
		for (var i = 0; i < bytesToWrite.length; i++) 
		{
			bytesToWriteAsUIntArray[i] = bytesToWrite[i];
		}

		var bytesToWriteAsBlob = new Blob
		(
			[ bytesToWriteAsArrayBuffer ], 
			{ type:"application/type" }
		);

		var downloadLink = document.createElement("a");
		downloadLink.download = fileNameToSaveAs;
		downloadLink.innerHTML = "Download File";
		downloadLink.href = window.URL.createObjectURL(bytesToWriteAsBlob);
		downloadLink.onclick = FileHelper.destroyClickedElement;
		downloadLink.style.display = "none";
		document.body.appendChild(downloadLink);	
		downloadLink.click();
	}

	Deconcatenator.prototype.saveTextAsFile = function(textToSave, fileNameToSaveTo)
	{
		var textToSaveAsBlob = new Blob([textToSave], {type:"text/plain"});
		var textToSaveAsURL = window.URL.createObjectURL(textToSaveAsBlob);
	
		var downloadLink = document.createElement("a");
		downloadLink.download = fileNameToSaveTo;
		downloadLink.innerHTML = "Download File";
		downloadLink.href = textToSaveAsURL;
		downloadLink.onclick = function(event) 
		{ 
			document.body.removeChild(event.target); 
		}
		downloadLink.style.display = "none";
		document.body.appendChild(downloadLink);
		downloadLink.click();
	}

}

function TarFile(fileName, entries)
{
	this.fileName = fileName;
	this.entries = entries;
}
{
	// constants

	TarFile.ChunkSize = 512;

	// static methods

	TarFile.fromBytes = function(fileName, bytes)
	{
		var reader = new ByteStream(bytes);

		var entries = [];

		var chunkSize = TarFile.ChunkSize;

		var numberOfConsecutiveZeroChunks = 0;

		while (reader.hasMoreBytes() == true)
		{
			var chunkAsBytes = reader.readBytes(chunkSize);

			var areAllBytesInChunkZeroes = true;

			for (var b = 0; b < chunkAsBytes.length; b++)
			{
				if (chunkAsBytes[b] != 0)
				{
					areAllBytesInChunkZeroes = false;
					break;
				}
			}

			if (areAllBytesInChunkZeroes == true)
			{
				numberOfConsecutiveZeroChunks++;

				if (numberOfConsecutiveZeroChunks == 2)
				{
					break;
				}
			}
			else
			{
				numberOfConsecutiveZeroChunks = 0;

				var entry = TarFileEntry.fromBytes(chunkAsBytes, reader);

				entries.push(entry);
			}
		}

		var returnValue = new TarFile
		(
			fileName,
			entries
		);

		return returnValue;
	}
	
	TarFile.new = function(fileName)
	{
		return new TarFile
		(
			fileName,
			[] // entries
		);
	}

	// instance methods
	
	TarFile.prototype.downloadAs = function(fileNameToSaveAs)
	{	
		FileHelper.saveBytesAsFile
		(
			this.toBytes(),
			fileNameToSaveAs
		)
	}	
	
	TarFile.prototype.entriesForDirectories = function()
	{
		var returnValues = [];
		
		for (var i = 0; i < this.entries.length; i++)
		{
			var entry = this.entries[i];
			if (entry.header.typeFlag.name == "Directory")
			{
				returnValues.push(entry);
			}
		}
		
		return returnValues;
	}
	
	TarFile.prototype.toBytes = function()
	{
		var fileAsBytes = [];		

		// hack - For easier debugging.
		var entriesAsByteArrays = [];
		
		for (var i = 0; i < this.entries.length; i++)
		{
			var entry = this.entries[i];
			var entryAsBytes = entry.toBytes();
			entriesAsByteArrays.push(entryAsBytes);
		}		
		
		for (var i = 0; i < entriesAsByteArrays.length; i++)
		{
			var entryAsBytes = entriesAsByteArrays[i];
			fileAsBytes = fileAsBytes.concat(entryAsBytes);
		}
		
		var chunkSize = TarFile.ChunkSize;
		
		var numberOfZeroChunksToWrite = 2;
		
		for (var i = 0; i < numberOfZeroChunksToWrite; i++)
		{
			for (var b = 0; b < chunkSize; b++)
			{
				fileAsBytes.push(0);
			}
		}

		return fileAsBytes;
	}
	
	// strings

	TarFile.prototype.toString = function()
	{
		var newline = "\n";

		var returnValue = "[TarFile]" + newline;

		for (var i = 0; i < this.entries.length; i++)
		{
			var entry = this.entries[i];
			var entryAsString = entry.toString();
			returnValue += entryAsString;
		}

		returnValue += "[/TarFile]" + newline;

		return returnValue;
	}
}

function TarFileEntry(header, dataAsBytes)
{
	this.header = header;
	this.dataAsBytes = dataAsBytes;
}
{
	// methods
	
	// static methods
	
	TarFileEntry.directoryNew = function(directoryName)
	{
		var header = new TarFileEntryHeader.directoryNew(directoryName);
		
		var entry = new TarFileEntry(header, []);
		
		return entry;
	}
	
	TarFileEntry.fileNew = function(fileName, fileContentsAsBytes)
	{
		var header = new TarFileEntryHeader.fileNew(fileName, fileContentsAsBytes);
		
		var entry = new TarFileEntry(header, fileContentsAsBytes);
		
		return entry;
	}
	
	TarFileEntry.fromBytes = function(chunkAsBytes, reader)
	{
		var chunkSize = TarFile.ChunkSize;
	
		var header = TarFileEntryHeader.fromBytes
		(
			chunkAsBytes
		);
	
		var sizeOfDataEntryInBytesUnpadded = header.fileSizeInBytes;	

		var numberOfChunksOccupiedByDataEntry = Math.ceil
		(
			sizeOfDataEntryInBytesUnpadded / chunkSize
		)
	
		var sizeOfDataEntryInBytesPadded = 
			numberOfChunksOccupiedByDataEntry
			* chunkSize;
	
		var dataAsBytes = reader.readBytes
		(
			sizeOfDataEntryInBytesPadded
		).slice
		(
			0, sizeOfDataEntryInBytesUnpadded
		);
	
		var entry = new TarFileEntry(header, dataAsBytes);
		
		return entry;
	}
	
	TarFileEntry.manyFromByteArrays = function
	(
		fileNamePrefix, fileNameSuffix, entriesAsByteArrays
	)
	{
		var returnValues = [];
		
		for (var i = 0; i < entriesAsByteArrays.length; i++)
		{
			var entryAsBytes = entriesAsByteArrays[i];
			var entry = TarFileEntry.fileNew
			(		
				fileNamePrefix + i + fileNameSuffix,
				entryAsBytes
			);
			
			returnValues.push(entry);
		}
		
		return returnValues;
	}
	
	// instance methods

	TarFileEntry.prototype.download = function(event)
	{
		FileHelper.saveBytesAsFile
		(
			this.dataAsBytes,
			this.header.fileName
		);
	}
	
	TarFileEntry.prototype.remove = function(event)
	{
		alert("Not yet implemented!"); // todo
	}
	
	TarFileEntry.prototype.toBytes = function()
	{
		var entryAsBytes = [];
	
		var chunkSize = TarFile.ChunkSize;
	
		var headerAsBytes = this.header.toBytes();
		entryAsBytes = entryAsBytes.concat(headerAsBytes);
		
		entryAsBytes = entryAsBytes.concat(this.dataAsBytes);

		var sizeOfDataEntryInBytesUnpadded = this.header.fileSizeInBytes;	

		var numberOfChunksOccupiedByDataEntry = Math.ceil
		(
			sizeOfDataEntryInBytesUnpadded / chunkSize
		)
	
		var sizeOfDataEntryInBytesPadded = 
			numberOfChunksOccupiedByDataEntry
			* chunkSize;
			
		var numberOfBytesOfPadding = 
			sizeOfDataEntryInBytesPadded - sizeOfDataEntryInBytesUnpadded;
	
		for (var i = 0; i < numberOfBytesOfPadding; i++)
		{
			entryAsBytes.push(0);
		}
		
		return entryAsBytes;
	}	
		
	// strings
	
	TarFileEntry.prototype.toString = function()
	{
		var newline = "\n";

		headerAsString = this.header.toString();

		var dataAsHexadecimalString = ByteHelper.bytesToStringHexadecimal
		(
			this.dataAsBytes
		);

		var returnValue = 
			"[TarFileEntry]" + newline
			+ headerAsString
			+ "[Data]"
			+ dataAsHexadecimalString
			+ "[/Data]" + newline
			+ "[/TarFileEntry]"
			+ newline;

		return returnValue
	}
	
}

function TarFileEntryHeader
(
	fileName,
	fileMode,
	userIDOfOwner,
	userIDOfGroup,
	fileSizeInBytes,
	timeModifiedInUnixFormat,
	checksum,
	typeFlag,
	nameOfLinkedFile,
	uStarIndicator,
	uStarVersion,
	userNameOfOwner,
	groupNameOfOwner,
	deviceNumberMajor,
	deviceNumberMinor,
	filenamePrefix
)
{
	this.fileName = fileName;
	this.fileMode = fileMode;
	this.userIDOfOwner = userIDOfOwner;
	this.userIDOfGroup = userIDOfGroup;
	this.fileSizeInBytes = fileSizeInBytes;
	this.timeModifiedInUnixFormat = timeModifiedInUnixFormat;
	this.checksum = checksum;
	this.typeFlag = typeFlag;
	this.nameOfLinkedFile = nameOfLinkedFile;
	this.uStarIndicator = uStarIndicator;
	this.uStarVersion = uStarVersion;
	this.userNameOfOwner = userNameOfOwner;
	this.groupNameOfOwner = groupNameOfOwner;
	this.deviceNumberMajor = deviceNumberMajor;
	this.deviceNumberMinor = deviceNumberMinor;
	this.filenamePrefix = filenamePrefix;
}
{
	TarFileEntryHeader.SizeInBytes = 500;

	// static methods
	
	TarFileEntryHeader.default = function()
	{
		var returnValue = new TarFileEntryHeader
		(
			"".padRight(100, "\0"), // fileName
			"100777 \0", // fileMode
			"0 \0".padLeft(8), // userIDOfOwner
			"0 \0".padLeft(8), // userIDOfGroup
			0, // fileSizeInBytes
			[49, 50, 55, 50, 49, 49, 48, 55, 53, 55, 52, 32], // hack - timeModifiedInUnixFormat
			0, // checksum
			TarFileTypeFlag.Instances.Normal,		
			"".padRight(100, "\0"), // nameOfLinkedFile,
			"".padRight(6, "\0"), // uStarIndicator,
			"".padRight(2, "\0"), // uStarVersion,
			"".padRight(32, "\0"), // userNameOfOwner,
			"".padRight(32, "\0"), // groupNameOfOwner,
			"".padRight(8, "\0"), // deviceNumberMajor,
			"".padRight(8, "\0"), // deviceNumberMinor,
			"".padRight(155, "\0") // filenamePrefix	
		);		
		
		return returnValue;
	}
	
	TarFileEntryHeader.directoryNew = function(directoryName)
	{
		var header = TarFileEntryHeader.default();
		header.fileName = directoryName;
		header.typeFlag = TarFileTypeFlag.Instances.Directory;
		header.fileSizeInBytes = 0;
		header.checksumCalculate();
		
		return header;
	}
	
	TarFileEntryHeader.fileNew = function(fileName, fileContentsAsBytes)
	{
		var header = TarFileEntryHeader.default();
		header.fileName = fileName;
		header.typeFlag = TarFileTypeFlag.Instances.Normal;
		header.fileSizeInBytes = fileContentsAsBytes.length;
		header.checksumCalculate();
		
		return header;
	}

	TarFileEntryHeader.fromBytes = function(bytes)
	{
		var reader = new ByteStream(bytes);

		var fileName = reader.readString(100).trim();
		var fileMode = reader.readString(8);
		var userIDOfOwner = reader.readString(8);
		var userIDOfGroup = reader.readString(8);
		var fileSizeInBytesAsStringOctal = reader.readString(12);
		var timeModifiedInUnixFormat = reader.readBytes(12);
		var checksumAsStringOctal = reader.readString(8);
		var typeFlagValue = reader.readString(1);
		var nameOfLinkedFile = reader.readString(100);
		var uStarIndicator = reader.readString(6);
		var uStarVersion = reader.readString(2);
		var userNameOfOwner = reader.readString(32);
		var groupNameOfOwner = reader.readString(32);
		var deviceNumberMajor = reader.readString(8);
		var deviceNumberMinor = reader.readString(8);
		var filenamePrefix = reader.readString(155);
		var reserved = reader.readBytes(12);

		var fileSizeInBytes = parseInt
		(
			fileSizeInBytesAsStringOctal.trim(), 8
		);
		
		var checksum = parseInt
		(
			checksumAsStringOctal, 8
		);		
		
		var typeFlags = TarFileTypeFlag.Instances._All;
		var typeFlagID = "_" + typeFlagValue;
		var typeFlag = typeFlags[typeFlagID];

		var returnValue = new TarFileEntryHeader
		(
			fileName,
			fileMode,
			userIDOfOwner,
			userIDOfGroup,
			fileSizeInBytes,
			timeModifiedInUnixFormat,
			checksum,
			typeFlag,
			nameOfLinkedFile,
			uStarIndicator,
			uStarVersion,
			userNameOfOwner,
			groupNameOfOwner,
			deviceNumberMajor,
			deviceNumberMinor,
			filenamePrefix
		);

		return returnValue;
	}

	// instance methods
	
	TarFileEntryHeader.prototype.checksumCalculate = function()
	{	
		var thisAsBytes = this.toBytes();
	
		// The checksum is the sum of all bytes in the header,
		// except we obviously can't include the checksum itself.
		// So it's assumed that all 8 of checksum's bytes are spaces (0x20=32).
		// So we need to set this manually.
						
		var offsetOfChecksumInBytes = 148;
		var numberOfBytesInChecksum = 8;
		var presumedValueOfEachChecksumByte = " ".charCodeAt(0);
		for (var i = 0; i < numberOfBytesInChecksum; i++)
		{
			var offsetOfByte = offsetOfChecksumInBytes + i;
			thisAsBytes[offsetOfByte] = presumedValueOfEachChecksumByte;
		}
		
		var checksumSoFar = 0;

		for (var i = 0; i < thisAsBytes.length; i++)
		{
			var byteToAdd = thisAsBytes[i];
			checksumSoFar += byteToAdd;
		}		

		this.checksum = checksumSoFar;
		
		return this.checksum;
	}
	
	TarFileEntryHeader.prototype.toBytes = function()
	{
		var headerAsBytes = [];
		var writer = new ByteStream(headerAsBytes);
		
		var fileSizeInBytesAsStringOctal = (this.fileSizeInBytes.toString(8) + " ").padLeft(12, " ")
		var checksumAsStringOctal = (this.checksum.toString(8) + " \0").padLeft(8, " ");

		writer.writeString(this.fileName, 100);
		writer.writeString(this.fileMode, 8);
		writer.writeString(this.userIDOfOwner, 8);
		writer.writeString(this.userIDOfGroup, 8);
		writer.writeString(fileSizeInBytesAsStringOctal, 12);
		writer.writeBytes(this.timeModifiedInUnixFormat);
		writer.writeString(checksumAsStringOctal, 8);
		writer.writeString(this.typeFlag.value, 1);		
		writer.writeString(this.nameOfLinkedFile, 100);
		writer.writeString(this.uStarIndicator, 6);
		writer.writeString(this.uStarVersion, 2);
		writer.writeString(this.userNameOfOwner, 32);
		writer.writeString(this.groupNameOfOwner, 32);
		writer.writeString(this.deviceNumberMajor, 8);
		writer.writeString(this.deviceNumberMinor, 8);
		writer.writeString(this.filenamePrefix, 155);
		writer.writeString("".padRight(12, "\0")); // reserved

		return headerAsBytes;
	}		
		
	// strings

	TarFileEntryHeader.prototype.toString = function()
	{		
		var newline = "\n";
	
		var returnValue = 
			"[TarFileEntryHeader "
			+ "fileName='" + this.fileName + "' "
			+ "typeFlag='" + (this.typeFlag == null ? "err" : this.typeFlag.name) + "' "
			+ "fileSizeInBytes='" + this.fileSizeInBytes + "' "

			/*
			+ "fileMode='" + "[value]" + "' "
			+ "userIDOfOwner='" + "[value]" + "' "
			+ "userIDOfGroup='" + "[value]" + "' "
			+ "timeModifiedInUnixFormat='" + "[value]" + "' "
			+ "checksum='" + "[value]" + "' "
			+ "nameOfLinkedFile='" + "[value]" + "' "
			+ "uStarIndicator='" + "[value]" + "' "
			+ "uStarVersion='" + "[value]" + "' "
			+ "userNameOfOwner='" + "[value]" + "' "
			+ "groupNameOfOwner='" + "[value]" + "' "
			+ "deviceNumberMajor='" + "[value]" + "' "
			+ "deviceNumberMinor='" + "[value]" + "' "
			+ "filenamePrefix='" + "[value]" + "' "
			*/

			+ "]"
			+ newline;

		return returnValue;
	}
}	

function TarFileTypeFlag(value, name)
{
	this.value = value;
	this.id = "_" + this.value;
	this.name = name;
}
{
	TarFileTypeFlag.Instances = new TarFileTypeFlag_Instances();

	function TarFileTypeFlag_Instances()
	{
		this.Normal 		= new TarFileTypeFlag("0", "Normal");
		this.HardLink 		= new TarFileTypeFlag("1", "Hard Link");
		this.SymbolicLink 	= new TarFileTypeFlag("2", "Symbolic Link");
		this.CharacterSpecial 	= new TarFileTypeFlag("3", "Character Special");
		this.BlockSpecial 	= new TarFileTypeFlag("4", "Block Special");
		this.Directory		= new TarFileTypeFlag("5", "Directory");
		this.FIFO		= new TarFileTypeFlag("6", "FIFO");
		this.ContiguousFile 	= new TarFileTypeFlag("7", "Contiguous File");

		// Additional types not implemented:
		// 'g' - global extended header with meta data (POSIX.1-2001)
		// 'x' - extended header with meta data for the next file in the archive (POSIX.1-2001)
		// 'A'–'Z' - Vendor specific extensions (POSIX.1-1988)
		// [other values] - reserved for future standardization

		this._All = 
		[
			this.Normal,
			this.HardLink,
			this.SymbolicLink,
			this.CharacterSpecial,
			this.BlockSpecial,
			this.Directory,
			this.FIFO,
			this.ContiguousFile,
		];

		for (var i = 0; i < this._All.length; i++)
		{
			var item = this._All[i];
			this._All[item.id] = item;
		}
	}
}

</script>
</body>
</html>

Advertisements
This entry was posted in Uncategorized and tagged , . Bookmark the permalink.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s