evil-blocks-rails 0.4.2 → 0.5.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.
- 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.
|