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.
- data/MIT-LICENSE +20 -0
- data/README.markdown +57 -0
- data/Rakefile +84 -0
- data/app/assets/images/cartilage/patterns/background.dark.png +0 -0
- data/app/assets/images/cartilage/patterns/background.dark.psd +0 -0
- data/app/assets/images/cartilage/patterns/background.light.png +0 -0
- data/app/assets/images/cartilage/patterns/background.light.psd +0 -0
- data/app/assets/images/cartilage/patterns/background.medium.png +0 -0
- data/app/assets/images/cartilage/patterns/background.medium.psd +0 -0
- data/app/assets/images/cartilage/patterns/background.png +0 -0
- data/app/assets/images/cartilage/patterns/background.psd +0 -0
- data/app/assets/javascripts/cartilage/application.js.coffee +45 -0
- data/app/assets/javascripts/cartilage/collections/segments.js.coffee +4 -0
- data/app/assets/javascripts/cartilage/core.js.coffee +8 -0
- data/app/assets/javascripts/cartilage/models/model.js.coffee +2 -0
- data/app/assets/javascripts/cartilage/models/segment.js.coffee +2 -0
- data/app/assets/javascripts/cartilage/views/bar_segment_view.js.coffee +58 -0
- data/app/assets/javascripts/cartilage/views/bar_view.js.coffee +85 -0
- data/app/assets/javascripts/cartilage/views/content_view.js.coffee +16 -0
- data/app/assets/javascripts/cartilage/views/image_view.js.coffee +76 -0
- data/app/assets/javascripts/cartilage/views/list_view.js.coffee +515 -0
- data/app/assets/javascripts/cartilage/views/list_view_item.js.coffee +85 -0
- data/app/assets/javascripts/cartilage/views/loading_indicator_view.js.coffee +147 -0
- data/app/assets/javascripts/cartilage/views/matrix_view.js.coffee +253 -0
- data/app/assets/javascripts/cartilage/views/matrix_view_item.js.coffee +5 -0
- data/app/assets/javascripts/cartilage/views/modal_view.js.coffee +26 -0
- data/app/assets/javascripts/cartilage/views/popover_view.js.coffee +212 -0
- data/app/assets/javascripts/cartilage/views/source_list_view.js.coffee +69 -0
- data/app/assets/javascripts/cartilage/views/source_list_view_item.js.coffee +5 -0
- data/app/assets/javascripts/cartilage/views/split_view.js.coffee +175 -0
- data/app/assets/javascripts/cartilage/views/usage_bar_view.js.coffee +5 -0
- data/app/assets/javascripts/cartilage/views/view.js.coffee +232 -0
- data/app/assets/javascripts/cartilage.js.coffee +18 -0
- data/app/assets/javascripts/extensions/console.js.coffee +9 -0
- data/app/assets/javascripts/extensions/constructor_name.js.coffee +10 -0
- data/app/assets/javascripts/extensions/properties.js.coffee +45 -0
- data/app/assets/javascripts/extensions/underscore.js.coffee +71 -0
- data/app/assets/stylesheets/cartilage/core.css.scss +432 -0
- data/app/assets/stylesheets/cartilage/mixins.css.scss +19 -0
- data/app/assets/stylesheets/cartilage/responsive.css.scss +192 -0
- data/app/assets/stylesheets/cartilage/variables.css.scss +113 -0
- data/app/assets/stylesheets/cartilage/views/list_view.css.scss +41 -0
- data/app/assets/stylesheets/cartilage/views/list_view_item.css.scss +47 -0
- data/app/assets/stylesheets/cartilage/views/loading_indicator_view.css.scss +50 -0
- data/app/assets/stylesheets/cartilage/views/matrix_view.css.scss +87 -0
- data/app/assets/stylesheets/cartilage/views/popover_view.css.scss +124 -0
- data/app/assets/stylesheets/cartilage/views/segmented_view.css.scss +98 -0
- data/app/assets/stylesheets/cartilage/views/source_list_view.css.scss +65 -0
- data/app/assets/stylesheets/cartilage/views/split_view.css.scss +80 -0
- data/app/assets/stylesheets/cartilage/views/usage_bar_view.css.scss +101 -0
- data/app/assets/stylesheets/cartilage/views/view.css.scss +13 -0
- data/app/assets/stylesheets/cartilage.css.scss +5 -0
- data/lib/cartilage/engine.rb +14 -0
- data/lib/cartilage/version.rb +3 -0
- data/lib/cartilage.rb +5 -0
- data/lib/tasks/cartilage_tasks.rake +4 -0
- data/test/cartilage_test.rb +7 -0
- data/test/dummy/Rakefile +7 -0
- data/test/dummy/app/assets/javascripts/application.js +9 -0
- data/test/dummy/app/assets/stylesheets/application.css +7 -0
- data/test/dummy/app/controllers/application_controller.rb +3 -0
- data/test/dummy/app/helpers/application_helper.rb +2 -0
- data/test/dummy/app/views/layouts/application.html.erb +14 -0
- data/test/dummy/config/application.rb +45 -0
- data/test/dummy/config/boot.rb +10 -0
- data/test/dummy/config/database.yml +25 -0
- data/test/dummy/config/environment.rb +5 -0
- data/test/dummy/config/environments/development.rb +30 -0
- data/test/dummy/config/environments/production.rb +60 -0
- data/test/dummy/config/environments/test.rb +42 -0
- data/test/dummy/config/initializers/backtrace_silencers.rb +7 -0
- data/test/dummy/config/initializers/inflections.rb +10 -0
- data/test/dummy/config/initializers/mime_types.rb +5 -0
- data/test/dummy/config/initializers/secret_token.rb +7 -0
- data/test/dummy/config/initializers/session_store.rb +8 -0
- data/test/dummy/config/initializers/wrap_parameters.rb +14 -0
- data/test/dummy/config/locales/en.yml +5 -0
- data/test/dummy/config/routes.rb +58 -0
- data/test/dummy/config.ru +4 -0
- data/test/dummy/db/readme +6 -0
- data/test/dummy/public/404.html +26 -0
- data/test/dummy/public/422.html +26 -0
- data/test/dummy/public/500.html +26 -0
- data/test/dummy/public/favicon.ico +0 -0
- data/test/dummy/script/rails +6 -0
- data/test/framework/cartilage/application_test.js.coffee +14 -0
- data/test/framework/cartilage/views/bar_segment_view_test.js.coffee +5 -0
- data/test/framework/cartilage/views/bar_view_test.js.coffee +30 -0
- data/test/framework/cartilage/views/image_view_test.js.coffee +27 -0
- data/test/framework/cartilage/views/list_view_item_test.js.coffee +17 -0
- data/test/framework/cartilage/views/list_view_test.js.coffee +190 -0
- data/test/framework/cartilage/views/loading_indicator_view_test.js.coffee +5 -0
- data/test/framework/cartilage/views/matrix_view_item_test.js.coffee +5 -0
- data/test/framework/cartilage/views/matrix_view_test.js.coffee +5 -0
- data/test/framework/cartilage/views/popover_view_test.js.coffee +5 -0
- data/test/framework/cartilage/views/source_list_view_item_test.js.coffee +5 -0
- data/test/framework/cartilage/views/source_list_view_test.js.coffee +5 -0
- data/test/framework/cartilage/views/split_view_test.js.coffee +5 -0
- data/test/framework/cartilage/views/usage_bar_view_test.js.coffee +5 -0
- data/test/framework/cartilage/views/view_test.js.coffee +85 -0
- data/test/framework/extensions/properties_test.js.coffee +82 -0
- data/test/framework/extensions/underscore_test.js.coffee +73 -0
- data/test/framework/index.html +72 -0
- data/test/framework/vendor/backbone.js +1431 -0
- data/test/framework/vendor/coffee-script.js +8 -0
- data/test/framework/vendor/jquery.js +9440 -0
- data/test/framework/vendor/phantomjs-runner.js +196 -0
- data/test/framework/vendor/qunit.css +235 -0
- data/test/framework/vendor/qunit.js +1977 -0
- data/test/framework/vendor/run-qunit.js +81 -0
- data/test/framework/vendor/sinon-1.5.0.js +4142 -0
- data/test/framework/vendor/sinon-qunit-1.0.0.js +62 -0
- data/test/framework/vendor/underscore.js +1059 -0
- data/test/test_helper.rb +10 -0
- 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,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,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,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
|