docs: function(userOptions) {
  	        //todo: split processing into separate queues
  	        userOptions = userOptions || {};
  	        var engineSettings = {};
  	        var options = extend(App.settings, userOptions);
  
  	        App.vars.preempted = true;
  	        App.vars.dataAttr = options.dataAttr || App.setup.dataAttr;
  
  	        engineSettings.renderer = options.renderer ? options.renderer : App.setup.renderer;
  	        if (App.setup.renderers.join(',').indexOf(engineSettings.renderer) === -1) {
  	            engineSettings.renderer = App.setup.supportsSVG ? 'svg' : (App.setup.supportsCanvas ? 'canvas' : 'html');
  	        }
  
  	        var images = DOM.getNodeArray(options.images);
  	        var bgnodes = DOM.getNodeArray(options.bgnodes);
  	        var stylenodes = DOM.getNodeArray(options.stylenodes);
  	        var objects = DOM.getNodeArray(options.objects);
  
  	        engineSettings.stylesheets = [];
  	        engineSettings.svgXMLStylesheet = true;
  	        engineSettings.noFontFallback = !!options.noFontFallback;
  	        engineSettings.noBackgroundSize = !!options.noBackgroundSize;
  
  	        stylenodes.forEach(function (styleNode) {
  	            if (styleNode.attributes.rel && styleNode.attributes.href && styleNode.attributes.rel.value == 'stylesheet') {
  	                var href = styleNode.attributes.href.value;
  	                //todo: write isomorphic relative-to-absolute URL function
  	                var proxyLink = DOM.newEl('a');
  	                proxyLink.href = href;
  	                var stylesheetURL = proxyLink.protocol + '//' + proxyLink.host + proxyLink.pathname + proxyLink.search;
  	                engineSettings.stylesheets.push(stylesheetURL);
  	            }
  	        });
  
  	        bgnodes.forEach(function (bgNode) {
  	            //Skip processing background nodes if getComputedStyle is unavailable, since only modern browsers would be able to use canvas or SVG to render to background
  	            if (!global.getComputedStyle) return;
  	            var backgroundImage = global.getComputedStyle(bgNode, null).getPropertyValue('background-image');
  	            var dataBackgroundImage = bgNode.getAttribute('data-background-src');
  	            var rawURL = dataBackgroundImage || backgroundImage;
  
  	            var holderURL = null;
  	            var holderString = options.domain + '/';
  	            var holderStringIndex = rawURL.indexOf(holderString);
  
  	            if (holderStringIndex === 0) {
  	                holderURL = rawURL;
  	            } else if (holderStringIndex === 1 && rawURL[0] === '?') {
  	                holderURL = rawURL.slice(1);
  	            } else {
  	                var fragment = rawURL.substr(holderStringIndex).match(/([^\"]*)"?\)/);
  	                if (fragment !== null) {
  	                    holderURL = fragment[1];
  	                } else if (rawURL.indexOf('url(') === 0) {
  	                    throw 'Holder: unable to parse background URL: ' + rawURL;
  	                }
  	            }
  
  	            if (holderURL) {
  	                var holderFlags = parseURL(holderURL, options);
  	                if (holderFlags) {
  	                    prepareDOMElement({
  	                        mode: 'background',
  	                        el: bgNode,
  	                        flags: holderFlags,
  	                        engineSettings: engineSettings
  	                    });
  	                }
  	            }
  	        });
  
  	        objects.forEach(function (object) {
  	            var objectAttr = {};
  
  	            try {
  	                objectAttr.data = object.getAttribute('data');
  	                objectAttr.dataSrc = object.getAttribute(App.vars.dataAttr);
  	            } catch (e) {}
  
  	            var objectHasSrcURL = objectAttr.data != null && objectAttr.data.indexOf(options.domain) === 0;
  	            var objectHasDataSrcURL = objectAttr.dataSrc != null && objectAttr.dataSrc.indexOf(options.domain) === 0;
  
  	            if (objectHasSrcURL) {
  	                prepareImageElement(options, engineSettings, objectAttr.data, object);
  	            } else if (objectHasDataSrcURL) {
  	                prepareImageElement(options, engineSettings, objectAttr.dataSrc, object);
  	            }
  	        });
  
  	        images.forEach(function (image) {
  	            var imageAttr = {};
  
  	            try {
  	                imageAttr.src = image.getAttribute('src');
  	                imageAttr.dataSrc = image.getAttribute(App.vars.dataAttr);
  	                imageAttr.rendered = image.getAttribute('data-holder-rendered');
  	            } catch (e) {}
  
  	            var imageHasSrc = imageAttr.src != null;
  	            var imageHasDataSrcURL = imageAttr.dataSrc != null && imageAttr.dataSrc.indexOf(options.domain) === 0;
  	            var imageRendered = imageAttr.rendered != null && imageAttr.rendered == 'true';
  
  	            if (imageHasSrc) {
  	                if (imageAttr.src.indexOf(options.domain) === 0) {
  	                    prepareImageElement(options, engineSettings, imageAttr.src, image);
  	                } else if (imageHasDataSrcURL) {
  	                    //Image has a valid data-src and an invalid src
  	                    if (imageRendered) {
  	                        //If the placeholder has already been render, re-render it
  	                        prepareImageElement(options, engineSettings, imageAttr.dataSrc, image);
  	                    } else {
  	                        //If the placeholder has not been rendered, check if the image exists and render a fallback if it doesn't
  	                        (function(src, options, engineSettings, dataSrc, image) {
  	                            utils.imageExists(src, function(exists) {
  	                                if (!exists) {
  	                                    prepareImageElement(options, engineSettings, dataSrc, image);
  	                                }
  	                            });
  	                        })(imageAttr.src, options, engineSettings, imageAttr.dataSrc, image);
  	                    }
  	                }
  	            } else if (imageHasDataSrcURL) {
  	                prepareImageElement(options, engineSettings, imageAttr.dataSrc, image);
  	            }
  	        });
  
  	        return this;
  	    }