# encoding: UTF-8
require 'erb'
require 'uri'
module Pizzazz
# A class for turning JSON into HTML.
class Colorer
def initialize(object, options = nil)
options ||= {}
@object = object
@indent = 0
@array_limit = options[:array_limit] || options[:limit] || 0
@array_omission = options[:array_omission] || '…'
@value_limit = options[:value_limit] || 0
@value_omission = options[:value_omission] || '…'
@tab = options[:tab] || ' '
@prefix = options[:prefix]
@detect_links = options[:detect_links].nil? ? true : options[:detect_links]
@sort_keys = options[:sort_keys].nil? ? true : options[:sort_keys]
@class_name_prefix = options[:class_name_prefix] || ''
end
def ify
return '' unless @object
# Parse
output = node(@object)
return output unless @prefix
# Add prefix
prefix = span(@prefix, 'prefix')
lines = output.split("\n")
prefix + lines.join("\n#{prefix}")
end
private
URL_PATTERN = %r{^(https?://)?([\da-z\.-]+)\.([a-z\.]{2,})([/\w \.-]*)*/?$}i
def tab
span @tab * @indent, 'control tab'
end
def truncate(string)
return string if @value_limit < 1
text = string.dup
stop = @value_limit - @value_omission.length
(text.length > @value_limit ? text[0...stop] + span(@value_omission, 'omission') : text).to_s
end
def node(object)
case object
when String
@detect_links && link?(object) ? link(object) : string(object)
when Time
span object.to_json, 'string time'
when TrueClass
span 'true', 'boolean true'
when FalseClass
span 'false', 'boolean false'
when NilClass
span 'null', 'null'
when Numeric
span object, 'number'
when Hash
hash object
when Array
array object
end
end
def link?(string)
scheme = URI.parse(string).scheme
scheme == 'http' || scheme == 'https'
rescue
false
end
def span(content, class_names = nil)
class_names = class_names.split(' ') if class_names.is_a?(String)
class_names = if class_names.empty?
''
else
%( class="#{class_names.map { |name| @class_name_prefix + name }.join(' ')}")
end
%(#{content})
end
def array_omission
span @array_omission, 'omission array'
end
def text(object, should_truncate = false)
object = object.gsub("\n", '\n')
object = ::ERB::Util.h(object)
object = truncate(object) if should_truncate
opening_quote + span(object, 'text') + closing_quote
end
def string(object)
span text(object, true), 'string'
end
def link(object)
a = %(#{text(object)})
span a, 'string link'
end
def hash(object)
return span(opening_curly + closing_curly, 'dictionary') if object.empty?
@indent += 1
string = opening_curly + "\n"
rows = []
keys = object.keys.collect(&:to_s)
keys.sort! if @sort_keys
keys.each do |key|
value = (!object[key].nil? ? object[key] : object[key.to_sym])
row = %(#{span(text(key), 'string key') + colon} #{node(value)})
# Wrap row in with class for key.
# Hopefully most keys will be sane since it's probably JSON.
row = tab + span(row, "key-#{key}")
rows << row
end
string << rows.join(comma + "\n")
@indent -= 1
string << "\n" + tab + closing_curly
span string, 'dictionary'
end
def array(object)
return span(opening_square + closing_square, 'array') if object.empty?
@indent += 1
string = opening_square + "\n"
rows = []
array = @array_limit > 0 ? object[0...@array_limit] : object
array.each do |value|
rows << tab + node(value)
end
if @array_limit > 0 && object.length > @array_limit
rows << tab + (object[0].is_a?(Hash) ? %(#{opening_curly} #{array_omission} #{closing_curly}) : array_omission)
end
string << rows.join(comma + "\n")
@indent -= 1
string << "\n" + tab + closing_square
span string, 'array'
end
def opening_quote
span '"', 'control quote opening'
end
def closing_quote
span '"', 'control quote closing'
end
def opening_curly
span '{', 'control bracket curly opening'
end
def closing_curly
span '}', 'control bracket curly closing'
end
def opening_square
span '[', 'control bracket square opening'
end
def closing_square
span ']', 'control bracket square closing'
end
def comma
span ',', 'control comma'
end
def colon
span ':', 'control colon'
end
end
end