archetype 0.0.1.pre.1 → 0.0.1.pre.3.00dfd9a

Sign up to get free protection for your applications and to get access to all the features.
Files changed (185) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +88 -0
  3. data/LICENSE +16 -0
  4. data/README.md +87 -1
  5. data/VERSION.yml +6 -0
  6. data/lib/README.rdoc +4 -0
  7. data/lib/archetype.rb +45 -0
  8. data/lib/archetype/functions.rb +9 -0
  9. data/lib/archetype/functions/hash.rb +149 -0
  10. data/lib/archetype/functions/helpers.rb +125 -0
  11. data/lib/archetype/functions/styleguide_memoizer.rb +61 -0
  12. data/lib/archetype/sass_extensions.rb +6 -0
  13. data/lib/archetype/sass_extensions/functions.rb +14 -0
  14. data/lib/archetype/sass_extensions/functions/environment.rb +14 -0
  15. data/lib/archetype/sass_extensions/functions/lists.rb +284 -0
  16. data/lib/archetype/sass_extensions/functions/locale.rb +77 -0
  17. data/lib/archetype/sass_extensions/functions/numbers.rb +19 -0
  18. data/lib/archetype/sass_extensions/functions/styleguide.rb +406 -0
  19. data/lib/archetype/sass_extensions/functions/ui.rb +59 -0
  20. data/lib/archetype/sass_extensions/functions/version.rb +95 -0
  21. data/lib/archetype/version.rb +75 -0
  22. data/stylesheets/_archetype.scss +2 -0
  23. data/stylesheets/archetype/_base.scss +46 -0
  24. data/stylesheets/archetype/_config.scss +366 -0
  25. data/stylesheets/archetype/_grid.scss +3 -0
  26. data/stylesheets/archetype/_hacks.scss +72 -0
  27. data/stylesheets/archetype/_init.scss +23 -0
  28. data/stylesheets/archetype/_styleguide.scss +6 -0
  29. data/stylesheets/archetype/_ui.scss +326 -0
  30. data/stylesheets/archetype/_util.scss +12 -0
  31. data/stylesheets/archetype/base/_h5bp.scss +307 -0
  32. data/stylesheets/archetype/base/_hybrid.scss +25 -0
  33. data/stylesheets/archetype/base/_normalize.scss +595 -0
  34. data/stylesheets/archetype/base/_reset.scss +72 -0
  35. data/stylesheets/archetype/grid/_config.scss +14 -0
  36. data/stylesheets/archetype/grid/_grid.scss +391 -0
  37. data/stylesheets/archetype/styleguide/_components.scss +25 -0
  38. data/stylesheets/archetype/styleguide/_helpers.scss +215 -0
  39. data/stylesheets/archetype/styleguide/_primitives.scss +10 -0
  40. data/stylesheets/archetype/styleguide/_styleguide.scss +41 -0
  41. data/stylesheets/archetype/styleguide/components/_alerts.scss +59 -0
  42. data/stylesheets/archetype/styleguide/components/_annotations.scss +27 -0
  43. data/stylesheets/archetype/styleguide/components/_bristol.scss +15 -0
  44. data/stylesheets/archetype/styleguide/components/_button_groups.scss +47 -0
  45. data/stylesheets/archetype/styleguide/components/_button_toolbars.scss +17 -0
  46. data/stylesheets/archetype/styleguide/components/_buttons.scss +338 -0
  47. data/stylesheets/archetype/styleguide/components/_canvas.scss +15 -0
  48. data/stylesheets/archetype/styleguide/components/_carets.scss +336 -0
  49. data/stylesheets/archetype/styleguide/components/_closes.scss +63 -0
  50. data/stylesheets/archetype/styleguide/components/_container.scss +27 -0
  51. data/stylesheets/archetype/styleguide/components/_copy.scss +85 -0
  52. data/stylesheets/archetype/styleguide/components/_flyouts.scss +52 -0
  53. data/stylesheets/archetype/styleguide/components/_headings.scss +33 -0
  54. data/stylesheets/archetype/styleguide/components/_headlines.scss +63 -0
  55. data/stylesheets/archetype/styleguide/components/_hovercards.scss +27 -0
  56. data/stylesheets/archetype/styleguide/components/_icons.scss +17 -0
  57. data/stylesheets/archetype/styleguide/components/_identities.scss +34 -0
  58. data/stylesheets/archetype/styleguide/components/_links.scss +66 -0
  59. data/stylesheets/archetype/styleguide/components/_loaders.scss +154 -0
  60. data/stylesheets/archetype/styleguide/components/_menu_items.scss +31 -0
  61. data/stylesheets/archetype/styleguide/components/_module.scss +15 -0
  62. data/stylesheets/archetype/styleguide/components/_pullquotes.scss +29 -0
  63. data/stylesheets/archetype/styleguide/components/_punchcut.scss +18 -0
  64. data/stylesheets/archetype/styleguide/components/_tooltips.scss +28 -0
  65. data/stylesheets/archetype/styleguide/primitives/_animations.scss +17 -0
  66. data/stylesheets/archetype/styleguide/primitives/_dimensions.scss +50 -0
  67. data/stylesheets/archetype/styleguide/primitives/_glyphs.scss +11 -0
  68. data/stylesheets/archetype/styleguide/primitives/_misc.scss +8 -0
  69. data/stylesheets/archetype/styleguide/primitives/_palettes.scss +94 -0
  70. data/stylesheets/archetype/styleguide/primitives/_shadows.scss +23 -0
  71. data/stylesheets/archetype/styleguide/primitives/_sprites.scss +46 -0
  72. data/stylesheets/archetype/styleguide/primitives/_textures.scss +10 -0
  73. data/stylesheets/archetype/styleguide/primitives/_typography.scss +56 -0
  74. data/stylesheets/archetype/util/_debug.scss +40 -0
  75. data/stylesheets/archetype/util/_lists.scss +57 -0
  76. data/stylesheets/archetype/util/_misc.scss +108 -0
  77. data/stylesheets/archetype/util/_rtl.scss +279 -0
  78. data/stylesheets/archetype/util/_spacing.scss +78 -0
  79. data/stylesheets/archetype/util/_styles.scss +466 -0
  80. data/stylesheets/archetype/util/_targeting.scss +210 -0
  81. data/stylesheets/archetype/util/_units.scss +18 -0
  82. data/templates/example/index.html +40 -0
  83. data/templates/example/manifest.rb +13 -0
  84. data/templates/example/screen.scss +99 -0
  85. data/templates/example/vendor/archetype/animations/loaders/large/large.png +0 -0
  86. data/templates/example/vendor/archetype/animations/loaders/large/large_dark.png +0 -0
  87. data/templates/example/vendor/archetype/animations/loaders/large/large_dark_static.png +0 -0
  88. data/templates/example/vendor/archetype/animations/loaders/large/large_static.png +0 -0
  89. data/templates/example/vendor/archetype/animations/loaders/medium/medium.png +0 -0
  90. data/templates/example/vendor/archetype/animations/loaders/medium/medium_dark.png +0 -0
  91. data/templates/example/vendor/archetype/animations/loaders/medium/medium_dark_static.png +0 -0
  92. data/templates/example/vendor/archetype/animations/loaders/medium/medium_static.png +0 -0
  93. data/templates/example/vendor/archetype/animations/loaders/small/small.png +0 -0
  94. data/templates/example/vendor/archetype/animations/loaders/small/small_dark.png +0 -0
  95. data/templates/example/vendor/archetype/animations/loaders/small/small_dark_static.png +0 -0
  96. data/templates/example/vendor/archetype/animations/loaders/small/small_static.png +0 -0
  97. data/templates/example/vendor/archetype/fontawesome-webfont.eot +0 -0
  98. data/templates/example/vendor/archetype/fontawesome-webfont.svg +255 -0
  99. data/templates/example/vendor/archetype/fontawesome-webfont.ttf +0 -0
  100. data/templates/example/vendor/archetype/fontawesome-webfont.woff +0 -0
  101. data/templates/project/manifest.rb +9 -0
  102. data/templates/project/screen.scss +1 -0
  103. data/templates/project/vendor/archetype/animations/loaders/large/large.png +0 -0
  104. data/templates/project/vendor/archetype/animations/loaders/large/large_dark.png +0 -0
  105. data/templates/project/vendor/archetype/animations/loaders/large/large_dark_static.png +0 -0
  106. data/templates/project/vendor/archetype/animations/loaders/large/large_static.png +0 -0
  107. data/templates/project/vendor/archetype/animations/loaders/medium/medium.png +0 -0
  108. data/templates/project/vendor/archetype/animations/loaders/medium/medium_dark.png +0 -0
  109. data/templates/project/vendor/archetype/animations/loaders/medium/medium_dark_static.png +0 -0
  110. data/templates/project/vendor/archetype/animations/loaders/medium/medium_static.png +0 -0
  111. data/templates/project/vendor/archetype/animations/loaders/small/small.png +0 -0
  112. data/templates/project/vendor/archetype/animations/loaders/small/small_dark.png +0 -0
  113. data/templates/project/vendor/archetype/animations/loaders/small/small_dark_static.png +0 -0
  114. data/templates/project/vendor/archetype/animations/loaders/small/small_static.png +0 -0
  115. data/templates/project/vendor/archetype/fontawesome-webfont.eot +0 -0
  116. data/templates/project/vendor/archetype/fontawesome-webfont.svg +255 -0
  117. data/templates/project/vendor/archetype/fontawesome-webfont.ttf +0 -0
  118. data/templates/project/vendor/archetype/fontawesome-webfont.woff +0 -0
  119. data/test/fixtures/stylesheets/archetype/assets/fonts/fontawesome-webfont.eot +0 -0
  120. data/test/fixtures/stylesheets/archetype/assets/fonts/fontawesome-webfont.svg +255 -0
  121. data/test/fixtures/stylesheets/archetype/assets/fonts/fontawesome-webfont.ttf +0 -0
  122. data/test/fixtures/stylesheets/archetype/assets/fonts/fontawesome-webfont.woff +0 -0
  123. data/test/fixtures/stylesheets/archetype/assets/images/vendor/archetype/animations/loaders/large/large.png +0 -0
  124. data/test/fixtures/stylesheets/archetype/assets/images/vendor/archetype/animations/loaders/large/large_dark.png +0 -0
  125. data/test/fixtures/stylesheets/archetype/assets/images/vendor/archetype/animations/loaders/large/large_dark_static.png +0 -0
  126. data/test/fixtures/stylesheets/archetype/assets/images/vendor/archetype/animations/loaders/large/large_static.png +0 -0
  127. data/test/fixtures/stylesheets/archetype/assets/images/vendor/archetype/animations/loaders/medium/medium.png +0 -0
  128. data/test/fixtures/stylesheets/archetype/assets/images/vendor/archetype/animations/loaders/medium/medium_dark.png +0 -0
  129. data/test/fixtures/stylesheets/archetype/assets/images/vendor/archetype/animations/loaders/medium/medium_dark_static.png +0 -0
  130. data/test/fixtures/stylesheets/archetype/assets/images/vendor/archetype/animations/loaders/medium/medium_static.png +0 -0
  131. data/test/fixtures/stylesheets/archetype/assets/images/vendor/archetype/animations/loaders/small/small.png +0 -0
  132. data/test/fixtures/stylesheets/archetype/assets/images/vendor/archetype/animations/loaders/small/small_dark.png +0 -0
  133. data/test/fixtures/stylesheets/archetype/assets/images/vendor/archetype/animations/loaders/small/small_dark_static.png +0 -0
  134. data/test/fixtures/stylesheets/archetype/assets/images/vendor/archetype/animations/loaders/small/small_static.png +0 -0
  135. data/test/fixtures/stylesheets/archetype/assets/images/vendor/archetype/sprites/hovercard_tip.png +0 -0
  136. data/test/fixtures/stylesheets/archetype/config.rb +19 -0
  137. data/test/fixtures/stylesheets/archetype/expected/b.css +14 -0
  138. data/test/fixtures/stylesheets/archetype/expected/base.css +349 -0
  139. data/test/fixtures/stylesheets/archetype/expected/hacks/ie_pseudo.css +11 -0
  140. data/test/fixtures/stylesheets/archetype/expected/locale.css +23 -0
  141. data/test/fixtures/stylesheets/archetype/expected/styleguide/buttons.css +2091 -0
  142. data/test/fixtures/stylesheets/archetype/expected/styleguide/fallback_styles.css +9 -0
  143. data/test/fixtures/stylesheets/archetype/expected/styleguide/nested_styleguides.css +24 -0
  144. data/test/fixtures/stylesheets/archetype/expected/styleguide/selective_state.css +174 -0
  145. data/test/fixtures/stylesheets/archetype/expected/ui/glyph_icon.css +37 -0
  146. data/test/fixtures/stylesheets/archetype/expected/ui/hide_element.css +8 -0
  147. data/test/fixtures/stylesheets/archetype/expected/ui/stroke.css +17 -0
  148. data/test/fixtures/stylesheets/archetype/expected/ui/triangle.css +35 -0
  149. data/test/fixtures/stylesheets/archetype/expected/utilities/associative.css +9 -0
  150. data/test/fixtures/stylesheets/archetype/expected/utilities/if-set.css +9 -0
  151. data/test/fixtures/stylesheets/archetype/expected/utilities/spacing/horizontal-spacing.css +29 -0
  152. data/test/fixtures/stylesheets/archetype/expected/utilities/spacing/vertical-spacing.css +29 -0
  153. data/test/fixtures/stylesheets/archetype/expected/utilities/styles/filter.css +11 -0
  154. data/test/fixtures/stylesheets/archetype/expected/utilities/styles/font-family.css +16 -0
  155. data/test/fixtures/stylesheets/archetype/expected/utilities/styles/z-index.css +15 -0
  156. data/test/fixtures/stylesheets/archetype/expected/utilities/targeting/target-browser.css +100 -0
  157. data/test/fixtures/stylesheets/archetype/expected/utilities/targeting/target-os.css +55 -0
  158. data/test/fixtures/stylesheets/archetype/source/b.scss +9 -0
  159. data/test/fixtures/stylesheets/archetype/source/base.scss +3 -0
  160. data/test/fixtures/stylesheets/archetype/source/hacks/ie_pseudo.scss +13 -0
  161. data/test/fixtures/stylesheets/archetype/source/locale.scss +43 -0
  162. data/test/fixtures/stylesheets/archetype/source/styleguide/buttons.scss +18 -0
  163. data/test/fixtures/stylesheets/archetype/source/styleguide/fallback_styles.scss +22 -0
  164. data/test/fixtures/stylesheets/archetype/source/styleguide/nested_styleguides.scss +40 -0
  165. data/test/fixtures/stylesheets/archetype/source/styleguide/selective_state.scss +22 -0
  166. data/test/fixtures/stylesheets/archetype/source/ui/glyph_icon.scss +13 -0
  167. data/test/fixtures/stylesheets/archetype/source/ui/hide_element.scss +5 -0
  168. data/test/fixtures/stylesheets/archetype/source/ui/stroke.scss +13 -0
  169. data/test/fixtures/stylesheets/archetype/source/ui/triangle.scss +13 -0
  170. data/test/fixtures/stylesheets/archetype/source/utilities/associative.scss +24 -0
  171. data/test/fixtures/stylesheets/archetype/source/utilities/if-set.scss +16 -0
  172. data/test/fixtures/stylesheets/archetype/source/utilities/spacing/horizontal-spacing.scss +27 -0
  173. data/test/fixtures/stylesheets/archetype/source/utilities/spacing/vertical-spacing.scss +27 -0
  174. data/test/fixtures/stylesheets/archetype/source/utilities/styles/filter.scss +9 -0
  175. data/test/fixtures/stylesheets/archetype/source/utilities/styles/font-family.scss +9 -0
  176. data/test/fixtures/stylesheets/archetype/source/utilities/styles/z-index.scss +18 -0
  177. data/test/fixtures/stylesheets/archetype/source/utilities/targeting/target-browser.scss +70 -0
  178. data/test/fixtures/stylesheets/archetype/source/utilities/targeting/target-os.scss +42 -0
  179. data/test/helpers/diff.rb +49 -0
  180. data/test/helpers/io.rb +36 -0
  181. data/test/helpers/test_case.rb +62 -0
  182. data/test/integrations/archetype_test.rb +126 -0
  183. data/test/test_helper.rb +26 -0
  184. data/test/units/sass_extensions_test.rb +207 -0
  185. metadata +303 -15
@@ -0,0 +1,19 @@
1
+ #
2
+ # This module provides a set of Sass functions for working with Sass::Number
3
+ #
4
+ module Archetype::SassExtensions::Numbers
5
+ #
6
+ # remove the units from a number
7
+ #
8
+ # *Parameters*:
9
+ # - <tt>$number</tt> {Number} the number to remove units from
10
+ # *Returns*:
11
+ # - {Number} the number without units
12
+ #
13
+ def strip_units(number)
14
+ value = 0
15
+ value = number.value.to_f if number.is_a?(Sass::Script::String)
16
+ value = number.value if number.is_a?(Sass::Script::Number)
17
+ return Sass::Script::Number.new(value)
18
+ end
19
+ end
@@ -0,0 +1,406 @@
1
+ require 'archetype/functions/helpers'
2
+ require 'archetype/functions/styleguide_memoizer'
3
+ require 'thread'
4
+
5
+ #
6
+ # This is the magic of Archetype. This module provides the interfaces for constructing,
7
+ # extending, and retrieving reusable UI components
8
+ #
9
+ module Archetype::SassExtensions::Styleguide
10
+
11
+ # :stopdoc:
12
+ INHERIT = 'inherit'
13
+ STYLEGUIDE = 'styleguide'
14
+ DROP = 'drop'
15
+ DEFAULT = 'default'
16
+ REGEX = 'regex'
17
+ SPECIAL = %w(states selectors)
18
+ # these are unique CSS keys that can be exploited to provide fallback functionality by providing a second value
19
+ # e.g color: red; color: rgba(255,0,0, 0.8);
20
+ FALLBACKS = %w(background background-image background-color border border-bottom border-bottom-color border-color border-left border-left-color border-right border-right-color border-top border-top-color clip color layer-background-color outline outline-color white-space)
21
+ ADDITIVES = FALLBACKS + [DROP, INHERIT, STYLEGUIDE]
22
+ @@archetype_styleguide_mutex = Mutex.new
23
+ # :startdoc:
24
+
25
+ #
26
+ # interface for adding new components to the styleguide structure
27
+ #
28
+ # *Parameters*:
29
+ # - <tt>$id</tt> {String} the component identifier
30
+ # - <tt>$data</tt> {List} the component data object
31
+ # - <tt>$default</tt> {List} the default data object (for extending)
32
+ # - <tt>$theme</tt> {String} the theme to insert the component into
33
+ # - <tt>$force</tt> {Boolean} if true, forcibly insert the component
34
+ # *Returns*:
35
+ # - {Boolean} whether or not the component was added
36
+ #
37
+ def styleguide_add_component(id, data, default = nil, theme = nil, force = false)
38
+ @@archetype_styleguide_mutex.synchronize do
39
+ theme = get_theme(theme)
40
+ components = theme[:components]
41
+ id = helpers.to_str(id)
42
+ # if force was true, we have to invalidate the memoizer
43
+ memoizer.clear(theme[:name]) if force
44
+ # if we already have the component, don't create it again
45
+ return Sass::Script::Bool.new(false) if component_exists(id, theme, nil, force)
46
+ # otherwise add it
47
+ components[id] = helpers.list_to_hash(default, 1, SPECIAL, ADDITIVES).merge(helpers.list_to_hash(data, 1, SPECIAL, ADDITIVES))
48
+ return Sass::Script::Bool.new(true)
49
+ end
50
+ end
51
+ Sass::Script::Functions.declare :styleguide_add_component, [:id, :data]
52
+ Sass::Script::Functions.declare :styleguide_add_component, [:id, :data, :default]
53
+ Sass::Script::Functions.declare :styleguide_add_component, [:id, :data, :default, :theme]
54
+
55
+ #
56
+ # interface for extending an existing components in the styleguide structure
57
+ #
58
+ # *Parameters*:
59
+ # - <tt>$id</tt> {String} the component identifier
60
+ # - <tt>$data</tt> {List} the component data object
61
+ # - <tt>$theme</tt> {String} the theme to insert the component into
62
+ # - <tt>$extension</tt> {String} the name of the extension
63
+ # - <tt>$force</tt> {Boolean} if true, forcibly extend the component
64
+ # *Returns*:
65
+ # - {Boolean} whether or not the component was extended
66
+ #
67
+ def styleguide_extend_component(id, data, theme = nil, extension = nil, force = false)
68
+ @@archetype_styleguide_mutex.synchronize do
69
+ theme = get_theme(theme)
70
+ components = theme[:components]
71
+ id = helpers.to_str(id)
72
+ # if force was set, we'll create a random token for the name
73
+ extension = rand(36**8).to_s(36) if force
74
+ # convert the extension into a hash (if we don't have an extension, compose one out of its data)
75
+ extension = helpers.to_str(extension || data).hash
76
+ extensions = theme[:extensions]
77
+ return Sass::Script::Bool.new(false) if component_exists(id, theme, extension, force)
78
+ extensions.push(extension)
79
+ components[id] = (components[id] ||= Archetype::Hash.new).rmerge(helpers.list_to_hash(data, 1, SPECIAL, ADDITIVES))
80
+ return Sass::Script::Bool.new(true)
81
+ end
82
+ end
83
+ Sass::Script::Functions.declare :styleguide_extend_component, [:id, :data]
84
+ Sass::Script::Functions.declare :styleguide_extend_component, [:id, :data, :theme]
85
+ Sass::Script::Functions.declare :styleguide_extend_component, [:id, :data, :theme, :extension]
86
+
87
+ #
88
+ # check whether or not a component (or a component extension) has already been defined
89
+ #
90
+ # *Parameters*:
91
+ # - <tt>$id</tt> {String} the component identifier
92
+ # - <tt>$data</tt> {List} the component data object
93
+ # - <tt>$theme</tt> {String} the theme to insert the component into
94
+ # - <tt>$extension</tt> {String} the name of the extension
95
+ # - <tt>$force</tt> {Boolean} if true, forcibly extend the component
96
+ # *Returns*:
97
+ # - {Boolean} whether or not the component/extension exists
98
+ #
99
+ def styleguide_component_exists(id, theme = nil, extension = nil, force = false)
100
+ @@archetype_styleguide_mutex.synchronize do
101
+ extension = helpers.to_str(extension).hash if not extension.nil?
102
+ return Sass::Script::Bool.new( component_exists(id, theme, extension, force) )
103
+ end
104
+ end
105
+ Sass::Script::Functions.declare :styleguide_extend_component, [:id]
106
+ Sass::Script::Functions.declare :styleguide_extend_component, [:id, :theme]
107
+ Sass::Script::Functions.declare :styleguide_extend_component, [:id, :theme, :extension]
108
+ Sass::Script::Functions.declare :styleguide_extend_component, [:id, :theme, :extension, :force]
109
+
110
+ #
111
+ # given a description of the component, convert it into CSS
112
+ #
113
+ # *Parameters*:
114
+ # - <tt>$description</tt> {String|List} the description of the component
115
+ # - <tt>$theme</tt> {String} the theme to use
116
+ # *Returns*:
117
+ # - {List} a key-value paired list of styles
118
+ #
119
+ def styleguide(description, state = 'false', theme = nil)
120
+ @@archetype_styleguide_mutex.synchronize do
121
+ # convert it back to a Sass:List and carry on
122
+ return helpers.hash_to_list(get_styles(description, theme, state), 0)
123
+ end
124
+ end
125
+
126
+ #
127
+ # output the CSS differences between components
128
+ #
129
+ # *Parameters*:
130
+ # - <tt>$original</tt> {String|List} the description of the original component
131
+ # - <tt>$other</tt> {String|List} the description of the new component
132
+ # - <tt>$theme</tt> {String} the theme to use
133
+ # *Returns*:
134
+ # - {List} a key-value paired list of styles
135
+ #
136
+ def styleguide_diff(original, other, theme = nil)
137
+ @@archetype_styleguide_mutex.synchronize do
138
+ original = get_styles(original, theme)
139
+ other = get_styles(other, theme)
140
+ diff = original.diff(other)
141
+ return helpers.hash_to_list(diff, 0)
142
+ end
143
+ end
144
+
145
+ private
146
+ def helpers
147
+ @helpers ||= Archetype::Functions::Helpers
148
+ end
149
+ def memoizer
150
+ Archetype::Functions::StyleguideMemoizer
151
+ end
152
+
153
+ #
154
+ # given a sentence, deconstruct it into it's identifier and verbages
155
+ #
156
+ # *Parameters*:
157
+ # - <tt>sentence</tt> {String|List} the sentence describing the component
158
+ # - <tt>theme</tt> {String} the theme to use
159
+ # - <tt>state</tt> {String} the name of a state to return
160
+ # *Returns*:
161
+ # - {Array} an array containing the identifer, modifiers, and a token
162
+ #
163
+ def grammar(sentence, theme = nil, state = 'false')
164
+ theme = get_theme(theme)
165
+ components = theme[:components]
166
+ # get a list of valid ids
167
+ styleguideIds = components.keys
168
+ sentence = sentence.split if sentence.is_a? String
169
+ sentence = sentence.to_a
170
+ id = nil
171
+ modifiers = []
172
+ if not sentence.empty?
173
+ prefix = ''
174
+ order = ''
175
+ # these define various attributes for modifiers (e.g. `button with a shadow`)
176
+ extras = %w(on with without)
177
+ # these are things that are useless to us, so we just leave them out
178
+ ignore = %w(a an also the this that is was it)
179
+ # these are our context switches (e.g. `headline in a button`)
180
+ contexts = %w(in)
181
+ sentence.each do |item|
182
+ item = item.value
183
+ # find the ID
184
+ if id.nil? and styleguideIds.include?(item) and prefix.empty? and order.empty?
185
+ id = item
186
+ # if it's a `context`, we need to increase the depth and reset the prefix
187
+ elsif contexts.include?(item)
188
+ order = "#{item}-#{order}"
189
+ prefix = ''
190
+ # if it's an `extra`, we update the prefix
191
+ elsif extras.include?(item)
192
+ prefix = "#{item}-"
193
+ # finally, check that it's not on the ignore (useless) list. if it is, we just skip over it
194
+ # (maybe this should be the first thing we check?)
195
+ elsif not ignore.include?(item)
196
+ modifiers.push("#{order}#{prefix}#{item}")
197
+ end
198
+ end
199
+ end
200
+ # if there was no id, return a list of valid IDs for reporting
201
+ modifiers = styleguideIds if id.nil?
202
+ # get the list of currenty installed component extensions
203
+ extensions = theme[:extensions] if not id.nil?
204
+ # TODO - low - eoneill: make sure we always want to return unique modifiers
205
+ # i can't think of a case where we wouldn't want to remove dups
206
+ # maybe in the case where we're looking for strict keys on the lookup?
207
+ modifiers = modifiers.uniq
208
+ token = memoizer.tokenize(theme[:name], extensions, id, modifiers, state)
209
+ return id, modifiers, token
210
+ end
211
+
212
+ #
213
+ # interface for extracting styles in the styleguide references
214
+ #
215
+ # *Parameters*:
216
+ # - <tt>id</tt> {String} the component identifier
217
+ # - <tt>modifiers</tt> {Array} the component modifiers
218
+ # - <tt>strict</tt> {Boolean} is it a strict lookup?
219
+ # - <tt>theme</tt> {String} the theme to use
220
+ # - <tt>context</tt> {Hash} the context to work in
221
+ # *Returns*:
222
+ # - {Hash} a hash of the extracted styles
223
+ #
224
+ def extract_styles(id, modifiers, strict = false, theme = nil, context = nil)
225
+ theme = get_theme(theme)
226
+ context ||= theme[:components][id] || Archetype::Hash.new
227
+ modifiers = helpers.to_str(modifiers)
228
+ return Archetype::Hash.new if context.nil? or context.empty?
229
+ # push on the defaults first
230
+ out = (strict ? resolve_dependents(id, context[modifiers], theme[:name], context) : context[DEFAULT]) || Archetype::Hash.new
231
+ out = out.clone
232
+ # if it's not strict, find anything that matched
233
+ if not strict
234
+ modifiers = modifiers.split
235
+ context.each do |key, definition|
236
+ definition = [key, definition]
237
+ modifier = definition[0]
238
+ if modifier != DEFAULT
239
+ match = true
240
+ modifier = modifier.split
241
+ if modifier[0] == REGEX
242
+ # if it's a regex pattern, test if it matches
243
+ match = modifiers.join(' ') =~ /#{modifier[1].gsub(/\A"|"\Z/, '')}/i
244
+ else
245
+ # otherwise, if the modifier isn't in our list of modifiers, it's not valid and just move on
246
+ modifier.each { |i| match = false if not modifiers.include?(i) }
247
+ end
248
+ # if it matched, process it
249
+ out = out.rmerge(resolve_dependents(id, definition[1], theme[:name], nil, out.keys)) if match
250
+ end
251
+ end
252
+ end
253
+ # recompose the special keys and extract any nested/inherited styles
254
+ # this lets us define special states and elements
255
+ SPECIAL.each do |special_key|
256
+ if out.is_a? Hash
257
+ special = out[special_key]
258
+ tmp = Archetype::Hash.new
259
+ (special || Archetype::Hash.new).each { |key, value| tmp[key] = extract_styles(key, key, true, theme[:name], special) }
260
+ out[special_key] = tmp if not tmp.empty?
261
+ end
262
+ end
263
+ # check for nested styleguides
264
+ styleguide = out[STYLEGUIDE]
265
+ if styleguide and not styleguide.empty?
266
+ styles = get_styles(styleguide, theme[:name])
267
+ out.delete(STYLEGUIDE)
268
+ out = styles.rmerge(out)
269
+ end
270
+ return out
271
+ end
272
+
273
+ #
274
+ # resolve any dependent references from the component
275
+ #
276
+ # *Parameters*:
277
+ # - <tt>id</tt> {String} the component identifier
278
+ # - <tt>value</tt> {Hash} the current value
279
+ # - <tt>theme</tt> {String} the theme to use
280
+ # - <tt>context</tt> {Hash} the context to work in
281
+ # - <tt>keys</tt> {Array} list of the external keys
282
+ # *Returns*:
283
+ # - {Hash} a hash of the resolved styles
284
+ #
285
+ def resolve_dependents(id, value, theme = nil, context = nil, keys = nil)
286
+ # we have to create a clone here as the passed in value is volatile and we're performing destructive changes
287
+ value = value.clone
288
+ # check that we're dealing with a hash
289
+ if value.is_a?(Hash)
290
+ # check for dropped styles
291
+ drop = value[DROP]
292
+ if not drop.nil?
293
+ tmp = Archetype::Hash.new
294
+ if %w(all true).include?(helpers.to_str(drop)) and not keys.nil? and not keys.empty?
295
+ keys.each do |key|
296
+ tmp[key] = 'nil'
297
+ end
298
+ else
299
+ drop = drop.to_a
300
+ drop.each do |key|
301
+ tmp[helpers.to_str(key)] = 'nil'
302
+ end
303
+ end
304
+ value.delete(DROP)
305
+ value = tmp.rmerge(value)
306
+ end
307
+ # check for inheritance
308
+ inherit = value[INHERIT]
309
+ if inherit and not inherit.empty?
310
+ # create a temporary object and extract the nested styles
311
+ tmp = Archetype::Hash.new
312
+ inherit.each { |related| tmp = tmp.rmerge(extract_styles(id, related, true, theme, context)) }
313
+ # remove the inheritance key and update the styles
314
+ value.delete(INHERIT)
315
+ value = tmp.rmerge(value)
316
+ end
317
+ end
318
+ # return whatever we got
319
+ return value
320
+ end
321
+
322
+ #
323
+ # keep a registry of styleguide themes
324
+ #
325
+ # *Parameters*:
326
+ # - <tt>theme</tt> {String} the theme to use
327
+ # *Returns*:
328
+ # - {Hash} the theme
329
+ #
330
+ def get_theme(theme)
331
+ theme_name = helpers.to_str(theme || 'archetype')
332
+ @@styleguide_themes ||= {}
333
+ theme = @@styleguide_themes[theme_name] ||= {}
334
+ theme[:name] ||= theme_name
335
+ theme[:components] ||= {}
336
+ theme[:extensions] ||= []
337
+ return theme
338
+ end
339
+
340
+ #
341
+ # driver method for converting a sentence into a list of styles
342
+ #
343
+ # *Parameters*:
344
+ # - <tt>description</tt> {String|List} the description of the component
345
+ # - <tt>theme</tt> {String} the theme to use
346
+ # - <tt>state</tt> {String} the name of a state to return
347
+ # *Returns*:
348
+ # - {Hash} the styles
349
+ #
350
+ def get_styles(description, theme = nil, state = 'false')
351
+ state = helpers.to_str(state)
352
+ description = description.to_a
353
+ styles = Archetype::Hash.new
354
+ description.each do |sentence|
355
+ # get the grammar from the sentence
356
+ id, modifiers, token = grammar(sentence, theme, state)
357
+ if id
358
+ # check memoizer
359
+ memoized = memoizer.fetch(theme, token)
360
+ if memoized
361
+ styles = styles.rmerge(memoized)
362
+ else
363
+ # fetch additional styles
364
+ extracted = extract_styles(id, modifiers, false, theme)
365
+ # we can delete anything that had a value of `nil` as we won't be outputting those
366
+ extracted.delete_if { |k,v| helpers.is_value(v, :nil) }
367
+ styles = styles.rmerge(extracted)
368
+ memoizer.add(theme, token, extracted)
369
+ end
370
+ elsif not helpers.is_value(sentence, :nil)
371
+ helpers.logger.record(:warning, "[archetype:styleguide:missing_identifier] `#{helpers.to_str(sentence)}` does not contain an identifier. please specify one of: #{modifiers.sort.join(', ')}")
372
+ end
373
+ end
374
+ # now that we've collected all of our styles, if we requested a single state, merge that state upstream
375
+ if state != 'false' and styles['states']
376
+ state = styles['states'][state]
377
+ # remove any nested/special keys
378
+ SPECIAL.each do |special|
379
+ styles.delete(special)
380
+ end
381
+ styles = styles.merge(state) if not (state.nil? or state.empty?)
382
+ end
383
+ return styles
384
+ end
385
+
386
+ #
387
+ # check whether or not a component (or a component extension) has already been defined
388
+ #
389
+ # *Parameters*:
390
+ # - <tt>$id</tt> {String} the component identifier
391
+ # - <tt>$data</tt> {List} the component data object
392
+ # - <tt>$theme</tt> {String} the theme to insert the component into
393
+ # - <tt>$extension</tt> {String} the name of the extension
394
+ # - <tt>$force</tt> {Boolean} if true, forcibly extend the component
395
+ # *Returns*:
396
+ # - {Boolean} whether or not the component/extension exists
397
+ #
398
+ def component_exists(id, theme = nil, extension = nil, force = false)
399
+ status = false
400
+ theme = get_theme(theme) if not theme.is_a? Hash
401
+ id = helpers.to_str(id)
402
+ # determine the status of the component
403
+ status = (extension.nil?) ? (not theme[:components][id].nil?) : theme[:extensions].include?(extension)
404
+ return (status and not force and Compass.configuration.memoize)
405
+ end
406
+ end
@@ -0,0 +1,59 @@
1
+ require 'archetype/functions/helpers'
2
+ require 'thread'
3
+
4
+ #
5
+ # This module provides some UI helper methods.
6
+ #
7
+ module Archetype::SassExtensions::UI
8
+ # :stopdoc:
9
+ @@archetype_ui_mutex = Mutex.new
10
+ # :startdoc:
11
+
12
+ #
13
+ # generate a unique token
14
+ #
15
+ # *Parameters*:
16
+ # - <tt>$prefix</tt> {String} a string to prefix the UID with, `class` and `id` will generate a unique selector
17
+ # *Returns*:
18
+ # - {String} the unique string
19
+ #
20
+ def unique(prefix = '')
21
+ prefix = helpers.to_str(prefix, ' ', :quotes)
22
+ prefix = '.' if prefix == 'class'
23
+ prefix = '#' if prefix == 'id'
24
+ return Sass::Script::String.new("#{prefix}archetype-uid-#{uid}")
25
+ end
26
+
27
+ #
28
+ # parse a CSS content string and format it for injection into innerHTML
29
+ #
30
+ # *Parameters*:
31
+ # - <tt>$content</tt> {String} the CSS content string
32
+ # *Returns*:
33
+ # - {String} the processed string
34
+ #
35
+ def _ie_pseudo_content(content)
36
+ content = helpers.to_str(content)
37
+ # escape &
38
+ content = content.gsub(/\&/, '&amp;')
39
+ # convert char codes (and remove single trailing whitespace if present) (e.g. \2079 -> &#x2079;)
40
+ content = content.gsub(/\\([\da-zA-Z]{4})\s?/, '&#x\1;')
41
+ # escape tags and cleanup quotes
42
+ content = content.gsub(/\</, '&lt;').gsub(/\>/, '&gt;')
43
+ # cleanup quotes
44
+ content = content.gsub(/\A"|"\Z/, '').gsub(/\"/, '\\"')
45
+ return Sass::Script::String.new(content)
46
+ end
47
+
48
+ private
49
+ def helpers
50
+ @helpers ||= Archetype::Functions::Helpers
51
+ end
52
+
53
+ def uid
54
+ @@archetype_ui_mutex.synchronize do
55
+ @@uid ||= 0
56
+ @@uid += 1
57
+ end
58
+ end
59
+ end