cartilage 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (115) hide show
  1. data/MIT-LICENSE +20 -0
  2. data/README.markdown +57 -0
  3. data/Rakefile +84 -0
  4. data/app/assets/images/cartilage/patterns/background.dark.png +0 -0
  5. data/app/assets/images/cartilage/patterns/background.dark.psd +0 -0
  6. data/app/assets/images/cartilage/patterns/background.light.png +0 -0
  7. data/app/assets/images/cartilage/patterns/background.light.psd +0 -0
  8. data/app/assets/images/cartilage/patterns/background.medium.png +0 -0
  9. data/app/assets/images/cartilage/patterns/background.medium.psd +0 -0
  10. data/app/assets/images/cartilage/patterns/background.png +0 -0
  11. data/app/assets/images/cartilage/patterns/background.psd +0 -0
  12. data/app/assets/javascripts/cartilage/application.js.coffee +45 -0
  13. data/app/assets/javascripts/cartilage/collections/segments.js.coffee +4 -0
  14. data/app/assets/javascripts/cartilage/core.js.coffee +8 -0
  15. data/app/assets/javascripts/cartilage/models/model.js.coffee +2 -0
  16. data/app/assets/javascripts/cartilage/models/segment.js.coffee +2 -0
  17. data/app/assets/javascripts/cartilage/views/bar_segment_view.js.coffee +58 -0
  18. data/app/assets/javascripts/cartilage/views/bar_view.js.coffee +85 -0
  19. data/app/assets/javascripts/cartilage/views/content_view.js.coffee +16 -0
  20. data/app/assets/javascripts/cartilage/views/image_view.js.coffee +76 -0
  21. data/app/assets/javascripts/cartilage/views/list_view.js.coffee +515 -0
  22. data/app/assets/javascripts/cartilage/views/list_view_item.js.coffee +85 -0
  23. data/app/assets/javascripts/cartilage/views/loading_indicator_view.js.coffee +147 -0
  24. data/app/assets/javascripts/cartilage/views/matrix_view.js.coffee +253 -0
  25. data/app/assets/javascripts/cartilage/views/matrix_view_item.js.coffee +5 -0
  26. data/app/assets/javascripts/cartilage/views/modal_view.js.coffee +26 -0
  27. data/app/assets/javascripts/cartilage/views/popover_view.js.coffee +212 -0
  28. data/app/assets/javascripts/cartilage/views/source_list_view.js.coffee +69 -0
  29. data/app/assets/javascripts/cartilage/views/source_list_view_item.js.coffee +5 -0
  30. data/app/assets/javascripts/cartilage/views/split_view.js.coffee +175 -0
  31. data/app/assets/javascripts/cartilage/views/usage_bar_view.js.coffee +5 -0
  32. data/app/assets/javascripts/cartilage/views/view.js.coffee +232 -0
  33. data/app/assets/javascripts/cartilage.js.coffee +18 -0
  34. data/app/assets/javascripts/extensions/console.js.coffee +9 -0
  35. data/app/assets/javascripts/extensions/constructor_name.js.coffee +10 -0
  36. data/app/assets/javascripts/extensions/properties.js.coffee +45 -0
  37. data/app/assets/javascripts/extensions/underscore.js.coffee +71 -0
  38. data/app/assets/stylesheets/cartilage/core.css.scss +432 -0
  39. data/app/assets/stylesheets/cartilage/mixins.css.scss +19 -0
  40. data/app/assets/stylesheets/cartilage/responsive.css.scss +192 -0
  41. data/app/assets/stylesheets/cartilage/variables.css.scss +113 -0
  42. data/app/assets/stylesheets/cartilage/views/list_view.css.scss +41 -0
  43. data/app/assets/stylesheets/cartilage/views/list_view_item.css.scss +47 -0
  44. data/app/assets/stylesheets/cartilage/views/loading_indicator_view.css.scss +50 -0
  45. data/app/assets/stylesheets/cartilage/views/matrix_view.css.scss +87 -0
  46. data/app/assets/stylesheets/cartilage/views/popover_view.css.scss +124 -0
  47. data/app/assets/stylesheets/cartilage/views/segmented_view.css.scss +98 -0
  48. data/app/assets/stylesheets/cartilage/views/source_list_view.css.scss +65 -0
  49. data/app/assets/stylesheets/cartilage/views/split_view.css.scss +80 -0
  50. data/app/assets/stylesheets/cartilage/views/usage_bar_view.css.scss +101 -0
  51. data/app/assets/stylesheets/cartilage/views/view.css.scss +13 -0
  52. data/app/assets/stylesheets/cartilage.css.scss +5 -0
  53. data/lib/cartilage/engine.rb +14 -0
  54. data/lib/cartilage/version.rb +3 -0
  55. data/lib/cartilage.rb +5 -0
  56. data/lib/tasks/cartilage_tasks.rake +4 -0
  57. data/test/cartilage_test.rb +7 -0
  58. data/test/dummy/Rakefile +7 -0
  59. data/test/dummy/app/assets/javascripts/application.js +9 -0
  60. data/test/dummy/app/assets/stylesheets/application.css +7 -0
  61. data/test/dummy/app/controllers/application_controller.rb +3 -0
  62. data/test/dummy/app/helpers/application_helper.rb +2 -0
  63. data/test/dummy/app/views/layouts/application.html.erb +14 -0
  64. data/test/dummy/config/application.rb +45 -0
  65. data/test/dummy/config/boot.rb +10 -0
  66. data/test/dummy/config/database.yml +25 -0
  67. data/test/dummy/config/environment.rb +5 -0
  68. data/test/dummy/config/environments/development.rb +30 -0
  69. data/test/dummy/config/environments/production.rb +60 -0
  70. data/test/dummy/config/environments/test.rb +42 -0
  71. data/test/dummy/config/initializers/backtrace_silencers.rb +7 -0
  72. data/test/dummy/config/initializers/inflections.rb +10 -0
  73. data/test/dummy/config/initializers/mime_types.rb +5 -0
  74. data/test/dummy/config/initializers/secret_token.rb +7 -0
  75. data/test/dummy/config/initializers/session_store.rb +8 -0
  76. data/test/dummy/config/initializers/wrap_parameters.rb +14 -0
  77. data/test/dummy/config/locales/en.yml +5 -0
  78. data/test/dummy/config/routes.rb +58 -0
  79. data/test/dummy/config.ru +4 -0
  80. data/test/dummy/db/readme +6 -0
  81. data/test/dummy/public/404.html +26 -0
  82. data/test/dummy/public/422.html +26 -0
  83. data/test/dummy/public/500.html +26 -0
  84. data/test/dummy/public/favicon.ico +0 -0
  85. data/test/dummy/script/rails +6 -0
  86. data/test/framework/cartilage/application_test.js.coffee +14 -0
  87. data/test/framework/cartilage/views/bar_segment_view_test.js.coffee +5 -0
  88. data/test/framework/cartilage/views/bar_view_test.js.coffee +30 -0
  89. data/test/framework/cartilage/views/image_view_test.js.coffee +27 -0
  90. data/test/framework/cartilage/views/list_view_item_test.js.coffee +17 -0
  91. data/test/framework/cartilage/views/list_view_test.js.coffee +190 -0
  92. data/test/framework/cartilage/views/loading_indicator_view_test.js.coffee +5 -0
  93. data/test/framework/cartilage/views/matrix_view_item_test.js.coffee +5 -0
  94. data/test/framework/cartilage/views/matrix_view_test.js.coffee +5 -0
  95. data/test/framework/cartilage/views/popover_view_test.js.coffee +5 -0
  96. data/test/framework/cartilage/views/source_list_view_item_test.js.coffee +5 -0
  97. data/test/framework/cartilage/views/source_list_view_test.js.coffee +5 -0
  98. data/test/framework/cartilage/views/split_view_test.js.coffee +5 -0
  99. data/test/framework/cartilage/views/usage_bar_view_test.js.coffee +5 -0
  100. data/test/framework/cartilage/views/view_test.js.coffee +85 -0
  101. data/test/framework/extensions/properties_test.js.coffee +82 -0
  102. data/test/framework/extensions/underscore_test.js.coffee +73 -0
  103. data/test/framework/index.html +72 -0
  104. data/test/framework/vendor/backbone.js +1431 -0
  105. data/test/framework/vendor/coffee-script.js +8 -0
  106. data/test/framework/vendor/jquery.js +9440 -0
  107. data/test/framework/vendor/phantomjs-runner.js +196 -0
  108. data/test/framework/vendor/qunit.css +235 -0
  109. data/test/framework/vendor/qunit.js +1977 -0
  110. data/test/framework/vendor/run-qunit.js +81 -0
  111. data/test/framework/vendor/sinon-1.5.0.js +4142 -0
  112. data/test/framework/vendor/sinon-qunit-1.0.0.js +62 -0
  113. data/test/framework/vendor/underscore.js +1059 -0
  114. data/test/test_helper.rb +10 -0
  115. metadata +373 -0
@@ -0,0 +1,69 @@
1
+ #
2
+ # Source List View
3
+ #
4
+ # As the name implies, the source list view is a subclass of ListView that
5
+ # attempts to emulate the common source list master/detail pattern. It applies
6
+ # some default styling to the list view and allows for header and footer views
7
+ # to be used.
8
+ #
9
+
10
+ #= require cartilage/views/source_list_view_item
11
+
12
+ class window.Cartilage.Views.SourceListView extends Cartilage.Views.ListView
13
+
14
+ # Properties ---------------------------------------------------------------
15
+
16
+ #
17
+ # If defined, displays the specified view above the list view. Dimensions of
18
+ # the view are automatically determined, so be sure that the passed view has
19
+ # height or is relatively positioned so that height may be determined.
20
+ #
21
+ @property "headerView"
22
+
23
+ #
24
+ # If defined, displays the specified view below the list view. Dimensions of
25
+ # the view are automatically determined, so be sure that the passed view has
26
+ # height or is relatively positioned so that height may be determined.
27
+ #
28
+ @property "footerView"
29
+
30
+ #
31
+ # Override the default itemView to SourceListViewItem.
32
+ #
33
+ @property "itemView", default: Cartilage.Views.SourceListViewItem
34
+
35
+ # --------------------------------------------------------------------------
36
+
37
+ initialize: (options = {}) ->
38
+
39
+ # Initialize the List View
40
+ super(options)
41
+
42
+ # Initialize Header View Container
43
+ if @headerView
44
+ ($ @el).append @_headerViewContainer = @make "div",
45
+ class: "header-view"
46
+
47
+ # Initialize Footer View Container
48
+ if @footerView
49
+ ($ @el).append @_footerViewContainer = @make "div",
50
+ class: "footer-view"
51
+
52
+ prepare: ->
53
+
54
+ # Prepare the List View
55
+ super()
56
+
57
+ # Add the header view as a subview and reposition the list view to account
58
+ # for the height of the header view.
59
+ if @headerView
60
+ @addSubview @headerView, @_headerViewContainer
61
+ ($ @_listViewItemsContainer).css("top", ($ @headerView.el).outerHeight())
62
+ ($ @_headerViewContainer).css("height", ($ @headerView.el).outerHeight())
63
+
64
+ # Add the footer view as a subview and reposition the list view to account
65
+ # for the height of the footer view.
66
+ if @footerView
67
+ @addSubview @footerView, @_footerViewContainer
68
+ ($ @_listViewItemsContainer).css("bottom", ($ @footerView.el).outerHeight())
69
+ ($ @_footerViewContainer).css("height", ($ @footerView.el).outerHeight())
@@ -0,0 +1,5 @@
1
+ #
2
+ # Source List View Item
3
+ #
4
+
5
+ class window.Cartilage.Views.SourceListViewItem extends Cartilage.Views.ListViewItem
@@ -0,0 +1,175 @@
1
+ #
2
+ # Split View
3
+ #
4
+ # Manages a split view layout with either two side-by-side views or views
5
+ # stacked on top and bottom.
6
+ #
7
+
8
+ class window.Cartilage.Views.SplitView extends Cartilage.View
9
+
10
+ # Properties ---------------------------------------------------------------
11
+
12
+ #
13
+ # The first view in split view. This is the view on the left when the
14
+ # orientation is set to vertical or the view on the top when set to
15
+ # horizontal.
16
+ #
17
+ @property "firstView", default: null
18
+
19
+ #
20
+ # The second view in split view. This is the view on the right when the
21
+ # orientation is set to vertical or the view on the bottom when set to
22
+ # horizontal.
23
+ #
24
+ @property "secondView", default: null
25
+
26
+ #
27
+ # The orientation of the split view, either "horizontal" or "vertical".
28
+ # Defaults to "vertical".
29
+ #
30
+ @property "orientation", default: "vertical"
31
+
32
+ #
33
+ # Whether or not the split view is resizable. Defaults to true.
34
+ #
35
+ @property "isResizable", default: yes
36
+
37
+ # Internal Properties ------------------------------------------------------
38
+
39
+ # Events -------------------------------------------------------------------
40
+
41
+ events:
42
+ "mousedown .drag-handle": "onMouseDown"
43
+ "mouseup": "onMouseUp",
44
+ "mousemove": "onMouseMove"
45
+
46
+ # --------------------------------------------------------------------------
47
+
48
+ #
49
+ # Initializes a new instance of the split view. Pass firstView and
50
+ # secondView as options, at a minimum, to specify the view setup.
51
+ #
52
+ initialize: (options = {}) ->
53
+
54
+ # Initialize View Containers
55
+ ($ @el).append @_firstViewContainer = @make "div",
56
+ class: "first-view"
57
+ ($ @el).append @_secondViewContainer = @make "div",
58
+ class: "second-view"
59
+
60
+ # Initialize Drag Handle
61
+ if @isResizable
62
+ ($ @el).append @dragElement = @make "div",
63
+ class: "drag-handle"
64
+
65
+ # Set Orientation
66
+ ($ @el).addClass @orientation
67
+
68
+ # Observe for window resize events and re-render the view when it occurs...
69
+ ($ window).on "resize", @handleWindowResize
70
+
71
+ super(options)
72
+
73
+ prepare: ->
74
+
75
+ super()
76
+
77
+ @addSubview @firstView, @_firstViewContainer
78
+ @addSubview @secondView, @_secondViewContainer
79
+
80
+ if @orientation is "vertical"
81
+ @position(($ @_firstViewContainer).width())
82
+ else
83
+ @position(0)
84
+
85
+ cleanup: ->
86
+ ($ window).off "resize", @handleWindowResize
87
+ @firstView.cleanup() if @firstView and @firstView.cleanup
88
+ @secondView.cleanup() if @secondView and @secondView.cleanup
89
+ super()
90
+
91
+ #
92
+ # Sets the position of the divider that separates the two views. In the case
93
+ # of a horizontal split view, this is the height of the secondView. For
94
+ # vertical split views, this is the width of the firstView.
95
+ #
96
+ position: (newPosition, options = {}) =>
97
+ return @_currentPosition unless newPosition?
98
+
99
+ if @orientation is "vertical"
100
+ minWidth = parseInt ($ @_firstViewContainer).css("min-width")
101
+ maxWidth = parseInt ($ @_firstViewContainer).css("max-width")
102
+
103
+ if newPosition < minWidth
104
+ newPosition = minWidth
105
+ else if newPosition > maxWidth
106
+ newPosition = maxWidth
107
+
108
+ if options["animated"]
109
+ ($ @_firstViewContainer).css { left: '' }
110
+ ($ @_firstViewContainer).animate { width: newPosition + 'px' }, 250
111
+ ($ @_secondViewContainer).css { width: '' }
112
+ ($ @_secondViewContainer).animate { left: newPosition + 'px' }, 250
113
+ else
114
+ ($ @_firstViewContainer).css { width: newPosition + 'px', left: '' }
115
+ ($ @_secondViewContainer).css { left: newPosition + 'px', width: '' }
116
+
117
+ ($ @dragElement).css("left", (newPosition - ($ @dragElement).width() / 2) + "px")
118
+
119
+ else
120
+ minHeight = parseInt ($ @_secondViewContainer).css("min-height")
121
+ maxHeight = parseInt ($ @_secondViewContainer).css("max-height")
122
+
123
+ if newPosition < minHeight
124
+ newPosition = minHeight
125
+ else if newPosition > maxHeight
126
+ newPosition = maxHeight
127
+
128
+ bottom = ($ @el).height() - newPosition
129
+
130
+ if options["animated"]
131
+ ($ @_firstViewContainer).css { bottom: '' }
132
+ ($ @_firstViewContainer).animate { height: bottom + 'px' }, 250
133
+ ($ @_secondViewContainer).css { height: '' }
134
+ ($ @_secondViewContainer).animate { top: bottom + "px" }, 250
135
+ else
136
+ ($ @_firstViewContainer).css { height: bottom + 'px', bottom: '' }
137
+ ($ @_secondViewContainer).css { top: bottom + 'px', height: newPosition + 'px' }
138
+
139
+ ($ @dragElement).css("top", (newPosition - ($ @dragElement).height() / 2) + "px")
140
+
141
+ @_currentPosition = newPosition
142
+ @trigger("resize")
143
+
144
+ handleWindowResize: (event) =>
145
+ @position(@_currentPosition) if @_currentPosition?
146
+
147
+ setFirstView: (view) ->
148
+ @firstView.removeFromSuperview() if @firstView
149
+ @firstView = view
150
+ @addSubview view, @_firstViewContainer
151
+
152
+ setSecondView: (view) ->
153
+ @secondView.removeFromSuperview() if @secondView
154
+ @secondView = view
155
+ @addSubview view, @_secondViewContainer
156
+
157
+ onMouseDown: (event) =>
158
+ return unless @isResizable
159
+ @isResizing = true
160
+
161
+ onMouseUp: (event) =>
162
+ return unless @isResizable
163
+ @isResizing = false
164
+
165
+ onMouseMove: (event) =>
166
+ return unless @isResizable and @isResizing
167
+
168
+ event.preventDefault()
169
+
170
+ if @orientation is "vertical"
171
+ offset = ($ @el).offset().left
172
+ @position((event.pageX - (($ @dragElement).width() / 2)) - offset)
173
+ else
174
+ offset = ($ @el).offset().top
175
+ @position((event.pageY - (($ @dragElement).height() / 2)) - offset)
@@ -0,0 +1,5 @@
1
+ #
2
+ # Usage Bar View
3
+ #
4
+
5
+ class window.Cartilage.Views.UsageBarView extends Cartilage.Views.BarView
@@ -0,0 +1,232 @@
1
+ #
2
+ # Cartilage "Base" View
3
+ #
4
+
5
+ class window.Cartilage.View extends Backbone.View
6
+
7
+ # Properties ---------------------------------------------------------------
8
+
9
+ #
10
+ # The superview that considers this view to be a subview. This will be set
11
+ # automatically on the view that has been added by using addSubview.
12
+ #
13
+ @property "superview", access: READONLY
14
+
15
+ #
16
+ # An array containing a reference to each of the views subviews. Views that
17
+ # are added as subviews (with addSubview) will be added here automatically.
18
+ #
19
+ @property "subviews", access: READONLY, default: []
20
+
21
+ #
22
+ # A reference to all observers currently assigned to this view. This is
23
+ # needed for cleanup.
24
+ #
25
+ @property "observers", access: READONLY, default: []
26
+
27
+ # --------------------------------------------------------------------------
28
+
29
+ #
30
+ # Attempt to determine the name of the template (which, by default, should
31
+ # be the underscored-version of a class name in the templates folder (i.e.
32
+ # for MyAwesomeView, the template my_awesome_view.jst.ejs should exist in
33
+ # templates).
34
+ #
35
+ template: (options) ->
36
+ try
37
+ if JST[_.underscore(@constructor.name)]
38
+ JST[_.underscore(@constructor.name)](options)
39
+ else
40
+ if console
41
+ console.info "Missing template #{_.underscore(@constructor.name)}.jst.[eco|ejs] for #{@constructor.name}"
42
+ catch error
43
+ if console
44
+ console.error "Template error in #{_.underscore(@constructor.name)}.jst.[eco|ejs]: \"#{error.message}\"", error
45
+
46
+ #
47
+ # Override the standard constructor so that we can extend each view with any
48
+ # of the options passed, instead of simply @collection, @model and others
49
+ # that are baked into Backbone.js.
50
+ #
51
+ constructor: (options = {}) ->
52
+ _.extend(@, options)
53
+ Backbone.View.apply(@, arguments)
54
+
55
+ initialize: (options = {}) ->
56
+ _.extend(@, options)
57
+
58
+ prepare: ->
59
+ # Empty implementation
60
+
61
+ render: ->
62
+
63
+ # Determine CSS class names from the view class hierarchy and any other
64
+ # custom classes defined by a subclass.
65
+ ($ @el).addClass @determineClassName()
66
+
67
+ # Automatically assign collections and models to the template, as their
68
+ # respective class names.
69
+ @templateVariables ?= {}
70
+ if @collection
71
+ @templateVariables[_.camelize(@collection.constructor.name)] = @collection
72
+ if @model
73
+ @templateVariables[_.camelize(@model.constructor.name)] = @model
74
+ ($ @el).html @template(@templateVariables)
75
+
76
+ @
77
+
78
+ cleanup: ->
79
+ @off()
80
+ @removeObservers()
81
+ $(@el).remove()
82
+
83
+ observe: (source, event, callback) ->
84
+ source.on(event, callback, @)
85
+ @_observers.push { source: source, event: event, callback: callback }
86
+
87
+ removeObservers: ->
88
+ _.each @observers, (observer) ->
89
+ observer.source.off(observer.event, observer.callback, @)
90
+ @_observers = []
91
+
92
+ #
93
+ # Adds the provided view instance as a subclass of the current view. If a
94
+ # container element is specified, it will be added within that element of
95
+ # the view's element.
96
+ #
97
+ addSubview: (view, container = @el, animated = false) ->
98
+ # Don't allow nil objects to be passed...
99
+ return unless view
100
+
101
+ # Set a reference to this view as the added view's superview
102
+ view._superview = @
103
+
104
+ # Maintain a reference to the added subview for later cleanup, or other
105
+ # operations
106
+ @storeSubview(view)
107
+
108
+ # Render the View, if necessary
109
+ unless view.isRendered
110
+ view.render()
111
+ view.isRendered = true
112
+ view.trigger("rendered")
113
+
114
+ # Make the View's Element invisible
115
+ ($ view.el).css("visibility", "hidden").show()
116
+
117
+ # Add the View's Element to the DOM
118
+ @insertSubviewElement(view, container)
119
+
120
+ # Prepare the View for display, if necessary
121
+ unless view.isPrepared
122
+ view.prepare() if view.prepare
123
+ view.isPrepared = true
124
+ view.trigger("prepared")
125
+
126
+ # Trigger the "willPresent" event so that views have a chance to
127
+ # manipulate their state before being presented to the user
128
+ view.trigger("willPresent")
129
+
130
+ # Make the View's Element visible and trigger the "presented" event so
131
+ # views have a chance to manipulate their state after being presented
132
+ # to the user
133
+ if animated
134
+ ($ view.el).hide()
135
+ ($ view.el).css("visibility", "visible")
136
+ ($ view.el).fadeIn 750, -> view.trigger("presented")
137
+
138
+ else
139
+ ($ view.el).css("visibility", "visible")
140
+ view.trigger("presented")
141
+
142
+ addSubviewAnimated: (view, container = @el) ->
143
+ @addSubview(view, container, true)
144
+
145
+ insertSubviewElement: (view, container) ->
146
+ ($ container).append(view.el)
147
+
148
+ storeSubview: (view) ->
149
+ @_subviews.push(view)
150
+
151
+ #
152
+ # Removes the view from its superview, if it currently belongs to another
153
+ # view as a subview.
154
+ #
155
+ removeFromSuperview: (animated = false) ->
156
+
157
+ # Trigger the "willRemove" event so that views have a chance to
158
+ # manipulate their state before being removed from view
159
+ @trigger("willRemove")
160
+
161
+ # Remove observers for all DOM events
162
+ @off()
163
+
164
+ # Remove all other event observers
165
+ @removeObservers()
166
+
167
+ # Remove the View's Element from the DOM and trigger the "removed" event
168
+ # so views have a chance to manipulate their state after being removed
169
+ if animated
170
+ ($ @el).fadeOut 750, =>
171
+ @trigger("removed")
172
+ ($ @el).detach()
173
+
174
+ else
175
+ ($ @el).hide()
176
+ @trigger("removed")
177
+ ($ @el).detach()
178
+
179
+ # Remove the reference to this view from its superview's subviews
180
+ # array
181
+ _.remove @_superview._subviews, @
182
+
183
+ # Clear the superview reference
184
+ @_superview = null
185
+
186
+ removeFromSuperviewAnimated: ->
187
+ @removeFromSuperview(true)
188
+
189
+ #
190
+ # Attempts to determine the class name(s) of the view by iterating and
191
+ # assigning the dasherized name of each of the class in the hierarchy.
192
+ #
193
+ determineClassName: ->
194
+ classNames = [ _.dasherize(@constructor.name) ]
195
+
196
+ # Get class names for each view in the class heirarchy
197
+ # TODO Update this to use @superclasses()
198
+ superklass = @constructor.__super__
199
+ while superklass
200
+ classNames.push _.dasherize(superklass.constructor.name)
201
+ superklass = superklass.constructor.__super__
202
+
203
+ # Reverse the order of the class names
204
+ classNames = classNames.reverse()
205
+
206
+ # Merge in custom class name(s)
207
+ if _.isFunction(@className)
208
+ classNames.push @className().split(" ")
209
+ else if @className
210
+ classNames.push @className.split(" ")
211
+
212
+ # Flatten the Array
213
+ classNames = _.flatten(classNames)
214
+
215
+ # Compact the Array to remove any empty values
216
+ classNames = _.compact(classNames)
217
+
218
+ # Remove any duplicate class names
219
+ classNames = _.unique(classNames)
220
+
221
+ # Convert the class names array to a string
222
+ classNames.join(" ")
223
+
224
+ #
225
+ # Returns an array containing each of the superclasses that this class
226
+ # derives from.
227
+ #
228
+ @superclasses: ->
229
+ classes = [ superklass = @.__super__ ]
230
+ while superklass = superklass.constructor.__super__
231
+ classes.push superklass
232
+ classes.reverse()
@@ -0,0 +1,18 @@
1
+ #
2
+ #= require_tree ./extensions
3
+ #
4
+ #= require jquery-points
5
+ #
6
+ #= require cartilage/core
7
+ #
8
+ #= require cartilage/models/model
9
+ #= require cartilage/views/view
10
+ #
11
+ #= require_tree ./cartilage/models
12
+ #= require_tree ./cartilage/collections
13
+ #= require_tree ./cartilage/views
14
+ #
15
+ #= require cartilage/application
16
+ #
17
+ #= require_self
18
+ #
@@ -0,0 +1,9 @@
1
+ # Add a no-op version of the console object so calls to
2
+ # console won't raise errors in IE < 10.
3
+ if typeof(console) == "undefined"
4
+ window.console =
5
+ log: () ->
6
+ debug: () ->
7
+ info: () ->
8
+ warn: () ->
9
+ error: () ->
@@ -0,0 +1,10 @@
1
+ # Hack in support for constructor.name for IE >= 9
2
+ # http://matt.scharley.me/2012/03/09/monkey-patch-name-ie.html
3
+ # http://stackoverflow.com/questions/332422/how-do-i-get-the-name-of-an-objects-type-in-javascript
4
+ if Function.prototype.name == undefined and Object.defineProperty != undefined
5
+ Object.defineProperty( Function.prototype,'name',
6
+ get: () ->
7
+ funcNameRegex = /function\s([^(]{1,})\(/
8
+ results = (funcNameRegex).exec(@toString())
9
+ return if results and results.length > 1 then results[1].trim() else ""
10
+ set: (value) -> {} )
@@ -0,0 +1,45 @@
1
+
2
+ @READONLY = 1
3
+ @READWRITE = 2
4
+
5
+ Function::property = (name, options = {}) ->
6
+
7
+ options.access ?= (READONLY | READWRITE)
8
+ options.default ?= null
9
+ options.internal_accessor ?= "_#{name}"
10
+ options.variable ?= "__#{name}"
11
+ options.get ?= null
12
+ options.set ?= null
13
+
14
+ readable = options.access & READONLY
15
+ writeable = options.access & READWRITE
16
+
17
+ getter = options.get or ->
18
+ if !_.has(@, options.variable) and _.isObject(@[options.variable]) and !_.isFunction(@[options.variable])
19
+ @[options.variable] = _.clone(@[options.variable])
20
+ @[options.variable]
21
+
22
+ setter = options.set or (value) ->
23
+ @[options.variable] = value
24
+
25
+ public_config =
26
+ writeable: writeable
27
+ get: getter if readable
28
+ set: if writeable then setter else ()->
29
+ configurable: no
30
+ enumerable: yes
31
+
32
+ internal_config =
33
+ writeable: true
34
+ get: getter
35
+ set: setter
36
+ configurable: no
37
+ enumerable: yes
38
+
39
+ # Create the public accessor
40
+ Object.defineProperty @prototype, name, public_config
41
+
42
+ # Create the internal accessor
43
+ Object.defineProperty @prototype, options.internal_accessor, internal_config
44
+
45
+ @prototype[options.variable] = options.default
@@ -0,0 +1,71 @@
1
+
2
+ #
3
+ # Iterates over an array of numbers and returns the sum. Example:
4
+ #
5
+ # _.sum([1, 2, 3]) => 6
6
+ #
7
+ _.sum = (obj) ->
8
+ return 0 if !_.isArray(obj) or obj.length is 0
9
+ _.reduce obj, (sum, n) -> sum += parseInt(n, 10)
10
+
11
+ #
12
+ # Converts underscored or camel-cased strings to dashes. Example:
13
+ #
14
+ # _.dasherize("FooBarView") => "foo-bar-view"
15
+ # _.dasherize("foo_bar_view") => "foo-bar-view"
16
+ #
17
+ _.dasherize = (string) ->
18
+ return unless string
19
+
20
+ # Remove Camelization
21
+ string = string.replace /([A-Z])/g, (match) -> "-#{match.toLowerCase()}"
22
+
23
+ # Convert Underscores to Dashes
24
+ string = string.replace /_/g, "-"
25
+
26
+ # Remove Leading Dash
27
+ string.replace /^-/, ""
28
+
29
+ #
30
+ # Converts title-cased, underscored or dashed strings to camel-case. Example:
31
+ #
32
+ # _.camelize("FooBarView") => "fooBarView"
33
+ # _.camelize("foo-bar-view") => "fooBarView"
34
+ # _.camelize("foo_bar_view") => "fooBarView"
35
+ #
36
+ _.camelize = (string) ->
37
+ return unless string
38
+
39
+ # Remove Camelization
40
+ string = string.replace /((_|-)([a-z]))/g, (match) -> match[1].toUpperCase()
41
+
42
+ # Convert First Letter to Lowercase
43
+ string = string.replace /^([A-Z])/g, (match) -> match.toLowerCase()
44
+
45
+ #
46
+ # Converts dashed, title-cased or camel-cased strings underscores. Example:
47
+ #
48
+ # _.underscore("FooBarView") => "foo_bar_view"
49
+ # _.underscore("foo-bar-view") => "foo_bar_view"
50
+ #
51
+ _.underscore = (string) ->
52
+ return unless string
53
+
54
+ # Remove Camelization
55
+ string = string.replace /([A-Z])/g, (match) -> "_#{match.toLowerCase()}"
56
+
57
+ # Convert Dashes to Underscores
58
+ string = string.replace /-/g, "_"
59
+
60
+ # Remove Leading Underscore
61
+ string.replace /^_/, ""
62
+
63
+ #
64
+ # Removes the specified value from the given array by altering the array
65
+ # in place, without making a copy.
66
+ #
67
+ # _.remove([ "A", "B", "C" ], "B") => [ "A", "C" ]
68
+ #
69
+ _.remove = (array, value) ->
70
+ index = array.indexOf(value)
71
+ array.splice(index, 1) unless index is -1