diff --git a/spec/spec-bootstrap.coffee b/spec/spec-bootstrap.coffee
index 24f084725..d39d4cb7d 100644
--- a/spec/spec-bootstrap.coffee
+++ b/spec/spec-bootstrap.coffee
@@ -1,14 +1,14 @@
-$$ = require 'template/builder'
+{$$} = require 'space-pen'
nakedLoad 'jasmine'
nakedLoad 'jasmine-html'
nakedLoad 'jasmine-focused'
$ = require 'jquery'
-$('head').append $$.render ->
+$('head').append $$ ->
@link rel: "stylesheet", type: "text/css", href: "static/jasmine.css"
-$('body').append $$.render ->
+$('body').append $$ ->
@div id: 'jasmine_runner'
@div id: 'jasmine-content'
diff --git a/spec/stdlib/template/builder-spec.coffee b/spec/stdlib/template/builder-spec.coffee
deleted file mode 100644
index 5c4695255..000000000
--- a/spec/stdlib/template/builder-spec.coffee
+++ /dev/null
@@ -1,75 +0,0 @@
-Builder = require 'template/builder'
-
-describe "Builder", ->
- builder = null
-
- beforeEach -> builder = new Builder
-
- describe "tag class methods", ->
- it "calls render, assuming the arguments to the current method as the first tag", ->
- fragment =
- Builder.div ->
- @ol class: 'cool-list', =>
- @li()
- @li()
-
- expect(fragment).toMatchSelector('div')
- expect(fragment.find('ol.cool-list')).toExist()
- expect(fragment.find('li').length).toBe 2
-
- describe "@render", ->
- it "runs the given function in a fresh builder instance and returns the resulting view fragment", ->
- fragment =
- Builder.render ->
- @div =>
- @ol class: 'cool-list', =>
- @li()
- @li()
-
- expect(fragment).toMatchSelector('div')
- expect(fragment.find('ol.cool-list')).toExist()
- expect(fragment.find('li').length).toBe 2
-
- describe ".tag(name, args...)", ->
- it "can generate simple tags", ->
- builder.tag 'div'
- expect(builder.toHtml()).toBe "
"
-
- builder.reset()
- builder.tag 'ol'
- expect(builder.toHtml()).toBe "
"
-
- it "can generate tags with content", ->
- builder.tag 'ol', ->
- builder.tag 'li'
- builder.tag 'li'
-
- expect(builder.toHtml()).toBe "
"
-
- it "can generate tags with text", ->
- builder.tag 'div', "hello"
- expect(builder.toHtml()).toBe "hello
"
-
- builder.reset()
- builder.tag 'div', 22
- expect(builder.toHtml()).toBe "22
"
-
- it "HTML escapes tag text", ->
- builder.tag('div', "
")
- expect(builder.toHtml()).toBe "<br/>
"
-
- it "can generate tags with attributes", ->
- builder.tag 'div', id: 'foo', class: 'bar'
- fragment = builder.toFragment()
- expect(fragment.attr('id')).toBe 'foo'
- expect(fragment.attr('class')).toBe 'bar'
-
- it "can generate self-closing tags", ->
- builder.tag 'br', id: 'foo'
- expect(builder.toHtml()).toBe '
'
-
- describe ".raw(text)", ->
- it "does not escape html entities", ->
- builder.raw ' '
- expect(builder.toHtml()).toBe ' '
-
diff --git a/src/atom/editor.coffee b/src/atom/editor.coffee
index 9f0e7f4fc..9621e8b5b 100644
--- a/src/atom/editor.coffee
+++ b/src/atom/editor.coffee
@@ -1,4 +1,4 @@
-{View} = require 'space-pen'
+{View, $$} = require 'space-pen'
Buffer = require 'buffer'
Point = require 'point'
Cursor = require 'cursor'
@@ -7,7 +7,6 @@ Highlighter = require 'highlighter'
Range = require 'range'
$ = require 'jquery'
-$$ = require 'template/builder'
_ = require 'underscore'
module.exports =
@@ -108,13 +107,14 @@ class Editor extends View
buildLineElement: (row) ->
tokens = @highlighter.tokensForRow(row)
- $$.pre class: 'line', ->
- if tokens.length
- for token in tokens
- classes = token.type.split('.').map((c) -> "ace_#{c}").join(' ')
- @span { class: token.type.replace('.', ' ') }, token.value
- else
- @raw ' '
+ $$ ->
+ @pre class: 'line', =>
+ if tokens.length
+ for token in tokens
+ classes = token.type.split('.').map((c) -> "ace_#{c}").join(' ')
+ @span { class: token.type.replace('.', ' ') }, token.value
+ else
+ @raw ' '
setBuffer: (@buffer) ->
@highlighter = new Highlighter(@buffer)
diff --git a/src/atom/selection.coffee b/src/atom/selection.coffee
index d84ea7ab8..c9e9275f7 100644
--- a/src/atom/selection.coffee
+++ b/src/atom/selection.coffee
@@ -1,7 +1,6 @@
Cursor = require 'cursor'
Range = require 'range'
-{View} = require 'space-pen'
-$$ = require 'template/builder'
+{View, $$} = require 'space-pen'
module.exports =
class Selection extends View
@@ -52,7 +51,7 @@ class Selection extends View
else
css.right = 0
- region = $$.div(class: 'selection').css(css)
+ region = ($$ -> @div class: 'selection').css(css)
@append(region)
@regions.push(region)
diff --git a/src/stdlib/template/builder.coffee b/src/stdlib/template/builder.coffee
deleted file mode 100644
index fa68e447c..000000000
--- a/src/stdlib/template/builder.coffee
+++ /dev/null
@@ -1,105 +0,0 @@
-_ = require 'underscore'
-$ = require 'jquery'
-OpenTag = require 'template/open-tag'
-CloseTag = require 'template/close-tag'
-Text = require 'template/text'
-
-module.exports =
-class Builder
- @render: (fn) ->
- builder = new this
- fn.call(builder)
- builder.toFragment()
-
- @elements:
- normal: 'a abbr address article aside audio b bdi bdo blockquote body button
- canvas caption cite code colgroup datalist dd del details dfn div dl dt em
- fieldset figcaption figure footer form h1 h2 h3 h4 h5 h6 head header hgroup
- html i iframe ins kbd label legend li map mark menu meter nav noscript object
- ol optgroup option output p pre progress q rp rt ruby s samp script section
- select small span strong style sub summary sup table tbody td textarea tfoot
- th thead time title tr u ul video'.split /\s+/
-
- void: 'area base br col command embed hr img input keygen link meta param
- source track wbr'.split /\s+/
-
- @allElements: ->
- @elements.normal.concat(@elements.void)
-
- @buildTagClassMethod: (tagName) ->
- this[tagName] = (args...) ->
- @render ->
- argsWithBoundFunctions = args.map (arg) =>
- if _.isFunction(arg)
- _.bind(arg, this)
- else
- arg
- @tag(tagName, argsWithBoundFunctions...)
-
- @buildTagInstanceMethod: (tagName) ->
- @prototype[tagName] = (args...) -> @tag(tagName, args...)
-
- @allElements().forEach (tagName) => @buildTagClassMethod(tagName)
- @allElements().forEach (tagName) => @buildTagInstanceMethod(tagName)
-
- constructor: ->
- @reset()
-
- toHtml: ->
- _.map(@document, (x) -> x.toHtml()).join('')
-
- toFragment: ->
- fragment = $(@toHtml())
- @wireOutlets fragment
- fragment.find('*').andSelf().data('view', fragment)
- fn(fragment) for fn in @postProcessingFns
- fragment
-
- tag: (name, args...) ->
- options = @extractOptions(args)
-
- @document.push(new OpenTag(name, options.attributes))
- if @elementIsVoid(name)
- if (options.text? or options.content?)
- throw new Error("Self-closing tag #{name} cannot have text or content")
- else
- options.content?()
- @text(options.text) if options.text
- @document.push(new CloseTag(name))
-
- subview: (outletName, subview) ->
- subviewId = _.uniqueId('subview')
- @tag 'div', id: subviewId
- @postProcessingFns.push (view) ->
- view[outletName] = subview
- subview.parentView = view
- view.find("div##{subviewId}").replaceWith(subview)
-
- elementIsVoid: (name) ->
- name in @constructor.elements.void
-
- extractOptions: (args) ->
- options = {}
- for arg in args
- options.content = arg if _.isFunction(arg)
- options.text = arg if _.isString(arg)
- options.text = arg.toString() if _.isNumber(arg)
- options.attributes = arg if _.isObject(arg) and not _.isFunction(arg)
- options
-
- text: (string) ->
- @document.push(new Text(string))
-
- raw: (string) ->
- @document.push(new Text(string, true))
-
- wireOutlets: (view) ->
- view.find('[outlet]').each ->
- elt = $(this)
- outletName = elt.attr('outlet')
- view[outletName] = elt
-
- reset: ->
- @document = []
- @postProcessingFns = []
-
diff --git a/src/stdlib/template/close-tag.coffee b/src/stdlib/template/close-tag.coffee
deleted file mode 100644
index 3e4874dfc..000000000
--- a/src/stdlib/template/close-tag.coffee
+++ /dev/null
@@ -1,7 +0,0 @@
-module.exports =
-class CloseTag
- constructor: (@name) ->
-
- toHtml: ->
- "#{@name}>"
-
diff --git a/src/stdlib/template/open-tag.coffee b/src/stdlib/template/open-tag.coffee
deleted file mode 100644
index ec37868aa..000000000
--- a/src/stdlib/template/open-tag.coffee
+++ /dev/null
@@ -1,12 +0,0 @@
-_ = require 'underscore'
-
-module.exports =
-class OpenTag
- constructor: (@name, @attributes) ->
-
- toHtml: ->
- "<#{@name}#{@attributesHtml()}>"
-
- attributesHtml: ->
- s = _.map(@attributes, (value, key) -> "#{key}=\"#{value}\"").join(' ')
- if s == "" then "" else " " + s
diff --git a/src/stdlib/template/text.coffee b/src/stdlib/template/text.coffee
deleted file mode 100644
index 1270086f8..000000000
--- a/src/stdlib/template/text.coffee
+++ /dev/null
@@ -1,15 +0,0 @@
-module.exports =
-class Text
- constructor: (@string, @raw=false) ->
-
- toHtml: ->
- if @raw
- @string
- else
- @string
- .replace(/&/g, '&')
- .replace(/"/g, '"')
- .replace(/'/g, ''')
- .replace(//g, '>')
-
diff --git a/vendor/space-pen.coffee b/vendor/space-pen.coffee
index 648f8f5ef..fc0b04480 100644
--- a/vendor/space-pen.coffee
+++ b/vendor/space-pen.coffee
@@ -1,4 +1,4 @@
-# Modified from 26fca5374e546fd8cc2f12d1140f915185611bdc
+# Modified from e2c7296822952f9dcb4b7d3a39e16cca7b5dd462
# Add require 'jquery'
$ = jQuery = require('jquery')
@@ -24,15 +24,41 @@ events =
idCounter = 0
class View extends jQuery
- elements.forEach (tagName) ->
- View[tagName] = (args...) -> @builder.tag(tagName, args...)
+ @builderStack: []
- @subview: (name, view) -> @builder.subview(name, view)
- @text: (string) -> @builder.text(string)
- @raw: (string) -> @builder.raw(string)
+ elements.forEach (tagName) ->
+ View[tagName] = (args...) -> @currentBuilder().tag(tagName, args...)
+
+ @subview: (name, view) ->
+ @currentBuilder().subview(name, view)
+
+ @text: (string) -> @currentBuilder().text(string)
+
+ @raw: (string) -> @currentBuilder().raw(string)
+
+ @currentBuilder: ->
+ @builderStack[@builderStack.length - 1]
+
+ @pushBuilder: ->
+ @builderStack.push(new Builder)
+
+ @popBuilder: ->
+ @builderStack.pop()
+
+ @buildHtml: (fn) ->
+ @pushBuilder()
+ fn.call(this)
+ [html, postProcessingSteps] = @popBuilder().buildHtml()
+
+ @render: (fn) ->
+ [html, postProcessingSteps] = @buildHtml(fn)
+ fragment = $(html)
+ step(fragment) for step in postProcessingSteps
+ fragment
constructor: (params={}) ->
- postProcessingSteps = @buildHtml(params)
+ [html, postProcessingSteps] = @constructor.buildHtml -> @content(params)
+ jQuery.fn.init.call(this, html)
@constructor = jQuery # sadly, jQuery assumes this.constructor == jQuery in pushStack
@wireOutlets(this)
@bindEventHandlers(this)
@@ -46,7 +72,6 @@ class View extends jQuery
@constructor.content(params)
[html, postProcessingSteps] = @constructor.builder.buildHtml()
@constructor.builder = null
- jQuery.fn.init.call(this, html)
postProcessingSteps
wireOutlets: (view) ->
@@ -157,5 +182,5 @@ for methodName in ['prependTo', 'appendTo', 'insertAfter', 'insertBefore']
result
(exports ? this).View = View
-
+(exports ? this).$$ = (fn) -> View.render.call(View, fn)