storefront 0.3.2 → 0.5.0
Sign up to get free protection for your applications and to get access to all the features.
- data/Rakefile +4 -4
- data/lib/storefront.rb +22 -35
- data/lib/storefront/components/base.rb +99 -0
- data/lib/storefront/components/breadcrumb.rb +7 -0
- data/lib/storefront/components/button.rb +37 -0
- data/lib/storefront/components/definition_list.rb +34 -0
- data/lib/storefront/components/definition_list_item.rb +63 -0
- data/lib/storefront/components/flash.rb +12 -0
- data/lib/storefront/components/footer.rb +19 -0
- data/lib/storefront/components/form.rb +82 -0
- data/lib/storefront/{form → components/form}/base.rb +3 -3
- data/lib/storefront/{form → components/form}/builder.rb +3 -3
- data/lib/storefront/{form → components/form}/errors.rb +1 -3
- data/lib/storefront/{form → components/form}/field.rb +1 -1
- data/lib/storefront/{form → components/form}/fieldset.rb +0 -0
- data/lib/storefront/{form → components/form}/hint.rb +0 -0
- data/lib/storefront/{form → components/form}/input.rb +7 -5
- data/lib/storefront/components/form/inputs.rb +7 -0
- data/lib/storefront/{form → components/form}/inputs/checkbox.rb +0 -0
- data/lib/storefront/{form → components/form}/inputs/date.rb +0 -0
- data/lib/storefront/{form → components/form}/inputs/file.rb +0 -0
- data/lib/storefront/{form → components/form}/inputs/hidden.rb +0 -0
- data/lib/storefront/{form → components/form}/inputs/radio.rb +0 -0
- data/lib/storefront/{form → components/form}/inputs/range.rb +0 -0
- data/lib/storefront/{form → components/form}/inputs/select.rb +6 -6
- data/lib/storefront/{form → components/form}/inputs/string.rb +0 -0
- data/lib/storefront/{form → components/form}/inputs/submit.rb +0 -0
- data/lib/storefront/{form → components/form}/inputs/textarea.rb +0 -0
- data/lib/storefront/{form → components/form}/inputs/value.rb +0 -0
- data/lib/storefront/{form → components/form}/label.rb +1 -1
- data/lib/storefront/components/header.rb +73 -0
- data/lib/storefront/components/image.rb +12 -0
- data/lib/storefront/components/javascript_template.rb +21 -0
- data/lib/storefront/components/list.rb +27 -0
- data/lib/storefront/components/menu.rb +41 -0
- data/lib/storefront/components/nav.rb +78 -0
- data/lib/storefront/components/sidebar.rb +23 -0
- data/lib/storefront/components/table.rb +232 -0
- data/lib/storefront/components/text.rb +29 -0
- data/lib/storefront/configuration.rb +53 -5
- data/lib/storefront/helpers/cache_helper.rb +37 -19
- data/lib/storefront/helpers/component_helper.rb +80 -41
- data/lib/storefront/helpers/content_helper.rb +211 -0
- data/lib/storefront/helpers/dom_helper.rb +136 -0
- data/lib/storefront/helpers/head_helper.rb +277 -205
- data/lib/storefront/helpers/model_helper.rb +21 -0
- data/lib/storefront/helpers/request_helper.rb +301 -53
- data/lib/storefront/helpers/tag_helper.rb +29 -0
- data/lib/storefront/{proxies/attribute_proxy.rb → models/attribute.rb} +1 -1
- data/lib/storefront/{proxies/model_proxy.rb → models/model.rb} +4 -6
- data/lib/storefront/railtie.rb +3 -16
- metadata +48 -64
- data/lib/storefront/dashboard.rb +0 -185
- data/lib/storefront/form.rb +0 -82
- data/lib/storefront/helpers/attribute_helper.rb +0 -81
- data/lib/storefront/helpers/body_helper.rb +0 -18
- data/lib/storefront/helpers/browser_helper.rb +0 -54
- data/lib/storefront/helpers/collection_helper.rb +0 -14
- data/lib/storefront/helpers/dashboard_helper.rb +0 -29
- data/lib/storefront/helpers/debug_helper.rb +0 -11
- data/lib/storefront/helpers/definition_list_helper.rb +0 -110
- data/lib/storefront/helpers/error_helper.rb +0 -11
- data/lib/storefront/helpers/flash_helper.rb +0 -14
- data/lib/storefront/helpers/form_helper.rb +0 -13
- data/lib/storefront/helpers/format_helper.rb +0 -59
- data/lib/storefront/helpers/image_helper.rb +0 -30
- data/lib/storefront/helpers/list_helper.rb +0 -135
- data/lib/storefront/helpers/locale_helper.rb +0 -245
- data/lib/storefront/helpers/open_graph_helper.rb +0 -5
- data/lib/storefront/helpers/page_helper.rb +0 -91
- data/lib/storefront/helpers/presenter_helper.rb +0 -5
- data/lib/storefront/helpers/semantic/location_helper.rb +0 -18
- data/lib/storefront/helpers/semantic/time_helper.rb +0 -14
- data/lib/storefront/helpers/sidebar_helper.rb +0 -27
- data/lib/storefront/helpers/system_helper.rb +0 -18
- data/lib/storefront/helpers/table_helper.rb +0 -38
- data/lib/storefront/helpers/template_helper.rb +0 -11
- data/lib/storefront/helpers/time_helper.rb +0 -21
- data/lib/storefront/helpers/url_helper.rb +0 -32
- data/lib/storefront/helpers/user_helper.rb +0 -15
- data/lib/storefront/helpers/widget_helper.rb +0 -142
- data/lib/storefront/helpers/workflow_helper.rb +0 -50
- data/lib/storefront/model_helper.rb +0 -17
- data/lib/storefront/table.rb +0 -198
- data/rails/init.rb +0 -1
- data/test/form_helper_test.rb +0 -5
- data/test/support/mock_controller.rb +0 -15
- data/test/support/mock_response.rb +0 -14
- data/test/support/models.rb +0 -0
- data/test/test_helper.rb +0 -34
@@ -0,0 +1,211 @@
|
|
1
|
+
module Storefront
|
2
|
+
module Helpers
|
3
|
+
module ContentHelper
|
4
|
+
SCOPES_WITH_NAMESPACE_AND_NESTED_MODEL = [
|
5
|
+
'%{namespace}.%{model}.%{nested_model}.%{action}.%{attribute}',
|
6
|
+
'%{model}.%{nested_model}.%{action}.%{attribute}',
|
7
|
+
'%{namespace}.%{model}.%{action}.%{attribute}',
|
8
|
+
'%{model}.%{action}.%{attribute}',
|
9
|
+
'%{namespace}.%{model}.%{nested_model}.%{attribute}',
|
10
|
+
'%{model}.%{nested_model}.%{attribute}',
|
11
|
+
'%{namespace}.%{model}.%{attribute}',
|
12
|
+
'%{model}.%{attribute}',
|
13
|
+
'%{namespace}.%{nested_model}.%{attribute}',
|
14
|
+
'%{nested_model}.%{attribute}',
|
15
|
+
'%{namespace}.%{attribute}',
|
16
|
+
'%{attribute}'
|
17
|
+
]
|
18
|
+
|
19
|
+
SCOPES_WITH_NESTED_MODEL = [
|
20
|
+
'%{model}.%{nested_model}.%{action}.%{attribute}',
|
21
|
+
'%{model}.%{action}.%{attribute}',
|
22
|
+
'%{model}.%{nested_model}.%{attribute}',
|
23
|
+
'%{model}.%{attribute}',
|
24
|
+
'%{nested_model}.%{attribute}',
|
25
|
+
'%{attribute}'
|
26
|
+
]
|
27
|
+
|
28
|
+
SCOPES_WITH_NAMESPACE = [
|
29
|
+
'%{namespace}.%{model}.%{action}.%{attribute}',
|
30
|
+
'%{namespace}.%{model}.%{attribute}',
|
31
|
+
'%{model}.%{action}.%{attribute}',
|
32
|
+
'%{model}.%{attribute}',
|
33
|
+
'%{namespace}.%{attribute}',
|
34
|
+
'%{attribute}'
|
35
|
+
]
|
36
|
+
|
37
|
+
SCOPES = [
|
38
|
+
'%{model}.%{action}.%{attribute}',
|
39
|
+
'%{model}.%{attribute}',
|
40
|
+
'%{attribute}'
|
41
|
+
]
|
42
|
+
|
43
|
+
# t?(:"what.summary", :scope => :"reports.titles", :model => deal)
|
44
|
+
def t?(key, options = {})
|
45
|
+
return key if key.is_a?(::String) || key.blank?
|
46
|
+
|
47
|
+
models = Storefront::Model.model_names(options[:model] || options[:models] || options[:model_names] || storefront_config.current_scope[:resource] || [])
|
48
|
+
models << "" if models.blank?
|
49
|
+
namespaces = Array(options[:namespaces] || options[:namespace] || storefront_config.current_scope[:namespace])
|
50
|
+
namespaces << "" if namespaces.blank? && !!storefront_config.localize_with_namespace
|
51
|
+
action_name = options[:action] || storefront_config.current_scope[:action]
|
52
|
+
defaults = []
|
53
|
+
allow_blank = options[:allow_blank] == true
|
54
|
+
count = options[:count]
|
55
|
+
# none, one, many, other
|
56
|
+
count_key = count != 1 ? :other : :one
|
57
|
+
|
58
|
+
keys = [key.to_s]
|
59
|
+
|
60
|
+
if options.has_key?(:present) || options.has_key?(:past) || options.has_key?(:future)
|
61
|
+
if options[:future] == true
|
62
|
+
tense_key = :future
|
63
|
+
else
|
64
|
+
tense_key = (options[:present] == true || options[:past] == false) ? :present : :past
|
65
|
+
end
|
66
|
+
else
|
67
|
+
tense_key = nil
|
68
|
+
end
|
69
|
+
|
70
|
+
if options.has_key?(:nested_model)
|
71
|
+
with_nested_model = !!options[:nested_model]
|
72
|
+
else
|
73
|
+
with_nested_model = !!storefront_config.localize_with_nested_model
|
74
|
+
end
|
75
|
+
|
76
|
+
if options.has_key?(:namespace)
|
77
|
+
with_namespace = !!options[:namespace]
|
78
|
+
else
|
79
|
+
with_namespace = !!storefront_config.localize_with_namespace
|
80
|
+
end
|
81
|
+
|
82
|
+
if with_nested_model && with_namespace
|
83
|
+
# TODO, need to performance test before we do this many operations
|
84
|
+
SCOPES_WITH_NAMESPACE_AND_NESTED_MODEL
|
85
|
+
elsif with_nested_model
|
86
|
+
# TODO
|
87
|
+
SCOPES_WITH_NESTED_MODEL
|
88
|
+
elsif with_namespace
|
89
|
+
namespaces.each do |namespace|
|
90
|
+
keys.each do |_key|
|
91
|
+
SCOPES_WITH_NAMESPACE.each do |_path|
|
92
|
+
models.each do |model|
|
93
|
+
path = _path.dup
|
94
|
+
path.gsub!('%{namespace}', namespace)
|
95
|
+
path.gsub!('%{model}', model)
|
96
|
+
path.gsub!('%{action}', action_name)
|
97
|
+
path.gsub!('%{attribute}', _key)
|
98
|
+
path.gsub!('..', '.')
|
99
|
+
defaults << path
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|
104
|
+
else
|
105
|
+
keys.each do |_key|
|
106
|
+
SCOPES.each do |_path|
|
107
|
+
models.each do |model|
|
108
|
+
path = _path.dup
|
109
|
+
path.gsub!('%{model}', model)
|
110
|
+
path.gsub!('%{action}', action_name)
|
111
|
+
path.gsub!('%{attribute}', _key)
|
112
|
+
path.gsub!('..', '.')
|
113
|
+
defaults << path
|
114
|
+
end
|
115
|
+
end
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
119
|
+
# RESERVED KEYS
|
120
|
+
[:past, :present, :future, :allow_blank, :model, :models, :model_names, :action, :nested_model, :namespace, :namespaces].each do |key|
|
121
|
+
options.delete(key)
|
122
|
+
end
|
123
|
+
|
124
|
+
defaults.map!(&:to_sym)
|
125
|
+
defaults << ''
|
126
|
+
|
127
|
+
value = I18n.t(defaults.shift, options.merge(:default => defaults))
|
128
|
+
|
129
|
+
if value.is_a?(::Hash)
|
130
|
+
if value.has_key?(count_key)
|
131
|
+
value = value[count_key]
|
132
|
+
elsif value.has_key?(tense_key)
|
133
|
+
value = value[tense_key]
|
134
|
+
if value.is_a?(::Hash) && value.has_key?(count_key)
|
135
|
+
value = value[count_key]
|
136
|
+
end
|
137
|
+
elsif value.has_key?(count_key)
|
138
|
+
value = value[count_key]
|
139
|
+
end
|
140
|
+
end
|
141
|
+
|
142
|
+
if value.is_a?(::Hash)
|
143
|
+
if value.has_key?(:one)
|
144
|
+
value = value[:one]
|
145
|
+
end
|
146
|
+
end
|
147
|
+
|
148
|
+
if value.is_a?(::Hash)
|
149
|
+
if value.has_key?(:present)
|
150
|
+
value = value[:present]
|
151
|
+
elsif value.has_key?(:past)
|
152
|
+
value = value[:past]
|
153
|
+
elsif value.has_key?(:future)
|
154
|
+
value = value[:future]
|
155
|
+
end
|
156
|
+
end
|
157
|
+
|
158
|
+
# MORE RESERVED KEYS
|
159
|
+
[:scope].each do |key|
|
160
|
+
options.delete(key)
|
161
|
+
end
|
162
|
+
|
163
|
+
if value.blank? && !allow_blank
|
164
|
+
key.to_s.split(".").last.titleize
|
165
|
+
elsif options.present?
|
166
|
+
I18n.interpolate(value, options)
|
167
|
+
else
|
168
|
+
value
|
169
|
+
end
|
170
|
+
end
|
171
|
+
|
172
|
+
def rendered_text(string)
|
173
|
+
string.to_s.
|
174
|
+
gsub(/<\/?[^>]*>/, '').
|
175
|
+
gsub(/\n/, " ").
|
176
|
+
gsub("\r", " ").
|
177
|
+
squeeze(" ").
|
178
|
+
gsub(/\"/, "″"). # convert quotes to inches
|
179
|
+
gsub(/\'/, "′"). # single quote to feet
|
180
|
+
gsub(/\$0?0*\.(\d+)/, "\\1¢").html_safe
|
181
|
+
end
|
182
|
+
|
183
|
+
def sanitize(text)
|
184
|
+
text.gsub(/<\/?[^>]*>/, '').gsub(/\n/, " ").gsub("\r", " ").squeeze(" ")
|
185
|
+
end
|
186
|
+
|
187
|
+
def read_binary_file(path)
|
188
|
+
File.open(path, 'rb') {|f| f.read }
|
189
|
+
end
|
190
|
+
|
191
|
+
def encoded_contents(path)
|
192
|
+
Base64.encode64(read_binary_file(path)).gsub(/\n/, '')
|
193
|
+
end
|
194
|
+
|
195
|
+
def font_face_data_uri(path)
|
196
|
+
"url(\"data:font/truetype;base64,#{encoded_contents(path)}\")"
|
197
|
+
end
|
198
|
+
|
199
|
+
# http://snippets.dzone.com/posts/show/805
|
200
|
+
def read_image_size(path)
|
201
|
+
width, height = IO.read("#{Rails.root}/public/images/#{path}")[0x10..0x18].unpack('NN')
|
202
|
+
{:width => width, :height => height}
|
203
|
+
end
|
204
|
+
|
205
|
+
def html5_time(date_or_time)
|
206
|
+
return nil if date_or_time.blank?
|
207
|
+
date_or_time.acts_like?(:time) ? date_or_time.xmlschema : date_or_time.rfc3339
|
208
|
+
end
|
209
|
+
end
|
210
|
+
end
|
211
|
+
end
|
@@ -0,0 +1,136 @@
|
|
1
|
+
module Storefront
|
2
|
+
module Helpers
|
3
|
+
# HTML Element Attribute Helper
|
4
|
+
module DomHelper
|
5
|
+
def clone_attributes(hash)
|
6
|
+
result = {}
|
7
|
+
return result if hash.blank?
|
8
|
+
hash.each do |key, value|
|
9
|
+
result[key] = value.is_a?(::Hash) ? clone_attributes(value) : value
|
10
|
+
end
|
11
|
+
result
|
12
|
+
end
|
13
|
+
|
14
|
+
def merge_class!(attributes, *values)
|
15
|
+
return {} if attributes.nil?
|
16
|
+
hash = merge_class(attributes, *values)
|
17
|
+
attributes[:class] = hash[:class] if hash
|
18
|
+
attributes
|
19
|
+
end
|
20
|
+
|
21
|
+
def merge_class(attributes, *values)
|
22
|
+
if values.present?
|
23
|
+
if attributes[:class].present?
|
24
|
+
classes = attributes[:class].split(/\s+/)
|
25
|
+
else
|
26
|
+
classes = []
|
27
|
+
end
|
28
|
+
classes += values.compact.map(&:to_s)
|
29
|
+
classes.uniq!
|
30
|
+
else
|
31
|
+
classes = nil
|
32
|
+
end
|
33
|
+
|
34
|
+
if classes.present?
|
35
|
+
attributes.merge(:class => classes.join(" "))
|
36
|
+
else
|
37
|
+
attributes
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def index_class(index, array_or_length)
|
42
|
+
length = array_or_length.is_a?(::Array) ? array_or_length.length : array_or_length
|
43
|
+
return nil if length.blank? || (length <= 1)
|
44
|
+
|
45
|
+
if index == 0
|
46
|
+
"first"
|
47
|
+
elsif index == length - 1
|
48
|
+
"last"
|
49
|
+
else
|
50
|
+
nil
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
# @example Aria role main.
|
55
|
+
#
|
56
|
+
# aria(:main) #=> {:role => :main}
|
57
|
+
def aria(key)
|
58
|
+
{:role => key}
|
59
|
+
end
|
60
|
+
|
61
|
+
# @example Customize <tt><body></tt> tag attributes from inside a partial
|
62
|
+
#
|
63
|
+
# # app/views/posts/_post.haml
|
64
|
+
# - dom :body => {:class => "admin", :id => "admin"}
|
65
|
+
#
|
66
|
+
# # app/views/layouts/application.haml
|
67
|
+
# %body{dom[:body]}
|
68
|
+
def dom(attributes = {})
|
69
|
+
@dom ||= {}
|
70
|
+
@dom.deep_merge!(attributes)
|
71
|
+
@dom
|
72
|
+
end
|
73
|
+
|
74
|
+
# calls <tt>model.to_title</tt>
|
75
|
+
def page(*args)
|
76
|
+
options = args.extract_options!
|
77
|
+
title = args.shift
|
78
|
+
options[:action] ||= storefront_config.current_scope[:action]
|
79
|
+
|
80
|
+
title = case title
|
81
|
+
when ::String
|
82
|
+
title
|
83
|
+
when ::Symbol
|
84
|
+
t?(title, :scope => :"pages.titles")
|
85
|
+
else
|
86
|
+
if options[:title].present?
|
87
|
+
options[:title]
|
88
|
+
else
|
89
|
+
parent_name = !!options[:parent] ? resource_to_title(storefront_config.current_scope[:parent], options) : nil
|
90
|
+
resource_name = resource_to_title(storefront_config.current_scope[:resource], options)
|
91
|
+
[parent_name, resource_name].compact.join(storefront_config.breadcrumb)
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
self.title(options[:browser] || title, options)
|
96
|
+
|
97
|
+
options = header_widget_options(options.merge(:title => title))
|
98
|
+
|
99
|
+
options[:header_html].reverse_merge!(:id => storefront_config.page_header_id) if storefront_config.page_header_id.present?
|
100
|
+
options[:title_html].reverse_merge!(:id => storefront_config.page_title_id) if storefront_config.page_title_id.present?
|
101
|
+
options[:subtitle_html].reverse_merge!(:id => storefront_config.page_subtitle_id) if storefront_config.page_subtitle_id.present?
|
102
|
+
options[:level] = 1
|
103
|
+
options[:skip_format] = true
|
104
|
+
|
105
|
+
header_widget options.merge(:title => title)
|
106
|
+
end
|
107
|
+
|
108
|
+
def title_widget(*args, &block)
|
109
|
+
options = args.extract_options!
|
110
|
+
options[:header_html] ||= {}
|
111
|
+
merge_class! options[:header_html], *widget_classes("title-widget")
|
112
|
+
header_widget(options, &block)
|
113
|
+
end
|
114
|
+
|
115
|
+
def title_widget_options
|
116
|
+
|
117
|
+
end
|
118
|
+
|
119
|
+
def resource_to_title(resource, options = {})
|
120
|
+
if resource.present?
|
121
|
+
|
122
|
+
# handle subdomains as well
|
123
|
+
t?(options[:action].to_sym,
|
124
|
+
:scope => :"pages.titles",
|
125
|
+
:model => Storefront::ModelProxy.model_names(resource),
|
126
|
+
:namespaces => storefront_config.current_scope[:namespace],
|
127
|
+
:allow_blank => true,
|
128
|
+
:action => "",
|
129
|
+
:name => resource.is_a?(::Class) ? resource.name.titleize.pluralize : resource.to_title,
|
130
|
+
:clazz => resource.is_a?(::Class) ? resource.name.titleize : resource.class.name
|
131
|
+
)
|
132
|
+
end
|
133
|
+
end
|
134
|
+
end
|
135
|
+
end
|
136
|
+
end
|
@@ -1,229 +1,301 @@
|
|
1
1
|
module Storefront
|
2
|
-
module
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
result
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
result << description(post.description)
|
15
|
-
result << "\n"
|
16
|
-
end
|
17
|
-
result
|
18
|
-
end
|
19
|
-
|
20
|
-
def meta(options = {})
|
21
|
-
title(options[:title]) if options.has_key?(:title)
|
22
|
-
description(options[:description]) if options.has_key?(:description)
|
23
|
-
keywords(options[:keywords]) if options.has_key?(:keywords)
|
24
|
-
copyright(options[:copyright]) if options.has_key?(:copyright)
|
25
|
-
end
|
26
|
-
|
27
|
-
def meta_tags(*args)
|
28
|
-
options = args.extract_options!
|
29
|
-
site = options[:site] || args.first
|
30
|
-
result = title_tag(site, options[:title])
|
31
|
-
result << "\n"
|
32
|
-
result << content_type_tag
|
33
|
-
result << "\n"
|
34
|
-
result << description_tag(options[:description])
|
35
|
-
result << "\n"
|
36
|
-
result << keywords_tag(options[:keywords])
|
37
|
-
result << "\n"
|
38
|
-
result << copyright_tag(options[:copyright])
|
39
|
-
result
|
40
|
-
end
|
41
|
-
|
42
|
-
# country
|
43
|
-
# classification
|
44
|
-
# author
|
45
|
-
# apple-mobile-web-app-capable
|
46
|
-
# apple-touch-fullscreen
|
47
|
-
# google-analytics
|
48
|
-
# Revisit-After
|
49
|
-
# category
|
50
|
-
def meta_tag(name, content = '')
|
51
|
-
tag(:meta, :name => name, :content => content)
|
52
|
-
end
|
53
|
-
|
54
|
-
def title(string)
|
55
|
-
@content_for_title = string
|
56
|
-
end
|
57
|
-
|
58
|
-
def title_tag(*args)
|
59
|
-
options = args.extract_options!
|
60
|
-
|
61
|
-
site = options[:site] || args.first
|
62
|
-
# Prefix (leading space)
|
63
|
-
if options[:prefix]
|
64
|
-
prefix = options[:prefix]
|
65
|
-
elsif options[:prefix] === false
|
66
|
-
prefix = ''
|
67
|
-
else
|
68
|
-
prefix = ' '
|
2
|
+
module Helpers
|
3
|
+
module HeadHelper
|
4
|
+
def meta_tags(*args)
|
5
|
+
options = args.extract_options!
|
6
|
+
options[:site] ||= args.shift
|
7
|
+
result = []
|
8
|
+
storefront_config.meta_tags.each do |meta_tag_name|
|
9
|
+
if options[meta_tag_name].present?
|
10
|
+
result << meta_tag(meta_tag_name, options[meta_tag_name])
|
11
|
+
end
|
12
|
+
end
|
13
|
+
result.join("\n")
|
69
14
|
end
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
else
|
75
|
-
separator = '|'
|
15
|
+
alias_method :meta, :meta_tags
|
16
|
+
|
17
|
+
def meta_tag(name, content = '')
|
18
|
+
capture_haml { haml_tag :meta, :name => name, :content => content }
|
76
19
|
end
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
suffix = options[:suffix]
|
81
|
-
elsif options[:suffix] === false
|
82
|
-
suffix = ''
|
83
|
-
else
|
84
|
-
suffix = ' '
|
20
|
+
|
21
|
+
def title(*args)
|
22
|
+
@content_for_title = *args
|
85
23
|
end
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
title
|
24
|
+
|
25
|
+
def title_tag(*args)
|
26
|
+
options = args.extract_options!
|
27
|
+
# Title
|
28
|
+
title = @content_for_title
|
29
|
+
|
30
|
+
if title.present?
|
31
|
+
options.merge!(title.extract_options!)
|
32
|
+
title = @content_for_title.shift || options[:title]
|
33
|
+
end
|
34
|
+
|
35
|
+
if options[:lowercase] === true
|
36
|
+
title = title.downcase unless title.blank?
|
37
|
+
end
|
38
|
+
|
39
|
+
site = options.has_key?(:site) ? options[:site] : args.first
|
40
|
+
|
41
|
+
# Prefix (leading space)
|
42
|
+
if options[:prefix]
|
43
|
+
prefix = options[:prefix]
|
44
|
+
elsif options[:prefix] === false
|
45
|
+
prefix = ''
|
46
|
+
else
|
47
|
+
prefix = ' '
|
48
|
+
end
|
49
|
+
|
50
|
+
# Separator
|
51
|
+
unless options[:separator].blank?
|
52
|
+
separator = options[:separator]
|
53
|
+
else
|
54
|
+
separator = '|'
|
55
|
+
end
|
56
|
+
|
57
|
+
# Suffix (trailing space)
|
58
|
+
if options[:suffix]
|
59
|
+
suffix = options[:suffix]
|
60
|
+
elsif options[:suffix] === false
|
61
|
+
suffix = ''
|
62
|
+
else
|
63
|
+
suffix = ' '
|
64
|
+
end
|
65
|
+
|
66
|
+
# title
|
67
|
+
if title.blank?
|
68
|
+
result = content_tag :title, site
|
69
|
+
else
|
70
|
+
title = normalize_title(title)
|
71
|
+
title = [site] + title if site
|
72
|
+
title.reverse! unless options[:reverse] === false
|
73
|
+
sep = prefix + separator + suffix
|
74
|
+
result = content_tag(:title, title.join(sep))
|
75
|
+
end
|
76
|
+
result
|
77
|
+
end
|
78
|
+
|
79
|
+
def description(string)
|
80
|
+
@content_for_description = string
|
81
|
+
end
|
82
|
+
|
83
|
+
def description_tag(default='')
|
84
|
+
content = normalize_description(@content_for_description || default)
|
85
|
+
meta_tag(:description, content) unless content.blank?
|
91
86
|
end
|
92
|
-
|
93
|
-
# title
|
94
|
-
if title.blank?
|
95
|
-
result = content_tag :title, site
|
96
|
-
else
|
97
|
-
title = normalize_title(title)
|
98
|
-
title = [site] + title
|
99
|
-
title.reverse! unless options[:reverse] === false
|
100
|
-
sep = prefix + separator + suffix
|
101
|
-
result = content_tag(:title, title.join(sep))
|
102
|
-
end
|
103
|
-
result
|
104
|
-
end
|
105
|
-
|
106
|
-
def description(string)
|
107
|
-
@content_for_description = string
|
108
|
-
end
|
109
|
-
|
110
|
-
def description_tag(default='')
|
111
|
-
content = normalize_description(@content_for_description || default)
|
112
|
-
tag(:meta, :name => :description, :content => content) unless content.blank?
|
113
|
-
end
|
114
87
|
|
115
|
-
|
116
|
-
|
117
|
-
|
88
|
+
def keywords(string)
|
89
|
+
@content_for_keywords = string
|
90
|
+
end
|
118
91
|
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
92
|
+
def keywords_tag(default = '')
|
93
|
+
content = normalize_keywords(@content_for_keywords || default)
|
94
|
+
meta_tag(:keywords, content) unless content.blank?
|
95
|
+
end
|
123
96
|
|
124
|
-
|
125
|
-
|
126
|
-
|
97
|
+
def copyright(string)
|
98
|
+
@content_for_copyright = string
|
99
|
+
end
|
127
100
|
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
101
|
+
def copyright_tag(default='')
|
102
|
+
content = @content_for_copyright || default
|
103
|
+
meta_tag(:copyright, content) unless content.blank?
|
104
|
+
end
|
132
105
|
|
133
|
-
|
134
|
-
|
135
|
-
|
106
|
+
def robots(*args)
|
107
|
+
content_for(:robots, args.join(", "))
|
108
|
+
end
|
136
109
|
|
137
|
-
|
138
|
-
|
110
|
+
def robots_tag(*args)
|
111
|
+
options = args.extract_options!
|
139
112
|
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
113
|
+
noindex_name = tags[:noindex].is_a?(String) ? tags[:noindex] : 'robots'
|
114
|
+
nofollow_name = tags[:nofollow].is_a?(String) ? tags[:nofollow] : 'robots'
|
115
|
+
|
116
|
+
if noindex_name == nofollow_name
|
117
|
+
content = [(tags[:noindex] ? 'noindex' : nil), (tags[:nofollow] ? 'nofollow' : nil)].compact.join(', ')
|
118
|
+
unless content.blank?
|
119
|
+
result << "\n"
|
120
|
+
result << meta_tag(noindex_name, content)
|
121
|
+
end
|
122
|
+
else
|
123
|
+
if tags[:noindex]
|
124
|
+
result << "\n"
|
125
|
+
result << meta_tag(noindex_name, "noindex")
|
126
|
+
end
|
127
|
+
if tags[:nofollow]
|
128
|
+
result << "\n"
|
129
|
+
result << meta_tag(nofollow_name, "nofollow")
|
130
|
+
end
|
157
131
|
end
|
132
|
+
|
133
|
+
return result
|
158
134
|
end
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
result << tag(:link, :rel => :canonical, :href => tags[:canonical])
|
135
|
+
|
136
|
+
def canonical_link_tag(path)
|
137
|
+
link_tag(:rel => :canonical, :href => path)
|
163
138
|
end
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
# http://wiki.developers.facebook.com/index.php/Facebook_Share/Specifying_Meta_Tags
|
185
|
-
def medium(type)
|
186
|
-
tag(:meta, :name => :medium, :content => type)
|
187
|
-
end
|
188
|
-
|
189
|
-
def classification(type)
|
190
|
-
tag(:meta, :name => "og:type", :content => type)
|
191
|
-
end
|
192
|
-
|
193
|
-
def content_type_tag(type = "text/html", charset = "UTF-8")
|
194
|
-
meta_tag("Content-Type", "#{type}; charset=#{charset}")
|
195
|
-
end
|
139
|
+
|
140
|
+
def http_meta_tag(name, content)
|
141
|
+
capture_haml { haml_tag :meta, :"http-equiv" => name, :content => content }
|
142
|
+
end
|
143
|
+
|
144
|
+
# @example Redirect to the site home page in 20 seconds.
|
145
|
+
#
|
146
|
+
# refresh_meta_tag root_url, :in => 20.seconds
|
147
|
+
def refresh_meta_tag(path, options = {})
|
148
|
+
http_meta_tag("refresh", "#{options[:in].to_i};url=#{path}")
|
149
|
+
end
|
150
|
+
|
151
|
+
def copyright(from)
|
152
|
+
now = Time.now.year
|
153
|
+
now.eql?(from) ? now : "#{from} - #{now}"
|
154
|
+
end
|
155
|
+
|
156
|
+
# Apple
|
157
|
+
# http://developer.apple.com/safari/library/documentation/AppleApplications/Reference/SafariHTMLRef/Articles/MetaTags.html
|
196
158
|
|
197
|
-
|
198
|
-
|
199
|
-
end
|
159
|
+
# Facebook (and opengraph)
|
160
|
+
# http://developers.facebook.com/docs/opengraph#types
|
200
161
|
|
201
|
-
|
202
|
-
|
203
|
-
|
162
|
+
# http://wiki.developers.facebook.com/index.php/Facebook_Share/Specifying_Meta_Tags
|
163
|
+
def snapshot_link_tag(href)
|
164
|
+
link_tag(:rel => "image_src", :href => href)
|
165
|
+
end
|
204
166
|
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
|
167
|
+
def html4_content_type_tag(charset = "UTF-8", type = "text/html")
|
168
|
+
http_meta_tag("Content-Type", "#{type}; charset=#{charset}")
|
169
|
+
end
|
170
|
+
|
171
|
+
def html5_content_type_tag(charset = "UTF-8")
|
172
|
+
capture_haml { haml_tag :meta, "charset" => charset }
|
173
|
+
end
|
174
|
+
alias_method :content_type_tag, :html5_content_type_tag
|
175
|
+
|
176
|
+
def search_link_tag(href, title)
|
177
|
+
link_tag(:rel => "search", :type => "application/opensearchdescription+xml", :href => href, :title => title)
|
178
|
+
end
|
179
|
+
|
180
|
+
def favicon_link_tag(favicon = "/favicon.ico")
|
181
|
+
link_tag(:rel => "shortcut icon", :href => favicon, :type => "image/x-icon")
|
182
|
+
end
|
183
|
+
|
184
|
+
def link_tag(options = {})
|
185
|
+
capture_haml { haml_tag :link, options }
|
186
|
+
end
|
187
|
+
|
188
|
+
def ie_application_meta_tags(title, options = {})
|
189
|
+
result = []
|
190
|
+
result << meta_tag("application-name", title)
|
191
|
+
result << meta_tag("msapplication-tooltip", options[:tooltip]) if options[:tooltip].present?
|
192
|
+
result << meta_tag("msapplication-starturl", options[:url]) if options[:url].present?
|
193
|
+
if options[:width].present? && options[:height].present?
|
194
|
+
result << meta_tag("msapplication-window", "width=#{options[:width]};height=#{options[:height]}")
|
214
195
|
end
|
215
|
-
|
196
|
+
result << meta_tag("msapplication-navbutton-color", options[:color]) if options[:color].present?
|
197
|
+
result.join("\n")
|
216
198
|
end
|
217
|
-
|
218
|
-
def
|
219
|
-
|
220
|
-
|
199
|
+
|
200
|
+
def ie_task_meta_tag(name, path, icon = nil)
|
201
|
+
content = []
|
202
|
+
content << "name=#{name}"
|
203
|
+
content << "uri=#{path}"
|
204
|
+
content << "icon-uri=#{icon}" if icon
|
205
|
+
meta_tag("msapplication-task", content.join(";"))
|
221
206
|
end
|
222
|
-
|
223
|
-
def
|
224
|
-
|
225
|
-
|
226
|
-
|
207
|
+
|
208
|
+
def apple_meta_tags(options = {})
|
209
|
+
result = []
|
210
|
+
result << apple_viewport_meta_tag(options)
|
211
|
+
result << apple_full_screen_meta_tag(options[:full_screen]) if options.has_key?(:full_screen)
|
212
|
+
result << apple_mobile_compatible_meta_tag(options[:mobile]) if options.has_key?(:mobile)
|
213
|
+
result.join
|
214
|
+
end
|
215
|
+
|
216
|
+
# http://www.html5rocks.com/en/mobile/mobifying.html
|
217
|
+
def apple_viewport_meta_tag(options = {})
|
218
|
+
viewport = []
|
219
|
+
viewport << "width=#{options[:width]}" if options[:width].present?
|
220
|
+
viewport << "height=#{options[:height]}" if options[:height].present?
|
221
|
+
viewport << "initial-scale=#{options[:scale] || 1.0}"
|
222
|
+
viewport << "minimum-scale=#{options[:min]}" if options[:min].present?
|
223
|
+
viewport << "maximum-scale=#{options[:max]}" if options[:max].present?
|
224
|
+
viewport << "user-scalable=#{options[:scalable] == true ? "yes" : "no"}" if options.has_key?(:scalable)
|
225
|
+
|
226
|
+
meta_tag("viewport", viewport.join(", "))
|
227
|
+
end
|
228
|
+
|
229
|
+
def apple_full_screen_meta_tag(value)
|
230
|
+
meta_tag("apple-touch-fullscreen", value == true ? "yes" : "no")
|
231
|
+
end
|
232
|
+
|
233
|
+
def apple_mobile_compatible_meta_tag(value)
|
234
|
+
meta_tag("apple-mobile-web-app-capable", value == true ? "yes" : "no")
|
235
|
+
end
|
236
|
+
|
237
|
+
def apple_touch_icon_link_tag(path, options = {})
|
238
|
+
rel = ["apple-touch-icon"]
|
239
|
+
rel << "#{options[:size]}x#{options[:size]}" if options[:size].present?
|
240
|
+
rel << "precomposed" if options[:precomposed]
|
241
|
+
link_tag(:rel => rel.join("-"), :href => path)
|
227
242
|
end
|
243
|
+
|
244
|
+
def apple_touch_icon_link_tags(path, *sizes)
|
245
|
+
options = sizes.extract_options!
|
246
|
+
sizes.map { |size| apple_touch_icon_link_tag(path, options.merge(:size => size)) }.join
|
247
|
+
end
|
248
|
+
|
249
|
+
def open_graph_meta_tags(options = {})
|
250
|
+
result = []
|
251
|
+
|
252
|
+
result << open_graph_meta_tag("og:title", options[:title]) if options[:title]
|
253
|
+
result << open_graph_meta_tag("og:type", options[:type]) if options[:type]
|
254
|
+
result << open_graph_meta_tag("og:image", options[:image]) if options[:image]
|
255
|
+
result << open_graph_meta_tag("og:site_name", options[:site]) if options[:site]
|
256
|
+
result << open_graph_meta_tag("og:description", options[:description]) if options[:description]
|
257
|
+
result << open_graph_meta_tag("og:email", options[:email]) if options[:email]
|
258
|
+
result << open_graph_meta_tag("og:phone_number", options[:phone]) if options[:phone]
|
259
|
+
result << open_graph_meta_tag("og:fax_number", options[:fax]) if options[:fax]
|
260
|
+
result << open_graph_meta_tag("og:latitude", options[:lat]) if options[:lat]
|
261
|
+
result << open_graph_meta_tag("og:longitude", options[:lng]) if options[:lng]
|
262
|
+
result << open_graph_meta_tag("og:street-address", options[:street]) if options[:street]
|
263
|
+
result << open_graph_meta_tag("og:locality", options[:city]) if options[:city]
|
264
|
+
result << open_graph_meta_tag("og:region", options[:state]) if options[:state]
|
265
|
+
result << open_graph_meta_tag("og:postal-code", options[:zip]) if options[:zip]
|
266
|
+
result << open_graph_meta_tag("og:country-name", options[:country]) if options[:country]
|
267
|
+
|
268
|
+
result.join("\n")
|
269
|
+
end
|
270
|
+
|
271
|
+
def open_graph_meta_tag(property, content)
|
272
|
+
capture_haml { haml_tag :meta, :property => property, :content => content }
|
273
|
+
end
|
274
|
+
|
275
|
+
# first, last, next, prev
|
276
|
+
def pagination_meta_tags(options = {})
|
277
|
+
|
278
|
+
end
|
279
|
+
|
280
|
+
private
|
281
|
+
|
282
|
+
def normalize_title(title)
|
283
|
+
if title.is_a? String
|
284
|
+
title = [title]
|
285
|
+
end
|
286
|
+
title.map { |t| h(strip_tags(t)) }
|
287
|
+
end
|
288
|
+
|
289
|
+
def normalize_description(description)
|
290
|
+
return '' unless description
|
291
|
+
truncate(strip_tags(description).gsub(/\s+/, ' '), :length => 200)
|
292
|
+
end
|
293
|
+
|
294
|
+
def normalize_keywords(keywords)
|
295
|
+
return '' unless keywords
|
296
|
+
keywords = keywords.flatten.join(', ') if keywords.is_a?(Array)
|
297
|
+
strip_tags(keywords).mb_chars.downcase
|
298
|
+
end
|
299
|
+
end
|
228
300
|
end
|
229
301
|
end
|