1 # encoding: utf-8
2 require 'active_support/core_ext/module/delegation'
3 require 'active_support/deprecation'
4
5 module ActiveSupport
6 class << self
7 delegate :use_standard_json_time_format, :use_standard_json_time_format=,
8 :escape_html_entities_in_json, :escape_html_entities_in_json=,
9 :to => :'ActiveSupport::JSON::Encoding'
10 end
11
12 module JSON
13 # matches YAML-formatted dates
14 DATE_REGEX = /^(?:\d{4}-\d{2}-\d{2}|\d{4}-\d{1,2}-\d{1,2}[ \t]+\d{1,2}:\d{2}:\d{2}(\.[0-9]*)?(([ \t]*)Z|[-+]\d{2}?(:\d{2})?))$/
15
16 class << self
17 delegate :encode, :to => :'ActiveSupport::JSON::Encoding'
18 end
19
20 module Encoding #:nodoc:
21 class CircularReferenceError < StandardError
22 end
23
24 ESCAPED_CHARS = {
25 "\x00" => '\u0000', "\x01" => '\u0001', "\x02" => '\u0002',
26 "\x03" => '\u0003', "\x04" => '\u0004', "\x05" => '\u0005',
27 "\x06" => '\u0006', "\x07" => '\u0007', "\x0B" => '\u000B',
28 "\x0E" => '\u000E', "\x0F" => '\u000F', "\x10" => '\u0010',
29 "\x11" => '\u0011', "\x12" => '\u0012', "\x13" => '\u0013',
30 "\x14" => '\u0014', "\x15" => '\u0015', "\x16" => '\u0016',
31 "\x17" => '\u0017', "\x18" => '\u0018', "\x19" => '\u0019',
32 "\x1A" => '\u001A', "\x1B" => '\u001B', "\x1C" => '\u001C',
33 "\x1D" => '\u001D', "\x1E" => '\u001E', "\x1F" => '\u001F',
34 "\010" => '\b',
35 "\f" => '\f',
36 "\n" => '\n',
37 "\r" => '\r',
38 "\t" => '\t',
39 '"' => '\"',
40 '\\' => '\\\\',
41 '>' => '\u003E',
42 '<' => '\u003C',
43 '&' => '\u0026' }
44
45 class << self
46 # If true, use ISO 8601 format for dates and times. Otherwise, fall back to the Active Support legacy format.
47 attr_accessor :use_standard_json_time_format
48
49 attr_accessor :escape_regex
50 attr_reader :escape_html_entities_in_json
51
52 def escape_html_entities_in_json=(value)
53 self.escape_regex = \
54 if @escape_html_entities_in_json = value
55 /[\x00-\x1F"\\><&]/
56 else
57 /[\x00-\x1F"\\]/
58 end
59 end
60
61 def escape(string)
62 string = string.dup.force_encoding(::Encoding::BINARY) if string.respond_to?(:force_encoding)
63 json = string.
64 gsub(escape_regex) { |s| ESCAPED_CHARS[s] }.
65 gsub(/([\xC0-\xDF][\x80-\xBF]|
66 [\xE0-\xEF][\x80-\xBF]{2}|
67 [\xF0-\xF7][\x80-\xBF]{3})+/nx) { |s|
68 s.unpack("U*").pack("n*").unpack("H*")[0].gsub(/.{4}/n, '\\\\u\&')
69 }
70 %("#{json}")
71 end
72
73 # Converts a Ruby object into a JSON string.
74 def encode(value, options = nil)
75 options = {} unless Hash === options
76 seen = (options[:seen] ||= [])
77 raise CircularReferenceError, 'object references itself' if seen.include?(value)
78 seen << value
79 value.to_json(options)
80 ensure
81 seen.pop
82 end
83 end
84
85 self.escape_html_entities_in_json = true
86 end
87
88 CircularReferenceError = Deprecation::DeprecatedConstantProxy.new('ActiveSupport::JSON::CircularReferenceError', Encoding::CircularReferenceError)
89 end
90 end
91
92 # Hack to load json gem first so we can overwrite its to_json.
93 begin
94 require 'json'
95 rescue LoadError
96 end
97
98 require 'active_support/json/variable'
99 require 'active_support/json/encoders/date'
100 require 'active_support/json/encoders/date_time'
101 require 'active_support/json/encoders/enumerable'
102 require 'active_support/json/encoders/false_class'
103 require 'active_support/json/encoders/hash'
104 require 'active_support/json/encoders/nil_class'
105 require 'active_support/json/encoders/numeric'
106 require 'active_support/json/encoders/object'
107 require 'active_support/json/encoders/regexp'
108 require 'active_support/json/encoders/string'
109 require 'active_support/json/encoders/symbol'
110 require 'active_support/json/encoders/time'
111 require 'active_support/json/encoders/true_class'