docs: function(el, componentTagData) {
  			// Save arguments so if this component gets re-inserted,
  			// we can setup again.
  			this._initialArgs = [el,componentTagData];
  
  			var component = this;
  
  			var options = {
  				helpers: {},
  				tags: {}
  			};
  
  			// #### Clean up arguments
  
  			// If componentTagData isn’t defined, check for el and use it if it’s defined;
  			// otherwise, an empty object is needed for componentTagData.
  			if (componentTagData === undefined) {
  				if (el === undefined) {
  					componentTagData = {};
  				} else {
  					componentTagData = el;
  					el = undefined;
  				}
  			}
  
  			// Create an element if it doesn’t exist and make it available outside of this
  			if (el === undefined) {
  				el = DOCUMENT().createElement(this.tag);
  				el[createdByCanComponentSymbol] = true;
  			}
  			this.element = el;
  
  			if(componentTagData.initializeBindings === false && !this._skippedSetup) {
  				// Temporary, will be overridden.
  				this._skippedSetup = this._torndown = true;
  				this.viewModel = Object.create(null);
  				return;
  			}
  
  			var componentContent = componentTagData.content;
  			if (componentContent !== undefined) {
  				// Check if it’s already a renderer function or
  				// a string that needs to be parsed by stache
  				if (typeof componentContent === "function") {
  					componentTagData.subtemplate = componentContent;
  				} else if (typeof componentContent === "string") {
  					componentTagData.subtemplate = stache(componentContent);
  				}
  			}
  
  			var componentScope = componentTagData.scope;
  			if (componentScope !== undefined && componentScope instanceof Scope === false) {
  				componentTagData.scope = new Scope(componentScope);
  			}
  
  			// Hook up any templates with which the component was instantiated
  			var componentTemplates = componentTagData.templates;
  			if (componentTemplates !== undefined) {
  				canReflect.eachKey(componentTemplates, function(template, name) {
  					// Check if it’s a string that needs to be parsed by stache
  					if (typeof template === "string") {
  						var debugName = name + " template";
  						componentTemplates[name] = stache(debugName, template);
  					}
  				});
  			}
  
  			// #### Setup ViewModel
  			var viewModel;
  			var initialViewModelData = {};
  
  			var preventDataBindings = domData.get(el, "preventDataBindings");
  
  			var teardownBindings;
  			if (preventDataBindings) {
  				viewModel = el[viewModelSymbol];
  			} else {
  				// Set up the bindings
  				var setupFn;
  				if (componentTagData.setupBindings) {
  					setupFn = function(el, componentTagData, callback, initialViewModelData){
  						return componentTagData.setupBindings(el, callback, initialViewModelData);
  					};
  				} else if (componentTagData.viewModel) {
  					// Component is being instantiated with a viewModel
  					setupFn = getSetupFunctionForComponentVM(componentTagData.viewModel);
  				} else {
  					setupFn = stacheBindings.behaviors.viewModel;
  				}
  				teardownBindings = setupFn(el, componentTagData, function(initialViewModelData) {
  
  					var ViewModel = component.constructor.ViewModel,
  						viewModelHandler = component.constructor.viewModelHandler,
  						viewModelInstance = component.constructor.viewModelInstance;
  
  					if(viewModelHandler) {
  						var scopeResult = viewModelHandler.call(component, initialViewModelData, componentTagData.scope, el);
  						if (canReflect.isObservableLike(scopeResult) && canReflect.isMapLike(scopeResult) ) {
  							// If the function returns a can.Map, use that as the viewModel
  							viewModelInstance = scopeResult;
  						} else if (canReflect.isObservableLike(scopeResult.prototype) && canReflect.isMapLike(scopeResult.prototype)) {
  							// If `scopeResult` is of a `can.Map` type, use it to wrap the `initialViewModelData`
  							ViewModel = scopeResult;
  						} else {
  							// Otherwise extend `SimpleMap` with the `scopeResult` and initialize it with the `initialViewModelData`
  							ViewModel = SimpleMap.extend(scopeResult);
  						}
  					}
  
  					if(ViewModel) {
  						viewModelInstance = new ViewModel(initialViewModelData);
  					}
  					viewModel = viewModelInstance;
  					return viewModelInstance;
  				}, initialViewModelData);
  			}
  
  			// Set `viewModel` to `this.viewModel` and set it to the element's `data` object as a `viewModel` property
  			this.viewModel = viewModel;
  			el[viewModelSymbol] = viewModel;
  			el.viewModel = viewModel;
  			domData.set(el, "preventDataBindings", true);
  
  			// an array of teardown stuff that should happen when the element is removed
  			var teardownFunctions = [];
  			var callTeardownFunctions = function() {
  					for (var i = 0, len = teardownFunctions.length; i < len; i++) {
  						teardownFunctions[i]();
  					}
  				};
  
  			// #### Helpers
  			// TODO: remove in next release
  			// Setup helpers to callback with `this` as the component
  			if(this.helpers !== undefined) {
  				canReflect.eachKey(this.helpers, function(val, prop) {
  					if (typeof val === "function") {
  						options.helpers[prop] = val.bind(viewModel);
  					}
  				});
  			}
  
  
  			// #### `events` control
  			// TODO: remove in next release
  			// Create a control to listen to events
  			if(this.constructor.Control) {
  				this._control = new this.constructor.Control(el, {
  					// Pass the viewModel to the control so we can listen to it's changes from the controller.
  					scope: this.viewModel,
  					viewModel: this.viewModel,
  					destroy: callTeardownFunctions
  				});
  			} else {
  				var removalDisposal = domMutate.onNodeRemoval(el, function () {
  					var doc = el.ownerDocument;
  					var rootNode = doc.contains ? doc : doc.documentElement;
  					if (!rootNode || !rootNode.contains(el)) {
  						if(removalDisposal) {
  							nodeRemoved = true;
  							removalDisposal();
  							callTeardownFunctions();
  							removalDisposal = null;
  							callTeardownFunctions = null;
  						}
  					}
  				});
  			}
  
  			// #### Rendering
  
  			var leakScope = {
  				toLightContent: this.leakScope === true,
  				intoShadowContent: this.leakScope === true
  			};
  
  			var hasShadowView = !!(this.constructor.view);
  			var shadowFragment;
  
  			// Get what we should render between the component tags
  			// and the data for it.
  			var betweenTagsView;
  			var betweenTagsTagData;
  			if( hasShadowView ) {
  				var shadowTagData;
  				if (leakScope.intoShadowContent) {
  					// Give access to the component's data and the VM
  					shadowTagData = {
  						scope: componentTagData.scope.add(this.viewModel, { viewModel: true }),
  						options: options
  					};
  
  				} else { // lexical
  					// only give access to the VM
  					shadowTagData = {
  						scope: new Scope(this.viewModel, null, { viewModel: true }),
  						options: options
  					};
  				}
  
  				// Add a hookup for each <can-slot>
  				options.tags['can-slot'] = makeReplacementTagCallback('can-slot', componentTagData, shadowTagData, leakScope, function(el) {
  					var templates = componentTagData.templates;
  					if (templates) {// This is undefined if the component is <self-closing/>
  						return templates[el.getAttribute("name")];
  					}
  				});
  
  				// Add a hookup for <content>
  				options.tags.content = makeReplacementTagCallback('content',  componentTagData, shadowTagData, leakScope, function() {
  					return componentTagData.subtemplate;
  				});
  
  				betweenTagsView = this.constructor.view;
  				betweenTagsTagData = shadowTagData;
  			}
  			else {
  				// No shadow template.
  				// Render light template with viewModel on top
  				var lightTemplateTagData = {
  					scope: componentTagData.scope.add(this.viewModel, {
  						viewModel: true
  					}),
  					options: options
  				};
  				betweenTagsTagData = lightTemplateTagData;
  				betweenTagsView = componentTagData.subtemplate || el.ownerDocument.createDocumentFragment.bind(el.ownerDocument);
  			}
  			var viewModelDisconnectedCallback,
  				insertionDisposal,
  				componentInPage,
  				nodeRemoved;
  
  			// Keep a nodeList so we can kill any directly nested nodeLists within this component
  			var nodeList = nodeLists.register([], function() {
  				if(removalDisposal && !nodeRemoved) {
  					removalDisposal();
  					callTeardownFunctions();
  					removalDisposal = null;
  					callTeardownFunctions = null;
  				}
  				component._torndown = true;
  				domEvents.dispatch(el, "beforeremove", false);
  				if(teardownBindings) {
  					teardownBindings();
  				}
  				if(viewModelDisconnectedCallback) {
  					viewModelDisconnectedCallback(el);
  				} else if(typeof viewModel.stopListening === "function"){
  					viewModel.stopListening();
  				}
  				if(insertionDisposal) {
  					insertionDisposal();
  					insertionDisposal = null;
  				}
  			}, componentTagData.parentNodeList || true, false);
  			nodeList.expression = "<" + this.tag + ">";
  			teardownFunctions.push(function() {
  				nodeLists.unregister(nodeList);
  			});
  			this.nodeList = nodeList;
  
  			shadowFragment = betweenTagsView(betweenTagsTagData.scope, betweenTagsTagData.options, nodeList);
  
  			// TODO: afterRender
  
  			// Append the resulting document fragment to the element
  			domMutateNode.appendChild.call(el, shadowFragment);
  
  			// update the nodeList with the new children so the mapping gets applied
  			nodeLists.update(nodeList, getChildNodes(el));
  
  			// Call connectedCallback
  			if(viewModel && viewModel.connectedCallback) {
  				var body = DOCUMENT().body;
  				componentInPage = body && body.contains(el);
  
  				if(componentInPage) {
  					viewModelDisconnectedCallback = viewModel.connectedCallback(el);
  				} else {
  					insertionDisposal = domMutate.onNodeInsertion(el, function () {
  						insertionDisposal();
  						insertionDisposal = null;
  						viewModelDisconnectedCallback = viewModel.connectedCallback(el);
  					});
  				}
  
  			}
  			component._torndown = false;
  		}