sproutcore 1.6.0.1 → 1.7.1.beta
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/CHANGELOG +21 -0
- data/Gemfile +5 -0
- data/Rakefile +26 -13
- data/VERSION.yml +2 -2
- data/lib/Buildfile +43 -4
- data/lib/buildtasks/build.rake +10 -0
- data/lib/buildtasks/helpers/file_rule.rb +22 -0
- data/lib/buildtasks/helpers/file_rule_list.rb +137 -0
- data/lib/buildtasks/manifest.rake +133 -122
- data/lib/frameworks/sproutcore/CHANGELOG.md +69 -2
- data/lib/frameworks/sproutcore/apps/tests/english.lproj/strings.js +1 -0
- data/lib/frameworks/sproutcore/frameworks/bootstrap/system/browser.js +28 -22
- data/lib/frameworks/sproutcore/frameworks/core_foundation/controllers/array.js +9 -5
- data/lib/frameworks/sproutcore/frameworks/core_foundation/controllers/controller.js +1 -1
- data/lib/frameworks/sproutcore/frameworks/core_foundation/controls/button.js +18 -13
- data/lib/frameworks/sproutcore/frameworks/core_foundation/ext/handlebars/bind.js +5 -3
- data/lib/frameworks/sproutcore/frameworks/core_foundation/ext/handlebars/collection.js +2 -0
- data/lib/frameworks/sproutcore/frameworks/core_foundation/mixins/action_support.js +80 -0
- data/lib/frameworks/sproutcore/frameworks/core_foundation/mixins/template_helpers/text_field_support.js +84 -116
- data/lib/frameworks/sproutcore/frameworks/core_foundation/panes/pane.js +8 -5
- data/lib/frameworks/sproutcore/frameworks/core_foundation/system/event.js +157 -157
- data/lib/frameworks/sproutcore/frameworks/core_foundation/system/platform.js +5 -3
- data/lib/frameworks/sproutcore/frameworks/core_foundation/system/root_responder.js +6 -6
- data/lib/frameworks/sproutcore/frameworks/core_foundation/system/sparse_array.js +10 -7
- data/lib/frameworks/sproutcore/frameworks/core_foundation/tests/mixins/action_support.js +106 -0
- data/lib/frameworks/sproutcore/frameworks/core_foundation/tests/views/template/collection.js +18 -0
- data/lib/frameworks/sproutcore/frameworks/core_foundation/tests/views/template/handlebars.js +71 -1
- data/lib/frameworks/sproutcore/frameworks/core_foundation/tests/views/view/attribute_bindings_test.js +38 -0
- data/lib/frameworks/sproutcore/frameworks/core_foundation/tests/views/view/class_name_bindings_test.js +47 -0
- data/lib/frameworks/sproutcore/frameworks/core_foundation/tests/views/view/layoutChildViews.js +18 -18
- data/lib/frameworks/sproutcore/frameworks/core_foundation/tests/views/view/layoutStyle.js +42 -10
- data/lib/frameworks/sproutcore/frameworks/core_foundation/views/view/keyboard.js +26 -1
- data/lib/frameworks/sproutcore/frameworks/core_foundation/views/view/layout_style.js +14 -8
- data/lib/frameworks/sproutcore/frameworks/core_foundation/views/view.js +158 -1
- data/lib/frameworks/sproutcore/frameworks/datastore/models/record.js +15 -2
- data/lib/frameworks/sproutcore/frameworks/datastore/models/record_attribute.js +108 -108
- data/lib/frameworks/sproutcore/frameworks/datastore/system/query.js +1 -1
- data/lib/frameworks/sproutcore/frameworks/datastore/system/record_array.js +2 -4
- data/lib/frameworks/sproutcore/frameworks/datastore/tests/models/record/error_methods.js +2 -2
- data/lib/frameworks/sproutcore/frameworks/datastore/tests/models/single_attribute.js +26 -0
- data/lib/frameworks/sproutcore/frameworks/datastore/tests/system/query/builders.js +7 -0
- data/lib/frameworks/sproutcore/frameworks/datastore/tests/system/record_array/error_methods.js +1 -1
- data/lib/frameworks/sproutcore/frameworks/datetime/frameworks/core/system/datetime.js +4 -1
- data/lib/frameworks/sproutcore/frameworks/datetime/frameworks/core/tests/system/datetime.js +6 -0
- data/lib/frameworks/sproutcore/frameworks/desktop/panes/menu.js +26 -5
- data/lib/frameworks/sproutcore/frameworks/desktop/panes/picker.js +97 -96
- data/lib/frameworks/sproutcore/frameworks/desktop/system/drag.js +4 -3
- data/lib/frameworks/sproutcore/frameworks/desktop/tests/panes/menu/ui.js +17 -4
- data/lib/frameworks/sproutcore/frameworks/desktop/views/collection.js +7 -7
- data/lib/frameworks/sproutcore/frameworks/desktop/views/menu_item.js +7 -5
- data/lib/frameworks/sproutcore/frameworks/desktop/views/scroll.js +12 -3
- data/lib/frameworks/sproutcore/frameworks/desktop/views/web.js +23 -14
- data/lib/frameworks/sproutcore/frameworks/experimental/Buildfile +5 -1
- data/lib/frameworks/sproutcore/frameworks/experimental/frameworks/menu/render_delegates/menu_scroller.js +28 -0
- data/lib/frameworks/sproutcore/frameworks/experimental/frameworks/menu/tests/menu/scroll.js +235 -0
- data/lib/frameworks/sproutcore/frameworks/experimental/frameworks/menu/views/menu/scroll.js +363 -0
- data/lib/frameworks/sproutcore/frameworks/experimental/frameworks/menu/views/menu/scroller.js +250 -0
- data/lib/frameworks/sproutcore/frameworks/experimental/frameworks/scroll_view/render_delegates/desktop_scroller.js +92 -0
- data/lib/frameworks/sproutcore/frameworks/experimental/frameworks/scroll_view/render_delegates/native_scroll.js +25 -0
- data/lib/frameworks/sproutcore/frameworks/experimental/frameworks/scroll_view/render_delegates/scroll.js +33 -0
- data/lib/frameworks/sproutcore/frameworks/experimental/frameworks/scroll_view/render_delegates/touch_scroller.js +76 -0
- data/lib/frameworks/sproutcore/frameworks/experimental/frameworks/scroll_view/tests/scroll/integration.js +50 -0
- data/lib/frameworks/sproutcore/frameworks/experimental/frameworks/scroll_view/tests/scroll/methods.js +143 -0
- data/lib/frameworks/sproutcore/frameworks/experimental/frameworks/scroll_view/tests/scroll/ui.js +258 -0
- data/lib/frameworks/sproutcore/frameworks/experimental/frameworks/scroll_view/views/core_scroll.js +1164 -0
- data/lib/frameworks/sproutcore/frameworks/experimental/frameworks/scroll_view/views/core_scroller.js +332 -0
- data/lib/frameworks/sproutcore/frameworks/experimental/frameworks/scroll_view/views/desktop/scroll.js +236 -0
- data/lib/frameworks/sproutcore/frameworks/experimental/frameworks/scroll_view/views/desktop/scroller.js +347 -0
- data/lib/frameworks/sproutcore/frameworks/experimental/frameworks/scroll_view/views/scroll.js +15 -0
- data/lib/frameworks/sproutcore/frameworks/experimental/frameworks/scroll_view/views/scroller.js +10 -0
- data/lib/frameworks/sproutcore/frameworks/experimental/frameworks/scroll_view/views/touch/scroll.js +804 -0
- data/lib/frameworks/sproutcore/frameworks/experimental/frameworks/scroll_view/views/touch/scroller.js +133 -0
- data/lib/frameworks/sproutcore/frameworks/foundation/resources/text_field.css +3 -3
- data/lib/frameworks/sproutcore/frameworks/foundation/validators/number.js +3 -1
- data/lib/frameworks/sproutcore/frameworks/foundation/views/text_field.js +3 -3
- data/lib/frameworks/sproutcore/frameworks/media/views/audio.js +2 -1
- data/lib/frameworks/sproutcore/frameworks/media/views/controls.js +2 -1
- data/lib/frameworks/sproutcore/frameworks/media/views/media_slider.js +2 -4
- data/lib/frameworks/sproutcore/frameworks/media/views/mini_controls.js +2 -4
- data/lib/frameworks/sproutcore/frameworks/media/views/simple_controls.js +2 -4
- data/lib/frameworks/sproutcore/frameworks/media/views/video.js +2 -2
- data/lib/frameworks/sproutcore/frameworks/routing/system/routes.js +29 -3
- data/lib/frameworks/sproutcore/frameworks/runtime/core.js +2 -2
- data/lib/frameworks/sproutcore/frameworks/runtime/debug/test_suites/array/replace.js +1 -1
- data/lib/frameworks/sproutcore/frameworks/runtime/private/property_chain.js +2 -1
- data/lib/frameworks/sproutcore/frameworks/runtime/system/binding.js +3 -3
- data/lib/frameworks/sproutcore/frameworks/runtime/system/index_set.js +2 -2
- data/lib/frameworks/sproutcore/frameworks/runtime/system/object.js +1 -1
- data/lib/frameworks/sproutcore/themes/ace/resources/collection/normal/list_item.css +2 -2
- data/lib/frameworks/sproutcore/themes/legacy_theme/english.lproj/segmented.css +1 -1
- data/lib/gen/app/templates/apps/@target_name@/Buildfile +3 -5
- data/lib/gen/app/templates/apps/@target_name@/resources/_theme.css +18 -0
- data/lib/gen/project/templates/@filename@/Buildfile +2 -2
- data/lib/sproutcore/builders/chance_file.rb +9 -16
- data/lib/sproutcore/builders/html.rb +2 -1
- data/lib/sproutcore/builders/minify.rb +4 -35
- data/lib/sproutcore/builders/module.rb +38 -1
- data/lib/sproutcore/builders/split.rb +63 -0
- data/lib/sproutcore/builders/strings.rb +7 -1
- data/lib/sproutcore/builders.rb +1 -0
- data/lib/sproutcore/helpers/css_split.rb +190 -0
- data/lib/sproutcore/helpers/entry_sorter.rb +2 -0
- data/lib/sproutcore/helpers/minifier.rb +40 -16
- data/lib/sproutcore/helpers/static_helper.rb +35 -17
- data/lib/sproutcore/helpers.rb +1 -1
- data/lib/sproutcore/models/manifest.rb +26 -0
- data/lib/sproutcore/models/target.rb +12 -1
- data/lib/sproutcore/rack/proxy.rb +244 -225
- data/lib/sproutcore/rack/restrict_ip.rb +67 -0
- data/lib/sproutcore/rack/service.rb +8 -2
- data/lib/sproutcore/rack.rb +1 -0
- data/lib/sproutcore/tools/build.rb +91 -43
- data/lib/sproutcore/tools/gen.rb +2 -3
- data/lib/sproutcore/tools/manifest.rb +22 -16
- data/lib/sproutcore/tools/server.rb +21 -0
- data/lib/sproutcore/tools.rb +102 -46
- data/lib/sproutcore.rb +30 -5
- data/spec/buildtasks/helpers/accept_list +22 -0
- data/spec/buildtasks/helpers/accept_list.rb +128 -0
- data/spec/buildtasks/helpers/list.json +11 -0
- data/spec/buildtasks/manifest/prepare_build_tasks/chance_2x_spec.rb +1 -39
- data/spec/buildtasks/manifest/prepare_build_tasks/chance_spec.rb +0 -38
- data/spec/buildtasks/manifest/prepare_build_tasks/combine_spec.rb +4 -4
- data/spec/buildtasks/manifest/prepare_build_tasks/module_spec.rb +2 -2
- data/spec/buildtasks/manifest/prepare_build_tasks/packed_2x_indirect_spec.rb +7 -16
- data/spec/buildtasks/manifest/prepare_build_tasks/packed_2x_spec.rb +7 -17
- data/spec/buildtasks/manifest/prepare_build_tasks/packed_spec.rb +11 -6
- data/spec/fixtures/builder_tests/Buildfile +2 -1
- data/spec/fixtures/builder_tests/apps/module_test/modules/required_module/core.js +0 -0
- data/spec/lib/builders/module_spec.rb +1 -1
- data/spec/spec_helper.rb +1 -0
- data/sproutcore.gemspec +4 -9
- data/vendor/chance/lib/chance/factory.rb +45 -0
- data/vendor/chance/lib/chance/instance/data_url.rb +0 -29
- data/vendor/chance/lib/chance/instance/slicing.rb +57 -4
- data/vendor/chance/lib/chance/instance/spriting.rb +112 -21
- data/vendor/chance/lib/chance/instance.rb +173 -28
- data/vendor/chance/lib/chance/parser.rb +80 -52
- data/vendor/chance/lib/chance.rb +25 -6
- data/vendor/sproutcore/SCCompiler.jar +0 -0
- data/vendor/sproutcore/lib/args4j-2.0.12.jar +0 -0
- data/vendor/sproutcore/lib/yuicompressor-2.4.2.jar +0 -0
- metadata +97 -38
|
@@ -18,12 +18,12 @@ module Chance
|
|
|
18
18
|
# images in the class's @sprites property and updating the individual slices
|
|
19
19
|
# with a :sprite property containing the identifier of the sprite, and offset
|
|
20
20
|
# properties for the offsets within the image.
|
|
21
|
-
def generate_sprite_definitions(opts)
|
|
21
|
+
def generate_sprite_definitions(opts)
|
|
22
22
|
@sprites = {}
|
|
23
23
|
|
|
24
24
|
group_slices_into_sprites(opts)
|
|
25
25
|
@sprites.each do |key, sprite|
|
|
26
|
-
layout_slices_in_sprite sprite
|
|
26
|
+
layout_slices_in_sprite sprite, opts
|
|
27
27
|
end
|
|
28
28
|
end
|
|
29
29
|
|
|
@@ -70,7 +70,7 @@ module Chance
|
|
|
70
70
|
# Determines the name of the sprite for the given slice. The sprite
|
|
71
71
|
# by this name may not exist yet.
|
|
72
72
|
def sprite_name_for_slice(slice, opts)
|
|
73
|
-
if slice[:repeat] == "repeat
|
|
73
|
+
if slice[:repeat] == "repeat"
|
|
74
74
|
return slice[:path] + (opts[:x2] ? "@2x" : "")
|
|
75
75
|
end
|
|
76
76
|
|
|
@@ -79,10 +79,23 @@ module Chance
|
|
|
79
79
|
|
|
80
80
|
# Performs the layout operation, laying either up-to-down, or "
|
|
81
81
|
# (for repeat-y slices) left-to-right.
|
|
82
|
-
def layout_slices_in_sprite(sprite)
|
|
82
|
+
def layout_slices_in_sprite(sprite, opts)
|
|
83
83
|
# The position is the position in the layout direction. In vertical mode
|
|
84
84
|
# (the usual) it is the Y position.
|
|
85
85
|
pos = 0
|
|
86
|
+
|
|
87
|
+
# Adds some padding that will be painted with a pattern so that it is apparent that
|
|
88
|
+
# CSS is wrong.
|
|
89
|
+
# NOTE: though this is only in debug mode, we DO need to make sure it is on a 2px boundary.
|
|
90
|
+
# This makes sure 2x works properly.
|
|
91
|
+
padding = @options[:pad_sprites_for_debugging] ? 2 : 0
|
|
92
|
+
|
|
93
|
+
# The position within a row. It starts at 0 even if we have padding,
|
|
94
|
+
# because we always just add padding when we set the individual x/y pos.
|
|
95
|
+
inset = 0
|
|
96
|
+
|
|
97
|
+
# The length of the row. Length, when layout out vertically (the usual), is the height
|
|
98
|
+
row_length = 0
|
|
86
99
|
|
|
87
100
|
# The size is the current size of the sprite in the non-layout direction;
|
|
88
101
|
# for example, in the usual, vertical mode, the size is the width.
|
|
@@ -94,17 +107,19 @@ module Chance
|
|
|
94
107
|
smallest_size = nil
|
|
95
108
|
|
|
96
109
|
is_horizontal = sprite[:use_horizontal_layout]
|
|
97
|
-
|
|
98
|
-
|
|
110
|
+
|
|
111
|
+
# Figure out slice width/heights. We cannot rely on slicing to do this for us
|
|
112
|
+
# because some images may be being passed through as-is.
|
|
113
|
+
sprite[:slices].each {|slice|
|
|
99
114
|
# We must find a canvas either on the slice (if it was actually sliced),
|
|
100
115
|
# or on the slice's file. Otherwise, we're in big shit.
|
|
101
116
|
canvas = slice[:canvas] || slice[:file][:canvas]
|
|
102
|
-
|
|
117
|
+
|
|
103
118
|
# TODO: MAKE A BETTER ERROR.
|
|
104
119
|
unless canvas
|
|
105
|
-
throw "Could not sprite image " + slice[:path] + "; if it is not a PNG"
|
|
120
|
+
throw "Could not sprite image " + slice[:path] + "; if it is not a PNG, make sure you have rmagick installed"
|
|
106
121
|
end
|
|
107
|
-
|
|
122
|
+
|
|
108
123
|
# RMagick has a different API than ChunkyPNG; we have to detect
|
|
109
124
|
# which one we are using, and use the correct API accordingly.
|
|
110
125
|
if canvas.respond_to?('columns')
|
|
@@ -114,10 +129,10 @@ module Chance
|
|
|
114
129
|
slice_width = canvas.width
|
|
115
130
|
slice_height = canvas.height
|
|
116
131
|
end
|
|
117
|
-
|
|
132
|
+
|
|
118
133
|
slice_length = is_horizontal ? slice_width : slice_height
|
|
119
134
|
slice_size = is_horizontal ? slice_height : slice_width
|
|
120
|
-
|
|
135
|
+
|
|
121
136
|
# When repeating, we must use the least common multiple so that
|
|
122
137
|
# we can ensure the repeat pattern works even with multiple repeat
|
|
123
138
|
# sizes. However, we should take into account how much extra we are
|
|
@@ -128,8 +143,41 @@ module Chance
|
|
|
128
143
|
|
|
129
144
|
size = size.lcm slice_size
|
|
130
145
|
else
|
|
131
|
-
size = [size, slice_size].max
|
|
146
|
+
size = [size, slice_size + padding * 2].max
|
|
147
|
+
end
|
|
148
|
+
|
|
149
|
+
slice[:slice_width] = slice_width.to_i
|
|
150
|
+
slice[:slice_height] = slice_height.to_i
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
# Sort slices from widest/tallest (dependent on is_horizontal) or is_vertical
|
|
154
|
+
# NOTE: This means we are technically sorting reversed
|
|
155
|
+
sprite[:slices].sort! {|a, b|
|
|
156
|
+
# WHY <=> NO WORK?
|
|
157
|
+
if is_horizontal
|
|
158
|
+
b[:slice_height] <=> a[:slice_height]
|
|
159
|
+
else
|
|
160
|
+
b[:slice_width] <=> a[:slice_width]
|
|
161
|
+
end
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
sprite[:slices].each do |slice|
|
|
165
|
+
# We must find a canvas either on the slice (if it was actually sliced),
|
|
166
|
+
# or on the slice's file. Otherwise, we're in big shit.
|
|
167
|
+
canvas = slice[:canvas] || slice[:file][:canvas]
|
|
168
|
+
|
|
169
|
+
slice_width = slice[:slice_width]
|
|
170
|
+
slice_height = slice[:slice_height]
|
|
171
|
+
|
|
172
|
+
slice_length = is_horizontal ? slice_width : slice_height
|
|
173
|
+
slice_size = is_horizontal ? slice_height : slice_width
|
|
174
|
+
|
|
175
|
+
if slice[:repeat] != "no-repeat" or inset + slice_size + padding * 2 > size or not @options[:optimize_sprites]
|
|
176
|
+
pos += row_length
|
|
177
|
+
inset = 0
|
|
178
|
+
row_length = 0
|
|
132
179
|
end
|
|
180
|
+
|
|
133
181
|
|
|
134
182
|
# We have extras for manual tweaking of offsetx/y. We have to make sure there
|
|
135
183
|
# is padding for this (on either side)
|
|
@@ -149,20 +197,44 @@ module Chance
|
|
|
149
197
|
elsif slice[:max_offset_y] > 0 and not is_horizontal
|
|
150
198
|
pos += slice[:max_offset_y]
|
|
151
199
|
end
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
slice[:
|
|
155
|
-
|
|
200
|
+
|
|
201
|
+
slice[:sprite_slice_x] = (is_horizontal ? pos : inset)
|
|
202
|
+
slice[:sprite_slice_y] = (is_horizontal ? inset : pos)
|
|
203
|
+
|
|
204
|
+
# add padding for x, only if it a) doesn't repeat or b) repeats vertically because it has horizontal layout
|
|
205
|
+
if slice[:repeat] == "no-repeat" or slice[:repeat] == "repeat-y"
|
|
206
|
+
slice[:sprite_slice_x] += padding
|
|
207
|
+
end
|
|
208
|
+
|
|
209
|
+
if slice[:repeat] == "no-repeat" or slice[:repeat] == "repeat-x"
|
|
210
|
+
slice[:sprite_slice_y] += padding
|
|
211
|
+
end
|
|
212
|
+
|
|
156
213
|
slice[:sprite_slice_width] = slice_width
|
|
157
214
|
slice[:sprite_slice_height] = slice_height
|
|
158
215
|
|
|
159
|
-
|
|
216
|
+
inset += slice_size + padding * 2
|
|
217
|
+
|
|
218
|
+
# We pad the row length ONLY if it is a repeat-x, repeat-y, or no-repeat image.
|
|
219
|
+
# If it is 'repeat', we do not pad it, because it should be processed raw.
|
|
220
|
+
row_length = [slice_length + (slice[:repeat] != "repeat" ? padding * 2 : 0), row_length].max
|
|
221
|
+
|
|
222
|
+
# In 2X, make sure we are aligned on a 2px grid.
|
|
223
|
+
# We correct this AFTER positioning because we always position on an even grid anyway;
|
|
224
|
+
# we just may leave that even grid if we have an odd-sized image. We do this after positioning
|
|
225
|
+
# so that the next loop knows if there is space.
|
|
226
|
+
if opts[:x2]
|
|
227
|
+
row_length = (row_length.to_f / 2).ceil * 2
|
|
228
|
+
inset = (inset.to_f / 2).ceil * 2
|
|
229
|
+
end
|
|
230
|
+
|
|
160
231
|
end
|
|
232
|
+
pos += row_length
|
|
161
233
|
|
|
162
234
|
# TODO: USE A CONSTANT FOR THIS WARNING
|
|
163
235
|
smallest_size = size if smallest_size == nil
|
|
164
236
|
if size - smallest_size > 10
|
|
165
|
-
puts "WARNING: Used more than 10 extra rows or columns to
|
|
237
|
+
puts "WARNING: Used more than 10 extra rows or columns to accommodate repeating slices."
|
|
166
238
|
puts "Wasted up to " + (pos * size-smallest_size).to_s + " pixels"
|
|
167
239
|
end
|
|
168
240
|
|
|
@@ -177,6 +249,14 @@ module Chance
|
|
|
177
249
|
def generate_sprite(sprite)
|
|
178
250
|
canvas = canvas_for_sprite(sprite)
|
|
179
251
|
sprite[:canvas] = canvas
|
|
252
|
+
|
|
253
|
+
# If we are padding sprites, we should paint the background something really
|
|
254
|
+
# obvious & obnoxious. Say, magenta. That's obnoxious. A nice light purple wouldn't
|
|
255
|
+
# be bad, but magenta... that will stick out like a sore thumb (I hope)
|
|
256
|
+
if @options[:pad_sprites_for_debugging]
|
|
257
|
+
magenta = ChunkyPNG::Color.rgb(255, 0, 255)
|
|
258
|
+
canvas.rect(0, 0, sprite[:width], sprite[:height], magenta, magenta)
|
|
259
|
+
end
|
|
180
260
|
|
|
181
261
|
sprite[:slices].each do |slice|
|
|
182
262
|
x = slice[:sprite_slice_x]
|
|
@@ -221,14 +301,18 @@ module Chance
|
|
|
221
301
|
|
|
222
302
|
# Repeat the pattern to fill the width/height.
|
|
223
303
|
while top < height do
|
|
304
|
+
left = 0
|
|
305
|
+
|
|
224
306
|
while left < width do
|
|
225
|
-
if target.respond_to?(:
|
|
226
|
-
target.
|
|
307
|
+
if target.respond_to?(:replace!)
|
|
308
|
+
target.replace!(source_canvas, left + x, top + y)
|
|
227
309
|
else
|
|
228
310
|
target.composite!(source_canvas, left + x, top + y)
|
|
229
311
|
end
|
|
312
|
+
|
|
230
313
|
left += source_width
|
|
231
314
|
end
|
|
315
|
+
|
|
232
316
|
top += source_height
|
|
233
317
|
end
|
|
234
318
|
|
|
@@ -295,7 +379,14 @@ module Chance
|
|
|
295
379
|
|
|
296
380
|
generate_sprite(sprite) if not sprite[:has_generated]
|
|
297
381
|
|
|
298
|
-
sprite[:canvas].to_blob
|
|
382
|
+
ret = sprite[:canvas].to_blob
|
|
383
|
+
|
|
384
|
+
if Chance.clear_files_immediately
|
|
385
|
+
sprite[:canvas] = nil
|
|
386
|
+
sprite[:has_generated] = false
|
|
387
|
+
end
|
|
388
|
+
|
|
389
|
+
ret
|
|
299
390
|
end
|
|
300
391
|
|
|
301
392
|
def sprite_names(opts={})
|
|
@@ -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 =
|
|
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
|
-
|
|
283
|
+
|
|
284
|
+
output = []
|
|
222
285
|
slices = @slices
|
|
223
286
|
|
|
224
287
|
slices.each do |name, slice|
|
|
225
|
-
#
|
|
226
|
-
output
|
|
227
|
-
|
|
228
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
#
|
|
299
|
-
#
|
|
300
|
-
#
|
|
301
|
-
|
|
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 +=
|
|
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
|
-
|
|
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
|
-
|
|
319
|
-
|
|
320
|
-
|
|
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
|
|