# -*- coding: utf-8 -*-##--# Copyright (C) 2009-2013 Thomas Leitner <t_leitner@gmx.at>## This file is part of kramdown which is licensed under the MIT.#++#moduleKramdownmoduleParserclassKramdownIAL_CLASS_ATTR='class'# Parse the string +str+ and extract all attributes and add all found attributes to the hash# +opts+.defparse_attribute_list(str,opts)attrs=str.scan(ALD_TYPE_ANY)attrs.eachdo|key,sep,val,ref,id_and_or_class,_,_|ifref(opts[:refs]||=[])<<refelsifid_and_or_classid_and_or_class.scan(ALD_TYPE_ID_OR_CLASS).eachdo|id_attr,class_attr|ifclass_attropts[IAL_CLASS_ATTR]=(opts[IAL_CLASS_ATTR]||'')<<" #{class_attr}"opts[IAL_CLASS_ATTR].lstrip!elseopts['id']=id_attrendendelseval.gsub!(/\\(\}|#{sep})/,"\\1")opts[key]=valendendwarning("No or invalid attributes found in IAL/ALD content: #{str}")ifattrs.length==0end# Update the +ial+ with the information from the inline attribute list +opts+.defupdate_ial_with_ial(ial,opts)(ial[:refs]||=[])<<opts[:refs]opts.eachdo|k,v|ifk==IAL_CLASS_ATTRial[k]=(ial[k]||'')<<" #{v}"ial[k].lstrip!elsifk.kind_of?(String)ial[k]=vendendend# Parse the generic extension at the current point. The parameter +type+ can either be :block# or :span depending whether we parse a block or span extension tag.defparse_extension_start_tag(type)orig_pos=@src.pos@src.pos+=@src.matched_sizeerror_block=lambdado|msg|warning(msg)@src.pos=orig_posadd_text(@src.getch)iftype==:spanfalseendif@src[4]||@src.matched=='{:/}'name=(@src[4]?"for '#{@src[4]}' ":'')returnerror_block.call("Invalid extension stop tag #{name}found - ignoring it")endext=@src[1]opts={}body=nilparse_attribute_list(@src[2]||'',opts)if!@src[3]stop_re=(type==:block?/#{EXT_BLOCK_STOP_STR%ext}/:/#{EXT_STOP_STR%ext}/)ifresult=@src.scan_until(stop_re)body=result.sub!(stop_re,'')body.chomp!iftype==:blockelsereturnerror_block.call("No stop tag for extension '#{ext}' found - ignoring it")endendif!handle_extension(ext,opts,body,type)error_block.call("Invalid extension with name '#{ext}' specified - ignoring it")elsetrueendenddefhandle_extension(name,opts,body,type)casenamewhen'comment'@tree.children<<Element.new(:comment,body,nil,:category=>type)ifbody.kind_of?(String)truewhen'nomarkdown'@tree.children<<Element.new(:raw,body,nil,:category=>type,:type=>opts['type'].to_s.split(/\s+/))ifbody.kind_of?(String)truewhen'options'opts.selectdo|k,v|k=k.to_symifKramdown::Options.defined?(k)beginval=Kramdown::Options.parse(k,v)@options[k]=val(@root.options[:options]||={})[k]=valrescueendfalseelsetrueendend.eachdo|k,v|warning("Unknown kramdown option '#{k}'")end@tree.children<<Element.new(:eob,:extension)iftype==:blocktrueelsefalseendendALD_ID_CHARS=/[\w-]/ALD_ANY_CHARS=/\\\}|[^\}]/ALD_ID_NAME=/\w#{ALD_ID_CHARS}*/ALD_TYPE_KEY_VALUE_PAIR=/(#{ALD_ID_NAME})=("|')((?:\\\}|\\\2|[^\}\2])*?)\2/ALD_TYPE_CLASS_NAME=/\.(#{ALD_ID_NAME})/ALD_TYPE_ID_NAME=/#(\w[\w:-]*)/ALD_TYPE_ID_OR_CLASS=/#{ALD_TYPE_ID_NAME}|#{ALD_TYPE_CLASS_NAME}/ALD_TYPE_ID_OR_CLASS_MULTI=/((?:#{ALD_TYPE_ID_NAME}|#{ALD_TYPE_CLASS_NAME})+)/ALD_TYPE_REF=/(#{ALD_ID_NAME})/ALD_TYPE_ANY=/(?:\A|\s)(?:#{ALD_TYPE_KEY_VALUE_PAIR}|#{ALD_TYPE_REF}|#{ALD_TYPE_ID_OR_CLASS_MULTI})(?=\s|\Z)/ALD_START=/^#{OPT_SPACE}\{:(#{ALD_ID_NAME}):(#{ALD_ANY_CHARS}+)\}\s*?\n/EXT_STOP_STR="\\{:/(%s)?\\}"EXT_START_STR="\\{::(\\w+)(?:\\s(#{ALD_ANY_CHARS}*?)|)(\\/)?\\}"EXT_BLOCK_START=/^#{OPT_SPACE}(?:#{EXT_START_STR}|#{EXT_STOP_STR%ALD_ID_NAME})\s*?\n/EXT_BLOCK_STOP_STR="^#{OPT_SPACE}#{EXT_STOP_STR}\s*?\n"IAL_BLOCK=/\{:(?!:|\/)(#{ALD_ANY_CHARS}+)\}\s*?\n/IAL_BLOCK_START=/^#{OPT_SPACE}#{IAL_BLOCK}/BLOCK_EXTENSIONS_START=/^#{OPT_SPACE}\{:/# Parse one of the block extensions (ALD, block IAL or generic extension) at the current# location.defparse_block_extensionsif@src.scan(ALD_START)parse_attribute_list(@src[2],@alds[@src[1]]||=Utils::OrderedHash.new)@tree.children<<Element.new(:eob,:ald)trueelsif@src.check(EXT_BLOCK_START)parse_extension_start_tag(:block)elsif@src.scan(IAL_BLOCK_START)if@tree.children.last&&@tree.children.last.type!=:blank&&@tree.children.last.type!=:eobparse_attribute_list(@src[1],@tree.children.last.options[:ial]||=Utils::OrderedHash.new)@tree.children<<Element.new(:eob,:ial)unless@src.check(IAL_BLOCK_START)elseparse_attribute_list(@src[1],@block_ial=Utils::OrderedHash.new)endtrueelsefalseendenddefine_parser(:block_extensions,BLOCK_EXTENSIONS_START)EXT_SPAN_START=/#{EXT_START_STR}|#{EXT_STOP_STR%ALD_ID_NAME}/IAL_SPAN_START=/\{:(#{ALD_ANY_CHARS}+)\}/SPAN_EXTENSIONS_START=/\{:/# Parse the extension span at the current location.defparse_span_extensionsif@src.check(EXT_SPAN_START)parse_extension_start_tag(:span)elsif@src.check(IAL_SPAN_START)if@tree.children.last&&@tree.children.last.type!=:text@src.pos+=@src.matched_sizeattr=Utils::OrderedHash.newparse_attribute_list(@src[1],attr)update_ial_with_ial(@tree.children.last.options[:ial]||=Utils::OrderedHash.new,attr)update_attr_with_ial(@tree.children.last.attr,attr)elsewarning("Found span IAL after text - ignoring it")add_text(@src.getch)endelseadd_text(@src.getch)endenddefine_parser(:span_extensions,SPAN_EXTENSIONS_START,'\{:')endendend