evil-blocks-rails 0.3.2 → 0.4.0

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/ChangeLog CHANGED
@@ -1,3 +1,6 @@
1
+ == 0.4 (X-Ray, 14th April 1948)
2
+ * Add new object style.
3
+
1
4
  == 0.3.2 (Helen of Bikini, 25th July 1946)
2
5
  * Fix searching elements inside several blocks (by Andrei Miroshnik).
3
6
 
data/README.md CHANGED
@@ -17,8 +17,8 @@ and [role-rails](https://github.com/kossnocorp/role-rails) by Sasha Koss.
17
17
  .gallery-control.is-big
18
18
  // Evil Blocks add @data-role alias to Slim
19
19
  // .class to bind styles, @data-role to bind JavaScript
20
- a.gallery-left@next-photo href="#"
21
- a.gallery-right@prev-photo href="#"
20
+ a.gallery-left@nextPhoto href="#"
21
+ a.gallery-right@prevPhoto href="#"
22
22
  img src="photos/1.jpg"
23
23
  img src="photos/2.jpg"
24
24
  img src="photos/3.jpg"
@@ -46,32 +46,29 @@ and [role-rails](https://github.com/kossnocorp/role-rails) by Sasha Koss.
46
46
  ### CoffeeScript
47
47
 
48
48
  ```coffee
49
- # Will execute callback only if .gallery-control is in current page
50
- evil.block '.gallery-control', ($, b, block) ->
51
- current = 0
52
- showPhoto = (num) ->
53
- # b-function finds only inside .gallery-control
54
- b('img').hide()
55
- b("img:eql(#{ num })").show()
56
-
57
- initState: ->
58
- showPhoto(current)
59
-
60
- buttons: ->
61
- # Shortcuts for data-role="next-photo"
62
- b.nextPhoto.click ->
63
- showPhoto(current += 1)
64
-
65
- slideShow: ->
49
+ # Will execute init only if .gallery-control is in current page
50
+ evil.block '.gallery-control',
51
+ current: 0
52
+
53
+ showPhoto: (num) ->
54
+ @('img').hide().
55
+ filter("eql(#{ num })").show()
56
+
57
+ init: ->
58
+ @showPhoto(@current)
59
+
60
+ 'click on @nextPhoto', (link, event) ->
61
+ @showPhoto(current += 1)
62
+
63
+ 'on start-slideshow', ->
66
64
  # You can communicate between blocks by simple events
67
- block.on 'slideshow-start', ->
68
- setTimeout( -> b.nextPhoto.click() , 5000)
65
+ setTimeout( => @nextPhoto.click() , 5000)
69
66
 
70
- # Will execute callback only on user page, where .user-page exists
71
- evil.block '.user-page', ($, b, block) ->
67
+ # Will execute init only on user page, where .user-page exists
68
+ evil.block '.user-page',
72
69
 
73
- startSlidehow: ->
74
- b('.gallery-control').trigger('slideshow-start')
70
+ init: ->
71
+ @('.gallery-control').trigger('start-slideshow')
75
72
  ```
76
73
 
77
74
  ## Styles
@@ -96,20 +93,19 @@ evil.block '.user-page', ($, b, block) ->
96
93
  * Unobtrusive JavaScript.
97
94
  * Write animation and states in CSS. JavaScript just changes CSS classes.
98
95
  * Avoid rendering. Send from server HTML, not JSON.
99
- * Wrap scrips to revive blocks in `evil.block(selector, callback)`.
100
- It will execute `callback` only if block `selector` exists in current page.
101
- So you can be free to join all JS files in one.
102
- * `evil.block` will send to `callback` three arguments: `$` is jQuery,
103
- `b` is “b-function”, `block` is blocks found by `selector`.
104
- * B-function is like jQuery function, but find only inside finded block
105
- (alias `b('a') = $('a', selector)`).
96
+ * Split JS by widgets. Describe widget class in `evil.block(selector, klass)`.
97
+ It will create `klass` instance and call `init` method for each selectors,
98
+ which exist in current page. So you can be free to join all JS files in one.
99
+ * Describe events by `EVENTS on SELECTORS` methods
100
+ (like `keyup submit on @name, @family`). This methods save this and
101
+ receive jQuery node in first argument and event in second.
106
102
  * Bind JavaScript to `data-role` attribute to be free to change styles
107
103
  and classes without dangeros of breaking scripts.
108
- * You can easy find special`data-role` by b-function. It will has properties
109
- for all roles inside block (`@role-name` will be camelized to `b.roleName`).
110
- * If `callback` return object, `evil-block` will execute all it methods.
111
- It’s useful to split your script to several initializer with separated
112
- variables scope.
104
+ * Every tag with `data-role` will by as property in object with jQuery node.
105
+ * If you need to find elements inside block, use `@(selector)` function.
106
+ * If you need to communicate between blocks, use custom events and create
107
+ block events listeners by `on EVENTS` method. It will receive events object
108
+ as first argument and event parameters as next arguments.
113
109
 
114
110
  ## Install
115
111
 
@@ -53,68 +53,166 @@
53
53
  }
54
54
 
55
55
  /**
56
- * Call `callback` only if `selector` was founded on page.
57
- * `callback` will receive 3 arguments:
58
- * * `$` - jQuery.
59
- * * `b` - like jQuery, but find only inside founded blocks.
60
- * * `block` - blocks, founded by `selector`.
56
+ * Evil blocks list.
57
+ */
58
+ var vitalizers = [];
59
+
60
+ /**
61
+ * If onready event is already happend.
62
+ */
63
+ var ready = false;
64
+
65
+ /**
66
+ * Execute `callback` on every finded `selector` inside `base`.
67
+ */
68
+ var vitalize = function (base, selector, callback) {
69
+ var blocks = $().add( base.filter(selector) ).
70
+ add( base.find(selector) );
71
+
72
+ if ( blocks.length == 0 ) {
73
+ return;
74
+ }
75
+
76
+ for ( var i = 0; i < blocks.length; i++ ) {
77
+ var block = $(blocks[i]);
78
+
79
+ var b = (function (block) {
80
+ return function (subselector) {
81
+ return $(subselector, block);
82
+ };
83
+ })(block);
84
+
85
+ var actives = { };
86
+ block.find('[data-role]').each(function (_, el) {
87
+ var roles = el.attributes['data-role'].value.split(' ');
88
+ for ( var i = 0; i < roles.length; i++ ) {
89
+ var role = roles[i].replace(/-\w/g, function (s) {
90
+ return s[1].toUpperCase();
91
+ });
92
+ if ( !actives[role] ) {
93
+ actives[role] = [];
94
+ }
95
+ actives[role].push(el);
96
+ }
97
+ });
98
+
99
+ for ( var role in actives ) {
100
+ b[role] = b(actives[role]);
101
+ }
102
+
103
+ var inits = callback($, b, block);
104
+ if ( typeof(inits) == 'object' ) {
105
+ for ( var init in inits ) {
106
+ inits[init]($, b, block);
107
+ }
108
+ }
109
+ }
110
+ };
111
+
112
+ /**
113
+ * Convert block class to callback.
114
+ */
115
+ var convert = function (klass) {
116
+ return function ($, obj, block) {
117
+ obj.block = block;
118
+
119
+ for ( var prop in klass ) {
120
+ (function (prop) {
121
+ if ( prop.indexOf('on ') != -1 ) {
122
+ var parts = prop.split(' on ');
123
+ if ( parts[1] ) {
124
+ block.on(parts[0], parts[1], function () {
125
+ var arg = Array.prototype.slice.call(arguments);
126
+ var el = $(this);
127
+ arg.unshift(el);
128
+ klass[prop].apply(obj, arg);
129
+ });
130
+ } else {
131
+ block.on(parts[0], function () {
132
+ klass[prop].apply(obj, arguments);
133
+ });
134
+ }
135
+ } else {
136
+ obj[prop] = klass[prop];
137
+ }
138
+ })(prop);
139
+ }
140
+
141
+ if ( typeof(obj.init) == 'function' ) {
142
+ obj.init();
143
+ }
144
+ }
145
+ }
146
+
147
+ /**
148
+ * Create object for every `selector` finded in page and call their
149
+ * `init` method.
150
+ *
151
+ * evil.block '.user-page .buttons',
152
+ * init: ->
153
+ * @gallery.fotorama()
154
+ * delete: ->
155
+ * @deleteForm.submit ->
156
+ * $('user-status').trigger('deleted')
157
+ * 'click on @deleleLink': (link) ->
158
+ * link.addClass('is-loading')
159
+ * delete()
160
+ * 'on update': ->
161
+ * location.reload()
61
162
  *
62
- * All elements with `data-role` will be add as property to `b`.
163
+ * Every `data-role="aName"` in HTML will create in object `aName` property
164
+ * with jQuery node.
63
165
  *
64
- * `callback` can return hash of inititalizer functions,
65
- * `evil.block` will execute them. It is good way to isolate variables
66
- * in separated initializers.
166
+ * To bind delegate listener just create `on EVENT on SELECTOR` method.
167
+ * In first argument it will receive jQuery node of `e.currentTarget`,
168
+ * second will be event object and others will be parameters.
67
169
  *
68
- * evil.block '.user-page', ($, b, block) ->
170
+ * To communicate between blocks, just trigget custom events. To receive
171
+ * events from another blocks, create `on EVENT` method. Event object will
172
+ * be on first argument here.
69
173
  *
70
- * editUser: ->
71
- * # Click on `data-role="edit"` inside `.user-page`
72
- * b.edit.click ->
73
- * b('.edit-form').show()
174
+ * Block node will be in `@block` property and you can search only inside
175
+ * block by `@(selector)` method.
74
176
  *
75
- * delUser: ->
76
- * b.del.click -> b('.delete-form').submit()
177
+ * If your block contrain only `init` method, you can use shortcut:
77
178
  *
78
- * initGallery: ->
79
- * b.gallery.fotorama()
179
+ * evil.block '.block', ->
180
+ * # init method
80
181
  */
81
- window.evil.block = function (selector, callback) {
82
- $(function () {
83
- var blocks = $(selector);
84
- for ( var i = 0; i < blocks.length; i++ ) {
85
- var block = $(blocks[i]);
86
-
87
- var b = (function (block) {
88
- return function (subselector) {
89
- return $(subselector, block);
90
- };
91
- })(block);
92
- var actives = { };
93
- block.find('[data-role]').each(function (_, el) {
94
- var roles = el.attributes['data-role'].value.split(' ');
95
- for ( var i = 0; i < roles.length; i++ ) {
96
- var role = roles[i].replace(/-\w/g, function (s) {
97
- return s[1].toUpperCase();
98
- });
99
- if ( !actives[role] ) {
100
- actives[role] = [];
101
- }
102
- actives[role].push(el);
103
- }
104
- });
182
+ window.evil.block = function (selector, vitalizer) {
183
+ if ( typeof(vitalizer) != 'function' ) {
184
+ vitalizer = convert(vitalizer)
185
+ }
186
+ vitalizers.push([selector, vitalizer]);
105
187
 
106
- for ( var role in actives ) {
107
- b[role] = $(actives[role]);
108
- }
188
+ if ( ready ) {
189
+ vitalize($(document), selector, vitalizer);
190
+ }
191
+ };
109
192
 
110
- var inits = callback($, b, block);
111
- if ( typeof(inits) == 'object' ) {
112
- for ( var init in inits ) {
113
- inits[init]($, b, block);
114
- }
115
- }
116
- }
117
- });
193
+ /**
194
+ * Vitalize all current blocks inside base. You must call it on every
195
+ * new content from AJAX.
196
+ *
197
+ * 'on click on @load': ->
198
+ * $.get '/comments', (comments) =>
199
+ * evil.block.vitalize $(comments).applyTo(@comments)
200
+ */
201
+ window.evil.block.vitalize = function (base) {
202
+ base = $(base);
203
+
204
+ for ( var i = 0; i < vitalizers.length; i++ ) {
205
+ var vitalizer = vitalizers[i];
206
+ vitalize(base, vitalizer[0], vitalizer[1]);
207
+ }
118
208
  };
119
209
 
210
+ /*
211
+ * Run all blocks on load.
212
+ */
213
+ $(document).ready(function () {
214
+ ready = true;
215
+ evil.block.vitalize(document);
216
+ });
217
+
120
218
  })(jQuery);
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: evil-blocks-rails
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.2
4
+ version: 0.4.0
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2013-06-25 00:00:00.000000000 Z
12
+ date: 2013-09-09 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: sprockets