sproutcore 0.9.6 → 0.9.7
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
-
});
|