diff options
Diffstat (limited to 'framework')
-rw-r--r-- | framework/3rdParty/Markdown/License.text | 34 | ||||
-rw-r--r-- | framework/3rdParty/Markdown/MarkdownParser.php | 1257 | ||||
-rw-r--r-- | framework/3rdParty/readme.html | 10 | ||||
-rw-r--r-- | framework/I18N/TTranslate.php | 78 | ||||
-rw-r--r-- | framework/Web/Javascripts/datepicker/datepicker.js | 34 | ||||
-rw-r--r-- | framework/Web/Javascripts/js/datepicker.js | 14 | ||||
-rw-r--r-- | framework/Web/Javascripts/js/prado.js | 2 | ||||
-rw-r--r-- | framework/Web/Javascripts/prototype/event.js | 1 | ||||
-rw-r--r-- | framework/Web/UI/TTemplateManager.php | 34 | ||||
-rw-r--r-- | framework/Web/UI/WebControls/TDataBoundControl.php | 2 | ||||
-rw-r--r-- | framework/Web/UI/WebControls/TMarkdown.php | 113 | ||||
-rw-r--r-- | framework/Web/UI/WebControls/TRepeater.php | 3 | ||||
-rw-r--r-- | framework/Web/UI/WebControls/TValidationSummary.php | 17 | ||||
-rw-r--r-- | framework/Web/UI/WebControls/TWizard.php | 6 |
14 files changed, 1565 insertions, 40 deletions
diff --git a/framework/3rdParty/Markdown/License.text b/framework/3rdParty/Markdown/License.text new file mode 100644 index 00000000..ea6a6a1a --- /dev/null +++ b/framework/3rdParty/Markdown/License.text @@ -0,0 +1,34 @@ +Copyright (c) 2004-2005, John Gruber +<http://daringfireball.net/> +All rights reserved. + +Copyright (c) 2004-2005, Michel Fortin +<http://www.michelf.com/> +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + +* Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + +* Neither the name "Markdown" nor the names of its contributors may + be used to endorse or promote products derived from this software + without specific prior written permission. + +This software is provided by the copyright holders and contributors "as +is" and any express or implied warranties, including, but not limited +to, the implied warranties of merchantability and fitness for a +particular purpose are disclaimed. In no event shall the copyright owner +or contributors be liable for any direct, indirect, incidental, special, +exemplary, or consequential damages (including, but not limited to, +procurement of substitute goods or services; loss of use, data, or +profits; or business interruption) however caused and on any theory of +liability, whether in contract, strict liability, or tort (including +negligence or otherwise) arising in any way out of the use of this +software, even if advised of the possibility of such damage. diff --git a/framework/3rdParty/Markdown/MarkdownParser.php b/framework/3rdParty/Markdown/MarkdownParser.php new file mode 100644 index 00000000..c0d2becf --- /dev/null +++ b/framework/3rdParty/Markdown/MarkdownParser.php @@ -0,0 +1,1257 @@ +<?php + +# +# Markdown - A text-to-HTML conversion tool for web writers +# +# Copyright (c) 2004-2005 John Gruber +# <http://daringfireball.net/projects/markdown/> +# +# Copyright (c) 2004-2005 Michel Fortin - PHP Port +# <http://www.michelf.com/projects/php-markdown/> +# + +/** + * PHP5 version of the markdown parser. + * Usage: + * <code> + * $markdown = new MarkdownParser; + * echo $markdown->parse($text); + * </code> + */ +class MarkdownParser +{ + private static $md_nested_brackets; + private static $md_escape_table = array(); + private static $md_backslash_escape_table = array(); + private static $md_nested_brackets_depth = 6; + + protected $md_empty_element_suffix = " />"; # Change to ">" for HTML output + protected $md_tab_width = 4; + + private $md_list_level = 0; + private $md_urls = array(); + private $md_titles = array(); + private $md_html_blocks = array(); + + public function __construct() + { + if(is_null(self::$md_nested_brackets)) + $this->initialize(); + } + + private function initialize() + { + self::$md_nested_brackets = + str_repeat('(?>[^\[\]]+|\[', self::$md_nested_brackets_depth). + str_repeat('\])*', self::$md_nested_brackets_depth); + + self::$md_escape_table = array( + "\\" => md5("\\"), + "`" => md5("`"), + "*" => md5("*"), + "_" => md5("_"), + "{" => md5("{"), + "}" => md5("}"), + "[" => md5("["), + "]" => md5("]"), + "(" => md5("("), + ")" => md5(")"), + ">" => md5(">"), + "#" => md5("#"), + "+" => md5("+"), + "-" => md5("-"), + "." => md5("."), + "!" => md5("!") + ); + + # Table of hash values for escaped characters: + # Create an identical table but for escaped characters. + foreach (self::$md_escape_table as $key => $char) + self::$md_backslash_escape_table["\\$key"] = $char; + } + + public function parse($text) + { + # + # Main function. The order in which other subs are called here is + # essential. Link and image substitutions need to happen before + # _EscapeSpecialCharsWithinTagAttributes(), so that any *'s or _'s in the <a> + # and <img> tags get encoded. + # + # Clear the hashes. If we don't clear these, you get conflicts + # from other articles when generating a page which contains more than + # one article (e.g. an index page that shows the N most recent + # articles): + $this->md_urls = array(); + $this->md_titles = array(); + $this->md_html_blocks = array(); + + # Standardize line endings: + # DOS to Unix and Mac to Unix + $text = str_replace(array("\r\n", "\r"), "\n", $text); + + # Make sure $text ends with a couple of newlines: + $text .= "\n\n"; + + # Convert all tabs to spaces. + $text = $this->_Detab($text); + + # Strip any lines consisting only of spaces and tabs. + # This makes subsequent regexen easier to write, because we can + # match consecutive blank lines with /\n+/ instead of something + # contorted like /[ \t]*\n+/ . + $text = preg_replace('/^[ \t]+$/m', '', $text); + + # Turn block-level HTML blocks into hash entries + $text = $this->_HashHTMLBlocks($text); + + # Strip link definitions, store in hashes. + $text = $this->_StripLinkDefinitions($text); + + $text = $this->_RunBlockGamut($text); + + $text = $this->_UnescapeSpecialChars($text); + + return $text . "\n"; + } + + + private function _StripLinkDefinitions($text) { + # + # Strips link definitions from text, stores the URLs and titles in + # hash references. + # + $less_than_tab = $this->md_tab_width - 1; + + # Link defs are in the form: ^[id]: url "optional title" + $text = preg_replace_callback('{ + ^[ ]{0,'.$less_than_tab.'}\[(.+)\]: # id = $1 + [ \t]* + \n? # maybe *one* newline + [ \t]* + <?(\S+?)>? # url = $2 + [ \t]* + \n? # maybe one newline + [ \t]* + (?: + (?<=\s) # lookbehind for whitespace + ["(] + (.+?) # title = $3 + [")] + [ \t]* + )? # title is optional + (?:\n+|\Z) + }xm', + array($this,'_StripLinkDefinitions_callback'), + $text); + return $text; + } + + private function _StripLinkDefinitions_callback($matches) { + $link_id = strtolower($matches[1]); + $this->md_urls[$link_id] = $this->_EncodeAmpsAndAngles($matches[2]); + if (isset($matches[3])) + $this->md_titles[$link_id] = str_replace('"', '"', $matches[3]); + return ''; # String that will replace the block + } + + + private function _HashHTMLBlocks($text) { + $less_than_tab = $this->md_tab_width - 1; + + # Hashify HTML blocks: + # We only want to do this for block-level HTML tags, such as headers, + # lists, and tables. That's because we still want to wrap <p>s around + # "paragraphs" that are wrapped in non-block-level tags, such as anchors, + # phrase emphasis, and spans. The list of tags we're looking for is + # hard-coded: + $block_tags_a = 'p|div|h[1-6]|blockquote|pre|table|dl|ol|ul|'. + 'script|noscript|form|fieldset|iframe|math|ins|del'; + $block_tags_b = 'p|div|h[1-6]|blockquote|pre|table|dl|ol|ul|'. + 'script|noscript|form|fieldset|iframe|math'; + + # First, look for nested blocks, e.g.: + # <div> + # <div> + # tags for inner block must be indented. + # </div> + # </div> + # + # The outermost tags must start at the left margin for this to match, and + # the inner nested divs must be indented. + # We need to do this before the next, more liberal match, because the next + # match will start at the first `<div>` and stop at the first `</div>`. + $text = preg_replace_callback("{ + ( # save in $1 + ^ # start of line (with /m) + <($block_tags_a) # start tag = $2 + \\b # word break + (.*\\n)*? # any number of lines, minimally matching + </\\2> # the matching end tag + [ \\t]* # trailing spaces/tabs + (?=\\n+|\\Z) # followed by a newline or end of document + ) + }xm", + array($this,'_HashHTMLBlocks_callback'), + $text); + + # + # Now match more liberally, simply from `\n<tag>` to `</tag>\n` + # + $text = preg_replace_callback("{ + ( # save in $1 + ^ # start of line (with /m) + <($block_tags_b) # start tag = $2 + \\b # word break + (.*\\n)*? # any number of lines, minimally matching + .*</\\2> # the matching end tag + [ \\t]* # trailing spaces/tabs + (?=\\n+|\\Z) # followed by a newline or end of document + ) + }xm", + array($this,'_HashHTMLBlocks_callback'), + $text); + + # Special case just for <hr />. It was easier to make a special case than + # to make the other regex more complicated. + $text = preg_replace_callback('{ + (?: + (?<=\n\n) # Starting after a blank line + | # or + \A\n? # the beginning of the doc + ) + ( # save in $1 + [ ]{0,'.$less_than_tab.'} + <(hr) # start tag = $2 + \b # word break + ([^<>])*? # + /?> # the matching end tag + [ \t]* + (?=\n{2,}|\Z) # followed by a blank line or end of document + ) + }x', + array($this,'_HashHTMLBlocks_callback'), + $text); + + # Special case for standalone HTML comments: + $text = preg_replace_callback('{ + (?: + (?<=\n\n) # Starting after a blank line + | # or + \A\n? # the beginning of the doc + ) + ( # save in $1 + [ ]{0,'.$less_than_tab.'} + (?s: + <! + (--.*?--\s*)+ + > + ) + [ \t]* + (?=\n{2,}|\Z) # followed by a blank line or end of document + ) + }x', + array($this,'_HashHTMLBlocks_callback'), + $text); + + return $text; + } + private function _HashHTMLBlocks_callback($matches) { + $text = $matches[1]; + $key = md5($text); + $this->md_html_blocks[$key] = $text; + return "\n\n$key\n\n"; # String that will replace the block + } + + + private function _RunBlockGamut($text) { + # + # These are all the transformations that form block-level + # tags like paragraphs, headers, and list items. + # + $text = $this->_DoHeaders($text); + + # Do Horizontal Rules: + $text = preg_replace( + array('{^[ ]{0,2}([ ]?\*[ ]?){3,}[ \t]*$}mx', + '{^[ ]{0,2}([ ]? -[ ]?){3,}[ \t]*$}mx', + '{^[ ]{0,2}([ ]? _[ ]?){3,}[ \t]*$}mx'), + "\n<hr{$this->md_empty_element_suffix}\n", + $text); + + $text = $this->_DoLists($text); + $text = $this->_DoCodeBlocks($text); + $text = $this->_DoBlockQuotes($text); + + # We already ran _HashHTMLBlocks() before, in Markdown(), but that + # was to escape raw HTML in the original Markdown source. This time, + # we're escaping the markup we've just created, so that we don't wrap + # <p> tags around block-level tags. + $text = $this->_HashHTMLBlocks($text); + $text = $this->_FormParagraphs($text); + + return $text; + } + + + private function _RunSpanGamut($text) { + # + # These are all the transformations that occur *within* block-level + # tags like paragraphs, headers, and list items. + # + + $text = $this->_DoCodeSpans($text); + + $text = $this->_EscapeSpecialChars($text); + + # Process anchor and image tags. Images must come first, + # because ![foo][f] looks like an anchor. + $text = $this->_DoImages($text); + $text = $this->_DoAnchors($text); + + # Make links out of things like `<http://example.com/>` + # Must come after _DoAnchors(), because you can use < and > + # delimiters in inline links like [this](<url>). + $text = $this->_DoAutoLinks($text); + $text = $this->_EncodeAmpsAndAngles($text); + $text = $this->_DoItalicsAndBold($text); + + # Do hard breaks: + $text = preg_replace('/ {2,}\n/', "<br{$this->md_empty_element_suffix}\n", $text); + + return $text; + } + + + private function _EscapeSpecialChars($text) { + $tokens = $this->_TokenizeHTML($text); + + $text = ''; # rebuild $text from the tokens + # $in_pre = 0; # Keep track of when we're inside <pre> or <code> tags. + # $tags_to_skip = "!<(/?)(?:pre|code|kbd|script|math)[\s>]!"; + + foreach ($tokens as $cur_token) { + if ($cur_token[0] == 'tag') { + # Within tags, encode * and _ so they don't conflict + # with their use in Markdown for italics and strong. + # We're replacing each such character with its + # corresponding MD5 checksum value; this is likely + # overkill, but it should prevent us from colliding + # with the escape values by accident. + $cur_token[1] = str_replace(array('*', '_'), + array(self::$md_escape_table['*'], self::$md_escape_table['_']), + $cur_token[1]); + $text .= $cur_token[1]; + } else { + $t = $cur_token[1]; + $t = $this->_EncodeBackslashEscapes($t); + $text .= $t; + } + } + return $text; + } + + + private function _DoAnchors($text) { + # + # Turn Markdown link shortcuts into XHTML <a> tags. + # + # + # First, handle reference-style links: [link text] [id] + # + $bracket = self::$md_nested_brackets; + $text = preg_replace_callback("{ + ( # wrap whole match in $1 + \\[ + ({$bracket}) # link text = $2 + \\] + + [ ]? # one optional space + (?:\\n[ ]*)? # one optional newline followed by spaces + + \\[ + (.*?) # id = $3 + \\] + ) + }xs", + array($this,'_DoAnchors_reference_callback'), $text); + + # + # Next, inline-style links: [link text](url "optional title") + # + $text = preg_replace_callback("{ + ( # wrap whole match in $1 + \\[ + ({$bracket}) # link text = $2 + \\] + \\( # literal paren + [ \\t]* + <?(.*?)>? # href = $3 + [ \\t]* + ( # $4 + (['\"]) # quote char = $5 + (.*?) # Title = $6 + \\5 # matching quote + )? # title is optional + \\) + ) + }xs", + array($this,'_DoAnchors_inline_callback'), $text); + + return $text; + } + private function _DoAnchors_reference_callback($matches) { + $whole_match = $matches[1]; + $link_text = $matches[2]; + $link_id = strtolower($matches[3]); + + if ($link_id == "") { + $link_id = strtolower($link_text); # for shortcut links like [this][]. + } + + if (isset($this->md_urls[$link_id])) { + $url = $this->md_urls[$link_id]; + # We've got to encode these to avoid conflicting with italics/bold. + $url = str_replace(array('*', '_'), + array(self::$md_escape_table['*'], self::$md_escape_table['_']), + $url); + $result = "<a href=\"$url\""; + if ( isset( $this->md_titles[$link_id] ) ) { + $title = $this->md_titles[$link_id]; + $title = str_replace(array('*', '_'), + array(self::$md_escape_table['*'], + self::$md_escape_table['_']), $title); + $result .= " title=\"$title\""; + } + $result .= ">$link_text</a>"; + } + else { + $result = $whole_match; + } + return $result; + } + private function _DoAnchors_inline_callback($matches) { + $whole_match = $matches[1]; + $link_text = $matches[2]; + $url = $matches[3]; + $title =& $matches[6]; + + # We've got to encode these to avoid conflicting with italics/bold. + $url = str_replace(array('*', '_'), + array(self::$md_escape_table['*'], self::$md_escape_table['_']), + $url); + $result = "<a href=\"$url\""; + if (isset($title)) { + $title = str_replace('"', '"', $title); + $title = str_replace(array('*', '_'), + array(self::$md_escape_table['*'], self::$md_escape_table['_']), + $title); + $result .= " title=\"$title\""; + } + + $result .= ">$link_text</a>"; + + return $result; + } + + + private function _DoImages($text) { + # + # Turn Markdown image shortcuts into <img> tags. + # + # + # First, handle reference-style labeled images: ![alt text][id] + # + $text = preg_replace_callback('{ + ( # wrap whole match in $1 + !\[ + ('.self::$md_nested_brackets.') # alt text = $2 + \] + + [ ]? # one optional space + (?:\n[ ]*)? # one optional newline followed by spaces + + \[ + (.*?) # id = $3 + \] + + ) + }xs', + array($this,'_DoImages_reference_callback'), $text); + + # + # Next, handle inline images: ![alt text](url "optional title") + # Don't forget: encode * and _ + + $text = preg_replace_callback('{ + ( # wrap whole match in $1 + !\[ + ('.self::$md_nested_brackets.') # alt text = $2 + \] + \( # literal paren + [ \t]* + <?(\S+?)>? # src url = $3 + [ \t]* + ( # $4 + ([\'"]) # quote char = $5 + (.*?) # title = $6 + \5 # matching quote + [ \t]* + )? # title is optional + \) + ) + }xs', + array($this,'_DoImages_inline_callback'), $text); + + return $text; + } + private function _DoImages_reference_callback($matches) { + $whole_match = $matches[1]; + $alt_text = $matches[2]; + $link_id = strtolower($matches[3]); + + if ($link_id == "") { + $link_id = strtolower($alt_text); # for shortcut links like ![this][]. + } + + $alt_text = str_replace('"', '"', $alt_text); + if (isset($this->md_urls[$link_id])) { + $url = $this->md_urls[$link_id]; + # We've got to encode these to avoid conflicting with italics/bold. + $url = str_replace(array('*', '_'), + array(self::$md_escape_table['*'], self::$md_escape_table['_']), + $url); + $result = "<img src=\"$url\" alt=\"$alt_text\""; + if (isset($this->md_titles[$link_id])) { + $title = $this->md_titles[$link_id]; + $title = str_replace(array('*', '_'), + array(self::$md_escape_table['*'], + self::$md_escape_table['_']), $title); + $result .= " title=\"$title\""; + } + $result .= $this->md_empty_element_suffix; + } + else { + # If there's no such link ID, leave intact: + $result = $whole_match; + } + + return $result; + } + private function _DoImages_inline_callback($matches) { + $whole_match = $matches[1]; + $alt_text = $matches[2]; + $url = $matches[3]; + $title = ''; + if (isset($matches[6])) { + $title = $matches[6]; + } + + $alt_text = str_replace('"', '"', $alt_text); + $title = str_replace('"', '"', $title); + # We've got to encode these to avoid conflicting with italics/bold. + $url = str_replace(array('*', '_'), + array(self::$md_escape_table['*'], self::$md_escape_table['_']), + $url); + $result = "<img src=\"$url\" alt=\"$alt_text\""; + if (isset($title)) { + $title = str_replace(array('*', '_'), + array(self::$md_escape_table['*'], self::$md_escape_table['_']), + $title); + $result .= " title=\"$title\""; # $title already quoted + } + $result .= $this->md_empty_element_suffix; + + return $result; + } + + + private function _DoHeaders($text) { + # Setext-style headers: + # Header 1 + # ======== + # + # Header 2 + # -------- + # + $text = preg_replace( + array('{ ^(.+)[ \t]*\n=+[ \t]*\n+ }emx', + '{ ^(.+)[ \t]*\n-+[ \t]*\n+ }emx'), + array("'<h1>'.\$this->_RunSpanGamut(\$this->_UnslashQuotes('\\1')).'</h1>\n\n'", + "'<h2>'.\$this->_RunSpanGamut(\$this->_UnslashQuotes('\\1')).'</h2>\n\n'"), + $text); + + # atx-style headers: + # # Header 1 + # ## Header 2 + # ## Header 2 with closing hashes ## + # ... + # ###### Header 6 + # + $text = preg_replace("{ + ^(\\#{1,6}) # $1 = string of #'s + [ \\t]* + (.+?) # $2 = Header text + [ \\t]* + \\#* # optional closing #'s (not counted) + \\n+ + }xme", + "'<h'.strlen('\\1').'>'.\$this->_RunSpanGamut(\$this->_UnslashQuotes('\\2')).'</h'.strlen('\\1').'>\n\n'", + $text); + + return $text; + } + + + private function _DoLists($text) { + # + # Form HTML ordered (numbered) and unordered (bulleted) lists. + # + $less_than_tab = $this->md_tab_width - 1; + + # Re-usable patterns to match list item bullets and number markers: + $marker_ul = '[*+-]'; + $marker_ol = '\d+[.]'; + $marker_any = "(?:$marker_ul|$marker_ol)"; + + $markers = array($marker_ul, $marker_ol); + + foreach ($markers as $marker) { + # Re-usable pattern to match any entirel ul or ol list: + $whole_list = ' + ( # $1 = whole list + ( # $2 + [ ]{0,'.$less_than_tab.'} + ('.$marker.') # $3 = first list item marker + [ \t]+ + ) + (?s:.+?) + ( # $4 + \z + | + \n{2,} + (?=\S) + (?! # Negative lookahead for another list item marker + [ \t]* + '.$marker.'[ \t]+ + ) + ) + ) + '; // mx + + # We use a different prefix before nested lists than top-level lists. + # See extended comment in _ProcessListItems(). + + if ($this->md_list_level) { + $text = preg_replace_callback('{ + ^ + '.$whole_list.' + }mx', + array($this,'_DoLists_callback_top'), $text); + } + else { + $text = preg_replace_callback('{ + (?:(?<=\n\n)|\A\n?) + '.$whole_list.' + }mx', + array($this,'_DoLists_callback_nested'), $text); + } + } + + return $text; + } + private function _DoLists_callback_top($matches) { + # Re-usable patterns to match list item bullets and number markers: + $marker_ul = '[*+-]'; + $marker_ol = '\d+[.]'; + $marker_any = "(?:$marker_ul|$marker_ol)"; + + $list = $matches[1]; + $list_type = preg_match("/$marker_ul/", $matches[3]) ? "ul" : "ol"; + + $marker_any = ( $list_type == "ul" ? $marker_ul : $marker_ol ); + + # Turn double returns into triple returns, so that we can make a + # paragraph for the last item in a list, if necessary: + $list = preg_replace("/\n{2,}/", "\n\n\n", $list); + $result = $this->_ProcessListItems($list, $marker_any); + + # Trim any trailing whitespace, to put the closing `</$list_type>` + # up on the preceding line, to get it past the current stupid + # HTML block parser. This is a hack to work around the terrible + # hack that is the HTML block parser. + $result = rtrim($result); + $result = "<$list_type>" . $result . "</$list_type>\n"; + return $result; + } + private function _DoLists_callback_nested($matches) { + # Re-usable patterns to match list item bullets and number markers: + $marker_ul = '[*+-]'; + $marker_ol = '\d+[.]'; + $marker_any = "(?:$marker_ul|$marker_ol)"; + + $list = $matches[1]; + $list_type = preg_match("/$marker_ul/", $matches[3]) ? "ul" : "ol"; + + $marker_any = ( $list_type == "ul" ? $marker_ul : $marker_ol ); + + # Turn double returns into triple returns, so that we can make a + # paragraph for the last item in a list, if necessary: + $list = preg_replace("/\n{2,}/", "\n\n\n", $list); + $result = $this->_ProcessListItems($list, $marker_any); + $result = "<$list_type>\n" . $result . "</$list_type>\n"; + return $result; + } + + + private function _ProcessListItems($list_str, $marker_any) { + # + # Process the contents of a single ordered or unordered list, splitting it + # into individual list items. + # + + # The $md_list_level keeps track of when we're inside a list. + # Each time we enter a list, we increment it; when we leave a list, + # we decrement. If it's zero, we're not in a list anymore. + # + # We do this because when we're not inside a list, we want to treat + # something like this: + # + # I recommend upgrading to version + # 8. Oops, now this line is treated + # as a sub-list. + # + # As a single paragraph, despite the fact that the second line starts + # with a digit-period-space sequence. + # + # Whereas when we're inside a list (or sub-list), that line will be + # treated as the start of a sub-list. What a kludge, huh? This is + # an aspect of Markdown's syntax that's hard to parse perfectly + # without resorting to mind-reading. Perhaps the solution is to + # change the syntax rules such that sub-lists must start with a + # starting cardinal number; e.g. "1." or "a.". + + $this->md_list_level++; + + # trim trailing blank lines: + $list_str = preg_replace("/\n{2,}\\z/", "\n", $list_str); + + $list_str = preg_replace_callback('{ + (\n)? # leading line = $1 + (^[ \t]*) # leading whitespace = $2 + ('.$marker_any.') [ \t]+ # list marker = $3 + ((?s:.+?) # list item text = $4 + (\n{1,2})) + (?= \n* (\z | \2 ('.$marker_any.') [ \t]+)) + }xm', + array($this,'_ProcessListItems_callback'), $list_str); + + $this->md_list_level--; + return $list_str; + } + private function _ProcessListItems_callback($matches) { + $item = $matches[4]; + $leading_line =& $matches[1]; + $leading_space =& $matches[2]; + + if ($leading_line || preg_match('/\n{2,}/', $item)) { + $item = $this->_RunBlockGamut($this->_Outdent($item)); + } + else { + # Recursion for sub-lists: + $item = $this->_DoLists($this->_Outdent($item)); + $item = preg_replace('/\n+$/', '', $item); + $item = $this->_RunSpanGamut($item); + } + + return "<li>" . $item . "</li>\n"; + } + + + private function _DoCodeBlocks($text) { + # + # Process Markdown `<pre><code>` blocks. + # + $text = preg_replace_callback('{ + (?:\n\n|\A) + ( # $1 = the code block -- one or more lines, starting with a space/tab + (?: + (?:[ ]{'.$this->md_tab_width.'} | \t) # Lines must start with a tab or a tab-width of spaces + .*\n+ + )+ + ) + ((?=^[ ]{0,'.$this->md_tab_width.'}\S)|\Z) # Lookahead for non-space at line-start, or end of doc + }xm', + array($this,'_DoCodeBlocks_callback'), $text); + + return $text; + } + private function _DoCodeBlocks_callback($matches) { + $codeblock = $matches[1]; + + $codeblock = $this->_EncodeCode($this->_Outdent($codeblock)); + // $codeblock = _Detab($codeblock); + # trim leading newlines and trailing whitespace + $codeblock = preg_replace(array('/\A\n+/', '/\s+\z/'), '', $codeblock); + + $result = "\n\n<pre><code>" . $codeblock . "\n</code></pre>\n\n"; + + return $result; + } + + + private function _DoCodeSpans($text) { + # + # * Backtick quotes are used for <code></code> spans. + # + # * You can use multiple backticks as the delimiters if you want to + # include literal backticks in the code span. So, this input: + # + # Just type ``foo `bar` baz`` at the prompt. + # + # Will translate to: + # + # <p>Just type <code>foo `bar` baz</code> at the prompt.</p> + # + # There's no arbitrary limit to the number of backticks you + # can use as delimters. If you need three consecutive backticks + # in your code, use four for delimiters, etc. + # + # * You can use spaces to get literal backticks at the edges: + # + # ... type `` `bar` `` ... + # + # Turns to: + # + # ... type <code>`bar`</code> ... + # + $text = preg_replace_callback('@ + (?<!\\\) # Character before opening ` can\'t be a backslash + (`+) # $1 = Opening run of ` + (.+?) # $2 = The code block + (?<!`) + \1 # Matching closer + (?!`) + @xs', + array($this,'_DoCodeSpans_callback'), $text); + + return $text; + } + private function _DoCodeSpans_callback($matches) { + $c = $matches[2]; + $c = preg_replace('/^[ \t]*/', '', $c); # leading whitespace + $c = preg_replace('/[ \t]*$/', '', $c); # trailing whitespace + $c = $this->_EncodeCode($c); + return "<code>$c</code>"; + } + + + private function _EncodeCode($_) { + # + # Encode/escape certain characters inside Markdown code runs. + # The point is that in code, these characters are literals, + # and lose their special Markdown meanings. + # + # Encode all ampersands; HTML entities are not + # entities within a Markdown code span. + $_ = str_replace('&', '&', $_); + + # Do the angle bracket song and dance: + $_ = str_replace(array('<', '>'), + array('<', '>'), $_); + + # Now, escape characters that are magic in Markdown: + $_ = str_replace(array_keys(self::$md_escape_table), + array_values(self::$md_escape_table), $_); + + return $_; + } + + + private function _DoItalicsAndBold($text) { + # <strong> must go first: + $text = preg_replace('{ + ( # $1: Marker + (?<!\*\*) \*\* | # (not preceded by two chars of + (?<!__) __ # the same marker) + ) + (?=\S) # Not followed by whitespace + (?!\1) # or two others marker chars. + ( # $2: Content + (?: + [^*_]+? # Anthing not em markers. + | + # Balence any regular emphasis inside. + ([*_]) (?=\S) .+? (?<=\S) \3 # $3: em char (* or _) + | + (?! \1 ) . # Allow unbalenced * and _. + )+? + ) + (?<=\S) \1 # End mark not preceded by whitespace. + }sx', + '<strong>\2</strong>', $text); + # Then <em>: + $text = preg_replace( + '{ ( (?<!\*)\* | (?<!_)_ ) (?=\S) (?! \1) (.+?) (?<=\S) \1 }sx', + '<em>\2</em>', $text); + + return $text; + } + + + private function _DoBlockQuotes($text) { + $text = preg_replace_callback('/ + ( # Wrap whole match in $1 + ( + ^[ \t]*>[ \t]? # ">" at the start of a line + .+\n # rest of the first line + (.+\n)* # subsequent consecutive lines + \n* # blanks + )+ + ) + /xm', + array($this,'_DoBlockQuotes_callback'), $text); + + return $text; + } + private function _DoBlockQuotes_callback($matches) { + $bq = $matches[1]; + # trim one level of quoting - trim whitespace-only lines + $bq = preg_replace(array('/^[ \t]*>[ \t]?/m', '/^[ \t]+$/m'), '', $bq); + $bq = $this->_RunBlockGamut($bq); # recurse + + $bq = preg_replace('/^/m', " ", $bq); + # These leading spaces screw with <pre> content, so we need to fix that: + $bq = preg_replace_callback('{(\s*<pre>.+?</pre>)}sx', + array($this,'_DoBlockQuotes_callback2'), $bq); + + return "<blockquote>\n$bq\n</blockquote>\n\n"; + } + private function _DoBlockQuotes_callback2($matches) { + $pre = $matches[1]; + $pre = preg_replace('/^ /m', '', $pre); + return $pre; + } + + + private function _FormParagraphs($text) { + # + # Params: + # $text - string to process with html <p> tags + # + # Strip leading and trailing lines: + $text = preg_replace(array('/\A\n+/', '/\n+\z/'), '', $text); + + $grafs = preg_split('/\n{2,}/', $text, -1, PREG_SPLIT_NO_EMPTY); + + # + # Wrap <p> tags. + # + foreach ($grafs as $key => $value) { + if (!isset( $this->md_html_blocks[$value] )) { + $value = $this->_RunSpanGamut($value); + $value = preg_replace('/^([ \t]*)/', '<p>', $value); + $value .= "</p>"; + $grafs[$key] = $value; + } + } + + # + # Unhashify HTML blocks + # + foreach ($grafs as $key => $value) { + if (isset( $this->md_html_blocks[$value] )) { + $grafs[$key] = $this->md_html_blocks[$value]; + } + } + + return implode("\n\n", $grafs); + } + + + private function _EncodeAmpsAndAngles($text) { + # Smart processing for ampersands and angle brackets that need to be encoded. + + # Ampersand-encoding based entirely on Nat Irons's Amputator MT plugin: + # http://bumppo.net/projects/amputator/ + $text = preg_replace('/&(?!#?[xX]?(?:[0-9a-fA-F]+|\w+);)/', + '&', $text);; + + # Encode naked <'s + $text = preg_replace('{<(?![a-z/?\$!])}i', '<', $text); + + return $text; + } + + + private function _EncodeBackslashEscapes($text) { + # + # Parameter: String. + # Returns: The string, with after processing the following backslash + # escape sequences. + # + # Must process escaped backslashes first. + return str_replace(array_keys(self::$md_backslash_escape_table), + array_values(self::$md_backslash_escape_table), $text); + } + + + private function _DoAutoLinks($text) { + $text = preg_replace("!<((https?|ftp):[^'\">\\s]+)>!", + '<a href="\1">\1</a>', $text); + + # Email addresses: <address@domain.foo> + $text = preg_replace('{ + < + (?:mailto:)? + ( + [-.\w]+ + \@ + [-a-z0-9]+(\.[-a-z0-9]+)*\.[a-z]+ + ) + > + }exi', + "\$this->_EncodeEmailAddress(\$this->_UnescapeSpecialChars(\$this->_UnslashQuotes('\\1')))", + $text); + + return $text; + } + + + private function _EncodeEmailAddress($addr) { + # + # Input: an email address, e.g. "foo@example.com" + # + # Output: the email address as a mailto link, with each character + # of the address encoded as either a decimal or hex entity, in + # the hopes of foiling most address harvesting spam bots. E.g.: + # + # <a href="mailto:foo@e + # xample.com">foo + # @example.com</a> + # + # Based by a filter by Matthew Wickline, posted to the BBEdit-Talk + # mailing list: <http://tinyurl.com/yu7ue> + # + $addr = "mailto:" . $addr; + $length = strlen($addr); + + # leave ':' alone (to spot mailto: later) + $addr = preg_replace_callback('/([^\:])/', + array($this,'_EncodeEmailAddress_callback'), $addr); + + $addr = "<a href=\"$addr\">$addr</a>"; + # strip the mailto: from the visible part + $addr = preg_replace('/">.+?:/', '">', $addr); + + return $addr; + } + private function _EncodeEmailAddress_callback($matches) { + $char = $matches[1]; + $r = rand(0, 100); + # roughly 10% raw, 45% hex, 45% dec + # '@' *must* be encoded. I insist. + if ($r > 90 && $char != '@') return $char; + if ($r < 45) return '&#x'.dechex(ord($char)).';'; + return '&#'.ord($char).';'; + } + + + private function _UnescapeSpecialChars($text) { + # + # Swap back in all the special characters we've hidden. + # + return str_replace(array_values(self::$md_escape_table), + array_keys(self::$md_escape_table), $text); + } + + + # _TokenizeHTML is shared between PHP Markdown and PHP SmartyPants. + # We only define it if it is not already defined. + + private function _TokenizeHTML($str) { + # + # Parameter: String containing HTML markup. + # Returns: An array of the tokens comprising the input + # string. Each token is either a tag (possibly with nested, + # tags contained therein, such as <a href="<MTFoo>">, or a + # run of text between tags. Each element of the array is a + # two-element array; the first is either 'tag' or 'text'; + # the second is the actual value. + # + # + # Regular expression derived from the _tokenize() subroutine in + # Brad Choate's MTRegex plugin. + # <http://www.bradchoate.com/past/mtregex.php> + # + $index = 0; + $tokens = array(); + + $match = '(?s:<!(?:--.*?--\s*)+>)|'. # comment + '(?s:<\?.*?\?>)|'. # processing instruction + # regular tags + '(?:<[/!$]?[-a-zA-Z0-9:]+\b(?>[^"\'>]+|"[^"]*"|\'[^\']*\')*>)'; + + $parts = preg_split("{($match)}", $str, -1, PREG_SPLIT_DELIM_CAPTURE); + + foreach ($parts as $part) { + if (++$index % 2 && $part != '') + $tokens[] = array('text', $part); + else + $tokens[] = array('tag', $part); + } + + return $tokens; + } + + private function _Outdent($text) { + # + # Remove one level of line-leading tabs or spaces + # + return preg_replace("/^(\\t|[ ]{1,".$this->md_tab_width."})/m", "", $text); + } + + + private function _Detab($text) { + # + # Replace tabs with the appropriate amount of space. + # + # For each line we separate the line in blocks delemited by + # tab characters. Then we reconstruct every line by adding the + # appropriate number of space between each blocks. + + $lines = explode("\n", $text); + $text = ""; + + foreach ($lines as $line) { + # Split in blocks. + $blocks = explode("\t", $line); + # Add each blocks to the line. + $line = $blocks[0]; + unset($blocks[0]); # Do not add first block twice. + foreach ($blocks as $block) { + # Calculate amount of space, insert spaces, insert block. + $amount = $this->md_tab_width - strlen($line) % $this->md_tab_width; + $line .= str_repeat(" ", $amount) . $block; + } + $text .= "$line\n"; + } + return $text; + } + + + private function _UnslashQuotes($text) { + # + # This function is useful to remove automaticaly slashed double quotes + # when using preg_replace and evaluating an expression. + # Parameter: String. + # Returns: The string with any slash-double-quote (\") sequence replaced + # by a single double quote. + # + return str_replace('\"', '"', $text); + } +} + +/* + +PHP Markdown +============ + +Description +----------- + +This is a PHP translation of the original Markdown formatter written in +Perl by John Gruber. + +Markdown is a text-to-HTML filter; it translates an easy-to-read / +easy-to-write structured text format into HTML. Markdown's text format +is most similar to that of plain text email, and supports features such +as headers, *emphasis*, code blocks, blockquotes, and links. + +Markdown's syntax is designed not as a generic markup language, but +specifically to serve as a front-end to (X)HTML. You can use span-level +HTML tags anywhere in a Markdown document, and you can use block level +HTML tags (like <div> and <table> as well). + +For more information about Markdown's syntax, see: + +<http://daringfireball.net/projects/markdown/> + + +Bugs +---- + +To file bug reports please send email to: + +<michel.fortin@michelf.com> + +Please include with your report: (1) the example input; (2) the output you +expected; (3) the output Markdown actually produced. + + +Version History +--------------- + +See the readme file for detailed release notes for this version. + +1.0.1c - 9 Dec 2005 + +1.0.1b - 6 Jun 2005 + +1.0.1a - 15 Apr 2005 + +1.0.1 - 16 Dec 2004 + +1.0 - 21 Aug 2004 + + +Author & Contributors +--------------------- + +Original Perl version by John Gruber +<http://daringfireball.net/> + +PHP port and other contributions by Michel Fortin +<http://www.michelf.com/> + + +Copyright and License +--------------------- + +Copyright (c) 2004-2005 Michel Fortin +<http://www.michelf.com/> +All rights reserved. + +Copyright (c) 2003-2004 John Gruber +<http://daringfireball.net/> +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + +* Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + +* Neither the name "Markdown" nor the names of its contributors may + be used to endorse or promote products derived from this software + without specific prior written permission. + +This software is provided by the copyright holders and contributors "as +is" and any express or implied warranties, including, but not limited +to, the implied warranties of merchantability and fitness for a +particular purpose are disclaimed. In no event shall the copyright owner +or contributors be liable for any direct, indirect, incidental, special, +exemplary, or consequential damages (including, but not limited to, +procurement of substitute goods or services; loss of use, data, or +profits; or business interruption) however caused and on any theory of +liability, whether in contract, strict liability, or tort (including +negligence or otherwise) arising in any way out of the use of this +software, even if advised of the possibility of such damage. + +*/ +?> diff --git a/framework/3rdParty/readme.html b/framework/3rdParty/readme.html index f67526ae..590f1174 100644 --- a/framework/3rdParty/readme.html +++ b/framework/3rdParty/readme.html @@ -133,7 +133,15 @@ projects. <td>Slightly modified to work nicely with E_STRICT in php 5.</td>
</tr>
+<tr>
+ <td><a href="Markdown">Markdown</a></td>
+ <td><a href="http://www.michelf.com/projects/php-markdown/">PHP Markdown</a></td>
+ <td><a href="Markdown/License.text">BSD</a></td>
+ <td>System.Web.UI.WebControls.TMarkdown</td>
+ <td>PHP5 class implementation of the PHP Markdown.</td>
+</tr>
+
</table>
</p>
</body>
-</html>
\ No newline at end of file +</html>
diff --git a/framework/I18N/TTranslate.php b/framework/I18N/TTranslate.php index 6a30f3a6..da5876d4 100644 --- a/framework/I18N/TTranslate.php +++ b/framework/I18N/TTranslate.php @@ -28,19 +28,19 @@ Prado::using('System.I18N.TI18NControl'); * Depending on the culture set on the page, the phrase "Goodbye" will
* be translated.
*
- * The values of any attribute in TTranslate are consider as values for
- * substitution. Strings enclosed with "{" and "}" are consider as the
- * parameters. The following example will substitution the string
- * "{time}" with the value of the attribute "time="#time()". Note that
- * the value of the attribute time is evaluated.
+ * The {@link getParameters Parameters} property can be use to add name values pairs for
+ * substitution. Substrings enclosed with "{" and "}" in the translation message are consider as the
+ * parameter names during substitution lookup. The following example will substitute the substring
+ * "{time}" with the value of the parameter attribute "Parameters.time=<%= time() %>. Note that
+ * the value of the parameter named "time" is evaluated.
* <code>
- * <com:TTranslate time="#time()">
+ * <com:TTranslate Parameters.time=<%= time() %> >
* The unix-time is "{time}".
* </com:TTranslate>
* </code>
*
* More complex string substitution can be applied using the
- * TParam component.
+ * TTranslateParameter component.
*
* Namespace: System.I18N
*
@@ -134,6 +134,68 @@ class TTranslate extends TI18NControl }
/**
+ * Returns the list of custom parameters.
+ * Custom parameters are name-value pairs that may subsititute translation
+ * place holders during rendering.
+ * @return TAttributeCollection the list of custom parameters
+ */
+ public function getParameters()
+ {
+ if($parameters=$this->getViewState('Parameters',null))
+ return $parameters;
+ else
+ {
+ $parameters=new TAttributeCollection;
+ $this->setViewState('Parameters',$parameters,null);
+ return $parameters;
+ }
+ }
+
+ /**
+ * @return boolean whether the named parameter exists
+ */
+ public function hasParameter($name)
+ {
+ if($parameters=$this->getViewState('Parameters',null))
+ return $parameters->contains($name);
+ else
+ return false;
+ }
+
+ /**
+ * @return string parameter value, null if parameter does not exist
+ */
+ public function getParameter($name)
+ {
+ if($parameters=$this->getViewState('Parameters',null))
+ return $parameters->itemAt($name);
+ else
+ return null;
+ }
+
+ /**
+ * @param string parameter name
+ * @param string value of the parameter
+ */
+ public function setParameter($name,$value)
+ {
+ $this->getParameters()->add($name,$value);
+ }
+
+ /**
+ * Removes the named parameter.
+ * @param string the name of the parameter to be removed.
+ * @return string parameter value removed, null if parameter does not exist.
+ */
+ public function removeParameter($name)
+ {
+ if($parameters=$this->getViewState('Parameters',null))
+ return $parameters->remove($name);
+ else
+ return null;
+ }
+
+ /**
* renders the translated string.
*/
public function render($writer)
@@ -141,6 +203,8 @@ class TTranslate extends TI18NControl $textWriter=new TTextWriter;
$htmlWriter=new THtmlWriter($textWriter);
$subs = array();
+ foreach($this->getParameters() as $key => $value)
+ $subs['{'.$key.'}'] = $value;
foreach($this->getControls() as $control)
{
if($control instanceof TTranslateParameter)
diff --git a/framework/Web/Javascripts/datepicker/datepicker.js b/framework/Web/Javascripts/datepicker/datepicker.js index d5d9496c..e704e950 100644 --- a/framework/Web/Javascripts/datepicker/datepicker.js +++ b/framework/Web/Javascripts/datepicker/datepicker.js @@ -282,8 +282,11 @@ Prado.WebUI.TDatePicker.prototype = Event.observe(this._calDiv, "mousewheel", this.mouseWheelChange.bindEvent(this));
Event.observe(calendarBody, "click", this.selectDate.bindEvent(this));
+
+ Event.observe(this.control, "blur", this.hide.bind(this));
+ Prado.Element.focus(this.control);
- },
+ },A
ieHack : function(cleanup)
{
@@ -305,7 +308,7 @@ Prado.WebUI.TDatePicker.prototype = if (!ev) ev = document.parentWindow.event;
var kc = ev.keyCode != null ? ev.keyCode : ev.charCode;
- if(kc == Event.KEY_RETURN)
+ if(kc == Event.KEY_RETURN || kc == Event.KEY_SPACEBAR)
{
this.setSelectedDate(this.selectedDate);
Event.stop(ev);
@@ -526,6 +529,22 @@ Prado.WebUI.TDatePicker.prototype = this.setMonth(this.selectedDate.getMonth()-1);
},
+ getDatePickerOffsetHeight : function()
+ {
+ if(this.options.InputMode == "TextBox")
+ return this.control.offsetHeight;
+
+ var control = Prado.WebUI.TDatePicker.getDayListControl(this.control);
+ if(control) return control.offsetHeight;
+
+ var control = Prado.WebUI.TDatePicker.getMonthListControl(this.control);
+ if(control) return control.offsetHeight;
+
+ var control = Prado.WebUI.TDatePicker.getYearListControl(this.control);
+ if(control) return control.offsetHeight;
+ return 0;
+ },
+
show : function()
{
this.create();
@@ -534,15 +553,8 @@ Prado.WebUI.TDatePicker.prototype = {
var pos = Position.cumulativeOffset(this.control);
- if(this.options.InputMode == "TextBox")
- pos[1] += this.control.offsetHeight;
- else
- {
- var dayList = Prado.WebUI.TDatePicker.getDayListControl(this.control);
- if(dayList)
- pos[1] += dayList.offsetHeight-1;
- }
-
+ pos[1] += this.getDatePickerOffsetHeight();
+
this._calDiv.style.display = "block";
this._calDiv.style.top = (pos[1]-1) + "px";
this._calDiv.style.left = pos[0] + "px";
diff --git a/framework/Web/Javascripts/js/datepicker.js b/framework/Web/Javascripts/js/datepicker.js index a81bbeb4..e0bba502 100644 --- a/framework/Web/Javascripts/js/datepicker.js +++ b/framework/Web/Javascripts/js/datepicker.js @@ -23,10 +23,10 @@ div=document.createElement("div");div.className="calendarFooter";this._calDiv.ap this.iePopUp.scrolling="no" this.iePopUp.frameBorder="0" document.body.appendChild(this.iePopUp);} -document.body.appendChild(this._calDiv);this.update();this.updateHeader();this.ieHack(true);previousMonth.hideFocus=true;nextMonth.hideFocus=true;todayButton.hideFocus=true;Event.observe(previousMonth,"click",this.prevMonth.bindEvent(this));Event.observe(nextMonth,"click",this.nextMonth.bindEvent(this));Event.observe(todayButton,"click",this.selectToday.bindEvent(this));Event.observe(this._monthSelect,"change",this.monthSelect.bindEvent(this));Event.observe(this._yearSelect,"change",this.yearSelect.bindEvent(this));Event.observe(this._calDiv,"mousewheel",this.mouseWheelChange.bindEvent(this));Event.observe(calendarBody,"click",this.selectDate.bindEvent(this));},ieHack:function(cleanup) +document.body.appendChild(this._calDiv);this.update();this.updateHeader();this.ieHack(true);previousMonth.hideFocus=true;nextMonth.hideFocus=true;todayButton.hideFocus=true;Event.observe(previousMonth,"click",this.prevMonth.bindEvent(this));Event.observe(nextMonth,"click",this.nextMonth.bindEvent(this));Event.observe(todayButton,"click",this.selectToday.bindEvent(this));Event.observe(this._monthSelect,"change",this.monthSelect.bindEvent(this));Event.observe(this._yearSelect,"change",this.yearSelect.bindEvent(this));Event.observe(this._calDiv,"mousewheel",this.mouseWheelChange.bindEvent(this));Event.observe(calendarBody,"click",this.selectDate.bindEvent(this));Event.observe(this.control,"blur",this.hide.bind(this));Prado.Element.focus(this.control);},ieHack:function(cleanup) {if(this.iePopUp) {this.iePopUp.style.display="block";this.iePopUp.style.top=(this._calDiv.offsetTop-1)+"px";this.iePopUp.style.left=(this._calDiv.offsetLeft-1)+"px";this.iePopUp.style.width=Math.abs(this._calDiv.offsetWidth-2)+"px";this.iePopUp.style.height=(this._calDiv.offsetHeight+1)+"px";if(cleanup)this.iePopUp.style.display="none";}},keyPressed:function(ev) -{if(!this.showing)return;if(!ev)ev=document.parentWindow.event;var kc=ev.keyCode!=null?ev.keyCode:ev.charCode;if(kc==Event.KEY_RETURN) +{if(!this.showing)return;if(!ev)ev=document.parentWindow.event;var kc=ev.keyCode!=null?ev.keyCode:ev.charCode;if(kc==Event.KEY_RETURN||kc==Event.KEY_SPACEBAR) {this.setSelectedDate(this.selectedDate);Event.stop(ev);this.hide();} if(kc==Event.KEY_ESC) {Event.stop(ev);this.hide();} @@ -88,13 +88,11 @@ this.onChange();},getElement:function() {var d=this.newDate(this.selectedDate);d.setFullYear(year);this.setSelectedDate(d);},setMonth:function(month) {var d=this.newDate(this.selectedDate);d.setMonth(month);this.setSelectedDate(d);},nextMonth:function() {this.setMonth(this.selectedDate.getMonth()+1);},prevMonth:function() -{this.setMonth(this.selectedDate.getMonth()-1);},show:function() +{this.setMonth(this.selectedDate.getMonth()-1);},getDatePickerOffsetHeight:function() +{if(this.options.InputMode=="TextBox") +return this.control.offsetHeight;var control=Prado.WebUI.TDatePicker.getDayListControl(this.control);if(control)return control.offsetHeight;var control=Prado.WebUI.TDatePicker.getMonthListControl(this.control);if(control)return control.offsetHeight;var control=Prado.WebUI.TDatePicker.getYearListControl(this.control);if(control)return control.offsetHeight;return 0;},show:function() {this.create();if(!this.showing) -{var pos=Position.cumulativeOffset(this.control);if(this.options.InputMode=="TextBox") -pos[1]+=this.control.offsetHeight;else -{var dayList=Prado.WebUI.TDatePicker.getDayListControl(this.control);if(dayList) -pos[1]+=dayList.offsetHeight-1;} -this._calDiv.style.display="block";this._calDiv.style.top=(pos[1]-1)+"px";this._calDiv.style.left=pos[0]+"px";this.ieHack(false);this.documentClickEvent=this.hideOnClick.bindEvent(this);this.documentKeyDownEvent=this.keyPressed.bindEvent(this);Event.observe(document.body,"click",this.documentClickEvent);var date=this.getDateFromInput();if(date) +{var pos=Position.cumulativeOffset(this.control);pos[1]+=this.getDatePickerOffsetHeight();this._calDiv.style.display="block";this._calDiv.style.top=(pos[1]-1)+"px";this._calDiv.style.left=pos[0]+"px";this.ieHack(false);this.documentClickEvent=this.hideOnClick.bindEvent(this);this.documentKeyDownEvent=this.keyPressed.bindEvent(this);Event.observe(document.body,"click",this.documentClickEvent);var date=this.getDateFromInput();if(date) {this.selectedDate=date;this.setSelectedDate(date);} Event.observe(document,"keydown",this.documentKeyDownEvent);this.showing=true;}},getDateFromInput:function() {if(this.options.InputMode=="TextBox") diff --git a/framework/Web/Javascripts/js/prado.js b/framework/Web/Javascripts/js/prado.js index aa02e98e..87fd69a6 100644 --- a/framework/Web/Javascripts/js/prado.js +++ b/framework/Web/Javascripts/js/prado.js @@ -111,7 +111,7 @@ this.registerFormCallbacks();else this.registerCallback(this.element);},onElementEvent:function(){var value=this.getValue();if(this.lastValue!=value){this.callback(this.element,value);this.lastValue=value;}},registerFormCallbacks:function(){var elements=Form.getElements(this.element);for(var i=0;i<elements.length;i++) this.registerCallback(elements[i]);},registerCallback:function(element){if(element.type){switch(element.type.toLowerCase()){case'checkbox':case'radio':Event.observe(element,'click',this.onElementEvent.bind(this));break;case'password':case'text':case'textarea':case'select-one':case'select-multiple':Event.observe(element,'change',this.onElementEvent.bind(this));break;}}}} Form.Element.EventObserver=Class.create();Form.Element.EventObserver.prototype=Object.extend(new Abstract.EventObserver(),{getValue:function(){return Form.Element.getValue(this.element);}});Form.EventObserver=Class.create();Form.EventObserver.prototype=Object.extend(new Abstract.EventObserver(),{getValue:function(){return Form.serialize(this.element);}});if(!window.Event){var Event=new Object();} -Object.extend(Event,{KEY_BACKSPACE:8,KEY_TAB:9,KEY_RETURN:13,KEY_ESC:27,KEY_LEFT:37,KEY_UP:38,KEY_RIGHT:39,KEY_DOWN:40,KEY_DELETE:46,element:function(event){return event.target||event.srcElement;},isLeftClick:function(event){return(((event.which)&&(event.which==1))||((event.button)&&(event.button==1)));},pointerX:function(event){return event.pageX||(event.clientX+ +Object.extend(Event,{KEY_BACKSPACE:8,KEY_TAB:9,KEY_RETURN:13,KEY_ESC:27,KEY_LEFT:37,KEY_UP:38,KEY_RIGHT:39,KEY_DOWN:40,KEY_DELETE:46,KEY_SPACEBAR:32,element:function(event){return event.target||event.srcElement;},isLeftClick:function(event){return(((event.which)&&(event.which==1))||((event.button)&&(event.button==1)));},pointerX:function(event){return event.pageX||(event.clientX+ (document.documentElement.scrollLeft||document.body.scrollLeft));},pointerY:function(event){return event.pageY||(event.clientY+ (document.documentElement.scrollTop||document.body.scrollTop));},stop:function(event){if(event.preventDefault){event.preventDefault();event.stopPropagation();}else{event.returnValue=false;event.cancelBubble=true;}},findElement:function(event,tagName){var element=Event.element(event);while(element.parentNode&&(!element.tagName||(element.tagName.toUpperCase()!=tagName.toUpperCase()))) element=element.parentNode;return element;},observers:false,_observeAndCache:function(element,name,observer,useCapture){if(!this.observers)this.observers=[];if(element.addEventListener){this.observers.push([element,name,observer,useCapture]);element.addEventListener(name,observer,useCapture);}else if(element.attachEvent){this.observers.push([element,name,observer,useCapture]);element.attachEvent('on'+name,observer);}},unloadCache:function(){if(!Event.observers)return;for(var i=0;i<Event.observers.length;i++){Event.stopObserving.apply(this,Event.observers[i]);Event.observers[i][0]=null;} diff --git a/framework/Web/Javascripts/prototype/event.js b/framework/Web/Javascripts/prototype/event.js index 51b9010e..4def0e4a 100644 --- a/framework/Web/Javascripts/prototype/event.js +++ b/framework/Web/Javascripts/prototype/event.js @@ -12,6 +12,7 @@ Object.extend(Event, { KEY_RIGHT: 39, KEY_DOWN: 40, KEY_DELETE: 46, + KEY_SPACEBAR: 32, element: function(event) { return event.target || event.srcElement; diff --git a/framework/Web/UI/TTemplateManager.php b/framework/Web/UI/TTemplateManager.php index 60ce11ca..cb05fa35 100644 --- a/framework/Web/UI/TTemplateManager.php +++ b/framework/Web/UI/TTemplateManager.php @@ -383,16 +383,37 @@ class TTemplate extends TApplicationComponent implements ITemplate * @param string property name
* @param mixed property initial value
*/
+ protected function configureControl2($control,$name,$value)
+ {
+ if(strncasecmp($name,'on',2)===0) // is an event
+ $this->configureEvent($control,$name,$value,$control);
+ else if(($pos=strrpos($name,'.'))===false) // is a simple property or custom attribute
+ $this->configureProperty($control,$name,$value);
+ else // is a subproperty
+ {
+ $subName=substr($name,$pos+1);
+ if(strncasecmp($subName,'on',2)===0) // is an event: XXX.YYY.OnZZZ
+ {
+ $object=$control->getSubProperty(substr($name,0,$pos));
+ if(($object instanceof TControl))
+ $this->configureEvent($object,$subName,$value,$control);
+ else
+ $this->configureSubProperty($control,$name,$value);
+ }
+ else
+ $this->configureSubProperty($control,$name,$value);
+ }
+ }
+
protected function configureControl($control,$name,$value)
{
if(strncasecmp($name,'on',2)===0) // is an event
- $this->configureEvent($control,$name,$value);
- else if(strpos($name,'.')===false) // is a simple property or custom attribute
+ $this->configureEvent($control,$name,$value,$control);
+ else if(($pos=strrpos($name,'.'))===false) // is a simple property or custom attribute
$this->configureProperty($control,$name,$value);
else // is a subproperty
$this->configureSubProperty($control,$name,$value);
}
-
/**
* Configures a property of a non-control component.
* @param TComponent component to be configured
@@ -412,13 +433,14 @@ class TTemplate extends TApplicationComponent implements ITemplate * @param TControl control to be configured
* @param string event name
* @param string event handler
+ * @param TControl context control
*/
- protected function configureEvent($component,$name,$value)
+ protected function configureEvent($control,$name,$value,$contextControl)
{
if(strpos($value,'.')===false)
- $component->attachEventHandler($name,array($component,'TemplateControl.'.$value));
+ $control->attachEventHandler($name,array($contextControl,'TemplateControl.'.$value));
else
- $component->attachEventHandler($name,array($component,$value));
+ $control->attachEventHandler($name,array($contextControl,$value));
}
/**
diff --git a/framework/Web/UI/WebControls/TDataBoundControl.php b/framework/Web/UI/WebControls/TDataBoundControl.php index 0bb771bf..9e6ecbf3 100644 --- a/framework/Web/UI/WebControls/TDataBoundControl.php +++ b/framework/Web/UI/WebControls/TDataBoundControl.php @@ -59,7 +59,7 @@ abstract class TDataBoundControl extends TWebControl */
public function setDataSource($value)
{
- $this->_dataSource=$this->validateDataSource($value);;
+ $this->_dataSource=$this->validateDataSource($value);
$this->onDataSourceChanged();
}
diff --git a/framework/Web/UI/WebControls/TMarkdown.php b/framework/Web/UI/WebControls/TMarkdown.php new file mode 100644 index 00000000..49660b4e --- /dev/null +++ b/framework/Web/UI/WebControls/TMarkdown.php @@ -0,0 +1,113 @@ +<?php
+/**
+ * TMarkdown class file
+ *
+ * @author Wei Zhuo <weizhuo[at]gmail[dot]com>
+ * @link http://www.pradosoft.com/
+ * @copyright Copyright © 2005 PradoSoft
+ * @license http://www.pradosoft.com/license/
+ * @version $Revision: $ $Date: $
+ * @package System.Web.UI.WebControls
+ */
+
+/**
+ * TMarkdown class
+ *
+ * TMarkdown is a control that produces HTML from code with markdown syntax.
+ *
+ * Markdown is a text-to-HTML conversion tool for web writers. Markdown allows
+ * you to write using an easy-to-read, easy-to-write plain text format, then
+ * convert it to structurally valid XHTML (or HTML).
+ * Further documentation regarding Markdown can be found at
+ * http://daringfireball.net/projects/markdown/
+ *
+ * To use TMarkdown, simply enclose the content to be rendered within
+ * the body of TMarkdown in a template.
+ *
+ * See http://www.pradosoft.com/demos/quickstart/?page=Markdown for
+ * details on the Markdown syntax usage.
+ *
+ * @author Wei Zhuo <weizhuo[at]gmail[dot]com>
+ * @version $Revision: $ $Date: $
+ * @package System.Web.UI.WebControls
+ * @since 3.0
+ */
+class TMarkdown extends TControl
+{
+ /**
+ * @var TTextHighlighter
+ */
+ private $_highlighter;
+
+ /**
+ * Renders body content.
+ * This method overrides parent implementation by removing
+ * malicious javascript code from the body content
+ * @param THtmlWriter writer
+ */
+ public function render($writer)
+ {
+ $textWriter=new TTextWriter;
+ parent::render(new THtmlWriter($textWriter));
+ $writer->write($this->renderMarkdown($textWriter->flush()));
+ }
+
+ /**
+ * Use MarkdownParser to render the HTML content.
+ * @param string markdown content
+ * @return string HTML content
+ */
+ protected function renderMarkdown($text)
+ {
+ $renderer = Prado::createComponent('System.3rdParty.Markdown.MarkdownParser');
+ $result = $renderer->parse($text);
+ return preg_replace_callback(
+ '/<pre><code>\[\s*(\w+)\s*\]\n+((.|\n)*?)\s*<\\/code><\\/pre>/im',
+ array($this, 'highlightCode'), $result);
+ }
+
+ /**
+ * @return TTextHighlighter source code highlighter
+ */
+ public function getTextHighlighter()
+ {
+ if(is_null($this->_highlighter))
+ $this->_highlighter = new TTextHighlighter;
+ return $this->_highlighter;
+ }
+
+
+ /**
+ * Highlights source code using TTextHighlighter
+ * @param array matches of code blocks
+ * @return string highlighted code.
+ */
+ protected function highlightCode($matches)
+ {
+ $text = new TTextWriter;
+ $writer = new THtmlWriter($text);
+ $hi = $this->getTextHighlighter();
+ if($hi->getControls()->getCount() > 0)
+ $hi->getControls()->removeAt(0);
+ $hi->addParsedObject(html_entity_decode($matches[2]));
+ $hi->setLanguage($matches[1]);
+ $hi->render($writer);
+ return $text->flush();
+ }
+
+ /**
+ * Registers css style for the highlighted result.
+ * This method overrides parent implementation.
+ * @param THtmlWriter writer
+ */
+ public function onPreRender($writer)
+ {
+ parent::onPreRender($writer);
+ $hi = $this->getTextHighlighter();
+ $this->getControls()->insertAt(0,$hi);
+ $hi->onPreRender($writer);
+ $this->getControls()->removeAt(0);
+ }
+}
+
+?>
\ No newline at end of file diff --git a/framework/Web/UI/WebControls/TRepeater.php b/framework/Web/UI/WebControls/TRepeater.php index 1acdc766..9aa7af8d 100644 --- a/framework/Web/UI/WebControls/TRepeater.php +++ b/framework/Web/UI/WebControls/TRepeater.php @@ -11,9 +11,10 @@ */
/**
- * Using TDataBoundControl cass
+ * Using TDataBoundControl and TDataFieldAccessor cass
*/
Prado::using('System.Web.UI.WebControls.TDataBoundControl');
+Prado::using('System.Util.TDataFieldAccessor');
/**
* TRepeater class
diff --git a/framework/Web/UI/WebControls/TValidationSummary.php b/framework/Web/UI/WebControls/TValidationSummary.php index 8148036b..cfb57c5b 100644 --- a/framework/Web/UI/WebControls/TValidationSummary.php +++ b/framework/Web/UI/WebControls/TValidationSummary.php @@ -223,9 +223,24 @@ class TValidationSummary extends TWebControl {
if(!$this->getEnabled(true) || !$this->getEnableClientScript())
return;
+ $cs = $this->getPage()->getClientScript();
+ $cs->registerPradoScript('validator');
+
+ //need to register the validation manager is validation summary is alone.
+ $formID=$this->getPage()->getForm()->getClientID();
+ $scriptKey = "TBaseValidator:$formID";
+ if($this->getEnableClientScript() && !$cs->isEndScriptRegistered($scriptKey))
+ {
+ $manager['FormID'] = $formID;
+ $options = TJavaScript::encode($manager);
+ $cs->registerPradoScript('validator');
+ $cs->registerEndScript($scriptKey, "new Prado.ValidationManager({$options});");
+ }
+
+
$options=TJavaScript::encode($this->getClientScriptOptions());
$script = "new Prado.WebUI.TValidationSummary({$options});";
- $this->getPage()->getClientScript()->registerEndScript($this->getClientID(), $script);
+ $cs->registerEndScript($this->getClientID(), $script);
}
/**
diff --git a/framework/Web/UI/WebControls/TWizard.php b/framework/Web/UI/WebControls/TWizard.php index 2d2815ba..811c4e76 100644 --- a/framework/Web/UI/WebControls/TWizard.php +++ b/framework/Web/UI/WebControls/TWizard.php @@ -622,7 +622,7 @@ class TWizard extends TWebControl implements INamingContainer }
/**
- * @var TWizardNavigationContainer container of the start navigation
+ * @return TWizardNavigationContainer container of the start navigation
*/
public function getStartNavigation()
{
@@ -630,7 +630,7 @@ class TWizard extends TWebControl implements INamingContainer }
/**
- * @var TWizardNavigationContainer container of the step navigation
+ * @return TWizardNavigationContainer container of the step navigation
*/
public function getStepNavigation()
{
@@ -638,7 +638,7 @@ class TWizard extends TWebControl implements INamingContainer }
/**
- * @var TWizardNavigationContainer container of the finish navigation
+ * @return TWizardNavigationContainer container of the finish navigation
*/
public function getFinishNavigation()
{
|