sproutcore 1.6.0.1-java → 1.7.1.beta-java

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.
Files changed (143) hide show
  1. data/CHANGELOG +21 -0
  2. data/Gemfile +5 -0
  3. data/Rakefile +26 -13
  4. data/VERSION.yml +2 -2
  5. data/lib/Buildfile +43 -4
  6. data/lib/buildtasks/build.rake +10 -0
  7. data/lib/buildtasks/helpers/file_rule.rb +22 -0
  8. data/lib/buildtasks/helpers/file_rule_list.rb +137 -0
  9. data/lib/buildtasks/manifest.rake +133 -122
  10. data/lib/frameworks/sproutcore/CHANGELOG.md +69 -2
  11. data/lib/frameworks/sproutcore/apps/tests/english.lproj/strings.js +1 -0
  12. data/lib/frameworks/sproutcore/frameworks/bootstrap/system/browser.js +28 -22
  13. data/lib/frameworks/sproutcore/frameworks/core_foundation/controllers/array.js +9 -5
  14. data/lib/frameworks/sproutcore/frameworks/core_foundation/controllers/controller.js +1 -1
  15. data/lib/frameworks/sproutcore/frameworks/core_foundation/controls/button.js +18 -13
  16. data/lib/frameworks/sproutcore/frameworks/core_foundation/ext/handlebars/bind.js +5 -3
  17. data/lib/frameworks/sproutcore/frameworks/core_foundation/ext/handlebars/collection.js +2 -0
  18. data/lib/frameworks/sproutcore/frameworks/core_foundation/mixins/action_support.js +80 -0
  19. data/lib/frameworks/sproutcore/frameworks/core_foundation/mixins/template_helpers/text_field_support.js +84 -116
  20. data/lib/frameworks/sproutcore/frameworks/core_foundation/panes/pane.js +8 -5
  21. data/lib/frameworks/sproutcore/frameworks/core_foundation/system/event.js +157 -157
  22. data/lib/frameworks/sproutcore/frameworks/core_foundation/system/platform.js +5 -3
  23. data/lib/frameworks/sproutcore/frameworks/core_foundation/system/root_responder.js +6 -6
  24. data/lib/frameworks/sproutcore/frameworks/core_foundation/system/sparse_array.js +10 -7
  25. data/lib/frameworks/sproutcore/frameworks/core_foundation/tests/mixins/action_support.js +106 -0
  26. data/lib/frameworks/sproutcore/frameworks/core_foundation/tests/views/template/collection.js +18 -0
  27. data/lib/frameworks/sproutcore/frameworks/core_foundation/tests/views/template/handlebars.js +71 -1
  28. data/lib/frameworks/sproutcore/frameworks/core_foundation/tests/views/view/attribute_bindings_test.js +38 -0
  29. data/lib/frameworks/sproutcore/frameworks/core_foundation/tests/views/view/class_name_bindings_test.js +47 -0
  30. data/lib/frameworks/sproutcore/frameworks/core_foundation/tests/views/view/layoutChildViews.js +18 -18
  31. data/lib/frameworks/sproutcore/frameworks/core_foundation/tests/views/view/layoutStyle.js +42 -10
  32. data/lib/frameworks/sproutcore/frameworks/core_foundation/views/view.js +158 -1
  33. data/lib/frameworks/sproutcore/frameworks/core_foundation/views/view/keyboard.js +26 -1
  34. data/lib/frameworks/sproutcore/frameworks/core_foundation/views/view/layout_style.js +14 -8
  35. data/lib/frameworks/sproutcore/frameworks/datastore/models/record.js +15 -2
  36. data/lib/frameworks/sproutcore/frameworks/datastore/models/record_attribute.js +108 -108
  37. data/lib/frameworks/sproutcore/frameworks/datastore/system/query.js +1 -1
  38. data/lib/frameworks/sproutcore/frameworks/datastore/system/record_array.js +2 -4
  39. data/lib/frameworks/sproutcore/frameworks/datastore/tests/models/record/error_methods.js +2 -2
  40. data/lib/frameworks/sproutcore/frameworks/datastore/tests/models/single_attribute.js +26 -0
  41. data/lib/frameworks/sproutcore/frameworks/datastore/tests/system/query/builders.js +7 -0
  42. data/lib/frameworks/sproutcore/frameworks/datastore/tests/system/record_array/error_methods.js +1 -1
  43. data/lib/frameworks/sproutcore/frameworks/datetime/frameworks/core/system/datetime.js +4 -1
  44. data/lib/frameworks/sproutcore/frameworks/datetime/frameworks/core/tests/system/datetime.js +6 -0
  45. data/lib/frameworks/sproutcore/frameworks/desktop/panes/menu.js +26 -5
  46. data/lib/frameworks/sproutcore/frameworks/desktop/panes/picker.js +97 -96
  47. data/lib/frameworks/sproutcore/frameworks/desktop/system/drag.js +4 -3
  48. data/lib/frameworks/sproutcore/frameworks/desktop/tests/panes/menu/ui.js +17 -4
  49. data/lib/frameworks/sproutcore/frameworks/desktop/views/collection.js +7 -7
  50. data/lib/frameworks/sproutcore/frameworks/desktop/views/menu_item.js +7 -5
  51. data/lib/frameworks/sproutcore/frameworks/desktop/views/scroll.js +12 -3
  52. data/lib/frameworks/sproutcore/frameworks/desktop/views/web.js +23 -14
  53. data/lib/frameworks/sproutcore/frameworks/experimental/Buildfile +5 -1
  54. data/lib/frameworks/sproutcore/frameworks/experimental/frameworks/menu/render_delegates/menu_scroller.js +28 -0
  55. data/lib/frameworks/sproutcore/frameworks/experimental/frameworks/menu/tests/menu/scroll.js +235 -0
  56. data/lib/frameworks/sproutcore/frameworks/experimental/frameworks/menu/views/menu/scroll.js +363 -0
  57. data/lib/frameworks/sproutcore/frameworks/experimental/frameworks/menu/views/menu/scroller.js +250 -0
  58. data/lib/frameworks/sproutcore/frameworks/experimental/frameworks/scroll_view/render_delegates/desktop_scroller.js +92 -0
  59. data/lib/frameworks/sproutcore/frameworks/experimental/frameworks/scroll_view/render_delegates/native_scroll.js +25 -0
  60. data/lib/frameworks/sproutcore/frameworks/experimental/frameworks/scroll_view/render_delegates/scroll.js +33 -0
  61. data/lib/frameworks/sproutcore/frameworks/experimental/frameworks/scroll_view/render_delegates/touch_scroller.js +76 -0
  62. data/lib/frameworks/sproutcore/frameworks/experimental/frameworks/scroll_view/tests/scroll/integration.js +50 -0
  63. data/lib/frameworks/sproutcore/frameworks/experimental/frameworks/scroll_view/tests/scroll/methods.js +143 -0
  64. data/lib/frameworks/sproutcore/frameworks/experimental/frameworks/scroll_view/tests/scroll/ui.js +258 -0
  65. data/lib/frameworks/sproutcore/frameworks/experimental/frameworks/scroll_view/views/core_scroll.js +1164 -0
  66. data/lib/frameworks/sproutcore/frameworks/experimental/frameworks/scroll_view/views/core_scroller.js +332 -0
  67. data/lib/frameworks/sproutcore/frameworks/experimental/frameworks/scroll_view/views/desktop/scroll.js +236 -0
  68. data/lib/frameworks/sproutcore/frameworks/experimental/frameworks/scroll_view/views/desktop/scroller.js +347 -0
  69. data/lib/frameworks/sproutcore/frameworks/experimental/frameworks/scroll_view/views/scroll.js +15 -0
  70. data/lib/frameworks/sproutcore/frameworks/experimental/frameworks/scroll_view/views/scroller.js +10 -0
  71. data/lib/frameworks/sproutcore/frameworks/experimental/frameworks/scroll_view/views/touch/scroll.js +804 -0
  72. data/lib/frameworks/sproutcore/frameworks/experimental/frameworks/scroll_view/views/touch/scroller.js +133 -0
  73. data/lib/frameworks/sproutcore/frameworks/foundation/resources/text_field.css +3 -3
  74. data/lib/frameworks/sproutcore/frameworks/foundation/validators/number.js +3 -1
  75. data/lib/frameworks/sproutcore/frameworks/foundation/views/text_field.js +3 -3
  76. data/lib/frameworks/sproutcore/frameworks/media/views/audio.js +2 -1
  77. data/lib/frameworks/sproutcore/frameworks/media/views/controls.js +2 -1
  78. data/lib/frameworks/sproutcore/frameworks/media/views/media_slider.js +2 -4
  79. data/lib/frameworks/sproutcore/frameworks/media/views/mini_controls.js +2 -4
  80. data/lib/frameworks/sproutcore/frameworks/media/views/simple_controls.js +2 -4
  81. data/lib/frameworks/sproutcore/frameworks/media/views/video.js +2 -2
  82. data/lib/frameworks/sproutcore/frameworks/routing/system/routes.js +29 -3
  83. data/lib/frameworks/sproutcore/frameworks/runtime/core.js +2 -2
  84. data/lib/frameworks/sproutcore/frameworks/runtime/debug/test_suites/array/replace.js +1 -1
  85. data/lib/frameworks/sproutcore/frameworks/runtime/private/property_chain.js +2 -1
  86. data/lib/frameworks/sproutcore/frameworks/runtime/system/binding.js +3 -3
  87. data/lib/frameworks/sproutcore/frameworks/runtime/system/index_set.js +2 -2
  88. data/lib/frameworks/sproutcore/frameworks/runtime/system/object.js +1 -1
  89. data/lib/frameworks/sproutcore/themes/ace/resources/collection/normal/list_item.css +2 -2
  90. data/lib/frameworks/sproutcore/themes/legacy_theme/english.lproj/segmented.css +1 -1
  91. data/lib/gen/app/templates/apps/@target_name@/Buildfile +3 -5
  92. data/lib/gen/app/templates/apps/@target_name@/resources/_theme.css +18 -0
  93. data/lib/gen/project/templates/@filename@/Buildfile +2 -2
  94. data/lib/sproutcore.rb +30 -5
  95. data/lib/sproutcore/builders.rb +1 -0
  96. data/lib/sproutcore/builders/chance_file.rb +9 -16
  97. data/lib/sproutcore/builders/html.rb +2 -1
  98. data/lib/sproutcore/builders/minify.rb +4 -35
  99. data/lib/sproutcore/builders/module.rb +38 -1
  100. data/lib/sproutcore/builders/split.rb +63 -0
  101. data/lib/sproutcore/builders/strings.rb +7 -1
  102. data/lib/sproutcore/helpers.rb +1 -1
  103. data/lib/sproutcore/helpers/css_split.rb +190 -0
  104. data/lib/sproutcore/helpers/entry_sorter.rb +2 -0
  105. data/lib/sproutcore/helpers/minifier.rb +40 -16
  106. data/lib/sproutcore/helpers/static_helper.rb +35 -17
  107. data/lib/sproutcore/models/manifest.rb +26 -0
  108. data/lib/sproutcore/models/target.rb +12 -1
  109. data/lib/sproutcore/rack.rb +1 -0
  110. data/lib/sproutcore/rack/proxy.rb +244 -225
  111. data/lib/sproutcore/rack/restrict_ip.rb +67 -0
  112. data/lib/sproutcore/rack/service.rb +8 -2
  113. data/lib/sproutcore/tools.rb +102 -46
  114. data/lib/sproutcore/tools/build.rb +91 -43
  115. data/lib/sproutcore/tools/gen.rb +2 -3
  116. data/lib/sproutcore/tools/manifest.rb +22 -16
  117. data/lib/sproutcore/tools/server.rb +21 -0
  118. data/spec/buildtasks/helpers/accept_list +22 -0
  119. data/spec/buildtasks/helpers/accept_list.rb +128 -0
  120. data/spec/buildtasks/helpers/list.json +11 -0
  121. data/spec/buildtasks/manifest/prepare_build_tasks/chance_2x_spec.rb +1 -39
  122. data/spec/buildtasks/manifest/prepare_build_tasks/chance_spec.rb +0 -38
  123. data/spec/buildtasks/manifest/prepare_build_tasks/combine_spec.rb +4 -4
  124. data/spec/buildtasks/manifest/prepare_build_tasks/module_spec.rb +2 -2
  125. data/spec/buildtasks/manifest/prepare_build_tasks/packed_2x_indirect_spec.rb +7 -16
  126. data/spec/buildtasks/manifest/prepare_build_tasks/packed_2x_spec.rb +7 -17
  127. data/spec/buildtasks/manifest/prepare_build_tasks/packed_spec.rb +11 -6
  128. data/spec/fixtures/builder_tests/Buildfile +2 -1
  129. data/spec/fixtures/builder_tests/apps/module_test/modules/required_module/core.js +0 -0
  130. data/spec/lib/builders/module_spec.rb +1 -1
  131. data/spec/spec_helper.rb +1 -0
  132. data/sproutcore.gemspec +4 -9
  133. data/vendor/chance/lib/chance.rb +25 -6
  134. data/vendor/chance/lib/chance/factory.rb +45 -0
  135. data/vendor/chance/lib/chance/instance.rb +173 -28
  136. data/vendor/chance/lib/chance/instance/data_url.rb +0 -29
  137. data/vendor/chance/lib/chance/instance/slicing.rb +57 -4
  138. data/vendor/chance/lib/chance/instance/spriting.rb +112 -21
  139. data/vendor/chance/lib/chance/parser.rb +80 -52
  140. data/vendor/sproutcore/SCCompiler.jar +0 -0
  141. data/vendor/sproutcore/lib/args4j-2.0.12.jar +0 -0
  142. data/vendor/sproutcore/lib/yuicompressor-2.4.2.jar +0 -0
  143. metadata +84 -25
@@ -111,7 +111,13 @@ describe "manifest:prepare_build_tasks:packed" do
111
111
 
112
112
  before do
113
113
  run_task
114
- @entry = entry_for('stylesheet-packed.css')
114
+
115
+ # for 1x (but not 2x) there should be an intermediary entry for a transform
116
+ # (split_css) that... splits the CSS. See its documentation.
117
+ #
118
+ # we are looking for the stylesheet-packed.css that is set on its source entry.
119
+ @entry = entry_for('stylesheet-packed.css')[:source_entry]
120
+
115
121
  @entry_2x = entry_for('stylesheet@2x-packed.css')
116
122
  end
117
123
 
@@ -125,11 +131,6 @@ describe "manifest:prepare_build_tasks:packed" do
125
131
  @entry.source_entries.each do |entry|
126
132
  entry.filename.should == 'stylesheet.css'
127
133
  end
128
-
129
- @entry_2x.source_entries.size.should > 0
130
- @entry_2x.source_entries.each do |entry|
131
- entry.filename.should == 'stylesheet@2x.css'
132
- end
133
134
  end
134
135
 
135
136
  it "should include ordered_entries ordered by required target order" do
@@ -145,17 +146,6 @@ describe "manifest:prepare_build_tasks:packed" do
145
146
  @entry.ordered_entries.each do |entry|
146
147
  entry.target.should == targets.shift
147
148
  end
148
-
149
- targets = @target.expand_required_targets(:theme => true) + [@target]
150
- variation = @entry.manifest.variation
151
- targets.reject! do |t|
152
- t.manifest_for(variation).build!.entry_for('stylesheet@2x.css').nil?
153
- end
154
-
155
- @entry_2x.ordered_entries.each do |entry|
156
- entry.target.should == targets.shift
157
- end
158
-
159
149
  end
160
150
 
161
151
  it "should include the actual targets this packed version covers in the targets property (even those w no stylesheet.css)" do
@@ -40,8 +40,8 @@ describe "manifest:prepare_build_tasks:packed" do
40
40
  @manifest.entry_for('javascript-packed.js').should_not be_nil
41
41
  @manifest.entry_for('stylesheet-packed.css').should_not be_nil
42
42
 
43
- # In this version, there should _not_ be an @2x entry.
44
- @manifest.entry_for('stylesheet@2x-packed.css').should be_nil
43
+ # there must always be a 2x-packed entry
44
+ @manifest.entry_for('stylesheet@2x-packed.css').should_not be_nil
45
45
  end
46
46
 
47
47
  it "should remove non-packed JS entries from apps" do
@@ -132,16 +132,21 @@ describe "manifest:prepare_build_tasks:packed" do
132
132
 
133
133
  run_task
134
134
 
135
- @entry = entry_for('stylesheet-packed.css')
135
+ # for 1x (but not 2x) there should be an intermediary entry for a transform
136
+ # (split_css) that... splits the CSS. See its documentation.
137
+ #
138
+ # we are looking for the stylesheet-packed.css that is set on its source entry.
139
+ @entry = entry_for('stylesheet-packed.css')[:source_entry]
140
+
136
141
  @entry_2x = entry_for('stylesheet@2x-packed.css')
137
142
  end
138
143
 
139
144
  it "should generate a stylesheet-packed.css entry" do
140
145
  @entry.should_not be_nil
141
146
 
142
- # should be _no_ @2x entry for this version (see packed_2x_spec)
143
- # because the @2x version is only included as-needed.
144
- @entry_2x.should be_nil
147
+ # There should always be a 2x packed entry, because default JS
148
+ # looks for 2x when running in 2x mode, and has no fallback.
149
+ @entry_2x.should_not be_nil
145
150
  end
146
151
 
147
152
  it "should include stylesheet.css entries from all required targets" do
@@ -19,4 +19,5 @@ config '/module_test/deferred_module',
19
19
  config :module_test,
20
20
  :deferred_modules => [:deferred_module],
21
21
  :prefetched_modules => [:required_target],
22
- :inlined_modules => [:inlined_module]
22
+ :inlined_modules => [:inlined_module],
23
+ :required => [:required_module]
@@ -45,7 +45,7 @@ describe 'SC::Builder::ModuleInfo' do
45
45
  req = @target.required_targets
46
46
 
47
47
  req.size.should == 1
48
- req.first.target_name.should == :'/module_test/inlined_module'
48
+ req.first.target_name.should == :'/module_test/required_module'
49
49
  end
50
50
 
51
51
  it "should require one deferred module" do
data/spec/spec_helper.rb CHANGED
@@ -2,6 +2,7 @@ require 'fileutils'
2
2
  require 'tempfile'
3
3
 
4
4
  require "sproutcore"
5
+ Dir["#{File.dirname(__FILE__)}/../lib/buildtasks/helpers/*.rb"].each {|f| require f}
5
6
 
6
7
  module SC
7
8
 
data/sproutcore.gemspec CHANGED
@@ -3,7 +3,6 @@ require "sproutcore/version"
3
3
 
4
4
  os = Gem::Platform.local.os
5
5
  is_jruby = (os == "java")
6
- is_mingw = (os == "mingw32")
7
6
 
8
7
  Gem::Specification.new do |s|
9
8
  s.name = 'sproutcore'
@@ -14,17 +13,17 @@ Gem::Specification.new do |s|
14
13
  s.summary = "SproutCore is a platform for building native look-and-feel applications on the web"
15
14
 
16
15
  s.platform = 'java' if is_jruby
17
- s.platform = 'x86-mingw32' if is_mingw
18
16
 
19
17
  s.add_dependency 'rack', '~> 1.2'
20
18
  s.add_dependency 'json_pure', "~> 1.4.6"
21
19
  s.add_dependency 'extlib', "~> 0.9.15"
22
20
  s.add_dependency 'erubis', "~> 2.6"
23
21
  s.add_dependency 'thor', '~> 0.14.3'
24
- s.add_dependency 'haml', '~> 3.1.1'
25
-
26
- s.add_dependency 'compass', '~> 0.11.1'
22
+ s.add_dependency 'sass', '~> 3.1.3'
23
+ s.add_dependency 'haml', '~> 3.1.2'
27
24
 
25
+ s.add_dependency 'compass', '~> 0.11.3'
26
+ s.add_dependency 'chunky_png', '~> 1.2.0'
28
27
  s.add_dependency 'em-http-request', '~> 1.0.0.beta'
29
28
 
30
29
  if is_jruby
@@ -33,10 +32,6 @@ Gem::Specification.new do |s|
33
32
  s.add_dependency 'thin', '~> 1.2.11'
34
33
  end
35
34
 
36
- if is_mingw
37
- s.add_dependency 'eventmachine', '~> 1.0.0.beta'
38
- end
39
-
40
35
  s.add_development_dependency 'gemcutter', "~> 0.6.0"
41
36
  s.add_development_dependency 'rspec', "~> 2.5.0"
42
37
  s.add_development_dependency 'rake'
@@ -1,4 +1,5 @@
1
1
  require "chance/instance"
2
+ require "chance/factory"
2
3
 
3
4
  require 'sass'
4
5
 
@@ -19,8 +20,10 @@ module Chance
19
20
 
20
21
  @files = {}
21
22
 
23
+ @clear_files_immediately = false
24
+
22
25
  class << self
23
- attr_accessor :files, :_current_instance
26
+ attr_accessor :files, :_current_instance, :clear_files_immediately
24
27
 
25
28
  def add_file(path, content=nil)
26
29
  mtime = 0
@@ -29,9 +32,7 @@ module Chance
29
32
  end
30
33
 
31
34
  if @files[path]
32
- mtime = File.mtime(path).to_f
33
- update_file(path, content) if mtime > @files[path][:mtime]
34
- return
35
+ return update_file_if_needed(path, content)
35
36
  end
36
37
 
37
38
  file = {
@@ -67,6 +68,21 @@ module Chance
67
68
  puts "Updated " + path if Chance::CONFIG[:verbose]
68
69
  end
69
70
 
71
+ # if the path is a valid filesystem path and the mtime has changed, this invalidates
72
+ # the file. Returns the mtime if the file was updated.
73
+ def update_file_if_needed(path, content=nil)
74
+ if @files[path]
75
+ mtime = File.mtime(path).to_f
76
+ if mtime > @files[path][:mtime]
77
+ update_file(path, content)
78
+ end
79
+
80
+ return mtime
81
+ else
82
+ return false
83
+ end
84
+ end
85
+
70
86
  def remove_file(path)
71
87
  if not @files.has_key? path
72
88
  puts "Could not remove " + path + " because it is not in system."
@@ -144,10 +160,13 @@ module Chance
144
160
  content = file[:content]
145
161
 
146
162
  requires = []
147
- content = content.gsub(/sc_require\(['"]?(.*?)['"]?\);?/) {|match|
148
- requires.push $1
163
+ content = content.gsub(/(sc_)?require\(['"]?(.*?)['"]?\);?/) {|match|
164
+ requires.push $2
149
165
  ""
150
166
  }
167
+
168
+ # sc_resource will already be handled by the build tool. We just need to ignore it.
169
+ content.gsub!(/sc_resource\(['"]?(.*?)['"]?\);?/, '')
151
170
 
152
171
  file[:requires] = requires
153
172
  file[:content] = content
@@ -0,0 +1,45 @@
1
+ #
2
+ # The Chance factory.
3
+ # All methods require two parts: a key and a hash of options. The hash will be used in
4
+ # the case of a cache missed.
5
+ #
6
+ module Chance
7
+ module ChanceFactory
8
+
9
+ @instances = {}
10
+ @file_hashes = {}
11
+ class << self
12
+ def clear_instances
13
+ @file_hashes = {}
14
+ @instances = {}
15
+ end
16
+
17
+ def instance_for_key(key, opts)
18
+ if not @instances.include? key
19
+ @instances[key] = Chance::Instance.new(opts)
20
+ end
21
+
22
+ return @instances[key]
23
+ end
24
+
25
+ # Call with a hash mapping instance paths to absolute paths. This will compare with
26
+ # the last
27
+ def update_instance(key, opts, files)
28
+ instance = instance_for_key(key, opts)
29
+ last_hash = @file_hashes[key] || {}
30
+
31
+ # If they are not equal, we might as well throw everything. The biggest cost is from
32
+ # Chance re-running, and it will have to anyway.
33
+ if not last_hash.eql? files
34
+ instance.unmap_all
35
+ files.each {|path, identifier|
36
+ instance.map_file path, identifier
37
+ }
38
+
39
+ @file_hashes[key] = files
40
+ end
41
+ end
42
+
43
+ end
44
+ end
45
+ end
@@ -10,6 +10,8 @@ require 'chance/instance/spriting'
10
10
  require 'chance/instance/data_url'
11
11
  require 'chance/instance/javascript'
12
12
 
13
+ require 'digest/md5'
14
+
13
15
 
14
16
  Compass.discover_extensions!
15
17
  Compass.configure_sass_plugin!
@@ -21,7 +23,9 @@ module Chance
21
23
  attr_reader :path
22
24
  def initialize(path)
23
25
  @path = path
26
+ super(message)
24
27
  end
28
+
25
29
  def message
26
30
  "File not mapped in Chance instance: #{path}"
27
31
  end
@@ -48,18 +52,25 @@ module Chance
48
52
  "chance@2x.css" => { :method => :css, :x2 => true },
49
53
  "chance-sprited.css" => { :method => :css, :sprited => true },
50
54
  "chance-sprited@2x.css" => { :method => :css, :sprited => true, :x2 => true },
51
- "chance.js" => { :method => :javascript },
52
- "chance-mhtml.txt" => { :method => :mhtml },
53
55
 
54
56
  # For Testing Purposes...
55
57
  "chance-test.css" => { :method => :chance_test }
56
58
  }
57
59
 
60
+ @@uid = 0
61
+
58
62
  @@generation = 0
59
63
 
60
64
  def initialize(options = {})
61
65
  @options = options
62
66
  @options[:theme] = "" if @options[:theme].nil?
67
+ @options[:pad_sprites_for_debugging] = true if @options[:pad_sprites_for_debugging].nil?
68
+ @options[:optimize_sprites] = true if @options[:optimize_sprites].nil?
69
+
70
+ @@uid += 1
71
+ @uid = @@uid
72
+
73
+ @options[:instance_id] = @uid if @options[:instance_id].nil?
63
74
 
64
75
  if @options[:theme].length > 0 and @options[:theme][0] != "."
65
76
  @options[:theme] = "." + @options[:theme].to_s
@@ -68,6 +79,10 @@ module Chance
68
79
  # The mapped files are a map from file names in the Chance Instance to
69
80
  # their identifiers in Chance itself.
70
81
  @mapped_files = { }
82
+
83
+ # The file mtimes are a collection of mtimes for all the files we have. Each time we
84
+ # read a file we record the mtime, and then we compare on check_all_files
85
+ @file_mtimes = { }
71
86
 
72
87
  # The @files set is a set cached generated output files, used by the output_for
73
88
  # method.
@@ -80,6 +95,10 @@ module Chance
80
95
 
81
96
  # Tracks whether _render has been called.
82
97
  @has_rendered = false
98
+
99
+ # A generation number for the current render. This allows the slicing and spriting
100
+ # to be invalidated smartly.
101
+ @render_cycle = 0
83
102
  end
84
103
 
85
104
  # maps a path relative to the instance to a file identifier
@@ -90,6 +109,11 @@ module Chance
90
109
  # The identifier would be a name of a file that you added to
91
110
  # Chance using add_file.
92
111
  def map_file(path, identifier)
112
+ if @mapped_files[path] == identifier
113
+ # Don't do anything if there is nothing to do.
114
+ return
115
+ end
116
+
93
117
  path = path.to_s
94
118
  file = Chance.has_file(identifier)
95
119
 
@@ -104,12 +128,35 @@ module Chance
104
128
  # unmaps a path from its identifier. In short, removes a file
105
129
  # from this Chance instance. The file will remain in Chance's "virtual filesystem".
106
130
  def unmap_file(path)
131
+ if not @mapped_files.include?(path)
132
+ # Don't do anything if there is nothing to do
133
+ return
134
+ end
135
+
107
136
  path = path.to_s
108
137
  @mapped_files.delete path
109
138
 
110
139
  # Invalidate our render because things have changed.
111
140
  clean
112
141
  end
142
+
143
+ # unmaps all files
144
+ def unmap_all
145
+ @mapped_files = {}
146
+ end
147
+
148
+ # checks all files to see if they have changed
149
+ def check_all_files
150
+ needs_clean = false
151
+ @mapped_files.each {|p, f|
152
+ mtime = Chance.update_file_if_needed(f)
153
+ if @file_mtimes[p].nil? or mtime > @file_mtimes[p]
154
+ needs_clean = true
155
+ end
156
+ }
157
+
158
+ clean if needs_clean
159
+ end
113
160
 
114
161
  # Using a path relative to this instance, gets an actual Chance file
115
162
  # hash, with any necessary preprocessing already performed. For instance,
@@ -178,6 +225,9 @@ module Chance
178
225
  def _render
179
226
  return if @has_rendered
180
227
 
228
+ # Update the render cycle to invalidate sprites, slices, etc.
229
+ @render_cycle = @render_cycle + 1
230
+
181
231
  @files = {}
182
232
  begin
183
233
  # SCSS code executing needs to know what the current instance of Chance is,
@@ -188,13 +238,25 @@ module Chance
188
238
  # The output of this process is a "virtual" file that imports all of the
189
239
  # SCSS files used by this Chance instance. This also sets up the @slices hash.
190
240
  import_css = _preprocess
241
+
242
+ # Because we encapsulate with instance_id, we should not have collisions even IF another chance
243
+ # instance were running at the same time (which it couldn't; if it were, there'd be MANY other issues)
244
+ image_css_path = File.join('./tmp/chance/image_css', @options[:instance_id].to_s, '_image_css.scss')
245
+ FileUtils.mkdir_p(File.dirname(image_css_path))
246
+
247
+ file = File.new(image_css_path, "w")
248
+ file.write(_css_for_slices)
249
+ file.close
250
+
251
+ image_css_path = File.join('./tmp/chance/image_css', @options[:instance_id].to_s, 'image_css')
252
+
191
253
 
192
254
  # STEP 2: Preparing input CSS
193
255
  # The main CSS file we pass to the Sass Engine will have placeholder CSS for the
194
256
  # slices (the details will be postprocessed out).
195
257
  # After that, all of the individual files (using the import CSS generated
196
258
  # in Step 1)
197
- css = _css_for_slices + "\n" + import_css
259
+ css = "@import \"#{image_css_path}\";\n" + import_css
198
260
 
199
261
  # Step 3: Apply Sass Engine
200
262
  engine = Sass::Engine.new(css, Compass.sass_engine_options.merge({
@@ -218,17 +280,24 @@ module Chance
218
280
  # slicing operation has not yet taken place. The postprocessing portion
219
281
  # receives sliced versions.
220
282
  def _css_for_slices
221
- output = ""
283
+
284
+ output = []
222
285
  slices = @slices
223
286
 
224
287
  slices.each do |name, slice|
225
- # so, the path should be the path in the chance instance
226
- output += "." + slice[:css_name] + " { "
227
- output += "_sc_chance: \"#{name}\";"
228
- output += "} \n"
288
+ # Write out comments specifying all the files the slice is used from
289
+ output << "/* Slice #{name}, used in: \n"
290
+ slice[:used_by].each {|used_by|
291
+ output << "\t#{used_by[:path]}\n"
292
+ }
293
+ output << "*/"
294
+
295
+ output << "." + slice[:css_name] + " { "
296
+ output << "_sc_chance: \"#{name}\";"
297
+ output << "} \n"
229
298
  end
230
299
 
231
- return output
300
+ return output.join ""
232
301
 
233
302
  end
234
303
 
@@ -241,16 +310,63 @@ module Chance
241
310
  # :sprited => whether to use spriting instead of data uris.
242
311
  def _postprocess_css(opts)
243
312
  if opts[:sprited]
244
- postprocess_css_sprited(opts)
313
+ ret = postprocess_css_sprited(opts)
245
314
  else
246
- postprocess_css_dataurl(opts)
315
+ ret = postprocess_css_dataurl(opts)
247
316
  end
317
+
318
+ ret = _strip_slice_class_names(ret)
319
+ end
320
+
321
+ #
322
+ # Strips dummy slice class names that were added by Chance so that SCSS could do its magic,
323
+ # but which are no longer needed.
324
+ #
325
+ def _strip_slice_class_names(css)
326
+ css.gsub! /\.__chance_slice[^{]*?,/, ""
327
+ css
248
328
  end
249
329
 
250
330
 
251
331
  #
252
332
  # COMBINING CSS
253
333
  #
334
+
335
+ # Determines the "Chance Header" to add at the beginning of the file. The
336
+ # Chance Header can set, for instance, the $theme variable.
337
+ #
338
+ # The Chance Header is loaded from the nearest _theme.css file in this folder
339
+ # or a containing folder (the file list specifically ignores such files; they are
340
+ # only used for this purpose)
341
+ #
342
+ # For backwards-compatibility, the fallback if no _theme.css file is present
343
+ # is to return code setting $theme to the now-deprecated @options[:theme]
344
+ # passed to Chance
345
+ def chance_header_for_file(file)
346
+ # 'file' is the name of a file, so we actually need to start at dirname(file)
347
+ dir = File.dirname(file)
348
+
349
+ # This should not be slow, as this is just a hash lookup
350
+ while dir.length > 0 and not dir == "."
351
+ header_file = @mapped_files[File.join(dir, "_theme.css")]
352
+ if not header_file.nil?
353
+ return Chance.get_file(header_file)
354
+ end
355
+
356
+ dir = File.dirname(dir)
357
+ end
358
+
359
+ # Make sure to look globally
360
+ header_file = @mapped_files["_theme.css"]
361
+ return Chance.get_file(header_file) if not header_file.nil?
362
+
363
+ {
364
+ # Never changes (without a restart, at least)
365
+ :mtime => 0,
366
+ :content => "$theme: '" + @options[:theme] + "';\n"
367
+ }
368
+ end
369
+
254
370
  #
255
371
  # _include_file is the recursive method in the depth-first-search
256
372
  # that creates the ordered list of files.
@@ -262,6 +378,9 @@ module Chance
262
378
  #
263
379
  def _include_file(file)
264
380
  return if not file =~ /\.css$/
381
+
382
+ # skip _theme.css files
383
+ return if file =~ /_theme\.css$/
265
384
 
266
385
  file = Chance.get_file(file)
267
386
 
@@ -271,7 +390,13 @@ module Chance
271
390
  requires = file[:requires]
272
391
  file[:included] = @@generation
273
392
 
274
- requires.each {|r| _include_file(@mapped_files[r]) } unless requires.nil?
393
+ if not requires.nil?
394
+ requires.each {|r|
395
+ # Add the .css extension if needed. it is optional for sc_require
396
+ r = r + ".css" if not r =~ /\.css$/
397
+ _include_file(@mapped_files[r])
398
+ }
399
+ end
275
400
 
276
401
 
277
402
 
@@ -289,35 +414,55 @@ module Chance
289
414
  @@generation = @@generation + 1
290
415
  files = @mapped_files.values
291
416
  @file_list = []
292
-
293
- files.each {|f| _include_file(f) }
417
+
418
+ # We have to sort alphabetically first...
419
+ tmp_file_list = []
420
+ @mapped_files.each {|p, f| tmp_file_list.push([p, f]) }
421
+ tmp_file_list.sort_by! {|a| a[0] }
422
+
423
+ tmp_file_list.each {|paths|
424
+ p, f = paths
425
+
426
+ # Save the mtime for caching
427
+ mtime = Chance.update_file_if_needed(f)
428
+ @file_mtimes[p] = mtime
429
+
430
+ _include_file(f)
431
+ }
294
432
 
295
433
  relative_paths = @mapped_files.invert
296
434
 
297
435
  @file_list.map {|file|
298
- # The parser accepts single files that contain many files. As such,
299
- # its method of determing the current file name is a marker in the
300
- # file. We may want to consider changing this to a parser option
301
- # now that we don't need this feature so much, but this works for now.
436
+ # NOTE: WE MUST CALL CHANCE PARSER NOW, because it generates our slicses.
437
+ # We can't be picky and just call it if something has changed. Thankfully,
438
+ # parser is fast. Unlike SCSS.
439
+ header_file = chance_header_for_file(relative_paths[file[:path]])
440
+
302
441
  content = "@_chance_file " + relative_paths[file[:path]] + ";\n"
303
- content += "$theme: '" + @options[:theme] + "';"
442
+ content += header_file[:content]
304
443
  content += file[:content]
305
444
 
306
445
  parser = Chance::Parser.new(content, @options)
307
446
  parser.parse
308
447
  file[:parsed_css] = parser.css
448
+
449
+ # We used to use an md5 hash here, but this hides the original file name
450
+ # from SCSS, which makes the file name + line number comments useless.
451
+ #
452
+ # Instead, we sanitize the path.
453
+ path_safe = file[:path].gsub(/[^a-zA-Z0-9\-_\\\/]/, '-')
309
454
 
310
- # Remove any non-letter characters that could cause a problem in filenames,
311
- # i.e. colons in drive names on Windows
312
- # Also removing leading slashes
313
- cleaned_path = file[:path].gsub(/[^\w\/_-]+/,'_').sub(/^\//,'')
314
- tmp_path = "./tmp/chance/#{cleaned_path}.scss"
455
+ tmp_path = "./tmp/chance/#{path_safe}.scss"
315
456
 
316
457
  FileUtils.mkdir_p(File.dirname(tmp_path))
317
-
318
- f = File.new(tmp_path, "w")
319
- f.write(parser.css)
320
- f.close
458
+
459
+ if (not file[:mtime] or not file[:wtime] or file[:wtime] < file[:mtime] or
460
+ not header_file[:mtime] or file[:wtime] < header_file[:mtime])
461
+ f = File.new(tmp_path, "w")
462
+ f.write(parser.css)
463
+ f.close
464
+ file[:wtime] = Time.now.to_f
465
+ end
321
466
 
322
467
  css = "@import \"" + tmp_path + "\";"
323
468