Commit 0bfbe505 authored by Jacob Thornton's avatar Jacob Thornton

Merge branch '2.0-wip'

Conflicts:
	.gitignore
	LICENSE
	Makefile
	bootstrap.css
	bootstrap.min.css
	docs/assets/js/application.js
	docs/assets/js/google-code-prettify/prettify.css
	docs/index.html
	docs/javascript.html
	examples/container-app.html
	examples/fluid.html
	examples/hero.html
	js/bootstrap-alerts.js
	js/bootstrap-dropdown.js
	js/bootstrap-modal.js
	js/bootstrap-popover.js
	js/bootstrap-scrollspy.js
	js/bootstrap-tabs.js
	js/bootstrap-twipsy.js
	js/tests/index.html
	js/tests/unit/bootstrap-modal.js
	js/tests/unit/bootstrap-popover.js
	js/tests/unit/bootstrap-tabs.js
	lib/forms.less
	lib/mixins.less
	lib/patterns.less
	lib/scaffolding.less
	lib/tables.less
parents 43cbc944 4bd1ba4e
# Numerous always-ignore extensions
*.diff
*.err
*.orig
*.log
*.rej
*.swo
*.swp
*.vi
*~ *~
*.sass-cache
# OS or Editor folders
.DS_Store .DS_Store
thumbs.db Thumbs.db
js/min .cache
\ No newline at end of file .project
.settings
.tmproj
*.esproj
nbproject
*.sublime-project
*.sublime-workspace
# Komodo
*.komodoproject
.komodotools
# Folders to ignore
.hg
.svn
.CVS
.idea
\ No newline at end of file
This diff is collapsed.
VERSION=1.4.0 BOOTSTRAP = ./docs/assets/css/bootstrap.css
DATE=$(shell date) BOOTSTRAP_LESS = ./less/bootstrap.less
BOOTSTRAP = ./bootstrap.css BOOTSTRAP_RESPONSIVE = ./docs/assets/css/bootstrap-responsive.css
BOOTSTRAP_MIN = ./bootstrap.min.css BOOTSTRAP_RESPONSIVE_LESS = ./less/responsive.less
BOOTSTRAP_LESS = ./lib/bootstrap.less
LESS_COMPRESSOR ?= `which lessc` LESS_COMPRESSOR ?= `which lessc`
UGLIFY_JS ?= `which uglifyjs`
WATCHR ?= `which watchr` WATCHR ?= `which watchr`
build: #
@@if test ! -z ${LESS_COMPRESSOR}; then \ # BUILD DOCS
sed -e 's/@VERSION/'"v${VERSION}"'/' -e 's/@DATE/'"${DATE}"'/' <${BOOTSTRAP_LESS} >${BOOTSTRAP_LESS}.tmp; \ #
lessc ${BOOTSTRAP_LESS}.tmp > ${BOOTSTRAP}; \
lessc ${BOOTSTRAP_LESS}.tmp > ${BOOTSTRAP_MIN} --compress; \ docs: bootstrap
rm -f ${BOOTSTRAP_LESS}.tmp; \ zip -r docs/assets/bootstrap.zip bootstrap
echo "Bootstrap successfully built! - `date`"; \ rm -r bootstrap
else \ lessc ${BOOTSTRAP_LESS} > ${BOOTSTRAP}
echo "You must have the LESS compiler installed in order to build Bootstrap."; \ lessc ${BOOTSTRAP_RESPONSIVE_LESS} > ${BOOTSTRAP_RESPONSIVE}
echo "You can install it by running: npm install less -g"; \ node docs/build
fi cp img/* docs/assets/img/
js/min: #
@@if test ! -z ${UGLIFY_JS}; then \ # BUILD SIMPLE BOOTSTRAP DIRECTORY
mkdir -p js/min; \ # lessc & uglifyjs are required
uglifyjs -o js/min/bootstrap-alerts.min.js js/bootstrap-alerts.js;\ #
uglifyjs -o js/min/bootstrap-buttons.min.js js/bootstrap-buttons.js;\
uglifyjs -o js/min/bootstrap-dropdown.min.js js/bootstrap-dropdown.js;\ bootstrap:
uglifyjs -o js/min/bootstrap-modal.min.js js/bootstrap-modal.js;\ mkdir -p bootstrap/img
uglifyjs -o js/min/bootstrap-popover.min.js js/bootstrap-popover.js;\ mkdir -p bootstrap/css
uglifyjs -o js/min/bootstrap-scrollspy.min.js js/bootstrap-scrollspy.js;\ mkdir -p bootstrap/js
uglifyjs -o js/min/bootstrap-tabs.min.js js/bootstrap-tabs.js;\ cp img/* bootstrap/img/
uglifyjs -o js/min/bootstrap-twipsy.min.js js/bootstrap-twipsy.js;\ lessc ${BOOTSTRAP_LESS} > bootstrap/css/bootstrap.css
else \ lessc --compress ${BOOTSTRAP_LESS} > bootstrap/css/bootstrap.min.css
echo "You must have the UGLIFYJS minifier installed in order to minify Bootstrap's js."; \ lessc ${BOOTSTRAP_RESPONSIVE_LESS} > bootstrap/css/bootstrap.responsive
echo "You can install it by running: npm install uglify-js -g"; \ lessc --compress ${BOOTSTRAP_RESPONSIVE_LESS} > bootstrap/css/bootstrap.min.responsive
fi cat js/bootstrap-transition.js js/bootstrap-alert.js js/bootstrap-button.js js/bootstrap-carousel.js js/bootstrap-collapse.js js/bootstrap-dropdown.js js/bootstrap-modal.js js/bootstrap-tooltip.js js/bootstrap-popover.js js/bootstrap-scrollspy.js js/bootstrap-tab.js js/bootstrap-typeahead.js > bootstrap/js/bootstrap.js
uglifyjs -nc bootstrap/js/bootstrap.js > bootstrap/js/bootstrap.min.js
#
# WATCH LESS FILES
#
watch: watch:
@@if test ! -z ${WATCHR}; then \ echo "Watching less files..."; \
echo "Watching less files..."; \ watchr -e "watch('less/.*\.less') { system 'make' }"
watchr -e "watch('lib/.*\.less') { system 'make' }"; \
else \
echo "You must have the watchr installed in order to watch Bootstrap less files."; \ .PHONY: dist docs watch
echo "You can install it by running: gem install watchr"; \ \ No newline at end of file
fi
.PHONY: build watch
\ No newline at end of file
...@@ -6,27 +6,6 @@ Bootstrap is Twitter's toolkit for kickstarting CSS for websites, apps, and more ...@@ -6,27 +6,6 @@ Bootstrap is Twitter's toolkit for kickstarting CSS for websites, apps, and more
To get started -- checkout http://twitter.github.com/bootstrap! To get started -- checkout http://twitter.github.com/bootstrap!
Usage
-----
You can use Twitter Bootstrap in one of two ways: just drop the compiled CSS into any new project and start cranking, or run LESS on your site and compile on the fly like a boss.
Here's what the LESS version looks like:
``` html
<link rel="stylesheet/less" type="text/css" href="lib/bootstrap.less">
<script src="less.js" type="text/javascript"></script>
```
Or if you prefer, the standard css way:
``` html
<link rel="stylesheet" type="text/css" href="bootstrap.css">
```
For more info, refer to the docs!
Versioning Versioning
---------- ----------
...@@ -72,10 +51,10 @@ http://groups.google.com/group/twitter-bootstrap ...@@ -72,10 +51,10 @@ http://groups.google.com/group/twitter-bootstrap
Developers Developers
---------- ----------
We have included a makefile with convenience methods for working with the bootstrap library. We have included a makefile with convenience methods for working with the Bootstrap library.
+ **build** - `make build` + **build** - `make`
This will run the less compiler on the bootstrap lib and generate a bootstrap.css and bootstrap.min.css file. This will run the less compiler on the bootstrap lib and regenerate the docs dir.
The lessc compiler is required for this command to run. The lessc compiler is required for this command to run.
+ **watch** - `make watch` + **watch** - `make watch`
...@@ -97,9 +76,19 @@ Authors ...@@ -97,9 +76,19 @@ Authors
+ http://github.com/fat + http://github.com/fat
License Copyright and license
--------------------- ---------------------
Copyright 2011 Twitter, Inc. Copyright 2012 Twitter, Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this work except in compliance with the License.
You may obtain a copy of the License in the LICENSE file, or at:
http://www.apache.org/licenses/LICENSE-2.0
Licensed under the Apache License, Version 2.0: http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software
\ No newline at end of file distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
docs/assets/img/bird.png

4.33 KB | W: | H:

docs/assets/img/bird.png

3.02 KB | W: | H:

docs/assets/img/bird.png
docs/assets/img/bird.png
docs/assets/img/bird.png
docs/assets/img/bird.png
  • 2-up
  • Swipe
  • Onion skin
docs/assets/img/browsers.png

21.3 KB | W: | H:

docs/assets/img/browsers.png

19.3 KB | W: | H:

docs/assets/img/browsers.png
docs/assets/img/browsers.png
docs/assets/img/browsers.png
docs/assets/img/browsers.png
  • 2-up
  • Swipe
  • Onion skin
docs/assets/img/example-diagram-01.png

1.6 KB | W: | H:

docs/assets/img/example-diagram-01.png

486 Bytes | W: | H:

docs/assets/img/example-diagram-01.png
docs/assets/img/example-diagram-01.png
docs/assets/img/example-diagram-01.png
docs/assets/img/example-diagram-01.png
  • 2-up
  • Swipe
  • Onion skin
docs/assets/img/example-diagram-02.png

1.7 KB | W: | H:

docs/assets/img/example-diagram-02.png

564 Bytes | W: | H:

docs/assets/img/example-diagram-02.png
docs/assets/img/example-diagram-02.png
docs/assets/img/example-diagram-02.png
docs/assets/img/example-diagram-02.png
  • 2-up
  • Swipe
  • Onion skin
docs/assets/img/example-diagram-03.png

1.39 KB | W: | H:

docs/assets/img/example-diagram-03.png

369 Bytes | W: | H:

docs/assets/img/example-diagram-03.png
docs/assets/img/example-diagram-03.png
docs/assets/img/example-diagram-03.png
docs/assets/img/example-diagram-03.png
  • 2-up
  • Swipe
  • Onion skin
$(document).ready(function(){ // NOTICE!! DO NOT USE ANY OF THIS JAVASCRIPT
// IT'S ALL JUST JUNK FOR OUR DOCS!
// ++++++++++++++++++++++++++++++++++++++++++
// table sort example !function ($) {
// ==================
$("#sortTableExample").tablesorter( { sortList: [[ 1, 0 ]] } ) $(function(){
// Disable certain links in docs
$('section [href^=#]').click(function (e) {
e.preventDefault()
})
// add on logic // make code pretty
// ============ window.prettyPrint && prettyPrint()
$('.add-on :checkbox').click(function () { // add-ons
if ($(this).attr('checked')) { $('.add-on :checkbox').on('click', function () {
$(this).parents('.add-on').addClass('active') var $this = $(this)
} else { , method = $this.attr('checked') ? 'addClass' : 'removeClass'
$(this).parents('.add-on').removeClass('active') $(this).parents('.add-on')[method]('active')
})
// position static twipsies for components page
if ($(".twipsies a").length) {
$(window).on('load resize', function () {
$(".twipsies a").each(function () {
$(this)
.tooltip({
placement: $(this).attr('title')
, trigger: 'manual'
})
.tooltip('show')
})
})
} }
})
// add tipsies to grid for scaffolding
if ($('#grid-system').length) {
$('#grid-system').tooltip({
selector: '.show-grid > div'
, title: function () { return $(this).width() + 'px' }
})
}
// Disable certain links in docs // fix sub nav on scroll
// ============================= var $win = $(window)
// Please do not carry these styles over to your projects, it's merely here to prevent button clicks form taking you away from your spot on page , $nav = $('.subnav')
, navTop = $('.subnav').length && $('.subnav').offset().top - 40
, isFixed = 0
$('ul.tabs a, ul.pills a, .pagination a, .well .btn, .actions .btn, .alert-message .btn, a.close').click(function (e) { processScroll()
e.preventDefault()
}) $win.on('scroll', processScroll)
function processScroll() {
var i, scrollTop = $win.scrollTop()
if (scrollTop >= navTop && !isFixed) {
isFixed = 1
$nav.addClass('subnav-fixed')
} else if (scrollTop <= navTop && isFixed) {
isFixed = 0
$nav.removeClass('subnav-fixed')
}
}
// Copy code blocks in docs // tooltip demo
$(".copy-code").focus(function () { $('.tooltip-demo.well').tooltip({
var el = this; selector: "a[rel=tooltip]"
// push select to event loop for chrome :{o })
setTimeout(function () { $(el).select(); }, 0);
}); $('.tooltip-test').tooltip()
$('.popover-test').popover()
// POSITION STATIC TWIPSIES // popover demo
// ======================== $("a[rel=popover]")
.popover()
$(window).bind( 'load resize', function () { .click(function(e) {
$(".twipsies a").each(function () { e.preventDefault()
$(this)
.twipsy({
live: false
, placement: $(this).attr('title')
, trigger: 'manual'
, offset: 2
})
.twipsy('show')
}) })
// button state demo
$('#fat-btn')
.click(function () {
var btn = $(this)
btn.button('loading')
setTimeout(function () {
btn.button('reset')
}, 3000)
})
// carousel demo
$('#myCarousel').carousel()
// javascript build logic
var inputsComponent = $("#components.download input")
, inputsPlugin = $("#plugins.download input")
, inputsVariables = $("#variables.download input")
// toggle all plugin checkboxes
$('#components.download .toggle-all').on('click', function (e) {
e.preventDefault()
inputsComponent.attr('checked', !inputsComponent.is(':checked'))
})
$('#plugins.download .toggle-all').on('click', function (e) {
e.preventDefault()
inputsPlugin.attr('checked', !inputsPlugin.is(':checked'))
})
$('#variables.download .toggle-all').on('click', function (e) {
e.preventDefault()
inputsVariables.val('')
})
// request built javascript
$('.download-btn').on('click', function () {
var css = $("#components.download input:checked")
.map(function () { return this.value })
.toArray()
, js = $("#plugins.download input:checked")
.map(function () { return this.value })
.toArray()
, vars = {}
, img = ['glyphicons-halflings.png', 'glyphicons-halflings-white.png']
$("#variables.download input")
.each(function () {
$(this).val() && (vars[ $(this).prev().text() ] = $(this).val())
})
$.ajax({
type: 'POST'
, url: 'http://bootstrap.herokuapp.com'
, dataType: 'jsonpi'
, params: {
branch: '2.0-wip'
, js: js
, css: css
, vars: vars
, img: img
}
})
})
}) })
});
// Modified from the original jsonpi https://github.com/benvinegar/jquery-jsonpi
$.ajaxTransport('jsonpi', function(opts, originalOptions, jqXHR) {
var url = opts.url;
return {
send: function(_, completeCallback) {
var name = 'jQuery_iframe_' + jQuery.now()
, iframe, form
iframe = $('<iframe>')
.attr('name', name)
.appendTo('head')
form = $('<form>')
.attr('method', opts.type) // GET or POST
.attr('action', url)
.attr('target', name)
$.each(opts.params, function(k, v) {
$('<input>')
.attr('type', 'hidden')
.attr('name', k)
.attr('value', typeof v == 'string' ? v : JSON.stringify(v))
.appendTo(form)
})
form.appendTo('body').submit()
}
}
})
}(window.jQuery)
\ No newline at end of file
...@@ -2,93 +2,29 @@ ...@@ -2,93 +2,29 @@
.lit { color: #195f91; } .lit { color: #195f91; }
.pun, .opn, .clo { color: #93a1a1; } .pun, .opn, .clo { color: #93a1a1; }
.fun { color: #dc322f; } .fun { color: #dc322f; }
.str, .atv { color: #268bd2; } .str, .atv { color: #D14; }
.kwd, .tag { color: #195f91; } .kwd, .linenums .tag { color: #1e347b; }
.typ, .atn, .dec, .var { color: #CB4B16; } .typ, .atn, .dec, .var { color: teal; }
.pln { color: #93a1a1; } .pln { color: #48484c; }
.prettyprint { .prettyprint {
background-color: #fefbf3; padding: 8px;
padding: 9px; background-color: #f7f7f9;
border: 1px solid rgba(0,0,0,.2); border: 1px solid #e1e1e8;
-webkit-box-shadow: 0 1px 2px rgba(0,0,0,.1); }
-moz-box-shadow: 0 1px 2px rgba(0,0,0,.1); .prettyprint.linenums {
box-shadow: 0 1px 2px rgba(0,0,0,.1); -webkit-box-shadow: inset 40px 0 0 #fbfbfc, inset 41px 0 0 #ececf0;
-moz-box-shadow: inset 40px 0 0 #fbfbfc, inset 41px 0 0 #ececf0;
box-shadow: inset 40px 0 0 #fbfbfc, inset 41px 0 0 #ececf0;
} }
/* Specify class=linenums on a pre to get line numbering */ /* Specify class=linenums on a pre to get line numbering */
ol.linenums { ol.linenums {
margin: 0 0 0 40px; margin: 0 0 0 33px; /* IE indents via margin-left */
} }
/* IE indents via margin-left */
ol.linenums li { ol.linenums li {
padding: 0 5px; padding-left: 12px;
color: rgba(0,0,0,.15); color: #bebec5;
line-height: 20px; line-height: 18px;
-webkit-border-radius: 2px; text-shadow: 0 1px 0 #fff;
-moz-border-radius: 2px; }
border-radius: 2px; \ No newline at end of file
}
/* Alternate shading for lines */
li.L1, li.L3, li.L5, li.L7, li.L9 { }
/*
$base03: #002b36;
$base02: #073642;
$base01: #586e75;
$base00: #657b83;
$base0: #839496;
$base1: #93a1a1;
$base2: #eee8d5;
$base3: #fdf6e3;
$yellow: #b58900;
$orange: #cb4b16;
$red: #dc322f;
$magenta: #d33682;
$violet: #6c71c4;
$blue: #268bd2;
$cyan: #2aa198;
$green: #859900;
*/
/*
#1d1f21 Background
#282a2e Current Line
#373b41 Selection
#c5c8c6 Foreground
#969896 Comment
#cc6666 Red
#de935f Orange
#f0c674 Yellow
#b5bd68 Green
#8abeb7 Aqua
#81a2be Blue
#b294bb Purple
*/
/* DARK THEME */
/* ---------- */
.prettyprint-dark {
background-color: #1d1f21;
border: 0;
padding: 10px;
}
.prettyprint-dark .linenums li {
color: #444;
}
.prettyprint-dark .linenums li:hover {
background-color: #282a2e;
}
/* tags in html */
.prettyprint-dark .kwd,
.prettyprint-dark .tag { color: #cc6666; }
/* html attr */
.prettyprint-dark .typ,
.prettyprint-dark .atn,
.prettyprint-dark .dec,
.prettyprint-dark .var { color: #de935f; }
/* html attr values */
.prettyprint-dark .str,
.prettyprint-dark .atv { color: #b5bd68; }
This diff is collapsed.
#!/usr/bin/env node
var hogan = require('hogan.js')
, fs = require('fs')
var layout, pages
// compile layout template
layout = fs.readFileSync(__dirname + '/../templates/layout.mustache', 'utf-8')
layout = hogan.compile(layout, { sectionTags: [{o:'_i', c:'i'}] })
// retrieve pages
pages = fs.readdirSync(__dirname + '/../templates/pages')
// iterate over pages
pages.forEach(function (name) {
if (!name.match(/\.mustache$/)) return
var page = fs.readFileSync(__dirname + '/../templates/pages/' + name, 'utf-8')
, context = {}
context[name.replace(/\.mustache$/, '')] = 'active'
context._i = true
page = hogan.compile(page, { sectionTags: [{o:'_i', c:'i'}] })
page = layout.render(context, {
body: page
})
fs.writeFileSync(__dirname + '/../' + name.replace(/mustache$/, 'html'), page, 'utf-8')
})
\ No newline at end of file
#!/usr/bin/env node
/*
* Copyright 2011 Twitter, Inc.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
var hogan = require('../lib/hogan.js'),
path = require('path'),
fs = require('fs');
var specials = ['/', '.', '*', '+', '?', '|','(', ')', '[', ']', '{', '}', '\\'],
specialsRegExp = new RegExp('(\\' + specials.join('|\\') + ')', 'g'),
templates;
// Escape special regexp characters
function esc(text) {
return text.replace(specialsRegExp, '\\$1');
}
// Check for dirs and correct ext (<3 for windows)
function extractFiles(args) {
var usage = 'USAGE: hulk ./templates/*.mustaches\n' +
'NOTE: hulk supports the "*" wildcard and allows you to target specific extensions too',
files = [];
if (!args.length) {
console.log(usage);
process.exit(-1);
}
args.forEach(function (arg) {
if (/\*/.test(arg)) {
arg = arg.split('*');
return files = files.concat(
fs.readdirSync(arg[0] || '.')
.map(function (f) {
return new RegExp(esc(arg[1]) + '$').test(f) && path.join(arg[0], f);
})
.filter(function (f) {
return f;
})
);
}
files.push(arg);
})
return files;
}
// Remove utf-8 byte order mark, http://en.wikipedia.org/wiki/Byte_order_mark
function removeByteOrderMark(text) {
if (text.charCodeAt(0) === 0xfeff) {
return text.substring(1);
}
return text;
}
// Write a template foreach file that matches template extension
templates = extractFiles(process.argv.slice(2))
.map(function (file) {
var openedFile = fs.readFileSync(file, 'utf-8'), name;
if (!openedFile) return;
name = file.replace(/\..*$/, '');
openedFile = removeByteOrderMark(openedFile.trim());
return 'templates.' + name + ' = new Hogan.Template(' + hogan.compile(openedFile, { asString: 1 }) + ');';
})
.filter(function (t) {
return t;
});
// Output templates
if (!templates.length) return;
console.log('var templates = {};');
console.log(templates.join('\n'));
\ No newline at end of file
[submodule "test/spec"]
path = test/spec
url = https://github.com/mustache/spec.git
This diff is collapsed.
REPO = git@github.com:twitter/hogan.js.git
BUILD := build
VERSION = ${shell node -e 'var s = JSON.parse(require("fs").readFileSync("package.json").toString()).version; console.log(s.substring(0, s.indexOf("-")));'}
#
# Run command line tests
#
test:
@ node test/index.js
#
# Run Mustache spec tests
#
spec:
@ node test/spec.js
#
# Run benchmark
#
benchmark:
@ node benchmark/console/index.js
clean:
@ rm -rf dist/*
#
# Make a new version of Hogan from the current dev version.
#
release: clean
@ echo "Creating a new version of Hogan."
@ mkdir -p dist/nodejs
@ cp -R lib dist/nodejs/lib
@ node tools/release.js
@ mkdir -p web/builds/$(VERSION)
@ cp dist/*.* web/builds/$(VERSION)/.
#
# Make the gh-pages website
#
# This target builds the hogan.js github website using hogan.js.
#
# cd into build/gh-pages to check in the new site.
#
GH_PAGES = $(BUILD)/gh-pages
web: | pages
@cp -R web/* $(GH_PAGES)
@@ node tools/web_templates.js
@echo
@echo "Website built in $(GH_PAGES)."
#
# Checkout the gh-pages branch.
#
pages: | $(BUILD)
@if [ ! -d "$(GH_PAGES)" ]; then \
git clone -b gh-pages $(REPO) $(GH_PAGES); \
rm -rf $(GH_PAGES)/*; \
fi;
@mkdir -p $(GH_PAGES)/images
$(BUILD):
mkdir -p $(BUILD)
.PHONY: test spec benchmark web release
## Hogan.js - A mustache compiler.
[Hogan.js](http://twitter.github.com/hogan.js/) is a compiler for the
[Mustache](http://mustache.github.com/) templating language. For information
on Mustache, see the [manpage](http://mustache.github.com/mustache.5.html) and
the [spec](https://github.com/mustache/spec).
## Basics
Hogan compiles templates to HoganTemplate objects, which have a render method.
```js
var data = {
screenName: "dhg",
};
var template = Hogan.compile("Follow @{{screenName}}.");
var output = template.render(data);
// prints "Follow @dhg."
console.log(output);
```
## Features
Hogan is fast--try it on your workload.
Hogan has separate scanning, parsing and code generation phases. This way it's
possible to add new features without touching the scanner at all, and many
different code generation techniques can be tried without changing the parser.
Hogan exposes scan and parse methods. These can be useful for
pre-processing templates on the server.
```js
var text = "{{^check}}{{i18n}}No{{/i18n}}{{/check}}";
text += "{{#check}}{{i18n}}Yes{{/i18n}}{{/check}}";
var tree = Hogan.parse(Hogan.scan(text));
// outputs "# check"
console.log(tree[0].tag + " " + tree[0].name);
// outputs "Yes"
console.log(tree[1].nodes[0].nodes[0]);
```
It's also possible to use HoganTemplate objects without the Hogan compiler
present. That means you can pre-compile your templates on the server, and
avoid shipping the compiler. However, the optional lambda features from the
Mustache spec do require the compiler to be present.
## Why Hogan.js?
Why another templating library?
Hogan.js was written to meet three templating library requirements: good
performance, standalone template objects, and a parser API.
## Issues
Have a bug? Please create an issue here on GitHub!
https://github.com/twitter/hogan.js/issues
## Versioning
For transparency and insight into our release cycle, releases will be numbered with the follow format:
`<major>.<minor>.<patch>`
And constructed with the following guidelines:
* Breaking backwards compatibility bumps the major
* New additions without breaking backwards compatibility bumps the minor
* Bug fixes and misc changes bump the patch
For more information on semantic versioning, please visit http://semver.org/.
## Authors
**Robert Sayre**
+ http://github.com/sayrer
**Jacob Thornton**
+ http://github.com/fat
## License
Copyright 2011 Twitter, Inc.
Licensed under the Apache License, Version 2.0: http://www.apache.org/licenses/LICENSE-2.0
\ No newline at end of file
#!/usr/bin/env node
/*
* Copyright 2011 Twitter, Inc.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
var hogan = require('../lib/hogan.js'),
path = require('path'),
fs = require('fs');
var specials = ['/', '.', '*', '+', '?', '|','(', ')', '[', ']', '{', '}', '\\'],
specialsRegExp = new RegExp('(\\' + specials.join('|\\') + ')', 'g'),
templates;
// Escape special regexp characters
function esc(text) {
return text.replace(specialsRegExp, '\\$1');
}
// Check for dirs and correct ext (<3 for windows)
function extractFiles(args) {
var usage = 'USAGE: hulk ./templates/*.mustaches\n' +
'NOTE: hulk supports the "*" wildcard and allows you to target specific extensions too',
files = [];
if (!args.length) {
console.log(usage);
process.exit(-1);
}
args.forEach(function (arg) {
if (/\*/.test(arg)) {
arg = arg.split('*');
return files = files.concat(
fs.readdirSync(arg[0] || '.')
.map(function (f) {
return new RegExp(esc(arg[1]) + '$').test(f) && path.join(arg[0], f);
})
.filter(function (f) {
return f;
})
);
}
files.push(arg);
})
return files;
}
// Remove utf-8 byte order mark, http://en.wikipedia.org/wiki/Byte_order_mark
function removeByteOrderMark(text) {
if (text.charCodeAt(0) === 0xfeff) {
return text.substring(1);
}
return text;
}
// Write a template foreach file that matches template extension
templates = extractFiles(process.argv.slice(2))
.map(function (file) {
var openedFile = fs.readFileSync(file, 'utf-8'), name;
if (!openedFile) return;
name = file.replace(/\..*$/, '');
openedFile = removeByteOrderMark(openedFile.trim());
return 'templates.' + name + ' = new Hogan.Template(' + hogan.compile(openedFile, { asString: 1 }) + ');';
})
.filter(function (t) {
return t;
});
// Output templates
if (!templates.length) return;
console.log('var templates = {};');
console.log(templates.join('\n'));
\ No newline at end of file
This diff is collapsed.
/*
* Copyright 2011 Twitter, Inc.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
// This file is for use with Node.js. See dist/ for browser files.
var Hogan = require('./compiler');
Hogan.Template = require('./template').Template;
module.exports = Hogan;
\ No newline at end of file
/*
* Copyright 2011 Twitter, Inc.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
var Hogan = {};
(function (Hogan) {
Hogan.Template = function constructor(renderFunc, text, compiler) {
if (renderFunc) {
this.r = renderFunc;
}
this.c = compiler;
this.text = text || '';
}
Hogan.Template.prototype = {
// render: replaced by generated code.
r: function (context, partials, indent) { return ''; },
// variable escaping
v: hoganEscape,
render: function render(context, partials, indent) {
return this.ri([context], partials || {}, indent);
},
// render internal -- a hook for overrides that catches partials too
ri: function (context, partials, indent) {
return this.r(context, partials, indent);
},
// tries to find a partial in the curent scope and render it
rp: function(name, context, partials, indent) {
var partial = partials[name];
if (!partial) {
return '';
}
if (this.c && typeof partial == 'string') {
partial = this.c.compile(partial);
}
return partial.ri(context, partials, indent);
},
// render a section
rs: function(context, partials, section) {
var buf = '',
tail = context[context.length - 1];
if (!isArray(tail)) {
return buf = section(context, partials);
}
for (var i = 0; i < tail.length; i++) {
context.push(tail[i]);
buf += section(context, partials);
context.pop();
}
return buf;
},
// maybe start a section
s: function(val, ctx, partials, inverted, start, end, tags) {
var pass;
if (isArray(val) && val.length === 0) {
return false;
}
if (typeof val == 'function') {
val = this.ls(val, ctx, partials, inverted, start, end, tags);
}
pass = (val === '') || !!val;
if (!inverted && pass && ctx) {
ctx.push((typeof val == 'object') ? val : ctx[ctx.length - 1]);
}
return pass;
},
// find values with dotted names
d: function(key, ctx, partials, returnFound) {
var names = key.split('.'),
val = this.f(names[0], ctx, partials, returnFound),
cx = null;
if (key === '.' && isArray(ctx[ctx.length - 2])) {
return ctx[ctx.length - 1];
}
for (var i = 1; i < names.length; i++) {
if (val && typeof val == 'object' && names[i] in val) {
cx = val;
val = val[names[i]];
} else {
val = '';
}
}
if (returnFound && !val) {
return false;
}
if (!returnFound && typeof val == 'function') {
ctx.push(cx);
val = this.lv(val, ctx, partials);
ctx.pop();
}
return val;
},
// find values with normal names
f: function(key, ctx, partials, returnFound) {
var val = false,
v = null,
found = false;
for (var i = ctx.length - 1; i >= 0; i--) {
v = ctx[i];
if (v && typeof v == 'object' && key in v) {
val = v[key];
found = true;
break;
}
}
if (!found) {
return (returnFound) ? false : "";
}
if (!returnFound && typeof val == 'function') {
val = this.lv(val, ctx, partials);
}
return val;
},
// higher order templates
ho: function(val, cx, partials, text, tags) {
var compiler = this.c;
var t = val.call(cx, text, function(t) {
return compiler.compile(t, {delimiters: tags}).render(cx, partials);
});
var s = compiler.compile(t.toString(), {delimiters: tags}).render(cx, partials);
this.b = s;
return false;
},
// higher order template result buffer
b: '',
// lambda replace section
ls: function(val, ctx, partials, inverted, start, end, tags) {
var cx = ctx[ctx.length - 1],
t = null;
if (!inverted && this.c && val.length > 0) {
return this.ho(val, cx, partials, this.text.substring(start, end), tags);
}
t = val.call(cx);
if (typeof t == 'function') {
if (inverted) {
return true;
} else if (this.c) {
return this.ho(t, cx, partials, this.text.substring(start, end), tags);
}
}
return t;
},
// lambda replace variable
lv: function(val, ctx, partials) {
var cx = ctx[ctx.length - 1];
var result = val.call(cx);
if (typeof result == 'function') {
result = result.call(cx);
}
result = result.toString();
if (this.c && ~result.indexOf("{{")) {
return this.c.compile(result).render(cx, partials);
}
return result;
}
};
var rAmp = /&/g,
rLt = /</g,
rGt = />/g,
rApos =/\'/g,
rQuot = /\"/g,
hChars =/[&<>\"\']/;
function hoganEscape(str) {
str = String((str === null || str === undefined) ? '' : str);
return hChars.test(str) ?
str
.replace(rAmp,'&amp;')
.replace(rLt,'&lt;')
.replace(rGt,'&gt;')
.replace(rApos,'&#39;')
.replace(rQuot, '&quot;') :
str;
}
var isArray = Array.isArray || function(a) {
return Object.prototype.toString.call(a) === '[object Array]';
};
})(typeof exports !== 'undefined' ? exports : Hogan);
This diff is collapsed.
<ul>
<li></li>
<li></li>
<li></li>
<li></li>
<li></li>
<li></li>
</ul>
\ No newline at end of file
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
{"__ATTN__":"Do not edit this file; changes belong in the appropriate YAML file.","overview":"Comment tags represent content that should never appear in the resulting\noutput.\n\nThe tag's content may contain any substring (including newlines) EXCEPT the\nclosing delimiter.\n\nComment tags SHOULD be treated as standalone when appropriate.\n","tests":[{"name":"Inline","data":{},"expected":"1234567890","template":"12345{{! Comment Block! }}67890","desc":"Comment blocks should be removed from the template."},{"name":"Multiline","data":{},"expected":"1234567890\n","template":"12345{{!\n This is a\n multi-line comment...\n}}67890\n","desc":"Multiline comments should be permitted."},{"name":"Standalone","data":{},"expected":"Begin.\nEnd.\n","template":"Begin.\n{{! Comment Block! }}\nEnd.\n","desc":"All standalone comment lines should be removed."},{"name":"Indented Standalone","data":{},"expected":"Begin.\nEnd.\n","template":"Begin.\n {{! Indented Comment Block! }}\nEnd.\n","desc":"All standalone comment lines should be removed."},{"name":"Standalone Line Endings","data":{},"expected":"|\r\n|","template":"|\r\n{{! Standalone Comment }}\r\n|","desc":"\"\\r\\n\" should be considered a newline for standalone tags."},{"name":"Standalone Without Previous Line","data":{},"expected":"!","template":" {{! I'm Still Standalone }}\n!","desc":"Standalone tags should not require a newline to precede them."},{"name":"Standalone Without Newline","data":{},"expected":"!\n","template":"!\n {{! I'm Still Standalone }}","desc":"Standalone tags should not require a newline to follow them."},{"name":"Multiline Standalone","data":{},"expected":"Begin.\nEnd.\n","template":"Begin.\n{{!\nSomething's going on here...\n}}\nEnd.\n","desc":"All standalone comment lines should be removed."},{"name":"Indented Multiline Standalone","data":{},"expected":"Begin.\nEnd.\n","template":"Begin.\n {{!\n Something's going on here...\n }}\nEnd.\n","desc":"All standalone comment lines should be removed."},{"name":"Indented Inline","data":{},"expected":" 12 \n","template":" 12 {{! 34 }}\n","desc":"Inline comments should not strip whitespace"},{"name":"Surrounding Whitespace","data":{},"expected":"12345 67890","template":"12345 {{! Comment Block! }} 67890","desc":"Comment removal should preserve surrounding whitespace."}]}
\ No newline at end of file
This diff is collapsed.
{"__ATTN__":"Do not edit this file; changes belong in the appropriate YAML file.","overview":"Set Delimiter tags are used to change the tag delimiters for all content\nfollowing the tag in the current compilation unit.\n\nThe tag's content MUST be any two non-whitespace sequences (separated by\nwhitespace) EXCEPT an equals sign ('=') followed by the current closing\ndelimiter.\n\nSet Delimiter tags SHOULD be treated as standalone when appropriate.\n","tests":[{"name":"Pair Behavior","data":{"text":"Hey!"},"expected":"(Hey!)","template":"{{=<% %>=}}(<%text%>)","desc":"The equals sign (used on both sides) should permit delimiter changes."},{"name":"Special Characters","data":{"text":"It worked!"},"expected":"(It worked!)","template":"({{=[ ]=}}[text])","desc":"Characters with special meaning regexen should be valid delimiters."},{"name":"Sections","data":{"section":true,"data":"I got interpolated."},"expected":"[\n I got interpolated.\n |data|\n\n {{data}}\n I got interpolated.\n]\n","template":"[\n{{#section}}\n {{data}}\n |data|\n{{/section}}\n\n{{= | | =}}\n|#section|\n {{data}}\n |data|\n|/section|\n]\n","desc":"Delimiters set outside sections should persist."},{"name":"Inverted Sections","data":{"section":false,"data":"I got interpolated."},"expected":"[\n I got interpolated.\n |data|\n\n {{data}}\n I got interpolated.\n]\n","template":"[\n{{^section}}\n {{data}}\n |data|\n{{/section}}\n\n{{= | | =}}\n|^section|\n {{data}}\n |data|\n|/section|\n]\n","desc":"Delimiters set outside inverted sections should persist."},{"name":"Partial Inheritence","data":{"value":"yes"},"expected":"[ .yes. ]\n[ .yes. ]\n","template":"[ {{>include}} ]\n{{= | | =}}\n[ |>include| ]\n","desc":"Delimiters set in a parent template should not affect a partial.","partials":{"include":".{{value}}."}},{"name":"Post-Partial Behavior","data":{"value":"yes"},"expected":"[ .yes. .yes. ]\n[ .yes. .|value|. ]\n","template":"[ {{>include}} ]\n[ .{{value}}. .|value|. ]\n","desc":"Delimiters set in a partial should not affect the parent template.","partials":{"include":".{{value}}. {{= | | =}} .|value|."}},{"name":"Surrounding Whitespace","data":{},"expected":"| |","template":"| {{=@ @=}} |","desc":"Surrounding whitespace should be left untouched."},{"name":"Outlying Whitespace (Inline)","data":{},"expected":" | \n","template":" | {{=@ @=}}\n","desc":"Whitespace should be left untouched."},{"name":"Standalone Tag","data":{},"expected":"Begin.\nEnd.\n","template":"Begin.\n{{=@ @=}}\nEnd.\n","desc":"Standalone lines should be removed from the template."},{"name":"Indented Standalone Tag","data":{},"expected":"Begin.\nEnd.\n","template":"Begin.\n {{=@ @=}}\nEnd.\n","desc":"Indented standalone lines should be removed from the template."},{"name":"Standalone Line Endings","data":{},"expected":"|\r\n|","template":"|\r\n{{= @ @ =}}\r\n|","desc":"\"\\r\\n\" should be considered a newline for standalone tags."},{"name":"Standalone Without Previous Line","data":{},"expected":"=","template":" {{=@ @=}}\n=","desc":"Standalone tags should not require a newline to precede them."},{"name":"Standalone Without Newline","data":{},"expected":"=\n","template":"=\n {{=@ @=}}","desc":"Standalone tags should not require a newline to follow them."},{"name":"Pair with Padding","data":{},"expected":"||","template":"|{{= @ @ =}}|","desc":"Superfluous in-tag whitespace should be ignored."}]}
\ No newline at end of file
{"__ATTN__":"Do not edit this file; changes belong in the appropriate YAML file.","overview":"Interpolation tags are used to integrate dynamic content into the template.\n\nThe tag's content MUST be a non-whitespace character sequence NOT containing\nthe current closing delimiter.\n\nThis tag's content names the data to replace the tag. A single period (`.`)\nindicates that the item currently sitting atop the context stack should be\nused; otherwise, name resolution is as follows:\n 1) Split the name on periods; the first part is the name to resolve, any\n remaining parts should be retained.\n 2) Walk the context stack from top to bottom, finding the first context\n that is a) a hash containing the name as a key OR b) an object responding\n to a method with the given name.\n 3) If the context is a hash, the data is the value associated with the\n name.\n 4) If the context is an object, the data is the value returned by the\n method with the given name.\n 5) If any name parts were retained in step 1, each should be resolved\n against a context stack containing only the result from the former\n resolution. If any part fails resolution, the result should be considered\n falsey, and should interpolate as the empty string.\nData should be coerced into a string (and escaped, if appropriate) before\ninterpolation.\n\nThe Interpolation tags MUST NOT be treated as standalone.\n","tests":[{"name":"No Interpolation","data":{},"expected":"Hello from {Mustache}!\n","template":"Hello from {Mustache}!\n","desc":"Mustache-free templates should render as-is."},{"name":"Basic Interpolation","data":{"subject":"world"},"expected":"Hello, world!\n","template":"Hello, {{subject}}!\n","desc":"Unadorned tags should interpolate content into the template."},{"name":"HTML Escaping","data":{"forbidden":"& \" < >"},"expected":"These characters should be HTML escaped: &amp; &quot; &lt; &gt;\n","template":"These characters should be HTML escaped: {{forbidden}}\n","desc":"Basic interpolation should be HTML escaped."},{"name":"Triple Mustache","data":{"forbidden":"& \" < >"},"expected":"These characters should not be HTML escaped: & \" < >\n","template":"These characters should not be HTML escaped: {{{forbidden}}}\n","desc":"Triple mustaches should interpolate without HTML escaping."},{"name":"Ampersand","data":{"forbidden":"& \" < >"},"expected":"These characters should not be HTML escaped: & \" < >\n","template":"These characters should not be HTML escaped: {{&forbidden}}\n","desc":"Ampersand should interpolate without HTML escaping."},{"name":"Basic Integer Interpolation","data":{"mph":85},"expected":"\"85 miles an hour!\"","template":"\"{{mph}} miles an hour!\"","desc":"Integers should interpolate seamlessly."},{"name":"Triple Mustache Integer Interpolation","data":{"mph":85},"expected":"\"85 miles an hour!\"","template":"\"{{{mph}}} miles an hour!\"","desc":"Integers should interpolate seamlessly."},{"name":"Ampersand Integer Interpolation","data":{"mph":85},"expected":"\"85 miles an hour!\"","template":"\"{{&mph}} miles an hour!\"","desc":"Integers should interpolate seamlessly."},{"name":"Basic Decimal Interpolation","data":{"power":1.21},"expected":"\"1.21 jiggawatts!\"","template":"\"{{power}} jiggawatts!\"","desc":"Decimals should interpolate seamlessly with proper significance."},{"name":"Triple Mustache Decimal Interpolation","data":{"power":1.21},"expected":"\"1.21 jiggawatts!\"","template":"\"{{{power}}} jiggawatts!\"","desc":"Decimals should interpolate seamlessly with proper significance."},{"name":"Ampersand Decimal Interpolation","data":{"power":1.21},"expected":"\"1.21 jiggawatts!\"","template":"\"{{&power}} jiggawatts!\"","desc":"Decimals should interpolate seamlessly with proper significance."},{"name":"Basic Context Miss Interpolation","data":{},"expected":"I () be seen!","template":"I ({{cannot}}) be seen!","desc":"Failed context lookups should default to empty strings."},{"name":"Triple Mustache Context Miss Interpolation","data":{},"expected":"I () be seen!","template":"I ({{{cannot}}}) be seen!","desc":"Failed context lookups should default to empty strings."},{"name":"Ampersand Context Miss Interpolation","data":{},"expected":"I () be seen!","template":"I ({{&cannot}}) be seen!","desc":"Failed context lookups should default to empty strings."},{"name":"Dotted Names - Basic Interpolation","data":{"person":{"name":"Joe"}},"expected":"\"Joe\" == \"Joe\"","template":"\"{{person.name}}\" == \"{{#person}}{{name}}{{/person}}\"","desc":"Dotted names should be considered a form of shorthand for sections."},{"name":"Dotted Names - Triple Mustache Interpolation","data":{"person":{"name":"Joe"}},"expected":"\"Joe\" == \"Joe\"","template":"\"{{{person.name}}}\" == \"{{#person}}{{{name}}}{{/person}}\"","desc":"Dotted names should be considered a form of shorthand for sections."},{"name":"Dotted Names - Ampersand Interpolation","data":{"person":{"name":"Joe"}},"expected":"\"Joe\" == \"Joe\"","template":"\"{{&person.name}}\" == \"{{#person}}{{&name}}{{/person}}\"","desc":"Dotted names should be considered a form of shorthand for sections."},{"name":"Dotted Names - Arbitrary Depth","data":{"a":{"b":{"c":{"d":{"e":{"name":"Phil"}}}}}},"expected":"\"Phil\" == \"Phil\"","template":"\"{{a.b.c.d.e.name}}\" == \"Phil\"","desc":"Dotted names should be functional to any level of nesting."},{"name":"Dotted Names - Broken Chains","data":{"a":{}},"expected":"\"\" == \"\"","template":"\"{{a.b.c}}\" == \"\"","desc":"Any falsey value prior to the last part of the name should yield ''."},{"name":"Dotted Names - Broken Chain Resolution","data":{"a":{"b":{}},"c":{"name":"Jim"}},"expected":"\"\" == \"\"","template":"\"{{a.b.c.name}}\" == \"\"","desc":"Each part of a dotted name should resolve only against its parent."},{"name":"Dotted Names - Initial Resolution","data":{"a":{"b":{"c":{"d":{"e":{"name":"Phil"}}}}},"b":{"c":{"d":{"e":{"name":"Wrong"}}}}},"expected":"\"Phil\" == \"Phil\"","template":"\"{{#a}}{{b.c.d.e.name}}{{/a}}\" == \"Phil\"","desc":"The first part of a dotted name should resolve as any other name."},{"name":"Interpolation - Surrounding Whitespace","data":{"string":"---"},"expected":"| --- |","template":"| {{string}} |","desc":"Interpolation should not alter surrounding whitespace."},{"name":"Triple Mustache - Surrounding Whitespace","data":{"string":"---"},"expected":"| --- |","template":"| {{{string}}} |","desc":"Interpolation should not alter surrounding whitespace."},{"name":"Ampersand - Surrounding Whitespace","data":{"string":"---"},"expected":"| --- |","template":"| {{&string}} |","desc":"Interpolation should not alter surrounding whitespace."},{"name":"Interpolation - Standalone","data":{"string":"---"},"expected":" ---\n","template":" {{string}}\n","desc":"Standalone interpolation should not alter surrounding whitespace."},{"name":"Triple Mustache - Standalone","data":{"string":"---"},"expected":" ---\n","template":" {{{string}}}\n","desc":"Standalone interpolation should not alter surrounding whitespace."},{"name":"Ampersand - Standalone","data":{"string":"---"},"expected":" ---\n","template":" {{&string}}\n","desc":"Standalone interpolation should not alter surrounding whitespace."},{"name":"Interpolation With Padding","data":{"string":"---"},"expected":"|---|","template":"|{{ string }}|","desc":"Superfluous in-tag whitespace should be ignored."},{"name":"Triple Mustache With Padding","data":{"string":"---"},"expected":"|---|","template":"|{{{ string }}}|","desc":"Superfluous in-tag whitespace should be ignored."},{"name":"Ampersand With Padding","data":{"string":"---"},"expected":"|---|","template":"|{{& string }}|","desc":"Superfluous in-tag whitespace should be ignored."}]}
\ No newline at end of file
{"__ATTN__":"Do not edit this file; changes belong in the appropriate YAML file.","overview":"Inverted Section tags and End Section tags are used in combination to wrap a\nsection of the template.\n\nThese tags' content MUST be a non-whitespace character sequence NOT\ncontaining the current closing delimiter; each Inverted Section tag MUST be\nfollowed by an End Section tag with the same content within the same\nsection.\n\nThis tag's content names the data to replace the tag. Name resolution is as\nfollows:\n 1) Split the name on periods; the first part is the name to resolve, any\n remaining parts should be retained.\n 2) Walk the context stack from top to bottom, finding the first context\n that is a) a hash containing the name as a key OR b) an object responding\n to a method with the given name.\n 3) If the context is a hash, the data is the value associated with the\n name.\n 4) If the context is an object and the method with the given name has an\n arity of 1, the method SHOULD be called with a String containing the\n unprocessed contents of the sections; the data is the value returned.\n 5) Otherwise, the data is the value returned by calling the method with\n the given name.\n 6) If any name parts were retained in step 1, each should be resolved\n against a context stack containing only the result from the former\n resolution. If any part fails resolution, the result should be considered\n falsey, and should interpolate as the empty string.\nIf the data is not of a list type, it is coerced into a list as follows: if\nthe data is truthy (e.g. `!!data == true`), use a single-element list\ncontaining the data, otherwise use an empty list.\n\nThis section MUST NOT be rendered unless the data list is empty.\n\nInverted Section and End Section tags SHOULD be treated as standalone when\nappropriate.\n","tests":[{"name":"Falsey","data":{"boolean":false},"expected":"\"This should be rendered.\"","template":"\"{{^boolean}}This should be rendered.{{/boolean}}\"","desc":"Falsey sections should have their contents rendered."},{"name":"Truthy","data":{"boolean":true},"expected":"\"\"","template":"\"{{^boolean}}This should not be rendered.{{/boolean}}\"","desc":"Truthy sections should have their contents omitted."},{"name":"Context","data":{"context":{"name":"Joe"}},"expected":"\"\"","template":"\"{{^context}}Hi {{name}}.{{/context}}\"","desc":"Objects and hashes should behave like truthy values."},{"name":"List","data":{"list":[{"n":1},{"n":2},{"n":3}]},"expected":"\"\"","template":"\"{{^list}}{{n}}{{/list}}\"","desc":"Lists should behave like truthy values."},{"name":"Empty List","data":{"list":[]},"expected":"\"Yay lists!\"","template":"\"{{^list}}Yay lists!{{/list}}\"","desc":"Empty lists should behave like falsey values."},{"name":"Doubled","data":{"two":"second","bool":false},"expected":"* first\n* second\n* third\n","template":"{{^bool}}\n* first\n{{/bool}}\n* {{two}}\n{{^bool}}\n* third\n{{/bool}}\n","desc":"Multiple inverted sections per template should be permitted."},{"name":"Nested (Falsey)","data":{"bool":false},"expected":"| A B C D E |","template":"| A {{^bool}}B {{^bool}}C{{/bool}} D{{/bool}} E |","desc":"Nested falsey sections should have their contents rendered."},{"name":"Nested (Truthy)","data":{"bool":true},"expected":"| A E |","template":"| A {{^bool}}B {{^bool}}C{{/bool}} D{{/bool}} E |","desc":"Nested truthy sections should be omitted."},{"name":"Context Misses","data":{},"expected":"[Cannot find key 'missing'!]","template":"[{{^missing}}Cannot find key 'missing'!{{/missing}}]","desc":"Failed context lookups should be considered falsey."},{"name":"Dotted Names - Truthy","data":{"a":{"b":{"c":true}}},"expected":"\"\" == \"\"","template":"\"{{^a.b.c}}Not Here{{/a.b.c}}\" == \"\"","desc":"Dotted names should be valid for Inverted Section tags."},{"name":"Dotted Names - Falsey","data":{"a":{"b":{"c":false}}},"expected":"\"Not Here\" == \"Not Here\"","template":"\"{{^a.b.c}}Not Here{{/a.b.c}}\" == \"Not Here\"","desc":"Dotted names should be valid for Inverted Section tags."},{"name":"Dotted Names - Broken Chains","data":{"a":{}},"expected":"\"Not Here\" == \"Not Here\"","template":"\"{{^a.b.c}}Not Here{{/a.b.c}}\" == \"Not Here\"","desc":"Dotted names that cannot be resolved should be considered falsey."},{"name":"Surrounding Whitespace","data":{"boolean":false},"expected":" | \t|\t | \n","template":" | {{^boolean}}\t|\t{{/boolean}} | \n","desc":"Inverted sections should not alter surrounding whitespace."},{"name":"Internal Whitespace","data":{"boolean":false},"expected":" | \n | \n","template":" | {{^boolean}} {{! Important Whitespace }}\n {{/boolean}} | \n","desc":"Inverted should not alter internal whitespace."},{"name":"Indented Inline Sections","data":{"boolean":false},"expected":" NO\n WAY\n","template":" {{^boolean}}NO{{/boolean}}\n {{^boolean}}WAY{{/boolean}}\n","desc":"Single-line sections should not alter surrounding whitespace."},{"name":"Standalone Lines","data":{"boolean":false},"expected":"| This Is\n|\n| A Line\n","template":"| This Is\n{{^boolean}}\n|\n{{/boolean}}\n| A Line\n","desc":"Standalone lines should be removed from the template."},{"name":"Standalone Indented Lines","data":{"boolean":false},"expected":"| This Is\n|\n| A Line\n","template":"| This Is\n {{^boolean}}\n|\n {{/boolean}}\n| A Line\n","desc":"Standalone indented lines should be removed from the template."},{"name":"Standalone Line Endings","data":{"boolean":false},"expected":"|\r\n|","template":"|\r\n{{^boolean}}\r\n{{/boolean}}\r\n|","desc":"\"\\r\\n\" should be considered a newline for standalone tags."},{"name":"Standalone Without Previous Line","data":{"boolean":false},"expected":"^\n/","template":" {{^boolean}}\n^{{/boolean}}\n/","desc":"Standalone tags should not require a newline to precede them."},{"name":"Standalone Without Newline","data":{"boolean":false},"expected":"^\n/\n","template":"^{{^boolean}}\n/\n {{/boolean}}","desc":"Standalone tags should not require a newline to follow them."},{"name":"Padding","data":{"boolean":false},"expected":"|=|","template":"|{{^ boolean }}={{/ boolean }}|","desc":"Superfluous in-tag whitespace should be ignored."}]}
\ No newline at end of file
This diff is collapsed.
{"__ATTN__":"Do not edit this file; changes belong in the appropriate YAML file.","overview":"Partial tags are used to expand an external template into the current\ntemplate.\n\nThe tag's content MUST be a non-whitespace character sequence NOT containing\nthe current closing delimiter.\n\nThis tag's content names the partial to inject. Set Delimiter tags MUST NOT\naffect the parsing of a partial. The partial MUST be rendered against the\ncontext stack local to the tag. If the named partial cannot be found, the\nempty string SHOULD be used instead, as in interpolations.\n\nPartial tags SHOULD be treated as standalone when appropriate. If this tag\nis used standalone, any whitespace preceding the tag should treated as\nindentation, and prepended to each line of the partial before rendering.\n","tests":[{"name":"Basic Behavior","data":{},"expected":"\"from partial\"","template":"\"{{>text}}\"","desc":"The greater-than operator should expand to the named partial.","partials":{"text":"from partial"}},{"name":"Failed Lookup","data":{},"expected":"\"\"","template":"\"{{>text}}\"","desc":"The empty string should be used when the named partial is not found.","partials":{}},{"name":"Context","data":{"text":"content"},"expected":"\"*content*\"","template":"\"{{>partial}}\"","desc":"The greater-than operator should operate within the current context.","partials":{"partial":"*{{text}}*"}},{"name":"Recursion","data":{"content":"X","nodes":[{"content":"Y","nodes":[]}]},"expected":"X<Y<>>","template":"{{>node}}","desc":"The greater-than operator should properly recurse.","partials":{"node":"{{content}}<{{#nodes}}{{>node}}{{/nodes}}>"}},{"name":"Surrounding Whitespace","data":{},"expected":"| \t|\t |","template":"| {{>partial}} |","desc":"The greater-than operator should not alter surrounding whitespace.","partials":{"partial":"\t|\t"}},{"name":"Inline Indentation","data":{"data":"|"},"expected":" | >\n>\n","template":" {{data}} {{> partial}}\n","desc":"Whitespace should be left untouched.","partials":{"partial":">\n>"}},{"name":"Standalone Line Endings","data":{},"expected":"|\r\n>|","template":"|\r\n{{>partial}}\r\n|","desc":"\"\\r\\n\" should be considered a newline for standalone tags.","partials":{"partial":">"}},{"name":"Standalone Without Previous Line","data":{},"expected":" >\n >>","template":" {{>partial}}\n>","desc":"Standalone tags should not require a newline to precede them.","partials":{"partial":">\n>"}},{"name":"Standalone Without Newline","data":{},"expected":">\n >\n >","template":">\n {{>partial}}","desc":"Standalone tags should not require a newline to follow them.","partials":{"partial":">\n>"}},{"name":"Standalone Indentation","data":{"content":"<\n->"},"expected":"\\\n |\n <\n->\n |\n/\n","template":"\\\n {{>partial}}\n/\n","desc":"Each line of the partial should be indented before rendering.","partials":{"partial":"|\n{{{content}}}\n|\n"}},{"name":"Padding Whitespace","data":{"boolean":true},"expected":"|[]|","template":"|{{> partial }}|","desc":"Superfluous in-tag whitespace should be ignored.","partials":{"partial":"[]"}}]}
\ No newline at end of file
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment