done-autorender
Version:
Autorender CanJS projects
27 lines (26 loc) • 16.6 kB
JavaScript
define([], function(){
return function(obj) {
obj || (obj = {});
var __t, __p = '';
with (obj) {
__p += 'define(' +
((__t = ( imports )) == null ? '' : __t) +
', function(' +
((__t = ( args )) == null ? '' : __t) +
'){\n var zoneOpts = ' +
((__t = ( zoneOpts )) == null ? '' : __t) +
';\n var useZones = zoneOpts.useZones;\n var tokens = ' +
((__t = ( intermediate )) == null ? '' : __t) +
';\n var renderer = stache("' +
((__t = ( filename )) == null ? '' : __t) +
'", tokens);\n var routeDataProp = "' +
((__t = ( routeData )) == null ? '' : __t) +
'";\n var isDevelopment = ' +
((__t = ( isDevelopment )) == null ? '' : __t) +
';\n\n var isNode = typeof process === "object" &&\n {}.toString.call(process) === "[object process]";\n var isNW = (function(){\n try{var nr = loader._nodeRequire; return nr && nr(\'nw.gui\') !== \'undefined\';}catch(e){return false;}\n })();\n var isElectron = isNode && !!process.versions.electron;\n\n /**\n * @function render\n * @hide\n * @description Call the stache renderer function with Scope and Options.\n * @signature `render(scope, options)`\n * @param {can-view-scope} scope A can-view-scope object.\n * @param {can-view-scope.Options} options An option object.\n * @return {DocumentFragment} The result of calling a can-stache renderer,\n * a document fragment.\n */\n function render(viewModel, options){\n var moduleOptions = { module: module };\n options = (options && options.add) ? options.add(moduleOptions) :\n moduleOptions;\n\n var nodeList = canNodeList.register([], null, true);\n nodeList.expression = "DONE-AUTO-RENDER";\n var fragment = renderer(new Scope(viewModel, null, { viewModel: true }), options, nodeList);\n canNodeList.update( nodeList, childNodes(fragment) );\n\n // Provide the nodeList for external use.\n if(useZones && typeof CanZone !== "undefined" && CanZone.current) {\n CanZone.current.data.nodeList = nodeList;\n }\n\n return {fragment: fragment, nodeList: nodeList};\n }\n\n /**\n * @function connectViewModel\n * @description Create a new instance of the provided ViewModel, set it\n * as the route’s data, and call route.start().\n * @signature `connectViewModel()`\n * @return {Map} an instance of some map type.\n */\n function connectViewModel() {\n var ctx = getContext(this);\n var ViewModel = ctx.ViewModel;\n\n if(!ViewModel) {\n var message = "done-autorender cannot start without a ViewModel. " +\n "Please ensure your template contains an export for your " +\n "application\'s ViewModel. https://github.com/donejs/autorender#viewmodel";\n console.error(message);\n return;\n }\n\n // Make sure the common index.stache props are defined.\n setupDefaultViewModelProps(ctx, ViewModel);\n\n var viewModel = ctx.state = new ViewModel();\n canReflect.setKeyValue(document.documentElement,\n canSymbol.for("can.viewModel"), viewModel);\n\n setRouteData(route, viewModel);\n route.start();\n return viewModel;\n }\n\n /**\n * @function connectViewModelAndAttach\n * @description Render the stache template, then update the\n * DOM to reflect these changes. Save the state of the ViewModel instance\n * so that it can be reused to do rerenders in case of live-reload. This is\n * the main entry point of rendering, and happens upon page load.\n * @signature `connectViewModelAndAttach()`\n **/\n function connectViewModelAndAttach() {\n connectViewModel();\n return renderAndAttach.call(this);\n }\n\n /**\n * @function reattachWithZone\n * @description Create a Zone for reattach.\n * @signature `reattachWithZone()`\n **/\n function reattachWithZone() {\n new Zone({\n plugins: [xhrZone]\n }).run(function(){\n var viewModel = connectViewModel();\n var result = renderInZone(viewModel);\n\n var incremental = document.documentElement.dataset.incrementallyRendered === "";\n\n // If incrementally rendering, attach right away. IR hydration will\n // handle reattachment.\n if(incremental) {\n attach(result);\n } else {\n result.promise.then(attach);\n }\n });\n }\n\n var tagsToIgnore = {\n "SCRIPT": true,\n "STYLE": true,\n "LINK": true,\n "BASE": true\n };\n\n function hasDataKeepAttr(node) {\n return node.dataset && node.dataset.keep === "";\n }\n\n function isKeepComment(node) {\n return node.nodeType === 8 && node.nodeValue.indexOf("autorender-keep") === 0;\n }\n\n var keepNodeSymbol = canSymbol.for("done.keepNode");\n function hasKeepSymbol(node) {\n return node[keepNodeSymbol];\n }\n\n function isKeepNode(node) {\n return hasDataKeepAttr(node) || isKeepComment(node) || hasKeepSymbol(node);\n }\n\n /**\n * Call a callback for each child Node within a parent, skipping\n * elements that should not be touched because of their side-effects.\n */\n function eachChild(parent, callback, noSkipping){\n var nodes = Array.prototype.slice.call(childNodes(parent)),\n i = 0, len = nodes.length,\n node, ignoreTag;\n\n for(; i < len; i++) {\n node = nodes[i];\n ignoreTag = tagsToIgnore[node.nodeName];\n if(noSkipping || (!ignoreTag && !isKeepNode(node))) {\n // Returning false breaks the loop\n if(callback(node) === false) {\n break;\n }\n }\n }\n }\n\n /**\n * Remove an element\n */\n function remove(el) {\n domMutateNode.removeChild.call(el.parentNode, el);\n }\n\n /**\n * Creates a function that will append to a parent Element.\n */\n function appendTo(parent){\n return function(el){\n domMutateNode.appendChild.call(parent, el);\n }\n }\n\n /**\n * Sets the route.data property\n */\n function setRouteData(route, appViewModel) {\n if(routeDataProp) {\n var obs = canReflect.getKeyValue(appViewModel, routeDataProp);\n\n if(obs.constructor) {\n route.data = new obs.constructor();\n canReflect.setKeyValue(appViewModel, routeDataProp, route.data);\n } else {\n route.data = obs;\n }\n } else {\n route.data = appViewModel;\n }\n }\n\n /**\n * @function attach\n * @hide\n * @description Receives the completely rendered DocumentFragment and\n * attaches the parts from the head into the document.head, the body into\n * document.body.\n * @signature `attach(result)`\n * @param {RenderResult} The result of rendering within a Zone.\n */\n function attach(result){\n var document = getContext(this).ownerDocument;\n var frag = result.fragment;\n var viewModel = result.viewModel;\n\n // If already attached skip this part.\n if(document.documentElement.hasAttribute("data-attached")) {\n return;\n }\n\n moveToDocument(frag, document, true);\n document.documentElement.setAttribute("data-attached", "");\n\n if(viewModel && viewModel.connectedCallback) {\n var dispose = viewModel.connectedCallback(document.documentElement);\n\n if(typeof dispose === "function") {\n domMutate.onNodeRemoval(document.documentElement, dispose);\n }\n }\n return result;\n }\n\n /**\n * Move content from a fragment into a document.\n **/\n function moveToDocument(frag, document, removeExistingNodes) {\n var head = document.head;\n var body = document.body;\n\n // Move elements from the fragment\'s head to the document head.\n if(removeExistingNodes) {\n eachChild(head, remove);\n }\n\n var top = frag.firstChild;\n\n // Find the <html> element.\n if(top.nodeType !== 1) {\n top = top.nextSibling;\n }\n\n var fragHead = top;\n var fragBody = top.firstChild;\n\n while(fragHead.tagName !== "HEAD") {\n fragHead = fragHead.firstChild || fragHead.nextSibling;\n }\n\n while(fragBody && fragBody.tagName !== "BODY") {\n fragBody = fragBody.nextSibling;\n }\n\n if(head && fragHead) {\n eachChild(fragHead, appendTo(head), !removeExistingNodes);\n }\n\n\n // Move elements from the fragment\'s body to the document body.\n if(removeExistingNodes) {\n eachChild(body, remove);\n }\n\n if(body && fragBody) {\n eachChild(fragBody, appendTo(body), !removeExistingNodes);\n }\n\n }\n\n\n /**\n * @function renderAndAttach\n * @hide\n * @description Render the template with a Zone, wait for all asynchronous\n * events to complete, and then attach the DocumentFragment to the page.\n * @signature `renderAndAttach()`\n * @return {Promise} A Promise that resolves after the template has been\n * attached to the DOM.\n */\n function renderAndAttach(){\n var boundAttach = attach.bind(this);\n\n var viewModel = context.state;\n return useZones\n ? renderInZone(viewModel).promise.then(boundAttach)\n : renderNoZone(viewModel).then(boundAttach);\n }\n\n /**\n * @function renderIntoZone\n * @hide\n * @description Render a viewModel in a Zone context, returning the\n * Zone promise.\n * @signature `renderIntoZone(viewModel)`\n * @param {Object} viewModel\n * @return {RenderResult} the promise that resolves when asynchronousity\n * within the Zone is complete, and the fragment generated.\n */\n function renderInZone(viewModel){\n function getZonePlugins() {\n var plugins = [xhrZone];\n if(zoneOpts.useDebug) {\n var timeout = zoneOpts.timeout;\n var opts = {\n break: zoneOpts.debugBrk\n };\n\n plugins.push(debugZone(timeout, opts));\n }\n return plugins;\n }\n\n function logDebugInfo() {\n var warn = Function.prototype.bind.call(console.warn, console);\n var zoneData = zone.data;\n if(zoneData.debugInfo) {\n zoneData.debugInfo.forEach(function(info){\n warn(info.task, info.stack);\n });\n }\n }\n\n var renderData;\n var zone = new Zone({\n plugins: getZonePlugins()\n });\n var zonePromise = zone.run(function(){\n renderData = render(viewModel, {});\n }).then(function(zoneData){\n return {\n nodeList: renderData.nodeList,\n fragment: renderData.fragment,\n zoneData: zoneData,\n viewModel: viewModel\n };\n })\n .then(null, function(err){\n if(err.timeout) {\n logDebugInfo();\n var error = new Error("Timeout of " + err.timeout + " exceeded");\n throw error;\n } else {\n throw err;\n }\n });\n\n return {\n fragment: renderData.fragment,\n promise: zonePromise,\n viewModel: viewModel,\n zoneData: zone.data\n };\n }\n\n /**\n * @function renderNoZone\n * @hide\n * @description Render a viewModel without a Zone.\n * @signature `renderIntoZone(viewModel)`\n * @param {Object} viewModel\n * @return {RenderResult} the promise that resolves immediately with a fragment.\n */\n function renderNoZone(viewModel){\n var renderData = render(viewModel, {});\n return Promise.resolve(renderData);\n }\n\n /**\n * @function renderIntoDocument\n * @description This is used in SSR, it provides a fresh document\n * and viewModel, and this function calls the stache renderer and updates\n * the document with the result.\n * @signature `renderIntoDocument(document, viewModel)`\n * @param {Document} document\n * @param {Object} viewModel\n **/\n function renderIntoDocument(document, viewModel) {\n var renderData = render.call(this, viewModel, {});;\n var frag = renderData.fragment;\n\n var firstChild = frag.firstChild;\n var documentElement = document.documentElement;\n var head = document.head;\n\n // If there is an <html> element, which there usually is,\n // replace the existing documentElement, otherwise just append the fragment\n if(firstChild && firstChild.nodeName === "HTML") {\n domMutateNode.replaceChild.call(document, firstChild, documentElement);\n } else {\n domMutateNode.appendChild.call(documentElement, frag);\n }\n\n // Move anything from the original head back to the new documentElement.\n if(head && head.firstChild) {\n var fragHead = document.createDocumentFragment();\n fragHead.appendChild(head);\n moveToDocument(fragHead, document, false);\n }\n\n // Set the can-automount=false attribute\n document.documentElement.setAttribute("data-can-automount", "false");\n }\n\n /**\n * @function teardownRouting\n * @hide\n * @description Teardown the connection to the route, for live-reload.\n * @signature `teardownRouting()`\n **/\n function teardownRouting() {\n route.stop();\n }\n\n if(typeof steal !== \'undefined\' && (isNW || isElectron || !isNode)) {\n steal.done().then(function() {\n if(steal.loader.autorenderAutostart !== false) {\n if (useZones){\n reattachWithZone();\n } else {\n connectViewModelAndAttach();\n }\n }\n });\n }\n\n var context = Object.create(Function.prototype, {\n ' +
((__t = ( ases )) == null ? '' : __t) +
'\n connectViewModelAndAttach: {\n value: connectViewModelAndAttach\n },\n ownerDocument: {\n get: function(){\n return document;\n }\n },\n renderAndAttach: {\n value: renderAndAttach\n },\n renderInZone: {\n value: renderInZone\n },\n teardownRouting: {\n value: teardownRouting\n },\n ViewModel: {\n get: function(){\n return this.viewModel;\n }\n }\n });\n\n var defaultProperties = ["env", "request", "statusCode", "statusMessage"];\n\n /**\n * @function setupDefaultViewModelProps\n * @hide\n * @description Given a ViewModel constructor, ensure that the\n * default properties like `env` and `request` are not serialized\n */\n function setupDefaultViewModelProps(ctx, ViewModel) {\n var sym = canSymbol.for("autorender.props");\n if(!canReflect.getKeyValue(ctx, sym) &&\n canReflect.isConstructorLike(ViewModel)) {\n var proto = ViewModel.prototype;\n\n defaultProperties.forEach(function(prop){\n if(!canReflect.hasOwnKey(proto, prop)) {\n canReflect.defineInstanceKey(ViewModel, prop, {\n enumerable: false\n });\n }\n });\n\n canReflect.setKeyValue(ctx, sym, true);\n }\n }\n\n /**\n * @function getContext\n * @hide\n * @description Gets the context (an object containing information about the render).\n * This is used mostly as a DI mechanism for testing.\n */\n function getContext(thisArg) {\n return context.isPrototypeOf(thisArg) ? thisArg : context;\n }\n\n /**\n * @function createViewModelAndRender\n * @hide\n * @description Create an instance of the ViewModel and render it with\n * the renderIntoDocument function.\n * @param {node.IncomingMessage|Object} requestOrHeaders A request object, from Node.js\n */\n function createViewModelAndRender(requestOrHeaders) {\n // Check if we were called with .call/apply, otherwise use the\n // context within the scope. This is for testing.\n var ctx = getContext(this);\n\n var document = ctx.ownerDocument;\n var ViewModel = ctx.ViewModel;\n\n if(!ViewModel) {\n var msg = "done-autorender cannot render your application " +\n "without a viewModel defined. " +\n "See the guide for information. " +\n "http://donejs.com/Guide.html#section_Createatemplateandmainfile";\n throw new Error(msg);\n }\n\n //!steal-remove-start\n if (isDevelopment) {\n if(canReflect.size(route.routes) === 0) {\n var msg = "done-autorender didn\'t receive route definitions." +\n "For Server-Side-Rendering you have to provide an initialized can-route." +\n "See the guide for information. " +\n "https://canjs.com/doc/can-route.html and https://donejs.com/Apis.html#can-route"\n canDev.warn(msg);\n }\n }\n //!steal-remove-end\n\n setupDefaultViewModelProps(ctx, ViewModel);\n\n var urlObj;\n\n // Test if this is http/2 headers object\n var isH2 = !!requestOrHeaders[":authority"];\n if(isH2) {\n var h = requestOrHeaders;\n urlObj = new URL(h[":scheme"] + ":" + h[":authority"] + h[":path"]);\n } else {\n urlObj = new URL(fullUrl(requestOrHeaders));\n }\n\n // Get the route params\n var pathWithoutSlash = urlObj.pathname.substr(1);\n var routeStr = pathWithoutSlash + urlObj.search;\n var hasMatchingRoute = !!route.rule(routeStr);\n var routeParams = route.deparam(routeStr);\n\n var viewModelParams = {\n env: Object.assign({}, process.env),\n request: requestOrHeaders\n };\n\n if(!routeDataProp) {\n viewModelParams = Object.assign(routeParams, viewModelParams);\n }\n\n var viewModel = new ViewModel(viewModelParams);\n\n if(!canReflect.getKeyValue(viewModel, "statusCode") && canReflect.size(route.routes) !== 0) {\n if(hasMatchingRoute) {\n canReflect.setKeyValue(viewModel, "statusCode", 200);\n } else {\n canReflect.setKeyValue(viewModel, "statusCode", 404);\n canReflect.setKeyValue(viewModel, "statusMessage", "Not found");\n }\n }\n\n setRouteData(route, viewModel);\n\n if(routeDataProp) {\n canReflect.update(route.data, routeParams);\n }\n\n route.start();\n\n renderIntoDocument.call(ctx, document, viewModel);\n\n return createViewModelAndRender;\n }\n\n Object.setPrototypeOf(createViewModelAndRender, context);\n\n return createViewModelAndRender;\n});\n';
}
return __p
}
});