evrobone 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 2baf24c64a08d5f463fc312f832fbda17bd01b15
4
+ data.tar.gz: 9c5702ca5a0ce30b22eef32385f88762589ba5e5
5
+ SHA512:
6
+ metadata.gz: a5ebdfba039cb37c56a6c5ea3f5f8e7d6b967209045ee1ebd4c2cad5d16ffb7fe2496da01177f922b87246fab18b84a2744f6b7c24b0b463d37b19bdab7a9ae1
7
+ data.tar.gz: 435af2792b86a3c13f8a9e2704f653226e20fb7778355093a3bb0c9c54775fd546926d9c8c0c596e4e31df64d64517cdd98b7966f9558f98484537219f231785
data/.gitignore ADDED
@@ -0,0 +1,3 @@
1
+ *.gem
2
+ /.bundle/
3
+ /Gemfile.lock
data/.rubocop.yml ADDED
@@ -0,0 +1,5 @@
1
+ Metrics/LineLength:
2
+ Max: 120
3
+
4
+ Style/Documentation:
5
+ Enabled: false
data/.travis.yml ADDED
@@ -0,0 +1,9 @@
1
+ language: ruby
2
+ rvm:
3
+ - 2.2.2
4
+ before_install: gem install bundler -v 1.10.4
5
+ env:
6
+ matrix:
7
+ - BUILD_CMD='rubocop'
8
+ - BUILD_CMD='rake coffeelint'
9
+ script: bundle exec ${BUILD_CMD}
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in evrobone.gemspec
4
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2015 Dmitry Karpunin (aka KODer) koderfunk@gmail.com
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,53 @@
1
+ # Evrobone
2
+
3
+ Light-weight client-side framework based on Backbone.js for Ruby on Rails Front-end
4
+
5
+ ## Installation
6
+
7
+ Add this line to your application's Gemfile:
8
+
9
+ ```ruby
10
+ gem 'evrobone'
11
+ ```
12
+
13
+ And then execute:
14
+
15
+ $ bundle
16
+
17
+ Or install it yourself as:
18
+
19
+ $ gem install evrobone
20
+
21
+ ## Usage
22
+
23
+ Coming soon...
24
+
25
+ ### `Evrobone.AppClass` и организация приложения
26
+
27
+ ### `Evrobone.AppMixins.ViewsManagement` и `Evrobone.View`
28
+
29
+ ### `Evrobone.AppMixins.CustomElementBinding` и `/initializers`
30
+
31
+ ### `Evrobone.AppMixins.WindowNavigation` и совместимость с Turbolinks
32
+
33
+ ### `Evrobone.AppMixins.WindowRefresh`
34
+
35
+ ### LiveReload plugin for Ruby on Rails
36
+
37
+ Для более удобного использования LiveReload в Ruby on Rails проекте, можно подключить плагин, добавив в `config/initializers/assets.rb`:
38
+ ```ruby
39
+ Rails.application.config.assets.precompile += %w( evrobone/lib/livereload-plugin-rails.js ) if Rails.env.development?
40
+ ```
41
+ и подключить скрипт в лэйауте:
42
+ ```haml
43
+ - if Rails.env.development?
44
+ = javascript_include_tag 'evrobone/lib/livereload-plugin-rails'
45
+ ```
46
+
47
+ ## Contributing
48
+
49
+ Bug reports and pull requests are welcome on GitHub at https://github.com/KODerFunk/evrobone.
50
+
51
+ ## License
52
+
53
+ The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
data/Rakefile ADDED
@@ -0,0 +1,7 @@
1
+ require 'bundler/gem_tasks'
2
+ require 'coffeelint'
3
+
4
+ desc 'Run CoffeeLint'
5
+ task 'coffeelint' do
6
+ Coffeelint.run_test_suite 'app/assets/javascripts'
7
+ end
@@ -0,0 +1,80 @@
1
+ #= require ./helpers
2
+ #= require ./jquery-additions
3
+
4
+ @Evrobone ||= {}
5
+
6
+ Evrobone.AppMixins ||= {}
7
+
8
+ class Evrobone.AppClass
9
+ @App: null
10
+
11
+ name: null
12
+ performanceReport: true
13
+ performanceMicro: false
14
+
15
+ touchDevice: ('ontouchstart' of document.documentElement and not TEST_MODE)
16
+ # or !!(window.DocumentTouch and document instanceof DocumentTouch)
17
+ $window: null
18
+
19
+ @mixinNames: null
20
+
21
+ # Reset mixinNames
22
+ @mixinable: ->
23
+ @mixinNames = if @mixinNames then _.clone(@mixinNames) else []
24
+
25
+ # Mixins support
26
+ @include: (mixin, name = null) ->
27
+ @mixinNames ||= []
28
+ if name?
29
+ @mixinNames = _.without(@mixinNames, name)
30
+ @mixinNames.push(name)
31
+ unless mixin?
32
+ cout 'error', "AppMixin #{name or '_no_name_'} is undefined"
33
+ _.extend @::, mixin
34
+
35
+ @include Backbone.Events
36
+
37
+ constructor: (name = null) ->
38
+ if @constructor.App
39
+ throw new Error('Can\'t create new AppClass instance because the single instance has already been created')
40
+ else
41
+ cout 'info', 'Evrobone.AppClass.constructor', name, @
42
+ @constructor.App = @
43
+ @name = name
44
+ for mixinName in @constructor.mixinNames
45
+ @[mixinName + 'Initialize']?()
46
+ return
47
+
48
+ start: (initial = true) ->
49
+ @$window = $(window) if initial
50
+ $('body').addClass('touch-device') if @touchDevice
51
+ @performanceReport = DEBUG_MODE if @performanceReport
52
+ @performanceStart() if @performanceReport
53
+ @trigger 'start', initial
54
+ return
55
+
56
+ stop: ->
57
+ @trigger 'stop'
58
+ return
59
+
60
+ performanceStartAt: null
61
+
62
+ performanceStart: ->
63
+ if _.isFunction(performance?.now)
64
+ @performanceStartAt = performance.now()
65
+ else
66
+ cout 'warn', 'performance.now() isnt available'
67
+ @performanceReport = null
68
+
69
+ performancePoint: (message, count = null) ->
70
+ pointAt = performance.now()
71
+ message = @_plurMessage(message, count) if count?
72
+ time = if @performanceMicro
73
+ "#{Math.round((pointAt - @performanceStartAt) * 1000)}\u00B5s"
74
+ else
75
+ "#{Math.round(pointAt - @performanceStartAt)}ms"
76
+ cout 'info', "#{message} in #{time}"
77
+ @performanceStartAt = pointAt
78
+
79
+ _plurMessage: (message, count) ->
80
+ message.replace('%count%', count).replace('%s%', if count is 1 then '' else 's')
@@ -0,0 +1,33 @@
1
+ Evrobone.AppMixins.CustomElementBinding =
2
+
3
+ customElementBindingInitialize: ->
4
+ @on 'start', @customElementBindingStart, @
5
+ return
6
+
7
+ customElementBindingStart: (initial) ->
8
+ @bindCustomElements null, initial
9
+ @performancePoint('Processed %count% custom element binder%s%', @customElementBinders.length) if @performanceReport
10
+ return
11
+
12
+ customElementBinders: []
13
+
14
+ registerCustomElementBinder: (binder) ->
15
+ @customElementBinders.push binder
16
+
17
+ bindCustomElements: ($root = $('body'), initial = false) ->
18
+ for binder in @customElementBinders
19
+ binder arguments...
20
+ return
21
+
22
+ registerEasyBinder: (name, handle) ->
23
+ if _.isArray(name)
24
+ [name, selector] = name
25
+ else
26
+ selector = ".js-#{name}"
27
+ @registerCustomElementBinder ($root, initial) ->
28
+ $elements = $root.find("#{selector}:not(.bound-#{name})")
29
+ $elements.addClass "bound-#{name}"
30
+ if $elements.length
31
+ handle $elements, initial
32
+ return
33
+ return
@@ -0,0 +1,102 @@
1
+ #= require ../view
2
+
3
+ Evrobone.AppMixins.ViewsManagement =
4
+ View: Evrobone.View
5
+
6
+ ViewMixins: {}
7
+ ProtoViews: {}
8
+ Views: {}
9
+
10
+ warnOnMultibind: true
11
+ preventMultibind: false
12
+ groupBindingLog: true
13
+
14
+ viewInstances: null
15
+
16
+ viewsManagementInitialize: ->
17
+ @viewInstances = []
18
+ @on 'start', @viewsManagementStart, @
19
+ @on 'stop', @viewsManagementStop, @
20
+ return
21
+
22
+ viewsManagementStart: (initial) ->
23
+ boundViews = @bindViews()
24
+ @performancePoint('Bound %count% view%s%', boundViews.length) if @performanceReport
25
+ return
26
+
27
+ viewsManagementStop: (initial) ->
28
+ @unbindViews @viewInstances
29
+ return
30
+
31
+ bindViews: ($root = $('html'), checkRoot = true) ->
32
+ if @groupBindingLog
33
+ console?.groupCollapsed? 'bindViews on', $root
34
+ boundViews = []
35
+ sortedViews = _.sortBy(_.pairs(@Views), (p) -> -p[1].priority or 0)
36
+ for [viewName, viewClass] in sortedViews when viewClass::el
37
+ if checkRoot
38
+ @_bindViews boundViews, $root.filter(viewClass::el), viewClass, viewName
39
+ @_bindViews boundViews, $root.find(viewClass::el), viewClass, viewName
40
+ if @groupBindingLog
41
+ console?.groupEnd?()
42
+ boundViews
43
+
44
+ _bindViews: (boundViews, $elements, viewClass, viewName) ->
45
+ $elements.each (index, el) =>
46
+ if view = @bindView(el, viewClass, viewName)
47
+ boundViews.push view
48
+ return
49
+
50
+ bindView: (element, viewClass, viewName) ->
51
+ if @canBind(element, viewClass)
52
+ options = _.defaults( el: element, $(element).data() )
53
+ view = new viewClass(options)
54
+ if viewName
55
+ cout 'info', "Bound view #{viewName}:", view
56
+ @viewInstances.push view
57
+ view
58
+
59
+ canBind: (element, viewClass) ->
60
+ if @warnOnMultibind or @preventMultibind
61
+ views = @getViewsOnElement(element)
62
+ l = views.length
63
+ if l > 0
64
+ if @warnOnMultibind
65
+ cout 'warn', @_plurMessage('Element already has bound %count% view%s%', l), element, viewClass, views
66
+ not @preventMultibind
67
+ else
68
+ true
69
+ else
70
+ true
71
+
72
+ unbindViews: (views, context = null) ->
73
+ return unless views?.length > 0
74
+ for view in views
75
+ view.undelegateEvents()
76
+ view.leave context
77
+ @viewInstances = _.without(@viewInstances, views...)
78
+ cout 'info', @_plurMessage('Unbound %count% view%s%:', views.length), views
79
+ return
80
+
81
+ getViewsOnElement: (element) ->
82
+ element = if element instanceof jQuery then element[0] else element
83
+ _.where @viewInstances, el: element
84
+
85
+ getViewsInContainer: ($container, checkRoot = true) ->
86
+ _.filter @viewInstances, (view) ->
87
+ view.$el.closest($container).length > 0 and (checkRoot or view.el isnt $container[0])
88
+
89
+ getFirstView: (viewClass) ->
90
+ for view in @viewInstances
91
+ if view.constructor is viewClass
92
+ return view
93
+ null
94
+
95
+ getFirstChildView: (viewClass) ->
96
+ for view in @viewInstances
97
+ if view instanceof viewClass
98
+ return view
99
+ null
100
+
101
+ getAllViews: (viewClass) ->
102
+ _.filter(@viewInstances, (view) -> view.constructor is viewClass)
@@ -0,0 +1,38 @@
1
+ Evrobone.AppMixins.WindowNavigation =
2
+
3
+ changeLocation: (url, push = false) ->
4
+ #cout '!> changeLocation', url
5
+ historyMethod = window.history[if push then 'pushState' else 'replaceState']
6
+ if historyMethod
7
+ historyMethod.call window.history, { turbolinks: Turbolinks?, url: url }, '', url
8
+ else
9
+ window.location.hash = url
10
+ return
11
+
12
+ visit: (location) ->
13
+ #cout '!> visit', location
14
+ if Turbolinks?
15
+ Turbolinks.visit location
16
+ else
17
+ window.location = location
18
+ return
19
+
20
+ reloadPage: ->
21
+ #cout '!> reloadPage'
22
+ @visit window.location
23
+
24
+ refreshPage: ->
25
+ if Turbolinks?
26
+ $document = $(document)
27
+ scrollTop = 0
28
+ $document.once 'page:before-unload.refreshPage', =>
29
+ scrollTop = @$window.scrollTop()
30
+ return
31
+ $document.on 'page:load.refreshPage page:restore.refreshPage', =>
32
+ @$window.scrollTop scrollTop
33
+ return
34
+ Turbolinks.visit window.location
35
+ else
36
+ # TODO: сделать возврат scrollTop пробросом через sessionStorage
37
+ window.location = window.location
38
+ return
@@ -0,0 +1,17 @@
1
+ Evrobone.AppMixins.WindowRefresh =
2
+
3
+ windowRefreshEvents: ['DOMContentLoaded', 'load', 'scroll', 'resize', 'orientationchange', 'touchmove']
4
+ windowRefreshBound: false
5
+
6
+ _bindRefresh: ($scrollable, name) ->
7
+ @windowRefreshBound = true
8
+ events = _.map(@windowRefreshEvents, (e) -> "#{e}.#{name}-refresh").join(' ')
9
+ $scrollable.on events, => @trigger "#{name}Refresh", $scrollable.scrollTop(), $scrollable
10
+ return
11
+
12
+ onWindowRefresh: (callback, context) ->
13
+ @_bindRefresh(@$window, 'window') unless @windowRefreshBound
14
+ context.listenTo @, 'windowRefresh', callback
15
+
16
+ offWindowRefresh: (callback, context) ->
17
+ context.stopListening @, 'windowRefresh', callback
@@ -0,0 +1,60 @@
1
+ window.DEBUG_MODE ?= <%= Rails.env.development? %>
2
+ window.TEST_MODE ?= <%= Rails.env.test? %>
3
+ window.LOG_TODO ?= DEBUG_MODE
4
+
5
+ window.cout = =>
6
+ args = _.toArray(arguments)
7
+ method = if args[0] in ['log', 'info', 'warn', 'error', 'assert', 'clear'] then args.shift() else 'log'
8
+ if DEBUG_MODE and console?
9
+ method = console[method]
10
+ if method.apply?
11
+ method.apply(console, args)
12
+ else
13
+ method(args)
14
+ args[0]
15
+
16
+ window._cout = ->
17
+ console.log(arguments) if console?
18
+ arguments[0]
19
+
20
+ window.todo = (subject, location = null, numberOrString = null) =>
21
+ if LOG_TODO
22
+ cout 'warn', "TODO: #{subject}#{if location then " ### #{location}" else ''}#{if numberOrString then (if _.isNumber(numberOrString) then ":#{numberOrString}" else " > #{numberOrString}") else ''}"
23
+
24
+ window.getParams = (searchString = location.search) ->
25
+ q = searchString.replace(/^\?/, '').split('&')
26
+ r = {}
27
+ for e in q
28
+ t = e.split('=')
29
+ r[decodeURIComponent(t[0])] = decodeURIComponent(t[1])
30
+ r
31
+
32
+ window.waitFor = (delay, times, check, success, fail) ->
33
+ startWaitFor = ->
34
+ setTimeout ( ->
35
+ times--
36
+ if check()
37
+ success?(times)
38
+ else if times > 0
39
+ startWaitFor()
40
+ else
41
+ fail?()
42
+ ), delay
43
+ startWaitFor()
44
+
45
+ window.prepareFilterParams = (serializedArray) ->
46
+ filteredParams = _.filter(serializedArray, (param) -> param.name isnt 'utf8' and param.value isnt '')
47
+ params = []
48
+ names = []
49
+ for param in filteredParams
50
+ index = if /\[\]$/.test(param.name) then -1 else _.indexOf(names, param.name)
51
+ if index < 0
52
+ names.push param.name
53
+ params.push param
54
+ else
55
+ params[index] = param
56
+ params.sort (a, b) ->
57
+ if a.name is b.name
58
+ if a.value >= b.value then 1 else -1
59
+ else
60
+ if a.name > b.name then 1 else -1
@@ -0,0 +1,89 @@
1
+ # coffeelint: disable=cyclomatic_complexity
2
+ do ($ = jQuery) =>
3
+
4
+ $.fn.nearestFind = (selector, extremeEdgeSelector = 'form, body') ->
5
+ $edge = @
6
+ $result = $edge.find(selector)
7
+ until $result.length or $edge.is(extremeEdgeSelector)
8
+ $edge = $edge.parent()
9
+ $result = $edge.find(selector)
10
+ $result
11
+
12
+ # [showOrHide[, duration[, callback]]]
13
+ $.fn.slideToggleByState = ->
14
+ if @length
15
+ if arguments.length > 0
16
+ a = _.toArray(arguments)
17
+ if a.shift()
18
+ @slideDown.apply @, a
19
+ else
20
+ @slideUp.apply @, a
21
+ else
22
+ @slideToggle()
23
+ @
24
+
25
+ # http://css-tricks.com/snippets/jquery/mover-cursor-to-end-of-textarea/
26
+ $.fn.focusToEnd = ->
27
+ @each ->
28
+ $this = $(@)
29
+ val = $this.val()
30
+ $this.focus().val('').val val
31
+ return
32
+
33
+ $.regexp ||= {}
34
+
35
+ $.regexp.rorId ||= /(\w+)_(\d+)$/
36
+
37
+ @ror_id = ($elementOrString) ->
38
+ if $elementOrString instanceof jQuery
39
+ id = $elementOrString.data('rorId')
40
+ unless id?
41
+ id = ror_id($elementOrString.attr('id'))
42
+ $elementOrString.data('rorId', id) if id?
43
+ id
44
+ else if _.isString($elementOrString)
45
+ matchResult = $elementOrString.match($.regexp.rorId)
46
+ if matchResult then parseInt(matchResult[2]) else null
47
+ else
48
+ null
49
+
50
+ $.fn.rorId = (id = null, prefix = null) ->
51
+ if arguments.length
52
+ $element = @first()
53
+ elementId = @attr('id')
54
+ if not prefix? and _.isString(elementId)
55
+ prefix = matchResult[1] if matchResult = elementId.match($.regexp.rorId)
56
+ @data('rorId', id)
57
+ if prefix?
58
+ $element.attr 'id', "#{prefix}_#{id}"
59
+ $element
60
+ else
61
+ ror_id @
62
+
63
+ $.fn.getFileName = ->
64
+ fileName = @val()
65
+ if matches = fileName.match(/(.+\\)?(.+)$/)
66
+ fileName = matches[2] or fileName
67
+ fileName
68
+
69
+ $.fn.$each = (callback) ->
70
+ @each (index, element) ->
71
+ callback $(element)
72
+
73
+ $.fn.closestScrollable = ->
74
+ $(_.find(@parents().get(), (p) -> $(p).css('overflowY') is 'auto') or window)
75
+
76
+ # RESEARCH
77
+ # $.fn.blockHide = ->
78
+ # @each -> jQuery._data @, 'olddisplay', 'block'
79
+ # @css 'display', 'none'
80
+ # @
81
+
82
+ $.getCachedScript = (url, callback) ->
83
+ $.ajax
84
+ type: 'GET'
85
+ url: url
86
+ success: callback
87
+ dataType: 'script'
88
+ cache: true
89
+ # coffeelint: enable=cyclomatic_complexity
@@ -0,0 +1,50 @@
1
+ # jQuery UI Effects Stacked Drop based on jQuery UI Effects Drop 1.10.0
2
+ #= require jquery-ui/effect
3
+
4
+ # coffeelint: disable=cyclomatic_complexity
5
+ do ($ = jQuery) ->
6
+
7
+ $.effects.effect.stackedDrop = (o, done) ->
8
+ el = $(@)
9
+ props = ['position', 'top', 'bottom', 'left', 'right', 'opacity', 'height', 'width']
10
+ mode = $.effects.setMode(el, o.mode or 'hide')
11
+ show = mode is 'show'
12
+ direction = o.direction or 'left'
13
+ ref = if (direction is 'up' or direction is 'down') then 'top' else 'left'
14
+ motion = if (direction is 'up' or direction is 'left') then 'pos' else 'neg'
15
+ animation = opacity: (if show then 1 else 0)
16
+
17
+ # Adjust
18
+ $.effects.save el, props
19
+ el.show()
20
+ $.effects.createWrapper el
21
+ distance = o.distance or el[(if ref is 'top' then 'outerHeight' else 'outerWidth')](true) / 2
22
+ el.css('opacity', 0).css ref, (if motion is 'pos' then -distance else distance) if show
23
+
24
+ # Animation
25
+ animationValue = if show then (if motion is 'pos' then '+=' else '-=') else (if motion is 'pos' then '-=' else '+=')
26
+ animation[ref] = animationValue + distance
27
+
28
+ _complete = ->
29
+ $.effects.restore el, props
30
+ $.effects.removeWrapper el
31
+ done()
32
+
33
+ # Animate
34
+ el.animate animation,
35
+ queue: false
36
+ duration: o.duration
37
+ easing: o.easing
38
+ complete: ->
39
+ if mode is 'hide'
40
+ el.hide()
41
+ wrapper = el.parent('.ui-effects-wrapper')
42
+ if wrapper.length
43
+ wrapper.slideUp
44
+ duration: o.duration
45
+ easing: o.easing
46
+ complete: _complete
47
+ else
48
+ _complete()
49
+ undefined
50
+ # coffeelint: enable=cyclomatic_complexity
@@ -0,0 +1,75 @@
1
+ class @LiveReloadPluginRails
2
+ @identifier = 'rails'
3
+ @version = '1.0'
4
+
5
+ window: null
6
+ host: null
7
+ document: null
8
+ console: null
9
+
10
+ constructor: (@window, @host) ->
11
+ @document = @host._reloader.document
12
+ @console = @host._reloader.console
13
+ return
14
+
15
+ debounced: null
16
+
17
+ reload: (path, options) ->
18
+ # в path бывает полный путь до руби файла или имя файла последнего звена пайплайна
19
+ # в options.originalPath же бывает бывает полный путь до файла первого звена пайплайна
20
+ #cout 'reload', path, options
21
+ if /\.css_scsslint_tmp\d+\.css$/.test(path)
22
+ true
23
+ else if /\.css$/i.test(path)
24
+ @reloadStylesheet(path)
25
+ else if App?.refreshPage? and ( /\.(rb|html)$/i.test(path) or /\.haml$/i.test(options.originalPath) )
26
+ @debounced ||= _.debounce(( -> App.refreshPage() ), 300)
27
+ @debounced()
28
+ true
29
+ else
30
+ false
31
+
32
+ reloadStylesheet: (path) ->
33
+ # has to be a real array, because DOMNodeList will be modified
34
+ # coffeelint: disable=max_line_length
35
+ links = (link for link in @document.getElementsByTagName('link') when link.rel.match(/^stylesheet$/i) and not link.__LiveReload_pendingRemoval)
36
+ # coffeelint: enable=max_line_length
37
+ # handle prefixfree
38
+ if @window.StyleFix and @document.querySelectorAll
39
+ for style in @document.querySelectorAll('style[data-href]')
40
+ links.push style
41
+ @console.log "!!! LiveReload found #{links.length} LINKed stylesheets"
42
+ pathRX = new RegExp(path.replace(/\.css$/, '(\\.self)?(-[\\da-f]{32,64})?\\.css$'))
43
+ links = (link for link in links when pathRX.test(pathFromUrl(@host._reloader.linkHref(link))))
44
+ @console.log "!!! Detected #{links.length} LINKed stylesheets for path: #{path}"
45
+ if links.length
46
+ for link in links
47
+ @host._reloader.reattachStylesheetLink(link)
48
+ true
49
+ else
50
+ false
51
+
52
+
53
+
54
+ splitUrl = (url) ->
55
+ if (index = url.indexOf('#')) >= 0
56
+ hash = url.slice(index)
57
+ url = url.slice(0, index)
58
+ else
59
+ hash = ''
60
+ if (index = url.indexOf('?')) >= 0
61
+ params = url.slice(index)
62
+ url = url.slice(0, index)
63
+ else
64
+ params = ''
65
+ return { url, params, hash }
66
+
67
+ pathFromUrl = (url) ->
68
+ url = splitUrl(url).url
69
+ if url.indexOf('file://') is 0
70
+ path = url.replace ///^ file:// (localhost)? ///, ''
71
+ else
72
+ # http : // hostname :8080 /
73
+ path = url.replace ///^ ([^:]+ :)? // ([^:/]+) (:\d*)? / ///, '/'
74
+ # decodeURI has special handling of stuff like semicolons, so use decodeURIComponent
75
+ return decodeURIComponent(path)
@@ -0,0 +1,63 @@
1
+ @Evrobone ||= {}
2
+
3
+ delegateEventSplitter = /^(\S+)\s*(.*)$/
4
+
5
+ class Evrobone.View extends Backbone.View
6
+
7
+ @mixinNames: null
8
+
9
+ constructor: (options) ->
10
+ @reflectOptions options
11
+ super
12
+
13
+ reflectOptions: (options = @$el.data()) ->
14
+ @[attr] = value for attr, value of options when not _.isUndefined(@[attr])
15
+ @
16
+
17
+ # Rest mixinNames
18
+ @mixinable: ->
19
+ @mixinNames = if @mixinNames then _.clone(@mixinNames) else []
20
+
21
+ # Mixins support
22
+ @include: (mixin, name = null) ->
23
+ @mixinNames ||= []
24
+ if name?
25
+ @mixinNames = _.without(@mixinNames, name)
26
+ @mixinNames.push(name)
27
+ unless mixin?
28
+ cout 'error', "ViewMixin #{name or '_no_name_'} is undefined"
29
+ _.extend @::, mixin
30
+
31
+ mixinsEvents: (events = {}) ->
32
+ _.reduce( @constructor.mixinNames, ( (memo, name) -> _.extend(memo, _.result(@, name + 'Events')) ), events, @ )
33
+
34
+ mixinsInitialize: ->
35
+ for name in @constructor.mixinNames
36
+ @[name + 'Initialize']? arguments...
37
+ return
38
+
39
+ mixinsLeave: ->
40
+ if @constructor.mixinNames
41
+ for name in @constructor.mixinNames
42
+ @[name + 'Leave']? arguments...
43
+ return
44
+
45
+ leave: ->
46
+ @mixinsLeave()
47
+ @stopListening()
48
+ return
49
+
50
+ destroy: (destroyDOM = true) =>
51
+ App.unbindViews [@]
52
+ @$el.remove() if destroyDOM
53
+ return
54
+
55
+ # TODO: see https://github.com/jashkenas/backbone/pull/3003/files
56
+ delegateEvents: (events) ->
57
+ return super unless App.touchDevice
58
+ return @ unless events or events = _.result(@, 'events')
59
+ patchedEvents = {}
60
+ for key, method of events
61
+ [[], eventName, selector] = key.match(delegateEventSplitter)
62
+ patchedEvents[if eventName is 'click' then "touchend #{selector}" else key] = method
63
+ super patchedEvents
File without changes
data/bin/console ADDED
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'bundler/setup'
4
+ require 'evrobone'
5
+
6
+ # You can add fixtures and/or initialization code here to make experimenting
7
+ # with your gem easier. You can also use a different console, if you like.
8
+
9
+ # (If you use this, don't forget to add pry to your Gemfile!)
10
+ # require 'pry'
11
+ # Pry.start
12
+
13
+ require 'irb'
14
+ IRB.start
data/bin/setup ADDED
@@ -0,0 +1,7 @@
1
+ #!/bin/bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+
5
+ bundle install
6
+
7
+ # Do any other automated setup that you need to do here
data/coffeelint.json ADDED
@@ -0,0 +1,129 @@
1
+ {
2
+ "arrow_spacing": {
3
+ "level": "error"
4
+ },
5
+ "braces_spacing": {
6
+ "level": "warn",
7
+ "spaces": 1,
8
+ "empty_object_spaces": 0
9
+ },
10
+ "camel_case_classes": {
11
+ "level": "error"
12
+ },
13
+ "coffeescript_error": {
14
+ "level": "error"
15
+ },
16
+ "colon_assignment_spacing": {
17
+ "level": "ignore",
18
+ "spacing": {
19
+ "left": 0,
20
+ "right": 1
21
+ }
22
+ },
23
+ "cyclomatic_complexity": {
24
+ "value": 10,
25
+ "level": "warn"
26
+ },
27
+ "duplicate_key": {
28
+ "level": "error"
29
+ },
30
+ "empty_constructor_needs_parens": {
31
+ "level": "warn"
32
+ },
33
+ "ensure_comprehensions": {
34
+ "level": "warn"
35
+ },
36
+ "eol_last": {
37
+ "level": "warn"
38
+ },
39
+ "indentation": {
40
+ "value": 2,
41
+ "level": "error"
42
+ },
43
+ "line_endings": {
44
+ "level": "warn",
45
+ "value": "unix"
46
+ },
47
+ "max_line_length": {
48
+ "value": 120,
49
+ "level": "error",
50
+ "limitComments": true
51
+ },
52
+ "missing_fat_arrows": {
53
+ "level": "ignore",
54
+ "is_strict": false
55
+ },
56
+ "newlines_after_classes": {
57
+ "value": 3,
58
+ "level": "warn"
59
+ },
60
+ "no_backticks": {
61
+ "level": "error"
62
+ },
63
+ "no_debugger": {
64
+ "level": "warn",
65
+ "console": false
66
+ },
67
+ "no_empty_functions": {
68
+ "level": "warn"
69
+ },
70
+ "no_empty_param_list": {
71
+ "level": "warn"
72
+ },
73
+ "no_implicit_braces": {
74
+ "level": "ignore",
75
+ "strict": true
76
+ },
77
+ "no_implicit_parens": {
78
+ "strict": true,
79
+ "level": "ignore"
80
+ },
81
+ "no_interpolation_in_single_quotes": {
82
+ "level": "warn"
83
+ },
84
+ "no_plusplus": {
85
+ "level": "warn"
86
+ },
87
+ "no_stand_alone_at": {
88
+ "level": "ignore"
89
+ },
90
+ "no_tabs": {
91
+ "level": "error"
92
+ },
93
+ "no_this": {
94
+ "level": "warn"
95
+ },
96
+ "no_throwing_strings": {
97
+ "level": "error"
98
+ },
99
+ "no_trailing_semicolons": {
100
+ "level": "error"
101
+ },
102
+ "no_trailing_whitespace": {
103
+ "level": "error",
104
+ "allowed_in_comments": false,
105
+ "allowed_in_empty_lines": true
106
+ },
107
+ "no_unnecessary_double_quotes": {
108
+ "level": "warn"
109
+ },
110
+ "no_unnecessary_fat_arrows": {
111
+ "level": "warn"
112
+ },
113
+ "non_empty_constructor_needs_parens": {
114
+ "level": "warn"
115
+ },
116
+ "prefer_english_operator": {
117
+ "level": "warn",
118
+ "doubleNotLevel": "warn"
119
+ },
120
+ "space_operators": {
121
+ "level": "warn"
122
+ },
123
+ "spacing_after_comma": {
124
+ "level": "warn"
125
+ },
126
+ "transform_messes_up_line_numbers": {
127
+ "level": "warn"
128
+ }
129
+ }
data/evrobone.gemspec ADDED
@@ -0,0 +1,25 @@
1
+ lib = File.expand_path('../lib', __FILE__)
2
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
3
+ require 'evrobone/version'
4
+
5
+ Gem::Specification.new do |spec|
6
+ spec.name = 'evrobone'
7
+ spec.version = Evrobone::VERSION
8
+ spec.authors = ['Dmitry KODer Karpunin']
9
+ spec.email = ['koderfunk@gmail.com']
10
+
11
+ spec.summary = 'Light-weight client-side framework based on Backbone.js for Ruby on Rails Front-end'
12
+ spec.description = 'Light-weight client-side framework based on Backbone.js for Ruby on Rails Front-end'
13
+ spec.homepage = 'http://github.com/KODerFunk/evrobone'
14
+ spec.license = 'MIT'
15
+
16
+ spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
17
+ spec.bindir = 'exe'
18
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
19
+ spec.require_paths = ['lib']
20
+
21
+ spec.add_development_dependency 'bundler', '~> 1.10'
22
+ spec.add_development_dependency 'rake', '~> 10.0'
23
+ spec.add_development_dependency 'rubocop'
24
+ spec.add_development_dependency 'coffeelint'
25
+ end
data/lib/evrobone.rb ADDED
@@ -0,0 +1,9 @@
1
+ require 'evrobone/version'
2
+
3
+ module Evrobone
4
+ if defined?(Rails)
5
+ class Engine < ::Rails::Engine
6
+ # Rails -> use app/assets directory.
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,3 @@
1
+ module Evrobone
2
+ VERSION = '0.1.0'
3
+ end
metadata ADDED
@@ -0,0 +1,127 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: evrobone
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Dmitry KODer Karpunin
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2015-09-10 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '1.10'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1.10'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '10.0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '10.0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rubocop
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: coffeelint
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ description: Light-weight client-side framework based on Backbone.js for Ruby on Rails
70
+ Front-end
71
+ email:
72
+ - koderfunk@gmail.com
73
+ executables: []
74
+ extensions: []
75
+ extra_rdoc_files: []
76
+ files:
77
+ - ".gitignore"
78
+ - ".rubocop.yml"
79
+ - ".travis.yml"
80
+ - Gemfile
81
+ - LICENSE.txt
82
+ - README.md
83
+ - Rakefile
84
+ - app/assets/javascripts/evrobone/app-class.js.coffee
85
+ - app/assets/javascripts/evrobone/app-mixins/custom-element-binding.js.coffee
86
+ - app/assets/javascripts/evrobone/app-mixins/views-management.js.coffee
87
+ - app/assets/javascripts/evrobone/app-mixins/window-navigation.js.coffee
88
+ - app/assets/javascripts/evrobone/app-mixins/window-refresh.js.coffee
89
+ - app/assets/javascripts/evrobone/helpers.js.coffee.erb
90
+ - app/assets/javascripts/evrobone/initializers/.keep
91
+ - app/assets/javascripts/evrobone/jquery-additions.js.coffee
92
+ - app/assets/javascripts/evrobone/lib/jquery.ui.effect-stacked-drop.js.coffee
93
+ - app/assets/javascripts/evrobone/lib/livereload-plugin-rails.js.coffee
94
+ - app/assets/javascripts/evrobone/view.js.coffee
95
+ - app/assets/javascripts/evrobone/views/.keep
96
+ - bin/console
97
+ - bin/setup
98
+ - coffeelint.json
99
+ - evrobone.gemspec
100
+ - lib/evrobone.rb
101
+ - lib/evrobone/version.rb
102
+ homepage: http://github.com/KODerFunk/evrobone
103
+ licenses:
104
+ - MIT
105
+ metadata: {}
106
+ post_install_message:
107
+ rdoc_options: []
108
+ require_paths:
109
+ - lib
110
+ required_ruby_version: !ruby/object:Gem::Requirement
111
+ requirements:
112
+ - - ">="
113
+ - !ruby/object:Gem::Version
114
+ version: '0'
115
+ required_rubygems_version: !ruby/object:Gem::Requirement
116
+ requirements:
117
+ - - ">="
118
+ - !ruby/object:Gem::Version
119
+ version: '0'
120
+ requirements: []
121
+ rubyforge_project:
122
+ rubygems_version: 2.4.8
123
+ signing_key:
124
+ specification_version: 4
125
+ summary: Light-weight client-side framework based on Backbone.js for Ruby on Rails
126
+ Front-end
127
+ test_files: []