Permalink
Browse files

added sub-views and fixed some bugs

  • Loading branch information...
collin committed Apr 13, 2012
1 parent a626c43 commit c23f0295194544430b84fa011acce4cfab78e9c4
@@ -10,7 +10,7 @@ Client.require """
binding/model binding/field binding/input binding/select
binding/check_box binding/edit_line binding/one binding/many
- views/panel views/region
+ views/panel views/region views/dialog
models/targets
@@ -3,29 +3,43 @@ Taxi = require("taxi")
{each} = require("underscore")
jwerty = require("jwerty").jwerty
domready = $ = require("jquery")
+_ = require "underscore"
AS.Application = AS.Object.extend ({def, include}) ->
include Taxi.Mixin
- def initialize: () ->
+ def initialize: (config={}) ->
+ _.extend(this, config)
@params = AS.params
+ @el ?= $("body")
@god_given_key_handlers()
domready =>
@boot()
- @el ?= $("body")
def boot: ->
def god_given_key_handlers: ->
handlers =
- '': 'esc'
+ '': 'escape'
'⌘+↩': 'accept'
'': 'delete'
+ #TODO: add to test suite
+ "": "open"
+ "up": "up"
+ "down": "down"
+ "home": "first"
+ "end": "last"
+ "left": "left"
+ "right": "right"
+ "tab": "indent"
+ "shift+tab": "dedent"
+ "[a-z]/[0-9]/shift+[a-z]": "alphanum"
+
+
each handlers, (trigger, key) =>
- jwerty.key key, (event) =>
- @trigger(trigger, event)
+ jwerty.key key, ( (event) => @trigger(trigger, event) ), @el
def view: (constructor, options={}) ->
options.application = this
@@ -1,5 +1,6 @@
AS = require("alpha_simprini")
_ = require "underscore"
+jQuery = require "jquery"
AS.Binding.Field = AS.Binding.extend ({def}) ->
@@ -14,16 +15,29 @@ AS.Binding.Field = AS.Binding.extend ({def}) ->
def setContent: ->
if @fn
+ @content.remove()
@container.empty()
+
+ fieldValue = @fieldValue()
+ # FIXME: this turned into a string :(
+ return if fieldValue is null
+ return if fieldValue is undefined
+ return if fieldValue is "null"
+ return if fieldValue is "undefined"
@bindingGroup.unbind()
@context.withinBindingGroup @bindingGroup, =>
@context.withinNode @container, =>
- @fn.call(@context)
+ made = if _.include(fieldValue.model?.constructor.ancestors, AS.Model)
+ value = AS.ViewModel.build(@context, fieldValue.model)
+ made = @fn.call(@context, value, AS.Binding.Model.new(@context, value, @container))
+ else
+ @fn.call(@context)
+ @content.push if made instanceof jQuery then made[0] else made
else
@content.text @fieldValue()
def makeContent: ->
- if @fn
+ @content = if @fn
@context.$ []
else
@context.$ @context.span()
@@ -95,7 +95,7 @@ AS.Binding.Many = AS.Binding.extend ({def}) ->
content.push made
# FIXME: paint!
- # binding.paint()
+ binding.paint()
@contents[item.cid] = content
return content
@@ -1,5 +1,6 @@
AS = require("alpha_simprini")
_ = require "underscore"
+$ = require "jquery"
AS.Binding.Model = AS.Object.extend ({def}) ->
def initialize: (@context, @model, @content=$([])) ->
@@ -25,8 +25,7 @@ AS.BindingGroup = AS.Object.extend ({def}) ->
handler: handler
context: context
- def addChild: ->
- child = AS.BindingGroup.new()
+ def addChild: (child=AS.BindingGroup.new())->
@children.push child
return child
@@ -6,21 +6,28 @@ fleck = require("fleck")
AS.View = AS.DOM.extend ({delegate, include, def, defs}) ->
include Taxi.Mixin
+ delegate 'addClass', 'removeClass', 'show', 'hide', 'html', to: "el"
+
def tagName: "div"
def _ensureElement: -> @el ?= @$(@buildElement())
def initialize: (config={}) ->
+ config.el = @$(config.el) if config.el and !(config.el.jquery)
+
@cid = _.uniqueId("c")
for key, value of config
- if value instanceof AS.Model
- @[key] = AS.ViewModel.build(this, value)
+ if value?.model instanceof AS.Model
+ @[key] = AS.ViewModel.build(this, value.model)
else
@[key] = value
@bindingGroup = AS.BindingGroup.new()
@_ensureElement()
+
+ @currentNode = @el[0]
+ @childViews = []
@delegateEvents()
def append: (view) -> @el.append view.el
@@ -55,14 +62,44 @@ AS.View = AS.DOM.extend ({delegate, include, def, defs}) ->
classes.join(" ")
-
def baseAttributes: ->
attrs =
class: @klassString()
def buildElement: ->
@currentNode = @[@tagName](@baseAttributes())
+ def view: (constructor, options={}) ->
+ options.application = @application
+ options.parentView = this
+ view = constructor.new(options)
+ @childViews.push(view)
+ @bindingGroup.addChild(view)
+ view.el[0]
+
+ def descendantViews: (views=[], constructor) ->
+ for view in @childViews
+ if constructor
+ views.push view if view instanceof constructor
+ else
+ views.push view
+ view.descendantViews(views, constructor)
+
+ views
+
+ def removeChild: (child) ->
+ @childViews = _.without(@childViews, child)
+
+ def unbind: ->
+ @parentView.removeChild(this)
+ @el.remove()
+
+ def binding: (bindable, fnOrElement) ->
+ if bindable instanceof AS.Collection
+ AS.Binding.Many.new(this, bindable, bindable, fnOrElement)
+ else if bindable instanceof AS.Model
+ AS.Binding.Model.new(this, bindable, fnOrElement)
+
def delegateEvents: () ->
if @events
@standardEvents = AS.ViewEvents.new(this, @events)
@@ -2,6 +2,7 @@ AS = require "alpha_simprini"
_ = require "underscore"
jQuery = require "jQuery"
Pathology = require "pathology"
+Taxi = require("taxi")
AS.ViewModel = AS.Object.extend ({def, defs}) ->
@@ -21,14 +22,12 @@ AS.ViewModel = AS.Object.extend ({def, defs}) ->
for name, property of model.properties
klass.bindables[name] = switch property.constructor
- when AS.Model.Field
+ when AS.Model.Field, Taxi.Property, AS.Model.VirtualProperty
AS.Binding.Field
when AS.Model.BelongsTo, AS.Model.EmbedsOne, AS.Model.HasOne
AS.Binding.One
when AS.Model.HasMany, AS.Model.EmbedsMany
AS.Binding.Many
- # when AS.Model.HasOne
- # AS.Binding.HasOne
for method in AS.instanceMethods(model)
continue if _.include _.keys(Pathology.Object::), method
@@ -9,6 +9,11 @@ AS.Model = AS.Object.extend ({def, include}) ->
def initialize: (attributes={}) ->
@model = this
+ if id = attributes.id
+ delete attributes.id
+ @setId(id)
+ else if !@id
+ @setId(AS.uniq())
@set(attributes)
def set: (attributes) ->
@@ -19,14 +24,17 @@ AS.Model = AS.Object.extend ({def, include}) ->
# assert @[key].set
@[key].set(value)
- def setId: (id=AS.uniq()) ->
+ def setId: (id) ->
if @id
delete AS.All.byId[@id]
delete AS.All.byIdRef["#{@id}-#{@constructor.path()}"]
@id = id
@idRef = "#{@id}-#{@constructor.path()}"
- @cid = @idRef or uniqueId("c")
+
+ # NEVER CHANGE THE CID
+ @cid ?= @idRef or uniqueId("c")
+
AS.All.byCid[@cid] = AS.All.byId[@id] = AS.All.byIdRef[@idRef] = this
# Don't trigger 'change', this must be specifically listened for.
@@ -79,6 +79,7 @@ AS.Model.REST = AS.Module.extend ({delegate, include, def, defs}) ->
@constructor.mappings()[key].sideloadData(embed, references) for embed in embeds
references.each (model, ids) -> model.resolveReferences(ids)
+ @trigger("ready")
return this
defs sideloadData: (modelData, references) ->
@@ -1,6 +1,6 @@
AS = require "alpha_simprini"
AS.Models.RadioSelectionModel = AS.Model.extend ({def}) ->
- @hasOne 'selected'
+ @property 'selected'
def initialize: ->
@_super()
@@ -28,7 +28,16 @@ AS.Model.HasOne.Instance = AS.Model.Field.Instance.extend ({def}) ->
value = @options.model().new(value)
@value?.unbind(@namespace)
+
+ # TODO: test inverse
+ if @value and @options.inverse and @object[@options.inverse]
+ @value[@options.inverse].remove(@object)
+
@value = value
+
+ if @value and @options.inverse and @object[@options.inverse]
+ @value[@options.inverse].add(@object)
+
@value?.bind "all#{@namespace}", _.bind(@trigger, this)
@object.trigger("change")
@object.trigger("change:#{@options.name}")
@@ -16,11 +16,15 @@ AS.Model.VirtualProperty.Instance = AS.Property.Instance.extend ({def}) ->
for dependency in @options.dependencies
@object.bind "change:#{dependency}", _.bind @triggerFor, this, dependency
- def set: () -> throw "Can't set a VirtualProperty name: #{@options.name}, dependencies: #{@options.dependencies.join(',')}"
+ def set: (value) ->
+ if set = @options.getSet.set
+ set.call(@object, value)
+ else
+ throw "Can't set a VirtualProperty name: #{@options.name}, dependencies: #{@options.dependencies.join(',')}"
def get: -> @cached = @compute()
- def compute: (args) -> @options.fn.call(@object)
+ def compute: (args) -> @options.getSet.get.call(@object)
def triggerFor: (dependency) ->
computed = @compute()
@@ -36,4 +40,8 @@ AS.Model.VirtualProperty.Instance = AS.Property.Instance.extend ({def}) ->
AS.Model.defs virtualProperties: (dependencies..., properties) ->
for name, fn of properties
- AS.Model.VirtualProperty.new(name, this, dependencies: dependencies, fn: fn)
+ if _.isFunction(fn)
+ getSet = {get: fn}
+ else
+ getSet = fn
+ AS.Model.VirtualProperty.new(name, this, dependencies: dependencies, getSet: getSet)
@@ -1,17 +1,31 @@
{AS, $, _, sinon, jwerty} = require require("path").resolve("./test/client_helper")
exports.Application =
setUp: (callback) ->
- @app = AS.Application.new()
+ @app = AS.Application.new(el: @el = $("<div>"))
callback()
"attaches global key handlers w/jwerty": (test) ->
- test.expect 3
- @app.bind "esc", (event) -> test.ok event
- @app.bind "accept", (event) -> test.ok event
- @app.bind "delete", (event) -> test.ok event
- jwerty.fire "esc"
- jwerty.fire "cmd+enter"
- jwerty.fire "backspace"
+ events = [
+ "open", "up", "down", "first", "last", "left",
+ "right", "indent", "dedent", "alphanum"
+ "escape", "accept", "delete"
+ ]
+
+ for event in events
+ do (event) =>
+ @app.bind event, (_event) -> test.ok(_event)
+
+ triggers = [
+ "esc", "cmd+enter", "backspace", "enter", "up", "down",
+ "home", "end", "left", "right", "tab", "shift+tab"
+ "a", "b", "C", "D", "1", "2"
+ ]
+ test.expect triggers.length
+
+
+ for trigger in triggers
+ jwerty.fire trigger, @el
+
test.done()
"initializes views into the application context": (test) ->
@@ -9,6 +9,24 @@ exports.Binding =
test.equal binding.container.find("span").text(), "value"
test.done()
+ "clears content when value undefined": (test) ->
+ [mocks, binding] = mock_binding AS.Binding.Field,
+ fn: ->
+ @h1 -> @span "fn value"
+
+ binding.model.field.set(undefined)
+ test.equal "", binding.container.html()
+ test.done()
+
+ "clears content when value null": (test) ->
+ [mocks, binding] = mock_binding AS.Binding.Field,
+ fn: ->
+ @h1 -> @span "fn value"
+
+ binding.model.field.set(null)
+ test.equal "", binding.container.html()
+ test.done()
+
"updates content when model changes": (test) ->
[mocks, binding] = mock_binding(AS.Binding.Field)
binding.model.field.set("new value")
Oops, something went wrong.

0 comments on commit c23f029

Please sign in to comment.