luca 0.8.599 → 0.9.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (149) hide show
  1. data/.gitignore +3 -0
  2. data/.rvmrc +1 -0
  3. data/CHANGELOG +51 -2
  4. data/README.md +10 -247
  5. data/ROADMAP +6 -2
  6. data/app.rb +16 -2
  7. data/assets/javascripts/dependencies/bootstrap.min.js +7 -1
  8. data/assets/javascripts/dependencies/codemirror-coffeescript.js +347 -0
  9. data/assets/javascripts/dependencies/codemirror-css.js +124 -0
  10. data/assets/javascripts/dependencies/codemirror-html.js +410 -0
  11. data/assets/javascripts/dependencies/codemirror-javascript.js +361 -0
  12. data/assets/javascripts/dependencies/codemirror-less.js +232 -0
  13. data/assets/javascripts/dependencies/codemirror-vim.js +500 -0
  14. data/assets/javascripts/dependencies/codemirror.js +3076 -0
  15. data/assets/javascripts/dependencies.coffee +0 -1
  16. data/assets/javascripts/luca-ui-base.coffee +10 -3
  17. data/assets/javascripts/luca-ui-bootstrap.js +1 -0
  18. data/assets/javascripts/luca-ui-development-tools.coffee +9 -0
  19. data/assets/javascripts/luca-ui.coffee +6 -1
  20. data/assets/javascripts/sandbox/application.coffee +51 -0
  21. data/assets/javascripts/sandbox/router.coffee +14 -0
  22. data/assets/javascripts/sandbox/templates/main.luca +33 -0
  23. data/assets/javascripts/sandbox/templates/sandbox/navigation.luca +1 -0
  24. data/assets/javascripts/sandbox/templates/sandbox.luca +1 -0
  25. data/assets/javascripts/sandbox/views/top_navigation.coffee +4 -0
  26. data/assets/javascripts/sandbox.coffee +2 -2
  27. data/assets/stylesheets/bootstrap.min.css +395 -297
  28. data/assets/stylesheets/codemirror-blackboard.css +25 -0
  29. data/assets/stylesheets/codemirror-monokai.css +33 -0
  30. data/assets/stylesheets/codemirror.css +126 -0
  31. data/assets/stylesheets/luca-ui-bootstrap.css +0 -1
  32. data/assets/stylesheets/luca-ui-development-tools.css +5 -0
  33. data/assets/stylesheets/sandbox/sandbox.scss +1 -3
  34. data/assets/stylesheets/themes/amelia-bootstrap.css +826 -0
  35. data/assets/stylesheets/themes/slate-bootstrap.css +797 -0
  36. data/assets/stylesheets/themes/superhero-bootstrap.css +830 -0
  37. data/lib/luca/code_browser.rb +55 -0
  38. data/lib/luca/rails/version.rb +1 -1
  39. data/lib/luca/rails.rb +1 -0
  40. data/spec/components/fields/checkbox_array_spec.coffee +46 -0
  41. data/spec/components/form_view_spec.coffee +10 -4
  42. data/spec/containers/card_view_spec.coffee +7 -0
  43. data/spec/core/collection_spec.coffee +58 -4
  44. data/spec/core/container_spec.coffee +6 -6
  45. data/spec/core/view_spec.coffee +93 -7
  46. data/spec/framework_spec.coffee +15 -12
  47. data/src/components/application.coffee +126 -18
  48. data/src/components/base_toolbar.coffee +2 -2
  49. data/src/components/collection_loader_view.coffee +1 -2
  50. data/src/components/collection_view.coffee +77 -0
  51. data/src/components/controller.coffee +1 -4
  52. data/src/components/fields/button_field.coffee +1 -1
  53. data/src/components/fields/checkbox_array.coffee +2 -2
  54. data/src/components/fields/checkbox_field.coffee +3 -1
  55. data/src/components/fields/file_upload_field.coffee +1 -1
  56. data/src/components/fields/hidden_field.coffee +1 -1
  57. data/src/components/fields/select_field.coffee +1 -1
  58. data/src/components/fields/text_area_field.coffee +1 -1
  59. data/src/components/fields/text_field.coffee +10 -6
  60. data/src/components/fields/type_ahead_field.coffee +18 -5
  61. data/src/components/form_button_toolbar.coffee +1 -2
  62. data/src/components/form_view.coffee +44 -62
  63. data/src/components/grid_view.coffee +27 -20
  64. data/src/components/load_mask.coffee +3 -0
  65. data/src/components/nav_bar.coffee +26 -0
  66. data/src/components/record_manager.coffee +1 -3
  67. data/src/components/router.coffee +1 -1
  68. data/src/components/template.coffee +3 -15
  69. data/src/components/toolbar_dialog.coffee +25 -0
  70. data/src/containers/card_view.coffee +22 -23
  71. data/src/containers/column_view.coffee +1 -6
  72. data/src/containers/modal_view.coffee +20 -71
  73. data/src/containers/panel_toolbar.coffee +156 -0
  74. data/src/containers/panel_view.coffee +1 -1
  75. data/src/containers/split_view.coffee +1 -3
  76. data/src/containers/tab_view.coffee +29 -29
  77. data/src/containers/viewport.coffee +38 -3
  78. data/src/core/collection.coffee +80 -48
  79. data/src/core/container.coffee +153 -72
  80. data/src/core/core.coffee +181 -0
  81. data/src/core/field.coffee +4 -2
  82. data/src/core/model.coffee +1 -1
  83. data/src/core/observer.coffee +3 -3
  84. data/src/core/panel.coffee +143 -0
  85. data/src/core/registry.coffee +104 -0
  86. data/src/core/util.coffee +82 -0
  87. data/src/core/view.coffee +158 -85
  88. data/src/framework.coffee +112 -178
  89. data/src/index.coffee +0 -255
  90. data/src/managers/collection_manager.coffee +1 -0
  91. data/src/samples/definition.coffee +49 -0
  92. data/src/stylesheets/base.scss +0 -78
  93. data/src/stylesheets/components/form_view.scss +8 -3
  94. data/src/stylesheets/components/grid_view.scss +3 -7
  95. data/src/stylesheets/components/load_mask.scss +14 -0
  96. data/src/stylesheets/components/toolbar.scss +0 -15
  97. data/src/stylesheets/containers/container.scss +14 -2
  98. data/src/stylesheets/containers/panels.scss +23 -0
  99. data/src/stylesheets/tools/class_browser.scss +32 -0
  100. data/src/stylesheets/tools/code_editor.scss +24 -0
  101. data/src/stylesheets/tools/component_tester.scss +8 -0
  102. data/src/stylesheets/tools/console.scss +26 -0
  103. data/src/templates/components/collection_loader_view.luca +1 -1
  104. data/src/templates/components/form_view.luca +2 -13
  105. data/src/templates/components/grid_view.luca +0 -2
  106. data/src/templates/components/load_mask.luca +3 -0
  107. data/src/templates/components/nav_bar.luca +2 -0
  108. data/src/templates/containers/tab_view.luca +1 -0
  109. data/src/templates/fields/text_field.luca +4 -1
  110. data/src/tools/class_browser.coffee +39 -0
  111. data/src/tools/code_editor.coffee +258 -0
  112. data/src/tools/code_mirror_field.coffee +57 -0
  113. data/src/tools/coffee_script_editor.coffee +60 -0
  114. data/src/tools/collection_inspector.coffee +4 -0
  115. data/src/tools/component_tester.coffee +472 -0
  116. data/src/tools/components/class_browser_detail.coffee +10 -0
  117. data/src/tools/components/class_browser_list.coffee +74 -0
  118. data/src/tools/console.coffee +147 -0
  119. data/src/tools/development_console.coffee +147 -0
  120. data/src/tools/models/components.coffee +63 -0
  121. data/src/tools/templates/component_tester/help.luca +14 -0
  122. data/vendor/assets/javascripts/luca-ui-base.js +1389 -611
  123. data/vendor/assets/javascripts/luca-ui-bootstrap.js +9 -0
  124. data/vendor/assets/javascripts/luca-ui-development-tools.js +18719 -0
  125. data/vendor/assets/javascripts/luca-ui-spec.js +2065 -878
  126. data/vendor/assets/javascripts/luca-ui.js +1759 -852
  127. data/vendor/assets/javascripts/luca-ui.min.js +3 -3
  128. data/vendor/assets/stylesheets/luca-ui-bootstrap.css +494 -440
  129. data/vendor/assets/stylesheets/luca-ui-development-tools.css +224 -0
  130. data/vendor/assets/stylesheets/luca-ui-spec.css +99 -140
  131. data/vendor/assets/stylesheets/luca-ui.css +99 -140
  132. data/views/index.erb +6 -3
  133. metadata +60 -18
  134. data/assets/javascripts/dependencies/jquery-console.js +0 -649
  135. data/assets/javascripts/development-console.coffee +0 -2
  136. data/assets/javascripts/sandbox/sandbox.coffee +0 -16
  137. data/assets/javascripts/sandbox/templates/features/collection_helpers.luca +0 -33
  138. data/assets/javascripts/sandbox/templates/features/form_demo_code.luca +0 -48
  139. data/assets/javascripts/sandbox/templates/features/grid_demo_code.luca +0 -24
  140. data/assets/javascripts/sandbox/templates/features/introduction.luca +0 -11
  141. data/assets/javascripts/sandbox/templates/features/view_helpers.luca +0 -43
  142. data/assets/javascripts/sandbox/templates/navigation.luca +0 -8
  143. data/assets/javascripts/sandbox/views/form_demo.coffee +0 -47
  144. data/assets/javascripts/sandbox/views/grid_demo.coffee +0 -23
  145. data/assets/javascripts/sandbox/views/pages/collection_events_sample.coffee +0 -1
  146. data/assets/javascripts/sandbox/views/pages/pages_controller.coffee +0 -38
  147. data/src/components/collection_inspector.coffee +0 -2
  148. data/src/components/development_console.coffee +0 -59
  149. data/src/stylesheets/components/development_console.scss +0 -47
@@ -0,0 +1,181 @@
1
+ # Component Definition Helpers
2
+ #
3
+ #
4
+ # We have customized the core Backbone.extend process to use a slightly
5
+ # different syntax, which allows us to intercept the component definition at
6
+ # various points, and maintain information about classes being defined, and
7
+ # the relationships between inherited classes, etc.
8
+
9
+ # _.def, or Luca.define returns a chainable object which allows you to define
10
+ # your components with a readable syntax. For example:
11
+
12
+ # _.def("Luca.View").extends("Backbone.View").with the_good:"shit"
13
+ # _.def("MyView").extends("Luca.View").with the_custom:"shit"
14
+
15
+ Luca.define = (componentName)->
16
+ new DefineProxy(componentName)
17
+
18
+ Luca.component = Luca.define
19
+
20
+ # The define proxy chain sets up a call to Luca.extend, which is a wrapper around Luca and Backbone component class' extend function.
21
+ class DefineProxy
22
+ constructor:(componentName)->
23
+ @namespace = Luca.util.namespace()
24
+ @componentId = @componentName = componentName
25
+
26
+ if componentName.match(/\./)
27
+ @namespaced = true
28
+ parts = componentName.split('.')
29
+ @componentId = parts.pop()
30
+ @namespace = parts.join('.')
31
+
32
+ # automatically add the namespace to the namespace registry
33
+ Luca.registry.addNamespace( parts.join('.') )
34
+
35
+ # allow for specifying the namespace
36
+ in: (@namespace)-> @
37
+
38
+ # allow for multiple ways of saying the same thing for readability purposes
39
+ from: (@superClassName)-> @
40
+ extends: (@superClassName)-> @
41
+ extend: (@superClassName)-> @
42
+
43
+ # an alias for with, or a readability helper in multi-line definitions
44
+ enhance: (properties)->
45
+ return @with(properties) if properties?
46
+ @
47
+
48
+ # which properties, methods, etc will you be extending the base class with?
49
+ with: (properties)->
50
+ at = if @namespaced
51
+ Luca.util.resolve(@namespace, (window || global))
52
+ else
53
+ (window||global)
54
+
55
+ # automatically create the namespace
56
+ if @namespaced and not at?
57
+ eval("(window||global).#{ @namespace } = {}")
58
+ at = Luca.util.resolve(@namespace,(window || global))
59
+
60
+ at[@componentId] = Luca.extend(@superClassName,@componentName, properties)
61
+
62
+ # automatically register this with the component registry
63
+ Luca.register( _.string.underscored(@componentId), @componentName)
64
+
65
+ at[@componentId]
66
+
67
+ # The last method of the DefineProxy chain is always going to result in
68
+ # a call to Luca.extend. Luca.extend wraps the call to Luca.View.extend,
69
+ # or Backbone.Collection.extend, and accepts the names of the extending,
70
+ # and extended classes as strings. This allows us to maintain information
71
+ # and references to the classes and their prototypes, mainly for the purposes
72
+ # of introspection and development tools
73
+ Luca.extend = (superClassName, childName, properties={})->
74
+ superClass = Luca.util.resolve( superClassName, (window || global) )
75
+
76
+ unless _.isFunction(superClass?.extend)
77
+ throw "#{ superClassName } is not a valid component to extend from"
78
+
79
+ properties.displayName = childName
80
+
81
+ properties._superClass = ()->
82
+ superClass.displayName ||= superClassName
83
+ superClass
84
+
85
+ properties._super = (method, context, args)->
86
+ @_superClass().prototype[method]?.apply(context, args)
87
+
88
+ superClass.extend(properties)
89
+
90
+ _.mixin
91
+ def: Luca.define
92
+
93
+
94
+ # Luca.Events
95
+ #
96
+ # These helpers will get mixed into Luca.Collection, Luca.View, and Luca.Model.
97
+ #
98
+ # They allow for syntactic sugar like:
99
+ #
100
+ # view.defer("someMethodOnTheView").until("collection","fetch")
101
+ #
102
+ # or
103
+ #
104
+ # view.defer( myCallback ).until("triggered:event")
105
+ class DeferredBindingProxy
106
+ constructor: (@object, operation, wrapWithUnderscore=true)->
107
+ if _.isFunction(operation)
108
+ fn = operation
109
+
110
+ else if _.isString(operation) and _.isFunction(@object[operation])
111
+ fn = @object[operation]
112
+
113
+ unless _.isFunction(fn)
114
+ throw "Must pass a function or a string representing one"
115
+
116
+ if wrapWithUnderscore is true
117
+ @fn = ()=>
118
+ _.defer(fn)
119
+ else
120
+ @fn = fn
121
+
122
+ @
123
+
124
+ # until accepts an object to bind to, and a trigger to bind with
125
+ # if you just pass a trigger, the object getting bound to
126
+ # will implicitly be @object
127
+ until: (watch, trigger)->
128
+ if watch? and not trigger?
129
+ trigger = watch
130
+ watch = @object
131
+
132
+ watch.once(trigger, @fn)
133
+
134
+ @object
135
+
136
+ Luca.Events =
137
+ defer: (operation, wrapWithUnderscore=true)->
138
+ new DeferredBindingProxy(@, operation, wrapWithUnderscore)
139
+
140
+ once: (trigger, callback, context)->
141
+ context ||= @
142
+
143
+ onceFn = ()->
144
+ callback.apply(context, arguments)
145
+ @unbind(trigger, onceFn)
146
+
147
+ @bind trigger, onceFn
148
+
149
+ class Luca.ScriptLoader
150
+ @loaded: {}
151
+
152
+ constructor: (options={})->
153
+ _.extend(@, Backbone.Events, Luca.Events)
154
+ @autoStart = options.autoStart is true
155
+ @scripts = options.scripts
156
+
157
+ ready = ()-> @trigger("ready")
158
+
159
+ @ready = _.after( @scripts.length, ready)
160
+
161
+ _.bindAll @, "load", "ready"
162
+
163
+ @defer("load").until(@, "start")
164
+
165
+ if @autoStart is true
166
+ @trigger("start")
167
+
168
+ @bind "ready", @onReady
169
+
170
+ applyPrefix: (script)->
171
+ script
172
+
173
+ onReady: ()->
174
+ console.log "All dependencies loaded"
175
+
176
+ start: ()->
177
+ @trigger("start")
178
+
179
+ load: ()->
180
+ Luca.util.loadScript( @applyPrefix(script), @ready ) for script in @scripts
181
+
@@ -1,4 +1,4 @@
1
- _.component('Luca.core.Field').extends('Luca.View').with
1
+ _.def('Luca.core.Field').extends('Luca.View').with
2
2
 
3
3
  className: 'luca-ui-text-field luca-ui-field'
4
4
 
@@ -23,10 +23,10 @@ _.component('Luca.core.Field').extends('Luca.View').with
23
23
 
24
24
  initialize: (@options={})->
25
25
  _.extend @, @options
26
- Luca.View::initialize.apply(@, arguments)
27
26
 
28
27
  @input_id ||= _.uniqueId('field')
29
28
  @input_name ||= @name
29
+ @input_class ||= ""
30
30
  @helperText ||= ""
31
31
  @label ||= "*#{ @label }" if @required and not @label?.match(/^\*/)
32
32
  @inputStyles ||= ""
@@ -36,6 +36,8 @@ _.component('Luca.core.Field').extends('Luca.View').with
36
36
  @updateState( @state )
37
37
  @placeHolder ||= ""
38
38
 
39
+ Luca.View::initialize.apply(@, arguments)
40
+
39
41
  beforeRender: ()->
40
42
  if Luca.enableBootstrap
41
43
  @$el.addClass('control-group')
@@ -4,7 +4,7 @@
4
4
  # few useful patterns:
5
5
  #
6
6
  # - computed properties support
7
- _.component('Luca.Model').extends('Backbone.Model').with
7
+ _.def('Luca.Model').extends('Backbone.Model').with
8
8
  initialize: ()->
9
9
  Backbone.Model::initialize @, arguments
10
10
 
@@ -4,10 +4,10 @@ class Luca.Observer
4
4
  @type = @options.type
5
5
 
6
6
  if @options.debugAll
7
- @bind "event", (t, args...)=>
8
- console.log "Observed #{ @type } #{ (t.name || t.id || t.cid) }", t, _(args).flatten()
9
-
7
+ @bind "all", (trigger, one, two)=>
8
+ console.log "ALL", trigger, one, two
10
9
  relay: (triggerer, args...)->
10
+ console.log "Relaying", trigger, args
11
11
  @trigger "event", triggerer, args
12
12
  @trigger "event:#{ args[0] }", triggerer, args.slice(1)
13
13
 
@@ -0,0 +1,143 @@
1
+ # This is a helper for creating the DOM element that go along with
2
+ # a given component, if it is configured to use one via the topToolbar
3
+ # and bottomToolbar properties
4
+ attachToolbar = (config={})->
5
+ config.orientation ||= "top"
6
+ config.ctype ||= @toolbarType || "panel_toolbar"
7
+
8
+ id = "#{ @cid }-tbc-#{ config.orientation }"
9
+
10
+ toolbar = Luca.util.lazyComponent( config )
11
+
12
+ container = @make "div",
13
+ class:"toolbar-container #{ config.orientation }",
14
+ id: id
15
+ ,
16
+ toolbar.render().el
17
+
18
+ hasBody = @bodyClassName or @bodyTagName
19
+
20
+ # there will be a body panel inside of the views $el
21
+ # so just place the toolbar before, or after the body
22
+ action = switch config.orientation
23
+ when "top", "left"
24
+ if hasBody then "before" else "prepend"
25
+ when "bottom", "right"
26
+ if hasBody then "after" else "append"
27
+
28
+ @$bodyEl()[action]( container )
29
+
30
+ # A Panel is a basic Luca.View but with Toolbar extensions
31
+ #
32
+ # In general other components should inherit from the panel class.
33
+
34
+ _.def("Luca.components.Panel").extends("Luca.View").with
35
+
36
+ topToolbar: undefined
37
+
38
+ bottomToolbar: undefined
39
+
40
+ # Load Mask will apply a transparent overlay over the form
41
+ # upon submission, with a moving progress bar which will be
42
+ # hidden upon successful response
43
+ loadMask: false
44
+ loadMaskTemplate: ["components/load_mask"]
45
+
46
+ initialize: (@options={})->
47
+ Luca.View::initialize.apply(@, arguments)
48
+
49
+ if @loadMask is true
50
+ @defer ()=>
51
+ @$el.addClass('with-mask')
52
+
53
+ if @$('.load-mask').length is 0
54
+ @loadMaskTarget().prepend Luca.template(@loadMaskTemplate, @)
55
+ @$('.load-mask').hide()
56
+ .until("after:render")
57
+
58
+ @on "enable:loadmask", @applyLoadMask
59
+ @on "disable:loadmask", @applyLoadMask
60
+
61
+ loadMaskTarget: ()->
62
+ if @loadMaskEl? then @$(@loadMaskEl) else @$bodyEl()
63
+
64
+ applyLoadMask: ()->
65
+ if @$('.load-mask').is(":visible")
66
+ @$('.load-mask .bar').css("width","100%")
67
+ @$('.load-mask').hide()
68
+ clearInterval(@loadMaskInterval)
69
+ else
70
+ @$('.load-mask').show().find('.bar').css("width","0%")
71
+ maxWidth = @$('.load-mask .progress').width()
72
+ if maxWidth < 20 and (maxWidth = @$el.width()) < 20
73
+ maxWidth = @$el.parent().width()
74
+
75
+ @loadMaskInterval = setInterval ()=>
76
+ currentWidth = @$('.load-mask .bar').width()
77
+ newWidth = currentWidth + 12
78
+ @$('.load-mask .bar').css('width', newWidth)
79
+ , 200
80
+
81
+ applyStyles: (styles={},body=false)->
82
+
83
+ target = if body then @$bodyEl() else @$el
84
+
85
+ for setting, value of styles
86
+ target.css(setting,value)
87
+
88
+ @
89
+
90
+ beforeRender: ()->
91
+ Luca.View::beforeRender?.apply(@, arguments)
92
+ @applyStyles( @styles ) if @styles?
93
+ @applyStyles( @bodyStyles, true ) if @bodyStyles?
94
+ @renderToolbars?()
95
+
96
+ $bodyEl: ()->
97
+ element = @bodyTagName || "div"
98
+ className = @bodyClassName || "view-body"
99
+
100
+ @bodyEl ||= "#{ element }.#{ className }"
101
+
102
+ bodyEl = @$(@bodyEl)
103
+
104
+ return bodyEl if bodyEl.length > 0
105
+
106
+ # if we've been configured to have one, and it doesn't exist
107
+ # then we should append it to ourselves
108
+ if bodyEl.length is 0 and (@bodyClassName? || @bodyTagName?)
109
+ newElement = @make(element,class:className,"data-auto-appended":true)
110
+ $(@el).append( newElement )
111
+ return @$(@bodyEl)
112
+
113
+
114
+ $(@el)
115
+
116
+ $wrap: (wrapper)->
117
+ if !wrapper.match(/[<>]/)
118
+ wrapper = @make("div",class:wrapper)
119
+
120
+ @$el.wrap( wrapper )
121
+
122
+ $template: (template, variables={})->
123
+ @$html( Luca.template(template,variables) )
124
+
125
+ $html: (content)->
126
+ @$bodyEl().html( content )
127
+
128
+ $append: (content)->
129
+ @$bodyEl().append(content)
130
+
131
+ # Luca containers can have toolbars,
132
+ # these will get injected before or after the bodyEl, or at the top
133
+ # or bottom of the $el
134
+ renderToolbars: ()->
135
+ _( ["top","left","right","bottom"] ).each (orientation)=>
136
+ if config = @["#{ orientation }Toolbar"]
137
+ @renderToolbar( orientation, config)
138
+
139
+ renderToolbar: (orientation="top", config={})->
140
+ config.parent = @
141
+ config.orientation = orientation
142
+
143
+ attachToolbar.call(@, config)
@@ -0,0 +1,104 @@
1
+ registry =
2
+ classes:{}
3
+ namespaces:['Luca.containers','Luca.components']
4
+
5
+ component_cache =
6
+ cid_index: {}
7
+ name_index: {}
8
+
9
+ # For container views, if a component is defined with no ctype
10
+ # then we will pick this one when using
11
+ Luca.defaultComponentType = 'view'
12
+
13
+
14
+ # When you use _.def to define a component, you say
15
+ # which class it extends() from, and with() which enhancements.
16
+
17
+ # We register that component class for you:
18
+ Luca.register = (component, prototypeName)->
19
+ Luca.trigger "component:registered", component, prototypeName
20
+ registry.classes[ component ] = prototypeName
21
+
22
+ Luca.development_mode_register = (component, prototypeName)->
23
+ existing = registry.classes[component]
24
+
25
+ if Luca.enableDevelopmentTools is true and existing?
26
+ prototypeDefinition = Luca.util.resolve( existing, window)
27
+
28
+ liveInstances = Luca.registry.findInstancesByClassName( prototypeName )
29
+
30
+ _( liveInstances ).each (instance)->
31
+ instance?.refreshCode?.call(instance, prototypeDefinition)
32
+
33
+ Luca.register( component, prototypeName )
34
+
35
+ # We create a @ctype alias for this component definition, and register
36
+ # the class in a registry.
37
+
38
+ # If you use a custom namespace like MyApp.views.ListView,
39
+ # then we will register MyApp.views as a namespace. You can
40
+ # do this yourself too.
41
+ Luca.registry.addNamespace = (identifier)->
42
+ registry.namespaces.push( identifier )
43
+ registry.namespaces = _( registry.namespaces ).uniq()
44
+
45
+ # This allows us to declare relationships between objects at definition time
46
+ # and have the instances of these objects be created at runtime when they
47
+ # are available.
48
+ #
49
+ # it also allows us to build tools to monitor what is going on inside of an
50
+ # application, which makes testing and debugging easier, and also serves as
51
+ # the basis of Luca's in browser development tools.
52
+ Luca.registry.namespaces = (resolve=true)->
53
+ _( registry.namespaces ).map (namespace)->
54
+ if resolve then Luca.util.resolve( namespace ) else namespace
55
+
56
+ # Lookup a component in the Luca component registry
57
+ # by it's ctype identifier. If it doesn't exist,
58
+ # check any other registered namespace
59
+ Luca.registry.lookup = (ctype)->
60
+ c = registry.classes[ctype]
61
+
62
+ return c if c?
63
+
64
+ className = Luca.util.classify(ctype)
65
+
66
+ parents = Luca.registry.namespaces()
67
+
68
+ fullPath = _( parents ).chain().map((parent)->
69
+ parent[className]).compact().value()?[0]
70
+
71
+ Luca.registry.findInstancesByClassName = (className)->
72
+ instances = _( component_cache.cid_index ).values()
73
+ _( instances ).select (instance)->
74
+ instance.displayName is className or instance._superClass?()?.displayName is className
75
+
76
+ Luca.registry.classes = (toString=false)->
77
+ _( registry.classes ).map (className, ctype)->
78
+ if toString
79
+ className
80
+ else
81
+ className: className
82
+ ctype: ctype
83
+
84
+ Luca.cache = (needle, component)->
85
+ component_cache.cid_index[ needle ] = component if component?
86
+
87
+ component = component_cache.cid_index[ needle ]
88
+
89
+ # optionally, cache it by tying its name to its cid for easier lookups
90
+ if component?.component_name?
91
+ Luca.trigger "component:created:#{ component.component_name }", component
92
+ component_cache.name_index[ component.component_name ] = component.cid
93
+ else if component?.name?
94
+ Luca.trigger "component:created:#{ component.component_name }", component
95
+ component_cache.name_index[ component.name ] = component.cid
96
+
97
+ return component if component?
98
+
99
+ # perform a lookup by name if the component_id didn't turn anything
100
+ lookup_id = component_cache.name_index[ needle ]
101
+
102
+ component_cache.cid_index[ lookup_id ]
103
+
104
+
@@ -0,0 +1,82 @@
1
+ # Takes an string like "deep.nested.value" and an object like window
2
+ # and returns the value of window.deep.nested.value. useful for defining
3
+ # references on objects which don't yet exist, as strings, which get
4
+ # evaluated at runtime when such references will be available
5
+ Luca.util.resolve = (accessor, source_object)->
6
+ source_object ||= (window || global)
7
+ _( accessor.split(/\./) ).inject (obj,key)->
8
+ obj = obj?[key]
9
+ , source_object
10
+
11
+ # A better name for Luca.util.nestedValue
12
+ Luca.util.nestedValue = Luca.util.resolve
13
+
14
+ # turns a word like form_view into FormView
15
+ Luca.util.classify = (string="")->
16
+ _.string.camelize( _.string.capitalize( string ) )
17
+
18
+ # looks up a method on an object by its event trigger
19
+ # in the format of what:ever => whatEver
20
+ Luca.util.hook = (eventId="")->
21
+ parts = eventId.split(':')
22
+ prefix = parts.shift()
23
+
24
+ parts = _( parts ).map (p)-> _.string.capitalize(p)
25
+ fn = prefix + parts.join('')
26
+
27
+ Luca.util.isIE = ()->
28
+ try
29
+ Object.defineProperty({}, '', {})
30
+ return false
31
+ catch e
32
+ return true
33
+
34
+ currentNamespace = (window || global)
35
+
36
+ Luca.util.namespace = (namespace)->
37
+ return currentNamespace unless namespace?
38
+ currentNamespace = if _.isString(namespace) then Luca.util.resolve(namespace,(window||global)) else namespace
39
+
40
+ if currentNamespace?
41
+ return currentNamespace
42
+
43
+ currentNamespace = eval("(window||global).#{ namespace } = {}")
44
+
45
+ # one of the main benefits of Luca is the ability to structure your app as
46
+ # large blocks of JSON configuration. In order to convert an object into
47
+ # a Luca component, we lookup the object's class by converting its ctype / type
48
+ # property into a class that has been registered in the component registry
49
+ Luca.util.lazyComponent = (config)->
50
+ if _.isObject(config)
51
+ ctype = config.ctype || config.type
52
+
53
+ if _.isString(config)
54
+ ctype = config
55
+
56
+ componentClass = Luca.registry.lookup( ctype )
57
+
58
+ throw "Invalid Component Type: #{ ctype }. Did you forget to register it?" unless componentClass
59
+
60
+ constructor = eval( componentClass )
61
+
62
+ new constructor(config)
63
+
64
+ Luca.util.selectProperties = (iterator, object, context)->
65
+ values = _( object ).values()
66
+ _( values ).select( iterator )
67
+
68
+ Luca.util.loadScript = (url, callback) ->
69
+ script = document.createElement("script")
70
+ script.type = "text/javascript"
71
+
72
+ if (script.readyState)
73
+ script.onreadystatechange = ()->
74
+ if script.readyState == "loaded" || script.readyState == "complete"
75
+ script.onreadystatechange = null
76
+ callback()
77
+ else
78
+ script.onload = ()->
79
+ callback()
80
+
81
+ script.src = url
82
+ document.body.appendChild(script)