PageRenderTime 41ms CodeModel.GetById 7ms RepoModel.GetById 0ms app.codeStats 0ms

/scalate-jruby/src/main/resources/haml-3.0.25/lib/haml/helpers.rb

http://github.com/scalate/scalate
Ruby | 605 lines | 228 code | 48 blank | 329 comment | 21 complexity | fc53e966f35a25d3dca423e69d510abb MD5 | raw file
  1. module Haml
  2. # This module contains various helpful methods to make it easier to do various tasks.
  3. # {Haml::Helpers} is automatically included in the context
  4. # that a Haml template is parsed in, so all these methods are at your
  5. # disposal from within the template.
  6. module Helpers
  7. # An object that raises an error when \{#to\_s} is called.
  8. # It's used to raise an error when the return value of a helper is used
  9. # when it shouldn't be.
  10. class ErrorReturn
  11. # @param message [String] The error message to raise when \{#to\_s} is called
  12. def initialize(method)
  13. @message = <<MESSAGE
  14. #{method} outputs directly to the Haml template.
  15. Disregard its return value and use the - operator,
  16. or use capture_haml to get the value as a String.
  17. MESSAGE
  18. end
  19. # Raises an error.
  20. #
  21. # @raise [Haml::Error] The error
  22. def to_s
  23. raise Haml::Error.new(@message)
  24. rescue Haml::Error => e
  25. e.backtrace.shift
  26. # If the ErrorReturn is used directly in the template,
  27. # we don't want Haml's stuff to get into the backtrace,
  28. # so we get rid of the format_script line.
  29. #
  30. # We also have to subtract one from the Haml line number
  31. # since the value is passed to format_script the line after
  32. # it's actually used.
  33. if e.backtrace.first =~ /^\(eval\):\d+:in `format_script/
  34. e.backtrace.shift
  35. e.backtrace.first.gsub!(/^\(haml\):(\d+)/) {|s| "(haml):#{$1.to_i - 1}"}
  36. end
  37. raise e
  38. end
  39. # @return [String] A human-readable string representation
  40. def inspect
  41. "Haml::Helpers::ErrorReturn(#{@message.inspect})"
  42. end
  43. end
  44. self.extend self
  45. @@action_view_defined = false
  46. # @return [Boolean] Whether or not ActionView is loaded
  47. def self.action_view?
  48. @@action_view_defined
  49. end
  50. # Note: this does **not** need to be called when using Haml helpers
  51. # normally in Rails.
  52. #
  53. # Initializes the current object as though it were in the same context
  54. # as a normal ActionView instance using Haml.
  55. # This is useful if you want to use the helpers in a context
  56. # other than the normal setup with ActionView.
  57. # For example:
  58. #
  59. # context = Object.new
  60. # class << context
  61. # include Haml::Helpers
  62. # end
  63. # context.init_haml_helpers
  64. # context.haml_tag :p, "Stuff"
  65. #
  66. def init_haml_helpers
  67. @haml_buffer = Haml::Buffer.new(@haml_buffer, Haml::Engine.new('').send(:options_for_buffer))
  68. nil
  69. end
  70. # Runs a block of code in a non-Haml context
  71. # (i.e. \{#is\_haml?} will return false).
  72. #
  73. # This is mainly useful for rendering sub-templates such as partials in a non-Haml language,
  74. # particularly where helpers may behave differently when run from Haml.
  75. #
  76. # Note that this is automatically applied to Rails partials.
  77. #
  78. # @yield A block which won't register as Haml
  79. def non_haml
  80. was_active = @haml_buffer.active?
  81. @haml_buffer.active = false
  82. yield
  83. ensure
  84. @haml_buffer.active = was_active
  85. end
  86. # Uses \{#preserve} to convert any newlines inside whitespace-sensitive tags
  87. # into the HTML entities for endlines.
  88. #
  89. # @param tags [Array<String>] Tags that should have newlines escaped
  90. #
  91. # @overload find_and_preserve(input, tags = haml_buffer.options[:preserve])
  92. # Escapes newlines within a string.
  93. #
  94. # @param input [String] The string within which to escape newlines
  95. # @overload find_and_preserve(tags = haml_buffer.options[:preserve])
  96. # Escapes newlines within a block of Haml code.
  97. #
  98. # @yield The block within which to escape newlines
  99. def find_and_preserve(input = nil, tags = haml_buffer.options[:preserve], &block)
  100. return find_and_preserve(capture_haml(&block), input || tags) if block
  101. input.to_s.gsub(/<(#{tags.map(&Regexp.method(:escape)).join('|')})([^>]*)>(.*?)(<\/\1>)/im) do
  102. "<#{$1}#{$2}>#{preserve($3)}</#{$1}>"
  103. end
  104. end
  105. # Takes any string, finds all the newlines, and converts them to
  106. # HTML entities so they'll render correctly in
  107. # whitespace-sensitive tags without screwing up the indentation.
  108. #
  109. # @overload perserve(input)
  110. # Escapes newlines within a string.
  111. #
  112. # @param input [String] The string within which to escape all newlines
  113. # @overload perserve
  114. # Escapes newlines within a block of Haml code.
  115. #
  116. # @yield The block within which to escape newlines
  117. def preserve(input = nil, &block)
  118. return preserve(capture_haml(&block)) if block
  119. input.to_s.chomp("\n").gsub(/\n/, '&#x000A;').gsub(/\r/, '')
  120. end
  121. alias_method :flatten, :preserve
  122. # Takes an `Enumerable` object and a block
  123. # and iterates over the enum,
  124. # yielding each element to a Haml block
  125. # and putting the result into `<li>` elements.
  126. # This creates a list of the results of the block.
  127. # For example:
  128. #
  129. # = list_of([['hello'], ['yall']]) do |i|
  130. # = i[0]
  131. #
  132. # Produces:
  133. #
  134. # <li>hello</li>
  135. # <li>yall</li>
  136. #
  137. # And
  138. #
  139. # = list_of({:title => 'All the stuff', :description => 'A book about all the stuff.'}) do |key, val|
  140. # %h3= key.humanize
  141. # %p= val
  142. #
  143. # Produces:
  144. #
  145. # <li>
  146. # <h3>Title</h3>
  147. # <p>All the stuff</p>
  148. # </li>
  149. # <li>
  150. # <h3>Description</h3>
  151. # <p>A book about all the stuff.</p>
  152. # </li>
  153. #
  154. # @param enum [Enumerable] The list of objects to iterate over
  155. # @yield [item] A block which contains Haml code that goes within list items
  156. # @yieldparam item An element of `enum`
  157. def list_of(enum, &block)
  158. to_return = enum.collect do |i|
  159. result = capture_haml(i, &block)
  160. if result.count("\n") > 1
  161. result.gsub!("\n", "\n ")
  162. result = "\n #{result.strip}\n"
  163. else
  164. result.strip!
  165. end
  166. "<li>#{result}</li>"
  167. end
  168. to_return.join("\n")
  169. end
  170. # Returns a hash containing default assignments for the `xmlns`, `lang`, and `xml:lang`
  171. # attributes of the `html` HTML element.
  172. # For example,
  173. #
  174. # %html{html_attrs}
  175. #
  176. # becomes
  177. #
  178. # <html xmlns='http://www.w3.org/1999/xhtml' xml:lang='en-US' lang='en-US'>
  179. #
  180. # @param lang [String] The value of `xml:lang` and `lang`
  181. # @return [{#to_s => String}] The attribute hash
  182. def html_attrs(lang = 'en-US')
  183. {:xmlns => "http://www.w3.org/1999/xhtml", 'xml:lang' => lang, :lang => lang}
  184. end
  185. # Increments the number of tabs the buffer automatically adds
  186. # to the lines of the template.
  187. # For example:
  188. #
  189. # %h1 foo
  190. # - tab_up
  191. # %p bar
  192. # - tab_down
  193. # %strong baz
  194. #
  195. # Produces:
  196. #
  197. # <h1>foo</h1>
  198. # <p>bar</p>
  199. # <strong>baz</strong>
  200. #
  201. # @param i [Fixnum] The number of tabs by which to increase the indentation
  202. # @see #tab_down
  203. def tab_up(i = 1)
  204. haml_buffer.tabulation += i
  205. end
  206. # Decrements the number of tabs the buffer automatically adds
  207. # to the lines of the template.
  208. #
  209. # @param i [Fixnum] The number of tabs by which to decrease the indentation
  210. # @see #tab_up
  211. def tab_down(i = 1)
  212. haml_buffer.tabulation -= i
  213. end
  214. # Sets the number of tabs the buffer automatically adds
  215. # to the lines of the template,
  216. # but only for the duration of the block.
  217. # For example:
  218. #
  219. # %h1 foo
  220. # - with_tabs(2) do
  221. # %p bar
  222. # %strong baz
  223. #
  224. # Produces:
  225. #
  226. # <h1>foo</h1>
  227. # <p>bar</p>
  228. # <strong>baz</strong>
  229. #
  230. #
  231. # @param i [Fixnum] The number of tabs to use
  232. # @yield A block in which the indentation will be `i` spaces
  233. def with_tabs(i)
  234. old_tabs = haml_buffer.tabulation
  235. haml_buffer.tabulation = i
  236. yield
  237. ensure
  238. haml_buffer.tabulation = old_tabs
  239. end
  240. # Surrounds a block of Haml code with strings,
  241. # with no whitespace in between.
  242. # For example:
  243. #
  244. # = surround '(', ')' do
  245. # %a{:href => "food"} chicken
  246. #
  247. # Produces:
  248. #
  249. # (<a href='food'>chicken</a>)
  250. #
  251. # and
  252. #
  253. # = surround '*' do
  254. # %strong angry
  255. #
  256. # Produces:
  257. #
  258. # *<strong>angry</strong>*
  259. #
  260. # @param front [String] The string to add before the Haml
  261. # @param back [String] The string to add after the Haml
  262. # @yield A block of Haml to surround
  263. def surround(front, back = front, &block)
  264. output = capture_haml(&block)
  265. "#{front}#{output.chomp}#{back}\n"
  266. end
  267. # Prepends a string to the beginning of a Haml block,
  268. # with no whitespace between.
  269. # For example:
  270. #
  271. # = precede '*' do
  272. # %span.small Not really
  273. #
  274. # Produces:
  275. #
  276. # *<span class='small'>Not really</span>
  277. #
  278. # @param str [String] The string to add before the Haml
  279. # @yield A block of Haml to prepend to
  280. def precede(str, &block)
  281. "#{str}#{capture_haml(&block).chomp}\n"
  282. end
  283. # Appends a string to the end of a Haml block,
  284. # with no whitespace between.
  285. # For example:
  286. #
  287. # click
  288. # = succeed '.' do
  289. # %a{:href=>"thing"} here
  290. #
  291. # Produces:
  292. #
  293. # click
  294. # <a href='thing'>here</a>.
  295. #
  296. # @param str [String] The string to add after the Haml
  297. # @yield A block of Haml to append to
  298. def succeed(str, &block)
  299. "#{capture_haml(&block).chomp}#{str}\n"
  300. end
  301. # Captures the result of a block of Haml code,
  302. # gets rid of the excess indentation,
  303. # and returns it as a string.
  304. # For example, after the following,
  305. #
  306. # .foo
  307. # - foo = capture_haml(13) do |a|
  308. # %p= a
  309. #
  310. # the local variable `foo` would be assigned to `"<p>13</p>\n"`.
  311. #
  312. # @param args [Array] Arguments to pass into the block
  313. # @yield [args] A block of Haml code that will be converted to a string
  314. # @yieldparam args [Array] `args`
  315. def capture_haml(*args, &block)
  316. buffer = eval('_hamlout', block.binding) rescue haml_buffer
  317. with_haml_buffer(buffer) do
  318. position = haml_buffer.buffer.length
  319. haml_buffer.capture_position = position
  320. block.call(*args)
  321. captured = haml_buffer.buffer.slice!(position..-1)
  322. return captured if haml_buffer.options[:ugly]
  323. captured = captured.split(/^/)
  324. min_tabs = nil
  325. captured.each do |line|
  326. tabs = line.index(/[^ ]/) || line.length
  327. min_tabs ||= tabs
  328. min_tabs = min_tabs > tabs ? tabs : min_tabs
  329. end
  330. captured.map do |line|
  331. line[min_tabs..-1]
  332. end.join
  333. end
  334. ensure
  335. haml_buffer.capture_position = nil
  336. end
  337. # Outputs text directly to the Haml buffer, with the proper indentation.
  338. #
  339. # @param text [#to_s] The text to output
  340. def haml_concat(text = "")
  341. unless haml_buffer.options[:ugly] || haml_indent == 0
  342. haml_buffer.buffer << haml_indent <<
  343. text.to_s.gsub("\n", "\n" + haml_indent) << "\n"
  344. else
  345. haml_buffer.buffer << text.to_s << "\n"
  346. end
  347. ErrorReturn.new("haml_concat")
  348. end
  349. # @return [String] The indentation string for the current line
  350. def haml_indent
  351. ' ' * haml_buffer.tabulation
  352. end
  353. # Creates an HTML tag with the given name and optionally text and attributes.
  354. # Can take a block that will run between the opening and closing tags.
  355. # If the block is a Haml block or outputs text using \{#haml\_concat},
  356. # the text will be properly indented.
  357. #
  358. # `name` can be a string using the standard Haml class/id shorthand
  359. # (e.g. "span#foo.bar", "#foo").
  360. # Just like standard Haml tags, these class and id values
  361. # will be merged with manually-specified attributes.
  362. #
  363. # `flags` is a list of symbol flags
  364. # like those that can be put at the end of a Haml tag
  365. # (`:/`, `:<`, and `:>`).
  366. # Currently, only `:/` and `:<` are supported.
  367. #
  368. # `haml_tag` outputs directly to the buffer;
  369. # its return value should not be used.
  370. # If you need to get the results as a string,
  371. # use \{#capture\_haml\}.
  372. #
  373. # For example,
  374. #
  375. # haml_tag :table do
  376. # haml_tag :tr do
  377. # haml_tag 'td.cell' do
  378. # haml_tag :strong, "strong!"
  379. # haml_concat "data"
  380. # end
  381. # haml_tag :td do
  382. # haml_concat "more_data"
  383. # end
  384. # end
  385. # end
  386. #
  387. # outputs
  388. #
  389. # <table>
  390. # <tr>
  391. # <td class='cell'>
  392. # <strong>
  393. # strong!
  394. # </strong>
  395. # data
  396. # </td>
  397. # <td>
  398. # more_data
  399. # </td>
  400. # </tr>
  401. # </table>
  402. #
  403. # @param name [#to_s] The name of the tag
  404. # @param flags [Array<Symbol>] Haml end-of-tag flags
  405. #
  406. # @overload haml_tag(name, *flags, attributes = {})
  407. # @yield The block of Haml code within the tag
  408. # @overload haml_tag(name, text, *flags, attributes = {})
  409. # @param text [#to_s] The text within the tag
  410. def haml_tag(name, *rest, &block)
  411. ret = ErrorReturn.new("haml_tag")
  412. text = rest.shift.to_s unless [Symbol, Hash, NilClass].any? {|t| rest.first.is_a? t}
  413. flags = []
  414. flags << rest.shift while rest.first.is_a? Symbol
  415. attrs = Haml::Util.map_keys(rest.shift || {}) {|key| key.to_s}
  416. name, attrs = merge_name_and_attributes(name.to_s, attrs)
  417. attributes = Haml::Precompiler.build_attributes(haml_buffer.html?,
  418. haml_buffer.options[:attr_wrapper],
  419. attrs)
  420. if text.nil? && block.nil? && (haml_buffer.options[:autoclose].include?(name) || flags.include?(:/))
  421. haml_concat "<#{name}#{attributes} />"
  422. return ret
  423. end
  424. if flags.include?(:/)
  425. raise Error.new("Self-closing tags can't have content.") if text
  426. raise Error.new("Illegal nesting: nesting within a self-closing tag is illegal.") if block
  427. end
  428. tag = "<#{name}#{attributes}>"
  429. if block.nil?
  430. text = text.to_s
  431. if text.include?("\n")
  432. haml_concat tag
  433. tab_up
  434. haml_concat text
  435. tab_down
  436. haml_concat "</#{name}>"
  437. else
  438. tag << text << "</#{name}>"
  439. haml_concat tag
  440. end
  441. return ret
  442. end
  443. if text
  444. raise Error.new("Illegal nesting: content can't be both given to haml_tag :#{name} and nested within it.")
  445. end
  446. if flags.include?(:<)
  447. tag << capture_haml(&block).strip << "</#{name}>"
  448. haml_concat tag
  449. return ret
  450. end
  451. haml_concat tag
  452. tab_up
  453. block.call
  454. tab_down
  455. haml_concat "</#{name}>"
  456. ret
  457. end
  458. # Characters that need to be escaped to HTML entities from user input
  459. HTML_ESCAPE = { '&'=>'&amp;', '<'=>'&lt;', '>'=>'&gt;', '"'=>'&quot;', "'"=>'&#039;', }
  460. # Returns a copy of `text` with ampersands, angle brackets and quotes
  461. # escaped into HTML entities.
  462. #
  463. # Note that if ActionView is loaded and XSS protection is enabled
  464. # (as is the default for Rails 3.0+, and optional for version 2.3.5+),
  465. # this won't escape text declared as "safe".
  466. #
  467. # @param text [String] The string to sanitize
  468. # @return [String] The sanitized string
  469. def html_escape(text)
  470. Haml::Util.silence_warnings {text.to_s.gsub(/[\"><&]/n) {|s| HTML_ESCAPE[s]}}
  471. end
  472. # Escapes HTML entities in `text`, but without escaping an ampersand
  473. # that is already part of an escaped entity.
  474. #
  475. # @param text [String] The string to sanitize
  476. # @return [String] The sanitized string
  477. def escape_once(text)
  478. Haml::Util.silence_warnings do
  479. text.to_s.gsub(/[\"><]|&(?!(?:[a-zA-Z]+|(#\d+));)/n) {|s| HTML_ESCAPE[s]}
  480. end
  481. end
  482. # Returns whether or not the current template is a Haml template.
  483. #
  484. # This function, unlike other {Haml::Helpers} functions,
  485. # also works in other `ActionView` templates,
  486. # where it will always return false.
  487. #
  488. # @return [Boolean] Whether or not the current template is a Haml template
  489. def is_haml?
  490. !@haml_buffer.nil? && @haml_buffer.active?
  491. end
  492. # Returns whether or not `block` is defined directly in a Haml template.
  493. #
  494. # @param block [Proc] A Ruby block
  495. # @return [Boolean] Whether or not `block` is defined directly in a Haml template
  496. def block_is_haml?(block)
  497. eval('_hamlout', block.binding)
  498. true
  499. rescue
  500. false
  501. end
  502. private
  503. # Parses the tag name used for \{#haml\_tag}
  504. # and merges it with the Ruby attributes hash.
  505. def merge_name_and_attributes(name, attributes_hash = {})
  506. # skip merging if no ids or classes found in name
  507. return name, attributes_hash unless name =~ /^(.+?)?([\.#].*)$/
  508. return $1 || "div", Buffer.merge_attrs(
  509. Precompiler.parse_class_and_id($2), attributes_hash)
  510. end
  511. # Runs a block of code with the given buffer as the currently active buffer.
  512. #
  513. # @param buffer [Haml::Buffer] The Haml buffer to use temporarily
  514. # @yield A block in which the given buffer should be used
  515. def with_haml_buffer(buffer)
  516. @haml_buffer, old_buffer = buffer, @haml_buffer
  517. old_buffer.active, old_was_active = false, old_buffer.active? if old_buffer
  518. @haml_buffer.active, was_active = true, @haml_buffer.active?
  519. yield
  520. ensure
  521. @haml_buffer.active = was_active
  522. old_buffer.active = old_was_active if old_buffer
  523. @haml_buffer = old_buffer
  524. end
  525. # The current {Haml::Buffer} object.
  526. #
  527. # @return [Haml::Buffer]
  528. def haml_buffer
  529. @haml_buffer
  530. end
  531. # Gives a proc the same local `_hamlout` and `_erbout` variables
  532. # that the current template has.
  533. #
  534. # @param proc [#call] The proc to bind
  535. # @return [Proc] A new proc with the new variables bound
  536. def haml_bind_proc(&proc)
  537. _hamlout = haml_buffer
  538. _erbout = _hamlout.buffer
  539. proc { |*args| proc.call(*args) }
  540. end
  541. end
  542. end
  543. # @private
  544. class Object
  545. # Haml overrides various `ActionView` helpers,
  546. # which call an \{#is\_haml?} method
  547. # to determine whether or not the current context object
  548. # is a proper Haml context.
  549. # Because `ActionView` helpers may be included in non-`ActionView::Base` classes,
  550. # it's a good idea to define \{#is\_haml?} for all objects.
  551. def is_haml?
  552. false
  553. end
  554. end