class StringMaster require "erb" require "action_view" include ERB::Util include ActionView::Helpers attr_reader(:modified_string) alias :string :modified_string def initialize(string) @initial_string = String.new(string.html_safe) @modified_string = String.new(string.html_safe) yield(self) if block_given? end # Closes all unclosed tags that need to be closed (i.e. skips ,
etc.) def close_tags text = @modified_string open_tags = [] text.scan(/<([a-zA-Z0-9]+?)(\s[^>]*)?>/).each { |t| open_tags.unshift(t[0]) } text.scan(/<\/\s*?([a-zA-Z0-9]+)\s*?>/).each { |t| open_tags.slice!(open_tags.index(t[0])) unless open_tags.index(t[0]).nil? } open_tags.each { |t| text += "" unless %w(img br hr).include?(t.to_s) } @modified_string = text return self end # escapes all tags except the ones, that are listed in :except option def html_escape(options={}) except = options[:except] || %w() close_tags @modified_string.gsub!(/<\/?([^<]*?)(\s[^>]*?)?\/?>/) do |tag| if except.include?($1) # sanitize attributes tag.gsub(/\s(.+?)=('|").*?\2(?=.*?>)/) do |a| ["href", "src", "lang"].include?($1) ? a : "" end else h(tag) end end # Convert all unclosed left tag brackets (<) into < @modified_string.gsub!(/(<)([^>]*\Z)/, '<\2') self end # Creates tags for all urls that look like images. def urls_to_images(options = {}) wrap_with = options[:wrap_with] || ['',''] html_options = options[:html_options] || '' @modified_string.gsub!( /(\s|^|\A|\n|\t|\r)((http|https):\/\/.*?\.(jpg|jpeg|png|gif|JPG|JPEG|PNG|GIF))([,.])?(\s|$|\n|\Z|\t|\r)/, "#{wrap_with[0]}\"\"\\6#{wrap_with[1]}" ) self end # Creates tags for all urls. # IMPORTANT: make sure you've used #urls_to_images method first # if you wanted all images urls to become tags. def urls_to_links(options = {}) wrap_with = options[:wrap_with] || ['',''] html_options = options[:html_options] || '' @modified_string.gsub!( /(\s|^|\A|\n|\t|\r)((http|https):\/\/.*?)([,.])?(\s|$|\n|\Z|\t|\r|<)/, '\1\2\4\5' ) self end # Breaks words that are longer than 'length' def break_long_words(length=75, break_char=" ", &block) @modified_string.gsub!(/]+>|]+>|([^\s^\n^\^^\A^\t^\r<]{#{length},}?)|<\/a>/) do |s| if $1 ns = block_given? ? yield($1) : $1 last_pos, result_string = 0, '' while string = ns[last_pos..(last_pos + length)] result_string += string + break_char last_pos += (length + 1) end result_string else s end end self end # Cuts a string starting at 'cut_at' if the string is longer than 'length'. # Appends characters (if specified in :append) to the end of the cut string. def cut(length, cut_at, options = {}) append = options[:append] || '' @modified_string.size > (length) ? @modified_string = @modified_string.mb_chars[0...cut_at] + append : @modified_message self end def newlines_to_br @modified_string.gsub!("\n", "
") self end # Finds lines of text that satisfy a 'regexp' and wraps them into an # opening and closing 'tag'. Best example of usage is #wrap_code. # # Also receives an option :remove_newlines (default is false) # which removes \n around the tag that wraps the lines of text. Useful if # you're using this method with #newlines_to_br to avoid extra space that may or # may not be in the user's input. def wrap_lines(tag, regexp, options={remove_newlines: false}) code_tag = nil; result = "" @modified_string.each_line do |line| if line =~ regexp unless code_tag == :opened result.chomp! if options[:remove_newlines] result += "<#{tag}>" code_tag = :opened end result += line.sub(regexp, '') else if code_tag == :opened code_tag = :closed result.chomp! result += "" elsif code_tag == :closed code_tag = nil result.chomp! if options[:remove_newlines] end result += line end end # Make sure there's always a closing tag if code_tag == :opened result.chomp! result += "\n" end @modified_string = result self end # Finds all lines that start with 4 spaces and wraps them into tags. # It also transforms each occurence of 2 or more spaces into an   entity, # which is available as a standalone method #preserve_whitespace def wrap_code wrap_lines("code", /\A\s{4}/, remove_newlines: true) preserve_whitespace_within("code") # already returns `self` end # Preserves whitespace within a given tag. Each occurence of 2 or more spaces # is transformed into a   entities. def preserve_whitespace_within(tag) @modified_string.gsub!(/<#{tag}>(.|\n)+?<\/#{tag}>/) do |match| match.gsub(/( )( )+/) { |m| " "*m.length } end self end def wrap_inline_code(opening_tag="", closing_tag="") @modified_string.gsub!(/`(.+?)`/m, opening_tag + '\1' + closing_tag) self end # Same as wrap_inline_code, but spans across multiplelines def wrap_backticks_code(opening_tag="", closing_tag="") @modified_string.gsub!(/`(.+?)`/m, opening_tag + '\1' + closing_tag) self end def to_s modified_string.html_safe end end