neat-rails 0.0.2

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.
@@ -0,0 +1,15 @@
1
+ $.fn.extend
2
+ cssHover: (selector)->
3
+ if arguments.length == 0
4
+ @hover(
5
+ -> $(@).addClass('hovered'),
6
+ -> $(@).removeClass('hovered'))
7
+ else
8
+ @delegate selector, 'hover', (e)->
9
+ if e.type == 'mouseenter'
10
+ $(@).addClass('hovered')
11
+ else
12
+ $(@).removeClass('hovered')
13
+
14
+ isIn: (selector)->
15
+ @is(selector) or (@parents(selector).length > 0)
@@ -0,0 +1,204 @@
1
+ var Lail; if(!Lail) Lail={};
2
+ Lail.PaginatedList = function(list, options) {
3
+ // todo: paste John Resig's code
4
+
5
+ options = options || {};
6
+ var self = this;
7
+
8
+ this.set = [];
9
+ this.set_length = 0;
10
+ this.current_page = 1;
11
+ this.page_size = options.page_size || 20;
12
+ this.always_show = (typeof options.always_show == 'undefined') ? true : options.always_show;
13
+ this.page_count = 1;
14
+ this.observer = new Observer();
15
+ this.pagination_container = null;
16
+ this.during_init = false;
17
+
18
+
19
+
20
+ this.count = function() {
21
+ return self.set_length;
22
+ }
23
+
24
+
25
+
26
+ this.init = function(list, initialPage, options) {
27
+ if((options || {}).page_size) {
28
+ self.page_size = options.page_size;
29
+ }
30
+ self.during_init = true;
31
+ self.set = list;
32
+ self.set_length = self.set.length;
33
+ self.page_count = parseInt(Math.ceil(self.set_length / self.page_size));
34
+ if(self.page_count < 1) {
35
+ self.page_count = 1;
36
+ }
37
+ self.current_page = 0;
38
+ self.gotoPage(initialPage || 1);
39
+ self.during_init = false;
40
+ }
41
+
42
+ this.gotoPage = function(page_number) {
43
+ page_number = +page_number; // needs to be an integer
44
+ if(page_number < 1) page_number = 1;
45
+ if(page_number > self.page_count) page_number = self.page_count;
46
+ if(page_number != self.current_page) __setCurrentPage(page_number);
47
+ }
48
+
49
+ function __setCurrentPage(page_number) {
50
+ self.current_page = page_number;
51
+ self.current_set = self.set.slice(self.firstItemIndex(), self.lastItemIndex());
52
+ renderPagination();
53
+ notifyOfPageChange();
54
+ }
55
+
56
+ this.firstItemIndex = function() {
57
+ return (self.current_page - 1) * self.page_size || 0;
58
+ }
59
+
60
+ this.lastItemIndex = function() {
61
+ var end = self.current_page * self.page_size;
62
+ return (end > self.set_length) ? self.set_length : end;
63
+ }
64
+
65
+ this.getCurrentPage = function() {
66
+ return self.current_page;
67
+ }
68
+
69
+ this.getCurrentSet = function() {
70
+ return self.current_set;
71
+ }
72
+
73
+ this.getEntireSet = function() {
74
+ return self.set;
75
+ }
76
+
77
+ function renderPagination() {
78
+ if(self.pagination_container) {
79
+ self.pagination_container.html(self.renderPagination());
80
+ }
81
+ }
82
+
83
+ function notifyOfPageChange() {
84
+ self.observer.fire('changed', {onInit: self.during_init});
85
+ }
86
+
87
+
88
+
89
+ this.onPageChange = function(callback) {
90
+ self.observer.observe('changed', callback);
91
+ }
92
+
93
+
94
+
95
+ this.gotoNextPage = function() {
96
+ if(self.isLastPage()) {
97
+ return false;
98
+ } else {
99
+ self.gotoPage(self.current_page + 1);
100
+ return true;
101
+ }
102
+ }
103
+
104
+ this.getNextPageNumber = function() {
105
+ return self.isLastPage() ? self.current_page : (self.current_page + 1);
106
+ }
107
+
108
+ this.isLastPage = function() {
109
+ return (self.current_page == self.page_count);
110
+ }
111
+
112
+
113
+
114
+ this.gotoPreviousPage = function() {
115
+ if(self.isFirstPage()) {
116
+ return false;
117
+ } else {
118
+ self.gotoPage(self.current_page - 1);
119
+ return true;
120
+ }
121
+ }
122
+
123
+ this.getPreviousPageNumber = function() {
124
+ return self.isFirstPage() ? self.current_page : (self.current_page - 1);
125
+ }
126
+
127
+ this.isFirstPage = function() {
128
+ return (self.current_page == 1);
129
+ }
130
+
131
+
132
+
133
+ // !nb: uses jQuery!!!
134
+ this.renderPaginationIn = function(selector) {
135
+ self.pagination_container = jQuery(selector);
136
+ self.pagination_container.delegate('a', 'click', function(e) {
137
+ e.preventDefault();
138
+ e.stopImmediatePropagation();
139
+ var a = jQuery(this);
140
+ if(a.hasClass('prev_page')) {
141
+ self.gotoPreviousPage();
142
+ } else if(a.hasClass('next_page')) {
143
+ self.gotoNextPage();
144
+ } else if(a.hasClass('goto_page')) {
145
+ self.gotoPage(parseInt(a.attr('id').substring(5)));
146
+ }
147
+ return false;
148
+ });
149
+ }
150
+
151
+ // !todo: use Handlebars
152
+ this.renderPagination = function() {
153
+ var html = '',
154
+ current = self.current_page,
155
+ count = self.page_count,
156
+ min = 1,
157
+ max = count;
158
+
159
+ if(this.always_show || count > 1) {
160
+ if(self.isFirstPage()) {
161
+ html += '<span class="prev_page disabled">&#171; Previous</span> ';
162
+ } else {
163
+ html += '<a class="prev_page" href="#" rel="previous">&#171; Previous</a> ';
164
+ }
165
+
166
+ // list no more than 7 page numbers
167
+ if(self.page_count > 7) {
168
+ min = current - 3;
169
+ max = current + 3;
170
+ var shift = (min < 1) ? (1 - min) : ((max > count) ? (count - max) : 0);
171
+ min += shift;
172
+ max += shift;
173
+ }
174
+ for(var i=min; i<=max; i++) {
175
+ if(i == self.current_page) {
176
+ html += ' <span class="currentPage">' + i + '</span> ';
177
+ } else {
178
+ html += ' <a class="goto_page" href="#" id="page_' + i + '">' + i + '</a> ';
179
+ }
180
+ }
181
+
182
+ if(self.isLastPage()) {
183
+ html += ' <span class="next_page disabled">Next &#187;</span>';
184
+ } else {
185
+ html += ' <a class="next_page" href="#" rel="next">Next &#187;</a>';
186
+ }
187
+ }
188
+
189
+ return html;
190
+ }
191
+
192
+ // !todo: use handlebars
193
+ this.renderExtendedPagination = function() {
194
+ var html = 'Listing <strong>';
195
+ if(self.page_count > 1) {
196
+ html += (self.firstItemIndex()+1) + '&ndash;' + self.lastItemIndex() + '</strong> of <strong>';
197
+ }
198
+ return html + self.count() + '</strong>';
199
+ }
200
+
201
+
202
+
203
+ this.init(list || []);
204
+ }
@@ -0,0 +1,107 @@
1
+ class window.Neat.ModelEditor extends Backbone.View
2
+ tagName: 'li'
3
+ className: 'row interactive editable'
4
+
5
+ initialize: (options)->
6
+ options = options ? {}
7
+ @templateOptions = options.templateOptions ? {}
8
+ @viewPath = @viewPath ? options.viewPath
9
+ @resource = @resource ? window.inflect.singularize(options.resource)
10
+ $(@el).addClass(@resource)
11
+
12
+ # Renders the 'show' template normally,
13
+ # renders 'edit' when in edit mode.
14
+ @showTemplate = JST["#{@viewPath}/show"]
15
+ @editTemplate = JST["#{@viewPath}/edit"]
16
+
17
+ # Wire up events.
18
+ # Don't use Backbone's events hash because if subclasses
19
+ # use that more familiar syntax, they'll blow away events
20
+ # defined in this class.
21
+ $(@el).delegate('.save-button', 'click', _.bind(@save, @))
22
+ $(@el).delegate('.delete-button', 'click', _.bind(@delete, @))
23
+ $(@el).delegate('.cancel-button', 'click', _.bind(@cancelEdit, @))
24
+
25
+ # Begin editing when this resource is clicked
26
+ # unless the user clicked a link or button.
27
+ $(@el).click (e)=>
28
+ @edit() if @canEdit() and !$(e.target).isIn('input, button, a, label')
29
+
30
+ render: ->
31
+ json = _.extend(@model.toJSON(), {options: @templateOptions})
32
+ $(@el).html @template()(json)
33
+ $(@el).attr('id', "#{@resource}_#{@model.get('id')}") # e.g. "calendar_5"
34
+ @
35
+
36
+ inEdit: -> $(@el).hasClass('editor')
37
+ canEdit: -> $(@el).hasClass('editable') and !@inEdit()
38
+ template: -> if @inEdit() then @editTemplate else @showTemplate
39
+
40
+ cancelEdit: (e)->
41
+ e?.preventDefault()
42
+ e?.stopImmediatePropagation()
43
+ if @inEdit()
44
+ $(@el).removeClass('editor').addClass('editable')
45
+ @render()
46
+ @trigger('edit:end')
47
+ @
48
+
49
+ edit: ->
50
+ unless @inEdit()
51
+ $el = $(@el)
52
+ $el.addClass('editor').removeClass('editable hovered')
53
+ @trigger('edit:begin')
54
+ @render()
55
+ $el.find(':input:visible').first().focus()
56
+ @
57
+
58
+ save: (e)->
59
+ e?.preventDefault()
60
+ $form = $(@el).closest('form')
61
+ newAttributes = $form.serializeObject()
62
+ @debug 'saving: ', newAttributes
63
+ attributes = @model.changedAttributes(newAttributes)
64
+
65
+ if attributes
66
+ previousAttributes = @model.toJSON()
67
+
68
+ @model.save attributes,
69
+ wait: true
70
+ success: =>
71
+ for attribute, newValue of attributes
72
+ @debug " . #{attribute} changed from ", previousAttributes[attribute], " to ", @model.get(attribute)
73
+ @onSaveSuccess()
74
+ error: _.bind(@onSaveError, @)
75
+
76
+ @cancelEdit()
77
+
78
+ delete: (e)->
79
+ e?.preventDefault()
80
+ if @confirmDelete(@resource)
81
+ $(@el).removeClass('editable').addClass('deleted')
82
+
83
+ @model.destroy
84
+ wait: true
85
+ success: =>
86
+ @model.collection.remove(@model) if @model.collection
87
+ @onDeleteSuccess
88
+ error: _.bind(@onSaveError, @)
89
+ @cancelEdit()
90
+
91
+ confirmDelete: (resource)->
92
+ confirm("Delete this #{resource}?")
93
+
94
+
95
+
96
+ onSaveSuccess: ->
97
+ onSaveError: ->
98
+ onDeleteSuccess: ->
99
+ onDeleteError: ->
100
+
101
+
102
+
103
+ debug: (o...)->
104
+ @log(o...) if Neat.debug
105
+
106
+ log: (o...)->
107
+ Neat.logger.log "[#{@resource}] ", o...
@@ -0,0 +1,23 @@
1
+ // This is a manifest file that'll be compiled into including all the files listed below.
2
+ // Add new JavaScript/Coffee code in separate files in this directory and they'll automatically
3
+ // be included in the compiled file accessible from http://example.com/assets/application.js
4
+ // It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the
5
+ // the compiled file.
6
+ //
7
+ //= require_self
8
+ //= require ./neat/lib/inflect
9
+ //= require ./neat/lib/paginated_list
10
+ //= require ./neat/lib/delayed_action
11
+ //= require ./neat/lib/jquery_extensions
12
+ //= require ./neat/collection_editor
13
+ //= require ./neat/model_editor
14
+
15
+ window.Neat = window.Neat || {}
16
+ window.Neat.debug = true;
17
+ window.Neat.logger = {
18
+ log: function() {
19
+ var log, __slice = [].slice, o, _ref;
20
+ o = 1 <= arguments.length ? __slice.call(arguments, 0) : [];
21
+ return (_ref = window.console).log.apply(_ref, o);
22
+ }
23
+ };
metadata ADDED
@@ -0,0 +1,75 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: neat-rails
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.2
5
+ platform: ruby
6
+ authors:
7
+ - Bob Lail
8
+ - Luke Booth
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2014-08-07 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: rake
16
+ requirement: !ruby/object:Gem::Requirement
17
+ requirements:
18
+ - - ">="
19
+ - !ruby/object:Gem::Version
20
+ version: '0'
21
+ type: :development
22
+ prerelease: false
23
+ version_requirements: !ruby/object:Gem::Requirement
24
+ requirements:
25
+ - - ">="
26
+ - !ruby/object:Gem::Version
27
+ version: '0'
28
+ description: It allows editing collections and models inline
29
+ email:
30
+ - bob.lailfamily@gmail.com
31
+ - luke.booth@cph.org
32
+ executables: []
33
+ extensions: []
34
+ extra_rdoc_files: []
35
+ files:
36
+ - ".gitignore"
37
+ - Gemfile
38
+ - LICENSE.txt
39
+ - README.md
40
+ - Rakefile
41
+ - lib/neat-rails.rb
42
+ - lib/neat/rails/engine.rb
43
+ - lib/neat/rails/version.rb
44
+ - neat-rails.gemspec
45
+ - vendor/assets/javascripts/neat.js
46
+ - vendor/assets/javascripts/neat/collection_editor.coffee
47
+ - vendor/assets/javascripts/neat/lib/delayed_action.js
48
+ - vendor/assets/javascripts/neat/lib/inflect.js
49
+ - vendor/assets/javascripts/neat/lib/jquery_extensions.coffee
50
+ - vendor/assets/javascripts/neat/lib/paginated_list.js
51
+ - vendor/assets/javascripts/neat/model_editor.coffee
52
+ homepage: ''
53
+ licenses: []
54
+ metadata: {}
55
+ post_install_message:
56
+ rdoc_options: []
57
+ require_paths:
58
+ - lib
59
+ required_ruby_version: !ruby/object:Gem::Requirement
60
+ requirements:
61
+ - - ">="
62
+ - !ruby/object:Gem::Version
63
+ version: '0'
64
+ required_rubygems_version: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ requirements: []
70
+ rubyforge_project:
71
+ rubygems_version: 2.2.0
72
+ signing_key:
73
+ specification_version: 4
74
+ summary: It's like FreightTrain for Backbone. That's pretty neat!
75
+ test_files: []