compass-core 1.0.0.alpha.13

Sign up to get free protection for your applications and to get access to all the features.
Files changed (141) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE.txt +22 -0
  3. data/VERSION +1 -0
  4. data/data/caniuse.json +1 -0
  5. data/data/caniuse_extras/css-placeholder.json +171 -0
  6. data/lib/compass-core.rb +1 -0
  7. data/lib/compass/browser_support.rb +62 -0
  8. data/lib/compass/configuration.rb +168 -0
  9. data/lib/compass/configuration/data.rb +178 -0
  10. data/lib/compass/configuration/defaults.rb +197 -0
  11. data/lib/compass/configuration/inheritance.rb +304 -0
  12. data/lib/compass/configuration/paths.rb +19 -0
  13. data/lib/compass/core.rb +64 -0
  14. data/lib/compass/core/caniuse.rb +282 -0
  15. data/lib/compass/core/sass_extensions.rb +10 -0
  16. data/lib/compass/core/sass_extensions/functions.rb +39 -0
  17. data/lib/compass/core/sass_extensions/functions/colors.rb +67 -0
  18. data/lib/compass/core/sass_extensions/functions/configuration.rb +162 -0
  19. data/lib/compass/core/sass_extensions/functions/constants.rb +74 -0
  20. data/lib/compass/core/sass_extensions/functions/cross_browser_support.rb +269 -0
  21. data/lib/compass/core/sass_extensions/functions/display.rb +32 -0
  22. data/lib/compass/core/sass_extensions/functions/enumerate.rb +7 -0
  23. data/lib/compass/core/sass_extensions/functions/env.rb +60 -0
  24. data/lib/compass/core/sass_extensions/functions/font_files.rb +41 -0
  25. data/lib/compass/core/sass_extensions/functions/gradient_support.rb +616 -0
  26. data/lib/compass/core/sass_extensions/functions/image_size.rb +117 -0
  27. data/lib/compass/core/sass_extensions/functions/inline_image.rb +64 -0
  28. data/lib/compass/core/sass_extensions/functions/lists.rb +101 -0
  29. data/lib/compass/core/sass_extensions/functions/math.rb +92 -0
  30. data/lib/compass/core/sass_extensions/functions/selectors.rb +64 -0
  31. data/lib/compass/core/sass_extensions/functions/urls.rb +297 -0
  32. data/lib/compass/core/sass_extensions/monkey_patches.rb +3 -0
  33. data/lib/compass/core/sass_extensions/monkey_patches/browser_support.rb +118 -0
  34. data/lib/compass/core/sass_extensions/monkey_patches/traversal.rb +23 -0
  35. data/lib/compass/core/version.rb +5 -0
  36. data/lib/compass/error.rb +5 -0
  37. data/stylesheets/_compass.scss +3 -0
  38. data/stylesheets/_lemonade.scss +38 -0
  39. data/stylesheets/compass/_configuration.scss +54 -0
  40. data/stylesheets/compass/_css3.scss +21 -0
  41. data/stylesheets/compass/_layout.scss +3 -0
  42. data/stylesheets/compass/_reset-legacy.scss +3 -0
  43. data/stylesheets/compass/_reset.scss +3 -0
  44. data/stylesheets/compass/_support.scss +441 -0
  45. data/stylesheets/compass/_typography.scss +4 -0
  46. data/stylesheets/compass/_utilities.scss +9 -0
  47. data/stylesheets/compass/css3/_animation.scss +122 -0
  48. data/stylesheets/compass/css3/_appearance.scss +17 -0
  49. data/stylesheets/compass/css3/_background-clip.scss +35 -0
  50. data/stylesheets/compass/css3/_background-origin.scss +37 -0
  51. data/stylesheets/compass/css3/_background-size.scss +19 -0
  52. data/stylesheets/compass/css3/_border-radius.scss +107 -0
  53. data/stylesheets/compass/css3/_box-shadow.scss +88 -0
  54. data/stylesheets/compass/css3/_box-sizing.scss +15 -0
  55. data/stylesheets/compass/css3/_box.scss +85 -0
  56. data/stylesheets/compass/css3/_columns.scss +210 -0
  57. data/stylesheets/compass/css3/_deprecated-support.scss +272 -0
  58. data/stylesheets/compass/css3/_filter.scss +50 -0
  59. data/stylesheets/compass/css3/_flexbox.scss +156 -0
  60. data/stylesheets/compass/css3/_font-face.scss +48 -0
  61. data/stylesheets/compass/css3/_hyphenation.scss +71 -0
  62. data/stylesheets/compass/css3/_images.scss +139 -0
  63. data/stylesheets/compass/css3/_inline-block.scss +31 -0
  64. data/stylesheets/compass/css3/_opacity.scss +23 -0
  65. data/stylesheets/compass/css3/_pie.scss +1 -0
  66. data/stylesheets/compass/css3/_regions.scss +27 -0
  67. data/stylesheets/compass/css3/_selection.scss +59 -0
  68. data/stylesheets/compass/css3/_shared.scss +5 -0
  69. data/stylesheets/compass/css3/_text-shadow.scss +82 -0
  70. data/stylesheets/compass/css3/_transform.scss +590 -0
  71. data/stylesheets/compass/css3/_transition.scss +171 -0
  72. data/stylesheets/compass/css3/_user-interface.scss +71 -0
  73. data/stylesheets/compass/layout/_grid-background.scss +178 -0
  74. data/stylesheets/compass/layout/_sticky-footer.scss +23 -0
  75. data/stylesheets/compass/layout/_stretching.scss +24 -0
  76. data/stylesheets/compass/reset/_utilities-legacy.scss +135 -0
  77. data/stylesheets/compass/reset/_utilities.scss +142 -0
  78. data/stylesheets/compass/typography/_links.scss +3 -0
  79. data/stylesheets/compass/typography/_lists.scss +4 -0
  80. data/stylesheets/compass/typography/_text.scss +4 -0
  81. data/stylesheets/compass/typography/_units.scss +152 -0
  82. data/stylesheets/compass/typography/_vertical_rhythm.scss +300 -0
  83. data/stylesheets/compass/typography/links/_hover-link.scss +5 -0
  84. data/stylesheets/compass/typography/links/_link-colors.scss +28 -0
  85. data/stylesheets/compass/typography/links/_unstyled-link.scss +7 -0
  86. data/stylesheets/compass/typography/lists/_bullets.scss +34 -0
  87. data/stylesheets/compass/typography/lists/_horizontal-list.scss +63 -0
  88. data/stylesheets/compass/typography/lists/_inline-block-list.scss +50 -0
  89. data/stylesheets/compass/typography/lists/_inline-list.scss +47 -0
  90. data/stylesheets/compass/typography/text/_ellipsis.scss +25 -0
  91. data/stylesheets/compass/typography/text/_force-wrap.scss +12 -0
  92. data/stylesheets/compass/typography/text/_nowrap.scss +2 -0
  93. data/stylesheets/compass/typography/text/_replacement.scss +68 -0
  94. data/stylesheets/compass/utilities/_color.scss +1 -0
  95. data/stylesheets/compass/utilities/_general.scss +6 -0
  96. data/stylesheets/compass/utilities/_links.scss +5 -0
  97. data/stylesheets/compass/utilities/_lists.scss +6 -0
  98. data/stylesheets/compass/utilities/_print.scss +17 -0
  99. data/stylesheets/compass/utilities/_sass.scss +2 -0
  100. data/stylesheets/compass/utilities/_sprites.scss +2 -0
  101. data/stylesheets/compass/utilities/_tables.scss +3 -0
  102. data/stylesheets/compass/utilities/_text.scss +5 -0
  103. data/stylesheets/compass/utilities/color/_brightness.scss +12 -0
  104. data/stylesheets/compass/utilities/color/_contrast.scss +52 -0
  105. data/stylesheets/compass/utilities/general/_clearfix.scss +44 -0
  106. data/stylesheets/compass/utilities/general/_float.scss +38 -0
  107. data/stylesheets/compass/utilities/general/_hacks.scss +65 -0
  108. data/stylesheets/compass/utilities/general/_min.scss +16 -0
  109. data/stylesheets/compass/utilities/general/_reset.scss +2 -0
  110. data/stylesheets/compass/utilities/general/_tabs.scss +1 -0
  111. data/stylesheets/compass/utilities/general/_tag-cloud.scss +18 -0
  112. data/stylesheets/compass/utilities/links/_hover-link.scss +3 -0
  113. data/stylesheets/compass/utilities/links/_link-colors.scss +3 -0
  114. data/stylesheets/compass/utilities/links/_unstyled-link.scss +3 -0
  115. data/stylesheets/compass/utilities/lists/_bullets.scss +3 -0
  116. data/stylesheets/compass/utilities/lists/_horizontal-list.scss +3 -0
  117. data/stylesheets/compass/utilities/lists/_inline-block-list.scss +3 -0
  118. data/stylesheets/compass/utilities/lists/_inline-list.scss +3 -0
  119. data/stylesheets/compass/utilities/sass/_lists.scss +16 -0
  120. data/stylesheets/compass/utilities/sass/_maps.scss +19 -0
  121. data/stylesheets/compass/utilities/sprites/_base.scss +92 -0
  122. data/stylesheets/compass/utilities/sprites/_sprite-img.scss +81 -0
  123. data/stylesheets/compass/utilities/tables/_alternating-rows-and-columns.scss +22 -0
  124. data/stylesheets/compass/utilities/tables/_borders.scss +38 -0
  125. data/stylesheets/compass/utilities/tables/_scaffolding.scss +9 -0
  126. data/stylesheets/compass/utilities/text/_ellipsis.scss +3 -0
  127. data/stylesheets/compass/utilities/text/_nowrap.scss +3 -0
  128. data/stylesheets/compass/utilities/text/_replacement.scss +3 -0
  129. data/templates/ellipsis/ellipsis.sass +9 -0
  130. data/templates/ellipsis/manifest.rb +27 -0
  131. data/templates/ellipsis/xml/ellipsis.xml +14 -0
  132. data/templates/extension/manifest.rb +26 -0
  133. data/templates/extension/stylesheets/main.sass +1 -0
  134. data/templates/extension/templates/project/manifest.rb +2 -0
  135. data/templates/extension/templates/project/screen.sass +2 -0
  136. data/templates/project/USAGE.markdown +32 -0
  137. data/templates/project/ie.sass +6 -0
  138. data/templates/project/manifest.rb +4 -0
  139. data/templates/project/print.sass +6 -0
  140. data/templates/project/screen.sass +7 -0
  141. metadata +241 -0
@@ -0,0 +1,117 @@
1
+ module Compass::Core::SassExtensions::Functions::ImageSize
2
+ KNOWN_TYPES = %w(png gif jpg jpeg)
3
+
4
+ # Returns the width of the image relative to the images directory
5
+ def image_width(image_file)
6
+ width, _ = image_dimensions(image_file)
7
+ Sass::Script::Number.new(width,["px"])
8
+ end
9
+
10
+ # Returns the height of the image relative to the images directory
11
+ def image_height(image_file)
12
+ _, height = image_dimensions(image_file)
13
+ Sass::Script::Number.new(height, ["px"])
14
+ end
15
+
16
+ class ImageProperties
17
+ def initialize(file)
18
+ @file = (file.respond_to?(:to_path) ? file.to_path : file)
19
+ @file_type = File.extname(@file)[1..-1].downcase
20
+ unless KNOWN_TYPES.include?(@file_type)
21
+ raise Sass::SyntaxError, "Unrecognized file type: #{@file_type}"
22
+ end
23
+ end
24
+
25
+ def size
26
+ @dimensions ||= send(:"get_size_for_#{@file_type}")
27
+ end
28
+
29
+ private
30
+ def get_size_for_png
31
+ File.open(@file, "rb") {|io| io.read}[0x10..0x18].unpack('NN')
32
+ end
33
+
34
+ def get_size_for_gif
35
+ File.open(@file, "rb") {|io| io.read}[6..10].unpack('SS')
36
+ end
37
+
38
+ def get_size_for_jpg
39
+ get_size_for_jpeg
40
+ end
41
+
42
+ def get_size_for_jpeg
43
+ jpeg = JPEG.new(@file)
44
+ [jpeg.width, jpeg.height]
45
+ end
46
+ end
47
+
48
+ private
49
+
50
+ def image_dimensions(image_file)
51
+ image_file = image_file.respond_to?(:value) ? image_file.value : image_file
52
+ options[:compass] ||= {}
53
+ options[:compass][:image_dimensions] ||= {}
54
+ options[:compass][:image_dimensions][image_file] = ImageProperties.new(image_path_for_size(image_file)).size
55
+ end
56
+
57
+ def image_path_for_size(image_file)
58
+ if File.exists?(image_file)
59
+ return image_file
60
+ end
61
+ real_path(image_file)
62
+ end
63
+
64
+ def real_path(image_file)
65
+ # Compute the real path to the image on the file stystem if the images_dir is set.
66
+ if Compass.configuration.images_path
67
+ File.join(Compass.configuration.images_path, image_file)
68
+ else
69
+ File.join(Compass.configuration.project_path, image_file)
70
+ end
71
+ end
72
+
73
+ class JPEG
74
+ attr_reader :width, :height, :bits
75
+
76
+ def initialize(file)
77
+ if file.kind_of? IO
78
+ examine(file)
79
+ else
80
+ File.open(file, 'rb') { |io| examine(io) }
81
+ end
82
+ end
83
+
84
+ private
85
+ def examine(io)
86
+ class << io
87
+ unless method_defined?(:readbyte)
88
+ def readbyte
89
+ getc
90
+ end
91
+ end
92
+ def readint; (readbyte << 8) + readbyte; end
93
+ def readframe; read(readint - 2); end
94
+ def readsof; [readint, readbyte, readint, readint, readbyte]; end
95
+ def next
96
+ c = readbyte while c != 0xFF
97
+ c = readbyte while c == 0xFF
98
+ c
99
+ end
100
+ end
101
+
102
+ raise 'malformed JPEG!' unless io.readbyte == 0xFF && io.readbyte == 0xD8 # SOI
103
+
104
+ while marker = io.next
105
+ case marker
106
+ when 0xC0..0xC3, 0xC5..0xC7, 0xC9..0xCB, 0xCD..0xCF # SOF markers
107
+ length, @bits, @height, @width, components = io.readsof
108
+ raise 'malformed JPEG' unless length == 8 + components * 3
109
+ when 0xD9, 0xDA then break # EOI, SOS
110
+ when 0xFE then @comment = io.readframe # COM
111
+ when 0xE1 then io.readframe # APP1, contains EXIF tag
112
+ else io.readframe # ignore frame
113
+ end
114
+ end
115
+ end
116
+ end
117
+ end
@@ -0,0 +1,64 @@
1
+ module Compass::Core::SassExtensions::Functions::InlineImage
2
+
3
+ def inline_image(path, mime_type = nil)
4
+ path = path.value
5
+ real_path = File.join(Compass.configuration.images_path, path)
6
+ inline_image_string(data(real_path), compute_mime_type(path, mime_type))
7
+ end
8
+
9
+ def inline_font_files(*args)
10
+ raise Sass::SyntaxError, "An even number of arguments must be passed to font_files()" unless args.size % 2 == 0
11
+ files = []
12
+ while args.size > 0
13
+ path = args.shift.value
14
+ real_path = File.join(Compass.configuration.fonts_path, path)
15
+ url = inline_image_string(data(real_path), compute_mime_type(path))
16
+ files << "#{url} format('#{args.shift}')"
17
+ end
18
+ Sass::Script::String.new(files.join(", "))
19
+ end
20
+
21
+ protected
22
+ def inline_image_string(data, mime_type)
23
+ data = [data].flatten.pack('m').gsub("\n","")
24
+ url = "url('data:#{mime_type};base64,#{data}')"
25
+ Sass::Script::String.new(url)
26
+ end
27
+
28
+ private
29
+ def compute_mime_type(path, mime_type = nil)
30
+ return mime_type if mime_type
31
+ case path
32
+ when /\.png$/i
33
+ 'image/png'
34
+ when /\.jpe?g$/i
35
+ 'image/jpeg'
36
+ when /\.gif$/i
37
+ 'image/gif'
38
+ when /\.svg$/i
39
+ 'image/svg+xml'
40
+ when /\.otf$/i
41
+ 'font/opentype'
42
+ when /\.eot$/i
43
+ 'application/vnd.ms-fontobject'
44
+ when /\.ttf$/i
45
+ 'font/truetype'
46
+ when /\.woff$/i
47
+ 'application/x-font-woff'
48
+ when /\.off$/i
49
+ 'font/openfont'
50
+ when /\.([a-zA-Z]+)$/
51
+ "image/#{Regexp.last_match(1).downcase}"
52
+ else
53
+ raise Compass::Error, "A mime type could not be determined for #{path}, please specify one explicitly."
54
+ end
55
+ end
56
+
57
+ def data(real_path)
58
+ if File.readable?(real_path)
59
+ File.open(real_path, "rb") {|io| io.read}
60
+ else
61
+ raise Compass::Error, "File not found or cannot be read: #{real_path}"
62
+ end
63
+ end
64
+ end
@@ -0,0 +1,101 @@
1
+ module Compass::Core::SassExtensions::Functions::Lists
2
+
3
+ # Returns true when the object is false, an empty string, or an empty list
4
+ def blank(obj)
5
+ case obj
6
+ when Sass::Script::Bool
7
+ Sass::Script::Bool.new !obj.to_bool
8
+ when Sass::Script::String
9
+ Sass::Script::Bool.new obj.value.strip.size == 0
10
+ when Sass::Script::List
11
+ Sass::Script::Bool.new obj.value.size == 0 || obj.value.all?{|el| blank(el).to_bool}
12
+ else
13
+ Sass::Script::Bool.new false
14
+ end
15
+ end
16
+
17
+ # Returns a new list after removing any non-true values
18
+ def compact(*args)
19
+ sep = :comma
20
+ if args.size == 1 && args.first.is_a?(Sass::Script::List)
21
+ list = args.first
22
+ args = list.value
23
+ sep = list.separator
24
+ end
25
+ Sass::Script::List.new(args.reject{|a| !a.to_bool}, sep)
26
+ end
27
+
28
+ # Get the nth value from a list
29
+ def _compass_nth(list, place)
30
+ assert_type list, :List
31
+ if place.value == "first"
32
+ list.value.first
33
+ elsif place.value == "last"
34
+ list.value.last
35
+ else
36
+ list.value[place.value - 1]
37
+ end
38
+ end
39
+
40
+ # Returns a list object from a value that was passed.
41
+ # This can be used to unpack a space separated list that got turned
42
+ # into a string by sass before it was passed to a mixin.
43
+ def _compass_list(arg)
44
+ if arg.is_a?(Sass::Script::List)
45
+ Sass::Script::List.new(arg.value.dup, arg.separator)
46
+ else
47
+ Sass::Script::List.new([arg], :space)
48
+ end
49
+ end
50
+
51
+ # If the argument is a list, it will return a new list that is space delimited
52
+ # Otherwise it returns a new, single element, space-delimited list.
53
+ def _compass_space_list(list)
54
+ if list.is_a?(Sass::Script::List)
55
+ Sass::Script::List.new(list.value.dup, :space)
56
+ else
57
+ Sass::Script::List.new([list], :space)
58
+ end
59
+ end
60
+
61
+ # Returns the size of the list.
62
+ def _compass_list_size(list)
63
+ assert_list list
64
+ Sass::Script::Number.new(list.value.size)
65
+ end
66
+
67
+ # slice a sublist from a list
68
+ def _compass_slice(list, start_index, end_index = nil)
69
+ end_index ||= Sass::Script::Number.new(-1)
70
+ start_index = start_index.value
71
+ end_index = end_index.value
72
+ start_index -= 1 unless start_index < 0
73
+ end_index -= 1 unless end_index < 0
74
+ Sass::Script::List.new list.values[start_index..end_index], list.separator
75
+ end
76
+
77
+ # removes the given values from the list.
78
+ def reject(list, *values)
79
+ Sass::Script::List.new(list.value.reject{|v| values.any?{|o| v == o}}, list.separator)
80
+ end
81
+
82
+ # returns the first value of a space delimited list.
83
+ def first_value_of(list)
84
+ if list.is_a?(Sass::Script::String)
85
+ Sass::Script::String.new(list.value.split(/\s+/).first)
86
+ elsif defined?(Sass::Script::List) && list.is_a?(Sass::Script::List)
87
+ list.value.first
88
+ else
89
+ list
90
+ end
91
+ end
92
+
93
+ protected
94
+
95
+ def assert_list(value)
96
+ unless value.is_a?(Sass::Script::List)
97
+ raise ArgumentError.new("#{value.inspect} is not a list")
98
+ end
99
+ end
100
+
101
+ end
@@ -0,0 +1,92 @@
1
+ module Compass::Core::SassExtensions::Functions::Math
2
+ extend Compass::Core::SassExtensions::Functions::SassDeclarationHelper
3
+
4
+ PI = Sass::Script::Number.new(Math::PI)
5
+ def pi()
6
+ PI
7
+ end
8
+ declare :pi, []
9
+
10
+ def random(*args)
11
+ value = if args.length == 1
12
+ rand(args.pop.value)
13
+ else
14
+ range = (args.first.value..args.last.value).to_a
15
+ range[rand(range.length)]
16
+ end
17
+ Sass::Script::Number.new(value)
18
+ end
19
+ declare :random, [:limit]
20
+ declare :random, [:start, :limit]
21
+
22
+ def sin(number)
23
+ trig(:sin, number)
24
+ end
25
+ declare :sin, [:number]
26
+
27
+ def asin(number)
28
+ trig(:asin, number)
29
+ end
30
+ declare :asin, [:number]
31
+
32
+ def cos(number)
33
+ trig(:cos, number)
34
+ end
35
+ declare :cos, [:number]
36
+
37
+ def acos(number)
38
+ trig(:acos, number)
39
+ end
40
+ declare :acos, [:number]
41
+
42
+ def tan(number)
43
+ trig(:tan, number)
44
+ end
45
+ declare :tan, [:number]
46
+
47
+ def atan(number)
48
+ trig(:atan, number)
49
+ end
50
+ declare :atan, [:number]
51
+
52
+ def e
53
+ Sass::Script::Number.new(Math::E)
54
+ end
55
+ declare :e, []
56
+
57
+ def logarithm(number, base = e )
58
+ assert_type number, :Number
59
+ assert_type base, :Number
60
+ raise Sass::SyntaxError, "base to logarithm must be unitless." unless base.unitless?
61
+
62
+ result = Math.log(number.value, base.value) rescue Math.log(number.value) / Math.log(base.value)
63
+ Sass::Script::Number.new(result, number.numerator_units, number.denominator_units)
64
+ end
65
+ declare :logarithm, [:number]
66
+ declare :logarithm, [:number, :base]
67
+
68
+ def sqrt(number)
69
+ numeric_transformation(number) { |n| Math.sqrt(n) }
70
+ end
71
+ declare :sqrt, [:number]
72
+
73
+ alias square_root sqrt
74
+ declare :square_root, [:number]
75
+
76
+ def pow(number, exponent)
77
+ assert_type number, :Number
78
+ assert_type exponent, :Number
79
+ raise Sass::SyntaxError, "exponent to pow must be unitless." unless exponent.unitless?
80
+ Sass::Script::Number.new(number.value**exponent.value, number.numerator_units, number.denominator_units)
81
+ end
82
+ declare :pow, [:number, :exponent]
83
+
84
+ private
85
+ def trig(operation, number)
86
+ if number.numerator_units == ["deg"] && number.denominator_units == []
87
+ Sass::Script::Number.new(Math.send(operation, Math::PI * number.value / 180))
88
+ else
89
+ Sass::Script::Number.new(Math.send(operation, number.value), number.numerator_units, number.denominator_units)
90
+ end
91
+ end
92
+ end
@@ -0,0 +1,64 @@
1
+ module Compass::Core::SassExtensions::Functions::Selectors
2
+ COMMA_SEPARATOR = /\s*,\s*/
3
+
4
+ # Permute multiple selectors each of which may be comma delimited, the end result is
5
+ # a new selector that is the equivalent of nesting each under the previous selector.
6
+ # To illustrate, the following mixins are equivalent:
7
+ # =mixin-a(!selector1, !selector2, !selector3)
8
+ # #{!selector1}
9
+ # #{selector2}
10
+ # #{selector3}
11
+ # width: 2px
12
+ # =mixin-b(!selector1, !selector2)
13
+ # #{nest(!selector, !selector2, !selector3)}
14
+ # width: 2px
15
+ def nest(*arguments)
16
+ nested = arguments.map{|a| a.value}.inject do |memo,arg|
17
+ ancestors = memo.split(COMMA_SEPARATOR)
18
+ descendants = arg.split(COMMA_SEPARATOR)
19
+ ancestors.map{|a| descendants.map{|d| "#{a} #{d}"}.join(", ")}.join(", ")
20
+ end
21
+ Sass::Script::String.new(nested)
22
+ end
23
+
24
+ # Permute two selectors, the first may be comma delimited.
25
+ # The end result is a new selector that is the equivalent of nesting the second
26
+ # selector under the first one in a sass file and preceding it with an &.
27
+ # To illustrate, the following mixins are equivalent:
28
+ # =mixin-a(!selector, !to_append)
29
+ # #{!selector}
30
+ # &#{!to_append}
31
+ # width: 2px
32
+ # =mixin-b(!selector, !to_append)
33
+ # #{append_selector(!selector, !to_append)}
34
+ # width: 2px
35
+ def append_selector(selector, to_append)
36
+ ancestors = selector.value.split(COMMA_SEPARATOR)
37
+ descendants = to_append.value.split(COMMA_SEPARATOR)
38
+ nested = ancestors.map{|a| descendants.map{|d| "#{a}#{d}"}.join(", ")}.join(", ")
39
+ Sass::Script::String.new(nested)
40
+ end
41
+
42
+ # Return the header selectors for the levels indicated
43
+ # Defaults to all headers h1 through h6
44
+ # For example:
45
+ # headers(all) => h1, h2, h3, h4, h5, h6
46
+ # headers(4) => h1, h2, h3, h4
47
+ # headers(2,4) => h2, h3, h4
48
+ def headers(from = nil, to = nil)
49
+ if from && !to
50
+ if from.is_a?(Sass::Script::String) && from.value == "all"
51
+ from = Sass::Script::Number.new(1)
52
+ to = Sass::Script::Number.new(6)
53
+ else
54
+ to = from
55
+ from = Sass::Script::Number.new(1)
56
+ end
57
+ else
58
+ from ||= Sass::Script::Number.new(1)
59
+ to ||= Sass::Script::Number.new(6)
60
+ end
61
+ Sass::Script::String.new((from.value..to.value).map{|n| "h#{n}"}.join(", "))
62
+ end
63
+ alias headings headers
64
+ end
@@ -0,0 +1,297 @@
1
+ module Compass::Core::SassExtensions::Functions::Urls
2
+
3
+
4
+ def self.has?(base, instance_method)
5
+ Sass::Util.has?(:instance_method, base, instance_method)
6
+ end
7
+
8
+ def self.included(base)
9
+ base.send(:include, StylesheetUrl) unless has?(base, :stylesheet_url)
10
+ base.send(:include, FontUrl) unless has?(base, :font_url)
11
+ base.send(:include, ImageUrl) unless has?(base, :image_url)
12
+ base.send(:include, GeneratedImageUrl) unless has?(base, :generated_image_url)
13
+ end
14
+
15
+ module StylesheetUrl
16
+ def self.included(base)
17
+ if base.respond_to?(:declare)
18
+ base.declare :stylesheet_url, [:path]
19
+ base.declare :stylesheet_url, [:path, :only_path]
20
+ end
21
+ end
22
+ def stylesheet_url(path, only_path = Sass::Script::Bool.new(false))
23
+ # Compute the path to the stylesheet, either root relative or stylesheet relative
24
+ # or nil if the http_images_path is not set in the configuration.
25
+ http_stylesheets_path = if relative?
26
+ compute_relative_path(Compass.configuration.css_path)
27
+ elsif Compass.configuration.http_stylesheets_path
28
+ Compass.configuration.http_stylesheets_path
29
+ else
30
+ Compass.configuration.http_root_relative(Compass.configuration.css_dir)
31
+ end
32
+
33
+ path = "#{http_stylesheets_path}/#{path.value}"
34
+ if only_path.to_bool
35
+ Sass::Script::String.new(clean_path(path))
36
+ else
37
+ clean_url(path)
38
+ end
39
+ end
40
+ end
41
+
42
+ module FontUrl
43
+ def self.included(base)
44
+ if base.respond_to?(:declare)
45
+ base.declare :font_url, [:path]
46
+ base.declare :font_url, [:path, :only_path]
47
+ base.declare :font_url, [:path, :only_path, :cache_buster]
48
+ end
49
+ end
50
+ def font_url(path, only_path = Sass::Script::Bool.new(false), cache_buster = Sass::Script::Bool.new(false))
51
+ path = path.value # get to the string value of the literal.
52
+
53
+ # Short curcuit if they have provided an absolute url.
54
+ if absolute_path?(path)
55
+ return Sass::Script::String.new("url(#{path})")
56
+ end
57
+
58
+ # Compute the path to the font file, either root relative or stylesheet relative
59
+ # or nil if the http_fonts_path cannot be determined from the configuration.
60
+ http_fonts_path = if relative?
61
+ compute_relative_path(Compass.configuration.fonts_path)
62
+ else
63
+ Compass.configuration.http_fonts_path
64
+ end
65
+
66
+ # Compute the real path to the font on the file system if the fonts_dir is set.
67
+ real_path = if Compass.configuration.fonts_dir
68
+ File.join(Compass.configuration.project_path, Compass.configuration.fonts_dir, path)
69
+ end
70
+
71
+ # prepend the path to the font if there's one
72
+ if http_fonts_path
73
+ http_fonts_path = "#{http_fonts_path}/" unless http_fonts_path[-1..-1] == "/"
74
+ path = "#{http_fonts_path}#{path}"
75
+ end
76
+
77
+ # Compute the asset host unless in relative mode.
78
+ asset_host = if !relative? && Compass.configuration.asset_host
79
+ Compass.configuration.asset_host.call(path)
80
+ end
81
+
82
+ # Compute and append the cache buster if there is one.
83
+ if cache_buster.to_bool
84
+ if cache_buster.is_a?(Sass::Script::String)
85
+ path += "?#{cache_buster.value}"
86
+ else
87
+ path = cache_busted_path(path, real_path)
88
+ end
89
+ end
90
+
91
+ # prepend the asset host if there is one.
92
+ path = "#{asset_host}#{'/' unless path[0..0] == "/"}#{path}" if asset_host
93
+
94
+ if only_path.to_bool
95
+ Sass::Script::String.new(clean_path(path))
96
+ else
97
+ clean_url(path)
98
+ end
99
+ end
100
+ end
101
+
102
+ module ImageUrl
103
+ def self.included(base)
104
+ if base.respond_to?(:declare)
105
+ base.declare :image_url, [:path]
106
+ base.declare :image_url, [:path, :only_path]
107
+ base.declare :image_url, [:path, :only_path, :cache_buster]
108
+ end
109
+ end
110
+ def image_url(path, only_path = Sass::Script::Bool.new(false), cache_buster = Sass::Script::Bool.new(true))
111
+ path = path.value # get to the string value of the literal.
112
+
113
+ if path =~ %r{^#{Regexp.escape(Compass.configuration.http_images_path)}/(.*)}
114
+ # Treat root relative urls (without a protocol) like normal if they start with
115
+ # the images path.
116
+ path = $1
117
+ elsif absolute_path?(path)
118
+ # Short curcuit if they have provided an absolute url.
119
+ return Sass::Script::String.new("url(#{path})")
120
+ end
121
+
122
+ # Compute the path to the image, either root relative or stylesheet relative
123
+ # or nil if the http_images_path is not set in the configuration.
124
+ http_images_path = if relative?
125
+ compute_relative_path(Compass.configuration.images_path)
126
+ elsif Compass.configuration.http_images_path
127
+ Compass.configuration.http_images_path
128
+ else
129
+ Compass.configuration.http_root_relative(Compass.configuration.images_dir)
130
+ end
131
+
132
+ # Compute the real path to the image on the file stystem if the images_dir is set.
133
+ real_path = if Compass.configuration.images_path
134
+ File.join(Compass.configuration.images_path, path)
135
+ else
136
+ File.join(Compass.configuration.project_path, path)
137
+ end
138
+
139
+ # prepend the path to the image if there's one
140
+ if http_images_path
141
+ http_images_path = "#{http_images_path}/" unless http_images_path[-1..-1] == "/"
142
+ path = "#{http_images_path}#{path}"
143
+ end
144
+
145
+ # Compute the asset host unless in relative mode.
146
+ asset_host = if !relative? && Compass.configuration.asset_host
147
+ Compass.configuration.asset_host.call(path)
148
+ end
149
+
150
+ # Compute and append the cache buster if there is one.
151
+ if cache_buster.to_bool
152
+ if cache_buster.is_a?(Sass::Script::String)
153
+ path += "?#{cache_buster.value}"
154
+ else
155
+ path = cache_busted_path(path, real_path)
156
+ end
157
+ end
158
+
159
+ # prepend the asset host if there is one.
160
+ path = "#{asset_host}#{'/' unless path[0..0] == "/"}#{path}" if asset_host
161
+
162
+ if only_path.to_bool
163
+ Sass::Script::String.new(clean_path(path))
164
+ else
165
+ clean_url(path)
166
+ end
167
+ end
168
+ end
169
+
170
+ module GeneratedImageUrl
171
+ def self.included(base)
172
+ if base.respond_to?(:declare)
173
+ base.declare :generated_image_url, [:path]
174
+ base.declare :generated_image_url, [:path, :cache_buster]
175
+ end
176
+ end
177
+ def generated_image_url(path, cache_buster = Sass::Script::Bool.new(false))
178
+ path = path.value # get to the string value of the literal.
179
+
180
+ if path =~ %r{^#{Regexp.escape(Compass.configuration.http_generated_images_path)}/(.*)}
181
+ # Treat root relative urls (without a protocol) like normal if they start with
182
+ # the generated_images path.
183
+ path = $1
184
+ elsif absolute_path?(path)
185
+ # Short curcuit if they have provided an absolute url.
186
+ return Sass::Script::String.new("url(#{path})")
187
+ end
188
+
189
+ # Compute the path to the image, either root relative or stylesheet relative
190
+ # or nil if the http_generated_images_path is not set in the configuration.
191
+ http_generated_images_path = if relative?
192
+ compute_relative_path(Compass.configuration.generated_images_path)
193
+ elsif Compass.configuration.http_generated_images_path
194
+ Compass.configuration.http_generated_images_path
195
+ else
196
+ Compass.configuration.http_root_relative(Compass.configuration.generated_images_dir)
197
+ end
198
+
199
+ # Compute the real path to the image on the file stystem if the generated_images_dir is set.
200
+ real_path = if Compass.configuration.images_path
201
+ File.join(Compass.configuration.images_path, path)
202
+ else
203
+ File.join(Compass.configuration.project_path, path)
204
+ end
205
+
206
+ # prepend the path to the image if there's one
207
+ if http_generated_images_path
208
+ http_generated_images_path = "#{http_generated_images_path}/" unless http_generated_images_path[-1..-1] == "/"
209
+ path = "#{http_generated_images_path}#{path}"
210
+ end
211
+
212
+ # Compute the asset host unless in relative mode.
213
+ asset_host = if !relative? && Compass.configuration.asset_host
214
+ Compass.configuration.asset_host.call(path)
215
+ end
216
+
217
+ # Compute and append the cache buster if there is one.
218
+ if cache_buster.to_bool
219
+ if cache_buster.is_a?(Sass::Script::String)
220
+ path += "?#{cache_buster.value}"
221
+ else
222
+ path = cache_busted_path(path, real_path)
223
+ end
224
+ end
225
+
226
+ # prepend the asset host if there is one.
227
+ path = "#{asset_host}#{'/' unless path[0..0] == "/"}#{path}" if asset_host
228
+
229
+ clean_url(path)
230
+ end
231
+ end
232
+
233
+ private
234
+
235
+ # Emits a path, taking off any leading "./"
236
+ def clean_path(url)
237
+ url = url.to_s
238
+ url = url[0..1] == "./" ? url[2..-1] : url
239
+ end
240
+
241
+ # Emits a url, taking off any leading "./"
242
+ def clean_url(url)
243
+ Sass::Script::String.new("url('#{clean_path(url)}')")
244
+ end
245
+
246
+ def relative?
247
+ Compass.configuration.relative_assets?
248
+ end
249
+
250
+ def absolute_path?(path)
251
+ path[0..0] == "/" || path[0..3] == "http"
252
+ end
253
+
254
+ def compute_relative_path(path)
255
+ if (target_css_file = options[:css_filename])
256
+ Pathname.new(path).relative_path_from(Pathname.new(File.dirname(target_css_file))).to_s
257
+ end
258
+ end
259
+
260
+ def cache_busted_path(path, real_path)
261
+ cache_buster = compute_cache_buster(path, real_path)
262
+ if cache_buster.nil?
263
+ return path
264
+ elsif cache_buster.is_a?(String)
265
+ cache_buster = {:query => cache_buster}
266
+ else
267
+ path = cache_buster[:path] if cache_buster[:path]
268
+ end
269
+
270
+ if cache_buster[:query]
271
+ "%s?%s" % [path, cache_buster[:query]]
272
+ else
273
+ path
274
+ end
275
+ end
276
+
277
+ def compute_cache_buster(path, real_path)
278
+ if Compass.configuration.asset_cache_buster
279
+ args = [path]
280
+ if Compass.configuration.asset_cache_buster.arity > 1
281
+ args << (File.new(real_path) if real_path)
282
+ end
283
+ Compass.configuration.asset_cache_buster.call(*args)
284
+ elsif real_path
285
+ default_cache_buster(path, real_path)
286
+ end
287
+ end
288
+
289
+ def default_cache_buster(path, real_path)
290
+ if File.readable?(real_path)
291
+ File.mtime(real_path).to_i.to_s
292
+ else
293
+ $stderr.puts "WARNING: '#{File.basename(path)}' was not found (or cannot be read) in #{File.dirname(real_path)}"
294
+ end
295
+ end
296
+
297
+ end