unpoly-rails 0.57.0 → 0.60.0
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of unpoly-rails might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/CHANGELOG.md +393 -1
- data/Gemfile.lock +5 -2
- data/README.md +1 -1
- data/README_RAILS.md +1 -1
- data/Rakefile +10 -1
- data/design/es6.js +32 -0
- data/design/ie11.txt +9 -0
- data/design/measure_jquery/element_list.js +41 -0
- data/design/measure_jquery/up.on_vs_addEventListener.js +56 -0
- data/design/todo_jquery.txt +13 -0
- data/dist/unpoly-bootstrap3.js +8 -8
- data/dist/unpoly-bootstrap3.min.js +1 -1
- data/dist/unpoly.css +22 -20
- data/dist/unpoly.js +6990 -5336
- data/dist/unpoly.min.css +1 -1
- data/dist/unpoly.min.js +4 -4
- data/lib/assets/javascripts/unpoly-bootstrap3/viewport-ext.coffee +5 -0
- data/lib/assets/javascripts/unpoly.coffee +8 -6
- data/lib/assets/javascripts/unpoly/browser.coffee.erb +23 -118
- data/lib/assets/javascripts/unpoly/classes/body_shifter.coffee +36 -0
- data/lib/assets/javascripts/unpoly/classes/cache.coffee +4 -4
- data/lib/assets/javascripts/unpoly/classes/compile_pass.coffee +45 -39
- data/lib/assets/javascripts/unpoly/classes/config.coffee +9 -0
- data/lib/assets/javascripts/unpoly/classes/css_transition.coffee +18 -27
- data/lib/assets/javascripts/unpoly/classes/divertible_chain.coffee +39 -0
- data/lib/assets/javascripts/unpoly/classes/event_listener.coffee +116 -0
- data/lib/assets/javascripts/unpoly/classes/extract_cascade.coffee +8 -8
- data/lib/assets/javascripts/unpoly/classes/extract_plan.coffee +19 -19
- data/lib/assets/javascripts/unpoly/classes/field_observer.coffee +54 -31
- data/lib/assets/javascripts/unpoly/classes/{focus_tracker.coffee → focus_follower.coffee} +2 -2
- data/lib/assets/javascripts/unpoly/classes/follow_variant.coffee +25 -25
- data/lib/assets/javascripts/unpoly/classes/html_parser.coffee +4 -11
- data/lib/assets/javascripts/unpoly/classes/motion_controller.coffee +157 -0
- data/lib/assets/javascripts/unpoly/classes/params.coffee.erb +525 -0
- data/lib/assets/javascripts/unpoly/classes/record.coffee +8 -2
- data/lib/assets/javascripts/unpoly/classes/rect.js +21 -0
- data/lib/assets/javascripts/unpoly/classes/request.coffee +41 -35
- data/lib/assets/javascripts/unpoly/classes/response.coffee +7 -3
- data/lib/assets/javascripts/unpoly/classes/reveal_motion.coffee +102 -0
- data/lib/assets/javascripts/unpoly/classes/scroll_motion.coffee +67 -0
- data/lib/assets/javascripts/unpoly/classes/selector.coffee +60 -0
- data/lib/assets/javascripts/unpoly/classes/tether.coffee +105 -0
- data/lib/assets/javascripts/unpoly/classes/url_set.coffee +12 -7
- data/lib/assets/javascripts/unpoly/element.coffee.erb +1126 -0
- data/lib/assets/javascripts/unpoly/event.coffee.erb +437 -0
- data/lib/assets/javascripts/unpoly/feedback.coffee +73 -94
- data/lib/assets/javascripts/unpoly/form.coffee.erb +188 -181
- data/lib/assets/javascripts/unpoly/{dom.coffee.erb → fragment.coffee.erb} +250 -283
- data/lib/assets/javascripts/unpoly/framework.coffee +67 -0
- data/lib/assets/javascripts/unpoly/history.coffee +29 -28
- data/lib/assets/javascripts/unpoly/legacy.coffee +60 -0
- data/lib/assets/javascripts/unpoly/link.coffee.erb +127 -119
- data/lib/assets/javascripts/unpoly/log.coffee +99 -19
- data/lib/assets/javascripts/unpoly/modal.coffee.erb +95 -118
- data/lib/assets/javascripts/unpoly/motion.coffee.erb +158 -138
- data/lib/assets/javascripts/unpoly/namespace.coffee.erb +0 -5
- data/lib/assets/javascripts/unpoly/popup.coffee.erb +119 -102
- data/lib/assets/javascripts/unpoly/protocol.coffee +11 -15
- data/lib/assets/javascripts/unpoly/proxy.coffee +62 -65
- data/lib/assets/javascripts/unpoly/radio.coffee +3 -5
- data/lib/assets/javascripts/unpoly/rails.coffee +8 -9
- data/lib/assets/javascripts/unpoly/syntax.coffee.erb +173 -125
- data/lib/assets/javascripts/unpoly/toast.coffee +25 -24
- data/lib/assets/javascripts/unpoly/tooltip.coffee +89 -79
- data/lib/assets/javascripts/unpoly/util.coffee.erb +579 -1074
- data/lib/assets/javascripts/unpoly/{layout.coffee.erb → viewport.coffee.erb} +334 -264
- data/lib/assets/stylesheets/unpoly/dom.sass +1 -1
- data/lib/assets/stylesheets/unpoly/layout.sass +2 -0
- data/lib/assets/stylesheets/unpoly/popup.sass +0 -1
- data/lib/assets/stylesheets/unpoly/tooltip.sass +17 -12
- data/lib/unpoly/rails/version.rb +1 -1
- data/package.json +1 -2
- data/spec_app/Gemfile +2 -1
- data/spec_app/Gemfile.lock +38 -27
- data/spec_app/app/assets/javascripts/integration_test.coffee +1 -0
- data/spec_app/app/assets/javascripts/jasmine_specs.coffee +1 -2
- data/spec_app/app/assets/stylesheets/integration_test.sass +14 -1
- data/spec_app/app/controllers/scroll_test_controller.rb +5 -0
- data/spec_app/app/views/css_test/modal.erb +6 -6
- data/spec_app/app/views/css_test/popup.erb +44 -18
- data/spec_app/app/views/css_test/tooltip.erb +23 -4
- data/spec_app/app/views/error_test/trigger.erb +1 -1
- data/spec_app/app/views/form_test/basics/new.erb +1 -3
- data/spec_app/app/views/pages/start.erb +9 -2
- data/spec_app/app/views/reveal_test/long1.erb +1 -1
- data/spec_app/app/views/reveal_test/long2.erb +1 -1
- data/spec_app/app/views/reveal_test/within_document_viewport.erb +24 -0
- data/spec_app/app/views/reveal_test/within_overflowing_div_viewport.erb +28 -0
- data/spec_app/app/views/scroll_test/long1.erb +30 -0
- data/spec_app/config/routes.rb +1 -0
- data/spec_app/spec/javascripts/helpers/agent_detector.coffee +3 -0
- data/spec_app/spec/javascripts/helpers/async_sequence.js.coffee +1 -0
- data/spec_app/spec/javascripts/helpers/browser_switches.js.coffee +17 -5
- data/spec_app/spec/javascripts/helpers/enable_logging.js.coffee +1 -1
- data/spec_app/spec/javascripts/helpers/fixture.js.coffee +25 -0
- data/spec_app/spec/javascripts/helpers/jquery_no_conflict.js +1 -0
- data/spec_app/spec/javascripts/helpers/last_request.js.coffee +1 -0
- data/spec_app/spec/javascripts/helpers/mock_ajax.js.coffee +1 -1
- data/spec_app/spec/javascripts/helpers/parse_form_data.js.coffee +2 -2
- data/spec_app/spec/javascripts/helpers/protect_jasmine_runner.coffee +4 -1
- data/spec_app/spec/javascripts/helpers/remove_body_margin.js.coffee +3 -0
- data/spec_app/spec/javascripts/helpers/reset_history.js.coffee +2 -1
- data/spec_app/spec/javascripts/helpers/reset_knife.js.coffee +2 -2
- data/spec_app/spec/javascripts/helpers/reset_up.js.coffee +18 -11
- data/spec_app/spec/javascripts/helpers/restore_body_scroll.js.coffee +3 -0
- data/spec_app/spec/javascripts/helpers/show_lib_versions.coffee +3 -0
- data/spec_app/spec/javascripts/helpers/spec_util.coffee +47 -0
- data/spec_app/spec/javascripts/helpers/to_be_around.js.coffee +3 -0
- data/spec_app/spec/javascripts/helpers/to_be_array.coffee +5 -0
- data/spec_app/spec/javascripts/helpers/to_be_attached.coffee +6 -2
- data/spec_app/spec/javascripts/helpers/to_be_blank.js.coffee +3 -0
- data/spec_app/spec/javascripts/helpers/to_be_detached.coffee +6 -2
- data/spec_app/spec/javascripts/helpers/to_be_element.js.coffee +8 -0
- data/spec_app/spec/javascripts/helpers/to_be_error.coffee +3 -0
- data/spec_app/spec/javascripts/helpers/to_be_given.js.coffee +3 -0
- data/spec_app/spec/javascripts/helpers/to_be_hidden.js.coffee +8 -0
- data/spec_app/spec/javascripts/helpers/to_be_missing.js.coffee +3 -0
- data/spec_app/spec/javascripts/helpers/to_be_present.js.coffee +3 -0
- data/spec_app/spec/javascripts/helpers/to_be_scrolled_to.coffee +3 -0
- data/spec_app/spec/javascripts/helpers/to_be_visible.js.coffee +9 -0
- data/spec_app/spec/javascripts/helpers/to_contain.js.coffee +3 -0
- data/spec_app/spec/javascripts/helpers/to_end_with.js.coffee +3 -0
- data/spec_app/spec/javascripts/helpers/to_equal_jquery.js.coffee +1 -2
- data/spec_app/spec/javascripts/helpers/to_equal_node_list.coffee +7 -0
- data/spec_app/spec/javascripts/helpers/to_equal_via_is_equal.js.coffee +7 -0
- data/spec_app/spec/javascripts/helpers/to_have_class.js.coffee +10 -0
- data/spec_app/spec/javascripts/helpers/to_have_descendant.js.coffee +10 -0
- data/spec_app/spec/javascripts/helpers/to_have_length.js.coffee +8 -0
- data/spec_app/spec/javascripts/helpers/to_have_opacity.coffee +7 -3
- data/spec_app/spec/javascripts/helpers/to_have_own_property.js.coffee +3 -0
- data/spec_app/spec/javascripts/helpers/to_have_request_method.js.coffee +1 -0
- data/spec_app/spec/javascripts/helpers/to_have_text.js.coffee +9 -0
- data/spec_app/spec/javascripts/helpers/to_have_unhandled_rejections.coffee +0 -21
- data/spec_app/spec/javascripts/helpers/to_match_list.coffee +14 -0
- data/spec_app/spec/javascripts/helpers/to_match_selector.coffee +3 -0
- data/spec_app/spec/javascripts/helpers/to_match_text.js.coffee +4 -1
- data/spec_app/spec/javascripts/helpers/to_match_url.coffee +1 -0
- data/spec_app/spec/javascripts/helpers/trigger.js.coffee +91 -7
- data/spec_app/spec/javascripts/helpers/wait_until_dom_ready.js.coffee +3 -0
- data/spec_app/spec/javascripts/up/browser_spec.js.coffee +23 -90
- data/spec_app/spec/javascripts/up/classes/cache_spec.js.coffee +3 -0
- data/spec_app/spec/javascripts/up/classes/config_spec.coffee +24 -0
- data/spec_app/spec/javascripts/up/classes/divertible_chain_spec.coffee +45 -0
- data/spec_app/spec/javascripts/up/classes/focus_tracker_spec.coffee +5 -2
- data/spec_app/spec/javascripts/up/classes/params_spec.coffee +557 -0
- data/spec_app/spec/javascripts/up/classes/request_spec.coffee +7 -4
- data/spec_app/spec/javascripts/up/classes/scroll_motion_spec.js.coffee +51 -0
- data/spec_app/spec/javascripts/up/classes/store/memory_spec.js.coffee +3 -0
- data/spec_app/spec/javascripts/up/classes/store/session_spec.js.coffee +3 -2
- data/spec_app/spec/javascripts/up/element_spec.coffee +897 -0
- data/spec_app/spec/javascripts/up/event_spec.js.coffee +496 -0
- data/spec_app/spec/javascripts/up/feedback_spec.js.coffee +69 -48
- data/spec_app/spec/javascripts/up/form_spec.js.coffee +252 -194
- data/spec_app/spec/javascripts/up/{dom_spec.js.coffee → fragment_spec.js.coffee} +381 -388
- data/spec_app/spec/javascripts/up/history_spec.js.coffee +21 -19
- data/spec_app/spec/javascripts/up/jquery_spec.js.coffee +4 -0
- data/spec_app/spec/javascripts/up/legacy_spec.js.coffee +27 -0
- data/spec_app/spec/javascripts/up/link_spec.js.coffee +163 -160
- data/spec_app/spec/javascripts/up/log_spec.js.coffee +85 -12
- data/spec_app/spec/javascripts/up/modal_spec.js.coffee +141 -123
- data/spec_app/spec/javascripts/up/motion_spec.js.coffee +117 -113
- data/spec_app/spec/javascripts/up/popup_spec.js.coffee +60 -77
- data/spec_app/spec/javascripts/up/protocol_spec.js.coffee +1 -0
- data/spec_app/spec/javascripts/up/proxy_spec.js.coffee +85 -78
- data/spec_app/spec/javascripts/up/radio_spec.js.coffee +29 -22
- data/spec_app/spec/javascripts/up/rails_spec.js.coffee +14 -13
- data/spec_app/spec/javascripts/up/spec_spec.js.coffee +9 -0
- data/spec_app/spec/javascripts/up/syntax_spec.js.coffee +96 -66
- data/spec_app/spec/javascripts/up/toast_spec.js.coffee +37 -0
- data/spec_app/spec/javascripts/up/tooltip_spec.js.coffee +31 -47
- data/spec_app/spec/javascripts/up/util_spec.js.coffee +725 -562
- data/spec_app/spec/javascripts/up/{layout_spec.js.coffee → viewport_spec.js.coffee} +175 -149
- metadata +57 -19
- data/lib/assets/javascripts/unpoly-bootstrap3/layout-ext.coffee +0 -5
- data/lib/assets/javascripts/unpoly/bus.coffee.erb +0 -518
- data/lib/assets/javascripts/unpoly/classes/extract_step.coffee +0 -4
- data/lib/assets/javascripts/unpoly/classes/motion_tracker.coffee +0 -125
- data/lib/assets/javascripts/unpoly/params.coffee.erb +0 -522
- data/spec_app/spec/javascripts/helpers/append_fixture.js.coffee +0 -8
- data/spec_app/spec/javascripts/up/bus_spec.js.coffee +0 -210
- data/spec_app/spec/javascripts/up/namespace_spec.js.coffee +0 -9
- data/spec_app/spec/javascripts/up/params_spec.coffee +0 -768
- data/spec_app/vendor/asset-libs/jasmine-fixture-1.3.4/jasmine-fixture.js +0 -433
- data/spec_app/vendor/asset-libs/jasmine-jquery-2.1.1/.bower.json +0 -26
- data/spec_app/vendor/asset-libs/jasmine-jquery-2.1.1/jasmine-jquery.js +0 -838
@@ -1,13 +1,12 @@
|
|
1
1
|
u = up.util
|
2
|
+
e = up.element
|
2
3
|
|
3
4
|
class up.CssTransition
|
4
5
|
|
5
|
-
constructor: (
|
6
|
-
@$element = $element
|
7
|
-
@element = u.element($element)
|
8
|
-
@lastFrameCamel = u.camelCaseKeys(lastFrame)
|
9
|
-
@lastFrameKebab = u.kebabCaseKeys(lastFrame)
|
6
|
+
constructor: (@element, @lastFrameKebab, options) ->
|
10
7
|
@lastFrameKeysKebab = Object.keys(@lastFrameKebab)
|
8
|
+
if u.some(@lastFrameKeysKebab, (key) -> key.match(/A-Z/))
|
9
|
+
up.fail('Animation keys must be kebab-case')
|
11
10
|
@finishEvent = options.finishEvent
|
12
11
|
@duration = options.duration
|
13
12
|
@delay = options.delay
|
@@ -35,11 +34,7 @@ class up.CssTransition
|
|
35
34
|
|
36
35
|
listenToFinishEvent: =>
|
37
36
|
if @finishEvent
|
38
|
-
|
39
|
-
|
40
|
-
stopListenToFinishEvent: =>
|
41
|
-
if @finishEvent
|
42
|
-
@$element.off(@finishEvent, @onFinishEvent)
|
37
|
+
@stopListenToFinishEvent = @element.addEventListener(@finishEvent, @onFinishEvent)
|
43
38
|
|
44
39
|
onFinishEvent: (event) =>
|
45
40
|
# don't waste time letting the event bubble up the DOM
|
@@ -48,17 +43,14 @@ class up.CssTransition
|
|
48
43
|
|
49
44
|
startFallbackTimer: =>
|
50
45
|
timingTolerance = 100
|
51
|
-
@fallbackTimer = u.
|
46
|
+
@fallbackTimer = u.timer (@totalDuration + timingTolerance), =>
|
52
47
|
@finish()
|
53
48
|
|
54
49
|
stopFallbackTimer: =>
|
55
50
|
clearTimeout(@fallbackTimer)
|
56
51
|
|
57
52
|
listenToTransitionEnd: =>
|
58
|
-
|
59
|
-
|
60
|
-
stopListenToTransitionEnd: =>
|
61
|
-
@$element.off 'transitionend', @onTransitionEnd
|
53
|
+
@stopListenToTransitionEnd = up.on(@element, 'transitionend', @onTransitionEnd)
|
62
54
|
|
63
55
|
onTransitionEnd: (event) =>
|
64
56
|
# Check if the transitionend event was caused by our own transition,
|
@@ -70,7 +62,7 @@ class up.CssTransition
|
|
70
62
|
elapsed = new Date() - @startTime
|
71
63
|
return unless elapsed > 0.25 * @totalDuration
|
72
64
|
|
73
|
-
completedPropertyKebab = event.
|
65
|
+
completedPropertyKebab = event.propertyName
|
74
66
|
return unless u.contains(@lastFrameKeysKebab, completedPropertyKebab)
|
75
67
|
|
76
68
|
@finish()
|
@@ -81,47 +73,46 @@ class up.CssTransition
|
|
81
73
|
@finished = true
|
82
74
|
|
83
75
|
@stopFallbackTimer()
|
84
|
-
@stopListenToFinishEvent()
|
85
|
-
@stopListenToTransitionEnd()
|
76
|
+
@stopListenToFinishEvent?()
|
77
|
+
@stopListenToTransitionEnd?()
|
86
78
|
|
87
79
|
# Cleanly finish our own transition so the old transition
|
88
80
|
# (or any other transition set right after that) will be able to take effect.
|
89
|
-
|
81
|
+
e.concludeCssTransition(@element)
|
90
82
|
|
91
83
|
@resumeOldTransition()
|
92
84
|
|
93
85
|
@deferred.resolve()
|
94
86
|
|
95
87
|
pauseOldTransition: =>
|
96
|
-
oldTransition =
|
88
|
+
oldTransition = e.style(@element, [
|
97
89
|
'transitionProperty',
|
98
90
|
'transitionDuration',
|
99
91
|
'transitionDelay',
|
100
92
|
'transitionTimingFunction'
|
101
93
|
])
|
102
94
|
|
103
|
-
if
|
95
|
+
if e.hasCssTransition(oldTransition)
|
104
96
|
# Freeze the previous transition at its current place, by setting the currently computed,
|
105
97
|
# animated CSS properties as inline styles. Transitions on all properties will not be frozen,
|
106
98
|
# since that would involve setting every single CSS property as an inline style.
|
107
99
|
unless oldTransition.transitionProperty == 'all'
|
108
100
|
oldTransitionProperties = oldTransition.transitionProperty.split(/\s*,\s*/)
|
109
|
-
oldTransitionFrameKebab =
|
110
|
-
|
111
|
-
@setOldTransitionTargetFrame = u.writeTemporaryStyle(@element, oldTransitionFrameCamel)
|
101
|
+
oldTransitionFrameKebab = e.style(@element, oldTransitionProperties)
|
102
|
+
@setOldTransitionTargetFrame = e.setTemporaryStyle(@element, oldTransitionFrameKebab)
|
112
103
|
|
113
104
|
# Stop the existing CSS transition so it does not emit transitionEnd events
|
114
|
-
@setOldTransition =
|
105
|
+
@setOldTransition = e.concludeCssTransition(@element)
|
115
106
|
|
116
107
|
resumeOldTransition: =>
|
117
108
|
@setOldTransitionTargetFrame?()
|
118
109
|
@setOldTransition?()
|
119
110
|
|
120
111
|
startMotion: =>
|
121
|
-
|
112
|
+
e.setStyle @element,
|
122
113
|
transitionProperty: Object.keys(@lastFrameKebab).join(', ')
|
123
114
|
transitionDuration: "#{@duration}ms"
|
124
115
|
transitionDelay: "#{@delay}ms"
|
125
116
|
transitionTimingFunction: @easing
|
126
|
-
|
117
|
+
e.setStyle(@element, @lastFrameKebab)
|
127
118
|
|
@@ -0,0 +1,39 @@
|
|
1
|
+
u = up.util
|
2
|
+
|
3
|
+
###**
|
4
|
+
A linear task queue whose (2..n)th tasks can be changed at any time.
|
5
|
+
|
6
|
+
@function up.DivertibleChain
|
7
|
+
@internal
|
8
|
+
###
|
9
|
+
class up.DivertibleChain
|
10
|
+
|
11
|
+
constructor: ->
|
12
|
+
@reset()
|
13
|
+
|
14
|
+
reset: =>
|
15
|
+
@queue = []
|
16
|
+
@currentTask = undefined
|
17
|
+
|
18
|
+
promise: =>
|
19
|
+
lastTask = u.last(@allTasks())
|
20
|
+
lastTask?.promise || Promise.resolve()
|
21
|
+
|
22
|
+
allTasks: =>
|
23
|
+
tasks = []
|
24
|
+
tasks.push(@currentTask) if @currentTask
|
25
|
+
tasks = tasks.concat(@queue)
|
26
|
+
tasks
|
27
|
+
|
28
|
+
poke: =>
|
29
|
+
unless @currentTask # don't start a new task while we're still running one
|
30
|
+
if @currentTask = @queue.shift()
|
31
|
+
promise = @currentTask()
|
32
|
+
u.always promise, =>
|
33
|
+
@currentTask = undefined
|
34
|
+
@poke()
|
35
|
+
|
36
|
+
asap: (newTasks...) =>
|
37
|
+
@queue = u.map(newTasks, u.previewable)
|
38
|
+
@poke()
|
39
|
+
@promise()
|
@@ -0,0 +1,116 @@
|
|
1
|
+
u = up.util
|
2
|
+
e = up.element
|
3
|
+
|
4
|
+
class up.EventListener
|
5
|
+
|
6
|
+
constructor: (@element, @eventName, @selector, @callback, options = {}) ->
|
7
|
+
{ @jQuery } = options
|
8
|
+
@key = @constructor.key(@eventName, @selector, @callback)
|
9
|
+
@isDefault = up.framework.isBooting()
|
10
|
+
|
11
|
+
bind: ->
|
12
|
+
map = (@element.upEventListeners ||= {})
|
13
|
+
if map[@key]
|
14
|
+
up.fail('up.on(): The %o callback %o cannot be registered more than once', @eventName, @callback)
|
15
|
+
map[@key] = this
|
16
|
+
@element.addEventListener(@eventName, @nativeCallback)
|
17
|
+
|
18
|
+
unbind: =>
|
19
|
+
if map = @element.upEventListeners
|
20
|
+
delete map[@key]
|
21
|
+
@element.removeEventListener(@eventName, @nativeCallback)
|
22
|
+
|
23
|
+
nativeCallback: (event) =>
|
24
|
+
# 1. Since we're listing on `document`, event.currentTarget is now `document`.
|
25
|
+
# 2. event.target is the element that received an event, which might be a
|
26
|
+
# child of `selector`.
|
27
|
+
# 3. There is only a single event bubbling up the DOM, so we are only called once.
|
28
|
+
|
29
|
+
element = event.target
|
30
|
+
element = e.closest(element, @selector) if @selector
|
31
|
+
|
32
|
+
if element
|
33
|
+
elementArg = if @jQuery then jQuery(element) else element
|
34
|
+
args = [event, elementArg]
|
35
|
+
|
36
|
+
# Do not retrieve and parse [up-data] unless the listener function
|
37
|
+
# expects a third argument. Note that we must pass data for an argument
|
38
|
+
# count of 0, since then the function might take varargs.
|
39
|
+
expectedArgCount = @callback.length
|
40
|
+
|
41
|
+
unless expectedArgCount == 1 || expectedArgCount == 2
|
42
|
+
data = up.syntax.data(element)
|
43
|
+
args.push(data)
|
44
|
+
|
45
|
+
@callback.apply(element, args)
|
46
|
+
|
47
|
+
###
|
48
|
+
Parses the following arg variants into an object:
|
49
|
+
|
50
|
+
- [elements, eventNames, selector, callback]
|
51
|
+
- [elements, eventNames, callback]
|
52
|
+
- [ eventNames, selector, callback]
|
53
|
+
- [ eventNames, callback]
|
54
|
+
|
55
|
+
@function up.EventListener#parseArgs
|
56
|
+
@internal
|
57
|
+
###
|
58
|
+
@parseArgs = (args) ->
|
59
|
+
args = u.copy(args)
|
60
|
+
|
61
|
+
# A callback function is given in all arg variants.
|
62
|
+
callback = args.pop()
|
63
|
+
# Give the callback function a numeric identifier so it
|
64
|
+
# can become part of the upEventListeners key.
|
65
|
+
callback.upUid ||= u.uid()
|
66
|
+
|
67
|
+
# The user can pass an element (or the document, or the window) as the
|
68
|
+
# first argument. If omitted, the listener will bind to the document.
|
69
|
+
if args[0].addEventListener
|
70
|
+
elements = [args.shift()]
|
71
|
+
else if u.isJQuery(args[0]) || (u.isList(args[0]) && args[0][0].addEventListener)
|
72
|
+
elements = args.shift()
|
73
|
+
else
|
74
|
+
elements = [document]
|
75
|
+
|
76
|
+
# Event names are given in all arg variants
|
77
|
+
eventNames = u.splitValues(args.shift())
|
78
|
+
# eventNames = u.map(eventNames, up.legacy.fixEventName)
|
79
|
+
|
80
|
+
# A selector is given if the user wants to delegate events.
|
81
|
+
# It might be undefined.
|
82
|
+
selector = args[0]
|
83
|
+
|
84
|
+
{ elements, eventNames, selector, callback }
|
85
|
+
|
86
|
+
@bind: (args, options) ->
|
87
|
+
parsed = @parseArgs(args)
|
88
|
+
unbindFns = []
|
89
|
+
|
90
|
+
for element in parsed.elements
|
91
|
+
for eventName in parsed.eventNames
|
92
|
+
listener = new @(element, eventName, parsed.selector, parsed.callback, options)
|
93
|
+
listener.bind()
|
94
|
+
unbindFns.push(listener.unbind)
|
95
|
+
|
96
|
+
u.sequence(unbindFns)
|
97
|
+
|
98
|
+
@key: (eventName, selector, callback) ->
|
99
|
+
[eventName, selector, callback.upUid].join('|')
|
100
|
+
|
101
|
+
@unbind: (args) ->
|
102
|
+
parsed = @parseArgs(args)
|
103
|
+
for element in parsed.elements
|
104
|
+
map = element.upEventListeners
|
105
|
+
for eventName in parsed.eventNames
|
106
|
+
key = @key(eventName, parsed.selector, parsed.callback)
|
107
|
+
if map && (listener = map[key])
|
108
|
+
listener.unbind()
|
109
|
+
|
110
|
+
@unbindNonDefault: (element) ->
|
111
|
+
if map = element.upEventListeners
|
112
|
+
listeners = u.values(map)
|
113
|
+
for listener in listeners
|
114
|
+
# Calling unbind() also removes the listener from element.upEventListeners
|
115
|
+
unless listener.isDefault
|
116
|
+
listener.unbind()
|
@@ -2,25 +2,25 @@ u = up.util
|
|
2
2
|
|
3
3
|
class up.ExtractCascade
|
4
4
|
|
5
|
-
constructor: (
|
5
|
+
constructor: (selectorOrElement, options) ->
|
6
6
|
@options = u.options(options, humanizedTarget: 'selector', layer: 'auto')
|
7
|
-
@options.transition
|
8
|
-
@options.hungry
|
7
|
+
@options.transition ?= @options.animation
|
8
|
+
@options.hungry ?= true
|
9
9
|
|
10
|
-
@candidates = @buildCandidates(
|
10
|
+
@candidates = @buildCandidates(selectorOrElement)
|
11
11
|
@plans = u.map @candidates, (candidate, i) =>
|
12
12
|
planOptions = u.copy(@options)
|
13
13
|
if i > 0
|
14
14
|
# If we're using a fallback (any candidate that's not the first),
|
15
15
|
# the original transition might no longer be appropriate.
|
16
|
-
planOptions.transition =
|
16
|
+
planOptions.transition = up.fragment.config.fallbackTransition ? @options.transition
|
17
17
|
new up.ExtractPlan(candidate, planOptions)
|
18
18
|
|
19
19
|
buildCandidates: (selector) ->
|
20
|
-
candidates = [selector, @options.fallback, up.
|
20
|
+
candidates = [selector, @options.fallback, up.fragment.config.fallbacks]
|
21
21
|
candidates = u.flatten(candidates)
|
22
22
|
# Remove undefined, null and false from the list
|
23
|
-
candidates = u.
|
23
|
+
candidates = u.filter(candidates, u.isTruthy)
|
24
24
|
candidates = u.uniq(candidates)
|
25
25
|
|
26
26
|
if @options.fallback == false || @options.provideTarget
|
@@ -39,7 +39,7 @@ class up.ExtractCascade
|
|
39
39
|
@detectPlan('matchExists')
|
40
40
|
|
41
41
|
detectPlan: (checker) =>
|
42
|
-
u.
|
42
|
+
u.find @plans, (plan) -> plan[checker]()
|
43
43
|
|
44
44
|
bestPreflightSelector: =>
|
45
45
|
if @options.provideTarget
|
@@ -1,4 +1,5 @@
|
|
1
1
|
u = up.util
|
2
|
+
e = up.element
|
2
3
|
|
3
4
|
class up.ExtractPlan
|
4
5
|
|
@@ -9,25 +10,25 @@ class up.ExtractPlan
|
|
9
10
|
@transition = options.transition
|
10
11
|
@response = options.response
|
11
12
|
@oldLayer = options.layer
|
12
|
-
originalSelector =
|
13
|
+
originalSelector = e.resolveSelector(selector, @origin)
|
13
14
|
@parseSteps(originalSelector)
|
14
15
|
|
15
16
|
findOld: =>
|
16
17
|
u.each @steps, (step) =>
|
17
|
-
step
|
18
|
+
step.oldElement = up.fragment.first(step.selector, layer: @oldLayer)
|
18
19
|
|
19
20
|
findNew: =>
|
20
21
|
u.each @steps, (step) =>
|
21
22
|
# The response has no layers. It's always just the page.
|
22
|
-
step
|
23
|
+
step.newElement = @response.first(step.selector)
|
23
24
|
|
24
25
|
oldExists: =>
|
25
26
|
@findOld()
|
26
|
-
u.
|
27
|
+
u.every @steps, (step) -> step.oldElement
|
27
28
|
|
28
29
|
newExists: =>
|
29
30
|
@findNew()
|
30
|
-
u.
|
31
|
+
u.every @steps, (step) -> step.newElement
|
31
32
|
|
32
33
|
matchExists: =>
|
33
34
|
@oldExists() && @newExists()
|
@@ -42,16 +43,16 @@ class up.ExtractPlan
|
|
42
43
|
|
43
44
|
# When two replacements target the same element, we would process
|
44
45
|
# the same content twice. We never want that, so we only keep the first step.
|
45
|
-
compressed = u.uniqBy(compressed, (step) -> step
|
46
|
+
compressed = u.uniqBy(compressed, (step) -> step.oldElement)
|
46
47
|
|
47
|
-
compressed = u.
|
48
|
-
u.
|
48
|
+
compressed = u.filter compressed, (candidateStep, candidateIndex) =>
|
49
|
+
u.every compressed, (rivalStep, rivalIndex) =>
|
49
50
|
if rivalIndex == candidateIndex
|
50
51
|
true
|
51
52
|
else
|
52
|
-
candidateElement = candidateStep
|
53
|
-
rivalElement = rivalStep
|
54
|
-
rivalStep.pseudoClass ||
|
53
|
+
candidateElement = candidateStep.oldElement
|
54
|
+
rivalElement = rivalStep.oldElement
|
55
|
+
rivalStep.pseudoClass || !rivalElement.contains(candidateElement)
|
55
56
|
|
56
57
|
# If we revealed before, we should reveal now
|
57
58
|
compressed[0].reveal = @steps[0].reveal
|
@@ -93,16 +94,15 @@ class up.ExtractPlan
|
|
93
94
|
addHungrySteps: =>
|
94
95
|
hungrySteps = []
|
95
96
|
if @hungry
|
96
|
-
|
97
|
-
transition =
|
98
|
-
for hungry in
|
99
|
-
|
100
|
-
|
101
|
-
if $newHungry = @response.first(selector)
|
97
|
+
hungries = e.all(up.radio.hungrySelector())
|
98
|
+
transition = up.radio.config.hungryTransition ? @transition
|
99
|
+
for hungry in hungries
|
100
|
+
selector = e.toSelector(hungry)
|
101
|
+
if newHungry = @response.first(selector)
|
102
102
|
hungrySteps.push
|
103
103
|
selector: selector
|
104
|
-
|
105
|
-
|
104
|
+
oldElement: hungry
|
105
|
+
newElement: newHungry
|
106
106
|
transition: transition
|
107
107
|
reveal: false # we never auto-reveal a hungry element
|
108
108
|
origin: null # don't let the hungry element auto-close a non-sticky modal or popup
|
@@ -1,25 +1,24 @@
|
|
1
1
|
u = up.util
|
2
|
+
e = up.element
|
2
3
|
|
3
4
|
class up.FieldObserver
|
4
5
|
|
5
|
-
|
6
|
-
|
7
|
-
CHANGE_EVENTS = 'input change'
|
8
|
-
|
9
|
-
constructor: (@$field, options) ->
|
6
|
+
constructor: (fieldOrFields, options, @callback) ->
|
7
|
+
@fields = e.list(fieldOrFields)
|
10
8
|
@delay = options.delay
|
11
|
-
@
|
9
|
+
@batch = options.batch
|
12
10
|
|
13
11
|
start: =>
|
14
|
-
|
15
|
-
@
|
16
|
-
@processedValue = @readFieldValue()
|
12
|
+
@scheduledValues = null
|
13
|
+
@processedValues = @readFieldValues()
|
17
14
|
@currentTimer = undefined
|
18
|
-
@
|
19
|
-
|
15
|
+
@callbackRunning = false
|
16
|
+
# Although (depending on the browser) we only need/receive either input or change,
|
17
|
+
# we always bind to both events in case another script manually triggers it.
|
18
|
+
@unbind = up.on(@fields, 'input change', @check)
|
20
19
|
|
21
20
|
stop: =>
|
22
|
-
|
21
|
+
@unbind()
|
23
22
|
@cancelTimer()
|
24
23
|
|
25
24
|
cancelTimer: =>
|
@@ -27,31 +26,55 @@ class up.FieldObserver
|
|
27
26
|
@currentTimer = undefined
|
28
27
|
|
29
28
|
scheduleTimer: =>
|
30
|
-
@
|
29
|
+
@cancelTimer()
|
30
|
+
@currentTimer = u.timer @delay, =>
|
31
31
|
@currentTimer = undefined
|
32
32
|
@requestCallback()
|
33
33
|
|
34
|
-
|
35
|
-
|
34
|
+
scheduleValues: (values) =>
|
35
|
+
@scheduledValues = values
|
36
|
+
@scheduleTimer()
|
37
|
+
|
38
|
+
isNewValues: (values) =>
|
39
|
+
!u.isEqual(values, @processedValues) && !u.isEqual(@scheduledValues, values)
|
36
40
|
|
37
41
|
requestCallback: =>
|
38
|
-
if @
|
39
|
-
|
40
|
-
@
|
41
|
-
@
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
@
|
42
|
+
if @scheduledValues != null && !@currentTimer && !@callbackRunning
|
43
|
+
diff = @changedValues(@processedValues, @scheduledValues)
|
44
|
+
@processedValues = @scheduledValues
|
45
|
+
@scheduledValues = null
|
46
|
+
@callbackRunning = true
|
47
|
+
|
48
|
+
callbackReturnValues = []
|
49
|
+
if @batch
|
50
|
+
callbackReturnValues.push(@callback(diff))
|
51
|
+
else
|
52
|
+
for name, value of diff
|
53
|
+
callbackReturnValues.push(@callback(value, name))
|
54
|
+
|
55
|
+
# Promise.all() will wait for any promises that might be
|
56
|
+
# contained in the `callbackReturnValues` array.
|
57
|
+
callbacksDone = Promise.all(callbackReturnValues)
|
58
|
+
|
59
|
+
u.always callbacksDone, =>
|
60
|
+
@callbackRunning = false
|
47
61
|
@requestCallback()
|
48
62
|
|
49
|
-
|
50
|
-
|
63
|
+
changedValues: (previous, next) ->
|
64
|
+
changes = {}
|
65
|
+
keys = Object.keys(previous)
|
66
|
+
keys = keys.concat(Object.keys(next))
|
67
|
+
keys = u.uniq(keys)
|
68
|
+
for key in keys
|
69
|
+
previousValue = previous[key]
|
70
|
+
nextValue = next[key]
|
71
|
+
unless u.isEqual(previousValue, nextValue)
|
72
|
+
changes[key] = nextValue
|
73
|
+
changes
|
74
|
+
|
75
|
+
readFieldValues: =>
|
76
|
+
up.Params.fromFields(@fields).toObject()
|
51
77
|
|
52
78
|
check: =>
|
53
|
-
|
54
|
-
if @
|
55
|
-
@scheduledValue = value
|
56
|
-
@cancelTimer()
|
57
|
-
@scheduleTimer()
|
79
|
+
values = @readFieldValues()
|
80
|
+
@scheduleValues(values) if @isNewValues(values)
|