1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235
|
#!/usr/bin/env ruby -w
# encoding: UTF-8
#
# = RichText.rb -- The TaskJuggler III Project Management Software
#
# Copyright (c) 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013, 2014
# by Chris Schlaeger <cs@taskjuggler.org>
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of version 2 of the GNU General Public License as
# published by the Free Software Foundation.
#
require 'taskjuggler/RichText/Element'
require 'taskjuggler/RichText/Parser'
require 'taskjuggler/MessageHandler'
class TaskJuggler
# RichText is a MediaWiki markup parser and HTML generator implemented in
# pure Ruby. It can also generate plain text versions of the original markup
# text. It is based on the TextParser class to implement the
# RichTextParser. The scanner is implemented in the RichTextScanner class.
# The read-in text is converted into a tree of RichTextElement objects.
# These can then be turned into HTML element trees modelled by XMLElement or
# plain text.
#
# This class supports the following mark-ups:
#
# The following markups are block commands and must start at the beginning of
# the line.
#
# == Headline 1 ==
# === Headline 2 ===
# ==== Headline 3 ====
#
# ---- creates a horizontal line
#
# * Bullet 1
# ** Bullet 2
# *** Bullet 3
#
# # Enumeration Level 1
# ## Enumeration Level 2
# ### Enumeration Level 3
#
# Preformatted text start with
# a single space at the start of
# each line.
#
#
# The following are in-line mark-ups and can occur within any text block
#
# This is an ''italic'' word.
# This is a '''bold''' word.
# This is a ''''monospaced'''' word. This is not part of the original
# MediaWiki markup, but we needed monospaced as well.
# This is a '''''italic and bold''''' word.
#
# Linebreaks are ignored if not followed by a blank line.
#
# [http://www.taskjuggler.org] A web link
# [http://www.taskjuggler.org The TaskJuggler Web Site] another link
#
# [[item]] site internal internal reference (in HTML .html gets appended
# automatically)
# [[item An item]] another internal reference
# [[function:path arg1 arg2 ...]]
#
# <nowiki> ... </nowiki> Disable markup interpretation for the enclosed
# portion of text.
#
class RichText
attr_reader :inputText
# The Parser uses complex to setup data structures that are identical for
# all RichText instances. So, we'll share them across the instances.
@@parser = nil
# Create a rich text object by passing a String with markup elements to it.
# _text_ must be plain text with MediaWiki compatible markup elements. In
# case an error occurs, an exception of type TjException will be raised.
# _functionHandlers_ is a Hash that maps RichTextFunctionHandler objects
# by their function name.
def initialize(text, functionHandlers = [])
# Keep a copy of the original text.
@inputText = text
@functionHandlers = functionHandlers
end
# Convert the @inputText into an abstract syntax tree that can then be
# converted into the various output formats. _sectionCounter_ is an Array
# that holds the initial values for the section counters.
def generateIntermediateFormat(sectionCounter = [ 0, 0, 0], tokenSet = nil)
rti = RichTextIntermediate.new(self)
# Copy the function handlers.
@functionHandlers.each do |h|
rti.registerFunctionHandler(h)
end
# We'll setup the RichTextParser once and share it across all instances.
if @@parser
# We already have a RichTextParser that we can reuse.
@@parser.reuse(rti, sectionCounter, tokenSet)
else
# There is no RichTextParser yet, create one.
@@parser = RichTextParser.new(rti, sectionCounter, tokenSet)
end
@@parser.open(@inputText)
# Parse the input text and convert it to the intermediate representation.
return nil if (tree = @@parser.parse(:richtext)) == false
# In case the result is empty, use an empty RichTextElement as result
tree = RichTextElement.new(rti, :richtext, nil) unless tree
tree.cleanUp
rti.tree = tree
rti
end
# Return the RichTextFunctionHandler for the function _name_. _block_
# specifies whether we are looking for a block or inline function.
def functionHandler(name, block)
@functionHandlers.each do |handler|
return handler if handler.function == name &&
handler.blockFunction == block
end
nil
end
private
end
# The RichTextIntermediate is a container for the intermediate
# representation of a RichText object. By calling the to_* members it can be
# converted into the respective formats. A RichTextIntermediate object is
# generated by RichText::generateIntermediateFormat.
class RichTextIntermediate
attr_reader :richText, :functionHandlers
attr_accessor :blockMode, :sectionNumbers,
:lineWidth, :indent, :titleIndent, :parIndent, :listIndent,
:preIndent,
:linkTarget, :cssClass, :tree
def initialize(richText)
# A reference to the corresponding RichText object the RTI is derived
# from.
@richText = richText
# The root of the generated intermediate format. This is a
# RichTextElement.
@tree = nil
# The blockMode specifies whether the RichText should be interpreted as
# a line of text or a block (default).
@blockMode = true
# Set this to false to disable automatically generated section numbers.
@sectionNumbers = true
# Set this to the maximum width used for text output.
@lineWidth = 80
# The indentation used for all text output.
@indent = 0
# Additional indentation used for titles in text output.
@titleIndent = 0
# Additional indentation used for paragraph text output.
@parIndent = 0
# Additional indentation used for lists in text output.
@listIndent = 1
# Additional indentation used for <pre> sections in text output.
@preIndent = 0
# The target used for hypertext links.
@linkTarget = nil
# The CSS class used for some key HTML elements.
@cssClass = nil
# These are the RichTextFunctionHandler objects to handle references with
# a function specification.
@functionHandlers = {}
end
# Use this function to register new RichTextFunctionHandler objects with
# this class.
def registerFunctionHandler(functionHandler)
raise "Bad function handler" unless functionHandler
@functionHandlers[functionHandler.function] = functionHandler.dup
end
# Return the handler for the given _function_ or raise an exception if it
# does not exist.
def functionHandler(function)
@functionHandlers[function]
end
# Return true if the RichText has no content.
def empty?
@tree.empty?
end
# Recursively extract the section headings from the RichTextElement and
# build the TableOfContents _toc_ with the gathered sections. _fileName_
# is the base name (without .html or other suffix) of the file the
# TOCEntries should point to.
def tableOfContents(toc, fileName)
@tree.tableOfContents(toc, fileName)
end
# Return an Array with all other snippet names that are referenced by
# internal references in this RichTextElement.
def internalReferences
@tree.internalReferences
end
# Convert the intermediate format into a plain text String object.
def to_s
str = @tree.to_s
str.chomp! while str[-1] == ?\n
str
end
# Convert the intermediate format into a XMLElement objects tree.
def to_html
html = @tree.to_html
html.chomp! while html[-1] == ?\n
html
end
# Convert the intermediate format into a tagged syntax String object.
def to_tagged
@tree.to_tagged
end
end
end
|