module EditorJs module Blocks class Base InvalidBlockDataError = Class.new(StandardError) InvalidBlockTypeError = Class.new(StandardError) include ActionView::Helpers::TagHelper include ActionView::Helpers::TextHelper include ERB::Util require 'active_support/core_ext/string/inflections' # ActionView::Helpers::TagHelper requires output_buffer accessor attr_accessor :raw, :output_buffer def initialize(raw) @raw = raw @content = cast_block_data_to_hash(raw.deep_dup) sanitize! end # Define JSON format of data def schema raise NotImplementedError end # Render HTML def render(_options = {}) raise NotImplementedError end # Sanitize content of data def sanitize!; end # Render plain text, for full-text searching def plain; end # Validate data def valid? JSON::Validator.validate(schema, data) end def type self.class.type end def data @content['data'] end def decode_html(string) html_decoder.decode(string) end def css_name(name = nil) "#{css_prefix}#{name}" end def output @content end def self.type @type ||= self.to_s.underscore.split('/').last.gsub('_block', '') end def self.inherited(parent) ::EditorJs::Blocks::Base.registry[parent.type] = parent super end def self.load(block_data) block_data = JSON.parse(block_data) unless block_data.is_a?(Hash) klass = ::EditorJs::Blocks::Base.registry[block_data['type'].underscore] raise InvalidBlockTypeError, block_data['type'] if klass.nil? klass.new(block_data) end def self.registry @registry ||= {} end private def cast_block_data_to_hash(block_data) raise InvalidBlockDataError, block_data unless block_data.is_a?(Hash) block_data = block_data.deep_stringify_keys unless block_data['type'].underscore == type raise InvalidBlockDataError,\ "block type <#{block_data['type']}> doesn't match <#{type}>" end block_data rescue JSON::ParserError => _e raise InvalidBlockDataError, "Invalid JSON: #{block_data}" end def css_prefix @css_prefix ||= "#{EditorJs.css_name_prefix}#{type}" end def html_decoder @html_decoder ||= begin with_customized_html_mappings do HTMLEntities::Decoder.new('expanded') end end end def with_customized_html_mappings original_mappings = HTMLEntities::MAPPINGS['expanded'] customized_mappings = original_mappings.dup customized_mappings['nbsp'] = 32 HTMLEntities::MAPPINGS['expanded'] = customized_mappings yield ensure HTMLEntities::MAPPINGS['expanded'] = original_mappings end end end end