sproutcore 1.0.1037 → 1.0.1042

Sign up to get free protection for your applications and to get access to all the features.
Files changed (134) hide show
  1. data/History.txt +31 -0
  2. data/README.txt +3 -1
  3. data/Rakefile +11 -4
  4. data/VERSION.yml +3 -3
  5. data/buildtasks/build.rake +5 -0
  6. data/frameworks/sproutcore/Buildfile +3 -1
  7. data/frameworks/sproutcore/frameworks/animation/Buildfile +3 -0
  8. data/frameworks/sproutcore/frameworks/animation/LICENSE +25 -0
  9. data/frameworks/sproutcore/frameworks/animation/README.md +102 -0
  10. data/frameworks/sproutcore/frameworks/animation/core.js +934 -0
  11. data/frameworks/sproutcore/frameworks/animation/tests/core.js +65 -0
  12. data/frameworks/sproutcore/frameworks/datastore/models/record.js +28 -16
  13. data/frameworks/sproutcore/frameworks/datastore/models/record_attribute.js +5 -2
  14. data/frameworks/sproutcore/frameworks/datastore/system/many_array.js +4 -0
  15. data/frameworks/sproutcore/frameworks/datastore/system/query.js +27 -13
  16. data/frameworks/sproutcore/frameworks/datastore/system/record_array.js +36 -6
  17. data/frameworks/sproutcore/frameworks/datastore/system/store.js +7 -7
  18. data/frameworks/sproutcore/frameworks/datastore/tests/models/record/storeDidChangeProperties.js +2 -1
  19. data/frameworks/sproutcore/frameworks/datastore/tests/models/record_attribute.js +13 -0
  20. data/frameworks/sproutcore/frameworks/debug/invoke_once_last_debugging.js +250 -0
  21. data/frameworks/sproutcore/frameworks/desktop/english.lproj/list_item.css +0 -12
  22. data/frameworks/sproutcore/frameworks/desktop/english.lproj/menu_item_view.css +3 -6
  23. data/frameworks/sproutcore/frameworks/desktop/english.lproj/panel.css +0 -8
  24. data/frameworks/sproutcore/frameworks/desktop/english.lproj/picker.css +0 -4
  25. data/frameworks/sproutcore/frameworks/desktop/english.lproj/segmented.css +1 -0
  26. data/frameworks/sproutcore/frameworks/desktop/english.lproj/well.css +0 -1
  27. data/frameworks/sproutcore/frameworks/desktop/mixins/border.js +1 -1
  28. data/frameworks/sproutcore/frameworks/desktop/panes/alert.js +2 -1
  29. data/frameworks/sproutcore/frameworks/desktop/panes/menu.js +11 -4
  30. data/frameworks/sproutcore/frameworks/desktop/panes/palette.js +2 -0
  31. data/frameworks/sproutcore/frameworks/desktop/panes/panel.js +1 -5
  32. data/frameworks/sproutcore/frameworks/desktop/panes/picker.js +24 -23
  33. data/frameworks/sproutcore/frameworks/desktop/panes/select_button.js +91 -60
  34. data/frameworks/sproutcore/frameworks/desktop/panes/sheet.js +124 -24
  35. data/frameworks/sproutcore/frameworks/desktop/system/drag.js +5 -5
  36. data/frameworks/sproutcore/frameworks/desktop/system/root_responder.js +33 -25
  37. data/frameworks/sproutcore/frameworks/desktop/tests/panes/pane_page.js +41 -0
  38. data/frameworks/sproutcore/frameworks/desktop/tests/panes/select_button/methods.js +30 -1
  39. data/frameworks/sproutcore/frameworks/desktop/tests/panes/select_button/ui.js +13 -0
  40. data/frameworks/sproutcore/frameworks/desktop/tests/panes/sheet/ui.js +27 -21
  41. data/frameworks/sproutcore/frameworks/desktop/tests/views/button/methods.js +81 -1
  42. data/frameworks/sproutcore/frameworks/desktop/tests/views/radio/methods.js +3 -4
  43. data/frameworks/sproutcore/frameworks/desktop/views/button.js +65 -36
  44. data/frameworks/sproutcore/frameworks/desktop/views/collection.js +4 -7
  45. data/frameworks/sproutcore/frameworks/desktop/views/disclosure.js +8 -4
  46. data/frameworks/sproutcore/frameworks/desktop/views/list.js +1 -1
  47. data/frameworks/sproutcore/frameworks/desktop/views/list_item.js +38 -5
  48. data/frameworks/sproutcore/frameworks/desktop/views/menu_item.js +5 -1
  49. data/frameworks/sproutcore/frameworks/desktop/views/popup_button.js +4 -1
  50. data/frameworks/sproutcore/frameworks/desktop/views/progress.js +19 -13
  51. data/frameworks/sproutcore/frameworks/desktop/views/radio.js +30 -2
  52. data/frameworks/sproutcore/frameworks/desktop/views/scroll.js +2 -3
  53. data/frameworks/sproutcore/frameworks/desktop/views/segmented.js +14 -17
  54. data/frameworks/sproutcore/frameworks/desktop/views/select_field.js +5 -3
  55. data/frameworks/sproutcore/frameworks/desktop/views/slider.js +4 -2
  56. data/frameworks/sproutcore/frameworks/desktop/views/split.js +58 -59
  57. data/frameworks/sproutcore/frameworks/desktop/views/tab.js +2 -1
  58. data/frameworks/sproutcore/frameworks/desktop/views/well.js +1 -1
  59. data/frameworks/sproutcore/frameworks/foundation/core.js +6 -0
  60. data/frameworks/sproutcore/frameworks/foundation/english.lproj/view.css +2 -1
  61. data/frameworks/sproutcore/frameworks/foundation/mixins/button.js +33 -30
  62. data/frameworks/sproutcore/frameworks/foundation/mixins/inline_text_field.js +8 -4
  63. data/frameworks/sproutcore/frameworks/foundation/mixins/string.js +15 -10
  64. data/frameworks/sproutcore/frameworks/foundation/panes/pane.js +61 -12
  65. data/frameworks/sproutcore/frameworks/foundation/system/bundle.js +2 -1
  66. data/frameworks/sproutcore/frameworks/foundation/system/core_query.js +151 -131
  67. data/frameworks/sproutcore/frameworks/foundation/system/datetime.js +29 -23
  68. data/frameworks/sproutcore/frameworks/foundation/system/event.js +18 -10
  69. data/frameworks/sproutcore/frameworks/foundation/system/page.js +7 -5
  70. data/frameworks/sproutcore/frameworks/foundation/system/ready.js +1 -0
  71. data/frameworks/sproutcore/frameworks/foundation/system/render_context.js +9 -6
  72. data/frameworks/sproutcore/frameworks/foundation/system/request.js +41 -6
  73. data/frameworks/sproutcore/frameworks/foundation/system/response.js +89 -24
  74. data/frameworks/sproutcore/frameworks/foundation/system/routes.js +1 -1
  75. data/frameworks/sproutcore/frameworks/foundation/system/utils.js +0 -1
  76. data/frameworks/sproutcore/frameworks/foundation/tests/controllers/array/array_case.js +27 -8
  77. data/frameworks/sproutcore/frameworks/foundation/tests/system/core_query/jquery_core.js +6 -6
  78. data/frameworks/sproutcore/frameworks/foundation/tests/system/render_context/helpers_style.js +2 -2
  79. data/frameworks/sproutcore/frameworks/foundation/tests/system/request.js +65 -3
  80. data/frameworks/sproutcore/frameworks/foundation/tests/views/pane/append_remove.js +1 -1
  81. data/frameworks/sproutcore/frameworks/foundation/tests/views/view/convertLayouts.js +145 -0
  82. data/frameworks/sproutcore/frameworks/foundation/tests/views/view/didAppendToDocument.js +48 -0
  83. data/frameworks/sproutcore/frameworks/foundation/tests/views/view/nextValidKeyView.js +91 -0
  84. data/frameworks/sproutcore/frameworks/foundation/validators/number.js +9 -5
  85. data/frameworks/sproutcore/frameworks/foundation/views/field.js +16 -14
  86. data/frameworks/sproutcore/frameworks/foundation/views/text_field.js +89 -67
  87. data/frameworks/sproutcore/frameworks/foundation/views/view.js +221 -73
  88. data/frameworks/sproutcore/frameworks/runtime/core.js +43 -22
  89. data/frameworks/sproutcore/frameworks/runtime/mixins/array.js +6 -0
  90. data/frameworks/sproutcore/frameworks/runtime/mixins/copyable.js +1 -1
  91. data/frameworks/sproutcore/frameworks/runtime/mixins/observable.js +53 -34
  92. data/frameworks/sproutcore/frameworks/runtime/private/observer_set.js +7 -3
  93. data/frameworks/sproutcore/frameworks/runtime/system/binding.js +19 -19
  94. data/frameworks/sproutcore/frameworks/runtime/system/logger.js +132 -88
  95. data/frameworks/sproutcore/frameworks/runtime/system/object.js +15 -9
  96. data/frameworks/sproutcore/frameworks/runtime/system/run_loop.js +6 -0
  97. data/frameworks/sproutcore/frameworks/runtime/tests/mixins/observable/observable.js +69 -0
  98. data/frameworks/sproutcore/frameworks/runtime/tests/system/logger.js +14 -2
  99. data/frameworks/sproutcore/license.js +3 -1
  100. data/frameworks/sproutcore/themes/standard_theme/Source/sc-theme-repeat-x.psd +0 -0
  101. data/frameworks/sproutcore/{frameworks/desktop → themes/standard_theme}/english.lproj/images/icons/mini_222222.png +0 -0
  102. data/frameworks/sproutcore/{frameworks/desktop → themes/standard_theme}/english.lproj/images/icons/mini_454545.png +0 -0
  103. data/frameworks/sproutcore/{frameworks/desktop → themes/standard_theme}/english.lproj/images/icons/mini_888888.png +0 -0
  104. data/frameworks/sproutcore/{frameworks/desktop → themes/standard_theme}/english.lproj/images/icons/mini_ffffff.png +0 -0
  105. data/frameworks/sproutcore/{frameworks/desktop → themes/standard_theme}/english.lproj/images/panels/sprite-x.png +0 -0
  106. data/frameworks/sproutcore/{frameworks/desktop → themes/standard_theme}/english.lproj/images/panels/sprite-y.png +0 -0
  107. data/frameworks/sproutcore/themes/standard_theme/english.lproj/images/sc-theme-repeat-x.png +0 -0
  108. data/frameworks/sproutcore/themes/standard_theme/english.lproj/images/sc-theme-ysprite.png +0 -0
  109. data/frameworks/sproutcore/themes/standard_theme/english.lproj/list_item.css +15 -1
  110. data/frameworks/sproutcore/themes/standard_theme/english.lproj/menu_item_view.css +9 -0
  111. data/frameworks/sproutcore/themes/standard_theme/english.lproj/panel.css +33 -0
  112. data/frameworks/sproutcore/themes/standard_theme/english.lproj/picker.css +17 -0
  113. data/frameworks/sproutcore/themes/standard_theme/english.lproj/radio.css +9 -6
  114. data/frameworks/sproutcore/themes/standard_theme/english.lproj/tab.css +0 -4
  115. data/frameworks/sproutcore/themes/standard_theme/english.lproj/well.css +36 -0
  116. data/gen/controller/templates/controllers/@filename@.js +1 -1
  117. data/lib/sproutcore/builders/minify.rb +45 -13
  118. data/lib/sproutcore/helpers/static_helper.rb +2 -2
  119. data/lib/sproutcore/models/manifest_entry.rb +42 -1
  120. data/lib/sproutcore/tools/build.rb +18 -2
  121. data/spec/lib/models/manifest_entry/hyperdomain_prefix.rb +34 -0
  122. data/vendor/yui-compressor/SCyuicompressor-2.4.2.jar +0 -0
  123. metadata +28 -22
  124. data/frameworks/sproutcore/themes/standard_theme/english.lproj/panels/background-fat.jpg +0 -0
  125. data/frameworks/sproutcore/themes/standard_theme/english.lproj/panels/background-thin.jpg +0 -0
  126. data/frameworks/sproutcore/themes/standard_theme/english.lproj/panels/bottom-edge.png +0 -0
  127. data/frameworks/sproutcore/themes/standard_theme/english.lproj/panels/bottom-left-corner.png +0 -0
  128. data/frameworks/sproutcore/themes/standard_theme/english.lproj/panels/bottom-right-corner.png +0 -0
  129. data/frameworks/sproutcore/themes/standard_theme/english.lproj/panels/left-edge.png +0 -0
  130. data/frameworks/sproutcore/themes/standard_theme/english.lproj/panels/overlay.png +0 -0
  131. data/frameworks/sproutcore/themes/standard_theme/english.lproj/panels/right-edge.png +0 -0
  132. data/frameworks/sproutcore/themes/standard_theme/english.lproj/panels/top-edge.png +0 -0
  133. data/frameworks/sproutcore/themes/standard_theme/english.lproj/panels/top-left-corner.png +0 -0
  134. data/frameworks/sproutcore/themes/standard_theme/english.lproj/panels/top-right-corner.png +0 -0
@@ -1,3 +1,34 @@
1
+ == 1.0.1041 / 21-Dec-2009
2
+
3
+ - yui-compressor now runs once at the end of a build, significantly shortening build time for many projects.
4
+
5
+ - Added Button Hold behavior for SC.Button. Holding a button will repeatedly fire an action (think arrow buttons on a scroll bar)
6
+
7
+ - Improved SC.Logger
8
+
9
+ - Incorporated rough draft of new SC.Animatable framework
10
+
11
+ - Minor bug fixes - mostly tweaks required for better IE7 support
12
+
13
+ - Improved documentation on a few methods
14
+
15
+ - Added a logging for invokeOnce() and invokeLater() making it easier to track when these events occurs and what caused them
16
+
17
+ - Moved some images into themes so they won’t load when Apple uses its own private theme
18
+
19
+ - Fixed some bugs in SC.SheetPane introduced when merging the public source code
20
+
21
+ - Added didAppendLayer and willRemoveLayer callbacks to SC.View so we can add events for video tags
22
+
23
+ - Added SC.get() which works on any object, even null values
24
+
25
+ - Added SC.WellView
26
+
27
+ - New animating SC.SheetPane
28
+
29
+ - Better support for SC.DateTime
30
+
31
+
1
32
  == 1.0.1036
2
33
 
3
34
  Added mising .tmpl files that prevented doc tool from running
data/README.txt CHANGED
@@ -80,7 +80,7 @@ sudo gem install sproutcore
80
80
  == LICENSE:
81
81
 
82
82
  Copyright (c) 2009 Apple Inc.
83
- Portions copyright (c) 2006-2009 Sprout Systems, Inc. and contributors
83
+ Portions copyright (c) 2006-2009 Sprout Systems Inc. and contributors
84
84
 
85
85
  Permission is hereby granted, free of charge, to any person obtaining
86
86
  a copy of this software and associated documentation files (the
@@ -100,3 +100,5 @@ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
100
100
  CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
101
101
  TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
102
102
  SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
103
+
104
+ SproutCore and the SproutCore logo are trademarks of Sprout Systems Inc.
data/Rakefile CHANGED
@@ -109,10 +109,14 @@ namespace :dist do
109
109
 
110
110
  if !File.exists?(path / ".git")
111
111
  $stdout.puts " Creating repo for #{rel_path}"
112
- FileUtils.mkdir_p File.dirname(path)
112
+ FileUtils.mkdir_p path
113
113
 
114
114
  $stdout.puts "\n> git clone #{repo_url} #{path}"
115
- system %[git clone #{repo_url} #{path}]
115
+ system "GIT_DIR=#{path / '.git'}; GIT_WORK_TREE=#{path}; git init"
116
+
117
+ git(path,"remote add origin #{repo_url}")
118
+ git(path,"fetch origin")
119
+ git(path,"checkout -b origin remotes/origin/master")
116
120
 
117
121
  else
118
122
  $stdout.puts "Found #{rel_path}"
@@ -141,8 +145,11 @@ namespace :dist do
141
145
  $stdout.puts "\n> git fetch"
142
146
  $stdout.puts git(path, 'fetch')
143
147
 
144
- $stdout.puts "\n> git checkout #{sha}"
145
- $stdout.puts git(path, 'checkout #{sha}')
148
+ if sha
149
+ $stdout.puts "\n> git checkout #{sha}"
150
+ $stdout.puts git(path, "checkout #{sha}")
151
+ end
152
+
146
153
  else
147
154
  $stdout.puts "WARN: cannot fix version for #{rel_path}"
148
155
  end
@@ -1,7 +1,7 @@
1
1
  ---
2
- :patch: 1037
3
- :digest: 512f2d22a6b1087cb7b042421b533639a0836279
2
+ :digest: 0d264600bbd6854ae2fca462e6ef64fb9ec9f114
4
3
  :dist:
5
- frameworks/sproutcore: edcf66df94134f479ddad658d95b27e94b685c2a
4
+ frameworks/sproutcore: 77d6e1c493db4cc03be061890c3c53409523b534
6
5
  :major: 1
7
6
  :minor: 0
7
+ :patch: 1042
@@ -57,6 +57,11 @@ namespace :build do
57
57
  SC::Builder::Minify.build ENTRY, DST_PATH, :javascript
58
58
  end
59
59
 
60
+ desc "minifies a Javascript file immediately by invoking the YUI compressor"
61
+ build_task :inline_javascript do
62
+ SC::Builder::Minify.build ENTRY, DST_PATH, :inline_javascript
63
+ end
64
+
60
65
  end
61
66
 
62
67
  desc "builds a unit test"
@@ -32,7 +32,7 @@ config :foundation, :required => [:runtime]
32
32
  config :datastore, :required => [:runtime]
33
33
 
34
34
  # APP-LEVEL FRAMEWORKS
35
- %w(desktop mobile).each do |app_framework|
35
+ %w(desktop mobile designer).each do |app_framework|
36
36
  config app_framework, :required => [:runtime, :datastore, :foundation]
37
37
  end
38
38
 
@@ -41,9 +41,11 @@ config :mobile,
41
41
  :test_layout => 'sproutcore/mobile:lib/index.rhtml'
42
42
 
43
43
  # WRAPPER FRAMEWORKS
44
+ config :designer, :required => [:runtime, :foundation]
44
45
  config :sproutcore, :required => :desktop
45
46
  config :mini, :required => [:runtime, :datastore]
46
47
 
48
+
47
49
  # SPECIAL FRAMEWORKS AND THEMES
48
50
  # These do not require any of the built-in SproutCore frameworks
49
51
  %w(testing debug standard_theme empty_theme).each do |target_name|
@@ -0,0 +1,3 @@
1
+ # SproutCore is required for animation to work. This shouldn't be an issue,
2
+ # but is required to force the tester to include SproutCore.
3
+ config :all, :required=>[:sproutcore]
@@ -0,0 +1,25 @@
1
+ Copyright (C) 2009 Alex Iskander and TPSi. All Rights Reserved.
2
+
3
+ Redistribution and use in source and binary forms, with or without
4
+ modification, are permitted provided that the following conditions
5
+ are met:
6
+ 1. Redistributions of source code must retain the above copyright
7
+ notice, this list of conditions and the following disclaimer.
8
+ 2. Redistributions in binary form must reproduce the above copyright
9
+ notice, this list of conditions and the following disclaimer in the
10
+ documentation and/or other materials provided with the distribution.
11
+ 3. Neither the name of Technical Programming Services, Inc. ("TPSi") nor the
12
+ names of its contributors may be used to endorse or promote products
13
+ derived from this software without specific prior written permission.
14
+
15
+ THIS SOFTWARE IS PROVIDED BY TPSi ``AS IS'' AND ANY
16
+ EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
18
+ PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL TPSi OR
19
+ CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
20
+ EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
21
+ PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
22
+ PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
23
+ OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
25
+ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
@@ -0,0 +1,102 @@
1
+ NOTE: RENAMED to SC.Animatable.
2
+
3
+ A simple mixin called Animatable is provided. What does it do?
4
+ It makes CSS transitions for you, and if they aren't available,
5
+ implements them in JavaScript.
6
+
7
+ Current good things:
8
+ * Seems to work!
9
+ * Animates 300 SC.LabelViews acceptably with only JavaScript. Animates >500
10
+ just as well (if not better) with CSS transitions.
11
+ * Automatically detects if CSS transitions are available.
12
+
13
+ Current flaws:
14
+ * Supports only a limited subset of properties. For instance, no support for
15
+ animating background colors.
16
+
17
+ Example Usage:
18
+
19
+ aView: SC.LabelView.design(Animate.Animatable, {
20
+ transitions: {
21
+ left: .25,
22
+ top: {duration: .25},
23
+ width: { duration: .25, timing: Animate.TIMING_EASE_IN_OUT }, // with timing curve
24
+ height: { duration: .5, timing: [0, 0, 0.58, 1.0] } // with custom timing curve
25
+ }
26
+ })
27
+
28
+ Including in your SproutCore Project
29
+ ===============================================================================
30
+ First, if you know what you are doing, or just want to hack it, you can just
31
+ take the one (and only) code file, core.js, rename it, put it somewhere, whatever.
32
+ Though that won't keep you up-to-date with any improvements...
33
+
34
+
35
+ Getting the Code Using git:
36
+ * Navigate to your SproutCore project folder (the one that contains apps/, under
37
+ which your application is located).
38
+
39
+ * git clone git://github.com/ialexi/animate.git
40
+
41
+ Getting the Code through GitHub:
42
+ Go to http://github.com/ialexi/animate and select download. Download in whichever
43
+ format you prefer.
44
+
45
+ Navigate to your SproutCore project folder; if there is no subfolder "frameworks",
46
+ create one. Under the "frameworks" folder, create a folder "animate".
47
+
48
+ Place the files that were extracted (not the containing folder) into the "animate"
49
+ folder you created.
50
+
51
+
52
+ Installing
53
+ ===============================================================================
54
+ In your project-level Buildfile, there is a line like this:
55
+ config :all, :required => :sproutcore
56
+
57
+ Add the animate framework to it by changing it to this:
58
+ config :all, :required => [:sproutcore, :animate]
59
+
60
+ Now, you should be able to use it in any view, as demonstrated above!
61
+
62
+
63
+ Reference
64
+ ===============================================================================
65
+ The API is simple. You set up transitions in the "transitions" property, and can
66
+ supply them with a few parameters:
67
+ * duration: The amount of time in seconds that the animation should last.
68
+ * timing: The timing curve to use. Defaults to Animate.defaultTimingFunction. See "Timing"
69
+
70
+
71
+ Timing
72
+ ------
73
+ Timing is handled using beziér curves (like in CSS transitions).
74
+
75
+ The variable Animate.defaultTimingFunction can be used to globally set the timing
76
+ curve; any transition that has "null" as its timing curve will use this default
77
+ timing curve.
78
+
79
+ For example, you could include this line in one of your JavaScript files:
80
+ Animate.defaultTimingFunction = Animate.TRANSITION_EASE_IN_OUT;
81
+
82
+ There are a few built-in timing curves (shown here with their values). The curves
83
+ with CSS in their name are CSS-only versions; JavaScript-based animations will use
84
+ linear. The big benefit of CSS-only timing functions is that JavaScript performance
85
+ is not impacted; to handle timing curves, JavaScript has to do some semi-heavy calculations
86
+ **each frame**. Using CSS-only transitions gives a graceful degradation of sorts.
87
+
88
+ * Animate.TRANSITION_NONE: "linear"
89
+ * Animate.TRANSITION_CSS_EASE: "ease"
90
+ * Animate.TRANSITION_CSS_EASE_IN: "ease-in"
91
+ * Animate.TRANSITION_CSS_EASE_OUT: "ease-out"
92
+ * Animate.TRANSITION_CSS_EASE_OUT: "ease-in-out"
93
+ * Animate.TRANSITION_EASE: [0.25, 0.1, 0.25, 1.0]
94
+ * Animate.TRANSITION_LINEAR: [0.0, 0.0, 1.0, 1.0]
95
+ * Animate.TRANSITION_EASE_IN: [0.42, 0.0, 1.0, 1.0]
96
+ * Animate.TRANSITION_EASE_OUT: [0, 0, 0.58, 1.0]
97
+ * Animate.TRANSITION_EASE_IN_OUT: [0.42, 0, 0.58, 1.0]
98
+
99
+
100
+ **Note**: The TRANSITION_NONE value is technically a CSS-only "linear" curve; as CSS-only
101
+ curves make JavaScript use linear, it all works out.
102
+
@@ -0,0 +1,934 @@
1
+ // ==========================================================================
2
+ // Project: SproutCore Animation
3
+ // Copyright: ©2009 TPSi
4
+ // Copyright: ©2009 Alex Iskander
5
+ // Portions © Apple Inc under BSD License:
6
+ // See: http://trac.webkit.org/browser/trunk/WebCore/platform/graphics/UnitBezier.h
7
+ // ==========================================================================
8
+ /*globals */
9
+
10
+ /** @namespace
11
+ A simple mixin called Animatable is provided. What does it do?
12
+ It makes CSS transitions for you, and if they aren't available,
13
+ implements them in JavaScript.
14
+
15
+ Animatable things:
16
+ - layout. You can animate any layout property, even centerX and centerY
17
+ - opacity.
18
+ - display, in a way. All animating display does is delay setting display:none
19
+ until <em>after</em> the transition duration has passed. This allows you
20
+ to set display:none after fading out. If mixing with CSS transitions, you will
21
+ need to set the delay a tad longer to accomodate any delays in beginning the
22
+ transition.
23
+
24
+ @example Example Usage:
25
+ {{{
26
+ aView: SC.LabelView.design(Animate.Animatable, {
27
+ transitions: {
28
+ left: {duration: .25},
29
+ top: .25, // only possible during design; otherwise you must use long form.
30
+ width: {duration: , timing: SC.Animatable.TRANSITION_EASE_IN_OUT }
31
+ }
32
+ })
33
+ }}}
34
+ @extends SC.Object
35
+ */
36
+ SC.Animatable = {
37
+ /**
38
+ Walks like a duck.
39
+ */
40
+ isAnimatable: YES,
41
+
42
+
43
+ transitions: {},
44
+ concatenatedProperties: ["transitions"],
45
+
46
+ /**
47
+ The style properties. Works somewhat similarly to layout properties, though
48
+ is a tad bit simpler, as it does not involve parent views at all.
49
+ */
50
+ style: { },
51
+
52
+ // collections of CSS transitions we have available
53
+ _cssTransitionFor: {
54
+ "left": "left", "top": "top",
55
+ "right": "right", "bottom": "bottom",
56
+ "width": "width", "height": "height",
57
+ "opacity": "opacity"
58
+ },
59
+
60
+ // properties that adjust should relay to style
61
+ _styleProperties: [ "opacity", "display" ],
62
+ _layoutStyles: ["left", "right", "top", "bottom", "width", "height", "centerX", "centerY"],
63
+
64
+ // we cache this dictionary so we don't generate a new one each time we make
65
+ // a new animation. It is used so we can start the animations in order—
66
+ // for instance, centerX and centerY need to be animated _after_ width and height.
67
+ _animationsToStart: {},
68
+
69
+ // and, said animation order
70
+ _animationOrder: ["top", "left", "bottom", "right", "width", "height", "centerX", "centerY", "opacity", "display"],
71
+
72
+
73
+ initMixin: function()
74
+ {
75
+ // substitute our didUpdateLayer method (but saving the old one)
76
+ this._animatable_original_did_update_layer = this.didUpdateLayer || function(){};
77
+ this.didUpdateLayer = this._animatable_did_update_layer;
78
+
79
+ // for debugging
80
+ this._animateTickPixel.displayName = "animate-tick";
81
+
82
+ // if transitions was concatenated...
83
+ var i;
84
+ if (SC.isArray(this.transitions))
85
+ {
86
+ var tl = {}; // prepare a new one mixed in
87
+ for (i = 0; i < this.transitions.length; i++)
88
+ {
89
+ SC.mixin(tl, this.transitions[i]);
90
+ }
91
+ this.transitions = tl;
92
+ }
93
+
94
+ // go through transitions and make duration-only ones follow normal pattern
95
+ for (i in this.transitions)
96
+ {
97
+ if (typeof this.transitions[i] == "number")
98
+ {
99
+ this.transitions[i] = { duration: this.transitions[i] };
100
+ }
101
+ }
102
+
103
+ // live animators
104
+ this._animators = {}; // keyAnimated => object describing it.
105
+ this._animatableSetCSS = "";
106
+ this._last_transition_css = ""; // to keep from re-setting unnecessarily
107
+ this._disableAnimation = 0; // calls to disableAnimation add one; enableAnimation remove one.
108
+ },
109
+
110
+ /**
111
+ Disables animation.
112
+
113
+ It is like parenthesis. Each "disable" must be matched by an "enable".
114
+ If you call disable twice, you need two enables to start it. Three times, you need
115
+ three enables.
116
+ */
117
+ disableAnimation: function() { this._disableAnimation++; },
118
+
119
+ /**
120
+ Enables animation if it was disabled (or moves towards that direction, at least).
121
+ */
122
+ enableAnimation: function() { this._disableAnimation--; if (this._disableAnimation < 0) this._disableAnimation = 0; },
123
+
124
+ /**
125
+ Adds support for some style properties to adjust.
126
+
127
+ These added properties are currently:
128
+ - opacity.
129
+ - display.
130
+
131
+ This is a complete rewrite of adjust. Its performance can probably be boosted. Do it!
132
+ */
133
+ adjust: function(dictionary, value)
134
+ {
135
+ if (!SC.none(value)) {
136
+ var key = dictionary;
137
+ dictionary = { };
138
+ dictionary[key] = value;
139
+ }
140
+ else {
141
+ dictionary = SC.clone(dictionary);
142
+ }
143
+
144
+ var style = SC.clone(this.get("style")), didChangeStyle = NO, layout = SC.clone(this.get("layout")), didChangeLayout = NO;
145
+ var sprops = this._styleProperties;
146
+ for (var i in dictionary)
147
+ {
148
+ var didChange = NO;
149
+
150
+ var current = (sprops.indexOf(i) >= 0) ? style : layout;
151
+ var cval = current[i], nval = dictionary[i];
152
+
153
+ if (nval !== undefined && cval !== nval)
154
+ {
155
+ if (nval === null)
156
+ {
157
+ if (cval !== undefined) didChange = YES;
158
+ delete current[i];
159
+ }
160
+ else
161
+ {
162
+ current[i] = nval;
163
+ didChange = YES;
164
+ }
165
+ }
166
+
167
+ if (didChange) {
168
+ if (current === style) didChangeStyle = YES; else didChangeLayout = YES;
169
+ }
170
+ }
171
+
172
+ if (didChangeStyle) this.set("style", style);
173
+ if (didChangeLayout) this.set("layout", layout);
174
+
175
+ // call base with whatever is leftover
176
+ return this;
177
+ },
178
+
179
+ /**
180
+ Resets animation so that next manipulation of style will not animate.
181
+
182
+ Currently does not stop existing animations, so won't work very well
183
+ if there are any (will actually stutter).
184
+ */
185
+ resetAnimation: function()
186
+ {
187
+ this._animatableCurrentStyle = this.style;
188
+ this.updateStyle();
189
+ },
190
+
191
+ _getStartStyleHash: function(start, target)
192
+ {
193
+ // temporarily set layout to "start", in the fastest way possible;
194
+ // note that start is an entire style structure—get("frame") doesn't care! HAH!
195
+ var original_layout = this.layout;
196
+ this.layout = start;
197
+
198
+ // get our frame and parent's frame
199
+ var f = this.get("frame");
200
+ var p = this.getPath("layoutView.frame");
201
+
202
+ // set back to target
203
+ this.layout = original_layout;
204
+
205
+ // prepare a new style set, empty.
206
+ var l = {};
207
+
208
+ // loop through properties in target
209
+ for (var i in target)
210
+ {
211
+ if (f)
212
+ {
213
+ if (i == "left") { l[i] = f.x; continue; }
214
+ else if (i == " top") { l[i] = f.y; continue; }
215
+ else if (i == "right") { l[i] = p.width - f.x - f.width; continue; }
216
+ else if (i == "bottom") { l[i] = p.height - f.y - f.height; continue; }
217
+ else if (i == "width") { l[i] = f.width; continue; }
218
+ else if (i == "height") { l[i] = f.height; continue; }
219
+ else if (i == "centerX") { l[i] = f.x + (f.width / 2) - (p.width / 2); continue; }
220
+ else if (i == "centerY") { l[i] = f.y + (f.height / 2) - (p.height / 2); continue; }
221
+ }
222
+
223
+ if (!SC.none(start[i])) l[i] = start[i];
224
+ else l[i] = target[i];
225
+ }
226
+
227
+ return l;
228
+ },
229
+
230
+ _TMP_CSS_TRANSITIONS: [],
231
+
232
+ /**
233
+ Immediately applies styles to elements, and starts any needed transitions.
234
+
235
+ Called automatically when style changes, but if you need styles to be adjusted
236
+ immediately (for instance, if you have temporarily disabled animation to set a
237
+ start state), you may want to call manually too.
238
+ */
239
+ updateStyle: function()
240
+ {
241
+
242
+ // get the layer. We need it for a great many things.
243
+ var layer = this.get("layer");
244
+
245
+ // cont. with other stuff
246
+ var newStyle = this.get("style");
247
+
248
+ // make sure there _is_ a previous style to animate from. Otherwise,
249
+ // we don't animate—and this is sometimes used to temporarily disable animation.
250
+ var i;
251
+ if (!this._animatableCurrentStyle || this._disableAnimation > 0)
252
+ {
253
+ // clone it to be a nice starting point next time.
254
+ this._animatableCurrentStyle = {};
255
+ for (i in newStyle)
256
+ {
257
+ if (i[0] != "_") this._animatableCurrentStyle[i] = newStyle[i];
258
+ }
259
+
260
+ if (layer) this._animatableApplyStyles(layer, newStyle);
261
+ return this;
262
+ }
263
+
264
+ // no use doing anything else if no layer.
265
+ if (!layer) return;
266
+
267
+ // compare new style to old style. Manually, to skip guid stuff that can
268
+ // mess things up a bit.
269
+ var equal = true;
270
+ for (i in newStyle)
271
+ {
272
+ if (i[0] == "_") continue;
273
+ if (newStyle[i] != this._animatableCurrentStyle[i])
274
+ {
275
+ equal = false;
276
+ break;
277
+ }
278
+ }
279
+ if (equal) return this;
280
+
281
+ // get a normalized starting point based off of our style
282
+ var startingPoint = this._getStartStyleHash(this._animatableCurrentStyle, newStyle);
283
+
284
+ // prepare stuff for timing function calc
285
+ var timing;
286
+
287
+ // also prepare an array of CSS transitions to set up. Do this always so we get (and keep) all transitions.
288
+ var cssTransitions = this._TMP_CSS_TRANSITIONS;
289
+ if (SC.Animatable.enableCSSTransitions) {
290
+ // loop
291
+ for (i in this.transitions) {
292
+ if (!this._cssTransitionFor[i]) continue;
293
+
294
+ // get timing function
295
+ var timing_function = "linear";
296
+ if (this.transitions[i].timing || SC.Animatable.defaultTimingFunction) {
297
+ timing = this.transitions[i].timing || SC.Animatable.defaultTimingFunction;
298
+ if (SC.typeOf(timing) != SC.T_STRING) {
299
+ timing_function = "cubic-bezier(" + timing[0] + ", " + timing[1] + ", " + timing[2] + ", " + timing[3] + ")";
300
+ } else {
301
+ timing_function = timing;
302
+ }
303
+ }
304
+
305
+ // add transition
306
+ cssTransitions.push(this._cssTransitionFor[i] + " " + this.transitions[i].duration + "s " + timing_function);
307
+ }
308
+ }
309
+
310
+ for (i in newStyle)
311
+ {
312
+ if (i[0] == "_") continue; // guid (or something else we can't deal with anyway)
313
+
314
+ // if it needs to be set right away since it is not animatable, _getStartStyleHash
315
+ // will have done that. But if we aren't supposed to animate it, we need to know, now.
316
+ var shouldSetImmediately = !this.transitions[i] || newStyle[i] == startingPoint[i];
317
+ if (i == "display" && newStyle[i] != "none") shouldSetImmediately = true;
318
+
319
+ if (shouldSetImmediately)
320
+ {
321
+ // set
322
+ startingPoint[i] = newStyle[i];
323
+
324
+ // you can't easily stop the animator. So just set its endpoint and make it end soon.
325
+ var animator = this._animators[i];
326
+ if (animator)
327
+ {
328
+ animator.endValue = newStyle[i];
329
+ animator.end = 0;
330
+ }
331
+ continue;
332
+ }
333
+
334
+ // If there is an available CSS transition, use that.
335
+ if (SC.Animatable.enableCSSTransitions && this._cssTransitionFor[i])
336
+ {
337
+ // the transition is already set up.
338
+ // we can just set it as part of the starting point
339
+ startingPoint[i] = newStyle[i];
340
+ continue;
341
+ }
342
+
343
+ // well well well... looks like we need to animate. Prepare an animation structure.
344
+ // (WHY ARE WE ALWAYS PREPARING?)
345
+ var applier = this._animateTickPixel,
346
+ property = i,
347
+ startValue = startingPoint[i],
348
+ endValue = newStyle[i];
349
+
350
+ // special property stuff
351
+ if (property == "centerX" || property == "centerY")
352
+ {
353
+ // uh... need a special applier; it needs to update currentlayout differently than actual
354
+ // layout, since one gets "layout," and the other gets styles.
355
+ applier = this._animateTickCenter;
356
+ }
357
+ else if (property == "opacity")
358
+ {
359
+ applier = this._animateTickNumber;
360
+ }
361
+ else if (property == "display")
362
+ {
363
+ applier = this._animateTickDisplay;
364
+ }
365
+
366
+ // cache animator objects, not for memory, but so we can modify them.
367
+ if (!this._animators[i]) this._animators[i] = {};
368
+
369
+ // used to mixin a struct. But I think that would create a new struct.
370
+ // also, why waste cycles on a SC.mixin()? So I go the direct approach.
371
+ var a = this._animators[i];
372
+
373
+ // set settings...
374
+ // start: Date.now(), // you could put this here. But it is better to wait. The animation is smoother
375
+ // if its beginning time is whenever the first frame fires.
376
+ // otherwise, if there is a big delay before the first frame (perhaps we are animating other elements)
377
+ // the items will "jump" unattractively
378
+ a.start = null;
379
+ a.duration = Math.floor(this.transitions[i].duration * 1000);
380
+ a.startValue = startValue;
381
+ a.endValue = endValue;
382
+ a.layer = layer;
383
+ a.property = property;
384
+ a.action = applier;
385
+ a.style = layer.style;
386
+ a.holder = this;
387
+
388
+ timing = this.transitions[i].timing || SC.Animatable.defaultTimingFunction;
389
+ if (timing && SC.typeOf(timing) != SC.T_STRING) a.timingFunction = timing;
390
+
391
+ // add timer
392
+ if (!a.going) this._animationsToStart[i] = a;
393
+ }
394
+
395
+ // start animations, in order
396
+ var ao = this._animationOrder, l = this._animationOrder.length;
397
+ for (i = 0; i < l; i++)
398
+ {
399
+ var nextAnimation = ao[i];
400
+ if (this._animationsToStart[nextAnimation])
401
+ {
402
+ SC.Animatable.addTimer(this._animationsToStart[nextAnimation]);
403
+ delete this._animationsToStart[nextAnimation];
404
+ }
405
+ }
406
+
407
+ // and update layout to the normalized start.
408
+ var css = cssTransitions.join(",");
409
+ cssTransitions.length = "";
410
+ this._animatableSetCSS = css;
411
+
412
+ // apply starting styles directly to layer
413
+ this._animatableApplyStyles(layer, startingPoint);
414
+
415
+ // all our timers are scheduled, we should be good to go. YAY.
416
+ return this;
417
+
418
+ }.observes("style"),
419
+
420
+ _style_opacity_helper: function(style, key, props)
421
+ {
422
+ style["opacity"] = props["opacity"];
423
+ style["mozOpacity"] = props["opacity"]; // older Firefox?
424
+ style["filter"] = "alpha(opacity=" + props["opacity"] * 100 + ")";
425
+ },
426
+
427
+ /**
428
+ Adjusts display and queues a change for the other properties.
429
+ */
430
+ _animatableApplyStyles: function(layer, styles)
431
+ {
432
+ if (!layer) return;
433
+
434
+ // handle a specific style first: display. There is a special case because it disrupts transitions.
435
+ if (styles["display"]) {
436
+ layer.style["display"] = styles["display"];
437
+ }
438
+
439
+ // set CSS transitions very first thing
440
+ if (this._animatableSetCSS != this._last_transition_css) {
441
+ layer.style["-webkit-transition"] = this._animatableSetCSS;
442
+ layer.style["-moz-transition"] = this._animatableSetCSS;
443
+ this._last_transition_css = this._animatableSetCSS;
444
+ }
445
+
446
+ if (!this._animators["display-styles"]) this._animators["display-styles"] = {};
447
+
448
+ // get timer
449
+ var timer = this._animators["display-styles"];
450
+
451
+ // fire if there is already a pending one
452
+ if (timer.isQueued) {
453
+ // timer.action.call(timer, 0);
454
+ }
455
+
456
+ timer.holder = this;
457
+ timer.action = this._animatableApplyNonDisplayStyles;
458
+ timer.layer = layer;
459
+ timer.styles = styles;
460
+ this._animatableCurrentStyle = styles;
461
+ SC.Animatable.addTimer(timer);
462
+ },
463
+
464
+ _animatableApplyNonDisplayStyles: function(){
465
+ var loop = SC.RunLoop.begin();
466
+ var layer = this.layer, styles = this.styles;
467
+ var styleHelpers = {
468
+ opacity: this.holder._style_opacity_helper
469
+ // more to be added here...
470
+ };
471
+
472
+ var newLayout = {}, updateLayout = NO, style = layer.style;
473
+
474
+ // we extract the layout portion so SproutCore can do its own thing...
475
+ for (var i in styles)
476
+ {
477
+ if (i == "display") continue;
478
+ if (this.holder._layoutStyles.indexOf(i) >= 0)
479
+ {
480
+ newLayout[i] = styles[i];
481
+ updateLayout = YES;
482
+ continue;
483
+ }
484
+ if (styleHelpers[i]) styleHelpers[i](style, i, styles);
485
+ }
486
+
487
+ // don't want to set because we don't want updateLayout... again.
488
+ if (updateLayout) {
489
+ var prev = this.holder.layout;
490
+ this.holder.layout = newLayout;
491
+
492
+ // set layout
493
+ this.holder.notifyPropertyChange("layoutStyle");
494
+
495
+ // apply the styles (but we have to mix it in, because we still have transitions, etc. that we set)
496
+ var ls = this.holder.get("layoutStyle");
497
+ for (var key in ls) {
498
+ if (SC.none(ls[key])) delete style[key];
499
+ else if (style[key] != ls[key]) style[key] = ls[key];
500
+ }
501
+
502
+ // go back to previous
503
+ this.holder.layout = prev;
504
+ }
505
+ loop.end();
506
+ },
507
+
508
+ /**
509
+ Overridden so that the proper styles are always set after a call to render.
510
+ */
511
+ _animatable_did_update_layer: function()
512
+ {
513
+ this._animatable_original_did_update_layer();
514
+ var styles = this._animatableCurrentStyle || (this.get("style") || {}), layer = this.get("layer");
515
+ this._animatableApplyStyles(layer, styles);
516
+ },
517
+
518
+ /**
519
+ Overriden to support animation.
520
+
521
+ Works by keeping a copy of the current layout, called animatableCurrentLayout.
522
+ Whenever the layout needs updating, the old layout is consulted.
523
+
524
+ "layout" is kept at the new layout
525
+ */
526
+ updateLayout: function(context, firstTime)
527
+ {
528
+ var style = SC.clone(this.get("style"));
529
+ var newLayout = this.get("layout");
530
+ var i = 0, ls = this._layoutStyles, lsl = ls.length, didChange = NO;
531
+ for (i = 0; i < lsl; i++)
532
+ {
533
+ var key = ls[i];
534
+ if (style[key] !== newLayout[key])
535
+ {
536
+ if (SC.none(newLayout[key])) delete style[key];
537
+ else style[key] = newLayout[key];
538
+ didChange = YES;
539
+ }
540
+ }
541
+
542
+ if (didChange) {
543
+ this.style = style;
544
+ this.updateStyle(); // updateLayout is already called late, so why delay longer?
545
+ }
546
+
547
+ return this;
548
+ },
549
+
550
+ /**
551
+ Solves cubic bezier curves. Basically, returns the Y for the supplied X.
552
+
553
+ I have only a vague idea of how this works. But I do have a vague idea. It is originally
554
+ from WebKit's source code:
555
+ http://trac.webkit.org/browser/trunk/WebCore/platform/graphics/UnitBezier.h?rev=31808
556
+ */
557
+ _solveBezierForT: function(ax, ay, bx, by, cx, cy, x, duration) {
558
+ // determines accuracy. Which means animation is slower for longer duration animations.
559
+ // that seems ironic, for some reason, but I don't know why.
560
+
561
+ // SOME OPTIMIZATIONS COULD BE DONE, LIKE MOVING THIS INTO ITS OWN BIT AT BEGINNING OF ANIMATION.
562
+ var epsilon = 1.0 / (200.0 * duration);
563
+
564
+ // a method I have NO idea about... Newton's method
565
+ var t0, t1, t2, x2, d2, i;
566
+ for (t2 = x, i = 0; i < 8; i++) {
567
+ x2 = ((ax * t2 + bx) * t2 + cx) * t2 - x; // sample curve x for t2, - x
568
+ if (Math.abs(x2) < epsilon) return t2; // obviously, this is determining the accuracy
569
+ d2 = (3.0 * ax * t2 + 2.0 * bx) * t2 + cx;
570
+ if (Math.abs(d2) < Math.pow(10, -6)) break;
571
+ t2 = t2 - x2 / d2;
572
+ }
573
+
574
+ // fall back to bisection
575
+ t0 = 0.0;
576
+ t1 = 1.0;
577
+ t2 = x;
578
+ if (t2 < t0) return t0;
579
+ if (t2 > t1) return t1;
580
+ while (t0 < t1) {
581
+ x2 = ((ax * t2 + bx) * t2 + cx) * t2;
582
+ if (Math.abs(x2 - x) < epsilon) return t2;
583
+
584
+ if (x > x2) t0 = t2;
585
+ else t1 = t2;
586
+
587
+ t2 = (t1 - t0) * 0.5 + t0;
588
+ }
589
+
590
+ return t2; // on failure
591
+ },
592
+
593
+ _solveBezier: function(p1x, p1y, p2x, p2y, x, duration) {
594
+ // calculate coefficients
595
+ var cx = 3.0 * p1x;
596
+ var bx = 3.0 * (p2x - p1x) - cx;
597
+ var ax = 1.0 - cx - bx;
598
+
599
+ var cy = 3.0 * p1y;
600
+ var by = 3.0 * (p2y - p1y) - cy;
601
+ var ay = 1.0 - cy - by;
602
+
603
+ var t = this._solveBezierForT(ax, ay, bx, by, cx, cy, x, duration);
604
+
605
+ // now calculate Y
606
+ return ((ay * t + by) * t + cy) * t;
607
+ },
608
+
609
+ /**
610
+ Manages a single step in a single animation.
611
+ NOTE: this=>an animator hash
612
+ */
613
+ _animateTickPixel: function(t)
614
+ {
615
+ // prepare timing stuff
616
+ // first, setup this.start if needed (it is lazy, after all)
617
+ if (SC.none(this.start))
618
+ {
619
+ this.start = t;
620
+ this.end = this.start + this.duration;
621
+ }
622
+
623
+ // the differences
624
+ var s = this.start, e = this.end;
625
+ var sv = this.startValue, ev = this.endValue;
626
+ var d = e - s;
627
+ var dv = ev - sv;
628
+
629
+ // get current percent of animation completed
630
+ var c = t - s;
631
+ var percent = Math.min(c / d, 1);
632
+
633
+ // call interpolator (if any)
634
+ if (this.timingFunction) {
635
+ // this may be slow, but...
636
+ var timing = this.timingFunction;
637
+ percent = this.holder._solveBezier(timing[0], timing[1], timing[2], timing[3], percent, d);
638
+ }
639
+
640
+ // calculate new position
641
+ var value = Math.floor(sv + (dv * percent));
642
+ this.holder._animatableCurrentStyle[this.property] = value;
643
+
644
+ // note: the following tested faster than directly setting this.layer.style.cssText
645
+ this.style[this.property] = value + "px";
646
+
647
+ if (t < e) SC.Animatable.addTimer(this);
648
+ else {
649
+ this.going = false;
650
+ this.styles = null;
651
+ this.layer = null;
652
+ }
653
+ },
654
+
655
+ _animateTickDisplay: function(t)
656
+ {
657
+ // prepare timing stuff
658
+ // first, setup this.start if needed (it is lazy, after all)
659
+ if (SC.none(this.start))
660
+ {
661
+ this.start = t;
662
+ this.end = this.start + this.duration;
663
+ }
664
+
665
+ // check if we should keep going (we only set display none, and only at end)
666
+ var e = this.end;
667
+ if (t < e)
668
+ {
669
+ SC.Animatable.addTimer(this);
670
+ return;
671
+ }
672
+
673
+ this.holder._animatableCurrentStyle[this.property] = this.endValue;
674
+ this.style[this.property] = this.endValue;
675
+
676
+ this.going = false;
677
+ this.styles = null;
678
+ this.layer = null;
679
+ },
680
+
681
+ /**
682
+ Manages a single step in a single animation.
683
+ NOTE: this=>an animator hash
684
+ */
685
+ _animateTickNumber: function(t)
686
+ {
687
+ // prepare timing stuff
688
+ // first, setup this.start if needed (it is lazy, after all)
689
+ if (SC.none(this.start))
690
+ {
691
+ this.start = t;
692
+ this.end = this.start + this.duration;
693
+ }
694
+
695
+ // the differences
696
+ var s = this.start, e = this.end;
697
+ var sv = this.startValue, ev = this.endValue;
698
+ var d = e - s;
699
+ var dv = ev - sv;
700
+
701
+ // get current percent of animation completed
702
+ var c = t - s;
703
+ var percent = Math.min(c / d, 1);
704
+
705
+ // call interpolator (if any)
706
+ if (this.timingFunction) {
707
+ // this may be slow, but...
708
+ var timing = this.timingFunction;
709
+ percent = this.holder._solveBezier(timing[0], timing[1], timing[2], timing[3], percent, d);
710
+ }
711
+
712
+ // calculate new position
713
+ var value = Math.round((sv + (dv * percent)) * 100) / 100;
714
+ this.holder._animatableCurrentStyle[this.property] = value;
715
+
716
+ // note: the following tested faster than directly setting this.layer.style.cssText
717
+ this.style[this.property] = value;
718
+ if (this.property == "opacity")
719
+ {
720
+ this.style["zoom"] = 1;
721
+ this.style["filter"] = "alpha(opacity=" + Math.round(value * 20) * 5 + ")";
722
+ }
723
+
724
+ if (t < e) SC.Animatable.addTimer(this);
725
+ else {
726
+ this.going = false;
727
+ this.styles = null;
728
+ this.layer = null;
729
+ }
730
+ },
731
+
732
+ // NOTE: I tested this with two separate functions (one for each X and Y)
733
+ // no definite performance difference on Safari, at least.
734
+ _animateTickCenter: function(t)
735
+ {
736
+ // prepare timing stuff
737
+ // first, setup this.start if needed (it is lazy, after all)
738
+ if (SC.none(this.start))
739
+ {
740
+ this.start = t;
741
+ this.end = this.start + this.duration;
742
+ }
743
+
744
+ // the differences
745
+ var s = this.start, e = this.end;
746
+ var sv = this.startValue, ev = this.endValue;
747
+ var d = e - s;
748
+ var dv = ev - sv;
749
+
750
+ // get current percent of animation completed
751
+ var c = t - s;
752
+ var percent = Math.min(c / d, 1);
753
+
754
+ // call interpolator (if any)
755
+ if (this.timingFunction) {
756
+ // this may be slow, but...
757
+ var timing = this.timingFunction;
758
+ percent = this.holder._solveBezier(timing[0], timing[1], timing[2], timing[3], percent, d);
759
+ }
760
+
761
+ // calculate new position
762
+ var value = sv + (dv * percent);
763
+ this.holder._animatableCurrentStyle[this.property] = value;
764
+
765
+ // calculate style, which needs to subtract half of width/height
766
+ var widthOrHeight, style;
767
+ if (this.property == "centerX")
768
+ {
769
+ widthOrHeight = "width"; style = "margin-left";
770
+ }
771
+ else
772
+ {
773
+ widthOrHeight = "height"; style = "margin-top";
774
+ }
775
+
776
+ this.style[style] = Math.round(value - (this.holder._animatableCurrentStyle[widthOrHeight] / 2)) + "px";
777
+
778
+ if (t < e) SC.Animatable.addTimer(this);
779
+ else {
780
+ this.going = false;
781
+ this.styles = null;
782
+ this.layer = null;
783
+ }
784
+ }
785
+ };
786
+
787
+ /*
788
+ Add Singleton Portion
789
+ */
790
+ SC.mixin(SC.Animatable, {
791
+ NAMESPACE: 'SC.Animatable',
792
+ VERSION: '0.1.0',
793
+
794
+ // CSS-only
795
+ TRANSITION_NONE: "linear",
796
+ TRANSITION_CSS_EASE: "ease",
797
+ TRANSITION_CSS_EASE_IN: "ease-in",
798
+ TRANSITION_CSS_EASE_OUT: "ease-out",
799
+ TRANSITION_CSS_EASE_OUT: "ease-in-out",
800
+
801
+ // JavaScript-enabled
802
+ TRANSITION_EASE: [0.25, 0.1, 0.25, 1.0],
803
+ TRANSITION_LINEAR: [0.0, 0.0, 1.0, 1.0],
804
+ TRANSITION_EASE_IN: [0.42, 0.0, 1.0, 1.0],
805
+ TRANSITION_EASE_OUT: [0, 0, 0.58, 1.0],
806
+ TRANSITION_EASE_IN_OUT: [0.42, 0, 0.58, 1.0],
807
+
808
+ defaultTimingFunction: null, // you can change to TRANSITION_EASE, etc., but that may impact performance.
809
+
810
+ // For performance, use a custom linked-list timer
811
+ baseTimer: {
812
+ next: null
813
+ },
814
+
815
+ // keep track of whether the timer is running
816
+ going: false,
817
+
818
+ // ticks and tocs
819
+ _ticks: 0,
820
+ _timer_start_time: null,
821
+
822
+ // the global tiemr interval
823
+ interval: 10,
824
+
825
+ // the current time (a placeholder, really)
826
+ currentTime: new Date().getTime(),
827
+
828
+ // global setting deciding whether CSS transitions should be enabled
829
+ enableCSSTransitions: false, // automatically calculated. You can override, but only from OUTSIDE.
830
+
831
+ // keep track of some basic statistics in an object (so they can be observable)
832
+ stats: SC.Object.create({
833
+ lastFPS: 0
834
+ }),
835
+
836
+ addTimer: function(animator)
837
+ {
838
+ if (animator.isQueued) return;
839
+ animator.next = SC.Animatable.baseTimer.next;
840
+ SC.Animatable.baseTimer.next = animator;
841
+ animator.isQueued = true;
842
+ if (!SC.Animatable.going) SC.Animatable.start();
843
+ },
844
+
845
+ start: function()
846
+ {
847
+ SC.Animatable._ticks = 0;
848
+ SC.Animatable._timer_start_time = new Date().getTime();
849
+ SC.Animatable.going = true;
850
+
851
+ // set a timeout so tick only runs AFTER any pending animation timers are set.
852
+ setTimeout(SC.Animatable.timeout, 0);
853
+ },
854
+
855
+ timeout: function()
856
+ {
857
+ SC.Animatable.currentTime = new Date().getTime();
858
+ var start = SC.Animatable.currentTime;
859
+
860
+ var next = SC.Animatable.baseTimer.next;
861
+ SC.Animatable.baseTimer.next = null;
862
+ var i = 0;
863
+ while (next)
864
+ {
865
+ var t = next.next;
866
+ next.isQueued = false;
867
+ next.next = null;
868
+ next.action.call(next, start);
869
+ next = t;
870
+ i++;
871
+ }
872
+
873
+ // built-in FPS counter, so that FPS is only counted DURING animation.
874
+ // is there a way to make the minifier get rid of this? Because that would be lovely.
875
+ // still, only called once per frame, so should _very_ minimally impact performance and memory.
876
+ if (SC.Animatable._ticks < 1000000) SC.Animatable._ticks++; // okay, put _some_ limit on it
877
+
878
+ // now see about doing next bit...
879
+ var end = new Date().getTime();
880
+ var elapsed = end - start;
881
+ if (SC.Animatable.baseTimer.next)
882
+ {
883
+ setTimeout(function(){ SC.Animatable.timeout(); }, Math.max(0, SC.Animatable.interval - elapsed));
884
+ }
885
+ else
886
+ {
887
+ // we're done... so calculate FPS
888
+ SC.Animatable.going = false;
889
+
890
+ // get diff
891
+ var time_diff = end - SC.Animatable._timer_start_time;
892
+ var loop = SC.RunLoop.begin();
893
+ SC.Animatable.stats.set("lastFPS", SC.Animatable._ticks / (time_diff / 1000));
894
+ loop.end();
895
+ }
896
+ }
897
+ });
898
+
899
+
900
+ /*
901
+ Test for CSS transition capability...
902
+ */
903
+ (function(){
904
+ var test = function(){ //return false;
905
+ // a test element
906
+ var el = document.createElement("div");
907
+
908
+ // the css and javascript to test
909
+ var css_browsers = ["-webkit"];
910
+ var test_browsers = ["moz", "Moz", "o", "ms", "webkit"];
911
+
912
+ // prepare css
913
+ var css = "", i = null;
914
+ for (i = 0; i < css_browsers.length; i++) css += css_browsers[i] + "-transition:all 1s linear;"
915
+
916
+ // set css text
917
+ el.style.cssText = css;
918
+
919
+ // test
920
+ for (i = 0; i < test_browsers.length; i++)
921
+ {
922
+ if (el.style[test_browsers[i] + "TransitionProperty"] !== undefined) return true;
923
+ }
924
+
925
+ return false;
926
+ };
927
+
928
+ // test
929
+ var testResult = test();
930
+ // console.error("Supports CSS transitions: " + testResult);
931
+
932
+ // and apply what we found
933
+ if (testResult) SC.Animatable.enableCSSTransitions = true;
934
+ })();