mirror of
https://github.com/pulsar-edit/pulsar.git
synced 2025-01-08 16:19:17 +03:00
Use $$ ->
to render ad-hoc document fragments
Also eliminate stdlib/template directory which held code related to SpacePen's precursor framework.
This commit is contained in:
parent
c712f598e5
commit
bb640dd342
@ -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'
|
||||
|
||||
|
@ -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 "<div></div>"
|
||||
|
||||
builder.reset()
|
||||
builder.tag 'ol'
|
||||
expect(builder.toHtml()).toBe "<ol></ol>"
|
||||
|
||||
it "can generate tags with content", ->
|
||||
builder.tag 'ol', ->
|
||||
builder.tag 'li'
|
||||
builder.tag 'li'
|
||||
|
||||
expect(builder.toHtml()).toBe "<ol><li></li><li></li></ol>"
|
||||
|
||||
it "can generate tags with text", ->
|
||||
builder.tag 'div', "hello"
|
||||
expect(builder.toHtml()).toBe "<div>hello</div>"
|
||||
|
||||
builder.reset()
|
||||
builder.tag 'div', 22
|
||||
expect(builder.toHtml()).toBe "<div>22</div>"
|
||||
|
||||
it "HTML escapes tag text", ->
|
||||
builder.tag('div', "<br/>")
|
||||
expect(builder.toHtml()).toBe "<div><br/></div>"
|
||||
|
||||
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 '<br id="foo">'
|
||||
|
||||
describe ".raw(text)", ->
|
||||
it "does not escape html entities", ->
|
||||
builder.raw ' '
|
||||
expect(builder.toHtml()).toBe ' '
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
|
||||
|
@ -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 = []
|
||||
|
@ -1,7 +0,0 @@
|
||||
module.exports =
|
||||
class CloseTag
|
||||
constructor: (@name) ->
|
||||
|
||||
toHtml: ->
|
||||
"</#{@name}>"
|
||||
|
@ -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
|
@ -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, '<')
|
||||
.replace(/>/g, '>')
|
||||
|
43
vendor/space-pen.coffee
vendored
43
vendor/space-pen.coffee
vendored
@ -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)
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user