cartilage 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
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