handlers-js 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +20 -0
- data/.travis.yml +18 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +107 -0
- data/Rakefile +17 -0
- data/handlers-js.gemspec +21 -0
- data/lib/assets/javascripts/handlers.coffee +46 -0
- data/lib/handlers-js.rb +5 -0
- data/spec/javascripts/HandlersSpec.coffee +56 -0
- data/spec/javascripts/TriggerSpec.coffee +51 -0
- data/spec/javascripts/support/jasmine.yml +75 -0
- data/src/jquery-1.8.3.js +9472 -0
- metadata +99 -0
data/.gitignore
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
*.gem
|
2
|
+
*.rbc
|
3
|
+
.bundle
|
4
|
+
.config
|
5
|
+
.yardoc
|
6
|
+
Gemfile.lock
|
7
|
+
InstalledFiles
|
8
|
+
_yardoc
|
9
|
+
coverage
|
10
|
+
doc/
|
11
|
+
lib/bundler/man
|
12
|
+
pkg
|
13
|
+
rdoc
|
14
|
+
spec/reports
|
15
|
+
test/tmp
|
16
|
+
test/version_tmp
|
17
|
+
tmp
|
18
|
+
src/handlers.js
|
19
|
+
spec/javascripts/HandlersSpec.js
|
20
|
+
spec/javascripts/TriggerSpec.js
|
data/.travis.yml
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
language: ruby
|
2
|
+
before_install:
|
3
|
+
- gem install bundler
|
4
|
+
- npm install coffee-script
|
5
|
+
- "export DISPLAY=:99.0"
|
6
|
+
- "sh -e /etc/init.d/xvfb start"
|
7
|
+
|
8
|
+
rvm:
|
9
|
+
- 1.9.3
|
10
|
+
|
11
|
+
branches:
|
12
|
+
only:
|
13
|
+
- master
|
14
|
+
|
15
|
+
notifications:
|
16
|
+
email:
|
17
|
+
recipients:
|
18
|
+
- handlers@erichmenge.com
|
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2012 Erich Menge
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,107 @@
|
|
1
|
+
# Handlers-js [![Build Status](https://travis-ci.org/erichmenge/handlers-js.png?branch=master)](https://travis-ci.org/erichmenge/handlers-js)
|
2
|
+
|
3
|
+
Handlers-js provides an easy to use, dead simple, unobtrusive Javascript modularization through the Rails asset pipeline.
|
4
|
+
|
5
|
+
What's that mean? That means all you need to do is register your handler, and then call it with `data-handler="MyHandler"` on your HTML tags.
|
6
|
+
|
7
|
+
## Features
|
8
|
+
|
9
|
+
* Works "out of the box" with Turbolinks
|
10
|
+
* Easy setup with pjax
|
11
|
+
* Isolation. Code isn't executed where it doesn't belong. No worrying about code leaking by binding to different styles and element IDs.
|
12
|
+
* Consistent patterns.
|
13
|
+
|
14
|
+
## Installation
|
15
|
+
|
16
|
+
Add this line to your application's Gemfile:
|
17
|
+
|
18
|
+
gem 'handlers-js'
|
19
|
+
|
20
|
+
And then execute:
|
21
|
+
|
22
|
+
$ bundle
|
23
|
+
|
24
|
+
## Usage
|
25
|
+
|
26
|
+
CoffeeScript is provided via the asset pipeline. A single class called `Handlers` is created.
|
27
|
+
Your asset manifest should look something like this:
|
28
|
+
|
29
|
+
```
|
30
|
+
//= require turbolinks
|
31
|
+
//= require handlers
|
32
|
+
//= require_tree .
|
33
|
+
```
|
34
|
+
|
35
|
+
**IMPORTANT** If you're using Turbolinks, make sure it is included first so that Handlers-js knows about it.
|
36
|
+
|
37
|
+
Now, simply create your CoffeeScript files like so:
|
38
|
+
|
39
|
+
```
|
40
|
+
Handlers.register 'TypeAhead', class
|
41
|
+
constructor: (el) ->
|
42
|
+
url = $(el).data('typeahead-url')
|
43
|
+
property = $(el).data('typeahead-property')
|
44
|
+
value = $(el).data('typeahead-value')
|
45
|
+
|
46
|
+
$(el).typeahead
|
47
|
+
source: (typeahead, query) ->
|
48
|
+
$.ajax
|
49
|
+
dataType: 'json'
|
50
|
+
url: url + query
|
51
|
+
success: (data) ->
|
52
|
+
typeahead.process(data)
|
53
|
+
property: property
|
54
|
+
onselect: (val) ->
|
55
|
+
$(el).val(val.name)
|
56
|
+
$(el).closest('form').submit()
|
57
|
+
|
58
|
+
destroy: ->
|
59
|
+
doSomeCleanup()
|
60
|
+
```
|
61
|
+
|
62
|
+
Then, in your HTML:
|
63
|
+
|
64
|
+
```
|
65
|
+
<span data-handler="TypeAhead" data-typeahead-url="..." data-typeahead-property="..." data-typeahead-value="...">
|
66
|
+
...
|
67
|
+
</span>
|
68
|
+
```
|
69
|
+
|
70
|
+
Need to use more than one Handler on a particular element? No problem. They are comma separated.
|
71
|
+
|
72
|
+
```
|
73
|
+
<span data-handler="TypeAhead,SomethingSpecial" ...></span>
|
74
|
+
```
|
75
|
+
|
76
|
+
### If you're not using Turbolinks
|
77
|
+
|
78
|
+
If you're not using Turbolinks, fear not. Handlers are still easy to use.
|
79
|
+
|
80
|
+
Handlers responds to three triggers:
|
81
|
+
|
82
|
+
* handlers:pageChanged - pageChanged should be triggered when the page is loaded for the first time, this will load any handler found on the page.
|
83
|
+
* handlers:pageUpdating - This should be triggered when the page is changing, it must be passed the scope it should perform on
|
84
|
+
* handlers:pageUpdated - This should be triggered when the page change is complete, it must be passed the scope it should perform on
|
85
|
+
|
86
|
+
A pjax example:
|
87
|
+
|
88
|
+
``` main.coffee
|
89
|
+
|
90
|
+
$(document).on 'pjax:start', ->
|
91
|
+
$(document).trigger 'handlers:pageUpdating', '[data-pjax-container]'
|
92
|
+
|
93
|
+
$(document).on 'pjax:end', ->
|
94
|
+
$(document).trigger 'handlers:pageUpdated', '[data-pjax-container]'
|
95
|
+
|
96
|
+
$ ->
|
97
|
+
$(document).trigger 'handlers:pageChanged'
|
98
|
+
```
|
99
|
+
|
100
|
+
|
101
|
+
## Contributing
|
102
|
+
|
103
|
+
1. Fork it
|
104
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
105
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
106
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
107
|
+
5. Create new Pull Request
|
data/Rakefile
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
require "bundler/gem_tasks"
|
2
|
+
|
3
|
+
begin
|
4
|
+
require 'jasmine'
|
5
|
+
load 'jasmine/tasks/jasmine.rake'
|
6
|
+
rescue LoadError
|
7
|
+
task :jasmine do
|
8
|
+
abort "Jasmine is not available. In order to run jasmine, you must: (sudo) gem install jasmine"
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
task :build_js do
|
13
|
+
`coffee -o src -c lib/assets/javascripts/handlers.coffee`
|
14
|
+
`coffee -c spec/javascripts`
|
15
|
+
end
|
16
|
+
|
17
|
+
task :default => [:build_js, 'jasmine:ci']
|
data/handlers-js.gemspec
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
|
5
|
+
Gem::Specification.new do |gem|
|
6
|
+
gem.name = "handlers-js"
|
7
|
+
gem.version = "0.0.1"
|
8
|
+
gem.authors = ["Erich Menge"]
|
9
|
+
gem.email = ["erich.menge@me.com"]
|
10
|
+
gem.description = %q{Easy, modular UJS for your Rails apps}
|
11
|
+
gem.summary = %q{Easy, modular UJS for your Rails apps}
|
12
|
+
gem.homepage = "https://github.com/erichmenge/handlers-js"
|
13
|
+
|
14
|
+
gem.files = `git ls-files`.split($/)
|
15
|
+
gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
|
16
|
+
gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
|
17
|
+
gem.require_paths = ["lib"]
|
18
|
+
|
19
|
+
gem.add_development_dependency('rake')
|
20
|
+
gem.add_development_dependency('jasmine')
|
21
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
class @Handlers
|
2
|
+
@handlers = {}
|
3
|
+
|
4
|
+
@register: (handler, registered_class) ->
|
5
|
+
throw "No constructor on " + handler unless registered_class.constructor
|
6
|
+
@handlers[handler] = registered_class
|
7
|
+
|
8
|
+
@instantiate: (handlers, element) ->
|
9
|
+
handlers = handlers.replace(/\s/g, '').split(',')
|
10
|
+
element.handlers = []
|
11
|
+
$.each handlers, (index, handler) =>
|
12
|
+
if @handlers[handler]
|
13
|
+
instance = new @handlers[handler](element)
|
14
|
+
element.handlers.push instance
|
15
|
+
else
|
16
|
+
throw "Unknown handler " + handler
|
17
|
+
|
18
|
+
@destroy: (instances) ->
|
19
|
+
$.each instances, (index, instance) ->
|
20
|
+
instance.destroy() if instance.destroy
|
21
|
+
|
22
|
+
@unregister_all: ->
|
23
|
+
@handlers = {}
|
24
|
+
|
25
|
+
$(document).on 'handlers:pageChanged', ->
|
26
|
+
$('[data-handler]').each ->
|
27
|
+
Handlers.instantiate $(this).attr('data-handler'), this
|
28
|
+
|
29
|
+
$(document).on 'handlers:pageUpdated', (event, scope) ->
|
30
|
+
$(scope).find('[data-handler]').each ->
|
31
|
+
Handlers.instantiate $(this).attr('data-handler'), this
|
32
|
+
|
33
|
+
$(document).on 'handlers:pageUpdating', (event, scope) ->
|
34
|
+
$(scope).find('[data-handler]').each ->
|
35
|
+
Handlers.destroy @handlers if @handlers
|
36
|
+
delete @handlers if @handlers
|
37
|
+
|
38
|
+
if typeof(Turbolinks) != undefined
|
39
|
+
$(document).on 'page:fetch', ->
|
40
|
+
$(document).trigger 'handlers:pageUpdating', 'body'
|
41
|
+
|
42
|
+
$(document).on 'page:change', ->
|
43
|
+
$(document).trigger 'handlers:pageUpdated', 'body'
|
44
|
+
|
45
|
+
$ ->
|
46
|
+
$(document).trigger 'handlers:pageChanged'
|
data/lib/handlers-js.rb
ADDED
@@ -0,0 +1,56 @@
|
|
1
|
+
describe "Handlers", =>
|
2
|
+
el = null
|
3
|
+
|
4
|
+
beforeEach =>
|
5
|
+
el = {}
|
6
|
+
|
7
|
+
afterEach =>
|
8
|
+
Handlers.unregister_all()
|
9
|
+
|
10
|
+
it "should be defined", ->
|
11
|
+
expect(Handlers).toBeDefined()
|
12
|
+
|
13
|
+
it "should register a handler", ->
|
14
|
+
Handlers.register 'Foo', class
|
15
|
+
expect(Handlers.handlers['Foo']).toBeDefined()
|
16
|
+
|
17
|
+
describe "multiple handlers", ->
|
18
|
+
|
19
|
+
beforeEach ->
|
20
|
+
Handlers.register 'Foo', class
|
21
|
+
Handlers.register 'Bar', class
|
22
|
+
|
23
|
+
afterEach ->
|
24
|
+
expect(Handlers.handlers['Foo']).toBeDefined()
|
25
|
+
expect(Handlers.handlers['Bar']).toBeDefined()
|
26
|
+
|
27
|
+
it "should instantiate them", ->
|
28
|
+
expect(-> Handlers.instantiate('Foo, Bar', el)).not.toThrow()
|
29
|
+
|
30
|
+
it "should allow spaces", ->
|
31
|
+
expect(-> Handlers.instantiate('Foo, Bar', el)).not.toThrow()
|
32
|
+
|
33
|
+
it "should attach to an element", ->
|
34
|
+
Handlers.register 'Foo', class
|
35
|
+
expect(el.handlers).not.toBeDefined()
|
36
|
+
expect(-> Handlers.instantiate('Foo', el)).not.toThrow()
|
37
|
+
expect(el.handlers).toBeDefined()
|
38
|
+
|
39
|
+
it "should throw if the handler isn't known", ->
|
40
|
+
expect(-> Handlers.instantiate('Foo', el)).toThrow()
|
41
|
+
|
42
|
+
it "should destroy if the destroy function is available", ->
|
43
|
+
Handlers.register 'Foo', class
|
44
|
+
constructor: (el) ->
|
45
|
+
destroy: ->
|
46
|
+
|
47
|
+
Handlers.instantiate('Foo', el)
|
48
|
+
spyOn(el.handlers[0], 'destroy')
|
49
|
+
Handlers.destroy(el.handlers)
|
50
|
+
expect(el.handlers[0].destroy).toHaveBeenCalled()
|
51
|
+
|
52
|
+
it "should not throw if there is not a destructor", ->
|
53
|
+
Handlers.register 'Foo', class
|
54
|
+
Handlers.instantiate 'Foo', el
|
55
|
+
expect(-> Handlers.destroy(el.handlers)).not.toThrow()
|
56
|
+
|
@@ -0,0 +1,51 @@
|
|
1
|
+
describe 'Triggers', =>
|
2
|
+
beforeEach =>
|
3
|
+
$('body').append('<span style="display: none"; id="foospan" data-handler="Foo"></span>')
|
4
|
+
Handlers.register 'Foo', class
|
5
|
+
constructor: (el) ->
|
6
|
+
@el = $(el)
|
7
|
+
$(el).html('filled')
|
8
|
+
destroy: ->
|
9
|
+
@el.html("destroyed")
|
10
|
+
|
11
|
+
afterEach =>
|
12
|
+
$('#foospan').remove()
|
13
|
+
Handlers.unregister_all
|
14
|
+
|
15
|
+
describe 'When the page changes', ->
|
16
|
+
it 'should trigger the event', ->
|
17
|
+
$(document).trigger 'handlers:pageChanged'
|
18
|
+
expect($('#foospan').html()).toEqual "filled"
|
19
|
+
|
20
|
+
describe 'When page is updated', ->
|
21
|
+
it 'should trigger the event', ->
|
22
|
+
$(document).trigger 'handlers:pageUpdated', 'body'
|
23
|
+
expect($('#foospan').html()).toEqual "filled"
|
24
|
+
|
25
|
+
it 'should maintain scope', ->
|
26
|
+
$('#foospan').html('')
|
27
|
+
$('body').append(
|
28
|
+
'<span style="display: none"; id="onlyscope">
|
29
|
+
<span style="display: none"; id="scopedspan" data-handler="Foo"></span>
|
30
|
+
</span>'
|
31
|
+
)
|
32
|
+
$(document).trigger 'handlers:pageUpdated', '#onlyscope'
|
33
|
+
expect($('#foospan').html()).toEqual ""
|
34
|
+
expect($('#scopedspan').html()).toEqual "filled"
|
35
|
+
|
36
|
+
describe 'When page is changing', ->
|
37
|
+
it 'should destroy', ->
|
38
|
+
$(document).trigger('handlers:pageChanged')
|
39
|
+
$(document).trigger('handlers:pageUpdating', 'body')
|
40
|
+
expect($('#foospan').html()).toEqual "destroyed"
|
41
|
+
|
42
|
+
it 'should maintain scope', ->
|
43
|
+
$('body').append(
|
44
|
+
'<span style="display: none"; id="onlyscope">
|
45
|
+
<span style="display: none"; id="scopedspan" data-handler="Foo"></span>
|
46
|
+
</span>'
|
47
|
+
)
|
48
|
+
$(document).trigger('handlers:pageChanged')
|
49
|
+
$(document).trigger('handlers:pageUpdating', '#onlyscope')
|
50
|
+
expect($('#foospan').html()).toEqual "filled"
|
51
|
+
expect($('#scopedspan').html()).toEqual "destroyed"
|
@@ -0,0 +1,75 @@
|
|
1
|
+
# src_files
|
2
|
+
#
|
3
|
+
# Return an array of filepaths relative to src_dir to include before jasmine specs.
|
4
|
+
# Default: []
|
5
|
+
#
|
6
|
+
# EXAMPLE:
|
7
|
+
#
|
8
|
+
# src_files:
|
9
|
+
# - lib/source1.js
|
10
|
+
# - lib/source2.js
|
11
|
+
# - dist/**/*.js
|
12
|
+
#
|
13
|
+
src_files:
|
14
|
+
- src/jquery-1.8.3.js
|
15
|
+
- src/handlers.js
|
16
|
+
|
17
|
+
# stylesheets
|
18
|
+
#
|
19
|
+
# Return an array of stylesheet filepaths relative to src_dir to include before jasmine specs.
|
20
|
+
# Default: []
|
21
|
+
#
|
22
|
+
# EXAMPLE:
|
23
|
+
#
|
24
|
+
# stylesheets:
|
25
|
+
# - css/style.css
|
26
|
+
# - stylesheets/*.css
|
27
|
+
#
|
28
|
+
stylesheets:
|
29
|
+
|
30
|
+
# helpers
|
31
|
+
#
|
32
|
+
# Return an array of filepaths relative to spec_dir to include before jasmine specs.
|
33
|
+
# Default: ["helpers/**/*.js"]
|
34
|
+
#
|
35
|
+
# EXAMPLE:
|
36
|
+
#
|
37
|
+
# helpers:
|
38
|
+
# - helpers/**/*.js
|
39
|
+
#
|
40
|
+
helpers:
|
41
|
+
- 'helpers/**/*.js'
|
42
|
+
# spec_files
|
43
|
+
#
|
44
|
+
# Return an array of filepaths relative to spec_dir to include.
|
45
|
+
# Default: ["**/*[sS]pec.js"]
|
46
|
+
#
|
47
|
+
# EXAMPLE:
|
48
|
+
#
|
49
|
+
# spec_files:
|
50
|
+
# - **/*[sS]pec.js
|
51
|
+
#
|
52
|
+
spec_files:
|
53
|
+
- '**/*[sS]pec.js'
|
54
|
+
|
55
|
+
# src_dir
|
56
|
+
#
|
57
|
+
# Source directory path. Your src_files must be returned relative to this path. Will use root if left blank.
|
58
|
+
# Default: project root
|
59
|
+
#
|
60
|
+
# EXAMPLE:
|
61
|
+
#
|
62
|
+
# src_dir: public
|
63
|
+
#
|
64
|
+
src_dir:
|
65
|
+
|
66
|
+
# spec_dir
|
67
|
+
#
|
68
|
+
# Spec directory path. Your spec_files must be returned relative to this path.
|
69
|
+
# Default: spec/javascripts
|
70
|
+
#
|
71
|
+
# EXAMPLE:
|
72
|
+
#
|
73
|
+
# spec_dir: spec/javascripts
|
74
|
+
#
|
75
|
+
spec_dir:
|