Gildas lormeau 12 лет назад
Родитель
Сommit
9feaac7212
2 измененных файлов с 450 добавлено и 446 удалено
  1. 8 4
      WebContent/core/scripts/bg/nio.js
  2. 442 442
      WebContent/core/scripts/common/docprocessor.js

+ 8 - 4
WebContent/core/scripts/bg/nio.js

@@ -37,10 +37,12 @@
 		function arrayBufferToBase64(buffer) {
 			var binary, bytes, len, i;
 			binary = "";
-			bytes = new Uint8Array(buffer);
-			len = bytes.byteLength;
-			for (i = 0; i < len; i++) {
-				binary += String.fromCharCode(bytes[i]);
+			if (buffer) {
+				bytes = new Uint8Array(buffer);
+				len = bytes.byteLength;
+				for (i = 0; i < len; i++) {
+					binary += String.fromCharCode(bytes[i]);
+				}
 			}
 			return btoa(binary);
 		}
@@ -79,6 +81,8 @@
 					sendResponses(key);
 				};
 				xhr.onerror = function() {
+					cache[key] = {};
+					keys.push(key);
 					sendResponses(key);
 				};
 				xhr.open("GET", url, true);

+ 442 - 442
WebContent/core/scripts/common/docprocessor.js

@@ -1,442 +1,442 @@
-/*
- * Copyright 2011 Gildas Lormeau
- * contact : gildas.lormeau <at> gmail.com
- * 
- * This file is part of SingleFile Core.
- *
- *   SingleFile Core is free software: you can redistribute it and/or modify
- *   it under the terms of the GNU Lesser General Public License as published by
- *   the Free Software Foundation, either version 3 of the License, or
- *   (at your option) any later version.
- *
- *   SingleFile Core is distributed in the hope that it will be useful,
- *   but WITHOUT ANY WARRANTY; without even the implied warranty of
- *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- *   GNU Lesser General Public License for more details.
- *
- *   You should have received a copy of the GNU Lesser General Public License
- *   along with SingleFile Core.  If not, see <http://www.gnu.org/licenses/>.
- */
-
-(function() {
-
-	var IMPORT_URL_VALUE_EXP = /(url\s*\(\s*(?:'|")?\s*([^('|"|\))]*)\s*(?:'|")?\s*\))|(@import\s*\(?\s*(?:'|")?\s*([^('|"|\))]*)\s*(?:'|")?\s*(?:\)|;))/i;
-	var URL_VALUE_EXP = /url\s*\(\s*(?:'|")?\s*([^('|"|\))]*)\s*(?:'|")?\s*\)/i;
-	var IMPORT_VALUE_ALT_EXP = /@import\s*\(?\s*(?:'|")?\s*([^('|"|\))]*)\s*(?:'|")?\s*(?:\)|;)/i;
-	var URL_EXP = /url\s*\(([^\)]*)\)/gi;
-	var IMPORT_EXP = /(@import\s*url\s*\([^\)]*\)\s*;?)|(@import\s*('|")?\s*[^\(|;|'|"]*\s*('|")?\s*;)/gi;
-	var IMPORT_ALT_EXP = /@import\s*('|")?\s*[^\(|;|'|"]*\s*('|")?\s*;/gi;
-	var EMPTY_PIXEL_DATA = "data:image/gif;base64,R0lGODlhAQABAIAAAP///////yH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==";
-
-	function formatURL(link, host) {
-		var i, newlinkparts, hparts, lparts;
-		if (!link)
-			return "";
-
-		lparts = link.split('/');
-		host = host.split("#")[0].split("?")[0];
-		if (/http:|https:|ftp:|data:|javascript:/i.test(lparts[0]))
-			return link.trim();
-		hparts = host.split('/');
-		newlinkparts = [];
-		if (hparts.length > 3)
-			hparts.pop();
-		if (lparts[0] == '') {
-			if (lparts[1] == '')
-				host = hparts[0] + '//' + lparts[2];
-			else
-				host = hparts[0] + '//' + hparts[2];
-			hparts = host.split('/');
-			delete lparts[0];
-			if (lparts[1] == '') {
-				delete lparts[1];
-				delete lparts[2];
-			}
-		}
-		for (i = 0; i < lparts.length; i++) {
-			if (lparts[i] == '..') {
-				if (lparts[i - 1])
-					delete lparts[i - 1];
-				else if (hparts.length > 3)
-					hparts.pop();
-				delete lparts[i];
-			}
-			if (lparts[i] == '.')
-				delete lparts[i];
-		}
-		for (i = 0; i < lparts.length; i++)
-			if (lparts[i])
-				newlinkparts[newlinkparts.length] = lparts[i];
-		return (hparts.join('/') + '/' + newlinkparts.join('/')).trim();
-	}
-
-	function resolveURLs(content, host) {
-		var ret = content.replace(URL_EXP, function(value) {
-			var result = value.match(URL_VALUE_EXP);
-			if (result)
-				if (result[1].indexOf("data:") != 0)
-					return value.replace(result[1], formatURL(result[1], host));
-			return value;
-		});
-		return ret.replace(IMPORT_ALT_EXP, function(value) {
-			var result = value.match(IMPORT_VALUE_ALT_EXP);
-			if (result)
-				if (result[1].indexOf("data:") != 0)
-					return "@import \"" + formatURL(result[1], host) + "\";";
-			return value;
-		});
-	}
-
-	function getDataURI(data, defaultURL, woURL) {
-		if (data.content)
-			return (woURL ? "" : "url(") + "data:" + data.mediaType + ";" + data.mediaTypeParam + "," + data.content + (woURL ? "" : ")");
-		else
-			return woURL ? defaultURL : "url(" + defaultURL + ")";
-	}
-
-	function removeComments(content) {
-		var start, end;
-		do {
-			start = content.indexOf("/*");
-			end = content.indexOf("*/", start);
-			if (start != -1 && end != -1)
-				content = content.substring(0, start) + content.substr(end + 2);
-		} while (start != -1 && end != -1);
-		return content;
-	}
-
-	function replaceURLs(content, host, requestManager, callback) {
-		var i, url, result, values = removeComments(content).match(URL_EXP), requestMax = 0, requestIndex = 0;
-
-		function sendRequest(origUrl) {
-			requestMax++;
-			requestManager.send(url, function(data) {
-				requestIndex++;
-				if (content.indexOf(origUrl) != -1) {
-					data.mediaType = data.mediaType ? data.mediaType.split(";")[0] : null;
-					content = content.replace(new RegExp(origUrl.replace(/([{}\(\)\^$&.\*\?\/\+\|\[\\\\]|\]|\-)/g, "\\$1"), "gi"), getDataURI(data,
-							EMPTY_PIXEL_DATA, true));
-				}
-				if (requestIndex == requestMax)
-					callback(content);
-			}, "x-user-defined", "base64");
-		}
-
-		if (values)
-			for (i = 0; i < values.length; i++) {
-				result = values[i].match(URL_VALUE_EXP);
-				if (result && result[1]) {
-					url = formatURL(result[1], host);
-					if (url.indexOf("data:") != 0)
-						sendRequest(result[1]);
-				}
-			}
-	}
-
-	// ----------------------------------------------------------------------------------------------
-
-	function processStylesheets(doc, docElement, baseURI, requestManager) {
-		Array.prototype.forEach.call(docElement.querySelectorAll('link[href][rel*="stylesheet"]'), function(node) {
-			var href = node.getAttribute("href"), fullHref = formatURL(href, baseURI);
-			if (href.indexOf("data:") != 0) {
-				requestManager.send(fullHref, function(data) {
-					var i, newNode, commentNode;
-					if (data.status >= 400) {
-						node.parentElement.removeChild(node);
-						return;
-					}
-					newNode = doc.createElement("style");
-					for (i = 0; i < node.attributes.length; i++)
-						if (node.attributes[i].value)
-							newNode.setAttribute(node.attributes[i].name, node.attributes[i].value);
-					newNode._baseURI = fullHref;
-					newNode.removeAttribute("href");
-					newNode.textContent = resolveURLs(data.content || "", data.url);
-					if (node.disabled) {
-						commentNode = doc.createComment();
-						commentNode.textContent = newNode.outerHTML.replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/--/g, "&minus;&minus;");
-						node.parentElement.replaceChild(commentNode, node);
-					} else
-						node.parentElement.replaceChild(newNode, node);
-				});
-			}
-		});
-	}
-
-	function processImports(docElement, baseURI, characterSet, requestManager) {
-		var ret = true;
-		Array.prototype.forEach.call(docElement.querySelectorAll("style"), function(styleSheet) {
-			var i, url, result, imports = removeComments(styleSheet.textContent).match(IMPORT_EXP);
-
-			function sendRequest(imp) {
-				requestManager.send(url, function(data) {
-					styleSheet.textContent = styleSheet.textContent.replace(imp, data.status < 400 && data.content ? resolveURLs(data.content, data.url) : "");
-				}, null, characterSet);
-				ret = false;
-			}
-
-			if (imports)
-				for (i = 0; i < imports.length; i++) {
-					result = imports[i].match(IMPORT_URL_VALUE_EXP);
-					if (result && (result[2] || result[4])) {
-						url = formatURL(result[2] || result[4], styleSheet._baseURI || baseURI);
-						if (url.indexOf("data:") != 0)
-							sendRequest(imports[i]);
-					}
-				}
-		});
-		return ret;
-	}
-
-	function processStyleAttributes(docElement, baseURI, requestManager) {
-		Array.prototype.forEach.call(docElement.querySelectorAll("*[style]"), function(node) {
-			replaceURLs(node.getAttribute("style"), baseURI, requestManager, function(style) {
-				node.setAttribute("style", style);
-			});
-		});
-	}
-
-	function processBgAttributes(docElement, baseURI, requestManager) {
-		var backgrounds = docElement.querySelectorAll("*[background]");
-		Array.prototype.forEach.call(backgrounds, function(node) {
-			var url, value = node.getAttribute("background");
-			if (value.indexOf(".") != -1) {
-				url = formatURL(value, baseURI);
-				if (url.indexOf("data:") != 0)
-					requestManager.send(url, function(data) {
-						node.setAttribute("background", getDataURI(data, EMPTY_PIXEL_DATA, true));
-					}, "x-user-defined", "base64");
-			}
-		});
-	}
-
-	function insertDefaultFavico(doc, docElement, baseURI) {
-		var node, docHead = docElement.querySelector("html > head"), favIcon = docElement
-				.querySelector('link[href][rel="shortcut icon"], link[href][rel="apple-touch-icon"], link[href][rel="icon"]');
-		if (!favIcon && docHead) {
-			node = doc.createElement("link");
-			node.setAttribute("type", "image/x-icon");
-			node.setAttribute("rel", "shortcut icon");
-			node.setAttribute("href", formatURL("/favicon.ico", baseURI));
-			docHead.appendChild(node);
-		}
-	}
-
-	function processImages(docElement, baseURI, requestManager) {
-		var images;
-
-		function process(attributeName) {
-			Array.prototype.forEach.call(images, function(node) {
-				var url = formatURL(node.getAttribute(attributeName), baseURI);
-				if (url.indexOf("data:") != 0)
-					requestManager.send(url, function(data) {
-						node.setAttribute(attributeName, getDataURI(data, EMPTY_PIXEL_DATA, true));
-					}, "x-user-defined", "base64");
-			});
-		}
-
-		images = docElement.querySelectorAll('link[href][rel="shortcut icon"], link[href][rel="apple-touch-icon"], link[href][rel="icon"]');
-		process("href");
-		images = docElement.querySelectorAll('img[src], input[src][type="image"]');
-		process("src");
-		images = docElement.querySelectorAll('video[poster]');
-		process("poster");
-
-	}
-
-	function processSVGs(docElement, baseURI, requestManager) {
-		var images = docElement.querySelectorAll('object[type="image/svg+xml"], object[type="image/svg-xml"], embed[src*=".svg"]');
-		Array.prototype.forEach.call(images, function(node) {
-			var data = node.getAttribute("data"), src = node.getAttribute("src"), url = formatURL(data || src, baseURI);
-			if (url.indexOf("data:") != 0)
-				requestManager.send(url, function(data) {
-					node.setAttribute(data ? "data" : "src", getDataURI(data, "data:text/xml,<svg></svg>", true));
-				}, null, null);
-		});
-	}
-
-	function processStyles(docElement, baseURI, requestManager) {
-		Array.prototype.forEach.call(docElement.querySelectorAll("style"), function(styleSheet) {
-			replaceURLs(styleSheet.textContent, styleSheet._baseURI || baseURI, requestManager, function(textContent) {
-				styleSheet.textContent = textContent;
-			});
-		});
-	}
-
-	function processScripts(docElement, baseURI, characterSet, requestManager) {
-		Array.prototype.forEach.call(docElement.querySelectorAll("script[src]"), function(node) {
-			var src = node.getAttribute("src");
-			if (src.indexOf("data:") != 0)
-				requestManager.send(formatURL(src, baseURI), function(data) {
-					if (data.status < 400) {
-						data.content = data.content.replace(/"([^"]*)<\/\s*script\s*>([^"]*)"/gi, '"$1<"+"/script>$2"');
-						data.content = data.content.replace(/'([^']*)<\/\s*script\s*>([^']*)'/gi, "'$1<'+'/script>$2'");
-						node.textContent = "\n" + data.content + "\n";
-					}
-					node.removeAttribute("src");
-				}, characterSet);
-		});
-	}
-
-	function processCanvas(doc, docElement, canvasData) {
-		var index = 0;
-		Array.prototype.forEach.call(docElement.querySelectorAll("canvas"), function(node) {
-			var i, data = canvasData[index], newNode = doc.createElement("img");
-			if (data) {
-				newNode.setAttribute("src", data);
-				for (i = 0; i < node.attributes.length; i++)
-					if (node.attributes[i].value)
-						newNode.setAttribute(node.attributes[i].name, node.attributes[i].value);
-				if (!newNode.width)
-					newNode.style.pixelWidth = node.clientWidth;
-				if (!newNode.height)
-					newNode.style.pixelHeight = node.clientHeight;
-				node.parentElement.replaceChild(newNode, node);
-			}
-			index++;
-		});
-	}
-
-	function removeScripts(docElement) {
-		Array.prototype.forEach.call(docElement.querySelectorAll("script"), function(node) {
-			node.parentElement.removeChild(node);
-		});
-		Array.prototype.forEach.call(docElement.querySelectorAll("*[onload]"), function(node) {
-			node.removeAttribute("onload");
-		});
-	}
-
-	function removeObjects(docElement) {
-		var objects = docElement.querySelectorAll('applet, object:not([type="image/svg+xml"]):not([type="image/svg-xml"]), embed:not([src*=".svg"])');
-		Array.prototype.forEach.call(objects, function(node) {
-			node.parentElement.removeChild(node);
-		});
-		objects = docElement.querySelectorAll('audio[src], video[src]');
-		Array.prototype.forEach.call(objects, function(node) {
-			node.removeAttribute("src");
-		});
-	}
-
-	function removeBlockquotesCite(docElement) {
-		Array.prototype.forEach.call(docElement.querySelectorAll("blockquote[cite]"), function(node) {
-			node.removeAttribute("cite");
-		});
-	}
-
-	function removeFrames(docElement) {
-		Array.prototype.forEach.call(docElement.querySelectorAll("iframe, frame"), function(node) {
-			node.parentElement.removeChild(node);
-		});
-	}
-
-	function removeMetaRefresh(docElement) {
-		Array.prototype.forEach.call(docElement.querySelectorAll("meta[http-equiv=refresh]"), function(node) {
-			node.parentElement.removeChild(node);
-		});
-	}
-
-	function resetFrames(docElement, baseURI) {
-		Array.prototype.forEach.call(docElement.querySelectorAll("iframe, frame"), function(node) {
-			var src = formatURL(node.getAttribute("src"), baseURI);
-			if (src.indexOf("data:") != 0)
-				node.setAttribute("src", "about:blank");
-		});
-	}
-
-	function setAbsoluteLinks(docElement, baseURI) {
-		Array.prototype.forEach.call(docElement.querySelectorAll("a:not([href^='#'])"), function(link) {
-			var fullHref = formatURL(link.getAttribute("href"), baseURI);
-			if (fullHref && (!(fullHref.indexOf(baseURI.split("#")[0]) == 0) || fullHref.indexOf("#") == -1))
-				link.setAttribute("href", fullHref);
-		});
-	}
-
-	// ----------------------------------------------------------------------------------------------
-
-	singlefile.initProcess = function(doc, docElement, addDefaultFavico, baseURI, characterSet, config, canvasData, requestManager, onInit, onProgress, onEnd) {
-		var initManager = new RequestManager(), manager = new RequestManager(onProgress);
-
-		function RequestManager(onProgress) {
-			var that = this, currentCount = 0, requests = [];
-			this.requestCount = 0;
-			this.send = function(url, responseHandler, characterSet, mediaTypeParam) {
-				this.requestCount++;
-				requests.push({
-					url : url,
-					responseHandler : responseHandler,
-					characterSet : characterSet,
-					mediaTypeParam : mediaTypeParam
-				});
-			};
-			this.doSend = function() {
-				requests.forEach(function(request) {
-					requestManager.send(request.url, function(response) {
-						request.responseHandler(response);
-						currentCount++;
-						if (onProgress)
-							onProgress(currentCount, that.requestCount);
-						if (currentCount == that.requestCount) {
-							that.requestCount = 0;
-							currentCount = 0;
-							if (that.onEnd)
-								that.onEnd();
-						}
-					}, request.characterSet, request.mediaTypeParam);
-				});
-				requests = [];
-			};
-		}
-
-		function cbImports() {
-			if (config.removeScripts)
-				removeScripts(docElement);
-			if (config.removeObjects)
-				removeObjects(docElement);
-			if (config.removeFrames || config.getRawDoc)
-				removeFrames(docElement);
-			resetFrames(docElement, baseURI);
-			removeBlockquotesCite(docElement);
-			removeMetaRefresh(docElement);
-			setAbsoluteLinks(docElement, baseURI);
-			if (addDefaultFavico)
-				insertDefaultFavico(doc, docElement, baseURI);
-			processStyleAttributes(docElement, baseURI, manager);
-			processBgAttributes(docElement, baseURI, manager);
-			processImages(docElement, baseURI, manager);
-			processSVGs(docElement, baseURI, manager);
-			processStyles(docElement, baseURI, manager);
-			processScripts(docElement, baseURI, characterSet, manager);
-			processCanvas(doc, docElement, canvasData);
-			if (onInit)
-				setTimeout(function() {
-					onInit(manager.requestCount);
-				}, 1);
-		}
-
-		function cbStylesheets() {
-			initManager.onEnd = function(noRequests) {
-				if (noRequests)
-					cbImports();
-				else
-					cbStylesheets();
-			};
-			processImports(docElement, baseURI, characterSet, initManager);
-			initManager.doSend();
-			if (initManager.requestCount == 0)
-				cbImports();
-		}
-
-		manager.onEnd = onEnd;
-		processStylesheets(doc, docElement, baseURI, initManager);
-		initManager.onEnd = cbStylesheets;
-		initManager.doSend();
-		if (initManager.requestCount == 0)
-			initManager.onEnd();
-		return function() {
-			manager.doSend();
-			if (manager.onEnd && manager.requestCount == 0)
-				manager.onEnd();
-		};
-	};
-
-})();
+/*
+ * Copyright 2011 Gildas Lormeau
+ * contact : gildas.lormeau <at> gmail.com
+ * 
+ * This file is part of SingleFile Core.
+ *
+ *   SingleFile Core is free software: you can redistribute it and/or modify
+ *   it under the terms of the GNU Lesser General Public License as published by
+ *   the Free Software Foundation, either version 3 of the License, or
+ *   (at your option) any later version.
+ *
+ *   SingleFile Core is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU Lesser General Public License for more details.
+ *
+ *   You should have received a copy of the GNU Lesser General Public License
+ *   along with SingleFile Core.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+(function() {
+
+	var IMPORT_URL_VALUE_EXP = /(url\s*\(\s*(?:'|")?\s*([^('|"|\))]*)\s*(?:'|")?\s*\))|(@import\s*\(?\s*(?:'|")?\s*([^('|"|\))]*)\s*(?:'|")?\s*(?:\)|;))/i;
+	var URL_VALUE_EXP = /url\s*\(\s*(?:'|")?\s*([^('|"|\))]*)\s*(?:'|")?\s*\)/i;
+	var IMPORT_VALUE_ALT_EXP = /@import\s*\(?\s*(?:'|")?\s*([^('|"|\))]*)\s*(?:'|")?\s*(?:\)|;)/i;
+	var URL_EXP = /url\s*\(([^\)]*)\)/gi;
+	var IMPORT_EXP = /(@import\s*url\s*\([^\)]*\)\s*;?)|(@import\s*('|")?\s*[^\(|;|'|"]*\s*('|")?\s*;)/gi;
+	var IMPORT_ALT_EXP = /@import\s*('|")?\s*[^\(|;|'|"]*\s*('|")?\s*;/gi;
+	var EMPTY_PIXEL_DATA = "data:image/gif;base64,R0lGODlhAQABAIAAAP///////yH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==";
+
+	function formatURL(link, host) {
+		var i, newlinkparts, hparts, lparts;
+		if (!link)
+			return "";
+
+		lparts = link.split('/');
+		host = host.split("#")[0].split("?")[0];
+		if (/http:|https:|ftp:|data:|javascript:/i.test(lparts[0]))
+			return link.trim();
+		hparts = host.split('/');
+		newlinkparts = [];
+		if (hparts.length > 3)
+			hparts.pop();
+		if (lparts[0] == '') {
+			if (lparts[1] == '')
+				host = hparts[0] + '//' + lparts[2];
+			else
+				host = hparts[0] + '//' + hparts[2];
+			hparts = host.split('/');
+			delete lparts[0];
+			if (lparts[1] == '') {
+				delete lparts[1];
+				delete lparts[2];
+			}
+		}
+		for (i = 0; i < lparts.length; i++) {
+			if (lparts[i] == '..') {
+				if (lparts[i - 1])
+					delete lparts[i - 1];
+				else if (hparts.length > 3)
+					hparts.pop();
+				delete lparts[i];
+			}
+			if (lparts[i] == '.')
+				delete lparts[i];
+		}
+		for (i = 0; i < lparts.length; i++)
+			if (lparts[i])
+				newlinkparts[newlinkparts.length] = lparts[i];
+		return (hparts.join('/') + '/' + newlinkparts.join('/')).trim();
+	}
+
+	function resolveURLs(content, host) {
+		var ret = content.replace(URL_EXP, function(value) {
+			var result = value.match(URL_VALUE_EXP);
+			if (result)
+				if (result[1].indexOf("data:") != 0)
+					return value.replace(result[1], formatURL(result[1], host));
+			return value;
+		});
+		return ret.replace(IMPORT_ALT_EXP, function(value) {
+			var result = value.match(IMPORT_VALUE_ALT_EXP);
+			if (result)
+				if (result[1].indexOf("data:") != 0)
+					return "@import \"" + formatURL(result[1], host) + "\";";
+			return value;
+		});
+	}
+
+	function getDataURI(data, defaultURL, woURL) {
+		if (data.content)
+			return (woURL ? "" : "url(") + "data:" + data.mediaType + ";" + data.mediaTypeParam + "," + data.content + (woURL ? "" : ")");
+		else
+			return woURL ? defaultURL : "url(" + defaultURL + ")";
+	}
+
+	function removeComments(content) {
+		var start, end;
+		do {
+			start = content.indexOf("/*");
+			end = content.indexOf("*/", start);
+			if (start != -1 && end != -1)
+				content = content.substring(0, start) + content.substr(end + 2);
+		} while (start != -1 && end != -1);
+		return content;
+	}
+
+	function replaceURLs(content, host, requestManager, callback) {
+		var i, url, result, values = removeComments(content).match(URL_EXP), requestMax = 0, requestIndex = 0;
+
+		function sendRequest(origUrl) {
+			requestMax++;
+			requestManager.send(url, function(data) {
+				requestIndex++;
+				if (content.indexOf(origUrl) != -1) {
+					data.mediaType = data.mediaType ? data.mediaType.split(";")[0] : null;
+					content = content.replace(new RegExp(origUrl.replace(/([{}\(\)\^$&.\*\?\/\+\|\[\\\\]|\]|\-)/g, "\\$1"), "gi"), getDataURI(data,
+							EMPTY_PIXEL_DATA, true));
+				}
+				if (requestIndex == requestMax)
+					callback(content);
+			}, null, "base64");
+		}
+
+		if (values)
+			for (i = 0; i < values.length; i++) {
+				result = values[i].match(URL_VALUE_EXP);
+				if (result && result[1]) {
+					url = formatURL(result[1], host);
+					if (url.indexOf("data:") != 0)
+						sendRequest(result[1]);
+				}
+			}
+	}
+
+	// ----------------------------------------------------------------------------------------------
+
+	function processStylesheets(doc, docElement, baseURI, requestManager) {
+		Array.prototype.forEach.call(docElement.querySelectorAll('link[href][rel*="stylesheet"]'), function(node) {
+			var href = node.getAttribute("href"), fullHref = formatURL(href, baseURI);
+			if (href.indexOf("data:") != 0) {
+				requestManager.send(fullHref, function(data) {
+					var i, newNode, commentNode;
+					if (data.status >= 400) {
+						node.parentElement.removeChild(node);
+						return;
+					}
+					newNode = doc.createElement("style");
+					for (i = 0; i < node.attributes.length; i++)
+						if (node.attributes[i].value)
+							newNode.setAttribute(node.attributes[i].name, node.attributes[i].value);
+					newNode._baseURI = fullHref;
+					newNode.removeAttribute("href");
+					newNode.textContent = resolveURLs(data.content || "", data.url);
+					if (node.disabled) {
+						commentNode = doc.createComment();
+						commentNode.textContent = newNode.outerHTML.replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/--/g, "&minus;&minus;");
+						node.parentElement.replaceChild(commentNode, node);
+					} else
+						node.parentElement.replaceChild(newNode, node);
+				});
+			}
+		});
+	}
+
+	function processImports(docElement, baseURI, characterSet, requestManager) {
+		var ret = true;
+		Array.prototype.forEach.call(docElement.querySelectorAll("style"), function(styleSheet) {
+			var i, url, result, imports = removeComments(styleSheet.textContent).match(IMPORT_EXP);
+
+			function sendRequest(imp) {
+				requestManager.send(url, function(data) {
+					styleSheet.textContent = styleSheet.textContent.replace(imp, data.status < 400 && data.content ? resolveURLs(data.content, data.url) : "");
+				}, null, characterSet);
+				ret = false;
+			}
+
+			if (imports)
+				for (i = 0; i < imports.length; i++) {
+					result = imports[i].match(IMPORT_URL_VALUE_EXP);
+					if (result && (result[2] || result[4])) {
+						url = formatURL(result[2] || result[4], styleSheet._baseURI || baseURI);
+						if (url.indexOf("data:") != 0)
+							sendRequest(imports[i]);
+					}
+				}
+		});
+		return ret;
+	}
+
+	function processStyleAttributes(docElement, baseURI, requestManager) {
+		Array.prototype.forEach.call(docElement.querySelectorAll("*[style]"), function(node) {
+			replaceURLs(node.getAttribute("style"), baseURI, requestManager, function(style) {
+				node.setAttribute("style", style);
+			});
+		});
+	}
+
+	function processBgAttributes(docElement, baseURI, requestManager) {
+		var backgrounds = docElement.querySelectorAll("*[background]");
+		Array.prototype.forEach.call(backgrounds, function(node) {
+			var url, value = node.getAttribute("background");
+			if (value.indexOf(".") != -1) {
+				url = formatURL(value, baseURI);
+				if (url.indexOf("data:") != 0)
+					requestManager.send(url, function(data) {
+						node.setAttribute("background", getDataURI(data, EMPTY_PIXEL_DATA, true));
+					}, null, "base64");
+			}
+		});
+	}
+
+	function insertDefaultFavico(doc, docElement, baseURI) {
+		var node, docHead = docElement.querySelector("html > head"), favIcon = docElement
+				.querySelector('link[href][rel="shortcut icon"], link[href][rel="apple-touch-icon"], link[href][rel="icon"]');
+		if (!favIcon && docHead) {
+			node = doc.createElement("link");
+			node.setAttribute("type", "image/x-icon");
+			node.setAttribute("rel", "shortcut icon");
+			node.setAttribute("href", formatURL("/favicon.ico", baseURI));
+			docHead.appendChild(node);
+		}
+	}
+
+	function processImages(docElement, baseURI, requestManager) {
+		var images;
+
+		function process(attributeName) {
+			Array.prototype.forEach.call(images, function(node) {
+				var url = formatURL(node.getAttribute(attributeName), baseURI);
+				if (url.indexOf("data:") != 0)
+					requestManager.send(url, function(data) {
+						node.setAttribute(attributeName, getDataURI(data, EMPTY_PIXEL_DATA, true));
+					}, null, "base64");
+			});
+		}
+
+		images = docElement.querySelectorAll('link[href][rel="shortcut icon"], link[href][rel="apple-touch-icon"], link[href][rel="icon"]');
+		process("href");
+		images = docElement.querySelectorAll('img[src], input[src][type="image"]');
+		process("src");
+		images = docElement.querySelectorAll('video[poster]');
+		process("poster");
+
+	}
+
+	function processSVGs(docElement, baseURI, requestManager) {
+		var images = docElement.querySelectorAll('object[type="image/svg+xml"], object[type="image/svg-xml"], embed[src*=".svg"]');
+		Array.prototype.forEach.call(images, function(node) {
+			var data = node.getAttribute("data"), src = node.getAttribute("src"), url = formatURL(data || src, baseURI);
+			if (url.indexOf("data:") != 0)
+				requestManager.send(url, function(data) {
+					node.setAttribute(data ? "data" : "src", getDataURI(data, "data:text/xml,<svg></svg>", true));
+				}, null, null);
+		});
+	}
+
+	function processStyles(docElement, baseURI, requestManager) {
+		Array.prototype.forEach.call(docElement.querySelectorAll("style"), function(styleSheet) {
+			replaceURLs(styleSheet.textContent, styleSheet._baseURI || baseURI, requestManager, function(textContent) {
+				styleSheet.textContent = textContent;
+			});
+		});
+	}
+
+	function processScripts(docElement, baseURI, characterSet, requestManager) {
+		Array.prototype.forEach.call(docElement.querySelectorAll("script[src]"), function(node) {
+			var src = node.getAttribute("src");
+			if (src.indexOf("data:") != 0)
+				requestManager.send(formatURL(src, baseURI), function(data) {
+					if (data.status < 400) {
+						data.content = data.content.replace(/"([^"]*)<\/\s*script\s*>([^"]*)"/gi, '"$1<"+"/script>$2"');
+						data.content = data.content.replace(/'([^']*)<\/\s*script\s*>([^']*)'/gi, "'$1<'+'/script>$2'");
+						node.textContent = "\n" + data.content + "\n";
+					}
+					node.removeAttribute("src");
+				}, characterSet);
+		});
+	}
+
+	function processCanvas(doc, docElement, canvasData) {
+		var index = 0;
+		Array.prototype.forEach.call(docElement.querySelectorAll("canvas"), function(node) {
+			var i, data = canvasData[index], newNode = doc.createElement("img");
+			if (data) {
+				newNode.setAttribute("src", data);
+				for (i = 0; i < node.attributes.length; i++)
+					if (node.attributes[i].value)
+						newNode.setAttribute(node.attributes[i].name, node.attributes[i].value);
+				if (!newNode.width)
+					newNode.style.pixelWidth = node.clientWidth;
+				if (!newNode.height)
+					newNode.style.pixelHeight = node.clientHeight;
+				node.parentElement.replaceChild(newNode, node);
+			}
+			index++;
+		});
+	}
+
+	function removeScripts(docElement) {
+		Array.prototype.forEach.call(docElement.querySelectorAll("script"), function(node) {
+			node.parentElement.removeChild(node);
+		});
+		Array.prototype.forEach.call(docElement.querySelectorAll("*[onload]"), function(node) {
+			node.removeAttribute("onload");
+		});
+	}
+
+	function removeObjects(docElement) {
+		var objects = docElement.querySelectorAll('applet, object:not([type="image/svg+xml"]):not([type="image/svg-xml"]), embed:not([src*=".svg"])');
+		Array.prototype.forEach.call(objects, function(node) {
+			node.parentElement.removeChild(node);
+		});
+		objects = docElement.querySelectorAll('audio[src], video[src]');
+		Array.prototype.forEach.call(objects, function(node) {
+			node.removeAttribute("src");
+		});
+	}
+
+	function removeBlockquotesCite(docElement) {
+		Array.prototype.forEach.call(docElement.querySelectorAll("blockquote[cite]"), function(node) {
+			node.removeAttribute("cite");
+		});
+	}
+
+	function removeFrames(docElement) {
+		Array.prototype.forEach.call(docElement.querySelectorAll("iframe, frame"), function(node) {
+			node.parentElement.removeChild(node);
+		});
+	}
+
+	function removeMetaRefresh(docElement) {
+		Array.prototype.forEach.call(docElement.querySelectorAll("meta[http-equiv=refresh]"), function(node) {
+			node.parentElement.removeChild(node);
+		});
+	}
+
+	function resetFrames(docElement, baseURI) {
+		Array.prototype.forEach.call(docElement.querySelectorAll("iframe, frame"), function(node) {
+			var src = formatURL(node.getAttribute("src"), baseURI);
+			if (src.indexOf("data:") != 0)
+				node.setAttribute("src", "about:blank");
+		});
+	}
+
+	function setAbsoluteLinks(docElement, baseURI) {
+		Array.prototype.forEach.call(docElement.querySelectorAll("a:not([href^='#'])"), function(link) {
+			var fullHref = formatURL(link.getAttribute("href"), baseURI);
+			if (fullHref && (!(fullHref.indexOf(baseURI.split("#")[0]) == 0) || fullHref.indexOf("#") == -1))
+				link.setAttribute("href", fullHref);
+		});
+	}
+
+	// ----------------------------------------------------------------------------------------------
+
+	singlefile.initProcess = function(doc, docElement, addDefaultFavico, baseURI, characterSet, config, canvasData, requestManager, onInit, onProgress, onEnd) {
+		var initManager = new RequestManager(), manager = new RequestManager(onProgress);
+
+		function RequestManager(onProgress) {
+			var that = this, currentCount = 0, requests = [];
+			this.requestCount = 0;
+			this.send = function(url, responseHandler, characterSet, mediaTypeParam) {
+				this.requestCount++;
+				requests.push({
+					url : url,
+					responseHandler : responseHandler,
+					characterSet : characterSet,
+					mediaTypeParam : mediaTypeParam
+				});
+			};
+			this.doSend = function() {
+				requests.forEach(function(request) {
+					requestManager.send(request.url, function(response) {
+						request.responseHandler(response);
+						currentCount++;
+						if (onProgress)
+							onProgress(currentCount, that.requestCount);
+						if (currentCount == that.requestCount) {
+							that.requestCount = 0;
+							currentCount = 0;
+							if (that.onEnd)
+								that.onEnd();
+						}
+					}, request.characterSet, request.mediaTypeParam);
+				});
+				requests = [];
+			};
+		}
+
+		function cbImports() {
+			if (config.removeScripts)
+				removeScripts(docElement);
+			if (config.removeObjects)
+				removeObjects(docElement);
+			if (config.removeFrames || config.getRawDoc)
+				removeFrames(docElement);
+			resetFrames(docElement, baseURI);
+			removeBlockquotesCite(docElement);
+			removeMetaRefresh(docElement);
+			setAbsoluteLinks(docElement, baseURI);
+			if (addDefaultFavico)
+				insertDefaultFavico(doc, docElement, baseURI);
+			processStyleAttributes(docElement, baseURI, manager);
+			processBgAttributes(docElement, baseURI, manager);
+			processImages(docElement, baseURI, manager);
+			processSVGs(docElement, baseURI, manager);
+			processStyles(docElement, baseURI, manager);
+			processScripts(docElement, baseURI, characterSet, manager);
+			processCanvas(doc, docElement, canvasData);
+			if (onInit)
+				setTimeout(function() {
+					onInit(manager.requestCount);
+				}, 1);
+		}
+
+		function cbStylesheets() {
+			initManager.onEnd = function(noRequests) {
+				if (noRequests)
+					cbImports();
+				else
+					cbStylesheets();
+			};
+			processImports(docElement, baseURI, characterSet, initManager);
+			initManager.doSend();
+			if (initManager.requestCount == 0)
+				cbImports();
+		}
+
+		manager.onEnd = onEnd;
+		processStylesheets(doc, docElement, baseURI, initManager);
+		initManager.onEnd = cbStylesheets;
+		initManager.doSend();
+		if (initManager.requestCount == 0)
+			initManager.onEnd();
+		return function() {
+			manager.doSend();
+			if (manager.onEnd && manager.requestCount == 0)
+				manager.onEnd();
+		};
+	};
+
+})();