Skip to content

Instantly share code, notes, and snippets.

@hashchange
Last active February 11, 2024 22:37
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save hashchange/f99dca479f91b55a61d7f4994ea5d438 to your computer and use it in GitHub Desktop.
Save hashchange/f99dca479f91b55a61d7f4994ea5d438 to your computer and use it in GitHub Desktop.
Precompiled.Declarative.Handlebars.Templates – a plug-in for Backbone.Declarative.Views, enabling the use of precompiled Handlebars templates.
/**
* Precompiled.Declarative.Handlebars.Templates
*
* Plug-in for Backbone.Declarative.Views, enabling the use of precompiled Handlebars templates.
*
* For defining `el`-related properties along with a precompiled template, modify the template source. Add a special
* comment to the source, just as you would in a template string. An example:
*
* <!-- data-tag-name="ul" data-class-name="list" -->
*
* For more on that special comment, see the documentation of Backbone.Declarative.Views, in the section about "Setting
* the template property to a template string rather than a selector" (http://goo.gl/37GJmw).
*/
;( function ( root, factory ) {
"use strict";
// UMD for a Backbone plugin. Supports AMD, Node.js, CommonJS and globals.
//
// - Code lives in the Backbone namespace.
// - The module does not export a meaningful value.
// - The module does not create a global.
var supportsExports = typeof exports === "object" && exports && !exports.nodeType && typeof module === "object" && module && !module.nodeType;
// AMD:
// - Some AMD build optimizers like r.js check for condition patterns like the AMD check below, so keep it as is.
// - Check for `exports` after `define` in case a build optimizer adds an `exports` object.
// - The AMD spec requires the dependencies to be an array **literal** of module IDs. Don't use a variable there,
// or optimizers may fail.
if ( typeof define === "function" && typeof define.amd === "object" && define.amd ) {
// AMD module
define( [ "exports", "underscore", "backbone", "handlebars", "backbone.declarative.views" ], factory );
} else if ( supportsExports ) {
// Node module, CommonJS module
factory( exports, require( "underscore" ), require( "backbone" ), require( "handlebars" ), require( "backbone.declarative.views" ) );
} else {
// Global (browser or Rhino)
factory( {}, _, Backbone, Handlebars );
}
}( this, function ( exports, _, Backbone, Handlebars ) {
"use strict";
var infoMessage = "ATTN Temporary, bogus HTML, generated by the Precompiled.Declarative.Handlebars.Templates plugin. Do not use it. Use the compiled template instead.",
commentPrefix = "<!-- " + infoMessage + " -->";
Backbone.DeclarativeViews.custom.loadTemplate = function ( templateProperty, view, viewOptions ) {
var precompiled, temporaryHtml, $template;
precompiled = getPrecompiledTemplate( templateProperty );
if ( precompiled ) {
// We need to extract the comment defining the el. To get to it, we just call the compiled template without
// template variables, passing in an empty hash.
//
// The remaining HTML might be utter junk, and syntactically invalid. It doesn't matter. As soon as
// Backbone.Declarative.Views has checked the template for the presence of a special comment - and evaluated
// that comment -, the HTML won't be used anymore.
//
// (And immediately after the cache entry has come into existence, the bogus HTML is purged from it. See the
// event handler for the "cacheEntry:create" event, below.)
temporaryHtml = precompiled( {} );
// We hand the bogus HTML to the default loader, and leave further processing to Backbone.Declarative.Views.
//
// The default loader will wrap the HTML into a script node. Later on, Backbone.Declarative.Views will
// create the cache entry. In the process, it extracts the el definition from the embedded special comment,
// if there is one.
//
// We also prepend an HTML comment to the bogus HTML, to make sure the content does not accidentally turn
// into a selector. Even though that is unlikely, it could happen if the special comment is absent and the
// template content evaluates to a selector for an existing DOM element ("#template").
//
// The prepended comment also serves to identify the bogus HTML later on, during clean-up in the
// "cacheEntry:create" event handler.
$template = Backbone.DeclarativeViews.defaults.loadTemplate( commentPrefix + temporaryHtml );
// On the generated $template node, we set a data attribute as a flag for the template compiler. It
// identifies the node as containing temporary HTML, which needs to be ignored. The flag also serves to
// store the template ID on the node.
$template.attr( "data-precompiled-template-id", templateProperty );
} else {
// The template is not precompiled. Handle as usual.
$template = Backbone.DeclarativeViews.defaults.loadTemplate( templateProperty, view, viewOptions );
}
return $template;
};
Backbone.DeclarativeViews.custom.compiler = function ( templateHtml, $template ) {
// Check if we are dealing with temporary, bogus HTML from the custom loader, which should be ignored.
//
// - If we are dealing with bogus HTML, we use the template ID to retrieve the precompiled template. The
// template ID is stored as a data attribute on the template node (was created by the loader).
//
// - Otherwise, we are dealing with real template HTML. We don't have a precompiled template at our disposal, so
// we simply compile the HTML.
var templateId = $template && $template.data( "precompiled-template-id" );
return templateId ? getPrecompiledTemplate( templateId ) : Handlebars.compile( templateHtml );
};
Backbone.DeclarativeViews.plugins.events.on( "cacheEntry:create", function ( cacheEntry, templateProperty ) {
// Clean up the cache entry. If the template has been precompiled and the loader has generated temporary, bogus
// HTML, remove that HTML from the cache and set cacheEntry.html to a clarifying message.
//
// This step might help to avoid confusion during debugging, but is completely optional otherwise.
if ( getPrecompiledTemplate( templateProperty ) && cacheEntry.html.indexOf( commentPrefix ) === 0 ) cacheEntry.html = infoMessage;
} );
function getPrecompiledTemplate( templateId ) {
return Handlebars.templates[templateId];
}
// Module return value
// -------------------
//
// A return value may be necessary for AMD to detect that the module is loaded. It ony exists for that reason and is
// purely symbolic. Don't use it in client code. The functionality of this module lives in the Backbone namespace.
exports.info = "Precompiled.Declarative.Handlebars.Templates has loaded. Don't use the exported value of the module. Its functionality is available inside the Backbone.DeclarativeViews namespace.";
} ) );
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment