text: /**
   * jsPDF fromHTML plugin. BETA stage. API subject to change. Needs browser
   * Copyright (c) 2012 Willow Systems Corporation, willow-systems.com
   *               2014 Juan Pablo Gaviria, https://github.com/juanpgaviria
   *               2014 Diego Casorran, https://github.com/diegocr
   *               2014 Daniel Husar, https://github.com/danielhusar
   *               2014 Wolfgang Gassler, https://github.com/woolfg
   *               2014 Steven Spungin, https://github.com/flamenco
   *
   * @license
   * Permission is hereby granted, free of charge, to any person obtaining
   * a copy of this software and associated documentation files (the
   * "Software"), to deal in the Software without restriction, including
   * without limitation the rights to use, copy, modify, merge, publish,
   * distribute, sublicense, and/or sell copies of the Software, and to
   * permit persons to whom the Software is furnished to do so, subject to
   * the following conditions:
   *
   * The above copyright notice and this permission notice shall be
   * included in all copies or substantial portions of the Software.
   *
   * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
   * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
   * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
   * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
   * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
   * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
   * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
   * ====================================================================
   */
  
  (function (jsPDFAPI) {
  	var clone,
  		DrillForContent,
  		FontNameDB,
  		FontStyleMap,
  		TextAlignMap,
  		FontWeightMap,
  		FloatMap,
  		ClearMap,
  		GetCSS,
  		PurgeWhiteSpace,
  		Renderer,
  		ResolveFont,
  		ResolveUnitedNumber,
  		UnitedNumberMap,
  		elementHandledElsewhere,
  		images,
  		loadImgs,
  		checkForFooter,
  		process,
  		tableToJson;
  	clone = (function () {
  		return function (obj) {
  			Clone.prototype = obj;
  			return new Clone()
  		};
  		function Clone() { }
  	})();
  	PurgeWhiteSpace = function (array) {
  		var fragment,
  			i,
  			l,
  			lTrimmed,
  			r,
  			rTrimmed,
  			trailingSpace;
  		i = 0;
  		l = array.length;
  		fragment = void 0;
  		lTrimmed = false;
  		rTrimmed = false;
  		while (!lTrimmed && i !== l) {
  			fragment = array[i] = array[i].trimLeft();
  			if (fragment) {
  				lTrimmed = true;
  			}
  			i++;
  		}
  		i = l - 1;
  		while (l && !rTrimmed && i !== -1) {
  			fragment = array[i] = array[i].trimRight();
  			if (fragment) {
  				rTrimmed = true;
  			}
  			i--;
  		}
  		r = /\s+$/g;
  		trailingSpace = true;
  		i = 0;
  		while (i !== l) {
  			// Leave the line breaks intact
  			if (array[i] != "\u2028") {
  				fragment = array[i].replace(/\s+/g, " ");
  				if (trailingSpace) {
  					fragment = fragment.trimLeft();
  				}
  				if (fragment) {
  					trailingSpace = r.test(fragment);
  				}
  				array[i] = fragment;
  			}
  			i++;
  		}
  		return array;
  	};
  	Renderer = function (pdf, x, y, settings) {
  		this.pdf = pdf;
  		this.x = x;
  		this.y = y;
  		this.settings = settings;
  		//list of functions which are called after each element-rendering process
  		this.watchFunctions = [];
  		this.init();
  		return this;
  	};
  	ResolveFont = function (css_font_family_string) {
  		var name,
  			part,
  			parts;
  		name = void 0;
  		parts = css_font_family_string.split(",");
  		part = parts.shift();
  		while (!name && part) {
  			name = FontNameDB[part.trim().toLowerCase()];
  			part = parts.shift();
  		}
  		return name;
  	};
  	ResolveUnitedNumber = function (css_line_height_string) {
  
  		//IE8 issues
  		css_line_height_string = css_line_height_string === "auto" ? "0px" : css_line_height_string;
  		if (css_line_height_string.indexOf("em") > -1 && !isNaN(Number(css_line_height_string.replace("em", "")))) {
  			css_line_height_string = Number(css_line_height_string.replace("em", "")) * 18.719 + "px";
  		}
  		if (css_line_height_string.indexOf("pt") > -1 && !isNaN(Number(css_line_height_string.replace("pt", "")))) {
  			css_line_height_string = Number(css_line_height_string.replace("pt", "")) * 1.333 + "px";
  		}
  
  		var normal,
  			undef,
  			value;
  		undef = void 0;
  		normal = 16.00;
  		value = UnitedNumberMap[css_line_height_string];
  		if (value) {
  			return value;
  		}
  		value = {
  			"xx-small": 9,
  			"x-small": 11,
  			small: 13,
  			medium: 16,
  			large: 19,
  			"x-large": 23,
  			"xx-large": 28,
  			auto: 0
  		}[css_line_height_string];
  
  		if (value !== undef) {
  			return UnitedNumberMap[css_line_height_string] = value / normal;
  		}
  		if (value = parseFloat(css_line_height_string)) {
  			return UnitedNumberMap[css_line_height_string] = value / normal;
  		}
  		value = css_line_height_string.match(/([\d\.]+)(px)/);
  		if (Array.isArray(value) && value.length === 3) {
  			return UnitedNumberMap[css_line_height_string] = parseFloat(value[1]) / normal;
  		}
  		return UnitedNumberMap[css_line_height_string] = 1;
  	};
  	GetCSS = function (element) {
  		var css, tmp, computedCSSElement;
  		computedCSSElement = (function (el) {
  			var compCSS;
  			compCSS = (function (el) {
  				if (document.defaultView && document.defaultView.getComputedStyle) {
  					return document.defaultView.getComputedStyle(el, null);
  				} else if (el.currentStyle) {
  					return el.currentStyle;
  				} else {
  					return el.style;
  				}
  			})(el);
  			return function (prop) {
  				prop = prop.replace(/-\D/g, function (match) {
  					return match.charAt(1).toUpperCase();
  				});
  				return compCSS[prop];
  			};
  		})(element);
  		css = {};
  		tmp = void 0;
  		css["font-family"] = ResolveFont(computedCSSElement("font-family")) || "times";
  		css["font-style"] = FontStyleMap[computedCSSElement("font-style")] || "normal";
  		css["text-align"] = TextAlignMap[computedCSSElement("text-align")] || "left";
  		tmp = FontWeightMap[computedCSSElement("font-weight")] || "normal";
  		if (tmp === "bold") {
  			if (css["font-style"] === "normal") {
  				css["font-style"] = tmp;
  			} else {
  				css["font-style"] = tmp + css["font-style"];
  			}
  		}
  		css["font-size"] = ResolveUnitedNumber(computedCSSElement("font-size")) || 1;
  		css["line-height"] = ResolveUnitedNumber(computedCSSElement("line-height")) || 1;
  		css["display"] = (computedCSSElement("display") === "inline" ? "inline" : "block");
  
  		tmp = (css["display"] === "block");
  		css["margin-top"] = tmp && ResolveUnitedNumber(computedCSSElement("margin-top")) || 0;
  		css["margin-bottom"] = tmp && ResolveUnitedNumber(computedCSSElement("margin-bottom")) || 0;
  		css["padding-top"] = tmp && ResolveUnitedNumber(computedCSSElement("padding-top")) || 0;
  		css["padding-bottom"] = tmp && ResolveUnitedNumber(computedCSSElement("padding-bottom")) || 0;
  		css["margin-left"] = tmp && ResolveUnitedNumber(computedCSSElement("margin-left")) || 0;
  		css["margin-right"] = tmp && ResolveUnitedNumber(computedCSSElement("margin-right")) || 0;
  		css["padding-left"] = tmp && ResolveUnitedNumber(computedCSSElement("padding-left")) || 0;
  		css["padding-right"] = tmp && ResolveUnitedNumber(computedCSSElement("padding-right")) || 0;
  
  		css["page-break-before"] = computedCSSElement("page-break-before") || "auto";
  
  		//float and clearing of floats
  		css["float"] = FloatMap[computedCSSElement("cssFloat")] || "none";
  		css["clear"] = ClearMap[computedCSSElement("clear")] || "none";
  
  		css["color"] = computedCSSElement("color");
  
  		return css;
  	};
  	elementHandledElsewhere = function (element, renderer, elementHandlers) {
  		var handlers,
  			i,
  			isHandledElsewhere,
  			l,
  			classNames,
  			t;
  		isHandledElsewhere = false;
  		i = void 0;
  		l = void 0;
  		t = void 0;
  		handlers = elementHandlers["#" + element.id];
  		if (handlers) {
  			if (typeof handlers === "function") {
  				isHandledElsewhere = handlers(element, renderer);
  			} else {
  				i = 0;
  				l = handlers.length;
  				while (!isHandledElsewhere && i !== l) {
  					isHandledElsewhere = handlers[i](element, renderer);
  					i++;
  				}
  			}
  		}
  		handlers = elementHandlers[element.nodeName];
  		if (!isHandledElsewhere && handlers) {
  			if (typeof handlers === "function") {
  				isHandledElsewhere = handlers(element, renderer);
  			} else {
  				i = 0;
  				l = handlers.length;
  				while (!isHandledElsewhere && i !== l) {
  					isHandledElsewhere = handlers[i](element, renderer);
  					i++;
  				}
  			}
  		}
  
  		// Try class names
  		classNames = typeof (element.className) === 'string' ? element.className.split(' ') : [];
  		for (i = 0; i < classNames.length; i++) {
  			handlers = elementHandlers['.' + classNames[i]];
  			if (!isHandledElsewhere && handlers) {
  				if (typeof handlers === "function") {
  					isHandledElsewhere = handlers(element, renderer);
  				} else {
  					i = 0;
  					l = handlers.length;
  					while (!isHandledElsewhere && i !== l) {
  						isHandledElsewhere = handlers[i](element, renderer);
  						i++;
  					}
  				}
  			}
  		}
  
  		return isHandledElsewhere;
  	};
  	tableToJson = function (table, renderer) {
  		var data,
  			headers,
  			i,
  			j,
  			rowData,
  			tableRow,
  			table_with,
  			cell,
  			l;
  		data = [];
  		headers = [];
  		i = 0;
  		l = table.rows[0].cells.length;
  		table_with = table.clientWidth;
  		while (i < l) {
  			cell = table.rows[0].cells[i];
  			headers[i] = {
  				name: cell.textContent.toLowerCase().replace(/\s+/g, ''),
  				prompt: cell.textContent.replace(/\r?\n/g, ''),
  				width: (cell.clientWidth / table_with) * renderer.pdf.internal.pageSize.getWidth()
  			};
  			i++;
  		}
  		i = 1;
  		while (i < table.rows.length) {
  			tableRow = table.rows[i];
  			rowData = {};
  			j = 0;
  			while (j < tableRow.cells.length) {
  				rowData[headers[j].name] = tableRow.cells[j].textContent.replace(/\r?\n/g, '');
  				j++;
  			}
  			data.push(rowData);
  			i++;
  		}
  		return {
  			rows: data,
  			headers: headers
  		};
  	};
  	var SkipNode = {
  		SCRIPT: 1,
  		STYLE: 1,
  		NOSCRIPT: 1,
  		OBJECT: 1,
  		EMBED: 1,
  		SELECT: 1
  	};
  	var listCount = 1;
  	DrillForContent = function (element, renderer, elementHandlers) {
  		var cn,
  			cns,
  			fragmentCSS,
  			i,
  			isBlock,
  			l,
  			table2json,
  			cb;
  		cns = element.childNodes;
  		cn = void 0;
  		fragmentCSS = GetCSS(element);
  		isBlock = fragmentCSS.display === "block";
  		if (isBlock) {
  			renderer.setBlockBoundary();
  			renderer.setBlockStyle(fragmentCSS);
  		}
  		i = 0;
  		l = cns.length;
  		while (i < l) {
  			cn = cns[i];
  			if (typeof cn === "object") {
  
  				//execute all watcher functions to e.g. reset floating
  				renderer.executeWatchFunctions(cn);
  
  				/*** HEADER rendering **/
  				if (cn.nodeType === 1 && cn.nodeName === 'HEADER') {
  					var header = cn;
  					//store old top margin
  					var oldMarginTop = renderer.pdf.margins_doc.top;
  					//subscribe for new page event and render header first on every page
  					renderer.pdf.internal.events.subscribe('addPage', function (pageInfo) {
  						//set current y position to old margin
  						renderer.y = oldMarginTop;
  						//render all child nodes of the header element
  						DrillForContent(header, renderer, elementHandlers);
  						//set margin to old margin + rendered header + 10 space to prevent overlapping
  						//important for other plugins (e.g. table) to start rendering at correct position after header
  						renderer.pdf.margins_doc.top = renderer.y + 10;
  						renderer.y += 10;
  					}, false);
  				}
  
  				if (cn.nodeType === 8 && cn.nodeName === "#comment") {
  					if (~cn.textContent.indexOf("ADD_PAGE")) {
  						renderer.pdf.addPage();
  						renderer.y = renderer.pdf.margins_doc.top;
  					}
  
  				} else if (cn.nodeType === 1 && !SkipNode[cn.nodeName]) {
  					/*** IMAGE RENDERING ***/
  					var cached_image;
  					if (cn.nodeName === "IMG") {
  						var url = cn.getAttribute("src");
  						cached_image = images[renderer.pdf.API.__addimage__.sHashCode(url) || url];
  					}
  					if (cached_image) {
  						if ((renderer.pdf.internal.pageSize.getHeight() - renderer.pdf.margins_doc.bottom < renderer.y + cn.height) && (renderer.y > renderer.pdf.margins_doc.top)) {
  							renderer.pdf.addPage();
  							renderer.y = renderer.pdf.margins_doc.top;
  							//check if we have to set back some values due to e.g. header rendering for new page
  							renderer.executeWatchFunctions(cn);
  						}
  
  						var imagesCSS = GetCSS(cn);
  						var imageX = renderer.x;
  						var fontToUnitRatio = 12 / renderer.pdf.internal.scaleFactor;
  
  						//define additional paddings, margins which have to be taken into account for margin calculations
  						var additionalSpaceLeft = (imagesCSS["margin-left"] + imagesCSS["padding-left"]) * fontToUnitRatio;
  						var additionalSpaceRight = (imagesCSS["margin-right"] + imagesCSS["padding-right"]) * fontToUnitRatio;
  						var additionalSpaceTop = (imagesCSS["margin-top"] + imagesCSS["padding-top"]) * fontToUnitRatio;
  						var additionalSpaceBottom = (imagesCSS["margin-bottom"] + imagesCSS["padding-bottom"]) * fontToUnitRatio;
  
  						//if float is set to right, move the image to the right border
  						//add space if margin is set
  						if (imagesCSS['float'] !== undefined && imagesCSS['float'] === 'right') {
  							imageX += renderer.settings.width - cn.width - additionalSpaceRight;
  						} else {
  							imageX += additionalSpaceLeft;
  						}
  
  						renderer.pdf.addImage(cached_image, imageX, renderer.y + additionalSpaceTop, cn.width, cn.height);
  						cached_image = undefined;
  						//if the float prop is specified we have to float the text around the image
  						if (imagesCSS['float'] === 'right' || imagesCSS['float'] === 'left') {
  							//add functiont to set back coordinates after image rendering
  							renderer.watchFunctions.push((function (diffX, thresholdY, diffWidth, el) {
  								//undo drawing box adaptions which were set by floating
  								if (renderer.y >= thresholdY) {
  									renderer.x += diffX;
  									renderer.settings.width += diffWidth;
  									return true;
  								} else if (el && el.nodeType === 1 && !SkipNode[el.nodeName] && renderer.x + el.width > (renderer.pdf.margins_doc.left + renderer.pdf.margins_doc.width)) {
  									renderer.x += diffX;
  									renderer.y = thresholdY;
  									renderer.settings.width += diffWidth;
  									return true;
  								} else {
  									return false;
  								}
  							}).bind(this, (imagesCSS['float'] === 'left') ? -cn.width - additionalSpaceLeft - additionalSpaceRight : 0, renderer.y + cn.height + additionalSpaceTop + additionalSpaceBottom, cn.width));
  							//reset floating by clear:both divs
  							//just set cursorY after the floating element
  							renderer.watchFunctions.push((function (yPositionAfterFloating, pages, el) {
  								if (renderer.y < yPositionAfterFloating && pages === renderer.pdf.internal.getNumberOfPages()) {
  									if (el.nodeType === 1 && GetCSS(el).clear === 'both') {
  										renderer.y = yPositionAfterFloating;
  										return true;
  									} else {
  										return false;
  									}
  								} else {
  									return true;
  								}
  							}).bind(this, renderer.y + cn.height, renderer.pdf.internal.getNumberOfPages()));
  
  							//if floating is set we decrease the available width by the image width
  							renderer.settings.width -= cn.width + additionalSpaceLeft + additionalSpaceRight;
  							//if left just add the image width to the X coordinate
  							if (imagesCSS['float'] === 'left') {
  								renderer.x += cn.width + additionalSpaceLeft + additionalSpaceRight;
  							}
  						} else {
  							//if no floating is set, move the rendering cursor after the image height
  							renderer.y += cn.height + additionalSpaceTop + additionalSpaceBottom;
  						}
  
  						/*** TABLE RENDERING ***/
  					} else if (cn.nodeName === "TABLE") {
  						table2json = tableToJson(cn, renderer);
  						renderer.y += 10;
  						renderer.pdf.table(renderer.x, renderer.y, table2json.rows, table2json.headers, {
  							autoSize: false,
  							printHeaders: elementHandlers.printHeaders,
  							margins: renderer.pdf.margins_doc,
  							css: GetCSS(cn)
  						});
  						renderer.y = renderer.pdf.lastCellPos.y + renderer.pdf.lastCellPos.h + 20;
  					} else if (cn.nodeName === "OL" || cn.nodeName === "UL") {
  						listCount = 1;
  						if (!elementHandledElsewhere(cn, renderer, elementHandlers)) {
  							DrillForContent(cn, renderer, elementHandlers);
  						}
  						renderer.y += 10;
  					} else if (cn.nodeName === "LI") {
  						var temp = renderer.x;
  						renderer.x += 20 / renderer.pdf.internal.scaleFactor;
  						renderer.y += 3;
  						if (!elementHandledElsewhere(cn, renderer, elementHandlers)) {
  							DrillForContent(cn, renderer, elementHandlers);
  						}
  						renderer.x = temp;
  					} else if (cn.nodeName === "BR") {
  						renderer.y += fragmentCSS["font-size"] * renderer.pdf.internal.scaleFactor;
  						renderer.addText("\u2028", clone(fragmentCSS));
  					} else {
  						if (!elementHandledElsewhere(cn, renderer, elementHandlers)) {
  							DrillForContent(cn, renderer, elementHandlers);
  						}
  					}
  				} else if (cn.nodeType === 3) {
  					var value = cn.nodeValue;
  					if (cn.nodeValue && cn.parentNode.nodeName === "LI") {
  						if (cn.parentNode.parentNode.nodeName === "OL") {
  							value = listCount++ + '. ' + value;
  						} else {
  							var fontSize = fragmentCSS["font-size"];
  							var offsetX = (3 - fontSize * 0.75) * renderer.pdf.internal.scaleFactor;
  							var offsetY = fontSize * 0.75 * renderer.pdf.internal.scaleFactor;
  							var radius = fontSize * 1.74 / renderer.pdf.internal.scaleFactor;
  							cb = function (x, y) {
  								this.pdf.circle(x + offsetX, y + offsetY, radius, 'FD');
  							};
  						}
  					}
  					// Only add the text if the text node is in the body element
  					// Add compatibility with IE11
  					if (!!(cn.ownerDocument.body.compareDocumentPosition(cn) & 16)) {
  						renderer.addText(value, fragmentCSS);
  					}
  				} else if (typeof cn === "string") {
  					renderer.addText(cn, fragmentCSS);
  				}
  			}
  			i++;
  		}
  		elementHandlers.outY = renderer.y;
  
  		if (isBlock) {
  			return renderer.setBlockBoundary(cb);
  		}
  	};
  	images = {};
  	loadImgs = function (element, renderer, elementHandlers, cb) {
  		var imgs = element.getElementsByTagName('img'),
  			l = imgs.length, found_images,
  			x = 0;
  		function done() {
  			renderer.pdf.internal.events.publish('imagesLoaded');
  			cb(found_images);
  		}
  		function loadImage(url, width, height) {
  			if (!url)
  				return;
  			var img = new Image();
  			found_images = ++x;
  			img.crossOrigin = '';
  			img.onerror = img.onload = function () {
  				if (img.complete) {
  					//to support data urls in images, set width and height
  					//as those values are not recognized automatically
  					if (img.src.indexOf('data:image/') === 0) {
  						img.width = width || img.width || 0;
  						img.height = height || img.height || 0;
  					}
  					//if valid image add to known images array
  					if (img.width + img.height) {
  						var hash = renderer.pdf.API.__addimage__.sHashCode(url) || url;
  						images[hash] = images[hash] || img;
  					}
  				}
  				if (!--x) {
  					done();
  				}
  			};
  			img.src = url;
  		}
  		while (l--)
  			loadImage(imgs[l].getAttribute("src"), imgs[l].width, imgs[l].height);
  		return x || done();
  	};
  	checkForFooter = function (elem, renderer, elementHandlers) {
  		//check if we can found a <footer> element
  		var footer = elem.getElementsByTagName("footer");
  		if (footer.length > 0) {
  
  			footer = footer[0];
  
  			//bad hack to get height of footer
  			//creat dummy out and check new y after fake rendering
  			var oldOut = renderer.pdf.internal.write;
  			var oldY = renderer.y;
  			renderer.pdf.internal.write = function () { };
  			DrillForContent(footer, renderer, elementHandlers);
  			var footerHeight = Math.ceil(renderer.y - oldY) + 5;
  			renderer.y = oldY;
  			renderer.pdf.internal.write = oldOut;
  
  			//add 20% to prevent overlapping
  			renderer.pdf.margins_doc.bottom += footerHeight;
  
  			//Create function render header on every page
  			var renderFooter = function (pageInfo) {
  				var pageNumber = pageInfo !== undefined ? pageInfo.pageNumber : 1;
  				//set current y position to old margin
  				var oldPosition = renderer.y;
  				//render all child nodes of the header element
  				renderer.y = renderer.pdf.internal.pageSize.getHeight() - renderer.pdf.margins_doc.bottom;
  				renderer.pdf.margins_doc.bottom -= footerHeight;
  
  				//check if we have to add page numbers
  				var spans = footer.getElementsByTagName('span');
  				for (var i = 0; i < spans.length; ++i) {
  					//if we find some span element with class pageCounter, set the page
  					if ((" " + spans[i].className + " ").replace(/[\n\t]/g, " ").indexOf(" pageCounter ") > -1) {
  						spans[i].innerHTML = pageNumber;
  					}
  					//if we find some span element with class totalPages, set a variable which is replaced after rendering of all pages
  					if ((" " + spans[i].className + " ").replace(/[\n\t]/g, " ").indexOf(" totalPages ") > -1) {
  						spans[i].innerHTML = '###jsPDFVarTotalPages###';
  					}
  				}
  
  				//render footer content
  				DrillForContent(footer, renderer, elementHandlers);
  				//set bottom margin to previous height including the footer height
  				renderer.pdf.margins_doc.bottom += footerHeight;
  				//important for other plugins (e.g. table) to start rendering at correct position after header
  				renderer.y = oldPosition;
  			};
  
  			//check if footer contains totalPages which should be replace at the disoposal of the document
  			var spans = footer.getElementsByTagName('span');
  			for (var i = 0; i < spans.length; ++i) {
  				if ((" " + spans[i].className + " ").replace(/[\n\t]/g, " ").indexOf(" totalPages ") > -1) {
  					renderer.pdf.internal.events.subscribe('htmlRenderingFinished', renderer.pdf.putTotalPages.bind(renderer.pdf, '###jsPDFVarTotalPages###'), true);
  				}
  			}
  
  			//register event to render footer on every new page
  			renderer.pdf.internal.events.subscribe('addPage', renderFooter, false);
  			//render footer on first page
  			renderFooter();
  
  			//prevent footer rendering
  			SkipNode['FOOTER'] = 1;
  		}
  	};
  	process = function (pdf, element, x, y, settings, callback) {
  		if (!element)
  			return false;
  		if (typeof element !== "string" && !element.parentNode)
  			element = '' + element.innerHTML;
  		if (typeof element === "string") {
  			element = (function (element) {
  				var $frame,
  					$hiddendiv,
  					framename,
  					visuallyhidden;
  				framename = "jsPDFhtmlText" + Date.now().toString() + (Math.random() * 1000).toFixed(0);
  				visuallyhidden = "position: absolute !important;" + "clip: rect(1px 1px 1px 1px); /* IE6, IE7 */" + "clip: rect(1px, 1px, 1px, 1px);" + "padding:0 !important;" + "border:0 !important;" + "height: 1px !important;" + "width: 1px !important; " + "top:auto;" + "left:-100px;" + "overflow: hidden;";
  				$hiddendiv = document.createElement('div');
  				$hiddendiv.style.cssText = visuallyhidden;
  				$hiddendiv.innerHTML = "<iframe style=\"height:1px;width:1px\" name=\"" + framename + "\" />";
  				document.body.appendChild($hiddendiv);
  				$frame = window.frames[framename];
  				$frame.document.open();
  				$frame.document.writeln(element);
  				$frame.document.close();
  				return $frame.document.body;
  			})(element.replace(/<\/?script[^>]*?>/gi, ''));
  		}
  		var r = new Renderer(pdf, x, y, settings), out;
  
  		// 1. load images
  		// 2. prepare optional footer elements
  		// 3. render content
  		loadImgs.call(this, element, r, settings.elementHandlers, function (found_images) {
  			checkForFooter(element, r, settings.elementHandlers);
  			DrillForContent(element, r, settings.elementHandlers);
  			//send event dispose for final taks (e.g. footer totalpage replacement)
  			r.pdf.internal.events.publish('htmlRenderingFinished');
  			out = r.dispose();
  			if (typeof callback === 'function') callback(out);
  			else if (found_images) console.error('jsPDF Warning: rendering issues? provide a callback to fromHTML!');
  		});
  		return out || { x: r.x, y: r.y };
  	};
  	Renderer.prototype.init = function () {
  		this.paragraph = {
  			text: [],
  			style: []
  		};
  		return this.pdf.internal.write("q");
  	};
  	Renderer.prototype.dispose = function () {
  		this.pdf.internal.write("Q");
  		return {
  			x: this.x,
  			y: this.y,
  			ready: true
  		};
  	};
  
  	//Checks if we have to execute some watcher functions
  	//e.g. to end text floating around an image
  	Renderer.prototype.executeWatchFunctions = function (el) {
  		var ret = false;
  		var narray = [];
  		if (this.watchFunctions.length > 0) {
  			for (var i = 0; i < this.watchFunctions.length; ++i) {
  				if (this.watchFunctions[i](el) === true) {
  					ret = true;
  				} else {
  					narray.push(this.watchFunctions[i]);
  				}
  			}
  			this.watchFunctions = narray;
  		}
  		return ret;
  	};
  
  	Renderer.prototype.splitFragmentsIntoLines = function (fragments, styles) {
  		var currentLineLength,
  			defaultFontSize,
  			ff,
  			fontMetrics,
  			fontMetricsCache,
  			fragment,
  			fragmentChopped,
  			fragmentLength,
  			fragmentSpecificMetrics,
  			fs,
  			k,
  			line,
  			lines,
  			maxLineLength,
  			style;
  		defaultFontSize = 12;
  		k = this.pdf.internal.scaleFactor;
  		fontMetricsCache = {};
  		ff = void 0;
  		fs = void 0;
  		fontMetrics = void 0;
  		fragment = void 0;
  		style = void 0;
  		fragmentSpecificMetrics = void 0;
  		fragmentLength = void 0;
  		fragmentChopped = void 0;
  		line = [];
  		lines = [line];
  		currentLineLength = 0;
  		maxLineLength = this.settings.width;
  		while (fragments.length) {
  			fragment = fragments.shift();
  			style = styles.shift();
  			if (fragment) {
  				ff = style["font-family"];
  				fs = style["font-style"];
  				fontMetrics = fontMetricsCache[ff + fs];
  				if (!fontMetrics) {
  					fontMetrics = this.pdf.internal.getFont(ff, fs).metadata.Unicode;
  					fontMetricsCache[ff + fs] = fontMetrics;
  				}
  				fragmentSpecificMetrics = {
  					widths: fontMetrics.widths,
  					kerning: fontMetrics.kerning,
  					fontSize: style["font-size"] * defaultFontSize,
  					textIndent: currentLineLength
  				};
  				fragmentLength = this.pdf.getStringUnitWidth(fragment, fragmentSpecificMetrics) * fragmentSpecificMetrics.fontSize / k;
  				if (fragment == "\u2028") {
  					line = [];
  					lines.push(line);
  				} else if (currentLineLength + fragmentLength > maxLineLength) {
  					fragmentChopped = this.pdf.splitTextToSize(fragment, maxLineLength, fragmentSpecificMetrics);
  					line.push([fragmentChopped.shift(), style]);
  					while (fragmentChopped.length) {
  						line = [[fragmentChopped.shift(), style]];
  						lines.push(line);
  					}
  					currentLineLength = this.pdf.getStringUnitWidth(line[0][0], fragmentSpecificMetrics) * fragmentSpecificMetrics.fontSize / k;
  				} else {
  					line.push([fragment, style]);
  					currentLineLength += fragmentLength;
  				}
  			}
  		}
  
  		//if text alignment was set, set margin/indent of each line
  		if (style['text-align'] !== undefined && (style['text-align'] === 'center' || style['text-align'] === 'right' || style['text-align'] === 'justify')) {
  			for (var i = 0; i < lines.length; ++i) {
  				var length = this.pdf.getStringUnitWidth(lines[i][0][0], fragmentSpecificMetrics) * fragmentSpecificMetrics.fontSize / k;
  				//if there is more than on line we have to clone the style object as all lines hold a reference on this object
  				if (i > 0) {
  					lines[i][0][1] = clone(lines[i][0][1]);
  				}
  				var space = (maxLineLength - length);
  
  				if (style['text-align'] === 'right') {
  					lines[i][0][1]['margin-left'] = space;
  					//if alignment is not right, it has to be center so split the space to the left and the right
  				} else if (style['text-align'] === 'center') {
  					lines[i][0][1]['margin-left'] = space / 2;
  					//if justify was set, calculate the word spacing and define in by using the css property
  				} else if (style['text-align'] === 'justify') {
  					var countSpaces = lines[i][0][0].split(' ').length - 1;
  					lines[i][0][1]['word-spacing'] = space / countSpaces;
  					//ignore the last line in justify mode
  					if (i === (lines.length - 1)) {
  						lines[i][0][1]['word-spacing'] = 0;
  					}
  				}
  			}
  		}
  
  		return lines;
  	};
  	Renderer.prototype.RenderTextFragment = function (text, style) {
  		var defaultFontSize,
  			font,
  			maxLineHeight;
  
  		maxLineHeight = 0;
  		defaultFontSize = 12;
  
  		if (this.pdf.internal.pageSize.getHeight() - this.pdf.margins_doc.bottom < this.y + this.pdf.internal.getFontSize()) {
  			this.pdf.internal.write("ET", "Q");
  			this.pdf.addPage();
  			this.y = this.pdf.margins_doc.top;
  			this.pdf.internal.write("q", "BT", this.getPdfColor(style.color), this.pdf.internal.getCoordinateString(this.x), this.pdf.internal.getVerticalCoordinateString(this.y), "Td");
  			//move cursor by one line on new page
  			maxLineHeight = Math.max(maxLineHeight, style["line-height"], style["font-size"]);
  			this.pdf.internal.write(0, (-1 * defaultFontSize * maxLineHeight).toFixed(2), "Td");
  		}
  
  		font = this.pdf.internal.getFont(style["font-family"], style["font-style"]);
  
  		// text color
  		var pdfTextColor = this.getPdfColor(style["color"]);
  		if (pdfTextColor !== this.lastTextColor) {
  			this.pdf.internal.write(pdfTextColor);
  			this.lastTextColor = pdfTextColor;
  		}
  
  		//set the word spacing for e.g. justify style
  		if (style['word-spacing'] !== undefined && style['word-spacing'] > 0) {
  			this.pdf.internal.write(style['word-spacing'].toFixed(2), "Tw");
  		}
  
  		this.pdf.internal.write("/" + font.id, (defaultFontSize * style["font-size"]).toFixed(2), "Tf", "(" + this.pdf.internal.pdfEscape(text) + ") Tj");
  
  
  		//set the word spacing back to neutral => 0
  		if (style['word-spacing'] !== undefined) {
  			this.pdf.internal.write(0, "Tw");
  		}
  	};
  
  	// Accepts #FFFFFF, rgb(int,int,int), or CSS Color Name
  	Renderer.prototype.getPdfColor = function (style) {
  		var textColor;
  		var r, g, b;
  
  		var rx = /rgb\s*\(\s*(\d+),\s*(\d+),\s*(\d+\s*)\)/;
  		var m = rx.exec(style);
  		if (m != null) {
  			r = parseInt(m[1]);
  			g = parseInt(m[2]);
  			b = parseInt(m[3]);
  		}
  		else {
  			if (typeof style === "string" && style.charAt(0) != '#') {
  				var rgbColor = new RGBColor(style);
  				if (rgbColor.ok) {
  					style = rgbColor.toHex();
  				} else {
  					style = '#000000';
  				}
  			}
  			r = style.substring(1, 3);
  			r = parseInt(r, 16);
  			g = style.substring(3, 5);
  			g = parseInt(g, 16);
  			b = style.substring(5, 7);
  			b = parseInt(b, 16);
  		}
  
  		if ((typeof r === 'string') && /^#[0-9A-Fa-f]{6}$/.test(r)) {
  			var hex = parseInt(r.substr(1), 16);
  			r = (hex >> 16) & 255;
  			g = (hex >> 8) & 255;
  			b = (hex & 255);
  		}
  
  		var f3 = this.f3;
  		if ((r === 0 && g === 0 && b === 0) || (typeof g === 'undefined')) {
  			textColor = f3(r / 255) + ' g';
  		} else {
  			textColor = [f3(r / 255), f3(g / 255), f3(b / 255), 'rg'].join(' ');
  		}
  		return textColor;
  	};
  
  	Renderer.prototype.f3 = function (number) {
  		return number.toFixed(3); // Ie, %.3f
  	},
  
  
  		Renderer.prototype.renderParagraph = function (cb) {
  			var blockstyle,
  				defaultFontSize,
  				fontToUnitRatio,
  				fragments,
  				i,
  				l,
  				line,
  				lines,
  				maxLineHeight,
  				out,
  				paragraphspacing_after,
  				paragraphspacing_before,
  				styles,
  				fontSize;
  			fragments = PurgeWhiteSpace(this.paragraph.text);
  			styles = this.paragraph.style;
  			blockstyle = this.paragraph.blockstyle;
  			this.paragraph = {
  				text: [],
  				style: [],
  				blockstyle: {},
  				priorblockstyle: blockstyle
  			};
  			if (!fragments.join("").trim()) {
  				return;
  			}
  			lines = this.splitFragmentsIntoLines(fragments, styles);
  			line = void 0;
  			maxLineHeight = void 0;
  			defaultFontSize = 12;
  			fontToUnitRatio = defaultFontSize / this.pdf.internal.scaleFactor;
  			this.priorMarginBottom = this.priorMarginBottom || 0;
  			paragraphspacing_before = (Math.max((blockstyle["margin-top"] || 0) - this.priorMarginBottom, 0) + (blockstyle["padding-top"] || 0)) * fontToUnitRatio;
  			paragraphspacing_after = ((blockstyle["margin-bottom"] || 0) + (blockstyle["padding-bottom"] || 0)) * fontToUnitRatio;
  			this.priorMarginBottom = blockstyle["margin-bottom"] || 0;
  
  			if (blockstyle['page-break-before'] === 'always') {
  				this.pdf.addPage();
  				this.y = 0;
  				paragraphspacing_before = ((blockstyle["margin-top"] || 0) + (blockstyle["padding-top"] || 0)) * fontToUnitRatio;
  			}
  
  			out = this.pdf.internal.write;
  			i = void 0;
  			l = void 0;
  			this.y += paragraphspacing_before;
  			out("q", "BT 0 g", this.pdf.internal.getCoordinateString(this.x), this.pdf.internal.getVerticalCoordinateString(this.y), "Td");
  
  			//stores the current indent of cursor position
  			var currentIndent = 0;
  
  			while (lines.length) {
  				line = lines.shift();
  				maxLineHeight = 0;
  				i = 0;
  				l = line.length;
  				while (i !== l) {
  					if (line[i][0].trim()) {
  						maxLineHeight = Math.max(maxLineHeight, line[i][1]["line-height"], line[i][1]["font-size"]);
  						fontSize = line[i][1]["font-size"] * 7;
  					}
  					i++;
  				}
  				//if we have to move the cursor to adapt the indent
  				var indentMove = 0;
  				var wantedIndent = 0;
  				//if a margin was added (by e.g. a text-alignment), move the cursor
  				if (line[0][1]["margin-left"] !== undefined && line[0][1]["margin-left"] > 0) {
  					wantedIndent = this.pdf.internal.getCoordinateString(line[0][1]["margin-left"]);
  					indentMove = wantedIndent - currentIndent;
  					currentIndent = wantedIndent;
  				}
  				var indentMore = (Math.max(blockstyle["margin-left"] || 0, 0)) * fontToUnitRatio;
  				//move the cursor
  				out(indentMove + indentMore, (-1 * defaultFontSize * maxLineHeight).toFixed(2), "Td");
  				i = 0;
  				l = line.length;
  				while (i !== l) {
  					if (line[i][0]) {
  						this.RenderTextFragment(line[i][0], line[i][1]);
  					}
  					i++;
  				}
  				this.y += maxLineHeight * fontToUnitRatio;
  
  				//if some watcher function was executed successful, so e.g. margin and widths were changed,
  				//reset line drawing and calculate position and lines again
  				//e.g. to stop text floating around an image
  				if (this.executeWatchFunctions(line[0][1]) && lines.length > 0) {
  					var localFragments = [];
  					var localStyles = [];
  					//create fragment array of
  					lines.forEach(function (localLine) {
  						var i = 0;
  						var l = localLine.length;
  						while (i !== l) {
  							if (localLine[i][0]) {
  								localFragments.push(localLine[i][0] + ' ');
  								localStyles.push(localLine[i][1]);
  							}
  							++i;
  						}
  					});
  					//split lines again due to possible coordinate changes
  					lines = this.splitFragmentsIntoLines(PurgeWhiteSpace(localFragments), localStyles);
  					//reposition the current cursor
  					out("ET", "Q");
  					out("q", "BT 0 g", this.pdf.internal.getCoordinateString(this.x), this.pdf.internal.getVerticalCoordinateString(this.y), "Td");
  				}
  
  			}
  			if (cb && typeof cb === "function") {
  				cb.call(this, this.x - 9, this.y - fontSize / 2);
  			}
  			out("ET", "Q");
  			return this.y += paragraphspacing_after;
  		};
  	Renderer.prototype.setBlockBoundary = function (cb) {
  		return this.renderParagraph(cb);
  	};
  	Renderer.prototype.setBlockStyle = function (css) {
  		return this.paragraph.blockstyle = css;
  	};
  	Renderer.prototype.addText = function (text, css) {
  		this.paragraph.text.push(text);
  		return this.paragraph.style.push(css);
  	};
  	FontNameDB = {
  		helvetica: "helvetica",
  		"sans-serif": "helvetica",
  		"times new roman": "times",
  		serif: "times",
  		times: "times",
  		monospace: "courier",
  		courier: "courier"
  	};
  	FontWeightMap = {
  		100: "normal",
  		200: "normal",
  		300: "normal",
  		400: "normal",
  		500: "bold",
  		600: "bold",
  		700: "bold",
  		800: "bold",
  		900: "bold",
  		normal: "normal",
  		bold: "bold",
  		bolder: "bold",
  		lighter: "normal"
  	};
  	FontStyleMap = {
  		normal: "normal",
  		italic: "italic",
  		oblique: "italic"
  	};
  	TextAlignMap = {
  		left: "left",
  		right: "right",
  		center: "center",
  		justify: "justify"
  	};
  	FloatMap = {
  		none: 'none',
  		right: 'right',
  		left: 'left'
  	};
  	ClearMap = {
  		none: 'none',
  		both: 'both'
  	};
  	UnitedNumberMap = {
  		normal: 1
  	};
  	/**
  	 * Converts HTML-formatted text into formatted PDF text.
  	 *
  	 * Notes:
  	 * 2012-07-18
  	 * Plugin relies on having browser, DOM around. The HTML is pushed into dom and traversed.
  	 * Plugin relies on jQuery for CSS extraction.
  	 * Targeting HTML output from Markdown templating, which is a very simple
  	 * markup - div, span, em, strong, p. No br-based paragraph separation supported explicitly (but still may work.)
  	 * Images, tables are NOT supported.
  	 *
  	 * @public
  	 * @function
  	 * @param HTML {String|Object} HTML-formatted text, or pointer to DOM element that is to be rendered into PDF.
  	 * @param x {Number} starting X coordinate in jsPDF instance's declared units.
  	 * @param y {Number} starting Y coordinate in jsPDF instance's declared units.
  	 * @param settings {Object} Additional / optional variables controlling parsing, rendering.
  	 * @returns {Object} jsPDF instance
  	 */
  	jsPDFAPI.fromHTML = function (HTML, x, y, settings, callback, margins) {
  		"use strict";
  
  		this.margins_doc = margins || {
  			top: 0,
  			bottom: 0
  		};
  		if (!settings)
  			settings = {};
  		if (!settings.elementHandlers)
  			settings.elementHandlers = {};
  
  		return process(this, HTML, isNaN(x) ? 4 : x, isNaN(y) ? 4 : y, settings, callback);
  	};
  })(jsPDF.API);
  