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