sproutcore 0.9.6 → 0.9.7
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.
- data/Manifest.txt +12 -2
- data/bin/sc-build +3 -0
- data/bin/sc-gen +3 -0
- data/bin/sc-server +3 -0
- data/bin/sproutcore +3 -0
- data/clients/sc_docs/HISTORY +3 -0
- data/clients/sc_docs/README +6 -0
- data/clients/sc_test_runner/HISTORY +3 -0
- data/clients/sc_test_runner/README +4 -0
- data/clients/view_builder/core.js +19 -0
- data/clients/view_builder/english.lproj/body.css +149 -0
- data/clients/view_builder/english.lproj/body.rhtml +18 -0
- data/clients/view_builder/english.lproj/strings.js +14 -0
- data/clients/view_builder/main.js +37 -0
- data/frameworks/prototype/HISTORY +3 -0
- data/frameworks/prototype/README +2 -0
- data/frameworks/sproutcore/HISTORY +345 -0
- data/frameworks/sproutcore/README +15 -1
- data/frameworks/sproutcore/foundation/utils.js +8 -5
- data/frameworks/sproutcore/tests/views/popup_button.rhtml +3 -2
- data/frameworks/sproutcore/views/collection/collection.js +4 -5
- data/frameworks/sproutcore/views/collection/source_list.js +8 -5
- data/frameworks/sproutcore/views/field/text_field.js +16 -5
- data/frameworks/sproutcore/views/view.js +19 -1
- data/lib/sproutcore/bundle.rb +1 -1
- data/lib/sproutcore/version.rb +1 -1
- metadata +14 -4
- data/frameworks/sproutcore/animation/animation.js +0 -411
- data/frameworks/sproutcore/foundation/sorted_set.js +0 -590
@@ -1 +1,15 @@
|
|
1
|
-
SproutCore
|
1
|
+
SproutCore JavaScript Application Framework
|
2
|
+
|
3
|
+
SproutCore is a framework for building desktop-like applications in the web without the use of plugins. With SproutCore, you build rich, interactive
|
4
|
+
applications in the web in less code than most simple web pages require today.
|
5
|
+
|
6
|
+
The easiest way to get started with SproutCore is using the BlueRibbon build
|
7
|
+
tools included with the SproutCore package when you install it from the Ruby
|
8
|
+
Gems. Please see the Getting Started documentation on the SproutCore website
|
9
|
+
for more information.
|
10
|
+
|
11
|
+
http://www.sproutcore.com
|
12
|
+
|
13
|
+
If you would like to contact the authors, you can reach the current maintainer
|
14
|
+
at charles@sproutit.com
|
15
|
+
|
@@ -133,6 +133,7 @@ Object.extend(SC,
|
|
133
133
|
/** Finds the absolute viewportOffset for a given element.
|
134
134
|
This method is more accurate than the version provided by prototype.
|
135
135
|
|
136
|
+
If you pass NULL to this method, it will return a { x:0, y:0 }
|
136
137
|
@param el The DOM element
|
137
138
|
@returns {Point} A hash with x,y offsets.
|
138
139
|
*/
|
@@ -141,7 +142,7 @@ Object.extend(SC,
|
|
141
142
|
|
142
143
|
// add up all the offsets for the element.
|
143
144
|
var element = el ;
|
144
|
-
|
145
|
+
while (element) {
|
145
146
|
valueT += (element.offsetTop || 0) + (element.clientTop || 0);
|
146
147
|
valueL += (element.offsetLeft || 0) + (element.clientLeft || 0);
|
147
148
|
|
@@ -163,17 +164,19 @@ Object.extend(SC,
|
|
163
164
|
if (element.offsetParent == document.body &&
|
164
165
|
Element.getStyle(element, 'position') == 'absolute') break;
|
165
166
|
|
167
|
+
element = element.offsetParent ;
|
166
168
|
|
167
|
-
|
168
|
-
} while (element = element.offsetParent);
|
169
|
+
}
|
169
170
|
|
170
171
|
element = el;
|
171
|
-
|
172
|
+
while (element) {
|
172
173
|
if (!Prototype.Browser.Opera || element.tagName == 'BODY') {
|
173
174
|
valueT -= element.scrollTop || 0;
|
174
175
|
valueL -= element.scrollLeft || 0;
|
175
176
|
}
|
176
|
-
|
177
|
+
|
178
|
+
element = element.parentNode ;
|
179
|
+
}
|
177
180
|
|
178
181
|
return { x: valueL, y: valueT } ;
|
179
182
|
},
|
@@ -8,7 +8,7 @@ Test.context("A SC.PopupButtonView with no configured menu", {
|
|
8
8
|
{
|
9
9
|
for (var i=0, n=this.buttons.length; i < n; i++)
|
10
10
|
{
|
11
|
-
(
|
11
|
+
(buttons[i].get('menu') == null).shouldEqual(true);
|
12
12
|
}
|
13
13
|
},
|
14
14
|
"Should return false if asked to trigger it's action": function()
|
@@ -32,9 +32,10 @@ Test.context("A SC.PopupButtonView with no configured menu", {
|
|
32
32
|
SC.PopupButtonView.extend({ menuName: '' }).viewFor(null),
|
33
33
|
SC.PopupButtonView.extend({ menuName: 'foobarMenu' }).viewFor(null),
|
34
34
|
SC.PopupButtonView.extend({ menuName: false }).viewFor(null),
|
35
|
-
SC.PopupButtonView.extend({ menuName: null }).viewFor(null)
|
35
|
+
SC.PopupButtonView.extend({ menuName: null }).viewFor(null)
|
36
36
|
];
|
37
37
|
},
|
38
|
+
|
38
39
|
teardown: function()
|
39
40
|
{
|
40
41
|
delete this.buttons;
|
@@ -583,12 +583,12 @@ SC.CollectionView = SC.View.extend(SC.CollectionViewDelegate,
|
|
583
583
|
@returns {Range} a range of objects
|
584
584
|
*/
|
585
585
|
groupRangeForContentIndex: function(contentIndex) {
|
586
|
+
var content = Array.from(this.get('content')) ;
|
587
|
+
var len = content.get('length') ;
|
586
588
|
var groupBy = this.get('groupBy') ;
|
587
|
-
if (!groupBy) return { start:
|
589
|
+
if (!groupBy) return { start: 0, length: len } ;
|
588
590
|
|
589
591
|
var min = contentIndex, max = contentIndex ;
|
590
|
-
var content = Array.from(this.get('content')) ;
|
591
|
-
var len = content.get('length') ;
|
592
592
|
var cur = content.objectAt(contentIndex) ;
|
593
593
|
var groupValue = (cur) ? cur.get(groupBy) : null ;
|
594
594
|
|
@@ -2111,7 +2111,6 @@ SC.CollectionView = SC.View.extend(SC.CollectionViewDelegate,
|
|
2111
2111
|
// index.
|
2112
2112
|
var loc = drag.get('location') ;
|
2113
2113
|
loc = this.convertFrameFromView(loc, null) ;
|
2114
|
-
|
2115
2114
|
var dropOp = SC.DROP_BEFORE ;
|
2116
2115
|
var dragOp = SC.DRAG_NONE ;
|
2117
2116
|
|
@@ -2125,7 +2124,7 @@ SC.CollectionView = SC.View.extend(SC.CollectionViewDelegate,
|
|
2125
2124
|
dropOp = idx[1] ;
|
2126
2125
|
idx = idx[0] ;
|
2127
2126
|
}
|
2128
|
-
|
2127
|
+
|
2129
2128
|
// if the return drop operation is DROP_ON, then just check it with the
|
2130
2129
|
// delegate method. If the delegate method does not support dropping on,
|
2131
2130
|
// then it will return DRAG_NONE, in which case we will try again with
|
@@ -392,9 +392,11 @@ SC.SourceListView = SC.CollectionView.extend(
|
|
392
392
|
var f = this.get('innerFrame') ;
|
393
393
|
var sf = this.get('scrollFrame') ;
|
394
394
|
var rowHeight = this.get('rowHeight') || 0 ;
|
395
|
-
|
395
|
+
var headerRowCount = (this.get("groupBy")) ? 1 : 0;
|
396
|
+
|
396
397
|
// find the offset to work with.
|
397
398
|
var offset = loc.y - f.y - sf.y ;
|
399
|
+
|
398
400
|
var ret = -1; // the return value
|
399
401
|
var retOp = SC.DROP_BEFORE ;
|
400
402
|
|
@@ -402,13 +404,14 @@ SC.SourceListView = SC.CollectionView.extend(
|
|
402
404
|
var top = 0;
|
403
405
|
var idx = 0 ;
|
404
406
|
while((ret<0) && (range = this.groupRangeForContentIndex(idx)).length>0){
|
405
|
-
var max = top + ((range.length+
|
407
|
+
var max = top + ((range.length+headerRowCount) * rowHeight) ;
|
406
408
|
|
407
409
|
// the offset is within the group, find the row in the group. Remember
|
408
410
|
// that the top row is actually the label, so we should return -1 if
|
409
411
|
// we hit there.
|
410
412
|
if (max >= offset) {
|
411
413
|
offset -= top ;
|
414
|
+
|
412
415
|
ret = Math.floor(offset / rowHeight) ;
|
413
416
|
|
414
417
|
// find the percent through the row...
|
@@ -426,11 +429,11 @@ SC.SourceListView = SC.CollectionView.extend(
|
|
426
429
|
}
|
427
430
|
|
428
431
|
// handle dropping on top row...
|
429
|
-
if (ret <
|
432
|
+
if (ret < headerRowCount) return [-1, SC.DROP_BEFORE] ; // top row!
|
430
433
|
|
431
434
|
// convert to index
|
432
|
-
ret = (ret -
|
433
|
-
|
435
|
+
ret = (ret - headerRowCount) + idx ;
|
436
|
+
|
434
437
|
// we are not yet within the group, go on to the next group.
|
435
438
|
} else {
|
436
439
|
idx += range.length ;
|
@@ -100,7 +100,9 @@ SC.TextFieldView = SC.FieldView.extend(SC.Editable,
|
|
100
100
|
this._isFocused = true ;
|
101
101
|
if (this.get('isVisibleInWindow')) {
|
102
102
|
this.rootElement.focus();
|
103
|
-
|
103
|
+
|
104
|
+
this.invokeLater(this._selectRootElement, 1) ;
|
105
|
+
|
104
106
|
}
|
105
107
|
}
|
106
108
|
|
@@ -108,6 +110,12 @@ SC.TextFieldView = SC.FieldView.extend(SC.Editable,
|
|
108
110
|
this._updateFieldHint() ;
|
109
111
|
},
|
110
112
|
|
113
|
+
// In IE, you can't modify functions on DOM elements so we need to wrap the call to select()
|
114
|
+
// like this.
|
115
|
+
_selectRootElement: function() {
|
116
|
+
this.rootElement.select() ;
|
117
|
+
},
|
118
|
+
|
111
119
|
// when we lose first responder, blur the text field if needed and show
|
112
120
|
// the hint text if needed.
|
113
121
|
/** @private */
|
@@ -115,12 +123,15 @@ SC.TextFieldView = SC.FieldView.extend(SC.Editable,
|
|
115
123
|
|
116
124
|
if (this._isFocused) {
|
117
125
|
this._isFocused = false ;
|
126
|
+
this._updateFieldHint() ;
|
118
127
|
return this.rootElement.blur() ;
|
119
128
|
}
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
129
|
+
else {
|
130
|
+
this._value = this.rootElement.value ;
|
131
|
+
this.fieldValueDidChange() ;
|
132
|
+
this._updateFieldHint() ;
|
133
|
+
return true;
|
134
|
+
}
|
124
135
|
},
|
125
136
|
|
126
137
|
_isFocused: false,
|
@@ -877,7 +877,25 @@ SC.View = SC.Responder.extend(SC.PathModule, SC.DelegateSupport,
|
|
877
877
|
@type {Element}
|
878
878
|
*/
|
879
879
|
offsetParent: function() {
|
880
|
-
|
880
|
+
|
881
|
+
// handle simple cases.
|
882
|
+
var el = this.rootElement ;
|
883
|
+
if (!el || el === document.body) return el;
|
884
|
+
if (el.offsetParent) return el.offsetParent ;
|
885
|
+
|
886
|
+
// in some cases, we can't find the offset parent so we walk up the
|
887
|
+
// chain until an element is found with a position other than
|
888
|
+
// 'static'
|
889
|
+
//
|
890
|
+
// Note that IE places DOM elements not in the main body inside of a
|
891
|
+
// document-fragment root. We need to treat document-fragments (i.e.
|
892
|
+
// nodeType === 11) as null values
|
893
|
+
var ret = null ;
|
894
|
+
while(!ret && (el = el.parentNode) && (el.nodeType !== 11) && (el !== document.body)) {
|
895
|
+
if (Element.getStyle(el, 'position') !== 'static') ret = el;
|
896
|
+
}
|
897
|
+
if (!ret && (el === document.body)) ret = el ;
|
898
|
+
return ret ;
|
881
899
|
}.property(),
|
882
900
|
|
883
901
|
/**
|
data/lib/sproutcore/bundle.rb
CHANGED
@@ -575,7 +575,7 @@ module SproutCore
|
|
575
575
|
str.scan(/['"](.+)['"]\s*:\s*['"](.+)['"],?\s*$/) do |x,y|
|
576
576
|
# x & y are JS strings that must be evaled as such..
|
577
577
|
#x = eval(%("#{x}"))
|
578
|
-
y = eval(%[
|
578
|
+
y = eval(%[<<__EOF__\n#{y}\n__EOF__]).chop
|
579
579
|
ret[x] = y
|
580
580
|
end
|
581
581
|
|
data/lib/sproutcore/version.rb
CHANGED
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: sproutcore
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.9.
|
4
|
+
version: 0.9.7
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Charles Jolley
|
@@ -9,7 +9,7 @@ autorequire:
|
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
11
|
|
12
|
-
date: 2008-05-
|
12
|
+
date: 2008-05-27 00:00:00 -07:00
|
13
13
|
default_executable:
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
@@ -90,8 +90,10 @@ files:
|
|
90
90
|
- clients/sc_docs/english.lproj/strings.js
|
91
91
|
- clients/sc_docs/english.lproj/tabs.rhtml
|
92
92
|
- clients/sc_docs/fixtures/doc.js
|
93
|
+
- clients/sc_docs/HISTORY
|
93
94
|
- clients/sc_docs/main.js
|
94
95
|
- clients/sc_docs/models/doc.js
|
96
|
+
- clients/sc_docs/README
|
95
97
|
- clients/sc_docs/tests/controllers/docs.rhtml
|
96
98
|
- clients/sc_docs/tests/models/doc.rhtml
|
97
99
|
- clients/sc_docs/tests/views/doc_frame.rhtml
|
@@ -111,14 +113,22 @@ files:
|
|
111
113
|
- clients/sc_test_runner/english.lproj/no_tests.rhtml
|
112
114
|
- clients/sc_test_runner/english.lproj/strings.js
|
113
115
|
- clients/sc_test_runner/fixtures/test.js
|
116
|
+
- clients/sc_test_runner/HISTORY
|
114
117
|
- clients/sc_test_runner/main.js
|
115
118
|
- clients/sc_test_runner/models/test.js
|
119
|
+
- clients/sc_test_runner/README
|
116
120
|
- clients/sc_test_runner/views/runner_frame.js
|
117
121
|
- clients/sc_test_runner/views/test_label.js
|
122
|
+
- clients/view_builder/core.js
|
123
|
+
- clients/view_builder/english.lproj/body.css
|
124
|
+
- clients/view_builder/english.lproj/body.rhtml
|
125
|
+
- clients/view_builder/english.lproj/strings.js
|
126
|
+
- clients/view_builder/main.js
|
118
127
|
- config/hoe.rb
|
119
128
|
- config/requirements.rb
|
129
|
+
- frameworks/prototype/HISTORY
|
120
130
|
- frameworks/prototype/prototype.js
|
121
|
-
- frameworks/
|
131
|
+
- frameworks/prototype/README
|
122
132
|
- frameworks/sproutcore/controllers/array.js
|
123
133
|
- frameworks/sproutcore/controllers/collection.js
|
124
134
|
- frameworks/sproutcore/controllers/controller.js
|
@@ -174,7 +184,6 @@ files:
|
|
174
184
|
- frameworks/sproutcore/foundation/run_loop.js
|
175
185
|
- frameworks/sproutcore/foundation/server.js
|
176
186
|
- frameworks/sproutcore/foundation/set.js
|
177
|
-
- frameworks/sproutcore/foundation/sorted_set.js
|
178
187
|
- frameworks/sproutcore/foundation/string.js
|
179
188
|
- frameworks/sproutcore/foundation/timer.js
|
180
189
|
- frameworks/sproutcore/foundation/undo_manager.js
|
@@ -183,6 +192,7 @@ files:
|
|
183
192
|
- frameworks/sproutcore/globals/panels.js
|
184
193
|
- frameworks/sproutcore/globals/popups.js
|
185
194
|
- frameworks/sproutcore/globals/window.js
|
195
|
+
- frameworks/sproutcore/HISTORY
|
186
196
|
- frameworks/sproutcore/lib/button_views.rb
|
187
197
|
- frameworks/sproutcore/lib/collection_view.rb
|
188
198
|
- frameworks/sproutcore/lib/core_views.rb
|
@@ -1,411 +0,0 @@
|
|
1
|
-
// ========================================================================
|
2
|
-
// SproutCore
|
3
|
-
// copyright 2006-2008 Sprout Systems, Inc.
|
4
|
-
// ========================================================================
|
5
|
-
|
6
|
-
require('Core') ;
|
7
|
-
require('foundation/timer') ;
|
8
|
-
|
9
|
-
/**
|
10
|
-
@class
|
11
|
-
|
12
|
-
An animator is a special timer that will transition its value property from
|
13
|
-
0 to 1 according to a transform function you define. You can also specify
|
14
|
-
one or more "subjects" that will be invoked whenever the animation value
|
15
|
-
changes, which can apply the animation to any target you like.
|
16
|
-
|
17
|
-
This class and its related classes are based on Bernie's Better Animator
|
18
|
-
class.
|
19
|
-
|
20
|
-
Use Cases:
|
21
|
-
|
22
|
-
1. I just want to transition from x=0 -> x=20.
|
23
|
-
v.transitionTo('styleLeft', 20) ;
|
24
|
-
-> creates an animation for styleLeft. If you add a new animation for
|
25
|
-
the same effect, it will cancel the old one automatically.
|
26
|
-
|
27
|
-
2. I want to have an 'animation behavior' attached to some property.
|
28
|
-
whenever I change it, it should automatically transition the property
|
29
|
-
instead of just updating it. i.e. a "Tracker".
|
30
|
-
|
31
|
-
-> I may want the tracker to update several things at once. But that
|
32
|
-
could be done through changing one property which will in turn
|
33
|
-
update everything else. Better idea.
|
34
|
-
|
35
|
-
Tracker simply stores some animation properties and calls transitionTo
|
36
|
-
whenever your target value changes.
|
37
|
-
|
38
|
-
3. I want to have some property animating on its own. For example a
|
39
|
-
throbbing button
|
40
|
-
|
41
|
-
a = v.addAnimation('opacity', {
|
42
|
-
repeatCount: ,
|
43
|
-
autoreverses: YES,
|
44
|
-
end: 1.0,
|
45
|
-
start: 0.0,
|
46
|
-
property: 'opacity'
|
47
|
-
}) ;
|
48
|
-
|
49
|
-
a.set('isPaused', YES) ;
|
50
|
-
v.removeAnimation('opacity');
|
51
|
-
|
52
|
-
--
|
53
|
-
We will need an animation builder. It would be nice if I could have a
|
54
|
-
complete timeline for an animation setup and you just tell it to run. Set
|
55
|
-
the timecode to anytime that you want, tell it to start or stop.
|
56
|
-
|
57
|
-
@extends SC.Timer
|
58
|
-
@author Charles Jolley
|
59
|
-
@since SproutCore 1.0
|
60
|
-
*/
|
61
|
-
SC.Animation = SC.Timer.extend(
|
62
|
-
/** @scope SC.Animation.prototype */ {
|
63
|
-
|
64
|
-
/**
|
65
|
-
The target frame rate. This is used to determine the interval of the
|
66
|
-
timer.
|
67
|
-
|
68
|
-
@type {Number}
|
69
|
-
@field
|
70
|
-
*/
|
71
|
-
frameRate: 60,
|
72
|
-
|
73
|
-
/**
|
74
|
-
The interval between frames. Calculated automatically from the frameRate.
|
75
|
-
This property is read only.
|
76
|
-
|
77
|
-
@type {Number}
|
78
|
-
@field
|
79
|
-
*/
|
80
|
-
interval: function() {
|
81
|
-
return 1000 / this.get('frameRate') ;
|
82
|
-
}.property('frameRate'),
|
83
|
-
|
84
|
-
/**
|
85
|
-
The total amount of time in msec you want to take for the animation to
|
86
|
-
go from 0-1.
|
87
|
-
*/
|
88
|
-
duration: 0,
|
89
|
-
|
90
|
-
/**
|
91
|
-
The speed at which the timecode for the animation will progress. 1.0
|
92
|
-
means it will progress in real time, 2.0 means twice as fast, etc.
|
93
|
-
*/
|
94
|
-
speed: 1.0,
|
95
|
-
|
96
|
-
/**
|
97
|
-
The total number of times you want the animation to repeat.
|
98
|
-
|
99
|
-
This can be any number, including partial numbers. If you set this
|
100
|
-
value you must not set repeatDuration as well. This will be ignored
|
101
|
-
if the value is 0, which is the default.
|
102
|
-
|
103
|
-
If you set this property to -1, then the animation will repeat
|
104
|
-
indefinitely.
|
105
|
-
|
106
|
-
Setting this property will update the targetTimecode.
|
107
|
-
|
108
|
-
@type {Number}
|
109
|
-
@field
|
110
|
-
*/
|
111
|
-
repeatCount: 0,
|
112
|
-
|
113
|
-
/**
|
114
|
-
The total amount of time the animation should run, repeating itself, in
|
115
|
-
msec.
|
116
|
-
|
117
|
-
If you set this value you must not set repeatCount as well. This will
|
118
|
-
be ignored if the value is 0, which is the default.
|
119
|
-
|
120
|
-
Setting this property will update the targetTimecode.
|
121
|
-
|
122
|
-
@type {Number}
|
123
|
-
@field
|
124
|
-
*/
|
125
|
-
repeatDuration: 0,
|
126
|
-
|
127
|
-
/**
|
128
|
-
If YES, then the animation will automatically reverse itself on each
|
129
|
-
repeat.
|
130
|
-
|
131
|
-
@type {Boolean}
|
132
|
-
@field
|
133
|
-
*/
|
134
|
-
autoreverses: NO,
|
135
|
-
|
136
|
-
/**
|
137
|
-
A transition function. This is used to convert the progress into a
|
138
|
-
state value. You can supply your own transition function to provide
|
139
|
-
varied behaviors such as ease in, ease out, etc.
|
140
|
-
|
141
|
-
The function must have a signature like:
|
142
|
-
|
143
|
-
transition: function(value, animator)
|
144
|
-
|
145
|
-
It must accept a value from 0-1 indicating the current animation progress
|
146
|
-
and return a value from 0-1 indication the current transition progress.
|
147
|
-
|
148
|
-
@type {Function}
|
149
|
-
@field
|
150
|
-
*/
|
151
|
-
transition: null,
|
152
|
-
|
153
|
-
/**
|
154
|
-
This is the current timecode for the animation, in msec. You can set
|
155
|
-
this to any value that you want and the animation to compute the current
|
156
|
-
transition value from it.
|
157
|
-
|
158
|
-
To move instantly to any part of your animation, you can simply set this
|
159
|
-
timecode or use jumpTo().
|
160
|
-
|
161
|
-
@type {Number}
|
162
|
-
@field
|
163
|
-
*/
|
164
|
-
currentTimecode: 0,
|
165
|
-
|
166
|
-
/**
|
167
|
-
This is the target timecode. Whenever the target timecode and the
|
168
|
-
current timecode do not match, the animation will animate until it
|
169
|
-
reaches the target timecode.
|
170
|
-
|
171
|
-
To transition the animation to a new state, set the timecode to any
|
172
|
-
value that you want.
|
173
|
-
|
174
|
-
@type {Number}
|
175
|
-
*/
|
176
|
-
targetTimecode: 0,
|
177
|
-
|
178
|
-
/**
|
179
|
-
The current animation progress.
|
180
|
-
|
181
|
-
This value will loop from 0-1 based on the current timecode and the
|
182
|
-
setting of autoreverses. You generally do not want to use this value.
|
183
|
-
Instead use the value property to compute the current transition.
|
184
|
-
|
185
|
-
@type {Number}
|
186
|
-
@field
|
187
|
-
*/
|
188
|
-
progress: function() {
|
189
|
-
|
190
|
-
// current progress is calculated from the currentTimecode by dividing
|
191
|
-
// it by the animation duration and autoreverses.
|
192
|
-
var currentTimecode = this.get('currentTimecode') ;
|
193
|
-
var duration = this.get('duration') ;
|
194
|
-
var reverses = this.get('autoreverses') ;
|
195
|
-
|
196
|
-
// find the progress through the current cycle.
|
197
|
-
var cycle = Math.floor(currentTimecode / duration) ;
|
198
|
-
var progress = (currentTimecode - (duration * cycle)) / duration;
|
199
|
-
|
200
|
-
// if we autoreverse and the cycle is odd numbered, invert.
|
201
|
-
if (reverses && (cycle % 2) > 0) progress = 1 - progress ;
|
202
|
-
return progress ;
|
203
|
-
}.property('currentTimecode', 'duration', 'autoreverses'),
|
204
|
-
|
205
|
-
/**
|
206
|
-
The current animation value. Transform functions can observe this
|
207
|
-
property and update the target object accordingly.
|
208
|
-
|
209
|
-
@type {Number}
|
210
|
-
@field
|
211
|
-
*/
|
212
|
-
value: function() {
|
213
|
-
var value = this.get('progress');
|
214
|
-
if (this.transition) value = this.transition(value, this) ;
|
215
|
-
return value ;
|
216
|
-
}.property('progress'),
|
217
|
-
|
218
|
-
/**
|
219
|
-
The target object of the animation. This is often used by transform
|
220
|
-
functions as the target of their change. It is not strictly required.
|
221
|
-
|
222
|
-
@type {Object}
|
223
|
-
@field
|
224
|
-
*/
|
225
|
-
target: null,
|
226
|
-
|
227
|
-
|
228
|
-
/**
|
229
|
-
This method must be called once when the animation is created to schedule
|
230
|
-
it with the run loop. Once animation has been started, you should can
|
231
|
-
pause it to remove it from the runloop temporarily.
|
232
|
-
|
233
|
-
@returns {void}
|
234
|
-
*/
|
235
|
-
play: function() {
|
236
|
-
this.beginPropertyChanges() ;
|
237
|
-
if (!this.get('isScheduled')) {
|
238
|
-
this.schedule() ;
|
239
|
-
this._lastActionTime = SC.runLoop.get('startTime') ;
|
240
|
-
}
|
241
|
-
|
242
|
-
if (this.get('isPaused')) this.set('isPaused', NO) ;
|
243
|
-
this.endPropertyChanges() ;
|
244
|
-
},
|
245
|
-
|
246
|
-
/**
|
247
|
-
Once an animation has been started, you can pause it anytime with this
|
248
|
-
method. Call start() to restart it or set isPaused to NO.
|
249
|
-
|
250
|
-
@returns {void}
|
251
|
-
*/
|
252
|
-
pause: function() {
|
253
|
-
this.set('isPaused', YES) ;
|
254
|
-
},
|
255
|
-
|
256
|
-
/**
|
257
|
-
Immediately changes the currentTimecode to reflect the requested amount
|
258
|
-
of progress through a single cycle of the animation.
|
259
|
-
|
260
|
-
@param progress {Number} a progress value from 0 to 1.
|
261
|
-
@returns {void}
|
262
|
-
*/
|
263
|
-
jumpTo: function(progress) {
|
264
|
-
// get the current progress.
|
265
|
-
var cur = this.get('progress') ;
|
266
|
-
var diff = progress - cur ;
|
267
|
-
var duration = this.get('duration') ;
|
268
|
-
diff *= duration ;
|
269
|
-
|
270
|
-
var timecode = this.get('currentTimecode') ;
|
271
|
-
if (diff !== 0) this.set('currentTimecode', timecode + diff) ;
|
272
|
-
},
|
273
|
-
|
274
|
-
/**
|
275
|
-
Changes the targetTimecode to reflect the request amount of progress
|
276
|
-
through the current cycle of the animation. If the animation is in
|
277
|
-
reverse, this could move the timecode in the opposite direction.
|
278
|
-
|
279
|
-
@param progress {Number} a progress value from 0 to 1.
|
280
|
-
@returns {void}
|
281
|
-
*/
|
282
|
-
seekTo: function(progress) {
|
283
|
-
// get the current progress.
|
284
|
-
var cur = this.get('progress') ;
|
285
|
-
var diff = progress - cur ;
|
286
|
-
var duration = this.get('duration') ;
|
287
|
-
diff *= duration ;
|
288
|
-
|
289
|
-
var timecode = this.get('currentTimecode') ;
|
290
|
-
if (diff !== 0) this.set('targetTimecode', timecode + diff) ;
|
291
|
-
},
|
292
|
-
|
293
|
-
/**
|
294
|
-
Changes the currentTimecode and targetTimecode to reflect the requested
|
295
|
-
amount of progress through the current cycle of the animation. If the
|
296
|
-
animation is in reverse, this could move the timecode in the opposite
|
297
|
-
direction.
|
298
|
-
|
299
|
-
@param fromProgress {Number} a progress value from 0 to 1
|
300
|
-
@param toProgress {Number} a progress value from 0 to 1
|
301
|
-
@returns {void}
|
302
|
-
*/
|
303
|
-
seekFromTo: function(fromProgress, toProgress) {
|
304
|
-
// get the current progress.
|
305
|
-
var timecode = this.get('currentTimecode') ;
|
306
|
-
var cur = this.get('progress') ;
|
307
|
-
var duration = this.get('duration') ;
|
308
|
-
|
309
|
-
this.beginPropertyChanges() ;
|
310
|
-
|
311
|
-
var diff = fromProgress - cur ;
|
312
|
-
diff *= duration ;
|
313
|
-
if (diff !== 0) this.set('currentTimecode', timecode + diff) ;
|
314
|
-
|
315
|
-
diff = toProgress - cur ;
|
316
|
-
diff *= duration ;
|
317
|
-
if (diff !== 0) this.set('targetTimecode', timecode + diff) ;
|
318
|
-
|
319
|
-
this.endPropertyChanges() ;
|
320
|
-
},
|
321
|
-
|
322
|
-
/**
|
323
|
-
Changes the targetTimecode to invert the direction of the animation.
|
324
|
-
If the progress value is currently headed towards 1.0, it will be instead
|
325
|
-
headed to 0.
|
326
|
-
|
327
|
-
@returns {Number} the new progress
|
328
|
-
*/
|
329
|
-
toggle: function() {
|
330
|
-
throw "toggle not yet implemented" ;
|
331
|
-
},
|
332
|
-
|
333
|
-
/**
|
334
|
-
The core action executed by the timer.
|
335
|
-
*/
|
336
|
-
performAction: function() {
|
337
|
-
|
338
|
-
// if timecodes match, then we suspend the animation and return, there
|
339
|
-
// is nothing more to do.
|
340
|
-
var cur = this.get('currentTimecode') ;
|
341
|
-
var target = this.get('targetTimecode') ;
|
342
|
-
if (cur == target) {
|
343
|
-
this.set('isPaused', YES) ;
|
344
|
-
return ;
|
345
|
-
}
|
346
|
-
|
347
|
-
// determine the amount of time that has elapsed since the last time
|
348
|
-
// we were called.
|
349
|
-
var curTime = SC.runLoop.get('startTime') ;
|
350
|
-
var lapsed = (curTime - this._lastActionTime) * this.get('speed') ;
|
351
|
-
this._lastActionTime = curTime ;
|
352
|
-
|
353
|
-
// update the current timecode. Moving in the direction of the target
|
354
|
-
// timecode. If timecodes match, there is nothing to do.
|
355
|
-
if (target > cur) {
|
356
|
-
cur += lapsed ;
|
357
|
-
if (cur > target) cur = target ;
|
358
|
-
} else {
|
359
|
-
cur -= lapsed ;
|
360
|
-
if (cur < target) cur = target ;
|
361
|
-
}
|
362
|
-
|
363
|
-
// now set the new timecode, this should trigger other observers to
|
364
|
-
// update.
|
365
|
-
this.set('currentTimecode', cur) ;
|
366
|
-
},
|
367
|
-
|
368
|
-
// ......................................
|
369
|
-
// INTERNAL METHODS
|
370
|
-
//
|
371
|
-
|
372
|
-
/** @private
|
373
|
-
Observes changes to repeatCount; updates the targetTimecode.
|
374
|
-
*/
|
375
|
-
_repeatCountObserver: function() {
|
376
|
-
var repeatCount = this.get('repeatCount') ;
|
377
|
-
var target ;
|
378
|
-
if (repeatCount < 0) {
|
379
|
-
target = Date.now() * 2 ;
|
380
|
-
} else if (repeatCount > 0) {
|
381
|
-
target = this.get('duration') * repeatCount ;
|
382
|
-
} else return ; // nothing to do.
|
383
|
-
this.set('targetTimecode', target) ;
|
384
|
-
}.observes('repeatCount'),
|
385
|
-
|
386
|
-
/** @private
|
387
|
-
Observes changes to the repeatDuration; updates the targetTimecode.
|
388
|
-
*/
|
389
|
-
_repeatDurationObserver: function() {
|
390
|
-
var dur = this.get('repeatDuration') ;
|
391
|
-
if (dur < 0) {
|
392
|
-
dur = Date.now() * 2 ;
|
393
|
-
} else if (dur == 0) return; // nothing to do
|
394
|
-
this.set('targetTimecode', dur) ;
|
395
|
-
}.observes('repeatDuration'),
|
396
|
-
|
397
|
-
init: function() {
|
398
|
-
arguments.callee.base.call(this) ;
|
399
|
-
|
400
|
-
this.targetTimecode = this.get('duration') ;
|
401
|
-
this._repeatDurationObserver() ;
|
402
|
-
this._repeatCountObserver() ;
|
403
|
-
},
|
404
|
-
|
405
|
-
repeats: YES, // for timer
|
406
|
-
|
407
|
-
_logValueObserver: function() {
|
408
|
-
console.log('value did change to: %@'.fmt(this.get('value')));
|
409
|
-
}.observes('value')
|
410
|
-
|
411
|
-
});
|