evil-blocks-rails 0.4.2 → 0.5.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/ChangeLog.md +37 -0
- data/README.md +358 -94
- data/lib/evil-blocks-rails.rb +7 -6
- data/lib/evil-blocks.debug.js +29 -0
- data/lib/{assets/javascripts/evil-blocks.js → evil-blocks.js} +112 -89
- metadata +16 -17
- data/ChangeLog +0 -24
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: d18be5dfc85ca1bdff5b9748f2a033f09ab24a81
|
4
|
+
data.tar.gz: 58bc3e5399b808b2e8025c91df312e2472d34943
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 7bc25d378340da42e2533f0b1eb7baf366a7f74b76f74a44093ffee067442cad9036e143d30d28779f9259b1468cd4a7f1868ab0f5cbcbbdbdc496857e983d4b
|
7
|
+
data.tar.gz: 4daffdd46ff66ec0f9a133c4c9186e5220858d140848a0153f37e7d7d9ed7a35eabd1f46085e1784229ce0e8239925da4d87e26ea9469a88868f61edfaa933c3
|
data/ChangeLog.md
ADDED
@@ -0,0 +1,37 @@
|
|
1
|
+
## 0.5 (RDS-1, 29th August 1949)
|
2
|
+
* Current event target was moved from first argument to `event.el`.
|
3
|
+
* Inside finder was moved from `@(selector)` to `@$(selector)`.
|
4
|
+
* Remove old function style API.
|
5
|
+
* Add `@@block` alias.
|
6
|
+
* Add debugger extension.
|
7
|
+
* Vitalize blocks on next tick after page ready.
|
8
|
+
* Don’t vitalize blocks twice.
|
9
|
+
* Method `evil.block.vitalize()` calls on `document` by default.
|
10
|
+
* Allow to use GitHub master in Bundler.
|
11
|
+
* Add Bower support.
|
12
|
+
|
13
|
+
## 0.4.2 (Zebra, 14th May 1948)
|
14
|
+
* Don’t listen bubbled events as block event.
|
15
|
+
* Change license to MIT.
|
16
|
+
|
17
|
+
## 0.4.1 (Yoke, 30th April 1948)
|
18
|
+
* Allow to listen body and window events from object style.
|
19
|
+
|
20
|
+
## 0.4 (X-Ray, 14th April 1948)
|
21
|
+
* Add new object style.
|
22
|
+
|
23
|
+
## 0.3.2 (Helen of Bikini, 25th July 1946)
|
24
|
+
* Fix searching elements inside several blocks (by Andrei Miroshnik).
|
25
|
+
|
26
|
+
## 0.3.1 (Gilda, 1st July 1946)
|
27
|
+
* Fix in multiple block copies on one page (by Andrei Miroshnik).
|
28
|
+
|
29
|
+
## 0.3 (Fat Man, 9th August 1945)
|
30
|
+
* Add shortcut to Slim to set data-role and class.
|
31
|
+
* Run callback on every block copy in page.
|
32
|
+
|
33
|
+
## 0.2 (Little Boy, 6th August 1945)
|
34
|
+
* Support non-Rails applications in gem.
|
35
|
+
|
36
|
+
## 0.1 (The Gadget, 16th July 1945)
|
37
|
+
* Initial release.
|
data/README.md
CHANGED
@@ -1,122 +1,388 @@
|
|
1
1
|
# Evil Blocks
|
2
2
|
|
3
|
-
Evil Block is a tiny framework for web pages. It
|
4
|
-
to separated blocks with isolated styles and scripts.
|
3
|
+
Evil Block is a tiny JS framework for web pages. It is based on 4 ideas:
|
5
4
|
|
6
|
-
|
7
|
-
|
8
|
-
|
5
|
+
* **Split code to independent blocks.** “Divide and rule” is always good idea.
|
6
|
+
* **Blocks communicate by events.** Events is easy and safe method to clean
|
7
|
+
very complicated dependencies between controls.
|
8
|
+
* **Separate JS and CSS.** You use classes only for styles and bind JS
|
9
|
+
by selectors with special attributes. So you can update your styles without
|
10
|
+
fear to break your scripts.
|
11
|
+
* **Try not to render on client.** 2 way data-binding looks very cool,
|
12
|
+
but it has [big price]. Most of web pages (instead of web applications)
|
13
|
+
can render all HTML on server and use client rendering only in few small
|
14
|
+
places. Without rendering we can incredibly clean code and architecture.
|
15
|
+
|
16
|
+
See also [Evil Front], a pack of helpers for Ruby on Rails and Evil Blocks.
|
17
|
+
|
18
|
+
Sponsored by [Evil Martians]. Role aliases was taken from [Role.js].
|
19
|
+
|
20
|
+
[Role.js]: https://github.com/kossnocorp/role
|
21
|
+
[big price]: http://staal.io/blog/2014/02/05/2-way-data-binding-under-the-microscope/
|
22
|
+
[Evil Front]: https://github.com/ai/evil-front
|
23
|
+
[Evil Martians]: http://evilmartians.com/
|
9
24
|
|
10
25
|
## Quick Example
|
11
26
|
|
12
|
-
|
27
|
+
Slim template:
|
28
|
+
|
29
|
+
```haml
|
30
|
+
.todo-control@@todo
|
31
|
+
ul@tasks
|
32
|
+
|
33
|
+
- @tasks.each do |task|
|
34
|
+
.task@task
|
35
|
+
= task.name
|
36
|
+
form@finishForm action="/tasks/#{ task.id }/finish"
|
37
|
+
input type="submit" value="Finish"
|
38
|
+
|
39
|
+
form@addForm action="/tasks/"
|
40
|
+
input type="text" name="name"
|
41
|
+
input type="submit" value="Add"
|
42
|
+
```
|
43
|
+
|
44
|
+
Block’s CoffeeScript:
|
45
|
+
|
46
|
+
```coffee
|
47
|
+
evil.block '@@todo',
|
48
|
+
|
49
|
+
ajaxSubmit: (e) ->
|
50
|
+
e.preventDefault()
|
51
|
+
|
52
|
+
form = e.el
|
53
|
+
form.addClass('is-loading')
|
54
|
+
|
55
|
+
$.ajax
|
56
|
+
url: form.attr('action')
|
57
|
+
data: form.serialize()
|
58
|
+
complete: -> form.addClass('is-loading')
|
59
|
+
|
60
|
+
'submit on @finishForm': (e) ->
|
61
|
+
@ajaxSubmit(e).done ->
|
62
|
+
e.el.closest("@task").addClass("is-finished")
|
63
|
+
|
64
|
+
'submit on @addForm': (e) ->
|
65
|
+
e.preventDefault()
|
66
|
+
@ajaxSubmit(e).done (newTaskHTML) ->
|
67
|
+
@tasks.append(newTaskHTML)
|
68
|
+
```
|
69
|
+
|
70
|
+
## Attributes
|
71
|
+
|
72
|
+
If you use classes selectors in CSS and JS, your scripts will be depend
|
73
|
+
on styles. If you will change `.small-button` to `.big-button`, you must
|
74
|
+
change all button’s selectors in scripts.
|
75
|
+
|
76
|
+
Separated scripts and styles are better, so Evil Blocks prefer to work with
|
77
|
+
two HTML attributes to bind your JS: `data-block` (to define blocks)
|
78
|
+
and `data-role` (to define elements inside block).
|
79
|
+
|
80
|
+
```html
|
81
|
+
<div data-block="todo">
|
82
|
+
<ul data-role="tasks">
|
83
|
+
</ul>
|
84
|
+
</div>
|
85
|
+
```
|
86
|
+
|
87
|
+
Evil Blocks extends Slim and jQuery, so you can use shortcuts for this
|
88
|
+
attributes: `@@block` and `@role`:
|
89
|
+
|
90
|
+
```haml
|
91
|
+
@@todo
|
92
|
+
ul@tasks
|
93
|
+
```
|
94
|
+
|
95
|
+
```js
|
96
|
+
$('@tasks')
|
97
|
+
```
|
98
|
+
|
99
|
+
With this attributes you can easily change interface style
|
100
|
+
and be sure in scripts:
|
13
101
|
|
14
102
|
```haml
|
15
|
-
.
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
.
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
103
|
+
.big-button@addButton
|
104
|
+
```
|
105
|
+
|
106
|
+
Of course, Evil Block doesn’t force you to use only this selectors.
|
107
|
+
You can any attributes, that you like.
|
108
|
+
|
109
|
+
## Blocks
|
110
|
+
|
111
|
+
You should split your interface to independent controls and mark them
|
112
|
+
with `data-block`:
|
113
|
+
|
114
|
+
```haml
|
115
|
+
header@@header
|
116
|
+
a.exit href="#"
|
117
|
+
|
118
|
+
.todo-control@@todo
|
119
|
+
ul.tasks
|
120
|
+
|
121
|
+
.docs-page@@docs
|
122
|
+
```
|
123
|
+
|
124
|
+
Then you can vitalize your blocks in scripts by `evil.block` function:
|
125
|
+
|
126
|
+
```coffee
|
127
|
+
evil.block '@@header',
|
128
|
+
|
129
|
+
init: ->
|
130
|
+
console.log('Vitalize', @block)
|
131
|
+
```
|
132
|
+
|
133
|
+
When page will be loaded Evil Blocks finds blocks by `@@header` selector
|
134
|
+
(this is shortcut for `[data-block=header]`) and call `init` on every
|
135
|
+
founded block. So, if your page contains two headers, `init` will be called
|
136
|
+
twice with different `@block`.
|
137
|
+
|
138
|
+
Property `@block` will contain jQuery-node of current block. You can search
|
139
|
+
inside current block by `@$(selector)` method:
|
140
|
+
|
141
|
+
```coffee
|
142
|
+
evil.block '@@docs',
|
143
|
+
|
144
|
+
init: ->
|
145
|
+
@$('a').attr(target: '_blank') # Open all links inside docs in new tab
|
146
|
+
# Same as @block.find('a')
|
147
|
+
```
|
148
|
+
|
149
|
+
You can add any methods and properties to your block class:
|
150
|
+
|
151
|
+
```coffee
|
152
|
+
evil.block '@@gallery',
|
51
153
|
current: 0
|
52
154
|
|
53
155
|
showPhoto: (num) ->
|
54
|
-
|
156
|
+
@$('img').hide().
|
55
157
|
filter("eql(#{ num })").show()
|
56
158
|
|
57
159
|
init: ->
|
58
160
|
@showPhoto(@current)
|
161
|
+
```
|
162
|
+
|
163
|
+
Evil Blocks will automatically create properties with jQuery-nodes
|
164
|
+
for every element inside block with `data-role` attribute:
|
165
|
+
|
166
|
+
```haml
|
167
|
+
.todo-control@@todo
|
168
|
+
ul.tasks@tasks
|
169
|
+
```
|
170
|
+
|
171
|
+
```coffee
|
172
|
+
evil.block '@@todo',
|
173
|
+
|
174
|
+
addTask: (task) ->
|
175
|
+
@tasks.append(task)
|
176
|
+
```
|
177
|
+
|
178
|
+
If you add new HTML by AJAX, you can vitalize new blocks by
|
179
|
+
`evil.block.vitalize()`. This function will vitalize only new blocks in
|
180
|
+
document.
|
181
|
+
|
182
|
+
```coffee
|
183
|
+
@sections.append(html)
|
184
|
+
evil.block.vitalize()
|
185
|
+
```
|
186
|
+
|
187
|
+
## Events
|
188
|
+
|
189
|
+
You can bind listeners to events inside block by `"events on selectors"` method:
|
190
|
+
|
191
|
+
```coffee
|
192
|
+
evil.block '@@todo',
|
193
|
+
|
194
|
+
'submit on @finishForm': ->
|
195
|
+
# Event listener
|
196
|
+
```
|
197
|
+
|
198
|
+
More difficult example:
|
199
|
+
|
200
|
+
```coffee
|
201
|
+
evil.block '@@form',
|
202
|
+
ajaxSearch: -> …
|
59
203
|
|
60
|
-
'
|
61
|
-
|
204
|
+
'change, keyup on input, select': (event) ->
|
205
|
+
field = event.el()
|
206
|
+
@ajaxSearch('Changed', field.val())
|
207
|
+
```
|
208
|
+
|
209
|
+
Listener will receive jQuery Event object as first argument.
|
210
|
+
Current element (`this` in jQuery listeners) will be in `event.el` property.
|
211
|
+
All listeners are delegated on current block, so `click on @button` will be
|
212
|
+
equal to `@block.on 'click', '@button', ->`.
|
62
213
|
|
63
|
-
|
64
|
-
|
65
|
-
|
214
|
+
You should prevent default event behavior by `event.preventDefault()`,
|
215
|
+
`return false` will not do anything in block’s listeners. I recommend
|
216
|
+
[evil-front/links] to prevent default behavior in any links with `href="#"`
|
217
|
+
to clean your code.
|
66
218
|
|
67
|
-
|
68
|
-
|
219
|
+
You can also bind events on body and window:
|
220
|
+
|
221
|
+
```coffee
|
222
|
+
evil.blocks '@@docs',
|
223
|
+
recalcMenu: -> …
|
224
|
+
openPage: -> …
|
69
225
|
|
70
226
|
init: ->
|
71
|
-
@(
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
227
|
+
@recalcMenu()
|
228
|
+
|
229
|
+
'resize on window': ->
|
230
|
+
@recalcMenu()
|
231
|
+
|
232
|
+
'hashchange on window': ->
|
233
|
+
@openPage(location.hash)
|
234
|
+
```
|
235
|
+
|
236
|
+
[evil-front/links]: https://github.com/ai/evil-front/blob/master/evil-front/lib/assets/javascripts/evil-front/links.js
|
237
|
+
|
238
|
+
## Blocks Communications
|
239
|
+
|
240
|
+
Blocks should communicates by custom jQuery events. You can bind event listener
|
241
|
+
to block node by `"on events"` method:
|
242
|
+
|
243
|
+
```coffee
|
244
|
+
evil.block '@@slideshow', ->
|
245
|
+
nextSlide: -> …
|
246
|
+
|
247
|
+
'on play': ->
|
248
|
+
@timer = setInterval(=> @nextSlide, 5000)
|
249
|
+
|
250
|
+
'on stop': ->
|
251
|
+
clearInterval(@timer)
|
252
|
+
|
253
|
+
evil.block '@@video', ->
|
254
|
+
|
255
|
+
'click on @fullscreenButton': ->
|
256
|
+
$('@@slideshow').trigger('stop')
|
257
|
+
```
|
258
|
+
|
259
|
+
If you want to use broadcast messages, you can use custom events on body:
|
260
|
+
|
261
|
+
```coffee
|
262
|
+
evil.block '@@callUs', ->
|
263
|
+
|
264
|
+
'change-city on body': (e, city) ->
|
265
|
+
@phoneNumber.text(city.phone)
|
266
|
+
|
267
|
+
evil.block '@@cityChanger', ->
|
268
|
+
getCurrentCity: -> …
|
269
|
+
|
270
|
+
'change on @citySelect': ->
|
271
|
+
$('body').trigger('change-city', @getCurrentCity())
|
272
|
+
```
|
273
|
+
|
274
|
+
## Rendering
|
275
|
+
|
276
|
+
If you will render on client and on server-side, you must repeat helpers, i18n,
|
277
|
+
templates. Client rendering requires a lot of libraries and architecture.
|
278
|
+
2-way data binding looks cool, but has very [big price] in performance,
|
279
|
+
templates, animation and overengeniring.
|
280
|
+
|
281
|
+
If you develop web page (not web application with offline support, etc),
|
282
|
+
server-side rendering will be more useful. Users will see your interface
|
283
|
+
imminently, search engines will index your content and your code will be much
|
284
|
+
simple and clear.
|
285
|
+
|
286
|
+
In most of cases you can avoid client rendering. If you need to add some block
|
287
|
+
by JS, you can render it hidden to page HTML and show in right time:
|
288
|
+
|
289
|
+
```coffee
|
290
|
+
evil.block '@@comment',
|
291
|
+
|
292
|
+
'click on @addCommentButton': ->
|
293
|
+
@newCommentForm.slideDown()
|
294
|
+
```
|
295
|
+
|
296
|
+
If user change some data and you need to update view, you anyway need to send
|
297
|
+
request to save new data on server. Just ask server to render new view.
|
298
|
+
For example, on new comment server can return new comment HTML:
|
299
|
+
|
300
|
+
```coffee
|
301
|
+
evil.block '@@comment',
|
302
|
+
|
303
|
+
'submit on @addCommentForm': ->
|
304
|
+
$.post '/comments', @addCommentForm.serialize(), (newComment) ->
|
305
|
+
@comments.append(newComment)
|
306
|
+
```
|
307
|
+
|
308
|
+
But, of course, some cases require client rendering. Evil Blocks only recommend
|
309
|
+
to do it server-side, but not force you:
|
310
|
+
|
311
|
+
```coffee
|
312
|
+
evil.block '@@comment',
|
313
|
+
|
314
|
+
'change, keyup on @commentField', ->
|
315
|
+
html = JST['comment'](text: @commentField.text())
|
316
|
+
@preview.html(html)
|
317
|
+
```
|
318
|
+
|
319
|
+
[big price]: http://staal.io/blog/2014/02/05/2-way-data-binding-under-the-microscope/
|
320
|
+
|
321
|
+
## Modules
|
322
|
+
|
323
|
+
If your blocks has same behavior, you can create module-block and set
|
324
|
+
multiple blocks on same tag:
|
325
|
+
|
326
|
+
```haml
|
327
|
+
@popup@@closable
|
328
|
+
a@closeLink href="#"
|
329
|
+
```
|
330
|
+
|
331
|
+
```coffee
|
332
|
+
evil.block '@@closable', ->
|
333
|
+
|
334
|
+
'click on @closeLink': ->
|
335
|
+
@block.trigger('close')
|
336
|
+
|
337
|
+
evil.block '@@popup', ->
|
338
|
+
|
339
|
+
'on close': ->
|
340
|
+
@clock.removeClass('is-open')
|
341
|
+
```
|
342
|
+
|
343
|
+
If you want to use same methods inside multiple block, you can create
|
344
|
+
inject-function:
|
345
|
+
|
346
|
+
```coffee
|
347
|
+
fancybox = (obj) ->
|
348
|
+
for name, value of fancybox.module
|
349
|
+
obj[name] = value
|
350
|
+
# Initializer code
|
351
|
+
|
352
|
+
fancybox.module =
|
353
|
+
openInFancybox: (node) ->
|
354
|
+
|
355
|
+
evil.block '@@docs',
|
356
|
+
|
357
|
+
init: ->
|
358
|
+
fancybox(@)
|
359
|
+
|
360
|
+
'click on @showExampleButton': ->
|
361
|
+
@openInFancybox(@example)
|
362
|
+
```
|
363
|
+
|
364
|
+
## Debug
|
365
|
+
|
366
|
+
Evil Blocks contains debug extension, which log every events inside blocks.
|
367
|
+
To enable it, just load `evil-block.debug.js`. For example, in Rails:
|
368
|
+
|
369
|
+
```haml
|
370
|
+
- if Rails.env.development?
|
371
|
+
= javascript_include_tag 'evil-block.debug'
|
372
|
+
```
|
109
373
|
|
110
374
|
## Install
|
111
375
|
|
112
376
|
### Ruby on Rails
|
113
377
|
|
114
378
|
Add `evil-block-rails` gem to `Gemfile`:
|
379
|
+
|
115
380
|
```ruby
|
116
381
|
gem "evil-blocks-rails"
|
117
382
|
```
|
118
383
|
|
119
384
|
Load `evil-blocks.js` in your script:
|
385
|
+
|
120
386
|
```js
|
121
387
|
//= require evil-blocks
|
122
388
|
```
|
@@ -125,16 +391,19 @@ Load `evil-blocks.js` in your script:
|
|
125
391
|
|
126
392
|
If you use Sinatra or other non-Rails framework you can add Evil Blocks path
|
127
393
|
to Sprockets environment:
|
394
|
+
|
128
395
|
```ruby
|
129
396
|
EvilBlocks.install(sprockets)
|
130
397
|
```
|
131
398
|
|
132
|
-
And change Slim options to support `@
|
399
|
+
And change Slim options to support `@@block` and `@rule` shortcuts:
|
400
|
+
|
133
401
|
```ruby
|
134
402
|
EvilBlocks.install_to_slim!
|
135
403
|
```
|
136
404
|
|
137
405
|
Then just load `evil-blocks.js` in your script:
|
406
|
+
|
138
407
|
```js
|
139
408
|
//= require evil-blocks
|
140
409
|
```
|
@@ -142,8 +411,3 @@ Then just load `evil-blocks.js` in your script:
|
|
142
411
|
### Others
|
143
412
|
|
144
413
|
Add file `lib/evil-blocks.js` to your project.
|
145
|
-
|
146
|
-
## See Also
|
147
|
-
|
148
|
-
* [Evil Front](https://github.com/ai/evil-front) – pack of helpers and libraries
|
149
|
-
for Ruby on Rails and Evil Blocks.
|
data/lib/evil-blocks-rails.rb
CHANGED
@@ -6,20 +6,21 @@ module EvilBlocks
|
|
6
6
|
# Copy from role-rails by Sasha Koss.
|
7
7
|
# https://github.com/kossnocorp/role-rails
|
8
8
|
shortcut = Slim::Parser.default_options[:shortcut]
|
9
|
-
shortcut['@'] = { :
|
10
|
-
shortcut['
|
11
|
-
Slim::Engine.default_options[:merge_attrs]['data-role']
|
9
|
+
shortcut['@'] = { attr: 'data-role' }
|
10
|
+
shortcut['@@'] = { attr: 'data-block' }
|
11
|
+
Slim::Engine.default_options[:merge_attrs]['data-role'] = ' '
|
12
|
+
Slim::Engine.default_options[:merge_attrs]['data-block'] = ' '
|
12
13
|
end
|
13
14
|
|
14
15
|
# Add assets paths to standalone Sprockets environment.
|
15
16
|
def self.install(sprockets)
|
16
|
-
sprockets.append_path(Pathname(__FILE__).dirname
|
17
|
+
sprockets.append_path(Pathname(__FILE__).dirname)
|
17
18
|
end
|
18
19
|
|
19
20
|
if defined? ::Rails
|
20
|
-
# Tell Ruby on Rails to add `evil-block.js` to Rails Admin load paths.
|
21
21
|
class Engine < ::Rails::Engine
|
22
|
-
initializer 'evil-
|
22
|
+
initializer 'evil-blocks' do
|
23
|
+
EvilBlocks.install(Rails.application.assets)
|
23
24
|
EvilBlocks.install_to_slim! if defined?(Slim::Parser)
|
24
25
|
end
|
25
26
|
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
;(function () {
|
2
|
+
"use strict";
|
3
|
+
|
4
|
+
var log = function () {
|
5
|
+
if ( !console || !console.log ) {
|
6
|
+
return;
|
7
|
+
}
|
8
|
+
console.log.apply(console, arguments);
|
9
|
+
};
|
10
|
+
|
11
|
+
if ( !window.evil || !window.evil.block ) {
|
12
|
+
log("You should include evil-blocks.debug.js after evil-blocks.js");
|
13
|
+
return;
|
14
|
+
}
|
15
|
+
|
16
|
+
window.evil.block.eventFilter = function (callback, block, event) {
|
17
|
+
return function () {
|
18
|
+
var params = Array.prototype.slice.call(arguments, 1);
|
19
|
+
var messages = ['Event "' + event + '" on', block.block[0]];
|
20
|
+
if ( params.length > 0 ) {
|
21
|
+
messages.push('with params');
|
22
|
+
messages = messages.concat(params);
|
23
|
+
}
|
24
|
+
log.apply(this, messages);
|
25
|
+
|
26
|
+
callback.apply(this, arguments);
|
27
|
+
};
|
28
|
+
}
|
29
|
+
})();
|
@@ -15,7 +15,9 @@
|
|
15
15
|
|
16
16
|
context[name] = function () {
|
17
17
|
arguments[pos] = arguments[pos].replace(
|
18
|
-
|
18
|
+
/@@([\w\u00c0-\uFFFF\-]+)/g, '[data-block~="$1"]');
|
19
|
+
arguments[pos] = arguments[pos].replace(
|
20
|
+
/@([\w\u00c0-\uFFFF\-]+)/g, '[data-role~="$1"]');
|
19
21
|
return original.apply(context, arguments);
|
20
22
|
};
|
21
23
|
|
@@ -34,20 +36,48 @@
|
|
34
36
|
window.evil = { };
|
35
37
|
}
|
36
38
|
|
37
|
-
/**
|
38
|
-
* Evil blocks list.
|
39
|
-
*/
|
40
|
-
var vitalizers = [];
|
41
|
-
|
42
39
|
/**
|
43
40
|
* If onready event is already happend.
|
44
41
|
*/
|
45
42
|
var ready = false;
|
46
43
|
|
44
|
+
var callbacks = {
|
45
|
+
/**
|
46
|
+
* Create callback wrapper for block listener.
|
47
|
+
*/
|
48
|
+
block: function (self, func) {
|
49
|
+
return function (e) {
|
50
|
+
if ( e.currentTarget == e.target ) {
|
51
|
+
func.apply(self, arguments);
|
52
|
+
}
|
53
|
+
}
|
54
|
+
},
|
55
|
+
|
56
|
+
/**
|
57
|
+
* Create callback wrapper for body/document listener.
|
58
|
+
*/
|
59
|
+
global: function (self, func) {
|
60
|
+
return function () {
|
61
|
+
func.apply(self, arguments);
|
62
|
+
}
|
63
|
+
},
|
64
|
+
|
65
|
+
/**
|
66
|
+
* Create callback wrapper for element listener.
|
67
|
+
*/
|
68
|
+
elem: function (self, func) {
|
69
|
+
return function () {
|
70
|
+
var event = arguments[0];
|
71
|
+
event.el = $(this);
|
72
|
+
func.apply(self, arguments);
|
73
|
+
}
|
74
|
+
}
|
75
|
+
};
|
76
|
+
|
47
77
|
/**
|
48
78
|
* Execute `callback` on every finded `selector` inside `base`.
|
49
79
|
*/
|
50
|
-
var vitalize = function (base, selector,
|
80
|
+
var vitalize = function (base, selector, klass) {
|
51
81
|
var blocks = $().add( base.filter(selector) ).
|
52
82
|
add( base.find(selector) );
|
53
83
|
|
@@ -55,10 +85,19 @@
|
|
55
85
|
return;
|
56
86
|
}
|
57
87
|
|
88
|
+
var inits = [];
|
89
|
+
|
58
90
|
for ( var i = 0; i < blocks.length; i++ ) {
|
59
91
|
var block = $(blocks[i]);
|
60
92
|
|
61
|
-
|
93
|
+
if ( block.data('evil-vitalized') ) {
|
94
|
+
continue;
|
95
|
+
}
|
96
|
+
block.data('evil-vitalized', true);
|
97
|
+
|
98
|
+
var obj = { };
|
99
|
+
|
100
|
+
obj.$ = (function (block) {
|
62
101
|
return function (subselector) {
|
63
102
|
return $(subselector, block);
|
64
103
|
};
|
@@ -68,99 +107,61 @@
|
|
68
107
|
block.find('[data-role]').each(function (_, el) {
|
69
108
|
var roles = el.attributes['data-role'].value.split(' ');
|
70
109
|
for ( var i = 0; i < roles.length; i++ ) {
|
71
|
-
var role = roles[i]
|
72
|
-
|
73
|
-
|
74
|
-
if ( !actives[role] ) {
|
75
|
-
actives[role] = [];
|
110
|
+
var role = roles[i];
|
111
|
+
if ( !obj[role] ) {
|
112
|
+
obj[role] = $();
|
76
113
|
}
|
77
|
-
|
114
|
+
obj[role].push(el);
|
78
115
|
}
|
79
116
|
});
|
80
117
|
|
81
|
-
|
82
|
-
b[role] = b(actives[role]);
|
83
|
-
}
|
118
|
+
obj.block = block;
|
84
119
|
|
85
|
-
var
|
86
|
-
|
87
|
-
|
88
|
-
|
120
|
+
var event = function (type, name, prop) {
|
121
|
+
var result = callbacks[type](obj, prop);
|
122
|
+
if ( window.evil.block.eventFilter ) {
|
123
|
+
result = window.evil.block.eventFilter(result, obj, name);
|
89
124
|
}
|
90
|
-
|
91
|
-
|
92
|
-
};
|
93
|
-
|
94
|
-
/**
|
95
|
-
* Create callback wrapper for block listener.
|
96
|
-
*/
|
97
|
-
var blockCallback = function (self, func) {
|
98
|
-
return function (e) {
|
99
|
-
if ( e.currentTarget == e.target ) {
|
100
|
-
func.apply(self, arguments);
|
101
|
-
}
|
102
|
-
}
|
103
|
-
};
|
104
|
-
|
105
|
-
/**
|
106
|
-
* Create callback wrapper for body/document listener.
|
107
|
-
*/
|
108
|
-
var globalCallback = function (self, func) {
|
109
|
-
return function () {
|
110
|
-
func.apply(self, arguments);
|
111
|
-
}
|
112
|
-
};
|
113
|
-
|
114
|
-
/**
|
115
|
-
* Create callback wrapper for element listener.
|
116
|
-
*/
|
117
|
-
var elemCallback = function (self, func) {
|
118
|
-
return function () {
|
119
|
-
var args = Array.prototype.slice.call(arguments);
|
120
|
-
var el = $(this);
|
121
|
-
args.unshift(el);
|
122
|
-
func.apply(self, args);
|
123
|
-
}
|
124
|
-
};
|
125
|
-
|
126
|
-
/**
|
127
|
-
* Convert block class to callback.
|
128
|
-
*/
|
129
|
-
var convert = function (klass) {
|
130
|
-
return function ($, obj, block) {
|
131
|
-
obj.block = block;
|
125
|
+
return result;
|
126
|
+
};
|
132
127
|
|
133
128
|
for ( var name in klass ) {
|
134
129
|
var prop = klass[name];
|
135
130
|
|
136
|
-
(
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
}
|
131
|
+
if ( name.indexOf('on ') == -1 ) {
|
132
|
+
obj[name] = prop;
|
133
|
+
continue;
|
134
|
+
}
|
141
135
|
|
136
|
+
(function (name, prop) {
|
142
137
|
var parts = name.split(' on ');
|
143
138
|
|
144
139
|
if ( parts[1] == 'body' ) {
|
145
|
-
$('body').on(parts[0],
|
140
|
+
$('body').on(parts[0], event('global', name, prop));
|
146
141
|
|
147
142
|
} else if ( parts[1] == 'window' ) {
|
148
|
-
$(window).on(parts[0],
|
143
|
+
$(window).on(parts[0], event('global', name, prop));
|
149
144
|
|
150
145
|
} else if ( parts[1] ) {
|
151
|
-
block.on(parts[0], parts[1],
|
146
|
+
block.on(parts[0], parts[1], event('elem', name, prop));
|
152
147
|
|
153
148
|
} else {
|
154
|
-
block.on(parts[0],
|
149
|
+
block.on(parts[0], event('block', name, prop));
|
155
150
|
}
|
156
151
|
})(name, prop);
|
157
152
|
}
|
158
153
|
|
159
|
-
|
160
|
-
obj.init();
|
161
|
-
}
|
154
|
+
inits.push(obj);
|
162
155
|
}
|
163
|
-
|
156
|
+
|
157
|
+
if ( klass.init ) {
|
158
|
+
return function () {
|
159
|
+
for ( var i = 0; i < inits.length; i++ ) {
|
160
|
+
klass.init.apply(inits[i]);
|
161
|
+
}
|
162
|
+
};
|
163
|
+
}
|
164
|
+
};
|
164
165
|
|
165
166
|
/**
|
166
167
|
* Create object for every `selector` finded in page and call their
|
@@ -172,8 +173,8 @@
|
|
172
173
|
* delete: ->
|
173
174
|
* @deleteForm.submit ->
|
174
175
|
* $('user-status').trigger('deleted')
|
175
|
-
* 'click on @deleleLink': (
|
176
|
-
*
|
176
|
+
* 'click on @deleleLink': (e) ->
|
177
|
+
* e.el.addClass('is-loading')
|
177
178
|
* delete()
|
178
179
|
* 'on update': ->
|
179
180
|
* location.reload()
|
@@ -181,7 +182,7 @@
|
|
181
182
|
* Every `data-role="aName"` in HTML will create in object `aName` property
|
182
183
|
* with jQuery node.
|
183
184
|
*
|
184
|
-
* To bind delegate listener just create `
|
185
|
+
* To bind delegate listener just create `EVENT on SELECTOR` method.
|
185
186
|
* In first argument it will receive jQuery node of `e.currentTarget`,
|
186
187
|
* second will be event object and others will be parameters.
|
187
188
|
*
|
@@ -198,16 +199,25 @@
|
|
198
199
|
* # init method
|
199
200
|
*/
|
200
201
|
window.evil.block = function (selector, vitalizer) {
|
201
|
-
if ( typeof(vitalizer)
|
202
|
-
vitalizer =
|
202
|
+
if ( typeof(vitalizer) == 'function' ) {
|
203
|
+
vitalizer = { init: vitalizer };
|
203
204
|
}
|
204
|
-
|
205
|
+
|
206
|
+
window.evil.block.vitalizers.push([selector, vitalizer]);
|
205
207
|
|
206
208
|
if ( ready ) {
|
207
|
-
vitalize($(document), selector, vitalizer);
|
209
|
+
var init = vitalize($(document), selector, vitalizer);
|
210
|
+
if ( init ) {
|
211
|
+
init();
|
212
|
+
}
|
208
213
|
}
|
209
214
|
};
|
210
215
|
|
216
|
+
/**
|
217
|
+
* Evil blocks list.
|
218
|
+
*/
|
219
|
+
window.evil.block.vitalizers = [];
|
220
|
+
|
211
221
|
/**
|
212
222
|
* Vitalize all current blocks inside base. You must call it on every
|
213
223
|
* new content from AJAX.
|
@@ -217,11 +227,22 @@
|
|
217
227
|
* evil.block.vitalize $(comments).applyTo(@comments)
|
218
228
|
*/
|
219
229
|
window.evil.block.vitalize = function (base) {
|
220
|
-
|
230
|
+
if ( base ) {
|
231
|
+
base = $(base);
|
232
|
+
} else {
|
233
|
+
base = $(document);
|
234
|
+
}
|
235
|
+
|
236
|
+
var inits = [];
|
237
|
+
for ( var i = 0; i < window.evil.block.vitalizers.length; i++ ) {
|
238
|
+
var vitalizer = window.evil.block.vitalizers[i];
|
239
|
+
inits.push( vitalize(base, vitalizer[0], vitalizer[1]) );
|
240
|
+
}
|
221
241
|
|
222
|
-
for ( var i = 0; i <
|
223
|
-
|
224
|
-
|
242
|
+
for ( var i = 0; i < inits.length; i++ ) {
|
243
|
+
if ( inits[i] ) {
|
244
|
+
inits[i]();
|
245
|
+
}
|
225
246
|
}
|
226
247
|
};
|
227
248
|
|
@@ -229,8 +250,10 @@
|
|
229
250
|
* Run all blocks on load.
|
230
251
|
*/
|
231
252
|
$(document).ready(function () {
|
232
|
-
|
233
|
-
|
253
|
+
setTimeout(function () {
|
254
|
+
ready = true;
|
255
|
+
evil.block.vitalize();
|
256
|
+
}, 1);
|
234
257
|
});
|
235
258
|
|
236
259
|
})(jQuery);
|
metadata
CHANGED
@@ -1,45 +1,44 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: evil-blocks-rails
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.5.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
|
-
- Andrey
|
7
|
+
- Andrey Sitnik
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2014-02-19 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: sprockets
|
15
15
|
requirement: !ruby/object:Gem::Requirement
|
16
16
|
requirements:
|
17
|
-
- -
|
17
|
+
- - ">="
|
18
18
|
- !ruby/object:Gem::Version
|
19
19
|
version: '2'
|
20
20
|
type: :runtime
|
21
21
|
prerelease: false
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
23
23
|
requirements:
|
24
|
-
- -
|
24
|
+
- - ">="
|
25
25
|
- !ruby/object:Gem::Version
|
26
26
|
version: '2'
|
27
|
-
description:
|
28
|
-
|
29
|
-
email:
|
30
|
-
- andrey@sitnik.ru
|
27
|
+
description:
|
28
|
+
email: andrey@sitnik.ru
|
31
29
|
executables: []
|
32
30
|
extensions: []
|
33
31
|
extra_rdoc_files:
|
34
32
|
- LICENSE
|
35
33
|
- README.md
|
36
|
-
- ChangeLog
|
34
|
+
- ChangeLog.md
|
37
35
|
files:
|
38
|
-
-
|
39
|
-
- lib/evil-blocks-rails.rb
|
36
|
+
- ChangeLog.md
|
40
37
|
- LICENSE
|
41
38
|
- README.md
|
42
|
-
-
|
39
|
+
- lib/evil-blocks-rails.rb
|
40
|
+
- lib/evil-blocks.debug.js
|
41
|
+
- lib/evil-blocks.js
|
43
42
|
homepage: https://github.com/ai/evil-blocks
|
44
43
|
licenses:
|
45
44
|
- MIT
|
@@ -50,18 +49,18 @@ require_paths:
|
|
50
49
|
- lib
|
51
50
|
required_ruby_version: !ruby/object:Gem::Requirement
|
52
51
|
requirements:
|
53
|
-
- -
|
52
|
+
- - ">="
|
54
53
|
- !ruby/object:Gem::Version
|
55
54
|
version: '0'
|
56
55
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
57
56
|
requirements:
|
58
|
-
- -
|
57
|
+
- - ">="
|
59
58
|
- !ruby/object:Gem::Version
|
60
59
|
version: '0'
|
61
60
|
requirements: []
|
62
61
|
rubyforge_project:
|
63
|
-
rubygems_version: 2.0
|
62
|
+
rubygems_version: 2.2.0
|
64
63
|
signing_key:
|
65
64
|
specification_version: 4
|
66
|
-
summary: Tiny framework for web pages to split your app to
|
65
|
+
summary: Tiny JS framework for web pages to split your app to independent blocks
|
67
66
|
test_files: []
|
data/ChangeLog
DELETED
@@ -1,24 +0,0 @@
|
|
1
|
-
== 0.4.2 (Zebra, 14th May 1948)
|
2
|
-
* Don’t listen bubbled events as block event.
|
3
|
-
|
4
|
-
== 0.4.1 (Yoke, 30th April 1948)
|
5
|
-
* Allow to listen body and window events from object style.
|
6
|
-
|
7
|
-
== 0.4 (X-Ray, 14th April 1948)
|
8
|
-
* Add new object style.
|
9
|
-
|
10
|
-
== 0.3.2 (Helen of Bikini, 25th July 1946)
|
11
|
-
* Fix searching elements inside several blocks (by Andrei Miroshnik).
|
12
|
-
|
13
|
-
== 0.3.1 (Gilda, 1st July 1946)
|
14
|
-
* Fix in multiple block copies on one page (by Andrei Miroshnik).
|
15
|
-
|
16
|
-
== 0.3 (Fat Man, 9th August 1945)
|
17
|
-
* Add shortcut to Slim to set data-role and class.
|
18
|
-
* Run callback on every block copy in page.
|
19
|
-
|
20
|
-
== 0.2 (Little Boy, 6th August 1945)
|
21
|
-
* Support non-Rails applications in gem.
|
22
|
-
|
23
|
-
== 0.1 (The Gadget, 16th July 1945)
|
24
|
-
* Initial release.
|