text: /* jshint worker:true */
  (function main(global) {
  	"use strict";
  
  	if (global.zWorkerInitialized)
  		throw new Error('z-worker.js should be run only once');
  
  	global.zWorkerInitialized = true;
  
  	addEventListener("message", function(event) {
  		var message = event.data, type = message.type, sn = message.sn;
  		var handler = handlers[type];
  		if (handler) {
  			try {
  				handler(message);
  			} catch (e) {
  				onError(type, sn, e);
  			}
  		}
  		//for debug
  		//postMessage({type: 'echo', originalType: type, sn: sn});
  	});
  
  	var handlers = {
  		importScripts: doImportScripts,
  		newTask: newTask,
  		append: processData,
  		flush: processData,
  	};
  
  	// deflater/inflater tasks indexed by serial numbers
  	var tasks = {};
  
  	function doImportScripts(msg) {
  		if (msg.scripts && msg.scripts.length > 0)
  			importScripts.apply(undefined, msg.scripts);
  		postMessage({type: 'importScripts'});
  	}
  
  	function newTask(msg) {
  		var CodecClass = global[msg.codecClass];
  		var sn = msg.sn;
  		if (tasks[sn])
  			throw Error('duplicated sn');
  		tasks[sn] =  {
  			codec: new CodecClass(msg.options),
  			crcInput: msg.crcType === 'input',
  			crcOutput: msg.crcType === 'output',
  			crc: new Crc32(),
  		};
  		postMessage({type: 'newTask', sn: sn});
  	}
  
  	// performance may not be supported
  	var now = global.performance ? global.performance.now.bind(global.performance) : Date.now;
  
  	function processData(msg) {
  		var sn = msg.sn, type = msg.type, input = msg.data;
  		var task = tasks[sn];
  		// allow creating codec on first append
  		if (!task && msg.codecClass) {
  			newTask(msg);
  			task = tasks[sn];
  		}
  		var isAppend = type === 'append';
  		var start = now();
  		var output;
  		if (isAppend) {
  			try {
  				output = task.codec.append(input, function onprogress(loaded) {
  					postMessage({type: 'progress', sn: sn, loaded: loaded});
  				});
  			} catch (e) {
  				delete tasks[sn];
  				throw e;
  			}
  		} else {
  			delete tasks[sn];
  			output = task.codec.flush();
  		}
  		var codecTime = now() - start;
  
  		start = now();
  		if (input && task.crcInput)
  			task.crc.append(input);
  		if (output && task.crcOutput)
  			task.crc.append(output);
  		var crcTime = now() - start;
  
  		var rmsg = {type: type, sn: sn, codecTime: codecTime, crcTime: crcTime};
  		var transferables = [];
  		if (output) {
  			rmsg.data = output;
  			transferables.push(output.buffer);
  		}
  		if (!isAppend && (task.crcInput || task.crcOutput))
  			rmsg.crc = task.crc.get();
  
  		// posting a message with transferables will fail on IE10
  		try {
  			postMessage(rmsg, transferables);
  		} catch(ex) {
  			postMessage(rmsg); // retry without transferables
  		}
  	}
  
  	function onError(type, sn, e) {
  		var msg = {
  			type: type,
  			sn: sn,
  			error: formatError(e)
  		};
  		postMessage(msg);
  	}
  
  	function formatError(e) {
  		return { message: e.message, stack: e.stack };
  	}
  
  	// Crc32 code copied from file zip.js
  	function Crc32() {
  		this.crc = -1;
  	}
  	Crc32.prototype.append = function append(data) {
  		var crc = this.crc | 0, table = this.table;
  		for (var offset = 0, len = data.length | 0; offset < len; offset++)
  			crc = (crc >>> 8) ^ table[(crc ^ data[offset]) & 0xFF];
  		this.crc = crc;
  	};
  	Crc32.prototype.get = function get() {
  		return ~this.crc;
  	};
  	Crc32.prototype.table = (function() {
  		var i, j, t, table = []; // Uint32Array is actually slower than []
  		for (i = 0; i < 256; i++) {
  			t = i;
  			for (j = 0; j < 8; j++)
  				if (t & 1)
  					t = (t >>> 1) ^ 0xEDB88320;
  				else
  					t = t >>> 1;
  			table[i] = t;
  		}
  		return table;
  	})();
  
  	// "no-op" codec
  	function NOOP() {}
  	global.NOOP = NOOP;
  	NOOP.prototype.append = function append(bytes, onprogress) {
  		return bytes;
  	};
  	NOOP.prototype.flush = function flush() {};
  })(this);
  