diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..acc5026 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,15 @@ +# Set default behaviour, in case users don't have core.autocrlf set. +* text=auto + +# Explicitly declare text files we want to always be normalized and converted +# to native line endings on checkout. +*.php text eol=lf +*.js text eol=lf +*.css text eol=lf +*.html text eol=lf +*.xml text eol=lf + +# Denote all files that are truly binary and should not be modified. +*.png binary +*.jpg binary + diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..cd0f9ed --- /dev/null +++ b/.gitignore @@ -0,0 +1,26 @@ +.*.swp +.*.swo +._* +.DS_Store +/Debug/ +/ImgCache/ +/Backup_rar/ +/Debug/ +/debug/ +/upload/ +/avatar/ +/.idea/ +.svn/ +*.orig +*.aps +*.APS +*.chm +*.exp +*.pdb +*.rar +.smbdelete* +*.sublime* +.sass-cache +config.rb +/config.inc.php +/usr/uploads/ diff --git a/Akismet/Plugin.php b/Akismet/Plugin.php new file mode 100644 index 0000000..3b6adf9 --- /dev/null +++ b/Akismet/Plugin.php @@ -0,0 +1,213 @@ +comment = array('Akismet_Plugin', 'filter'); + Typecho_Plugin::factory('Widget_Feedback')->trackback = array('Akismet_Plugin', 'filter'); + Typecho_Plugin::factory('Widget_XmlRpc')->pingback = array('Akismet_Plugin', 'filter'); + Typecho_Plugin::factory('Widget_Comments_Edit')->mark = array('Akismet_Plugin', 'mark'); + + return _t('请配置此插件的API KEY, 以使您的反垃圾策略生效'); + } + + /** + * 禁用插件方法,如果禁用失败,直接抛出异常 + * + * @static + * @access public + * @return void + * @throws Typecho_Plugin_Exception + */ + public static function deactivate(){} + + /** + * 获取插件配置面板 + * + * @access public + * @param Typecho_Widget_Helper_Form $form 配置面板 + * @return void + */ + public static function config(Typecho_Widget_Helper_Form $form) + { + $key = new Typecho_Widget_Helper_Form_Element_Textarea('key', NULL, NULL, _t('服务密钥'), _t('此密钥需要向服务提供商注册
+ 它是一个用于表明您合法用户身份的字符串')); + $form->addInput($key->addRule('required', _t('您必须填写一个服务密钥')) + ->addRule(array('Akismet_Plugin', 'validate'), _t('您使用的服务密钥错误'))); + + $url = new Typecho_Widget_Helper_Form_Element_Text('url', NULL, 'http://rest.akismet.com', + _t('服务地址'), _t('这是反垃圾评论服务提供商的服务器地址
+ 我们推荐您使用 Akismet 或者 Typepad 的反垃圾服务')); + $form->addInput($url->addRule('url', _t('您使用的地址格式错误'))); + } + + /** + * 个人用户的配置面板 + * + * @access public + * @param Typecho_Widget_Helper_Form $form + * @return void + */ + public static function personalConfig(Typecho_Widget_Helper_Form $form){} + + /** + * 验证api的key值 + * + * @access public + * @param string $key 服务密钥 + * @return boolean + */ + public static function validate($key) + { + $options = Typecho_Widget::widget('Widget_Options'); + $url = Typecho_Request::getInstance()->url; + + $data = array( + 'key' => $key, + 'blog' => $options->siteUrl + ); + + $client = Typecho_Http_Client::get('Curl', 'Socket'); + if (false != $client) { + $client->setData($data) + ->setHeader('User-Agent', $options->generator . ' | Akismet/1.1') + ->send(Typecho_Common::url('/1.1/verify-key', $url)); + + if ('valid' == $client->getResponseBody()) { + return true; + } + } + + return false; + } + + /** + * 标记评论状态时的插件接口 + * + * @access public + * @param array $comment 评论数据的结构体 + * @param Typecho_Widget $commentWidget 评论组件 + * @param string $status 评论状态 + * @return void + */ + public static function mark($comment, $commentWidget, $status) + { + if ('spam' == $comment['status'] && $status != 'spam') { + self::filter($comment, $commentWidget, NULL, 'submit-ham'); + } else if ('spam' != $comment['status'] && $status == 'spam') { + self::filter($comment, $commentWidget, NULL, 'submit-spam'); + } + } + + /** + * 评论过滤器 + * + * @access public + * @param array $comment 评论结构 + * @param Typecho_Widget $post 被评论的文章 + * @param array $result 返回的结果上下文 + * @param string $api api地址 + * @return void + */ + public static function filter($comment, $post, $result, $api = 'comment-check') + { + $comment = empty($result) ? $comment : $result; + + $options = Typecho_Widget::widget('Widget_Options'); + $url = $options->plugin('Akismet')->url; + $key = $options->plugin('Akismet')->key; + + $allowedServerVars = 'comment-check' == $api ? array( + 'SCRIPT_URI', + 'HTTP_HOST', + 'HTTP_USER_AGENT', + 'HTTP_ACCEPT', + 'HTTP_ACCEPT_LANGUAGE', + 'HTTP_ACCEPT_ENCODING', + 'HTTP_ACCEPT_CHARSET', + 'HTTP_KEEP_ALIVE', + 'HTTP_CONNECTION', + 'HTTP_CACHE_CONTROL', + 'HTTP_PRAGMA', + 'HTTP_DATE', + 'HTTP_EXPECT', + 'HTTP_MAX_FORWARDS', + 'HTTP_RANGE', + 'CONTENT_TYPE', + 'CONTENT_LENGTH', + 'SERVER_SIGNATURE', + 'SERVER_SOFTWARE', + 'SERVER_NAME', + 'SERVER_ADDR', + 'SERVER_PORT', + 'REMOTE_PORT', + 'GATEWAY_INTERFACE', + 'SERVER_PROTOCOL', + 'REQUEST_METHOD', + 'QUERY_STRING', + 'REQUEST_URI', + 'SCRIPT_NAME', + 'REQUEST_TIME' + ) : array(); + + $data = array( + 'blog' => $options->siteUrl, + 'user_ip' => $comment['ip'], + 'user_agent' => $comment['agent'], + 'referrer' => Typecho_Request::getInstance()->getReferer(), + 'permalink' => $post->permalink, + 'comment_type' => $comment['type'], + 'comment_author' => $comment['author'], + 'comment_author_email' => $comment['mail'], + 'comment_author_url' => $comment['url'], + 'comment_content' => $comment['text'] + ); + + foreach ($allowedServerVars as $val) { + if (array_key_exists($val, $_SERVER)) { + $data[$val] = $_SERVER[$val]; + } + } + + try { + $client = Typecho_Http_Client::get(); + if (false != $client && $key) { + $params = parse_url($url); + $url = $params['scheme'] . '://' . $key . '.' . $params['host'] . (isset($params['path']) ? $params['path'] : NULL); + + $client->setHeader('User-Agent', $options->generator . ' | Akismet/1.1') + ->setTimeout(5) + ->setData($data) + ->send(Typecho_Common::url('/1.1/' . $api, $url)); + + if ('true' == $client->getResponseBody()) { + $comment['status'] = 'spam'; + } + } + } catch (Typecho_Http_Client_Exception $e) { + //do nothing + error_log($e->getMessage()); + } + + return $comment; + } +} diff --git a/BlockComment/Plugin.php b/BlockComment/Plugin.php new file mode 100644 index 0000000..b80ecd8 --- /dev/null +++ b/BlockComment/Plugin.php @@ -0,0 +1,124 @@ +comment = array('BlockComment_Plugin', 'filter'); + Typecho_Plugin::factory('Widget_Feedback')->trackback = array('BlockComment_Plugin', 'filter'); + Typecho_Plugin::factory('Widget_XmlRpc')->pingback = array('BlockComment_Plugin', 'filter'); + } + + /** + * 禁用插件方法,如果禁用失败,直接抛出异常 + * + * @static + * @access public + * @return void + * @throws Typecho_Plugin_Exception + */ + public static function deactivate(){} + + /** + * 获取插件配置面板 + * + * @access public + * @param Typecho_Widget_Helper_Form $form 配置面板 + * @return void + */ + public static function config(Typecho_Widget_Helper_Form $form) + { + $hosts = new Typecho_Widget_Helper_Form_Element_Textarea('hosts', NULL, NULL, + _t('地址列表'), _t('每行单个地址,请仔细匹配以免误封杀')); + + $form->addInput($hosts); + } + + /** + * 个人用户的配置面板 + * + * @access public + * @param Typecho_Widget_Helper_Form $form + * @return void + */ + public static function personalConfig(Typecho_Widget_Helper_Form $form){} + + + /** + * 标记评论状态时的插件接口 + * + * @access public + * @param array $comment 评论数据的结构体 + * @param Typecho_Widget $commentWidget 评论组件 + * @param string $status 评论状态 + * @return void + */ + public static function mark($comment, $commentWidget, $status) + { + if ('spam' == $comment['status'] && $status != 'spam') { + self::filter($comment, $commentWidget, NULL, 'submit-ham'); + } else if ('spam' != $comment['status'] && $status == 'spam') { + self::filter($comment, $commentWidget, NULL, 'submit-spam'); + } + } + + /** + * 评论过滤器 + * + * @access public + * @param array $comment 评论结构 + * @param Typecho_Widget $post 被评论的文章 + * @param array $result 返回的结果上下文 + * @param string $api api地址 + * @return void + */ + public static function filter($comment, $post, $result, $api = 'comment-check') + { + $comment = empty($result) ? $comment : $result; + + $options = Typecho_Widget::widget('Widget_Options'); + $hosts = $options->plugin('BlockComment')->hosts; + + $data = array( + 'blog' => $options->siteUrl, + 'user_ip' => $comment['ip'], + 'user_agent' => $comment['agent'], + 'referrer' => Typecho_Request::getInstance()->getReferer(), + 'permalink' => $post->permalink, + 'comment_type' => $comment['type'], + 'comment_author' => $comment['author'], + 'comment_author_email' => $comment['mail'], + 'comment_author_url' => $comment['url'], + 'comment_content' => $comment['text'] + ); + + foreach(split("\n", $hosts) as $key => $value){ + $value = trim($value); + if (strlen($value)) { + $regex = sprintf("/^%s/i", preg_quote($value)); + + // 如果提交者符合指定的 IP,则扔进垃圾评论中 + if (preg_match($regex, $data['user_ip'])) { + $comment['status'] = 'spam'; + break; + } + } + } + + return $comment; + } +} diff --git a/ConnectToTwitter/OAuth.php b/ConnectToTwitter/OAuth.php new file mode 100644 index 0000000..b36520e --- /dev/null +++ b/ConnectToTwitter/OAuth.php @@ -0,0 +1,768 @@ +key = $key; + $this->secret = $secret; + $this->callback_url = $callback_url; + }/*}}}*/ + + function __toString() {/*{{{*/ + return "OAuthConsumer[key=$this->key,secret=$this->secret]"; + }/*}}}*/ +}/*}}}*/ + +class OAuthToken {/*{{{*/ + // access tokens and request tokens + public $key; + public $secret; + + /** + * key = the token + * secret = the token secret + */ + function __construct($key, $secret) {/*{{{*/ + $this->key = $key; + $this->secret = $secret; + }/*}}}*/ + + /** + * generates the basic string serialization of a token that a server + * would respond to request_token and access_token calls with + */ + function to_string() {/*{{{*/ + return "oauth_token=" . OAuthUtil::urlencode_rfc3986($this->key) . + "&oauth_token_secret=" . OAuthUtil::urlencode_rfc3986($this->secret); + }/*}}}*/ + + function __toString() {/*{{{*/ + return $this->to_string(); + }/*}}}*/ +}/*}}}*/ + +class OAuthSignatureMethod {/*{{{*/ + public function check_signature(&$request, $consumer, $token, $signature) { + $built = $this->build_signature($request, $consumer, $token); + return $built == $signature; + } +}/*}}}*/ + +class OAuthSignatureMethod_HMAC_SHA1 extends OAuthSignatureMethod {/*{{{*/ + function get_name() {/*{{{*/ + return "HMAC-SHA1"; + }/*}}}*/ + + public function build_signature($request, $consumer, $token) {/*{{{*/ + $base_string = $request->get_signature_base_string(); + $request->base_string = $base_string; + + $key_parts = array( + $consumer->secret, + ($token) ? $token->secret : "" + ); + + $key_parts = OAuthUtil::urlencode_rfc3986($key_parts); + $key = implode('&', $key_parts); + + return base64_encode( hash_hmac('sha1', $base_string, $key, true)); + }/*}}}*/ +}/*}}}*/ + +class OAuthSignatureMethod_PLAINTEXT extends OAuthSignatureMethod {/*{{{*/ + public function get_name() {/*{{{*/ + return "PLAINTEXT"; + }/*}}}*/ + + public function build_signature($request, $consumer, $token) {/*{{{*/ + $sig = array( + OAuthUtil::urlencode_rfc3986($consumer->secret) + ); + + if ($token) { + array_push($sig, OAuthUtil::urlencode_rfc3986($token->secret)); + } else { + array_push($sig, ''); + } + + $raw = implode("&", $sig); + // for debug purposes + $request->base_string = $raw; + + return OAuthUtil::urlencode_rfc3986($raw); + }/*}}}*/ +}/*}}}*/ + +class OAuthSignatureMethod_RSA_SHA1 extends OAuthSignatureMethod {/*{{{*/ + public function get_name() {/*{{{*/ + return "RSA-SHA1"; + }/*}}}*/ + + protected function fetch_public_cert(&$request) {/*{{{*/ + // not implemented yet, ideas are: + // (1) do a lookup in a table of trusted certs keyed off of consumer + // (2) fetch via http using a url provided by the requester + // (3) some sort of specific discovery code based on request + // + // either way should return a string representation of the certificate + throw Exception("fetch_public_cert not implemented"); + }/*}}}*/ + + protected function fetch_private_cert(&$request) {/*{{{*/ + // not implemented yet, ideas are: + // (1) do a lookup in a table of trusted certs keyed off of consumer + // + // either way should return a string representation of the certificate + throw Exception("fetch_private_cert not implemented"); + }/*}}}*/ + + public function build_signature(&$request, $consumer, $token) {/*{{{*/ + $base_string = $request->get_signature_base_string(); + $request->base_string = $base_string; + + // Fetch the private key cert based on the request + $cert = $this->fetch_private_cert($request); + + // Pull the private key ID from the certificate + $privatekeyid = openssl_get_privatekey($cert); + + // Sign using the key + $ok = openssl_sign($base_string, $signature, $privatekeyid); + + // Release the key resource + openssl_free_key($privatekeyid); + + return base64_encode($signature); + } /*}}}*/ + + public function check_signature(&$request, $consumer, $token, $signature) {/*{{{*/ + $decoded_sig = base64_decode($signature); + + $base_string = $request->get_signature_base_string(); + + // Fetch the public key cert based on the request + $cert = $this->fetch_public_cert($request); + + // Pull the public key ID from the certificate + $publickeyid = openssl_get_publickey($cert); + + // Check the computed signature against the one passed in the query + $ok = openssl_verify($base_string, $decoded_sig, $publickeyid); + + // Release the key resource + openssl_free_key($publickeyid); + + return $ok == 1; + } /*}}}*/ +}/*}}}*/ + +class OAuthRequest {/*{{{*/ + private $parameters; + private $http_method; + private $http_url; + // for debug purposes + public $base_string; + public static $version = '1.0'; + + function __construct($http_method, $http_url, $parameters=NULL) {/*{{{*/ + @$parameters or $parameters = array(); + $this->parameters = $parameters; + $this->http_method = $http_method; + $this->http_url = $http_url; + }/*}}}*/ + + + /** + * attempt to build up a request from what was passed to the server + */ + public static function from_request($http_method=NULL, $http_url=NULL, $parameters=NULL) {/*{{{*/ + $scheme = (!isset($_SERVER['HTTPS']) || $_SERVER['HTTPS'] != "on") ? 'http' : 'https'; + @$http_url or $http_url = $scheme . '://' . $_SERVER['HTTP_HOST'] . ':' . $_SERVER['SERVER_PORT'] . $_SERVER['REQUEST_URI']; + @$http_method or $http_method = $_SERVER['REQUEST_METHOD']; + + $request_headers = OAuthRequest::get_headers(); + + // let the library user override things however they'd like, if they know + // which parameters to use then go for it, for example XMLRPC might want to + // do this + if ($parameters) { + $req = new OAuthRequest($http_method, $http_url, $parameters); + } else { + // collect request parameters from query string (GET) and post-data (POST) if appropriate (note: POST vars have priority) + $req_parameters = $_GET; + if ($http_method == "POST" && @strstr($request_headers["Content-Type"], "application/x-www-form-urlencoded") ) { + $req_parameters = array_merge($req_parameters, $_POST); + } + + // next check for the auth header, we need to do some extra stuff + // if that is the case, namely suck in the parameters from GET or POST + // so that we can include them in the signature + if (@substr($request_headers['Authorization'], 0, 6) == "OAuth ") { + $header_parameters = OAuthRequest::split_header($request_headers['Authorization']); + $parameters = array_merge($req_parameters, $header_parameters); + $req = new OAuthRequest($http_method, $http_url, $parameters); + } else $req = new OAuthRequest($http_method, $http_url, $req_parameters); + } + + return $req; + }/*}}}*/ + + /** + * pretty much a helper function to set up the request + */ + public static function from_consumer_and_token($consumer, $token, $http_method, $http_url, $parameters=NULL) {/*{{{*/ + @$parameters or $parameters = array(); + $defaults = array("oauth_version" => OAuthRequest::$version, + "oauth_nonce" => OAuthRequest::generate_nonce(), + "oauth_timestamp" => OAuthRequest::generate_timestamp(), + "oauth_consumer_key" => $consumer->key); + $parameters = array_merge($defaults, $parameters); + + if ($token) { + $parameters['oauth_token'] = $token->key; + } + return new OAuthRequest($http_method, $http_url, $parameters); + }/*}}}*/ + + public function set_parameter($name, $value) {/*{{{*/ + $this->parameters[$name] = $value; + }/*}}}*/ + + public function get_parameter($name) {/*{{{*/ + return isset($this->parameters[$name]) ? $this->parameters[$name] : null; + }/*}}}*/ + + public function get_parameters() {/*{{{*/ + return $this->parameters; + }/*}}}*/ + + /** + * Returns the normalized parameters of the request + * + * This will be all (except oauth_signature) parameters, + * sorted first by key, and if duplicate keys, then by + * value. + * + * The returned string will be all the key=value pairs + * concated by &. + * + * @return string + */ + public function get_signable_parameters() {/*{{{*/ + // Grab all parameters + $params = $this->parameters; + + // Remove oauth_signature if present + if (isset($params['oauth_signature'])) { + unset($params['oauth_signature']); + } + + // Urlencode both keys and values + $keys = OAuthUtil::urlencode_rfc3986(array_keys($params)); + $values = OAuthUtil::urlencode_rfc3986(array_values($params)); + $params = array_combine($keys, $values); + + // Sort by keys (natsort) + uksort($params, 'strcmp'); + + // Generate key=value pairs + $pairs = array(); + foreach ($params as $key=>$value ) { + if (is_array($value)) { + // If the value is an array, it's because there are multiple + // with the same key, sort them, then add all the pairs + natsort($value); + foreach ($value as $v2) { + $pairs[] = $key . '=' . $v2; + } + } else { + $pairs[] = $key . '=' . $value; + } + } + + // Return the pairs, concated with & + return implode('&', $pairs); + }/*}}}*/ + + /** + * Returns the base string of this request + * + * The base string defined as the method, the url + * and the parameters (normalized), each urlencoded + * and the concated with &. + */ + public function get_signature_base_string() {/*{{{*/ + $parts = array( + $this->get_normalized_http_method(), + $this->get_normalized_http_url(), + $this->get_signable_parameters() + ); + + $parts = OAuthUtil::urlencode_rfc3986($parts); + + return implode('&', $parts); + }/*}}}*/ + + /** + * just uppercases the http method + */ + public function get_normalized_http_method() {/*{{{*/ + return strtoupper($this->http_method); + }/*}}}*/ + + /** + * parses the url and rebuilds it to be + * scheme://host/path + */ + public function get_normalized_http_url() {/*{{{*/ + $parts = parse_url($this->http_url); + + $port = @$parts['port']; + $scheme = $parts['scheme']; + $host = $parts['host']; + $path = @$parts['path']; + + $port or $port = ($scheme == 'https') ? '443' : '80'; + + if (($scheme == 'https' && $port != '443') + || ($scheme == 'http' && $port != '80')) { + $host = "$host:$port"; + } + return "$scheme://$host$path"; + }/*}}}*/ + + /** + * builds a url usable for a GET request + */ + public function to_url() {/*{{{*/ + $out = $this->get_normalized_http_url() . "?"; + $out .= $this->to_postdata(); + return $out; + }/*}}}*/ + + /** + * builds the data one would send in a POST request + * + * TODO(morten.fangel): + * this function might be easily replaced with http_build_query() + * and corrections for rfc3986 compatibility.. but not sure + */ + public function to_postdata() {/*{{{*/ + $total = array(); + foreach ($this->parameters as $k => $v) { + if (is_array($v)) { + foreach ($v as $va) { + $total[] = OAuthUtil::urlencode_rfc3986($k) . "[]=" . OAuthUtil::urlencode_rfc3986($va); + } + } else { + $total[] = OAuthUtil::urlencode_rfc3986($k) . "=" . OAuthUtil::urlencode_rfc3986($v); + } + } + $out = implode("&", $total); + return $out; + }/*}}}*/ + + /** + * builds the Authorization: header + */ + public function to_header() {/*{{{*/ + $out ='Authorization: OAuth realm=""'; + $total = array(); + foreach ($this->parameters as $k => $v) { + if (substr($k, 0, 5) != "oauth") continue; + if (is_array($v)) throw new OAuthException('Arrays not supported in headers'); + $out .= ',' . OAuthUtil::urlencode_rfc3986($k) . '="' . OAuthUtil::urlencode_rfc3986($v) . '"'; + } + return $out; + }/*}}}*/ + + public function __toString() {/*{{{*/ + return $this->to_url(); + }/*}}}*/ + + + public function sign_request($signature_method, $consumer, $token) {/*{{{*/ + $this->set_parameter("oauth_signature_method", $signature_method->get_name()); + $signature = $this->build_signature($signature_method, $consumer, $token); + $this->set_parameter("oauth_signature", $signature); + }/*}}}*/ + + public function build_signature($signature_method, $consumer, $token) {/*{{{*/ + $signature = $signature_method->build_signature($this, $consumer, $token); + return $signature; + }/*}}}*/ + + /** + * util function: current timestamp + */ + private static function generate_timestamp() {/*{{{*/ + return time(); + }/*}}}*/ + + /** + * util function: current nonce + */ + private static function generate_nonce() {/*{{{*/ + $mt = microtime(); + $rand = mt_rand(); + + return md5($mt . $rand); // md5s look nicer than numbers + }/*}}}*/ + + /** + * util function for turning the Authorization: header into + * parameters, has to do some unescaping + */ + private static function split_header($header) {/*{{{*/ + $pattern = '/(([-_a-z]*)=("([^"]*)"|([^,]*)),?)/'; + $offset = 0; + $params = array(); + while (preg_match($pattern, $header, $matches, PREG_OFFSET_CAPTURE, $offset) > 0) { + $match = $matches[0]; + $header_name = $matches[2][0]; + $header_content = (isset($matches[5])) ? $matches[5][0] : $matches[4][0]; + $params[$header_name] = OAuthUtil::urldecode_rfc3986( $header_content ); + $offset = $match[1] + strlen($match[0]); + } + + if (isset($params['realm'])) { + unset($params['realm']); + } + + return $params; + }/*}}}*/ + + /** + * helper to try to sort out headers for people who aren't running apache + */ + private static function get_headers() {/*{{{*/ + if (function_exists('apache_request_headers')) { + // we need this to get the actual Authorization: header + // because apache tends to tell us it doesn't exist + return apache_request_headers(); + } + // otherwise we don't have apache and are just going to have to hope + // that $_SERVER actually contains what we need + $out = array(); + foreach ($_SERVER as $key => $value) { + if (substr($key, 0, 5) == "HTTP_") { + // this is chaos, basically it is just there to capitalize the first + // letter of every word that is not an initial HTTP and strip HTTP + // code from przemek + $key = str_replace(" ", "-", ucwords(strtolower(str_replace("_", " ", substr($key, 5))))); + $out[$key] = $value; + } + } + return $out; + }/*}}}*/ +}/*}}}*/ + +class OAuthServer {/*{{{*/ + protected $timestamp_threshold = 300; // in seconds, five minutes + protected $version = 1.0; // hi blaine + protected $signature_methods = array(); + + protected $data_store; + + function __construct($data_store) {/*{{{*/ + $this->data_store = $data_store; + }/*}}}*/ + + public function add_signature_method($signature_method) {/*{{{*/ + $this->signature_methods[$signature_method->get_name()] = + $signature_method; + }/*}}}*/ + + // high level functions + + /** + * process a request_token request + * returns the request token on success + */ + public function fetch_request_token(&$request) {/*{{{*/ + $this->get_version($request); + + $consumer = $this->get_consumer($request); + + // no token required for the initial token request + $token = NULL; + + $this->check_signature($request, $consumer, $token); + + $new_token = $this->data_store->new_request_token($consumer); + + return $new_token; + }/*}}}*/ + + /** + * process an access_token request + * returns the access token on success + */ + public function fetch_access_token(&$request) {/*{{{*/ + $this->get_version($request); + + $consumer = $this->get_consumer($request); + + // requires authorized request token + $token = $this->get_token($request, $consumer, "request"); + + + $this->check_signature($request, $consumer, $token); + + $new_token = $this->data_store->new_access_token($token, $consumer); + + return $new_token; + }/*}}}*/ + + /** + * verify an api call, checks all the parameters + */ + public function verify_request(&$request) {/*{{{*/ + $this->get_version($request); + $consumer = $this->get_consumer($request); + $token = $this->get_token($request, $consumer, "access"); + $this->check_signature($request, $consumer, $token); + return array($consumer, $token); + }/*}}}*/ + + // Internals from here + /** + * version 1 + */ + private function get_version(&$request) {/*{{{*/ + $version = $request->get_parameter("oauth_version"); + if (!$version) { + $version = 1.0; + } + if ($version && $version != $this->version) { + throw new OAuthException("OAuth version '$version' not supported"); + } + return $version; + }/*}}}*/ + + /** + * figure out the signature with some defaults + */ + private function get_signature_method(&$request) {/*{{{*/ + $signature_method = + @$request->get_parameter("oauth_signature_method"); + if (!$signature_method) { + $signature_method = "PLAINTEXT"; + } + if (!in_array($signature_method, + array_keys($this->signature_methods))) { + throw new OAuthException( + "Signature method '$signature_method' not supported try one of the following: " . implode(", ", array_keys($this->signature_methods)) + ); + } + return $this->signature_methods[$signature_method]; + }/*}}}*/ + + /** + * try to find the consumer for the provided request's consumer key + */ + private function get_consumer(&$request) {/*{{{*/ + $consumer_key = @$request->get_parameter("oauth_consumer_key"); + if (!$consumer_key) { + throw new OAuthException("Invalid consumer key"); + } + + $consumer = $this->data_store->lookup_consumer($consumer_key); + if (!$consumer) { + throw new OAuthException("Invalid consumer"); + } + + return $consumer; + }/*}}}*/ + + /** + * try to find the token for the provided request's token key + */ + private function get_token(&$request, $consumer, $token_type="access") {/*{{{*/ + $token_field = @$request->get_parameter('oauth_token'); + $token = $this->data_store->lookup_token( + $consumer, $token_type, $token_field + ); + if (!$token) { + throw new OAuthException("Invalid $token_type token: $token_field"); + } + return $token; + }/*}}}*/ + + /** + * all-in-one function to check the signature on a request + * should guess the signature method appropriately + */ + private function check_signature(&$request, $consumer, $token) {/*{{{*/ + // this should probably be in a different method + $timestamp = @$request->get_parameter('oauth_timestamp'); + $nonce = @$request->get_parameter('oauth_nonce'); + + $this->check_timestamp($timestamp); + $this->check_nonce($consumer, $token, $nonce, $timestamp); + + $signature_method = $this->get_signature_method($request); + + $signature = $request->get_parameter('oauth_signature'); + $valid_sig = $signature_method->check_signature( + $request, + $consumer, + $token, + $signature + ); + + if (!$valid_sig) { + throw new OAuthException("Invalid signature"); + } + }/*}}}*/ + + /** + * check that the timestamp is new enough + */ + private function check_timestamp($timestamp) {/*{{{*/ + // verify that timestamp is recentish + $now = time(); + if ($now - $timestamp > $this->timestamp_threshold) { + throw new OAuthException("Expired timestamp, yours $timestamp, ours $now"); + } + }/*}}}*/ + + /** + * check that the nonce is not repeated + */ + private function check_nonce($consumer, $token, $nonce, $timestamp) {/*{{{*/ + // verify that the nonce is uniqueish + $found = $this->data_store->lookup_nonce($consumer, $token, $nonce, $timestamp); + if ($found) { + throw new OAuthException("Nonce already used: $nonce"); + } + }/*}}}*/ + + + +}/*}}}*/ + +class OAuthDataStore {/*{{{*/ + function lookup_consumer($consumer_key) {/*{{{*/ + // implement me + }/*}}}*/ + + function lookup_token($consumer, $token_type, $token) {/*{{{*/ + // implement me + }/*}}}*/ + + function lookup_nonce($consumer, $token, $nonce, $timestamp) {/*{{{*/ + // implement me + }/*}}}*/ + + function new_request_token($consumer) {/*{{{*/ + // return a new token attached to this consumer + }/*}}}*/ + + function new_access_token($token, $consumer) {/*{{{*/ + // return a new access token attached to this consumer + // for the user associated with this token if the request token + // is authorized + // should also invalidate the request token + }/*}}}*/ + +}/*}}}*/ + + +/* A very naive dbm-based oauth storage + */ +class SimpleOAuthDataStore extends OAuthDataStore {/*{{{*/ + private $dbh; + + function __construct($path = "oauth.gdbm") {/*{{{*/ + $this->dbh = dba_popen($path, 'c', 'gdbm'); + }/*}}}*/ + + function __destruct() {/*{{{*/ + dba_close($this->dbh); + }/*}}}*/ + + function lookup_consumer($consumer_key) {/*{{{*/ + $rv = dba_fetch("consumer_$consumer_key", $this->dbh); + if ($rv === FALSE) { + return NULL; + } + $obj = unserialize($rv); + if (!($obj instanceof OAuthConsumer)) { + return NULL; + } + return $obj; + }/*}}}*/ + + function lookup_token($consumer, $token_type, $token) {/*{{{*/ + $rv = dba_fetch("${token_type}_${token}", $this->dbh); + if ($rv === FALSE) { + return NULL; + } + $obj = unserialize($rv); + if (!($obj instanceof OAuthToken)) { + return NULL; + } + return $obj; + }/*}}}*/ + + function lookup_nonce($consumer, $token, $nonce, $timestamp) {/*{{{*/ + if (dba_exists("nonce_$nonce", $this->dbh)) { + return TRUE; + } else { + dba_insert("nonce_$nonce", "1", $this->dbh); + return FALSE; + } + }/*}}}*/ + + function new_token($consumer, $type="request") {/*{{{*/ + $key = md5(time()); + $secret = time() + time(); + $token = new OAuthToken($key, md5(md5($secret))); + if (!dba_insert("${type}_$key", serialize($token), $this->dbh)) { + throw new OAuthException("doooom!"); + } + return $token; + }/*}}}*/ + + function new_request_token($consumer) {/*{{{*/ + return $this->new_token($consumer, "request"); + }/*}}}*/ + + function new_access_token($token, $consumer) {/*{{{*/ + + $token = $this->new_token($consumer, 'access'); + dba_delete("request_" . $token->key, $this->dbh); + return $token; + }/*}}}*/ +}/*}}}*/ + +class OAuthUtil {/*{{{*/ + public static function urlencode_rfc3986($input) {/*{{{*/ + if (is_array($input)) { + return array_map(array('OAuthUtil','urlencode_rfc3986'), $input); + } else if (is_scalar($input)) { + return str_replace('+', ' ', + str_replace('%7E', '~', rawurlencode($input))); + } else { + return ''; + } + }/*}}}*/ + + + // This decode function isn't taking into consideration the above + // modifications to the encoding process. However, this method doesn't + // seem to be used anywhere so leaving it as is. + public static function urldecode_rfc3986($string) {/*{{{*/ + return rawurldecode($string); + }/*}}}*/ +}/*}}}*/ \ No newline at end of file diff --git a/ConnectToTwitter/Plugin.php b/ConnectToTwitter/Plugin.php new file mode 100644 index 0000000..9f29058 --- /dev/null +++ b/ConnectToTwitter/Plugin.php @@ -0,0 +1,144 @@ +finishComment = array('ConnectToTwitter_Plugin', 'postToTwitter'); + Typecho_Plugin::factory('Widget_Archive')->beforeRender = array('ConnectToTwitter_Plugin', 'initComment'); + } + + /** + * 禁用插件方法,如果禁用失败,直接抛出异常 + * + * @static + * @access public + * @return void + * @throws Typecho_Plugin_Exception + */ + public static function deactivate() { + + } + + /** + * 获取插件配置面板 + * + * @access public + * @param Typecho_Widget_Helper_Form $form 配置面板 + * @return void + */ + public static function config(Typecho_Widget_Helper_Form $form) + { + $consumerKey = new Typecho_Widget_Helper_Form_Element_Text('consumerKey', NULL, '', + _t('Consumer Key'), _t('Your application consumer key from Twitter.com. ')); + $form->addInput($consumerKey->addRule('required', _t('You must give the Consumer Key from Twitter.com'))); + + $consumerSecret = new Typecho_Widget_Helper_Form_Element_Text('consumerSecret', NULL, '', + _t('Consumer Secret'), _t('Your application consumer secret from Twitter.com. ')); + $form->addInput($consumerSecret->addRule('required', _t('You must give the Consumer Key from Twitter.com'))); + } + + /** + * 个人用户的配置面板 + * + * @access public + * @param Typecho_Widget_Helper_Form $form + * @return void + */ + public static function personalConfig(Typecho_Widget_Helper_Form $form){} + + public static function initComment($api) + { + session_start(); + $options = Typecho_Widget::widget('Widget_Options'); + $config = $options->plugin('ConnectToTwitter'); + + //发送请求到twitter + if(isset($api->request->connect_to_twitter)) + { + $to = new TwitterOAuth($config->consumerKey, $config->consumerSecret); + + $tok = $to->getRequestToken(); + + Typecho_Cookie::set('oauth_request_token', $tok['oauth_token']); + Typecho_Cookie::set('oauth_request_token_secret', $tok['oauth_token_secret']); + + /* Build the authorization URL */ + $request_link = $to->getAuthorizeURL($tok['oauth_token']); + header('Location:'.$request_link); + } + + //从twitter返回 + if(isset($api->request->oauth_token)) { + if(Typecho_Cookie::get('oauth_request_token') && Typecho_Cookie::get('oauth_request_token_secret')) + { + $to = new TwitterOAuth($config->consumerKey, $config->consumerSecret, Typecho_Cookie::get('oauth_request_token'), Typecho_Cookie::get('oauth_request_token_secret')); + + $tok = $to->getAccessToken(); + + Typecho_Cookie::set('oauth_access_token', $tok['oauth_token'], time()+60*60*24*30); + Typecho_Cookie::set('oauth_access_token_secret', $tok['oauth_token_secret'], time()+60*60*24*30); + + $info_json = $to->OAuthRequest('https://twitter.com/account/verify_credentials.json', array(), 'GET'); + $info = Typecho_Json::decode($info_json, true); + + self::twitterLogin($info, $api); + } + } + } + + //登录,暂时做为setcookie,以后要和用户帐号相关联 + public static function twitterLogin($info, $api) + { + if (!empty($info['screen_name'])) { + Typecho_Cookie::set('__typecho_remember_author', $info['screen_name'], time()+60*60*24*30); + } + + if (!empty($info['url'])) { + Typecho_Cookie::set('__typecho_remember_url', $info['url'], time()+60*60*24*30); + } + } + + //发送信息到twitter + public static function postToTwitter($api) + { + if(Typecho_Cookie::get('oauth_access_token') && Typecho_Cookie::get('oauth_access_token_secret') && $api->request->post_to_twitter) { + $options = Typecho_Widget::widget('Widget_Options'); + $config = $options->plugin('ConnectToTwitter'); + $to = new TwitterOAuth($config->consumerKey, $config->consumerSecret, Typecho_Cookie::get('oauth_access_token'), Typecho_Cookie::get('oauth_access_token_secret')); + + $url_array = array(); + $url_array = explode('?', $api->request->getReferer()); + $url = $url_array[0] . '#comment-' . $api->coid; + $post = $api->text . ' ( from ' . $url . ' ) '; + $twitter = $to->OAuthRequest('https://twitter.com/statuses/update.xml', array('status' => $post), 'POST'); + } + return $comment; + } + + function showButton() + { + if(Typecho_Cookie::get('oauth_access_token') && Typecho_Cookie::get('oauth_access_token_secret')) { + echo '

'; + } else { + echo '

'; + } + } +} diff --git a/ConnectToTwitter/twitterOAuth.php b/ConnectToTwitter/twitterOAuth.php new file mode 100644 index 0000000..a8ff5fa --- /dev/null +++ b/ConnectToTwitter/twitterOAuth.php @@ -0,0 +1,146 @@ +http_status; } + function lastAPICall() { return $this->last_api_call; } + + /** + * construct TwitterOAuth object + */ + function __construct($consumer_key, $consumer_secret, $oauth_token = NULL, $oauth_token_secret = NULL) {/*{{{*/ + $this->sha1_method = new OAuthSignatureMethod_HMAC_SHA1(); + $this->consumer = new OAuthConsumer($consumer_key, $consumer_secret); + if (!empty($oauth_token) && !empty($oauth_token_secret)) { + $this->token = new OAuthConsumer($oauth_token, $oauth_token_secret); + } else { + $this->token = NULL; + } + }/*}}}*/ + + + /** + * Get a request_token from Twitter + * + * @returns a key/value array containing oauth_token and oauth_token_secret + */ + function getRequestToken() {/*{{{*/ + $r = $this->oAuthRequest($this->requestTokenURL()); + $token = $this->oAuthParseResponse($r); + $this->token = new OAuthConsumer($token['oauth_token'], $token['oauth_token_secret']); + return $token; + }/*}}}*/ + + /** + * Parse a URL-encoded OAuth response + * + * @return a key/value array + */ + function oAuthParseResponse($responseString) { + $r = array(); + foreach (explode('&', $responseString) as $param) { + $pair = explode('=', $param, 2); + if (count($pair) != 2) continue; + $r[urldecode($pair[0])] = urldecode($pair[1]); + } + return $r; + } + + /** + * Get the authorize URL + * + * @returns a string + */ + function getAuthorizeURL($token) {/*{{{*/ + if (is_array($token)) $token = $token['oauth_token']; + return $this->authorizeURL() . '?oauth_token=' . $token; + }/*}}}*/ + + /** + * Exchange the request token and secret for an access token and + * secret, to sign API calls. + * + * @returns array("oauth_token" => the access token, + * "oauth_token_secret" => the access secret) + */ + function getAccessToken($token = NULL) {/*{{{*/ + $r = $this->oAuthRequest($this->accessTokenURL()); + $token = $this->oAuthParseResponse($r); + $this->token = new OAuthConsumer($token['oauth_token'], $token['oauth_token_secret']); + return $token; + }/*}}}*/ + + /** + * Format and sign an OAuth / API request + */ + function oAuthRequest($url, $args = array(), $method = NULL) {/*{{{*/ + if (empty($method)) $method = empty($args) ? "GET" : "POST"; + $req = OAuthRequest::from_consumer_and_token($this->consumer, $this->token, $method, $url, $args); + $req->sign_request($this->sha1_method, $this->consumer, $this->token); + switch ($method) { + case 'GET': return $this->http($req->to_url()); + case 'POST': return $this->http($req->get_normalized_http_url(), $req->to_postdata()); + } + }/*}}}*/ + + /** + * Make an HTTP request + * + * @return API results + */ + function http($url, $post_data = null) {/*{{{*/ + $ch = curl_init(); + if (defined("CURL_CA_BUNDLE_PATH")) curl_setopt($ch, CURLOPT_CAINFO, CURL_CA_BUNDLE_PATH); + curl_setopt($ch, CURLOPT_URL, $url); + curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 30); + curl_setopt($ch, CURLOPT_TIMEOUT, 30); + curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); + ////////////////////////////////////////////////// + ///// Set to 1 to verify Twitter's SSL Cert ////// + ////////////////////////////////////////////////// + curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 0); + if (isset($post_data)) { + curl_setopt($ch, CURLOPT_POST, 1); + curl_setopt($ch, CURLOPT_POSTFIELDS, $post_data); + } + $response = curl_exec($ch); + $this->http_status = curl_getinfo($ch, CURLINFO_HTTP_CODE); + $this->last_api_call = $url; + curl_close ($ch); + return $response; + }/*}}}*/ +}/*}}}*/ \ No newline at end of file diff --git a/Creole/Creole_Wiki.php b/Creole/Creole_Wiki.php new file mode 100644 index 0000000..dfeeee4 --- /dev/null +++ b/Creole/Creole_Wiki.php @@ -0,0 +1,1599 @@ + + * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 + * @version CVS: $Id: Creole_Wiki.php 182 2008-09-14 15:56:00Z i.feelinglucky $ + * @link http://pear.php.net/package/Text_Wiki + */ + +/** + * The baseline abstract parser class. + */ +require_once 'Parse.inc.php'; + +/** + * The baseline abstract render class. + */ +require_once 'Render.inc.php'; + +/** + * Parse structured wiki text and render into arbitrary formats such as XHTML. + * + * This is the "master" class for handling the management and convenience + * functions to transform Wiki-formatted text. + * + * @category Text + * @package Text_Wiki + * @author Paul M. Jones + * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 + * @version Release: 1.2.0 + * @link http://pear.php.net/package/Text_Wiki + */ +class Creole_Wiki { + // *single newlines* are handled as in most wikis (ignored) + // if Newline is removed from rules, they will be handled as in word-processors (meaning a paragraph break) + protected $rules = array( + 'Prefilter', + 'Delimiter', + 'Preformatted', + 'Tt', + //'Trim', + 'Break', + 'Raw', + 'Box', + //'Footnote', + 'Table', + 'Newline', + 'Blockquote', + 'Newline', + //'Wikilink', + 'Heading', + 'Center', + 'Horiz', + 'List', + 'Address', + 'Paragraph', + 'Superscript', + 'Subscript', + 'Underline', + 'Strong', + 'Tighten', + 'Image', + 'Url', + 'Emphasis' + ); + + + /** + * + * The list of rules to not-apply to the source text. + * + * @access public + * + * @var array + * + */ + public $disable = array( + 'Html', + 'Include', + 'Embed' + ); + + + /** + * + * Custom configuration for rules at the parsing stage. + * + * In this array, the key is the parsing rule name, and the value is + * an array of key-value configuration pairs corresponding to the $conf + * property in the target parsing rule. + * + * For example: + * + * + * $parseConf = array( + * 'Include' => array( + * 'base' => '/path/to/scripts/' + * ) + * ); + * + * + * Note that most default rules do not need any parsing configuration. + * + * @access public + * + * @var array + * + */ + public $parseConf = array(); + + + /** + * + * Custom configuration for rules at the rendering stage. + * + * Because rendering may be different for each target format, the + * first-level element in this array is always a format name (e.g., + * 'Xhtml'). + * + * Within that first level element, the subsequent elements match the + * $parseConf format. That is, the sub-key is the rendering rule name, + * and the sub-value is an array of key-value configuration pairs + * corresponding to the $conf property in the target rendering rule. + * + * @access public + * + * @var array + * + */ + public $renderConf = array( + 'Docbook' => array(), + 'Latex' => array(), + 'Pdf' => array(), + 'Plain' => array(), + 'Rtf' => array(), + 'Xhtml' => array() + ); + + + /** + * + * Custom configuration for the output format itself. + * + * Even though Text_Wiki will render the tokens from parsed text, + * the format itself may require some configuration. For example, + * RTF needs to know font names and sizes, PDF requires page layout + * information, and DocBook needs a section hierarchy. This array + * matches the $conf property of the the format-level renderer + * (e.g., Text_Wiki_Render_Xhtml). + * + * In this array, the key is the rendering format name, and the value is + * an array of key-value configuration pairs corresponding to the $conf + * property in the rendering format rule. + * + * @access public + * + * @var array + * + */ + public $formatConf = array( + 'Docbook' => array(), + 'Latex' => array(), + 'Pdf' => array(), + 'Plain' => array(), + 'Rtf' => array(), + 'Xhtml' => array() + ); + + + /** + * + * The delimiter for token numbers of parsed elements in source text. + * + * @access public + * + * @var string + * + */ + public $delim = "\xFF"; + + + /** + * + * The tokens generated by rules as the source text is parsed. + * + * As Text_Wiki applies rule classes to the source text, it will + * replace portions of the text with a delimited token number. This + * is the array of those tokens, representing the replaced text and + * any options set by the parser for that replaced text. + * + * The tokens array is sequential; each element is itself a sequential + * array where element 0 is the name of the rule that generated the + * token, and element 1 is an associative array where the key is an + * option name and the value is an option value. + * + * @access private + * + * @var array + * + */ + public $tokens = array(); + + /** + * How many tokens generated pro rules. + * + * Intended to load only necessary render objects + * + * @access private + * @var array + */ + private $_countRulesTokens = array(); + + + /** + * + * The source text to which rules will be applied. + * + * This text will be transformed in-place, which means that it will + * change as the rules are applied. + * + * @access public + * + * @var string + * + */ + public $source = ''; + + /** + * The output text + * + * @var string + */ + protected $output = ''; + + + /** + * + * Array of rule parsers. + * + * Text_Wiki creates one instance of every rule that is applied to + * the source text; this array holds those instances. The array key + * is the rule name, and the array value is an instance of the rule + * class. + * + * @access private + * + * @var array + * + */ + + protected $parseObj = array(); + + + /** + * + * Array of rule renderers. + * + * Text_Wiki creates one instance of every rule that is applied to + * the source text; this array holds those instances. The array key + * is the rule name, and the array value is an instance of the rule + * class. + * + * @access private + * + * @var array + * + */ + protected $renderObj = array(); + + + /** + * + * Array of format renderers. + * + * @access private + * + * @var array + * + */ + protected $formatObj = array(); + + + /** + * + * Array of paths to search, in order, for parsing and rendering rules. + * + * @access private + * + * @var array + * + */ + protected $path = array( + 'parse' => array(), + 'render' => array() + ); + + + + /** + * + * The directory separator character. + * + * @access private + * + * @var string + * + */ + private $_dirSep = DIRECTORY_SEPARATOR; + + /** + * Temporary configuration variable + * + * @var string + */ + protected $renderingType = 'preg'; + + /** + * Stack of rendering callbacks + * + * @var Array + */ + private $_renderCallbacks = array(); + + /** + * Current output block + * + * @var string + */ + private $_block; + + /** + * A stack of blocks + * + * @param Array + */ + private $_blocks; + + /** + * + * Constructor. + * + * **DEPRECATED** + * Please use the singleton() or factory() methods. + * + * @access public + * + * @param array $rules The set of rules to load for this object. Defaults + * to null, which will load the default ruleset for this parser. + */ + + function __construct($rules = null) + { + if (is_array($rules)) { + $this->rules = array(); + foreach ($rules as $rule) { + $this->rules[] = ucfirst($rule); + } + } + + /* + $this->addPath( + 'parse', + $this->fixPath(dirname(__FILE__)) . 'Wiki/Parse/Default/' + ); + */ + + $this->addPath( + 'parse', $this->fixPath(dirname(__FILE__) . '/Parse/') + ); + + $this->addPath( + 'render', + $this->fixPath(dirname(__FILE__) . '/Render/' ) + ); + + $this->renderingType = 'char'; + $this->setRenderConf('xhtml', 'center', 'css', 'center'); + $this->setRenderConf('xhtml', 'url', 'target', null); + } + + + /** + * Singleton. + * + * This avoids instantiating multiple Text_Wiki instances where a number + * of objects are required in one call, e.g. to save memory in a + * CMS invironment where several parsers are required in a single page. + * + * $single = & singleton(); + * + * or + * + * $single = & singleton('Parser', array('Prefilter', 'Delimiter', 'Code', 'Function', + * 'Html', 'Raw', 'Include', 'Embed', 'Anchor', 'Heading', 'Toc', 'Horiz', + * 'Break', 'Blockquote', 'List', 'Deflist', 'Table', 'Image', 'Phplookup', + * 'Center', 'Newline', 'Paragraph', 'Url', 'Freelink', 'Interwiki', 'Wikilink', + * 'Colortext', 'Strong', 'Bold', 'Emphasis', 'Italic', 'Underline', 'Tt', + * 'Superscript', 'Subscript', 'Revise', 'Tighten')); + * + * Call using a subset of this list. The order of passing rulesets in the + * $rules array is important! + * + * After calling this, call $single->setParseConf(), setRenderConf() or setFormatConf() + * as usual for a constructed object of this class. + * + * The internal static array of singleton objects has no index on the parser + * rules, the only index is on the parser name. So if you call this multiple + * times with different rules but the same parser name, you will get the same + * static parser object each time. + * + * @access public + * @static + * @since Method available since Release 1.1.0 + * @param string $parser The parser to be used (defaults to 'Default'). + * @param array $rules The set of rules to instantiate the object. This + * will only be used when the first call to singleton is made, if included + * in further calls it will be effectively ignored. + * @return &object a reference to the Text_Wiki unique instantiation. + */ + /* + public function &singleton($parser = 'Default', $rules = null) + { + static $only = array(); + if (!isset($only[$parser])) { + $ret = & Text_Wiki::factory($parser, $rules); + if (Text_Wiki::isError($ret)) { + return $ret; + } + $only[$parser] =& $ret; + } + return $only[$parser]; + } + */ + + /** + * Returns a Text_Wiki Parser class for the specified parser. + * + * @access public + * @static + * @param string $parser The name of the parse to instantiate + * you need to have Text_Wiki_XXX installed to use $parser = 'XXX', it's E_FATAL + * @param array $rules The rules to pass into the constructor + * {@see Text_Wiki::singleton} for a list of rules + * @return Text_Wiki a Parser object extended from Text_Wiki + */ + /* + public function &factory($parser = 'Default', $rules = null) + { + $class = 'Text_Wiki_' . ucfirst(strtolower($parser)); + $file = str_replace('_', '/', $class).'.php'; + if (!class_exists($class)) { + require_once $file; + if (!class_exists($class)) { + return Text_Wiki::error( + 'Class ' . $class . ' does not exist after requiring '. $file . + ', install package ' . $class . "\n"); + } + } + + $obj =& new $class($rules); + return $obj; + } + */ + + /** + * + * Set parser configuration for a specific rule and key. + * + * @access public + * + * @param string $rule The parse rule to set config for. + * + * @param array|string $arg1 The full config array to use for the + * parse rule, or a conf key in that array. + * + * @param string $arg2 The config value for the key. + * + * @return void + * + */ + public function setParseConf($rule, $arg1, $arg2 = null) + { + $rule = ucwords(strtolower($rule)); + + if (! isset($this->parseConf[$rule])) { + $this->parseConf[$rule] = array(); + } + + // if first arg is an array, use it as the entire + // conf array for the rule. otherwise, treat arg1 + // as a key and arg2 as a value for the rule conf. + if (is_array($arg1)) { + $this->parseConf[$rule] = $arg1; + } else { + $this->parseConf[$rule][$arg1] = $arg2; + } + } + + + /** + * + * Get parser configuration for a specific rule and key. + * + * @access public + * + * @param string $rule The parse rule to get config for. + * + * @param string $key A key in the conf array; if null, + * returns the entire conf array. + * + * @return mixed The whole conf array if no key is specified, + * or the specific conf key value. + * + */ + public function getParseConf($rule, $key = null) + { + $rule = ucwords(strtolower($rule)); + + // the rule does not exist + if (! isset($this->parseConf[$rule])) { + return null; + } + + // no key requested, return the whole array + if (is_null($key)) { + return $this->parseConf[$rule]; + } + + // does the requested key exist? + if (isset($this->parseConf[$rule][$key])) { + // yes, return that value + return $this->parseConf[$rule][$key]; + } else { + // no + return null; + } + } + + + /** + * + * Set renderer configuration for a specific format, rule, and key. + * + * @access public + * + * @param string $format The render format to set config for. + * + * @param string $rule The render rule to set config for in the format. + * + * @param array|string $arg1 The config array, or the config key + * within the render rule. + * + * @param string $arg2 The config value for the key. + * + * @return void + * + */ + + function setRenderConf($format, $rule, $arg1, $arg2 = null) + { + $format = ucwords(strtolower($format)); + $rule = ucwords(strtolower($rule)); + + if (! isset($this->renderConf[$format])) { + $this->renderConf[$format] = array(); + } + + if (! isset($this->renderConf[$format][$rule])) { + $this->renderConf[$format][$rule] = array(); + } + + // if first arg is an array, use it as the entire + // conf array for the render rule. otherwise, treat arg1 + // as a key and arg2 as a value for the render rule conf. + if (is_array($arg1)) { + $this->renderConf[$format][$rule] = $arg1; + } else { + $this->renderConf[$format][$rule][$arg1] = $arg2; + } + } + + + /** + * + * Get renderer configuration for a specific format, rule, and key. + * + * @access public + * + * @param string $format The render format to get config for. + * + * @param string $rule The render format rule to get config for. + * + * @param string $key A key in the conf array; if null, + * returns the entire conf array. + * + * @return mixed The whole conf array if no key is specified, + * or the specific conf key value. + * + */ + + function getRenderConf($format, $rule, $key = null) + { + $format = ucwords(strtolower($format)); + $rule = ucwords(strtolower($rule)); + + if (! isset($this->renderConf[$format]) || + ! isset($this->renderConf[$format][$rule])) { + return null; + } + + // no key requested, return the whole array + if (is_null($key)) { + return $this->renderConf[$format][$rule]; + } + + // does the requested key exist? + if (isset($this->renderConf[$format][$rule][$key])) { + // yes, return that value + return $this->renderConf[$format][$rule][$key]; + } else { + // no + return null; + } + + } + + /** + * + * Set format configuration for a specific rule and key. + * + * @access public + * + * @param string $format The format to set config for. + * + * @param string $key The config key within the format. + * + * @param string $val The config value for the key. + * + * @return void + * + */ + + function setFormatConf($format, $arg1, $arg2 = null) + { + if (! is_array($this->formatConf[$format])) { + $this->formatConf[$format] = array(); + } + + // if first arg is an array, use it as the entire + // conf array for the format. otherwise, treat arg1 + // as a key and arg2 as a value for the format conf. + if (is_array($arg1)) { + $this->formatConf[$format] = $arg1; + } else { + $this->formatConf[$format][$arg1] = $arg2; + } + } + + + + /** + * + * Get configuration for a specific format and key. + * + * @access public + * + * @param string $format The format to get config for. + * + * @param mixed $key A key in the conf array; if null, + * returns the entire conf array. + * + * @return mixed The whole conf array if no key is specified, + * or the specific conf key value. + * + */ + + function getFormatConf($format, $key = null) + { + // the format does not exist + if (! isset($this->formatConf[$format])) { + return null; + } + + // no key requested, return the whole array + if (is_null($key)) { + return $this->formatConf[$format]; + } + + // does the requested key exist? + if (isset($this->formatConf[$format][$key])) { + // yes, return that value + return $this->formatConf[$format][$key]; + } else { + // no + return null; + } + } + + + /** + * + * Inserts a rule into to the rule set. + * + * @access public + * + * @param string $name The name of the rule. Should be different from + * all other keys in the rule set. + * + * @param string $tgt The rule after which to insert this new rule. By + * default (null) the rule is inserted at the end; if set to '', inserts + * at the beginning. + * + * @return void + * + */ + + function insertRule($name, $tgt = null) + { + $name = ucwords(strtolower($name)); + if (! is_null($tgt)) { + $tgt = ucwords(strtolower($tgt)); + } + + // does the rule name to be inserted already exist? + if (in_array($name, $this->rules)) { + // yes, return + return null; + } + + // the target name is not null, and not '', but does not exist + // in the list of rules. this means we're trying to insert after + // a target key, but the target key isn't there. + if (! is_null($tgt) && $tgt != '' && + ! in_array($tgt, $this->rules)) { + return false; + } + + // if $tgt is null, insert at the end. We know this is at the + // end (instead of resetting an existing rule) becuase we exited + // at the top of this method if the rule was already in place. + if (is_null($tgt)) { + $this->rules[] = $name; + return true; + } + + // save a copy of the current rules, then reset the rule set + // so we can insert in the proper place later. + // where to insert the rule? + if ($tgt == '') { + // insert at the beginning + array_unshift($this->rules, $name); + return true; + } + + // insert after the named rule + $tmp = $this->rules; + $this->rules = array(); + + foreach ($tmp as $val) { + $this->rules[] = $val; + if ($val == $tgt) { + $this->rules[] = $name; + } + } + + return true; + + } + + + /** + * + * Delete (remove or unset) a rule from the $rules property. + * + * @access public + * + * @param string $rule The name of the rule to remove. + * + * @return void + * + */ + + function deleteRule($name) + { + $name = ucwords(strtolower($name)); + $key = array_search($name, $this->rules); + if ($key !== false) { + unset($this->rules[$key]); + } + } + + + /** + * + * Change from one rule to another in-place. + * + * @access public + * + * @param string $old The name of the rule to change from. + * + * @param string $new The name of the rule to change to. + * + * @return void + * + */ + + function changeRule($old, $new) + { + $old = ucwords(strtolower($old)); + $new = ucwords(strtolower($new)); + $key = array_search($old, $this->rules); + if ($key !== false) { + // delete the new name , case it was already there + $this->deleteRule($new); + $this->rules[$key] = $new; + } + } + + + /** + * + * Enables a rule so that it is applied when parsing. + * + * @access public + * + * @param string $rule The name of the rule to enable. + * + * @return void + * + */ + + function enableRule($name) + { + $name = ucwords(strtolower($name)); + $key = array_search($name, $this->disable); + if ($key !== false) { + unset($this->disable[$key]); + } + } + + + /** + * + * Disables a rule so that it is not applied when parsing. + * + * @access public + * + * @param string $rule The name of the rule to disable. + * + * @return void + * + */ + + function disableRule($name) + { + $name = ucwords(strtolower($name)); + $key = array_search($name, $this->disable); + if ($key === false) { + $this->disable[] = $name; + } + } + + + /** + * + * Parses and renders the text passed to it, and returns the results. + * + * First, the method parses the source text, applying rules to the + * text as it goes. These rules will modify the source text + * in-place, replacing some text with delimited tokens (and + * populating the $this->tokens array as it goes). + * + * Next, the method renders the in-place tokens into the requested + * output format. + * + * Finally, the method returns the transformed text. Note that the + * source text is transformed in place; once it is transformed, it is + * no longer the same as the original source text. + * + * @access public + * + * @param string $text The source text to which wiki rules should be + * applied, both for parsing and for rendering. + * + * @param string $format The target output format, typically 'xhtml'. + * If a rule does not support a given format, the output from that + * rule is rule-specific. + * + * @return string The transformed wiki text. + * + */ + function transform($text, $format = 'Xhtml') + { + $this->parse($text); + return $this->render($format); + } + + + /** + * + * Sets the $_source text property, then parses it in place and + * retains tokens in the $_tokens array property. + * + * @access public + * + * @param string $text The source text to which wiki rules should be + * applied, both for parsing and for rendering. + * + * @return void + * + */ + + function parse($text) + { + // set the object property for the source text + $this->source = $text; + + // reset the tokens. + $this->tokens = array(); + $this->_countRulesTokens = array(); + + // apply the parse() method of each requested rule to the source + // text. + foreach ($this->rules as $name) { + // do not parse the rules listed in $disable + if (! in_array($name, $this->disable)) { + + // load the parsing object + $this->loadParseObj($name); + + // load may have failed; only parse if + // an object is in the array now + if (is_object($this->parseObj[$name])) { + $this->parseObj[$name]->parse(); + } + } + } + } + + + /** + * + * Renders tokens back into the source text, based on the requested format. + * + * @access public + * + * @param string $format The target output format, typically 'xhtml'. + * If a rule does not support a given format, the output from that + * rule is rule-specific. + * + * @return string The transformed wiki text. + * + */ + + function render($format = 'Xhtml') + { + // the rendering method we're going to use from each rule + $format = ucwords(strtolower($format)); + + // the eventual output text + $this->output = ''; + + // when passing through the parsed source text, keep track of when + // we are in a delimited section + $in_delim = false; + + // when in a delimited section, capture the token key number + $key = ''; + + // load the format object, or crap out if we can't find it + $result = $this->loadFormatObj($format); + if ($this->isError($result)) { + return $result; + } + + /* + * hunked by feelinglucky.. + // pre-rendering activity + if (is_object($this->formatObj[$format])) { + $this->output .= $this->formatObj[$format]->pre(); + } + */ + // load the render objects + foreach (array_keys($this->_countRulesTokens) as $rule) { + $this->loadRenderObj($format, $rule); + } + + + if ($this->renderingType == 'preg') { + $this->output = preg_replace_callback('/'.$this->delim.'(\d+)'.$this->delim.'/', + array(&$this, '_renderToken'), + $this->source); + + /* + //Damn strtok()! Why does it "skip" empty parts of the string. It's useless now! + } elseif ($this->renderingType == 'strtok') { + echo '
'.htmlentities($this->source).'
'; + $t = strtok($this->source, $this->delim); + $inToken = true; + $i = 0; + while ($t !== false) { + echo 'Token: '.$i.'
"'.htmlentities($t).'"


'; + if ($inToken) { + //$this->output .= $this->renderObj[$this->tokens[$t][0]]->token($this->tokens[$t][1]); + } else { + $this->output .= $t; + } + $inToken = !$inToken; + $t = strtok($this->delim); + ++$i; + } + */ + } else { + // pass through the parsed source text character by character + $this->_block = ''; + $tokenStack = array(); + $k = strlen($this->source); + + for ($i = 0; $i < $k; $i++) { + + // the current character + $char = $this->source{$i}; + + // are alredy in a delimited section? + if ($in_delim) { + + // yes; are we ending the section? + if ($char == $this->delim) { + + if (count($this->_renderCallbacks) == 0) { + $this->output .= $this->_block; + $this->_block = ''; + } + + if (isset($opts['type'])) { + if ($opts['type'] == 'start') { + array_push($tokenStack, $rule); + } elseif ($opts['type'] == 'end') { + if ($tokenStack[count($tokenStack) - 1] != $rule) { + return Text_Wiki::error('Unbalanced tokens, check your syntax'); + } else { + array_pop($tokenStack); + } + } + } + + // yes, get the replacement text for the delimited + // token number and unset the flag. + $key = (int)$key; + $rule = $this->tokens[$key][0]; + $opts = $this->tokens[$key][1]; + $this->_block .= $this->renderObj[$rule]->token($opts); + $in_delim = false; + + } else { + + // no, add to the delimited token key number + $key .= $char; + + } + + } else { + + // not currently in a delimited section. + // are we starting into a delimited section? + if ($char == $this->delim) { + // yes, reset the previous key and + // set the flag. + $key = ''; + $in_delim = true; + + } else { + // no, add to the output as-is + $this->_block .= $char; + } + } + } + } + + if (count($this->_renderCallbacks)) { + return $this->error('Render callbacks left over after processing finished'); + } + /* + while (count($this->_renderCallbacks)) { + $this->popRenderCallback(); + } + */ + if (strlen($this->_block)) { + $this->output .= $this->_block; + $this->_block = ''; + } + +/* tunk by feelinglucky + // post-rendering activity + if (is_object($this->formatObj[$format])) { + $this->output .= $this->formatObj[$format]->post(); + } + */ + + // return the rendered source text. + return $this->output; + } + + /** + * Renders a token, for use only as an internal callback + * + * @param array Matches from preg_rpelace_callback, [1] is the token number + * @return string The rendered text for the token + * @access private + */ + function _renderToken($matches) { + return $this->renderObj[$this->tokens[$matches[1]][0]]->token($this->tokens[$matches[1]][1]); + } + + function registerRenderCallback($callback) { + $this->_blocks[] = $this->_block; + $this->_block = ''; + $this->_renderCallbacks[] = $callback; + } + + function popRenderCallback() { + if (count($this->_renderCallbacks) == 0) { + return Text_Wiki::error('Render callback popped when no render callbacks in stack'); + } else { + $callback = array_pop($this->_renderCallbacks); + $this->_block = call_user_func($callback, $this->_block); + if (count($this->_blocks)) { + $parentBlock = array_pop($this->_blocks); + $this->_block = $parentBlock.$this->_block; + } + if (count($this->_renderCallbacks) == 0) { + $this->output .= $this->_block; + $this->_block = ''; + } + } + } + + /** + * + * Returns the parsed source text with delimited token placeholders. + * + * @access public + * + * @return string The parsed source text. + * + */ + + function getSource() + { + return $this->source; + } + + + /** + * + * Returns tokens that have been parsed out of the source text. + * + * @access public + * + * @param array $rules If an array of rule names is passed, only return + * tokens matching these rule names. If no array is passed, return all + * tokens. + * + * @return array An array of tokens. + * + */ + + function getTokens($rules = null) + { + if (is_null($rules)) { + return $this->tokens; + } else { + settype($rules, 'array'); + $result = array(); + foreach ($this->tokens as $key => $val) { + if (in_array($val[0], $rules)) { + $result[$key] = $val; + } + } + return $result; + } + } + + + /** + * + * Add a token to the Text_Wiki tokens array, and return a delimited + * token number. + * + * @access public + * + * @param array $options An associative array of options for the new + * token array element. The keys and values are specific to the + * rule, and may or may not be common to other rule options. Typical + * options keys are 'text' and 'type' but may include others. + * + * @param boolean $id_only If true, return only the token number, not + * a delimited token string. + * + * @return string|int By default, return the number of the + * newly-created token array element with a delimiter prefix and + * suffix; however, if $id_only is set to true, return only the token + * number (no delimiters). + * + */ + + function addToken($rule, $options = array(), $id_only = false) + { + // increment the token ID number. note that if you parse + // multiple times with the same Text_Wiki object, the ID number + // will not reset to zero. + static $id; + if (! isset($id)) { + $id = 0; + } else { + $id ++; + } + + // force the options to be an array + settype($options, 'array'); + + // add the token + $this->tokens[$id] = array( + 0 => $rule, + 1 => $options + ); + if (!isset($this->_countRulesTokens[$rule])) { + $this->_countRulesTokens[$rule] = 1; + } else { + ++$this->_countRulesTokens[$rule]; + } + + // return a value + if ($id_only) { + // return the last token number + return $id; + } else { + // return the token number with delimiters + return $this->delim . $id . $this->delim; + } + } + + + /** + * + * Set or re-set a token with specific information, overwriting any + * previous rule name and rule options. + * + * @access public + * + * @param int $id The token number to reset. + * + * @param int $rule The rule name to use. + * + * @param array $options An associative array of options for the + * token array element. The keys and values are specific to the + * rule, and may or may not be common to other rule options. Typical + * options keys are 'text' and 'type' but may include others. + * + * @return void + * + */ + + function setToken($id, $rule, $options = array()) + { + $oldRule = $this->tokens[$id][0]; + // reset the token + $this->tokens[$id] = array( + 0 => $rule, + 1 => $options + ); + if ($rule != $oldRule) { + if (!($this->_countRulesTokens[$oldRule]--)) { + unset($this->_countRulesTokens[$oldRule]); + } + if (!isset($this->_countRulesTokens[$rule])) { + $this->_countRulesTokens[$rule] = 1; + } else { + ++$this->_countRulesTokens[$rule]; + } + } + } + + + /** + * + * Load a rule parser class file. + * + * @access public + * + * @return bool True if loaded, false if not. + * + */ + + function loadParseObj($rule) + { + $rule = ucwords(strtolower($rule)); + $file = $rule . '.php'; + $class = "Text_Wiki_Parse_$rule"; + + if (!Typecho_Common::isAvailableClass($class)) { + $loc = $this->findFile('parse', $file); + + if ($loc) { + // found the class + include_once $loc; + } else { + // can't find the class + $this->parseObj[$rule] = null; + // can't find the class + return $this->error( + "Parse rule '$rule' not found" + ); + } + } + + $this->parseObj[$rule] = new $class($this); + } + + + /** + * + * Load a rule-render class file. + * + * @access public + * + * @return bool True if loaded, false if not. + * + */ + + function loadRenderObj($format, $rule) + { + $format = ucwords(strtolower($format)); + $rule = ucwords(strtolower($rule)); + $file = "$format/$rule.php"; + $class = "Text_Wiki_Render_$format" . "_$rule"; + + if (! Typecho_Common::isAvailableClass($class)) { + // load the class + $loc = $this->findFile('render', $file); + if ($loc) { + // found the class + include_once $loc; + } else { + // can't find the class + return $this->error( + "Render rule '$rule' in format '$format' not found" + ); + } + } + + $this->renderObj[$rule] = new $class($this); + } + + + /** + * + * Load a format-render class file. + * + * @access public + * + * @return bool True if loaded, false if not. + * + */ + + function loadFormatObj($format) + { + $format = ucwords(strtolower($format)); + $file = $format . '.php'; + $class = "Text_Wiki_Render_$format"; + + if (! Typecho_Common::isAvailableClass($class)) { + $loc = $this->findFile('render', $file); + if ($loc) { + // found the class + include_once $loc; + } else { + // can't find the class + return $this->error( + "Rendering format class '$class' not found" + ); + } + } + + $this->formatObj[$format] = new $class($this); + } + + + /** + * + * Add a path to a path array. + * + * @access public + * + * @param string $type The path-type to add (parse or render). + * + * @param string $dir The directory to add to the path-type. + * + * @return void + * + */ + + function addPath($type, $dir) + { + $dir = $this->fixPath($dir); + if (! isset($this->path[$type])) { + $this->path[$type] = array($dir); + } else { + array_unshift($this->path[$type], $dir); + } + } + + + /** + * + * Get the current path array for a path-type. + * + * @access public + * + * @param string $type The path-type to look up (plugin, filter, or + * template). If not set, returns all path types. + * + * @return array The array of paths for the requested type. + * + */ + + function getPath($type = null) + { + if (is_null($type)) { + return $this->path; + } elseif (! isset($this->path[$type])) { + return array(); + } else { + return $this->path[$type]; + } + } + + + /** + * + * Searches a series of paths for a given file. + * + * @param array $type The type of paths to search (template, plugin, + * or filter). + * + * @param string $file The file name to look for. + * + * @return string|bool The full path and file name for the target file, + * or boolean false if the file is not found in any of the paths. + * + */ + + function findFile($type, $file) + { + // get the set of paths + $set = $this->getPath($type); + + // start looping through them + foreach ($set as $path) { + $fullname = $path . $this->_dirSep . $file; + if (file_exists($fullname) && is_readable($fullname)) { + return realpath($fullname); + } + } + + // could not find the file in the set of paths + return false; + } + + + /** + * + * Append a trailing '/' to paths, unless the path is empty. + * + * @access private + * + * @param string $path The file path to fix + * + * @return string The fixed file path + * + */ + function fixPath($path) + { + if (realpath($path)){ + return realpath($path); // . (is_dir($path) ? $this->_dirSep : ''); + } else { + return ''; + } + + /* + $len = strlen($this->_dirSep); + if (! empty($path) && + substr($path, -1 * $len, $len) != $this->_dirSep) { + return realpath($path) . $this->_dirSep; + } else { + return realpath($path); + } + */ + } + + + /** + * + * Simple error-object generator. + * + * @access public + * + * @param string $message The error message. + * + * @return object PEAR_Error + * + */ + + function &error($message) + { + /* + if (! class_exists('PEAR_Error')) { + include_once 'PEAR.php'; + } + */ + throw new Exception($message); + return false; + //return PEAR::throwError($message); + } + + + /** + * + * Simple error checker. + * + * @access public + * + * @param mixed $obj Check if this is a PEAR_Error object or not. + * + * @return bool True if a PEAR_Error, false if not. + * + */ + + function isError(&$obj) + { + return is_a($obj, 'PEAR_Error'); + } + + + /** + * Constructor: just adds the path to Creole rules + * + * @access public + * @param array $rules The set of rules to load for this object. + */ + function checkInnerTags(&$text) { + $started = array(); + $i = false; + while (($i = strpos($text, $this->delim, $i)) !== false) { + $j = strpos($text, $this->delim, $i + 1); + $t = substr($text, $i + 1, $j - $i - 1); + $i = $j + 1; + $rule = strtolower($this->tokens[$t][0]); + $type = $this->tokens[$t][1]['type']; + + if ($type == 'start') { + if (empty($started[$rule])) { + $started[$rule] = 0; + } + $started[$rule] += 1; + } + else if ($type == 'end') { + if (! $started[$rule]) return false; + + $started[$rule] -= 1; + if (! $started[$rule]) unset($started[$rule]); + } + } + return ! (count($started) > 0); + } + + function restoreRaw($text) { + $i = false; + while (($i = strpos($text, $this->delim, $i)) !== false) { + $j = strpos($text, $this->delim, $i + 1); + $t = substr($text, $i + 1, $j - $i - 1); + $rule = strtolower($this->tokens[$t][0]); + + if ($rule == 'raw') { + $text = str_replace($this->delim. $t. $this->delim, $this->tokens[$t][1]['text'], $text); + } else { + $i = $j + 1; + } + } + return $text; + } +} +?> diff --git a/Creole/Parse.inc.php b/Creole/Parse.inc.php new file mode 100644 index 0000000..0965b03 --- /dev/null +++ b/Creole/Parse.inc.php @@ -0,0 +1,262 @@ + + * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 + * @version CVS: $Id: Parse.inc.php 182 2008-09-14 15:56:00Z i.feelinglucky $ + * @link http://pear.php.net/package/Text_Wiki + */ + +/** + * Baseline rule class for extension into a "real" parser component. + * + * Text_Wiki_Rule classes do not stand on their own; they are called by a + * Text_Wiki object, typcially in the transform() method. Each rule class + * performs three main activities: parse, process, and render. + * + * The parse() method takes a regex and applies it to the whole block of + * source text at one time. Each match is sent as $matches to the + * process() method. + * + * The process() method acts on the matched text from the source, and + * then processes the source text is some way. This may mean the + * creation of a delimited token using addToken(). In every case, the + * process() method returns the text that should replace the matched text + * from parse(). + * + * @category Text + * @package Text_Wiki + * @author Paul M. Jones + * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 + * @version Release: @package_version@ + * @link http://pear.php.net/package/Text_Wiki + */ +class Text_Wiki_Parse { + + + /** + * + * Configuration options for this parser rule. + * + * @access public + * + * @var string + * + */ + + var $conf = array(); + + + /** + * + * Regular expression to find matching text for this rule. + * + * @access public + * + * @var string + * + * @see parse() + * + */ + + var $regex = null; + + + /** + * + * The name of this rule for new token array elements. + * + * @access public + * + * @var string + * + */ + + var $rule = null; + + + /** + * + * A reference to the calling Text_Wiki object. + * + * This is needed so that each rule has access to the same source + * text, token set, URLs, interwiki maps, page names, etc. + * + * @access public + * + * @var object + */ + + var $wiki = null; + + + /** + * + * Constructor for this parser rule. + * + * @access public + * + * @param object &$obj The calling "parent" Text_Wiki object. + * + */ + + function Text_Wiki_Parse(&$obj) + { + // set the reference to the calling Text_Wiki object; + // this allows us access to the shared source text, token + // array, etc. + $this->wiki =& $obj; + + // set the name of this rule; generally used when adding + // to the tokens array. strip off the Text_Wiki_Parse_ portion. + // text_wiki_parse_ + // 0123456789012345 + $tmp = substr(get_class($this), 16); + $this->rule = ucwords(strtolower($tmp)); + + // override config options for the rule if specified + if (isset($this->wiki->parseConf[$this->rule]) && + is_array($this->wiki->parseConf[$this->rule])) { + + $this->conf = array_merge( + $this->conf, + $this->wiki->parseConf[$this->rule] + ); + } + } + + + /** + * + * Abstrct method to parse source text for matches. + * + * Applies the rule's regular expression to the source text, passes + * every match to the process() method, and replaces the matched text + * with the results of the processing. + * + * @access public + * + * @see Text_Wiki_Parse::process() + * + */ + + function parse() + { + $this->wiki->source = preg_replace_callback( + $this->regex, + array(&$this, 'process'), + $this->wiki->source + ); + } + + + /** + * + * Abstract method to generate replacements for matched text. + * + * @access public + * + * @param array $matches An array of matches from the parse() method + * as generated by preg_replace_callback. $matches[0] is the full + * matched string, $matches[1] is the first matched pattern, + * $matches[2] is the second matched pattern, and so on. + * + * @return string The processed text replacement; defaults to the + * full matched string (i.e., no changes to the text). + * + * @see Text_Wiki_Parse::parse() + * + */ + + function process(&$matches) + { + return $matches[0]; + } + + + /** + * + * Simple method to safely get configuration key values. + * + * @access public + * + * @param string $key The configuration key. + * + * @param mixed $default If the key does not exist, return this value + * instead. + * + * @return mixed The configuration key value (if it exists) or the + * default value (if not). + * + */ + + function getConf($key, $default = null) + { + if (isset($this->conf[$key])) { + return $this->conf[$key]; + } else { + return $default; + } + } + + + /** + * + * Extract 'attribute="value"' portions of wiki markup. + * + * This kind of markup is typically used only in macros, but is useful + * anywhere. + * + * The syntax is pretty strict; there can be no spaces between the + * option name, the equals, and the first double-quote; the value + * must be surrounded by double-quotes. You can escape characters in + * the value with a backslash, and the backslash will be stripped for + * you. + * + * @access public + * + * @param string $text The "attributes" portion of markup. + * + * @return array An associative array of key-value pairs where the + * key is the option name and the value is the option value. + * + */ + + function getAttrs($text) + { + // find the =" sections; + $tmp = explode('="', trim($text)); + + // basic setup + $k = count($tmp) - 1; + $attrs = array(); + $key = null; + + // loop through the sections + foreach ($tmp as $i => $val) { + + // first element is always the first key + if ($i == 0) { + $key = trim($val); + continue; + } + + // find the last double-quote in the value. + // the part to the left is the value for the last key, + // the part to the right is the next key name + $pos = strrpos($val, '"'); + $attrs[$key] = stripslashes(substr($val, 0, $pos)); + $key = trim(substr($val, $pos+1)); + + } + + return $attrs; + } +} +?> diff --git a/Creole/Parse/Address.php b/Creole/Parse/Address.php new file mode 100644 index 0000000..78f98da --- /dev/null +++ b/Creole/Parse/Address.php @@ -0,0 +1,67 @@ + + * + * @license LGPL + * + * @version $Id: Address.php 182 2008-09-14 15:56:00Z i.feelinglucky $ + * + */ + +class Text_Wiki_Parse_Address extends Text_Wiki_Parse { + + /** + * + * The regular expression used to find source text matching this + * rule. + * + * @access public + * + * @var string + * + */ + + var $regex = '/^--([^-].*)$/m'; + + /** + * + * Generates a token entry for the matched text. Token options are: + * + * 'start' => The starting point of the signature. + * + * 'end' => The ending point of the signature. + * + * @access public + * + * @param array &$matches The array of matches from parse(). + * + * @return A delimited token number to be used as a placeholder in + * the source text. + * + */ + + function process(&$matches) + { + $start = $this->wiki->addToken( + $this->rule, array('type' => 'start') + ); + + $end = $this->wiki->addToken( + $this->rule, array('type' => 'end') + ); + + return "\n" . $start . trim($matches[1]) . $end; + } +} +?> \ No newline at end of file diff --git a/Creole/Parse/Blockquote.php b/Creole/Parse/Blockquote.php new file mode 100644 index 0000000..dcaacbf --- /dev/null +++ b/Creole/Parse/Blockquote.php @@ -0,0 +1,176 @@ +' at the start of the line, followed by an + * optional space, and then the quote text; each '>' indicates an + * additional level of quoting. + * + * @category Text + * + * @package Text_Wiki + * + * @author Paul M. Jones + * @author Michele Tomaiuolo + * + * @license LGPL + * + * @version $Id: Blockquote.php 182 2008-09-14 15:56:00Z i.feelinglucky $ + * + */ + +class Text_Wiki_Parse_Blockquote extends Text_Wiki_Parse { + + + /** + * + * Regex for parsing the source text. + * + * @access public + * + * @var string + * + * @see parse() + * + */ + + var $regex = '/\n(([>:]).*\n)(?!([>:]))/Us'; + + + /** + * + * Generates a replacement for the matched text. + * + * Token options are: + * + * 'type' => + * 'start' : the start of a blockquote + * 'end' : the end of a blockquote + * + * 'level' => the indent level (0 for the first level, 1 for the + * second, etc) + * + * @access public + * + * @param array &$matches The array of matches from parse(). + * + * @return A series of text and delimited tokens marking the different + * list text and list elements. + * + */ + + function process(&$matches) + { + // the replacement text we will return to parse() + $return = ''; + + // the list of post-processing matches + $list = array(); + + // $matches[1] is the text matched as a list set by parse(); + // create an array called $list that contains a new set of + // matches for the various list-item elements. + preg_match_all( + '=^([>:]+)(.*?\n)=ms', + $matches[1], + $list, + PREG_SET_ORDER + ); + + // a stack of starts and ends; we keep this so that we know what + // indent level we're at. + $stack = array(); + + // loop through each list-item element. + foreach ($list as $key => $val) { + + // $val[0] is the full matched list-item line + // $val[1] is the number of initial '>' chars (indent level) + // $val[2] is the quote text + + // we number levels starting at 1, not zero + $level = strlen($val[1]); + + // get the text of the line + $text = trim($val[2]); + + // add a level to the list? + while ($level > count($stack)) { + + $css = ($val[1][count($stack)] == ':') ? 'remark' : ''; + + // the current indent level is greater than the number + // of stack elements, so we must be starting a new + // level. push the new level onto the stack with a + // dummy value (boolean true)... + array_push($stack, true); + + $return .= "\n\n"; + + // ...and add a start token to the return. + $return .= $this->wiki->addToken( + $this->rule, + array( + 'type' => 'start', + 'level' => $level - 1, + 'css' => $css + ) + ); + + $return .= "\n\n"; + } + + // remove a level? + while (count($stack) > $level) { + + // as long as the stack count is greater than the + // current indent level, we need to end list types. + // continue adding end-list tokens until the stack count + // and the indent level are the same. + array_pop($stack); + + $return .= "\n\n"; + + $return .= $this->wiki->addToken( + $this->rule, + array ( + 'type' => 'end', + 'level' => count($stack) + ) + ); + + $return .= "\n\n"; + } + + // add the line text. + $return .= $text . "\n"; + } + + // the last line may have been indented. go through the stack + // and create end-tokens until the stack is empty. + $return .= "\n\n"; + + while (count($stack) > 0) { + array_pop($stack); + + $return .= "\n\n"; + + $return .= $this->wiki->addToken( + $this->rule, + array ( + 'type' => 'end', + 'level' => count($stack) + ) + ); + + $return .= "\n\n"; + } + + // we're done! send back the replacement text. + return "\n\n$return\n\n"; + } +} +?> \ No newline at end of file diff --git a/Creole/Parse/Box.php b/Creole/Parse/Box.php new file mode 100644 index 0000000..7c2a212 --- /dev/null +++ b/Creole/Parse/Box.php @@ -0,0 +1,81 @@ + +* @author Paul M. Jones +* +* @license LGPL +* +* @version $Id: Box.php 182 2008-09-14 15:56:00Z i.feelinglucky $ +* +*/ + +/** +* +* Parses for bold text. +* +* This class implements a Text_Wiki_Rule to find source text marked for +* strong emphasis (bold) as defined by text surrounded by three +* single-quotes. On parsing, the text itself is left in place, but the +* starting and ending instances of three single-quotes are replaced with +* tokens. +* +* @category Text +* +* @package Text_Wiki +* +* @author Justin Patrin +* @author Paul M. Jones +* +*/ + +class Text_Wiki_Parse_Box extends Text_Wiki_Parse { + + + /** + * + * The regular expression used to parse the source text and find + * matches conforming to this rule. Used by the parse() method. + * + * @access public + * + * @var string + * + * @see parse() + * + */ + + var $regex = '/\n\[\d+\].*/s'; + + + /** + * + * Generates a replacement for the matched text. Token options are: + * + * 'type' => ['start'|'end'] The starting or ending point of the + * emphasized text. The text itself is left in the source. + * + * @access public + * + * @param array &$matches The array of matches from parse(). + * + * @return A pair of delimited tokens to be used as a placeholder in + * the source text surrounding the text to be emphasized. + * + */ + + function process(&$matches) + { + $start = $this->wiki->addToken($this->rule, array('type' => 'start', 'css' => 'footnotes')); + $end = $this->wiki->addToken($this->rule, array('type' => 'end')); + return $start . $matches[0] . "\n" . $end . "\n\n"; + } +} +?> \ No newline at end of file diff --git a/Creole/Parse/Break.php b/Creole/Parse/Break.php new file mode 100644 index 0000000..b2dd21f --- /dev/null +++ b/Creole/Parse/Break.php @@ -0,0 +1,73 @@ + +* +* @license LGPL +* +* @version $Id: Break.php 182 2008-09-14 15:56:00Z i.feelinglucky $ +* +*/ + +/** +* +* Parses for explicit line breaks. +* +* This class implements a Text_Wiki_Parse to mark forced line breaks in the +* source text. +* +* @category Text +* +* @package Text_Wiki +* +* @author Paul M. Jones +* +*/ + +class Text_Wiki_Parse_Break extends Text_Wiki_Parse { + + + /** + * + * The regular expression used to parse the source text and find + * matches conforming to this rule. Used by the parse() method. + * + * @access public + * + * @var string + * + * @see parse() + * + */ + + //var $regex = "/[ \n]*([\\\][\\\]|\%\%\%)[ \n]*/"; + var $regex = "/ *([\\\][\\\]|\%\%\%)\n?/"; + + + /** + * + * Generates a replacement token for the matched text. + * + * @access public + * + * @param array &$matches The array of matches from parse(). + * + * @return string A delimited token to be used as a placeholder in + * the source text. + * + */ + + function process(&$matches) + { + return $this->wiki->addToken($this->rule); + } +} + +?> \ No newline at end of file diff --git a/Creole/Parse/Center.php b/Creole/Parse/Center.php new file mode 100644 index 0000000..c1bb150 --- /dev/null +++ b/Creole/Parse/Center.php @@ -0,0 +1,78 @@ + + * + * @license LGPL + * + * @version $Id: Center.php 182 2008-09-14 15:56:00Z i.feelinglucky $ + * + */ + +class Text_Wiki_Parse_Center extends Text_Wiki_Parse { + + + /** + * + * The regular expression used to parse the source text and find + * matches conforming to this rule. Used by the parse() method. + * + * @access public + * + * @var string + * + * @see parse() + * + */ + + var $regex = '/^! *(.*?)$/m'; + + /** + * + * Generates a replacement for the matched text. Token options are: + * + * 'type' => ['start'|'end'] The starting or ending point of the + * centered text. The text itself is left in the source. + * + * @access public + * + * @param array &$matches The array of matches from parse(). + * + * @return string A pair of delimited tokens to be used as a + * placeholder in the source text surrounding the centered text. + * + */ + + function process(&$matches) + { + $start = $this->wiki->addToken( + $this->rule, + array( + 'type' => 'start' + ) + ); + + $end = $this->wiki->addToken( + $this->rule, + array( + 'type' => 'end' + ) + ); + + return $start . trim($matches[1]) . $end . "\n\n"; + } +} +?> diff --git a/Creole/Parse/Delete.php b/Creole/Parse/Delete.php new file mode 100644 index 0000000..f3e99ff --- /dev/null +++ b/Creole/Parse/Delete.php @@ -0,0 +1,78 @@ + + * @author Michele Tomaiuolo + * + */ + +class Text_Wiki_Parse_Delete extends Text_Wiki_Parse { + + + /** + * + * The regular expression used to parse the source text and find + * matches conforming to this rule. Used by the parse() method. + * + * @access public + * + * @var string + * + * @see parse() + * + */ + + //var $regex = "/__(.*?)__/"; + var $regex = "/(?:\-\-(.+?)\-\-|(?:(?<=[\W-\xFF])\-(?![ \-]))(.+?)(?:(? ['start'|'end'] The starting or ending point of the + * superscript text. The text itself is left in the source. + * + * @access public + * + * @param array &$matches The array of matches from parse(). + * + * @return string A pair of delimited tokens to be used as a + * placeholder in the source text surrounding the text to be + * superscripted. + * + */ + + function process(&$matches) + { + $text = $matches[1] ? $matches[1] : $matches[2]; + + if (! $this->wiki->checkInnerTags($text)) { + return $matches[0]; + } + + $start = $this->wiki->addToken( + 'Delete', // $this->rule, + array('type' => 'start') + ); + + $end = $this->wiki->addToken( + 'Delete', // $this->rule, + array('type' => 'end') + ); + + return $start . $text . $end; + } +} +?> diff --git a/Creole/Parse/Delimiter.php b/Creole/Parse/Delimiter.php new file mode 100644 index 0000000..f2938da --- /dev/null +++ b/Creole/Parse/Delimiter.php @@ -0,0 +1,68 @@ + + * + * @license LGPL + * + * @version $Id: Delimiter.php 182 2008-09-14 15:56:00Z i.feelinglucky $ + * + */ + +class Text_Wiki_Parse_Delimiter extends Text_Wiki_Parse { + + /** + * + * Constructor. Overrides the Text_Wiki_Parse constructor so that we + * can set the $regex property dynamically (we need to include the + * Text_Wiki $delim character. + * + * @param object &$obj The calling "parent" Text_Wiki object. + * + * @param string $name The token name to use for this rule. + * + */ + + function Text_Wiki_Parse_Delimiter(&$obj) + { + parent::Text_Wiki_Parse($obj); + $this->regex = '/' . $this->wiki->delim . '/'; + } + + + /** + * + * Generates a token entry for the matched text. Token options are: + * + * 'text' => The full matched text. + * + * @access public + * + * @param array &$matches The array of matches from parse(). + * + * @return A delimited token number to be used as a placeholder in + * the source text. + * + */ + + function process(&$matches) + { + return $this->wiki->addToken( + $this->rule, + array('text' => $this->wiki->delim) + ); + } +} +?> \ No newline at end of file diff --git a/Creole/Parse/Emphasis.php b/Creole/Parse/Emphasis.php new file mode 100644 index 0000000..0fa03ad --- /dev/null +++ b/Creole/Parse/Emphasis.php @@ -0,0 +1,78 @@ + + * @author Michele Tomaiuolo + * + */ + +class Text_Wiki_Parse_Emphasis extends Text_Wiki_Parse { + + + /** + * + * The regular expression used to parse the source text and find + * matches conforming to this rule. Used by the parse() method. + * + * @access public + * + * @var string + * + * @see parse() + * + */ + + //var $regex = "/\/\/(.*?)\/\//"; + var $regex = "/(?:\/\/(.+?)\/\/|(?:(?<=[\W_\xFF])\/(?![ \/]))(.+?)(?:(? ['start'|'end'] The starting or ending point of the + * emphasized text. The text itself is left in the source. + * + * @access public + * + * @param array &$matches The array of matches from parse(). + * + * @return string A pair of delimited tokens to be used as a + * placeholder in the source text surrounding the text to be + * emphasized. + * + */ + + function process(&$matches) + { + $text = $matches[1] ? $matches[1] : $matches[2]; + + if (! $this->wiki->checkInnerTags($text)) { + return $matches[0]; + } + + $start = $this->wiki->addToken( + $this->rule, + array('type' => 'start') + ); + + $end = $this->wiki->addToken( + $this->rule, + array('type' => 'end') + ); + + return $start . $text . $end; + } +} +?> \ No newline at end of file diff --git a/Creole/Parse/Footnote.php b/Creole/Parse/Footnote.php new file mode 100644 index 0000000..2d9e4af --- /dev/null +++ b/Creole/Parse/Footnote.php @@ -0,0 +1,83 @@ + + * @author Michele Tomaiuolo + * + * @license LGPL + * + * @version $Id: Footnote.php 182 2008-09-14 15:56:00Z i.feelinglucky $ + * + */ + +class Text_Wiki_Parse_Footnote extends Text_Wiki_Parse { + + + /** + * + * The regular expression used to parse the source text and find + * matches conforming to this rule. Used by the parse() method. + * + * @access public + * + * @var string + * + * @see parse() + * + */ + + var $regex = "/(\n)*\[([0-9]+)\]/"; + + + /** + * + * Generates a replacement for the matched text. Token options are: + * + * 'type' => ['start'|'end'] The starting or ending point of the + * emphasized text. The text itself is left in the source. + * + * @access public + * + * @param array &$matches The array of matches from parse(). + * + * @return A pair of delimited tokens to be used as a placeholder in + * the source text surrounding the text to be emphasized. + * + */ + + function process(&$matches) + { + $id = $matches[2]; + + if ($matches[1] == "\n") { + $matches[1] = "\n\n"; + $name = "fn$id"; + $href = "#ref$id"; + } + else { + $name = "ref$id"; + $href = "#fn$id"; + } + + $token = $this->wiki->addToken( + 'Url', + array('text' => "[$id]", 'href' => $href, 'name' => $name, 'type' => 'inline') + ); + + return $matches[1] . $token; + } +} +?> \ No newline at end of file diff --git a/Creole/Parse/Heading.php b/Creole/Parse/Heading.php new file mode 100644 index 0000000..bcd061c --- /dev/null +++ b/Creole/Parse/Heading.php @@ -0,0 +1,97 @@ + + * @author Tomaiuolo Michele + * + * @license LGPL + * + * @version $Id: Heading.php 182 2008-09-14 15:56:00Z i.feelinglucky $ + * + */ + +class Text_Wiki_Parse_Heading extends Text_Wiki_Parse { + + + /** + * + * The regular expression used to parse the source text and find + * matches conforming to this rule. Used by the parse() method. + * + * @access public + * + * @var string + * + * @see parse() + * + */ + + var $regex = '/^(={1,6}) *(.*?) *=*$/m'; + + var $conf = array( + 'id_prefix' => 'toc' + ); + + /** + * + * Generates a replacement for the matched text. Token options are: + * + * 'type' => ['start'|'end'] The starting or ending point of the + * heading text. The text itself is left in the source. + * + * @access public + * + * @param array &$matches The array of matches from parse(). + * + * @return string A pair of delimited tokens to be used as a + * placeholder in the source text surrounding the heading text. + * + */ + + function process(&$matches) + { + // keep a running count for header IDs. we use this later + // when constructing TOC entries, etc. + static $id; + if (! isset($id)) { + $id = 0; + } + + $prefix = htmlspecialchars($this->getConf('id_prefix')); + + $start = $this->wiki->addToken( + $this->rule, + array( + 'type' => 'start', + 'level' => strlen($matches[1]), + 'text' => trim($matches[2]), + 'id' => $prefix . $id ++ + ) + ); + + $end = $this->wiki->addToken( + $this->rule, + array( + 'type' => 'end', + 'level' => strlen($matches[1]) + ) + ); + + return $start . trim($matches[2]) . $end . "\n\n"; + } +} +?> diff --git a/Creole/Parse/Horiz.php b/Creole/Parse/Horiz.php new file mode 100644 index 0000000..a2627bb --- /dev/null +++ b/Creole/Parse/Horiz.php @@ -0,0 +1,58 @@ + + * + * @license LGPL + * + * @version $Id: Horiz.php 182 2008-09-14 15:56:00Z i.feelinglucky $ + * + */ + +class Text_Wiki_Parse_Horiz extends Text_Wiki_Parse { + + + /** + * + * The regular expression used to parse the source text and find + * matches conforming to this rule. Used by the parse() method. + * + * @access public + * + * @var string + * + * @see parse() + * + */ + + var $regex = '/^([-]{4,})$/m'; + + + /** + * + * Generates a replacement token for the matched text. + * + * @access public + * + * @param array &$matches The array of matches from parse(). + * + * @return string A token marking the horizontal rule. + * + */ + + function process(&$matches) + { + return "\n" . $this->wiki->addToken($this->rule) . "\n"; + } +} +?> \ No newline at end of file diff --git a/Creole/Parse/Image.php b/Creole/Parse/Image.php new file mode 100644 index 0000000..e90343c --- /dev/null +++ b/Creole/Parse/Image.php @@ -0,0 +1,66 @@ + + * + * @license LGPL + * + * @version $Id: Image.php 182 2008-09-14 15:56:00Z i.feelinglucky $ + * + */ + + +class Text_Wiki_Parse_Image extends Text_Wiki_Parse { + + /** + * + * The regular expression used to parse the source text and find + * matches conforming to this rule. Used by the parse() method. + * + * @access public + * + * @var string + * + * @see parse() + * + */ + + var $regex = '/{{(.*)(\|(.*))?}}/U'; + + + /** + * + * Generates a replacement token for the matched text. + * + * @access public + * + * @param array &$matches The array of matches from parse(). + * + * @return string A token marking the horizontal rule. + * + */ + + function process(&$matches) + { + $src = trim($matches[1]); + $src = ltrim($src, '/'); + $alt = isset($matches[3]) ? trim($matches[3]) : null; + if (!$alt) $alt = $src; + + return $this->wiki->addToken( + $this->rule, + array( + 'src' => $src, + 'attr' => array('alt' => $alt, 'title' => $alt) + ) + ); + } +} +?> diff --git a/Creole/Parse/List.php b/Creole/Parse/List.php new file mode 100644 index 0000000..72ff406 --- /dev/null +++ b/Creole/Parse/List.php @@ -0,0 +1,244 @@ + + * @author Paul M. Jones + * @author Michele Tomaiuolo + * + * @license LGPL + * + * @version $Id: List.php 182 2008-09-14 15:56:00Z i.feelinglucky $ + * + */ + +class Text_Wiki_Parse_List extends Text_Wiki_Parse { + + + /** + * + * The regular expression used to parse the source text and find + * matches conforming to this rule. Used by the parse() method. + * + * @access public + * + * @var string + * + * @see parse() + * + */ + + var $regex = '/\n((\*[^\#\-\*]|\-[^\-\d\*\#]|\#[^\#\-\*]).*?)\n(?![\*\-#])/s'; + + /** + * + * Generates a replacement for the matched text. Token options are: + * + * 'type' => + * 'bullet_start' : the start of a bullet list + * 'bullet_end' : the end of a bullet list + * 'number_start' : the start of a number list + * 'number_end' : the end of a number list + * 'item_start' : the start of item text (bullet or number) + * 'item_end' : the end of item text (bullet or number) + * 'unknown' : unknown type of list or item + * + * 'level' => the indent level (0 for the first level, 1 for the + * second, etc) + * + * 'count' => the list item number at this level. not needed for + * xhtml, but very useful for PDF and RTF. + * + * @access public + * + * @param array &$matches The array of matches from parse(). + * + * @return A series of text and delimited tokens marking the different + * list text and list elements. + * + */ + + function process(&$matches) + { + // the replacement text we will return + $return = ''; + + // the list of post-processing matches + $list = array(); + + // a stack of list-start and list-end types; we keep this + // so that we know what kind of list we're working with + // (bullet or number) and what indent level we're at. + $stack = array(); + + // the item count is the number of list items for any + // given list-type on the stack + $itemcount = array(); + + // have we processed the very first list item? + $pastFirst = false; + + // populate $list with this set of matches. $matches[1] is the + // text matched as a list set by parse(). + preg_match_all( + '/^((\*|\-|#)+) *(.*?)$/ms', + $matches[1], + $list, + PREG_SET_ORDER + ); + + if (count($list) === 1 && $matches[0][0] === '*' && $matches[0][1] !== ' ' && strpos($matches[0], '*', 1)) { + return $matches[0]; + } + + // loop through each list-item element. + foreach ($list as $key => $val) { + // $val[0] is the full matched list-item line + // $val[1] is the level (number) + // $val[2] is the type (* or #) + // $val[3] is the list item text + + // how many levels are we indented? (1 means the "root" + // list level, no indenting.) + $stars = $val[1]; + $level = strlen($stars); + $last = $stars[strlen($stars) - 1]; + + // get the list item type + if ($last == '*' || $last == '-') { + $type = 'bullet'; + } elseif ($last == '#') { + $type = 'number'; + } else { + $type = 'unknown'; + } + + // get the text of the list item + $text = $val[3]; + + // remove a level from the list? + while (count($stack) > $level || (count($stack) == $level && $type != $stack[$level - 1])) { + + // so we don't keep counting the stack, we set up a temp + // var for the count. -1 becuase we're going to pop the + // stack in the next command. $tmp will then equal the + // current level of indent. + $tmp = count($stack) - 1; + + // as long as the stack count is greater than the + // current indent level, we need to end list types. + // continue adding end-list tokens until the stack count + // and the indent level are the same. + $return .= $this->wiki->addToken( + $this->rule, + array ( + 'type' => array_pop($stack) . '_list_end', + 'level' => $tmp + ) + ); + + // reset to the current (previous) list type so that + // the new list item matches the proper list type. + if ($tmp) { + $oldtype = $stack[$tmp - 1]; + } + + // reset the item count for the popped indent level + unset($itemcount[$tmp + 1]); + } + + // add a level to the list? + if ($level > count($stack)) { + + // the current indent level is greater than the + // number of stack elements, so we must be starting + // a new list. push the new list type onto the + // stack... + array_push($stack, $type); + + // ...and add a list-start token to the return. + $return .= $this->wiki->addToken( + $this->rule, + array( + 'type' => $type . '_list_start', + 'level' => $level - 1 + ) + ); + } + + // add to the item count for this list (taking into account + // which level we are at). + if (! isset($itemcount[$level])) { + // first count + $itemcount[$level] = 0; + } else { + // increment count + $itemcount[$level]++; + } + + // is this the very first item in the list? + if (! $pastFirst) { + $first = true; + $pastFirst = true; + } else { + $first = false; + } + + // create a list-item starting token. + $start = $this->wiki->addToken( + $this->rule, + array( + 'type' => $type . '_item_start', + 'level' => $level, + 'count' => $itemcount[$level], + 'first' => $first + ) + ); + + // create a list-item ending token. + $end = $this->wiki->addToken( + $this->rule, + array( + 'type' => $type . '_item_end', + 'level' => $level, + 'count' => $itemcount[$level] + ) + ); + + // add the starting token, list-item text, and ending token + // to the return. + $return .= "\n" . $start . $text . $end; + } + + // the last list-item may have been indented. go through the + // list-type stack and create end-list tokens until the stack + // is empty. + while (count($stack) > 0) { + $return .= $this->wiki->addToken( + $this->rule, + array ( + 'type' => array_pop($stack) . '_list_end', + 'level' => count($stack) + ) + ); + } + + // we're done! send back the replacement text. + return "\n\n" . $return . "\n\n"; + } +} +?> \ No newline at end of file diff --git a/Creole/Parse/Newline.php b/Creole/Parse/Newline.php new file mode 100644 index 0000000..bf73d8a --- /dev/null +++ b/Creole/Parse/Newline.php @@ -0,0 +1,60 @@ + + * + * @license LGPL + * + * @version $Id: Newline.php 182 2008-09-14 15:56:00Z i.feelinglucky $ + * + */ + +class Text_Wiki_Parse_Newline extends Text_Wiki_Parse { + + + /** + * + * The regular expression used to parse the source text and find + * matches conforming to this rule. Used by the parse() method. + * + * @access public + * + * @var string + * + * @see parse() + * + */ + + //var $regex = '/(?\:]|\*[^\*\#]|\*+ )/m'; + var $regex = '/(?|\:|\!|\-\D)/m'; + + + /** + * + * Generates a replacement token for the matched text. + * + * @access public + * + * @param array &$matches The array of matches from parse(). + * + * @return string A delimited token to be used as a placeholder in + * the source text. + * + */ + + function process(&$matches) + { + return ' '; // $this->wiki->addToken($this->rule); + } +} + +?> \ No newline at end of file diff --git a/Creole/Parse/Paragraph.php b/Creole/Parse/Paragraph.php new file mode 100644 index 0000000..8363f89 --- /dev/null +++ b/Creole/Parse/Paragraph.php @@ -0,0 +1,139 @@ + + * @author Michele Tomaiuolo + * + * @license LGPL + * + * @version $Id: Paragraph.php 182 2008-09-14 15:56:00Z i.feelinglucky $ + * + */ + +class Text_Wiki_Parse_Paragraph extends Text_Wiki_Parse { + + /** + * + * The regular expression used to find source text matching this + * rule. + * + * @access public + * + * @var string + * + */ + + var $regex = "/^.+?\n/m"; // (?=[\n\-\|#{=]) + + var $conf = array( + 'skip' => array( + 'address', + 'box', + 'blockquote', + 'code', + 'heading', + 'center', + 'horiz', + 'deflist', + 'table', + 'list', + 'paragraph', + 'preformatted', + 'toc' + ) + ); + + + /** + * + * Generates a token entry for the matched text. Token options are: + * + * 'start' => The starting point of the paragraph. + * + * 'end' => The ending point of the paragraph. + * + * @access public + * + * @param array &$matches The array of matches from parse(). + * + * @return A delimited token number to be used as a placeholder in + * the source text. + * + */ + + function process(&$matches) + { + $delim = $this->wiki->delim; + + // was anything there? + if (trim($matches[0]) == '') { + return ''; + } + + // does the match start with a delimiter? + if (substr($matches[0], 0, 1) != $delim) { + // no. + + $start = $this->wiki->addToken( + $this->rule, array('type' => 'start') + ); + + $end = $this->wiki->addToken( + $this->rule, array('type' => 'end') + ); + + return $start . trim($matches[0]) . $end; + } + + // the line starts with a delimiter. read in the delimited + // token number, check the token, and see if we should + // skip it. + + // loop starting at the second character (we already know + // the first is a delimiter) until we find another + // delimiter; the text between them is a token key number. + $key = ''; + $len = strlen($matches[0]); + for ($i = 1; $i < $len; $i++) { + $char = $matches[0]{$i}; + if ($char == $delim) { + break; + } else { + $key .= $char; + } + } + + // look at the token and see if it's skippable (if we skip, + // it will not be marked as a paragraph) + $token_type = strtolower($this->wiki->tokens[$key][0]); + $skip = $this->getConf('skip', array()); + + if (in_array($token_type, $skip)) { + // this type of token should not have paragraphs applied to it. + // return the entire matched text. + return $matches[0]; + } else { + + $start = $this->wiki->addToken( + $this->rule, array('type' => 'start') + ); + + $end = $this->wiki->addToken( + $this->rule, array('type' => 'end') + ); + + return $start . trim($matches[0]) . $end; + } + } +} +?> \ No newline at end of file diff --git a/Creole/Parse/Prefilter.php b/Creole/Parse/Prefilter.php new file mode 100644 index 0000000..ca1a13a --- /dev/null +++ b/Creole/Parse/Prefilter.php @@ -0,0 +1,54 @@ + + * @author Michele Tomaiuolo + * + * @license LGPL + * + * @version $Id: Prefilter.php 182 2008-09-14 15:56:00Z i.feelinglucky $ + * + */ + +class Text_Wiki_Parse_Prefilter extends Text_Wiki_Parse { + + + /** + * + * Simple parsing method. + * + * @access public + * + */ + + function parse() + { + // convert DOS line endings + $this->wiki->source = str_replace("\r\n", "\n", + $this->wiki->source); + + // convert Macintosh line endings + $this->wiki->source = str_replace("\r", "\n", + $this->wiki->source); + + // convert tabs to four-spaces + $this->wiki->source = str_replace("\t", " ", + $this->wiki->source); + + // add extra newlines at the top and end; this + // seems to help many rules. + $this->wiki->source = "\n\n" . $this->wiki->source . "\n\n"; + } + +} +?> \ No newline at end of file diff --git a/Creole/Parse/Preformatted.php b/Creole/Parse/Preformatted.php new file mode 100644 index 0000000..a340aa9 --- /dev/null +++ b/Creole/Parse/Preformatted.php @@ -0,0 +1,68 @@ + + * + * @license LGPL + * + * @version $Id: Preformatted.php 182 2008-09-14 15:56:00Z i.feelinglucky $ + * + */ + +class Text_Wiki_Parse_Preformatted extends Text_Wiki_Parse { + + + /** + * + * The regular expression used to parse the source text and find + * matches conforming to this rule. Used by the parse() method. + * + * @access public + * + * @var string + * + * @see parse() + * + */ + + var $regex = '/\n{{{\n(.*)\n}}}\n/Us'; + + /** + * + * Generates a replacement for the matched text. Token options are: + * + * 'text' => The preformatted text. + * + * @access public + * + * @param array &$matches The array of matches from parse(). + * + * @return string A token to be used as a placeholder + * in the source text for the preformatted text. + * + */ + + function process(&$matches) + { + // > any line consisting of only indented three closing curly braces + // > will have one space removed from the indentation + // > -- http://www.wikicreole.org/wiki/AddNoWikiEscapeProposal + $find = "/\n( *) }}}/"; + $replace = "\n$1}}}"; + $matches[1] = preg_replace($find, $replace, $matches[1]); + + $token = $this->wiki->addToken( + $this->rule, + array('text' => $matches[1]) + ); + return "\n\n" . $token . "\n\n"; + } +} +?> diff --git a/Creole/Parse/Raw.php b/Creole/Parse/Raw.php new file mode 100644 index 0000000..d75137c --- /dev/null +++ b/Creole/Parse/Raw.php @@ -0,0 +1,61 @@ + + * + * @license LGPL + * + * @version $Id: Raw.php 182 2008-09-14 15:56:00Z i.feelinglucky $ + * + */ + +class Text_Wiki_Parse_Raw extends Text_Wiki_Parse { + + + /** + * + * The regular expression used to parse the source text and find + * matches conforming to this rule. Used by the parse() method. + * + * @access public + * + * @var string + * + * @see parse() + * + */ + + var $regex = '/~~([^ \n])/'; + + /** + * + * Generates a replacement for the matched text. Token options are: + * + * 'type' => ['start'|'end'] The starting or ending point of the + * monospaced text. The text itself is encapsulated into a Raw token. + * + * @access public + * + * @param array &$matches The array of matches from parse(). + * + * @return string A token to be used as a placeholder + * in the source text for the preformatted text. + * + */ + + function process(&$matches) + { + return $this->wiki->addToken( + $this->rule, + array('text' => $matches[1], 'type' => 'escape') + ); + } +} +?> diff --git a/Creole/Parse/Strong.php b/Creole/Parse/Strong.php new file mode 100644 index 0000000..4c45223 --- /dev/null +++ b/Creole/Parse/Strong.php @@ -0,0 +1,83 @@ + + * @author Michele Tomaiuolo + * + * @license LGPL + * + * @version $Id: Strong.php 182 2008-09-14 15:56:00Z i.feelinglucky $ + * + */ + +class Text_Wiki_Parse_Strong extends Text_Wiki_Parse { + + + /** + * + * The regular expression used to parse the source text and find + * matches conforming to this rule. Used by the parse() method. + * + * @access public + * + * @var string + * + * @see parse() + * + */ + + //var $regex = "/\*\*(.*?)\*\*/"; + var $regex = "/(?:\*\*(.+?)\*\*|(?:(?<=[\W_\xFF])\*(?![ \*]))(.+?)(?:(? ['start'|'end'] The starting or ending point of the + * emphasized text. The text itself is left in the source. + * + * @access public + * + * @param array &$matches The array of matches from parse(). + * + * @return A pair of delimited tokens to be used as a placeholder in + * the source text surrounding the text to be emphasized. + * + */ + + function process(&$matches) + { + $text = $matches[1] ? $matches[1] : $matches[2]; + + if (! $this->wiki->checkInnerTags($text)) { + return $matches[0]; + } + + $start = $this->wiki->addToken( + $this->rule, + array('type' => 'start') + ); + + $end = $this->wiki->addToken( + $this->rule, + array('type' => 'end') + ); + + return $start . $text . $end; + } +} +?> \ No newline at end of file diff --git a/Creole/Parse/Subscript.php b/Creole/Parse/Subscript.php new file mode 100644 index 0000000..596f652 --- /dev/null +++ b/Creole/Parse/Subscript.php @@ -0,0 +1,75 @@ + + * @author Michele Tomaiuolo + * + */ + +class Text_Wiki_Parse_Subscript extends Text_Wiki_Parse { + + + /** + * + * The regular expression used to parse the source text and find + * matches conforming to this rule. Used by the parse() method. + * + * @access public + * + * @var string + * + * @see parse() + * + */ + + var $regex = "/\,\,(.*?)\,\,/"; + + /** + * + * Generates a replacement for the matched text. Token options are: + * + * 'type' => ['start'|'end'] The starting or ending point of the + * superscript text. The text itself is left in the source. + * + * @access public + * + * @param array &$matches The array of matches from parse(). + * + * @return string A pair of delimited tokens to be used as a + * placeholder in the source text surrounding the text to be + * superscripted. + * + */ + + function process(&$matches) + { + if (! $this->wiki->checkInnerTags($matches[1])) { + return $matches[0]; + } + + $start = $this->wiki->addToken( + $this->rule, + array('type' => 'start') + ); + + $end = $this->wiki->addToken( + $this->rule, + array('type' => 'end') + ); + + return $start . $matches[1] . $end; + } +} +?> \ No newline at end of file diff --git a/Creole/Parse/Superscript.php b/Creole/Parse/Superscript.php new file mode 100644 index 0000000..6b0ee9e --- /dev/null +++ b/Creole/Parse/Superscript.php @@ -0,0 +1,75 @@ + + * @author Michele Tomaiuolo + * + */ + +class Text_Wiki_Parse_Superscript extends Text_Wiki_Parse { + + + /** + * + * The regular expression used to parse the source text and find + * matches conforming to this rule. Used by the parse() method. + * + * @access public + * + * @var string + * + * @see parse() + * + */ + + var $regex = "/\^\^(.*?)\^\^/"; + + /** + * + * Generates a replacement for the matched text. Token options are: + * + * 'type' => ['start'|'end'] The starting or ending point of the + * superscript text. The text itself is left in the source. + * + * @access public + * + * @param array &$matches The array of matches from parse(). + * + * @return string A pair of delimited tokens to be used as a + * placeholder in the source text surrounding the text to be + * superscripted. + * + */ + + function process(&$matches) + { + if (! $this->wiki->checkInnerTags($matches[1])) { + return $matches[0]; + } + + $start = $this->wiki->addToken( + $this->rule, + array('type' => 'start') + ); + + $end = $this->wiki->addToken( + $this->rule, + array('type' => 'end') + ); + + return $start . $matches[1] . $end; + } +} +?> \ No newline at end of file diff --git a/Creole/Parse/Table.php b/Creole/Parse/Table.php new file mode 100644 index 0000000..df5952a --- /dev/null +++ b/Creole/Parse/Table.php @@ -0,0 +1,207 @@ + + * @author Paul M. Jones + * + * @license LGPL + * + * @version $Id: Table.php 182 2008-09-14 15:56:00Z i.feelinglucky $ + * + */ + + +class Text_Wiki_Parse_Table extends Text_Wiki_Parse { + + + /** + * + * The regular expression used to parse the source text and find + * matches conforming to this rule. Used by the parse() method. + * + * @access public + * + * @var string + * + * @see parse() + * + */ + + var $regex = '/\n((\|).*)(\n)(?!(\|))/Us'; + + + /** + * + * Generates a replacement for the matched text. + * + * Token options are: + * + * 'type' => + * 'table_start' : the start of a bullet list + * 'table_end' : the end of a bullet list + * 'row_start' : the start of a number list + * 'row_end' : the end of a number list + * 'cell_start' : the start of item text (bullet or number) + * 'cell_end' : the end of item text (bullet or number) + * + * 'cols' => the number of columns in the table (for 'table_start') + * + * 'rows' => the number of rows in the table (for 'table_start') + * + * 'span' => column span (for 'cell_start') + * + * 'attr' => column attribute flag (for 'cell_start') + * + * @access public + * + * @param array &$matches The array of matches from parse(). + * + * @return A series of text and delimited tokens marking the different + * table elements and cell text. + * + */ + + function process(&$matches) + { + // our eventual return value + $return = ''; + + // the number of columns in the table + $num_cols = 0; + + // the number of rows in the table + $num_rows = 0; + + // rows are separated by newlines in the matched text + $rows = explode("\n", $matches[1]); + + // loop through each row + foreach ($rows as $row) { + + // increase the row count + $num_rows ++; + + // remove first and last (optional) pipe + $row = substr($row, 1); + if ($row[strlen($row) - 1] == '|') { + $row = substr($row, 0, -1); + } + + // cells are separated by pipes + $cells = explode("|", $row); + + if (count($cells) == 1 && $cells[0][0] == '=' && ($num_rows == 1 || $num_rows == count($rows)) && ! $caption) { + $caption = trim(trim($cells[0], '=')); + + // start the caption... + $return .= $this->wiki->addToken( + $this->rule, + array ('type' => 'caption_start') + ); + + // ...add the content... + $return .= $caption; + + // ...and end the caption. + $return .= $this->wiki->addToken( + $this->rule, + array ('type' => 'caption_end') + ); + } + else { + + // update the column count + if (count($cells) > $num_cols) { + $num_cols = count($cells); + } + + // start a new row + $return .= $this->wiki->addToken( + $this->rule, + array('type' => 'row_start') + ); + + for ($i = 0; $i < count($cells); $i++) { + $cell = $cells[$i]; + + // by default, cells span only one column (their own) + $span = 1; + $attr = ''; + + while ($i + 1 < count($cells) && ! strlen($cells[$i + 1])) { + $i++; + $span++; + } + + if ($cell[0] == '=') { + $attr = 'header'; + $cell = trim($cell, '='); + } + + // start a new cell... + $return .= $this->wiki->addToken( + $this->rule, + array ( + 'type' => 'cell_start', + 'attr' => $attr, + 'span' => $span + ) + ); + + // ...add the content... + $return .= trim($cell); + + // ...and end the cell. + $return .= $this->wiki->addToken( + $this->rule, + array ( + 'type' => 'cell_end', + 'attr' => $attr, + 'span' => $span + ) + ); + } + + // end the row + $return .= $this->wiki->addToken( + $this->rule, + array('type' => 'row_end') + ); + } + } + + // we're done! + return + "\n\n". + $this->wiki->addToken( + $this->rule, + array( + 'type' => 'table_start', + 'rows' => $num_rows, + 'cols' => $num_cols + ) + ). + $return. + $this->wiki->addToken( + $this->rule, + array( + 'type' => 'table_end' + ) + ). + "\n\n"; + } +} +?> \ No newline at end of file diff --git a/Creole/Parse/Tighten.php b/Creole/Parse/Tighten.php new file mode 100644 index 0000000..f801894 --- /dev/null +++ b/Creole/Parse/Tighten.php @@ -0,0 +1,37 @@ + + * + * @license LGPL + * + * @version $Id: Tighten.php 182 2008-09-14 15:56:00Z i.feelinglucky $ + * + */ + + +class Text_Wiki_Parse_Tighten extends Text_Wiki_Parse { + + + /** + * + * Apply tightening directly to the source text. + * + * @access public + * + */ + + function parse() + { + $this->wiki->source = str_replace("\n", '', + $this->wiki->source); + } +} +?> \ No newline at end of file diff --git a/Creole/Parse/Trim.php b/Creole/Parse/Trim.php new file mode 100644 index 0000000..58c90b4 --- /dev/null +++ b/Creole/Parse/Trim.php @@ -0,0 +1,69 @@ + + * @author Michele Tomaiuolo + * + */ + +class Text_Wiki_Parse_Trim extends Text_Wiki_Parse { + + + /** + * + * Simple parsing method. + * + * @access public + * + */ + + function parse() + { + // trim lines + $find = "/ *\n */"; + $replace = "\n"; + $this->wiki->source = preg_replace($find, $replace, $this->wiki->source); + + // trim lines with only one dash or star + $find = "/\n[\-\*]\n/"; + $replace = "\n\n"; + $this->wiki->source = preg_replace($find, $replace, $this->wiki->source); + + // finally, compress all instances of 3 or more newlines + // down to two newlines. + $find = "/\n{3,}/m"; + $replace = "\n\n"; + $this->wiki->source = preg_replace($find, $replace, $this->wiki->source); + + // numbered lists + $find = "/(\n[\*\#]*)([\d]+[\.\)]|[\w]\))/s"; + $replace = "$1#"; + $this->wiki->source = preg_replace($find, $replace, $this->wiki->source); + + // make ordinal numbers superscripted + $find = "/([\d])(st|nd|rd|th|er|e|re|ers|res|nds|de|des|re|me|res|mes|o|a)([\W])/"; + $replace = "$1^^$2^^$3"; + $this->wiki->source = preg_replace($find, $replace, $this->wiki->source); + + // numbers in parentesis are footnotes and references + $find = "/\(([\d][\d]?)\)/"; + $replace = "[$1]"; + $this->wiki->source = preg_replace($find, $replace, $this->wiki->source); + + // add hr before footnotes + $find = "/(\n+\-\-\-\-+\n*)?(\n\[[\d]+\].*)/s"; + $replace = "\n\n----\n\n$2"; + $this->wiki->source = preg_replace($find, $replace, $this->wiki->source); + + } + +} +?> \ No newline at end of file diff --git a/Creole/Parse/Tt.php b/Creole/Parse/Tt.php new file mode 100644 index 0000000..0be1b4a --- /dev/null +++ b/Creole/Parse/Tt.php @@ -0,0 +1,78 @@ + + * + * @license LGPL + * + * @version $Id: Tt.php 182 2008-09-14 15:56:00Z i.feelinglucky $ + * + */ + +class Text_Wiki_Parse_Tt extends Text_Wiki_Parse { + + + /** + * + * The regular expression used to parse the source text and find + * matches conforming to this rule. Used by the parse() method. + * + * @access public + * + * @var string + * + * @see parse() + * + */ + + var $regex = '/{{{(.*?)}}}(?!}|{{{)/'; + + /** + * + * Generates a replacement for the matched text. Token options are: + * + * 'type' => ['start'|'end'] The starting or ending point of the + * monospaced text. The text itself is encapsulated into a Raw token. + * + * @access public + * + * @param array &$matches The array of matches from parse(). + * + * @return string A token to be used as a placeholder + * in the source text for the preformatted text. + * + */ + + function process(&$matches) + { + // remove the sequence }}}{{{ + $find = "/}}}{{{/"; + $replace = ""; + $matches[1] = preg_replace($find, $replace, $matches[1]); + + $start = $this->wiki->addToken( + $this->rule, + array('type' => 'start') + ); + + $raw = $this->wiki->addToken( + 'Raw', + array('text' => $matches[1]) + ); + + $end = $this->wiki->addToken( + $this->rule, + array('type' => 'end') + ); + + return $start . $raw . $end; + } +} +?> diff --git a/Creole/Parse/Underline.php b/Creole/Parse/Underline.php new file mode 100644 index 0000000..16099ed --- /dev/null +++ b/Creole/Parse/Underline.php @@ -0,0 +1,78 @@ + + * @author Michele Tomaiuolo + * + */ + +class Text_Wiki_Parse_Underline extends Text_Wiki_Parse { + + + /** + * + * The regular expression used to parse the source text and find + * matches conforming to this rule. Used by the parse() method. + * + * @access public + * + * @var string + * + * @see parse() + * + */ + + //var $regex = "/__(.*?)__/"; + var $regex = "/(?:\_\_(.+?)\_\_|(?:(?<=[\W_\xFF])\_(?![ \_]))(.+?)(?:(? ['start'|'end'] The starting or ending point of the + * superscript text. The text itself is left in the source. + * + * @access public + * + * @param array &$matches The array of matches from parse(). + * + * @return string A pair of delimited tokens to be used as a + * placeholder in the source text surrounding the text to be + * superscripted. + * + */ + + function process(&$matches) + { + $text = $matches[1] ? $matches[1] : $matches[2]; + + if (! $this->wiki->checkInnerTags($text)) { + return $matches[0]; + } + + $start = $this->wiki->addToken( + 'Underline', // $this->rule, + array('type' => 'start') + ); + + $end = $this->wiki->addToken( + 'Underline', // $this->rule, + array('type' => 'end') + ); + + return $start . $text . $end; + } +} +?> diff --git a/Creole/Parse/Url.php b/Creole/Parse/Url.php new file mode 100644 index 0000000..82fd226 --- /dev/null +++ b/Creole/Parse/Url.php @@ -0,0 +1,109 @@ + tag (for the 'xhtml' + * format). + * + * @category Text + * + * @package Text_Wiki + * + * @author Michele Tomaiuolo + * + * @license LGPL + * + * @version $Id: Url.php 182 2008-09-14 15:56:00Z i.feelinglucky $ + * + */ + +class Text_Wiki_Parse_Url extends Text_Wiki_Parse { + + /** + * + * Constructor. Overrides the Text_Wiki_Parse constructor so that we + * can set the $regex property dynamically (we need to include the + * Text_Wiki $delim character). + * + * @param object &$obj The calling "parent" Text_Wiki object. + * + * @param string $name The token name to use for this rule. + * + */ + + function Text_Wiki_Parse_Url(&$obj) + { + parent::Text_Wiki_Parse($obj); + $this->regex = '/((?:\[\[ *((?:http:\/\/|https:\/\/|ftp:\/\/|mailto:|\/)[^\|\]\n ]*)( *\| *([^\]\n]*))? *\]\])|((http:\/\/|https:\/\/|ftp:\/\/|mailto:)[^\'\"\n ' . $this->wiki->delim . ']*[A-Za-z0-9\/\?\=\&\~\_]))/'; + } + + + /** + * + * Generates a replacement for the matched text. + * + * Token options are: + * + * 'href' => the URL link href portion + * + * 'text' => the displayed text of the URL link + * + * @access public + * + * @param array &$matches The array of matches from parse(). + * + * @return string A token to be used as a placeholder + * in the source text for the preformatted text. + * + */ + + function process(&$matches) + { + if (isset($matches[2])) $href = trim($matches[2]); + if (isset($matches[4])) $text = trim($matches[4]); + if (isset($matches[5])) $rawurl = $matches[5]; + if (empty($href)) $href = $rawurl; + + if (empty($text)) { + $text = $href; + if (strpos($text, '/') === FALSE) { + $text = str_replace('http://', '', $text); + $text = str_replace('mailto:', '', $text); + } + return $this->wiki->addToken( + $this->rule, + array( + 'type' => 'inline', + 'href' => $href, + 'text' => $text + ) + ); + } else { + return $this->wiki->addToken( + $this->rule, + array( + 'type' => 'start', + 'href' => $href, + 'text' => $text + ) + ) . $text . + $this->wiki->addToken( + $this->rule, + array( + 'type' => 'end', + 'href' => $href, + 'text' => $text + ) + ); + } + } + +} +?> \ No newline at end of file diff --git a/Creole/Plugin.php b/Creole/Plugin.php new file mode 100644 index 0000000..776a126 --- /dev/null +++ b/Creole/Plugin.php @@ -0,0 +1,72 @@ +Creole 语法发布文章。改进版本支持中文(utf-8 编码)、并除去不必要的标签。 + * + * @package Creole 解析器(改进版) + * @author 明城 + * @version 0.2 + * @link http://www.gracecode.com/ + */ + +require_once 'Creole/Creole_Wiki.php'; + +class Creole_Plugin implements Typecho_Plugin_Interface +{ + /** + * 激活插件方法,如果激活失败,直接抛出异常 + * + * @access public + * @return void + * @throws Typecho_Plugin_Exception + */ + public static function activate() { + Typecho_Plugin::factory('Widget_Abstract_Contents')->excerpt = array('Creole_Plugin', 'parse'); + Typecho_Plugin::factory('Widget_Abstract_Contents')->content = array('Creole_Plugin', 'parse'); + } + + /** + * 禁用插件方法,如果禁用失败,直接抛出异常 + * + * @static + * @access public + * @return void + * @throws Typecho_Plugin_Exception + */ + public static function deactivate() { + + } + + /** + * 获取插件配置面板 + * + * @access public + * @param Typecho_Widget_Helper_Form $form 配置面板 + * @return void + */ + public static function config(Typecho_Widget_Helper_Form $form) + { + + } + + /** + * 个人用户的配置面板 + * + * @access public + * @param Typecho_Widget_Helper_Form $form + * @return void + */ + public static function personalConfig(Typecho_Widget_Helper_Form $form){} + + + /** + * 插件实现方法 + * + * @access public + * @return void + */ + public static function parse($text, $widget, $lastResult) { + $text = empty($lastResult) ? $text : $lastResult; + $creole_parse = new Creole_Wiki; + return $creole_parse->transform(trim($text)); + } +} diff --git a/Creole/Render.inc.php b/Creole/Render.inc.php new file mode 100644 index 0000000..7a7d062 --- /dev/null +++ b/Creole/Render.inc.php @@ -0,0 +1,218 @@ + + * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 + * @version CVS: $Id: Render.inc.php 182 2008-09-14 15:56:00Z i.feelinglucky $ + * @link http://pear.php.net/package/Text_Wiki + */ + +/** + * Base rendering class for parsed and tokenized text. + * + * @category Text + * @package Text_Wiki + * @author Paul M. Jones + * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 + * @version Release: @package_version@ + * @link http://pear.php.net/package/Text_Wiki + */ +class Text_Wiki_Render { + + + /** + * + * Configuration options for this render rule. + * + * @access public + * + * @var string + * + */ + + var $conf = array(); + + + /** + * + * The name of this rule's format. + * + * @access public + * + * @var string + * + */ + + var $format = null; + + + /** + * + * The name of this rule's token array elements. + * + * @access public + * + * @var string + * + */ + + var $rule = null; + + + /** + * + * A reference to the calling Text_Wiki object. + * + * This is needed so that each rule has access to the same source + * text, token set, URLs, interwiki maps, page names, etc. + * + * @access public + * + * @var object + */ + + var $wiki = null; + + + /** + * + * Constructor for this render format or rule. + * + * @access public + * + * @param object &$obj The calling "parent" Text_Wiki object. + * + */ + + function Text_Wiki_Render(&$obj) + { + // keep a reference to the calling Text_Wiki object + $this->wiki =& $obj; + + // get the config-key-name for this object, + // strip the Text_Wiki_Render_ part + // 01234567890123456 + $tmp = get_class($this); + $tmp = substr($tmp, 17); + + // split into pieces at the _ mark. + // first part is format, second part is rule. + $part = explode('_', $tmp); + $this->format = isset($part[0]) ? ucwords(strtolower($part[0])) : null; + $this->rule = isset($part[1]) ? ucwords(strtolower($part[1])) : null; + + // is there a format but no rule? + // then this is the "main" render object, with + // pre() and post() methods. + if ($this->format && ! $this->rule && + isset($this->wiki->formatConf[$this->format]) && + is_array($this->wiki->formatConf[$this->format])) { + + // this is a format render object + $this->conf = array_merge( + $this->conf, + $this->wiki->formatConf[$this->format] + ); + + } + + // is there a format and a rule? + if ($this->format && $this->rule && + isset($this->wiki->renderConf[$this->format][$this->rule]) && + is_array($this->wiki->renderConf[$this->format][$this->rule])) { + + // this is a rule render object + $this->conf = array_merge( + $this->conf, + $this->wiki->renderConf[$this->format][$this->rule] + ); + } + } + + + /** + * + * Simple method to safely get configuration key values. + * + * @access public + * + * @param string $key The configuration key. + * + * @param mixed $default If the key does not exist, return this value + * instead. + * + * @return mixed The configuration key value (if it exists) or the + * default value (if not). + * + */ + + function getConf($key, $default = null) + { + if (isset($this->conf[$key])) { + return $this->conf[$key]; + } else { + return $default; + } + } + + + /** + * + * Simple method to wrap a configuration in an sprintf() format. + * + * @access public + * + * @param string $key The configuration key. + * + * @param string $format The sprintf() format string. + * + * @return mixed The formatted configuration key value (if it exists) + * or null (if it does not). + * + */ + + function formatConf($format, $key) + { + if (isset($this->conf[$key])) { + //$this->conf[$key] needs a textEncode....at least for Xhtml output... + return sprintf($format, $this->conf[$key]); + } else { + return null; + } + } + + /** + * Default method to render url + * + * @access public + * @param string $urlChunk a part of an url to render + * @return rendered url + * + */ + + function urlEncode($urlChunk) + { + return rawurlencode($urlChunk); + } + + /** + * Default method to render text (htmlspecialchars) + * + * @access public + * @param string $text the text to render + * @return rendered text + * + */ + + function textEncode($text) + { + return htmlspecialchars($text); + } +} +?> diff --git a/Creole/Render/Plain.php b/Creole/Render/Plain.php new file mode 100644 index 0000000..e1b02a8 --- /dev/null +++ b/Creole/Render/Plain.php @@ -0,0 +1,16 @@ + \ No newline at end of file diff --git a/Creole/Render/Plain/Anchor.php b/Creole/Render/Plain/Anchor.php new file mode 100644 index 0000000..f24f467 --- /dev/null +++ b/Creole/Render/Plain/Anchor.php @@ -0,0 +1,23 @@ + +* +* @author Paul M. Jones +* +* @package Text_Wiki +* +*/ + +class Text_Wiki_Render_Plain_Anchor extends Text_Wiki_Render { + + function token($options) + { + return $options['name']; + } +} + +?> diff --git a/Creole/Render/Plain/Blockquote.php b/Creole/Render/Plain/Blockquote.php new file mode 100644 index 0000000..08da94a --- /dev/null +++ b/Creole/Render/Plain/Blockquote.php @@ -0,0 +1,39 @@ + \ No newline at end of file diff --git a/Creole/Render/Plain/Bold.php b/Creole/Render/Plain/Bold.php new file mode 100644 index 0000000..628fb51 --- /dev/null +++ b/Creole/Render/Plain/Bold.php @@ -0,0 +1,23 @@ + \ No newline at end of file diff --git a/Creole/Render/Plain/Box.php b/Creole/Render/Plain/Box.php new file mode 100644 index 0000000..8d76735 --- /dev/null +++ b/Creole/Render/Plain/Box.php @@ -0,0 +1,48 @@ + + * @copyright 2005 bertrand Gugger + * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 + * @version CVS: $Id: Box.php 182 2008-09-14 15:56:00Z i.feelinglucky $ + * @link http://pear.php.net/package/Text_Wiki + */ + +/** + * This class renders a box drawn in Plain. + * + * @category Text + * @package Text_Wiki + * @author Bertrand Gugger + * @copyright 2005 bertrand Gugger + * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 + * @version Release: @package_version@ + * @link http://pear.php.net/package/Text_Wiki + */ +class Text_Wiki_Render_Plain_Box extends Text_Wiki_Render { + + /** + * + * Renders a token into text matching the requested format. + * + * @access public + * + * @param array $options The "options" portion of the token (second + * element). + * + * @return string The text rendered from the token options. + * + */ + + function token($options) + { + return ''; + } +} +?> diff --git a/Creole/Render/Plain/Break.php b/Creole/Render/Plain/Break.php new file mode 100644 index 0000000..5705bed --- /dev/null +++ b/Creole/Render/Plain/Break.php @@ -0,0 +1,24 @@ + \ No newline at end of file diff --git a/Creole/Render/Plain/Center.php b/Creole/Render/Plain/Center.php new file mode 100644 index 0000000..7b36367 --- /dev/null +++ b/Creole/Render/Plain/Center.php @@ -0,0 +1,23 @@ + \ No newline at end of file diff --git a/Creole/Render/Plain/Code.php b/Creole/Render/Plain/Code.php new file mode 100644 index 0000000..5f523a3 --- /dev/null +++ b/Creole/Render/Plain/Code.php @@ -0,0 +1,24 @@ + \ No newline at end of file diff --git a/Creole/Render/Plain/Colortext.php b/Creole/Render/Plain/Colortext.php new file mode 100644 index 0000000..d577fe0 --- /dev/null +++ b/Creole/Render/Plain/Colortext.php @@ -0,0 +1,23 @@ + \ No newline at end of file diff --git a/Creole/Render/Plain/Deflist.php b/Creole/Render/Plain/Deflist.php new file mode 100644 index 0000000..d05593b --- /dev/null +++ b/Creole/Render/Plain/Deflist.php @@ -0,0 +1,59 @@ + \ No newline at end of file diff --git a/Creole/Render/Plain/Delete.php b/Creole/Render/Plain/Delete.php new file mode 100644 index 0000000..8c00c7e --- /dev/null +++ b/Creole/Render/Plain/Delete.php @@ -0,0 +1,23 @@ + diff --git a/Creole/Render/Plain/Delimiter.php b/Creole/Render/Plain/Delimiter.php new file mode 100644 index 0000000..0e436aa --- /dev/null +++ b/Creole/Render/Plain/Delimiter.php @@ -0,0 +1,23 @@ + \ No newline at end of file diff --git a/Creole/Render/Plain/Embed.php b/Creole/Render/Plain/Embed.php new file mode 100644 index 0000000..3a4304f --- /dev/null +++ b/Creole/Render/Plain/Embed.php @@ -0,0 +1,23 @@ + \ No newline at end of file diff --git a/Creole/Render/Plain/Emphasis.php b/Creole/Render/Plain/Emphasis.php new file mode 100644 index 0000000..8d32995 --- /dev/null +++ b/Creole/Render/Plain/Emphasis.php @@ -0,0 +1,23 @@ + \ No newline at end of file diff --git a/Creole/Render/Plain/Font.php b/Creole/Render/Plain/Font.php new file mode 100644 index 0000000..e879d7a --- /dev/null +++ b/Creole/Render/Plain/Font.php @@ -0,0 +1,44 @@ + + * @copyright 2005 bertrand Gugger + * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 + * @version CVS: $Id: Font.php 182 2008-09-14 15:56:00Z i.feelinglucky $ + * @link http://pear.php.net/package/Text_Wiki + */ + +/** + * Font rule render class (used for BBCode) + * + * @category Text + * @package Text_Wiki + * @author Bertrand Gugger + * @copyright 2005 bertrand Gugger + * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 + * @version Release: @package_version@ + * @link http://pear.php.net/package/Text_Wiki + * @see Text_Wiki::Text_Wiki_Render() + */ +class Text_Wiki_Render_Plain_Font extends Text_Wiki_Render { + + /** + * Renders a token into text matching the requested format. + * process the font size option + * + * @access public + * @param array $options The "options" portion of the token (second element). + * @return string The text rendered from the token options. + */ + function token($options) + { + return; + } +} +?> diff --git a/Creole/Render/Plain/Freelink.php b/Creole/Render/Plain/Freelink.php new file mode 100644 index 0000000..61de294 --- /dev/null +++ b/Creole/Render/Plain/Freelink.php @@ -0,0 +1,23 @@ + \ No newline at end of file diff --git a/Creole/Render/Plain/Function.php b/Creole/Render/Plain/Function.php new file mode 100644 index 0000000..01dd3b0 --- /dev/null +++ b/Creole/Render/Plain/Function.php @@ -0,0 +1,39 @@ + $val) { + $output .= "{$val['type']} {$val['descr']} {$val['default']} "; + } + + $output .= ') '; + + foreach ($throws as $key => $val) { + $output .= "{$val['type']} {$val['descr']} "; + } + + return $output; + } +} +?> \ No newline at end of file diff --git a/Creole/Render/Plain/Heading.php b/Creole/Render/Plain/Heading.php new file mode 100644 index 0000000..306f2bf --- /dev/null +++ b/Creole/Render/Plain/Heading.php @@ -0,0 +1,14 @@ + \ No newline at end of file diff --git a/Creole/Render/Plain/Horiz.php b/Creole/Render/Plain/Horiz.php new file mode 100644 index 0000000..9d93764 --- /dev/null +++ b/Creole/Render/Plain/Horiz.php @@ -0,0 +1,23 @@ + \ No newline at end of file diff --git a/Creole/Render/Plain/Html.php b/Creole/Render/Plain/Html.php new file mode 100644 index 0000000..e64e94b --- /dev/null +++ b/Creole/Render/Plain/Html.php @@ -0,0 +1,24 @@ + \ No newline at end of file diff --git a/Creole/Render/Plain/Image.php b/Creole/Render/Plain/Image.php new file mode 100644 index 0000000..c5ddd6f --- /dev/null +++ b/Creole/Render/Plain/Image.php @@ -0,0 +1,22 @@ + \ No newline at end of file diff --git a/Creole/Render/Plain/Include.php b/Creole/Render/Plain/Include.php new file mode 100644 index 0000000..445990a --- /dev/null +++ b/Creole/Render/Plain/Include.php @@ -0,0 +1,8 @@ + \ No newline at end of file diff --git a/Creole/Render/Plain/Interwiki.php b/Creole/Render/Plain/Interwiki.php new file mode 100644 index 0000000..762a2a0 --- /dev/null +++ b/Creole/Render/Plain/Interwiki.php @@ -0,0 +1,29 @@ + diff --git a/Creole/Render/Plain/Italic.php b/Creole/Render/Plain/Italic.php new file mode 100644 index 0000000..e2a596c --- /dev/null +++ b/Creole/Render/Plain/Italic.php @@ -0,0 +1,23 @@ + \ No newline at end of file diff --git a/Creole/Render/Plain/List.php b/Creole/Render/Plain/List.php new file mode 100644 index 0000000..9983689 --- /dev/null +++ b/Creole/Render/Plain/List.php @@ -0,0 +1,68 @@ + \ No newline at end of file diff --git a/Creole/Render/Plain/Newline.php b/Creole/Render/Plain/Newline.php new file mode 100644 index 0000000..7c7903e --- /dev/null +++ b/Creole/Render/Plain/Newline.php @@ -0,0 +1,12 @@ + \ No newline at end of file diff --git a/Creole/Render/Plain/Page.php b/Creole/Render/Plain/Page.php new file mode 100644 index 0000000..7faa7bb --- /dev/null +++ b/Creole/Render/Plain/Page.php @@ -0,0 +1,48 @@ + + * @copyright 2005 bertrand Gugger + * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 + * @version CVS: $Id: Page.php 182 2008-09-14 15:56:00Z i.feelinglucky $ + * @link http://pear.php.net/package/Text_Wiki + */ + +/** + * This class renders page markers in Plain. + * + * @category Text + * @package Text_Wiki + * @author Bertrand Gugger + * @copyright 2005 bertrand Gugger + * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 + * @version Release: @package_version@ + * @link http://pear.php.net/package/Text_Wiki + */ +class Text_Wiki_Render_Plain_Page extends Text_Wiki_Render { + + /** + * + * Renders a token into text matching the requested format. + * + * @access public + * + * @param array $options The "options" portion of the token (second + * element). + * + * @return string The text rendered from the token options. + * + */ + + function token($options) + { + return "\x0C"; + } +} +?> diff --git a/Creole/Render/Plain/Paragraph.php b/Creole/Render/Plain/Paragraph.php new file mode 100644 index 0000000..16e536f --- /dev/null +++ b/Creole/Render/Plain/Paragraph.php @@ -0,0 +1,31 @@ + \ No newline at end of file diff --git a/Creole/Render/Plain/Phplookup.php b/Creole/Render/Plain/Phplookup.php new file mode 100644 index 0000000..a4268d1 --- /dev/null +++ b/Creole/Render/Plain/Phplookup.php @@ -0,0 +1,25 @@ + '_blank'); + + /** + * + * Renders a token into text matching the requested format. + * + * @access public + * + * @param array $options The "options" portion of the token (second + * element). + * + * @return string The text rendered from the token options. + * + */ + + function token($options) + { + return trim($options['text']); + } +} +?> \ No newline at end of file diff --git a/Creole/Render/Plain/Plugin.php b/Creole/Render/Plain/Plugin.php new file mode 100644 index 0000000..be7e457 --- /dev/null +++ b/Creole/Render/Plain/Plugin.php @@ -0,0 +1,49 @@ + + * @copyright 2005 bertrand Gugger + * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 + * @version CVS: $Id: Plugin.php 182 2008-09-14 15:56:00Z i.feelinglucky $ + * @link http://pear.php.net/package/Text_Wiki + */ + +/** + * This class renders wiki plugins in Plain. (empty) + * + * @category Text + * @package Text_Wiki + * @author Bertrand Gugger + * @copyright 2005 bertrand Gugger + * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 + * @version Release: @package_version@ + * @link http://pear.php.net/package/Text_Wiki + */ +class Text_Wiki_Render_Plain_Plugin extends Text_Wiki_Render { + + /** + * + * Renders a token into text matching the requested format. + * Plugins produce wiki markup so are processed by parsing, no tokens produced + * + * @access public + * + * @param array $options The "options" portion of the token (second + * element). + * + * @return string The text rendered from the token options. + * + */ + + function token($options) + { + return ''; + } +} +?> diff --git a/Creole/Render/Plain/Prefilter.php b/Creole/Render/Plain/Prefilter.php new file mode 100644 index 0000000..8452bdc --- /dev/null +++ b/Creole/Render/Plain/Prefilter.php @@ -0,0 +1,40 @@ + | +// +----------------------------------------------------------------------+ +// +// $Id: Prefilter.php 182 2008-09-14 15:56:00Z i.feelinglucky $ + + +/** +* +* This class implements a Text_Wiki_Render_Xhtml to "pre-filter" source text so +* that line endings are consistently \n, lines ending in a backslash \ +* are concatenated with the next line, and tabs are converted to spaces. +* +* @author Paul M. Jones +* +* @package Text_Wiki +* +*/ + +class Text_Wiki_Render_Plain_Prefilter extends Text_Wiki_Render { + function token() + { + return ''; + } +} +?> \ No newline at end of file diff --git a/Creole/Render/Plain/Preformatted.php b/Creole/Render/Plain/Preformatted.php new file mode 100644 index 0000000..ab3e821 --- /dev/null +++ b/Creole/Render/Plain/Preformatted.php @@ -0,0 +1,48 @@ + + * @copyright 2005 bertrand Gugger + * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 + * @version CVS: $Id: Preformatted.php 182 2008-09-14 15:56:00Z i.feelinglucky $ + * @link http://pear.php.net/package/Text_Wiki + */ + +/** + * This class renders preformated text in Plain. + * + * @category Text + * @package Text_Wiki + * @author Bertrand Gugger + * @copyright 2005 bertrand Gugger + * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 + * @version Release: @package_version@ + * @link http://pear.php.net/package/Text_Wiki + */ +class Text_Wiki_Render_Plain_Preformatted extends Text_Wiki_Render { + + /** + * + * Renders a token into text matching the requested format. + * + * @access public + * + * @param array $options The "options" portion of the token (second + * element). + * + * @return string The text rendered from the token options. + * + */ + + function token($options) + { + return $options['text']; + } +} +?> diff --git a/Creole/Render/Plain/Raw.php b/Creole/Render/Plain/Raw.php new file mode 100644 index 0000000..e7fa8a8 --- /dev/null +++ b/Creole/Render/Plain/Raw.php @@ -0,0 +1,23 @@ + \ No newline at end of file diff --git a/Creole/Render/Plain/Revise.php b/Creole/Render/Plain/Revise.php new file mode 100644 index 0000000..32bbcad --- /dev/null +++ b/Creole/Render/Plain/Revise.php @@ -0,0 +1,24 @@ + \ No newline at end of file diff --git a/Creole/Render/Plain/Smiley.php b/Creole/Render/Plain/Smiley.php new file mode 100644 index 0000000..0b87bab --- /dev/null +++ b/Creole/Render/Plain/Smiley.php @@ -0,0 +1,44 @@ + + * @copyright 2005 bertrand Gugger + * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 + * @version CVS: $Id: Smiley.php 182 2008-09-14 15:56:00Z i.feelinglucky $ + * @link http://pear.php.net/package/Text_Wiki + */ + +/** + * Smiley rule Plain render class + * + * @category Text + * @package Text_Wiki + * @author Bertrand Gugger + * @copyright 2005 bertrand Gugger + * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 + * @version Release: @package_version@ + * @link http://pear.php.net/package/Text_Wiki + * @see Text_Wiki::Text_Wiki_Render() + */ +class Text_Wiki_Render_Plain_Smiley extends Text_Wiki_Render { + + /** + * Renders a token into text matching the requested format. + * process the Smileys + * + * @access public + * @param array $options The "options" portion of the token (second element). + * @return string The text rendered from the token options. + */ + function token($options) + { + return $options['symbol']; + } +} +?> diff --git a/Creole/Render/Plain/Specialchar.php b/Creole/Render/Plain/Specialchar.php new file mode 100644 index 0000000..528f564 --- /dev/null +++ b/Creole/Render/Plain/Specialchar.php @@ -0,0 +1,54 @@ + + * @copyright 2005 bertrand Gugger + * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 + * @version CVS: $Id: Specialchar.php 182 2008-09-14 15:56:00Z i.feelinglucky $ + * @link http://pear.php.net/package/Text_Wiki + */ + +/** + * This class renders special characters in Plain. + * + * @category Text + * @package Text_Wiki + * @author Bertrand Gugger + * @copyright 2005 bertrand Gugger + * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 + * @version Release: @package_version@ + * @link http://pear.php.net/package/Text_Wiki + */ +class Text_Wiki_Render_Plain_SpecialChar extends Text_Wiki_Render { + + var $types = array('~bs~' => '\\', + '~hs~' => ' ', + '~amp~' => '&', + '~ldq~' => '"', + '~rdq~' => '"', + '~lsq~' => "'", + '~rsq~' => "'", + '~c~' => '©', + '~--~' => '-', + '" -- "' => '-', + '" -- "' => '-', + '~lt~' => '<', + '~gt~' => '>'); + + function token($options) + { + if (isset($this->types[$options['char']])) { + return $this->types[$options['char']]; + } else { + return $options['char']; + } + } +} + +?> diff --git a/Creole/Render/Plain/Strong.php b/Creole/Render/Plain/Strong.php new file mode 100644 index 0000000..7ff55a3 --- /dev/null +++ b/Creole/Render/Plain/Strong.php @@ -0,0 +1,24 @@ + \ No newline at end of file diff --git a/Creole/Render/Plain/Subscript.php b/Creole/Render/Plain/Subscript.php new file mode 100644 index 0000000..25b4052 --- /dev/null +++ b/Creole/Render/Plain/Subscript.php @@ -0,0 +1,48 @@ + + * @copyright 2005 bertrand Gugger + * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 + * @version CVS: $Id: Subscript.php 182 2008-09-14 15:56:00Z i.feelinglucky $ + * @link http://pear.php.net/package/Text_Wiki + */ + +/** + * This class renders subscript text in Plain. + * + * @category Text + * @package Text_Wiki + * @author Bertrand Gugger + * @copyright 2005 bertrand Gugger + * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 + * @version Release: @package_version@ + * @link http://pear.php.net/package/Text_Wiki + */ +class Text_Wiki_Render_Plain_Subscript extends Text_Wiki_Render { + + /** + * + * Renders a token into text matching the requested format. + * + * @access public + * + * @param array $options The "options" portion of the token (second + * element). + * + * @return string The text rendered from the token options. + * + */ + + function token($options) + { + return ''; + } +} +?> diff --git a/Creole/Render/Plain/Superscript.php b/Creole/Render/Plain/Superscript.php new file mode 100644 index 0000000..d239ee3 --- /dev/null +++ b/Creole/Render/Plain/Superscript.php @@ -0,0 +1,23 @@ + \ No newline at end of file diff --git a/Creole/Render/Plain/Table.php b/Creole/Render/Plain/Table.php new file mode 100644 index 0000000..86f09aa --- /dev/null +++ b/Creole/Render/Plain/Table.php @@ -0,0 +1,65 @@ + diff --git a/Creole/Render/Plain/Tighten.php b/Creole/Render/Plain/Tighten.php new file mode 100644 index 0000000..e2babf4 --- /dev/null +++ b/Creole/Render/Plain/Tighten.php @@ -0,0 +1,10 @@ + \ No newline at end of file diff --git a/Creole/Render/Plain/Titlebar.php b/Creole/Render/Plain/Titlebar.php new file mode 100644 index 0000000..ed5a794 --- /dev/null +++ b/Creole/Render/Plain/Titlebar.php @@ -0,0 +1,54 @@ + + * @copyright 2005 bertrand Gugger + * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 + * @version CVS: $Id: Titlebar.php 182 2008-09-14 15:56:00Z i.feelinglucky $ + * @link http://pear.php.net/package/Text_Wiki + */ + +/** + * This class renders a title bar in Plain. + * + * @category Text + * @package Text_Wiki + * @author Bertrand Gugger + * @copyright 2005 bertrand Gugger + * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 + * @version Release: @package_version@ + * @link http://pear.php.net/package/Text_Wiki + */ +class Text_Wiki_Render_Plain_Titlebar extends Text_Wiki_Render { + + /** + * + * Renders a token into text matching the requested format. + * + * @access public + * + * @param array $options The "options" portion of the token (second + * element). + * + * @return string The text rendered from the token options. + * + */ + + function token($options) + { + if ($options['type'] == 'start') { + return '***** '; + } + + if ($options['type'] == 'end') { + return " *****\n"; + } + } +} +?> diff --git a/Creole/Render/Plain/Toc.php b/Creole/Render/Plain/Toc.php new file mode 100644 index 0000000..34f3061 --- /dev/null +++ b/Creole/Render/Plain/Toc.php @@ -0,0 +1,39 @@ + \ No newline at end of file diff --git a/Creole/Render/Plain/Tt.php b/Creole/Render/Plain/Tt.php new file mode 100644 index 0000000..c59ccf4 --- /dev/null +++ b/Creole/Render/Plain/Tt.php @@ -0,0 +1,24 @@ + \ No newline at end of file diff --git a/Creole/Render/Plain/Underline.php b/Creole/Render/Plain/Underline.php new file mode 100644 index 0000000..8e4d18b --- /dev/null +++ b/Creole/Render/Plain/Underline.php @@ -0,0 +1,23 @@ + \ No newline at end of file diff --git a/Creole/Render/Plain/Url.php b/Creole/Render/Plain/Url.php new file mode 100644 index 0000000..5c758e2 --- /dev/null +++ b/Creole/Render/Plain/Url.php @@ -0,0 +1,29 @@ + \ No newline at end of file diff --git a/Creole/Render/Plain/Wikilink.php b/Creole/Render/Plain/Wikilink.php new file mode 100644 index 0000000..8337dda --- /dev/null +++ b/Creole/Render/Plain/Wikilink.php @@ -0,0 +1,24 @@ + \ No newline at end of file diff --git a/Creole/Render/Xhtml.php b/Creole/Render/Xhtml.php new file mode 100644 index 0000000..3658cc3 --- /dev/null +++ b/Creole/Render/Xhtml.php @@ -0,0 +1,109 @@ + + * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 + * @version CVS: $Id: Xhtml.php 182 2008-09-14 15:56:00Z i.feelinglucky $ + * @link http://pear.php.net/package/Text_Wiki + */ + +/** + * Format class for the Xhtml rendering + * + * @category Text + * @package Text_Wiki + * @author Paul M. Jones + * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 + * @version Release: @package_version@ + * @link http://pear.php.net/package/Text_Wiki + */ +class Text_Wiki_Render_Xhtml extends Text_Wiki_Render { + + var $conf = array( + 'translate' => HTML_ENTITIES, + 'quotes' => ENT_COMPAT, + //'charset' => 'ISO-8859-1' + 'charset' => 'UTF-8' + ); + + function pre() + { + $this->wiki->source = $this->textEncode($this->wiki->source); + } + + function post() + { + return; + } + + + /** + * Method to render text + * + * @access public + * @param string $text the text to render + * @return rendered text + * + */ + + function textEncode($text) + { + // attempt to translate HTML entities in the source. + // get the config options. + $type = $this->getConf('translate', HTML_ENTITIES); + $quotes = $this->getConf('quotes', ENT_COMPAT); + //$charset = $this->getConf('charset', 'ISO-8859-1'); + $charset = $this->getConf('charset', 'UTF-8'); + + // have to check null and false because HTML_ENTITIES is a zero + if ($type === HTML_ENTITIES) { + + // keep a copy of the translated version of the delimiter + // so we can convert it back. + $new_delim = htmlentities($this->wiki->delim, $quotes, $charset); + + // convert the entities. we silence the call here so that + // errors about charsets don't pop up, per counsel from + // Jan at Horde. (http://pear.php.net/bugs/bug.php?id=4474) + $text = @htmlentities( + $text, + $quotes, + $charset + ); + + // re-convert the delimiter + $text = str_replace( + $new_delim, $this->wiki->delim, $text + ); + + } elseif ($type === HTML_SPECIALCHARS) { + + // keep a copy of the translated version of the delimiter + // so we can convert it back. + $new_delim = htmlspecialchars($this->wiki->delim, $quotes, + $charset); + + // convert the entities. we silence the call here so that + // errors about charsets don't pop up, per counsel from + // Jan at Horde. (http://pear.php.net/bugs/bug.php?id=4474) + $text = @htmlspecialchars( + $text, + $quotes, + $charset + ); + + // re-convert the delimiter + $text = str_replace( + $new_delim, $this->wiki->delim, $text + ); + } + return $text; + } +} +?> diff --git a/Creole/Render/Xhtml/Address.php b/Creole/Render/Xhtml/Address.php new file mode 100644 index 0000000..f0b9f7c --- /dev/null +++ b/Creole/Render/Xhtml/Address.php @@ -0,0 +1,54 @@ + + * + * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 + * + * @version CVS: $Id: Address.php 182 2008-09-14 15:56:00Z i.feelinglucky $ + * + * @link http://pear.php.net/package/Text_Wiki + * + */ + +class Text_Wiki_Render_Xhtml_Address extends Text_Wiki_Render { + + var $conf = array( + 'css' => null + ); + + /** + * + * Renders a token into text matching the requested format. + * + * @access public + * + * @param array $options The "options" portion of the token (second + * element). + * + * @return string The text rendered from the token options. + * + */ + + function token($options) + { + if ($options['type'] == 'start') { + $css = $this->formatConf(' class="%s"', 'css'); + return ""; + } + + if ($options['type'] == 'end') { + return ''; + } + } +} +?> diff --git a/Creole/Render/Xhtml/Anchor.php b/Creole/Render/Xhtml/Anchor.php new file mode 100644 index 0000000..2bbf6b8 --- /dev/null +++ b/Creole/Render/Xhtml/Anchor.php @@ -0,0 +1,48 @@ + + * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 + * @version CVS: $Id: Anchor.php 182 2008-09-14 15:56:00Z i.feelinglucky $ + * @link http://pear.php.net/package/Text_Wiki + */ + +/** + * This class renders an anchor target name in XHTML. + * + * @category Text + * @package Text_Wiki + * @author Paul M. Jones + * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 + * @version Release: @package_version@ + * @link http://pear.php.net/package/Text_Wiki + */ +class Text_Wiki_Render_Xhtml_Anchor extends Text_Wiki_Render { + + var $conf = array( + 'css' => null + ); + + function token($options) + { + extract($options); // $type, $name + + if ($type == 'start') { + $css = $this->formatConf(' class="%s"', 'css'); + $format = ""; + return sprintf($format, $this->textEncode($name)); + } + + if ($type == 'end') { + return ''; + } + } +} + +?> diff --git a/Creole/Render/Xhtml/Blockquote.php b/Creole/Render/Xhtml/Blockquote.php new file mode 100644 index 0000000..2c41d9d --- /dev/null +++ b/Creole/Render/Xhtml/Blockquote.php @@ -0,0 +1,72 @@ + + * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 + * @version CVS: $Id: Blockquote.php 182 2008-09-14 15:56:00Z i.feelinglucky $ + * @link http://pear.php.net/package/Text_Wiki + */ + +/** + * This class renders a blockquote in XHTML. + * + * @category Text + * @package Text_Wiki + * @author Paul M. Jones + * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 + * @version Release: @package_version@ + * @link http://pear.php.net/package/Text_Wiki + */ +class Text_Wiki_Render_Xhtml_Blockquote extends Text_Wiki_Render { + + var $conf = array( + 'css' => null + ); + + /** + * + * Renders a token into text matching the requested format. + * + * @access public + * + * @param array $options The "options" portion of the token (second + * element). + * + * @return string The text rendered from the token options. + * + */ + + function token($options) + { + $type = $options['type']; + $level = $options['level']; + + // set up indenting so that the results look nice; we do this + // in two steps to avoid str_pad mathematics. ;-) + $pad = str_pad('', $level, "\t"); + $pad = str_replace("\t", ' ', $pad); + + // pick the css type + $css = $this->formatConf(' class="%s"', 'css'); + + if (isset($options['css'])) { + $css = ' class="' . $options['css']. '"'; + } + // starting + if ($type == 'start') { + return "$pad"; + } + + // ending + if ($type == 'end') { + return $pad . "\n"; + } + } +} +?> diff --git a/Creole/Render/Xhtml/Bold.php b/Creole/Render/Xhtml/Bold.php new file mode 100644 index 0000000..8bc59e8 --- /dev/null +++ b/Creole/Render/Xhtml/Bold.php @@ -0,0 +1,57 @@ + + * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 + * @version CVS: $Id: Bold.php 182 2008-09-14 15:56:00Z i.feelinglucky $ + * @link http://pear.php.net/package/Text_Wiki + */ + +/** + * This class renders bold text in XHTML. + * + * @category Text + * @package Text_Wiki + * @author Paul M. Jones + * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 + * @version Release: @package_version@ + * @link http://pear.php.net/package/Text_Wiki + */ +class Text_Wiki_Render_Xhtml_Bold extends Text_Wiki_Render { + + var $conf = array( + 'css' => null + ); + + /** + * + * Renders a token into text matching the requested format. + * + * @access public + * + * @param array $options The "options" portion of the token (second + * element). + * + * @return string The text rendered from the token options. + * + */ + + function token($options) + { + if ($options['type'] == 'start') { + $css = $this->formatConf(' class="%s"', 'css'); + return ""; + } + + if ($options['type'] == 'end') { + return ''; + } + } +} +?> diff --git a/Creole/Render/Xhtml/Box.php b/Creole/Render/Xhtml/Box.php new file mode 100644 index 0000000..3ff35b3 --- /dev/null +++ b/Creole/Render/Xhtml/Box.php @@ -0,0 +1,62 @@ + + * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 + * @version CVS: $Id: Box.php 182 2008-09-14 15:56:00Z i.feelinglucky $ + * @link http://pear.php.net/package/Text_Wiki + */ + +/** + * This class renders a box drawn in XHTML. + * + * @category Text + * @package Text_Wiki + * @author Paul M. Jones + * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 + * @version Release: @package_version@ + * @link http://pear.php.net/package/Text_Wiki + */ +class Text_Wiki_Render_Xhtml_Box extends Text_Wiki_Render { + + var $conf = array( + 'css' => 'simplebox' + ); + + /** + * + * Renders a token into text matching the requested format. + * + * @access public + * + * @param array $options The "options" portion of the token (second + * element). + * + * @return string The text rendered from the token options. + * + */ + + function token($options) + { + if ($options['type'] == 'start') { + if ($options['css']) { + $css = ' class="' . $options['css']. '"'; + } + else { + $css = $this->formatConf(' class="%s"', 'css'); + } + return "
"; + } + + if ($options['type'] == 'end') { + return '
'; + } + } +} +?> diff --git a/Creole/Render/Xhtml/Break.php b/Creole/Render/Xhtml/Break.php new file mode 100644 index 0000000..7bada1e --- /dev/null +++ b/Creole/Render/Xhtml/Break.php @@ -0,0 +1,52 @@ + + * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 + * @version CVS: $Id: Break.php 182 2008-09-14 15:56:00Z i.feelinglucky $ + * @link http://pear.php.net/package/Text_Wiki + */ + +/** + * This class renders line breaks in XHTML. + * + * @category Text + * @package Text_Wiki + * @author Paul M. Jones + * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 + * @version Release: @package_version@ + * @link http://pear.php.net/package/Text_Wiki + */ +class Text_Wiki_Render_Xhtml_Break extends Text_Wiki_Render { + + var $conf = array( + 'css' => null + ); + + /** + * + * Renders a token into text matching the requested format. + * + * @access public + * + * @param array $options The "options" portion of the token (second + * element). + * + * @return string The text rendered from the token options. + * + */ + + function token($options) + { + $css = $this->formatConf(' class="%s"', 'css'); + return "\n"; + } +} + +?> diff --git a/Creole/Render/Xhtml/Center.php b/Creole/Render/Xhtml/Center.php new file mode 100644 index 0000000..1f8a96b --- /dev/null +++ b/Creole/Render/Xhtml/Center.php @@ -0,0 +1,62 @@ + + * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 + * @version CVS: $Id: Center.php 182 2008-09-14 15:56:00Z i.feelinglucky $ + * @link http://pear.php.net/package/Text_Wiki + */ + +/** + * This class renders centered content in XHTML. + * + * @category Text + * @package Text_Wiki + * @author Paul M. Jones + * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 + * @version Release: @package_version@ + * @link http://pear.php.net/package/Text_Wiki + */ +class Text_Wiki_Render_Xhtml_Center extends Text_Wiki_Render { + + var $conf = array( + 'css' => null + ); + + /** + * + * Renders a token into text matching the requested format. + * + * @access public + * + * @param array $options The "options" portion of the token (second + * element). + * + * @return string The text rendered from the token options. + * + */ + + function token($options) + { + if ($options['type'] == 'start') { + $css = $this->getConf('css'); + if ($css) { + return "
"; + } + else { + return '
'; + } + } + + if ($options['type'] == 'end') { + return '
'; + } + } +} +?> diff --git a/Creole/Render/Xhtml/Code.php b/Creole/Render/Xhtml/Code.php new file mode 100644 index 0000000..479a55e --- /dev/null +++ b/Creole/Render/Xhtml/Code.php @@ -0,0 +1,133 @@ + + * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 + * @version CVS: $Id: Code.php 182 2008-09-14 15:56:00Z i.feelinglucky $ + * @link http://pear.php.net/package/Text_Wiki + */ + +/** + * This class renders code blocks in XHTML. + * + * @category Text + * @package Text_Wiki + * @author Paul M. Jones + * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 + * @version Release: @package_version@ + * @link http://pear.php.net/package/Text_Wiki + */ +class Text_Wiki_Render_Xhtml_Code extends Text_Wiki_Render { + + var $conf = array( + 'css' => null, // class for
+        'css_code' => null, // class for generic 
+        'css_php'  => null, // class for PHP 
+        'css_html' => null, // class for HTML 
+        'css_filename' => null // class for optional filename 
+ ); + + /** + * + * Renders a token into text matching the requested format. + * + * @access public + * + * @param array $options The "options" portion of the token (second + * element). + * + * @return string The text rendered from the token options. + * + */ + + function token($options) + { + $text = $options['text']; + $attr = $options['attr']; + $type = strtolower($attr['type']); + + $css = $this->formatConf(' class="%s"', 'css'); + $css_code = $this->formatConf(' class="%s"', 'css_code'); + $css_php = $this->formatConf(' class="%s"', 'css_php'); + $css_html = $this->formatConf(' class="%s"', 'css_html'); + $css_filename = $this->formatConf(' class="%s"', 'css_filename'); + + if ($type == 'php') { + if (substr($options['text'], 0, 5) != '"; // ... tags) + ob_start(); + highlight_string($text); + $text = ob_get_contents(); + ob_end_clean(); + + // replace
tags with simple newlines. + // replace non-breaking space with simple spaces. + // translate HTML and color to XHTML and style. + // courtesy of research by A. Kalin :-). + $map = array( + '
' => "\n", + ' ' => ' ', + ' '' => '
', + 'color="' => 'style="color:' + ); + $text = strtr($text, $map); + + // get rid of the last newline inside the code block + // (becuase higlight_string puts one there) + if (substr($text, -8) == "\n
") { + $text = substr($text, 0, -8) . "
"; + } + + // replace all tags with classed tags + if ($css_php) { + $text = str_replace('', "", $text); + } + + // done + $text = "$text
"; + + } elseif ($type == 'html' || $type == 'xhtml') { + + // HTML code example: + // add opening and closing tags, + // convert tabs to four spaces, + // convert entities. + $text = str_replace("\t", " ", $text); + $text = "\n$text\n"; + $text = $this->textEncode($text); + $text = "$text"; + + } else { + // generic code example: + // convert tabs to four spaces, + // convert entities. + $text = str_replace("\t", " ", $text); + $text = $this->textEncode($text); + $text = "$text"; + } + + if ($css_filename && isset($attr['filename'])) { + $text = "" . + $attr['filename'] . '
' . $text; + } + + return "\n$text\n\n"; + } +} +?> diff --git a/Creole/Render/Xhtml/Colortext.php b/Creole/Render/Xhtml/Colortext.php new file mode 100644 index 0000000..8b6d534 --- /dev/null +++ b/Creole/Render/Xhtml/Colortext.php @@ -0,0 +1,79 @@ + + * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 + * @version CVS: $Id: Colortext.php 182 2008-09-14 15:56:00Z i.feelinglucky $ + * @link http://pear.php.net/package/Text_Wiki + */ + +/** + * This class renders colored text in XHTML. + * + * @category Text + * @package Text_Wiki + * @author Paul M. Jones + * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 + * @version Release: @package_version@ + * @link http://pear.php.net/package/Text_Wiki + */ +class Text_Wiki_Render_Xhtml_Colortext extends Text_Wiki_Render { + + var $colors = array( + 'aqua', + 'black', + 'blue', + 'fuchsia', + 'gray', + 'green', + 'lime', + 'maroon', + 'navy', + 'olive', + 'purple', + 'red', + 'silver', + 'teal', + 'white', + 'yellow' + ); + + + /** + * + * Renders a token into text matching the requested format. + * + * @access public + * + * @param array $options The "options" portion of the token (second + * element). + * + * @return string The text rendered from the token options. + * + */ + + function token($options) + { + $type = $options['type']; + $color = $options['color']; + + if (! in_array($color, $this->colors) && $color{0} != '#') { + $color = '#' . $color; + } + + if ($type == 'start') { + return ""; + } + + if ($options['type'] == 'end') { + return ''; + } + } +} +?> diff --git a/Creole/Render/Xhtml/Deflist.php b/Creole/Render/Xhtml/Deflist.php new file mode 100644 index 0000000..0a80c9c --- /dev/null +++ b/Creole/Render/Xhtml/Deflist.php @@ -0,0 +1,87 @@ + + * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 + * @version CVS: $Id: Deflist.php 182 2008-09-14 15:56:00Z i.feelinglucky $ + * @link http://pear.php.net/package/Text_Wiki + */ + +/** + * This class renders definition lists in XHTML. + * + * @category Text + * @package Text_Wiki + * @author Paul M. Jones + * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 + * @version Release: @package_version@ + * @link http://pear.php.net/package/Text_Wiki + */ +class Text_Wiki_Render_Xhtml_Deflist extends Text_Wiki_Render { + + var $conf = array( + 'css_dl' => null, + 'css_dt' => null, + 'css_dd' => null + ); + + /** + * + * Renders a token into text matching the requested format. + * + * @access public + * + * @param array $options The "options" portion of the token (second + * element). + * + * @return string The text rendered from the token options. + * + */ + + function token($options) + { + $type = $options['type']; + $pad = " "; + + switch ($type) { + + case 'list_start': + $css = $this->formatConf(' class="%s"', 'css_dl'); + return "\n"; + break; + + case 'list_end': + return "\n\n"; + break; + + case 'term_start': + $css = $this->formatConf(' class="%s"', 'css_dt'); + return $pad . ""; + break; + + case 'term_end': + return "\n"; + break; + + case 'narr_start': + $css = $this->formatConf(' class="%s"', 'css_dd'); + return $pad . $pad . ""; + break; + + case 'narr_end': + return "\n"; + break; + + default: + return ''; + + } + } +} +?> diff --git a/Creole/Render/Xhtml/Delete.php b/Creole/Render/Xhtml/Delete.php new file mode 100644 index 0000000..ae55423 --- /dev/null +++ b/Creole/Render/Xhtml/Delete.php @@ -0,0 +1,58 @@ + + * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 + * @version CVS: $Id: Delete.php 182 2008-09-14 15:56:00Z i.feelinglucky $ + * @link http://pear.php.net/package/Text_Wiki + */ + +/** + * This class renders underlined text in XHTML. + * + * @category Text + * @package Text_Wiki + * @author Paul M. Jones + * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 + * @version Release: @package_version@ + * @link http://pear.php.net/package/Text_Wiki + */ +class Text_Wiki_Render_Xhtml_Delete extends Text_Wiki_Render { + + var $conf = array( + 'css' => null + ); + + /** + * + * Renders a token into text matching the requested format. + * + * @access public + * + * @param array $options The "options" portion of the token (second + * element). + * + * @return string The text rendered from the token options. + * + */ + function token($options) + { + if ($options['type'] == 'start') { + //$css = $this->formatConf(' class="%s"', 'css'); + //return ""; + //return ""; + return ""; + } + + if ($options['type'] == 'end') { + return ''; + } + } +} +?> diff --git a/Creole/Render/Xhtml/Delimiter.php b/Creole/Render/Xhtml/Delimiter.php new file mode 100644 index 0000000..c9543ba --- /dev/null +++ b/Creole/Render/Xhtml/Delimiter.php @@ -0,0 +1,46 @@ + + * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 + * @version CVS: $Id: Delimiter.php 182 2008-09-14 15:56:00Z i.feelinglucky $ + * @link http://pear.php.net/package/Text_Wiki + */ + +/** + * This class set back the replaced delimiters in XHTML. + * + * @category Text + * @package Text_Wiki + * @author Paul M. Jones + * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 + * @version Release: @package_version@ + * @link http://pear.php.net/package/Text_Wiki + */ +class Text_Wiki_Render_Xhtml_Delimiter extends Text_Wiki_Render { + + /** + * + * Renders a token into text matching the requested format. + * + * @access public + * + * @param array $options The "options" portion of the token (second + * element). + * + * @return string The text rendered from the token options. + * + */ + + function token($options) + { + return $options['text']; + } +} +?> diff --git a/Creole/Render/Xhtml/Embed.php b/Creole/Render/Xhtml/Embed.php new file mode 100644 index 0000000..cf5517a --- /dev/null +++ b/Creole/Render/Xhtml/Embed.php @@ -0,0 +1,46 @@ + + * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 + * @version CVS: $Id: Embed.php 182 2008-09-14 15:56:00Z i.feelinglucky $ + * @link http://pear.php.net/package/Text_Wiki + */ + +/** + * This class replaces the embedded php output in XHTML. + * + * @category Text + * @package Text_Wiki + * @author Paul M. Jones + * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 + * @version Release: @package_version@ + * @link http://pear.php.net/package/Text_Wiki + */ +class Text_Wiki_Render_Xhtml_Embed extends Text_Wiki_Render { + + /** + * + * Renders a token into text matching the requested format. + * + * @access public + * + * @param array $options The "options" portion of the token (second + * element). + * + * @return string The text rendered from the token options. + * + */ + + function token($options) + { + return $options['text']; + } +} +?> diff --git a/Creole/Render/Xhtml/Emphasis.php b/Creole/Render/Xhtml/Emphasis.php new file mode 100644 index 0000000..be06c37 --- /dev/null +++ b/Creole/Render/Xhtml/Emphasis.php @@ -0,0 +1,58 @@ + + * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 + * @version CVS: $Id: Emphasis.php 182 2008-09-14 15:56:00Z i.feelinglucky $ + * @link http://pear.php.net/package/Text_Wiki + */ + +/** + * This class renders emphasized text in XHTML. + * + * @category Text + * @package Text_Wiki + * @author Paul M. Jones + * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 + * @version Release: @package_version@ + * @link http://pear.php.net/package/Text_Wiki + */ +class Text_Wiki_Render_Xhtml_Emphasis extends Text_Wiki_Render { + + var $conf = array( + 'css' => null + ); + + + /** + * + * Renders a token into text matching the requested format. + * + * @access public + * + * @param array $options The "options" portion of the token (second + * element). + * + * @return string The text rendered from the token options. + * + */ + + function token($options) + { + if ($options['type'] == 'start') { + $css = $this->formatConf(' class="%s"', 'css'); + return ""; + } + + if ($options['type'] == 'end') { + return ''; + } + } +} +?> diff --git a/Creole/Render/Xhtml/Font.php b/Creole/Render/Xhtml/Font.php new file mode 100644 index 0000000..1127f15 --- /dev/null +++ b/Creole/Render/Xhtml/Font.php @@ -0,0 +1,83 @@ + + * @copyright 2005 bertrand Gugger + * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 + * @version CVS: $Id: Font.php 182 2008-09-14 15:56:00Z i.feelinglucky $ + * @link http://pear.php.net/package/Text_Wiki + */ + +/** + * Font rule render class (used for BBCode) + * + * @category Text + * @package Text_Wiki + * @author Bertrand Gugger + * @copyright 2005 bertrand Gugger + * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 + * @version Release: @package_version@ + * @link http://pear.php.net/package/Text_Wiki + * @see Text_Wiki::Text_Wiki_Render() + */ +class Text_Wiki_Render_Xhtml_Font extends Text_Wiki_Render { + +/* var $size = array( + 'xx-small', + 'x-small', + 'small', + 'medium', + 'large', + 'x-large', + 'xx-large', + 'larger', + 'smaller' + ); + var $units = array( + 'em', + 'ex', + 'px', + 'in', + 'cm', + 'mm', + 'pt', + 'pc' + ); +*/ + + /** + * Renders a token into text matching the requested format. + * process the font size option + * + * @access public + * @param array $options The "options" portion of the token (second element). + * @return string The text rendered from the token options. + */ + function token($options) + { + if ($options['type'] == 'end') { + return ''; + } + if ($options['type'] != 'start') { + return ''; + } + + $ret = ''; + } +} +?> diff --git a/Creole/Render/Xhtml/Freelink.php b/Creole/Render/Xhtml/Freelink.php new file mode 100644 index 0000000..0fade11 --- /dev/null +++ b/Creole/Render/Xhtml/Freelink.php @@ -0,0 +1,35 @@ + + * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 + * @version CVS: $Id: Freelink.php 182 2008-09-14 15:56:00Z i.feelinglucky $ + * @link http://pear.php.net/package/Text_Wiki + */ + +/** + * The wikilink render class. + */ +require_once 'Text/Wiki/Render/Xhtml/Wikilink.php'; + +/** + * This class renders free links in XHTML. + * + * @category Text + * @package Text_Wiki + * @author Paul M. Jones + * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 + * @version Release: @package_version@ + * @link http://pear.php.net/package/Text_Wiki + */ +class Text_Wiki_Render_Xhtml_Freelink extends Text_Wiki_Render_Xhtml_Wikilink { + // renders identically to wikilinks, only the parsing is different :-) +} + +?> diff --git a/Creole/Render/Xhtml/Function.php b/Creole/Render/Xhtml/Function.php new file mode 100644 index 0000000..a4e7e82 --- /dev/null +++ b/Creole/Render/Xhtml/Function.php @@ -0,0 +1,108 @@ + + * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 + * @version CVS: $Id: Function.php 182 2008-09-14 15:56:00Z i.feelinglucky $ + * @link http://pear.php.net/package/Text_Wiki + */ + +/** + * This class renders a function description in XHTML. + * + * @category Text + * @package Text_Wiki + * @author Paul M. Jones + * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 + * @version Release: @package_version@ + * @link http://pear.php.net/package/Text_Wiki + */ +class Text_Wiki_Render_Xhtml_Function extends Text_Wiki_Render { + + var $conf = array( + // list separator for params and throws + 'list_sep' => ', ', + + // the "main" format string + 'format_main' => '%access %return %name ( %params ) %throws', + + // the looped format string for required params + 'format_param' => '%type %descr', + + // the looped format string for params with default values + 'format_paramd' => '[%type %descr default %default]', + + // the looped format string for throws + 'format_throws' => 'throws %type %descr' + ); + + /** + * + * Renders a token into text matching the requested format. + * + * @access public + * + * @param array $options The "options" portion of the token (second + * element). + * + * @return string The text rendered from the token options. + * + */ + + function token($options) + { + extract($options); // name, access, return, params, throws + + // build the baseline output + $output = $this->conf['format_main']; + $output = str_replace('%access', $this->textEncode($access), $output); + $output = str_replace('%return', $this->textEncode($return), $output); + $output = str_replace('%name', $this->textEncode($name), $output); + + // build the set of params + $list = array(); + foreach ($params as $key => $val) { + + // is there a default value? + if ($val['default']) { + $tmp = $this->conf['format_paramd']; + } else { + $tmp = $this->conf['format_param']; + } + + // add the param elements + $tmp = str_replace('%type', $this->textEncode($val['type']), $tmp); + $tmp = str_replace('%descr', $this->textEncode($val['descr']), $tmp); + $tmp = str_replace('%default', $this->textEncode($val['default']), $tmp); + $list[] = $tmp; + } + + // insert params into output + $tmp = implode($this->conf['list_sep'], $list); + $output = str_replace('%params', $tmp, $output); + + // build the set of throws + $list = array(); + foreach ($throws as $key => $val) { + $tmp = $this->conf['format_throws']; + $tmp = str_replace('%type', $this->textEncode($val['type']), $tmp); + $tmp = str_replace('%descr', $this->textEncode($val['descr']), $tmp); + $list[] = $tmp; + } + + // insert throws into output + $tmp = implode($this->conf['list_sep'], $list); + $output = str_replace('%throws', $tmp, $output); + + // close the div and return the output + $output .= ''; + return "\n$output\n\n"; + } +} +?> diff --git a/Creole/Render/Xhtml/Heading.php b/Creole/Render/Xhtml/Heading.php new file mode 100644 index 0000000..ce3c50d --- /dev/null +++ b/Creole/Render/Xhtml/Heading.php @@ -0,0 +1,54 @@ + + * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 + * @version CVS: $Id: Heading.php 182 2008-09-14 15:56:00Z i.feelinglucky $ + * @link http://pear.php.net/package/Text_Wiki + */ + +/** + * This class renders headings in XHTML. + * + * @category Text + * @package Text_Wiki + * @author Paul M. Jones + * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 + * @version Release: @package_version@ + * @link http://pear.php.net/package/Text_Wiki + */ +class Text_Wiki_Render_Xhtml_Heading extends Text_Wiki_Render { + + var $conf = array( + 'css_h1' => null, + 'css_h2' => null, + 'css_h3' => null, + 'css_h4' => null, + 'css_h5' => null, + 'css_h6' => null + ); + + function token($options) + { + $collapse = null; + static $jsOutput = false; + // get nice variable names (id, type, level) + extract($options); + + switch($type) { + case 'start': + //$css = $this->formatConf(' class="%s"', "css_h$level"); + return ''; + //return ''; + case 'end': + return ''; + } + } +} +?> diff --git a/Creole/Render/Xhtml/Horiz.php b/Creole/Render/Xhtml/Horiz.php new file mode 100644 index 0000000..f8738e7 --- /dev/null +++ b/Creole/Render/Xhtml/Horiz.php @@ -0,0 +1,51 @@ + + * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 + * @version CVS: $Id: Horiz.php 182 2008-09-14 15:56:00Z i.feelinglucky $ + * @link http://pear.php.net/package/Text_Wiki + */ + +/** + * This class renders an horizontal bar in XHTML. + * + * @category Text + * @package Text_Wiki + * @author Paul M. Jones + * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 + * @version Release: @package_version@ + * @link http://pear.php.net/package/Text_Wiki + */ +class Text_Wiki_Render_Xhtml_Horiz extends Text_Wiki_Render { + + var $conf = array( + 'css' => null + ); + + /** + * + * Renders a token into text matching the requested format. + * + * @access public + * + * @param array $options The "options" portion of the token (second + * element). + * + * @return string The text rendered from the token options. + * + */ + + function token($options) + { + $css = $this->formatConf(' class="%s"', 'css'); + return "\n"; + } +} +?> diff --git a/Creole/Render/Xhtml/Html.php b/Creole/Render/Xhtml/Html.php new file mode 100644 index 0000000..bce7f87 --- /dev/null +++ b/Creole/Render/Xhtml/Html.php @@ -0,0 +1,47 @@ + + * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 + * @version CVS: $Id: Html.php 182 2008-09-14 15:56:00Z i.feelinglucky $ + * @link http://pear.php.net/package/Text_Wiki + */ + +/** + * This class renders preformated html in XHTML. + * + * @category Text + * @package Text_Wiki + * @author Paul M. Jones + * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 + * @version Release: @package_version@ + * @link http://pear.php.net/package/Text_Wiki + */ +class Text_Wiki_Render_Xhtml_Html extends Text_Wiki_Render { + + + /** + * + * Renders a token into text matching the requested format. + * + * @access public + * + * @param array $options The "options" portion of the token (second + * element). + * + * @return string The text rendered from the token options. + * + */ + + function token($options) + { + return $options['text']; + } +} +?> diff --git a/Creole/Render/Xhtml/Image.php b/Creole/Render/Xhtml/Image.php new file mode 100644 index 0000000..58edba6 --- /dev/null +++ b/Creole/Render/Xhtml/Image.php @@ -0,0 +1,183 @@ + + * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 + * @version CVS: $Id: Image.php 182 2008-09-14 15:56:00Z i.feelinglucky $ + * @link http://pear.php.net/package/Text_Wiki + */ + +/** + * This class inserts an image in XHTML. + * + * @category Text + * @package Text_Wiki + * @author Paul M. Jones + * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 + * @version Release: @package_version@ + * @link http://pear.php.net/package/Text_Wiki + */ +class Text_Wiki_Render_Xhtml_Image extends Text_Wiki_Render { + + var $conf = array( + 'base' => null, + 'url_base' => null, + 'css' => null, + 'css_link' => null + ); + + /** + * + * Renders a token into text matching the requested format. + * + * @access public + * + * @param array $options The "options" portion of the token (second + * element). + * + * @return string The text rendered from the token options. + * + */ + + function token($options) + { + // note the image source + $src = $options['src']; + + // is the source a local file or URL? + if (strpos($src, '://') === false) { + // the source refers to a local file. + // add the URL base to it. + $src = $this->getConf('base', '/') . $src; + } + + // stephane@metacites.net + // is the image clickable? + if (isset($options['attr']['link'])) { + // yes, the image is clickable. + // are we linked to a URL or a wiki page? + if (strpos($options['attr']['link'], '://')) { + // it's a URL, prefix the URL base + $href = $this->getConf('url_base') . $options['attr']['link']; + } else { + // it's a WikiPage; assume it exists. + /** @todo This needs to honor sprintf wikilinks (pmjones) */ + /** @todo This needs to honor interwiki (pmjones) */ + /** @todo This needs to honor freelinks (pmjones) */ + $href = $this->wiki->getRenderConf('xhtml', 'wikilink', 'view_url') . + $options['attr']['link']; + } + } else { + // image is not clickable. + $href = null; + } + // unset so it won't show up as an attribute + unset($options['attr']['link']); + + // stephane@metacites.net -- 25/07/2004 + // use CSS for all alignment + if (isset($options['attr']['align'])) { + // make sure we have a style attribute + if (!isset($options['attr']['style'])) { + // no style, set up a blank one + $options['attr']['style'] = ''; + } else { + // style exists, add a space + $options['attr']['style'] .= ' '; + } + + if ($options['attr']['align'] == 'center') { + // add a "center" style to the existing style. + $options['attr']['style'] .= + 'display: block; margin-left: auto; margin-right: auto;'; + } else { + // add a float style to the existing style + $options['attr']['style'] .= + 'float: '.$options['attr']['align']; + } + + // unset so it won't show up as an attribute + unset($options['attr']['align']); + } + + // stephane@metacites.net -- 25/07/2004 + // try to guess width and height + if (! isset($options['attr']['width']) && + ! isset($options['attr']['height'])) { + + // does the source refer to a local file or a URL? + if (strpos($src,'://')) { + // is a URL link + $imageFile = $src; + } elseif ($src[0] == '.') { + // reg at dav-muz dot net -- 2005-03-07 + // is a local file on relative path. + $imageFile = $src; # ...don't do anything because it's perfect! + } else { + // is a local file on absolute path. + $imageFile = $_SERVER['DOCUMENT_ROOT'] . $src; + } + + // attempt to get the image size + $imageSize = @getimagesize($imageFile); + + if (is_array($imageSize)) { + $options['attr']['width'] = $imageSize[0]; + $options['attr']['height'] = $imageSize[1]; + } + + } + + // start the HTML output + $output = 'formatConf(' class="%s"', 'css'); + + // add the attributes to the output, and be sure to + // track whether or not we find an "alt" attribute + $alt = false; + foreach ($options['attr'] as $key => $val) { + + // track the 'alt' attribute + if (strtolower($key) == 'alt') { + $alt = true; + } + + // the 'class' attribute overrides the CSS class conf + if (strtolower($key) == 'class') { + $css = null; + } + + $key = $this->textEncode($key); + $val = $this->textEncode($val); + $output .= " $key=\"$val\""; + } + + // always add an "alt" attribute per Stephane Solliec + if (! $alt) { + $alt = $this->textEncode(basename($options['src'])); + $output .= " alt=\"$alt\""; + } + + // end the image tag with the automatic CSS class (if any) + $output .= "$css />"; + + // was the image clickable? + if ($href) { + // yes, add the href and return + $href = $this->textEncode($href); + $css = $this->formatConf(' class="%s"', 'css_link'); + $output = "$output"; + } + + return $output; + } +} +?> diff --git a/Creole/Render/Xhtml/Include.php b/Creole/Render/Xhtml/Include.php new file mode 100644 index 0000000..3072770 --- /dev/null +++ b/Creole/Render/Xhtml/Include.php @@ -0,0 +1,32 @@ + + * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 + * @version CVS: $Id: Include.php 182 2008-09-14 15:56:00Z i.feelinglucky $ + * @link http://pear.php.net/package/Text_Wiki + */ + +/** + * This class renders included maekup in XHTML. (empty) + * + * @category Text + * @package Text_Wiki + * @author Paul M. Jones + * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 + * @version Release: @package_version@ + * @link http://pear.php.net/package/Text_Wiki + */ +class Text_Wiki_Render_Xhtml_Include extends Text_Wiki_Render { + function token() + { + return ''; + } +} +?> diff --git a/Creole/Render/Xhtml/Interwiki.php b/Creole/Render/Xhtml/Interwiki.php new file mode 100644 index 0000000..a71268c --- /dev/null +++ b/Creole/Render/Xhtml/Interwiki.php @@ -0,0 +1,103 @@ + + * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 + * @version CVS: $Id: Interwiki.php 182 2008-09-14 15:56:00Z i.feelinglucky $ + * @link http://pear.php.net/package/Text_Wiki + */ + +/** + * This class renders inter wikis links in XHTML. + * + * @category Text + * @package Text_Wiki + * @author Paul M. Jones + * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 + * @version Release: @package_version@ + * @link http://pear.php.net/package/Text_Wiki + */ +class Text_Wiki_Render_Xhtml_Interwiki extends Text_Wiki_Render { + + var $conf = array( + 'sites' => array( + 'MeatBall' => 'http://www.usemod.com/cgi-bin/mb.pl?%s', + 'Advogato' => 'http://advogato.org/%s', + 'Wiki' => 'http://c2.com/cgi/wiki?%s' + ), + 'target' => '_blank', + 'css' => null + ); + + + /** + * + * Renders a token into text matching the requested format. + * + * @access public + * + * @param array $options The "options" portion of the token (second + * element). + * + * @return string The text rendered from the token options. + * + */ + + function token($options) + { + $text = $options['text']; + if (isset($options['url'])) { + // calculated by the parser (e.g. Mediawiki) + $href = $options['url']; + } else { + $site = $options['site']; + // toggg 2006/02/05 page name must be url encoded (e.g. may contain spaces) + $page = $this->urlEncode($options['page']); + + if (isset($this->conf['sites'][$site])) { + $href = $this->conf['sites'][$site]; + } else { + return $text; + } + + // old form where page is at end, + // or new form with %s placeholder for sprintf()? + if (strpos($href, '%s') === false) { + // use the old form + $href = $href . $page; + } else { + // use the new form + $href = sprintf($href, $page); + } + } + + // allow for alternative targets + $target = $this->getConf('target'); + + // build base link + $css = $this->formatConf(' class="%s"', 'css'); + $text = $this->textEncode($text); + $output = "textEncode($target); + $output .= " onclick=\"window.open(this.href, '$target');"; + $output .= " return false;\""; + } + + $output .= ">$text"; + + return $output; + } +} +?> diff --git a/Creole/Render/Xhtml/Italic.php b/Creole/Render/Xhtml/Italic.php new file mode 100644 index 0000000..fbee3d8 --- /dev/null +++ b/Creole/Render/Xhtml/Italic.php @@ -0,0 +1,57 @@ + + * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 + * @version CVS: $Id: Italic.php 182 2008-09-14 15:56:00Z i.feelinglucky $ + * @link http://pear.php.net/package/Text_Wiki + */ + +/** + * This class renders italic text in XHTML. + * + * @category Text + * @package Text_Wiki + * @author Paul M. Jones + * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 + * @version Release: @package_version@ + * @link http://pear.php.net/package/Text_Wiki + */ +class Text_Wiki_Render_Xhtml_Italic extends Text_Wiki_Render { + + var $conf = array( + 'css' => null + ); + + /** + * + * Renders a token into text matching the requested format. + * + * @access public + * + * @param array $options The "options" portion of the token (second + * element). + * + * @return string The text rendered from the token options. + * + */ + + function token($options) + { + if ($options['type'] == 'start') { + $css = $this->formatConf(' class="%s"', 'css'); + return ""; + } + + if ($options['type'] == 'end') { + return ''; + } + } +} +?> diff --git a/Creole/Render/Xhtml/List.php b/Creole/Render/Xhtml/List.php new file mode 100644 index 0000000..cee17b7 --- /dev/null +++ b/Creole/Render/Xhtml/List.php @@ -0,0 +1,172 @@ + + * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 + * @version CVS: $Id: List.php 182 2008-09-14 15:56:00Z i.feelinglucky $ + * @link http://pear.php.net/package/Text_Wiki + */ + +/** + * This class renders bullet and ordered lists in XHTML. + * + * @category Text + * @package Text_Wiki + * @author Paul M. Jones + * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 + * @version Release: @package_version@ + * @link http://pear.php.net/package/Text_Wiki + */ +class Text_Wiki_Render_Xhtml_List extends Text_Wiki_Render { + + var $conf = array( + 'css_ol' => null, + 'css_ol_li' => null, + 'css_ul' => null, + 'css_ul_li' => null + ); + + /** + * + * Renders a token into text matching the requested format. + * + * This rendering method is syntactically and semantically compliant + * with XHTML 1.1 in that sub-lists are part of the previous list item. + * + * @access public + * + * @param array $options The "options" portion of the token (second + * element). + * + * @return string The text rendered from the token options. + * + */ + + function token($options) + { + // make nice variables (type, level, count) + extract($options); + + // set up indenting so that the results look nice; we do this + // in two steps to avoid str_pad mathematics. ;-) + $pad = str_pad('', $level, "\t"); + $pad = str_replace("\t", ' ', $pad); + + switch ($type) { + + case 'bullet_list_start': + + // build the base HTML + $css = $this->formatConf(' class="%s"', 'css_ul'); + $html = ""; + + /* + // if this is the opening block for the list, + // put an extra newline in front of it so the + // output looks nice. + if ($level == 0) { + $html = "\n$html"; + } + */ + + // done! + return $html; + break; + + case 'bullet_list_end': + + // build the base HTML + $html = "\n$pad"; + + // if this is the closing block for the list, + // put extra newlines after it so the output + // looks nice. + if ($level == 0) { + $html .= "\n\n"; + } + + // done! + return $html; + break; + + case 'number_list_start': + if (isset($format)) { + $format = ' type="' . $format . '"'; + } else { + $format = ''; + } + // build the base HTML + $css = $this->formatConf(' class="%s"', 'css_ol'); + $html = ""; + + /* + // if this is the opening block for the list, + // put an extra newline in front of it so the + // output looks nice. + if ($level == 0) { + $html = "\n$html"; + } + */ + + // done! + return $html; + break; + + case 'number_list_end': + + // build the base HTML + $html = "\n$pad"; + + // if this is the closing block for the list, + // put extra newlines after it so the output + // looks nice. + if ($level == 0) { + $html .= "\n\n"; + } + + // done! + return $html; + break; + + case 'bullet_item_start': + case 'number_item_start': + + // pick the proper CSS class + if ($type == 'bullet_item_start') { + $css = $this->formatConf(' class="%s"', 'css_ul_li'); + } else { + $css = $this->formatConf(' class="%s"', 'css_ol_li'); + } + + // build the base HTML + $html = "\n$pad"; + + // for the very first item in the list, do nothing. + // but for additional items, be sure to close the + // previous item. + if ($count > 0) { + $html = "$html"; + } + + // done! + return $html; + break; + + case 'bullet_item_end': + case 'number_item_end': + default: + // ignore item endings and all other types. + // item endings are taken care of by the other types + // depending on their place in the list. + return ''; + break; + } + } +} +?> diff --git a/Creole/Render/Xhtml/Newline.php b/Creole/Render/Xhtml/Newline.php new file mode 100644 index 0000000..c08b7ad --- /dev/null +++ b/Creole/Render/Xhtml/Newline.php @@ -0,0 +1,35 @@ + + * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 + * @version CVS: $Id: Newline.php 182 2008-09-14 15:56:00Z i.feelinglucky $ + * @link http://pear.php.net/package/Text_Wiki + */ + +/** + * This class renders new lines in XHTML. + * + * @category Text + * @package Text_Wiki + * @author Paul M. Jones + * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 + * @version Release: @package_version@ + * @link http://pear.php.net/package/Text_Wiki + */ +class Text_Wiki_Render_Xhtml_Newline extends Text_Wiki_Render { + + + function token($options) + { + return "
\n"; + } +} + +?> diff --git a/Creole/Render/Xhtml/Page.php b/Creole/Render/Xhtml/Page.php new file mode 100644 index 0000000..912f360 --- /dev/null +++ b/Creole/Render/Xhtml/Page.php @@ -0,0 +1,46 @@ + + * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 + * @version CVS: $Id: Page.php 182 2008-09-14 15:56:00Z i.feelinglucky $ + * @link http://pear.php.net/package/Text_Wiki + */ + +/** + * This class renders page markers in XHTML. + * + * @category Text + * @package Text_Wiki + * @author Paul M. Jones + * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 + * @version Release: @package_version@ + * @link http://pear.php.net/package/Text_Wiki + */ +class Text_Wiki_Render_Xhtml_Page extends Text_Wiki_Render { + + /** + * + * Renders a token into text matching the requested format. + * + * @access public + * + * @param array $options The "options" portion of the token (second + * element). + * + * @return string The text rendered from the token options. + * + */ + + function token($options) + { + return 'PAGE MARKER HERE*&^%$#^$%*PAGEMARKERHERE'; + } +} +?> diff --git a/Creole/Render/Xhtml/Paragraph.php b/Creole/Render/Xhtml/Paragraph.php new file mode 100644 index 0000000..07b2250 --- /dev/null +++ b/Creole/Render/Xhtml/Paragraph.php @@ -0,0 +1,59 @@ + + * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 + * @version CVS: $Id: Paragraph.php 182 2008-09-14 15:56:00Z i.feelinglucky $ + * @link http://pear.php.net/package/Text_Wiki + */ + +/** + * This class renders paragraphs in XHTML. + * + * @category Text + * @package Text_Wiki + * @author Paul M. Jones + * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 + * @version Release: @package_version@ + * @link http://pear.php.net/package/Text_Wiki + */ +class Text_Wiki_Render_Xhtml_Paragraph extends Text_Wiki_Render { + + var $conf = array( + 'css' => null + ); + + /** + * + * Renders a token into text matching the requested format. + * + * @access public + * + * @param array $options The "options" portion of the token (second + * element). + * + * @return string The text rendered from the token options. + * + */ + + function token($options) + { + extract($options); //type + + if ($type == 'start') { + $css = $this->formatConf(' class="%s"', 'css'); + return ""; + } + + if ($type == 'end') { + return "

\n\n"; + } + } +} +?> diff --git a/Creole/Render/Xhtml/Phplookup.php b/Creole/Render/Xhtml/Phplookup.php new file mode 100644 index 0000000..2e598a8 --- /dev/null +++ b/Creole/Render/Xhtml/Phplookup.php @@ -0,0 +1,81 @@ + + * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 + * @version CVS: $Id: Phplookup.php 182 2008-09-14 15:56:00Z i.feelinglucky $ + * @link http://pear.php.net/package/Text_Wiki + */ + +/** + * This class renders a link to php functions description in XHTML. + * + * @category Text + * @package Text_Wiki + * @author Paul M. Jones + * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 + * @version Release: @package_version@ + * @link http://pear.php.net/package/Text_Wiki + */ +class Text_Wiki_Render_Xhtml_Phplookup extends Text_Wiki_Render { + + var $conf = array( + 'target' => '_blank', + 'css' => null + ); + + + /** + * + * Renders a token into text matching the requested format. + * + * @access public + * + * @param array $options The "options" portion of the token (second + * element). + * + * @return string The text rendered from the token options. + * + */ + + function token($options) + { + $text = trim($options['text']); + $css = $this->formatConf(' class="%s"', 'css'); + + // start the html + $output = "getConf('target', ''); + if ($target && $target != '_self') { + // use a "popup" window. this is XHTML compliant, suggested by + // Aaron Kalin. uses the $target as the new window name. + $target = $this->textEncode($target); + $output .= " onclick=\"window.open(this.href, '$target');"; + $output .= " return false;\""; + } + + // take off the final parens for functions + if (substr($text, -2) == '()') { + $q = substr($text, 0, -2); + } else { + $q = $text; + } + + // toggg 2006/02/05 page name must be url encoded (e.g. may contain spaces) + $q = $this->urlEncode($q); + $text = $this->textEncode($text); + + // finish and return + $output .= " href=\"http://php.net/$q\">$text"; + return $output; + } +} +?> diff --git a/Creole/Render/Xhtml/Plugin.php b/Creole/Render/Xhtml/Plugin.php new file mode 100644 index 0000000..3419c1e --- /dev/null +++ b/Creole/Render/Xhtml/Plugin.php @@ -0,0 +1,47 @@ + + * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 + * @version CVS: $Id: Plugin.php 182 2008-09-14 15:56:00Z i.feelinglucky $ + * @link http://pear.php.net/package/Text_Wiki + */ + +/** + * This class renders wiki plugins in XHTML. (empty) + * + * @category Text + * @package Text_Wiki + * @author Paul M. Jones + * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 + * @version Release: @package_version@ + * @link http://pear.php.net/package/Text_Wiki + */ +class Text_Wiki_Render_Xhtml_Plugin extends Text_Wiki_Render { + + /** + * + * Renders a token into text matching the requested format. + * Plugins produce wiki markup so are processed by parsing, no tokens produced + * + * @access public + * + * @param array $options The "options" portion of the token (second + * element). + * + * @return string The text rendered from the token options. + * + */ + + function token($options) + { + return ''; + } +} +?> diff --git a/Creole/Render/Xhtml/Prefilter.php b/Creole/Render/Xhtml/Prefilter.php new file mode 100644 index 0000000..64d87dc --- /dev/null +++ b/Creole/Render/Xhtml/Prefilter.php @@ -0,0 +1,34 @@ + + * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 + * @version CVS: $Id: Prefilter.php 182 2008-09-14 15:56:00Z i.feelinglucky $ + * @link http://pear.php.net/package/Text_Wiki + */ + +/** + * This class implements a Text_Wiki_Render_Xhtml to "pre-filter" source text so + * that line endings are consistently \n, lines ending in a backslash \ + * are concatenated with the next line, and tabs are converted to spaces. + * + * @category Text + * @package Text_Wiki + * @author Paul M. Jones + * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 + * @version Release: @package_version@ + * @link http://pear.php.net/package/Text_Wiki + */ +class Text_Wiki_Render_Xhtml_Prefilter extends Text_Wiki_Render { + function token() + { + return ''; + } +} +?> diff --git a/Creole/Render/Xhtml/Preformatted.php b/Creole/Render/Xhtml/Preformatted.php new file mode 100644 index 0000000..ea079fb --- /dev/null +++ b/Creole/Render/Xhtml/Preformatted.php @@ -0,0 +1,47 @@ + + * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 + * @version CVS: $Id: Preformatted.php 182 2008-09-14 15:56:00Z i.feelinglucky $ + * @link http://pear.php.net/package/Text_Wiki + */ + +/** + * This class renders preformated text in XHTML. + * + * @category Text + * @package Text_Wiki + * @author Paul M. Jones + * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 + * @version Release: @package_version@ + * @link http://pear.php.net/package/Text_Wiki + */ +class Text_Wiki_Render_Xhtml_Preformatted extends Text_Wiki_Render { + + /** + * + * Renders a token into text matching the requested format. + * + * @access public + * + * @param array $options The "options" portion of the token (second + * element). + * + * @return string The text rendered from the token options. + * + */ + + function token($options) + { + $text = $this->textEncode($options['text']); + return '
'.$text.'
'; + } +} +?> diff --git a/Creole/Render/Xhtml/Raw.php b/Creole/Render/Xhtml/Raw.php new file mode 100644 index 0000000..dbbdc19 --- /dev/null +++ b/Creole/Render/Xhtml/Raw.php @@ -0,0 +1,46 @@ + + * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 + * @version CVS: $Id: Raw.php 182 2008-09-14 15:56:00Z i.feelinglucky $ + * @link http://pear.php.net/package/Text_Wiki + */ + +/** + * This class renders not processed blocks in XHTML. + * + * @category Text + * @package Text_Wiki + * @author Paul M. Jones + * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 + * @version Release: @package_version@ + * @link http://pear.php.net/package/Text_Wiki + */ +class Text_Wiki_Render_Xhtml_Raw extends Text_Wiki_Render { + + /** + * + * Renders a token into text matching the requested format. + * + * @access public + * + * @param array $options The "options" portion of the token (second + * element). + * + * @return string The text rendered from the token options. + * + */ + + function token($options) + { + return $this->textEncode($options['text']); + } +} +?> diff --git a/Creole/Render/Xhtml/Revise.php b/Creole/Render/Xhtml/Revise.php new file mode 100644 index 0000000..bad6f02 --- /dev/null +++ b/Creole/Render/Xhtml/Revise.php @@ -0,0 +1,68 @@ + + * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 + * @version CVS: $Id: Revise.php 182 2008-09-14 15:56:00Z i.feelinglucky $ + * @link http://pear.php.net/package/Text_Wiki + */ + +/** + * This class renders revision marks in XHTML. + * + * @category Text + * @package Text_Wiki + * @author Paul M. Jones + * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 + * @version Release: @package_version@ + * @link http://pear.php.net/package/Text_Wiki + */ +class Text_Wiki_Render_Xhtml_Revise extends Text_Wiki_Render { + + var $conf = array( + 'css_ins' => null, + 'css_del' => null + ); + + + /** + * + * Renders a token into text matching the requested format. + * + * @access public + * + * @param array $options The "options" portion of the token (second + * element). + * + * @return string The text rendered from the token options. + * + */ + + function token($options) + { + if ($options['type'] == 'del_start') { + $css = $this->formatConf(' class="%s"', 'css_del'); + return ""; + } + + if ($options['type'] == 'del_end') { + return ""; + } + + if ($options['type'] == 'ins_start') { + $css = $this->formatConf(' class="%s"', 'css_ins'); + return ""; + } + + if ($options['type'] == 'ins_end') { + return ""; + } + } +} +?> diff --git a/Creole/Render/Xhtml/Smiley.php b/Creole/Render/Xhtml/Smiley.php new file mode 100644 index 0000000..0e9a0ad --- /dev/null +++ b/Creole/Render/Xhtml/Smiley.php @@ -0,0 +1,74 @@ + + * @copyright 2005 bertrand Gugger + * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 + * @version CVS: $Id: Smiley.php 182 2008-09-14 15:56:00Z i.feelinglucky $ + * @link http://pear.php.net/package/Text_Wiki + */ + +/** + * Smiley rule Xhtml render class + * + * @category Text + * @package Text_Wiki + * @author Bertrand Gugger + * @copyright 2005 bertrand Gugger + * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 + * @version Release: @package_version@ + * @link http://pear.php.net/package/Text_Wiki + * @see Text_Wiki::Text_Wiki_Render() + */ +class Text_Wiki_Render_Xhtml_Smiley extends Text_Wiki_Render { + + /** + * Configuration keys for this rule + * 'prefix' => the path to smileys images inclusive file name prefix, + * starts with '/' ==> abolute reference + * if no file names prefix but some folder, terminates with '/' + * 'extension' => the file extension (inclusive '.'), e.g. : + * if prefix 'smileys/icon_' and extension '.gif' + * ':)' whose name is 'smile' will give relative file 'smileys/icon_smile.gif' + * if prefix '/image/smileys/' and extension '.png': absolute '/image/smileys/smile.gif' + * 'css' => optional style applied to smileys + * + * @access public + * @var array 'config-key' => mixed config-value + */ + var $conf = array( + 'prefix' => 'images/smiles/icon_', + 'extension' => '.gif', + 'css' => null + ); + + /** + * Renders a token into text matching the requested format. + * process the Smileys + * + * @access public + * @param array $options The "options" portion of the token (second element). + * @return string The text rendered from the token options. + */ + function token($options) + { + $imageFile = $this->getConf('prefix') . $options['name'] . $this->getConf('extension'); + + // attempt to get the image size + $imageSize = @getimagesize($imageFile); + + // return the HTML output + return '' . $options['desc'] . 'formatConf(' class="%s"', 'css') . ' />'; + } +} +?> diff --git a/Creole/Render/Xhtml/Specialchar.php b/Creole/Render/Xhtml/Specialchar.php new file mode 100644 index 0000000..36221b4 --- /dev/null +++ b/Creole/Render/Xhtml/Specialchar.php @@ -0,0 +1,52 @@ + + * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 + * @version CVS: $Id: Specialchar.php 182 2008-09-14 15:56:00Z i.feelinglucky $ + * @link http://pear.php.net/package/Text_Wiki + */ + +/** + * This class renders special characters in XHTML. + * + * @category Text + * @package Text_Wiki + * @author Paul M. Jones + * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 + * @version Release: @package_version@ + * @link http://pear.php.net/package/Text_Wiki + */ +class Text_Wiki_Render_Xhtml_SpecialChar extends Text_Wiki_Render { + + var $types = array('~bs~' => '\', + '~hs~' => ' ', + '~amp~' => '&', + '~ldq~' => '“', + '~rdq~' => '”', + '~lsq~' => '‘', + '~rsq~' => '’', + '~c~' => '©', + '~--~' => '—', + '" -- "' => '—', + '" -- "' => '—', + '~lt~' => '<', + '~gt~' => '>'); + + function token($options) + { + if (isset($this->types[$options['char']])) { + return $this->types[$options['char']]; + } else { + return '&#'.substr($options['char'], 1, -1).';'; + } + } +} + +?> diff --git a/Creole/Render/Xhtml/Strong.php b/Creole/Render/Xhtml/Strong.php new file mode 100644 index 0000000..9bc0b95 --- /dev/null +++ b/Creole/Render/Xhtml/Strong.php @@ -0,0 +1,58 @@ + + * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 + * @version CVS: $Id: Strong.php 182 2008-09-14 15:56:00Z i.feelinglucky $ + * @link http://pear.php.net/package/Text_Wiki + */ + +/** + * This class renders text marked as strong in XHTML. + * + * @category Text + * @package Text_Wiki + * @author Paul M. Jones + * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 + * @version Release: @package_version@ + * @link http://pear.php.net/package/Text_Wiki + */ +class Text_Wiki_Render_Xhtml_Strong extends Text_Wiki_Render { + + + var $conf = array( + 'css' => null + ); + + /** + * + * Renders a token into text matching the requested format. + * + * @access public + * + * @param array $options The "options" portion of the token (second + * element). + * + * @return string The text rendered from the token options. + * + */ + + function token($options) + { + if ($options['type'] == 'start') { + $css = $this->formatConf(' class="%s"', 'css'); + return ""; + } + + if ($options['type'] == 'end') { + return ''; + } + } +} +?> diff --git a/Creole/Render/Xhtml/Subscript.php b/Creole/Render/Xhtml/Subscript.php new file mode 100644 index 0000000..efe6192 --- /dev/null +++ b/Creole/Render/Xhtml/Subscript.php @@ -0,0 +1,57 @@ + + * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 + * @version CVS: $Id: Subscript.php 182 2008-09-14 15:56:00Z i.feelinglucky $ + * @link http://pear.php.net/package/Text_Wiki + */ + +/** + * This class renders subscript text in XHTML. + * + * @category Text + * @package Text_Wiki + * @author Paul M. Jones + * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 + * @version Release: @package_version@ + * @link http://pear.php.net/package/Text_Wiki + */ +class Text_Wiki_Render_Xhtml_Subscript extends Text_Wiki_Render { + + var $conf = array( + 'css' => null + ); + + /** + * + * Renders a token into text matching the requested format. + * + * @access public + * + * @param array $options The "options" portion of the token (second + * element). + * + * @return string The text rendered from the token options. + * + */ + + function token($options) + { + if ($options['type'] == 'start') { + $css = $this->formatConf(' class="%s"', 'css'); + return ""; + } + + if ($options['type'] == 'end') { + return ''; + } + } +} +?> diff --git a/Creole/Render/Xhtml/Superscript.php b/Creole/Render/Xhtml/Superscript.php new file mode 100644 index 0000000..a6c2b95 --- /dev/null +++ b/Creole/Render/Xhtml/Superscript.php @@ -0,0 +1,57 @@ + + * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 + * @version CVS: $Id: Superscript.php 182 2008-09-14 15:56:00Z i.feelinglucky $ + * @link http://pear.php.net/package/Text_Wiki + */ + +/** + * This class renders superscript text in XHTML. + * + * @category Text + * @package Text_Wiki + * @author Paul M. Jones + * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 + * @version Release: @package_version@ + * @link http://pear.php.net/package/Text_Wiki + */ +class Text_Wiki_Render_Xhtml_Superscript extends Text_Wiki_Render { + + var $conf = array( + 'css' => null + ); + + /** + * + * Renders a token into text matching the requested format. + * + * @access public + * + * @param array $options The "options" portion of the token (second + * element). + * + * @return string The text rendered from the token options. + * + */ + + function token($options) + { + if ($options['type'] == 'start') { + $css = $this->formatConf(' class="%s"', 'css'); + return ""; + } + + if ($options['type'] == 'end') { + return ''; + } + } +} +?> diff --git a/Creole/Render/Xhtml/Table.php b/Creole/Render/Xhtml/Table.php new file mode 100644 index 0000000..66fd867 --- /dev/null +++ b/Creole/Render/Xhtml/Table.php @@ -0,0 +1,140 @@ + + * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 + * @version CVS: $Id: Table.php 182 2008-09-14 15:56:00Z i.feelinglucky $ + * @link http://pear.php.net/package/Text_Wiki + */ + +/** + * This class renders tables in XHTML. + * + * @category Text + * @package Text_Wiki + * @author Paul M. Jones + * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 + * @version Release: @package_version@ + * @link http://pear.php.net/package/Text_Wiki + */ +class Text_Wiki_Render_Xhtml_Table extends Text_Wiki_Render { + + var $conf = array( + 'css_table' => null, + 'css_caption' => null, + 'css_tr' => null, + 'css_th' => null, + 'css_td' => null + ); + + + /** + * + * Renders a token into text matching the requested format. + * + * @access public + * + * @param array $options The "options" portion of the token (second + * element). + * + * @return string The text rendered from the token options. + * + */ + + function token($options) + { + // make nice variable names (type, attr, span) + $span = $rowspan = 1; + extract($options); + + // free format + $format = isset($format) ? ' '. $format : ''; + + $pad = ' '; + + switch ($type) { + + case 'table_start': + $css = $this->formatConf(' class="%s"', 'css_table'); + return "\n\n\n"; + break; + + case 'table_end': + return "\n\n"; + break; + + case 'caption_start': + $css = $this->formatConf(' class="%s"', 'css_caption'); + return "\n"; + break; + + case 'caption_end': + return "\n"; + break; + + case 'row_start': + $css = $this->formatConf(' class="%s"', 'css_tr'); + return "$pad\n"; + break; + + case 'row_end': + return "$pad\n"; + break; + + case 'cell_start': + + // base html + $html = $pad . $pad; + + // is this a TH or TD cell? + if ($attr == 'header') { + // start a header cell + $css = $this->formatConf(' class="%s"', 'css_th'); + $html .= "formatConf(' class="%s"', 'css_td'); + $html .= " 1) { + $html .= " colspan=\"$span\""; + } + + // add the row span + if ($rowspan > 1) { + $html .= " rowspan=\"$rowspan\""; + } + + // add alignment + if ($attr != 'header' && $attr != '') { + $html .= " style=\"text-align: $attr;\""; + } + + // done! + $html .= "$format>"; + return $html; + break; + + case 'cell_end': + if ($attr == 'header') { + return "\n"; + } else { + return "\n"; + } + break; + + default: + return ''; + + } + } +} +?> diff --git a/Creole/Render/Xhtml/Tighten.php b/Creole/Render/Xhtml/Tighten.php new file mode 100644 index 0000000..128764f --- /dev/null +++ b/Creole/Render/Xhtml/Tighten.php @@ -0,0 +1,34 @@ + + * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 + * @version CVS: $Id: Tighten.php 182 2008-09-14 15:56:00Z i.feelinglucky $ + * @link http://pear.php.net/package/Text_Wiki + */ + +/** + * This class makes the tightening in XHTML. (empty) + * + * @category Text + * @package Text_Wiki + * @author Paul M. Jones + * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 + * @version Release: @package_version@ + * @link http://pear.php.net/package/Text_Wiki + */ +class Text_Wiki_Render_Xhtml_Tighten extends Text_Wiki_Render { + + + function token() + { + return ''; + } +} +?> diff --git a/Creole/Render/Xhtml/Titlebar.php b/Creole/Render/Xhtml/Titlebar.php new file mode 100644 index 0000000..7a1be0a --- /dev/null +++ b/Creole/Render/Xhtml/Titlebar.php @@ -0,0 +1,57 @@ + + * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 + * @version CVS: $Id: Titlebar.php 182 2008-09-14 15:56:00Z i.feelinglucky $ + * @link http://pear.php.net/package/Text_Wiki + */ + +/** + * This class renders a title bar in XHTML. + * + * @category Text + * @package Text_Wiki + * @author Paul M. Jones + * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 + * @version Release: @package_version@ + * @link http://pear.php.net/package/Text_Wiki + */ +class Text_Wiki_Render_Xhtml_Titlebar extends Text_Wiki_Render { + + var $conf = array( + 'css' => 'titlebar' + ); + + /** + * + * Renders a token into text matching the requested format. + * + * @access public + * + * @param array $options The "options" portion of the token (second + * element). + * + * @return string The text rendered from the token options. + * + */ + + function token($options) + { + if ($options['type'] == 'start') { + $css = $this->formatConf(' class="%s"', 'css'); + return ""; + } + + if ($options['type'] == 'end') { + return ''; + } + } +} +?> diff --git a/Creole/Render/Xhtml/Toc.php b/Creole/Render/Xhtml/Toc.php new file mode 100644 index 0000000..c4a52cf --- /dev/null +++ b/Creole/Render/Xhtml/Toc.php @@ -0,0 +1,115 @@ + + * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 + * @version CVS: $Id: Toc.php 182 2008-09-14 15:56:00Z i.feelinglucky $ + * @link http://pear.php.net/package/Text_Wiki + */ + +/** + * This class inserts a table of content in XHTML. + * + * @category Text + * @package Text_Wiki + * @author Paul M. Jones + * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 + * @version Release: @package_version@ + * @link http://pear.php.net/package/Text_Wiki + */ +class Text_Wiki_Render_Xhtml_Toc extends Text_Wiki_Render { + + var $conf = array( + 'css_list' => null, + 'css_item' => null, + 'title' => 'Table of Contents', + 'div_id' => 'toc', + 'collapse' => true + ); + + var $min = 2; + + /** + * + * Renders a token into text matching the requested format. + * + * @access public + * + * @param array $options The "options" portion of the token (second + * element). + * + * @return string The text rendered from the token options. + * + */ + + function token($options) + { + // type, id, level, count, attr + extract($options); + + switch ($type) { + + case 'list_start': + + $css = $this->getConf('css_list'); + $html = ''; + + // collapse div within a table? + if ($this->getConf('collapse')) { + $html .= ''; + $html .= "
\n"; + } + + // add the div, class, and id + $html .= 'getConf('div_id'); + if ($div_id) { + $html .= " id=\"$div_id\""; + } + + // add the title, and done + $html .= '>'; + $html .= $this->getConf('title'); + return $html; + break; + + case 'list_end': + if ($this->getConf('collapse')) { + return "\n\n
\n\n"; + } else { + return "\n\n\n"; + } + break; + + case 'item_start': + $html = "\n\tgetConf('css_item'); + if ($css) { + $html .= " class=\"$css\""; + } + + $pad = ($level - $this->min); + $html .= " style=\"margin-left: {$pad}em;\">"; + + $html .= ""; + return $html; + break; + + case 'item_end': + return ""; + break; + } + } +} +?> diff --git a/Creole/Render/Xhtml/Tt.php b/Creole/Render/Xhtml/Tt.php new file mode 100644 index 0000000..57af2bd --- /dev/null +++ b/Creole/Render/Xhtml/Tt.php @@ -0,0 +1,58 @@ + + * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 + * @version CVS: $Id: Tt.php 182 2008-09-14 15:56:00Z i.feelinglucky $ + * @link http://pear.php.net/package/Text_Wiki + */ + +/** + * This class renders monospaced text in XHTML. + * + * @category Text + * @package Text_Wiki + * @author Paul M. Jones + * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 + * @version Release: @package_version@ + * @link http://pear.php.net/package/Text_Wiki + */ +class Text_Wiki_Render_Xhtml_Tt extends Text_Wiki_Render { + + + var $conf = array( + 'css' => null + ); + + /** + * + * Renders a token into text matching the requested format. + * + * @access public + * + * @param array $options The "options" portion of the token (second + * element). + * + * @return string The text rendered from the token options. + * + */ + + function token($options) + { + if ($options['type'] == 'start') { + $css = $this->formatConf(' class="%s"', 'css'); + return ""; + } + + if ($options['type'] == 'end') { + return ''; + } + } +} +?> diff --git a/Creole/Render/Xhtml/Underline.php b/Creole/Render/Xhtml/Underline.php new file mode 100644 index 0000000..96ccd7a --- /dev/null +++ b/Creole/Render/Xhtml/Underline.php @@ -0,0 +1,58 @@ + + * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 + * @version CVS: $Id: Underline.php 182 2008-09-14 15:56:00Z i.feelinglucky $ + * @link http://pear.php.net/package/Text_Wiki + */ + +/** + * This class renders underlined text in XHTML. + * + * @category Text + * @package Text_Wiki + * @author Paul M. Jones + * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 + * @version Release: @package_version@ + * @link http://pear.php.net/package/Text_Wiki + */ +class Text_Wiki_Render_Xhtml_Underline extends Text_Wiki_Render { + + var $conf = array( + 'css' => null + ); + + /** + * + * Renders a token into text matching the requested format. + * + * @access public + * + * @param array $options The "options" portion of the token (second + * element). + * + * @return string The text rendered from the token options. + * + */ + function token($options) + { + if ($options['type'] == 'start') { + //$css = $this->formatConf(' class="%s"', 'css'); + //return ""; + //return ""; + return ""; + } + + if ($options['type'] == 'end') { + return ''; + } + } +} +?> diff --git a/Creole/Render/Xhtml/Url.php b/Creole/Render/Xhtml/Url.php new file mode 100644 index 0000000..03eec30 --- /dev/null +++ b/Creole/Render/Xhtml/Url.php @@ -0,0 +1,138 @@ + + * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 + * @version CVS: $Id: Url.php 182 2008-09-14 15:56:00Z i.feelinglucky $ + * @link http://pear.php.net/package/Text_Wiki + */ + +/** + * This class renders URL links in XHTML. + * + * @category Text + * @package Text_Wiki + * @author Paul M. Jones + * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 + * @version Release: @package_version@ + * @link http://pear.php.net/package/Text_Wiki + */ +class Text_Wiki_Render_Xhtml_Url extends Text_Wiki_Render { + var $conf = array( + //'target' => '_blank', + 'images' => true, + 'img_ext' => array('jpg', 'jpeg', 'gif', 'png'), + 'css_inline' => null, + //'css_footnote' => null, + //'css_descr' => null, + //'css_img' => null + ); + + /** + * + * Renders a token into text matching the requested format. + * + * @access public + * + * @param array $options The "options" portion of the token (second + * element). + * + * @return string The text rendered from the token options. + * + */ + + function token($options) + { + // create local variables from the options array (text, + // href, type) + extract($options); + + // find the rightmost dot and determine the filename + // extension. + $pos = strrpos($href, '.'); + $ext = strtolower(substr($href, $pos + 1)); + $href = $this->textEncode($href); + + // does the filename extension indicate an image file? + if ($this->getConf('images') && + in_array($ext, $this->getConf('img_ext', array()))) { + + // create alt text for the image + if (! isset($text) || $text == '') { + $text = basename($href); + $text = $this->textEncode($text); + } + + // generate an image tag + // $css = $this->formatConf(' class="%s"', 'css_img'); + $start = "\"$text\""; //""; + $end = ""; + $text = ""; // cancel by feelinglucky + + } else { + + // should we build a target clause? + if ($href{0} == '#' || + strtolower(substr($href, 0, 7)) == 'mailto:') { + // targets not allowed for on-page anchors + // and mailto: links. + $target = ''; + } else { + // allow targets on non-anchor non-mailto links + $target = $this->getConf('target'); + } + + // generate a regular link (not an image) + $text = $this->textEncode($text); + //$css = $this->formatConf(' class="%s"', "css_$type"); + $start = "textEncode($target); + $start .= " onclick=\"window.open(this.href, '$target');"; + $start .= " return false;\""; + } + */ + + /* + if (isset($name)) { + $start .= " id=\"$name\""; + } + */ + + // finish up output + $start .= ">"; + $end = ""; + + // make numbered references look like footnotes when no + // CSS class specified, make them superscript by default + /* + if ($type == 'footnote' && ! $css) { + $start = '' . $start; + $end = $end . ''; + } + */ + } + + if ($options['type'] == 'start') { + $output = $start; + } else if ($options['type'] == 'end') { + $output = $end; + } else { + $output = $start . $text . $end; + } + + return $output; + } +} +?> diff --git a/Creole/Render/Xhtml/Wikilink.php b/Creole/Render/Xhtml/Wikilink.php new file mode 100644 index 0000000..c140c98 --- /dev/null +++ b/Creole/Render/Xhtml/Wikilink.php @@ -0,0 +1,177 @@ + + * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 + * @version CVS: $Id: Wikilink.php 182 2008-09-14 15:56:00Z i.feelinglucky $ + * @link http://pear.php.net/package/Text_Wiki + */ + +/** + * This class renders wiki links in XHTML. + * + * @category Text + * @package Text_Wiki + * @author Paul M. Jones + * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 + * @version Release: @package_version@ + * @link http://pear.php.net/package/Text_Wiki + */ +class Text_Wiki_Render_Xhtml_Wikilink extends Text_Wiki_Render { + + var $conf = array( + 'pages' => array(), // set to null or false to turn off page checks + 'view_url' => 'http://example.com/index.php?page=%s', + 'new_url' => 'http://example.com/new.php?page=%s', + 'new_text' => '?', + 'new_text_pos' => 'after', // 'before', 'after', or null/false + 'css' => null, + 'css_new' => null, + 'exists_callback' => null // call_user_func() callback + ); + + + /** + * + * Renders a token into XHTML. + * + * @access public + * + * @param array $options The "options" portion of the token (second + * element). + * + * @return string The text rendered from the token options. + * + */ + + function token($options) + { + // make nice variable names (page, anchor, text) + extract($options); + + // is there a "page existence" callback? + // we need to access it directly instead of through + // getConf() because we'll need a reference (for + // object instance method callbacks). + if (isset($this->conf['exists_callback'])) { + $callback =& $this->conf['exists_callback']; + } else { + $callback = false; + } + + if ($callback) { + // use the callback function + $exists = call_user_func($callback, $page); + } else { + // no callback, go to the naive page array. + $list = $this->getConf('pages'); + if (is_array($list)) { + // yes, check against the page list + $exists = in_array($page, $list); + } else { + // no, assume it exists + $exists = true; + } + } + + $anchor = '#'.$this->urlEncode(substr($anchor, 1)); + + // does the page exist? + if ($exists) { + + // PAGE EXISTS. + + // link to the page view, but we have to build + // the HREF. we support both the old form where + // the page always comes at the end, and the new + // form that uses %s for sprintf() + $href = $this->getConf('view_url'); + + if (strpos($href, '%s') === false) { + // use the old form (page-at-end) + $href = $href . $this->urlEncode($page) . $anchor; + } else { + // use the new form (sprintf format string) + $href = sprintf($href, $this->urlEncode($page)) . $anchor; + } + + // get the CSS class and generate output + $css = ' class="'.$this->textEncode($this->getConf('css')).'"'; + + $start = ''; + $end = ''; + } else { + + // PAGE DOES NOT EXIST. + + // link to a create-page url, but only if new_url is set + $href = $this->getConf('new_url', null); + + // set the proper HREF + if (! $href || trim($href) == '') { + + // no useful href, return the text as it is + //TODO: This is no longer used, need to look closer into this branch + $output = $text; + + } else { + + // yes, link to the new-page href, but we have to build + // it. we support both the old form where + // the page always comes at the end, and the new + // form that uses sprintf() + if (strpos($href, '%s') === false) { + // use the old form + $href = $href . $this->urlEncode($page); + } else { + // use the new form + $href = sprintf($href, $this->urlEncode($page)); + } + } + + // get the appropriate CSS class and new-link text + $css = ' class="'.$this->textEncode($this->getConf('css_new')).'"'; + $new = $this->getConf('new_text'); + + // what kind of linking are we doing? + $pos = $this->getConf('new_text_pos'); + if (! $pos || ! $new) { + // no position (or no new_text), use css only on the page name + + $start = ''; + $end = ''; + } elseif ($pos == 'before') { + // use the new_text BEFORE the page name + $start = ''.$this->textEncode($new).''; + $end = ''; + } else { + // default, use the new_text link AFTER the page name + $start = ''; + $end = ''.$this->textEncode($new).''; + } + } + if (!strlen($text)) { + $start .= $this->textEncode($page); + } + if (isset($type)) { + switch ($type) { + case 'start': + $output = $start; + break; + case 'end': + $output = $end; + break; + } + } else { + $output = $start.$this->textEncode($text).$end; + } + return $output; + } +} +?> diff --git a/FlashMp3Player/Plugin.php b/FlashMp3Player/Plugin.php new file mode 100644 index 0000000..1ab176c --- /dev/null +++ b/FlashMp3Player/Plugin.php @@ -0,0 +1,109 @@ +<mp3>http://...</mp3>的格式来添加一个音乐播放器 + * + * @package Dewplayer + * @author qining + * @version 1.0.1 + * @dependence 9.9.2-* + * @link http://typecho.org + */ +class FlashMp3Player_Plugin implements Typecho_Plugin_Interface +{ + /** + * 激活插件方法,如果激活失败,直接抛出异常 + * + * @access public + * @return void + * @throws Typecho_Plugin_Exception + */ + public static function activate() + { + //离线浏览器都是所见即所得模式 + Typecho_Plugin::factory('Widget_XmlRpc')->fromOfflineEditor = array('FlashMp3Player_Plugin', 'toCodeEditor'); + + /** 前端输出处理接口 */ + Typecho_Plugin::factory('Widget_Abstract_Contents')->excerptEx = array('FlashMp3Player_Plugin', 'parse'); + Typecho_Plugin::factory('Widget_Abstract_Contents')->contentEx = array('FlashMp3Player_Plugin', 'parse'); + } + + /** + * 禁用插件方法,如果禁用失败,直接抛出异常 + * + * @static + * @access public + * @return void + * @throws Typecho_Plugin_Exception + */ + public static function deactivate(){} + + /** + * 获取插件配置面板 + * + * @access public + * @param Typecho_Widget_Helper_Form $form 配置面板 + * @return void + */ + public static function config(Typecho_Widget_Helper_Form $form){} + + /** + * 个人用户的配置面板 + * + * @access public + * @param Typecho_Widget_Helper_Form $form + * @return void + */ + public static function personalConfig(Typecho_Widget_Helper_Form $form){} + + /** + * 将伪可视化代码转化为可视化代码 + * + * @access public + * @param string $content 需要处理的内容 + * @return string + */ + public static function toVisualEditor($content) + { + $swfUrl = Typecho_Common::url('FlashMp3Player/swf/dewplayer.swf', Helper::options()->pluginUrl); + return preg_replace("/<(mp3)>(.*?)<\/\\1>/is", + " + +", + $content); + } + + /** + * 将可视化代码转化为伪可视化代码 + * + * @access public + * @param string $content 需要处理的内容 + * @return string + */ + public static function toCodeEditor($content) + { + $swfUrl = preg_quote(Typecho_Common::url('FlashMp3Player/swf/dewplayer.swf', Helper::options()->pluginUrl), "/"); + return preg_replace("/<(object)[^>]*data=\"{$swfUrl}\?mp3\=([^\">]+)\"[^>]*>(.*?)<\/\\1>/is", "\\2", $content); + } + + /** + * 插件实现方法 + * + * @access public + * @return void + */ + public static function parse($text, $widget, $lastResult) + { + $text = empty($lastResult) ? $text : $lastResult; + + if ($widget instanceof Widget_Archive) { + $swfUrl = Typecho_Common::url('FlashMp3Player/swf/dewplayer.swf', Helper::options()->pluginUrl); + $text = preg_replace("/<(mp3)>(.*?)<\/\\1>/is", + " + +", + $text); + } + + return $text; + } +} diff --git a/FlashMp3Player/swf/dewplayer.swf b/FlashMp3Player/swf/dewplayer.swf new file mode 100644 index 0000000..8d36637 Binary files /dev/null and b/FlashMp3Player/swf/dewplayer.swf differ diff --git a/GoogleCodePrettify/Plugin.php b/GoogleCodePrettify/Plugin.php new file mode 100644 index 0000000..5615bde --- /dev/null +++ b/GoogleCodePrettify/Plugin.php @@ -0,0 +1,137 @@ +contentEx = array('GoogleCodePrettify_Plugin', 'parse'); + Typecho_Plugin::factory('Widget_Abstract_Contents')->excerptEx = array('GoogleCodePrettify_Plugin', 'parse'); + Typecho_Plugin::factory('Widget_Abstract_Comments')->contentEx = array('GoogleCodePrettify_Plugin', 'parse'); + Typecho_Plugin::factory('Widget_Archive')->header = array('GoogleCodePrettify_Plugin', 'header'); + Typecho_Plugin::factory('Widget_Archive')->footer = array('GoogleCodePrettify_Plugin', 'footer'); + } + + /** + * 禁用插件方法,如果禁用失败,直接抛出异常 + * + * @static + * @access public + * @return void + * @throws Typecho_Plugin_Exception + */ + public static function deactivate(){} + + /** + * 获取插件配置面板 + * + * @access public + * @param Typecho_Widget_Helper_Form $form 配置面板 + * @return void + */ + public static function config(Typecho_Widget_Helper_Form $form){} + + /** + * 个人用户的配置面板 + * + * @access public + * @param Typecho_Widget_Helper_Form $form + * @return void + */ + public static function personalConfig(Typecho_Widget_Helper_Form $form){} + + /** + * 输出头部css + * + * @access public + * @param unknown $header + * @return unknown + */ + public static function header() { + $cssUrl = Helper::options()->pluginUrl . '/GoogleCodePrettify/src/prettify.css'; + echo ''; + } + + /** + * 输出尾部js + * + * @access public + * @param unknown $header + * @return unknown + */ + public static function footer() { + $jsUrl = Helper::options()->pluginUrl . '/GoogleCodePrettify/src/prettify.js'; + echo ''; + echo ''; + } + + /** + * 解析 + * + * @access public + * @param array $matches 解析值 + * @return string + */ + public static function parseCallback($matches) + { + $language = trim($matches[2]); + + $map = array( + 'js' => 'javascript', + 'as' => 'actionscript', + 'as3' => 'actionscript3' + ); + + if (!empty($language) && isset($map[$language])) { + $language = $map[$language]; + } + + $source = '
'; + $numberItem = ''; + + return $source . $numberItem . $sourceItem . '
'; + $sourceItem = ''; + $sourceItem .= '
'; + + $sourceList = explode("\n", trim($matches[3])); + foreach ($sourceList as $key => $sourceLine) { + $numberItem .= ''; + $sourceItem .= ''; + } + + $numberItem .= '
' . ($key + 1) . '
' . htmlspecialchars($sourceLine) . '
'; + } + + /** + * 插件实现方法 + * + * @access public + * @return void + */ + public static function parse($text, $widget, $lastResult) + { + $text = empty($lastResult) ? $text : $lastResult; + + if ($widget instanceof Widget_Archive || $widget instanceof Widget_Abstract_Comments) { + return preg_replace_callback("/<(code|pre)(\s*[^>]*)>(.*?)<\/\\1>/is", array('GoogleCodePrettify_Plugin', 'parseCallback'), $text); + } else { + return $text; + } + } +} diff --git a/GoogleCodePrettify/src/lang-apollo.js b/GoogleCodePrettify/src/lang-apollo.js new file mode 100644 index 0000000..c218210 --- /dev/null +++ b/GoogleCodePrettify/src/lang-apollo.js @@ -0,0 +1,51 @@ +// Copyright (C) 2009 Onno Hommes. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + + +/** + * @fileoverview + * Registers a language handler for the AGC/AEA Assembly Language as described + * at http://virtualagc.googlecode.com + *

+ * This file could be used by goodle code to allow syntax highlight for + * Virtual AGC SVN repository or if you don't want to commonize + * the header for the agc/aea html assembly listing. + * + * @author ohommes@alumni.cmu.edu + */ + +PR.registerLangHandler( + PR.createSimpleLexer( + [ + // A line comment that starts with ; + [PR.PR_COMMENT, /^#[^\r\n]*/, null, '#'], + // Whitespace + [PR.PR_PLAIN, /^[\t\n\r \xA0]+/, null, '\t\n\r \xA0'], + // A double quoted, possibly multi-line, string. + [PR.PR_STRING, /^\"(?:[^\"\\]|\\[\s\S])*(?:\"|$)/, null, '"'] + ], + [ + [PR.PR_KEYWORD, /^(?:ADS|AD|AUG|BZF|BZMF|CAE|CAF|CA|CCS|COM|CS|DAS|DCA|DCOM|DCS|DDOUBL|DIM|DOUBLE|DTCB|DTCF|DV|DXCH|EDRUPT|EXTEND|INCR|INDEX|NDX|INHINT|LXCH|MASK|MSK|MP|MSU|NOOP|OVSK|QXCH|RAND|READ|RELINT|RESUME|RETURN|ROR|RXOR|SQUARE|SU|TCR|TCAA|OVSK|TCF|TC|TS|WAND|WOR|WRITE|XCH|XLQ|XXALQ|ZL|ZQ|ADD|ADZ|SUB|SUZ|MPY|MPR|MPZ|DVP|COM|ABS|CLA|CLZ|LDQ|STO|STQ|ALS|LLS|LRS|TRA|TSQ|TMI|TOV|AXT|TIX|DLY|INP|OUT)\s/,null], + [PR.PR_TYPE, /^(?:-?GENADR|=MINUS|2BCADR|VN|BOF|MM|-?2CADR|-?[1-6]DNADR|ADRES|BBCON|[SE]?BANK\=?|BLOCK|BNKSUM|E?CADR|COUNT\*?|2?DEC\*?|-?DNCHAN|-?DNPTR|EQUALS|ERASE|MEMORY|2?OCT|REMADR|SETLOC|SUBRO|ORG|BSS|BES|SYN|EQU|DEFINE|END)\s/,null], + // A single quote possibly followed by a word that optionally ends with + // = ! or ?. + [PR.PR_LITERAL, + /^\'(?:-*(?:\w|\\[\x21-\x7e])(?:[\w-]*|\\[\x21-\x7e])[=!?]?)?/], + // Any word including labels that optionally ends with = ! or ?. + [PR.PR_PLAIN, + /^-*(?:[!-z_]|\\[\x21-\x7e])(?:[\w-]*|\\[\x21-\x7e])[=!?]?/i], + // A printable non-space non-special character + [PR.PR_PUNCTUATION, /^[^\w\t\n\r \xA0()\"\\\';]+/] + ]), + ['apollo', 'agc', 'aea']); diff --git a/GoogleCodePrettify/src/lang-css.js b/GoogleCodePrettify/src/lang-css.js new file mode 100644 index 0000000..44013d2 --- /dev/null +++ b/GoogleCodePrettify/src/lang-css.js @@ -0,0 +1,78 @@ +// Copyright (C) 2009 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + + + +/** + * @fileoverview + * Registers a language handler for CSS. + * + * + * To use, include prettify.js and this file in your HTML page. + * Then put your code in an HTML tag like + *


+ *
+ *
+ * http://www.w3.org/TR/CSS21/grammar.html Section G2 defines the lexical
+ * grammar.  This scheme does not recognize keywords containing escapes.
+ *
+ * @author mikesamuel@gmail.com
+ */
+
+PR.registerLangHandler(
+    PR.createSimpleLexer(
+        [
+         // The space production 
+         [PR.PR_PLAIN,       /^[ \t\r\n\f]+/, null, ' \t\r\n\f']
+        ],
+        [
+         // Quoted strings.   and 
+         [PR.PR_STRING,
+          /^\"(?:[^\n\r\f\\\"]|\\(?:\r\n?|\n|\f)|\\[\s\S])*\"/, null],
+         [PR.PR_STRING,
+          /^\'(?:[^\n\r\f\\\']|\\(?:\r\n?|\n|\f)|\\[\s\S])*\'/, null],
+         ['lang-css-str', /^url\(([^\)\"\']*)\)/i],
+         [PR.PR_KEYWORD,
+          /^(?:url|rgb|\!important|@import|@page|@media|@charset|inherit)(?=[^\-\w]|$)/i,
+          null],
+         // A property name -- an identifier followed by a colon.
+         ['lang-css-kw', /^(-?(?:[_a-z]|(?:\\[0-9a-f]+ ?))(?:[_a-z0-9\-]|\\(?:\\[0-9a-f]+ ?))*)\s*:/i],
+         // A C style block comment.  The  production.
+         [PR.PR_COMMENT, /^\/\*[^*]*\*+(?:[^\/*][^*]*\*+)*\//],
+         // Escaping text spans
+         [PR.PR_COMMENT, /^(?:)/],
+         // A number possibly containing a suffix.
+         [PR.PR_LITERAL, /^(?:\d+|\d*\.\d+)(?:%|[a-z]+)?/i],
+         // A hex color
+         [PR.PR_LITERAL, /^#(?:[0-9a-f]{3}){1,2}/i],
+         // An identifier
+         [PR.PR_PLAIN,
+          /^-?(?:[_a-z]|(?:\\[\da-f]+ ?))(?:[_a-z\d\-]|\\(?:\\[\da-f]+ ?))*/i],
+         // A run of punctuation
+         [PR.PR_PUNCTUATION, /^[^\s\w\'\"]+/]
+        ]),
+    ['css']);
+PR.registerLangHandler(
+    PR.createSimpleLexer([],
+        [
+         [PR.PR_KEYWORD,
+          /^-?(?:[_a-z]|(?:\\[\da-f]+ ?))(?:[_a-z\d\-]|\\(?:\\[\da-f]+ ?))*/i]
+        ]),
+    ['css-kw']);
+PR.registerLangHandler(
+    PR.createSimpleLexer([],
+        [
+         [PR.PR_STRING, /^[^\)\"\']+/]
+        ]),
+    ['css-str']);
diff --git a/GoogleCodePrettify/src/lang-hs.js b/GoogleCodePrettify/src/lang-hs.js
new file mode 100644
index 0000000..91157b9
--- /dev/null
+++ b/GoogleCodePrettify/src/lang-hs.js
@@ -0,0 +1,101 @@
+// Copyright (C) 2009 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+
+
+/**
+ * @fileoverview
+ * Registers a language handler for Haskell.
+ *
+ *
+ * To use, include prettify.js and this file in your HTML page.
+ * Then put your code in an HTML tag like
+ *      
(my lisp code)
+ * The lang-cl class identifies the language as common lisp. + * This file supports the following language extensions: + * lang-cl - Common Lisp + * lang-el - Emacs Lisp + * lang-lisp - Lisp + * lang-scm - Scheme + * + * + * I used http://www.informatik.uni-freiburg.de/~thiemann/haskell/haskell98-report-html/syntax-iso.html + * as the basis, but ignore the way the ncomment production nests since this + * makes the lexical grammar irregular. It might be possible to support + * ncomments using the lookbehind filter. + * + * + * @author mikesamuel@gmail.com + */ + +PR.registerLangHandler( + PR.createSimpleLexer( + [ + // Whitespace + // whitechar -> newline | vertab | space | tab | uniWhite + // newline -> return linefeed | return | linefeed | formfeed + [PR.PR_PLAIN, /^[\t\n\x0B\x0C\r ]+/, null, '\t\n\x0B\x0C\r '], + // Single line double and single-quoted strings. + // char -> ' (graphic<' | \> | space | escape<\&>) ' + // string -> " {graphic<" | \> | space | escape | gap}" + // escape -> \ ( charesc | ascii | decimal | o octal + // | x hexadecimal ) + // charesc -> a | b | f | n | r | t | v | \ | " | ' | & + [PR.PR_STRING, /^\"(?:[^\"\\\n\x0C\r]|\\[\s\S])*(?:\"|$)/, + null, '"'], + [PR.PR_STRING, /^\'(?:[^\'\\\n\x0C\r]|\\[^&])\'?/, + null, "'"], + // decimal -> digit{digit} + // octal -> octit{octit} + // hexadecimal -> hexit{hexit} + // integer -> decimal + // | 0o octal | 0O octal + // | 0x hexadecimal | 0X hexadecimal + // float -> decimal . decimal [exponent] + // | decimal exponent + // exponent -> (e | E) [+ | -] decimal + [PR.PR_LITERAL, + /^(?:0o[0-7]+|0x[\da-f]+|\d+(?:\.\d+)?(?:e[+\-]?\d+)?)/i, + null, '0123456789'] + ], + [ + // Haskell does not have a regular lexical grammar due to the nested + // ncomment. + // comment -> dashes [ any {any}] newline + // ncomment -> opencom ANYseq {ncomment ANYseq}closecom + // dashes -> '--' {'-'} + // opencom -> '{-' + // closecom -> '-}' + [PR.PR_COMMENT, /^(?:(?:--+(?:[^\r\n\x0C]*)?)|(?:\{-(?:[^-]|-+[^-\}])*-\}))/], + // reservedid -> case | class | data | default | deriving | do + // | else | if | import | in | infix | infixl | infixr + // | instance | let | module | newtype | of | then + // | type | where | _ + [PR.PR_KEYWORD, /^(?:case|class|data|default|deriving|do|else|if|import|in|infix|infixl|infixr|instance|let|module|newtype|of|then|type|where|_)(?=[^a-zA-Z0-9\']|$)/, null], + // qvarid -> [ modid . ] varid + // qconid -> [ modid . ] conid + // varid -> (small {small | large | digit | ' }) + // conid -> large {small | large | digit | ' } + // modid -> conid + // small -> ascSmall | uniSmall | _ + // ascSmall -> a | b | ... | z + // uniSmall -> any Unicode lowercase letter + // large -> ascLarge | uniLarge + // ascLarge -> A | B | ... | Z + // uniLarge -> any uppercase or titlecase Unicode letter + [PR.PR_PLAIN, /^(?:[A-Z][\w\']*\.)*[a-zA-Z][\w\']*/], + // matches the symbol production + [PR.PR_PUNCTUATION, /^[^\t\n\x0B\x0C\r a-zA-Z0-9\'\"]+/] + ]), + ['hs']); diff --git a/GoogleCodePrettify/src/lang-lisp.js b/GoogleCodePrettify/src/lang-lisp.js new file mode 100644 index 0000000..4cffa53 --- /dev/null +++ b/GoogleCodePrettify/src/lang-lisp.js @@ -0,0 +1,93 @@ +// Copyright (C) 2008 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + + + +/** + * @fileoverview + * Registers a language handler for Common Lisp and related languages. + * + * + * To use, include prettify.js and this file in your HTML page. + * Then put your code in an HTML tag like + *
(my lisp code)
+ * The lang-cl class identifies the language as common lisp. + * This file supports the following language extensions: + * lang-cl - Common Lisp + * lang-el - Emacs Lisp + * lang-lisp - Lisp + * lang-scm - Scheme + * + * + * I used http://www.devincook.com/goldparser/doc/meta-language/grammar-LISP.htm + * as the basis, but added line comments that start with ; and changed the atom + * production to disallow unquoted semicolons. + * + * "Name" = 'LISP' + * "Author" = 'John McCarthy' + * "Version" = 'Minimal' + * "About" = 'LISP is an abstract language that organizes ALL' + * | 'data around "lists".' + * + * "Start Symbol" = [s-Expression] + * + * {Atom Char} = {Printable} - {Whitespace} - [()"\''] + * + * Atom = ( {Atom Char} | '\'{Printable} )+ + * + * [s-Expression] ::= [Quote] Atom + * | [Quote] '(' [Series] ')' + * | [Quote] '(' [s-Expression] '.' [s-Expression] ')' + * + * [Series] ::= [s-Expression] [Series] + * | + * + * [Quote] ::= '' !Quote = do not evaluate + * | + * + * + * I used Practical Common Lisp as + * the basis for the reserved word list. + * + * + * @author mikesamuel@gmail.com + */ + +PR.registerLangHandler( + PR.createSimpleLexer( + [ + ['opn', /^\(/, null, '('], + ['clo', /^\)/, null, ')'], + // A line comment that starts with ; + [PR.PR_COMMENT, /^;[^\r\n]*/, null, ';'], + // Whitespace + [PR.PR_PLAIN, /^[\t\n\r \xA0]+/, null, '\t\n\r \xA0'], + // A double quoted, possibly multi-line, string. + [PR.PR_STRING, /^\"(?:[^\"\\]|\\[\s\S])*(?:\"|$)/, null, '"'] + ], + [ + [PR.PR_KEYWORD, /^(?:block|c[ad]+r|catch|cons|defun|do|eq|eql|equal|equalp|eval-when|flet|format|go|if|labels|lambda|let|load-time-value|locally|macrolet|multiple-value-call|nil|progn|progv|quote|require|return-from|setq|symbol-macrolet|t|tagbody|the|throw|unwind)\b/, null], + [PR.PR_LITERAL, + /^[+\-]?(?:0x[0-9a-f]+|\d+\/\d+|(?:\.\d+|\d+(?:\.\d*)?)(?:[ed][+\-]?\d+)?)/i], + // A single quote possibly followed by a word that optionally ends with + // = ! or ?. + [PR.PR_LITERAL, + /^\'(?:-*(?:\w|\\[\x21-\x7e])(?:[\w-]*|\\[\x21-\x7e])[=!?]?)?/], + // A word that optionally ends with = ! or ?. + [PR.PR_PLAIN, + /^-*(?:[a-z_]|\\[\x21-\x7e])(?:[\w-]*|\\[\x21-\x7e])[=!?]?/i], + // A printable non-space non-special character + [PR.PR_PUNCTUATION, /^[^\w\t\n\r \xA0()\"\\\';]+/] + ]), + ['cl', 'el', 'lisp', 'scm']); diff --git a/GoogleCodePrettify/src/lang-lua.js b/GoogleCodePrettify/src/lang-lua.js new file mode 100644 index 0000000..68bb30b --- /dev/null +++ b/GoogleCodePrettify/src/lang-lua.js @@ -0,0 +1,59 @@ +// Copyright (C) 2008 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + + + +/** + * @fileoverview + * Registers a language handler for Lua. + * + * + * To use, include prettify.js and this file in your HTML page. + * Then put your code in an HTML tag like + *
(my Lua code)
+ * + * + * I used http://www.lua.org/manual/5.1/manual.html#2.1 + * Because of the long-bracket concept used in strings and comments, Lua does + * not have a regular lexical grammar, but luckily it fits within the space + * of irregular grammars supported by javascript regular expressions. + * + * @author mikesamuel@gmail.com + */ + +PR.registerLangHandler( + PR.createSimpleLexer( + [ + // Whitespace + [PR.PR_PLAIN, /^[\t\n\r \xA0]+/, null, '\t\n\r \xA0'], + // A double or single quoted, possibly multi-line, string. + [PR.PR_STRING, /^(?:\"(?:[^\"\\]|\\[\s\S])*(?:\"|$)|\'(?:[^\'\\]|\\[\s\S])*(?:\'|$))/, null, '"\''] + ], + [ + // A comment is either a line comment that starts with two dashes, or + // two dashes preceding a long bracketed block. + [PR.PR_COMMENT, /^--(?:\[(=*)\[[\s\S]*?(?:\]\1\]|$)|[^\r\n]*)/], + // A long bracketed block not preceded by -- is a string. + [PR.PR_STRING, /^\[(=*)\[[\s\S]*?(?:\]\1\]|$)/], + [PR.PR_KEYWORD, /^(?:and|break|do|else|elseif|end|false|for|function|if|in|local|nil|not|or|repeat|return|then|true|until|while)\b/, null], + // A number is a hex integer literal, a decimal real literal, or in + // scientific notation. + [PR.PR_LITERAL, + /^[+-]?(?:0x[\da-f]+|(?:(?:\.\d+|\d+(?:\.\d*)?)(?:e[+\-]?\d+)?))/i], + // An identifier + [PR.PR_PLAIN, /^[a-z_]\w*/i], + // A run of punctuation + [PR.PR_PUNCTUATION, /^[^\w\t\n\r \xA0][^\w\t\n\r \xA0\"\'\-\+=]*/] + ]), + ['lua']); diff --git a/GoogleCodePrettify/src/lang-ml.js b/GoogleCodePrettify/src/lang-ml.js new file mode 100644 index 0000000..c5a3db7 --- /dev/null +++ b/GoogleCodePrettify/src/lang-ml.js @@ -0,0 +1,56 @@ +// Copyright (C) 2008 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + + + +/** + * @fileoverview + * Registers a language handler for OCaml, SML, F# and similar languages. + * + * Based on the lexical grammar at + * http://research.microsoft.com/fsharp/manual/spec2.aspx#_Toc202383715 + * + * @author mikesamuel@gmail.com + */ + +PR.registerLangHandler( + PR.createSimpleLexer( + [ + // Whitespace is made up of spaces, tabs and newline characters. + [PR.PR_PLAIN, /^[\t\n\r \xA0]+/, null, '\t\n\r \xA0'], + // #if ident/#else/#endif directives delimit conditional compilation + // sections + [PR.PR_COMMENT, + /^#(?:if[\t\n\r \xA0]+(?:[a-z_$][\w\']*|``[^\r\n\t`]*(?:``|$))|else|endif|light)/i, + null, '#'], + // A double or single quoted, possibly multi-line, string. + // F# allows escaped newlines in strings. + [PR.PR_STRING, /^(?:\"(?:[^\"\\]|\\[\s\S])*(?:\"|$)|\'(?:[^\'\\]|\\[\s\S])*(?:\'|$))/, null, '"\''] + ], + [ + // Block comments are delimited by (* and *) and may be + // nested. Single-line comments begin with // and extend to + // the end of a line. + // TODO: (*...*) comments can be nested. This does not handle that. + [PR.PR_COMMENT, /^(?:\/\/[^\r\n]*|\(\*[\s\S]*?\*\))/], + [PR.PR_KEYWORD, /^(?:abstract|and|as|assert|begin|class|default|delegate|do|done|downcast|downto|elif|else|end|exception|extern|false|finally|for|fun|function|if|in|inherit|inline|interface|internal|lazy|let|match|member|module|mutable|namespace|new|null|of|open|or|override|private|public|rec|return|static|struct|then|to|true|try|type|upcast|use|val|void|when|while|with|yield|asr|land|lor|lsl|lsr|lxor|mod|sig|atomic|break|checked|component|const|constraint|constructor|continue|eager|event|external|fixed|functor|global|include|method|mixin|object|parallel|process|protected|pure|sealed|trait|virtual|volatile)\b/], + // A number is a hex integer literal, a decimal real literal, or in + // scientific notation. + [PR.PR_LITERAL, + /^[+\-]?(?:0x[\da-f]+|(?:(?:\.\d+|\d+(?:\.\d*)?)(?:e[+\-]?\d+)?))/i], + [PR.PR_PLAIN, /^(?:[a-z_]\w*[!?#]?|``[^\r\n\t`]*(?:``|$))/i], + // A printable non-space non-special character + [PR.PR_PUNCTUATION, /^[^\t\n\r \xA0\"\'\w]+/] + ]), + ['fs', 'ml']); diff --git a/GoogleCodePrettify/src/lang-proto.js b/GoogleCodePrettify/src/lang-proto.js new file mode 100644 index 0000000..d6531fd --- /dev/null +++ b/GoogleCodePrettify/src/lang-proto.js @@ -0,0 +1,35 @@ +// Copyright (C) 2006 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + + +/** + * @fileoverview + * Registers a language handler for Protocol Buffers as described at + * http://code.google.com/p/protobuf/. + * + * Based on the lexical grammar at + * http://research.microsoft.com/fsharp/manual/spec2.aspx#_Toc202383715 + * + * @author mikesamuel@gmail.com + */ + +PR.registerLangHandler(PR.sourceDecorator({ + keywords: ( + 'bool bytes default double enum extend extensions false fixed32 ' + + 'fixed64 float group import int32 int64 max message option ' + + 'optional package repeated required returns rpc service ' + + 'sfixed32 sfixed64 sint32 sint64 string syntax to true uint32 ' + + 'uint64'), + cStyleComments: true + }), ['proto']); diff --git a/GoogleCodePrettify/src/lang-sql.js b/GoogleCodePrettify/src/lang-sql.js new file mode 100644 index 0000000..7a58097 --- /dev/null +++ b/GoogleCodePrettify/src/lang-sql.js @@ -0,0 +1,57 @@ +// Copyright (C) 2008 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + + + +/** + * @fileoverview + * Registers a language handler for SQL. + * + * + * To use, include prettify.js and this file in your HTML page. + * Then put your code in an HTML tag like + *
(my SQL code)
+ * + * + * http://savage.net.au/SQL/sql-99.bnf.html is the basis for the grammar, and + * http://msdn.microsoft.com/en-us/library/aa238507(SQL.80).aspx as the basis + * for the keyword list. + * + * @author mikesamuel@gmail.com + */ + +PR.registerLangHandler( + PR.createSimpleLexer( + [ + // Whitespace + [PR.PR_PLAIN, /^[\t\n\r \xA0]+/, null, '\t\n\r \xA0'], + // A double or single quoted, possibly multi-line, string. + [PR.PR_STRING, /^(?:"(?:[^\"\\]|\\.)*"|'(?:[^\'\\]|\\.)*')/, null, + '"\''] + ], + [ + // A comment is either a line comment that starts with two dashes, or + // two dashes preceding a long bracketed block. + [PR.PR_COMMENT, /^(?:--[^\r\n]*|\/\*[\s\S]*?(?:\*\/|$))/], + [PR.PR_KEYWORD, /^(?:ADD|ALL|ALTER|AND|ANY|AS|ASC|AUTHORIZATION|BACKUP|BEGIN|BETWEEN|BREAK|BROWSE|BULK|BY|CASCADE|CASE|CHECK|CHECKPOINT|CLOSE|CLUSTERED|COALESCE|COLLATE|COLUMN|COMMIT|COMPUTE|CONSTRAINT|CONTAINS|CONTAINSTABLE|CONTINUE|CONVERT|CREATE|CROSS|CURRENT|CURRENT_DATE|CURRENT_TIME|CURRENT_TIMESTAMP|CURRENT_USER|CURSOR|DATABASE|DBCC|DEALLOCATE|DECLARE|DEFAULT|DELETE|DENY|DESC|DISK|DISTINCT|DISTRIBUTED|DOUBLE|DROP|DUMMY|DUMP|ELSE|END|ERRLVL|ESCAPE|EXCEPT|EXEC|EXECUTE|EXISTS|EXIT|FETCH|FILE|FILLFACTOR|FOR|FOREIGN|FREETEXT|FREETEXTTABLE|FROM|FULL|FUNCTION|GOTO|GRANT|GROUP|HAVING|HOLDLOCK|IDENTITY|IDENTITYCOL|IDENTITY_INSERT|IF|IN|INDEX|INNER|INSERT|INTERSECT|INTO|IS|JOIN|KEY|KILL|LEFT|LIKE|LINENO|LOAD|NATIONAL|NOCHECK|NONCLUSTERED|NOT|NULL|NULLIF|OF|OFF|OFFSETS|ON|OPEN|OPENDATASOURCE|OPENQUERY|OPENROWSET|OPENXML|OPTION|OR|ORDER|OUTER|OVER|PERCENT|PLAN|PRECISION|PRIMARY|PRINT|PROC|PROCEDURE|PUBLIC|RAISERROR|READ|READTEXT|RECONFIGURE|REFERENCES|REPLICATION|RESTORE|RESTRICT|RETURN|REVOKE|RIGHT|ROLLBACK|ROWCOUNT|ROWGUIDCOL|RULE|SAVE|SCHEMA|SELECT|SESSION_USER|SET|SETUSER|SHUTDOWN|SOME|STATISTICS|SYSTEM_USER|TABLE|TEXTSIZE|THEN|TO|TOP|TRAN|TRANSACTION|TRIGGER|TRUNCATE|TSEQUAL|UNION|UNIQUE|UPDATE|UPDATETEXT|USE|USER|VALUES|VARYING|VIEW|WAITFOR|WHEN|WHERE|WHILE|WITH|WRITETEXT)(?=[^\w-]|$)/i, null], + // A number is a hex integer literal, a decimal real literal, or in + // scientific notation. + [PR.PR_LITERAL, + /^[+-]?(?:0x[\da-f]+|(?:(?:\.\d+|\d+(?:\.\d*)?)(?:e[+\-]?\d+)?))/i], + // An identifier + [PR.PR_PLAIN, /^[a-z_][\w-]*/i], + // A run of punctuation + [PR.PR_PUNCTUATION, /^[^\w\t\n\r \xA0\"\'][^\w\t\n\r \xA0+\-\"\']*/] + ]), + ['sql']); diff --git a/GoogleCodePrettify/src/lang-vb.js b/GoogleCodePrettify/src/lang-vb.js new file mode 100644 index 0000000..a38db45 --- /dev/null +++ b/GoogleCodePrettify/src/lang-vb.js @@ -0,0 +1,61 @@ +// Copyright (C) 2009 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + + + +/** + * @fileoverview + * Registers a language handler for various flavors of basic. + * + * + * To use, include prettify.js and this file in your HTML page. + * Then put your code in an HTML tag like + *

+ *
+ *
+ * http://msdn.microsoft.com/en-us/library/aa711638(VS.71).aspx defines the
+ * visual basic grammar lexical grammar.
+ *
+ * @author mikesamuel@gmail.com
+ */
+
+PR.registerLangHandler(
+    PR.createSimpleLexer(
+        [
+         // Whitespace
+         [PR.PR_PLAIN,       /^[\t\n\r \xA0\u2028\u2029]+/, null, '\t\n\r \xA0\u2028\u2029'],
+         // A double quoted string with quotes escaped by doubling them.
+         // A single character can be suffixed with C.
+         [PR.PR_STRING,      /^(?:[\"\u201C\u201D](?:[^\"\u201C\u201D]|[\"\u201C\u201D]{2})(?:[\"\u201C\u201D]c|$)|[\"\u201C\u201D](?:[^\"\u201C\u201D]|[\"\u201C\u201D]{2})*(?:[\"\u201C\u201D]|$))/i, null,
+          '"\u201C\u201D'],
+         // A comment starts with a single quote and runs until the end of the
+         // line.
+         [PR.PR_COMMENT,     /^[\'\u2018\u2019][^\r\n\u2028\u2029]*/, null, '\'\u2018\u2019']
+        ],
+        [
+         [PR.PR_KEYWORD, /^(?:AddHandler|AddressOf|Alias|And|AndAlso|Ansi|As|Assembly|Auto|Boolean|ByRef|Byte|ByVal|Call|Case|Catch|CBool|CByte|CChar|CDate|CDbl|CDec|Char|CInt|Class|CLng|CObj|Const|CShort|CSng|CStr|CType|Date|Decimal|Declare|Default|Delegate|Dim|DirectCast|Do|Double|Each|Else|ElseIf|End|EndIf|Enum|Erase|Error|Event|Exit|Finally|For|Friend|Function|Get|GetType|GoSub|GoTo|Handles|If|Implements|Imports|In|Inherits|Integer|Interface|Is|Let|Lib|Like|Long|Loop|Me|Mod|Module|MustInherit|MustOverride|MyBase|MyClass|Namespace|New|Next|Not|NotInheritable|NotOverridable|Object|On|Option|Optional|Or|OrElse|Overloads|Overridable|Overrides|ParamArray|Preserve|Private|Property|Protected|Public|RaiseEvent|ReadOnly|ReDim|RemoveHandler|Resume|Return|Select|Set|Shadows|Shared|Short|Single|Static|Step|Stop|String|Structure|Sub|SyncLock|Then|Throw|To|Try|TypeOf|Unicode|Until|Variant|Wend|When|While|With|WithEvents|WriteOnly|Xor|EndIf|GoSub|Let|Variant|Wend)\b/i, null],
+         // A second comment form
+         [PR.PR_COMMENT, /^REM[^\r\n\u2028\u2029]*/i],
+         // A boolean, numeric, or date literal.
+         [PR.PR_LITERAL,
+          /^(?:True\b|False\b|Nothing\b|\d+(?:E[+\-]?\d+[FRD]?|[FRDSIL])?|(?:&H[0-9A-F]+|&O[0-7]+)[SIL]?|\d*\.\d+(?:E[+\-]?\d+)?[FRD]?|#\s+(?:\d+[\-\/]\d+[\-\/]\d+(?:\s+\d+:\d+(?::\d+)?(\s*(?:AM|PM))?)?|\d+:\d+(?::\d+)?(\s*(?:AM|PM))?)\s+#)/i],
+         // An identifier?
+         [PR.PR_PLAIN, /^(?:(?:[a-z]|_\w)\w*|\[(?:[a-z]|_\w)\w*\])/i],
+         // A run of punctuation
+         [PR.PR_PUNCTUATION,
+          /^[^\w\t\n\r \"\'\[\]\xA0\u2018\u2019\u201C\u201D\u2028\u2029]+/],
+         // Square brackets
+         [PR.PR_PUNCTUATION, /^(?:\[|\])/]
+        ]),
+    ['vb', 'vbs']);
diff --git a/GoogleCodePrettify/src/lang-wiki.js b/GoogleCodePrettify/src/lang-wiki.js
new file mode 100644
index 0000000..d4aa350
--- /dev/null
+++ b/GoogleCodePrettify/src/lang-wiki.js
@@ -0,0 +1,53 @@
+// Copyright (C) 2009 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+
+/**
+ * @fileoverview
+ * Registers a language handler for Wiki pages.
+ *
+ * Based on WikiSyntax at http://code.google.com/p/support/wiki/WikiSyntax
+ *
+ * @author mikesamuel@gmail.com
+ */
+
+PR.registerLangHandler(
+    PR.createSimpleLexer(
+        [
+         // Whitespace
+         [PR.PR_PLAIN,       /^[\t \xA0a-gi-z0-9]+/, null,
+          '\t \xA0abcdefgijklmnopqrstuvwxyz0123456789'],
+         // Wiki formatting
+         [PR.PR_PUNCTUATION, /^[=*~\^\[\]]+/, null, '=*~^[]']
+        ],
+        [
+         // Meta-info like #summary, #labels, etc.
+         ['lang-wiki.meta',  /(?:^^|\r\n?|\n)(#[a-z]+)\b/],
+         // A WikiWord
+         [PR.PR_LITERAL,     /^(?:[A-Z][a-z][a-z0-9]+[A-Z][a-z][a-zA-Z0-9]+)\b/
+          ],
+         // A preformatted block in an unknown language
+         ['lang-',           /^\{\{\{([\s\S]+?)\}\}\}/],
+         // A block of source code in an unknown language
+         ['lang-',           /^`([^\r\n`]+)`/],
+         // An inline URL.
+         [PR.PR_STRING,
+          /^https?:\/\/[^\/?#\s]*(?:\/[^?#\s]*)?(?:\?[^#\s]*)?(?:#\S*)?/i],
+         [PR.PR_PLAIN,       /^(?:\r\n|[\s\S])[^#=*~^A-Zh\{`\[\r\n]*/]
+        ]),
+    ['wiki']);
+
+PR.registerLangHandler(
+    PR.createSimpleLexer([[PR.PR_KEYWORD, /^#[a-z]+/i, null, '#']], []),
+    ['wiki.meta']);
diff --git a/GoogleCodePrettify/src/prettify.css b/GoogleCodePrettify/src/prettify.css
new file mode 100644
index 0000000..990d48c
--- /dev/null
+++ b/GoogleCodePrettify/src/prettify.css
@@ -0,0 +1,32 @@
+/* Pretty printing styles. Used with prettify.js. */
+
+.str { color: #B1D631; font-style: italic; }
+.kwd { color: #527AA2; }
+.com { color: #666; font-style: italic; }
+.typ { color: #FAF4C6; }
+.lit { color: #527AA2; }
+.pun { color: #FF8613; }
+.pln { color: #FAF4C6; }
+.tag { color: #527AA2; }
+.atn { color: #FAF4C6; }
+.atv { color: #B1D631; }
+.dec { color: #FAF4C6; }
+table.prettyprint-table { padding: 2px; border: 1px solid #000; background: #222; color: #eee; font-size: 13px; margin: 0; font: 12px/1.5 'andale mono','lucida console',monospace; }
+table.prettyprint-table pre, table.prettyprint-table table {margin: 0; background: #222; border: none; font-size: 13px;}
+pre.prettyprint tr:hover td {background: #333}
+table.prettyprint-table td.number {color: #666; font-family: "Courier New",Courier,monospace }
+.prettyprint-box {width: 100%; display: block; overflow-x: auto}
+table.prettyprint-table td {padding: 2px 4px}
+
+@media print {
+  .str { color: #060; }
+  .kwd { color: #006; font-weight: bold; }
+  .com { color: #600; font-style: italic; }
+  .typ { color: #404; font-weight: bold; }
+  .lit { color: #044; }
+  .pun { color: #440; }
+  .pln { color: #000; }
+  .tag { color: #006; font-weight: bold; }
+  .atn { color: #404; }
+  .atv { color: #060; }
+}
diff --git a/GoogleCodePrettify/src/prettify.js b/GoogleCodePrettify/src/prettify.js
new file mode 100644
index 0000000..09d6394
--- /dev/null
+++ b/GoogleCodePrettify/src/prettify.js
@@ -0,0 +1,1478 @@
+// Copyright (C) 2006 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+
+/**
+ * @fileoverview
+ * some functions for browser-side pretty printing of code contained in html.
+ * 

+ * + * For a fairly comprehensive set of languages see the + * README + * file that came with this source. At a minimum, the lexer should work on a + * number of languages including C and friends, Java, Python, Bash, SQL, HTML, + * XML, CSS, Javascript, and Makefiles. It works passably on Ruby, PHP and Awk + * and a subset of Perl, but, because of commenting conventions, doesn't work on + * Smalltalk, Lisp-like, or CAML-like languages without an explicit lang class. + *

+ * Usage:

    + *
  1. include this source file in an html page via + * {@code } + *
  2. define style rules. See the example page for examples. + *
  3. mark the {@code
    } and {@code } tags in your source with
    + *    {@code class=prettyprint.}
    + *    You can also use the (html deprecated) {@code } tag, but the pretty
    + *    printer needs to do more substantial DOM manipulations to support that, so
    + *    some css styles may not be preserved.
    + * </ol>
    + * That's it.  I wanted to keep the API as simple as possible, so there's no
    + * need to specify which language the code is in, but if you wish, you can add
    + * another class to the {@code <pre>} or {@code <code>} element to specify the
    + * language, as in {@code <pre class="prettyprint lang-java">}.  Any class that
    + * starts with "lang-" followed by a file extension, specifies the file type.
    + * See the "lang-*.js" files in this directory for code that implements
    + * per-language file handlers.
    + * <p>
    + * Change log:<br>
    + * cbeust, 2006/08/22
    + * <blockquote>
    + *   Java annotations (start with "@") are now captured as literals ("lit")
    + * </blockquote>
    + * @requires console
    + * @overrides window
    + */
    +
    +// JSLint declarations
    +/*global console, document, navigator, setTimeout, window */
    +
    +/**
    + * Split {@code prettyPrint} into multiple timeouts so as not to interfere with
    + * UI events.
    + * If set to {@code false}, {@code prettyPrint()} is synchronous.
    + */
    +window['PR_SHOULD_USE_CONTINUATION'] = true;
    +
    +/** the number of characters between tab columns */
    +window['PR_TAB_WIDTH'] = 8;
    +
    +/** Walks the DOM returning a properly escaped version of innerHTML.
    +  * @param {Node} node
    +  * @param {Array.<string>} out output buffer that receives chunks of HTML.
    +  */
    +window['PR_normalizedHtml']
    +
    +/** Contains functions for creating and registering new language handlers.
    +  * @type {Object}
    +  */
    +  = window['PR']
    +
    +/** Pretty print a chunk of code.
    +  *
    +  * @param {string} sourceCodeHtml code as html
    +  * @return {string} code as html, but prettier
    +  */
    +  = window['prettyPrintOne']
    +/** Find all the {@code <pre>} and {@code <code>} tags in the DOM with
    +  * {@code class=prettyprint} and prettify them.
    +  * @param {Function?} opt_whenDone if specified, called when the last entry
    +  *     has been finished.
    +  */
    +  = window['prettyPrint'] = void 0;
    +
    +/** browser detection. @extern @returns false if not IE, otherwise the major version. */
    +window['_pr_isIE6'] = function () {
    +  var ieVersion = navigator && navigator.userAgent &&
    +      navigator.userAgent.match(/\bMSIE ([678])\./);
    +  ieVersion = ieVersion ? +ieVersion[1] : false;
    +  window['_pr_isIE6'] = function () { return ieVersion; };
    +  return ieVersion;
    +};
    +
    +
    +(function () {
    +  // Keyword lists for various languages.
    +  var FLOW_CONTROL_KEYWORDS =
    +      "break continue do else for if return while ";
    +  var C_KEYWORDS = FLOW_CONTROL_KEYWORDS + "auto case char const default " +
    +      "double enum extern float goto int long register short signed sizeof " +
    +      "static struct switch typedef union unsigned void volatile ";
    +  var COMMON_KEYWORDS = C_KEYWORDS + "catch class delete false import " +
    +      "new operator private protected public this throw true try typeof ";
    +  var CPP_KEYWORDS = COMMON_KEYWORDS + "alignof align_union asm axiom bool " +
    +      "concept concept_map const_cast constexpr decltype " +
    +      "dynamic_cast explicit export friend inline late_check " +
    +      "mutable namespace nullptr reinterpret_cast static_assert static_cast " +
    +      "template typeid typename using virtual wchar_t where ";
    +  var JAVA_KEYWORDS = COMMON_KEYWORDS +
    +      "abstract boolean byte extends final finally implements import " +
    +      "instanceof null native package strictfp super synchronized throws " +
    +      "transient ";
    +  var CSHARP_KEYWORDS = JAVA_KEYWORDS +
    +      "as base by checked decimal delegate descending event " +
    +      "fixed foreach from group implicit in interface internal into is lock " +
    +      "object out override orderby params partial readonly ref sbyte sealed " +
    +      "stackalloc string select uint ulong unchecked unsafe ushort var ";
    +  var JSCRIPT_KEYWORDS = COMMON_KEYWORDS +
    +      "debugger eval export function get null set undefined var with " +
    +      "Infinity NaN ";
    +  var PERL_KEYWORDS = "caller delete die do dump elsif eval exit foreach for " +
    +      "goto if import last local my next no our print package redo require " +
    +      "sub undef unless until use wantarray while BEGIN END ";
    +  var PYTHON_KEYWORDS = FLOW_CONTROL_KEYWORDS + "and as assert class def del " +
    +      "elif except exec finally from global import in is lambda " +
    +      "nonlocal not or pass print raise try with yield " +
    +      "False True None ";
    +  var RUBY_KEYWORDS = FLOW_CONTROL_KEYWORDS + "alias and begin case class def" +
    +      " defined elsif end ensure false in module next nil not or redo rescue " +
    +      "retry self super then true undef unless until when yield BEGIN END ";
    +  var SH_KEYWORDS = FLOW_CONTROL_KEYWORDS + "case done elif esac eval fi " +
    +      "function in local set then until ";
    +  var ALL_KEYWORDS = (
    +      CPP_KEYWORDS + CSHARP_KEYWORDS + JSCRIPT_KEYWORDS + PERL_KEYWORDS +
    +      PYTHON_KEYWORDS + RUBY_KEYWORDS + SH_KEYWORDS);
    +
    +  // token style names.  correspond to css classes
    +  /** token style for a string literal */
    +  var PR_STRING = 'str';
    +  /** token style for a keyword */
    +  var PR_KEYWORD = 'kwd';
    +  /** token style for a comment */
    +  var PR_COMMENT = 'com';
    +  /** token style for a type */
    +  var PR_TYPE = 'typ';
    +  /** token style for a literal value.  e.g. 1, null, true. */
    +  var PR_LITERAL = 'lit';
    +  /** token style for a punctuation string. */
    +  var PR_PUNCTUATION = 'pun';
    +  /** token style for a punctuation string. */
    +  var PR_PLAIN = 'pln';
    +
    +  /** token style for an sgml tag. */
    +  var PR_TAG = 'tag';
    +  /** token style for a markup declaration such as a DOCTYPE. */
    +  var PR_DECLARATION = 'dec';
    +  /** token style for embedded source. */
    +  var PR_SOURCE = 'src';
    +  /** token style for an sgml attribute name. */
    +  var PR_ATTRIB_NAME = 'atn';
    +  /** token style for an sgml attribute value. */
    +  var PR_ATTRIB_VALUE = 'atv';
    +
    +  /**
    +   * A class that indicates a section of markup that is not code, e.g. to allow
    +   * embedding of line numbers within code listings.
    +   */
    +  var PR_NOCODE = 'nocode';
    +
    +  /** A set of tokens that can precede a regular expression literal in
    +    * javascript.
    +    * http://www.mozilla.org/js/language/js20/rationale/syntax.html has the full
    +    * list, but I've removed ones that might be problematic when seen in
    +    * languages that don't support regular expression literals.
    +    *
    +    * <p>Specifically, I've removed any keywords that can't precede a regexp
    +    * literal in a syntactically legal javascript program, and I've removed the
    +    * "in" keyword since it's not a keyword in many languages, and might be used
    +    * as a count of inches.
    +    *
    +    * <p>The link a above does not accurately describe EcmaScript rules since
    +    * it fails to distinguish between (a=++/b/i) and (a++/b/i) but it works
    +    * very well in practice.
    +    *
    +    * @private
    +    */
    +  var REGEXP_PRECEDER_PATTERN = function () {
    +      var preceders = [
    +          "!", "!=", "!==", "#", "%", "%=", "&", "&&", "&&=",
    +          "&=", "(", "*", "*=", /* "+", */ "+=", ",", /* "-", */ "-=",
    +          "->", /*".", "..", "...", handled below */ "/", "/=", ":", "::", ";",
    +          "<", "<<", "<<=", "<=", "=", "==", "===", ">",
    +          ">=", ">>", ">>=", ">>>", ">>>=", "?", "@", "[",
    +          "^", "^=", "^^", "^^=", "{", "|", "|=", "||",
    +          "||=", "~" /* handles =~ and !~ */,
    +          "break", "case", "continue", "delete",
    +          "do", "else", "finally", "instanceof",
    +          "return", "throw", "try", "typeof"
    +          ];
    +      var pattern = '(?:^^|[+-]';
    +      for (var i = 0; i < preceders.length; ++i) {
    +        pattern += '|' + preceders[i].replace(/([^=<>:&a-z])/g, '\\$1');
    +      }
    +      pattern += ')\\s*';  // matches at end, and matches empty string
    +      return pattern;
    +      // CAVEAT: this does not properly handle the case where a regular
    +      // expression immediately follows another since a regular expression may
    +      // have flags for case-sensitivity and the like.  Having regexp tokens
    +      // adjacent is not valid in any language I'm aware of, so I'm punting.
    +      // TODO: maybe style special characters inside a regexp as punctuation.
    +    }();
    +
    +  // Define regexps here so that the interpreter doesn't have to create an
    +  // object each time the function containing them is called.
    +  // The language spec requires a new object created even if you don't access
    +  // the $1 members.
    +  var pr_amp = /&/g;
    +  var pr_lt = /</g;
    +  var pr_gt = />/g;
    +  var pr_quot = /\"/g;
    +  /** like textToHtml but escapes double quotes to be attribute safe. */
    +  function attribToHtml(str) {
    +    return str.replace(pr_amp, '&amp;')
    +        .replace(pr_lt, '&lt;')
    +        .replace(pr_gt, '&gt;')
    +        .replace(pr_quot, '&quot;');
    +  }
    +
    +  /** escapest html special characters to html. */
    +  function textToHtml(str) {
    +    return str.replace(pr_amp, '&amp;')
    +        .replace(pr_lt, '&lt;')
    +        .replace(pr_gt, '&gt;');
    +  }
    +
    +
    +  var pr_ltEnt = /&lt;/g;
    +  var pr_gtEnt = /&gt;/g;
    +  var pr_aposEnt = /&apos;/g;
    +  var pr_quotEnt = /&quot;/g;
    +  var pr_ampEnt = /&amp;/g;
    +  var pr_nbspEnt = /&nbsp;/g;
    +  /** unescapes html to plain text. */
    +  function htmlToText(html) {
    +    var pos = html.indexOf('&');
    +    if (pos < 0) { return html; }
    +    // Handle numeric entities specially.  We can't use functional substitution
    +    // since that doesn't work in older versions of Safari.
    +    // These should be rare since most browsers convert them to normal chars.
    +    for (--pos; (pos = html.indexOf('&#', pos + 1)) >= 0;) {
    +      var end = html.indexOf(';', pos);
    +      if (end >= 0) {
    +        var num = html.substring(pos + 3, end);
    +        var radix = 10;
    +        if (num && num.charAt(0) === 'x') {
    +          num = num.substring(1);
    +          radix = 16;
    +        }
    +        var codePoint = parseInt(num, radix);
    +        if (!isNaN(codePoint)) {
    +          html = (html.substring(0, pos) + String.fromCharCode(codePoint) +
    +                  html.substring(end + 1));
    +        }
    +      }
    +    }
    +
    +    return html.replace(pr_ltEnt, '<')
    +        .replace(pr_gtEnt, '>')
    +        .replace(pr_aposEnt, "'")
    +        .replace(pr_quotEnt, '"')
    +        .replace(pr_nbspEnt, ' ')
    +        .replace(pr_ampEnt, '&');
    +  }
    +
    +  /** is the given node's innerHTML normally unescaped? */
    +  function isRawContent(node) {
    +    return 'XMP' === node.tagName;
    +  }
    +
    +  var newlineRe = /[\r\n]/g;
    +  /**
    +   * Are newlines and adjacent spaces significant in the given node's innerHTML?
    +   */
    +  function isPreformatted(node, content) {
    +    // PRE means preformatted, and is a very common case, so don't create
    +    // unnecessary computed style objects.
    +    if ('PRE' === node.tagName) { return true; }
    +    if (!newlineRe.test(content)) { return true; }  // Don't care
    +    var whitespace = '';
    +    // For disconnected nodes, IE has no currentStyle.
    +    if (node.currentStyle) {
    +      whitespace = node.currentStyle.whiteSpace;
    +    } else if (window.getComputedStyle) {
    +      // Firefox makes a best guess if node is disconnected whereas Safari
    +      // returns the empty string.
    +      whitespace = window.getComputedStyle(node, null).whiteSpace;
    +    }
    +    return !whitespace || whitespace === 'pre';
    +  }
    +
    +  function normalizedHtml(node, out) {
    +    switch (node.nodeType) {
    +      case 1:  // an element
    +        var name = node.tagName.toLowerCase();
    +        out.push('<', name);
    +        for (var i = 0; i < node.attributes.length; ++i) {
    +          var attr = node.attributes[i];
    +          if (!attr.specified) { continue; }
    +          out.push(' ');
    +          normalizedHtml(attr, out);
    +        }
    +        out.push('>');
    +        for (var child = node.firstChild; child; child = child.nextSibling) {
    +          normalizedHtml(child, out);
    +        }
    +        if (node.firstChild || !/^(?:br|link|img)$/.test(name)) {
    +          out.push('<\/', name, '>');
    +        }
    +        break;
    +      case 2: // an attribute
    +        out.push(node.name.toLowerCase(), '="', attribToHtml(node.value), '"');
    +        break;
    +      case 3: case 4: // text
    +        out.push(textToHtml(node.nodeValue));
    +        break;
    +    }
    +  }
    +
    +  /**
    +   * Given a group of {@link RegExp}s, returns a {@code RegExp} that globally
    +   * matches the union o the sets o strings matched d by the input RegExp.
    +   * Since it matches globally, if the input strings have a start-of-input
    +   * anchor (/^.../), it is ignored for the purposes of unioning.
    +   * @param {Array.<RegExp>} regexs non multiline, non-global regexs.
    +   * @return {RegExp} a global regex.
    +   */
    +  function combinePrefixPatterns(regexs) {
    +    var capturedGroupIndex = 0;
    +
    +    var needToFoldCase = false;
    +    var ignoreCase = false;
    +    for (var i = 0, n = regexs.length; i < n; ++i) {
    +      var regex = regexs[i];
    +      if (regex.ignoreCase) {
    +        ignoreCase = true;
    +      } else if (/[a-z]/i.test(regex.source.replace(
    +                     /\\u[0-9a-f]{4}|\\x[0-9a-f]{2}|\\[^ux]/gi, ''))) {
    +        needToFoldCase = true;
    +        ignoreCase = false;
    +        break;
    +      }
    +    }
    +
    +    function decodeEscape(charsetPart) {
    +      if (charsetPart.charAt(0) !== '\\') { return charsetPart.charCodeAt(0); }
    +      switch (charsetPart.charAt(1)) {
    +        case 'b': return 8;
    +        case 't': return 9;
    +        case 'n': return 0xa;
    +        case 'v': return 0xb;
    +        case 'f': return 0xc;
    +        case 'r': return 0xd;
    +        case 'u': case 'x':
    +          return parseInt(charsetPart.substring(2), 16)
    +              || charsetPart.charCodeAt(1);
    +        case '0': case '1': case '2': case '3': case '4':
    +        case '5': case '6': case '7':
    +          return parseInt(charsetPart.substring(1), 8);
    +        default: return charsetPart.charCodeAt(1);
    +      }
    +    }
    +
    +    function encodeEscape(charCode) {
    +      if (charCode < 0x20) {
    +        return (charCode < 0x10 ? '\\x0' : '\\x') + charCode.toString(16);
    +      }
    +      var ch = String.fromCharCode(charCode);
    +      if (ch === '\\' || ch === '-' || ch === '[' || ch === ']') {
    +        ch = '\\' + ch;
    +      }
    +      return ch;
    +    }
    +
    +    function caseFoldCharset(charSet) {
    +      var charsetParts = charSet.substring(1, charSet.length - 1).match(
    +          new RegExp(
    +              '\\\\u[0-9A-Fa-f]{4}'
    +              + '|\\\\x[0-9A-Fa-f]{2}'
    +              + '|\\\\[0-3][0-7]{0,2}'
    +              + '|\\\\[0-7]{1,2}'
    +              + '|\\\\[\\s\\S]'
    +              + '|-'
    +              + '|[^-\\\\]',
    +              'g'));
    +      var groups = [];
    +      var ranges = [];
    +      var inverse = charsetParts[0] === '^';
    +      for (var i = inverse ? 1 : 0, n = charsetParts.length; i < n; ++i) {
    +        var p = charsetParts[i];
    +        switch (p) {
    +          case '\\B': case '\\b':
    +          case '\\D': case '\\d':
    +          case '\\S': case '\\s':
    +          case '\\W': case '\\w':
    +            groups.push(p);
    +            continue;
    +        }
    +        var start = decodeEscape(p);
    +        var end;
    +        if (i + 2 < n && '-' === charsetParts[i + 1]) {
    +          end = decodeEscape(charsetParts[i + 2]);
    +          i += 2;
    +        } else {
    +          end = start;
    +        }
    +        ranges.push([start, end]);
    +        // If the range might intersect letters, then expand it.
    +        if (!(end < 65 || start > 122)) {
    +          if (!(end < 65 || start > 90)) {
    +            ranges.push([Math.max(65, start) | 32, Math.min(end, 90) | 32]);
    +          }
    +          if (!(end < 97 || start > 122)) {
    +            ranges.push([Math.max(97, start) & ~32, Math.min(end, 122) & ~32]);
    +          }
    +        }
    +      }
    +
    +      // [[1, 10], [3, 4], [8, 12], [14, 14], [16, 16], [17, 17]]
    +      // -> [[1, 12], [14, 14], [16, 17]]
    +      ranges.sort(function (a, b) { return (a[0] - b[0]) || (b[1]  - a[1]); });
    +      var consolidatedRanges = [];
    +      var lastRange = [NaN, NaN];
    +      for (var i = 0; i < ranges.length; ++i) {
    +        var range = ranges[i];
    +        if (range[0] <= lastRange[1] + 1) {
    +          lastRange[1] = Math.max(lastRange[1], range[1]);
    +        } else {
    +          consolidatedRanges.push(lastRange = range);
    +        }
    +      }
    +
    +      var out = ['['];
    +      if (inverse) { out.push('^'); }
    +      out.push.apply(out, groups);
    +      for (var i = 0; i < consolidatedRanges.length; ++i) {
    +        var range = consolidatedRanges[i];
    +        out.push(encodeEscape(range[0]));
    +        if (range[1] > range[0]) {
    +          if (range[1] + 1 > range[0]) { out.push('-'); }
    +          out.push(encodeEscape(range[1]));
    +        }
    +      }
    +      out.push(']');
    +      return out.join('');
    +    }
    +
    +    function allowAnywhereFoldCaseAndRenumberGroups(regex) {
    +      // Split into character sets, escape sequences, punctuation strings
    +      // like ('(', '(?:', ')', '^'), and runs of characters that do not
    +      // include any of the above.
    +      var parts = regex.source.match(
    +          new RegExp(
    +              '(?:'
    +              + '\\[(?:[^\\x5C\\x5D]|\\\\[\\s\\S])*\\]'  // a character set
    +              + '|\\\\u[A-Fa-f0-9]{4}'  // a unicode escape
    +              + '|\\\\x[A-Fa-f0-9]{2}'  // a hex escape
    +              + '|\\\\[0-9]+'  // a back-reference or octal escape
    +              + '|\\\\[^ux0-9]'  // other escape sequence
    +              + '|\\(\\?[:!=]'  // start of a non-capturing group
    +              + '|[\\(\\)\\^]'  // start/emd of a group, or line start
    +              + '|[^\\x5B\\x5C\\(\\)\\^]+'  // run of other characters
    +              + ')',
    +              'g'));
    +      var n = parts.length;
    +
    +      // Maps captured group numbers to the number they will occupy in
    +      // the output or to -1 if that has not been determined, or to
    +      // undefined if they need not be capturing in the output.
    +      var capturedGroups = [];
    +
    +      // Walk over and identify back references to build the capturedGroups
    +      // mapping.
    +      for (var i = 0, groupIndex = 0; i < n; ++i) {
    +        var p = parts[i];
    +        if (p === '(') {
    +          // groups are 1-indexed, so max group index is count of '('
    +          ++groupIndex;
    +        } else if ('\\' === p.charAt(0)) {
    +          var decimalValue = +p.substring(1);
    +          if (decimalValue && decimalValue <= groupIndex) {
    +            capturedGroups[decimalValue] = -1;
    +          }
    +        }
    +      }
    +
    +      // Renumber groups and reduce capturing groups to non-capturing groups
    +      // where possible.
    +      for (var i = 1; i < capturedGroups.length; ++i) {
    +        if (-1 === capturedGroups[i]) {
    +          capturedGroups[i] = ++capturedGroupIndex;
    +        }
    +      }
    +      for (var i = 0, groupIndex = 0; i < n; ++i) {
    +        var p = parts[i];
    +        if (p === '(') {
    +          ++groupIndex;
    +          if (capturedGroups[groupIndex] === undefined) {
    +            parts[i] = '(?:';
    +          }
    +        } else if ('\\' === p.charAt(0)) {
    +          var decimalValue = +p.substring(1);
    +          if (decimalValue && decimalValue <= groupIndex) {
    +            parts[i] = '\\' + capturedGroups[groupIndex];
    +          }
    +        }
    +      }
    +
    +      // Remove any prefix anchors so that the output will match anywhere.
    +      // ^^ really does mean an anchored match though.
    +      for (var i = 0, groupIndex = 0; i < n; ++i) {
    +        if ('^' === parts[i] && '^' !== parts[i + 1]) { parts[i] = ''; }
    +      }
    +
    +      // Expand letters to groupts to handle mixing of case-sensitive and
    +      // case-insensitive patterns if necessary.
    +      if (regex.ignoreCase && needToFoldCase) {
    +        for (var i = 0; i < n; ++i) {
    +          var p = parts[i];
    +          var ch0 = p.charAt(0);
    +          if (p.length >= 2 && ch0 === '[') {
    +            parts[i] = caseFoldCharset(p);
    +          } else if (ch0 !== '\\') {
    +            // TODO: handle letters in numeric escapes.
    +            parts[i] = p.replace(
    +                /[a-zA-Z]/g,
    +                function (ch) {
    +                  var cc = ch.charCodeAt(0);
    +                  return '[' + String.fromCharCode(cc & ~32, cc | 32) + ']';
    +                });
    +          }
    +        }
    +      }
    +
    +      return parts.join('');
    +    }
    +
    +    var rewritten = [];
    +    for (var i = 0, n = regexs.length; i < n; ++i) {
    +      var regex = regexs[i];
    +      if (regex.global || regex.multiline) { throw new Error('' + regex); }
    +      rewritten.push(
    +          '(?:' + allowAnywhereFoldCaseAndRenumberGroups(regex) + ')');
    +    }
    +
    +    return new RegExp(rewritten.join('|'), ignoreCase ? 'gi' : 'g');
    +  }
    +
    +  var PR_innerHtmlWorks = null;
    +  function getInnerHtml(node) {
    +    // inner html is hopelessly broken in Safari 2.0.4 when the content is
    +    // an html description of well formed XML and the containing tag is a PRE
    +    // tag, so we detect that case and emulate innerHTML.
    +    if (null === PR_innerHtmlWorks) {
    +      var testNode = document.createElement('PRE');
    +      testNode.appendChild(
    +          document.createTextNode('<!DOCTYPE foo PUBLIC "foo bar">\n<foo />'));
    +      PR_innerHtmlWorks = !/</.test(testNode.innerHTML);
    +    }
    +
    +    if (PR_innerHtmlWorks) {
    +      var content = node.innerHTML;
    +      // XMP tags contain unescaped entities so require special handling.
    +      if (isRawContent(node)) {
    +        content = textToHtml(content);
    +      } else if (!isPreformatted(node, content)) {
    +        content = content.replace(/(<br\s*\/?>)[\r\n]+/g, '$1')
    +            .replace(/(?:[\r\n]+[ \t]*)+/g, ' ');
    +      }
    +      return content;
    +    }
    +
    +    var out = [];
    +    for (var child = node.firstChild; child; child = child.nextSibling) {
    +      normalizedHtml(child, out);
    +    }
    +    return out.join('');
    +  }
    +
    +  /** returns a function that expand tabs to spaces.  This function can be fed
    +    * successive chunks of text, and will maintain its own internal state to
    +    * keep track of how tabs are expanded.
    +    * @return {function (string) : string} a function that takes
    +    *   plain text and return the text with tabs expanded.
    +    * @private
    +    */
    +  function makeTabExpander(tabWidth) {
    +    var SPACES = '                ';
    +    var charInLine = 0;
    +
    +    return function (plainText) {
    +      // walk over each character looking for tabs and newlines.
    +      // On tabs, expand them.  On newlines, reset charInLine.
    +      // Otherwise increment charInLine
    +      var out = null;
    +      var pos = 0;
    +      for (var i = 0, n = plainText.length; i < n; ++i) {
    +        var ch = plainText.charAt(i);
    +
    +        switch (ch) {
    +          case '\t':
    +            if (!out) { out = []; }
    +            out.push(plainText.substring(pos, i));
    +            // calculate how much space we need in front of this part
    +            // nSpaces is the amount of padding -- the number of spaces needed
    +            // to move us to the next column, where columns occur at factors of
    +            // tabWidth.
    +            var nSpaces = tabWidth - (charInLine % tabWidth);
    +            charInLine += nSpaces;
    +            for (; nSpaces >= 0; nSpaces -= SPACES.length) {
    +              out.push(SPACES.substring(0, nSpaces));
    +            }
    +            pos = i + 1;
    +            break;
    +          case '\n':
    +            charInLine = 0;
    +            break;
    +          default:
    +            ++charInLine;
    +        }
    +      }
    +      if (!out) { return plainText; }
    +      out.push(plainText.substring(pos));
    +      return out.join('');
    +    };
    +  }
    +
    +  var pr_chunkPattern = new RegExp(
    +      '[^<]+'  // A run of characters other than '<'
    +      + '|<\!--[\\s\\S]*?--\>'  // an HTML comment
    +      + '|<!\\[CDATA\\[[\\s\\S]*?\\]\\]>'  // a CDATA section
    +      // a probable tag that should not be highlighted
    +      + '|<\/?[a-zA-Z](?:[^>\"\']|\'[^\']*\'|\"[^\"]*\")*>'
    +      + '|<',  // A '<' that does not begin a larger chunk
    +      'g');
    +  var pr_commentPrefix = /^<\!--/;
    +  var pr_cdataPrefix = /^<!\[CDATA\[/;
    +  var pr_brPrefix = /^<br\b/i;
    +  var pr_tagNameRe = /^<(\/?)([a-zA-Z][a-zA-Z0-9]*)/;
    +
    +  /** split markup into chunks of html tags (style null) and
    +    * plain text (style {@link #PR_PLAIN}), converting tags which are
    +    * significant for tokenization (<br>) into their textual equivalent.
    +    *
    +    * @param {string} s html where whitespace is considered significant.
    +    * @return {Object} source code and extracted tags.
    +    * @private
    +    */
    +  function extractTags(s) {
    +    // since the pattern has the 'g' modifier and defines no capturing groups,
    +    // this will return a list of all chunks which we then classify and wrap as
    +    // PR_Tokens
    +    var matches = s.match(pr_chunkPattern);
    +    var sourceBuf = [];
    +    var sourceBufLen = 0;
    +    var extractedTags = [];
    +    if (matches) {
    +      for (var i = 0, n = matches.length; i < n; ++i) {
    +        var match = matches[i];
    +        if (match.length > 1 && match.charAt(0) === '<') {
    +          if (pr_commentPrefix.test(match)) { continue; }
    +          if (pr_cdataPrefix.test(match)) {
    +            // strip CDATA prefix and suffix.  Don't unescape since it's CDATA
    +            sourceBuf.push(match.substring(9, match.length - 3));
    +            sourceBufLen += match.length - 12;
    +          } else if (pr_brPrefix.test(match)) {
    +            // <br> tags are lexically significant so convert them to text.
    +            // This is undone later.
    +            sourceBuf.push('\n');
    +            ++sourceBufLen;
    +          } else {
    +            if (match.indexOf(PR_NOCODE) >= 0 && isNoCodeTag(match)) {
    +              // A <span class="nocode"> will start a section that should be
    +              // ignored.  Continue walking the list until we see a matching end
    +              // tag.
    +              var name = match.match(pr_tagNameRe)[2];
    +              var depth = 1;
    +              var j;
    +              end_tag_loop:
    +              for (j = i + 1; j < n; ++j) {
    +                var name2 = matches[j].match(pr_tagNameRe);
    +                if (name2 && name2[2] === name) {
    +                  if (name2[1] === '/') {
    +                    if (--depth === 0) { break end_tag_loop; }
    +                  } else {
    +                    ++depth;
    +                  }
    +                }
    +              }
    +              if (j < n) {
    +                extractedTags.push(
    +                    sourceBufLen, matches.slice(i, j + 1).join(''));
    +                i = j;
    +              } else {  // Ignore unclosed sections.
    +                extractedTags.push(sourceBufLen, match);
    +              }
    +            } else {
    +              extractedTags.push(sourceBufLen, match);
    +            }
    +          }
    +        } else {
    +          var literalText = htmlToText(match);
    +          sourceBuf.push(literalText);
    +          sourceBufLen += literalText.length;
    +        }
    +      }
    +    }
    +    return { source: sourceBuf.join(''), tags: extractedTags };
    +  }
    +
    +  /** True if the given tag contains a class attribute with the nocode class. */
    +  function isNoCodeTag(tag) {
    +    return !!tag
    +        // First canonicalize the representation of attributes
    +        .replace(/\s(\w+)\s*=\s*(?:\"([^\"]*)\"|'([^\']*)'|(\S+))/g,
    +                 ' $1="$2$3$4"')
    +        // Then look for the attribute we want.
    +        .match(/[cC][lL][aA][sS][sS]=\"[^\"]*\bnocode\b/);
    +  }
    +
    +  /**
    +   * Apply the given language handler to sourceCode and add the resulting
    +   * decorations to out.
    +   * @param {number} basePos the index of sourceCode within the chunk of source
    +   *    whose decorations are already present on out.
    +   */
    +  function appendDecorations(basePos, sourceCode, langHandler, out) {
    +    if (!sourceCode) { return; }
    +    var job = {
    +      source: sourceCode,
    +      basePos: basePos
    +    };
    +    langHandler(job);
    +    out.push.apply(out, job.decorations);
    +  }
    +
    +  /** Given triples of [style, pattern, context] returns a lexing function,
    +    * The lexing function interprets the patterns to find token boundaries and
    +    * returns a decoration list of the form
    +    * [index_0, style_0, index_1, style_1, ..., index_n, style_n]
    +    * where index_n is an index into the sourceCode, and style_n is a style
    +    * constant like PR_PLAIN.  index_n-1 <= index_n, and style_n-1 applies to
    +    * all characters in sourceCode[index_n-1:index_n].
    +    *
    +    * The stylePatterns is a list whose elements have the form
    +    * [style : string, pattern : RegExp, DEPRECATED, shortcut : string].
    +    *
    +    * Style is a style constant like PR_PLAIN, or can be a string of the
    +    * form 'lang-FOO', where FOO is a language extension describing the
    +    * language of the portion of the token in $1 after pattern executes.
    +    * E.g., if style is 'lang-lisp', and group 1 contains the text
    +    * '(hello (world))', then that portion of the token will be passed to the
    +    * registered lisp handler for formatting.
    +    * The text before and after group 1 will be restyled using this decorator
    +    * so decorators should take care that this doesn't result in infinite
    +    * recursion.  For example, the HTML lexer rule for SCRIPT elements looks
    +    * something like ['lang-js', /<[s]cript>(.+?)<\/script>/].  This may match
    +    * '<script>foo()<\/script>', which would cause the current decorator to
    +    * be called with '<script>' which would not match the same rule since
    +    * group 1 must not be empty, so it would be instead styled as PR_TAG by
    +    * the generic tag rule.  The handler registered for the 'js' extension would
    +    * then be called with 'foo()', and finally, the current decorator would
    +    * be called with '<\/script>' which would not match the original rule and
    +    * so the generic tag rule would identify it as a tag.
    +    *
    +    * Pattern must only match prefixes, and if it matches a prefix, then that
    +    * match is considered a token with the same style.
    +    *
    +    * Context is applied to the last non-whitespace, non-comment token
    +    * recognized.
    +    *
    +    * Shortcut is an optional string of characters, any of which, if the first
    +    * character, gurantee that this pattern and only this pattern matches.
    +    *
    +    * @param {Array} shortcutStylePatterns patterns that always start with
    +    *   a known character.  Must have a shortcut string.
    +    * @param {Array} fallthroughStylePatterns patterns that will be tried in
    +    *   order if the shortcut ones fail.  May have shortcuts.
    +    *
    +    * @return {function (Object)} a
    +    *   function that takes source code and returns a list of decorations.
    +    */
    +  function createSimpleLexer(shortcutStylePatterns, fallthroughStylePatterns) {
    +    var shortcuts = {};
    +    var tokenizer;
    +    (function () {
    +      var allPatterns = shortcutStylePatterns.concat(fallthroughStylePatterns);
    +      var allRegexs = [];
    +      var regexKeys = {};
    +      for (var i = 0, n = allPatterns.length; i < n; ++i) {
    +        var patternParts = allPatterns[i];
    +        var shortcutChars = patternParts[3];
    +        if (shortcutChars) {
    +          for (var c = shortcutChars.length; --c >= 0;) {
    +            shortcuts[shortcutChars.charAt(c)] = patternParts;
    +          }
    +        }
    +        var regex = patternParts[1];
    +        var k = '' + regex;
    +        if (!regexKeys.hasOwnProperty(k)) {
    +          allRegexs.push(regex);
    +          regexKeys[k] = null;
    +        }
    +      }
    +      allRegexs.push(/[\0-\uffff]/);
    +      tokenizer = combinePrefixPatterns(allRegexs);
    +    })();
    +
    +    var nPatterns = fallthroughStylePatterns.length;
    +    var notWs = /\S/;
    +
    +    /**
    +     * Lexes job.source and produces an output array job.decorations of style
    +     * classes preceded by the position at which they start in job.source in
    +     * order.
    +     *
    +     * @param {Object} job an object like {@code
    +     *    source: {string} sourceText plain text,
    +     *    basePos: {int} position of job.source in the larger chunk of
    +     *        sourceCode.
    +     * }
    +     */
    +    var decorate = function (job) {
    +      var sourceCode = job.source, basePos = job.basePos;
    +      /** Even entries are positions in source in ascending order.  Odd enties
    +        * are style markers (e.g., PR_COMMENT) that run from that position until
    +        * the end.
    +        * @type {Array.<number|string>}
    +        */
    +      var decorations = [basePos, PR_PLAIN];
    +      var pos = 0;  // index into sourceCode
    +      var tokens = sourceCode.match(tokenizer) || [];
    +      var styleCache = {};
    +
    +      for (var ti = 0, nTokens = tokens.length; ti < nTokens; ++ti) {
    +        var token = tokens[ti];
    +        var style = styleCache[token];
    +        var match = void 0;
    +
    +        var isEmbedded;
    +        if (typeof style === 'string') {
    +          isEmbedded = false;
    +        } else {
    +          var patternParts = shortcuts[token.charAt(0)];
    +          if (patternParts) {
    +            match = token.match(patternParts[1]);
    +            style = patternParts[0];
    +          } else {
    +            for (var i = 0; i < nPatterns; ++i) {
    +              patternParts = fallthroughStylePatterns[i];
    +              match = token.match(patternParts[1]);
    +              if (match) {
    +                style = patternParts[0];
    +                break;
    +              }
    +            }
    +
    +            if (!match) {  // make sure that we make progress
    +              style = PR_PLAIN;
    +            }
    +          }
    +
    +          isEmbedded = style.length >= 5 && 'lang-' === style.substring(0, 5);
    +          if (isEmbedded && !(match && typeof match[1] === 'string')) {
    +            isEmbedded = false;
    +            style = PR_SOURCE;
    +          }
    +
    +          if (!isEmbedded) { styleCache[token] = style; }
    +        }
    +
    +        var tokenStart = pos;
    +        pos += token.length;
    +
    +        if (!isEmbedded) {
    +          decorations.push(basePos + tokenStart, style);
    +        } else {  // Treat group 1 as an embedded block of source code.
    +          var embeddedSource = match[1];
    +          var embeddedSourceStart = token.indexOf(embeddedSource);
    +          var embeddedSourceEnd = embeddedSourceStart + embeddedSource.length;
    +          if (match[2]) {
    +            // If embeddedSource can be blank, then it would match at the
    +            // beginning which would cause us to infinitely recurse on the
    +            // entire token, so we catch the right context in match[2].
    +            embeddedSourceEnd = token.length - match[2].length;
    +            embeddedSourceStart = embeddedSourceEnd - embeddedSource.length;
    +          }
    +          var lang = style.substring(5);
    +          // Decorate the left of the embedded source
    +          appendDecorations(
    +              basePos + tokenStart,
    +              token.substring(0, embeddedSourceStart),
    +              decorate, decorations);
    +          // Decorate the embedded source
    +          appendDecorations(
    +              basePos + tokenStart + embeddedSourceStart,
    +              embeddedSource,
    +              langHandlerForExtension(lang, embeddedSource),
    +              decorations);
    +          // Decorate the right of the embedded section
    +          appendDecorations(
    +              basePos + tokenStart + embeddedSourceEnd,
    +              token.substring(embeddedSourceEnd),
    +              decorate, decorations);
    +        }
    +      }
    +      job.decorations = decorations;
    +    };
    +    return decorate;
    +  }
    +
    +  /** returns a function that produces a list of decorations from source text.
    +    *
    +    * This code treats ", ', and ` as string delimiters, and \ as a string
    +    * escape.  It does not recognize perl's qq() style strings.
    +    * It has no special handling for double delimiter escapes as in basic, or
    +    * the tripled delimiters used in python, but should work on those regardless
    +    * although in those cases a single string literal may be broken up into
    +    * multiple adjacent string literals.
    +    *
    +    * It recognizes C, C++, and shell style comments.
    +    *
    +    * @param {Object} options a set of optional parameters.
    +    * @return {function (Object)} a function that examines the source code
    +    *     in the input job and builds the decoration list.
    +    */
    +  function sourceDecorator(options) {
    +    var shortcutStylePatterns = [], fallthroughStylePatterns = [];
    +    if (options['tripleQuotedStrings']) {
    +      // '''multi-line-string''', 'single-line-string', and double-quoted
    +      shortcutStylePatterns.push(
    +          [PR_STRING,  /^(?:\'\'\'(?:[^\'\\]|\\[\s\S]|\'{1,2}(?=[^\']))*(?:\'\'\'|$)|\"\"\"(?:[^\"\\]|\\[\s\S]|\"{1,2}(?=[^\"]))*(?:\"\"\"|$)|\'(?:[^\\\']|\\[\s\S])*(?:\'|$)|\"(?:[^\\\"]|\\[\s\S])*(?:\"|$))/,
    +           null, '\'"']);
    +    } else if (options['multiLineStrings']) {
    +      // 'multi-line-string', "multi-line-string"
    +      shortcutStylePatterns.push(
    +          [PR_STRING,  /^(?:\'(?:[^\\\']|\\[\s\S])*(?:\'|$)|\"(?:[^\\\"]|\\[\s\S])*(?:\"|$)|\`(?:[^\\\`]|\\[\s\S])*(?:\`|$))/,
    +           null, '\'"`']);
    +    } else {
    +      // 'single-line-string', "single-line-string"
    +      shortcutStylePatterns.push(
    +          [PR_STRING,
    +           /^(?:\'(?:[^\\\'\r\n]|\\.)*(?:\'|$)|\"(?:[^\\\"\r\n]|\\.)*(?:\"|$))/,
    +           null, '"\'']);
    +    }
    +    if (options['verbatimStrings']) {
    +      // verbatim-string-literal production from the C# grammar.  See issue 93.
    +      fallthroughStylePatterns.push(
    +          [PR_STRING, /^@\"(?:[^\"]|\"\")*(?:\"|$)/, null]);
    +    }
    +    if (options['hashComments']) {
    +      if (options['cStyleComments']) {
    +        // Stop C preprocessor declarations at an unclosed open comment
    +        shortcutStylePatterns.push(
    +            [PR_COMMENT, /^#(?:(?:define|elif|else|endif|error|ifdef|include|ifndef|line|pragma|undef|warning)\b|[^\r\n]*)/,
    +             null, '#']);
    +        fallthroughStylePatterns.push(
    +            [PR_STRING,
    +             /^<(?:(?:(?:\.\.\/)*|\/?)(?:[\w-]+(?:\/[\w-]+)+)?[\w-]+\.h|[a-z]\w*)>/,
    +             null]);
    +      } else {
    +        shortcutStylePatterns.push([PR_COMMENT, /^#[^\r\n]*/, null, '#']);
    +      }
    +    }
    +    if (options['cStyleComments']) {
    +      fallthroughStylePatterns.push([PR_COMMENT, /^\/\/[^\r\n]*/, null]);
    +      fallthroughStylePatterns.push(
    +          [PR_COMMENT, /^\/\*[\s\S]*?(?:\*\/|$)/, null]);
    +    }
    +    if (options['regexLiterals']) {
    +      var REGEX_LITERAL = (
    +          // A regular expression literal starts with a slash that is
    +          // not followed by * or / so that it is not confused with
    +          // comments.
    +          '/(?=[^/*])'
    +          // and then contains any number of raw characters,
    +          + '(?:[^/\\x5B\\x5C]'
    +          // escape sequences (\x5C),
    +          +    '|\\x5C[\\s\\S]'
    +          // or non-nesting character sets (\x5B\x5D);
    +          +    '|\\x5B(?:[^\\x5C\\x5D]|\\x5C[\\s\\S])*(?:\\x5D|$))+'
    +          // finally closed by a /.
    +          + '/');
    +      fallthroughStylePatterns.push(
    +          ['lang-regex',
    +           new RegExp('^' + REGEXP_PRECEDER_PATTERN + '(' + REGEX_LITERAL + ')')
    +           ]);
    +    }
    +
    +    var keywords = options['keywords'].replace(/^\s+|\s+$/g, '');
    +    if (keywords.length) {
    +      fallthroughStylePatterns.push(
    +          [PR_KEYWORD,
    +           new RegExp('^(?:' + keywords.replace(/\s+/g, '|') + ')\\b'), null]);
    +    }
    +
    +    shortcutStylePatterns.push([PR_PLAIN,       /^\s+/, null, ' \r\n\t\xA0']);
    +    fallthroughStylePatterns.push(
    +        // TODO(mikesamuel): recognize non-latin letters and numerals in idents
    +        [PR_LITERAL,     /^@[a-z_$][a-z_$@0-9]*/i, null],
    +        [PR_TYPE,        /^@?[A-Z]+[a-z][A-Za-z_$@0-9]*/, null],
    +        [PR_PLAIN,       /^[a-z_$][a-z_$@0-9]*/i, null],
    +        [PR_LITERAL,
    +         new RegExp(
    +             '^(?:'
    +             // A hex number
    +             + '0x[a-f0-9]+'
    +             // or an octal or decimal number,
    +             + '|(?:\\d(?:_\\d+)*\\d*(?:\\.\\d*)?|\\.\\d\\+)'
    +             // possibly in scientific notation
    +             + '(?:e[+\\-]?\\d+)?'
    +             + ')'
    +             // with an optional modifier like UL for unsigned long
    +             + '[a-z]*', 'i'),
    +         null, '0123456789'],
    +        [PR_PUNCTUATION, /^.[^\s\w\.$@\'\"\`\/\#]*/, null]);
    +
    +    return createSimpleLexer(shortcutStylePatterns, fallthroughStylePatterns);
    +  }
    +
    +  var decorateSource = sourceDecorator({
    +        'keywords': ALL_KEYWORDS,
    +        'hashComments': true,
    +        'cStyleComments': true,
    +        'multiLineStrings': true,
    +        'regexLiterals': true
    +      });
    +
    +  /** Breaks {@code job.source} around style boundaries in
    +    * {@code job.decorations} while re-interleaving {@code job.extractedTags},
    +    * and leaves the result in {@code job.prettyPrintedHtml}.
    +    * @param {Object} job like {
    +    *    source: {string} source as plain text,
    +    *    extractedTags: {Array.<number|string>} extractedTags chunks of raw
    +    *                   html preceded by their position in {@code job.source}
    +    *                   in order
    +    *    decorations: {Array.<number|string} an array of style classes preceded
    +    *                 by the position at which they start in job.source in order
    +    * }
    +    * @private
    +    */
    +  function recombineTagsAndDecorations(job) {
    +    var sourceText = job.source;
    +    var extractedTags = job.extractedTags;
    +    var decorations = job.decorations;
    +
    +    var html = [];
    +    // index past the last char in sourceText written to html
    +    var outputIdx = 0;
    +
    +    var openDecoration = null;
    +    var currentDecoration = null;
    +    var tagPos = 0;  // index into extractedTags
    +    var decPos = 0;  // index into decorations
    +    var tabExpander = makeTabExpander(window['PR_TAB_WIDTH']);
    +
    +    var adjacentSpaceRe = /([\r\n ]) /g;
    +    var startOrSpaceRe = /(^| ) /gm;
    +    var newlineRe = /\r\n?|\n/g;
    +    var trailingSpaceRe = /[ \r\n]$/;
    +    var lastWasSpace = true;  // the last text chunk emitted ended with a space.
    +
    +    // A helper function that is responsible for opening sections of decoration
    +    // and outputing properly escaped chunks of source
    +    function emitTextUpTo(sourceIdx) {
    +      if (sourceIdx > outputIdx) {
    +        if (openDecoration && openDecoration !== currentDecoration) {
    +          // Close the current decoration
    +          html.push('</span>');
    +          openDecoration = null;
    +        }
    +        if (!openDecoration && currentDecoration) {
    +          openDecoration = currentDecoration;
    +          html.push('<span class="', openDecoration, '">');
    +        }
    +        // This interacts badly with some wikis which introduces paragraph tags
    +        // into pre blocks for some strange reason.
    +        // It's necessary for IE though which seems to lose the preformattedness
    +        // of <pre> tags when their innerHTML is assigned.
    +        // http://stud3.tuwien.ac.at/~e0226430/innerHtmlQuirk.html
    +        // and it serves to undo the conversion of <br>s to newlines done in
    +        // chunkify.
    +        var htmlChunk = textToHtml(
    +            tabExpander(sourceText.substring(outputIdx, sourceIdx)))
    +            .replace(lastWasSpace
    +                     ? startOrSpaceRe
    +                     : adjacentSpaceRe, '$1&nbsp;');
    +        // Keep track of whether we need to escape space at the beginning of the
    +        // next chunk.
    +        lastWasSpace = trailingSpaceRe.test(htmlChunk);
    +        // IE collapses multiple adjacient <br>s into 1 line break.
    +        // Prefix every <br> with '&nbsp;' can prevent such IE's behavior.
    +        var lineBreakHtml = window['_pr_isIE6']() ? '&nbsp;<br />' : '<br />';
    +        html.push(htmlChunk.replace(newlineRe, lineBreakHtml));
    +        outputIdx = sourceIdx;
    +      }
    +    }
    +
    +    while (true) {
    +      // Determine if we're going to consume a tag this time around.  Otherwise
    +      // we consume a decoration or exit.
    +      var outputTag;
    +      if (tagPos < extractedTags.length) {
    +        if (decPos < decorations.length) {
    +          // Pick one giving preference to extractedTags since we shouldn't open
    +          // a new style that we're going to have to immediately close in order
    +          // to output a tag.
    +          outputTag = extractedTags[tagPos] <= decorations[decPos];
    +        } else {
    +          outputTag = true;
    +        }
    +      } else {
    +        outputTag = false;
    +      }
    +      // Consume either a decoration or a tag or exit.
    +      if (outputTag) {
    +        emitTextUpTo(extractedTags[tagPos]);
    +        if (openDecoration) {
    +          // Close the current decoration
    +          html.push('</span>');
    +          openDecoration = null;
    +        }
    +        html.push(extractedTags[tagPos + 1]);
    +        tagPos += 2;
    +      } else if (decPos < decorations.length) {
    +        emitTextUpTo(decorations[decPos]);
    +        currentDecoration = decorations[decPos + 1];
    +        decPos += 2;
    +      } else {
    +        break;
    +      }
    +    }
    +    emitTextUpTo(sourceText.length);
    +    if (openDecoration) {
    +      html.push('</span>');
    +    }
    +    job.prettyPrintedHtml = html.join('');
    +  }
    +
    +  /** Maps language-specific file extensions to handlers. */
    +  var langHandlerRegistry = {};
    +  /** Register a language handler for the given file extensions.
    +    * @param {function (Object)} handler a function from source code to a list
    +    *      of decorations.  Takes a single argument job which describes the
    +    *      state of the computation.   The single parameter has the form
    +    *      {@code {
    +    *        source: {string} as plain text.
    +    *        decorations: {Array.<number|string>} an array of style classes
    +    *                     preceded by the position at which they start in
    +    *                     job.source in order.
    +    *                     The language handler should assigned this field.
    +    *        basePos: {int} the position of source in the larger source chunk.
    +    *                 All positions in the output decorations array are relative
    +    *                 to the larger source chunk.
    +    *      } }
    +    * @param {Array.<string>} fileExtensions
    +    */
    +  function registerLangHandler(handler, fileExtensions) {
    +    for (var i = fileExtensions.length; --i >= 0;) {
    +      var ext = fileExtensions[i];
    +      if (!langHandlerRegistry.hasOwnProperty(ext)) {
    +        langHandlerRegistry[ext] = handler;
    +      } else if ('console' in window) {
    +        console.warn('cannot override language handler %s', ext);
    +      }
    +    }
    +  }
    +  function langHandlerForExtension(extension, source) {
    +    if (!(extension && langHandlerRegistry.hasOwnProperty(extension))) {
    +      // Treat it as markup if the first non whitespace character is a < and
    +      // the last non-whitespace character is a >.
    +      extension = /^\s*</.test(source)
    +          ? 'default-markup'
    +          : 'default-code';
    +    }
    +    return langHandlerRegistry[extension];
    +  }
    +  registerLangHandler(decorateSource, ['default-code']);
    +  registerLangHandler(
    +      createSimpleLexer(
    +          [],
    +          [
    +           [PR_PLAIN,       /^[^<?]+/],
    +           [PR_DECLARATION, /^<!\w[^>]*(?:>|$)/],
    +           [PR_COMMENT,     /^<\!--[\s\S]*?(?:-\->|$)/],
    +           // Unescaped content in an unknown language
    +           ['lang-',        /^<\?([\s\S]+?)(?:\?>|$)/],
    +           ['lang-',        /^<%([\s\S]+?)(?:%>|$)/],
    +           [PR_PUNCTUATION, /^(?:<[%?]|[%?]>)/],
    +           ['lang-',        /^<xmp\b[^>]*>([\s\S]+?)<\/xmp\b[^>]*>/i],
    +           // Unescaped content in javascript.  (Or possibly vbscript).
    +           ['lang-js',      /^<script\b[^>]*>([\s\S]*?)(<\/script\b[^>]*>)/i],
    +           // Contains unescaped stylesheet content
    +           ['lang-css',     /^<style\b[^>]*>([\s\S]*?)(<\/style\b[^>]*>)/i],
    +           ['lang-in.tag',  /^(<\/?[a-z][^<>]*>)/i]
    +          ]),
    +      ['default-markup', 'htm', 'html', 'mxml', 'xhtml', 'xml', 'xsl']);
    +  registerLangHandler(
    +      createSimpleLexer(
    +          [
    +           [PR_PLAIN,        /^[\s]+/, null, ' \t\r\n'],
    +           [PR_ATTRIB_VALUE, /^(?:\"[^\"]*\"?|\'[^\']*\'?)/, null, '\"\'']
    +           ],
    +          [
    +           [PR_TAG,          /^^<\/?[a-z](?:[\w.:-]*\w)?|\/?>$/i],
    +           [PR_ATTRIB_NAME,  /^(?!style[\s=]|on)[a-z](?:[\w:-]*\w)?/i],
    +           ['lang-uq.val',   /^=\s*([^>\'\"\s]*(?:[^>\'\"\s\/]|\/(?=\s)))/],
    +           [PR_PUNCTUATION,  /^[=<>\/]+/],
    +           ['lang-js',       /^on\w+\s*=\s*\"([^\"]+)\"/i],
    +           ['lang-js',       /^on\w+\s*=\s*\'([^\']+)\'/i],
    +           ['lang-js',       /^on\w+\s*=\s*([^\"\'>\s]+)/i],
    +           ['lang-css',      /^style\s*=\s*\"([^\"]+)\"/i],
    +           ['lang-css',      /^style\s*=\s*\'([^\']+)\'/i],
    +           ['lang-css',      /^style\s*=\s*([^\"\'>\s]+)/i]
    +           ]),
    +      ['in.tag']);
    +  registerLangHandler(
    +      createSimpleLexer([], [[PR_ATTRIB_VALUE, /^[\s\S]+/]]), ['uq.val']);
    +  registerLangHandler(sourceDecorator({
    +          'keywords': CPP_KEYWORDS,
    +          'hashComments': true,
    +          'cStyleComments': true
    +        }), ['c', 'cc', 'cpp', 'cxx', 'cyc', 'm']);
    +  registerLangHandler(sourceDecorator({
    +          'keywords': 'null true false'
    +        }), ['json']);
    +  registerLangHandler(sourceDecorator({
    +          'keywords': CSHARP_KEYWORDS,
    +          'hashComments': true,
    +          'cStyleComments': true,
    +          'verbatimStrings': true
    +        }), ['cs']);
    +  registerLangHandler(sourceDecorator({
    +          'keywords': JAVA_KEYWORDS,
    +          'cStyleComments': true
    +        }), ['java']);
    +  registerLangHandler(sourceDecorator({
    +          'keywords': SH_KEYWORDS,
    +          'hashComments': true,
    +          'multiLineStrings': true
    +        }), ['bsh', 'csh', 'sh']);
    +  registerLangHandler(sourceDecorator({
    +          'keywords': PYTHON_KEYWORDS,
    +          'hashComments': true,
    +          'multiLineStrings': true,
    +          'tripleQuotedStrings': true
    +        }), ['cv', 'py']);
    +  registerLangHandler(sourceDecorator({
    +          'keywords': PERL_KEYWORDS,
    +          'hashComments': true,
    +          'multiLineStrings': true,
    +          'regexLiterals': true
    +        }), ['perl', 'pl', 'pm']);
    +  registerLangHandler(sourceDecorator({
    +          'keywords': RUBY_KEYWORDS,
    +          'hashComments': true,
    +          'multiLineStrings': true,
    +          'regexLiterals': true
    +        }), ['rb']);
    +  registerLangHandler(sourceDecorator({
    +          'keywords': JSCRIPT_KEYWORDS,
    +          'cStyleComments': true,
    +          'regexLiterals': true
    +        }), ['js']);
    +  registerLangHandler(
    +      createSimpleLexer([], [[PR_STRING, /^[\s\S]+/]]), ['regex']);
    +
    +  function applyDecorator(job) {
    +    var sourceCodeHtml = job.sourceCodeHtml;
    +    var opt_langExtension = job.langExtension;
    +
    +    // Prepopulate output in case processing fails with an exception.
    +    job.prettyPrintedHtml = sourceCodeHtml;
    +
    +    try {
    +      // Extract tags, and convert the source code to plain text.
    +      var sourceAndExtractedTags = extractTags(sourceCodeHtml);
    +      /** Plain text. @type {string} */
    +      var source = sourceAndExtractedTags.source;
    +      job.source = source;
    +      job.basePos = 0;
    +
    +      /** Even entries are positions in source in ascending order.  Odd entries
    +        * are tags that were extracted at that position.
    +        * @type {Array.<number|string>}
    +        */
    +      job.extractedTags = sourceAndExtractedTags.tags;
    +
    +      // Apply the appropriate language handler
    +      langHandlerForExtension(opt_langExtension, source)(job);
    +      // Integrate the decorations and tags back into the source code to produce
    +      // a decorated html string which is left in job.prettyPrintedHtml.
    +      recombineTagsAndDecorations(job);
    +    } catch (e) {
    +      if ('console' in window) {
    +        console.log(e);
    +        console.trace();
    +      }
    +    }
    +  }
    +
    +  function prettyPrintOne(sourceCodeHtml, opt_langExtension) {
    +    var job = {
    +      sourceCodeHtml: sourceCodeHtml,
    +      langExtension: opt_langExtension
    +    };
    +    applyDecorator(job);
    +    return job.prettyPrintedHtml;
    +  }
    +
    +  function prettyPrint(opt_whenDone) {
    +    var isIE678 = window['_pr_isIE6']();
    +    var ieNewline = isIE678 === 6 ? '\r\n' : '\r';
    +    // See bug 71 and http://stackoverflow.com/questions/136443/why-doesnt-ie7-
    +
    +    // fetch a list of nodes to rewrite
    +    var codeSegments = [
    +        document.getElementsByTagName('pre'),
    +        document.getElementsByTagName('code'),
    +        document.getElementsByTagName('xmp') ];
    +    var elements = [];
    +    for (var i = 0; i < codeSegments.length; ++i) {
    +      for (var j = 0, n = codeSegments[i].length; j < n; ++j) {
    +        elements.push(codeSegments[i][j]);
    +      }
    +    }
    +    codeSegments = null;
    +
    +    var clock = Date;
    +    if (!clock['now']) {
    +      clock = { 'now': function () { return (new Date).getTime(); } };
    +    }
    +
    +    // The loop is broken into a series of continuations to make sure that we
    +    // don't make the browser unresponsive when rewriting a large page.
    +    var k = 0;
    +    var prettyPrintingJob;
    +
    +    function doWork() {
    +      var endTime = (window['PR_SHOULD_USE_CONTINUATION'] ?
    +                     clock.now() + 250 /* ms */ :
    +                     Infinity);
    +      for (; k < elements.length && clock.now() < endTime; k++) {
    +        var cs = elements[k];
    +        if (cs.className && cs.className.indexOf('prettyprint') >= 0) {
    +          // If the classes includes a language extensions, use it.
    +          // Language extensions can be specified like
    +          //     <pre class="prettyprint lang-cpp">
    +          // the language extension "cpp" is used to find a language handler as
    +          // passed to PR_registerLangHandler.
    +          var langExtension = cs.className.match(/\blang-(\w+)\b/);
    +          if (langExtension) { langExtension = langExtension[1]; }
    +
    +          // make sure this is not nested in an already prettified element
    +          var nested = false;
    +          for (var p = cs.parentNode; p; p = p.parentNode) {
    +            if ((p.tagName === 'pre' || p.tagName === 'code' ||
    +                 p.tagName === 'xmp') &&
    +                p.className && p.className.indexOf('prettyprint') >= 0) {
    +              nested = true;
    +              break;
    +            }
    +          }
    +          if (!nested) {
    +            // fetch the content as a snippet of properly escaped HTML.
    +            // Firefox adds newlines at the end.
    +            var content = getInnerHtml(cs);
    +            content = content.replace(/(?:\r\n?|\n)$/, '');
    +
    +            // do the pretty printing
    +            prettyPrintingJob = {
    +              sourceCodeHtml: content,
    +              langExtension: langExtension,
    +              sourceNode: cs
    +            };
    +            applyDecorator(prettyPrintingJob);
    +            replaceWithPrettyPrintedHtml();
    +          }
    +        }
    +      }
    +      if (k < elements.length) {
    +        // finish up in a continuation
    +        setTimeout(doWork, 250);
    +      } else if (opt_whenDone) {
    +        opt_whenDone();
    +      }
    +    }
    +
    +    function replaceWithPrettyPrintedHtml() {
    +      var newContent = prettyPrintingJob.prettyPrintedHtml;
    +      if (!newContent) { return; }
    +      var cs = prettyPrintingJob.sourceNode;
    +
    +      // push the prettified html back into the tag.
    +      if (!isRawContent(cs)) {
    +        // just replace the old html with the new
    +        cs.innerHTML = newContent;
    +      } else {
    +        // we need to change the tag to a <pre> since <xmp>s do not allow
    +        // embedded tags such as the span tags used to attach styles to
    +        // sections of source code.
    +        var pre = document.createElement('PRE');
    +        for (var i = 0; i < cs.attributes.length; ++i) {
    +          var a = cs.attributes[i];
    +          if (a.specified) {
    +            var aname = a.name.toLowerCase();
    +            if (aname === 'class') {
    +              pre.className = a.value;  // For IE 6
    +            } else {
    +              pre.setAttribute(a.name, a.value);
    +            }
    +          }
    +        }
    +        pre.innerHTML = newContent;
    +
    +        // remove the old
    +        cs.parentNode.replaceChild(pre, cs);
    +        cs = pre;
    +      }
    +
    +      // Replace <br>s with line-feeds so that copying and pasting works
    +      // on IE 6.
    +      // Doing this on other browsers breaks lots of stuff since \r\n is
    +      // treated as two newlines on Firefox, and doing this also slows
    +      // down rendering.
    +      if (isIE678 && cs.tagName === 'PRE') {
    +        var lineBreaks = cs.getElementsByTagName('br');
    +        for (var j = lineBreaks.length; --j >= 0;) {
    +          var lineBreak = lineBreaks[j];
    +          lineBreak.parentNode.replaceChild(
    +              document.createTextNode(ieNewline), lineBreak);
    +        }
    +      }
    +    }
    +
    +    doWork();
    +  }
    +
    +  window['PR_normalizedHtml'] = normalizedHtml;
    +  window['prettyPrintOne'] = prettyPrintOne;
    +  window['prettyPrint'] = prettyPrint;
    +  window['PR'] = {
    +        'combinePrefixPatterns': combinePrefixPatterns,
    +        'createSimpleLexer': createSimpleLexer,
    +        'registerLangHandler': registerLangHandler,
    +        'sourceDecorator': sourceDecorator,
    +        'PR_ATTRIB_NAME': PR_ATTRIB_NAME,
    +        'PR_ATTRIB_VALUE': PR_ATTRIB_VALUE,
    +        'PR_COMMENT': PR_COMMENT,
    +        'PR_DECLARATION': PR_DECLARATION,
    +        'PR_KEYWORD': PR_KEYWORD,
    +        'PR_LITERAL': PR_LITERAL,
    +        'PR_NOCODE': PR_NOCODE,
    +        'PR_PLAIN': PR_PLAIN,
    +        'PR_PUNCTUATION': PR_PUNCTUATION,
    +        'PR_SOURCE': PR_SOURCE,
    +        'PR_STRING': PR_STRING,
    +        'PR_TAG': PR_TAG,
    +        'PR_TYPE': PR_TYPE
    +      };
    +})();
    diff --git a/GoogleCodeSVN/Action.php b/GoogleCodeSVN/Action.php
    new file mode 100644
    index 0000000..37d7ead
    --- /dev/null
    +++ b/GoogleCodeSVN/Action.php
    @@ -0,0 +1,157 @@
    +<?php
    +
    +class GoogleCodeSVN_Action extends Widget_Abstract_Contents implements Widget_Interface_Do
    +{
    +    private function parseFileName($fileName, $repositoryPath)
    +    {
    +        $result = array(
    +            'do'            =>  'publish',
    +            'allowComment'  =>  $this->options->defaultAllowComment,
    +            'allowPing'     =>  $this->options->defaultAllowPing,
    +            'allowFeed'     =>  $this->options->defaultAllowFeed
    +        );
    +    
    +        $basePath = Helper::options()->plugin('GoogleCodeSVN')->basePath;
    +        $basePath = '/' . trim($basePath, '/') . '/';
    +        
    +        if (0 !== strpos($fileName, $basePath)) {
    +            return false;
    +        }
    +        
    +        $path = substr($fileName, strlen($basePath));
    +        $part = explode('/', $path);
    +        
    +        if (2 != count($part)) {
    +            return false;
    +        }
    +        
    +        list($categoryName, $baseName) = $part;
    +        list($slug) = explode('.', $baseName);
    +        
    +        $result['slug'] = $slug;
    +        
    +        $post = $this->db->fetchRow($this->db->select()
    +        ->from('table.contents')->where('slug = ?', $slug)->limit(1));
    +        
    +        if (!empty($post)) {
    +            if ('post' != $post['type']) {
    +                return false;
    +            } else {
    +                $result['cid'] = $post['cid'];
    +            }
    +        }
    +        
    +        /** 将目录作为分类缩略名处理 */
    +        $categorySlug = Typecho_Common::slugName($categoryName);
    +        
    +        $category = $this->db->fetchRow($this->db->select()
    +        ->from('table.metas')->where('slug = ? OR name = ?', $categorySlug, $categoryName)
    +        ->where('type = ?', 'category')->limit(1));
    +        
    +        /** 如果分类不存在则直接重建分类 */
    +        if (empty($category)) {
    +            $input['name'] = $categoryName;
    +            $input['slug'] = $categorySlug;
    +            $input['type'] = 'category';
    +            $input['description'] = $categoryName;
    +            $input['do'] = 'insert';
    +            
    +            $this->widget('Widget_Metas_Category_Edit', NULL, $input, false)->action();
    +            $result['category'] = array($this->widget('Widget_Notice')->getHighlightId());
    +        } else {
    +            $result['category'] = array($category['mid']);
    +        }
    +        
    +        $url = rtrim($repositoryPath, '/') . $fileName;
    +        
    +        $client = Typecho_Http_Client::get('Curl', 'Socket');
    +        if (false == $client) {
    +            return false;
    +        }
    +        
    +        $client->send($url);
    +        $result['text'] = '';
    +        $result['title'] = '';
    +        
    +        if (200 == $client->getResponseStatus() || 304 == $client->getResponseStatus()) {
    +            $response = trim($client->getResponseBody());
    +            
    +            list($title, $text) = explode("\n", $response, 2);
    +            $result['title'] = $title;
    +            $result['text'] = $text;
    +        }
    +        
    +        return $result;
    +    }
    +
    +    public function action()
    +    {
    +        /** 验证合法性 */
    +        if (!isset($_SERVER['HTTP_GOOGLE_CODE_PROJECT_HOSTING_HOOK_HMAC'])) {
    +            return;
    +        }
    +    
    +        $googleSecretInfo = $_SERVER['HTTP_GOOGLE_CODE_PROJECT_HOSTING_HOOK_HMAC'];
    +        $revisionData = file_get_contents('php://input');
    +        
    +        if (empty($revisionData)) {
    +            return;
    +        }
    +        
    +        $secretVerify = hash_hmac("md5", $revisionData, Helper::options()->plugin('GoogleCodeSVN')->secretKey);
    +        
    +        if ($googleSecretInfo != $secretVerify) {
    +            return;
    +        }
    +        
    +        $data = Typecho_Json::decode($revisionData);
    +        
    +        if (!$data) {
    +            return;
    +        }
    +        
    +        /** 登录用户 */
    +        $master = $this->db->fetchRow($this->db->select()->from('table.users')
    +            ->where('group = ?', 'administrator')
    +            ->order('uid', Typecho_Db::SORT_ASC)
    +            ->limit(1));
    +        
    +        if (empty($master)) {
    +            return false;
    +        } else if (!$this->user->simpleLogin($master['uid'])) {
    +            return false;
    +        }
    +        
    +        if (isset($data->revisions) && is_array($data->revisions)) {
    +            foreach ($data->revisions as $revision) {
    +                if (!empty($revision->added)) {
    +                    foreach ($revision->added as $file) {
    +                        $input = $this->parseFileName($file, $data->repository_path);
    +                        if ($input) {
    +                            $this->widget('Widget_Contents_Post_Edit', NULL, $input, false)->action();
    +                        }
    +                    }
    +                }
    +                
    +                if (!empty($revision->modified)) {
    +                    foreach ($revision->modified as $file) {
    +                        $input = $this->parseFileName($file, $data->repository_path);
    +                        if ($input) {
    +                            $this->widget('Widget_Contents_Post_Edit', NULL, $input, false)->action();
    +                        }
    +                    }
    +                }
    +                
    +                if (!empty($revision->removed)) {
    +                    foreach ($revision->removed as $file) {
    +                        $input = $this->parseFileName($file, $data->repository_path);
    +                        if ($input && isset($input['cid'])) {
    +                            $postId = $input['cid'];
    +                            $this->widget('Widget_Contents_Post_Edit', NULL, "cid={$postId}", false)->deletePost();
    +                        }
    +                    }
    +                }
    +            }
    +        }
    +    }
    +}
    diff --git a/GoogleCodeSVN/Plugin.php b/GoogleCodeSVN/Plugin.php
    new file mode 100644
    index 0000000..1b7514a
    --- /dev/null
    +++ b/GoogleCodeSVN/Plugin.php
    @@ -0,0 +1,69 @@
    +<?php
    +/**
    + * google code svn 同步文章
    + * 
    + * @package Google Code SVN Transmit
    + * @author qining
    + * @version 1.0.0
    + * @dependence 10.6.24-*
    + * @link http://typecho.org
    + */
    +class GoogleCodeSVN_Plugin implements Typecho_Plugin_Interface
    +{
    +    /**
    +     * 激活插件方法,如果激活失败,直接抛出异常
    +     * 
    +     * @access public
    +     * @return void
    +     * @throws Typecho_Plugin_Exception
    +     */
    +    public static function activate()
    +    {
    +        if (false == Typecho_Http_Client::get()) {
    +            throw new Typecho_Plugin_Exception(_t('对不起, 您的主机不支持 php-curl 扩展而且没有打开 allow_url_fopen 功能, 无法正常使用此功能'));
    +        }
    +    
    +        Helper::addAction('googlecode-svn', 'GoogleCodeSVN_Action');
    +        return _t('请在插件设置里设置 Google Code 的SVN参数') . $error;
    +    }
    +    
    +    /**
    +     * 禁用插件方法,如果禁用失败,直接抛出异常
    +     * 
    +     * @static
    +     * @access public
    +     * @return void
    +     * @throws Typecho_Plugin_Exception
    +     */
    +    public static function deactivate()
    +    {
    +        Helper::removeAction('googlecode-svn');
    +    }
    +    
    +    /**
    +     * 获取插件配置面板
    +     * 
    +     * @access public
    +     * @param Typecho_Widget_Helper_Form $form 配置面板
    +     * @return void
    +     */
    +    public static function config(Typecho_Widget_Helper_Form $form)
    +    {
    +        $secretKey = new Typecho_Widget_Helper_Form_Element_Text('secretKey', NULL, NULL,
    +        _t('安全密钥'), _t('请在你的Google Code项目的管理员面板的source区的最下方找到此项'));
    +        $form->addInput($secretKey->addRule('required', _t('必须填写安全密钥')));
    +        
    +        $basePath = new Typecho_Widget_Helper_Form_Element_Text('basePath', NULL, '/trunk',
    +        _t('SVN目录'), _t('填写需要监控的SVN目录'));
    +        $form->addInput($basePath->addRule('required', _t('必须填写数据库用户名')));
    +    }
    +    
    +    /**
    +     * 个人用户的配置面板
    +     * 
    +     * @access public
    +     * @param Typecho_Widget_Helper_Form $form
    +     * @return void
    +     */
    +    public static function personalConfig(Typecho_Widget_Helper_Form $form){}
    +}
    diff --git a/MagikeToTypecho/Action.php b/MagikeToTypecho/Action.php
    new file mode 100644
    index 0000000..ee9fc62
    --- /dev/null
    +++ b/MagikeToTypecho/Action.php
    @@ -0,0 +1,214 @@
    +<?php
    +
    +class MagikeToTypecho_Action extends Typecho_Widget implements Widget_Interface_Do
    +{
    +    public function doImport()
    +    {
    +        $options = $this->widget('Widget_Options');
    +        $dbConfig = $options->plugin('MagikeToTypecho');
    +
    +        /** 初始化一个db */
    +        if (Typecho_Db_Adapter_Mysql::isAvailable()) {
    +            $db = new Typecho_Db('Mysql', $dbConfig->prefix);
    +        } else {
    +            $db = new Typecho_Db('Pdo_Mysql', $dbConfig->prefix);
    +        }
    +        
    +        /** 只读即可 */
    +        $db->addServer(array (
    +          'host' => $dbConfig->host,
    +          'user' => $dbConfig->user,
    +          'password' => $dbConfig->password,
    +          'charset' => 'utf8',
    +          'port' => $dbConfig->port,
    +          'database' => $dbConfig->database
    +        ), Typecho_Db::READ);
    +        
    +        /** 删除当前内容 */
    +        $masterDb = Typecho_Db::get();
    +        $this->widget('Widget_Abstract_Contents')->to($contents)->delete($masterDb->sql()->where('1 = 1'));
    +        $this->widget('Widget_Abstract_Comments')->to($comments)->delete($masterDb->sql()->where('1 = 1'));
    +        $this->widget('Widget_Abstract_Metas')->to($metas)->delete($masterDb->sql()->where('1 = 1'));
    +        $this->widget('Widget_Contents_Post_Edit')->to($edit);
    +        $this->widget('Widget_Abstract_Users')->to($users)->delete($masterDb->sql()->where('uid <> 1'));
    +        $masterDb->query($masterDb->delete('table.relationships')->where('1 = 1'));
    +        $userId = $this->widget('Widget_User')->uid;
    +        
    +        /** 转换用户 */
    +        $rows = $db->fetchAll($db->select()->from('table.users'));
    +        foreach ($rows as $row) {
    +            if (1 != $row['user_id']) {
    +                $users->insert(array(
    +                    'uid'       =>  $row['user_id'],
    +                    'name'      =>  $row['user_name'],
    +                    'password'  =>  $row['user_password'],
    +                    'mail'      =>  $row['user_mail'],
    +                    'url'       =>  $row['user_url'],
    +                    'screenName'=>  $row['user_nick'],
    +                    'created'   => strtotime($row['user_register']),
    +                    'group'     => array_search($row['user_group'], $this->widget('Widget_User')->groups)
    +                ));
    +            }
    +        }
    +        
    +        /** 转换全局变量 */
    +        $rows = $db->fetchAll($db->select()->from('table.statics'));
    +        $static = array();
    +        foreach ($rows as $row) {
    +            $static[$row['static_name']] = $row['static_value'];
    +        }
    +        
    +        /** 转换文件 */
    +        $files = $db->fetchAll($db->select()->from('table.files'));
    +        if (!is_dir(__TYPECHO_ROOT_DIR__ . '/usr/uploads/')) {
    +            mkdir(__TYPECHO_ROOT_DIR__ . '/usr/uploads/', 0766);
    +        }
    +        
    +        $pattern = array();
    +        $replace = array();
    +        foreach ($files as $file) {
    +            $path = __TYPECHO_ROOT_DIR__ . '/data/upload/' . substr($file['file_guid'], 0, 2) . '/' .
    +            substr($file['file_guid'], 2, 2) . '/' . $file['file_guid'];
    +            
    +            if (file_exists($path)) {
    +                $file['file_time'] = empty($file['file_time']) ? $options->gmtTime : $file['file_time'];
    +                $year = idate('Y', $file['file_time']);
    +                $month = idate('m', $file['file_time']);
    +                $day = idate('d', $file['file_time']);
    +                
    +                if (!is_dir(__TYPECHO_ROOT_DIR__ . "/usr/uploads/{$year}")) {
    +                    mkdir(__TYPECHO_ROOT_DIR__ . "/usr/uploads/{$year}", 0766);
    +                }
    +                
    +                if (!is_dir(__TYPECHO_ROOT_DIR__ . "/usr/uploads/{$year}/{$month}")) {
    +                    mkdir(__TYPECHO_ROOT_DIR__ . "/usr/uploads/{$year}/{$month}", 0766);
    +                }
    +                
    +                if (!is_dir(__TYPECHO_ROOT_DIR__ . "/usr/uploads/{$year}/{$month}/{$day}")) {
    +                    mkdir(__TYPECHO_ROOT_DIR__ . "/usr/uploads/{$year}/{$month}/{$day}", 0766);
    +                }
    +                
    +                $parts = explode('.', $file['file_name']);
    +                $ext = array_pop($parts);
    +                copy($path, __TYPECHO_ROOT_DIR__ . "/usr/uploads/{$year}/{$month}/{$day}/{$file['file_id']}.{$ext}");
    +                
    +                $new = Typecho_Common::url("/usr/uploads/{$year}/{$month}/{$day}/{$file['file_id']}.{$ext}", $options->siteUrl);
    +                $old = Typecho_Common::url("/res/{$file['file_id']}/{$file['file_name']}", $static['siteurl'] . '/index.php');
    +                $pattern[] = '/' . str_replace('\/index\.php', '(\/index\.php)?', preg_quote($old, '/')) . '/is';
    +                $replace[] = $new;
    +            }
    +        }
    +        
    +        /** 转换评论 */
    +        $i = 1;
    +        
    +        while (true) {
    +            $result = $db->query($db->select()->from('table.comments')
    +            ->order('comment_id', Typecho_Db::SORT_ASC)->page($i, 100));
    +            $j = 0;
    +            
    +            while ($row = $db->fetchRow($result)) {
    +                $comments->insert(array(
    +                    'coid'      =>  $row['comment_id'],
    +                    'cid'       =>  $row['post_id'],
    +                    'created'   =>  $row['comment_date'],
    +                    'author'    =>  $row['comment_user'],
    +                    'authorId'  =>  $row['user_id'],
    +                    'ownerId'   =>  $userId,
    +                    'mail'      =>  $row['comment_email'],
    +                    'url'       =>  $row['comment_homepage'],
    +                    'ip'        =>  $row['comment_ip'],
    +                    'agent'     =>  $row['comment_agent'],
    +                    'text'      =>  $row['comment_text'],
    +                    'type'      =>  $row['comment_type'],
    +                    'status'    =>  $row['comment_publish'],
    +                    'parent'    =>  $row['comment_parent']
    +                ));
    +                $j ++;
    +                unset($row);
    +            }
    +            
    +            if ($j < 100) {
    +                break;
    +            }
    +            
    +            $i ++;
    +            unset($result);
    +        }
    +        
    +        /** 转换分类 */
    +        $cats = $db->fetchAll($db->select()->from('table.categories'));
    +        foreach ($cats as $cat) {
    +            $metas->insert(array(
    +                'mid'           =>  $cat['category_id'],
    +                'name'          =>  $cat['category_name'],
    +                'slug'          =>  $cat['category_postname'],
    +                'description'   =>  $cat['category_describe'],
    +                'count'         =>  0,
    +                'type'          =>  'category',
    +                'order'         =>  $cat['category_sort']
    +            ));
    +        }
    +        
    +        /** 转换内容 */
    +        $i = 1;
    +        
    +        while (true) {
    +            $result = $db->query($db->select()->from('table.posts')
    +            ->order('post_id', Typecho_Db::SORT_ASC)->page($i, 100));
    +            $j = 0;
    +            
    +            while ($row = $db->fetchRow($result)) {
    +                $row['post_content'] = preg_replace(
    +                array("/\s*<p>/is", "/\s*<\/p>\s*/is", "/\s*<br\s*\/>\s*/is",
    +                "/\s*<(div|blockquote|pre|table|ol|ul)>/is", "/<\/(div|blockquote|pre|table|ol|ul)>\s*/is"),
    +                array('', "\n\n", "\n", "\n\n<\\1>", "</\\1>\n\n"), 
    +                $row['post_content']);
    +            
    +                $contents->insert(array(
    +                    'cid'           =>  $row['post_id'],
    +                    'title'         =>  $row['post_title'],
    +                    'slug'          =>  $row['post_name'],
    +                    'created'       =>  $row['post_time'],
    +                    'modified'      =>  $row['post_edit_time'],
    +                    'text'          =>  preg_replace($pattern, $replace, $row['post_content']),
    +                    'order'         =>  0,
    +                    'authorId'      =>  $row['user_id'],
    +                    'template'      =>  NULL,
    +                    'type'          =>  $row['post_is_page'] ? 'page' : 'post',
    +                    'status'        =>  $row['post_is_draft'] ? 'draft' : 'publish',
    +                    'password'      =>  $row['post_password'],
    +                    'commentsNum'   =>  $row['post_comment_num'],
    +                    'allowComment'  =>  $row['post_allow_comment'],
    +                    'allowFeed'     =>  $row['post_allow_feed'],
    +                    'allowPing'     =>  $row['post_allow_ping']
    +                ));
    +                
    +                /** 插入分类关系 */
    +                $edit->setCategories($row['post_id'], array($row['category_id']), !$row['post_is_draft']);
    +                
    +                /** 设置标签 */
    +                $edit->setTags($row['post_id'], $row['post_tags'], !$row['post_is_draft']);
    +                
    +                $j ++;
    +                unset($row);
    +            }
    +            
    +            if ($j < 100) {
    +                break;
    +            }
    +            
    +            $i ++;
    +            unset($result);
    +        }
    +        
    +        $this->widget('Widget_Notice')->set(_t("数据已经转换完成"), NULL, 'success');
    +        $this->response->goBack();
    +    }
    +
    +    public function action()
    +    {
    +        $this->widget('Widget_User')->pass('administrator');
    +        $this->on($this->request->isPost())->doImport();
    +    }
    +}
    diff --git a/MagikeToTypecho/Plugin.php b/MagikeToTypecho/Plugin.php
    new file mode 100644
    index 0000000..848f106
    --- /dev/null
    +++ b/MagikeToTypecho/Plugin.php
    @@ -0,0 +1,94 @@
    +<?php
    +/**
    + * 将 Magike 数据库中的数据转换为 Typecho
    + * 
    + * @package Magike to Typecho
    + * @author qining
    + * @version 1.0.0
    + * @link http://typecho.org
    + */
    +class MagikeToTypecho_Plugin implements Typecho_Plugin_Interface
    +{
    +    /**
    +     * 激活插件方法,如果激活失败,直接抛出异常
    +     * 
    +     * @access public
    +     * @return void
    +     * @throws Typecho_Plugin_Exception
    +     */
    +    public static function activate()
    +    {
    +        if (!Typecho_Db_Adapter_Mysql::isAvailable() && !Typecho_Db_Adapter_Pdo_Mysql::isAvailable()) {
    +            throw new Typecho_Plugin_Exception(_t('没有找到任何可用的 Mysql 适配器'));
    +        }
    +        
    +        $error = NULL;
    +        if ((!is_dir(__TYPECHO_ROOT_DIR__ . '/usr/uploads/') || !is_writeable(__TYPECHO_ROOT_DIR__ . '/usr/uploads/'))
    +        && !is_writeable(__TYPECHO_ROOT_DIR__ . '/usr/')) {
    +            $error = '<br /><strong>' . _t('%s 目录不可写, 可能会导致附件转换不成功', __TYPECHO_ROOT_DIR__ . '/usr/uploads/') . '</strong>';
    +        }
    +    
    +        Helper::addPanel(1, 'MagikeToTypecho/panel.php', _t('从Magike导入数据'), _t('从Magike导入数据'), 'administrator');
    +        Helper::addAction('magike-to-typecho', 'MagikeToTypecho_Action');
    +        return _t('请在插件设置里设置 Magike 所在的数据库参数') . $error;
    +    }
    +    
    +    /**
    +     * 禁用插件方法,如果禁用失败,直接抛出异常
    +     * 
    +     * @static
    +     * @access public
    +     * @return void
    +     * @throws Typecho_Plugin_Exception
    +     */
    +    public static function deactivate()
    +    {
    +        Helper::removeAction('magike-to-typecho');
    +        Helper::removePanel(1, 'MagikeToTypecho/panel.php');
    +    }
    +    
    +    /**
    +     * 获取插件配置面板
    +     * 
    +     * @access public
    +     * @param Typecho_Widget_Helper_Form $form 配置面板
    +     * @return void
    +     */
    +    public static function config(Typecho_Widget_Helper_Form $form)
    +    {
    +        $host = new Typecho_Widget_Helper_Form_Element_Text('host', NULL, 'localhost',
    +        _t('数据库地址'), _t('请填写 Magike 所在的数据库地址'));
    +        $form->addInput($host->addRule('required', _t('必须填写一个数据库地址')));
    +        
    +        $port = new Typecho_Widget_Helper_Form_Element_Text('port', NULL, '3306',
    +        _t('数据库端口'), _t('Magike 所在的数据库服务器端口'));
    +        $port->input->setAttribute('class', 'mini');
    +        $form->addInput($port->addRule('required', _t('必须填写数据库端口'))
    +        ->addRule('isInteger', _t('端口号必须是纯数字')));
    +        
    +        $user = new Typecho_Widget_Helper_Form_Element_Text('user', NULL, 'root',
    +        _t('数据库用户名'));
    +        $form->addInput($user->addRule('required', _t('必须填写数据库用户名')));
    +        
    +        $password = new Typecho_Widget_Helper_Form_Element_Password('password', NULL, NULL,
    +        _t('数据库密码'));
    +        $form->addInput($password);
    +        
    +        $database = new Typecho_Widget_Helper_Form_Element_Text('database', NULL, 'magike',
    +        _t('数据库名称'), _t('Magike 所在的数据库名称'));
    +        $form->addInput($database->addRule('required', _t('您必须填写数据库名称')));
    +    
    +        $prefix = new Typecho_Widget_Helper_Form_Element_Text('prefix', NULL, 'mg_',
    +        _t('表前缀'), _t('所有 Magike 数据表的前缀'));
    +        $form->addInput($prefix->addRule('required', _t('您必须填写表前缀')));
    +    }
    +    
    +    /**
    +     * 个人用户的配置面板
    +     * 
    +     * @access public
    +     * @param Typecho_Widget_Helper_Form $form
    +     * @return void
    +     */
    +    public static function personalConfig(Typecho_Widget_Helper_Form $form){}
    +}
    diff --git a/MagikeToTypecho/panel.php b/MagikeToTypecho/panel.php
    new file mode 100644
    index 0000000..ec6f3b6
    --- /dev/null
    +++ b/MagikeToTypecho/panel.php
    @@ -0,0 +1,73 @@
    +<?php
    +if (!defined('__TYPECHO_ROOT_DIR__')) {
    +    exit;
    +}
    +
    +$success = true;
    +try {
    +    $dbConfig = $options->plugin('MagikeToTypecho');
    +
    +    /** 初始化一个db */
    +    if (Typecho_Db_Adapter_Mysql::isAvailable()) {
    +        $magikeDb = new Typecho_Db('Mysql', $dbConfig->prefix);
    +    } else {
    +        $magikeDb = new Typecho_Db('Pdo_Mysql', $dbConfig->prefix);
    +    }
    +
    +    /** 只读即可 */
    +    $magikeDb->addServer(array (
    +      'host' => $dbConfig->host,
    +      'user' => $dbConfig->user,
    +      'password' => $dbConfig->password,
    +      'charset' => 'utf8',
    +      'port' => $dbConfig->port,
    +      'database' => $dbConfig->database
    +    ), Typecho_Db::READ);
    +    
    +    $rows = $magikeDb->fetchAll($magikeDb->select()->from('table.statics'));
    +    $static = array();
    +    foreach ($rows as $row) {
    +        $static[$row['static_name']] = $row['static_value'];
    +    }
    +} catch (Typecho_Db_Exception $e) {
    +    $success = false;
    +}
    +
    +include 'header.php';
    +include 'menu.php';
    +?>
    +<div class="main">
    +    <div class="body body-950">
    +        <?php include 'page-title.php'; ?>
    +        <div class="container typecho-page-main">
    +            <div class="column-22 start-02">
    +                <?php if ($success): ?>
    +                <div class="message notice typecho-radius-topleft typecho-radius-topright typecho-radius-bottomleft typecho-radius-bottomright">
    +                <form action="<?php $options->index('/action/magike-to-typecho'); ?>" method="post">
    +                    <?php _e('我们检测到了 Magike 系统信息, 点击下方的按钮开始数据转换, 数据转换可能会耗时较长.'); ?>
    +                    <blockquote>
    +                    <ul>
    +                        <li><strong><?php echo $static['blog_name']; ?></strong></li>
    +                        <li><strong><?php echo $static['description']; ?></strong></li>
    +                        <li><strong><?php echo $static['siteurl']; ?></strong></li>
    +                    </ul>
    +                    </blockquote>
    +                    <br />
    +                    <p><button type="submit"><?php _e('开始数据转换 &raquo;'); ?></button></p>
    +                </form>
    +                </div>
    +                <?php else: ?>
    +                <div class="message error">
    +                    <?php _e('我们在连接到 Magike 的数据库时发生了错误, 请<a href="%s">重新设置</a>你的信息.', 
    +                    Typecho_Common::url('options-plugin.php?config=MagikeToTypecho', $options->adminUrl)); ?>
    +                </div>
    +                <?php endif; ?>
    +            </div>
    +        </div>
    +    </div>
    +</div>
    +<?php
    +include 'copyright.php';
    +include 'common-js.php';
    +include 'footer.php';
    +?>
    diff --git a/PageToLinks.php b/PageToLinks.php
    new file mode 100644
    index 0000000..3ed4055
    --- /dev/null
    +++ b/PageToLinks.php
    @@ -0,0 +1,87 @@
    +<?php
    +/**
    + * 将页面转化为友情链接列表的插件
    + * 
    + * @package Page To Links
    + * @author qining
    + * @version 1.0.0
    + * @link http://typecho.org
    + */
    +class PageToLinks implements Typecho_Plugin_Interface
    +{
    +    /**
    +     * 激活插件方法,如果激活失败,直接抛出异常
    +     * 
    +     * @access public
    +     * @return void
    +     * @throws Typecho_Plugin_Exception
    +     */
    +    public static function activate(){}
    +    
    +    /**
    +     * 禁用插件方法,如果禁用失败,直接抛出异常
    +     * 
    +     * @static
    +     * @access public
    +     * @return void
    +     * @throws Typecho_Plugin_Exception
    +     */
    +    public static function deactivate(){}
    +    
    +    /**
    +     * 获取插件配置面板
    +     * 
    +     * @access public
    +     * @param Typecho_Widget_Helper_Form $form 配置面板
    +     * @return void
    +     */
    +    public static function config(Typecho_Widget_Helper_Form $form){}
    +    
    +    /**
    +     * 个人用户的配置面板
    +     * 
    +     * @access public
    +     * @param Typecho_Widget_Helper_Form $form
    +     * @return void
    +     */
    +    public static function personalConfig(Typecho_Widget_Helper_Form $form){}
    +    
    +    /**
    +     * 解析并输出
    +     * 
    +     * @access public
    +     * @param string $slug 页面标题
    +     * @param string $tag 标题的html tag
    +     * @param string $listTag 列表的html tag
    +     * @return void
    +     */
    +    public static function output($slug = 'links', $tag = 'h2', $listTag = 'ul')
    +    {
    +        /** 获取数据库支持 */
    +        $db = Typecho_Db::get();
    +        
    +        /** 获取文本 */
    +        $contents = $db->fetchObject($db->select('text')->from('table.contents')
    +        ->where('slug = ?', $slug)->limit(1));
    +        
    +        if (!$contents) {
    +            return;
    +        }
    +        
    +        $text = $contents->text;
    +        $cats = preg_split("/<\/(ol|ul)>/i", $text);
    +        
    +        foreach ($cats as $cat) {
    +            $item = trim($cat);
    +            
    +            if ($item) {
    +                $matches = array_map('trim', preg_split("/<(ol|ul)[^>]*>/i", $item));
    +                if (2 == count($matches)) {
    +                    list ($title, $list) = $matches;
    +                    echo "<$tag>" . strip_tags($title) . "</$tag>";
    +                    echo "<$listTag>" . $list . "</$listTag>";
    +                }
    +            }
    +        }
    +    }
    +}
    diff --git a/PostToQzone/Plugin.php b/PostToQzone/Plugin.php
    new file mode 100644
    index 0000000..34f301c
    --- /dev/null
    +++ b/PostToQzone/Plugin.php
    @@ -0,0 +1,185 @@
    +<?php
    +/**
    + * 将文章同时发布到您的Qzone
    + *
    + * @package PostToQzone
    + * @version 1.0 beta
    + * @author blankyao
    + * @link http://www.blankyao.cn
    + */
    +include "phpmailer.php";
    +include "smtp.php";
    +class PostToQzone_Plugin implements Typecho_Plugin_Interface
    +{
    +    /**
    +     * activate
    +     *
    +     * @static
    +     * @access public
    +     * @return void
    +     */
    +    public static function activate()
    +    {
    +        Typecho_Plugin::factory('Widget_Contents_Post_Edit')->insert =
    +            array('PostToQzone_Plugin', 'publish');
    +        if(!extension_loaded("sockets")){
    +            throw new Typecho_Plugin_Exception(_t('对不起, 您的主机不支持socket扩展, 无法正常使用此功能'));
    +        }
    +        return _t('请配置您的qq号码以及密码,以便发布文章到Qzone');
    +    }
    +
    +    /**
    +     * deactivate
    +     *
    +     * @static
    +     * @access public
    +     * @return void
    +     */
    +    public static function deactivate()
    +    {
    +    }
    +
    +    /**
    +     * 插件配置面板
    +     *
    +     * @param Typecho_Widget_Helper_Form $form
    +     * @static
    +     * @access public
    +     * @return void
    +     */
    +    public static function config(Typecho_Widget_Helper_Form $form)
    +    {
    +        $qq = new Typecho_Widget_Helper_Form_Element_Text('qq', NULL, NULL,
    +        _t('qq号码'), _t('请填写您的qq号码'));
    +        $qq->addRule('isInteger', _t('qq号码必须是纯数字'));
    +        $form->addInput($qq->addRule('required', _t('必须填写一个qq号码')));
    +        $psw = new Typecho_Widget_Helper_Form_Element_Password('psw', NULL, NULL,
    +        _t('qq邮箱密码'), _t('请填写您的qq邮箱密码'));
    +        $form->addInput($psw->addRule('required', _t('必须填写一个qq邮箱密码')));
    +        $title = new Typecho_Widget_Helper_Form_Element_Text('title', NULL, '{post_title}',
    +        _t('标题模板'), _t('请填写您的标题模板'));
    +        $form->addInput($title->addRule('required', _t('必须填写一个标题模板')));
    +        $content = new Typecho_Widget_Helper_Form_Element_Textarea('content', NULL, '{post_content}',
    +        _t('内容模板'), _t('请填写您的内容模板'));
    +        $form->addInput($content->addRule('required', _t('必须填写一个内容模板')));
    +    }
    +
    +    /**
    +     * 个人用户的配置面板
    +     *
    +     * @access public
    +     * @param Typecho_Widget_Helper_Form $form
    +     * @return void
    +     */
    +    public static function personalConfig(Typecho_Widget_Helper_Form $form){}
    +
    +    /**
    +     * 发送文章到qzone
    +     *
    +     * @param mixed $contents 文章结构体
    +     * @access public
    +     * @return mixed $contents 处理后的文章结构体
    +     */
    +    public function publish($contents)
    +    {
    +        //todo:增加一个选项,如果选择发送的qzone的话再发到qzone
    +        $options = Typecho_Widget::widget('Widget_Options');
    +        $config = $options->plugin('PostToQzone');
    +        $config = postToQzoneDefault($config);
    +
    +        if($config->qq > 1000 && !empty($contents['title'])  && !empty($contents['text'])){
    +
    +            $post_content = str_replace('{post_content}', $contents['text'], $config->content);
    +            $post_content = str_replace('{post_title}', $contents['title'], $post_content);
    +
    +            $post_title = str_replace('{post_title}', $contents['title'], $config->title);
    +
    +            $m=new Mailer($config->qq,$config->psw);
    +            $m->Halo($post_title,$post_content);
    +        }
    +        return $contents;
    +    }
    +}
    +
    +function postToQzoneDefault($config){
    +	if(strpos($config->title,'{post_title}') === false){
    +		$config->title = '{post_title}';
    +	}
    +
    +	if(strpos($config->content,'{post_content}') === false){
    +		$config->content = '{post_content}';
    +	}
    +	return $config;
    +}
    +
    +class Mailer extends PHPMailer
    +{
    +	var $qq=null;
    +	function Mailer($qq,$psw) {
    +		$this->qq=$qq;
    +		$this->From	 = "{$qq}@qq.com";
    +		$this->FromName = $qq;
    +		$this->Host	 = "smtp.qq.com";
    +		$this->Mailer   = "smtp";
    +		$this->WordWrap = 75;
    +		$this->CharSet = Typecho_Widget::widget('Widget_Options')->charset;
    +		$this->Encoding = 'base64';
    +		$this->SMTPAuth = true;
    +		$this->IsHTML(true);
    +		$this->Username = $qq;
    +		$this->Password = $psw;
    +	}
    +
    +	function Halo($subject,$body){
    +		$this->AddAddress("{$this->qq}@qzone.qq.com", "{$this->qq}@qzone.qq.com");
    +		$this->Subject = $subject;
    +		$this->Body	= $body;
    +		return $this->Send();
    +	}
    +}
    +
    +class Crypter
    +{
    +   var $key;
    +
    +   function Crypter($clave){
    +	  $this->key = $clave;
    +   }
    +
    +   function keyED($txt) {
    +	  $encrypt_key = md5($this->key);
    +	  $ctr=0;
    +	  $tmp = "";
    +	  for ($i=0;$i<strlen($txt);$i++) {
    +		 if ($ctr==strlen($encrypt_key)) $ctr=0;
    +		 $tmp.= substr($txt,$i,1) ^ substr($encrypt_key,$ctr,1);
    +		 $ctr++;
    +	  }
    +	  return $tmp;
    +   }
    +
    +   function encrypt($txt){
    +	  srand((double)microtime()*1000000);
    +	  $encrypt_key = md5(rand(0,32000));
    +	  $ctr=0;
    +	  $tmp = "";
    +	  for ($i=0;$i<strlen($txt);$i++){
    +		 if ($ctr==strlen($encrypt_key)) $ctr=0;
    +		 $tmp.= substr($encrypt_key,$ctr,1) .
    +			 (substr($txt,$i,1) ^ substr($encrypt_key,$ctr,1));
    +		 $ctr++;
    +	  }
    +	  return base64_encode($this->keyED($tmp));
    +   }
    +
    +   function decrypt($txt) {
    +	  $txt = $this->keyED(base64_decode($txt));
    +	  $tmp = "";
    +	  for ($i=0;$i<strlen($txt);$i++){
    +		 $md5 = substr($txt,$i,1);
    +		 $i++;
    +		 $tmp.= (substr($txt,$i,1) ^ $md5);
    +	  }
    +	  return $tmp;
    +   }
    +}
    diff --git a/PostToQzone/phpmailer.php b/PostToQzone/phpmailer.php
    new file mode 100644
    index 0000000..aeb8928
    --- /dev/null
    +++ b/PostToQzone/phpmailer.php
    @@ -0,0 +1,1896 @@
    +<?php
    +/*~ class.phpmailer.php
    +.---------------------------------------------------------------------------.
    +|  Software: PHPMailer - PHP email class                                    |
    +|   Version: 2.0.2                                                          |
    +|   Contact: via sourceforge.net support pages (also www.codeworxtech.com)  |
    +|      Info: http://phpmailer.sourceforge.net                               |
    +|   Support: http://sourceforge.net/projects/phpmailer/                     |
    +| ------------------------------------------------------------------------- |
    +|    Author: Andy Prevost (project admininistrator)                         |
    +|    Author: Brent R. Matzelle (original founder)                           |
    +| Copyright (c) 2004-2007, Andy Prevost. All Rights Reserved.               |
    +| Copyright (c) 2001-2003, Brent R. Matzelle                                |
    +| ------------------------------------------------------------------------- |
    +|   License: Distributed under the Lesser General Public License (LGPL)     |
    +|            http://www.gnu.org/copyleft/lesser.html                        |
    +| This program is distributed in the hope that it will be useful - WITHOUT  |
    +| ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or     |
    +| FITNESS FOR A PARTICULAR PURPOSE.                                         |
    +| ------------------------------------------------------------------------- |
    +| We offer a number of paid services (www.codeworxtech.com):                |
    +| - Web Hosting on highly optimized fast and secure servers                 |
    +| - Technology Consulting                                                   |
    +| - Oursourcing (highly qualified programmers and graphic designers)        |
    +'---------------------------------------------------------------------------'
    + */
    +/**
    + * PHPMailer - PHP email transport class
    + * @package PHPMailer
    + * @author Andy Prevost
    + * @copyright 2004 - 2008 Andy Prevost
    + */
    +
    +class PHPMailer {
    +
    +  /////////////////////////////////////////////////
    +  // PROPERTIES, PUBLIC
    +  /////////////////////////////////////////////////
    +
    +  /**
    +   * Email priority (1 = High, 3 = Normal, 5 = low).
    +   * @var int
    +   */
    +  var $Priority          = 3;
    +
    +  /**
    +   * Sets the CharSet of the message.
    +   * @var string
    +   */
    +  var $CharSet           = 'iso-8859-1';
    +
    +  /**
    +   * Sets the Content-type of the message.
    +   * @var string
    +   */
    +  var $ContentType        = 'text/plain';
    +
    +  /**
    +   * Sets the Encoding of the message. Options for this are "8bit",
    +   * "7bit", "binary", "base64", and "quoted-printable".
    +   * @var string
    +   */
    +  var $Encoding          = '8bit';
    +
    +  /**
    +   * Holds the most recent mailer error message.
    +   * @var string
    +   */
    +  var $ErrorInfo         = '';
    +
    +  /**
    +   * Sets the From email address for the message.
    +   * @var string
    +   */
    +  var $From              = 'root@localhost';
    +
    +  /**
    +   * Sets the From name of the message.
    +   * @var string
    +   */
    +  var $FromName          = 'Root User';
    +
    +  /**
    +   * Sets the Sender email (Return-Path) of the message.  If not empty,
    +   * will be sent via -f to sendmail or as 'MAIL FROM' in smtp mode.
    +   * @var string
    +   */
    +  var $Sender            = '';
    +
    +  /**
    +   * Sets the Subject of the message.
    +   * @var string
    +   */
    +  var $Subject           = '';
    +
    +  /**
    +   * Sets the Body of the message.  This can be either an HTML or text body.
    +   * If HTML then run IsHTML(true).
    +   * @var string
    +   */
    +  var $Body              = '';
    +
    +  /**
    +   * Sets the text-only body of the message.  This automatically sets the
    +   * email to multipart/alternative.  This body can be read by mail
    +   * clients that do not have HTML email capability such as mutt. Clients
    +   * that can read HTML will view the normal Body.
    +   * @var string
    +   */
    +  var $AltBody           = '';
    +
    +  /**
    +   * Sets word wrapping on the body of the message to a given number of
    +   * characters.
    +   * @var int
    +   */
    +  var $WordWrap          = 0;
    +
    +  /**
    +   * Method to send mail: ("mail", "sendmail", or "smtp").
    +   * @var string
    +   */
    +  var $Mailer            = 'mail';
    +
    +  /**
    +   * Sets the path of the sendmail program.
    +   * @var string
    +   */
    +  var $Sendmail          = '/usr/sbin/sendmail';
    +
    +  /**
    +   * Path to PHPMailer plugins.  This is now only useful if the SMTP class
    +   * is in a different directory than the PHP include path.
    +   * @var string
    +   */
    +  var $PluginDir         = '';
    +
    +  /**
    +   * Holds PHPMailer version.
    +   * @var string
    +   */
    +  var $Version           = "2.0.2";
    +
    +  /**
    +   * Sets the email address that a reading confirmation will be sent.
    +   * @var string
    +   */
    +  var $ConfirmReadingTo  = '';
    +
    +  /**
    +   * Sets the hostname to use in Message-Id and Received headers
    +   * and as default HELO string. If empty, the value returned
    +   * by SERVER_NAME is used or 'localhost.localdomain'.
    +   * @var string
    +   */
    +  var $Hostname          = '';
    +
    +  /**
    +   * Sets the message ID to be used in the Message-Id header.
    +   * If empty, a unique id will be generated.
    +   * @var string
    +   */
    +  var $MessageID         = '';
    +
    +  /////////////////////////////////////////////////
    +  // PROPERTIES FOR SMTP
    +  /////////////////////////////////////////////////
    +
    +  /**
    +   * Sets the SMTP hosts.  All hosts must be separated by a
    +   * semicolon.  You can also specify a different port
    +   * for each host by using this format: [hostname:port]
    +   * (e.g. "smtp1.example.com:25;smtp2.example.com").
    +   * Hosts will be tried in order.
    +   * @var string
    +   */
    +  var $Host        = 'localhost';
    +
    +  /**
    +   * Sets the default SMTP server port.
    +   * @var int
    +   */
    +  var $Port        = 25;
    +
    +  /**
    +   * Sets the SMTP HELO of the message (Default is $Hostname).
    +   * @var string
    +   */
    +  var $Helo        = '';
    +
    +  /**
    +   * Sets connection prefix.
    +   * Options are "", "ssl" or "tls"
    +   * @var string
    +   */
    +  var $SMTPSecure = "";
    +
    +  /**
    +   * Sets SMTP authentication. Utilizes the Username and Password variables.
    +   * @var bool
    +   */
    +  var $SMTPAuth     = false;
    +
    +  /**
    +   * Sets SMTP username.
    +   * @var string
    +   */
    +  var $Username     = '';
    +
    +  /**
    +   * Sets SMTP password.
    +   * @var string
    +   */
    +  var $Password     = '';
    +
    +  /**
    +   * Sets the SMTP server timeout in seconds. This function will not
    +   * work with the win32 version.
    +   * @var int
    +   */
    +  var $Timeout      = 10;
    +
    +  /**
    +   * Sets SMTP class debugging on or off.
    +   * @var bool
    +   */
    +  var $SMTPDebug    = false;
    +
    +  /**
    +   * Prevents the SMTP connection from being closed after each mail
    +   * sending.  If this is set to true then to close the connection
    +   * requires an explicit call to SmtpClose().
    +   * @var bool
    +   */
    +  var $SMTPKeepAlive = false;
    +
    +  /**
    +   * Provides the ability to have the TO field process individual
    +   * emails, instead of sending to entire TO addresses
    +   * @var bool
    +   */
    +  var $SingleTo = false;
    +
    +  /////////////////////////////////////////////////
    +  // PROPERTIES, PRIVATE
    +  /////////////////////////////////////////////////
    +
    +  var $smtp            = NULL;
    +  var $to              = array();
    +  var $cc              = array();
    +  var $bcc             = array();
    +  var $ReplyTo         = array();
    +  var $attachment      = array();
    +  var $CustomHeader    = array();
    +  var $message_type    = '';
    +  var $boundary        = array();
    +  var $language        = array();
    +  var $error_count     = 0;
    +  var $LE              = "\n";
    +  var $sign_key_file   = "";
    +  var $sign_key_pass   = "";
    +
    +  /////////////////////////////////////////////////
    +  // METHODS, VARIABLES
    +  /////////////////////////////////////////////////
    +
    +  /**
    +   * Sets message type to HTML.
    +   * @param bool $bool
    +   * @return void
    +   */
    +  function IsHTML($bool) {
    +    if($bool == true) {
    +      $this->ContentType = 'text/html';
    +    } else {
    +      $this->ContentType = 'text/plain';
    +    }
    +  }
    +
    +  /**
    +   * Sets Mailer to send message using SMTP.
    +   * @return void
    +   */
    +  function IsSMTP() {
    +    $this->Mailer = 'smtp';
    +  }
    +
    +  /**
    +   * Sets Mailer to send message using PHP mail() function.
    +   * @return void
    +   */
    +  function IsMail() {
    +    $this->Mailer = 'mail';
    +  }
    +
    +  /**
    +   * Sets Mailer to send message using the $Sendmail program.
    +   * @return void
    +   */
    +  function IsSendmail() {
    +    $this->Mailer = 'sendmail';
    +  }
    +
    +  /**
    +   * Sets Mailer to send message using the qmail MTA.
    +   * @return void
    +   */
    +  function IsQmail() {
    +    $this->Sendmail = '/var/qmail/bin/sendmail';
    +    $this->Mailer = 'sendmail';
    +  }
    +
    +  /////////////////////////////////////////////////
    +  // METHODS, RECIPIENTS
    +  /////////////////////////////////////////////////
    +
    +  /**
    +   * Adds a "To" address.
    +   * @param string $address
    +   * @param string $name
    +   * @return void
    +   */
    +  function AddAddress($address, $name = '') {
    +    $cur = count($this->to);
    +    $this->to[$cur][0] = trim($address);
    +    $this->to[$cur][1] = $name;
    +  }
    +
    +  /**
    +   * Adds a "Cc" address. Note: this function works
    +   * with the SMTP mailer on win32, not with the "mail"
    +   * mailer.
    +   * @param string $address
    +   * @param string $name
    +   * @return void
    +   */
    +  function AddCC($address, $name = '') {
    +    $cur = count($this->cc);
    +    $this->cc[$cur][0] = trim($address);
    +    $this->cc[$cur][1] = $name;
    +  }
    +
    +  /**
    +   * Adds a "Bcc" address. Note: this function works
    +   * with the SMTP mailer on win32, not with the "mail"
    +   * mailer.
    +   * @param string $address
    +   * @param string $name
    +   * @return void
    +   */
    +  function AddBCC($address, $name = '') {
    +    $cur = count($this->bcc);
    +    $this->bcc[$cur][0] = trim($address);
    +    $this->bcc[$cur][1] = $name;
    +  }
    +
    +  /**
    +   * Adds a "Reply-To" address.
    +   * @param string $address
    +   * @param string $name
    +   * @return void
    +   */
    +  function AddReplyTo($address, $name = '') {
    +    $cur = count($this->ReplyTo);
    +    $this->ReplyTo[$cur][0] = trim($address);
    +    $this->ReplyTo[$cur][1] = $name;
    +  }
    +
    +  /////////////////////////////////////////////////
    +  // METHODS, MAIL SENDING
    +  /////////////////////////////////////////////////
    +
    +  /**
    +   * Creates message and assigns Mailer. If the message is
    +   * not sent successfully then it returns false.  Use the ErrorInfo
    +   * variable to view description of the error.
    +   * @return bool
    +   */
    +  function Send() {
    +    $header = '';
    +    $body = '';
    +    $result = true;
    +
    +    if((count($this->to) + count($this->cc) + count($this->bcc)) < 1) {
    +      $this->SetError($this->Lang('provide_address'));
    +      return false;
    +    }
    +
    +    /* Set whether the message is multipart/alternative */
    +    if(!empty($this->AltBody)) {
    +      $this->ContentType = 'multipart/alternative';
    +    }
    +
    +    $this->error_count = 0; // reset errors
    +    $this->SetMessageType();
    +    $header .= $this->CreateHeader();
    +    $body = $this->CreateBody();
    +
    +    if($body == '') {
    +      return false;
    +    }
    +
    +    /* Choose the mailer */
    +    switch($this->Mailer) {
    +      case 'sendmail':
    +        $result = $this->SendmailSend($header, $body);
    +        break;
    +      case 'smtp':
    +        $result = $this->SmtpSend($header, $body);
    +        break;
    +      case 'mail':
    +        $result = $this->MailSend($header, $body);
    +        break;
    +      default:
    +        $result = $this->MailSend($header, $body);
    +        break;
    +        //$this->SetError($this->Mailer . $this->Lang('mailer_not_supported'));
    +        //$result = false;
    +        //break;
    +    }
    +
    +    return $result;
    +  }
    +
    +  /**
    +   * Sends mail using the $Sendmail program.
    +   * @access private
    +   * @return bool
    +   */
    +  function SendmailSend($header, $body) {
    +    if ($this->Sender != '') {
    +      $sendmail = sprintf("%s -oi -f %s -t", escapeshellcmd($this->Sendmail), escapeshellarg($this->Sender));
    +    } else {
    +      $sendmail = sprintf("%s -oi -t", escapeshellcmd($this->Sendmail));
    +    }
    +
    +    if(!@$mail = popen($sendmail, 'w')) {
    +      $this->SetError($this->Lang('execute') . $this->Sendmail);
    +      return false;
    +    }
    +
    +    fputs($mail, $header);
    +    fputs($mail, $body);
    +
    +    $result = pclose($mail);
    +    if (version_compare(phpversion(), '4.2.3') == -1) {
    +      $result = $result >> 8 & 0xFF;
    +    }
    +    if($result != 0) {
    +      $this->SetError($this->Lang('execute') . $this->Sendmail);
    +      return false;
    +    }
    +    return true;
    +  }
    +
    +  /**
    +   * Sends mail using the PHP mail() function.
    +   * @access private
    +   * @return bool
    +   */
    +  function MailSend($header, $body) {
    +
    +    $to = '';
    +    for($i = 0; $i < count($this->to); $i++) {
    +      if($i != 0) { $to .= ', '; }
    +      $to .= $this->AddrFormat($this->to[$i]);
    +    }
    +
    +    $toArr = split(',', $to);
    +
    +    $params = sprintf("-oi -f %s", $this->Sender);
    +    if ($this->Sender != '' && strlen(ini_get('safe_mode')) < 1) {
    +      $old_from = ini_get('sendmail_from');
    +      ini_set('sendmail_from', $this->Sender);
    +      if ($this->SingleTo === true && count($toArr) > 1) {
    +        foreach ($toArr as $key => $val) {
    +          $rt = @mail($val, $this->EncodeHeader($this->SecureHeader($this->Subject)), $body, $header, $params);
    +        }
    +      } else {
    +        $rt = @mail($to, $this->EncodeHeader($this->SecureHeader($this->Subject)), $body, $header, $params);
    +      }
    +    } else {
    +      if ($this->SingleTo === true && count($toArr) > 1) {
    +        foreach ($toArr as $key => $val) {
    +          $rt = @mail($val, $this->EncodeHeader($this->SecureHeader($this->Subject)), $body, $header, $params);
    +        }
    +      } else {
    +        $rt = @mail($to, $this->EncodeHeader($this->SecureHeader($this->Subject)), $body, $header);
    +      }
    +    }
    +
    +    if (isset($old_from)) {
    +      ini_set('sendmail_from', $old_from);
    +    }
    +
    +    if(!$rt) {
    +      $this->SetError($this->Lang('instantiate'));
    +      return false;
    +    }
    +
    +    return true;
    +  }
    +
    +  /**
    +   * Sends mail via SMTP using PhpSMTP (Author:
    +   * Chris Ryan).  Returns bool.  Returns false if there is a
    +   * bad MAIL FROM, RCPT, or DATA input.
    +   * @access private
    +   * @return bool
    +   */
    +  function SmtpSend($header, $body) {
    +    include_once($this->PluginDir . 'smtp.php');
    +    $error = '';
    +    $bad_rcpt = array();
    +
    +    if(!$this->SmtpConnect()) {
    +      return false;
    +    }
    +
    +    $smtp_from = ($this->Sender == '') ? $this->From : $this->Sender;
    +    if(!$this->smtp->Mail($smtp_from)) {
    +      $error = $this->Lang('from_failed') . $smtp_from;
    +      $this->SetError($error);
    +      $this->smtp->Reset();
    +      return false;
    +    }
    +
    +    /* Attempt to send attach all recipients */
    +    for($i = 0; $i < count($this->to); $i++) {
    +      if(!$this->smtp->Recipient($this->to[$i][0])) {
    +        $bad_rcpt[] = $this->to[$i][0];
    +      }
    +    }
    +    for($i = 0; $i < count($this->cc); $i++) {
    +      if(!$this->smtp->Recipient($this->cc[$i][0])) {
    +        $bad_rcpt[] = $this->cc[$i][0];
    +      }
    +    }
    +    for($i = 0; $i < count($this->bcc); $i++) {
    +      if(!$this->smtp->Recipient($this->bcc[$i][0])) {
    +        $bad_rcpt[] = $this->bcc[$i][0];
    +      }
    +    }
    +
    +    if(count($bad_rcpt) > 0) { // Create error message
    +      for($i = 0; $i < count($bad_rcpt); $i++) {
    +        if($i != 0) {
    +          $error .= ', ';
    +        }
    +        $error .= $bad_rcpt[$i];
    +      }
    +      $error = $this->Lang('recipients_failed') . $error;
    +      $this->SetError($error);
    +      $this->smtp->Reset();
    +      return false;
    +    }
    +
    +    if(!$this->smtp->Data($header . $body)) {
    +      $this->SetError($this->Lang('data_not_accepted'));
    +      $this->smtp->Reset();
    +      return false;
    +    }
    +    if($this->SMTPKeepAlive == true) {
    +      $this->smtp->Reset();
    +    } else {
    +      $this->SmtpClose();
    +    }
    +
    +    return true;
    +  }
    +
    +  /**
    +   * Initiates a connection to an SMTP server.  Returns false if the
    +   * operation failed.
    +   * @access private
    +   * @return bool
    +   */
    +  function SmtpConnect() {
    +    if($this->smtp == NULL) {
    +      $this->smtp = new SMTP();
    +    }
    +
    +    $this->smtp->do_debug = $this->SMTPDebug;
    +    $hosts = explode(';', $this->Host);
    +    $index = 0;
    +    $connection = ($this->smtp->Connected());
    +
    +    /* Retry while there is no connection */
    +    while($index < count($hosts) && $connection == false) {
    +      $hostinfo = array();
    +      if(eregi('^(.+):([0-9]+)$', $hosts[$index], $hostinfo)) {
    +        $host = $hostinfo[1];
    +        $port = $hostinfo[2];
    +      } else {
    +        $host = $hosts[$index];
    +        $port = $this->Port;
    +      }
    +
    +      if($this->smtp->Connect(((!empty($this->SMTPSecure))?$this->SMTPSecure.'://':'').$host, $port, $this->Timeout)) {
    +        if ($this->Helo != '') {
    +          $this->smtp->Hello($this->Helo);
    +        } else {
    +          $this->smtp->Hello($this->ServerHostname());
    +        }
    +
    +        $connection = true;
    +        if($this->SMTPAuth) {
    +          if(!$this->smtp->Authenticate($this->Username, $this->Password)) {
    +            $this->SetError($this->Lang('authenticate'));
    +            $this->smtp->Reset();
    +            $connection = false;
    +          }
    +        }
    +      }
    +      $index++;
    +    }
    +    if(!$connection) {
    +      $this->SetError($this->Lang('connect_host'));
    +    }
    +
    +    return $connection;
    +  }
    +
    +  /**
    +   * Closes the active SMTP session if one exists.
    +   * @return void
    +   */
    +  function SmtpClose() {
    +    if($this->smtp != NULL) {
    +      if($this->smtp->Connected()) {
    +        $this->smtp->Quit();
    +        $this->smtp->Close();
    +      }
    +    }
    +  }
    +
    +  /**
    +   * Sets the language for all class error messages.  Returns false
    +   * if it cannot load the language file.  The default language type
    +   * is English.
    +   * @param string $lang_type Type of language (e.g. Portuguese: "br")
    +   * @param string $lang_path Path to the language file directory
    +   * @access public
    +   * @return bool
    +   */
    +  function SetLanguage($lang_type, $lang_path = 'language/') {
    +    if(file_exists($lang_path.'phpmailer.lang-'.$lang_type.'.php')) {
    +      include($lang_path.'phpmailer.lang-'.$lang_type.'.php');
    +    } elseif (file_exists($lang_path.'phpmailer.lang-en.php')) {
    +      include($lang_path.'phpmailer.lang-en.php');
    +    } else {
    +      $this->SetError('Could not load language file');
    +      return false;
    +    }
    +    $this->language = $PHPMAILER_LANG;
    +
    +    return true;
    +  }
    +
    +  /////////////////////////////////////////////////
    +  // METHODS, MESSAGE CREATION
    +  /////////////////////////////////////////////////
    +
    +  /**
    +   * Creates recipient headers.
    +   * @access private
    +   * @return string
    +   */
    +  function AddrAppend($type, $addr) {
    +    $addr_str = $type . ': ';
    +    $addr_str .= $this->AddrFormat($addr[0]);
    +    if(count($addr) > 1) {
    +      for($i = 1; $i < count($addr); $i++) {
    +        $addr_str .= ', ' . $this->AddrFormat($addr[$i]);
    +      }
    +    }
    +    $addr_str .= $this->LE;
    +
    +    return $addr_str;
    +  }
    +
    +  /**
    +   * Formats an address correctly.
    +   * @access private
    +   * @return string
    +   */
    +  function AddrFormat($addr) {
    +    if(empty($addr[1])) {
    +      $formatted = $this->SecureHeader($addr[0]);
    +    } else {
    +      $formatted = $this->EncodeHeader($this->SecureHeader($addr[1]), 'phrase') . " <" . $this->SecureHeader($addr[0]) . ">";
    +    }
    +
    +    return $formatted;
    +  }
    +
    +  /**
    +   * Wraps message for use with mailers that do not
    +   * automatically perform wrapping and for quoted-printable.
    +   * Original written by philippe.
    +   * @access private
    +   * @return string
    +   */
    +  function WrapText($message, $length, $qp_mode = false) {
    +    $soft_break = ($qp_mode) ? sprintf(" =%s", $this->LE) : $this->LE;
    +    // If utf-8 encoding is used, we will need to make sure we don't
    +    // split multibyte characters when we wrap
    +    $is_utf8 = (strtolower($this->CharSet) == "utf-8");
    +
    +    $message = $this->FixEOL($message);
    +    if (substr($message, -1) == $this->LE) {
    +      $message = substr($message, 0, -1);
    +    }
    +
    +    $line = explode($this->LE, $message);
    +    $message = '';
    +    for ($i=0 ;$i < count($line); $i++) {
    +      $line_part = explode(' ', $line[$i]);
    +      $buf = '';
    +      for ($e = 0; $e<count($line_part); $e++) {
    +        $word = $line_part[$e];
    +        if ($qp_mode and (strlen($word) > $length)) {
    +          $space_left = $length - strlen($buf) - 1;
    +          if ($e != 0) {
    +            if ($space_left > 20) {
    +              $len = $space_left;
    +              if ($is_utf8) {
    +                $len = $this->UTF8CharBoundary($word, $len);
    +              } elseif (substr($word, $len - 1, 1) == "=") {
    +                $len--;
    +              } elseif (substr($word, $len - 2, 1) == "=") {
    +                $len -= 2;
    +              }
    +              $part = substr($word, 0, $len);
    +              $word = substr($word, $len);
    +              $buf .= ' ' . $part;
    +              $message .= $buf . sprintf("=%s", $this->LE);
    +            } else {
    +              $message .= $buf . $soft_break;
    +            }
    +            $buf = '';
    +          }
    +          while (strlen($word) > 0) {
    +            $len = $length;
    +            if ($is_utf8) {
    +              $len = $this->UTF8CharBoundary($word, $len);
    +            } elseif (substr($word, $len - 1, 1) == "=") {
    +              $len--;
    +            } elseif (substr($word, $len - 2, 1) == "=") {
    +              $len -= 2;
    +            }
    +            $part = substr($word, 0, $len);
    +            $word = substr($word, $len);
    +
    +            if (strlen($word) > 0) {
    +              $message .= $part . sprintf("=%s", $this->LE);
    +            } else {
    +              $buf = $part;
    +            }
    +          }
    +        } else {
    +          $buf_o = $buf;
    +          $buf .= ($e == 0) ? $word : (' ' . $word);
    +
    +          if (strlen($buf) > $length and $buf_o != '') {
    +            $message .= $buf_o . $soft_break;
    +            $buf = $word;
    +          }
    +        }
    +      }
    +      $message .= $buf . $this->LE;
    +    }
    +
    +    return $message;
    +  }
    +
    +  /**
    +   * Finds last character boundary prior to maxLength in a utf-8
    +   * quoted (printable) encoded string.
    +   * Original written by Colin Brown.
    +   * @access private
    +   * @param string $encodedText utf-8 QP text
    +   * @param int    $maxLength   find last character boundary prior to this length
    +   * @return int
    +   */
    +  function UTF8CharBoundary($encodedText, $maxLength) {
    +    $foundSplitPos = false;
    +    $lookBack = 3;
    +    while (!$foundSplitPos) {
    +      $lastChunk = substr($encodedText, $maxLength - $lookBack, $lookBack);
    +      $encodedCharPos = strpos($lastChunk, "=");
    +      if ($encodedCharPos !== false) {
    +        // Found start of encoded character byte within $lookBack block.
    +        // Check the encoded byte value (the 2 chars after the '=')
    +        $hex = substr($encodedText, $maxLength - $lookBack + $encodedCharPos + 1, 2);
    +        $dec = hexdec($hex);
    +        if ($dec < 128) { // Single byte character.
    +          // If the encoded char was found at pos 0, it will fit
    +          // otherwise reduce maxLength to start of the encoded char
    +          $maxLength = ($encodedCharPos == 0) ? $maxLength :
    +          $maxLength - ($lookBack - $encodedCharPos);
    +          $foundSplitPos = true;
    +        } elseif ($dec >= 192) { // First byte of a multi byte character
    +          // Reduce maxLength to split at start of character
    +          $maxLength = $maxLength - ($lookBack - $encodedCharPos);
    +          $foundSplitPos = true;
    +        } elseif ($dec < 192) { // Middle byte of a multi byte character, look further back
    +          $lookBack += 3;
    +        }
    +      } else {
    +        // No encoded character found
    +        $foundSplitPos = true;
    +      }
    +    }
    +    return $maxLength;
    +  }
    +
    +  /**
    +   * Set the body wrapping.
    +   * @access private
    +   * @return void
    +   */
    +  function SetWordWrap() {
    +    if($this->WordWrap < 1) {
    +      return;
    +    }
    +
    +    switch($this->message_type) {
    +      case 'alt':
    +        /* fall through */
    +      case 'alt_attachments':
    +        $this->AltBody = $this->WrapText($this->AltBody, $this->WordWrap);
    +        break;
    +      default:
    +        $this->Body = $this->WrapText($this->Body, $this->WordWrap);
    +        break;
    +    }
    +  }
    +
    +  /**
    +   * Assembles message header.
    +   * @access private
    +   * @return string
    +   */
    +  function CreateHeader() {
    +    $result = '';
    +
    +    /* Set the boundaries */
    +    $uniq_id = md5(uniqid(time()));
    +    $this->boundary[1] = 'b1_' . $uniq_id;
    +    $this->boundary[2] = 'b2_' . $uniq_id;
    +
    +    $result .= $this->HeaderLine('Date', $this->RFCDate());
    +    if($this->Sender == '') {
    +      $result .= $this->HeaderLine('Return-Path', trim($this->From));
    +    } else {
    +      $result .= $this->HeaderLine('Return-Path', trim($this->Sender));
    +    }
    +
    +    /* To be created automatically by mail() */
    +    if($this->Mailer != 'mail') {
    +      if(count($this->to) > 0) {
    +        $result .= $this->AddrAppend('To', $this->to);
    +      } elseif (count($this->cc) == 0) {
    +        $result .= $this->HeaderLine('To', 'undisclosed-recipients:;');
    +      }
    +      if(count($this->cc) > 0) {
    +        $result .= $this->AddrAppend('Cc', $this->cc);
    +      }
    +    }
    +
    +    $from = array();
    +    $from[0][0] = trim($this->From);
    +    $from[0][1] = $this->FromName;
    +    $result .= $this->AddrAppend('From', $from);
    +
    +    /* sendmail and mail() extract Cc from the header before sending */
    +    if((($this->Mailer == 'sendmail') || ($this->Mailer == 'mail')) && (count($this->cc) > 0)) {
    +      $result .= $this->AddrAppend('Cc', $this->cc);
    +    }
    +
    +    /* sendmail and mail() extract Bcc from the header before sending */
    +    if((($this->Mailer == 'sendmail') || ($this->Mailer == 'mail')) && (count($this->bcc) > 0)) {
    +      $result .= $this->AddrAppend('Bcc', $this->bcc);
    +    }
    +
    +    if(count($this->ReplyTo) > 0) {
    +      $result .= $this->AddrAppend('Reply-To', $this->ReplyTo);
    +    }
    +
    +    /* mail() sets the subject itself */
    +    if($this->Mailer != 'mail') {
    +      $result .= $this->HeaderLine('Subject', $this->EncodeHeader($this->SecureHeader($this->Subject)));
    +    }
    +
    +    if($this->MessageID != '') {
    +      $result .= $this->HeaderLine('Message-ID',$this->MessageID);
    +    } else {
    +      $result .= sprintf("Message-ID: <%s@%s>%s", $uniq_id, $this->ServerHostname(), $this->LE);
    +    }
    +    $result .= $this->HeaderLine('X-Priority', $this->Priority);
    +    $result .= $this->HeaderLine('X-Mailer', 'PHPMailer (phpmailer.sourceforge.net) [version ' . $this->Version . ']');
    +
    +    if($this->ConfirmReadingTo != '') {
    +      $result .= $this->HeaderLine('Disposition-Notification-To', '<' . trim($this->ConfirmReadingTo) . '>');
    +    }
    +
    +    // Add custom headers
    +    for($index = 0; $index < count($this->CustomHeader); $index++) {
    +      $result .= $this->HeaderLine(trim($this->CustomHeader[$index][0]), $this->EncodeHeader(trim($this->CustomHeader[$index][1])));
    +    }
    +    if (!$this->sign_key_file) {
    +      $result .= $this->HeaderLine('MIME-Version', '1.0');
    +      $result .= $this->GetMailMIME();
    +    }
    +
    +    return $result;
    +  }
    +
    +  /**
    +   * Returns the message MIME.
    +   * @access private
    +   * @return string
    +   */
    +  function GetMailMIME() {
    +    $result = '';
    +    switch($this->message_type) {
    +      case 'plain':
    +        $result .= $this->HeaderLine('Content-Transfer-Encoding', $this->Encoding);
    +        $result .= sprintf("Content-Type: %s; charset=\"%s\"", $this->ContentType, $this->CharSet);
    +        break;
    +      case 'attachments':
    +        /* fall through */
    +      case 'alt_attachments':
    +        if($this->InlineImageExists()){
    +          $result .= sprintf("Content-Type: %s;%s\ttype=\"text/html\";%s\tboundary=\"%s\"%s", 'multipart/related', $this->LE, $this->LE, $this->boundary[1], $this->LE);
    +        } else {
    +          $result .= $this->HeaderLine('Content-Type', 'multipart/mixed;');
    +          $result .= $this->TextLine("\tboundary=\"" . $this->boundary[1] . '"');
    +        }
    +        break;
    +      case 'alt':
    +        $result .= $this->HeaderLine('Content-Type', 'multipart/alternative;');
    +        $result .= $this->TextLine("\tboundary=\"" . $this->boundary[1] . '"');
    +        break;
    +    }
    +
    +    if($this->Mailer != 'mail') {
    +      $result .= $this->LE.$this->LE;
    +    }
    +
    +    return $result;
    +  }
    +
    +  /**
    +   * Assembles the message body.  Returns an empty string on failure.
    +   * @access private
    +   * @return string
    +   */
    +  function CreateBody() {
    +    $result = '';
    +    if ($this->sign_key_file) {
    +      $result .= $this->GetMailMIME();
    +    }
    +
    +    $this->SetWordWrap();
    +
    +    switch($this->message_type) {
    +      case 'alt':
    +        $result .= $this->GetBoundary($this->boundary[1], '', 'text/plain', '');
    +        $result .= $this->EncodeString($this->AltBody, $this->Encoding);
    +        $result .= $this->LE.$this->LE;
    +        $result .= $this->GetBoundary($this->boundary[1], '', 'text/html', '');
    +        $result .= $this->EncodeString($this->Body, $this->Encoding);
    +        $result .= $this->LE.$this->LE;
    +        $result .= $this->EndBoundary($this->boundary[1]);
    +        break;
    +      case 'plain':
    +        $result .= $this->EncodeString($this->Body, $this->Encoding);
    +        break;
    +      case 'attachments':
    +        $result .= $this->GetBoundary($this->boundary[1], '', '', '');
    +        $result .= $this->EncodeString($this->Body, $this->Encoding);
    +        $result .= $this->LE;
    +        $result .= $this->AttachAll();
    +        break;
    +      case 'alt_attachments':
    +        $result .= sprintf("--%s%s", $this->boundary[1], $this->LE);
    +        $result .= sprintf("Content-Type: %s;%s" . "\tboundary=\"%s\"%s", 'multipart/alternative', $this->LE, $this->boundary[2], $this->LE.$this->LE);
    +        $result .= $this->GetBoundary($this->boundary[2], '', 'text/plain', '') . $this->LE; // Create text body
    +        $result .= $this->EncodeString($this->AltBody, $this->Encoding);
    +        $result .= $this->LE.$this->LE;
    +        $result .= $this->GetBoundary($this->boundary[2], '', 'text/html', '') . $this->LE; // Create the HTML body
    +        $result .= $this->EncodeString($this->Body, $this->Encoding);
    +        $result .= $this->LE.$this->LE;
    +        $result .= $this->EndBoundary($this->boundary[2]);
    +        $result .= $this->AttachAll();
    +        break;
    +    }
    +
    +    if($this->IsError()) {
    +      $result = '';
    +    } else if ($this->sign_key_file) {
    +      $file = tempnam("", "mail");
    +      $fp = fopen($file, "w");
    +      fwrite($fp, $result);
    +      fclose($fp);
    +      $signed = tempnam("", "signed");
    +
    +      if (@openssl_pkcs7_sign($file, $signed, "file://".$this->sign_key_file, array("file://".$this->sign_key_file, $this->sign_key_pass), null)) {
    +        $fp = fopen($signed, "r");
    +        $result = fread($fp, filesize($this->sign_key_file));
    +        fclose($fp);
    +      } else {
    +        $this->SetError($this->Lang("signing").openssl_error_string());
    +        $result = '';
    +      }
    +
    +      unlink($file);
    +      unlink($signed);
    +    }
    +
    +    return $result;
    +  }
    +
    +  /**
    +   * Returns the start of a message boundary.
    +   * @access private
    +   */
    +  function GetBoundary($boundary, $charSet, $contentType, $encoding) {
    +    $result = '';
    +    if($charSet == '') {
    +      $charSet = $this->CharSet;
    +    }
    +    if($contentType == '') {
    +      $contentType = $this->ContentType;
    +    }
    +    if($encoding == '') {
    +      $encoding = $this->Encoding;
    +    }
    +    $result .= $this->TextLine('--' . $boundary);
    +    $result .= sprintf("Content-Type: %s; charset = \"%s\"", $contentType, $charSet);
    +    $result .= $this->LE;
    +    $result .= $this->HeaderLine('Content-Transfer-Encoding', $encoding);
    +    $result .= $this->LE;
    +
    +    return $result;
    +  }
    +
    +  /**
    +   * Returns the end of a message boundary.
    +   * @access private
    +   */
    +  function EndBoundary($boundary) {
    +    return $this->LE . '--' . $boundary . '--' . $this->LE;
    +  }
    +
    +  /**
    +   * Sets the message type.
    +   * @access private
    +   * @return void
    +   */
    +  function SetMessageType() {
    +    if(count($this->attachment) < 1 && strlen($this->AltBody) < 1) {
    +      $this->message_type = 'plain';
    +    } else {
    +      if(count($this->attachment) > 0) {
    +        $this->message_type = 'attachments';
    +      }
    +      if(strlen($this->AltBody) > 0 && count($this->attachment) < 1) {
    +        $this->message_type = 'alt';
    +      }
    +      if(strlen($this->AltBody) > 0 && count($this->attachment) > 0) {
    +        $this->message_type = 'alt_attachments';
    +      }
    +    }
    +  }
    +
    +  /* Returns a formatted header line.
    +   * @access private
    +   * @return string
    +   */
    +  function HeaderLine($name, $value) {
    +    return $name . ': ' . $value . $this->LE;
    +  }
    +
    +  /**
    +   * Returns a formatted mail line.
    +   * @access private
    +   * @return string
    +   */
    +  function TextLine($value) {
    +    return $value . $this->LE;
    +  }
    +
    +  /////////////////////////////////////////////////
    +  // CLASS METHODS, ATTACHMENTS
    +  /////////////////////////////////////////////////
    +
    +  /**
    +   * Adds an attachment from a path on the filesystem.
    +   * Returns false if the file could not be found
    +   * or accessed.
    +   * @param string $path Path to the attachment.
    +   * @param string $name Overrides the attachment name.
    +   * @param string $encoding File encoding (see $Encoding).
    +   * @param string $type File extension (MIME) type.
    +   * @return bool
    +   */
    +  function AddAttachment($path, $name = '', $encoding = 'base64', $type = 'application/octet-stream') {
    +    if(!@is_file($path)) {
    +      $this->SetError($this->Lang('file_access') . $path);
    +      return false;
    +    }
    +
    +    $filename = basename($path);
    +    if($name == '') {
    +      $name = $filename;
    +    }
    +
    +    $cur = count($this->attachment);
    +    $this->attachment[$cur][0] = $path;
    +    $this->attachment[$cur][1] = $filename;
    +    $this->attachment[$cur][2] = $name;
    +    $this->attachment[$cur][3] = $encoding;
    +    $this->attachment[$cur][4] = $type;
    +    $this->attachment[$cur][5] = false; // isStringAttachment
    +    $this->attachment[$cur][6] = 'attachment';
    +    $this->attachment[$cur][7] = 0;
    +
    +    return true;
    +  }
    +
    +  /**
    +   * Attaches all fs, string, and binary attachments to the message.
    +   * Returns an empty string on failure.
    +   * @access private
    +   * @return string
    +   */
    +  function AttachAll() {
    +    /* Return text of body */
    +    $mime = array();
    +
    +    /* Add all attachments */
    +    for($i = 0; $i < count($this->attachment); $i++) {
    +      /* Check for string attachment */
    +      $bString = $this->attachment[$i][5];
    +      if ($bString) {
    +        $string = $this->attachment[$i][0];
    +      } else {
    +        $path = $this->attachment[$i][0];
    +      }
    +
    +      $filename    = $this->attachment[$i][1];
    +      $name        = $this->attachment[$i][2];
    +      $encoding    = $this->attachment[$i][3];
    +      $type        = $this->attachment[$i][4];
    +      $disposition = $this->attachment[$i][6];
    +      $cid         = $this->attachment[$i][7];
    +
    +      $mime[] = sprintf("--%s%s", $this->boundary[1], $this->LE);
    +      $mime[] = sprintf("Content-Type: %s; name=\"%s\"%s", $type, $name, $this->LE);
    +      $mime[] = sprintf("Content-Transfer-Encoding: %s%s", $encoding, $this->LE);
    +
    +      if($disposition == 'inline') {
    +        $mime[] = sprintf("Content-ID: <%s>%s", $cid, $this->LE);
    +      }
    +
    +      $mime[] = sprintf("Content-Disposition: %s; filename=\"%s\"%s", $disposition, $name, $this->LE.$this->LE);
    +
    +      /* Encode as string attachment */
    +      if($bString) {
    +        $mime[] = $this->EncodeString($string, $encoding);
    +        if($this->IsError()) {
    +          return '';
    +        }
    +        $mime[] = $this->LE.$this->LE;
    +      } else {
    +        $mime[] = $this->EncodeFile($path, $encoding);
    +        if($this->IsError()) {
    +          return '';
    +        }
    +        $mime[] = $this->LE.$this->LE;
    +      }
    +    }
    +
    +    $mime[] = sprintf("--%s--%s", $this->boundary[1], $this->LE);
    +
    +    return join('', $mime);
    +  }
    +
    +  /**
    +   * Encodes attachment in requested format.  Returns an
    +   * empty string on failure.
    +   * @access private
    +   * @return string
    +   */
    +  function EncodeFile ($path, $encoding = 'base64') {
    +    if(!@$fd = fopen($path, 'rb')) {
    +      $this->SetError($this->Lang('file_open') . $path);
    +      return '';
    +    }
    +    $magic_quotes = get_magic_quotes_runtime();
    +    set_magic_quotes_runtime(0);
    +    $file_buffer = fread($fd, filesize($path));
    +    $file_buffer = $this->EncodeString($file_buffer, $encoding);
    +    fclose($fd);
    +    set_magic_quotes_runtime($magic_quotes);
    +
    +    return $file_buffer;
    +  }
    +
    +  /**
    +   * Encodes string to requested format. Returns an
    +   * empty string on failure.
    +   * @access private
    +   * @return string
    +   */
    +  function EncodeString ($str, $encoding = 'base64') {
    +    $encoded = '';
    +    switch(strtolower($encoding)) {
    +      case 'base64':
    +        /* chunk_split is found in PHP >= 3.0.6 */
    +        $encoded = chunk_split(base64_encode($str), 76, $this->LE);
    +        break;
    +      case '7bit':
    +      case '8bit':
    +        $encoded = $this->FixEOL($str);
    +        if (substr($encoded, -(strlen($this->LE))) != $this->LE)
    +          $encoded .= $this->LE;
    +        break;
    +      case 'binary':
    +        $encoded = $str;
    +        break;
    +      case 'quoted-printable':
    +        $encoded = $this->EncodeQP($str);
    +        break;
    +      default:
    +        $this->SetError($this->Lang('encoding') . $encoding);
    +        break;
    +    }
    +    return $encoded;
    +  }
    +
    +  /**
    +   * Encode a header string to best of Q, B, quoted or none.
    +   * @access private
    +   * @return string
    +   */
    +  function EncodeHeader ($str, $position = 'text') {
    +    $x = 0;
    +
    +    switch (strtolower($position)) {
    +      case 'phrase':
    +        if (!preg_match('/[\200-\377]/', $str)) {
    +          /* Can't use addslashes as we don't know what value has magic_quotes_sybase. */
    +          $encoded = addcslashes($str, "\0..\37\177\\\"");
    +          if (($str == $encoded) && !preg_match('/[^A-Za-z0-9!#$%&\'*+\/=?^_`{|}~ -]/', $str)) {
    +            return ($encoded);
    +          } else {
    +            return ("\"$encoded\"");
    +          }
    +        }
    +        $x = preg_match_all('/[^\040\041\043-\133\135-\176]/', $str, $matches);
    +        break;
    +      case 'comment':
    +        $x = preg_match_all('/[()"]/', $str, $matches);
    +        /* Fall-through */
    +      case 'text':
    +      default:
    +        $x += preg_match_all('/[\000-\010\013\014\016-\037\177-\377]/', $str, $matches);
    +        break;
    +    }
    +
    +    if ($x == 0) {
    +      return ($str);
    +    }
    +
    +    $maxlen = 75 - 7 - strlen($this->CharSet);
    +    /* Try to select the encoding which should produce the shortest output */
    +    if (strlen($str)/3 < $x) {
    +      $encoding = 'B';
    +      if (function_exists('mb_strlen') && $this->HasMultiBytes($str)) {
    +     // Use a custom function which correctly encodes and wraps long
    +     // multibyte strings without breaking lines within a character
    +        $encoded = $this->Base64EncodeWrapMB($str);
    +      } else {
    +        $encoded = base64_encode($str);
    +        $maxlen -= $maxlen % 4;
    +        $encoded = trim(chunk_split($encoded, $maxlen, "\n"));
    +      }
    +    } else {
    +      $encoding = 'Q';
    +      $encoded = $this->EncodeQ($str, $position);
    +      $encoded = $this->WrapText($encoded, $maxlen, true);
    +      $encoded = str_replace('='.$this->LE, "\n", trim($encoded));
    +    }
    +
    +    $encoded = preg_replace('/^(.*)$/m', " =?".$this->CharSet."?$encoding?\\1?=", $encoded);
    +    $encoded = trim(str_replace("\n", $this->LE, $encoded));
    +
    +    return $encoded;
    +  }
    +
    +  /**
    +   * Checks if a string contains multibyte characters.
    +   * @access private
    +   * @param string $str multi-byte text to wrap encode
    +   * @return bool
    +   */
    +  function HasMultiBytes($str) {
    +    if (function_exists('mb_strlen')) {
    +      return (strlen($str) > mb_strlen($str, $this->CharSet));
    +    } else { // Assume no multibytes (we can't handle without mbstring functions anyway)
    +      return False;
    +    }
    +  }
    +
    +  /**
    +   * Correctly encodes and wraps long multibyte strings for mail headers
    +   * without breaking lines within a character.
    +   * Adapted from a function by paravoid at http://uk.php.net/manual/en/function.mb-encode-mimeheader.php
    +   * @access private
    +   * @param string $str multi-byte text to wrap encode
    +   * @return string
    +   */
    +  function Base64EncodeWrapMB($str) {
    +    $start = "=?".$this->CharSet."?B?";
    +    $end = "?=";
    +    $encoded = "";
    +
    +    $mb_length = mb_strlen($str, $this->CharSet);
    +    // Each line must have length <= 75, including $start and $end
    +    $length = 75 - strlen($start) - strlen($end);
    +    // Average multi-byte ratio
    +    $ratio = $mb_length / strlen($str);
    +    // Base64 has a 4:3 ratio
    +    $offset = $avgLength = floor($length * $ratio * .75);
    +
    +    for ($i = 0; $i < $mb_length; $i += $offset) {
    +      $lookBack = 0;
    +
    +      do {
    +        $offset = $avgLength - $lookBack;
    +        $chunk = mb_substr($str, $i, $offset, $this->CharSet);
    +        $chunk = base64_encode($chunk);
    +        $lookBack++;
    +      }
    +      while (strlen($chunk) > $length);
    +
    +      $encoded .= $chunk . $this->LE;
    +    }
    +
    +    // Chomp the last linefeed
    +    $encoded = substr($encoded, 0, -strlen($this->LE));
    +    return $encoded;
    +  }
    +
    +  /**
    +   * Encode string to quoted-printable.
    +   * @access private
    +   * @return string
    +   */
    +  function EncodeQP( $input = '', $line_max = 76, $space_conv = false ) {
    +    $hex = array('0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F');
    +    $lines = preg_split('/(?:\r\n|\r|\n)/', $input);
    +    $eol = "\r\n";
    +    $escape = '=';
    +    $output = '';
    +    while( list(, $line) = each($lines) ) {
    +      $linlen = strlen($line);
    +      $newline = '';
    +      for($i = 0; $i < $linlen; $i++) {
    +        $c = substr( $line, $i, 1 );
    +        $dec = ord( $c );
    +        if ( ( $i == 0 ) && ( $dec == 46 ) ) { // convert first point in the line into =2E
    +          $c = '=2E';
    +        }
    +        if ( $dec == 32 ) {
    +          if ( $i == ( $linlen - 1 ) ) { // convert space at eol only
    +            $c = '=20';
    +          } else if ( $space_conv ) {
    +            $c = '=20';
    +          }
    +        } elseif ( ($dec == 61) || ($dec < 32 ) || ($dec > 126) ) { // always encode "\t", which is *not* required
    +          $h2 = floor($dec/16);
    +          $h1 = floor($dec%16);
    +          $c = $escape.$hex[$h2].$hex[$h1];
    +        }
    +        if ( (strlen($newline) + strlen($c)) >= $line_max ) { // CRLF is not counted
    +          $output .= $newline.$escape.$eol; //  soft line break; " =\r\n" is okay
    +          $newline = '';
    +          // check if newline first character will be point or not
    +          if ( $dec == 46 ) {
    +            $c = '=2E';
    +          }
    +        }
    +        $newline .= $c;
    +      } // end of for
    +      $output .= $newline.$eol;
    +    } // end of while
    +    return trim($output);
    +  }
    +
    +  /**
    +   * Encode string to q encoding.
    +   * @access private
    +   * @return string
    +   */
    +  function EncodeQ ($str, $position = 'text') {
    +    /* There should not be any EOL in the string */
    +    $encoded = preg_replace("[\r\n]", '', $str);
    +
    +    switch (strtolower($position)) {
    +      case 'phrase':
    +        $encoded = preg_replace("/([^A-Za-z0-9!*+\/ -])/e", "'='.sprintf('%02X', ord('\\1'))", $encoded);
    +        break;
    +      case 'comment':
    +        $encoded = preg_replace("/([\(\)\"])/e", "'='.sprintf('%02X', ord('\\1'))", $encoded);
    +      case 'text':
    +      default:
    +        /* Replace every high ascii, control =, ? and _ characters */
    +        $encoded = preg_replace('/([\000-\011\013\014\016-\037\075\077\137\177-\377])/e',
    +              "'='.sprintf('%02X', ord('\\1'))", $encoded);
    +        break;
    +    }
    +
    +    /* Replace every spaces to _ (more readable than =20) */
    +    $encoded = str_replace(' ', '_', $encoded);
    +
    +    return $encoded;
    +  }
    +
    +  /**
    +   * Adds a string or binary attachment (non-filesystem) to the list.
    +   * This method can be used to attach ascii or binary data,
    +   * such as a BLOB record from a database.
    +   * @param string $string String attachment data.
    +   * @param string $filename Name of the attachment.
    +   * @param string $encoding File encoding (see $Encoding).
    +   * @param string $type File extension (MIME) type.
    +   * @return void
    +   */
    +  function AddStringAttachment($string, $filename, $encoding = 'base64', $type = 'application/octet-stream') {
    +    /* Append to $attachment array */
    +    $cur = count($this->attachment);
    +    $this->attachment[$cur][0] = $string;
    +    $this->attachment[$cur][1] = $filename;
    +    $this->attachment[$cur][2] = $filename;
    +    $this->attachment[$cur][3] = $encoding;
    +    $this->attachment[$cur][4] = $type;
    +    $this->attachment[$cur][5] = true; // isString
    +    $this->attachment[$cur][6] = 'attachment';
    +    $this->attachment[$cur][7] = 0;
    +  }
    +
    +  /**
    +   * Adds an embedded attachment.  This can include images, sounds, and
    +   * just about any other document.  Make sure to set the $type to an
    +   * image type.  For JPEG images use "image/jpeg" and for GIF images
    +   * use "image/gif".
    +   * @param string $path Path to the attachment.
    +   * @param string $cid Content ID of the attachment.  Use this to identify
    +   *        the Id for accessing the image in an HTML form.
    +   * @param string $name Overrides the attachment name.
    +   * @param string $encoding File encoding (see $Encoding).
    +   * @param string $type File extension (MIME) type.
    +   * @return bool
    +   */
    +  function AddEmbeddedImage($path, $cid, $name = '', $encoding = 'base64', $type = 'application/octet-stream') {
    +
    +    if(!@is_file($path)) {
    +      $this->SetError($this->Lang('file_access') . $path);
    +      return false;
    +    }
    +
    +    $filename = basename($path);
    +    if($name == '') {
    +      $name = $filename;
    +    }
    +
    +    /* Append to $attachment array */
    +    $cur = count($this->attachment);
    +    $this->attachment[$cur][0] = $path;
    +    $this->attachment[$cur][1] = $filename;
    +    $this->attachment[$cur][2] = $name;
    +    $this->attachment[$cur][3] = $encoding;
    +    $this->attachment[$cur][4] = $type;
    +    $this->attachment[$cur][5] = false;
    +    $this->attachment[$cur][6] = 'inline';
    +    $this->attachment[$cur][7] = $cid;
    +
    +    return true;
    +  }
    +
    +  /**
    +   * Returns true if an inline attachment is present.
    +   * @access private
    +   * @return bool
    +   */
    +  function InlineImageExists() {
    +    $result = false;
    +    for($i = 0; $i < count($this->attachment); $i++) {
    +      if($this->attachment[$i][6] == 'inline') {
    +        $result = true;
    +        break;
    +      }
    +    }
    +
    +    return $result;
    +  }
    +
    +  /////////////////////////////////////////////////
    +  // CLASS METHODS, MESSAGE RESET
    +  /////////////////////////////////////////////////
    +
    +  /**
    +   * Clears all recipients assigned in the TO array.  Returns void.
    +   * @return void
    +   */
    +  function ClearAddresses() {
    +    $this->to = array();
    +  }
    +
    +  /**
    +   * Clears all recipients assigned in the CC array.  Returns void.
    +   * @return void
    +   */
    +  function ClearCCs() {
    +    $this->cc = array();
    +  }
    +
    +  /**
    +   * Clears all recipients assigned in the BCC array.  Returns void.
    +   * @return void
    +   */
    +  function ClearBCCs() {
    +    $this->bcc = array();
    +  }
    +
    +  /**
    +   * Clears all recipients assigned in the ReplyTo array.  Returns void.
    +   * @return void
    +   */
    +  function ClearReplyTos() {
    +    $this->ReplyTo = array();
    +  }
    +
    +  /**
    +   * Clears all recipients assigned in the TO, CC and BCC
    +   * array.  Returns void.
    +   * @return void
    +   */
    +  function ClearAllRecipients() {
    +    $this->to = array();
    +    $this->cc = array();
    +    $this->bcc = array();
    +  }
    +
    +  /**
    +   * Clears all previously set filesystem, string, and binary
    +   * attachments.  Returns void.
    +   * @return void
    +   */
    +  function ClearAttachments() {
    +    $this->attachment = array();
    +  }
    +
    +  /**
    +   * Clears all custom headers.  Returns void.
    +   * @return void
    +   */
    +  function ClearCustomHeaders() {
    +    $this->CustomHeader = array();
    +  }
    +
    +  /////////////////////////////////////////////////
    +  // CLASS METHODS, MISCELLANEOUS
    +  /////////////////////////////////////////////////
    +
    +  /**
    +   * Adds the error message to the error container.
    +   * Returns void.
    +   * @access private
    +   * @return void
    +   */
    +  function SetError($msg) {
    +    $this->error_count++;
    +    $this->ErrorInfo = $msg;
    +  }
    +
    +  /**
    +   * Returns the proper RFC 822 formatted date.
    +   * @access private
    +   * @return string
    +   */
    +  function RFCDate() {
    +    $tz = date('Z');
    +    $tzs = ($tz < 0) ? '-' : '+';
    +    $tz = abs($tz);
    +    $tz = (int)($tz/3600)*100 + ($tz%3600)/60;
    +    $result = sprintf("%s %s%04d", date('D, j M Y H:i:s'), $tzs, $tz);
    +
    +    return $result;
    +  }
    +
    +  /**
    +   * Returns the appropriate server variable.  Should work with both
    +   * PHP 4.1.0+ as well as older versions.  Returns an empty string
    +   * if nothing is found.
    +   * @access private
    +   * @return mixed
    +   */
    +  function ServerVar($varName) {
    +    global $HTTP_SERVER_VARS;
    +    global $HTTP_ENV_VARS;
    +
    +    if(!isset($_SERVER)) {
    +      $_SERVER = $HTTP_SERVER_VARS;
    +      if(!isset($_SERVER['REMOTE_ADDR'])) {
    +        $_SERVER = $HTTP_ENV_VARS; // must be Apache
    +      }
    +    }
    +
    +    if(isset($_SERVER[$varName])) {
    +      return $_SERVER[$varName];
    +    } else {
    +      return '';
    +    }
    +  }
    +
    +  /**
    +   * Returns the server hostname or 'localhost.localdomain' if unknown.
    +   * @access private
    +   * @return string
    +   */
    +  function ServerHostname() {
    +    if ($this->Hostname != '') {
    +      $result = $this->Hostname;
    +    } elseif ($this->ServerVar('SERVER_NAME') != '') {
    +      $result = $this->ServerVar('SERVER_NAME');
    +    } else {
    +      $result = 'localhost.localdomain';
    +    }
    +
    +    return $result;
    +  }
    +
    +  /**
    +   * Returns a message in the appropriate language.
    +   * @access private
    +   * @return string
    +   */
    +  function Lang($key) {
    +    if(count($this->language) < 1) {
    +      $this->SetLanguage('en'); // set the default language
    +    }
    +
    +    if(isset($this->language[$key])) {
    +      return $this->language[$key];
    +    } else {
    +      return 'Language string failed to load: ' . $key;
    +    }
    +  }
    +
    +  /**
    +   * Returns true if an error occurred.
    +   * @return bool
    +   */
    +  function IsError() {
    +    return ($this->error_count > 0);
    +  }
    +
    +  /**
    +   * Changes every end of line from CR or LF to CRLF.
    +   * @access private
    +   * @return string
    +   */
    +  function FixEOL($str) {
    +    $str = str_replace("\r\n", "\n", $str);
    +    $str = str_replace("\r", "\n", $str);
    +    $str = str_replace("\n", $this->LE, $str);
    +    return $str;
    +  }
    +
    +  /**
    +   * Adds a custom header.
    +   * @return void
    +   */
    +  function AddCustomHeader($custom_header) {
    +    $this->CustomHeader[] = explode(':', $custom_header, 2);
    +  }
    +
    +  /**
    +   * Evaluates the message and returns modifications for inline images and backgrounds
    +   * @access public
    +   * @return $message
    +   */
    +  function MsgHTML($message,$basedir='') {
    +    preg_match_all("/(src|background)=\"(.*)\"/Ui", $message, $images);
    +    if(isset($images[2])) {
    +      foreach($images[2] as $i => $url) {
    +        // do not change urls for absolute images (thanks to corvuscorax)
    +        if (!preg_match('/^[A-z][A-z]*:\/\//',$url)) {
    +          $filename = basename($url);
    +          $directory = dirname($url);
    +          ($directory == '.')?$directory='':'';
    +          $cid = 'cid:' . md5($filename);
    +          $fileParts = split("\.", $filename);
    +          $ext = $fileParts[1];
    +          $mimeType = $this->_mime_types($ext);
    +          if ( strlen($basedir) > 1 && substr($basedir,-1) != '/') { $basedir .= '/'; }
    +          if ( strlen($directory) > 1 && substr($basedir,-1) != '/') { $directory .= '/'; }
    +          $this->AddEmbeddedImage($basedir.$directory.$filename, md5($filename), $filename, 'base64', $mimeType);
    +          if ( $this->AddEmbeddedImage($basedir.$directory.$filename, md5($filename), $filename, 'base64',$mimeType) ) {
    +            $message = preg_replace("/".$images[1][$i]."=\"".preg_quote($url, '/')."\"/Ui", $images[1][$i]."=\"".$cid."\"", $message);
    +          }
    +        }
    +      }
    +    }
    +    $this->IsHTML(true);
    +    $this->Body = $message;
    +    $textMsg = trim(strip_tags(preg_replace('/<(head|title|style|script)[^>]*>.*?<\/\\1>/s','',$message)));
    +    if ( !empty($textMsg) && empty($this->AltBody) ) {
    +      $this->AltBody = $textMsg;
    +    }
    +    if ( empty($this->AltBody) ) {
    +      $this->AltBody = 'To view this email message, open the email in with HTML compatibility!' . "\n\n";
    +    }
    +  }
    +
    +  /**
    +   * Gets the mime type of the embedded or inline image
    +   * @access private
    +   * @return mime type of ext
    +   */
    +  function _mime_types($ext = '') {
    +    $mimes = array(
    +      'hqx'  =>  'application/mac-binhex40',
    +      'cpt'   =>  'application/mac-compactpro',
    +      'doc'   =>  'application/msword',
    +      'bin'   =>  'application/macbinary',
    +      'dms'   =>  'application/octet-stream',
    +      'lha'   =>  'application/octet-stream',
    +      'lzh'   =>  'application/octet-stream',
    +      'exe'   =>  'application/octet-stream',
    +      'class' =>  'application/octet-stream',
    +      'psd'   =>  'application/octet-stream',
    +      'so'    =>  'application/octet-stream',
    +      'sea'   =>  'application/octet-stream',
    +      'dll'   =>  'application/octet-stream',
    +      'oda'   =>  'application/oda',
    +      'pdf'   =>  'application/pdf',
    +      'ai'    =>  'application/postscript',
    +      'eps'   =>  'application/postscript',
    +      'ps'    =>  'application/postscript',
    +      'smi'   =>  'application/smil',
    +      'smil'  =>  'application/smil',
    +      'mif'   =>  'application/vnd.mif',
    +      'xls'   =>  'application/vnd.ms-excel',
    +      'ppt'   =>  'application/vnd.ms-powerpoint',
    +      'wbxml' =>  'application/vnd.wap.wbxml',
    +      'wmlc'  =>  'application/vnd.wap.wmlc',
    +      'dcr'   =>  'application/x-director',
    +      'dir'   =>  'application/x-director',
    +      'dxr'   =>  'application/x-director',
    +      'dvi'   =>  'application/x-dvi',
    +      'gtar'  =>  'application/x-gtar',
    +      'php'   =>  'application/x-httpd-php',
    +      'php4'  =>  'application/x-httpd-php',
    +      'php3'  =>  'application/x-httpd-php',
    +      'phtml' =>  'application/x-httpd-php',
    +      'phps'  =>  'application/x-httpd-php-source',
    +      'js'    =>  'application/x-javascript',
    +      'swf'   =>  'application/x-shockwave-flash',
    +      'sit'   =>  'application/x-stuffit',
    +      'tar'   =>  'application/x-tar',
    +      'tgz'   =>  'application/x-tar',
    +      'xhtml' =>  'application/xhtml+xml',
    +      'xht'   =>  'application/xhtml+xml',
    +      'zip'   =>  'application/zip',
    +      'mid'   =>  'audio/midi',
    +      'midi'  =>  'audio/midi',
    +      'mpga'  =>  'audio/mpeg',
    +      'mp2'   =>  'audio/mpeg',
    +      'mp3'   =>  'audio/mpeg',
    +      'aif'   =>  'audio/x-aiff',
    +      'aiff'  =>  'audio/x-aiff',
    +      'aifc'  =>  'audio/x-aiff',
    +      'ram'   =>  'audio/x-pn-realaudio',
    +      'rm'    =>  'audio/x-pn-realaudio',
    +      'rpm'   =>  'audio/x-pn-realaudio-plugin',
    +      'ra'    =>  'audio/x-realaudio',
    +      'rv'    =>  'video/vnd.rn-realvideo',
    +      'wav'   =>  'audio/x-wav',
    +      'bmp'   =>  'image/bmp',
    +      'gif'   =>  'image/gif',
    +      'jpeg'  =>  'image/jpeg',
    +      'jpg'   =>  'image/jpeg',
    +      'jpe'   =>  'image/jpeg',
    +      'png'   =>  'image/png',
    +      'tiff'  =>  'image/tiff',
    +      'tif'   =>  'image/tiff',
    +      'css'   =>  'text/css',
    +      'html'  =>  'text/html',
    +      'htm'   =>  'text/html',
    +      'shtml' =>  'text/html',
    +      'txt'   =>  'text/plain',
    +      'text'  =>  'text/plain',
    +      'log'   =>  'text/plain',
    +      'rtx'   =>  'text/richtext',
    +      'rtf'   =>  'text/rtf',
    +      'xml'   =>  'text/xml',
    +      'xsl'   =>  'text/xml',
    +      'mpeg'  =>  'video/mpeg',
    +      'mpg'   =>  'video/mpeg',
    +      'mpe'   =>  'video/mpeg',
    +      'qt'    =>  'video/quicktime',
    +      'mov'   =>  'video/quicktime',
    +      'avi'   =>  'video/x-msvideo',
    +      'movie' =>  'video/x-sgi-movie',
    +      'doc'   =>  'application/msword',
    +      'word'  =>  'application/msword',
    +      'xl'    =>  'application/excel',
    +      'eml'   =>  'message/rfc822'
    +    );
    +    return ( ! isset($mimes[strtolower($ext)])) ? 'application/octet-stream' : $mimes[strtolower($ext)];
    +  }
    +
    +  /**
    +   * Set (or reset) Class Objects (variables)
    +   *
    +   * Usage Example:
    +   * $page->set('X-Priority', '3');
    +   *
    +   * @access public
    +   * @param string $name Parameter Name
    +   * @param mixed $value Parameter Value
    +   * NOTE: will not work with arrays, there are no arrays to set/reset
    +   */
    +  function set ( $name, $value = '' ) {
    +    if ( isset($this->$name) ) {
    +      $this->$name = $value;
    +    } else {
    +      $this->SetError('Cannot set or reset variable ' . $name);
    +      return false;
    +    }
    +  }
    +
    +  /**
    +   * Read a file from a supplied filename and return it.
    +   *
    +   * @access public
    +   * @param string $filename Parameter File Name
    +   */
    +  function getFile($filename) {
    +    $return = '';
    +    if ($fp = fopen($filename, 'rb')) {
    +      while (!feof($fp)) {
    +        $return .= fread($fp, 1024);
    +      }
    +      fclose($fp);
    +      return $return;
    +    } else {
    +      return false;
    +    }
    +  }
    +
    +  /**
    +   * Strips newlines to prevent header injection.
    +   * @access private
    +   * @param string $str String
    +   * @return string
    +   */
    +  function SecureHeader($str) {
    +    $str = trim($str);
    +    $str = str_replace("\r", "", $str);
    +    $str = str_replace("\n", "", $str);
    +    return $str;
    +  }
    +
    +  /**
    +   * Set the private key file and password to sign the message.
    +   *
    +   * @access public
    +   * @param string $key_filename Parameter File Name
    +   * @param string $key_pass Password for private key
    +   */
    +  function Sign($key_filename, $key_pass) {
    +    $this->sign_key_file = $key_filename;
    +    $this->sign_key_pass = $key_pass;
    +  }
    +
    +}
    +
    +?>
    diff --git a/PostToQzone/smtp.php b/PostToQzone/smtp.php
    new file mode 100644
    index 0000000..048bd0f
    --- /dev/null
    +++ b/PostToQzone/smtp.php
    @@ -0,0 +1,1062 @@
    +<?php
    +/*~ class.smtp.php
    +.---------------------------------------------------------------------------.
    +|  Software: PHPMailer - PHP email class                                    |
    +|   Version: 2.0.2                                                          |
    +|   Contact: via sourceforge.net support pages (also www.codeworxtech.com)  |
    +|      Info: http://phpmailer.sourceforge.net                               |
    +|   Support: http://sourceforge.net/projects/phpmailer/                     |
    +| ------------------------------------------------------------------------- |
    +|    Author: Andy Prevost (project admininistrator)                         |
    +|    Author: Brent R. Matzelle (original founder)                           |
    +| Copyright (c) 2004-2007, Andy Prevost. All Rights Reserved.               |
    +| Copyright (c) 2001-2003, Brent R. Matzelle                                |
    +| ------------------------------------------------------------------------- |
    +|   License: Distributed under the Lesser General Public License (LGPL)     |
    +|            http://www.gnu.org/copyleft/lesser.html                        |
    +| This program is distributed in the hope that it will be useful - WITHOUT  |
    +| ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or     |
    +| FITNESS FOR A PARTICULAR PURPOSE.                                         |
    +| ------------------------------------------------------------------------- |
    +| We offer a number of paid services (www.codeworxtech.com):                |
    +| - Web Hosting on highly optimized fast and secure servers                 |
    +| - Technology Consulting                                                   |
    +| - Oursourcing (highly qualified programmers and graphic designers)        |
    +'---------------------------------------------------------------------------'
    + */
    +/**
    + * SMTP is rfc 821 compliant and implements all the rfc 821 SMTP
    + * commands except TURN which will always return a not implemented
    + * error. SMTP also provides some utility methods for sending mail
    + * to an SMTP server.
    + * @package PHPMailer
    + * @author Chris Ryan
    + */
    +
    +class SMTP
    +{
    +  /**
    +   *  SMTP server port
    +   *  @var int
    +   */
    +  var $SMTP_PORT = 25;
    +
    +  /**
    +   *  SMTP reply line ending
    +   *  @var string
    +   */
    +  var $CRLF = "\r\n";
    +
    +  /**
    +   *  Sets whether debugging is turned on
    +   *  @var bool
    +   */
    +  var $do_debug;       # the level of debug to perform
    +
    +  /**
    +   *  Sets VERP use on/off (default is off)
    +   *  @var bool
    +   */
    +  var $do_verp = false;
    +
    +  /**#@+
    +   * @access private
    +   */
    +  var $smtp_conn;      # the socket to the server
    +  var $error;          # error if any on the last call
    +  var $helo_rply;      # the reply the server sent to us for HELO
    +  /**#@-*/
    +
    +  /**
    +   * Initialize the class so that the data is in a known state.
    +   * @access public
    +   * @return void
    +   */
    +  function SMTP() {
    +    $this->smtp_conn = 0;
    +    $this->error = null;
    +    $this->helo_rply = null;
    +
    +    $this->do_debug = 0;
    +  }
    +
    +  /*************************************************************
    +   *                    CONNECTION FUNCTIONS                  *
    +   ***********************************************************/
    +
    +  /**
    +   * Connect to the server specified on the port specified.
    +   * If the port is not specified use the default SMTP_PORT.
    +   * If tval is specified then a connection will try and be
    +   * established with the server for that number of seconds.
    +   * If tval is not specified the default is 30 seconds to
    +   * try on the connection.
    +   *
    +   * SMTP CODE SUCCESS: 220
    +   * SMTP CODE FAILURE: 421
    +   * @access public
    +   * @return bool
    +   */
    +  function Connect($host,$port=0,$tval=30) {
    +    # set the error val to null so there is no confusion
    +    $this->error = null;
    +
    +    # make sure we are __not__ connected
    +    if($this->connected()) {
    +      # ok we are connected! what should we do?
    +      # for now we will just give an error saying we
    +      # are already connected
    +      $this->error = array("error" => "Already connected to a server");
    +      return false;
    +    }
    +
    +    if(empty($port)) {
    +      $port = $this->SMTP_PORT;
    +    }
    +
    +    #connect to the smtp server
    +    $this->smtp_conn = fsockopen($host,    # the host of the server
    +                                 $port,    # the port to use
    +                                 $errno,   # error number if any
    +                                 $errstr,  # error message if any
    +                                 $tval);   # give up after ? secs
    +    # verify we connected properly
    +    if(empty($this->smtp_conn)) {
    +      $this->error = array("error" => "Failed to connect to server",
    +                           "errno" => $errno,
    +                           "errstr" => $errstr);
    +      if($this->do_debug >= 1) {
    +        echo "SMTP -> ERROR: " . $this->error["error"] .
    +                 ": $errstr ($errno)" . $this->CRLF;
    +      }
    +      return false;
    +    }
    +
    +    # sometimes the SMTP server takes a little longer to respond
    +    # so we will give it a longer timeout for the first read
    +    // Windows still does not have support for this timeout function
    +    if(substr(PHP_OS, 0, 3) != "WIN")
    +     socket_set_timeout($this->smtp_conn, $tval, 0);
    +
    +    # get any announcement stuff
    +    $announce = $this->get_lines();
    +
    +    # set the timeout  of any socket functions at 1/10 of a second
    +    //if(function_exists("socket_set_timeout"))
    +    //   socket_set_timeout($this->smtp_conn, 0, 100000);
    +
    +    if($this->do_debug >= 2) {
    +      echo "SMTP -> FROM SERVER:" . $this->CRLF . $announce;
    +    }
    +
    +    return true;
    +  }
    +
    +  /**
    +   * Performs SMTP authentication.  Must be run after running the
    +   * Hello() method.  Returns true if successfully authenticated.
    +   * @access public
    +   * @return bool
    +   */
    +  function Authenticate($username, $password) {
    +    // Start authentication
    +    fputs($this->smtp_conn,"AUTH LOGIN" . $this->CRLF);
    +
    +    $rply = $this->get_lines();
    +    $code = substr($rply,0,3);
    +
    +    if($code != 334) {
    +      $this->error =
    +        array("error" => "AUTH not accepted from server",
    +              "smtp_code" => $code,
    +              "smtp_msg" => substr($rply,4));
    +      if($this->do_debug >= 1) {
    +        echo "SMTP -> ERROR: " . $this->error["error"] .
    +                 ": " . $rply . $this->CRLF;
    +      }
    +      return false;
    +    }
    +
    +    // Send encoded username
    +    fputs($this->smtp_conn, base64_encode($username) . $this->CRLF);
    +
    +    $rply = $this->get_lines();
    +    $code = substr($rply,0,3);
    +
    +    if($code != 334) {
    +      $this->error =
    +        array("error" => "Username not accepted from server",
    +              "smtp_code" => $code,
    +              "smtp_msg" => substr($rply,4));
    +      if($this->do_debug >= 1) {
    +        echo "SMTP -> ERROR: " . $this->error["error"] .
    +                 ": " . $rply . $this->CRLF;
    +      }
    +      return false;
    +    }
    +
    +    // Send encoded password
    +    fputs($this->smtp_conn, base64_encode($password) . $this->CRLF);
    +
    +    $rply = $this->get_lines();
    +    $code = substr($rply,0,3);
    +
    +    if($code != 235) {
    +      $this->error =
    +        array("error" => "Password not accepted from server",
    +              "smtp_code" => $code,
    +              "smtp_msg" => substr($rply,4));
    +      if($this->do_debug >= 1) {
    +        echo "SMTP -> ERROR: " . $this->error["error"] .
    +                 ": " . $rply . $this->CRLF;
    +      }
    +      return false;
    +    }
    +
    +    return true;
    +  }
    +
    +  /**
    +   * Returns true if connected to a server otherwise false
    +   * @access private
    +   * @return bool
    +   */
    +  function Connected() {
    +    if(!empty($this->smtp_conn)) {
    +      $sock_status = socket_get_status($this->smtp_conn);
    +      if($sock_status["eof"]) {
    +        # hmm this is an odd situation... the socket is
    +        # valid but we are not connected anymore
    +        if($this->do_debug >= 1) {
    +            echo "SMTP -> NOTICE:" . $this->CRLF .
    +                 "EOF caught while checking if connected";
    +        }
    +        $this->Close();
    +        return false;
    +      }
    +      return true; # everything looks good
    +    }
    +    return false;
    +  }
    +
    +  /**
    +   * Closes the socket and cleans up the state of the class.
    +   * It is not considered good to use this function without
    +   * first trying to use QUIT.
    +   * @access public
    +   * @return void
    +   */
    +  function Close() {
    +    $this->error = null; # so there is no confusion
    +    $this->helo_rply = null;
    +    if(!empty($this->smtp_conn)) {
    +      # close the connection and cleanup
    +      fclose($this->smtp_conn);
    +      $this->smtp_conn = 0;
    +    }
    +  }
    +
    +  /***************************************************************
    +   *                        SMTP COMMANDS                       *
    +   *************************************************************/
    +
    +  /**
    +   * Issues a data command and sends the msg_data to the server
    +   * finializing the mail transaction. $msg_data is the message
    +   * that is to be send with the headers. Each header needs to be
    +   * on a single line followed by a <CRLF> with the message headers
    +   * and the message body being seperated by and additional <CRLF>.
    +   *
    +   * Implements rfc 821: DATA <CRLF>
    +   *
    +   * SMTP CODE INTERMEDIATE: 354
    +   *     [data]
    +   *     <CRLF>.<CRLF>
    +   *     SMTP CODE SUCCESS: 250
    +   *     SMTP CODE FAILURE: 552,554,451,452
    +   * SMTP CODE FAILURE: 451,554
    +   * SMTP CODE ERROR  : 500,501,503,421
    +   * @access public
    +   * @return bool
    +   */
    +  function Data($msg_data) {
    +    $this->error = null; # so no confusion is caused
    +
    +    if(!$this->connected()) {
    +      $this->error = array(
    +              "error" => "Called Data() without being connected");
    +      return false;
    +    }
    +
    +    fputs($this->smtp_conn,"DATA" . $this->CRLF);
    +
    +    $rply = $this->get_lines();
    +    $code = substr($rply,0,3);
    +
    +    if($this->do_debug >= 2) {
    +      echo "SMTP -> FROM SERVER:" . $this->CRLF . $rply;
    +    }
    +
    +    if($code != 354) {
    +      $this->error =
    +        array("error" => "DATA command not accepted from server",
    +              "smtp_code" => $code,
    +              "smtp_msg" => substr($rply,4));
    +      if($this->do_debug >= 1) {
    +        echo "SMTP -> ERROR: " . $this->error["error"] .
    +                 ": " . $rply . $this->CRLF;
    +      }
    +      return false;
    +    }
    +
    +    # the server is ready to accept data!
    +    # according to rfc 821 we should not send more than 1000
    +    # including the CRLF
    +    # characters on a single line so we will break the data up
    +    # into lines by \r and/or \n then if needed we will break
    +    # each of those into smaller lines to fit within the limit.
    +    # in addition we will be looking for lines that start with
    +    # a period '.' and append and additional period '.' to that
    +    # line. NOTE: this does not count towards are limit.
    +
    +    # normalize the line breaks so we know the explode works
    +    $msg_data = str_replace("\r\n","\n",$msg_data);
    +    $msg_data = str_replace("\r","\n",$msg_data);
    +    $lines = explode("\n",$msg_data);
    +
    +    # we need to find a good way to determine is headers are
    +    # in the msg_data or if it is a straight msg body
    +    # currently I am assuming rfc 822 definitions of msg headers
    +    # and if the first field of the first line (':' sperated)
    +    # does not contain a space then it _should_ be a header
    +    # and we can process all lines before a blank "" line as
    +    # headers.
    +    $field = substr($lines[0],0,strpos($lines[0],":"));
    +    $in_headers = false;
    +    if(!empty($field) && !strstr($field," ")) {
    +      $in_headers = true;
    +    }
    +
    +    $max_line_length = 998; # used below; set here for ease in change
    +
    +    while(list(,$line) = @each($lines)) {
    +      $lines_out = null;
    +      if($line == "" && $in_headers) {
    +        $in_headers = false;
    +      }
    +      # ok we need to break this line up into several
    +      # smaller lines
    +      while(strlen($line) > $max_line_length) {
    +        $pos = strrpos(substr($line,0,$max_line_length)," ");
    +
    +        # Patch to fix DOS attack
    +        if(!$pos) {
    +          $pos = $max_line_length - 1;
    +        }
    +
    +        $lines_out[] = substr($line,0,$pos);
    +        $line = substr($line,$pos + 1);
    +        # if we are processing headers we need to
    +        # add a LWSP-char to the front of the new line
    +        # rfc 822 on long msg headers
    +        if($in_headers) {
    +          $line = "\t" . $line;
    +        }
    +      }
    +      $lines_out[] = $line;
    +
    +      # now send the lines to the server
    +      while(list(,$line_out) = @each($lines_out)) {
    +        if(strlen($line_out) > 0)
    +        {
    +          if(substr($line_out, 0, 1) == ".") {
    +            $line_out = "." . $line_out;
    +          }
    +        }
    +        fputs($this->smtp_conn,$line_out . $this->CRLF);
    +      }
    +    }
    +
    +    # ok all the message data has been sent so lets get this
    +    # over with aleady
    +    fputs($this->smtp_conn, $this->CRLF . "." . $this->CRLF);
    +
    +    $rply = $this->get_lines();
    +    $code = substr($rply,0,3);
    +
    +    if($this->do_debug >= 2) {
    +      echo "SMTP -> FROM SERVER:" . $this->CRLF . $rply;
    +    }
    +
    +    if($code != 250) {
    +      $this->error =
    +        array("error" => "DATA not accepted from server",
    +              "smtp_code" => $code,
    +              "smtp_msg" => substr($rply,4));
    +      if($this->do_debug >= 1) {
    +        echo "SMTP -> ERROR: " . $this->error["error"] .
    +                 ": " . $rply . $this->CRLF;
    +      }
    +      return false;
    +    }
    +    return true;
    +  }
    +
    +  /**
    +   * Expand takes the name and asks the server to list all the
    +   * people who are members of the _list_. Expand will return
    +   * back and array of the result or false if an error occurs.
    +   * Each value in the array returned has the format of:
    +   *     [ <full-name> <sp> ] <path>
    +   * The definition of <path> is defined in rfc 821
    +   *
    +   * Implements rfc 821: EXPN <SP> <string> <CRLF>
    +   *
    +   * SMTP CODE SUCCESS: 250
    +   * SMTP CODE FAILURE: 550
    +   * SMTP CODE ERROR  : 500,501,502,504,421
    +   * @access public
    +   * @return string array
    +   */
    +  function Expand($name) {
    +    $this->error = null; # so no confusion is caused
    +
    +    if(!$this->connected()) {
    +      $this->error = array(
    +            "error" => "Called Expand() without being connected");
    +      return false;
    +    }
    +
    +    fputs($this->smtp_conn,"EXPN " . $name . $this->CRLF);
    +
    +    $rply = $this->get_lines();
    +    $code = substr($rply,0,3);
    +
    +    if($this->do_debug >= 2) {
    +      echo "SMTP -> FROM SERVER:" . $this->CRLF . $rply;
    +    }
    +
    +    if($code != 250) {
    +      $this->error =
    +        array("error" => "EXPN not accepted from server",
    +              "smtp_code" => $code,
    +              "smtp_msg" => substr($rply,4));
    +      if($this->do_debug >= 1) {
    +        echo "SMTP -> ERROR: " . $this->error["error"] .
    +                 ": " . $rply . $this->CRLF;
    +      }
    +      return false;
    +    }
    +
    +    # parse the reply and place in our array to return to user
    +    $entries = explode($this->CRLF,$rply);
    +    while(list(,$l) = @each($entries)) {
    +      $list[] = substr($l,4);
    +    }
    +
    +    return $list;
    +  }
    +
    +  /**
    +   * Sends the HELO command to the smtp server.
    +   * This makes sure that we and the server are in
    +   * the same known state.
    +   *
    +   * Implements from rfc 821: HELO <SP> <domain> <CRLF>
    +   *
    +   * SMTP CODE SUCCESS: 250
    +   * SMTP CODE ERROR  : 500, 501, 504, 421
    +   * @access public
    +   * @return bool
    +   */
    +  function Hello($host="") {
    +    $this->error = null; # so no confusion is caused
    +
    +    if(!$this->connected()) {
    +      $this->error = array(
    +            "error" => "Called Hello() without being connected");
    +      return false;
    +    }
    +
    +    # if a hostname for the HELO was not specified determine
    +    # a suitable one to send
    +    if(empty($host)) {
    +      # we need to determine some sort of appopiate default
    +      # to send to the server
    +      $host = "localhost";
    +    }
    +
    +    // Send extended hello first (RFC 2821)
    +    if(!$this->SendHello("EHLO", $host))
    +    {
    +      if(!$this->SendHello("HELO", $host))
    +          return false;
    +    }
    +
    +    return true;
    +  }
    +
    +  /**
    +   * Sends a HELO/EHLO command.
    +   * @access private
    +   * @return bool
    +   */
    +  function SendHello($hello, $host) {
    +    fputs($this->smtp_conn, $hello . " " . $host . $this->CRLF);
    +
    +    $rply = $this->get_lines();
    +    $code = substr($rply,0,3);
    +
    +    if($this->do_debug >= 2) {
    +      echo "SMTP -> FROM SERVER: " . $this->CRLF . $rply;
    +    }
    +
    +    if($code != 250) {
    +      $this->error =
    +        array("error" => $hello . " not accepted from server",
    +              "smtp_code" => $code,
    +              "smtp_msg" => substr($rply,4));
    +      if($this->do_debug >= 1) {
    +        echo "SMTP -> ERROR: " . $this->error["error"] .
    +                 ": " . $rply . $this->CRLF;
    +      }
    +      return false;
    +    }
    +
    +    $this->helo_rply = $rply;
    +
    +    return true;
    +  }
    +
    +  /**
    +   * Gets help information on the keyword specified. If the keyword
    +   * is not specified then returns generic help, ussually contianing
    +   * A list of keywords that help is available on. This function
    +   * returns the results back to the user. It is up to the user to
    +   * handle the returned data. If an error occurs then false is
    +   * returned with $this->error set appropiately.
    +   *
    +   * Implements rfc 821: HELP [ <SP> <string> ] <CRLF>
    +   *
    +   * SMTP CODE SUCCESS: 211,214
    +   * SMTP CODE ERROR  : 500,501,502,504,421
    +   * @access public
    +   * @return string
    +   */
    +  function Help($keyword="") {
    +    $this->error = null; # to avoid confusion
    +
    +    if(!$this->connected()) {
    +      $this->error = array(
    +              "error" => "Called Help() without being connected");
    +      return false;
    +    }
    +
    +    $extra = "";
    +    if(!empty($keyword)) {
    +      $extra = " " . $keyword;
    +    }
    +
    +    fputs($this->smtp_conn,"HELP" . $extra . $this->CRLF);
    +
    +    $rply = $this->get_lines();
    +    $code = substr($rply,0,3);
    +
    +    if($this->do_debug >= 2) {
    +      echo "SMTP -> FROM SERVER:" . $this->CRLF . $rply;
    +    }
    +
    +    if($code != 211 && $code != 214) {
    +      $this->error =
    +        array("error" => "HELP not accepted from server",
    +              "smtp_code" => $code,
    +              "smtp_msg" => substr($rply,4));
    +      if($this->do_debug >= 1) {
    +        echo "SMTP -> ERROR: " . $this->error["error"] .
    +                 ": " . $rply . $this->CRLF;
    +      }
    +      return false;
    +    }
    +
    +    return $rply;
    +  }
    +
    +  /**
    +   * Starts a mail transaction from the email address specified in
    +   * $from. Returns true if successful or false otherwise. If True
    +   * the mail transaction is started and then one or more Recipient
    +   * commands may be called followed by a Data command.
    +   *
    +   * Implements rfc 821: MAIL <SP> FROM:<reverse-path> <CRLF>
    +   *
    +   * SMTP CODE SUCCESS: 250
    +   * SMTP CODE SUCCESS: 552,451,452
    +   * SMTP CODE SUCCESS: 500,501,421
    +   * @access public
    +   * @return bool
    +   */
    +  function Mail($from) {
    +    $this->error = null; # so no confusion is caused
    +
    +    if(!$this->connected()) {
    +      $this->error = array(
    +              "error" => "Called Mail() without being connected");
    +      return false;
    +    }
    +
    +    $useVerp = ($this->do_verp ? "XVERP" : "");
    +    fputs($this->smtp_conn,"MAIL FROM:<" . $from . ">" . $useVerp . $this->CRLF);
    +
    +    $rply = $this->get_lines();
    +    $code = substr($rply,0,3);
    +
    +    if($this->do_debug >= 2) {
    +      echo "SMTP -> FROM SERVER:" . $this->CRLF . $rply;
    +    }
    +
    +    if($code != 250) {
    +      $this->error =
    +        array("error" => "MAIL not accepted from server",
    +              "smtp_code" => $code,
    +              "smtp_msg" => substr($rply,4));
    +      if($this->do_debug >= 1) {
    +        echo "SMTP -> ERROR: " . $this->error["error"] .
    +                 ": " . $rply . $this->CRLF;
    +      }
    +      return false;
    +    }
    +    return true;
    +  }
    +
    +  /**
    +   * Sends the command NOOP to the SMTP server.
    +   *
    +   * Implements from rfc 821: NOOP <CRLF>
    +   *
    +   * SMTP CODE SUCCESS: 250
    +   * SMTP CODE ERROR  : 500, 421
    +   * @access public
    +   * @return bool
    +   */
    +  function Noop() {
    +    $this->error = null; # so no confusion is caused
    +
    +    if(!$this->connected()) {
    +      $this->error = array(
    +              "error" => "Called Noop() without being connected");
    +      return false;
    +    }
    +
    +    fputs($this->smtp_conn,"NOOP" . $this->CRLF);
    +
    +    $rply = $this->get_lines();
    +    $code = substr($rply,0,3);
    +
    +    if($this->do_debug >= 2) {
    +      echo "SMTP -> FROM SERVER:" . $this->CRLF . $rply;
    +    }
    +
    +    if($code != 250) {
    +      $this->error =
    +        array("error" => "NOOP not accepted from server",
    +              "smtp_code" => $code,
    +              "smtp_msg" => substr($rply,4));
    +      if($this->do_debug >= 1) {
    +        echo "SMTP -> ERROR: " . $this->error["error"] .
    +                 ": " . $rply . $this->CRLF;
    +      }
    +      return false;
    +    }
    +    return true;
    +  }
    +
    +  /**
    +   * Sends the quit command to the server and then closes the socket
    +   * if there is no error or the $close_on_error argument is true.
    +   *
    +   * Implements from rfc 821: QUIT <CRLF>
    +   *
    +   * SMTP CODE SUCCESS: 221
    +   * SMTP CODE ERROR  : 500
    +   * @access public
    +   * @return bool
    +   */
    +  function Quit($close_on_error=true) {
    +    $this->error = null; # so there is no confusion
    +
    +    if(!$this->connected()) {
    +      $this->error = array(
    +              "error" => "Called Quit() without being connected");
    +      return false;
    +    }
    +
    +    # send the quit command to the server
    +    fputs($this->smtp_conn,"quit" . $this->CRLF);
    +
    +    # get any good-bye messages
    +    $byemsg = $this->get_lines();
    +
    +    if($this->do_debug >= 2) {
    +      echo "SMTP -> FROM SERVER:" . $this->CRLF . $byemsg;
    +    }
    +
    +    $rval = true;
    +    $e = null;
    +
    +    $code = substr($byemsg,0,3);
    +    if($code != 221) {
    +      # use e as a tmp var cause Close will overwrite $this->error
    +      $e = array("error" => "SMTP server rejected quit command",
    +                 "smtp_code" => $code,
    +                 "smtp_rply" => substr($byemsg,4));
    +      $rval = false;
    +      if($this->do_debug >= 1) {
    +        echo "SMTP -> ERROR: " . $e["error"] . ": " .
    +                 $byemsg . $this->CRLF;
    +      }
    +    }
    +
    +    if(empty($e) || $close_on_error) {
    +      $this->Close();
    +    }
    +
    +    return $rval;
    +  }
    +
    +  /**
    +   * Sends the command RCPT to the SMTP server with the TO: argument of $to.
    +   * Returns true if the recipient was accepted false if it was rejected.
    +   *
    +   * Implements from rfc 821: RCPT <SP> TO:<forward-path> <CRLF>
    +   *
    +   * SMTP CODE SUCCESS: 250,251
    +   * SMTP CODE FAILURE: 550,551,552,553,450,451,452
    +   * SMTP CODE ERROR  : 500,501,503,421
    +   * @access public
    +   * @return bool
    +   */
    +  function Recipient($to) {
    +    $this->error = null; # so no confusion is caused
    +
    +    if(!$this->connected()) {
    +      $this->error = array(
    +              "error" => "Called Recipient() without being connected");
    +      return false;
    +    }
    +
    +    fputs($this->smtp_conn,"RCPT TO:<" . $to . ">" . $this->CRLF);
    +
    +    $rply = $this->get_lines();
    +    $code = substr($rply,0,3);
    +
    +    if($this->do_debug >= 2) {
    +      echo "SMTP -> FROM SERVER:" . $this->CRLF . $rply;
    +    }
    +
    +    if($code != 250 && $code != 251) {
    +      $this->error =
    +        array("error" => "RCPT not accepted from server",
    +              "smtp_code" => $code,
    +              "smtp_msg" => substr($rply,4));
    +      if($this->do_debug >= 1) {
    +        echo "SMTP -> ERROR: " . $this->error["error"] .
    +                 ": " . $rply . $this->CRLF;
    +      }
    +      return false;
    +    }
    +    return true;
    +  }
    +
    +  /**
    +   * Sends the RSET command to abort and transaction that is
    +   * currently in progress. Returns true if successful false
    +   * otherwise.
    +   *
    +   * Implements rfc 821: RSET <CRLF>
    +   *
    +   * SMTP CODE SUCCESS: 250
    +   * SMTP CODE ERROR  : 500,501,504,421
    +   * @access public
    +   * @return bool
    +   */
    +  function Reset() {
    +    $this->error = null; # so no confusion is caused
    +
    +    if(!$this->connected()) {
    +      $this->error = array(
    +              "error" => "Called Reset() without being connected");
    +      return false;
    +    }
    +
    +    fputs($this->smtp_conn,"RSET" . $this->CRLF);
    +
    +    $rply = $this->get_lines();
    +    $code = substr($rply,0,3);
    +
    +    if($this->do_debug >= 2) {
    +      echo "SMTP -> FROM SERVER:" . $this->CRLF . $rply;
    +    }
    +
    +    if($code != 250) {
    +      $this->error =
    +        array("error" => "RSET failed",
    +              "smtp_code" => $code,
    +              "smtp_msg" => substr($rply,4));
    +      if($this->do_debug >= 1) {
    +        echo "SMTP -> ERROR: " . $this->error["error"] .
    +                 ": " . $rply . $this->CRLF;
    +      }
    +      return false;
    +    }
    +
    +    return true;
    +  }
    +
    +  /**
    +   * Starts a mail transaction from the email address specified in
    +   * $from. Returns true if successful or false otherwise. If True
    +   * the mail transaction is started and then one or more Recipient
    +   * commands may be called followed by a Data command. This command
    +   * will send the message to the users terminal if they are logged
    +   * in.
    +   *
    +   * Implements rfc 821: SEND <SP> FROM:<reverse-path> <CRLF>
    +   *
    +   * SMTP CODE SUCCESS: 250
    +   * SMTP CODE SUCCESS: 552,451,452
    +   * SMTP CODE SUCCESS: 500,501,502,421
    +   * @access public
    +   * @return bool
    +   */
    +  function Send($from) {
    +    $this->error = null; # so no confusion is caused
    +
    +    if(!$this->connected()) {
    +      $this->error = array(
    +              "error" => "Called Send() without being connected");
    +      return false;
    +    }
    +
    +    fputs($this->smtp_conn,"SEND FROM:" . $from . $this->CRLF);
    +
    +    $rply = $this->get_lines();
    +    $code = substr($rply,0,3);
    +
    +    if($this->do_debug >= 2) {
    +      echo "SMTP -> FROM SERVER:" . $this->CRLF . $rply;
    +    }
    +
    +    if($code != 250) {
    +      $this->error =
    +        array("error" => "SEND not accepted from server",
    +              "smtp_code" => $code,
    +              "smtp_msg" => substr($rply,4));
    +      if($this->do_debug >= 1) {
    +        echo "SMTP -> ERROR: " . $this->error["error"] .
    +                 ": " . $rply . $this->CRLF;
    +      }
    +      return false;
    +    }
    +    return true;
    +  }
    +
    +  /**
    +   * Starts a mail transaction from the email address specified in
    +   * $from. Returns true if successful or false otherwise. If True
    +   * the mail transaction is started and then one or more Recipient
    +   * commands may be called followed by a Data command. This command
    +   * will send the message to the users terminal if they are logged
    +   * in and send them an email.
    +   *
    +   * Implements rfc 821: SAML <SP> FROM:<reverse-path> <CRLF>
    +   *
    +   * SMTP CODE SUCCESS: 250
    +   * SMTP CODE SUCCESS: 552,451,452
    +   * SMTP CODE SUCCESS: 500,501,502,421
    +   * @access public
    +   * @return bool
    +   */
    +  function SendAndMail($from) {
    +    $this->error = null; # so no confusion is caused
    +
    +    if(!$this->connected()) {
    +      $this->error = array(
    +          "error" => "Called SendAndMail() without being connected");
    +      return false;
    +    }
    +
    +    fputs($this->smtp_conn,"SAML FROM:" . $from . $this->CRLF);
    +
    +    $rply = $this->get_lines();
    +    $code = substr($rply,0,3);
    +
    +    if($this->do_debug >= 2) {
    +      echo "SMTP -> FROM SERVER:" . $this->CRLF . $rply;
    +    }
    +
    +    if($code != 250) {
    +      $this->error =
    +        array("error" => "SAML not accepted from server",
    +              "smtp_code" => $code,
    +              "smtp_msg" => substr($rply,4));
    +      if($this->do_debug >= 1) {
    +        echo "SMTP -> ERROR: " . $this->error["error"] .
    +                 ": " . $rply . $this->CRLF;
    +      }
    +      return false;
    +    }
    +    return true;
    +  }
    +
    +  /**
    +   * Starts a mail transaction from the email address specified in
    +   * $from. Returns true if successful or false otherwise. If True
    +   * the mail transaction is started and then one or more Recipient
    +   * commands may be called followed by a Data command. This command
    +   * will send the message to the users terminal if they are logged
    +   * in or mail it to them if they are not.
    +   *
    +   * Implements rfc 821: SOML <SP> FROM:<reverse-path> <CRLF>
    +   *
    +   * SMTP CODE SUCCESS: 250
    +   * SMTP CODE SUCCESS: 552,451,452
    +   * SMTP CODE SUCCESS: 500,501,502,421
    +   * @access public
    +   * @return bool
    +   */
    +  function SendOrMail($from) {
    +    $this->error = null; # so no confusion is caused
    +
    +    if(!$this->connected()) {
    +      $this->error = array(
    +          "error" => "Called SendOrMail() without being connected");
    +      return false;
    +    }
    +
    +    fputs($this->smtp_conn,"SOML FROM:" . $from . $this->CRLF);
    +
    +    $rply = $this->get_lines();
    +    $code = substr($rply,0,3);
    +
    +    if($this->do_debug >= 2) {
    +      echo "SMTP -> FROM SERVER:" . $this->CRLF . $rply;
    +    }
    +
    +    if($code != 250) {
    +      $this->error =
    +        array("error" => "SOML not accepted from server",
    +              "smtp_code" => $code,
    +              "smtp_msg" => substr($rply,4));
    +      if($this->do_debug >= 1) {
    +        echo "SMTP -> ERROR: " . $this->error["error"] .
    +                 ": " . $rply . $this->CRLF;
    +      }
    +      return false;
    +    }
    +    return true;
    +  }
    +
    +  /**
    +   * This is an optional command for SMTP that this class does not
    +   * support. This method is here to make the RFC821 Definition
    +   * complete for this class and __may__ be implimented in the future
    +   *
    +   * Implements from rfc 821: TURN <CRLF>
    +   *
    +   * SMTP CODE SUCCESS: 250
    +   * SMTP CODE FAILURE: 502
    +   * SMTP CODE ERROR  : 500, 503
    +   * @access public
    +   * @return bool
    +   */
    +  function Turn() {
    +    $this->error = array("error" => "This method, TURN, of the SMTP ".
    +                                    "is not implemented");
    +    if($this->do_debug >= 1) {
    +      echo "SMTP -> NOTICE: " . $this->error["error"] . $this->CRLF;
    +    }
    +    return false;
    +  }
    +
    +  /**
    +   * Verifies that the name is recognized by the server.
    +   * Returns false if the name could not be verified otherwise
    +   * the response from the server is returned.
    +   *
    +   * Implements rfc 821: VRFY <SP> <string> <CRLF>
    +   *
    +   * SMTP CODE SUCCESS: 250,251
    +   * SMTP CODE FAILURE: 550,551,553
    +   * SMTP CODE ERROR  : 500,501,502,421
    +   * @access public
    +   * @return int
    +   */
    +  function Verify($name) {
    +    $this->error = null; # so no confusion is caused
    +
    +    if(!$this->connected()) {
    +      $this->error = array(
    +              "error" => "Called Verify() without being connected");
    +      return false;
    +    }
    +
    +    fputs($this->smtp_conn,"VRFY " . $name . $this->CRLF);
    +
    +    $rply = $this->get_lines();
    +    $code = substr($rply,0,3);
    +
    +    if($this->do_debug >= 2) {
    +      echo "SMTP -> FROM SERVER:" . $this->CRLF . $rply;
    +    }
    +
    +    if($code != 250 && $code != 251) {
    +      $this->error =
    +        array("error" => "VRFY failed on name '$name'",
    +              "smtp_code" => $code,
    +              "smtp_msg" => substr($rply,4));
    +      if($this->do_debug >= 1) {
    +        echo "SMTP -> ERROR: " . $this->error["error"] .
    +                 ": " . $rply . $this->CRLF;
    +      }
    +      return false;
    +    }
    +    return $rply;
    +  }
    +
    +  /*******************************************************************
    +   *                       INTERNAL FUNCTIONS                       *
    +   ******************************************************************/
    +
    +  /**
    +   * Read in as many lines as possible
    +   * either before eof or socket timeout occurs on the operation.
    +   * With SMTP we can tell if we have more lines to read if the
    +   * 4th character is '-' symbol. If it is a space then we don't
    +   * need to read anything else.
    +   * @access private
    +   * @return string
    +   */
    +  function get_lines() {
    +    $data = "";
    +    while($str = @fgets($this->smtp_conn,515)) {
    +      if($this->do_debug >= 4) {
    +        echo "SMTP -> get_lines(): \$data was \"$data\"" .
    +                 $this->CRLF;
    +        echo "SMTP -> get_lines(): \$str is \"$str\"" .
    +                 $this->CRLF;
    +      }
    +      $data .= $str;
    +      if($this->do_debug >= 4) {
    +        echo "SMTP -> get_lines(): \$data is \"$data\"" . $this->CRLF;
    +      }
    +      # if the 4th character is a space then we are done reading
    +      # so just break the loop
    +      if(substr($str,3,1) == " ") { break; }
    +    }
    +    return $data;
    +  }
    +
    +}
    +
    +
    + ?>
    diff --git a/SaeUpload/Plugin.php b/SaeUpload/Plugin.php
    new file mode 100644
    index 0000000..7d3b6f3
    --- /dev/null
    +++ b/SaeUpload/Plugin.php
    @@ -0,0 +1,270 @@
    +<?php
    +/**
    + * <a href="http://sae.sina.com.cn" target="_blank">Sina App Engine</a>专用的文件上传插件,使用Storage做持久化存储。
    + * 
    + * @package SaeUpload
    + * @author Kimi
    + * @version 1.0.0 Beta
    + * @link http://www.ccvita.com/491.html
    + */
    +class SaeUpload_Plugin implements Typecho_Plugin_Interface
    +{
    +    /**
    +     * 激活插件方法,如果激活失败,直接抛出异常
    +     * 
    +     * @access public
    +     * @return void
    +     * @throws Typecho_Plugin_Exception
    +     */
    +    public static function activate()
    +    {
    +        Typecho_Plugin::factory('Widget_Upload')->uploadHandle = array('SaeUpload_Plugin', 'uploadHandle');
    +        Typecho_Plugin::factory('Widget_Upload')->modifyHandle = array('SaeUpload_Plugin', 'modifyHandle');
    +        Typecho_Plugin::factory('Widget_Upload')->deleteHandle = array('SaeUpload_Plugin', 'deleteHandle');
    +        Typecho_Plugin::factory('Widget_Upload')->attachmentHandle = array('SaeUpload_Plugin', 'attachmentHandle');
    +        Typecho_Plugin::factory('Widget_Upload')->attachmentDataHandle = array('SaeUpload_Plugin', 'attachmentDataHandle');
    +        
    +        return _t('请您在 <a href="http://sae.sina.com.cn/?m=storage&app_id='.$_SERVER['HTTP_APPNAME'].'" target="_blank">Sina App Engine控制面板</a> 中创建Storage的Domain: 名称固定为 <strong>typechoupload</strong>');
    +    }
    +    
    +    /**
    +     * 禁用插件方法,如果禁用失败,直接抛出异常
    +     * 
    +     * @static
    +     * @access public
    +     * @return void
    +     * @throws Typecho_Plugin_Exception
    +     */
    +    public static function deactivate(){}
    +    
    +    /**
    +     * 获取插件配置面板
    +     * 
    +     * @access public
    +     * @param Typecho_Widget_Helper_Form $form 配置面板
    +     * @return void
    +     */
    +    public static function config(Typecho_Widget_Helper_Form $form)
    +    {
    +        $domainName = new Typecho_Widget_Helper_Form_Element_Text('saestoragedomain', NULL, 'typechoupload',
    +        _t('Domain名称'), _t('请您在 <a href="http://sae.sina.com.cn/?m=storage&app_id='.$_SERVER['HTTP_APPNAME'].'" target="_blank">Sina App Engine控制面板</a> 中创建Storage的Domain: 名称固定为 <strong>typechoupload</strong>'));
    +        $form->addInput($domainName->addRule(array('SaeUpload_Plugin', 'validateDomainName'), _t('Domain名称错误,或者未上传文件!')));
    +    }
    +    
    +    /**
    +     * 个人用户的配置面板
    +     * 
    +     * @access public
    +     * @param Typecho_Widget_Helper_Form $form
    +     * @return void
    +     */
    +    public static function personalConfig(Typecho_Widget_Helper_Form $form){}
    +    
    +    /**
    +     * 验证Sina App Engine Storage中DomainName是否存在
    +     * 
    +     * @access public
    +     * @param string $domainName domainName
    +     * @return boolean
    +     */
    +    public static function validateDomainName($domainName)
    +    {
    +        return true;
    +        /*
    +        $stor = new SaeStorage();
    +        $ret = $stor->getFilesNum($domainName);
    +        if ($ret) {
    +            return true;
    +        } else {
    +            return false;
    +        }
    +        */
    +    }
    +
    +    /**
    +     * 上传文件处理函数
    +     *
    +     * @access public
    +     * @param array $file 上传的文件
    +     * @return mixed
    +     */
    +    public static function uploadHandle($file)
    +    {
    +        if (empty($file['name'])) {
    +            return false;
    +        }
    +
    +        $fileName = preg_split("(\/|\\|:)", $file['name']);
    +        $file['name'] = array_pop($fileName);
    +        
    +        //获取扩展名
    +        $ext = '';
    +        $part = explode('.', $file['name']);
    +        if (($length = count($part)) > 1) {
    +            $ext = strtolower($part[$length - 1]);
    +        }
    +
    +        if (!self::checkFileType($ext)) {
    +            return false;
    +        }
    +
    +        //获取文件名
    +        $fileName = sprintf('%u', crc32(uniqid())) . '.' . $ext;
    +        $path = $path . '/' . $fileName;//add for mkdir
    +
    +        $stor = new SaeStorage();
    +        $options = Typecho_Widget::widget('Widget_Options');
    +        $SaeStorageDomain = $options->plugin('SaeUpload')->saestoragedomain;
    +
    +        if (isset($file['tmp_name'])) {
    +            //移动上传文件
    +            if (!$path = $stor->upload($SaeStorageDomain,$fileName,$file['tmp_name'])) {
    +                return false;
    +            }
    +        } else if (isset($file['bits'])) {
    +            //直接写入文件
    +            if (!$path = $stor->write($SaeStorageDomain,$fileName,$file['bits'])) {
    +                return false;
    +            }
    +        } else {
    +            return false;
    +        }
    +
    +        if (!isset($file['size'])) {
    +            $attr = $stor->getAttr($SaeStorageDomain,$fileName,array('length'));
    +            $file['size'] = $attr['length'];
    +        }
    +
    +        //返回相对存储路径
    +        return array(
    +            'name' => $file['name'],
    +            'path' => $fileName,
    +            'size' => $file['size'],
    +            'type' => $ext,
    +            'mime' => Typecho_Common::mimeContentType($path)
    +        );
    +    }
    +
    +    /**
    +     * 修改文件处理函数
    +     *
    +     * @access public
    +     * @param array $content 老文件
    +     * @param array $file 新上传的文件
    +     * @return mixed
    +     */
    +    public static function modifyHandle($content, $file)
    +    {
    +        if (empty($file['name'])) {
    +            return false;
    +        }
    +
    +        $fileName = preg_split("(\/|\\|:)", $file['name']);
    +        $file['name'] = array_pop($fileName);
    +        
    +        //获取扩展名
    +        $ext = '';
    +        $part = explode('.', $file['name']);
    +        if (($length = count($part)) > 1) {
    +            $ext = strtolower($part[$length - 1]);
    +        }
    +
    +        if ($content['attachment']->type != $ext) {
    +            return false;
    +        }
    +
    +        //获取文件名
    +        $fileName = $content['attachment']->path;
    +        $path = $path . '/' . $fileName;//add for mkdir
    +
    +        $stor = new SaeStorage();
    +        $options = Typecho_Widget::widget('Widget_Options');
    +        $SaeStorageDomain = $options->plugin('SaeUpload')->saestoragedomain;
    +
    +        if (isset($file['tmp_name'])) {
    +            //移动上传文件
    +            if (!$path = $stor->upload($SaeStorageDomain,$fileName,$file['tmp_name'])) {
    +                return false;
    +            }
    +        } else if (isset($file['bits'])) {
    +            //直接写入文件
    +            if (!$path = $stor->write($SaeStorageDomain,$fileName,$file['bits'])) {
    +                return false;
    +            }
    +        } else {
    +            return false;
    +        }
    +
    +        if (!isset($file['size'])) {
    +            $attr = $stor->getAttr($SaeStorageDomain,$fileName,array('length'));
    +            $file['size'] = $attr['length'];
    +        }
    +
    +        //返回相对存储路径
    +        return array(
    +            'name' => $content['attachment']->name,
    +            'path' => $content['attachment']->path,
    +            'size' => $file['size'],
    +            'type' => $content['attachment']->type,
    +            'mime' => $content['attachment']->mime
    +        );
    +    }
    +
    +    /**
    +     * 删除文件
    +     *
    +     * @access public
    +     * @param array $content 文件相关信息
    +     * @return string
    +     */
    +    public static function deleteHandle(array $content)
    +    {
    +        $stor = new SaeStorage();
    +        $options = Typecho_Widget::widget('Widget_Options');
    +        $SaeStorageDomain = $options->plugin('SaeUpload')->saestoragedomain;
    +        return $stor->delete($SaeStorageDomain,$content['attachment']->path);
    +    }
    +
    +    /**
    +     * 获取实际文件绝对访问路径
    +     *
    +     * @access public
    +     * @param array $content 文件相关信息
    +     * @return string
    +     */
    +    public static function attachmentHandle(array $content)
    +    {
    +        $stor = new SaeStorage();
    +        $options = Typecho_Widget::widget('Widget_Options');
    +        $SaeStorageDomain = $options->plugin('SaeUpload')->saestoragedomain;
    +        return $stor->getUrl($SaeStorageDomain,$content['attachment']->path);
    +    }
    +
    +    /**
    +     * 获取实际文件数据
    +     *
    +     * @access public
    +     * @param array $content
    +     * @return string
    +     */
    +    public static function attachmentDataHandle(array $content)
    +    {
    +        $stor = new SaeStorage();
    +        $options = Typecho_Widget::widget('Widget_Options');
    +        $SaeStorageDomain = $options->plugin('SaeUpload')->saestoragedomain;
    +        return $stor->read($SaeStorageDomain,$content['attachment']->path);
    +    }
    +
    +    /**
    +     * 检查文件名
    +     *
    +     * @access private
    +     * @param string $ext 扩展名
    +     * @return boolean
    +     */
    +    public static function checkFileType($ext)
    +    {
    +        $options = Typecho_Widget::widget('Widget_Options');
    +        return in_array($ext, $options->allowedAttachmentTypes);
    +    }
    +}
    diff --git a/ShareCode/Plugin.php b/ShareCode/Plugin.php
    new file mode 100644
    index 0000000..6635728
    --- /dev/null
    +++ b/ShareCode/Plugin.php
    @@ -0,0 +1,78 @@
    +<?php
    +/**
    + * 直接在在文章中插入[embed_snipt:{code_id}]({code_id}为snipt上面的id)就可引用http://snipt.org/上分享的代码
    + * 
    + * @package ShareCode
    + * @author blankyao
    + * @version 1.0.0
    + * @link http://www.blankyao.cn
    + */
    +
    +class ShareCode_Plugin implements Typecho_Plugin_Interface
    +{
    +    /**
    +     * 激活插件方法,如果激活失败,直接抛出异常
    +     * 
    +     * @access public
    +     * @return void
    +     * @throws Typecho_Plugin_Exception
    +     */
    +    public static function activate()
    +    {
    +        /** 前端输出处理接口 */
    +        Typecho_Plugin::factory('Widget_Abstract_Contents')->content = array('ShareCode_Plugin', 'parse');
    +        Typecho_Plugin::factory('Widget_Abstract_Contents')->excerpt = array('ShareCode_Plugin', 'parse');
    +    }
    +    
    +    /**
    +     * 禁用插件方法,如果禁用失败,直接抛出异常
    +     * 
    +     * @static
    +     * @access public
    +     * @return void
    +     * @throws Typecho_Plugin_Exception
    +     */
    +    public static function deactivate(){}
    +    
    +    /**
    +     * 获取插件配置面板
    +     * 
    +     * @access public
    +     * @param Typecho_Widget_Helper_Form $form 配置面板
    +     * @return void
    +     */
    +    public static function config(Typecho_Widget_Helper_Form $form){}
    +    
    +    /**
    +     * 个人用户的配置面板
    +     * 
    +     * @access public
    +     * @param Typecho_Widget_Helper_Form $form
    +     * @return void
    +     */
    +    public static function personalConfig(Typecho_Widget_Helper_Form $form){}
    +
    +    /**
    +     * 解析内容
    +     * 
    +     * @access public
    +     * @param Typecho_Widget_Helper_Form $form
    +     * @return void
    +     */
    +    public static function parse($value, $lastResult)
    +    {
    +        $value = empty($lastResult) ? $value : $lastResult;
    +        $regex = '/\[embed_snipt:(.*?)]/i';
    +        preg_match_all( $regex, $value, $matches);
    +        
    +        $count = count($matches[0]);
    +        for($i = 0;$i < $count;$i++) {
    +            $url = $matches[1][$i];
    +            $url = '<script type="text/javascript" src="http://embed.snipt.org/'. $url .'"></script>';
    +            
    +            $value = str_replace($matches[0][$i], $url, $value);
    +        }
    +        
    +        return $value;
    +    }
    +}
    \ No newline at end of file
    diff --git a/SimpleCode.php b/SimpleCode.php
    new file mode 100644
    index 0000000..3b67481
    --- /dev/null
    +++ b/SimpleCode.php
    @@ -0,0 +1,83 @@
    +<?php
    +/**
    + * 解析内容源代码中的code串
    + * 
    + * @package Simple Code 
    + * @author qining
    + * @version 1.0.2
    + * @dependence 9.9.2-*
    + * @link http://typecho.org
    + */
    +class SimpleCode implements Typecho_Plugin_Interface
    +{
    +    /**
    +     * 激活插件方法,如果激活失败,直接抛出异常
    +     * 
    +     * @access public
    +     * @return void
    +     * @throws Typecho_Plugin_Exception
    +     */
    +    public static function activate()
    +    {
    +        Typecho_Plugin::factory('Widget_Abstract_Contents')->contentEx = array('SimpleCode', 'parse');
    +        Typecho_Plugin::factory('Widget_Abstract_Contents')->excerptEx = array('SimpleCode', 'parse');
    +        Typecho_Plugin::factory('Widget_Abstract_Comments')->contentEx = array('SimpleCode', 'parse');
    +    }
    +    
    +    /**
    +     * 禁用插件方法,如果禁用失败,直接抛出异常
    +     * 
    +     * @static
    +     * @access public
    +     * @return void
    +     * @throws Typecho_Plugin_Exception
    +     */
    +    public static function deactivate(){}
    +    
    +    /**
    +     * 获取插件配置面板
    +     * 
    +     * @access public
    +     * @param Typecho_Widget_Helper_Form $form 配置面板
    +     * @return void
    +     */
    +    public static function config(Typecho_Widget_Helper_Form $form){}
    +    
    +    /**
    +     * 个人用户的配置面板
    +     * 
    +     * @access public
    +     * @param Typecho_Widget_Helper_Form $form
    +     * @return void
    +     */
    +    public static function personalConfig(Typecho_Widget_Helper_Form $form){}
    +    
    +    /**
    +     * 解析
    +     * 
    +     * @access public
    +     * @param array $matches 解析值
    +     * @return string
    +     */
    +    public static function parseCallback($matches)
    +    {
    +        return highlight_string(trim($matches[2]), true);
    +    }
    +    
    +    /**
    +     * 插件实现方法
    +     * 
    +     * @access public
    +     * @return void
    +     */
    +    public static function parse($text, $widget, $lastResult)
    +    {
    +        $text = empty($lastResult) ? $text : $lastResult;
    +        
    +        if ($widget instanceof Widget_Archive || $widget instanceof Widget_Abstract_Comments) {
    +            return preg_replace_callback("/<code(\s*[^>]*)>(.*?)<\/code>/is", array('SimpleCode', 'parseCallback'), $text);
    +        } else {
    +            return $text;
    +        }
    +    }
    +}
    diff --git a/Textile2/Plugin.php b/Textile2/Plugin.php
    new file mode 100644
    index 0000000..2e7dae7
    --- /dev/null
    +++ b/Textile2/Plugin.php
    @@ -0,0 +1,121 @@
    +<?php
    +/**
    + * This is a wrapper for Jim Riggs' <a href="http://jimandlissa.com/project/textilephp">PHP implementation</a> of <a href="http://bradchoate.com/mt-plugins/textile">Brad Choate's Textile 2</a>.  It is feature compatible with the MovableType plugin. <strong>Does not play well with the Markdown, Textile, or Textile 2 plugins that ship with WordPress.</strong>  Packaged by <a href="http://idly.org/">Adam Gessaman</a>.
    + * 
    + * @package Textile 2 (Improved)
    + * @author Jim Riggs
    + * @version 2.1.1
    + * @dependence 9.9.2-*
    + * @link http://jimandlissa.com/project/textilephp
    + */
    + 
    +require('Textile2/Textile.php');
    +
    +class Textile2_Plugin implements Typecho_Plugin_Interface
    +{
    +    /**
    +     * 激活插件方法,如果激活失败,直接抛出异常
    +     * 
    +     * @access public
    +     * @return void
    +     * @throws Typecho_Plugin_Exception
    +     */
    +    public static function activate()
    +    {
    +        Typecho_Plugin::factory('Widget_Abstract_Contents')->excerpt = array('Textile2_Plugin', 'parse');
    +        Typecho_Plugin::factory('Widget_Abstract_Contents')->content = array('Textile2_Plugin', 'parse');
    +        Typecho_Plugin::factory('Widget_Abstract_Comments')->content = array('Textile2_Plugin', 'parse');
    +    }
    +    
    +    /**
    +     * 禁用插件方法,如果禁用失败,直接抛出异常
    +     * 
    +     * @static
    +     * @access public
    +     * @return void
    +     * @throws Typecho_Plugin_Exception
    +     */
    +    public static function deactivate(){}
    +    
    +    /**
    +     * 获取插件配置面板
    +     * 
    +     * @access public
    +     * @param Typecho_Widget_Helper_Form $form 配置面板
    +     * @return void
    +     */
    +    public static function config(Typecho_Widget_Helper_Form $form)
    +    {
    +        $version = new Typecho_Widget_Helper_Form_Element_Radio('version', 
    +        array('MTTextile' => 'MTTextile - includes Brad Choates\' extensions.',
    +        'Textile' => 'Textile for the Textile purist.'), 'MTTextile',
    +        'Textile Flavor');
    +        $form->addInput($version->multiMode());
    +
    +        $filters = new Typecho_Widget_Helper_Form_Element_Checkbox('filters', 
    +        array('SmartyPants' => 'Apply SmartyPants (provides em and en dashes, and other typographic niceities)',
    +        'EducateQuotes' => 'Apply Texturize (applies curly quotes)'),
    +        array('SmartyPants', 'EducateQuotes'), 'Text Filters');
    +        $form->addInput($filters->multiMode());
    +        
    +        $headerOffset = new Typecho_Widget_Helper_Form_Element_Select('headerOffset', 
    +        array('0 (.h1 = .h1)', '1 (.h1 = .h2)', '2 (.h1 = .h3)', '3 (.h1 = .h4)', '4 (.h1 = .h5)', '5 (.h1 = .h6)'),
    +        0, 'Header Offset');
    +        $form->addInput($headerOffset);
    +        
    +        $parsing = new Typecho_Widget_Helper_Form_Element_Checkbox('parsing', 
    +        array('ClearLines' => 'Strip extra spaces from the end of each line.',
    +        'PreserveSpaces' => 'Change double-spaces to the HTML entity for an em-space (&8195;).'),
    +        NULL, 'Parsing Options');
    +        $form->addInput($parsing->multiMode());
    +        
    +        $inputEncoding = new Typecho_Widget_Helper_Form_Element_Text('inputEncoding', NULL, Helper::options()->charset,
    +        _t('Input Character Encoding'));
    +        $inputEncoding->input->setAttribute('class', 'mini');
    +        $form->addInput($inputEncoding);
    +        
    +        $encoding = new Typecho_Widget_Helper_Form_Element_Text('encoding', NULL, Helper::options()->charset,
    +        _t('Output Character Encoding'));
    +        $encoding->input->setAttribute('class', 'mini');
    +        $form->addInput($encoding);
    +    }
    +    
    +    /**
    +     * 个人用户的配置面板
    +     * 
    +     * @access public
    +     * @param Typecho_Widget_Helper_Form $form
    +     * @return void
    +     */
    +    public static function personalConfig(Typecho_Widget_Helper_Form $form){}
    +    
    +    /**
    +     * 插件实现方法
    +     * 
    +     * @access public
    +     * @return void
    +     */
    +    public static function parse($text, $widget, $lastResult)
    +    {
    +        $text = empty($lastResult) ? $text : $lastResult;
    +        
    +        $settings = Helper::options()->plugin('Textile2');
    +        
    +        if ($settings->version == 'Textile') {
    +            $textile = new Textile;
    +        } else {
    +            $textile = new MTLikeTextile;
    +        }
    +
    +        $textile->options['head_offset'] = $settings->headerOffset;
    +        $textile->options['char_encoding'] = $settings->encoding;
    +        $textile->options['input_encoding'] = $settings->inputEncoding;
    +        
    +        $textile->options['do_quotes'] = $settings->filters && in_array('EducateQuotes', $settings->filters);
    +        $textile->options['smarty_mode'] = $settings->filters && in_array('SmartyPants', $settings->filters);
    +        $textile->options['trim_spaces'] = $settings->parsing && in_array('ClearLines', $settings->parsing);
    +        $textile->options['preserve_spaces'] = $settings->parsing && in_array('PreserveSpaces', $settings->parsing);
    +
    +        return $textile->process($text);
    +    }
    +}
    diff --git a/Textile2/Textile.php b/Textile2/Textile.php
    new file mode 100644
    index 0000000..a99141e
    --- /dev/null
    +++ b/Textile2/Textile.php
    @@ -0,0 +1,4080 @@
    +<?php
    +// @(#) $Id: Textile.php,v 1.13 2005/03/21 15:26:55 jhriggs Exp $
    +
    +/* This program is free software; you can redistribute it and/or modify
    + * it under the terms of the GNU General Public License as published by
    + * the Free Software Foundation; either version 2 of the License, or
    + * (at your option) any later version.
    + *
    + * This program is distributed in the hope that it will be useful,
    + * but WITHOUT ANY WARRANTY; without even the implied warranty of
    + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    + * GNU General Public License for more details.
    + *
    + * You should have received a copy of the GNU General Public License
    + * along with this program; if not, write to the Free Software
    + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307  USA
    + */
    +
    +/**
    + * The Textile class serves as a wrapper for all Textile
    + * functionality. It is not inherently necessary that Textile be a
    + * class; however, this is as close as one can get to a namespace in
    + * PHP. Wrapping the functionality in a class prevents name
    + * collisions and dirtying of the global namespace. The Textile class
    + * uses no global variables and will not have any side-effects on
    + * other code.
    + *
    + * @brief Class wrapper for the Textile functionality.
    + */
    +class Textile {
    +  /**
    +   * The @c array containing all of the Textile options for this
    +   * object.
    +   *
    +   * @private
    +   */
    +  var $options = array();
    +
    +  /**
    +   * The @c string containing the regular expression pattern for a
    +   * URL. This variable is initialized by @c _create_re() which is
    +   * called in the contructor.
    +   *
    +   * @private
    +   */
    +  var $urlre;
    +
    +  /**
    +   * The @c string containing the regular expression pattern for
    +   * punctuation characters. This variable is initialized by
    +   * @c _create_re() which is called in the contructor.
    +   *
    +   * @private
    +   */
    +  var $punct;
    +
    +  /**
    +   * The @c string containing the regular expression pattern for the
    +   * valid vertical alignment codes. This variable is initialized by
    +   * @c _create_re() which is called in the contructor.
    +   *
    +   * @private
    +   */
    +  var $valignre;
    +
    +  /**
    +   * The @c string containing the regular expression pattern for the
    +   * valid table alignment codes. This variable is initialized by
    +   * @c _create_re() which is called in the contructor.
    +   *
    +   * @private
    +   */
    +  var $tblalignre;
    +
    +  /**
    +   * The @c string containing the regular expression pattern for the
    +   * valid horizontal alignment codes. This variable is initialized by
    +   * @c _create_re() which is called in the contructor.
    +   *
    +   * @private
    +   */
    +  var $halignre;
    +
    +  /**
    +   * The @c string containing the regular expression pattern for the
    +   * valid alignment codes. This variable is initialized by
    +   * @c _create_re() which is called in the contructor.
    +   *
    +   * @private
    +   */
    +  var $alignre;
    +
    +  /**
    +   * The @c string containing the regular expression pattern for the
    +   * valid image alignment codes. This variable is initialized by
    +   * @c _create_re() which is called in the contructor.
    +   *
    +   * @private
    +   */
    +  var $imgalignre;
    +
    +  /**
    +   * The @c string containing the regular expression pattern for a
    +   * class, ID, and/or padding specification. This variable is
    +   * initialized by @c _create_re() which is called in the contructor.
    +   *
    +   * @private
    +   */
    +  var $clstypadre;
    +
    +  /**
    +   * The @c string containing the regular expression pattern for a
    +   * class and/or ID specification. This variable is initialized by
    +   * @c _create_re() which is called in the contructor.
    +   *
    +   * @private
    +   */
    +  var $clstyre;
    +
    +  /**
    +   * The @c string containing the regular expression pattern for a
    +   * class, ID, and/or filter specification. This variable is
    +   * initialized by @c _create_re() which is called in the contructor.
    +   *
    +   * @private
    +   */
    +  var $clstyfiltre;
    +
    +  /**
    +   * The @c string containing the regular expression pattern for a
    +   * code block. This variable is initialized by @c _create_re() which
    +   * is called in the contructor.
    +   *
    +   * @private
    +   */
    +  var $codere;
    +
    +  /**
    +   * The @c string containing the regular expression pattern for all
    +   * block tags. This variable is initialized by @c _create_re() which
    +   * is called in the contructor.
    +   *
    +   * @private
    +   */
    +  var $blocktags;
    +
    +  /**
    +   * The @c array containing the list of lookup links.
    +   *
    +   * @private
    +   */
    +  var $links = array();
    +
    +  /**
    +   * The @c array containing <code>array</code>s of replacement blocks
    +   * of text that are temporary removed from the input text to avoid
    +   * processing. Different functions use this replacement
    +   * functionality, and each shifts its own replacement array into
    +   * position 0 and removes it when finished. This avoids having
    +   * several replacement variables and/or functions clobbering
    +   * eachothers' replacement blocks.
    +   *
    +   * @private
    +   */
    +  var $repl = array();
    +
    +  /**
    +   * The @c array containing temporary <code>string</code>s used in
    +   * replacement callbacks. *JHR*
    +   *
    +   * @private
    +   */
    +  var $tmp = array();
    +
    +  /**
    +   * Instantiates a new Textile object. Optional options
    +   * can be passed to initialize the object. Attributes for the
    +   * options key are the same as the get/set method names
    +   * documented here.
    +   *
    +   * @param $options The @c array specifying the options to use for
    +   *        this object.
    +   *
    +   * @public
    +   */
    +  function Textile($options = array()) {
    +    $this->options = $options;
    +    $this->options['filters'] = ($this->options['filters'] ? $this->options['filters'] : array());
    +    $this->options['charset'] = ($this->options['charset'] ? $this->options['charset'] : 'iso-8859-1');
    +    $this->options['char_encoding'] = (isset($this->options['char_encoding']) ? $this->options['char_encoding'] : 1);
    +    $this->options['do_quotes'] = (isset($this->options['do_quotes']) ? $this->options['do_quotes'] : 1);
    +    $this->options['trim_spaces'] = (isset($this->options['trim_spaces']) ? $this->options['trim_spaces'] : 0);
    +    $this->options['smarty_mode'] = (isset($this->options['smarty_mode']) ? $this->options['smarty_mode'] : 1);
    +    $this->options['preserve_spaces'] = (isset($this->options['preserve_spaces']) ? $this->options['preserve_spaaces'] : 0);
    +    $this->options['head_offset'] = (isset($this->options['head_offset']) ? $this->options['head_offset'] : 0);
    +
    +    if (is_array($this->options['css'])) {
    +      $this->css($this->options['css']);
    +    }
    +    $this->options['macros'] = ($this->options['macros'] ? $this->options['macros'] : $this->default_macros());
    +    if (isset($this->options['flavor'])) {
    +      $this->flavor($this->options['flavor']);
    +    } else {
    +      $this->flavor('xhtml1/css');
    +    }
    +    $this->_create_re();
    +  } // function Textile
    +
    +  // getter/setter methods...
    +
    +  /**
    +   * Used to set Textile attributes. Attribute names are the same
    +   * as the get/set method names documented here.
    +   *
    +   * @param $opt A @c string specifying the name of the option to
    +   *        change or an @c array specifying options and values.
    +   * @param $value The value for the provided option name.
    +   *
    +   * @public
    +   */
    +  function set($opt, $value = NULL) {
    +    if (is_array($opt)) {
    +      foreach ($opt as $opt => $value) {
    +        $this->set($opt, $value);
    +      }
    +    } else {
    +      // the following options have special set methods
    +      // that activate upon setting:
    +      if ($opt == 'charset') {
    +        $this->charset($value);
    +      } elseif ($opt == 'css') {
    +        $this->css($value);
    +      } elseif ($opt == 'flavor') {
    +        $this->flavor($value);
    +      } else {
    +        $this->options[$opt] = $value;
    +      }
    +    }
    +  } // function set
    +
    +  /**
    +   * Used to get Textile attributes. Attribute names are the same
    +   * as the get/set method names documented here.
    +   *
    +   * @param $opt A @c string specifying the name of the option to get.
    +   *
    +   * @return The value for the provided option.
    +   *
    +   * @public
    +   */
    +  function get($opt) {
    +    return $this->options[$opt];
    +  } // function get
    +
    +  /**
    +   * Gets or sets the "disable html" control, which allows you to
    +   * prevent HTML tags from being used within the text processed.
    +   * Any HTML tags encountered will be removed if disable html is
    +   * enabled. Default behavior is to allow HTML.
    +   *
    +   * @param $disable_html If provided, a @c bool indicating whether or
    +   *        not this object should disable HTML.
    +   *
    +   * @return A true value if this object disables HTML; a false value
    +   *         otherwise.
    +   *
    +   * @public
    +   */
    +  function disable_html($disable_html = NULL) {
    +    if ($disable_html != NULL) {
    +      $this->options['disable_html'] = $disable_html;
    +    }
    +    return ($this->options['disable_html'] ? $this->options['disable_html'] : 0);
    +  } // function disable_html
    +
    +  /**
    +   * Gets or sets the relative heading offset, which allows you to
    +   * change the heading level used within the text processed. For
    +   * example, if the heading offset is '2' and the text contains an
    +   * 'h1' block, an \<h3\> block will be output.
    +   *
    +   * @param $head_offset If provided, an @c integer specifying the
    +   *        heading offset for this object.
    +   *
    +   * @return An @c integer containing the heading offset for this
    +   *         object.
    +   *
    +   * @public
    +   */
    +  function head_offset($head_offset = NULL) {
    +    if ($head_offset != NULL) {
    +      $this->options['head_offset'] = $head_offset;
    +    }
    +    return ($this->options['head_offset'] ? $this->options['head_offset'] : 0);
    +  } // function head_offset
    +
    +  /**
    +   * Assigns the HTML flavor of output from Textile. Currently
    +   * these are the valid choices: html, xhtml (behaves like "xhtml1"),
    +   * xhtml1, xhtml2. Default flavor is "xhtml1".
    +   *
    +   * Note that the xhtml2 flavor support is experimental and incomplete
    +   * (and will remain that way until the XHTML 2.0 draft becomes a
    +   * proper recommendation).
    +   *
    +   * @param $flavor If provided, a @c string specifying the flavor to
    +   *        be used for this object.
    +   *
    +   * @return A @c string containing the flavor for this object.
    +   *
    +   * @public
    +   */
    +  function flavor($flavor = NULL) {
    +    if ($flavor != NULL) {
    +      $this->options['flavor'] = $flavor;
    +      if (preg_match('/^xhtml(\d)?(\D|$)/', $flavor, $matches)) {
    +        if ($matches[1] == '2') {
    +          $this->options['_line_open'] = '<l>';
    +          $this->options['_line_close'] = '</l>';
    +          $this->options['_blockcode_open'] = '<blockcode>';
    +          $this->options['_blockcode_close'] = '</blockcode>';
    +          $this->options['css_mode'] = 1;
    +        } else {
    +          // xhtml 1.x
    +          $this->options['_line_open'] = '';
    +          $this->options['_line_close'] = '<br />';
    +          $this->options['_blockcode_open'] = '<pre><code>';
    +          $this->options['_blockcode_close'] = '</code></pre>';
    +          $this->options['css_mode'] = 1;
    +        }
    +      } elseif (preg_match('/^html/', $flavor)) {
    +        $this->options['_line_open'] = '';
    +        $this->options['_line_close'] = '<br>';
    +        $this->options['_blockcode_open'] = '<pre><code>';
    +        $this->options['_blockcode_close'] = '</code></pre>';
    +        $this->options['css_mode'] = preg_match('/\/css/', $flavor);
    +      }
    +      if ($this->options['css_mode'] && !isset($this->options['css'])) { $this->_css_defaults(); }
    +    }
    +    return $this->options['flavor'];
    +  } // function flavor
    +
    +  /**
    +   * Gets or sets the css support for Textile. If css is enabled,
    +   * Textile will emit CSS rules. You may pass a 1 or 0 to enable
    +   * or disable CSS behavior altogether. If you pass an associative array,
    +   * you may assign the CSS class names that are used by
    +   * Textile. The following key names for such an array are
    +   * recognized:
    +   *
    +   * <ul>
    +   * <li><b>class_align_right</b>
    +   *
    +   * defaults to 'right'</li>
    +   *
    +   * <li><b>class_align_left</b>
    +   *
    +   * defaults to 'left'</li>
    +   *
    +   * <li><b>class_align_center</b>
    +   *
    +   * defaults to 'center'</li>
    +   *
    +   * <li><b>class_align_top</b>
    +   *
    +   * defaults to 'top'</li>
    +   *
    +   * <li><b>class_align_bottom</b>
    +   *
    +   * defaults to 'bottom'</li>
    +   *
    +   * <li><b>class_align_middle</b>
    +   *
    +   * defaults to 'middle'</li>
    +   *
    +   * <li><b>class_align_justify</b>
    +   *
    +   * defaults to 'justify'</li>
    +   *
    +   * <li><b>class_caps</b>
    +   *
    +   * defaults to 'caps'</li>
    +   *
    +   * <li><b>class_footnote</b>
    +   *
    +   * defaults to 'footnote'</li>
    +   *
    +   * <li><b>id_footnote_prefix</b>
    +   *
    +   * defaults to 'fn'</li>
    +   *
    +   * </ul>
    +   *
    +   * @param $css If provided, either a @c bool indicating whether or
    +   *        not this object should use css or an associative @c array
    +   *        specifying class names to use.
    +   *
    +   * @return Either an associative @c array containing class names
    +   *         used by this object, or a true or false value indicating
    +   *         whether or not this object uses css.
    +   *
    +   * @public
    +   */
    +  function css($css = NULL) {
    +    if ($css != NULL) {
    +      if (is_array($css)) {
    +        $this->options['css'] = $css;
    +        $this->options['css_mode'] = 1;
    +      } else {
    +        $this->options['css_mode'] = $css;
    +        if ($this->options['css_mode'] && !isset($this->options['css'])) { $this->_css_defaults(); }
    +      }
    +    }
    +    return ($this->options['css_mode'] ? $this->options['css'] : 0);
    +  } // function css
    +
    +  /**
    +   * Gets or sets the character set targetted for publication.
    +   * At this time, Textile only changes its behavior
    +   * if the 'utf-8' character set is assigned.
    +   *
    +   * Specifically, if utf-8 is requested, any special characters
    +   * created by Textile will be output as native utf-8 characters
    +   * rather than HTML entities.
    +   *
    +   * @param $charset If provided, a @c string specifying the
    +   *        characater set to be used for this object.
    +   *
    +   * @return A @c string containing the character set for this object.
    +   *
    +   * @public
    +   */
    +  function charset($charset = NULL) {
    +    if ($charset != NULL) {
    +        $this->options['charset'] = $charset;
    +        if (preg_match('/^utf-?8$/i', $this->options['charset'])) {
    +          $this->char_encoding(0);
    +        } else {
    +          $this->char_encoding(1);
    +        }
    +    }
    +    return $this->options['charset'];
    +  } // function charset
    +
    +  /**
    +   * Gets or sets the physical file path to root of document files.
    +   * This path is utilized when images are referenced and size
    +   * calculations are needed (the getimagesize() function is used to read
    +   * the image dimensions).
    +   *
    +   * @param $docroot If provided, a @c string specifying the document
    +   *        root to use for this object.
    +   *
    +   * @return A @c string containing the docroot for this object.
    +   *
    +   * @public
    +   */
    +  function docroot($docroot = NULL) {
    +    if ($docroot != NULL) {
    +      $this->options['docroot'] = $docroot;
    +    }
    +    return $this->options['docroot'];
    +  } // function docroot
    +
    +  /**
    +   * Gets or sets the 'trim spaces' control flag. If enabled, this
    +   * will clear any lines that have only spaces on them (the newline
    +   * itself will remain).
    +   *
    +   * @param $trim_spaces If provided, a @c bool indicating whether or
    +   *        not this object should trim spaces.
    +   *
    +   * @return A true value if this object trims spaces; a false value
    +   *         otherwise.
    +   *
    +   * @public
    +   */
    +  function trim_spaces($trim_spaces = NULL) {
    +    if ($trim_spaces != NULL) {
    +      $this->options['trim_spaces'] = $trim_spaces;
    +    }
    +    return $this->options['trim_spaces'];
    +  } // function trim_spaces
    +
    +  /**
    +   * Gets or sets a parameter that is passed to filters.
    +   *
    +   * @param $filter_param If provided, a parameter that this object
    +   *        should pass to filters.
    +   *
    +   * @return The parameter this object passes to filters.
    +   *
    +   * @public
    +   */
    +  function filter_param($filter_param = NULL) {
    +    if ($filter_param != NULL) {
    +      $this->options['filter_param'] = $filter_param;
    +    }
    +    return $this->options['filter_param'];
    +  } // function filter_param
    +
    +  /**
    +   * Gets or sets the 'preserve spaces' control flag. If enabled, this
    +   * will replace any double spaces within the paragraph data with the
    +   * \&amp;#8195; HTML entity (wide space). The default is 0. Spaces will
    +   * pass through to the browser unchanged and render as a single space.
    +   * Note that this setting has no effect on spaces within \<pre\>,
    +   * \<code\> blocks or \<script\> sections.
    +   *
    +   * @param $preserve_spaces If provided, a @c bool indicating whether
    +   *        or not this object should preserve spaces.
    +   *
    +   * @return A true value if this object preserves spaces; a false
    +   *         value otherwise.
    +   *
    +   * @public
    +   */
    +  function preserve_spaces($preserve_spaces = NULL) {
    +    if ($preserve_spaces != NULL) {
    +      $this->options['preserve_spaces'] = $preserve_spaces;
    +    }
    +    return $this->options['preserve_spaces'];
    +  } // function preserve_spaces
    +
    +  /**
    +   * Gets or sets a list of filters to make available for
    +   * Textile to use. Returns a hash reference of the currently
    +   * assigned filters.
    +   *
    +   * @param $filters If provided, an @c array of filters to be used
    +   *        for this object.
    +   *
    +   * @return An @c array containing the filters for this object.
    +   *
    +   * @public
    +   */
    +  function filters($filters = NULL) {
    +    if ($filters != NULL) {
    +      $this->options['filters'] = $filters;
    +    }
    +    return $this->options['filters'];
    +  } // function filters
    +
    +  /**
    +   * Gets or sets the character encoding logical flag. If character
    +   * encoding is enabled, the htmlentities function is used to
    +   * encode special characters. If character encoding is disabled,
    +   * only \<, \>, " and & are encoded to HTML entities.
    +   *
    +   * @param $char_encoding If provided, a @c bool indicating whether
    +   *        or not this object should encode special characters.
    +   *
    +   * @return A true value if this object encodes special characters; a
    +   *         false value otherwise.
    +   *
    +   * @public
    +   */
    +  function char_encoding($char_encoding = NULL) {
    +    if ($char_encoding != NULL) {
    +      $this->options['char_encoding'] = $char_encoding;
    +    }
    +    return $this->options['char_encoding'];
    +  } // function char_encoding
    +
    +  /**
    +   * Gets or sets the "smart quoting" control flag. Returns the
    +   * current setting.
    +   *
    +   * @param $do_quotes If provided, a @c bool indicating whether or
    +   *        not this object should use smart quoting.
    +   *
    +   * @return A true value if this object uses smart quoting; a false
    +   *         value otherwise.
    +   *
    +   * @public
    +   */
    +  function handle_quotes($do_quotes = NULL) {
    +    if ($do_quotes != NULL) {
    +      $this->options['do_quotes'] = $do_quotes;
    +    }
    +    return $this->options['do_quotes'];
    +  } // function handle_quotes
    +
    +  // end of getter/setter methods
    +
    +  /**
    +   * Creates the class variable regular expression patterns used by
    +   * Textile. They are not initialized in the declaration, because
    +   * some rely on the others, requiring a @c $this reference.
    +   *
    +   * PHP does not have the Perl qr operator to quote or precompile
    +   * patterns, so to avoid escaping and matching problems, all
    +   * patterns must use the same delimiter; this implementation uses
    +   * {}. Every use of these patterns within this class has been
    +   * changed to use these delimiters. *JHR*
    +   *
    +   * @private
    +   */
    +  function _create_re() {
    +    // a URL discovery regex. This is from Mastering Regex from O'Reilly.
    +    // Some modifications by Brad Choate <brad at bradchoate dot com>
    +    $this->urlre = '(?:
    +    # Must start out right...
    +    (?=[a-zA-Z0-9./#])
    +    # Match the leading part (proto://hostname, or just hostname)
    +    (?:
    +        # ftp://, http://, or https:// leading part
    +        (?:ftp|https?|telnet|nntp)://(?:\w+(?::\w+)?@)?[-\w]+(?:\.\w[-\w]*)+
    +        |
    +        (?:mailto:)?[-\+\w]+@[-\w]+(?:\.\w[-\w]*)+
    +        |
    +        # or, try to find a hostname with our more specific sub-expression
    +        (?i: [a-z0-9] (?:[-a-z0-9]*[a-z0-9])? \. )+ # sub domains
    +        # Now ending .com, etc. For these, require lowercase
    +        (?-i: com\b
    +            | edu\b
    +            | biz\b
    +            | gov\b
    +            | in(?:t|fo)\b # .int or .info
    +            | mil\b
    +            | net\b
    +            | org\b
    +            | museum\b
    +            | aero\b
    +            | coop\b
    +            | name\b
    +            | pro\b
    +            | [a-z][a-z]\b # two-letter country codes
    +        )
    +    )?
    +
    +    # Allow an optional port number
    +    (?: : \d+ )?
    +
    +    # The rest of the URL is optional, and begins with / . . .
    +    (?:
    +     /?
    +     # The rest are heuristics for what seems to work well
    +     [^.!,?;:"\'<>()\[\]{}\s\x7F-\xFF]*
    +     (?:
    +        [.!,?;:]+  [^.!,?;:"\'<>()\[\]{}\s\x7F-\xFF]+ #\'"
    +     )*
    +    )?
    +)';
    +
    +    $this->punct = '[\!"\#\$%&\'()\*\+,\-\./:;<=>\?@\[\\\\\]\^_`{\|}\~]';
    +    $this->valignre = '[\-^~]';
    +    $this->tblalignre = '[<>=]';
    +    $this->halignre = '(?:<>|[<>=])';
    +    $this->alignre = '(?:(?:' . $this->valignre . '|<>' . $this->valignre . '?|' . $this->valignre . '?<>|' . $this->valignre . '?' . $this->halignre . '?|' . $this->halignre . '?' . $this->valignre . '?)(?!\w))';
    +    $this->imgalignre = '(?:(?:[<>]|' . $this->valignre . '){1,2})';
    +
    +    $this->clstypadre = '(?:
    +  (?:\([A-Za-z0-9_\- \#]+\))
    +  |
    +  (?:{
    +      (?: \( [^)]+ \) | [^\}] )+
    +     })
    +  |
    +  (?:\(+? (?![A-Za-z0-9_\-\#]) )
    +  |
    +  (?:\)+?)
    +  |
    +  (?: \[ [a-zA-Z\-]+? \] )
    +)';
    +
    +    $this->clstyre = '(?:
    +  (?:\([A-Za-z0-9_\- \#]+\))
    +  |
    +  (?:{
    +      [A-Za-z0-9_\-](?: \( [^)]+ \) | [^\}] )+
    +     })
    +  |
    +  (?: \[ [a-zA-Z\-]+? \] )
    +)';
    +
    +    $this->clstyfiltre = '(?:
    +  (?:\([A-Za-z0-9_\- \#]+\))
    +  |
    +  (?:{
    +      [A-Za-z0-9_\-](?: \( [^)]+ \) | [^\}] )+
    +     })
    +  |
    +  (?:\|[^\|]+\|)
    +  |
    +  (?:\(+?(?![A-Za-z0-9_\-\#]))
    +  |
    +  (?:\)+)
    +  |
    +  (?: \[ [a-zA-Z]+? \] )
    +)';
    +
    +    $this->codere = '(?:
    +    (?:
    +      [\[{]
    +      @                           # opening
    +      (?:\[([A-Za-z0-9]+)\])?     # $1: language id
    +      (.+?)                       # $2: code
    +      @                           # closing
    +      [\]}]
    +    )
    +    |
    +    (?:
    +      (?:^|(?<=[\s\(]))
    +      @                           # opening
    +      (?:\[([A-Za-z0-9]+)\])?     # $3: language id
    +      ([^\s].+?[^\s])             # $4: code itself
    +      @                           # closing
    +      (?:$|(?=' . $this->punct . '{1,2}|\s))
    +    )
    +)';
    +
    +    $this->blocktags = '
    +    <
    +    (( /? ( h[1-6]
    +     | p
    +     | pre
    +     | div
    +     | table
    +     | t[rdh]
    +     | [ou]l
    +     | li
    +     | block(?:quote|code)
    +     | form
    +     | input
    +     | select
    +     | option
    +     | textarea
    +     )
    +    [ >]
    +    )
    +    | !--
    +    )
    +';
    +  } // function _create_re
    +
    +  /**
    +   * Transforms the provided text using Textile markup rules.
    +   *
    +   * @param $str The @c string specifying the text to process.
    +   *
    +   * @return A @c string containing the processed (X)HTML.
    +   *
    +   * @public
    +   */
    +  function process($str) {
    +    /*
    +     * Function names in PHP are case insensitive, so function
    +     * textile() cannot be redefined.  Thus, this PHP implementation
    +     * will only use process().
    +     *
    +     *   return $this->textile($str);
    +     * } // function process
    +     *
    +     * function textile($str) {
    +     */
    +
    +    // quick translator for abbreviated block names
    +    // to their tag
    +    $macros = array('bq' => 'blockquote');
    +
    +    // an array to hold any portions of the text to be preserved
    +    // without further processing by Textile
    +    array_unshift($this->repl, array());
    +
    +    // strip out extra newline characters. we're only matching for \n herein
    +    //$str = preg_replace('!(?:\r?\n|\r)!', "\n", $str);
    +    $str = preg_replace('!(?:\015?\012|\015)!', "\n", $str);
    +
    +    // optionally remove trailing spaces
    +    if ($this->options['trim_spaces']) { $str = preg_replace('/ +$/m', '', $str); }
    +
    +    // preserve contents of the '==', 'pre', 'blockcode' sections
    +    $str = preg_replace_callback('{(^|\n\n)==(.+?)==($|\n\n)}s',
    +                                 $this->_cb('"$m[1]\n\n" . $me->_repl($me->repl[0], $me->format_block(array("text" => $m[2]))) . "\n\n$m[3]"'), $str);
    +
    +    if (!$this->disable_html()) {
    +      // preserve style, script tag contents
    +      $str = preg_replace_callback('!(<(style|script)(?:>| .+?>).*?</\2>)!s', $this->_cb('$me->_repl($me->repl[0], $m[1])'), $str);
    +
    +      // preserve HTML comments
    +      $str = preg_replace_callback('|(<!--.+?-->)|s', $this->_cb('$me->_repl($me->repl[0], $m[1])'), $str);
    +
    +      // preserve pre block contents, encode contents by default
    +      $pre_start = count($this->repl[0]);
    +      $str = preg_replace_callback('{(<pre(?: [^>]*)?>)(.+?)(</pre>)}s',
    +                                   $this->_cb('"\n\n" . $me->_repl($me->repl[0], $m[1] . $me->encode_html($m[2], 1) . $m[3]) . "\n\n"'), $str);
    +      // fix code tags within pre blocks we just saved.
    +      for ($i = $pre_start; $i < count($this->repl[0]); $i++) {
    +        $this->repl[0][$i] = preg_replace('|&lt;(/?)code(.*?)&gt;|s', '<$1code$2>', $this->repl[0][$i]);
    +      }
    +
    +      // preserve code blocks by default, encode contents
    +      $str = preg_replace_callback('{(<code(?: [^>]+)?>)(.+?)(</code>)}s',
    +                                   $this->_cb('$me->_repl($me->repl[0], $m[1] . $me->encode_html($m[2], 1) . $m[3])'), $str);
    +
    +      // encode blockcode tag (an XHTML 2 tag) and encode it's
    +      // content by default
    +      $str = preg_replace_callback('{(<blockcode(?: [^>]+)?>)(.+?)(</blockcode>)}s',
    +                                   $this->_cb('"\n\n" . $me->_repl($me->repl[0], $m[1] . $me->encode_html($m[2], 1) . $m[3]) . "\n\n"'), $str);
    +
    +      // preserve PHPish, ASPish code
    +      $str = preg_replace_callback('!(<([\?%]).*?(\2)>)!s', $this->_cb('$me->_repl($me->repl[0], $m[1])'), $str);
    +    }
    +
    +    // pass through and remove links that follow this format
    +    // [id_without_spaces (optional title text)]url
    +    // lines like this are stripped from the content, and can be
    +    // referred to using the "link text":id_without_spaces syntax
    +    //$links = array();
    +    $str = preg_replace_callback('{(?:\n|^) [ ]* \[ ([^ ]+?) [ ]*? (?:\( (.+?) \) )?  \] ((?:(?:ftp|https?|telnet|nntp)://|/)[^ ]+?) [ ]* (\n|$)}mx',
    +                                 $this->_cb('($me->links[$m[1]] = array("url" => $m[3], "title" => $m[2])) ? $m[4] : $m[4]'), $str);
    +    //$this->links = $links;
    +
    +    // eliminate starting/ending blank lines
    +    $str = preg_replace('/^\n+/s', '', $str, 1);
    +    $str = preg_replace('/\n+$/s', '', $str, 1);
    +
    +    // split up text into paragraph blocks, capturing newlines too
    +    $para = preg_split('/(\n{2,})/', $str, -1, PREG_SPLIT_DELIM_CAPTURE);
    +    unset($block, $bqlang, $filter, $class, $sticky, $lines,
    +          $style, $stickybuff, $lang, $clear);
    +
    +    $out = '';
    +
    +    foreach ($para as $para) {
    +      if (preg_match('/^\n+$/s', $para)) {
    +        if ($sticky && $stickybuff) {
    +          $stickybuff .= $para;
    +        } else {
    +          $out .= $para;
    +        }
    +        continue;
    +      }
    +
    +      if ($sticky) {
    +        $sticky++;
    +      } else {
    +        unset($block);
    +        unset($class);
    +        $style = '';
    +        unset($lang);
    +      }
    +
    +      unset($id, $cite, $align, $padleft, $padright, $lines, $buffer);
    +      if (preg_match('{^(h[1-6]|p|bq|bc|fn\d+)
    +                        ((?:' . $this->clstyfiltre . '*|' . $this->halignre . ')*)
    +                        (\.\.?)
    +                        (?::(\d+|' . $this->urlre . '))?\ (.*)$}sx', $para, $matches)) {
    +        if ($sticky) {
    +          if ($block == 'bc') {
    +            // close our blockcode section
    +            $out = preg_replace('/\n\n$/', '', $out, 1);
    +            $out .= $this->options['_blockcode_close'] . "\n\n";
    +          } elseif ($block == 'bq') {
    +            $out = preg_replace('/\n\n$/', '', $out, 1);
    +            $out .= '</blockquote>' . "\n\n";
    +          } elseif ($block == 'table') {
    +            $table_out = $this->format_table(array('text' => $stickybuff));
    +            if (!$table_out) { $table_out = ''; }
    +            $out .= $table_out;
    +            unset($stickybuff);
    +          } elseif ($block == 'dl') {
    +            $dl_out = $this->format_deflist(array('text' => $stickybuff));
    +            if (!$dl_out) { $dl_out = ''; }
    +            $out .= $dl_out;
    +            unset($stickybuff);
    +          }
    +          $sticky = 0;
    +        }
    +        // block macros: h[1-6](class)., bq(class)., bc(class)., p(class).
    +        //warn "paragraph: [[$para]]\n\tblock: $1\n\tparams: $2\n\tcite: $4";
    +        $block = $matches[1];
    +        $params = $matches[2];
    +        $cite = $matches[4];
    +        if ($matches[3] == '..') {
    +          $sticky = 1;
    +        } else {
    +          $sticky = 0;
    +          unset($class);
    +          unset($bqlang);
    +          unset($lang);
    +          $style = '';
    +          unset($filter);
    +        }
    +        if (preg_match('/^h([1-6])$/', $block, $matches2)) {
    +          if ($this->options['head_offset']) {
    +            $block = 'h' . ($matches2[1] + $this->options['head_offset']);
    +          }
    +        }
    +        if (preg_match('{(' . $this->halignre . '+)}', $params, $matches2)) {
    +          $align = $matches2[1];
    +          $params = preg_replace('{' . $this->halignre . '+}', '', $params, 1);
    +        }
    +        if ($params) {
    +          if (preg_match('/\|(.+)\|/', $params, $matches2)) {
    +            $filter = $matches2[1];
    +            $params = preg_replace('/\|.+?\|/', '', $params, 1);
    +          }
    +          if (preg_match('/{([^}]+)}/', $params, $matches2)) {
    +            $style = $matches2[1];
    +            $style = preg_replace('/\n/', ' ', $style);
    +            $params = preg_replace('/{[^}]+}/', '', $params);
    +          }
    +          if (preg_match('/\(([A-Za-z0-9_\-\ ]+?)(?:\#(.+?))?\)/', $params, $matches2) ||
    +              preg_match('/\(([A-Za-z0-9_\-\ ]+?)?(?:\#(.+?))\)/', $params, $matches2)) {
    +            if ($matches2[1] || $matches2[2]) {
    +              $class = $matches2[1];
    +              $id = $matches2[2];
    +              if ($class) {
    +                $params = preg_replace('/\([A-Za-z0-9_\-\ ]+?(#.*?)?\)/', '', $params);
    +              } elseif ($id) {
    +                $params = preg_replace('/\(#.+?\)/', '', $params);
    +              }
    +            }
    +          }
    +          if (preg_match('/(\(+)/', $params, $matches2)) {
    +            $padleft = strlen($matches2[1]);
    +            $params = preg_replace('/\(+/', '', $params, 1);
    +          }
    +          if (preg_match('/(\)+)/', $params, $matches2)) {
    +            $padright = strlen($matches2[1]);
    +            $params = preg_replace('/\)+/', '', $params, 1);
    +          }
    +          if (preg_match('/\[(.+?)\]/', $params, $matches2)) {
    +            $lang = $matches2[1];
    +            if ($block == 'bc') {
    +              $bqlang = $lang;
    +              unset($lang);
    +            }
    +            $params = preg_replace('/\[.+?\]/', '', $params, 1);
    +          }
    +        }
    +        // warn "settings:\n\tblock: $block\n\tpadleft: $padleft\n\tpadright: $padright\n\tclass: $class\n\tstyle: $style\n\tid: $id\n\tfilter: $filter\n\talign: $align\n\tlang: $lang\n\tsticky: $sticky";
    +        $para = $matches[5];
    +      } elseif (preg_match('|^<textile#(\d+)>$|', $para, $matches)) {
    +        $buffer = $this->repl[0][$matches[1] - 1];
    +      } elseif (preg_match('/^clear([<>]+)?\.$/', $para, $matches)) {
    +        if ($matches[1] == '<') {
    +          $clear = 'left';
    +        } elseif ($matches[1] == '>') {
    +          $clear = 'right';
    +        } else {
    +          $clear = 'both';
    +        }
    +        continue;
    +      } elseif ($sticky && $stickybuff &&
    +                ($block == 'table' || $block == 'dl')) {
    +        $stickybuff .= $para;
    +        continue;
    +      } elseif (preg_match('{^(?:' . $this->halignre . '|' . $this->clstypadre . '*)*
    +                              [\*\#]
    +                              (?:' . $this->halignre . '|' . $this->clstypadre . '*)*
    +                              \ }x', $para)) {
    +        // '*', '#' prefix means a list
    +        $buffer = $this->format_list(array('text' => $para));
    +      } elseif (preg_match('{^(?:table(?:' . $this->tblalignre . '|' . $this->clstypadre . '*)*
    +                              (\.\.?)\s+)?
    +                              (?:_|' . $this->alignre . '|' . $this->clstypadre . '*)*\|}x', $para, $matches)) {
    +        // handle wiki-style tables
    +        if ($matches[1] && ($matches[1] == '..')) {
    +          $block = 'table';
    +          $stickybuff = $para;
    +          $sticky = 1;
    +          continue;
    +        } else {
    +          $buffer = $this->format_table(array('text' => $para));
    +        }
    +      } elseif (preg_match('{^(?:dl(?:' . $this->clstyre . ')*(\.\.?)\s+)}x', $para, $matches)) {
    +        // handle definition lists
    +        if ($matches[1] && ($matches[1] == '..')) {
    +          $block = 'dl';
    +          $stickybuff = $para;
    +          $sticky = 1;
    +          continue;
    +        } else {
    +          $buffer = $this->format_deflist(array('text' => $para));
    +        }
    +      }
    +      if ($buffer) {
    +        $out .= $buffer;
    +        continue;
    +      }
    +      $lines = preg_split('/\n/', $para);
    +      if ((count($lines) == 1) && ($lines[0] == '')) {
    +        continue;
    +      }
    +
    +      $block = ($block ? $block : 'p');
    +
    +      $buffer = '';
    +      $pre = '';
    +      $post = '';
    +
    +      if ($block == 'bc') {
    +        if ($sticky <= 1) {
    +          $pre .= $this->options['_blockcode_open'];
    +          $pre = preg_replace('/>$/s', '', $pre, 1);
    +          if ($bqlang) { $pre .= " language=\"$bqlang\""; }
    +          if ($align) {
    +            $alignment = $this->_halign($align);
    +            if ($this->options['css_mode']) {
    +              if (($padleft || $padright) &&
    +                  (($alignment == 'left') || ($alignment == 'right'))) {
    +                $style .= ';float:' . $alignment;
    +              } else {
    +                $style .= ';text-align:' . $alignment;
    +              }
    +              $class .= ' ' . ($this->options['css']["class_align_$alignment"] ? $this->options['css']["class_align_$alignment"] : $alignment);
    +            } else {
    +              if ($alignment) { $pre .= " align=\"$alignment\""; }
    +            }
    +          }
    +          if ($padleft) { $style .= ";padding-left:${padleft}em"; }
    +          if ($padright) { $style .= ";padding-right:${padright}em"; }
    +          if ($clear) { $style .= ";clear:${clear}"; }
    +          if ($class) { $class = preg_replace('/^ /', '', $class, 1); }
    +          if ($class) { $pre .= " class=\"$class\""; }
    +          if ($id) { $pre .= " id=\"$id\""; }
    +          if ($style) { $style = preg_replace('/^;/', '', $style, 1); }
    +          if ($style) { $pre .= " style=\"$style\""; }
    +          if ($lang) { $pre .= " lang=\"$lang\""; }
    +          $pre .= '>';
    +          unset($lang);
    +          unset($bqlang);
    +          unset($clear);
    +        }
    +        $para = preg_replace_callback('{(?:^|(?<=[\s>])|([{[]))
    +                                        ==(.+?)==
    +                                        (?:$|([\]}])|(?=' . $this->punct . '{1,2}|\s))}sx',
    +                                      $this->_cb('$me->_repl($me->repl[0], $me->format_block(array("text" => $m[2], "inline" => 1, "pre" => $m[1], "post" => $m[3])))'), $para);
    +        $buffer .= $this->encode_html_basic($para, 1);
    +        $buffer = preg_replace('/&lt;textile#(\d+)&gt;/', '<textile#$1>', $buffer);
    +        if ($sticky == 0) {
    +          $post .= $this->options['_blockcode_close'];
    +        }
    +        $out .= $pre . $buffer . $post;
    +        continue;
    +      } elseif ($block == 'bq') {
    +        if ($sticky <= 1) {
    +          $pre .= '<blockquote';
    +          if ($align) {
    +            $alignment = $this->_halign($align);
    +            if ($this->options['css_mode']) {
    +              if (($padleft || $padright) &&
    +                  (($alignment == 'left') || ($alignment == 'right'))) {
    +                $style .= ';float:' . $alignment;
    +              } else {
    +                $style .= ';text-align:' . $alignment;
    +              }
    +              $class .= ' ' . ($this->options['css']["class_align_$alignment"] ? $this->options['css']["class_align_$alignment"] : $alignment);
    +            } else {
    +              if ($alignment) { $pre .= " align=\"$alignment\""; }
    +            }
    +          }
    +          if ($padleft) { $style .= ";padding-left:${padleft}em"; }
    +          if ($padright) { $style .= ";padding-right:${padright}em"; }
    +          if ($clear) { $style .= ";clear:${clear}"; }
    +          if ($class) { $class = preg_replace('/^ /', '', $class, 1); }
    +          if ($class) { $pre .= " class=\"$class\""; }
    +          if ($id) { $pre .= " id=\"$id\""; }
    +          if ($style) { $style = preg_replace('/^;/', '', $style, 1); }
    +          if ($style) { $pre .= " style=\"$style\""; }
    +          if ($lang) { $pre .= " lang=\"$lang\""; }
    +          if ($cite) { $pre .= ' cite="' . $this->format_url(array('url' => $cite)) . '"'; }
    +          $pre .= '>';
    +          unset($clear);
    +        }
    +        $pre .= '<p>';
    +      } elseif (preg_match('/fn(\d+)/', $block, $matches)) {
    +        $fnum = $matches[1];
    +        $pre .= '<p';
    +        if ($this->options['css']['class_footnote']) { $class .= ' ' . $this->options['css']['class_footnote']; }
    +        if ($align) {
    +          $alignment = $this->_halign($align);
    +          if ($this->options['css_mode']) {
    +            if (($padleft || $padright) &&
    +                (($alignment == 'left') || ($alignment == 'right'))) {
    +              $style .= ';float:' . $alignment;
    +            } else {
    +              $style .= ';text-align:' . $alignment;
    +            }
    +            $class .= ($this->options['css']["class_align_$alignment"] ? $this->options['css']["class_align_$alignment"] : $alignment);
    +          } else {
    +            $pre .= " align=\"$alignment\"";
    +          }
    +        }
    +        if ($padleft) { $style .= ";padding-left:${padleft}em"; }
    +        if ($padright) { $style .= ";padding-right:${padright}em"; }
    +        if ($clear) { $style .= ";clear:${clear}"; }
    +        if ($class) { $class = preg_replace('/^ /', '', $class, 1); }
    +        if ($class) { $pre .= " class=\"$class\""; }
    +        $pre .= ' id="' . ($this->options['css']['id_footnote_prefix'] ? $this->options['css']['id_footnote_prefix'] : 'fn') . $fnum . '"';
    +        if ($style) { $style = preg_replace('/^;/', '', $style, 1); }
    +        if ($style) { $pre .= " style=\"$style\""; }
    +        if ($lang) { $pre .= " lang=\"$lang\""; }
    +        $pre .= '>';
    +        $pre .= '<sup>' . $fnum . '</sup> ';
    +        // we can close like a regular paragraph tag now
    +        $block = 'p';
    +        unset($clear);
    +      } else {
    +        $pre .= '<' . ($macros[$block] ? $macros[$block] : $block);
    +        if ($align) {
    +          $alignment = $this->_halign($align);
    +          if ($this->options['css_mode']) {
    +            if (($padleft || $padright) &&
    +                (($alignment == 'left') || ($alignment == 'right'))) {
    +              $style .= ';float:' . $alignment;
    +            } else {
    +              $style .= ';text-align:' . $alignment;
    +            }
    +            $class .= ' ' . ($this->options['css']["class_align_$alignment"] ? $this->options['css']["class_align_$alignment"] : $alignment);
    +          } else {
    +            $pre .= " align=\"$alignment\"";
    +          }
    +        }
    +        if ($padleft) { $style .= ";padding-left:${padleft}em"; }
    +        if ($padright) { $style .= ";padding-right:${padright}em"; }
    +        if ($clear) { $style .= ";clear:${clear}"; }
    +        if ($class) { $class = preg_replace('/^ /', '', $class, 1); }
    +        if ($class) { $pre .= " class=\"$class\""; }
    +        if ($id) { $pre .= " id=\"$id\""; }
    +        if ($style) { $style = preg_replace('/^;/', '', $style, 1); }
    +        if ($style) { $pre .= " style=\"$style\""; }
    +        if ($lang) { $pre .= " lang=\"$lang\""; }
    +        if ($cite && ($block == 'bq')) { $pre .= ' cite="' . $this->format_url(array('url' => $cite)) . '"'; }
    +        $pre .= '>';
    +        unset($clear);
    +      }
    +
    +      $buffer = $this->format_paragraph(array('text' => $para));
    +
    +      if ($block == 'bq') {
    +        if (!preg_match('/<p[ >]/', $buffer)) { $post .= '</p>'; }
    +        if ($sticky == 0) {
    +          $post .= '</blockquote>';
    +        }
    +      } else {
    +        $post .= '</' . $block . '>';
    +      }
    +
    +      if (preg_match('{' . $this->blocktags . '}x', $buffer)) {
    +        $buffer = preg_replace('/^\n\n/s', '', $buffer, 1);
    +        $out .= $buffer;
    +      } else {
    +        if ($filter) { $buffer = $this->format_block(array('text' => "|$filter|" . $buffer, 'inline' => 1)); }
    +        $out .= $pre . $buffer . $post;
    +      }
    +    }
    +
    +    if ($sticky) {
    +      if ($block == 'bc') {
    +        // close our blockcode section
    +        $out .= $this->options['_blockcode_close']; // . "\n\n";
    +      } elseif ($block == 'bq') {
    +        $out .= '</blockquote>'; // . "\n\n";
    +      } elseif (($block == 'table') && $stickybuff) {
    +        $table_out = $this->format_table(array('text' => $stickybuff));
    +        if ($table_out) { $out .= $table_out; }
    +      } elseif (($block == 'dl') && $stickybuff) {
    +        $dl_out = $this->format_deflist(array('text' => $stickybuff));
    +        if ($dl_out) { $out .= $dl_out; }
    +      }
    +    }
    +
    +    // cleanup-- restore preserved blocks
    +    for ($i = count($this->repl[0]); $i > 0; $i--) {
    +      $out = preg_replace('!(?:<|&lt;)textile#' . $i . '(?:>|&gt;)!', str_replace('$', '\\$', $this->repl[0][$i - 1]), $out, 1);
    +    }
    +    array_shift($this->repl);
    +
    +    // scan for br, hr tags that are not closed and close them
    +    // only for xhtml! just the common ones -- don't fret over input
    +    // and the like.
    +    if (preg_match('/^xhtml/i', $this->flavor())) {
    +      $out = preg_replace('/(<(?:img|br|hr)[^>]*?(?<!\/))>/', '$1 />', $out);
    +    }
    +
    +    return $out;
    +  } // function process
    +
    +  /**
    +   * Processes a single paragraph. The following attributes are
    +   * allowed:
    +   *
    +   * <ul>
    +   *
    +   * <li><b>text</b>
    +   *
    +   * The text to be processed.</li>
    +   *
    +   * </ul>
    +   *
    +   * @param $args An @c array specifying the attributes for formatting
    +   *        the paragraph.
    +   *
    +   * @return A @c string containing the formatted paragraph.
    +   *
    +   * @private
    +   */
    +  function format_paragraph($args) {
    +    $buffer = (isset($args['text']) ? $args['text'] : '');
    +
    +    array_unshift($this->repl, array());
    +    $buffer = preg_replace_callback('{(?:^|(?<=[\s>])|([{[]))
    +                                      ==(.+?)==
    +                                      (?:$|([\]}])|(?=' . $this->punct . '{1,2}|\s))}sx',
    +                                    $this->_cb('$me->_repl($me->repl[0], $me->format_block(array("text" => $m[2], "inline" => 1, "pre" => $m[1], "post" => $m[3])))'), $buffer);
    +
    +    unset($tokens);
    +    if (preg_match('/</', $buffer) && (!$this->disable_html())) {  // optimization -- no point in tokenizing if we
    +                                       // have no tags to tokenize
    +      $tokens = $this->_tokenize($buffer);
    +    } else {
    +      $tokens = array(array('text', $buffer));
    +    }
    +    $result = '';
    +    foreach ($tokens as $token) {
    +      $text = $token[1];
    +      if ($token[0] == 'tag') {
    +        $text = preg_replace('/&(?!amp;)/', '&amp;', $text);
    +        $result .= $text;
    +      } else {
    +        $text = $this->format_inline(array('text' => $text));
    +        $result .= $text;
    +      }
    +    }
    +
    +    // now, add line breaks for lines that contain plaintext
    +    $lines = preg_split('/\n/', $result);
    +    $result = '';
    +    $needs_closing = 0;
    +    foreach ($lines as $line) {
    +      if (!preg_match('{(' . $this->blocktags . ')}x', $line)
    +          && ((preg_match('/^[^<]/', $line) || preg_match('/>[^<]/', $line))
    +              || !preg_match('/<img /', $line))) {
    +        if ($this->options['_line_open']) {
    +          if ($result != '') { $result .= "\n"; }
    +          $result .= $this->options['_line_open'] . $line . $this->options['_line_close'];
    +        } else {
    +          if ($needs_closing) {
    +            $result .= $this->options['_line_close'] . "\n";
    +          } else {
    +            $needs_closing = 1;
    +            if ($result != '') { $result .= "\n"; }
    +          }
    +          $result .= $line;
    +        }
    +      } else {
    +        if ($needs_closing) {
    +          $result .= $this->options['_line_close'] . "\n";
    +        } else {
    +          if ($result != '') { $result .= "\n"; }
    +        }
    +        $result .= $line;
    +        $needs_closing = 0;
    +      }
    +    }
    +
    +    // at this point, we will restore the \001's to \n's (reversing
    +    // the step taken in _tokenize).
    +    //$result = preg_replace('/\r/', "\n", $result);
    +    $result = preg_replace('/\001/', "\n", $result);
    +
    +    for ($i = count($this->repl[0]); $i > 0; $i--) {
    +      $result = preg_replace("|<textile#$i>|", str_replace('$', '\\$', $this->repl[0][$i - 1]), $result, 1);
    +    }
    +    array_shift($this->repl);
    +
    +    // quotalize
    +    if ($this->options['do_quotes']) {
    +      $result = $this->process_quotes($result);
    +    }
    +
    +    return $result;
    +  } // function format_paragraph
    +
    +  /**
    +   * Processes an inline string (plaintext) for Textile syntax.
    +   * The following attributes are allowed:
    +   *
    +   * <ul>
    +   *
    +   * <li><b>text</b>
    +   *
    +   * The text to be processed.</li>
    +   *
    +   * </ul>
    +   *
    +   * @param $args An @c array specifying the attributes for formatting
    +   *        the inline string.
    +   *
    +   * @return A @c string containing the formatted inline string.
    +   *
    +   * @private
    +   */
    +  function format_inline($args) {
    +    $qtags = array(array('**', 'b',      '(?<!\*)\*\*(?!\*)', '\*'),
    +                   array('__', 'i',      '(?<!_)__(?!_)', '_'),
    +                   array('??', 'cite',   '\?\?(?!\?)', '\?'),
    +                   array('*',  'strong', '(?<!\*)\*(?!\*)', '\*'),
    +                   array('_',  'em',     '(?<!_)_(?!_)', '_'),
    +                   array('-',  'del',    '(?<!\-)\-(?!\-)', '-'),
    +                   array('+',  'ins',    '(?<!\+)\+(?!\+)', '\+'),
    +                   array('++', 'big',    '(?<!\+)\+\+(?!\+)', '\+\+'),
    +                   array('--', 'small',  '(?<!\-)\-\-(?!\-)', '\-\-'),
    +                   array('~',  'sub',    '(?<!\~)\~(?![\\\\/~])', '\~'));
    +    $text = (isset($args['text']) ? $args['text'] : '');
    +
    +    array_unshift($this->repl, array());
    +
    +    $text = preg_replace_callback('{' . $this->codere . '}mx', $this->_cb('$me->_repl($me->repl[0], $me->format_code(array("text" => $m[2] . $m[4], "lang" => $m[1] . $m[3])))'), $text);
    +
    +    // images must be processed before encoding the text since they might
    +    // have the <, > alignment specifiers...
    +
    +    // !blah (alt)! -> image
    +    $text = preg_replace_callback('{(?:^|(?<=[\s>])|([{[]))      # $1: open brace/bracket
    +                                    !                            # opening
    +                                    (' . $this->imgalignre . '?) # $2: optional alignment
    +                                    (' . $this->clstypadre . '*) # $3: optional CSS class/id
    +                                    (' . $this->imgalignre . '?) # $4: optional alignment
    +                                    (?:\s*)                      # space between alignment/css stuff
    +                                    ([^\s\(!]+)                  # $5: filename
    +                                    (\s*[^\(!]*(?:\([^\)]+\))?[^!]*) # $6: extras (alt text)
    +                                    !                            # closing
    +                                    (?::(\d+|' . $this->urlre . '))? # $7: optional URL
    +                                    (?:$|([\]}])|(?=' . $this->punct . '{1,2}|\s)) # $8: closing brace/bracket
    +                                   }mx', $this->_cb('$me->_repl($me->repl[0], $me->format_image(array("pre" => $m[1], "src" => $m[5], "align" => ($m[2] ? $m[2] : $m[4]), "extra" => $m[6], "url" => $m[7], "clsty" => $m[3], "post" => $m[8])))'), $text);
    +
    +    $text = preg_replace_callback('{(?:^|(?<=[\s>])|([{[]))     # $1: open brace/bracket
    +                                    %                           # opening
    +                                    (' . $this->halignre . '?)  # $2: optional alignment
    +                                    (' . $this->clstyre . '*)   # $3: optional CSS class/id
    +                                    (' . $this->halignre . '?)  # $4: optional alignment
    +                                    (?:\s*)                     # spacing
    +                                    ([^%]+?)                    # $5: text
    +                                    %                           # closing
    +                                    (?::(\d+|' . $this->urlre . '))? # $6: optional URL
    +                                    (?:$|([]}])|(?=' . $this->punct . '{1,2}|\s)) # $7: closing brace/bracket
    +                                   }mx', $this->_cb('$me->_repl($me->repl[0], $me->format_span(array("pre" => $m[1], "text" => $m[5], "align" => ($m[2] ? $m[2] : $m[4]), "cite" => $m[6], "clsty" => $m[3], "post" => $m[7])))'), $text);
    +
    +    $text = $this->encode_html($text);
    +    $text = preg_replace('!&lt;textile#(\d+)&gt;!', '<textile#$1>', $text);
    +    $text = preg_replace('!&amp;quot;!', '&#34;', $text);
    +    $text = preg_replace('!&amp;(([a-z]+|#\d+);)!', '&$1', $text);
    +    $text = preg_replace('!&quot;!', '"', $text);
    +
    +    // These create markup with entities. Do first and 'save' result for later:
    +    // "text":url -> hyperlink
    +    // links with brackets surrounding
    +    $parenre = '\( (?: [^()] )* \)';
    +    $text = preg_replace_callback('{(
    +                                    [{[]
    +                                    (?:
    +                                        (?:"                                         # quote character
    +                                           (' . $this->clstyre . '*)?                # $2: optional CSS class/id
    +                                           ([^"]+?)                                  # $3: link text
    +                                           (?:\( ( (?:[^()]|' . $parenre . ')*) \))? # $4: optional link title
    +                                           "                                         # closing quote
    +                                        )
    +                                        |
    +                                        (?:\'                                        # open single quote
    +                                           (' . $this->clstyre . '*)?                # $5: optional CSS class/id
    +                                           ([^\']+?)                                 # $6: link text
    +                                           (?:\( ( (?:[^()]|' . $parenre . ')*) \))? # $7: optional link title
    +                                           \'                                        # closing quote
    +                                        )
    +                                    )
    +                                    :(.+?)                                           # $8: URL suffix
    +                                    [\]}]
    +                                   )
    +                                   }mx', $this->_cb('$me->_repl($me->repl[0], $me->format_link(array("text" => $m[1], "linktext" => $m[3] . $m[6], "title" => $me->encode_html_basic($m[4] . $m[7]), "url" => $m[8], "clsty" => $m[2] . $m[5])))'), $text);
    +
    +    $text = preg_replace_callback('{((?:^|(?<=[\s>\(]))                              # $1: open brace/bracket
    +                                    (?: (?:"                                         # quote character "
    +                                           (' . $this->clstyre . '*)?                # $2: optional CSS class/id
    +                                           ([^"]+?)                                  # $3: link text "
    +                                           (?:\( ( (?:[^()]|' . $parenre . ')*) \))? # $4: optional link title
    +                                           "                                         # closing quote # "
    +                                        )
    +                                        |
    +                                        (?:\'                                        # open single quote \'
    +                                           (' . $this->clstyre . '*)?                # $5: optional CSS class/id
    +                                           ([^\']+?)                                 # $6: link text \'
    +                                           (?:\( ( (?:[^()]|' . $parenre . ')*) \))? # $7: optional link title
    +                                           \'                                        # closing quote \'
    +                                        )
    +                                    )
    +                                    :(\d+|' . $this->urlre . ')                      # $8: URL suffix
    +                                    (?:$|(?=' . $this->punct . '{1,2}|\s)))          # $9: closing brace/bracket
    +                                   }mx', $this->_cb('$me->_repl($me->repl[0], $me->format_link(array("text" => $m[1], "linktext" => $m[3] . $m[6], "title" => $me->encode_html_basic($m[4] . $m[7]), "url" => $m[8], "clsty" => $m[2] . $m[5])))'), $text);
    +
    +    if (preg_match('/^xhtml2/', $this->flavor())) {
    +      // citation with cite link
    +      $text = preg_replace_callback('{(?:^|(?<=[\s>\'"\(])|([{[]))                   # $1: open brace/bracket \'
    +                                      \?\?                                           # opening \'??\'
    +                                      ([^\?]+?)                                      # $2: characters (can\'t contain \'?\')
    +                                      \?\?                                           # closing \'??\'
    +                                      :(\d+|' . $this->urlre . ')                    # $3: optional citation URL
    +                                      (?:$|([\]}])|(?=' . $this->punct . '{1,2}|\s)) # $4: closing brace/bracket
    +                                     }mx', $this->_cb('$me->_repl($me->repl[0], $me->format_cite(array("pre" => $m[1], "text" => $m[2], "cite" => $m[3], "post" => $m[4])))'), $text);
    +    }
    +
    +    // footnotes
    +    if (preg_match('/[^ ]\[\d+\]/', $text)) {
    +      $fntag = '<sup';
    +      if ($this->options['css']['class_footnote']) { $fntag .= ' class="' . $this->options['css']['class_footnote'] . '"'; }
    +      $fntag .= '><a href="#' . ($this->options['css']['id_footnote_prefix'] ? $this->options['css']['id_footnote_prefix'] : 'fn');
    +      $text = preg_replace('|([^ ])\[(\d+)\]|', '$1' . $fntag . '$2">$2</a></sup>', $text);
    +    }
    +
    +    // translate macros:
    +    $text = preg_replace_callback('{(\{)(.+?)(\})}x',
    +                                  $this->_cb('$me->format_macro(array("pre" => $m[1], "post" => $m[3], "macro" => $m[2]))'), $text);
    +
    +    // these were present with textile 1 and are common enough
    +    // to not require macro braces...
    +    // (tm) -> &trade;
    +    $text = preg_replace('|[\(\[]TM[\)\]]|i', '&#8482;', $text);
    +    // (c) -> &copy;
    +    $text = preg_replace('|[\(\[]C[\)\]]|i', '&#169;', $text);
    +    // (r) -> &reg;
    +    $text = preg_replace('|[\(\[]R[\)\]]|i', '&#174;', $text);
    +
    +    if ($this->preserve_spaces()) {
    +      // replace two spaces with an em space
    +      $text = preg_replace('/(?<!\s)\ \ (?!=\s)/', '&#8195;', $text);
    +    }
    +
    +    $redo = preg_match('/[\*_\?\-\+\^\~]/', $text);
    +    $last = $text;
    +    while ($redo) {
    +      // simple replacements...
    +      $redo = 0;
    +      foreach ($qtags as $tag) {
    +        list ($this->tmp['f'][], $this->tmp['r'][], $qf, $cls) = $tag;
    +        if ($last != ($text = preg_replace_callback('{(?:^|(?<=[\s>\'"])|([{[]))                     # "\' $1 - pre
    +                                                      ' . $qf . '                                    #
    +                                                      (?:(' . $this->clstyre . '*))?                 # $2 - attributes
    +                                                      ([^' . $cls . '\s].*?)                         # $3 - content
    +                                                      (?<=\S)' . $qf . '                             #
    +                                                      (?:$|([\]}])|(?=' . $this->punct . '{1,2}|\s)) # $4 - post
    +                                                     }mx', $this->_cb('$me->format_tag(array("tag" => end($me->tmp["r"]), "marker" => end($me->tmp["f"]), "pre" => $m[1], "text" => $m[3], "clsty" => $m[2], "post" => $m[4]))'), $text))) {
    +          $redo = ($redo || ($last != $text));
    +          $last = $text;
    +        }
    +        array_pop($this->tmp['f']); array_pop($this->tmp['r']);
    +      }
    +    }
    +
    +    // superscript is an even simpler replacement...
    +    $text = preg_replace('/(?<!\^)\^(?!\^)(.+?)(?<!\^)\^(?!\^)/', '<sup>$1</sup>', $text);
    +
    +    // ABC(Aye Bee Cee) -> acronym
    +    $text = preg_replace_callback('{\b([A-Z][A-Za-z0-9]*?[A-Z0-9]+?)\b(?:[(]([^)]*)[)])}',
    +                                  $this->_cb('$me->_repl($me->repl[0],"<acronym title=\"" . $me->encode_html_basic($m[2]) . "\">$m[1]</acronym>")'), $text);
    +
    +    // ABC -> 'capped' span
    +    if ($this->tmp['caps'][] = $this->options['css']['class_caps']) {
    +      $text = preg_replace_callback('/(^|[^"][>\s])  # "
    +                                      ((?:[A-Z](?:[A-Z0-9\.,\']|\&amp;){2,}\ *)+?) # \'
    +                                      (?=[^A-Z\.0-9]|$)
    +                                     /mx', $this->_cb('$m[1] . $me->_repl($me->repl[0], "<span class=\"" . end($me->tmp["caps"]) . "\">$m[2]</span>")'), $text);
    +    }
    +    array_pop($this->tmp['caps']);
    +
    +    // nxn -> n&times;n
    +    $text = preg_replace('!((?:[0-9\.]0|[1-9]|\d[\'"])\ ?)x(\ ?\d)!', '$1&#215;$2', $text);
    +
    +    // translate these entities to the Unicode equivalents:
    +    $text = preg_replace('/&#133;/', '&#8230;', $text);
    +    $text = preg_replace('/&#145;/', '&#8216;', $text);
    +    $text = preg_replace('/&#146;/', '&#8217;', $text);
    +    $text = preg_replace('/&#147;/', '&#8220;', $text);
    +    $text = preg_replace('/&#148;/', '&#8221;', $text);
    +    $text = preg_replace('/&#150;/', '&#8211;', $text);
    +    $text = preg_replace('/&#151;/', '&#8212;', $text);
    +
    +    // Restore replacements done earlier:
    +    for ($i = count($this->repl[0]); $i > 0; $i--) {
    +      $text = preg_replace("|<textile#$i>|", str_replace('$', '\\$', $this->repl[0][$i - 1]), $text);
    +    }
    +    array_shift($this->repl);
    +
    +    // translate entities to characters for highbit stuff since
    +    // we're using utf8
    +    // removed for backward compatability with older versions of Perl
    +    //if (preg_match('/^utf-?8$/i', $this->options['charset'])) {
    +    //    // translate any unicode entities to native UTF-8
    +    //    $text = preg_replace('/\&\#(\d+);/e', '($1 > 127) ? pack('U', $1) : chr($1)', $text);
    +    //}
    +
    +    return $text;
    +  } // function format_inline
    +
    +  /**
    +   * Responsible for processing a particular macro. Arguments passed
    +   * include:
    +   *
    +   * <ul>
    +   *
    +   * <li><b>pre</b>
    +   *
    +   * open brace character</li>
    +   *
    +   * <li><b>post</b>
    +   *
    +   * close brace character</li>
    +   *
    +   * <li><b>macro</b>
    +   *
    +   * the macro to be executed</li>
    +   *
    +   * </ul>
    +   *
    +   * The return value from this method would be the replacement
    +   * text for the macro given. If the macro is not defined, it will
    +   * return pre + macro + post, thereby preserving the original
    +   * macro string.
    +   *
    +   * @param $attrs An @c array containing the attributes for
    +   *        formatting the macro.
    +   *
    +   * @return A @c string containing the formatted macro.
    +   *
    +   * @private
    +   */
    +  function format_macro($attrs) {
    +    $macro = $attrs['macro'];
    +    if ($this->options['macros'][$macro]) {
    +      return $this->options['macros'][$macro];
    +    }
    +
    +    return $attrs['pre'] . $macro . $attrs['post'];
    +  } // function format_macro
    +
    +  /**
    +   * Processes text for a citation tag. The following attributes
    +   * are allowed:
    +   *
    +   * <ul>
    +   *
    +   * <li><b>pre</b>
    +   *
    +   * Any text that comes before the citation.</li>
    +   *
    +   * <li><b>text</b>
    +   *
    +   * The text that is being cited.</li>
    +   *
    +   * <li><b>cite</b>
    +   *
    +   * The URL of the citation.</li>
    +   *
    +   * <li><b>post</b>
    +   *
    +   * Any text that follows the citation.</li>
    +   *
    +   * </ul>
    +   *
    +   * @param $args An @c array specifying the attributes for formatting
    +   *        the citation.
    +   *
    +   * @return A @c string containing the formatted citation.
    +   *
    +   * @private
    +   */
    +  function format_cite($args) {
    +    $pre = (isset($args['pre']) ? $args['pre'] : '');
    +    $text = (isset($args['text']) ? $args['text'] : '');
    +    $cite = $args['cite'];
    +    $post = (isset($args['post']) ? $args['post'] : '');
    +    $this->_strip_borders($pre, $post);
    +    $tag = $pre . '<cite';
    +    if (preg_match('/^xhtml2/', $this->flavor()) && $cite) {
    +      $cite = $this->format_url(array('url' => $cite));
    +      $tag .= " cite=\"$cite\"";
    +    } else {
    +      $post .= ':';
    +    }
    +    $tag .= '>';
    +    return $tag . $this->format_inline(array('text' => $text)) . '</cite>' . $post;
    +  } // function format_cite
    +
    +  /**
    +   * Processes '@...@' type blocks (code snippets). The following
    +   * attributes are allowed:
    +   *
    +   * <ul>
    +   *
    +   * <li><b>text</b>
    +   *
    +   * The text of the code itself.</li>
    +   *
    +   * <li><b>lang</b>
    +   *
    +   * The language (programming language) for the code.</li>
    +   *
    +   * </ul>
    +   *
    +   * @param $args An @c array specifying the attributes for formatting
    +   *        the code.
    +   *
    +   * @return A @c string containing the formatted code.
    +   *
    +   * @private
    +   */
    +  function format_code($args) {
    +    $code = (isset($args['text']) ? $args['text'] : '');
    +    $lang = $args['lang'];
    +    $code = $this->encode_html($code, 1);
    +    $code = preg_replace('/&lt;textile#(\d+)&gt;/', '<textile#$1>', $code);
    +    $tag = '<code';
    +    if ($lang) { $tag .= " language=\"$lang\""; }
    +    return $tag . '>' . $code . '</code>';
    +  } // function format_code
    +
    +  /**
    +   * Returns a string of tag attributes to accomodate the class,
    +   * style and symbols present in @c $clsty.
    +   *
    +   * @c $clsty is checked for:
    +   *
    +   * <ul>
    +   *
    +   * <li><b><code>{...}</code></b>
    +   *
    +   * style rules. If present, they are appended to <code>$style</code>.</li>
    +   *
    +   * <li><b><code>(...#...)</code></b>
    +   *
    +   * class and/or ID name declaration</li>
    +   *
    +   * <li><b><code>(</code> (one or more)</b>
    +   *
    +   * pad left characters</li>
    +   *
    +   * <li><b><code>)</code> (one or more)</b>
    +   *
    +   * pad right characters</li>
    +   *
    +   * <li><b><code>[ll]</code></b>
    +   *
    +   * language declaration</li>
    +   *
    +   * </ul>
    +   *
    +   * The attribute string returned will contain any combination
    +   * of class, id, style and/or lang attributes.
    +   *
    +   * @param $clsty A @c string specifying the class/style to process.
    +   * @param $class A @c string specifying the predetermined class.
    +   * @param $style A @c string specifying the predetermined style.
    +   *
    +   * @return A @c string containing the formatted class, ID, style,
    +   *         and/or language.
    +   *
    +   * @private
    +   */
    +  function format_classstyle($clsty = NULL, $class = NULL, $style = NULL) {
    +    $class = preg_replace('/^ /', '', $class, 1);
    +
    +    unset($lang, $padleft, $padright, $id);
    +    if ($clsty && preg_match('/{([^}]+)}/', $clsty, $matches)) {
    +      $_style = $matches[1];
    +      $_style = preg_replace('/\n/', ' ', $_style);
    +      $style .= ';' . $_style;
    +      $clsty = preg_replace('/{[^}]+}/', '', $clsty);
    +    }
    +    if ($clsty && (preg_match('/\(([A-Za-z0-9_\- ]+?)(?:#(.+?))?\)/', $clsty, $matches) ||
    +                   preg_match('/\(([A-Za-z0-9_\- ]+?)?(?:#(.+?))\)/', $clsty, $matches))) {
    +      if ($matches[1] || $matches[2]) {
    +        if ($class) {
    +          $class = $matches[1] . ' ' . $class;
    +        } else {
    +          $class = $matches[1];
    +        }
    +        $id = $matches[2];
    +        if ($class) {
    +          $clsty = preg_replace('/\([A-Za-z0-9_\- ]+?(#.*?)?\)/', '', $clsty);
    +        }
    +        if ($id) {
    +          $clsty = preg_replace('/\(#.+?\)/', '', $clsty);
    +        }
    +      }
    +    }
    +    if ($clsty && preg_match('/(\(+)/', $clsty, $matches)) {
    +      $padleft = strlen($matches[1]);
    +      $clsty = preg_replace('/\(+/', '', $clsty, 1);
    +    }
    +    if ($clsty && preg_match('/(\)+)/', $clsty, $matches)) {
    +      $padright = strlen($matches[1]);
    +      $clsty = preg_replace('/\)+/', '', $clsty, 1);
    +    }
    +    if ($clsty && preg_match('/\[(.+?)\]/', $clsty, $matches)) {
    +      $lang = $matches[1];
    +      $clsty = preg_replace('/\[.+?\]/', '', $clsty);
    +    }
    +    $attrs = '';
    +    if ($padleft) { $style .= ";padding-left:${padleft}em"; }
    +    if ($padright) { $style .= ";padding-right:${padright}em"; }
    +    $style = preg_replace('/^;/', '', $style, 1);
    +    $class = preg_replace('/^ /', '', $class, 1);
    +    $class = preg_replace('/ $/', '', $class, 1);
    +    if ($class) { $attrs .= " class=\"$class\""; }
    +    if ($id) { $attrs .= " id=\"$id\""; }
    +    if ($style) { $attrs .= " style=\"$style\""; }
    +    if ($lang) { $attrs .= " lang=\"$lang\""; }
    +    $attrs = preg_replace('/^ /', '', $attrs, 1);
    +    return $attrs;
    +  } // function format_classstyle
    +
    +  /**
    +   * Constructs an HTML tag. Accepted arguments:
    +   *
    +   * <ul>
    +   *
    +   * <li><b>tag</b>
    +   *
    +   * the tag to produce</li>
    +   *
    +   * <li><b>text</b>
    +   *
    +   * the text to output inside the tag</li>
    +   *
    +   * <li><b>pre</b>
    +   *
    +   * text to produce before the tag</li>
    +   *
    +   * <li><b>post</b>
    +   *
    +   * text to produce following the tag</li>
    +   *
    +   * <li><b>clsty</b>
    +   *
    +   * class and/or style attributes that should be assigned to the tag.</li>
    +   *
    +   * </ul>
    +   *
    +   * @param $args @c array specifying the attributes for formatting
    +   *        the tag.
    +   *
    +   * @return A @c string containing the formatted tag.
    +   *
    +   * @private
    +   */
    +  function format_tag($args) {
    +    $tagname = $args['tag'];
    +    $text = (isset($args['text']) ? $args['text'] : '');
    +    $pre = (isset($args['pre']) ? $args['pre'] : '');
    +    $post = (isset($args['post']) ? $args['post'] : '');
    +    $clsty = (isset($args['clsty']) ? $args['clsty'] : '');
    +    $this->_strip_borders($pre, $post);
    +    $tag = "<$tagname";
    +    $attr = $this->format_classstyle($clsty);
    +    if ($attr) { $tag .= " $attr"; }
    +    $tag .= ">$text</$tagname>";
    +    return $pre . $tag . $post;
    +  } // function format_tag
    +
    +  /**
    +   * Takes a Textile formatted definition list and
    +   * returns the markup for it. Arguments accepted:
    +   *
    +   * <ul>
    +   *
    +   * <li><b>text</b>
    +   *
    +   * The text to be processed.</li>
    +   *
    +   * </ul>
    +   *
    +   * @param $args An @c array specifying the attributes for formatting
    +   *        the definition list.
    +   *
    +   * @return A @c string containing the formatted definition list.
    +   *
    +   * @private
    +   */
    +  function format_deflist($args) {
    +    $str = (isset($args['text']) ? $args['text'] : '');
    +    unset($clsty);
    +    $lines = preg_split('/\n/', $str);
    +    if (preg_match('{^(dl(' . $this->clstyre . '*?)\.\.?(?:\ +|$))}x', $lines[0], $matches)) {
    +      $clsty = $matches[2];
    +      $lines[0] = substr($lines[0], strlen($matches[1]));
    +    }
    +
    +    unset($dt, $dd);
    +    $out = '';
    +    foreach ($lines as $line) {
    +      if (preg_match('{^((?:' . $this->clstyre . '*)(?:[^\ ].*?)(?<!["\'\ ])):([^\ \/].*)$}x', $line, $matches)) {
    +        if ($dt && $dd) { $out .= $this->add_term($dt, $dd); }
    +        $dt = $matches[1];
    +        $dd = $matches[2];
    +      } else {
    +        $dd .= "\n" . $line;
    +      }
    +    }
    +    if ($dt && $dd) { $out .= $this->add_term($dt, $dd); }
    +
    +    $tag = '<dl';
    +    if ($clsty) { $attr = $this->format_classstyle($clsty); }
    +    if ($attr) { $tag .= " $attr"; }
    +    $tag .= '>' . "\n";
    +
    +    return $tag . $out . "</dl>\n";
    +  } // function format_deflist
    +
    +  /**
    +   * Processes a single definition list item from the provided term
    +   * and definition.
    +   *
    +   * @param $dt A @c string specifying the term to be defined.
    +   * @param $dd A @c string specifying the definition for the term.
    +   *
    +   * @return A @c string containing the formatted definition list
    +   *         item.
    +   *
    +   * @private
    +   */
    +  function add_term($dt, $dd) {
    +    unset($dtattr, $ddattr);
    +    unset($dtlang);
    +    if (preg_match('{^(' . $this->clstyre . '*)}x', $dt, $matches)) {
    +      $param = $matches[1];
    +      $dtattr = $this->format_classstyle($param);
    +      if (preg_match('/\[([A-Za-z]+?)\]/', $param, $matches)) {
    +        $dtlang = $matches[1];
    +      }
    +      $dt = substr($dt, strlen($param));
    +    }
    +    if (preg_match('{^(' . $this->clstyre . '*)}x', $dd, $matches)) {
    +      $param = $matches[1];
    +      // if the language was specified for the term,
    +      // then apply it to the definition as well (unless
    +      // already specified of course)
    +      if ($dtlang && preg_match('/\[([A-Za-z]+?)\]/', $param)) {
    +        unset($dtlang);
    +      }
    +      $ddattr = $this->format_classstyle(($dtlang ? "[$dtlang]" : '') . $param);
    +      $dd = substr($dd, strlen($param));
    +    }
    +    $out = '<dt';
    +    if ($dtattr) { $out .= " $dtattr"; }
    +    $out .= '>' . $this->format_inline(array('text' => $dt)) . '</dt>' . "\n";
    +    if (preg_match('/\n\n/', $dd)) {
    +      if (preg_match('/\n\n/', $dd)) { $dd = $this->process($dd); }
    +    } else {
    +      $dd = $this->format_paragraph(array('text' => $dd));
    +    }
    +    $out .= '<dd';
    +    if ($ddattr) { $out .= " $ddattr"; }
    +    $out .= '>' . $dd . '</dd>' . "\n";
    +    return $out;
    +  } // function add_term
    +
    +  /**
    +   * Takes a Textile formatted list (numeric or bulleted) and
    +   * returns the markup for it. Text that is passed in requires
    +   * substantial parsing, so the @c format_list method is a little
    +   * involved. But it should always produce a proper ordered
    +   * or unordered list. If it cannot (due to misbalanced input),
    +   * it will return the original text. Arguments accepted:
    +   *
    +   * <ul>
    +   *
    +   * <li><b>text</b>
    +   *
    +   * The text to be processed.</li>
    +   *
    +   * </ul>
    +   *
    +   * @param $args An @c array specifying the attributes for formatting
    +   *        the list.
    +   *
    +   * @return A @c string containing the formatted list.
    +   *
    +   * @private
    +   */
    +  function format_list($args) {
    +    $str = (isset($args['text']) ? $args['text'] : '');
    +
    +    $list_tags = array('*' => 'ul', '#' => 'ol');
    +
    +    $lines = preg_split('/\n/', $str);
    +
    +    unset($stack);
    +    $last_depth = 0;
    +    $item = '';
    +    $out = '';
    +    foreach ($lines as $line) {
    +      if (preg_match('{^((?:' . $this->clstypadre . '*|' . $this->halignre . ')*)
    +                       ([\#\*]+)
    +                       ((?:' . $this->halignre . '|' . $this->clstypadre . '*)*)
    +                       \ (.+)$}x', $line, $matches)) {
    +        if ($item != '') {
    +          if (preg_match('/\n/', $item)) {
    +            if ($this->options['_line_open']) {
    +              $item = preg_replace('/(<li[^>]*>|^)/m', '$1' . $this->options['_line_open'], $item);
    +              $item = preg_replace('/(\n|$)/s', $this->options['_line_close'] . '$1', $item);
    +            } else {
    +              $item = preg_replace('/(\n)/s', $this->options['_line_close'] . '$1', $item);
    +            }
    +          }
    +          $out .= $item;
    +          $item = '';
    +        }
    +        $type = substr($matches[2], 0, 1);
    +        $depth = strlen($matches[2]);
    +        $blockparam = $matches[1];
    +        $itemparam = $matches[3];
    +        $line = $matches[4];
    +        unset ($blockclsty, $blockalign, $blockattr, $itemattr, $itemclsty,
    +               $itemalign);
    +        if (preg_match('{(' . $this->clstypadre . '+)}x', $blockparam, $matches)) {
    +          $blockclsty = $matches[1];
    +        }
    +        if (preg_match('{(' . $this->halignre . '+)}', $blockparam, $matches)) {
    +          $blockalign = $matches[1];
    +        }
    +        if (preg_match('{(' . $this->clstypadre . '+)}x', $itemparam, $matches)) {
    +          $itemclsty = $matches[1];
    +        }
    +        if (preg_match('{(' . $this->halignre . '+)}', $itemparam, $matches)) {
    +          $itemalign = $matches[1];
    +        }
    +        if ($itemclsty) { $itemattr = $this->format_classstyle($itemclsty); }
    +        if ($depth > $last_depth) {
    +          for ($j = $last_depth; $j < $depth; $j++) {
    +            $out .= "\n<$list_tags[$type]";
    +            $stack[] = $type;
    +            if ($blockclsty) {
    +              $blockattr = $this->format_classstyle($blockclsty);
    +              if ($blockattr) { $out .= ' ' . $blockattr; }
    +            }
    +            $out .= ">\n<li";
    +            if ($itemattr) { $out .= " $itemattr"; }
    +            $out .= ">";
    +          }
    +        } elseif ($depth < $last_depth) {
    +          for ($j = $depth; $j < $last_depth; $j++) {
    +            if ($j == $depth) { $out .= "</li>\n"; }
    +            $type = array_pop($stack);
    +            $out .= "</$list_tags[$type]>\n</li>\n";
    +          }
    +          if ($depth) {
    +            $out .= '<li';
    +            if ($itemattr) { $out .= " $itemattr"; }
    +            $out .= '>';
    +          }
    +        } else {
    +          $out .= "</li>\n<li";
    +          if ($itemattr) { $out .= " $itemattr"; }
    +          $out .= '>';
    +        }
    +        $last_depth = $depth;
    +      }
    +      if ($item != '') { $item .= "\n"; }
    +      $item .= $this->format_paragraph(array('text' => $line));
    +    }
    +
    +    if (preg_match('/\n/', $item, $matches)) {
    +      if ($this->options['_line_open']) {
    +        $item = preg_replace('/(<li[^>]*>|^)/m', '$1' . $this->options['_line_open'], $item);
    +        $item = preg_replace('/(\n|$)/s', $this->options['_line_close'] . '$1', $item);
    +      } else {
    +        $item = preg_replace('/(\n)/s', $this->options['_line_close'] . '$1', $item);
    +      }
    +    }
    +    $out .= $item;
    +
    +    for ($j = 1; $j <= $last_depth; $j++) {
    +      if ($j == 1) { $out .= '</li>'; }
    +      $type = array_pop($stack);
    +      $out .= "\n" . '</' . $list_tags[$type] . '>' . "\n";
    +      if ($j != $last_depth) { $out .= '</li>'; }
    +    }
    +
    +    return $out . "\n";
    +  } // function format_list
    +
    +  /**
    +   * Processes '==xxxxx==' type blocks for filters. A filter
    +   * would follow the open '==' sequence and is specified within
    +   * pipe characters, like so:
    +   * <pre>
    +   *     ==|filter|text to be filtered==
    +   * </pre>
    +   * You may specify multiple filters in the filter portion of
    +   * the string. Simply comma delimit the filters you desire
    +   * to execute. Filters are defined using the filters method.
    +   *
    +   * @param $args An @c array specifying the attributes for formatting
    +   *        the block.
    +   *
    +   * @return A @c string containing the formatted block.
    +   *
    +   * @private
    +   */
    +  function format_block($args) {
    +    $str = (isset($args['text']) ? $args['text'] : '');
    +    $inline = $args['inline'];
    +    $pre = (isset($args['pre']) ? $args['pre'] : '');
    +    $post = (isset($args['post']) ? $args['post'] : '');
    +    $this->_strip_borders($pre, $post);
    +    $filters = (preg_match('/^(\|(?:(?:[a-z0-9_\-]+)\|)+)/', $str, $matches) ? $matches[1] : '');
    +    if ($filters) {
    +      $filtreg = preg_replace('/[^A-Za-z0-9]/', '\\\\$1', $filters);
    +      $str = preg_replace('/^' . $filtreg . '/', '', $str, 1);
    +      $filters = preg_replace('/^\|/', '', $filters, 1);
    +      $filters = preg_replace('/\|$/', '', $filter, 1);
    +      $filters = preg_split('/\|/', $filters);
    +      $str = $this->apply_filters(array('text' => $str, 'filters' => $filters));
    +      $count = count($filters);
    +      if ($str = preg_replace('!(<p>){' . $count . '}!se', '(++$i ? "$1" : "$1")', $str) && $i) {
    +        $str = preg_replace('!(</p>){' . $count . '}!s', '$1', $str);
    +        $str = preg_replace('!(<br( /)?>){' . $count . '}!s', '$1', $str);
    +      }
    +    }
    +    if ($inline) {
    +      // strip off opening para, closing para, since we're
    +      // operating within an inline block
    +      $str = preg_replace('/^\s*<p[^>]*>/', '', $str, 1);
    +      $str = preg_replace('/<\/p>\s*$/', '', $str, 1);
    +    }
    +    return $pre . $str . $post;
    +  } // function format_block
    +
    +  /**
    +   * Takes the Textile link attributes and transforms them into
    +   * a hyperlink.
    +   *
    +   * @param $args An @c array specifying the attributes for formatting
    +   *        the link.
    +   *
    +   * @return A @c string containing the formatted link.
    +   *
    +   * @private
    +   */
    +  function format_link($args) {
    +    $text = (isset($args['text']) ? $args['text'] : '');
    +    $linktext = (isset($args['linktext']) ? $args['linktext'] : '');
    +    $title = $args['title'];
    +    $url = $args['url'];
    +    $clsty = $args['clsty'];
    +
    +    if (!$url || ($url == '')) {
    +      return $text;
    +    }
    +    if (isset($this->links) && isset($this->links[$url])) {
    +      $title = ($title ? $title : $this->links[$url]['title']);
    +      $url = $this->links[$url]['url'];
    +    }
    +    $linktext = preg_replace('/ +$/', '', $linktext, 1);
    +    $linktext = $this->format_paragraph(array('text' => $linktext));
    +    $url = $this->format_url(array('linktext' => $linktext, 'url' => $url));
    +    $tag = "<a href=\"$url\"";
    +    $attr = $this->format_classstyle($clsty);
    +    if ($attr) { $tag .= " $attr"; }
    +    if ($title) {
    +      $title = preg_replace('/^\s+/', '', $title, 1);
    +      if (strlen($title)) { $tag .= " title=\"$title\""; }
    +    }
    +    $tag .= ">$linktext</a>";
    +    return $tag;
    +  } // function format_link
    +
    +  /**
    +   * Takes the given @c $url and transforms it appropriately.
    +   *
    +   * @param $args An @c array specifying the attributes for formatting
    +   *        the url.
    +   *
    +   * @return A @c string containing the formatted url.
    +   *
    +   * @private
    +   */
    +  function format_url($args) {
    +    $url = ($args['url'] ? $args['url'] : '');
    +    if (preg_match('/^(mailto:)?([-\+\w]+@[-\w]+(\.\w[-\w]*)+)$/', $url, $matches)) {
    +      $url = 'mailto:' . $this->mail_encode($matches[2]);
    +    }
    +    if (!preg_match('!^(/|\./|\.\./|#)!', $url)) {
    +      if (!preg_match('!^(https?|ftp|mailto|nntp|telnet)!', $url)) { $url = "http://$url"; }
    +    }
    +    $url = preg_replace('/&(?!amp;)/', '&amp;', $url);
    +    $url = preg_replace('/\ /', '+', $url);
    +    $url = preg_replace_callback('/^((?:.+?)\?)(.+)$/', $this->_cb('$m[1] . $me->encode_url($m[2])'), $url);
    +    return $url;
    +  } // function format_url
    +
    +  /**
    +   * Takes a Textile formatted span and returns the markup for it.
    +   *
    +   * @return A @c string containing the formatted span.
    +   *
    +   * @private
    +   */
    +  function format_span($args) {
    +    $text = (isset($args['text']) ? $args['text'] : '');
    +    $pre = (isset($args['pre']) ? $args['pre'] : '');
    +    $post = (isset($args['post']) ? $args['post'] : '');
    +    $align = $args['align'];
    +    $cite = (isset($args['cite']) ? $args['cite'] : '');
    +    $clsty = $args['clsty'];
    +    $this->_strip_borders($pre, $post);
    +    unset($class, $style);
    +    $tag  = "<span";
    +    $style = '';
    +    if ($align) {
    +      if ($self->options['css_mode']) {
    +        $alignment = $this->_halign($align);
    +        if ($alignment) { $style .= ";float:$alignment"; }
    +        if ($alignment) { $class .= ' ' . $this->options['css']["class_align_$alignment"]; }
    +      } else {
    +        $alignment = ($this->_halign($align) ? $this->_halign($align) : $this->_valign($align));
    +        if ($alignment) { $tag .= " align=\"$alignment\""; }
    +      }
    +    }
    +    $attr = $this->format_classstyle($clsty, $class, $style);
    +    if ($attr) { $tag .= " $attr"; }
    +    if ($cite) {
    +      $cite = preg_replace('/^:/', '', $cite, 1);
    +      $cite = $this->format_url(array('url' => $cite));
    +      $tag .= " cite=\"$cite\"";
    +    }
    +    return $pre . $tag . '>' . $this->format_paragraph(array('text' => $text)) . '</span>' . $post;
    +  } // function format_span
    +
    +  /**
    +   * Returns markup for the given image. @c $src is the location of
    +   * the image, @c $extra contains the optional height/width and/or
    +   * alt text. @c $url is an optional hyperlink for the image. @c $class
    +   * holds the optional CSS class attribute.
    +   *
    +   * Arguments you may pass:
    +   *
    +   * <ul>
    +   *
    +   * <li><b>src</b>
    +   *
    +   * The 'src' (URL) for the image. This may be a local path,
    +   * ideally starting with a '/'. Images can be located within
    +   * the file system if the docroot method is used to specify
    +   * where the docroot resides. If the image can be found, the
    +   * image_size method is used to determine the dimensions of
    +   * the image.</li>
    +   *
    +   * <li><b>extra</b>
    +   *
    +   * Additional parameters for the image. This would include
    +   * alt text, height/width specification or scaling instructions.</li>
    +   *
    +   * <li><b>align</b>
    +   *
    +   * Alignment attribute.</li>
    +   *
    +   * <li><b>pre</b>
    +   *
    +   * Text to produce prior to the tag.</li>
    +   *
    +   * <li><b>post</b>
    +   *
    +   * Text to produce following the tag.</li>
    +   *
    +   * <li><b>link</b>
    +   *
    +   * Optional URL to connect with the image tag.</li>
    +   *
    +   * <li><b>clsty</b>
    +   *
    +   * Class and/or style attributes.</li>
    +   *
    +   * </ul>
    +   *
    +   * @param $args An @c array specifying the attributes for formatting
    +   *        the image.
    +   *
    +   * @return A @c string containing the formatted image.
    +   *
    +   * @private
    +   */
    +  function format_image($args) {
    +    $src = (isset($args['src']) ? $args['src'] : '');
    +    $extra = $args['extra'];
    +    $align = $args['align'];
    +    $pre = (isset($args['pre']) ? $args['pre'] : '');
    +    $post = (isset($args['post']) ? $args['post'] : '');
    +    $link = $args['url'];
    +    $clsty = $args['clsty'];
    +    $this->_strip_borders($pre, $post);
    +    if (strlen($src) == 0) { return $pre . '!!' . $post; }
    +    unset($tag);
    +    if (preg_match('/^xhtml2/', $this->options['flavor'])) {
    +      unset($type); // poor man's mime typing. need to extend this externally
    +      if (preg_match('/(?:\.jpeg|\.jpg)$/i', $src)) {
    +        $type = 'image/jpeg';
    +      } elseif (preg_match('/\.gif$/i', $src)) {
    +        $type = 'image/gif';
    +      } elseif (preg_match('/\.png$/i', $src)) {
    +        $type = 'image/png';
    +      } elseif (preg_match('/\.tiff$/i', $src)) {
    +        $type = 'image/tiff';
    +      }
    +      $tag = "<object";
    +      if ($type) { $tag .= " type=\"$type\""; }
    +      $tag .= " data=\"$src\"";
    +    } else {
    +      $tag = "<img src=\"$src\"";
    +    }
    +    unset($class, $style);
    +    if ($align) {
    +      if ($this->options['css_mode']) {
    +        $alignment = $this->_halign($align);
    +        if ($alignment) { $style .= ";float:$alignment"; }
    +        if ($alignment) { $class .= ' ' . $alignment; }
    +        $alignment = $this->_valign($align);
    +        if ($alignment) {
    +          $imgvalign = (preg_match('/(top|bottom)/', $alignment) ? 'text-' . $alignment : $alignment);
    +          if ($imgvalign) { $style .= ";vertical-align:$imgvalign"; }
    +          if ($alignment) { $class .= ' ' . $this->options['css']["class_align_$alignment"]; }
    +        }
    +      } else {
    +        $alignment = ($this->_halign($align) ? $this->_halign($align) : $this->_valign($align));
    +        if ($alignment) { $tag .= " align=\"$alignment\""; }
    +      }
    +    }
    +    unset($pctw, $pcth, $w, $h, $alt);
    +    if ($extra) {
    +      $alt = (preg_match('/\(([^\)]+)\)/', $extra, $matches) ? $matches[1] : '');
    +      $extra = preg_replace('/\([^\)]+\)/', '', $extra, 1);
    +      $pct = (preg_match('/(^|\s)(\d+)%(\s|$)/', $extra, $matches) ? $matches[2] : '');
    +      if (!$pct) {
    +        list($pctw, $pcth) = (preg_match('/(^|\s)(\d+)%x(\d+)%(\s|$)/', $extra, $matches) ? array($matches[2], $matches[3]) : NULL);
    +      } else {
    +        $pctw = $pcth = $pct;
    +      }
    +      if (!$pctw && !$pcth) {
    +        list($w,$h) = (preg_match('/(^|\s)(\d+|\*)x(\d+|\*)(\s|$)/', $extra, $matches) ? array($matches[2], $matches[3]) : NULL);
    +        if ($w == '*') { $w = ''; }
    +        if ($h == '*') { $h = ''; }
    +        if (!$w) {
    +          $w = (preg_match('/(^|[,\s])(\d+)w([\s,]|$)/', $extra, $matches) ? $matches[2] : '');
    +        }
    +        if (!$h) {
    +          $h = (preg_match('/(^|[,\s])(\d+)h([\s,]|$)/', $extra, $matches) ? $matches[2] : '');
    +        }
    +      }
    +    }
    +    $alt = ($alt ? $alt : '');
    +    if (!preg_match('/^xhtml2/', $this->options['flavor'])) {
    +      $tag .= ' alt="' . $this->encode_html_basic($alt) . '"';
    +    }
    +    if ($w && $h) {
    +      if (!preg_match('/^xhtml2/', $this->options['flavor'])) {
    +        $tag .= " height=\"$h\" width=\"$w\"";
    +      } else {
    +        $style .= ";height:${h}px;width:${w}px";
    +      }
    +    } else {
    +      list($image_w, $image_h) = $this->image_size($src);
    +      if (($image_w && $image_h) && ($w || $h)) {
    +        // image size determined, but only width or height specified
    +        if ($w && !$h) {
    +          // width defined, scale down height proportionately
    +          $h = intval($image_h * ($w / $image_w));
    +        } elseif ($h && !$w) {
    +          $w = intval($image_w * ($h / $image_h));
    +        }
    +      } else {
    +        $w = $image_w;
    +        $h = $image_h;
    +      }
    +      if ($w && $h) {
    +        if ($pctw || $pcth) {
    +          $w = intval($w * $pctw / 100);
    +          $h = intval($h * $pcth / 100);
    +        }
    +        if (!preg_match('/^xhtml2/', $this->options['flavor'])) {
    +          $tag .= " height=\"$h\" width=\"$w\"";
    +        } else {
    +          $style .= ";height:{$h}px;width:{$w}px";
    +        }
    +      }
    +    }
    +    $attr = $this->format_classstyle($clsty, $class, $style);
    +    if ($attr) { $tag .= " $attr"; }
    +    if (preg_match('/^xhtml2/', $this->options['flavor'])) {
    +      $tag .= '><p>' . $this->encode_html_basic($alt) . '</p></object>';
    +    } elseif (preg_match('/^xhtml/', $this->options['flavor'])) {
    +      $tag .= ' />';
    +    } else {
    +      $tag .= '>';
    +    }
    +    if ($link) {
    +      $link = preg_replace('/^:/', '', $link, 1);
    +      $link = $this->format_url(array('url' => $link));
    +      $tag = '<a href="' . $link . '">' . $tag . '</a>';
    +    }
    +    return $pre . $tag . $post;
    +  } // function format_image
    +
    +  /**
    +   * Takes a Wiki-ish string of data and transforms it into a full
    +   * table.
    +   *
    +   * @param $args An @c array specifying the attributes for formatting
    +   *        the table.
    +   *
    +   * @return A @c string containing the formatted table.
    +   *
    +   * @private
    +   */
    +  function format_table($args) {
    +    $str = (isset($args['text']) ? $args['text'] : '');
    +
    +    $lines = preg_split('/\n/', $str);
    +    unset($rows);
    +    $line_count = count($lines);
    +    for ($i = 0; $i < $line_count; $i++) {
    +      if (!preg_match('/\|\s*$/', $lines[$i])) {
    +        if ($i + 1 < $line_count) {
    +          if ($i + 1 <= count($lines) - 1) { $lines[$i + 1] = $lines[$i] . "\n" . $lines[$i + 1]; }
    +        } else {
    +          $rows[] = $lines[$i];
    +        }
    +      } else {
    +        $rows[] = $lines[$i];
    +      }
    +    }
    +    unset($tid, $tpadl, $tpadr, $tlang);
    +    $tclass = '';
    +    $tstyle = '';
    +    $talign = '';
    +    if (preg_match('/^table[^\.]/', $rows[0])) {
    +      $row = $rows[0];
    +      $row = preg_replace('/^table/', '', $row, 1);
    +      $params = 1;
    +      // process row parameters until none are left
    +      while ($params) {
    +        if (preg_match('{^(' . $this->tblalignre . ')}', $row, $matches)) {
    +          // found row alignment
    +          $talign .= $matches[1];
    +          if ($matches[1]) { $row = substr($row, strlen($matches[1])); }
    +          if ($matches[1]) { continue; }
    +        }
    +        if (preg_match('{^(' . $this->clstypadre . ')}x', $row, $matches)) {
    +          // found a class/id/style/padding indicator
    +          $clsty = $matches[1];
    +          if ($clsty) { $row = substr($row, strlen($clsty)); }
    +          if (preg_match('/{([^}]+)}/', $clsty, $matches)) {
    +            $tstyle = $matches[1];
    +            $clsty = preg_replace('/{([^}]+)}/', '', $clsty, 1);
    +            if ($tstyle) { continue; }
    +          }
    +          if (preg_match('/\(([A-Za-z0-9_\- ]+?)(?:#(.+?))?\)/', $clsty, $matches) ||
    +              preg_match('/\(([A-Za-z0-9_\- ]+?)?(?:#(.+?))\)/', $clsty, $matches)) {
    +            if ($matches[1] || $matches[2]) {
    +              $tclass = $matches[1];
    +              $tid = $matches[2];
    +              continue;
    +            }
    +          }
    +          if (preg_match('/(\(+)/', $clsty, $matches)) { $tpadl = strlen($matches[1]); }
    +          if (preg_match('/(\)+)/', $clsty, $matches)) { $tpadr = strlen($matches[1]); }
    +          if (preg_match('/\[(.+?)\]/', $clsty, $matches)) { $tlang = $matches[1]; }
    +          if ($clsty) { continue; }
    +        }
    +        $params = 0;
    +      }
    +      $row = preg_replace('/\.\s+/', '', $row, 1);
    +      $rows[0] = $row;
    +    }
    +    $out = '';
    +    $cols = preg_split('/\|/', $rows[0] . ' ');
    +    unset($colaligns, $rowspans);
    +    foreach ($rows as $row) {
    +      $cols = preg_split('/\|/', $row . ' ');
    +      $colcount = count($cols) - 1;
    +      array_pop($cols);
    +      $colspan = 0;
    +      $row_out = '';
    +      unset($rowclass, $rowid, $rowalign, $rowstyle, $rowheader);
    +      if (!$cols[0]) { $cols[0] = ''; }
    +      if (preg_match('/_/', $cols[0])) {
    +        $cols[0] = preg_replace('/_/', '', $cols[0]);
    +        $rowheader = 1;
    +      }
    +      if (preg_match('/{([^}]+)}/', $cols[0], $matches)) {
    +        $rowstyle = $matches[1];
    +        $cols[0] = preg_replace('/{[^}]+}/', '', $cols[0]);
    +      }
    +      if (preg_match('/\(([^\#]+?)?(#(.+))?\)/', $cols[0], $matches)) {
    +        $rowclass = $matches[1];
    +        $rowid = $matches[3];
    +        $cols[0] = preg_replace('/\([^\)]+\)/', '', $cols[0]);
    +      }
    +      if (preg_match('{(' . $this->alignre . ')}', $cols[0], $matches)) { $rowalign = $matches[1]; }
    +      for ($c = $colcount - 1; $c > 0; $c--) {
    +        if ($rowspans[$c]) {
    +          $rowspans[$c]--;
    +          if ($rowspans[$c] > 1) { continue; }
    +        }
    +        unset($colclass, $colid, $header, $colparams, $colpadl, $colpadr, $collang);
    +        $colstyle = '';
    +        $colalign = $colaligns[$c];
    +        $col = array_pop($cols);
    +        $col = ($col ? $col : '');
    +        $attrs = '';
    +        if (preg_match('{^(((_|[/\\\\]\d+|' . $this->alignre . '|' . $this->clstypadre . ')+)\.\ )}x', $col, $matches)) {
    +          $colparams = $matches[2];
    +          $col = substr($col, strlen($matches[1]));
    +          $params = 1;
    +          // keep processing column parameters until there
    +          // are none left...
    +          while ($params) {
    +            if (preg_match('{^(_|' . $this->alignre . ')(.*)$}', $colparams, $matches)) {
    +              // found alignment or heading indicator
    +              $attrs .= $matches[1];
    +              if ($matches[1]) { $colparams = $matches[2]; }
    +              if ($matches[1]) { continue; }
    +            }
    +            if (preg_match('{^(' . $this->clstypadre . ')(.*)$}x', $colparams, $matches)) {
    +              // found a class/id/style/padding marker
    +              $clsty = $matches[1];
    +              if ($clsty) { $colparams = $matches[2]; }
    +              if (preg_match('/{([^}]+)}/', $clsty, $matches)) {
    +                $colstyle = $matches[1];
    +                $clsty = preg_replace('/{([^}]+)}/', '', $clsty, 1);
    +              }
    +              if (preg_match('/\(([A-Za-z0-9_\- ]+?)(?:#(.+?))?\)/', $clsty, $matches) ||
    +                  preg_match('/\(([A-Za-z0-9_\- ]+?)?(?:#(.+?))\)/', $clsty, $matches)) {
    +                if ($matches[1] || $matches[2]) {
    +                  $colclass = $matches[1];
    +                  $colid = $matches[2];
    +                  if ($colclass) {
    +                    $clsty = preg_replace('/\([A-Za-z0-9_\- ]+?(#.*?)?\)/', '', $clsty);
    +                  } elseif ($colid) {
    +                    $clsty = preg_replace('/\(#.+?\)/', '', $clsty);
    +                  }
    +                }
    +              }
    +              if (preg_match('/(\(+)/', $clsty, $matches)) {
    +                $colpadl = strlen($matches[1]);
    +                $clsty = preg_replace('/\(+/', '', $clsty, 1);
    +              }
    +              if (preg_match('/(\)+)/', $clsty, $matches)) {
    +                $colpadr = strlen($matches[1]);
    +                $clsty = preg_replace('/\)+/', '', $clsty, 1);
    +              }
    +              if (preg_match('/\[(.+?)\]/', $clsty, $matches)) {
    +                $collang = $matches[1];
    +                $clsty = preg_replace('/\[.+?\]/', '', $clsty, 1);
    +              }
    +              if ($clsty) { continue; }
    +            }
    +            if (preg_match('/^\\\\(\d+)/', $colparams, $matches)) {
    +              $colspan = $matches[1];
    +              $colparams = substr($colparams, strlen($matches[1]) + 1);
    +              if ($matches[1]) { continue; }
    +            }
    +            if (preg_match('/\/(\d+)/', $colparams, $matches)) {
    +              if ($matches[1]) { $rowspans[$c] = $matches[1]; }
    +              $colparams = substr($colparams, strlen($matches[1]) + 1);
    +              if ($matches[1]) { continue; }
    +            }
    +            $params = 0;
    +          }
    +        }
    +        if (strlen($attrs)) {
    +          if (preg_match('/_/', $attrs)) { $header = 1; }
    +          if (preg_match('{(' . $this->alignre . ')}', $attrs, $matches) && strlen($matches[1])) { $colalign = ''; }
    +          // determine column alignment
    +          if (preg_match('/<>/', $attrs)) {
    +            $colalign .= '<>';
    +          } elseif (preg_match('/</', $attrs)) {
    +            $colalign .= '<';
    +          } elseif (preg_match('/=/', $attrs)) {
    +            $colalign = '=';
    +          } elseif (preg_match('/>/', $attrs)) {
    +            $colalign = '>';
    +          }
    +          if (preg_match('/\^/', $attrs)) {
    +            $colalign .= '^';
    +          } elseif (preg_match('/~/', $attrs)) {
    +            $colalign .= '~';
    +          } elseif (preg_match('/-/', $attrs)) {
    +            $colalign .= '-';
    +          }
    +        }
    +        if ($rowheader) { $header = 1; }
    +        if ($header) { $colaligns[$c] = $colalign; }
    +        $col = preg_replace('/^ +/', '', $col, 1); $col = preg_replace('/ +$/', '', $col, 1);
    +        if (strlen($col)) {
    +          // create one cell tag
    +          $rowspan = ($rowspans[$c] ? $rowspans[$c] : 0);
    +          $col_out = '<' . ($header ? 'th' : 'td');
    +          if ($colalign) {
    +            // horizontal, vertical alignment
    +            $halign = $this->_halign($colalign);
    +            if ($halign) { $col_out .= " align=\"$halign\""; }
    +            $valign = $this->_valign($colalign);
    +            if ($valign) { $col_out .= " valign=\"$valign\""; }
    +          }
    +          // apply css attributes, row, column spans
    +          if ($colpadl) { $colstyle .= ";padding-left:${colpadl}em"; }
    +          if ($colpadr) { $colstyle .= ";padding-right:${colpadr}em"; }
    +          if ($colclass) { $col_out .= " class=\"$colclass\""; }
    +          if ($colid) { $col_out .= " id=\"$colid\""; }
    +          if ($colstyle) { $colstyle = preg_replace('/^;/', '', $colstyle, 1); }
    +          if ($colstyle) { $col_out .= " style=\"$colstyle\""; }
    +          if ($collang) { $col_out .= " lang=\"$collang\""; }
    +          if ($colspan > 1) { $col_out .= " colspan=\"$colspan\""; }
    +          if ($rowspan > 1) { $col_out .= " rowspan=\"$rowspan\""; }
    +          $col_out .= '>';
    +          // if the content of this cell has newlines OR matches
    +          // our paragraph block signature, process it as a full-blown
    +          // textile document
    +          if (preg_match('/\n\n/', $col) ||
    +              preg_match('{^(?:' . $this->halignre . '|' . $this->clstypadre . '*)*
    +                            [\*\#]
    +                            (?:' . $this->clstypadre . '*|' . $this->halignre . ')*\ }x', $col)) {
    +            $col_out .= $this->process($col);
    +          } else {
    +            $col_out .= $this->format_paragraph(array('text' => $col));
    +          }
    +          $col_out .= '</' . ($header ? 'th' : 'td') . '>';
    +          $row_out = $col_out . $row_out;
    +          if ($colspan) { $colspan = 0; }
    +        } else {
    +          if ($colspan == 0) { $colspan = 1; }
    +          $colspan++;
    +        }
    +      }
    +      if ($colspan > 1) {
    +        // handle the spanned column if we came up short
    +        $colspan--;
    +        $row_out = "<td"
    +                 . ($colspan > 1 ? " colspan=\"$colspan\"" : '')
    +                 . "></td>$row_out";
    +      }
    +
    +      // build one table row
    +      $out .= "<tr";
    +      if ($rowalign) {
    +        $valign = $this->_valign($rowalign);
    +        if ($valign) { $out .= " valign=\"$valign\""; }
    +      }
    +      if ($rowclass) { $out .= " class=\"$rowclass\""; }
    +      if ($rowid) { $out .= " id=\"$rowid\""; }
    +      if ($rowstyle) { $out .= " style=\"$rowstyle\""; }
    +      $out .= ">$row_out</tr>";
    +    }
    +
    +    // now, form the table tag itself
    +    $table = '';
    +    $table .= "<table";
    +    if ($talign) {
    +      if ($this->options['css_mode']) {
    +        // horizontal alignment
    +        $alignment = $this->_halign($talign);
    +        if ($talign == '=') {
    +          $tstyle .= ';margin-left:auto;margin-right:auto';
    +        } else {
    +          if ($alignment) { $tstyle .= ';float:' . $alignment; }
    +        }
    +        if ($alignment) { $tclass .= ' ' . $alignment; }
    +      } else {
    +        $alignment = $this->_halign($talign);
    +        if ($alignment) { $table .= " align=\"$alignment\""; }
    +      }
    +    }
    +    if ($tpadl) { $tstyle .= ";padding-left:${tpadl}em"; }
    +    if ($tpadr) { $tstyle .= ";padding-right:${tpadr}em"; }
    +    if ($tclass) { $tclass = preg_replace('/^ /', '', $tclass, 1); }
    +    if ($tclass) { $table .= " class=\"$tclass\""; }
    +    if ($tid) { $table .= " id=\"$tid\""; }
    +    if ($tstyle) { $tstyle = preg_replace('/^;/', '', $tstyle, 1); }
    +    if ($tstyle) { $table .= " style=\"$tstyle\""; }
    +    if ($tlang) { $table .= " lang=\"$tlang\""; }
    +    if ($tclass || $tid || $tstyle) { $table .= " cellspacing=\"0\""; }
    +    $table .= ">$out</table>";
    +
    +    if (preg_match('|<tr></tr>|', $table)) {
    +      // exception -- something isn't right so return fail case
    +      return NULL;
    +    }
    +
    +    return $table;
    +  } // function format_table
    +
    +  /**
    +   * The following attributes are allowed:
    +   *
    +   * <ul>
    +   *
    +   * <li><b>text</b>
    +   *
    +   * The text to be processed.</li>
    +   *
    +   * <li><b>filters</b>
    +   *
    +   * An array reference of filter names to run for the given text.</li>
    +   *
    +   * </ul>
    +   *
    +   * @param $args An @c array specifying the text and filters to
    +   *        apply.
    +   *
    +   * @return A @c string containing the filtered text.
    +   *
    +   * @private
    +   */
    +  function apply_filters($args) {
    +    $text = $args['text'];
    +    if (!$text) { return ''; }
    +    $list = $args['filters'];
    +    $filters = $this->options['filters'];
    +    if (!is_array($filters)) { return $text; }
    +
    +    $param = $this->filter_param();
    +    foreach ($list as $filter) {
    +      if (!isset($filters[$filter])) { continue; }
    +      if (is_string($filters[$filter])) {
    +        $text = (($f = create_function('$text, $param', $filters[$filter])) ? $f($text, $param) : $text);
    +      }
    +    }
    +    return $text;
    +  } // function apply_filters
    +
    +  // minor utility / formatting routines
    +
    +  var $Have_Entities = 1;
    +
    +  /**
    +   * Encodes input @c $html string, escaping characters as needed
    +   * to HTML entities. This relies on the @c htmlentities function
    +   * for full effect. If unavailable, @c encode_html_basic is used
    +   * as a fallback technique. If the "char_encoding" flag is
    +   * set to false, @c encode_html_basic is used exclusively.
    +   *
    +   * @param $html A @c string specifying the HTML to be encoded.
    +   * @param $can_double_encode If provided, a @c bool indicating
    +   *        whether or not ampersand characters should be
    +   *        unconditionally encoded.
    +   *
    +   * @return A @c string containing the encoded HTML.
    +   *
    +   * @private
    +   */
    +  function encode_html($html, $can_double_encode = FALSE) {
    +    if (!$html) { return ''; }
    +    if ($this->Have_Entities && $this->options['char_encoding']) {
    +      $html = htmlentities($html, ENT_QUOTES, $this->options['char_encoding']);
    +    } else {
    +      $html = $this->encode_html_basic($html, $can_double_encode);
    +    }
    +    return $html;
    +  } // function encode_html
    +
    +  /**
    +   * Decodes HTML entities in @c $html to their natural character
    +   * equivalents.
    +   *
    +   * @param $html A @c string specifying the HTML to be decoded.
    +   *
    +   * @return A @c string containing the decode HTML
    +   *
    +   * @private
    +   */
    +  function decode_html($html) {
    +    $html = preg_replace('!&quot;!', '"', $html);
    +    $html = preg_replace('!&amp;!', '&', $html);
    +    $html = preg_replace('!&lt;!', '<', $html);
    +    $html = preg_replace('!&gt;!', '>', $html);
    +    return $html;
    +  } // function decode_html
    +
    +  /**
    +   * Encodes the input @c $html string for the following characters:
    +   * \<, \>, & and ". If @c $can_double_encode is true, all
    +   * ampersand characters are escaped even if they already were.
    +   * If @c $can_double_encode is false, ampersands are only escaped
    +   * when they aren't part of a HTML entity already.
    +   *
    +   * @param $html A @c string specifying the HTML to be encoded.
    +   * @param $can_double_encode If provided, a @c bool indicating
    +   *        whether or not ampersand characters should be
    +   * unconditionally encoded.
    +   *
    +   * @return A @c string containing the encoded HTML.
    +   *
    +   * @private
    +   */
    +  function encode_html_basic($html, $can_double_encode = FALSE) {
    +    if (!$html) { return ''; }
    +    if (!preg_match('/[^\w\s]/', $html)) { return $html; }
    +    if ($can_double_encode) {
    +      $html = preg_replace('!&!', '&amp;', $html);
    +    } else {
    +      // Encode any & not followed by something that looks like
    +      // an entity, numeric or otherwise.
    +      $html = preg_replace('/&(?!#?[xX]?(?:[0-9a-fA-F]+|\w{1,8});)/', '&amp;', $html);
    +    }
    +    $html = preg_replace('!"!', '&quot;', $html);
    +    $html = preg_replace('!<!', '&lt;', $html);
    +    $html = preg_replace('!>!', '&gt;', $html);
    +    return $html;
    +  } // function encode_html_basic
    +
    +  /**
    +   * Returns the size for the image identified in @c $file. This
    +   * method relies upon the @c getimagesize function. If unavailable,
    +   * @c image_size will return @c NULL. Otherwise, the expected return
    +   * value is an array of the width and height (in that order), in
    +   * pixels.
    +   *
    +   * @param $file A @c string specifying the path or URL for the image
    +   *        file.
    +   *
    +   * @return An @c array containing the width and height
    +   *         (respectively) of the image.
    +   *
    +   * @private
    +   */
    +  function image_size($file) {
    +    $Have_ImageSize = function_exists('getimagesize');
    +
    +    if ($Have_ImageSize) {
    +      if (file_exists($file)) {
    +        return @getimagesize($file);
    +      } else {
    +        if ($docroot = ($this->docroot() ? $this->docroot() : $_SERVER['DOCUMENT_ROOT'])) {
    +          $fullpath = $docroot . preg_replace('|^/*(.*)$|', '/$1', $file);
    +          if (file_exists($fullpath)) {
    +            return @getimagesize($fullpath);
    +          }
    +        }
    +      }
    +    }
    +    return @getimagesize($file);
    +  } // function image_size
    +
    +  /**
    +   * Encodes the query portion of a URL, escaping characters
    +   * as necessary.
    +   *
    +   * @param $str A @c string specifying the URL to be encoded.
    +   *
    +   * @return A @c string containing the encoded URL.
    +   *
    +   * @private
    +   */
    +  function encode_url($str) {
    +    $str = preg_replace_callback('!([^A-Za-z0-9_\.\-\+\&=%;])!x',
    +                            $this->_cb('ord($m[1]) > 255 ? \'%u\' . sprintf("%04X", ord($m[1]))
    +                                                       : \'%\'  . sprintf("%02X", ord($m[1]))'), $str);
    +    return $str;
    +  } // function encode_url
    +
    +  /**
    +   * Encodes the email address in @c $addr for 'mailto:' links.
    +   *
    +   * @param $addr A @c string specifying the email address to encode.
    +   *
    +   * @return A @c string containing the encoded email address.
    +   *
    +   * @private
    +   */
    +  function mail_encode($addr) {
    +    // granted, this is simple, but it gives off warm fuzzies
    +    $addr = preg_replace_callback('!([^\$])!x',
    +                             $this->_cb('ord($m[1]) > 255 ? \'%u\' . sprintf("%04X", ord($m[1]))
    +                                                        : \'%\'  . sprintf("%02X", ord($m[1]))'), $addr);
    +    return $addr;
    +  } // function mail_encode
    +
    +  /**
    +   * Processes string, formatting plain quotes into curly quotes.
    +   *
    +   * @param $str A @c string specifying the text to process.
    +   *
    +   * @return A @c string containing the processed text.
    +   *
    +   * @private
    +   */
    +  function process_quotes($str) {
    +    // stub routine for now. subclass and implement.
    +    return $str;
    +  } // function process_quotes
    +
    +  // a default set of macros for the {...} macro syntax
    +  // just a handy way to write a lot of the international characters
    +  // and some commonly used symbols
    +
    +  /**
    +   * Returns an associative @c array of macros that are assigned to be processed by
    +   * default within the @c format_inline method.
    +   *
    +   * @return An @c array containing the default macros.
    +   *
    +   * @private
    +   */
    +  function default_macros() {
    +    // <, >, " must be html entities in the macro text since
    +    // those values are escaped by the time they are processed
    +    // for macros.
    +    return array(
    +      'c|' => '&#162;', // CENT SIGN
    +      '|c' => '&#162;', // CENT SIGN
    +      'L-' => '&#163;', // POUND SIGN
    +      '-L' => '&#163;', // POUND SIGN
    +      'Y=' => '&#165;', // YEN SIGN
    +      '=Y' => '&#165;', // YEN SIGN
    +      '(c)' => '&#169;', // COPYRIGHT SIGN
    +      '&lt;&lt;' => '&#171;', // LEFT-POINTING DOUBLE ANGLE QUOTATION
    +      '(r)' => '&#174;', // REGISTERED SIGN
    +      '+_' => '&#177;', // PLUS-MINUS SIGN
    +      '_+' => '&#177;', // PLUS-MINUS SIGN
    +      '&gt;&gt;' => '&#187;', // RIGHT-POINTING DOUBLE ANGLE QUOTATION
    +      '1/4' => '&#188;', // VULGAR FRACTION ONE QUARTER
    +      '1/2' => '&#189;', // VULGAR FRACTION ONE HALF
    +      '3/4' => '&#190;', // VULGAR FRACTION THREE QUARTERS
    +      'A`' => '&#192;', // LATIN CAPITAL LETTER A WITH GRAVE
    +      '`A' => '&#192;', // LATIN CAPITAL LETTER A WITH GRAVE
    +      'A\'' => '&#193;', // LATIN CAPITAL LETTER A WITH ACUTE
    +      '\'A' => '&#193;', // LATIN CAPITAL LETTER A WITH ACUTE
    +      'A^' => '&#194;', // LATIN CAPITAL LETTER A WITH CIRCUMFLEX
    +      '^A' => '&#194;', // LATIN CAPITAL LETTER A WITH CIRCUMFLEX
    +      'A~' => '&#195;', // LATIN CAPITAL LETTER A WITH TILDE
    +      '~A' => '&#195;', // LATIN CAPITAL LETTER A WITH TILDE
    +      'A"' => '&#196;', // LATIN CAPITAL LETTER A WITH DIAERESIS
    +      '"A' => '&#196;', // LATIN CAPITAL LETTER A WITH DIAERESIS
    +      'Ao' => '&#197;', // LATIN CAPITAL LETTER A WITH RING ABOVE
    +      'oA' => '&#197;', // LATIN CAPITAL LETTER A WITH RING ABOVE
    +      'AE' => '&#198;', // LATIN CAPITAL LETTER AE
    +      'C,' => '&#199;', // LATIN CAPITAL LETTER C WITH CEDILLA
    +      ',C' => '&#199;', // LATIN CAPITAL LETTER C WITH CEDILLA
    +      'E`' => '&#200;', // LATIN CAPITAL LETTER E WITH GRAVE
    +      '`E' => '&#200;', // LATIN CAPITAL LETTER E WITH GRAVE
    +      'E\'' => '&#201;', // LATIN CAPITAL LETTER E WITH ACUTE
    +      '\'E' => '&#201;', // LATIN CAPITAL LETTER E WITH ACUTE
    +      'E^' => '&#202;', // LATIN CAPITAL LETTER E WITH CIRCUMFLEX
    +      '^E' => '&#202;', // LATIN CAPITAL LETTER E WITH CIRCUMFLEX
    +      'E"' => '&#203;', // LATIN CAPITAL LETTER E WITH DIAERESIS
    +      '"E' => '&#203;', // LATIN CAPITAL LETTER E WITH DIAERESIS
    +      'I`' => '&#204;', // LATIN CAPITAL LETTER I WITH GRAVE
    +      '`I' => '&#204;', // LATIN CAPITAL LETTER I WITH GRAVE
    +      'I\'' => '&#205;', // LATIN CAPITAL LETTER I WITH ACUTE
    +      '\'I' => '&#205;', // LATIN CAPITAL LETTER I WITH ACUTE
    +      'I^' => '&#206;', // LATIN CAPITAL LETTER I WITH CIRCUMFLEX
    +      '^I' => '&#206;', // LATIN CAPITAL LETTER I WITH CIRCUMFLEX
    +      'I"' => '&#207;', // LATIN CAPITAL LETTER I WITH DIAERESIS
    +      '"I' => '&#207;', // LATIN CAPITAL LETTER I WITH DIAERESIS
    +      'D-' => '&#208;', // LATIN CAPITAL LETTER ETH
    +      '-D' => '&#208;', // LATIN CAPITAL LETTER ETH
    +      'N~' => '&#209;', // LATIN CAPITAL LETTER N WITH TILDE
    +      '~N' => '&#209;', // LATIN CAPITAL LETTER N WITH TILDE
    +      'O`' => '&#210;', // LATIN CAPITAL LETTER O WITH GRAVE
    +      '`O' => '&#210;', // LATIN CAPITAL LETTER O WITH GRAVE
    +      'O\'' => '&#211;', // LATIN CAPITAL LETTER O WITH ACUTE
    +      '\'O' => '&#211;', // LATIN CAPITAL LETTER O WITH ACUTE
    +      'O^' => '&#212;', // LATIN CAPITAL LETTER O WITH CIRCUMFLEX
    +      '^O' => '&#212;', // LATIN CAPITAL LETTER O WITH CIRCUMFLEX
    +      'O~' => '&#213;', // LATIN CAPITAL LETTER O WITH TILDE
    +      '~O' => '&#213;', // LATIN CAPITAL LETTER O WITH TILDE
    +      'O"' => '&#214;', // LATIN CAPITAL LETTER O WITH DIAERESIS
    +      '"O' => '&#214;', // LATIN CAPITAL LETTER O WITH DIAERESIS
    +      'O/' => '&#216;', // LATIN CAPITAL LETTER O WITH STROKE
    +      '/O' => '&#216;', // LATIN CAPITAL LETTER O WITH STROKE
    +      'U`' =>  '&#217;', // LATIN CAPITAL LETTER U WITH GRAVE
    +      '`U' =>  '&#217;', // LATIN CAPITAL LETTER U WITH GRAVE
    +      'U\'' => '&#218;', // LATIN CAPITAL LETTER U WITH ACUTE
    +      '\'U' => '&#218;', // LATIN CAPITAL LETTER U WITH ACUTE
    +      'U^' => '&#219;', // LATIN CAPITAL LETTER U WITH CIRCUMFLEX
    +      '^U' => '&#219;', // LATIN CAPITAL LETTER U WITH CIRCUMFLEX
    +      'U"' => '&#220;', // LATIN CAPITAL LETTER U WITH DIAERESIS
    +      '"U' => '&#220;', // LATIN CAPITAL LETTER U WITH DIAERESIS
    +      'Y\'' => '&#221;', // LATIN CAPITAL LETTER Y WITH ACUTE
    +      '\'Y' => '&#221;', // LATIN CAPITAL LETTER Y WITH ACUTE
    +      'a`' => '&#224;', // LATIN SMALL LETTER A WITH GRAVE
    +      '`a' => '&#224;', // LATIN SMALL LETTER A WITH GRAVE
    +      'a\'' => '&#225;', // LATIN SMALL LETTER A WITH ACUTE
    +      '\'a' => '&#225;', // LATIN SMALL LETTER A WITH ACUTE
    +      'a^' => '&#226;', // LATIN SMALL LETTER A WITH CIRCUMFLEX
    +      '^a' => '&#226;', // LATIN SMALL LETTER A WITH CIRCUMFLEX
    +      'a~' => '&#227;', // LATIN SMALL LETTER A WITH TILDE
    +      '~a' => '&#227;', // LATIN SMALL LETTER A WITH TILDE
    +      'a"' => '&#228;', // LATIN SMALL LETTER A WITH DIAERESIS
    +      '"a' => '&#228;', // LATIN SMALL LETTER A WITH DIAERESIS
    +      'ao' => '&#229;', // LATIN SMALL LETTER A WITH RING ABOVE
    +      'oa' => '&#229;', // LATIN SMALL LETTER A WITH RING ABOVE
    +      'ae' => '&#230;', // LATIN SMALL LETTER AE
    +      'c,' => '&#231;', // LATIN SMALL LETTER C WITH CEDILLA
    +      ',c' => '&#231;', // LATIN SMALL LETTER C WITH CEDILLA
    +      'e`' => '&#232;', // LATIN SMALL LETTER E WITH GRAVE
    +      '`e' => '&#232;', // LATIN SMALL LETTER E WITH GRAVE
    +      'e\'' => '&#233;', // LATIN SMALL LETTER E WITH ACUTE
    +      '\'e' => '&#233;', // LATIN SMALL LETTER E WITH ACUTE
    +      'e^' => '&#234;', // LATIN SMALL LETTER E WITH CIRCUMFLEX
    +      '^e' => '&#234;', // LATIN SMALL LETTER E WITH CIRCUMFLEX
    +      'e"' => '&#235;', // LATIN SMALL LETTER E WITH DIAERESIS
    +      '"e' => '&#235;', // LATIN SMALL LETTER E WITH DIAERESIS
    +      'i`' => '&#236;', // LATIN SMALL LETTER I WITH GRAVE
    +      '`i' => '&#236;', // LATIN SMALL LETTER I WITH GRAVE
    +      'i\'' => '&#237;', // LATIN SMALL LETTER I WITH ACUTE
    +      '\'i' => '&#237;', // LATIN SMALL LETTER I WITH ACUTE
    +      'i^' => '&#238;', // LATIN SMALL LETTER I WITH CIRCUMFLEX
    +      '^i' => '&#238;', // LATIN SMALL LETTER I WITH CIRCUMFLEX
    +      'i"' => '&#239;', // LATIN SMALL LETTER I WITH DIAERESIS
    +      '"i' => '&#239;', // LATIN SMALL LETTER I WITH DIAERESIS
    +      'n~' => '&#241;', // LATIN SMALL LETTER N WITH TILDE
    +      '~n' => '&#241;', // LATIN SMALL LETTER N WITH TILDE
    +      'o`' => '&#242;', // LATIN SMALL LETTER O WITH GRAVE
    +      '`o' => '&#242;', // LATIN SMALL LETTER O WITH GRAVE
    +      'o\'' => '&#243;', // LATIN SMALL LETTER O WITH ACUTE
    +      '\'o' => '&#243;', // LATIN SMALL LETTER O WITH ACUTE
    +      'o^' => '&#244;', // LATIN SMALL LETTER O WITH CIRCUMFLEX
    +      '^o' => '&#244;', // LATIN SMALL LETTER O WITH CIRCUMFLEX
    +      'o~' => '&#245;', // LATIN SMALL LETTER O WITH TILDE
    +      '~o' => '&#245;', // LATIN SMALL LETTER O WITH TILDE
    +      'o"' => '&#246;', // LATIN SMALL LETTER O WITH DIAERESIS
    +      '"o' => '&#246;', // LATIN SMALL LETTER O WITH DIAERESIS
    +      ':-' => '&#247;', // DIVISION SIGN
    +      '-:' => '&#247;', // DIVISION SIGN
    +      'o/' => '&#248;', // LATIN SMALL LETTER O WITH STROKE
    +      '/o' => '&#248;', // LATIN SMALL LETTER O WITH STROKE
    +      'u`' => '&#249;', // LATIN SMALL LETTER U WITH GRAVE
    +      '`u' => '&#249;', // LATIN SMALL LETTER U WITH GRAVE
    +      'u\'' => '&#250;', // LATIN SMALL LETTER U WITH ACUTE
    +      '\'u' => '&#250;', // LATIN SMALL LETTER U WITH ACUTE
    +      'u^' => '&#251;', // LATIN SMALL LETTER U WITH CIRCUMFLEX
    +      '^u' => '&#251;', // LATIN SMALL LETTER U WITH CIRCUMFLEX
    +      'u"' => '&#252;', // LATIN SMALL LETTER U WITH DIAERESIS
    +      '"u' => '&#252;', // LATIN SMALL LETTER U WITH DIAERESIS
    +      'y\'' => '&#253;', // LATIN SMALL LETTER Y WITH ACUTE
    +      '\'y' => '&#253;', // LATIN SMALL LETTER Y WITH ACUTE
    +      'y"' => '&#255;', // LATIN SMALL LETTER Y WITH DIAERESIS
    +      '"y' => '&#255;', // LATIN SMALL LETTER Y WITH DIAERESIS
    +      'OE' => '&#338;', // LATIN CAPITAL LIGATURE OE
    +      'oe' => '&#339;', // LATIN SMALL LIGATURE OE
    +      '*' => '&#8226;', // BULLET
    +      'Fr' => '&#8355;', // FRENCH FRANC SIGN
    +      'L=' => '&#8356;', // LIRA SIGN
    +      '=L' => '&#8356;', // LIRA SIGN
    +      'Rs' => '&#8360;', // RUPEE SIGN
    +      'C=' => '&#8364;', // EURO SIGN
    +      '=C' => '&#8364;', // EURO SIGN
    +      'tm' => '&#8482;', // TRADE MARK SIGN
    +      '&lt;-' => '&#8592;', // LEFTWARDS ARROW
    +      '-&gt;' => '&#8594;', // RIGHTWARDS ARROW
    +      '&lt;=' => '&#8656;', // LEFTWARDS DOUBLE ARROW
    +      '=&gt;' => '&#8658;', // RIGHTWARDS DOUBLE ARROW
    +      '=/' => '&#8800;', // NOT EQUAL TO
    +      '/=' => '&#8800;', // NOT EQUAL TO
    +      '&lt;_' => '&#8804;', // LESS-THAN OR EQUAL TO
    +      '_&lt;' => '&#8804;', // LESS-THAN OR EQUAL TO
    +      '&gt;_' => '&#8805;', // GREATER-THAN OR EQUAL TO
    +      '_&gt;' => '&#8805;', // GREATER-THAN OR EQUAL TO
    +      ':(' => '&#9785;', // WHITE FROWNING FACE
    +      ':)' => '&#9786;', // WHITE SMILING FACE
    +      'spade' => '&#9824;', // BLACK SPADE SUIT
    +      'club' => '&#9827;', // BLACK CLUB SUIT
    +      'heart' => '&#9829;', // BLACK HEART SUIT
    +      'diamond' => '&#9830;', // BLACK DIAMOND SUIT
    +    );
    +  } // function default_macros
    +
    +  // "private", internal routines
    +
    +  /**
    +   * Sets the default CSS names for CSS controlled markup. This
    +   * is an internal function that should not be called directly.
    +   *
    +   * @private
    +   */
    +  function _css_defaults() {
    +    $css_defaults = array(
    +      'class_align_right' => 'right',
    +      'class_align_left' => 'left',
    +      'class_align_center' => 'center',
    +      'class_align_top' => 'top',
    +      'class_align_bottom' => 'bottom',
    +      'class_align_middle' => 'middle',
    +      'class_align_justify' => 'justify',
    +      'class_caps' => 'caps',
    +      'class_footnote' => 'footnote',
    +      'id_footnote_prefix' => 'fn',
    +    );
    +    $this->css($css_defaults);
    +  } // function _css_defaults
    +
    +  /**
    +   * Returns the alignment keyword depending on the symbol passed.
    +   *
    +   * <ul>
    +   *
    +   * <li><b><code>\<\></code></b>
    +   *
    +   * becomes 'justify'</li>
    +   *
    +   * <li><b><code>\<</code></b>
    +   *
    +   * becomes 'left'</li>
    +   *
    +   * <li><b><code>\></code></b>
    +   *
    +   * becomes 'right'</li>
    +   *
    +   * <li><b><code>=</code></b>
    +   *
    +   * becomes 'center'</li>
    +   *
    +   * </ul>
    +   *
    +   * @param $align A @c string specifying the alignment code.
    +   *
    +   * @return A @c string containing the alignment text.
    +   *
    +   * @private
    +   */
    +  function _halign($align) {
    +    if (preg_match('/<>/', $align)) {
    +      return 'justify';
    +    } elseif (preg_match('/</', $align)) {
    +      return 'left';
    +    } elseif (preg_match('/>/', $align)) {
    +      return 'right';
    +    } elseif (preg_match('/=/', $align)) {
    +      return 'center';
    +    }
    +    return '';
    +  } // function _halign
    +
    +  /**
    +   * Returns the alignment keyword depending on the symbol passed.
    +   *
    +   * <ul>
    +   *
    +   * <li><b><code>^</code></b>
    +   *
    +   * becomes 'top'</li>
    +   *
    +   * <li><b><code>~</code></b>
    +   *
    +   * becomes 'bottom'</li>
    +   *
    +   * <li><b><code>-</code></b>
    +   *
    +   * becomes 'middle'</li>
    +   *
    +   * </ul>
    +   *
    +   * @param $align A @c string specifying the alignment code.
    +   *
    +   * @return A @c string containing the alignment text.
    +   *
    +   * @private
    +   */
    +  function _valign($align) {
    +    if (preg_match('/\^/', $align)) {
    +      return 'top';
    +    } elseif (preg_match('/~/', $align)) {
    +      return 'bottom';
    +    } elseif (preg_match('/-/', $align)) {
    +      return 'middle';
    +    }
    +    return '';
    +  } // function _valign
    +
    +  /**
    +   * Returns the alignment keyword depending on the symbol passed.
    +   * The following alignment symbols are recognized, and given
    +   * preference in the order listed:
    +   *
    +   * <ul>
    +   *
    +   * <li><b><code>^</code></b>
    +   *
    +   * becomes 'top'</li>
    +   *
    +   * <li><b><code>~</code></b>
    +   *
    +   * becomes 'bottom'</li>
    +   *
    +   * <li><b><code>-</code></b>
    +   *
    +   * becomes 'middle'</li>
    +   *
    +   * <li><b><code>\<</code></b>
    +   *
    +   * becomes 'left'</li>
    +   *
    +   * <li><b><code>\></code></b>
    +   *
    +   * becomes 'right'</li>
    +   *
    +   * </ul>
    +   *
    +   * @param $align A @c string containing the alignment code.
    +   *
    +   * @return A @c string containing the alignment text.
    +   *
    +   * @private
    +   */
    +  function _imgalign($align) {
    +    $align = preg_replace('/(<>|=)/', '', $align);
    +    return ($this->_valign($align) ? $this->_valign($align) : $this->_halign($align));
    +  } // function _imgalign
    +
    +  /**
    +   * This utility routine will take 'border' characters off of
    +   * the given @c $pre and @c $post strings if they match one of these
    +   * conditions:
    +   * <pre>
    +   *     $pre starts with '[', $post ends with ']'
    +   *     $pre starts with '{', $post ends with '}'
    +   * </pre>
    +   * If neither condition is met, then the @c $pre and @c $post
    +   * values are left untouched.
    +   *
    +   * @param $pre A @c string specifying the prefix.
    +   * @param $post A @c string specifying the postfix.
    +   *
    +   * @private
    +   */
    +  function _strip_borders(&$pre, &$post) {
    +    if ($post && $pre && preg_match('/[{[]/', ($open = substr($pre, 0, 1)))) {
    +      $close = substr($post, 0, 1);
    +      if ((($open == '{') && ($close == '}')) ||
    +          (($open == '[') && ($close == ']'))) {
    +        $pre = substr($pre, 1);
    +        $post = substr($post, 1);
    +      } else {
    +        if (!preg_match('/[}\]]/', $close)) { $close = substr($post, -1, 1); }
    +        if ((($open == '{') && ($close == '}')) ||
    +            (($open == '[') && ($close == ']'))) {
    +          $pre = substr($pre, 1);
    +          $post = substr($post, 0, strlen($post) - 1);
    +        }
    +      }
    +    }
    +  } // function _strip_borders
    +
    +  /**
    +   * An internal routine that takes a string and appends it to an array.
    +   * It returns a marker that is used later to restore the preserved
    +   * string.
    +   *
    +   * @param $array The @c array in which to store the replacement
    +   *        text.
    +   * @param $str A @c string specifying the replacement text.
    +   *
    +   * @return A @c string containing a temporary marker for the
    +   *         replacement.
    +   *
    +   * @private
    +   */
    +  function _repl(&$array, $str) {
    +    $array[] = $str;
    +    return '<textile#' . count($array) . '>';
    +  } // function _repl
    +
    +  /**
    +   * An internal routine responsible for breaking up a string into
    +   * individual tag and plaintext elements.
    +   *
    +   * @param $str A @c string specifying the text to tokenize.
    +   *
    +   * @return An @c array containing the tag and text tokens.
    +   *
    +   * @private
    +   */
    +  function _tokenize($str) {
    +    $pos = 0;
    +    $len = strlen($str);
    +    unset($tokens);
    +
    +    $depth = 6;
    +    $nested_tags = substr(str_repeat('(?:</?[A-Za-z0-9:]+ \s? (?:[^<>]|', $depth), 0, -1)
    +      . str_repeat(')*>)', $depth);
    +    $match = '(?s: <! ( -- .*? -- \s* )+ > )|  # comment
    +              (?s: <\? .*? \?> )|              # processing instruction
    +              (?s: <% .*? %> )|                # ASP-like
    +              (?:' . $nested_tags . ')|
    +              (?:' . $this->codere . ')';     // nested tags
    +
    +    while (preg_match('{(' . $match . ')}x', substr($str, $pos), $matches, PREG_OFFSET_CAPTURE)) {
    +      $whole_tag = $matches[1][0];
    +      $sec_start = $pos + $matches[1][1] + strlen($whole_tag);
    +      $tag_start = $sec_start - strlen($whole_tag);
    +      if ($pos < $tag_start) {
    +        $tokens[] = array('text', substr($str, $pos, $tag_start - $pos));
    +      }
    +      if (preg_match('/^[[{]?@/', $whole_tag)) {
    +        $tokens[] = array('text', $whole_tag);
    +      } else {
    +        // this clever hack allows us to preserve \n within tags.
    +        // this is restored at the end of the format_paragraph method
    +        //$whole_tag = preg_replace('/\n/', "\r", $whole_tag);
    +        $whole_tag = preg_replace('/\n/', "\001", $whole_tag);
    +        $tokens[] = array('tag', $whole_tag);
    +      }
    +      $pos = $sec_start;
    +    }
    +    if ($pos < $len) { $tokens[] = array('text', substr($str, $pos, $len - $pos)); }
    +    return $tokens;
    +  } // function _tokenize
    +
    +  /**
    +   * Returns the version of this release of Textile.php. *JHR*
    +   *
    +   * @return An @c array with keys 'text' and 'build' containing the
    +   *         text version and build ID of this release, respectively.
    +   *
    +   * @static
    +   */
    +  function version() {
    +    /* Why text and an ID?  Well, the text is easier for the user to
    +     * read and understand while the build ID, being a number (a date
    +     * with a serial, specifically), is easier for the developer to
    +     * use to determine newer/older versions for upgrade and
    +     * installation purposes.
    +     */
    +    return array("text" => "2.0.8", "build" => 2005032100);
    +  } // function version
    +
    +/**
    +   * Creates a custom callback function from the provided PHP
    +   * code. The result is used as the callback in
    +   * @c preg_replace_callback calls. *JHR*
    +   *
    +   * @param $function A @c string specifying the PHP code for the
    +   *        function body.
    +   *
    +   * @return A @c function to be used for the callback.
    +   *
    +   * @private
    +   */
    +  function _cb($function) {
    +    $current =& Textile::_current_store($this);
    +    return create_function('$m', '$me =& Textile::_current(); return ' . $function . ';');
    +  } // function _cb
    +
    +  /**
    +   * Stores a static variable for the Textile class. This helper
    +   * function is used by @c _current to simulate a static
    +   * class variable in PHP. *JHR*
    +   *
    +   * @param $new If a non-@c NULL object reference, the Textile object
    +   *        to be set as the current object.
    +   *
    +   * @return The @c array containing a reference to the current
    +   *         Textile object at index 0. An array is used because PHP
    +   *         does not allow static variables to be references.
    +   *
    +   * @static
    +   * @private
    +   */
    + /* static */ function &_current_store(&$new) {
    +   static $current = array();
    +
    +   if ($new != NULL) {
    +     $current = array(&$new);
    +   }
    +
    +   return $current;
    + } // function _current_store
    +
    +  /**
    +   * Returns the "current" Textile object. This is used within
    +   * anonymous callback functions which cannot have the scope of a
    +   * specific object. *JHR*
    +   *
    +   * @return An @c object reference to the current Textile object.
    +   *
    +   * @static
    +   * @private
    +   */
    + /* static */ function &_current() {
    +   $current =& Textile::_current_store($null = NULL);
    +   return $current[0];
    + } // function _current
    +} // class Textile
    +
    +/**
    + * Brad Choate's mttextile Movable Type plugin adds some additional
    + * functionality to the Textile.pm Perl module. This includes optional
    + * "SmartyPants" processing of text to produce smart quotes, dashes,
    + * etc., code colorizing using Beautifier, and some special lookup
    + * links (imdb, google, dict, and amazon). The @c MTLikeTextile class
    + * is a subclass of @c Textile that provides an MT-like implementation
    + * of Textile to produce results similar to that of the mttextile
    + * plugin. Currently only the SmartyPants and special lookup links are
    + * implemented.
    + *
    + * Using the @c MTLikeTextile class is exactly the same as using @c
    + * Textile. Simply use <code>$textile = new MTLikeTextile;</code>
    + * instead of <code>$textile = new Textile;</code> to create a Textile
    + * object.  This will enable the special lookup links.  To enable
    + * SmartyPants processing, you must install the SmartyPants-PHP
    + * implementation available at
    + * <a
    + * href="http://monauraljerk.org/smartypants-php/">http://monauraljerk.org/smartypants-php/</a>
    + * and include the
    + * SmartyPants-PHP.inc file.
    + *
    + * <pre><code>
    + * include_once("Textile.php");
    + * include_once("SmartyPants-PHP.inc");
    + * $text = \<\<\<EOT
    + * h1. Heading
    + *
    + * A _simple_ demonstration of Textile markup.
    + *
    + * * One
    + * * Two
    + * * Three
    + *
    + * "More information":http://www.textism.com/tools/textile is available.
    + * EOT;
    + *
    + * $textile = new MTLikeTextile;
    + * $html = $textile->process($text);
    + * print $html;
    + * </code></pre>
    + *
    + * @brief A Textile implementation providing additional
    + *        Movable-Type-like formatting to produce results similar to
    + *        the mttextile plugin.
    + *
    + * @author Jim Riggs \<textile at jimandlisa dot com\>
    + */
    +class MTLikeTextile extends Textile {
    +  /**
    +   * Instantiates a new MTLikeTextile object. Optional options
    +   * can be passed to initialize the object. Attributes for the
    +   * options key are the same as the get/set method names
    +   * documented here.
    +   *
    +   * @param $options The @c array specifying the options to use for
    +   *        this object.
    +   *
    +   * @public
    +   */
    +  function MTLikeTextile($options = array()) {
    +    parent::Textile($options);
    +  } // function MTLikeTextile
    +
    +  /**
    +   * @private
    +   */
    +  function process_quotes($str) {
    +    if (!$this->options['do_quotes'] || !function_exists('SmartyPants')) {
    +      return $str;
    +    }
    +
    +    return SmartyPants($str, $this->options['smarty_mode']);
    +  } // function process_quotes
    +
    +  /**
    +   * @private
    +   */
    +  function format_url($args) {
    +    $url = ($args['url'] ? $args['url'] : '');
    +
    +    if (preg_match('/^(imdb|google|dict|amazon)(:(.+))?$/x', $url, $matches)) {
    +      $term = $matches[3];
    +      $term = ($term ? $term : strip_tags($args['linktext']));
    +
    +      switch ($matches[1]) {
    +        case 'imdb':
    +          $args['url'] = 'http://www.imdb.com/Find?for=' . $term;
    +          break;
    +
    +        case 'google':
    +          $args['url'] = 'http://www.google.com/search?q=' . $term;
    +          break;
    +
    +        case 'dict':
    +          $args['url'] = 'http://www.dictionary.com/search?q=' . $term;
    +          break;
    +
    +        case 'amazon':
    +          $args['url'] = 'http://www.amazon.com/exec/obidos/external-search?index=blended&keyword=' . $term;
    +          break;
    +      }
    +    }
    +
    +    return parent::format_url($args);
    +  } // function format_url
    +} // class MTLikeTextile
    +
    +/**
    + * @mainpage
    + * Textile - A Humane Web Text Generator.
    + *
    + * @section synopsis SYNOPSIS
    + *
    + * <pre><code>
    + * include_once("Textile.php");
    + * $text = \<\<\<EOT
    + * h1. Heading
    + *
    + * A _simple_ demonstration of Textile markup.
    + *
    + * * One
    + * * Two
    + * * Three
    + *
    + * "More information":http://www.textism.com/tools/textile is available.
    + * EOT;
    + *
    + * $textile = new Textile;
    + * $html = $textile->process($text);
    + * print $html;
    + * </code></pre>
    + *
    + * @section abstract ABSTRACT
    + *
    + * Textile.php is a PHP-based implementation of Dean Allen's Textile
    + * syntax. Textile is shorthand for doing common formatting tasks.
    + *
    + * @section syntax SYNTAX
    + *
    + * Textile processes text in units of blocks and lines.
    + * A block might also be considered a paragraph, since blocks
    + * are separated from one another by a blank line. Blocks
    + * can begin with a signature that helps identify the rest
    + * of the block content. Block signatures include:
    + *
    + * <ul>
    + *
    + * <li><b>p</b>
    + *
    + * A paragraph block. This is the default signature if no
    + * signature is explicitly given. Paragraphs are formatted
    + * with all the inline rules (see inline formatting) and
    + * each line receives the appropriate markup rules for
    + * the flavor of HTML in use. For example, newlines for XHTML
    + * content receive a \<br /\> tag at the end of the line
    + * (with the exception of the last line in the paragraph).
    + * Paragraph blocks are enclosed in a \<p\> tag.</li>
    + *
    + * <li><b>pre</b>
    + *
    + * A pre-formatted block of text. Textile will not add any
    + * HTML tags for individual lines. Whitespace is also preserved.
    + * 
    + * Note that within a "pre" block, \< and \> are
    + * translated into HTML entities automatically.</li>
    + *
    + * <li><b>bc</b>
    + *
    + * A "bc" signature is short for "block code", which implies
    + * a preformatted section like the 'pre' block, but it also
    + * gets a \<code\> tag (or for XHTML 2, a \<blockcode\>
    + * tag is used instead).
    + * 
    + * Note that within a "bc" block, \< and \> are
    + * translated into HTML entities automatically.</li>
    + *
    + * <li><b>table</b>
    + *
    + * For composing HTML tables. See the "TABLES" section for more
    + * information.</li>
    + *
    + * <li><b>bq</b>
    + *
    + * A "bq" signature is short for "block quote". Paragraph text
    + * formatting is applied to these blocks and they are enclosed
    + * in a \<blockquote\> tag as well as \<p\> tags
    + * within.</li>
    + *
    + * <li><b>h1, h2, h3, h4, h5, h6</b>
    + *
    + * Headline signatures that produce \<h1\>, etc. tags.
    + * You can adjust the relative output of these using the
    + * head_offset attribute.</li>
    + *
    + * <li><b>clear</b>
    + *
    + * A 'clear' signature is simply used to indicate that the next
    + * block should emit a CSS style attribute that clears any
    + * floating elements. The default behavior is to clear "both",
    + * but you can use the left (\< or right \>) alignment
    + * characters to indicate which side to clear.</li>
    + *
    + * <li><b>dl</b>
    + *
    + * A "dl" signature is short for "definition list". See the
    + * "LISTS" section for more information.</li>
    + *
    + * <li><b>fn</b>
    + *
    + * A "fn" signature is short for "footnote". You add a number
    + * following the "fn" keyword to number the footnote. Footnotes
    + * are output as paragraph tags but are given a special CSS
    + * class name which can be used to style them as you see fit.</li>
    + *
    + * </ul>
    + *
    + * All signatures should end with a period and be followed
    + * with a space. Inbetween the signature and the period, you
    + * may use several parameters to further customize the block.
    + * These include:
    + *
    + * <ul>
    + *
    + * <li><b><code>{style rule}</code></b>
    + *
    + * A CSS style rule. Style rules can span multiple lines.</li>
    + *
    + * <li><b><code>[ll]</code></b>
    + *
    + * A language identifier (for a "lang" attribute).</li>
    + *
    + * <li><b><code>(class)</code> or <code>(#id)</code> or <code>(class#id)</code></b>
    + *
    + * For CSS class and id attributes.</li>
    + *
    + * <li><b><code>\></code>, <code>\<</code>, <code>=</code>, <code>\<\></code></b>
    + *
    + * Modifier characters for alignment. Right-justification, left-justification,
    + * centered, and full-justification.</li>
    + *
    + * <li><b><code>(</code> (one or more)</b>
    + *
    + * Adds padding on the left. 1em per "(" character is applied.
    + * When combined with the align-left or align-right modifier,
    + * it makes the block float.</li>
    + *
    + * <li><b><code>)</code> (one or more)</b>
    + *
    + * Adds padding on the right. 1em per ")" character is applied.
    + * When combined with the align-left or align-right modifier,
    + * it makes the block float.</li>
    + *
    + * <li><b><code>|filter|</code> or <code>|filter|filter|filter|</code></b>
    + *
    + * A filter may be invoked to further format the text for this
    + * signature. If one or more filters are identified, the text
    + * will be processed first using the filters and then by
    + * Textile's own block formatting rules.</li>
    + *
    + * </ul>
    + *
    + * @subsection extendedblocks Extended Blocks
    + *
    + * Normally, a block ends with the first blank line encountered.
    + * However, there are situations where you may want a block to continue
    + * for multiple paragraphs of text. To cause a given block signature
    + * to stay active, use two periods in your signature instead of one.
    + * This will tell Textile to keep processing using that signature
    + * until it hits the next signature is found.
    + *
    + * For example:
    + * <pre>
    + *     bq.. This is paragraph one of a block quote.
    + *
    + *     This is paragraph two of a block quote.
    + *
    + *     p. Now we're back to a regular paragraph.
    + * </pre>
    + * You can apply this technique to any signature (although for
    + * some it doesn't make sense, like "h1" for example). This is
    + * especially useful for "bc" blocks where your code may
    + * have many blank lines scattered through it.
    + *
    + * @subsection escaping Escaping
    + *
    + * Sometimes you want Textile to just get out of the way and
    + * let you put some regular HTML markup in your document. You
    + * can disable Textile formatting for a given block using the '=='
    + * escape mechanism:
    + * <pre>
    + *     p. Regular paragraph
    + *
    + *     ==
    + *     Escaped portion -- will not be formatted
    + *     by Textile at all
    + *     ==
    + *
    + *     p. Back to normal.
    + * </pre>
    + * You can also use this technique within a Textile block,
    + * temporarily disabling the inline formatting functions:
    + * <pre>
    + *     p. This is ==*a test*== of escaping.
    + * </pre>
    + * @subsection inlineformatting Inline Formatting
    + *
    + * Formatting within a block of text is covered by the "inline"
    + * formatting rules. These operators must be placed up against
    + * text/punctuation to be recognized. These include:
    + *
    + * <ul>
    + *
    + * <li><b><code>*strong*</code></b>
    + *
    + * Translates into \<strong\>strong\</strong\>.</li>
    + *
    + * <li><b>_emphasis_</b>
    + *
    + * Translates into \<em\>emphasis\</em\>.</li>
    + *
    + * <li><b><code>**bold**</code></b>
    + *
    + * Translates into \<b\>bold\</b\>.</li>
    + *
    + * <li><b><code>__italics__</code></b>
    + *
    + * Translates into \<i\>italics\</i\>.</li>
    + *
    + * <li><b><code>++bigger++</code></b>
    + *
    + * Translates into \<big\>bigger\</big\>.</li>
    + *
    + * <li><b><code>--smaller--</code></b>
    + *
    + * Translates into: \<small\>smaller\</small\>.</li>
    + *
    + * <li><b><code>-deleted text-</code></b>
    + *
    + * Translates into \<del\>deleted text\</del\>.</li>
    + *
    + * <li><b><code>+inserted text+</code></b>
    + *
    + * Translates into \<ins\>inserted text\</ins\>.</li>
    + *
    + * <li><b><code>^superscript^</code></b>
    + *
    + * Translates into \<sup\>superscript\</sup\>.</li>
    + *
    + * <li><b><code>~subscript~</code></b>
    + *
    + * Translates into \<sub\>subscript\</sub\>.</li>
    + *
    + * <li><b><code>\%span\%</code></b>
    + *
    + * Translates into \<span\>span\</span\>.</li>
    + *
    + * <li><b><code>\@code\@</code></b>
    + *
    + * Translates into \<code\>code\</code\>. Note
    + * that within a '\@...\@' section, \< and \> are
    + * translated into HTML entities automatically.</li>
    + *
    + * </ul>
    + *
    + * Inline formatting operators accept the following modifiers:
    + *
    + * <ul>
    + *
    + * <li><b><code>{style rule}</code></b>
    + *
    + * A CSS style rule.</li>
    + *
    + * <li><b><code>[ll]</code></b>
    + *
    + * A language identifier (for a "lang" attribute).</li>
    + *
    + * <li><b><code>(class)</code> or <code>(#id)</code> or <code>(class#id)</code></b>
    + *
    + * For CSS class and id attributes.</li>
    + *
    + * </ul>
    + *
    + * @subsubsection examples Examples
    + * <pre>
    + *     Textile is *way* cool.
    + *
    + *     Textile is *_way_* cool.
    + * </pre>
    + * Now this won't work, because the formatting
    + * characters need whitespace before and after
    + * to be properly recognized.
    + * <pre>
    + *     Textile is way c*oo*l.
    + * </pre>
    + * However, you can supply braces or brackets to
    + * further clarify that you want to format, so
    + * this would work:
    + * <pre>
    + *     Textile is way c[*oo*]l.
    + * </pre>
    + * @subsection footnotes Footnotes
    + *
    + * You can create footnotes like this:
    + * <pre>
    + *     And then he went on a long trip[1].
    + * </pre>
    + * By specifying the brackets with a number inside, Textile will
    + * recognize that as a footnote marker. It will replace that with
    + * a construct like this:
    + * <pre>
    + *     And then he went on a long
    + *     trip<sup class="footnote"><a href="#fn1">1</a></sup>
    + * </pre>
    + * To supply the content of the footnote, place it at the end of your
    + * document using a "fn" block signature:
    + * <pre>
    + *     fn1. And there was much rejoicing.
    + * </pre>
    + * Which creates a paragraph that looks like this:
    + * <pre>
    + *     <p class="footnote" id="fn1"><sup>1</sup> And there was
    + *     much rejoicing.</p>
    + * </pre>
    + * @subsection links Links
    + *
    + * Textile defines a shorthand for formatting hyperlinks.
    + * The format looks like this:
    + * <pre>
    + *     "Text to display":http://example.com
    + * </pre>
    + * In addition to this, you can add 'title' text to your link:
    + * <pre>
    + *     "Text to display (Title text)":http://example.com
    + * </pre>
    + * The URL portion of the link supports relative paths as well
    + * as other protocols like ftp, mailto, news, telnet, etc.
    + * <pre>
    + *     "E-mail me please":mailto:someone\@example.com
    + * </pre>
    + * You can also use single quotes instead of double-quotes if
    + * you prefer. As with the inline formatting rules, a hyperlink
    + * must be surrounded by whitespace to be recognized (an
    + * exception to this is common punctuation which can reside
    + * at the end of the URL). If you have to place a URL next to
    + * some other text, use the bracket or brace trick to do that:
    + * <pre>
    + *     You["gotta":http://example.com]seethis!
    + * </pre>
    + * Textile supports an alternate way to compose links. You can
    + * optionally create a lookup list of links and refer to them
    + * separately. To do this, place one or more links in a block
    + * of it's own (it can be anywhere within your document):
    + * <pre>
    + *     [excom]http://example.com
    + *     [exorg]http://example.org
    + * </pre>
    + * For a list like this, the text in the square brackets is
    + * used to uniquely identify the link given. To refer to that
    + * link, you would specify it like this:
    + * <pre>
    + *     "Text to display":excom
    + * </pre>
    + * Once you've defined your link lookup table, you can use
    + * the identifiers any number of times.
    + *
    + * @subsection images Images
    + *
    + * Images are identified by the following pattern:
    + * <pre>
    + *     !/path/to/image!
    + * </pre>
    + * Image attributes may also be specified:
    + * <pre>
    + *     !/path/to/image 10x20!
    + * </pre>
    + * Which will render an image 10 pixels wide and 20 pixels high.
    + * Another way to indicate width and height:
    + * <pre>
    + *     !/path/to/image 10w 20h!
    + * </pre>
    + * You may also redimension the image using a percentage.
    + * <pre>
    + *     !/path/to/image 20%x40%!
    + * </pre>
    + * Which will render the image at 20% of it's regular width
    + * and 40% of it's regular height.
    + *
    + * Or specify one percentage to resize proprotionately:
    + * <pre>
    + *     !/path/to/image 20%!
    + * </pre>
    + * Alt text can be given as well:
    + * <pre>
    + *     !/path/to/image (Alt text)!
    + * </pre>
    + * The path of the image may refer to a locally hosted image or
    + * can be a full URL.
    + *
    + * You can also use the following modifiers after the opening '!'
    + * character:
    + *
    + * <ul>
    + *
    + * <li><b><code>\<</code></b>
    + *
    + * Align the image to the left (causes the image to float if
    + * CSS options are enabled).</li>
    + *
    + * <li><b><code>\></code></b>
    + *
    + * Align the image to the right (causes the image to float if
    + * CSS options are enabled).</li>
    + *
    + * <li><b><code>-</code> (dash)</b>
    + *
    + * Aligns the image to the middle.</li>
    + *
    + * <li><b><code>^</code></b>
    + *
    + * Aligns the image to the top.</li>
    + *
    + * <li><b><code>~</code> (tilde)</b>
    + *
    + * Aligns the image to the bottom.</li>
    + *
    + * <li><b><code>{style rule}</code></b>
    + *
    + * Applies a CSS style rule to the image.</li>
    + *
    + * <li><b><code>(class)</code> or <code>(#id)</code> or <code>(class#id)</code></b>
    + *
    + * Applies a CSS class and/or id to the image.</li>
    + *
    + * <li><b><code>(</code> (one or more)</b>
    + *
    + * Pads 1em on the left for each '(' character.</li>
    + *
    + * <li><b><code>)</code> (one or more)</b>
    + *
    + * Pads 1em on the right for each ')' character.</li>
    + *
    + * </ul>
    + *
    + * @subsection characterreplacements Character Replacements
    + *
    + * A few simple, common symbols are automatically replaced:
    + * <pre>
    + *     (c)
    + *     (r)
    + *     (tm)
    + * </pre>
    + * In addition to these, there are a whole set of character
    + * macros that are defined by default. All macros are enclosed
    + * in curly braces. These include:
    + * <pre>
    + *     {c|} or {|c} cent sign
    + *     {L-} or {-L} pound sign
    + *     {Y=} or {=Y} yen sign
    + * </pre>
    + * Many of these macros can be guessed. For example:
    + * <pre>
    + *     {A'} or {'A}
    + *     {a"} or {"a}
    + *     {1/4}
    + *     {*}
    + *     {:)}
    + *     {:(}
    + * </pre>
    + * @subsection lists Lists
    + *
    + * Textile also supports ordered and unordered lists.
    + * You simply place an asterisk or pound sign, followed
    + * with a space at the start of your lines.
    + *
    + * Simple lists:
    + * <pre>
    + *     * one
    + *     * two
    + *     * three
    + * </pre>
    + * Multi-level lists:
    + * <pre>
    + *     * one
    + *     ** one A
    + *     ** one B
    + *     *** one B1
    + *     * two
    + *     ** two A
    + *     ** two B
    + *     * three
    + * </pre>
    + * Ordered lists:
    + * <pre>
    + *     # one
    + *     # two
    + *     # three
    + * </pre>
    + * Styling lists:
    + * <pre>
    + *     (class#id)* one
    + *     * two
    + *     * three
    + * </pre>
    + * The above sets the class and id attributes for the \<ul\>
    + * tag.
    + * <pre>
    + *     *(class#id) one
    + *     * two
    + *     * three
    + * </pre>
    + * The above sets the class and id attributes for the first \<li\>
    + * tag.
    + *
    + * Definition lists:
    + * <pre>
    + *     dl. textile:a cloth, especially one manufactured by weaving
    + *     or knitting; a fabric
    + *     format:the arrangement of data for storage or display.
    + * </pre>
    + * Note that there is no space between the term and definition. The
    + * term must be at the start of the line (or following the "dl"
    + * signature as shown above).
    + *
    + * @subsection tables Tables
    + *
    + * Textile supports tables. Tables must be in their own block and
    + * must have pipe characters delimiting the columns. An optional
    + * block signature of "table" may be used, usually for applying
    + * style, class, id or other options to the table element itself.
    + *
    + * From the simple:
    + * <pre>
    + *     |a|b|c|
    + *     |1|2|3|
    + * </pre>
    + * To the complex:
    + * <pre>
    + *     table(fig). {color:red}_|Top|Row|
    + *     {color:blue}|/2. Second|Row|
    + *     |_{color:green}. Last|
    + * </pre>
    + * Modifiers can be specified for the table signature itself,
    + * for a table row (prior to the first '|' character) and
    + * for any cell (following the '|' for that cell). Note that for
    + * cells, a period followed with a space must be placed after
    + * any modifiers to distinguish the modifier from the cell content.
    + *
    + * Modifiers allowed are:
    + *
    + * <ul>
    + *
    + * <li><b><code>{style rule}</code></b>
    + *
    + * A CSS style rule.</li>
    + *
    + * <li><b><code>(class)</code> or <code>(#id)</code> or <code>(class#id)</code></b>
    + *
    + * A CSS class and/or id attribute.</li>
    + *
    + * <li><b><code>(</code> (one or more)</b>
    + *
    + * Adds 1em of padding to the left for each '(' character.</li>
    + *
    + * <li><b><code>)</code> (one or more)</b>
    + *
    + * Adds 1em of padding to the right for each ')' character.</li>
    + *
    + * <li><b><code>\<</code></b>
    + *
    + * Aligns to the left (floats to left for tables if combined with the
    + * ')' modifier).</li>
    + *
    + * <li><b><code>\></code></b>
    + *
    + * Aligns to the right (floats to right for tables if combined with
    + * the '(' modifier).</li>
    + *
    + * <li><b><code>=</code></b>
    + *
    + * Aligns to center (sets left, right margins to 'auto' for tables).</li>
    + *
    + * <li><b><code>\<\></code></b>
    + *
    + * For cells only. Justifies text.</li>
    + *
    + * <li><b><code>^</code></b>
    + *
    + * For rows and cells only. Aligns to the top.</li>
    + *
    + * <li><b><code>~</code> (tilde)</b>
    + *
    + * For rows and cells only. Aligns to the bottom.</li>
    + *
    + * <li><b><code>_</code> (underscore)</b>
    + *
    + * Can be applied to a table row or cell to indicate a header
    + * row or cell.</li>
    + *
    + * <li><b><code>\\2</code> or <code>\\3</code> or <code>\\4</code>, etc.</b>
    + *
    + * Used within cells to indicate a colspan of 2, 3, 4, etc. columns.
    + * When you see "\\", think "push forward".</li>
    + *
    + * <li><b><code>/2</code> or <code>/3</code> or <code>/4</code>, etc.</b>
    + *
    + * Used within cells to indicate a rowspan of 2, 3, 4, etc. rows.
    + * When you see "/", think "push downward".</li>
    + *
    + * </ul>
    + *
    + * When a cell is identified as a header cell and an alignment
    + * is specified, that becomes the default alignment for
    + * cells below it. You can always override this behavior by
    + * specifying an alignment for one of the lower cells.
    + *
    + * @subsection cssnotes CSS Notes
    + *
    + * When CSS is enabled (and it is by default), CSS class names
    + * are automatically applied in certain situations.
    + *
    + * <ul>
    + *
    + * <li>Aligning a block or span or other element to
    + * left, right, etc.
    + *
    + * "left" for left justified, "right" for right justified,
    + * "center" for centered text, "justify" for full-justified
    + * text.</li>
    + *
    + * <li>Aligning an image to the top or bottom
    + *
    + * "top" for top alignment, "bottom" for bottom alignment,
    + * "middle" for middle alignment.</li>
    + *
    + * <li>Footnotes
    + *
    + * "footnote" is applied to the paragraph tag for the
    + * footnote text itself. An id of "fn" plus the footnote
    + * number is placed on the paragraph for the footnote as
    + * well. For the footnote superscript tag, a class of
    + * "footnote" is used.</li>
    + *
    + * <li>Capped text
    + *
    + * For a series of characters that are uppercased, a
    + * span is placed around them with a class of "caps".</li>
    + *
    + * </ul>
    + *
    + * @subsection miscellaneous Miscellaneous
    + *
    + * Textile tries to do it's very best to ensure proper XHTML
    + * syntax. It will even attempt to fix errors you may introduce
    + * writing in HTML yourself. Unescaped '&' characters within
    + * URLs will be properly escaped. Singlet tags such as br, img
    + * and hr are checked for the '/' terminator (and it's added
    + * if necessary). The best way to make sure you produce valid
    + * XHTML with Textile is to not use any HTML markup at all--
    + * use the Textile syntax and let it produce the markup for you.
    + *
    + * @section license LICENSE
    + *
    + * Text::Textile is licensed under the same terms as Perl
    + * itself. Textile.php is licensed under the terms of the GNU General
    + * Public License.
    + *
    + * @section authorandcopyright AUTHOR & COPYRIGHT
    + *
    + * Text::Textile was written by Brad Choate, \<brad at bradchoate dot com\>.
    + * It is an adaptation of Textile, developed by Dean Allen of Textism.com.
    + *
    + * Textile.php is a PHP port of Brad Choate's Text::Textile
    + * (Textile.pm) Perl module.
    + *
    + * Textile.php was ported by Jim Riggs \<textile at jimandlissa dot
    + * com\>. Great care has been taken to leave the Perl code in much the
    + * same form as Textile.pm. While changes were required due to
    + * syntactical differences between Perl and PHP, much of the code was
    + * left intact (even if alternative syntax or code optimizations could
    + * have been made in PHP), even to the point where one can compare
    + * functions/subroutines side by side between the two implementations.
    + * This has been done to ensure compatibility, reduce the possibility
    + * of introducing errors, and simplify maintainance as one version or
    + * the other is updated.
    + *
    + * @author Jim Riggs \<textile at jimandlissa dot com\>
    + * @author Brad Choate \<brad at bradchoate dot com\>
    + * @copyright Copyright &copy; 2004 Jim Riggs and Brad Choate
    + * @version @(#) $Id: Textile.php,v 1.13 2005/03/21 15:26:55 jhriggs Exp $
    + */
    +?>
    diff --git a/TinyMCE/Plugin.php b/TinyMCE/Plugin.php
    new file mode 100644
    index 0000000..6093236
    --- /dev/null
    +++ b/TinyMCE/Plugin.php
    @@ -0,0 +1,154 @@
    +<?php
    +/**
    + * 集成tinyMCE编辑器
    + * 
    + * @package tinyMCE Editor 
    + * @author qining
    + * @version 1.0.1
    + * @dependence 9.9.2-*
    + * @link http://typecho.org
    + */
    +class TinyMCE_Plugin implements Typecho_Plugin_Interface
    +{
    +    /**
    +     * 激活插件方法,如果激活失败,直接抛出异常
    +     * 
    +     * @access public
    +     * @return void
    +     * @throws Typecho_Plugin_Exception
    +     */
    +    public static function activate()
    +    {
    +        Typecho_Plugin::factory('admin/write-post.php')->richEditor = array('TinyMCE_Plugin', 'render');
    +        Typecho_Plugin::factory('admin/write-page.php')->richEditor = array('TinyMCE_Plugin', 'render');
    +        
    +        //去除段落
    +        Typecho_Plugin::factory('Widget_Contents_Post_Edit')->write = array('TinyMCE_Plugin', 'filter');
    +        Typecho_Plugin::factory('Widget_Contents_Page_Edit')->write = array('TinyMCE_Plugin', 'filter');
    +        
    +        Helper::addPanel(0, 'TinyMCE/tiny_mce/langs.php','', '', 'contributor');
    +    }
    +    
    +    /**
    +     * 禁用插件方法,如果禁用失败,直接抛出异常
    +     * 
    +     * @static
    +     * @access public
    +     * @return void
    +     * @throws Typecho_Plugin_Exception
    +     */
    +    public static function deactivate()
    +    {
    +        Helper::removePanel(0, 'TinyMCE/tiny_mce/langs.php');
    +    }
    +    
    +    /**
    +     * 获取插件配置面板
    +     * 
    +     * @access public
    +     * @param Typecho_Widget_Helper_Form $form 配置面板
    +     * @return void
    +     */
    +    public static function config(Typecho_Widget_Helper_Form $form){}
    +    
    +    /**
    +     * 个人用户的配置面板
    +     * 
    +     * @access public
    +     * @param Typecho_Widget_Helper_Form $form
    +     * @return void
    +     */
    +    public static function personalConfig(Typecho_Widget_Helper_Form $form){}
    +    
    +    /**
    +     * 去除段落
    +     * 
    +     * @access public
    +     * @param array $post 数据结构体
    +     * @return array
    +     */
    +    public static function filter($post)
    +    {
    +        $post['text'] = Typecho_Common::removeParagraph($post['text']);
    +        return $post;
    +    }
    +    
    +    /**
    +     * 插件实现方法
    +     * 
    +     * @access public
    +     * @return void
    +     */
    +    public static function render($post)
    +    {
    +        $options = Helper::options();
    +        $js = Typecho_Common::url('TinyMCE/tiny_mce/tiny_mce.js', $options->pluginUrl);
    +        $langs = Typecho_Common::url('extending.php?panel=TinyMCE/tiny_mce/langs.php', $options->adminUrl);
    +        echo "<script type=\"text/javascript\" src=\"{$js}\"></script>
    +<script type=\"text/javascript\" src=\"{$langs}\"></script>
    +<script type=\"text/javascript\">
    +    var insertImageToEditor = function (title, url, link) {
    +        tinyMCE.activeEditor.execCommand('mceInsertContent', false,
    +        '<a href=\"' + link + '\" title=\"' + title + '\"><img src=\"' + url + '\" alt=\"' + title + '\" /></a>');
    +        new Fx.Scroll(window).toElement($(document).getElement('.mceEditor'));
    +    };
    +    
    +    var insertLinkToEditor = function (title, url, link) {
    +        tinyMCE.activeEditor.execCommand('mceInsertContent', false, '<a href=\"' + url + '\" title=\"' + title + '\">' + title + '</a>');
    +        new Fx.Scroll(window).toElement($(document).getElement('.mceEditor'));
    +    };
    +
    +    //自动保存
    +    var autoSave;
    +    
    +    tinyMCE.init({
    +    // General options
    +    mode : 'exact',
    +    elements : 'text',
    +    theme : 'advanced',
    +    skin : 'typecho',
    +    plugins : 'safari,morebreak,inlinepopups,media,coder',
    +    extended_valid_elements : 'code[*],pre[*],script[*],iframe[*]',
    +    
    +    init_instance_callback : function(ed) {
    +        
    +        ed.setContent(\"" . str_replace(array("\n", "\r"), array("\\n", ""), addslashes($post->content)) . "\");
    +        "
    +        . ($options->autoSave ? 
    +        "autoSave = new Typecho.autoSave($('text').getParent('form').getProperty('action'), {
    +            time: 60,
    +            getContentHandle: tinyMCE.activeEditor.getContent.bind(ed),
    +            messageElement: 'auto-save-message',
    +            leaveMessage: '" . _t('您的内容尚未保存, 是否离开此页面?') . "',
    +            form: $('text').getParent('form')
    +        });" : "") .
    +        
    +        "
    +    },
    +    
    +    onchange_callback: function (inst) {
    +        if ('undefined' != typeof(autoSave)) {
    +            autoSave.onContentChange();
    +        }
    +    },
    +    
    +    save_callback: function (element_id, html, body) {
    +        if ('undefined' != typeof(autoSave)) {
    +            autoSave.saveRev = autoSave.rev;
    +        }
    +        
    +        return html;
    +    },
    +    
    +    // Theme options
    +    theme_advanced_buttons1 : 'bold,italic,underline,strikethrough,|,justifyleft,justifycenter,justifyright,justifyfull,|,bullist,numlist,blockquote,|,link,unlink,image,media,|,forecolor,backcolor,|,morebreak,code',
    +    theme_advanced_buttons2 : '',
    +    theme_advanced_buttons3 : '',
    +    theme_advanced_toolbar_location : 'top',
    +    theme_advanced_toolbar_align : 'left',
    +    convert_urls : false,
    +    language : 'typecho'
    +});
    +</script>";
    +    }
    +}
    diff --git a/TinyMCE/tiny_mce/langs.php b/TinyMCE/tiny_mce/langs.php
    new file mode 100644
    index 0000000..3608524
    --- /dev/null
    +++ b/TinyMCE/tiny_mce/langs.php
    @@ -0,0 +1,391 @@
    +<?php
    +if (!defined('__TYPECHO_ROOT_DIR__')) {
    +    exit;
    +}
    +
    +$response->setContentType('text/javascript');
    +?>
    +
    +tinyMCE.addI18n({typecho:{
    +common:{
    +edit_confirm:"<?php _e('在这个文本域启用所见即所得模式?'); ?>",
    +apply:"<?php _e('应用'); ?>",
    +insert:"<?php _e('插入'); ?>",
    +update:"<?php _e('更新'); ?>",
    +cancel:"<?php _e('取消'); ?>",
    +close:"<?php _e('关闭'); ?>",
    +browse:"<?php _e('浏览'); ?>",
    +class_name:"<?php _e('类别'); ?>",
    +not_set:"<?php _e('-- 未设定 --'); ?>",
    +clipboard_msg:"<?php _e('贴上复制、剪下和贴上功能在 Mozilla 和 Firefox 中无法使用。\n你需要了解更多相关信息吗?'); ?>",
    +clipboard_no_support:"<?php _e('目前你的浏览器无法支持,请用键盘快捷键。'); ?>",
    +popup_blocked:"<?php _e('抱歉,快捷功能在你的系统上被封锁,使程序无法正常使用,你需要暂时解除快捷封锁,使工具能正常使用。'); ?>",
    +invalid_data:"<?php _e('错误:输入无效的值,以红色字表示。'); ?>",
    +more_colors:"<?php _e('其它更多颜色'); ?>"
    +},
    +contextmenu:{
    +align:"<?php _e('对齐方式'); ?>",
    +left:"<?php _e('居左对齐'); ?>",
    +center:"<?php _e('居中对齐'); ?>",
    +right:"<?php _e('居右对齐'); ?>",
    +full:"<?php _e('左右对齐'); ?>"
    +},
    +insertdatetime:{
    +date_fmt:"<?php _e('%Y-%m-%d'); ?>",
    +time_fmt:"<?php _e('%H:%M:%S'); ?>",
    +insertdate_desc:"<?php _e('插入今天日期'); ?>",
    +inserttime_desc:"<?php _e('插入现在时间'); ?>",
    +months_long:"<?php _e('一月,二月,三月,四月,五月,六月,七月,八月,九月,十月,十一月,十二月'); ?>",
    +months_short:"<?php _e('1月,2月,3月,4月,5月,6月,7月,8月,9月,10月,11月,12月'); ?>",
    +day_long:"<?php _e('星期日,星期一,星期二,星期三,星期四,星期五,星期六,星期日'); ?>",
    +day_short:"<?php _e('周日,周一,周二,周三,周四,周五,周六,周日'); ?>"
    +},
    +print:{
    +print_desc:"<?php _e('打印'); ?>"
    +},
    +preview:{
    +preview_desc:"<?php _e('预览'); ?>"
    +},
    +directionality:{
    +ltr_desc:"<?php _e('文字从左到右'); ?>",
    +rtl_desc:"<?php _e('文字从右到左'); ?>"
    +},
    +layer:{
    +insertlayer_desc:"<?php _e('插入层'); ?>",
    +forward_desc:"<?php _e('前移'); ?>",
    +backward_desc:"<?php _e('后移'); ?>",
    +absolute_desc:"<?php _e('切换绝对寻址'); ?>",
    +content:"<?php _e('新增层..'); ?>"
    +},
    +save:{
    +save_desc:"<?php _e('保存'); ?>",
    +cancel_desc:"<?php _e('取消所有变更'); ?>"
    +},
    +nonbreaking:{
    +nonbreaking_desc:"<?php _e('插入非截断的空格符'); ?>"
    +},
    +iespell:{
    +iespell_desc:"<?php _e('执行拼写检查'); ?>",
    +download:"<?php _e('侦测不到ieSpell套件,是否立即安装?'); ?>"
    +},
    +advhr:{
    +advhr_desc:"<?php _e('水平分隔线'); ?>"
    +},
    +emotions:{
    +emotions_desc:"<?php _e('表情'); ?>"
    +},
    +searchreplace:{
    +search_desc:"<?php _e('查找'); ?>",
    +replace_desc:"<?php _e('查找/替换'); ?>"
    +},
    +advimage:{
    +image_desc:"<?php _e('插入/编辑 图片'); ?>"
    +},
    +advlink:{
    +link_desc:"<?php _e('插入/编辑 链接'); ?>"
    +},
    +xhtmlxtras:{
    +cite_desc:"<?php _e('引用'); ?>",
    +abbr_desc:"<?php _e('缩写'); ?>",
    +acronym_desc:"<?php _e('首字母缩写'); ?>",
    +del_desc:"<?php _e('删除'); ?>",
    +ins_desc:"<?php _e('插入'); ?>",
    +attribs_desc:"<?php _e('插入/编辑 属性'); ?>"
    +},
    +style:{
    +desc:"<?php _e('编辑 CSS 样式'); ?>"
    +},
    +paste:{
    +paste_text_desc:"<?php _e('以纯文字格式粘贴'); ?>",
    +paste_word_desc:"<?php _e('从Word粘贴'); ?>",
    +selectall_desc:"<?php _e('全选'); ?>"
    +},
    +paste_dlg:{
    +text_title:"<?php _e('用 Ctrl+V 组合键将文字贴入窗口中。'); ?>",
    +text_linebreaks:"<?php _e('保留换行符号'); ?>",
    +word_title:"<?php _e('用 Ctrl+V 组合键将文字贴入窗口中。'); ?>"
    +},
    +table:{
    +desc:"<?php _e('插入新表格'); ?>",
    +row_before_desc:"<?php _e('插入列于前'); ?>",
    +row_after_desc:"<?php _e('插入列于后'); ?>",
    +delete_row_desc:"<?php _e('删除本列'); ?>",
    +col_before_desc:"<?php _e('插入栏于前'); ?>",
    +col_after_desc:"<?php _e('插入栏于后'); ?>",
    +delete_col_desc:"<?php _e('删除本栏'); ?>",
    +split_cells_desc:"<?php _e('分割储存格'); ?>",
    +merge_cells_desc:"<?php _e('合并储存格'); ?>",
    +row_desc:"<?php _e('表格列 属性'); ?>",
    +cell_desc:"<?php _e('储存格 属性'); ?>",
    +props_desc:"<?php _e('表格 属性'); ?>",
    +paste_row_before_desc:"<?php _e('贴入列于前'); ?>",
    +paste_row_after_desc:"<?php _e('贴入列于后'); ?>",
    +cut_row_desc:"<?php _e('剪切此列'); ?>",
    +copy_row_desc:"<?php _e('复制此列'); ?>",
    +del:"<?php _e('删除表格'); ?>",
    +row:"<?php _e('列'); ?>",
    +col:"<?php _e('栏'); ?>",
    +cell:"<?php _e('储存格'); ?>"
    +},
    +autosave:{
    +unload_msg:"<?php _e('如果离开该页,将导致所有修改全部遗失。'); ?>"
    +},
    +fullscreen:{
    +desc:"<?php _e('切换全屏幕模式'); ?>"
    +},
    +media:{
    +desc:"<?php _e('插入/编辑 媒体文件'); ?>",
    +edit:"<?php _e('编辑 媒体文件'); ?>"
    +},
    +fullpage:{
    +desc:"<?php _e('文档属性'); ?>"
    +},
    +template:{
    +desc:"<?php _e('插入预先定义的模板内容'); ?>"
    +},
    +visualchars:{
    +desc:"<?php _e('可见控制字符 开/关'); ?>"
    +},
    +spellchecker:{
    +desc:"<?php _e('切换拼写检查'); ?>",
    +menu:"<?php _e('拼写检查设定'); ?>",
    +ignore_word:"<?php _e('忽略字'); ?>",
    +ignore_words:"<?php _e('忽略全部'); ?>",
    +langs:"<?php _e('语言'); ?>",
    +wait:"<?php _e('请稍后..'); ?>",
    +sug:"<?php _e('建议'); ?>",
    +no_sug:"<?php _e('无建议'); ?>",
    +no_mpell:"<?php _e('无拼写错误'); ?>"
    +},
    +morebreak:{
    +desc:"<?php _e('插入摘要分割符'); ?>"
    +}}});
    +
    +tinyMCE.addI18n('typecho.advanced',{
    +style_select:"<?php _e('样式'); ?>",
    +font_size:"<?php _e('字体样式'); ?>",
    +fontdefault:"<?php _e('字体'); ?>",
    +block:"<?php _e('格式'); ?>",
    +paragraph:"<?php _e('段落'); ?>",
    +div:"<?php _e('布局'); ?>",
    +address:"<?php _e('地址'); ?>",
    +pre:"<?php _e('原始格式'); ?>",
    +h1:"<?php _e('标题 1 (H1)'); ?>",
    +h2:"<?php _e('标题 2 (H2)'); ?>",
    +h3:"<?php _e('标题 3 (H3)'); ?>",
    +h4:"<?php _e('标题 4 (H4)'); ?>",
    +h5:"<?php _e('标题 5 (H5)'); ?>",
    +h6:"<?php _e('标题 6 (H6)'); ?>",
    +blockquote:"<?php _e('引用'); ?>",
    +code:"<?php _e('代码'); ?>",
    +samp:"<?php _e('程序范例'); ?>",
    +dt:"<?php _e('名词定义'); ?>",
    +dd:"<?php _e('名词解释'); ?>",
    +bold_desc:"<?php _e('粗体 (Ctrl+B)'); ?>",
    +italic_desc:"<?php _e('斜体 (Ctrl+I)'); ?>",
    +underline_desc:"<?php _e('下划线 (Ctrl+U)'); ?>",
    +striketrough_desc:"<?php _e('删除线'); ?>",
    +justifyleft_desc:"<?php _e('居左对齐'); ?>",
    +justifycenter_desc:"<?php _e('居中对齐'); ?>",
    +justifyright_desc:"<?php _e('居右对齐'); ?>",
    +justifyfull_desc:"<?php _e('左右对齐'); ?>",
    +bullist_desc:"<?php _e('无序列表'); ?>",
    +numlist_desc:"<?php _e('有序列表'); ?>",
    +outdent_desc:"<?php _e('减少缩排'); ?>",
    +indent_desc:"<?php _e('增加缩排'); ?>",
    +undo_desc:"<?php _e('撤销 (Ctrl+Z)'); ?>",
    +redo_desc:"<?php _e('重做 (Ctrl+Y)'); ?>",
    +link_desc:"<?php _e('插入/编辑 链接'); ?>",
    +unlink_desc:"<?php _e('取消链接'); ?>",
    +image_desc:"<?php _e('插入/编辑 图片'); ?>",
    +cleanup_desc:"<?php _e('清除冗余代码'); ?>",
    +code_desc:"<?php _e('编辑 HTML 源代码'); ?>",
    +sub_desc:"<?php _e('下标'); ?>",
    +sup_desc:"<?php _e('上标'); ?>",
    +hr_desc:"<?php _e('插入水平分割线'); ?>",
    +removeformat_desc:"<?php _e('清除样式'); ?>",
    +custom1_desc:"<?php _e('在此输入自定义描述'); ?>",
    +forecolor_desc:"<?php _e('选择文本前景色'); ?>",
    +backcolor_desc:"<?php _e('选择文本背景色'); ?>",
    +charmap_desc:"<?php _e('插入自定义符号'); ?>",
    +visualaid_desc:"<?php _e('切换可见/隐藏元素'); ?>",
    +anchor_desc:"<?php _e('插入/编辑 锚点'); ?>",
    +cut_desc:"<?php _e('剪切 (Ctrl+X)'); ?>",
    +copy_desc:"<?php _e('复制 (Ctrl+C)'); ?>",
    +paste_desc:"<?php _e('粘贴 (Ctrl+V)'); ?>",
    +image_props_desc:"<?php _e('图片属性'); ?>",
    +newdocument_desc:"<?php _e('新文件'); ?>",
    +help_desc:"<?php _e('帮助'); ?>",
    +blockquote_desc:"<?php _e('引用'); ?>",
    +clipboard_msg:"<?php _e('复制/剪切/粘贴功能在 Mozilla 和 Firefox 中无法使用。\r\n需要获取更多相关信息?'); ?>",
    +path:"<?php _e('路径'); ?>",
    +newdocument:"<?php _e('你确定要清除所有内容吗?'); ?>",
    +toolbar_focus:"<?php _e('移至工具栏 - Alt+Q, 移至编辑器 - Alt-Z, 移至元素路径 - Alt-X'); ?>",
    +more_colors:"<?php _e('其它更多颜色'); ?>",
    +
    +colorpicker_delta_height: 30,
    +image_delta_height: 30,
    +link_delta_height: -20,
    +link_delta_width: 10
    +});
    +
    +tinyMCE.addI18n('typecho.advanced_dlg',{
    +about_title:"<?php _e('关于TinyMCE'); ?>",
    +about_general:"<?php _e('关于'); ?>",
    +about_help:"<?php _e('帮助'); ?>",
    +about_license:"<?php _e('授权'); ?>",
    +about_plugins:"<?php _e('插件'); ?>",
    +about_plugin:"<?php _e('插件'); ?>",
    +about_author:"<?php _e('作者'); ?>",
    +about_version:"<?php _e('版本'); ?>",
    +about_loaded:"<?php _e('已加载的插件'); ?>",
    +anchor_title:"<?php _e('插入/编辑 锚点'); ?>",
    +anchor_name:"<?php _e('锚点名'); ?>",
    +code_title:"<?php _e('HTML 源代码编辑器'); ?>",
    +code_wordwrap:"<?php _e('自动换行'); ?>",
    +colorpicker_title:"<?php _e('选择颜色'); ?>",
    +colorpicker_picker_tab:"<?php _e('选色器'); ?>",
    +colorpicker_picker_title:"<?php _e('选色器'); ?>",
    +colorpicker_palette_tab:"<?php _e('色盘'); ?>",
    +colorpicker_palette_title:"<?php _e('色盘颜色'); ?>",
    +colorpicker_named_tab:"<?php _e('已指定'); ?>",
    +colorpicker_named_title:"<?php _e('已指定颜色'); ?>",
    +colorpicker_color:"<?php _e('颜色:'); ?>",
    +colorpicker_name:"<?php _e('名称:'); ?>",
    +charmap_title:"<?php _e('选择自定义符号'); ?>",
    +image_title:"<?php _e('插入/编辑图片'); ?>",
    +image_src:"<?php _e('图片地址'); ?>",
    +image_alt:"<?php _e('图片描述'); ?>",
    +image_list:"<?php _e('图片列表'); ?>",
    +image_border:"<?php _e('边框'); ?>",
    +image_dimensions:"<?php _e('尺寸'); ?>",
    +image_vspace:"<?php _e('垂直间距'); ?>",
    +image_hspace:"<?php _e('水平间距'); ?>",
    +image_align:"<?php _e('对齐'); ?>",
    +image_align_baseline:"<?php _e('基线对齐'); ?>",
    +image_align_top:"<?php _e('居上'); ?>",
    +image_align_middle:"<?php _e('居中'); ?>",
    +image_align_bottom:"<?php _e('居下'); ?>",
    +image_align_texttop:"<?php _e('文字上方'); ?>",
    +image_align_textbottom:"<?php _e('文字下方'); ?>",
    +image_align_left:"<?php _e('居左'); ?>",
    +image_align_right:"<?php _e('居右'); ?>",
    +link_title:"<?php _e('插入/编辑链接'); ?>",
    +link_url:"<?php _e('链接地址'); ?>",
    +link_target:"<?php _e('目标'); ?>",
    +link_target_same:"<?php _e('在当前窗口打开链接'); ?>",
    +link_target_blank:"<?php _e('在新窗口打开链接'); ?>",
    +link_titlefield:"<?php _e('标题'); ?>",
    +link_is_email:"<?php _e('你输入的URL似乎是一个email地址,是否要加上前辍字 mailto: ?'); ?>",
    +link_is_external:"<?php _e('你输入的URL似乎是一个外部链接,是否要加上前辍字 http:// ?'); ?>",
    +link_list:"<?php _e('链接列表'); ?>"
    +});
    +
    +tinyMCE.addI18n('typecho.media_dlg',{
    +title:"<?php _e('插入/编辑 媒体文件'); ?>",
    +general:"<?php _e('常规'); ?>",
    +advanced:"<?php _e('高级'); ?>",
    +file:"<?php _e('文件/URL'); ?>",
    +list:"<?php _e('列表'); ?>",
    +size:"<?php _e('尺寸'); ?>",
    +preview:"<?php _e('预览'); ?>",
    +constrain_proportions:"<?php _e('保持比例'); ?>",
    +type:"<?php _e('类型'); ?>",
    +id:"<?php _e('ID'); ?>",
    +name:"<?php _e('名称'); ?>",
    +class_name:"<?php _e('类别'); ?>",
    +vspace:"<?php _e('垂直间距'); ?>",
    +hspace:"<?php _e('水平间距'); ?>",
    +play:"<?php _e('自动播放'); ?>",
    +loop:"<?php _e('循环'); ?>",
    +menu:"<?php _e('显示菜单'); ?>",
    +quality:"<?php _e('质量'); ?>",
    +scale:"<?php _e('缩放'); ?>",
    +align:"<?php _e('对齐'); ?>",
    +salign:"<?php _e('SAlign'); ?>",
    +wmode:"<?php _e('WMode'); ?>",
    +bgcolor:"<?php _e('背景色'); ?>",
    +base:"<?php _e('基底'); ?>",
    +flashvars:"<?php _e('Flash变量'); ?>",
    +liveconnect:"<?php _e('SWLiveConnect'); ?>",
    +autohref:"<?php _e('AutoHREF'); ?>",
    +cache:"<?php _e('缓存'); ?>",
    +hidden:"<?php _e('隐藏'); ?>",
    +controller:"<?php _e('控制台'); ?>",
    +kioskmode:"<?php _e('Kiosk 模式'); ?>",
    +playeveryframe:"<?php _e('逐帧播放'); ?>",
    +targetcache:"<?php _e('目标暂存'); ?>",
    +correction:"<?php _e('修正'); ?>",
    +enablejavascript:"<?php _e('启用 JavaScript'); ?>",
    +starttime:"<?php _e('开始时间'); ?>",
    +endtime:"<?php _e('结束时间'); ?>",
    +href:"<?php _e('Href'); ?>",
    +qtsrcchokespeed:"<?php _e('Choke速度'); ?>",
    +target:"<?php _e('目标'); ?>",
    +volume:"<?php _e('音量'); ?>",
    +autostart:"<?php _e('自动播放'); ?>",
    +enabled:"<?php _e('启用'); ?>",
    +fullscreen:"<?php _e('全屏幕'); ?>",
    +invokeurls:"<?php _e('挂用的URLs'); ?>",
    +mute:"<?php _e('静音'); ?>",
    +stretchtofit:"<?php _e('缩放至适合大小'); ?>",
    +windowlessvideo:"<?php _e('无窗口播放'); ?>",
    +balance:"<?php _e('平衡'); ?>",
    +baseurl:"<?php _e('基底 网址'); ?>",
    +captioningid:"<?php _e('字幕ID'); ?>",
    +currentmarker:"<?php _e('目前标记'); ?>",
    +currentposition:"<?php _e('当前位置'); ?>",
    +defaultframe:"<?php _e('预设帧'); ?>",
    +playcount:"<?php _e('播放次数'); ?>",
    +rate:"<?php _e('码率'); ?>",
    +uimode:"<?php _e('UI 模式'); ?>",
    +flash_options:"<?php _e('Flash 选项'); ?>",
    +qt_options:"<?php _e('Quicktime 选项'); ?>",
    +wmp_options:"<?php _e('Windows Media Player 选项'); ?>",
    +rmp_options:"<?php _e('Real Media Player 选项'); ?>",
    +shockwave_options:"<?php _e('Shockwave 选项'); ?>",
    +autogotourl:"<?php _e('自动转至 URL'); ?>",
    +center:"<?php _e('居中'); ?>",
    +imagestatus:"<?php _e('图片状态'); ?>",
    +maintainaspect:"<?php _e('维持比例'); ?>",
    +nojava:"<?php _e('No java'); ?>",
    +prefetch:"<?php _e('预读'); ?>",
    +shuffle:"<?php _e('随机'); ?>",
    +console:"<?php _e('控制台'); ?>",
    +numloop:"<?php _e('循环次数'); ?>",
    +controls:"<?php _e('控制'); ?>",
    +scriptcallbacks:"<?php _e('Script回传'); ?>",
    +swstretchstyle:"<?php _e('缩放样式'); ?>",
    +swstretchhalign:"<?php _e('缩放至水平对齐'); ?>",
    +swstretchvalign:"<?php _e('缩放至垂直对齐'); ?>",
    +sound:"<?php _e('声音'); ?>",
    +progress:"<?php _e('进度'); ?>",
    +qtsrc:"<?php _e('QT Src'); ?>",
    +qt_stream_warn:"<?php _e('RTSP协议的流资源需要在高级选项中增加QT Src域。 \n您还要补充一个非流的SRC域..'); ?>",
    +align_top:"<?php _e('居顶'); ?>",
    +align_right:"<?php _e('居右'); ?>",
    +align_bottom:"<?php _e('居底'); ?>",
    +align_left:"<?php _e('居左'); ?>",
    +align_center:"<?php _e('居中'); ?>",
    +align_top_left:"<?php _e('居顶左'); ?>",
    +align_top_right:"<?php _e('居顶右'); ?>",
    +align_bottom_left:"<?php _e('居底左'); ?>",
    +align_bottom_right:"<?php _e('居底右'); ?>",
    +flv_options:"<?php _e('Flash 视频选项'); ?>",
    +flv_scalemode:"<?php _e('缩放模式'); ?>",
    +flv_buffer:"<?php _e('缓冲'); ?>",
    +flv_startimage:"<?php _e('启动图片'); ?>",
    +flv_starttime:"<?php _e('启动时间'); ?>",
    +flv_defaultvolume:"<?php _e('预设音量'); ?>",
    +flv_hiddengui:"<?php _e('隐藏GUI'); ?>",
    +flv_autostart:"<?php _e('自动启动'); ?>",
    +flv_loop:"<?php _e('循环'); ?>",
    +flv_showscalemodes:"<?php _e('显示缩放模式'); ?>",
    +flv_smoothvideo:"<?php _e('平滑视图'); ?>",
    +flv_jscallback:"<?php _e('JS 回传'); ?>"
    +});
    +
    +/** offset */
    +tinyMCE.addI18n('typecho.media',{
    +    delta_height:40
    +});
    diff --git a/TinyMCE/tiny_mce/langs/typecho.js b/TinyMCE/tiny_mce/langs/typecho.js
    new file mode 100644
    index 0000000..ad52ac3
    --- /dev/null
    +++ b/TinyMCE/tiny_mce/langs/typecho.js
    @@ -0,0 +1 @@
    +/** nothing to do, just sleep...Zzz... */
    diff --git a/TinyMCE/tiny_mce/plugins/coder/editor_plugin.js b/TinyMCE/tiny_mce/plugins/coder/editor_plugin.js
    new file mode 100644
    index 0000000..7abe85d
    --- /dev/null
    +++ b/TinyMCE/tiny_mce/plugins/coder/editor_plugin.js
    @@ -0,0 +1,79 @@
    +/**
    + * $Id: editor_plugin_src.js 201 2007-02-12 15:56:56Z spocke $
    + *
    + * @author Moxiecode
    + * @copyright Copyright  2004-2008, Moxiecode Systems AB, All rights reserved.
    + */
    +
    +(function() {
    +	tinymce.create('tinymce.plugins.CoderPlugin', {
    +		init : function(ed, url) {
    +            
    +			ed.onClick.add(function(ed, e) {
    +				e = e.target;
    +
    +				if (e.nodeName === 'CODE' || e.nodeName === 'PRE' || e.className.indexOf("typecho-plugin") >= 0)
    +					ed.selection.select(e);
    +			});
    +
    +            
    +			ed.onBeforeSetContent.add(function(ed, o) {
    +				
    +                var _replace = function (g, a, b, c) {
    +                    
    +                    c = c.trim().replace(/( |<|>|\r\n|\r|\n)/g, function (e) {
    +                    
    +                        switch (e) {
    +                        
    +                            case "<":
    +                                return "&lt;";
    +                                
    +                            case ">":
    +                                return "&gt;";
    +                            
    +                            case "\r\n":
    +                            case "\r":
    +                            case "\n":
    +                                return '<br />';
    +                                
    +                            case " ":
    +                                return '&nbsp;';
    +                                
    +                            default:
    +                                return;
    +                        
    +                        }
    +                    
    +                    });
    +                    
    +                    return '<' + a + b + '>' + c + '</' + a + '>';
    +                };
    +                
    +                o.content = o.content.replace(/<(code)([^>]*)>([\s\S]*?)<\/(code)>/ig, _replace);
    +                o.content = o.content.replace(/<(pre)([^>]*)>([\s\S]*?)<\/(pre)>/ig, _replace);
    +			});
    +            
    +            /*
    +			ed.onPostProcess.add(function(ed, o) {
    +				if (o.get) {
    +					o.content = o.content.replace(/<textarea([^>]*)>/ig, '<code$1>');
    +					o.content = o.content.replace(/<\/textarea>/ig, '</code>');
    +                }
    +			});
    +            */
    +		},
    +
    +		getInfo : function() {
    +			return {
    +				longname : 'Coder',
    +				author : 'Typecho Team',
    +				authorurl : 'http://typecho.org',
    +				infourl : 'http://typecho.org',
    +				version : tinymce.majorVersion + "." + tinymce.minorVersion
    +			};
    +		}
    +	});
    +
    +	// Register plugin
    +	tinymce.PluginManager.add('coder', tinymce.plugins.CoderPlugin);
    +})();
    diff --git a/TinyMCE/tiny_mce/plugins/inlinepopups/editor_plugin.js b/TinyMCE/tiny_mce/plugins/inlinepopups/editor_plugin.js
    new file mode 100644
    index 0000000..07ea477
    --- /dev/null
    +++ b/TinyMCE/tiny_mce/plugins/inlinepopups/editor_plugin.js
    @@ -0,0 +1 @@
    +(function(){var d=tinymce.DOM,b=tinymce.dom.Element,a=tinymce.dom.Event,e=tinymce.each,c=tinymce.is;tinymce.create("tinymce.plugins.InlinePopups",{init:function(f,g){f.onBeforeRenderUI.add(function(){f.windowManager=new tinymce.InlineWindowManager(f);d.loadCSS(g+"/skins/"+(f.settings.inlinepopups_skin||"clearlooks2")+"/window.css")})},getInfo:function(){return{longname:"InlinePopups",author:"Moxiecode Systems AB",authorurl:"http://tinymce.moxiecode.com",infourl:"http://wiki.moxiecode.com/index.php/TinyMCE:Plugins/inlinepopups",version:tinymce.majorVersion+"."+tinymce.minorVersion}}});tinymce.create("tinymce.InlineWindowManager:tinymce.WindowManager",{InlineWindowManager:function(f){var g=this;g.parent(f);g.zIndex=300000;g.count=0;g.windows={}},open:function(r,j){var y=this,i,k="",q=y.editor,g=0,s=0,h,m,n,o,l,v,x;r=r||{};j=j||{};if(!r.inline){return y.parent(r,j)}if(!r.type){y.bookmark=q.selection.getBookmark(1)}i=d.uniqueId();h=d.getViewPort();r.width=parseInt(r.width||320);r.height=parseInt(r.height||240)+(tinymce.isIE?8:0);r.min_width=parseInt(r.min_width||150);r.min_height=parseInt(r.min_height||100);r.max_width=parseInt(r.max_width||2000);r.max_height=parseInt(r.max_height||2000);r.left=r.left||Math.round(Math.max(h.x,h.x+(h.w/2)-(r.width/2)));r.top=r.top||Math.round(Math.max(h.y,h.y+(h.h/2)-(r.height/2)));r.movable=r.resizable=true;j.mce_width=r.width;j.mce_height=r.height;j.mce_inline=true;j.mce_window_id=i;j.mce_auto_focus=r.auto_focus;y.features=r;y.params=j;y.onOpen.dispatch(y,r,j);if(r.type){k+=" mceModal";if(r.type){k+=" mce"+r.type.substring(0,1).toUpperCase()+r.type.substring(1)}r.resizable=false}if(r.statusbar){k+=" mceStatusbar"}if(r.resizable){k+=" mceResizable"}if(r.minimizable){k+=" mceMinimizable"}if(r.maximizable){k+=" mceMaximizable"}if(r.movable){k+=" mceMovable"}y._addAll(d.doc.body,["div",{id:i,"class":q.settings.inlinepopups_skin||"clearlooks2",style:"width:100px;height:100px"},["div",{id:i+"_wrapper","class":"mceWrapper"+k},["div",{id:i+"_top","class":"mceTop"},["div",{"class":"mceLeft"}],["div",{"class":"mceCenter"}],["div",{"class":"mceRight"}],["span",{id:i+"_title"},r.title||""]],["div",{id:i+"_middle","class":"mceMiddle"},["div",{id:i+"_left","class":"mceLeft"}],["span",{id:i+"_content"}],["div",{id:i+"_right","class":"mceRight"}]],["div",{id:i+"_bottom","class":"mceBottom"},["div",{"class":"mceLeft"}],["div",{"class":"mceCenter"}],["div",{"class":"mceRight"}],["span",{id:i+"_status"},"Content"]],["a",{"class":"mceMove",tabindex:"-1",href:"javascript:;"}],["a",{"class":"mceMin",tabindex:"-1",href:"javascript:;",onmousedown:"return false;"}],["a",{"class":"mceMax",tabindex:"-1",href:"javascript:;",onmousedown:"return false;"}],["a",{"class":"mceMed",tabindex:"-1",href:"javascript:;",onmousedown:"return false;"}],["a",{"class":"mceClose",tabindex:"-1",href:"javascript:;",onmousedown:"return false;"}],["a",{id:i+"_resize_n","class":"mceResize mceResizeN",tabindex:"-1",href:"javascript:;"}],["a",{id:i+"_resize_s","class":"mceResize mceResizeS",tabindex:"-1",href:"javascript:;"}],["a",{id:i+"_resize_w","class":"mceResize mceResizeW",tabindex:"-1",href:"javascript:;"}],["a",{id:i+"_resize_e","class":"mceResize mceResizeE",tabindex:"-1",href:"javascript:;"}],["a",{id:i+"_resize_nw","class":"mceResize mceResizeNW",tabindex:"-1",href:"javascript:;"}],["a",{id:i+"_resize_ne","class":"mceResize mceResizeNE",tabindex:"-1",href:"javascript:;"}],["a",{id:i+"_resize_sw","class":"mceResize mceResizeSW",tabindex:"-1",href:"javascript:;"}],["a",{id:i+"_resize_se","class":"mceResize mceResizeSE",tabindex:"-1",href:"javascript:;"}]]]);d.setStyles(i,{top:-10000,left:-10000});if(tinymce.isGecko){d.setStyle(i,"overflow","auto")}if(!r.type){g+=d.get(i+"_left").clientWidth;g+=d.get(i+"_right").clientWidth;s+=d.get(i+"_top").clientHeight;s+=d.get(i+"_bottom").clientHeight}d.setStyles(i,{top:r.top,left:r.left,width:r.width+g,height:r.height+s});x=r.url||r.file;if(x){if(tinymce.relaxedDomain){x+=(x.indexOf("?")==-1?"?":"&")+"mce_rdomain="+tinymce.relaxedDomain}x=tinymce._addVer(x)}if(!r.type){d.add(i+"_content","iframe",{id:i+"_ifr",src:'javascript:""',frameBorder:0,style:"border:0;width:10px;height:10px"});d.setStyles(i+"_ifr",{width:r.width,height:r.height});d.setAttrib(i+"_ifr","src",x)}else{d.add(i+"_wrapper","a",{id:i+"_ok","class":"mceButton mceOk",href:"javascript:;",onmousedown:"return false;"},"Ok");if(r.type=="confirm"){d.add(i+"_wrapper","a",{"class":"mceButton mceCancel",href:"javascript:;",onmousedown:"return false;"},"Cancel")}d.add(i+"_middle","div",{"class":"mceIcon"});d.setHTML(i+"_content",r.content.replace("\n","<br />"))}n=a.add(i,"mousedown",function(t){var u=t.target,f,p;f=y.windows[i];y.focus(i);if(u.nodeName=="A"||u.nodeName=="a"){if(u.className=="mceMax"){f.oldPos=f.element.getXY();f.oldSize=f.element.getSize();p=d.getViewPort();p.w-=2;p.h-=2;f.element.moveTo(p.x,p.y);f.element.resizeTo(p.w,p.h);d.setStyles(i+"_ifr",{width:p.w-f.deltaWidth,height:p.h-f.deltaHeight});d.addClass(i+"_wrapper","mceMaximized")}else{if(u.className=="mceMed"){f.element.moveTo(f.oldPos.x,f.oldPos.y);f.element.resizeTo(f.oldSize.w,f.oldSize.h);f.iframeElement.resizeTo(f.oldSize.w-f.deltaWidth,f.oldSize.h-f.deltaHeight);d.removeClass(i+"_wrapper","mceMaximized")}else{if(u.className=="mceMove"){return y._startDrag(i,t,u.className)}else{if(d.hasClass(u,"mceResize")){return y._startDrag(i,t,u.className.substring(13))}}}}}});o=a.add(i,"click",function(f){var p=f.target;y.focus(i);if(p.nodeName=="A"||p.nodeName=="a"){switch(p.className){case"mceClose":y.close(null,i);return a.cancel(f);case"mceButton mceOk":case"mceButton mceCancel":r.button_func(p.className=="mceButton mceOk");return a.cancel(f)}}});v=y.windows[i]={id:i,mousedown_func:n,click_func:o,element:new b(i,{blocker:1,container:q.getContainer()}),iframeElement:new b(i+"_ifr"),features:r,deltaWidth:g,deltaHeight:s};v.iframeElement.on("focus",function(){y.focus(i)});if(y.count==0&&y.editor.getParam("dialog_type","modal")=="modal"){d.add(d.doc.body,"div",{id:"mceModalBlocker","class":(y.editor.settings.inlinepopups_skin||"clearlooks2")+"_modalBlocker",style:{zIndex:y.zIndex-1}});d.show("mceModalBlocker")}else{d.setStyle("mceModalBlocker","z-index",y.zIndex-1)}if(tinymce.isIE6||/Firefox\/2\./.test(navigator.userAgent)||(tinymce.isIE&&!d.boxModel)){d.setStyles("mceModalBlocker",{position:"absolute",left:h.x,top:h.y,width:h.w-2,height:h.h-2})}y.focus(i);y._fixIELayout(i,1);if(d.get(i+"_ok")){d.get(i+"_ok").focus()}y.count++;return v},focus:function(h){var g=this,f;if(f=g.windows[h]){f.zIndex=this.zIndex++;f.element.setStyle("zIndex",f.zIndex);f.element.update();h=h+"_wrapper";d.removeClass(g.lastId,"mceFocus");d.addClass(h,"mceFocus");g.lastId=h}},_addAll:function(k,h){var g,l,f=this,j=tinymce.DOM;if(c(h,"string")){k.appendChild(j.doc.createTextNode(h))}else{if(h.length){k=k.appendChild(j.create(h[0],h[1]));for(g=2;g<h.length;g++){f._addAll(k,h[g])}}}},_startDrag:function(v,G,E){var o=this,u,z,C=d.doc,f,l=o.windows[v],h=l.element,y=h.getXY(),x,q,F,g,A,s,r,j,i,m,k,n,B;g={x:0,y:0};A=d.getViewPort();A.w-=2;A.h-=2;j=G.screenX;i=G.screenY;m=k=n=B=0;u=a.add(C,"mouseup",function(p){a.remove(C,"mouseup",u);a.remove(C,"mousemove",z);if(f){f.remove()}h.moveBy(m,k);h.resizeBy(n,B);q=h.getSize();d.setStyles(v+"_ifr",{width:q.w-l.deltaWidth,height:q.h-l.deltaHeight});o._fixIELayout(v,1);return a.cancel(p)});if(E!="Move"){D()}function D(){if(f){return}o._fixIELayout(v,0);d.add(C.body,"div",{id:"mceEventBlocker","class":"mceEventBlocker "+(o.editor.settings.inlinepopups_skin||"clearlooks2"),style:{zIndex:o.zIndex+1}});if(tinymce.isIE6||(tinymce.isIE&&!d.boxModel)){d.setStyles("mceEventBlocker",{position:"absolute",left:A.x,top:A.y,width:A.w-2,height:A.h-2})}f=new b("mceEventBlocker");f.update();x=h.getXY();q=h.getSize();s=g.x+x.x-A.x;r=g.y+x.y-A.y;d.add(f.get(),"div",{id:"mcePlaceHolder","class":"mcePlaceHolder",style:{left:s,top:r,width:q.w,height:q.h}});F=new b("mcePlaceHolder")}z=a.add(C,"mousemove",function(w){var p,H,t;D();p=w.screenX-j;H=w.screenY-i;switch(E){case"ResizeW":m=p;n=0-p;break;case"ResizeE":n=p;break;case"ResizeN":case"ResizeNW":case"ResizeNE":if(E=="ResizeNW"){m=p;n=0-p}else{if(E=="ResizeNE"){n=p}}k=H;B=0-H;break;case"ResizeS":case"ResizeSW":case"ResizeSE":if(E=="ResizeSW"){m=p;n=0-p}else{if(E=="ResizeSE"){n=p}}B=H;break;case"mceMove":m=p;k=H;break}if(n<(t=l.features.min_width-q.w)){if(m!==0){m+=n-t}n=t}if(B<(t=l.features.min_height-q.h)){if(k!==0){k+=B-t}B=t}n=Math.min(n,l.features.max_width-q.w);B=Math.min(B,l.features.max_height-q.h);m=Math.max(m,A.x-(s+A.x));k=Math.max(k,A.y-(r+A.y));m=Math.min(m,(A.w+A.x)-(s+q.w+A.x));k=Math.min(k,(A.h+A.y)-(r+q.h+A.y));if(m+k!==0){if(s+m<0){m=0}if(r+k<0){k=0}F.moveTo(s+m,r+k)}if(n+B!==0){F.resizeTo(q.w+n,q.h+B)}return a.cancel(w)});return a.cancel(G)},resizeBy:function(g,h,i){var f=this.windows[i];if(f){f.element.resizeBy(g,h);f.iframeElement.resizeBy(g,h)}},close:function(j,l){var h=this,g,k=d.doc,f=0,i,l;l=h._findId(l||j);if(!h.windows[l]){h.parent(j);return}h.count--;if(h.count==0){d.remove("mceModalBlocker")}if(g=h.windows[l]){h.onClose.dispatch(h);a.remove(k,"mousedown",g.mousedownFunc);a.remove(k,"click",g.clickFunc);a.clear(l);a.clear(l+"_ifr");d.setAttrib(l+"_ifr","src",'javascript:""');g.element.remove();delete h.windows[l];e(h.windows,function(m){if(m.zIndex>f){i=m;f=m.zIndex}});if(i){h.focus(i.id)}}},setTitle:function(f,g){var h;f=this._findId(f);if(h=d.get(f+"_title")){h.innerHTML=d.encode(g)}},alert:function(g,f,j){var i=this,h;h=i.open({title:i,type:"alert",button_func:function(k){if(f){f.call(k||i,k)}i.close(null,h.id)},content:d.encode(i.editor.getLang(g,g)),inline:1,width:400,height:130})},confirm:function(g,f,j){var i=this,h;h=i.open({title:i,type:"confirm",button_func:function(k){if(f){f.call(k||i,k)}i.close(null,h.id)},content:d.encode(i.editor.getLang(g,g)),inline:1,width:400,height:130})},_findId:function(f){var g=this;if(typeof(f)=="string"){return f}e(g.windows,function(h){var i=d.get(h.id+"_ifr");if(i&&f==i.contentWindow){f=h.id;return false}});return f},_fixIELayout:function(i,h){var f,g;if(!tinymce.isIE6){return}e(["n","s","w","e","nw","ne","sw","se"],function(j){var k=d.get(i+"_resize_"+j);d.setStyles(k,{width:h?k.clientWidth:"",height:h?k.clientHeight:"",cursor:d.getStyle(k,"cursor",1)});d.setStyle(i+"_bottom","bottom","-1px");k=0});if(f=this.windows[i]){f.element.hide();f.element.show();e(d.select("div,a",i),function(k,j){if(k.currentStyle.backgroundImage!="none"){g=new Image();g.src=k.currentStyle.backgroundImage.replace(/url\(\"(.+)\"\)/,"$1")}});d.get(i).style.filter=""}}});tinymce.PluginManager.add("inlinepopups",tinymce.plugins.InlinePopups)})();
    \ No newline at end of file
    diff --git a/TinyMCE/tiny_mce/plugins/inlinepopups/skins/clearlooks2/img/alert.gif b/TinyMCE/tiny_mce/plugins/inlinepopups/skins/clearlooks2/img/alert.gif
    new file mode 100644
    index 0000000..94abd08
    Binary files /dev/null and b/TinyMCE/tiny_mce/plugins/inlinepopups/skins/clearlooks2/img/alert.gif differ
    diff --git a/TinyMCE/tiny_mce/plugins/inlinepopups/skins/clearlooks2/img/button.gif b/TinyMCE/tiny_mce/plugins/inlinepopups/skins/clearlooks2/img/button.gif
    new file mode 100644
    index 0000000..e671094
    Binary files /dev/null and b/TinyMCE/tiny_mce/plugins/inlinepopups/skins/clearlooks2/img/button.gif differ
    diff --git a/TinyMCE/tiny_mce/plugins/inlinepopups/skins/clearlooks2/img/buttons.gif b/TinyMCE/tiny_mce/plugins/inlinepopups/skins/clearlooks2/img/buttons.gif
    new file mode 100644
    index 0000000..6baf64a
    Binary files /dev/null and b/TinyMCE/tiny_mce/plugins/inlinepopups/skins/clearlooks2/img/buttons.gif differ
    diff --git a/TinyMCE/tiny_mce/plugins/inlinepopups/skins/clearlooks2/img/confirm.gif b/TinyMCE/tiny_mce/plugins/inlinepopups/skins/clearlooks2/img/confirm.gif
    new file mode 100644
    index 0000000..497307a
    Binary files /dev/null and b/TinyMCE/tiny_mce/plugins/inlinepopups/skins/clearlooks2/img/confirm.gif differ
    diff --git a/TinyMCE/tiny_mce/plugins/inlinepopups/skins/clearlooks2/img/corners.gif b/TinyMCE/tiny_mce/plugins/inlinepopups/skins/clearlooks2/img/corners.gif
    new file mode 100644
    index 0000000..c894b2e
    Binary files /dev/null and b/TinyMCE/tiny_mce/plugins/inlinepopups/skins/clearlooks2/img/corners.gif differ
    diff --git a/TinyMCE/tiny_mce/plugins/inlinepopups/skins/clearlooks2/img/horizontal.gif b/TinyMCE/tiny_mce/plugins/inlinepopups/skins/clearlooks2/img/horizontal.gif
    new file mode 100644
    index 0000000..c2a2ad4
    Binary files /dev/null and b/TinyMCE/tiny_mce/plugins/inlinepopups/skins/clearlooks2/img/horizontal.gif differ
    diff --git a/TinyMCE/tiny_mce/plugins/inlinepopups/skins/clearlooks2/img/vertical.gif b/TinyMCE/tiny_mce/plugins/inlinepopups/skins/clearlooks2/img/vertical.gif
    new file mode 100644
    index 0000000..43a735f
    Binary files /dev/null and b/TinyMCE/tiny_mce/plugins/inlinepopups/skins/clearlooks2/img/vertical.gif differ
    diff --git a/TinyMCE/tiny_mce/plugins/inlinepopups/skins/clearlooks2/window.css b/TinyMCE/tiny_mce/plugins/inlinepopups/skins/clearlooks2/window.css
    new file mode 100644
    index 0000000..8e016b6
    --- /dev/null
    +++ b/TinyMCE/tiny_mce/plugins/inlinepopups/skins/clearlooks2/window.css
    @@ -0,0 +1,90 @@
    +/* Clearlooks 2 */
    +
    +/* Reset */
    +.clearlooks2, .clearlooks2 div, .clearlooks2 span, .clearlooks2 a {vertical-align:baseline; text-align:left; position:absolute; border:0; padding:0; margin:0; background:transparent; font-family:"Lucida Grande","Lucida Sans Unicode",Tahoma,Verdana; font-size:11px; color:#000; text-decoration:none; font-weight:normal; width:auto; height:auto; overflow:hidden; display:block}
    +
    +/* General */
    +.clearlooks2 {position:absolute; direction:ltr}
    +.clearlooks2 .mceWrapper {position:static}
    +.mceEventBlocker {position:fixed; left:0; top:0; background:url(img/horizontal.gif) no-repeat 0 -75px; width:100%; height:100%}
    +.clearlooks2 .mcePlaceHolder {border:1px solid #000; background:#888; top:0; left:0; opacity:0.5; -ms-filter:'alpha(opacity=50)'; filter:alpha(opacity=50)}
    +.clearlooks2_modalBlocker {position:fixed; left:0; top:0; width:100%; height:100%; background:#333; opacity:0.6; -ms-filter:'alpha(opacity=60)'; filter:alpha(opacity=60); display:none}
    +
    +/* Top */
    +.clearlooks2 .mceTop, .clearlooks2 .mceTop div {top:0; width:100%; height:30px}
    +.clearlooks2 .mceTop .mceLeft {width:6px; background:#666}
    +.clearlooks2 .mceTop .mceCenter {right:6px; width:100%; height:30px; background:#666; clip:rect(auto auto auto 12px)}
    +.clearlooks2 .mceTop .mceRight {right:0; width:6px; height:30px; background:#666}
    +.clearlooks2 .mceTop span {width:100%; text-align:center; vertical-align:middle; line-height:30px; font-weight:bold; font-size:10pt}
    +.clearlooks2 .mceFocus .mceTop .mceLeft {background:#666}
    +.clearlooks2 .mceFocus .mceTop .mceCenter {background:#666}
    +.clearlooks2 .mceFocus .mceTop .mceRight {background:#666}
    +.clearlooks2 .mceFocus .mceTop span {color:#FFF}
    +
    +/* Middle */
    +.clearlooks2 .mceMiddle, .clearlooks2 .mceMiddle div {top:0}
    +.clearlooks2 .mceMiddle {width:100%; height:100%; clip:rect(30px auto auto auto)}
    +.clearlooks2 .mceMiddle .mceLeft {left:0; width:5px; height:100%; background: #DEE4C5}
    +.clearlooks2 .mceMiddle span {top:30px; left:5px; width:100%; height:100%; background:#FFF}
    +.clearlooks2 .mceMiddle .mceRight {right:0; width:5px; height:100%; background:#DEE4C5}
    +
    +/* Bottom */
    +.clearlooks2 .mceBottom, .clearlooks2 .mceBottom div {height:6px}
    +.clearlooks2 .mceBottom {left:0; bottom:0; width:100%}
    +.clearlooks2 .mceBottom div {top:0}
    +.clearlooks2 .mceBottom .mceLeft {left:0; width:5px; background:url(img/corners.gif) -34px -6px}
    +.clearlooks2 .mceBottom .mceCenter {left:5px; width:100%; background:#DEE4C5}
    +.clearlooks2 .mceBottom .mceRight {right:0; width:5px; background: url(img/corners.gif) -34px 0}
    +.clearlooks2 .mceBottom span {display:none}
    +.clearlooks2 .mceStatusbar .mceBottom, .clearlooks2 .mceStatusbar .mceBottom div {height:30px}
    +.clearlooks2 .mceStatusbar .mceBottom .mceLeft {background:url(img/corners.gif) -29px 0}
    +.clearlooks2 .mceStatusbar .mceBottom .mceCenter {background:url(img/horizontal.gif) 0 -52px}
    +.clearlooks2 .mceStatusbar .mceBottom .mceRight {background:url(img/corners.gif) -24px 0}
    +.clearlooks2 .mceStatusbar .mceBottom span {display:block; left:7px; font-family:Arial, Verdana; font-size:11px; line-height:30px}
    +
    +/* Actions */
    +.clearlooks2 a {width:29px; height:16px; top:7px;}
    +.clearlooks2 .mceClose {right:6px; background:url(img/buttons.gif) -87px 0}
    +.clearlooks2 .mceMin {display:none; right:68px; background:url(img/buttons.gif) 0 0}
    +.clearlooks2 .mceMed {display:none; right:37px; background:url(img/buttons.gif) -29px 0}
    +.clearlooks2 .mceMax {display:none; right:37px; background:url(img/buttons.gif) -58px 0}
    +.clearlooks2 .mceMove {display:none;width:100%;cursor:move;background:url(img/corners.gif) no-repeat -100px -100px}
    +.clearlooks2 .mceMovable .mceMove {display:block}
    +.clearlooks2 .mceFocus .mceClose {right:6px; background:url(img/buttons.gif) -87px 0}
    +.clearlooks2 .mceFocus .mceMin {right:68px; background:url(img/buttons.gif) 0 0}
    +.clearlooks2 .mceFocus .mceMed {right:37px; background:url(img/buttons.gif) -29px 0}
    +.clearlooks2 .mceFocus .mceMax {right:37px; background:url(img/buttons.gif) -58px 0}
    +.clearlooks2 .mceFocus .mceClose:hover {right:6px; background:url(img/buttons.gif) -87px 0}
    +.clearlooks2 .mceFocus .mceClose:hover {right:6px; background:url(img/buttons.gif) -87px 0}
    +.clearlooks2 .mceFocus .mceMin:hover {right:68px; background:url(img/buttons.gif) 0 0}
    +.clearlooks2 .mceFocus .mceMed:hover {right:37px; background:url(img/buttons.gif) -29px 0}
    +.clearlooks2 .mceFocus .mceMax:hover {right:37px; background:url(img/buttons.gif) -58px 0}
    +
    +/* Resize */
    +.clearlooks2 .mceResize {top:auto; left:auto; display:none; width:1px; height:1px; background:#333}
    +.clearlooks2 .mceResizable .mceResize {display:block}
    +.clearlooks2 .mceResizable .mceMin, .clearlooks2 .mceMax {display:none}
    +.clearlooks2 .mceMinimizable .mceMin {display:block}
    +.clearlooks2 .mceMaximizable .mceMax {display:block}
    +.clearlooks2 .mceMaximized .mceMed {display:block}
    +.clearlooks2 .mceMaximized .mceMax {display:none}
    +.clearlooks2 a.mceResizeN {top:0; left:0; width:100%; cursor:n-resize}
    +.clearlooks2 a.mceResizeNW {top:0; left:0; cursor:nw-resize}
    +.clearlooks2 a.mceResizeNE {top:0; right:0; cursor:ne-resize}
    +.clearlooks2 a.mceResizeW {top:0; left:0; height:100%; cursor:w-resize;}
    +.clearlooks2 a.mceResizeE {top:0; right:0; height:100%; cursor:e-resize}
    +.clearlooks2 a.mceResizeS {bottom:0; left:0; width:100%; cursor:s-resize}
    +.clearlooks2 a.mceResizeSW {bottom:0; left:0; cursor:sw-resize}
    +.clearlooks2 a.mceResizeSE {bottom:0; right:0; cursor:se-resize}
    +
    +/* Alert/Confirm */
    +.clearlooks2 .mceButton {font-weight:bold; bottom:10px; width:80px; height:30px; background:url(img/button.gif); line-height:30px; vertical-align:middle; text-align:center; outline:0}
    +.clearlooks2 .mceMiddle .mceIcon {left:15px; top:35px; width:32px; height:32px}
    +.clearlooks2 .mceAlert .mceMiddle span, .clearlooks2 .mceConfirm .mceMiddle span {background:transparent;left:60px; top:35px; width:320px; height:50px; font-weight:bold; overflow:auto; white-space:normal}
    +.clearlooks2 a:hover {font-weight:bold;}
    +.clearlooks2 .mceAlert .mceMiddle, .clearlooks2 .mceConfirm .mceMiddle {background:#D6D7D5}
    +.clearlooks2 .mceAlert .mceOk {left:50%; top:auto; margin-left: -40px}
    +.clearlooks2 .mceAlert .mceIcon {background:url(img/alert.gif)}
    +.clearlooks2 .mceConfirm .mceOk {left:50%; top:auto; margin-left: -90px}
    +.clearlooks2 .mceConfirm .mceCancel {left:50%; top:auto}
    +.clearlooks2 .mceConfirm .mceIcon {background:url(img/confirm.gif)} 
    diff --git a/TinyMCE/tiny_mce/plugins/inlinepopups/template.htm b/TinyMCE/tiny_mce/plugins/inlinepopups/template.htm
    new file mode 100644
    index 0000000..c98fe41
    --- /dev/null
    +++ b/TinyMCE/tiny_mce/plugins/inlinepopups/template.htm
    @@ -0,0 +1,387 @@
    +<!-- <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> -->
    +<html xmlns="http://www.w3.org/1999/xhtml">
    +<head>
    +<title>Template for dialogs</title>
    +<link rel="stylesheet" type="text/css" href="skins/clearlooks2/window.css" />
    +</head>
    +<body>
    +
    +<div class="mceEditor">
    +	<div class="clearlooks2" style="width:400px; height:100px; left:10px;">
    +		<div class="mceWrapper">
    +			<div class="mceTop">
    +				<div class="mceLeft"></div>
    +				<div class="mceCenter"></div>
    +				<div class="mceRight"></div>
    +				<span>Blured</span>
    +			</div>
    +
    +			<div class="mceMiddle">
    +				<div class="mceLeft"></div>
    +				<span>Content</span>
    +				<div class="mceRight"></div>
    +			</div>
    +
    +			<div class="mceBottom">
    +				<div class="mceLeft"></div>
    +				<div class="mceCenter"></div>
    +				<div class="mceRight"></div>
    +				<span>Statusbar text.</span>
    +			</div>
    +
    +			<a class="mceMove" href="#"></a>
    +			<a class="mceMin" href="#"></a>
    +			<a class="mceMax" href="#"></a>
    +			<a class="mceMed" href="#"></a>
    +			<a class="mceClose" href="#"></a>
    +			<a class="mceResize mceResizeN" href="#"></a>
    +			<a class="mceResize mceResizeS" href="#"></a>
    +			<a class="mceResize mceResizeW" href="#"></a>
    +			<a class="mceResize mceResizeE" href="#"></a>
    +			<a class="mceResize mceResizeNW" href="#"></a>
    +			<a class="mceResize mceResizeNE" href="#"></a>
    +			<a class="mceResize mceResizeSW" href="#"></a>
    +			<a class="mceResize mceResizeSE" href="#"></a>
    +		</div>
    +	</div>
    +
    +	<div class="clearlooks2" style="width:400px; height:100px; left:420px;">
    +		<div class="mceWrapper mceMovable mceFocus">
    +			<div class="mceTop">
    +				<div class="mceLeft"></div>
    +				<div class="mceCenter"></div>
    +				<div class="mceRight"></div>
    +				<span>Focused</span>
    +			</div>
    +
    +			<div class="mceMiddle">
    +				<div class="mceLeft"></div>
    +				<span>Content</span>
    +				<div class="mceRight"></div>
    +			</div>
    +
    +			<div class="mceBottom">
    +				<div class="mceLeft"></div>
    +				<div class="mceCenter"></div>
    +				<div class="mceRight"></div>
    +				<span>Statusbar text.</span>
    +			</div>
    +
    +			<a class="mceMove" href="#"></a>
    +			<a class="mceMin" href="#"></a>
    +			<a class="mceMax" href="#"></a>
    +			<a class="mceMed" href="#"></a>
    +			<a class="mceClose" href="#"></a>
    +			<a class="mceResize mceResizeN" href="#"></a>
    +			<a class="mceResize mceResizeS" href="#"></a>
    +			<a class="mceResize mceResizeW" href="#"></a>
    +			<a class="mceResize mceResizeE" href="#"></a>
    +			<a class="mceResize mceResizeNW" href="#"></a>
    +			<a class="mceResize mceResizeNE" href="#"></a>
    +			<a class="mceResize mceResizeSW" href="#"></a>
    +			<a class="mceResize mceResizeSE" href="#"></a>
    +		</div>
    +	</div>
    +
    +	<div class="clearlooks2" style="width:400px; height:100px; left:10px; top:120px;">
    +		<div class="mceWrapper mceMovable mceFocus mceStatusbar">
    +			<div class="mceTop">
    +				<div class="mceLeft"></div>
    +				<div class="mceCenter"></div>
    +				<div class="mceRight"></div>
    +				<span>Statusbar</span>
    +			</div>
    +
    +			<div class="mceMiddle">
    +				<div class="mceLeft"></div>
    +				<span>Content</span>
    +				<div class="mceRight"></div>
    +			</div>
    +
    +			<div class="mceBottom">
    +				<div class="mceLeft"></div>
    +				<div class="mceCenter"></div>
    +				<div class="mceRight"></div>
    +				<span>Statusbar text.</span>
    +			</div>
    +
    +			<a class="mceMove" href="#"></a>
    +			<a class="mceMin" href="#"></a>
    +			<a class="mceMax" href="#"></a>
    +			<a class="mceMed" href="#"></a>
    +			<a class="mceClose" href="#"></a>
    +			<a class="mceResize mceResizeN" href="#"></a>
    +			<a class="mceResize mceResizeS" href="#"></a>
    +			<a class="mceResize mceResizeW" href="#"></a>
    +			<a class="mceResize mceResizeE" href="#"></a>
    +			<a class="mceResize mceResizeNW" href="#"></a>
    +			<a class="mceResize mceResizeNE" href="#"></a>
    +			<a class="mceResize mceResizeSW" href="#"></a>
    +			<a class="mceResize mceResizeSE" href="#"></a>
    +		</div>
    +	</div>
    +
    +	<div class="clearlooks2" style="width:400px; height:100px; left:420px; top:120px;">
    +		<div class="mceWrapper mceMovable mceFocus mceStatusbar mceResizable">
    +			<div class="mceTop">
    +				<div class="mceLeft"></div>
    +				<div class="mceCenter"></div>
    +				<div class="mceRight"></div>
    +				<span>Statusbar, Resizable</span>
    +			</div>
    +
    +			<div class="mceMiddle">
    +				<div class="mceLeft"></div>
    +				<span>Content</span>
    +				<div class="mceRight"></div>
    +			</div>
    +
    +			<div class="mceBottom">
    +				<div class="mceLeft"></div>
    +				<div class="mceCenter"></div>
    +				<div class="mceRight"></div>
    +				<span>Statusbar text.</span>
    +			</div>
    +
    +			<a class="mceMove" href="#"></a>
    +			<a class="mceMin" href="#"></a>
    +			<a class="mceMax" href="#"></a>
    +			<a class="mceMed" href="#"></a>
    +			<a class="mceClose" href="#"></a>
    +			<a class="mceResize mceResizeN" href="#"></a>
    +			<a class="mceResize mceResizeS" href="#"></a>
    +			<a class="mceResize mceResizeW" href="#"></a>
    +			<a class="mceResize mceResizeE" href="#"></a>
    +			<a class="mceResize mceResizeNW" href="#"></a>
    +			<a class="mceResize mceResizeNE" href="#"></a>
    +			<a class="mceResize mceResizeSW" href="#"></a>
    +			<a class="mceResize mceResizeSE" href="#"></a>
    +		</div>
    +	</div>
    +
    +	<div class="clearlooks2" style="width:400px; height:100px; left:10px; top:230px;">
    +		<div class="mceWrapper mceMovable mceFocus mceResizable mceMaximizable">
    +			<div class="mceTop">
    +				<div class="mceLeft"></div>
    +				<div class="mceCenter"></div>
    +				<div class="mceRight"></div>
    +				<span>Resizable, Maximizable</span>
    +			</div>
    +
    +			<div class="mceMiddle">
    +				<div class="mceLeft"></div>
    +				<span>Content</span>
    +				<div class="mceRight"></div>
    +			</div>
    +
    +			<div class="mceBottom">
    +				<div class="mceLeft"></div>
    +				<div class="mceCenter"></div>
    +				<div class="mceRight"></div>
    +				<span>Statusbar text.</span>
    +			</div>
    +
    +			<a class="mceMove" href="#"></a>
    +			<a class="mceMin" href="#"></a>
    +			<a class="mceMax" href="#"></a>
    +			<a class="mceMed" href="#"></a>
    +			<a class="mceClose" href="#"></a>
    +			<a class="mceResize mceResizeN" href="#"></a>
    +			<a class="mceResize mceResizeS" href="#"></a>
    +			<a class="mceResize mceResizeW" href="#"></a>
    +			<a class="mceResize mceResizeE" href="#"></a>
    +			<a class="mceResize mceResizeNW" href="#"></a>
    +			<a class="mceResize mceResizeNE" href="#"></a>
    +			<a class="mceResize mceResizeSW" href="#"></a>
    +			<a class="mceResize mceResizeSE" href="#"></a>
    +		</div>
    +	</div>
    +
    +	<div class="clearlooks2" style="width:400px; height:100px; left:420px; top:230px;">
    +		<div class="mceWrapper mceMovable mceStatusbar mceResizable mceMaximizable">
    +			<div class="mceTop">
    +				<div class="mceLeft"></div>
    +				<div class="mceCenter"></div>
    +				<div class="mceRight"></div>
    +				<span>Blurred, Maximizable, Statusbar, Resizable</span>
    +			</div>
    +
    +			<div class="mceMiddle">
    +				<div class="mceLeft"></div>
    +				<span>Content</span>
    +				<div class="mceRight"></div>
    +			</div>
    +
    +			<div class="mceBottom">
    +				<div class="mceLeft"></div>
    +				<div class="mceCenter"></div>
    +				<div class="mceRight"></div>
    +				<span>Statusbar text.</span>
    +			</div>
    +
    +			<a class="mceMove" href="#"></a>
    +			<a class="mceMin" href="#"></a>
    +			<a class="mceMax" href="#"></a>
    +			<a class="mceMed" href="#"></a>
    +			<a class="mceClose" href="#"></a>
    +			<a class="mceResize mceResizeN" href="#"></a>
    +			<a class="mceResize mceResizeS" href="#"></a>
    +			<a class="mceResize mceResizeW" href="#"></a>
    +			<a class="mceResize mceResizeE" href="#"></a>
    +			<a class="mceResize mceResizeNW" href="#"></a>
    +			<a class="mceResize mceResizeNE" href="#"></a>
    +			<a class="mceResize mceResizeSW" href="#"></a>
    +			<a class="mceResize mceResizeSE" href="#"></a>
    +		</div>
    +	</div>
    +
    +	<div class="clearlooks2" style="width:400px; height:100px; left:10px; top:340px;">
    +		<div class="mceWrapper mceMovable mceFocus mceResizable mceMaximized mceMinimizable mceMaximizable">
    +			<div class="mceTop">
    +				<div class="mceLeft"></div>
    +				<div class="mceCenter"></div>
    +				<div class="mceRight"></div>
    +				<span>Maximized, Maximizable, Minimizable</span>
    +			</div>
    +
    +			<div class="mceMiddle">
    +				<div class="mceLeft"></div>
    +				<span>Content</span>
    +				<div class="mceRight"></div>
    +			</div>
    +
    +			<div class="mceBottom">
    +				<div class="mceLeft"></div>
    +				<div class="mceCenter"></div>
    +				<div class="mceRight"></div>
    +				<span>Statusbar text.</span>
    +			</div>
    +
    +			<a class="mceMove" href="#"></a>
    +			<a class="mceMin" href="#"></a>
    +			<a class="mceMax" href="#"></a>
    +			<a class="mceMed" href="#"></a>
    +			<a class="mceClose" href="#"></a>
    +			<a class="mceResize mceResizeN" href="#"></a>
    +			<a class="mceResize mceResizeS" href="#"></a>
    +			<a class="mceResize mceResizeW" href="#"></a>
    +			<a class="mceResize mceResizeE" href="#"></a>
    +			<a class="mceResize mceResizeNW" href="#"></a>
    +			<a class="mceResize mceResizeNE" href="#"></a>
    +			<a class="mceResize mceResizeSW" href="#"></a>
    +			<a class="mceResize mceResizeSE" href="#"></a>
    +		</div>
    +	</div>
    +
    +	<div class="clearlooks2" style="width:400px; height:100px; left:420px; top:340px;">
    +		<div class="mceWrapper mceMovable mceStatusbar mceResizable mceMaximized mceMinimizable mceMaximizable">
    +			<div class="mceTop">
    +				<div class="mceLeft"></div>
    +				<div class="mceCenter"></div>
    +				<div class="mceRight"></div>
    +				<span>Blured</span>
    +			</div>
    +
    +			<div class="mceMiddle">
    +				<div class="mceLeft"></div>
    +				<span>Content</span>
    +				<div class="mceRight"></div>
    +			</div>
    +
    +			<div class="mceBottom">
    +				<div class="mceLeft"></div>
    +				<div class="mceCenter"></div>
    +				<div class="mceRight"></div>
    +				<span>Statusbar text.</span>
    +			</div>
    +
    +			<a class="mceMove" href="#"></a>
    +			<a class="mceMin" href="#"></a>
    +			<a class="mceMax" href="#"></a>
    +			<a class="mceMed" href="#"></a>
    +			<a class="mceClose" href="#"></a>
    +			<a class="mceResize mceResizeN" href="#"></a>
    +			<a class="mceResize mceResizeS" href="#"></a>
    +			<a class="mceResize mceResizeW" href="#"></a>
    +			<a class="mceResize mceResizeE" href="#"></a>
    +			<a class="mceResize mceResizeNW" href="#"></a>
    +			<a class="mceResize mceResizeNE" href="#"></a>
    +			<a class="mceResize mceResizeSW" href="#"></a>
    +			<a class="mceResize mceResizeSE" href="#"></a>
    +		</div>
    +	</div>
    +
    +	<div class="clearlooks2" style="width:400px; height:130px; left:10px; top:450px;">
    +		<div class="mceWrapper mceMovable mceFocus mceModal mceAlert">
    +			<div class="mceTop">
    +				<div class="mceLeft"></div>
    +				<div class="mceCenter"></div>
    +				<div class="mceRight"></div>
    +				<span>Alert</span>
    +			</div>
    +
    +			<div class="mceMiddle">
    +				<div class="mceLeft"></div>
    +				<span>
    +					This is a very long error message. This is a very long error message.
    +					This is a very long error message. This is a very long error message.
    +					This is a very long error message. This is a very long error message.
    +					This is a very long error message. This is a very long error message.
    +					This is a very long error message. This is a very long error message.
    +					This is a very long error message. This is a very long error message.
    +				</span>
    +				<div class="mceRight"></div>
    +				<div class="mceIcon"></div>
    +			</div>
    +
    +			<div class="mceBottom">
    +				<div class="mceLeft"></div>
    +				<div class="mceCenter"></div>
    +				<div class="mceRight"></div>
    +			</div>
    +
    +			<a class="mceMove" href="#"></a>
    +			<a class="mceButton mceOk" href="#">Ok</a>
    +			<a class="mceClose" href="#"></a>
    +		</div>
    +	</div>
    +
    +	<div class="clearlooks2" style="width:400px; height:130px; left:420px; top:450px;">
    +		<div class="mceWrapper mceMovable mceFocus mceModal mceConfirm">
    +			<div class="mceTop">
    +				<div class="mceLeft"></div>
    +				<div class="mceCenter"></div>
    +				<div class="mceRight"></div>
    +				<span>Confirm</span>
    +			</div>
    +
    +			<div class="mceMiddle">
    +				<div class="mceLeft"></div>
    +				<span>
    +					This is a very long error message. This is a very long error message.
    +					This is a very long error message. This is a very long error message.
    +					This is a very long error message. This is a very long error message.
    +					This is a very long error message. This is a very long error message.
    +					This is a very long error message. This is a very long error message.
    +					This is a very long error message. This is a very long error message.
    +					</span>
    +				<div class="mceRight"></div>
    +				<div class="mceIcon"></div>
    +			</div>
    +
    +			<div class="mceBottom">
    +				<div class="mceLeft"></div>
    +				<div class="mceCenter"></div>
    +				<div class="mceRight"></div>
    +			</div>
    +
    +			<a class="mceMove" href="#"></a>
    +			<a class="mceButton mceOk" href="#">Ok</a>
    +			<a class="mceButton mceCancel" href="#">Cancel</a>
    +			<a class="mceClose" href="#"></a>
    +		</div>
    +	</div>
    +</div>
    +
    +</body>
    +</html>
    diff --git a/TinyMCE/tiny_mce/plugins/media/css/content.css b/TinyMCE/tiny_mce/plugins/media/css/content.css
    new file mode 100644
    index 0000000..7739381
    --- /dev/null
    +++ b/TinyMCE/tiny_mce/plugins/media/css/content.css
    @@ -0,0 +1,6 @@
    +.mceItemFlash, .mceItemShockWave, .mceItemQuickTime, .mceItemWindowsMedia, .mceItemRealMedia {border:1px dotted #cc0000; background-position:center; background-repeat:no-repeat; background-color:#ffffcc;}
    +.mceItemShockWave {background-image: url(../img/shockwave.gif);}
    +.mceItemFlash {background-image:url(../img/flash.gif);}
    +.mceItemQuickTime {background-image:url(../img/quicktime.gif);}
    +.mceItemWindowsMedia {background-image:url(../img/windowsmedia.gif);}
    +.mceItemRealMedia {background-image:url(../img/realmedia.gif);}
    diff --git a/TinyMCE/tiny_mce/plugins/media/css/media.css b/TinyMCE/tiny_mce/plugins/media/css/media.css
    new file mode 100644
    index 0000000..8b36efe
    --- /dev/null
    +++ b/TinyMCE/tiny_mce/plugins/media/css/media.css
    @@ -0,0 +1,16 @@
    +#id, #name, #hspace, #vspace, #class_name, #align {	width: 100px }
    +#hspace, #vspace { width: 50px }
    +#flash_quality, #flash_align, #flash_scale, #flash_salign, #flash_wmode { width: 100px }
    +#flash_base, #flash_flashvars { width: 240px }
    +#width, #height { width: 40px }
    +#src, #media_type { width: 250px }
    +#class { width: 120px }
    +#prev { margin: 0; border: 1px solid black; width: 360px; height: 230px; overflow: auto }
    +.panel_wrapper div.current { height: 420px; overflow: auto }
    +#flash_options, #shockwave_options, #qt_options, #wmp_options, #rmp_options { display: none }
    +.mceAddSelectValue { background-color: #DDDDDD }
    +#qt_starttime, #qt_endtime, #qt_fov, #qt_href, #qt_moveid, #qt_moviename, #qt_node, #qt_pan, #qt_qtsrc, #qt_qtsrcchokespeed, #qt_target, #qt_tilt, #qt_urlsubstituten, #qt_volume { width: 70px }
    +#wmp_balance, #wmp_baseurl, #wmp_captioningid, #wmp_currentmarker, #wmp_currentposition, #wmp_defaultframe, #wmp_playcount, #wmp_rate, #wmp_uimode, #wmp_volume { width: 70px }
    +#rmp_console, #rmp_numloop, #rmp_controls, #rmp_scriptcallbacks { width: 70px }
    +#shockwave_swvolume, #shockwave_swframe, #shockwave_swurl, #shockwave_swstretchvalign, #shockwave_swstretchhalign, #shockwave_swstretchstyle { width: 90px }
    +#qt_qtsrc { width: 200px }
    diff --git a/TinyMCE/tiny_mce/plugins/media/editor_plugin.js b/TinyMCE/tiny_mce/plugins/media/editor_plugin.js
    new file mode 100644
    index 0000000..2889be5
    --- /dev/null
    +++ b/TinyMCE/tiny_mce/plugins/media/editor_plugin.js
    @@ -0,0 +1 @@
    +(function(){var a=tinymce.each;tinymce.create("tinymce.plugins.MediaPlugin",{init:function(b,c){var e=this;e.editor=b;e.url=c;function f(g){return/^(mceItemFlash|mceItemShockWave|mceItemWindowsMedia|mceItemQuickTime|mceItemRealMedia)$/.test(g.className)}b.onPreInit.add(function(){b.serializer.addRules("param[name|value|_mce_value]")});b.addCommand("mceMedia",function(){b.windowManager.open({file:c+"/media.htm",width:430+parseInt(b.getLang("media.delta_width",0)),height:470+parseInt(b.getLang("media.delta_height",0)),inline:1},{plugin_url:c})});b.addButton("media",{title:"media.desc",cmd:"mceMedia"});b.onNodeChange.add(function(h,g,i){g.setActive("media",i.nodeName=="IMG"&&f(i))});b.onInit.add(function(){var g={mceItemFlash:"flash",mceItemShockWave:"shockwave",mceItemWindowsMedia:"windowsmedia",mceItemQuickTime:"quicktime",mceItemRealMedia:"realmedia"};b.selection.onSetContent.add(function(){e._spansToImgs(b.getBody())});b.selection.onBeforeSetContent.add(e._objectsToSpans,e);if(b.settings.content_css!==false){b.dom.loadCSS(c+"/css/content.css")}if(b.theme&&b.theme.onResolveName){b.theme.onResolveName.add(function(h,i){if(i.name=="img"){a(g,function(l,j){if(b.dom.hasClass(i.node,j)){i.name=l;i.title=b.dom.getAttrib(i.node,"title");return false}})}})}if(b&&b.plugins.contextmenu){b.plugins.contextmenu.onContextMenu.add(function(i,h,j){if(j.nodeName=="IMG"&&/mceItem(Flash|ShockWave|WindowsMedia|QuickTime|RealMedia)/.test(j.className)){h.add({title:"media.edit",icon:"media",cmd:"mceMedia"})}})}});b.onBeforeSetContent.add(e._objectsToSpans,e);b.onSetContent.add(function(){e._spansToImgs(b.getBody())});b.onPreProcess.add(function(g,i){var h=g.dom;if(i.set){e._spansToImgs(i.node);a(h.select("IMG",i.node),function(k){var j;if(f(k)){j=e._parse(k.title);h.setAttrib(k,"width",h.getAttrib(k,"width",j.width||100));h.setAttrib(k,"height",h.getAttrib(k,"height",j.height||100))}})}if(i.get){a(h.select("IMG",i.node),function(m){var l,j,k;if(g.getParam("media_use_script")){if(f(m)){m.className=m.className.replace(/mceItem/g,"mceTemp")}return}switch(m.className){case"mceItemFlash":l="d27cdb6e-ae6d-11cf-96b8-444553540000";j="http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=6,0,40,0";k="application/x-shockwave-flash";break;case"mceItemShockWave":l="166b1bca-3f9c-11cf-8075-444553540000";j="http://download.macromedia.com/pub/shockwave/cabs/director/sw.cab#version=8,5,1,0";k="application/x-director";break;case"mceItemWindowsMedia":l=g.getParam("media_wmp6_compatible")?"05589fa1-c356-11ce-bf01-00aa0055595a":"6bf52a52-394a-11d3-b153-00c04f79faa6";j="http://activex.microsoft.com/activex/controls/mplayer/en/nsmp2inf.cab#Version=5,1,52,701";k="application/x-mplayer2";break;case"mceItemQuickTime":l="02bf25d5-8c17-4b23-bc80-d3488abddc6b";j="http://www.apple.com/qtactivex/qtplugin.cab#version=6,0,2,0";k="video/quicktime";break;case"mceItemRealMedia":l="cfcdaa03-8be4-11cf-b84b-0020afbbccfa";j="http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=6,0,40,0";k="audio/x-pn-realaudio-plugin";break}if(l){h.replace(e._buildObj({classid:l,codebase:j,type:k},m),m)}})}});b.onPostProcess.add(function(g,h){h.content=h.content.replace(/_mce_value=/g,"value=")});function d(g,h){h=new RegExp(h+'="([^"]+)"',"g").exec(g);return h?b.dom.decode(h[1]):""}b.onPostProcess.add(function(g,h){if(g.getParam("media_use_script")){h.content=h.content.replace(/<img[^>]+>/g,function(j){var i=d(j,"class");if(/^(mceTempFlash|mceTempShockWave|mceTempWindowsMedia|mceTempQuickTime|mceTempRealMedia)$/.test(i)){at=e._parse(d(j,"title"));at.width=d(j,"width");at.height=d(j,"height");j='<script type="text/javascript">write'+i.substring(7)+"({"+e._serialize(at)+"});<\/script>"}return j})}})},getInfo:function(){return{longname:"Media",author:"Moxiecode Systems AB",authorurl:"http://tinymce.moxiecode.com",infourl:"http://wiki.moxiecode.com/index.php/TinyMCE:Plugins/media",version:tinymce.majorVersion+"."+tinymce.minorVersion}},_objectsToSpans:function(b,e){var c=this,d=e.content;d=d.replace(/<script[^>]*>\s*write(Flash|ShockWave|WindowsMedia|QuickTime|RealMedia)\(\{([^\)]*)\}\);\s*<\/script>/gi,function(g,f,i){var h=c._parse(i);return'<img class="mceItem'+f+'" title="'+b.dom.encode(i)+'" src="'+c.url+'/img/trans.gif" width="'+h.width+'" height="'+h.height+'" />'});d=d.replace(/<object([^>]*)>/gi,'<span class="mceItemObject" $1>');d=d.replace(/<embed([^>]*)\/?>/gi,'<span class="mceItemEmbed" $1></span>');d=d.replace(/<embed([^>]*)>/gi,'<span class="mceItemEmbed" $1>');d=d.replace(/<\/(object)([^>]*)>/gi,"</span>");d=d.replace(/<\/embed>/gi,"");d=d.replace(/<param([^>]*)>/gi,function(g,f){return"<span "+f.replace(/value=/gi,"_mce_value=")+' class="mceItemParam"></span>'});d=d.replace(/\/ class=\"mceItemParam\"><\/span>/gi,'class="mceItemParam"></span>');e.content=d},_buildObj:function(g,h){var d,c=this.editor,f=c.dom,e=this._parse(h.title),b;b=c.getParam("media_strict",true)&&g.type=="application/x-shockwave-flash";e.width=g.width=f.getAttrib(h,"width")||100;e.height=g.height=f.getAttrib(h,"height")||100;if(e.src){e.src=c.convertURL(e.src,"src",h)}if(b){d=f.create("span",{id:e.id,mce_name:"object",type:"application/x-shockwave-flash",data:e.src,style:f.getAttrib(h,"style"),width:g.width,height:g.height})}else{d=f.create("span",{id:e.id,mce_name:"object",classid:"clsid:"+g.classid,style:f.getAttrib(h,"style"),codebase:g.codebase,width:g.width,height:g.height})}a(e,function(j,i){if(!/^(width|height|codebase|classid|id|_cx|_cy)$/.test(i)){if(g.type=="application/x-mplayer2"&&i=="src"&&!e.url){i="url"}if(j){f.add(d,"span",{mce_name:"param",name:i,_mce_value:j})}}});if(!b){f.add(d,"span",tinymce.extend({mce_name:"embed",type:g.type,style:f.getAttrib(h,"style")},e))}return d},_spansToImgs:function(e){var d=this,f=d.editor.dom,b,c;a(f.select("span",e),function(g){if(f.getAttrib(g,"class")=="mceItemObject"){c=f.getAttrib(g,"classid").toLowerCase().replace(/\s+/g,"");switch(c){case"clsid:d27cdb6e-ae6d-11cf-96b8-444553540000":f.replace(d._createImg("mceItemFlash",g),g);break;case"clsid:166b1bca-3f9c-11cf-8075-444553540000":f.replace(d._createImg("mceItemShockWave",g),g);break;case"clsid:6bf52a52-394a-11d3-b153-00c04f79faa6":case"clsid:22d6f312-b0f6-11d0-94ab-0080c74c7e95":case"clsid:05589fa1-c356-11ce-bf01-00aa0055595a":f.replace(d._createImg("mceItemWindowsMedia",g),g);break;case"clsid:02bf25d5-8c17-4b23-bc80-d3488abddc6b":f.replace(d._createImg("mceItemQuickTime",g),g);break;case"clsid:cfcdaa03-8be4-11cf-b84b-0020afbbccfa":f.replace(d._createImg("mceItemRealMedia",g),g);break;default:f.replace(d._createImg("mceItemFlash",g),g)}return}if(f.getAttrib(g,"class")=="mceItemEmbed"){switch(f.getAttrib(g,"type")){case"application/x-shockwave-flash":f.replace(d._createImg("mceItemFlash",g),g);break;case"application/x-director":f.replace(d._createImg("mceItemShockWave",g),g);break;case"application/x-mplayer2":f.replace(d._createImg("mceItemWindowsMedia",g),g);break;case"video/quicktime":f.replace(d._createImg("mceItemQuickTime",g),g);break;case"audio/x-pn-realaudio-plugin":f.replace(d._createImg("mceItemRealMedia",g),g);break;default:f.replace(d._createImg("mceItemFlash",g),g)}}})},_createImg:function(c,h){var b,g=this.editor.dom,f={},e="",d;d=["id","name","width","height","bgcolor","align","flashvars","src","wmode","allowfullscreen","quality","data"];b=g.create("img",{src:this.url+"/img/trans.gif",width:g.getAttrib(h,"width")||100,height:g.getAttrib(h,"height")||100,style:g.getAttrib(h,"style"),"class":c});a(d,function(i){var j=g.getAttrib(h,i);if(j){f[i]=j}});a(g.select("span",h),function(i){if(g.hasClass(i,"mceItemParam")){f[g.getAttrib(i,"name")]=g.getAttrib(i,"_mce_value")}});if(f.movie){f.src=f.movie;delete f.movie}if(!f.src){f.src=f.data;delete f.data}h=g.select(".mceItemEmbed",h)[0];if(h){a(d,function(i){var j=g.getAttrib(h,i);if(j&&!f[i]){f[i]=j}})}delete f.width;delete f.height;b.title=this._serialize(f);return b},_parse:function(b){return tinymce.util.JSON.parse("{"+b+"}")},_serialize:function(b){return tinymce.util.JSON.serialize(b).replace(/[{}]/g,"")}});tinymce.PluginManager.add("media",tinymce.plugins.MediaPlugin)})();
    \ No newline at end of file
    diff --git a/TinyMCE/tiny_mce/plugins/media/img/flash.gif b/TinyMCE/tiny_mce/plugins/media/img/flash.gif
    new file mode 100644
    index 0000000..cb192e6
    Binary files /dev/null and b/TinyMCE/tiny_mce/plugins/media/img/flash.gif differ
    diff --git a/TinyMCE/tiny_mce/plugins/media/img/flv_player.swf b/TinyMCE/tiny_mce/plugins/media/img/flv_player.swf
    new file mode 100644
    index 0000000..042c2ab
    Binary files /dev/null and b/TinyMCE/tiny_mce/plugins/media/img/flv_player.swf differ
    diff --git a/TinyMCE/tiny_mce/plugins/media/img/quicktime.gif b/TinyMCE/tiny_mce/plugins/media/img/quicktime.gif
    new file mode 100644
    index 0000000..3b04991
    Binary files /dev/null and b/TinyMCE/tiny_mce/plugins/media/img/quicktime.gif differ
    diff --git a/TinyMCE/tiny_mce/plugins/media/img/realmedia.gif b/TinyMCE/tiny_mce/plugins/media/img/realmedia.gif
    new file mode 100644
    index 0000000..fdfe0b9
    Binary files /dev/null and b/TinyMCE/tiny_mce/plugins/media/img/realmedia.gif differ
    diff --git a/TinyMCE/tiny_mce/plugins/media/img/shockwave.gif b/TinyMCE/tiny_mce/plugins/media/img/shockwave.gif
    new file mode 100644
    index 0000000..5f235df
    Binary files /dev/null and b/TinyMCE/tiny_mce/plugins/media/img/shockwave.gif differ
    diff --git a/TinyMCE/tiny_mce/plugins/media/img/trans.gif b/TinyMCE/tiny_mce/plugins/media/img/trans.gif
    new file mode 100644
    index 0000000..3884865
    Binary files /dev/null and b/TinyMCE/tiny_mce/plugins/media/img/trans.gif differ
    diff --git a/TinyMCE/tiny_mce/plugins/media/img/windowsmedia.gif b/TinyMCE/tiny_mce/plugins/media/img/windowsmedia.gif
    new file mode 100644
    index 0000000..ab50f2d
    Binary files /dev/null and b/TinyMCE/tiny_mce/plugins/media/img/windowsmedia.gif differ
    diff --git a/TinyMCE/tiny_mce/plugins/media/js/embed.js b/TinyMCE/tiny_mce/plugins/media/js/embed.js
    new file mode 100644
    index 0000000..6fe25de
    --- /dev/null
    +++ b/TinyMCE/tiny_mce/plugins/media/js/embed.js
    @@ -0,0 +1,73 @@
    +/**
    + * This script contains embed functions for common plugins. This scripts are complety free to use for any purpose.
    + */
    +
    +function writeFlash(p) {
    +	writeEmbed(
    +		'D27CDB6E-AE6D-11cf-96B8-444553540000',
    +		'http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=6,0,40,0',
    +		'application/x-shockwave-flash',
    +		p
    +	);
    +}
    +
    +function writeShockWave(p) {
    +	writeEmbed(
    +	'166B1BCA-3F9C-11CF-8075-444553540000',
    +	'http://download.macromedia.com/pub/shockwave/cabs/director/sw.cab#version=8,5,1,0',
    +	'application/x-director',
    +		p
    +	);
    +}
    +
    +function writeQuickTime(p) {
    +	writeEmbed(
    +		'02BF25D5-8C17-4B23-BC80-D3488ABDDC6B',
    +		'http://www.apple.com/qtactivex/qtplugin.cab#version=6,0,2,0',
    +		'video/quicktime',
    +		p
    +	);
    +}
    +
    +function writeRealMedia(p) {
    +	writeEmbed(
    +		'CFCDAA03-8BE4-11cf-B84B-0020AFBBCCFA',
    +		'http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=6,0,40,0',
    +		'audio/x-pn-realaudio-plugin',
    +		p
    +	);
    +}
    +
    +function writeWindowsMedia(p) {
    +	p.url = p.src;
    +	writeEmbed(
    +		'6BF52A52-394A-11D3-B153-00C04F79FAA6',
    +		'http://activex.microsoft.com/activex/controls/mplayer/en/nsmp2inf.cab#Version=5,1,52,701',
    +		'application/x-mplayer2',
    +		p
    +	);
    +}
    +
    +function writeEmbed(cls, cb, mt, p) {
    +	var h = '', n;
    +
    +	h += '<object classid="clsid:' + cls + '" codebase="' + cb + '"';
    +	h += typeof(p.id) != "undefined" ? 'id="' + p.id + '"' : '';
    +	h += typeof(p.name) != "undefined" ? 'name="' + p.name + '"' : '';
    +	h += typeof(p.width) != "undefined" ? 'width="' + p.width + '"' : '';
    +	h += typeof(p.height) != "undefined" ? 'height="' + p.height + '"' : '';
    +	h += typeof(p.align) != "undefined" ? 'align="' + p.align + '"' : '';
    +	h += '>';
    +
    +	for (n in p)
    +		h += '<param name="' + n + '" value="' + p[n] + '">';
    +
    +	h += '<embed type="' + mt + '"';
    +
    +	for (n in p)
    +		h += n + '="' + p[n] + '" ';
    +
    +	h += '></embed></object>';
    +
    +	document.write(h);
    +}
    diff --git a/TinyMCE/tiny_mce/plugins/media/js/media.js b/TinyMCE/tiny_mce/plugins/media/js/media.js
    new file mode 100644
    index 0000000..cde592e
    --- /dev/null
    +++ b/TinyMCE/tiny_mce/plugins/media/js/media.js
    @@ -0,0 +1,630 @@
    +tinyMCEPopup.requireLangPack();
    +
    +var oldWidth, oldHeight, ed, url;
    +
    +if (url = tinyMCEPopup.getParam("media_external_list_url"))
    +	document.write('<script language="javascript" type="text/javascript" src="' + tinyMCEPopup.editor.documentBaseURI.toAbsolute(url) + '"></script>');
    +
    +function init() {
    +	var pl = "", f, val;
    +	var type = "flash", fe, i;
    +
    +	ed = tinyMCEPopup.editor;
    +
    +	tinyMCEPopup.resizeToInnerSize();
    +	f = document.forms[0]
    +
    +	fe = ed.selection.getNode();
    +	if (/mceItem(Flash|ShockWave|WindowsMedia|QuickTime|RealMedia)/.test(ed.dom.getAttrib(fe, 'class'))) {
    +		pl = fe.title;
    +
    +		switch (ed.dom.getAttrib(fe, 'class')) {
    +			case 'mceItemFlash':
    +				type = 'flash';
    +				break;
    +
    +			case 'mceItemFlashVideo':
    +				type = 'flv';
    +				break;
    +
    +			case 'mceItemShockWave':
    +				type = 'shockwave';
    +				break;
    +
    +			case 'mceItemWindowsMedia':
    +				type = 'wmp';
    +				break;
    +
    +			case 'mceItemQuickTime':
    +				type = 'qt';
    +				break;
    +
    +			case 'mceItemRealMedia':
    +				type = 'rmp';
    +				break;
    +		}
    +
    +		document.forms[0].insert.value = ed.getLang('update', 'Insert', true); 
    +	}
    +
    +	document.getElementById('filebrowsercontainer').innerHTML = getBrowserHTML('filebrowser','src','media','media');
    +	document.getElementById('qtsrcfilebrowsercontainer').innerHTML = getBrowserHTML('qtsrcfilebrowser','qt_qtsrc','media','media');
    +	document.getElementById('bgcolor_pickcontainer').innerHTML = getColorPickerHTML('bgcolor_pick','bgcolor');
    +
    +	var html = getMediaListHTML('medialist','src','media','media');
    +	if (html == "")
    +		document.getElementById("linklistrow").style.display = 'none';
    +	else
    +		document.getElementById("linklistcontainer").innerHTML = html;
    +
    +	// Resize some elements
    +	if (isVisible('filebrowser'))
    +		document.getElementById('src').style.width = '230px';
    +
    +	// Setup form
    +	if (pl != "") {
    +		pl = tinyMCEPopup.editor.plugins.media._parse(pl);
    +
    +		switch (type) {
    +			case "flash":
    +				setBool(pl, 'flash', 'play');
    +				setBool(pl, 'flash', 'loop');
    +				setBool(pl, 'flash', 'menu');
    +				setBool(pl, 'flash', 'swliveconnect');
    +				setStr(pl, 'flash', 'quality');
    +				setStr(pl, 'flash', 'scale');
    +				setStr(pl, 'flash', 'salign');
    +				setStr(pl, 'flash', 'wmode');
    +				setStr(pl, 'flash', 'base');
    +				setStr(pl, 'flash', 'flashvars');
    +			break;
    +
    +			case "qt":
    +				setBool(pl, 'qt', 'loop');
    +				setBool(pl, 'qt', 'autoplay');
    +				setBool(pl, 'qt', 'cache');
    +				setBool(pl, 'qt', 'controller');
    +				setBool(pl, 'qt', 'correction');
    +				setBool(pl, 'qt', 'enablejavascript');
    +				setBool(pl, 'qt', 'kioskmode');
    +				setBool(pl, 'qt', 'autohref');
    +				setBool(pl, 'qt', 'playeveryframe');
    +				setBool(pl, 'qt', 'tarsetcache');
    +				setStr(pl, 'qt', 'scale');
    +				setStr(pl, 'qt', 'starttime');
    +				setStr(pl, 'qt', 'endtime');
    +				setStr(pl, 'qt', 'tarset');
    +				setStr(pl, 'qt', 'qtsrcchokespeed');
    +				setStr(pl, 'qt', 'volume');
    +				setStr(pl, 'qt', 'qtsrc');
    +			break;
    +
    +			case "shockwave":
    +				setBool(pl, 'shockwave', 'sound');
    +				setBool(pl, 'shockwave', 'progress');
    +				setBool(pl, 'shockwave', 'autostart');
    +				setBool(pl, 'shockwave', 'swliveconnect');
    +				setStr(pl, 'shockwave', 'swvolume');
    +				setStr(pl, 'shockwave', 'swstretchstyle');
    +				setStr(pl, 'shockwave', 'swstretchhalign');
    +				setStr(pl, 'shockwave', 'swstretchvalign');
    +			break;
    +
    +			case "wmp":
    +				setBool(pl, 'wmp', 'autostart');
    +				setBool(pl, 'wmp', 'enabled');
    +				setBool(pl, 'wmp', 'enablecontextmenu');
    +				setBool(pl, 'wmp', 'fullscreen');
    +				setBool(pl, 'wmp', 'invokeurls');
    +				setBool(pl, 'wmp', 'mute');
    +				setBool(pl, 'wmp', 'stretchtofit');
    +				setBool(pl, 'wmp', 'windowlessvideo');
    +				setStr(pl, 'wmp', 'balance');
    +				setStr(pl, 'wmp', 'baseurl');
    +				setStr(pl, 'wmp', 'captioningid');
    +				setStr(pl, 'wmp', 'currentmarker');
    +				setStr(pl, 'wmp', 'currentposition');
    +				setStr(pl, 'wmp', 'defaultframe');
    +				setStr(pl, 'wmp', 'playcount');
    +				setStr(pl, 'wmp', 'rate');
    +				setStr(pl, 'wmp', 'uimode');
    +				setStr(pl, 'wmp', 'volume');
    +			break;
    +
    +			case "rmp":
    +				setBool(pl, 'rmp', 'autostart');
    +				setBool(pl, 'rmp', 'loop');
    +				setBool(pl, 'rmp', 'autogotourl');
    +				setBool(pl, 'rmp', 'center');
    +				setBool(pl, 'rmp', 'imagestatus');
    +				setBool(pl, 'rmp', 'maintainaspect');
    +				setBool(pl, 'rmp', 'nojava');
    +				setBool(pl, 'rmp', 'prefetch');
    +				setBool(pl, 'rmp', 'shuffle');
    +				setStr(pl, 'rmp', 'console');
    +				setStr(pl, 'rmp', 'controls');
    +				setStr(pl, 'rmp', 'numloop');
    +				setStr(pl, 'rmp', 'scriptcallbacks');
    +			break;
    +		}
    +
    +		setStr(pl, null, 'src');
    +		setStr(pl, null, 'id');
    +		setStr(pl, null, 'name');
    +		setStr(pl, null, 'vspace');
    +		setStr(pl, null, 'hspace');
    +		setStr(pl, null, 'bgcolor');
    +		setStr(pl, null, 'align');
    +		setStr(pl, null, 'width');
    +		setStr(pl, null, 'height');
    +
    +		if ((val = ed.dom.getAttrib(fe, "width")) != "")
    +			pl.width = f.width.value = val;
    +
    +		if ((val = ed.dom.getAttrib(fe, "height")) != "")
    +			pl.height = f.height.value = val;
    +
    +		oldWidth = pl.width ? parseInt(pl.width) : 0;
    +		oldHeight = pl.height ? parseInt(pl.height) : 0;
    +	} else
    +		oldWidth = oldHeight = 0;
    +
    +	selectByValue(f, 'media_type', type);
    +	changedType(type);
    +	updateColor('bgcolor_pick', 'bgcolor');
    +
    +	TinyMCE_EditableSelects.init();
    +	generatePreview();
    +}
    +
    +function insertMedia() {
    +	var fe, f = document.forms[0], h;
    +
    +	tinyMCEPopup.restoreSelection();
    +
    +	if (!AutoValidator.validate(f)) {
    +		tinyMCEPopup.alert(ed.getLang('invalid_data'));
    +		return false;
    +	}
    +
    +	f.width.value = f.width.value == "" ? 100 : f.width.value;
    +	f.height.value = f.height.value == "" ? 100 : f.height.value;
    +
    +	fe = ed.selection.getNode();
    +	if (fe != null && /mceItem(Flash|ShockWave|WindowsMedia|QuickTime|RealMedia)/.test(ed.dom.getAttrib(fe, 'class'))) {
    +		switch (f.media_type.options[f.media_type.selectedIndex].value) {
    +			case "flash":
    +				fe.className = "mceItemFlash";
    +				break;
    +
    +			case "flv":
    +				fe.className = "mceItemFlashVideo";
    +				break;
    +
    +			case "shockwave":
    +				fe.className = "mceItemShockWave";
    +				break;
    +
    +			case "qt":
    +				fe.className = "mceItemQuickTime";
    +				break;
    +
    +			case "wmp":
    +				fe.className = "mceItemWindowsMedia";
    +				break;
    +
    +			case "rmp":
    +				fe.className = "mceItemRealMedia";
    +				break;
    +		}
    +
    +		if (fe.width != f.width.value || fe.height != f.height.value)
    +			ed.execCommand('mceRepaint');
    +
    +		fe.title = serializeParameters();
    +		fe.width = f.width.value;
    +		fe.height = f.height.value;
    +		fe.style.width = f.width.value + (f.width.value.indexOf('%') == -1 ? 'px' : '');
    +		fe.style.height = f.height.value + (f.height.value.indexOf('%') == -1 ? 'px' : '');
    +		fe.align = f.align.options[f.align.selectedIndex].value;
    +	} else {
    +		h = '<img src="' + tinyMCEPopup.getWindowArg("plugin_url") + '/img/trans.gif"' ;
    +
    +		switch (f.media_type.options[f.media_type.selectedIndex].value) {
    +			case "flash":
    +				h += ' class="mceItemFlash"';
    +				break;
    +
    +			case "flv":
    +				h += ' class="mceItemFlashVideo"';
    +				break;
    +
    +			case "shockwave":
    +				h += ' class="mceItemShockWave"';
    +				break;
    +
    +			case "qt":
    +				h += ' class="mceItemQuickTime"';
    +				break;
    +
    +			case "wmp":
    +				h += ' class="mceItemWindowsMedia"';
    +				break;
    +
    +			case "rmp":
    +				h += ' class="mceItemRealMedia"';
    +				break;
    +		}
    +
    +		h += ' title="' + serializeParameters() + '"';
    +		h += ' width="' + f.width.value + '"';
    +		h += ' height="' + f.height.value + '"';
    +		h += ' align="' + f.align.options[f.align.selectedIndex].value + '"';
    +
    +		h += ' />';
    +
    +		ed.execCommand('mceInsertContent', false, h);
    +	}
    +
    +	tinyMCEPopup.close();
    +}
    +
    +function updatePreview() {
    +	var f = document.forms[0], type;
    +
    +	f.width.value = f.width.value || '320';
    +	f.height.value = f.height.value || '240';
    +
    +	type = getType(f.src.value);
    +	selectByValue(f, 'media_type', type);
    +	changedType(type);
    +	generatePreview();
    +}
    +
    +function getMediaListHTML() {
    +	if (typeof(tinyMCEMediaList) != "undefined" && tinyMCEMediaList.length > 0) {
    +		var html = "";
    +
    +		html += '<select id="linklist" name="linklist" style="width: 250px" onchange="this.form.src.value=this.options[this.selectedIndex].value;updatePreview();">';
    +		html += '<option value="">---</option>';
    +
    +		for (var i=0; i<tinyMCEMediaList.length; i++)
    +			html += '<option value="' + tinyMCEMediaList[i][1] + '">' + tinyMCEMediaList[i][0] + '</option>';
    +
    +		html += '</select>';
    +
    +		return html;
    +	}
    +
    +	return "";
    +}
    +
    +function getType(v) {
    +	var fo, i, c, el, x, f = document.forms[0];
    +
    +	fo = ed.getParam("media_types", "flash=swf;flv=flv;shockwave=dcr;qt=mov,qt,mpg,mp3,mp4,mpeg;shockwave=dcr;wmp=avi,wmv,wm,asf,asx,wmx,wvx;rmp=rm,ra,ram").split(';');
    +
    +	// YouTube
    +	if (v.match(/watch\?v=(.+)(.*)/)) {
    +		f.width.value = '425';
    +		f.height.value = '350';
    +		f.src.value = 'http://www.youtube.com/v/' + v.match(/v=(.*)(.*)/)[0].split('=')[1];
    +		return 'flash';
    +	}
    +
    +	// Google video
    +	if (v.indexOf('http://video.google.com/videoplay?docid=') == 0) {
    +		f.width.value = '425';
    +		f.height.value = '326';
    +		f.src.value = 'http://video.google.com/googleplayer.swf?docId=' + v.substring('http://video.google.com/videoplay?docid='.length) + '&hl=en';
    +		return 'flash';
    +	}
    +
    +	for (i=0; i<fo.length; i++) {
    +		c = fo[i].split('=');
    +
    +		el = c[1].split(',');
    +		for (x=0; x<el.length; x++)
    +		if (v.indexOf('.' + el[x]) != -1)
    +			return c[0];
    +	}
    +
    +	return null;
    +}
    +
    +function switchType(v) {
    +	var t = getType(v), d = document, f = d.forms[0];
    +
    +	if (!t)
    +		return;
    +
    +	selectByValue(d.forms[0], 'media_type', t);
    +	changedType(t);
    +
    +	// Update qtsrc also
    +	if (t == 'qt' && f.src.value.toLowerCase().indexOf('rtsp://') != -1) {
    +		alert(ed.getLang("media_qt_stream_warn"));
    +
    +		if (f.qt_qtsrc.value == '')
    +			f.qt_qtsrc.value = f.src.value;
    +	}
    +}
    +
    +function changedType(t) {
    +	var d = document;
    +
    +	d.getElementById('flash_options').style.display = 'none';
    +	d.getElementById('flv_options').style.display = 'none';
    +	d.getElementById('qt_options').style.display = 'none';
    +	d.getElementById('shockwave_options').style.display = 'none';
    +	d.getElementById('wmp_options').style.display = 'none';
    +	d.getElementById('rmp_options').style.display = 'none';
    +
    +	if (t)
    +		d.getElementById(t + '_options').style.display = 'block';
    +}
    +
    +function serializeParameters() {
    +	var d = document, f = d.forms[0], s = '';
    +
    +	switch (f.media_type.options[f.media_type.selectedIndex].value) {
    +		case "flash":
    +			s += getBool('flash', 'play', true);
    +			s += getBool('flash', 'loop', true);
    +			s += getBool('flash', 'menu', true);
    +			s += getBool('flash', 'swliveconnect', false);
    +			s += getStr('flash', 'quality');
    +			s += getStr('flash', 'scale');
    +			s += getStr('flash', 'salign');
    +			s += getStr('flash', 'wmode');
    +			s += getStr('flash', 'base');
    +			s += getStr('flash', 'flashvars');
    +		break;
    +
    +		case "qt":
    +			s += getBool('qt', 'loop', false);
    +			s += getBool('qt', 'autoplay', true);
    +			s += getBool('qt', 'cache', false);
    +			s += getBool('qt', 'controller', true);
    +			s += getBool('qt', 'correction', false, 'none', 'full');
    +			s += getBool('qt', 'enablejavascript', false);
    +			s += getBool('qt', 'kioskmode', false);
    +			s += getBool('qt', 'autohref', false);
    +			s += getBool('qt', 'playeveryframe', false);
    +			s += getBool('qt', 'targetcache', false);
    +			s += getStr('qt', 'scale');
    +			s += getStr('qt', 'starttime');
    +			s += getStr('qt', 'endtime');
    +			s += getStr('qt', 'target');
    +			s += getStr('qt', 'qtsrcchokespeed');
    +			s += getStr('qt', 'volume');
    +			s += getStr('qt', 'qtsrc');
    +		break;
    +
    +		case "shockwave":
    +			s += getBool('shockwave', 'sound');
    +			s += getBool('shockwave', 'progress');
    +			s += getBool('shockwave', 'autostart');
    +			s += getBool('shockwave', 'swliveconnect');
    +			s += getStr('shockwave', 'swvolume');
    +			s += getStr('shockwave', 'swstretchstyle');
    +			s += getStr('shockwave', 'swstretchhalign');
    +			s += getStr('shockwave', 'swstretchvalign');
    +		break;
    +
    +		case "wmp":
    +			s += getBool('wmp', 'autostart', true);
    +			s += getBool('wmp', 'enabled', false);
    +			s += getBool('wmp', 'enablecontextmenu', true);
    +			s += getBool('wmp', 'fullscreen', false);
    +			s += getBool('wmp', 'invokeurls', true);
    +			s += getBool('wmp', 'mute', false);
    +			s += getBool('wmp', 'stretchtofit', false);
    +			s += getBool('wmp', 'windowlessvideo', false);
    +			s += getStr('wmp', 'balance');
    +			s += getStr('wmp', 'baseurl');
    +			s += getStr('wmp', 'captioningid');
    +			s += getStr('wmp', 'currentmarker');
    +			s += getStr('wmp', 'currentposition');
    +			s += getStr('wmp', 'defaultframe');
    +			s += getStr('wmp', 'playcount');
    +			s += getStr('wmp', 'rate');
    +			s += getStr('wmp', 'uimode');
    +			s += getStr('wmp', 'volume');
    +		break;
    +
    +		case "rmp":
    +			s += getBool('rmp', 'autostart', false);
    +			s += getBool('rmp', 'loop', false);
    +			s += getBool('rmp', 'autogotourl', true);
    +			s += getBool('rmp', 'center', false);
    +			s += getBool('rmp', 'imagestatus', true);
    +			s += getBool('rmp', 'maintainaspect', false);
    +			s += getBool('rmp', 'nojava', false);
    +			s += getBool('rmp', 'prefetch', false);
    +			s += getBool('rmp', 'shuffle', false);
    +			s += getStr('rmp', 'console');
    +			s += getStr('rmp', 'controls');
    +			s += getStr('rmp', 'numloop');
    +			s += getStr('rmp', 'scriptcallbacks');
    +		break;
    +	}
    +
    +	s += getStr(null, 'id');
    +	s += getStr(null, 'name');
    +	s += getStr(null, 'src');
    +	s += getStr(null, 'align');
    +	s += getStr(null, 'bgcolor');
    +	s += getInt(null, 'vspace');
    +	s += getInt(null, 'hspace');
    +	s += getStr(null, 'width');
    +	s += getStr(null, 'height');
    +
    +	s = s.length > 0 ? s.substring(0, s.length - 1) : s;
    +
    +	return s;
    +}
    +
    +function setBool(pl, p, n) {
    +	if (typeof(pl[n]) == "undefined")
    +		return;
    +
    +	document.forms[0].elements[p + "_" + n].checked = pl[n] != 'false';
    +}
    +
    +function setStr(pl, p, n) {
    +	var f = document.forms[0], e = f.elements[(p != null ? p + "_" : '') + n];
    +
    +	if (typeof(pl[n]) == "undefined")
    +		return;
    +
    +	if (e.type == "text")
    +		e.value = pl[n];
    +	else
    +		selectByValue(f, (p != null ? p + "_" : '') + n, pl[n]);
    +}
    +
    +function getBool(p, n, d, tv, fv) {
    +	var v = document.forms[0].elements[p + "_" + n].checked;
    +
    +	tv = typeof(tv) == 'undefined' ? 'true' : "'" + jsEncode(tv) + "'";
    +	fv = typeof(fv) == 'undefined' ? 'false' : "'" + jsEncode(fv) + "'";
    +
    +	return (v == d) ? '' : n + (v ? ':' + tv + ',' : ":\'" + fv + "\',");
    +}
    +
    +function getStr(p, n, d) {
    +	var e = document.forms[0].elements[(p != null ? p + "_" : "") + n];
    +	var v = e.type == "text" ? e.value : e.options[e.selectedIndex].value;
    +
    +	if (n == 'src')
    +		v = tinyMCEPopup.editor.convertURL(v, 'src', null);
    +
    +	return ((n == d || v == '') ? '' : n + ":'" + jsEncode(v) + "',");
    +}
    +
    +function getInt(p, n, d) {
    +	var e = document.forms[0].elements[(p != null ? p + "_" : "") + n];
    +	var v = e.type == "text" ? e.value : e.options[e.selectedIndex].value;
    +
    +	return ((n == d || v == '') ? '' : n + ":" + v.replace(/[^0-9]+/g, '') + ",");
    +}
    +
    +function jsEncode(s) {
    +	s = s.replace(new RegExp('\\\\', 'g'), '\\\\');
    +	s = s.replace(new RegExp('"', 'g'), '\\"');
    +	s = s.replace(new RegExp("'", 'g'), "\\'");
    +
    +	return s;
    +}
    +
    +function generatePreview(c) {
    +	var f = document.forms[0], p = document.getElementById('prev'), h = '', cls, pl, n, type, codebase, wp, hp, nw, nh;
    +
    +	p.innerHTML = '<!-- x --->';
    +
    +	nw = parseInt(f.width.value);
    +	nh = parseInt(f.height.value);
    +
    +	if (f.width.value != "" && f.height.value != "") {
    +		if (f.constrain.checked) {
    +			if (c == 'width' && oldWidth != 0) {
    +				wp = nw / oldWidth;
    +				nh = Math.round(wp * nh);
    +				f.height.value = nh;
    +			} else if (c == 'height' && oldHeight != 0) {
    +				hp = nh / oldHeight;
    +				nw = Math.round(hp * nw);
    +				f.width.value = nw;
    +			}
    +		}
    +	}
    +
    +	if (f.width.value != "")
    +		oldWidth = nw;
    +
    +	if (f.height.value != "")
    +		oldHeight = nh;
    +
    +	// After constrain
    +	pl = serializeParameters();
    +
    +	switch (f.media_type.options[f.media_type.selectedIndex].value) {
    +		case "flash":
    +			cls = 'clsid:D27CDB6E-AE6D-11cf-96B8-444553540000';
    +			codebase = 'http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=6,0,40,0';
    +			type = 'application/x-shockwave-flash';
    +			break;
    +
    +		case "shockwave":
    +			cls = 'clsid:166B1BCA-3F9C-11CF-8075-444553540000';
    +			codebase = 'http://download.macromedia.com/pub/shockwave/cabs/director/sw.cab#version=8,5,1,0';
    +			type = 'application/x-director';
    +			break;
    +
    +		case "qt":
    +			cls = 'clsid:02BF25D5-8C17-4B23-BC80-D3488ABDDC6B';
    +			codebase = 'http://www.apple.com/qtactivex/qtplugin.cab#version=6,0,2,0';
    +			type = 'video/quicktime';
    +			break;
    +
    +		case "wmp":
    +			cls = ed.getParam('media_wmp6_compatible') ? 'clsid:05589FA1-C356-11CE-BF01-00AA0055595A' : 'clsid:6BF52A52-394A-11D3-B153-00C04F79FAA6';
    +			codebase = 'http://activex.microsoft.com/activex/controls/mplayer/en/nsmp2inf.cab#Version=5,1,52,701';
    +			type = 'application/x-mplayer2';
    +			break;
    +
    +		case "rmp":
    +			cls = 'clsid:CFCDAA03-8BE4-11cf-B84B-0020AFBBCCFA';
    +			codebase = 'http://activex.microsoft.com/activex/controls/mplayer/en/nsmp2inf.cab#Version=5,1,52,701';
    +			type = 'audio/x-pn-realaudio-plugin';
    +			break;
    +	}
    +
    +	if (pl == '') {
    +		p.innerHTML = '';
    +		return;
    +	}
    +
    +	pl = tinyMCEPopup.editor.plugins.media._parse(pl);
    +
    +	if (!pl.src) {
    +		p.innerHTML = '';
    +		return;
    +	}
    +
    +	pl.src = tinyMCEPopup.editor.documentBaseURI.toAbsolute(pl.src);
    +	pl.width = !pl.width ? 100 : pl.width;
    +	pl.height = !pl.height ? 100 : pl.height;
    +	pl.id = !pl.id ? 'obj' : pl.id;
    +	pl.name = !pl.name ? 'eobj' : pl.name;
    +	pl.align = !pl.align ? '' : pl.align;
    +
    +	// Avoid annoying warning about insecure items
    +	if (!tinymce.isIE || document.location.protocol != 'https:') {
    +		h += '<object classid="' + cls + '" codebase="' + codebase + '" width="' + pl.width + '" height="' + pl.height + '" id="' + pl.id + '" name="' + pl.name + '" align="' + pl.align + '">';
    +
    +		for (n in pl) {
    +			h += '<param name="' + n + '" value="' + pl[n] + '">';
    +
    +			// Add extra url parameter if it's an absolute URL
    +			if (n == 'src' && pl[n].indexOf('://') != -1)
    +				h += '<param name="url" value="' + pl[n] + '" />';
    +		}
    +	}
    +
    +	h += '<embed type="' + type + '" ';
    +
    +	for (n in pl)
    +		h += n + '="' + pl[n] + '" ';
    +
    +	h += '></embed>';
    +
    +	// Avoid annoying warning about insecure items
    +	if (!tinymce.isIE || document.location.protocol != 'https:')
    +		h += '</object>';
    +
    +	p.innerHTML = "<!-- x --->" + h;
    +}
    +
    +tinyMCEPopup.onInit.add(init);
    diff --git a/TinyMCE/tiny_mce/plugins/media/langs/typecho_dlg.js b/TinyMCE/tiny_mce/plugins/media/langs/typecho_dlg.js
    new file mode 100644
    index 0000000..8b13789
    --- /dev/null
    +++ b/TinyMCE/tiny_mce/plugins/media/langs/typecho_dlg.js
    @@ -0,0 +1 @@
    +
    diff --git a/TinyMCE/tiny_mce/plugins/media/media.htm b/TinyMCE/tiny_mce/plugins/media/media.htm
    new file mode 100644
    index 0000000..31c6e66
    --- /dev/null
    +++ b/TinyMCE/tiny_mce/plugins/media/media.htm
    @@ -0,0 +1,822 @@
    +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
    +<html xmlns="http://www.w3.org/1999/xhtml">
    +<head>
    +	<title>{#media_dlg.title}</title>
    +	<script type="text/javascript" src="../../tiny_mce_popup.js"></script>
    +	<script type="text/javascript" src="js/media.js"></script>
    +	<script type="text/javascript" src="../../utils/mctabs.js"></script>
    +	<script type="text/javascript" src="../../utils/validate.js"></script>
    +	<script type="text/javascript" src="../../utils/form_utils.js"></script>
    +	<script type="text/javascript" src="../../utils/editable_selects.js"></script>
    +	<link href="css/media.css" rel="stylesheet" type="text/css" />
    +</head>
    +<body style="display: none">
    +    <form onsubmit="insertMedia();return false;" action="#">
    +		<div class="tabs">
    +			<ul>
    +				<li id="general_tab" class="current"><span><a href="javascript:mcTabs.displayTab('general_tab','general_panel');generatePreview();" onmousedown="return false;">{#media_dlg.general}</a></span></li>
    +				<li id="advanced_tab"><span><a href="javascript:mcTabs.displayTab('advanced_tab','advanced_panel');" onmousedown="return false;">{#media_dlg.advanced}</a></span></li>
    +			</ul>
    +		</div>
    +
    +		<div class="panel_wrapper">
    +			<div id="general_panel" class="panel current">
    +				<fieldset>
    +					<legend>{#media_dlg.general}</legend>
    +
    +					<table border="0" cellpadding="4" cellspacing="0">
    +							<tr>
    +								<td><label for="media_type">{#media_dlg.type}</label></td>
    +								<td>
    +									<select id="media_type" name="media_type" onchange="changedType(this.value);generatePreview();">
    +										<option value="flash">Flash</option>
    +										<!-- <option value="flv">Flash video (FLV)</option> -->
    +										<option value="qt">Quicktime</option>
    +										<option value="shockwave">Shockwave</option>
    +										<option value="wmp">Windows Media</option>
    +										<option value="rmp">Real Media</option>
    +									</select>
    +								</td>
    +							</tr>
    +							<tr>
    +							<td><label for="src">{#media_dlg.file}</label></td>
    +							  <td>
    +									<table border="0" cellspacing="0" cellpadding="0">
    +									  <tr>
    +										<td><input id="src" name="src" type="text" value="" class="mceFocus" onchange="switchType(this.value);generatePreview();" /></td>
    +										<td id="filebrowsercontainer">&nbsp;</td>
    +									  </tr>
    +									</table>
    +								</td>
    +							</tr>
    +							<tr id="linklistrow">
    +								<td><label for="linklist">{#media_dlg.list}</label></td>
    +								<td id="linklistcontainer"><select id="linklist"><option value=""></option></select></td>
    +							</tr>
    +							<tr>
    +								<td><label for="width">{#media_dlg.size}</label></td>
    +								<td>
    +									<table border="0" cellpadding="0" cellspacing="0">
    +										<tr>
    +											<td><input type="text" id="width" name="width" value="" class="size" onchange="generatePreview('width');" /> x <input type="text" id="height" name="height" value="" class="size"  onchange="generatePreview('height');" /></td>
    +											<td>&nbsp;&nbsp;<input id="constrain" type="checkbox" name="constrain" class="checkbox" /></td>
    +											<td><label id="constrainlabel" for="constrain">{#media_dlg.constrain_proportions}</label></td>
    +										</tr>
    +									</table>
    +								</td>
    +							</tr>
    +					</table>
    +				</fieldset>
    +
    +				<fieldset>
    +					<legend>{#media_dlg.preview}</legend>
    +					<div id="prev"></div>
    +				</fieldset>
    +			</div>
    +
    +			<div id="advanced_panel" class="panel">
    +				<fieldset>
    +					<legend>{#media_dlg.advanced}</legend>
    +
    +					<table border="0" cellpadding="4" cellspacing="0" width="100%">
    +						<tr>
    +							<td><label for="id">{#media_dlg.id}</label></td>
    +							<td><input type="text" id="id" name="id" onchange="generatePreview();" /></td>
    +							<td><label for="name">{#media_dlg.name}</label></td>
    +							<td><input type="text" id="name" name="name" onchange="generatePreview();" /></td>
    +						</tr>
    +
    +						<tr>
    +							<td><label for="align">{#media_dlg.align}</label></td>
    +							<td>
    +								<select id="align" name="align" onchange="generatePreview();">
    +									<option value="">{#not_set}</option> 
    +									<option value="top">{#media_dlg.align_top}</option>
    +									<option value="right">{#media_dlg.align_right}</option>
    +									<option value="bottom">{#media_dlg.align_bottom}</option>
    +									<option value="left">{#media_dlg.align_left}</option>
    +								</select>
    +							</td>
    +
    +							<td><label for="bgcolor">{#media_dlg.bgcolor}</label></td>
    +							<td>
    +								<table border="0" cellpadding="0" cellspacing="0">
    +									<tr>
    +										<td><input id="bgcolor" name="bgcolor" type="text" value="" size="9" onchange="updateColor('bgcolor_pick','bgcolor');generatePreview();" /></td>
    +										<td id="bgcolor_pickcontainer">&nbsp;</td>
    +									</tr>
    +								</table>
    +							</td>
    +						</tr>
    +
    +						<tr>
    +							<td><label for="vspace">{#media_dlg.vspace}</label></td>
    +							<td><input type="text" id="vspace" name="vspace" class="number" onchange="generatePreview();" /></td>
    +							<td><label for="hspace">{#media_dlg.hspace}</label></td>
    +							<td><input type="text" id="hspace" name="hspace" class="number" onchange="generatePreview();" /></td>
    +						</tr>
    +					</table>
    +				</fieldset>
    +
    +				<fieldset id="flash_options">
    +					<legend>{#media_dlg.flash_options}</legend>
    +
    +					<table border="0" cellpadding="4" cellspacing="0">
    +						<tr>
    +							<td><label for="flash_quality">{#media_dlg.quality}</label></td>
    +							<td>
    +								<select id="flash_quality" name="flash_quality" onchange="generatePreview();">
    +									<option value="">{#not_set}</option> 
    +									<option value="high">high</option>
    +									<option value="low">low</option>
    +									<option value="autolow">autolow</option>
    +									<option value="autohigh">autohigh</option>
    +									<option value="best">best</option>
    +								</select>
    +							</td>
    +
    +							<td><label for="flash_scale">{#media_dlg.scale}</label></td>
    +							<td>
    +								<select id="flash_scale" name="flash_scale" onchange="generatePreview();">
    +									<option value="">{#not_set}</option> 
    +									<option value="showall">showall</option>
    +									<option value="noborder">noborder</option>
    +									<option value="exactfit">exactfit</option>
    +									<option value="noscale">noscale</option>
    +								</select>
    +							</td>
    +						</tr>
    +
    +						<tr>
    +							<td><label for="flash_wmode">{#media_dlg.wmode}</label></td>
    +							<td>
    +								<select id="flash_wmode" name="flash_wmode" onchange="generatePreview();">
    +									<option value="">{#not_set}</option> 
    +									<option value="window">window</option>
    +									<option value="opaque">opaque</option>
    +									<option value="transparent">transparent</option>
    +								</select>
    +							</td>
    +
    +							<td><label for="flash_salign">{#media_dlg.salign}</label></td>
    +							<td>
    +								<select id="flash_salign" name="flash_salign" onchange="generatePreview();">
    +									<option value="">{#not_set}</option> 
    +									<option value="l">{#media_dlg.align_left}</option>
    +									<option value="t">{#media_dlg.align_top}</option>
    +									<option value="r">{#media_dlg.align_right}</option>
    +									<option value="b">{#media_dlg.align_bottom}</option>
    +									<option value="tl">{#media_dlg.align_top_left}</option>
    +									<option value="tr">{#media_dlg.align_top_right}</option>
    +									<option value="bl">{#media_dlg.align_bottom_left}</option>
    +									<option value="br">{#media_dlg.align_bottom_right}</option>
    +								</select>
    +							</td>
    +						</tr>
    +
    +						<tr>
    +							<td colspan="2">
    +								<table border="0" cellpadding="0" cellspacing="0">
    +									<tr>
    +										<td><input type="checkbox" class="checkbox" id="flash_play" name="flash_play" checked="checked" onchange="generatePreview();" /></td>
    +										<td><label for="flash_play">{#media_dlg.play}</label></td>
    +									</tr>
    +								</table>
    +							</td>
    +
    +							<td colspan="2">
    +								<table border="0" cellpadding="0" cellspacing="0">
    +									<tr>
    +										<td><input type="checkbox" class="checkbox" id="flash_loop" name="flash_loop" checked="checked" onchange="generatePreview();" /></td>
    +										<td><label for="flash_loop">{#media_dlg.loop}</label></td>
    +									</tr>
    +								</table>
    +							</td>
    +						</tr>
    +
    +						<tr>
    +							<td colspan="2">
    +								<table border="0" cellpadding="0" cellspacing="0">
    +									<tr>
    +										<td><input type="checkbox" class="checkbox" id="flash_menu" name="flash_menu" checked="checked" onchange="generatePreview();" /></td>
    +										<td><label for="flash_menu">{#media_dlg.menu}</label></td>
    +									</tr>
    +								</table>
    +							</td>
    +
    +							<td colspan="2">
    +								<table border="0" cellpadding="0" cellspacing="0">
    +									<tr>
    +										<td><input type="checkbox" class="checkbox" id="flash_swliveconnect" name="flash_swliveconnect" onchange="generatePreview();" /></td>
    +										<td><label for="flash_swliveconnect">{#media_dlg.liveconnect}</label></td>
    +									</tr>
    +								</table>
    +							</td>
    +						</tr>
    +					</table>
    +
    +					<table>
    +						<tr>
    +							<td><label for="flash_base">{#media_dlg.base}</label></td>
    +							<td><input type="text" id="flash_base" name="flash_base" onchange="generatePreview();" /></td>
    +						</tr>
    +
    +						<tr>
    +							<td><label for="flash_flashvars">{#media_dlg.flashvars}</label></td>
    +							<td><input type="text" id="flash_flashvars" name="flash_flashvars" onchange="generatePreview();" /></td>
    +						</tr>
    +					</table>
    +				</fieldset>
    +
    +				<fieldset id="flv_options">
    +					<legend>{#media_dlg.flv_options}</legend>
    +
    +					<table border="0" cellpadding="4" cellspacing="0">
    +						<tr>
    +							<td><label for="flv_scalemode">{#media_dlg.flv_scalemode}</label></td>
    +							<td>
    +								<select id="flv_scalemode" name="flv_scalemode" onchange="generatePreview();">
    +									<option value="">{#not_set}</option> 
    +									<option value="none">none</option>
    +									<option value="double">double</option>
    +									<option value="full">full</option>
    +								</select>
    +							</td>
    +
    +							<td><label for="flv_buffer">{#media_dlg.flv_buffer}</label></td>
    +							<td><input type="text" id="flv_buffer" name="flv_buffer" onchange="generatePreview();" /></td>
    +						</tr>
    +
    +						<tr>
    +							<td><label for="flv_startimage">{#media_dlg.flv_startimage}</label></td>
    +							<td><input type="text" id="flv_startimage" name="flv_startimage" onchange="generatePreview();" /></td>
    +
    +							<td><label for="flv_starttime">{#media_dlg.flv_starttime}</label></td>
    +							<td><input type="text" id="flv_starttime" name="flv_starttime" onchange="generatePreview();" /></td>
    +						</tr>
    +
    +						<tr>
    +							<td><label for="flv_defaultvolume">{#media_dlg.flv_defaultvolume}</label></td>
    +							<td><input type="text" id="flv_defaultvolume" name="flv_defaultvolume" onchange="generatePreview();" /></td>
    +
    +
    +						</tr>
    +
    +						<tr>
    +							<td colspan="2">
    +								<table border="0" cellpadding="0" cellspacing="0">
    +									<tr>
    +										<td><input type="checkbox" class="checkbox" id="flv_hiddengui" name="flv_hiddengui" checked="checked" onchange="generatePreview();" /></td>
    +										<td><label for="flv_hiddengui">{#media_dlg.flv_hiddengui}</label></td>
    +									</tr>
    +								</table>
    +							</td>
    +
    +							<td colspan="2">
    +								<table border="0" cellpadding="0" cellspacing="0">
    +									<tr>
    +										<td><input type="checkbox" class="checkbox" id="flv_autostart" name="flv_autostart" checked="checked" onchange="generatePreview();" /></td>
    +										<td><label for="flv_autostart">{#media_dlg.flv_autostart}</label></td>
    +									</tr>
    +								</table>
    +							</td>
    +						</tr>
    +
    +						<tr>
    +							<td colspan="2">
    +								<table border="0" cellpadding="0" cellspacing="0">
    +									<tr>
    +										<td><input type="checkbox" class="checkbox" id="flv_loop" name="flv_loop" checked="checked" onchange="generatePreview();" /></td>
    +										<td><label for="flv_loop">{#media_dlg.flv_loop}</label></td>
    +									</tr>
    +								</table>
    +							</td>
    +
    +							<td colspan="2">
    +								<table border="0" cellpadding="0" cellspacing="0">
    +									<tr>
    +										<td><input type="checkbox" class="checkbox" id="flv_showscalemodes" name="flv_showscalemodes" onchange="generatePreview();" /></td>
    +										<td><label for="flv_showscalemodes">{#media_dlg.flv_showscalemodes}</label></td>
    +									</tr>
    +								</table>
    +							</td>
    +						</tr>
    +
    +						<tr>
    +							<td colspan="2">
    +								<table border="0" cellpadding="0" cellspacing="0">
    +									<tr>
    +										<td><input type="checkbox" class="checkbox" id="flv_smoothvideo" name="flash_flv_flv_smoothvideosmoothvideo" checked="checked" onchange="generatePreview();" /></td>
    +										<td><label for="flv_smoothvideo">{#media_dlg.flv_smoothvideo}</label></td>
    +									</tr>
    +								</table>
    +							</td>
    +
    +							<td colspan="2">
    +								<table border="0" cellpadding="0" cellspacing="0">
    +									<tr>
    +										<td><input type="checkbox" class="checkbox" id="flv_jscallback" name="flv_jscallback" onchange="generatePreview();" /></td>
    +										<td><label for="flv_jscallback">{#media_dlg.flv_jscallback}</label></td>
    +									</tr>
    +								</table>
    +							</td>
    +						</tr>
    +					</table>
    +				</fieldset>
    +
    +				<fieldset id="qt_options">
    +					<legend>{#media_dlg.qt_options}</legend>
    +
    +					<table border="0" cellpadding="4" cellspacing="0">
    +						<tr>
    +							<td colspan="2">
    +								<table border="0" cellpadding="0" cellspacing="0">
    +									<tr>
    +										<td><input type="checkbox" class="checkbox" id="qt_loop" name="qt_loop" onchange="generatePreview();" /></td>
    +										<td><label for="qt_loop">{#media_dlg.loop}</label></td>
    +									</tr>
    +								</table>
    +							</td>
    +
    +							<td colspan="2">
    +								<table border="0" cellpadding="0" cellspacing="0">
    +									<tr>
    +										<td><input type="checkbox" class="checkbox" id="qt_autoplay" name="qt_autoplay" checked="checked" onchange="generatePreview();" /></td>
    +										<td><label for="qt_autoplay">{#media_dlg.play}</label></td>
    +									</tr>
    +								</table>
    +							</td>
    +						</tr>
    +
    +						<tr>
    +							<td colspan="2">
    +								<table border="0" cellpadding="0" cellspacing="0">
    +									<tr>
    +										<td><input type="checkbox" class="checkbox" id="qt_cache" name="qt_cache" onchange="generatePreview();" /></td>
    +										<td><label for="qt_cache">{#media_dlg.cache}</label></td>
    +									</tr>
    +								</table>
    +							</td>
    +
    +							<td colspan="2">
    +								<table border="0" cellpadding="0" cellspacing="0">
    +									<tr>
    +										<td><input type="checkbox" class="checkbox" id="qt_controller" name="qt_controller" checked="checked" onchange="generatePreview();" /></td>
    +										<td><label for="qt_controller">{#media_dlg.controller}</label></td>
    +									</tr>
    +								</table>
    +							</td>
    +						</tr>
    +
    +						<tr>
    +							<td colspan="2">
    +								<table border="0" cellpadding="0" cellspacing="0">
    +									<tr>
    +										<td><input type="checkbox" class="checkbox" id="qt_correction" name="qt_correction" onchange="generatePreview();" /></td>
    +										<td><label for="qt_correction">{#media_dlg.correction}</label></td>
    +									</tr>
    +								</table>
    +							</td>
    +
    +							<td colspan="2">
    +								<table border="0" cellpadding="0" cellspacing="0">
    +									<tr>
    +										<td><input type="checkbox" class="checkbox" id="qt_enablejavascript" name="qt_enablejavascript" onchange="generatePreview();" /></td>
    +										<td><label for="qt_enablejavascript">{#media_dlg.enablejavascript}</label></td>
    +									</tr>
    +								</table>
    +							</td>
    +						</tr>
    +
    +						<tr>
    +							<td colspan="2">
    +								<table border="0" cellpadding="0" cellspacing="0">
    +									<tr>
    +										<td><input type="checkbox" class="checkbox" id="qt_kioskmode" name="qt_kioskmode" onchange="generatePreview();" /></td>
    +										<td><label for="qt_kioskmode">{#media_dlg.kioskmode}</label></td>
    +									</tr>
    +								</table>
    +							</td>
    +
    +							<td colspan="2">
    +								<table border="0" cellpadding="0" cellspacing="0">
    +									<tr>
    +										<td><input type="checkbox" class="checkbox" id="qt_autohref" name="qt_autohref" onchange="generatePreview();" /></td>
    +										<td><label for="qt_autohref">{#media_dlg.autohref}</label></td>
    +									</tr>
    +								</table>
    +							</td>
    +						</tr>
    +
    +						<tr>
    +							<td colspan="2">
    +								<table border="0" cellpadding="0" cellspacing="0">
    +									<tr>
    +										<td><input type="checkbox" class="checkbox" id="qt_playeveryframe" name="qt_playeveryframe" onchange="generatePreview();" /></td>
    +										<td><label for="qt_playeveryframe">{#media_dlg.playeveryframe}</label></td>
    +									</tr>
    +								</table>
    +							</td>
    +
    +							<td colspan="2">
    +								<table border="0" cellpadding="0" cellspacing="0">
    +									<tr>
    +										<td><input type="checkbox" class="checkbox" id="qt_targetcache" name="qt_targetcache" onchange="generatePreview();" /></td>
    +										<td><label for="qt_targetcache">{#media_dlg.targetcache}</label></td>
    +									</tr>
    +								</table>
    +							</td>
    +						</tr>
    +
    +						<tr>
    +							<td><label for="qt_scale">{#media_dlg.scale}</label></td>
    +							<td><select id="qt_scale" name="qt_scale" class="mceEditableSelect" onchange="generatePreview();">
    +									<option value="">{#not_set}</option> 
    +									<option value="tofit">tofit</option>
    +									<option value="aspect">aspect</option>
    +								</select>
    +							</td>
    +
    +							<td colspan="2">&nbsp;</td>
    +						</tr>
    +
    +						<tr>
    +							<td><label for="qt_starttime">{#media_dlg.starttime}</label></td>
    +							<td><input type="text" id="qt_starttime" name="qt_starttime" onchange="generatePreview();" /></td>
    +
    +							<td><label for="qt_endtime">{#media_dlg.endtime}</label></td>
    +							<td><input type="text" id="qt_endtime" name="qt_endtime" onchange="generatePreview();" /></td>
    +						</tr>
    +
    +						<tr>
    +							<td><label for="qt_target">{#media_dlg.target}</label></td>
    +							<td><input type="text" id="qt_target" name="qt_target" onchange="generatePreview();" /></td>
    +
    +							<td><label for="qt_href">{#media_dlg.href}</label></td>
    +							<td><input type="text" id="qt_href" name="qt_href" onchange="generatePreview();" /></td>
    +						</tr>
    +
    +						<tr>
    +							<td><label for="qt_qtsrcchokespeed">{#media_dlg.qtsrcchokespeed}</label></td>
    +							<td><input type="text" id="qt_qtsrcchokespeed" name="qt_qtsrcchokespeed" onchange="generatePreview();" /></td>
    +
    +							<td><label for="qt_volume">{#media_dlg.volume}</label></td>
    +							<td><input type="text" id="qt_volume" name="qt_volume" onchange="generatePreview();" /></td>
    +						</tr>
    +
    +						<tr>
    +							<td><label for="qt_qtsrc">{#media_dlg.qtsrc}</label></td>
    +							<td colspan="4">
    +							<table border="0" cellspacing="0" cellpadding="0">
    +								  <tr>
    +									<td><input type="text" id="qt_qtsrc" name="qt_qtsrc" onchange="generatePreview();" /></td>
    +									<td id="qtsrcfilebrowsercontainer">&nbsp;</td>
    +								  </tr>
    +							</table>
    +							</td>
    +						</tr>
    +					</table>
    +				</fieldset>
    +
    +				<fieldset id="wmp_options">
    +					<legend>{#media_dlg.wmp_options}</legend>
    +
    +					<table border="0" cellpadding="4" cellspacing="0">
    +						<tr>
    +							<td colspan="2">
    +								<table border="0" cellpadding="0" cellspacing="0">
    +									<tr>
    +										<td><input type="checkbox" class="checkbox" id="wmp_autostart" name="wmp_autostart" checked="checked" onchange="generatePreview();" /></td>
    +										<td><label for="wmp_autostart">{#media_dlg.autostart}</label></td>
    +									</tr>
    +								</table>
    +							</td>
    +
    +							<td colspan="2">
    +								<table border="0" cellpadding="0" cellspacing="0">
    +									<tr>
    +										<td><input type="checkbox" class="checkbox" id="wmp_enabled" name="wmp_enabled" onchange="generatePreview();" /></td>
    +										<td><label for="wmp_enabled">{#media_dlg.enabled}</label></td>
    +									</tr>
    +								</table>
    +							</td>
    +						</tr>
    +
    +						<tr>
    +							<td colspan="2">
    +								<table border="0" cellpadding="0" cellspacing="0">
    +									<tr>
    +										<td><input type="checkbox" class="checkbox" id="wmp_enablecontextmenu" name="wmp_enablecontextmenu" checked="checked" onchange="generatePreview();" /></td>
    +										<td><label for="wmp_enablecontextmenu">{#media_dlg.menu}</label></td>
    +									</tr>
    +								</table>
    +							</td>
    +
    +							<td colspan="2">
    +								<table border="0" cellpadding="0" cellspacing="0">
    +									<tr>
    +										<td><input type="checkbox" class="checkbox" id="wmp_fullscreen" name="wmp_fullscreen" onchange="generatePreview();" /></td>
    +										<td><label for="wmp_fullscreen">{#media_dlg.fullscreen}</label></td>
    +									</tr>
    +								</table>
    +							</td>
    +						</tr>
    +
    +						<tr>
    +							<td colspan="2">
    +								<table border="0" cellpadding="0" cellspacing="0">
    +									<tr>
    +										<td><input type="checkbox" class="checkbox" id="wmp_invokeurls" name="wmp_invokeurls" checked="checked" onchange="generatePreview();" /></td>
    +										<td><label for="wmp_invokeurls">{#media_dlg.invokeurls}</label></td>
    +									</tr>
    +								</table>
    +							</td>
    +
    +							<td colspan="2">
    +								<table border="0" cellpadding="0" cellspacing="0">
    +									<tr>
    +										<td><input type="checkbox" class="checkbox" id="wmp_mute" name="wmp_mute" onchange="generatePreview();" /></td>
    +										<td><label for="wmp_mute">{#media_dlg.mute}</label></td>
    +									</tr>
    +								</table>
    +							</td>
    +						</tr>
    +
    +						<tr>
    +							<td colspan="2">
    +								<table border="0" cellpadding="0" cellspacing="0">
    +									<tr>
    +										<td><input type="checkbox" class="checkbox" id="wmp_stretchtofit" name="wmp_stretchtofit" onchange="generatePreview();" /></td>
    +										<td><label for="wmp_stretchtofit">{#media_dlg.stretchtofit}</label></td>
    +									</tr>
    +								</table>
    +							</td>
    +
    +							<td colspan="2">
    +								<table border="0" cellpadding="0" cellspacing="0">
    +									<tr>
    +										<td><input type="checkbox" class="checkbox" id="wmp_windowlessvideo" name="wmp_windowlessvideo" onchange="generatePreview();" /></td>
    +										<td><label for="wmp_windowlessvideo">{#media_dlg.windowlessvideo}</label></td>
    +									</tr>
    +								</table>
    +							</td>
    +						</tr>
    +
    +						<tr>
    +							<td><label for="wmp_balance">{#media_dlg.balance}</label></td>
    +							<td><input type="text" id="wmp_balance" name="wmp_balance" onchange="generatePreview();" /></td>
    +
    +							<td><label for="wmp_baseurl">{#media_dlg.baseurl}</label></td>
    +							<td><input type="text" id="wmp_baseurl" name="wmp_baseurl" onchange="generatePreview();" /></td>
    +						</tr>
    +
    +						<tr>
    +							<td><label for="wmp_captioningid">{#media_dlg.captioningid}</label></td>
    +							<td><input type="text" id="wmp_captioningid" name="wmp_captioningid" onchange="generatePreview();" /></td>
    +
    +							<td><label for="wmp_currentmarker">{#media_dlg.currentmarker}</label></td>
    +							<td><input type="text" id="wmp_currentmarker" name="wmp_currentmarker" onchange="generatePreview();" /></td>
    +						</tr>
    +
    +						<tr>
    +							<td><label for="wmp_currentposition">{#media_dlg.currentposition}</label></td>
    +							<td><input type="text" id="wmp_currentposition" name="wmp_currentposition" onchange="generatePreview();" /></td>
    +
    +							<td><label for="wmp_defaultframe">{#media_dlg.defaultframe}</label></td>
    +							<td><input type="text" id="wmp_defaultframe" name="wmp_defaultframe" onchange="generatePreview();" /></td>
    +						</tr>
    +
    +						<tr>
    +							<td><label for="wmp_playcount">{#media_dlg.playcount}</label></td>
    +							<td><input type="text" id="wmp_playcount" name="wmp_playcount" onchange="generatePreview();" /></td>
    +
    +							<td><label for="wmp_rate">{#media_dlg.rate}</label></td>
    +							<td><input type="text" id="wmp_rate" name="wmp_rate" onchange="generatePreview();" /></td>
    +						</tr>
    +
    +						<tr>
    +							<td><label for="wmp_uimode">{#media_dlg.uimode}</label></td>
    +							<td><input type="text" id="wmp_uimode" name="wmp_uimode" onchange="generatePreview();" /></td>
    +
    +							<td><label for="wmp_volume">{#media_dlg.volume}</label></td>
    +							<td><input type="text" id="wmp_volume" name="wmp_volume" onchange="generatePreview();" /></td>
    +						</tr>
    +
    +					</table>
    +				</fieldset>
    +
    +				<fieldset id="rmp_options">
    +					<legend>{#media_dlg.rmp_options}</legend>
    +
    +					<table border="0" cellpadding="4" cellspacing="0">
    +						<tr>
    +							<td colspan="2">
    +								<table border="0" cellpadding="0" cellspacing="0">
    +									<tr>
    +										<td><input type="checkbox" class="checkbox" id="rmp_autostart" name="rmp_autostart" onchange="generatePreview();" /></td>
    +										<td><label for="rmp_autostart">{#media_dlg.autostart}</label></td>
    +									</tr>
    +								</table>
    +							</td>
    +
    +							<td colspan="2">
    +								<table border="0" cellpadding="0" cellspacing="0">
    +									<tr>
    +										<td><input type="checkbox" class="checkbox" id="rmp_loop" name="rmp_loop" onchange="generatePreview();" /></td>
    +										<td><label for="rmp_loop">{#media_dlg.loop}</label></td>
    +									</tr>
    +								</table>
    +							</td>
    +						</tr>
    +
    +						<tr>
    +							<td colspan="2">
    +								<table border="0" cellpadding="0" cellspacing="0">
    +									<tr>
    +										<td><input type="checkbox" class="checkbox" id="rmp_autogotourl" name="rmp_autogotourl" checked="checked" onchange="generatePreview();" /></td>
    +										<td><label for="rmp_autogotourl">{#media_dlg.autogotourl}</label></td>
    +									</tr>
    +								</table>
    +							</td>
    +
    +							<td colspan="2">
    +								<table border="0" cellpadding="0" cellspacing="0">
    +									<tr>
    +										<td><input type="checkbox" class="checkbox" id="rmp_center" name="rmp_center" onchange="generatePreview();" /></td>
    +										<td><label for="rmp_center">{#media_dlg.center}</label></td>
    +									</tr>
    +								</table>
    +							</td>
    +						</tr>
    +
    +						<tr>
    +							<td colspan="2">
    +								<table border="0" cellpadding="0" cellspacing="0">
    +									<tr>
    +										<td><input type="checkbox" class="checkbox" id="rmp_imagestatus" name="rmp_imagestatus" checked="checked" onchange="generatePreview();" /></td>
    +										<td><label for="rmp_imagestatus">{#media_dlg.imagestatus}</label></td>
    +									</tr>
    +								</table>
    +							</td>
    +
    +							<td colspan="2">
    +								<table border="0" cellpadding="0" cellspacing="0">
    +									<tr>
    +										<td><input type="checkbox" class="checkbox" id="rmp_maintainaspect" name="rmp_maintainaspect" onchange="generatePreview();" /></td>
    +										<td><label for="rmp_maintainaspect">{#media_dlg.maintainaspect}</label></td>
    +									</tr>
    +								</table>
    +							</td>
    +						</tr>
    +
    +						<tr>
    +							<td colspan="2">
    +								<table border="0" cellpadding="0" cellspacing="0">
    +									<tr>
    +										<td><input type="checkbox" class="checkbox" id="rmp_nojava" name="rmp_nojava" onchange="generatePreview();" /></td>
    +										<td><label for="rmp_nojava">{#media_dlg.nojava}</label></td>
    +									</tr>
    +								</table>
    +							</td>
    +
    +							<td colspan="2">
    +								<table border="0" cellpadding="0" cellspacing="0">
    +									<tr>
    +										<td><input type="checkbox" class="checkbox" id="rmp_prefetch" name="rmp_prefetch" onchange="generatePreview();" /></td>
    +										<td><label for="rmp_prefetch">{#media_dlg.prefetch}</label></td>
    +									</tr>
    +								</table>
    +							</td>
    +						</tr>
    +
    +						<tr>
    +							<td colspan="2">
    +								<table border="0" cellpadding="0" cellspacing="0">
    +									<tr>
    +										<td><input type="checkbox" class="checkbox" id="rmp_shuffle" name="rmp_shuffle" onchange="generatePreview();" /></td>
    +										<td><label for="rmp_shuffle">{#media_dlg.shuffle}</label></td>
    +									</tr>
    +								</table>
    +							</td>
    +
    +							<td colspan="2">
    +								&nbsp;
    +							</td>
    +						</tr>
    +
    +						<tr>
    +							<td><label for="rmp_console">{#media_dlg.console}</label></td>
    +							<td><input type="text" id="rmp_console" name="rmp_console" onchange="generatePreview();" /></td>
    +
    +							<td><label for="rmp_controls">{#media_dlg.controls}</label></td>
    +							<td><input type="text" id="rmp_controls" name="rmp_controls" onchange="generatePreview();" /></td>
    +						</tr>
    +
    +						<tr>
    +							<td><label for="rmp_numloop">{#media_dlg.numloop}</label></td>
    +							<td><input type="text" id="rmp_numloop" name="rmp_numloop" onchange="generatePreview();" /></td>
    +
    +							<td><label for="rmp_scriptcallbacks">{#media_dlg.scriptcallbacks}</label></td>
    +							<td><input type="text" id="rmp_scriptcallbacks" name="rmp_scriptcallbacks" onchange="generatePreview();" /></td>
    +						</tr>
    +					</table>
    +				</fieldset>
    +
    +				<fieldset id="shockwave_options">
    +					<legend>{#media_dlg.shockwave_options}</legend>
    +
    +					<table border="0" cellpadding="4" cellspacing="0">
    +						<tr>
    +							<td><label for="shockwave_swstretchstyle">{#media_dlg.swstretchstyle}</label></td>
    +							<td>
    +								<select id="shockwave_swstretchstyle" name="shockwave_swstretchstyle" onchange="generatePreview();">
    +									<option value="none">{#not_set}</option>
    +									<option value="meet">Meet</option>
    +									<option value="fill">Fill</option>
    +									<option value="stage">Stage</option>
    +								</select>
    +							</td>
    +
    +							<td><label for="shockwave_swvolume">{#media_dlg.volume}</label></td>
    +							<td><input type="text" id="shockwave_swvolume" name="shockwave_swvolume" onchange="generatePreview();" /></td>
    +						</tr>
    +
    +						<tr>
    +							<td><label for="shockwave_swstretchhalign">{#media_dlg.swstretchhalign}</label></td>
    +							<td>
    +								<select id="shockwave_swstretchhalign" name="shockwave_swstretchhalign" onchange="generatePreview();">
    +									<option value="none">{#not_set}</option>
    +									<option value="left">{#media_dlg.align_left}</option>
    +									<option value="center">{#media_dlg.align_center}</option>
    +									<option value="right">{#media_dlg.align_right}</option>
    +								</select>
    +							</td>
    +
    +							<td><label for="shockwave_swstretchvalign">{#media_dlg.swstretchvalign}</label></td>
    +							<td>
    +								<select id="shockwave_swstretchvalign" name="shockwave_swstretchvalign" onchange="generatePreview();">
    +									<option value="none">{#not_set}</option>
    +									<option value="meet">Meet</option>
    +									<option value="fill">Fill</option>
    +									<option value="stage">Stage</option>
    +								</select>
    +							</td>
    +						</tr>
    +
    +						<tr>
    +							<td colspan="2">
    +								<table border="0" cellpadding="0" cellspacing="0">
    +									<tr>
    +										<td><input type="checkbox" class="checkbox" id="shockwave_autostart" name="shockwave_autostart" onchange="generatePreview();" checked="checked" /></td>
    +										<td><label for="shockwave_autostart">{#media_dlg.autostart}</label></td>
    +									</tr>
    +								</table>
    +							</td>
    +
    +							<td colspan="2">
    +								<table border="0" cellpadding="0" cellspacing="0">
    +									<tr>
    +										<td><input type="checkbox" class="checkbox" id="shockwave_sound" name="shockwave_sound" onchange="generatePreview();" checked="checked" /></td>
    +										<td><label for="shockwave_sound">{#media_dlg.sound}</label></td>
    +									</tr>
    +								</table>
    +							</td>
    +						</tr>
    +
    +
    +						<tr>
    +							<td colspan="2">
    +								<table border="0" cellpadding="0" cellspacing="0">
    +									<tr>
    +										<td><input type="checkbox" class="checkbox" id="shockwave_swliveconnect" name="shockwave_swliveconnect" onchange="generatePreview();" /></td>
    +										<td><label for="shockwave_swliveconnect">{#media_dlg.liveconnect}</label></td>
    +									</tr>
    +								</table>
    +							</td>
    +
    +							<td colspan="2">
    +								<table border="0" cellpadding="0" cellspacing="0">
    +									<tr>
    +										<td><input type="checkbox" class="checkbox" id="shockwave_progress" name="shockwave_progress" onchange="generatePreview();" checked="checked" /></td>
    +										<td><label for="shockwave_progress">{#media_dlg.progress}</label></td>
    +									</tr>
    +								</table>
    +							</td>
    +						</tr>
    +					</table>
    +				</fieldset>
    +			</div>
    +		</div>
    +
    +		<div class="mceActionPanel">
    +			<div style="float: left">
    +				<input type="submit" id="insert" name="insert" value="{#insert}" />
    +			</div>
    +
    +			<div style="float: right">
    +				<input type="button" id="cancel" name="cancel" value="{#cancel}" onclick="tinyMCEPopup.close();" />
    +			</div>
    +		</div>
    +	</form>
    +</body>
    +</html>
    diff --git a/TinyMCE/tiny_mce/plugins/morebreak/css/content.css b/TinyMCE/tiny_mce/plugins/morebreak/css/content.css
    new file mode 100644
    index 0000000..99c85c9
    --- /dev/null
    +++ b/TinyMCE/tiny_mce/plugins/morebreak/css/content.css
    @@ -0,0 +1 @@
    +.mceMoreBreak {display:block;border:0;width:100%;height:12px;border-top:1px dotted #ccc;margin-top:15px;background:#fff url(../img/morebreak.gif) no-repeat center top;}
    diff --git a/TinyMCE/tiny_mce/plugins/morebreak/editor_plugin.js b/TinyMCE/tiny_mce/plugins/morebreak/editor_plugin.js
    new file mode 100644
    index 0000000..3bc680f
    --- /dev/null
    +++ b/TinyMCE/tiny_mce/plugins/morebreak/editor_plugin.js
    @@ -0,0 +1,74 @@
    +/**
    + * $Id: editor_plugin_src.js 201 2007-02-12 15:56:56Z spocke $
    + *
    + * @author Moxiecode
    + * @copyright Copyright  2004-2008, Moxiecode Systems AB, All rights reserved.
    + */
    +
    +(function() {
    +	tinymce.create('tinymce.plugins.MoreBreakPlugin', {
    +		init : function(ed, url) {
    +			var pb = '<img src="' + url + '/img/trans.gif" class="mceMoreBreak mceItemNoResize" />', cls = 'mceMoreBreak', sep = ed.getParam('morebreak_separator', '<!--more-->'), pbRE;
    +
    +			pbRE = new RegExp(sep.replace(/[\?\.\*\[\]\(\)\{\}\+\^\$\:]/g, function(a) {return '\\' + a;}), 'g');
    +
    +			// Register commands
    +			ed.addCommand('mceMoreBreak', function() {
    +				ed.execCommand('mceInsertContent', 0, pb);
    +			});
    +
    +			// Register buttons
    +			ed.addButton('morebreak', {title : 'morebreak.desc', cmd : cls});
    +
    +			ed.onInit.add(function() {
    +				if (ed.settings.content_css !== false)
    +					ed.dom.loadCSS(url + "/css/content.css");
    +
    +				if (ed.theme.onResolveName) {
    +					ed.theme.onResolveName.add(function(th, o) {
    +						if (o.node.nodeName == 'IMG' && ed.dom.hasClass(o.node, cls))
    +							o.name = 'morebreak';
    +					});
    +				}
    +			});
    +
    +			ed.onClick.add(function(ed, e) {
    +				e = e.target;
    +
    +				if (e.nodeName === 'IMG' && ed.dom.hasClass(e, cls))
    +					ed.selection.select(e);
    +			});
    +
    +			ed.onNodeChange.add(function(ed, cm, n) {
    +				cm.setActive('morebreak', n.nodeName === 'IMG' && ed.dom.hasClass(n, cls));
    +			});
    +
    +			ed.onBeforeSetContent.add(function(ed, o) {
    +				o.content = o.content.replace(pbRE, pb);
    +			});
    +
    +			ed.onPostProcess.add(function(ed, o) {
    +				if (o.get)
    +					o.content = o.content.replace(/<img[^>]+>/g, function(im) {
    +						if (im.indexOf('class="mceMoreBreak') !== -1)
    +							im = sep;
    +
    +						return im;
    +					});
    +			});
    +		},
    +
    +		getInfo : function() {
    +			return {
    +				longname : 'MoreBreak',
    +				author : 'Moxiecode Systems AB',
    +				authorurl : 'http://tinymce.moxiecode.com',
    +				infourl : 'http://wiki.moxiecode.com/index.php/TinyMCE:Plugins/morebreak',
    +				version : tinymce.majorVersion + "." + tinymce.minorVersion
    +			};
    +		}
    +	});
    +
    +	// Register plugin
    +	tinymce.PluginManager.add('morebreak', tinymce.plugins.MoreBreakPlugin);
    +})();
    diff --git a/TinyMCE/tiny_mce/plugins/morebreak/img/morebreak.gif b/TinyMCE/tiny_mce/plugins/morebreak/img/morebreak.gif
    new file mode 100644
    index 0000000..acdf408
    Binary files /dev/null and b/TinyMCE/tiny_mce/plugins/morebreak/img/morebreak.gif differ
    diff --git a/TinyMCE/tiny_mce/plugins/morebreak/img/trans.gif b/TinyMCE/tiny_mce/plugins/morebreak/img/trans.gif
    new file mode 100644
    index 0000000..3884865
    Binary files /dev/null and b/TinyMCE/tiny_mce/plugins/morebreak/img/trans.gif differ
    diff --git a/TinyMCE/tiny_mce/plugins/safari/blank.htm b/TinyMCE/tiny_mce/plugins/safari/blank.htm
    new file mode 100644
    index 0000000..266808c
    --- /dev/null
    +++ b/TinyMCE/tiny_mce/plugins/safari/blank.htm
    @@ -0,0 +1 @@
    +<!-- WebKit -->
    \ No newline at end of file
    diff --git a/TinyMCE/tiny_mce/plugins/safari/editor_plugin.js b/TinyMCE/tiny_mce/plugins/safari/editor_plugin.js
    new file mode 100644
    index 0000000..794477c
    --- /dev/null
    +++ b/TinyMCE/tiny_mce/plugins/safari/editor_plugin.js
    @@ -0,0 +1 @@
    +(function(){var a=tinymce.dom.Event,c=tinymce.grep,d=tinymce.each,b=tinymce.inArray;function e(j,i,h){var g,k;g=j.createTreeWalker(i,NodeFilter.SHOW_ALL,null,false);while(k=g.nextNode()){if(h){if(!h(k)){return false}}if(k.nodeType==3&&k.nodeValue&&/[^\s\u00a0]+/.test(k.nodeValue)){return false}if(k.nodeType==1&&/^(HR|IMG|TABLE)$/.test(k.nodeName)){return false}}return true}tinymce.create("tinymce.plugins.Safari",{init:function(f){var g=this,h;if(!tinymce.isWebKit){return}g.editor=f;g.webKitFontSizes=["x-small","small","medium","large","x-large","xx-large","-webkit-xxx-large"];g.namedFontSizes=["xx-small","x-small","small","medium","large","x-large","xx-large"];f.addCommand("CreateLink",function(k,j){var m=f.selection.getNode(),l=f.dom,i;if(m&&(/^(left|right)$/i.test(l.getStyle(m,"float",1))||/^(left|right)$/i.test(l.getAttrib(m,"align")))){i=l.create("a",{href:j},m.cloneNode());m.parentNode.replaceChild(i,m);f.selection.select(i)}else{f.getDoc().execCommand("CreateLink",false,j)}});f.onKeyUp.add(function(j,o){var l,i,m,p,k;if(o.keyCode==46||o.keyCode==8){i=j.getBody();l=i.innerHTML;k=j.selection;if(i.childNodes.length==1&&!/<(img|hr)/.test(l)&&tinymce.trim(l.replace(/<[^>]+>/g,"")).length==0){j.setContent('<p><br mce_bogus="1" /></p>',{format:"raw"});p=i.firstChild;m=k.getRng();m.setStart(p,0);m.setEnd(p,0);k.setRng(m)}}});f.addCommand("FormatBlock",function(j,i){var l=f.dom,k=l.getParent(f.selection.getNode(),l.isBlock);if(k){l.replace(l.create(i),k,1)}else{f.getDoc().execCommand("FormatBlock",false,i)}});f.addCommand("mceInsertContent",function(j,i){f.getDoc().execCommand("InsertText",false,"mce_marker");f.getBody().innerHTML=f.getBody().innerHTML.replace(/mce_marker/g,f.dom.processHTML(i)+'<span id="_mce_tmp">XX</span>');f.selection.select(f.dom.get("_mce_tmp"));f.getDoc().execCommand("Delete",false," ")});f.onKeyPress.add(function(o,p){var q,v,r,l,j,k,i,u,m,t,s;if(p.keyCode==13){i=o.selection;q=i.getNode();if(p.shiftKey||o.settings.force_br_newlines&&q.nodeName!="LI"){g._insertBR(o);a.cancel(p)}if(v=h.getParent(q,"LI")){r=h.getParent(v,"OL,UL");u=o.getDoc();s=h.create("p");h.add(s,"br",{mce_bogus:"1"});if(e(u,v)){if(k=h.getParent(r.parentNode,"LI,OL,UL")){return}k=h.getParent(r,"p,h1,h2,h3,h4,h5,h6,div")||r;l=u.createRange();l.setStartBefore(k);l.setEndBefore(v);j=u.createRange();j.setStartAfter(v);j.setEndAfter(k);m=l.cloneContents();t=j.cloneContents();if(!e(u,t)){h.insertAfter(t,k)}h.insertAfter(s,k);if(!e(u,m)){h.insertAfter(m,k)}h.remove(k);k=s.firstChild;l=u.createRange();l.setStartBefore(k);l.setEndBefore(k);i.setRng(l);return a.cancel(p)}}}});f.onExecCommand.add(function(i,k){var j,m,n,l;if(k=="InsertUnorderedList"||k=="InsertOrderedList"){j=i.selection;m=i.dom;if(n=m.getParent(j.getNode(),function(o){return/^(H[1-6]|P|ADDRESS|PRE)$/.test(o.nodeName)})){l=j.getBookmark();m.remove(n,1);j.moveToBookmark(l)}}});f.onClick.add(function(i,j){j=j.target;if(j.nodeName=="IMG"){g.selElm=j;i.selection.select(j)}else{g.selElm=null}});f.onInit.add(function(){g._fixWebKitSpans()});f.onSetContent.add(function(){h=f.dom;d(["strong","b","em","u","strike","sub","sup","a"],function(i){d(c(h.select(i)).reverse(),function(l){var k=l.nodeName.toLowerCase(),j;if(k=="a"){if(l.name){h.replace(h.create("img",{mce_name:"a",name:l.name,"class":"mceItemAnchor"}),l)}return}switch(k){case"b":case"strong":if(k=="b"){k="strong"}j="font-weight: bold;";break;case"em":j="font-style: italic;";break;case"u":j="text-decoration: underline;";break;case"sub":j="vertical-align: sub;";break;case"sup":j="vertical-align: super;";break;case"strike":j="text-decoration: line-through;";break}h.replace(h.create("span",{mce_name:k,style:j,"class":"Apple-style-span"}),l,1)})})});f.onPreProcess.add(function(i,j){h=i.dom;d(c(j.node.getElementsByTagName("span")).reverse(),function(m){var k,l;if(j.get){if(h.hasClass(m,"Apple-style-span")){l=m.style.backgroundColor;switch(h.getAttrib(m,"mce_name")){case"font":if(!i.settings.convert_fonts_to_spans){h.setAttrib(m,"style","")}break;case"strong":case"em":case"sub":case"sup":h.setAttrib(m,"style","");break;case"strike":case"u":if(!i.settings.inline_styles){h.setAttrib(m,"style","")}else{h.setAttrib(m,"mce_name","")}break;default:if(!i.settings.inline_styles){h.setAttrib(m,"style","")}}if(l){m.style.backgroundColor=l}}}if(h.hasClass(m,"mceItemRemoved")){h.remove(m,1)}})});f.onPostProcess.add(function(i,j){j.content=j.content.replace(/<br \/><\/(h[1-6]|div|p|address|pre)>/g,"</$1>");j.content=j.content.replace(/ id=\"undefined\"/g,"")})},getInfo:function(){return{longname:"Safari compatibility",author:"Moxiecode Systems AB",authorurl:"http://tinymce.moxiecode.com",infourl:"http://wiki.moxiecode.com/index.php/TinyMCE:Plugins/safari",version:tinymce.majorVersion+"."+tinymce.minorVersion}},_fixWebKitSpans:function(){var g=this,f=g.editor;a.add(f.getDoc(),"DOMNodeInserted",function(h){h=h.target;if(h&&h.nodeType==1){g._fixAppleSpan(h)}})},_fixAppleSpan:function(l){var g=this.editor,m=g.dom,i=this.webKitFontSizes,f=this.namedFontSizes,j=g.settings,h,k;if(m.getAttrib(l,"mce_fixed")){return}if(l.nodeName=="SPAN"&&l.className=="Apple-style-span"){h=l.style;if(!j.convert_fonts_to_spans){if(h.fontSize){m.setAttrib(l,"mce_name","font");m.setAttrib(l,"size",b(i,h.fontSize)+1)}if(h.fontFamily){m.setAttrib(l,"mce_name","font");m.setAttrib(l,"face",h.fontFamily)}if(h.color){m.setAttrib(l,"mce_name","font");m.setAttrib(l,"color",m.toHex(h.color))}if(h.backgroundColor){m.setAttrib(l,"mce_name","font");m.setStyle(l,"background-color",h.backgroundColor)}}else{if(h.fontSize){m.setStyle(l,"fontSize",f[b(i,h.fontSize)])}}if(h.fontWeight=="bold"){m.setAttrib(l,"mce_name","strong")}if(h.fontStyle=="italic"){m.setAttrib(l,"mce_name","em")}if(h.textDecoration=="underline"){m.setAttrib(l,"mce_name","u")}if(h.textDecoration=="line-through"){m.setAttrib(l,"mce_name","strike")}if(h.verticalAlign=="super"){m.setAttrib(l,"mce_name","sup")}if(h.verticalAlign=="sub"){m.setAttrib(l,"mce_name","sub")}m.setAttrib(l,"mce_fixed","1")}},_insertBR:function(f){var j=f.dom,h=f.selection,i=h.getRng(),g;i.insertNode(g=j.create("br"));i.setStartAfter(g);i.setEndAfter(g);h.setRng(i);if(h.getSel().focusNode==g.previousSibling){h.select(j.insertAfter(j.doc.createTextNode("\u00a0"),g));h.collapse(1)}f.getWin().scrollTo(0,j.getPos(h.getRng().startContainer).y)}});tinymce.PluginManager.add("safari",tinymce.plugins.Safari)})();
    \ No newline at end of file
    diff --git a/TinyMCE/tiny_mce/themes/advanced/about.htm b/TinyMCE/tiny_mce/themes/advanced/about.htm
    new file mode 100644
    index 0000000..0718060
    --- /dev/null
    +++ b/TinyMCE/tiny_mce/themes/advanced/about.htm
    @@ -0,0 +1,56 @@
    +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> 
    +<html xmlns="http://www.w3.org/1999/xhtml">
    +<head>
    +	<title>{#advanced_dlg.about_title}</title>
    +	<script type="text/javascript" src="../../tiny_mce_popup.js"></script>
    +	<script type="text/javascript" src="../../utils/mctabs.js"></script>
    +	<script type="text/javascript" src="js/about.js"></script>
    +</head>
    +<body id="about" style="display: none">
    +		<div class="tabs">
    +			<ul>
    +				<li id="general_tab" class="current"><span><a href="javascript:mcTabs.displayTab('general_tab','general_panel');" onmousedown="return false;">{#advanced_dlg.about_general}</a></span></li>
    +				<li id="help_tab" style="display:none"><span><a href="javascript:mcTabs.displayTab('help_tab','help_panel');" onmousedown="return false;">{#advanced_dlg.about_help}</a></span></li>
    +				<li id="plugins_tab"><span><a href="javascript:mcTabs.displayTab('plugins_tab','plugins_panel');" onmousedown="return false;">{#advanced_dlg.about_plugins}</a></span></li>
    +			</ul>
    +		</div>
    +
    +		<div class="panel_wrapper">
    +			<div id="general_panel" class="panel current">
    +				<h3>{#advanced_dlg.about_title}</h3>
    +				<p>Version: <span id="version"></span> (<span id="date"></span>)</p>
    +				<p>TinyMCE is a platform independent web based Javascript HTML WYSIWYG editor control released as Open Source under <a href="../../license.txt" target="_blank">LGPL</a>
    +				by Moxiecode Systems AB. It has the ability to convert HTML TEXTAREA fields or other HTML elements to editor instances.</p>
    +				<p>Copyright &copy; 2003-2008, <a href="http://www.moxiecode.com" target="_blank">Moxiecode Systems AB</a>, All rights reserved.</p>
    +				<p>For more information about this software visit the <a href="http://tinymce.moxiecode.com" target="_blank">TinyMCE website</a>.</p>
    +
    +				<div id="buttoncontainer">
    +					<a href="http://www.moxiecode.com" target="_blank"><img src="http://tinymce.moxiecode.com/images/gotmoxie.png" alt="Got Moxie?" border="0" /></a>
    +					<a href="http://sourceforge.net/projects/tinymce/" target="_blank"><img src="http://sourceforge.net/sflogo.php?group_id=103281" alt="Hosted By Sourceforge" border="0" /></a>
    +					<a href="http://www.freshmeat.net/projects/tinymce" target="_blank"><img src="http://tinymce.moxiecode.com/images/fm.gif" alt="Also on freshmeat" border="0" /></a>
    +				</div>
    +			</div>
    +
    +			<div id="plugins_panel" class="panel">
    +				<div id="pluginscontainer">
    +					<h3>{#advanced_dlg.about_loaded}</h3>
    +
    +					<div id="plugintablecontainer">
    +					</div>
    +
    +					<p>&nbsp;</p>
    +				</div>
    +			</div>
    +
    +			<div id="help_panel" class="panel noscroll" style="overflow: visible;">
    +				<div id="iframecontainer"></div>
    +			</div>
    +		</div>
    +
    +		<div class="mceActionPanel">
    +			<div style="float: right">
    +				<input type="button" id="cancel" name="cancel" value="{#close}" onclick="tinyMCEPopup.close();" />
    +			</div>
    +		</div>
    +</body>
    +</html>
    diff --git a/TinyMCE/tiny_mce/themes/advanced/anchor.htm b/TinyMCE/tiny_mce/themes/advanced/anchor.htm
    new file mode 100644
    index 0000000..86779cb
    --- /dev/null
    +++ b/TinyMCE/tiny_mce/themes/advanced/anchor.htm
    @@ -0,0 +1,31 @@
    +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
    +<html xmlns="http://www.w3.org/1999/xhtml">
    +<head>
    +	<title>{#advanced_dlg.anchor_title}</title>
    +	<script type="text/javascript" src="../../tiny_mce_popup.js"></script>
    +	<script type="text/javascript" src="js/anchor.js"></script>
    +</head>
    +<body style="display: none">
    +<form onsubmit="AnchorDialog.update();return false;" action="#">
    +	<table border="0" cellpadding="4" cellspacing="0">
    +		<tr>
    +			<td colspan="2" class="title">{#advanced_dlg.anchor_title}</td>
    +		</tr>
    +		<tr>
    +			<td class="nowrap">{#advanced_dlg.anchor_name}:</td>
    +			<td><input name="anchorName" type="text" class="mceFocus" id="anchorName" value="" style="width: 200px" /></td>
    +		</tr>
    +	</table>
    +
    +	<div class="mceActionPanel">
    +		<div style="float: left">
    +			<input type="submit" id="insert" name="insert" value="{#update}" />
    +		</div>
    +
    +		<div style="float: right">
    +			<input type="button" id="cancel" name="cancel" value="{#cancel}" onclick="tinyMCEPopup.close();" />
    +		</div>
    +	</div>
    +</form>
    +</body>
    +</html>
    diff --git a/TinyMCE/tiny_mce/themes/advanced/charmap.htm b/TinyMCE/tiny_mce/themes/advanced/charmap.htm
    new file mode 100644
    index 0000000..f1b5144
    --- /dev/null
    +++ b/TinyMCE/tiny_mce/themes/advanced/charmap.htm
    @@ -0,0 +1,53 @@
    +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
    +<html xmlns="http://www.w3.org/1999/xhtml">
    +<head>
    +	<title>{#advanced_dlg.charmap_title}</title>
    +	<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1" />
    +	<script type="text/javascript" src="../../tiny_mce_popup.js"></script>
    +	<script type="text/javascript" src="js/charmap.js"></script>
    +</head>
    +<body id="charmap" style="display:none">
    +<table align="center" border="0" cellspacing="0" cellpadding="2">
    +    <tr>
    +        <td colspan="2" class="title">{#advanced_dlg.charmap_title}</td>
    +    </tr>
    +    <tr>
    +        <td id="charmapView" rowspan="2" align="left" valign="top">
    +			<!-- Chars will be rendered here -->
    +        </td>
    +        <td width="100" align="center" valign="top">
    +            <table border="0" cellpadding="0" cellspacing="0" width="100" style="height:100px">
    +                <tr>
    +                    <td id="codeV">&nbsp;</td>
    +                </tr>
    +                <tr>
    +                    <td id="codeN">&nbsp;</td>
    +                </tr>
    +            </table>
    +        </td>
    +    </tr>
    +    <tr>
    +        <td valign="bottom" style="padding-bottom: 3px;">
    +            <table width="100" align="center" border="0" cellpadding="2" cellspacing="0">
    +                <tr>
    +                    <td align="center" style="border-left: 1px solid #666699; border-top: 1px solid #666699; border-right: 1px solid #666699;">HTML-Code</td>
    +                </tr>
    +                <tr>
    +                    <td style="font-size: 16px; font-weight: bold; border-left: 1px solid #666699; border-bottom: 1px solid #666699; border-right: 1px solid #666699;" id="codeA" align="center">&nbsp;</td>
    +                </tr>
    +                <tr>
    +                    <td style="font-size: 1px;">&nbsp;</td>
    +                </tr>
    +                <tr>
    +                    <td align="center" style="border-left: 1px solid #666699; border-top: 1px solid #666699; border-right: 1px solid #666699;">NUM-Code</td>
    +                </tr>
    +                <tr>
    +                    <td style="font-size: 16px; font-weight: bold; border-left: 1px solid #666699; border-bottom: 1px solid #666699; border-right: 1px solid #666699;" id="codeB" align="center">&nbsp;</td>
    +                </tr>
    +            </table>
    +        </td>
    +    </tr>
    +</table>
    +
    +</body>
    +</html>
    diff --git a/TinyMCE/tiny_mce/themes/advanced/color_picker.htm b/TinyMCE/tiny_mce/themes/advanced/color_picker.htm
    new file mode 100644
    index 0000000..ae057e2
    --- /dev/null
    +++ b/TinyMCE/tiny_mce/themes/advanced/color_picker.htm
    @@ -0,0 +1,75 @@
    +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
    +<html xmlns="http://www.w3.org/1999/xhtml">
    +<head>
    +	<title>{#advanced_dlg.colorpicker_title}</title>
    +	<script type="text/javascript" src="../../tiny_mce_popup.js"></script>
    +	<script type="text/javascript" src="../../utils/mctabs.js"></script>
    +	<script type="text/javascript" src="js/color_picker.js"></script>
    +</head>
    +<body id="colorpicker" style="display: none">
    +<form onsubmit="insertAction();return false" action="#">
    +	<div class="tabs">
    +		<ul>
    +			<li id="picker_tab" class="current"><span><a href="javascript:mcTabs.displayTab('picker_tab','picker_panel');" onmousedown="return false;">{#advanced_dlg.colorpicker_picker_tab}</a></span></li>
    +			<li id="rgb_tab"><span><a href="javascript:;" onclick="generateWebColors();mcTabs.displayTab('rgb_tab','rgb_panel');" onmousedown="return false;">{#advanced_dlg.colorpicker_palette_tab}</a></span></li>
    +			<li id="named_tab"><span><a  href="javascript:;" onclick="generateNamedColors();javascript:mcTabs.displayTab('named_tab','named_panel');" onmousedown="return false;">{#advanced_dlg.colorpicker_named_tab}</a></span></li>
    +		</ul>
    +	</div>
    +
    +	<div class="panel_wrapper">
    +		<div id="picker_panel" class="panel current">
    +			<fieldset>
    +				<legend>{#advanced_dlg.colorpicker_picker_title}</legend>
    +				<div id="picker">
    +					<img id="colors" src="img/colorpicker.jpg" onclick="computeColor(event)" onmousedown="isMouseDown = true;return false;" onmouseup="isMouseDown = false;" onmousemove="if (isMouseDown && isMouseOver) computeColor(event); return false;" onmouseover="isMouseOver=true;" onmouseout="isMouseOver=false;" alt="" />
    +
    +					<div id="light">
    +						<!-- Will be filled with divs -->
    +					</div>
    +
    +					<br style="clear: both" />
    +				</div>
    +			</fieldset>
    +		</div>
    +
    +		<div id="rgb_panel" class="panel">
    +			<fieldset>
    +				<legend>{#advanced_dlg.colorpicker_palette_title}</legend>
    +				<div id="webcolors">
    +					<!-- Gets filled with web safe colors-->
    +				</div>
    +
    +				<br style="clear: both" />
    +			</fieldset>
    +		</div>
    +
    +		<div id="named_panel" class="panel">
    +			<fieldset>
    +				<legend>{#advanced_dlg.colorpicker_named_title}</legend>
    +				<div id="namedcolors">
    +					<!-- Gets filled with named colors-->
    +				</div>
    +
    +				<br style="clear: both" />
    +
    +				<div id="colornamecontainer">
    +					{#advanced_dlg.colorpicker_name} <span id="colorname"></span>
    +				</div>
    +			</fieldset>
    +		</div>
    +	</div>
    +
    +	<div class="mceActionPanel">
    +		<div style="float: left">
    +			<input type="submit" id="insert" name="insert" value="{#apply}" />
    +		</div>
    +
    +		<div id="preview"></div>
    +
    +		<div id="previewblock">
    +			<label for="color">{#advanced_dlg.colorpicker_color}</label> <input id="color" type="text" size="8" maxlength="8" class="text mceFocus" />
    +		</div>
    +	</div>
    +</form>
    +</body>
    +</html>
    diff --git a/TinyMCE/tiny_mce/themes/advanced/editor_template.js b/TinyMCE/tiny_mce/themes/advanced/editor_template.js
    new file mode 100644
    index 0000000..628c793
    --- /dev/null
    +++ b/TinyMCE/tiny_mce/themes/advanced/editor_template.js
    @@ -0,0 +1 @@
    +(function(e){var d=e.DOM,b=e.dom.Event,h=e.extend,f=e.each,a=e.util.Cookie,g,c=e.explode;e.ThemeManager.requireLangPack("advanced");e.create("tinymce.themes.AdvancedTheme",{sizes:[8,10,12,14,18,24,36],controls:{bold:["bold_desc","Bold"],italic:["italic_desc","Italic"],underline:["underline_desc","Underline"],strikethrough:["striketrough_desc","Strikethrough"],justifyleft:["justifyleft_desc","JustifyLeft"],justifycenter:["justifycenter_desc","JustifyCenter"],justifyright:["justifyright_desc","JustifyRight"],justifyfull:["justifyfull_desc","JustifyFull"],bullist:["bullist_desc","InsertUnorderedList"],numlist:["numlist_desc","InsertOrderedList"],outdent:["outdent_desc","Outdent"],indent:["indent_desc","Indent"],cut:["cut_desc","Cut"],copy:["copy_desc","Copy"],paste:["paste_desc","Paste"],undo:["undo_desc","Undo"],redo:["redo_desc","Redo"],link:["link_desc","mceLink"],unlink:["unlink_desc","unlink"],image:["image_desc","mceImage"],cleanup:["cleanup_desc","mceCleanup"],help:["help_desc","mceHelp"],code:["code_desc","mceCodeEditor"],hr:["hr_desc","InsertHorizontalRule"],removeformat:["removeformat_desc","RemoveFormat"],sub:["sub_desc","subscript"],sup:["sup_desc","superscript"],forecolor:["forecolor_desc","ForeColor"],forecolorpicker:["forecolor_desc","mceForeColor"],backcolor:["backcolor_desc","HiliteColor"],backcolorpicker:["backcolor_desc","mceBackColor"],charmap:["charmap_desc","mceCharMap"],visualaid:["visualaid_desc","mceToggleVisualAid"],anchor:["anchor_desc","mceInsertAnchor"],newdocument:["newdocument_desc","mceNewDocument"],blockquote:["blockquote_desc","mceBlockQuote"]},stateControls:["bold","italic","underline","strikethrough","bullist","numlist","justifyleft","justifycenter","justifyright","justifyfull","sub","sup","blockquote"],init:function(j,k){var l=this,m,i,n;l.editor=j;l.url=k;l.onResolveName=new e.util.Dispatcher(this);l.settings=m=h({theme_advanced_path:true,theme_advanced_toolbar_location:"bottom",theme_advanced_buttons1:"bold,italic,underline,strikethrough,|,justifyleft,justifycenter,justifyright,justifyfull,|,styleselect,formatselect",theme_advanced_buttons2:"bullist,numlist,|,outdent,indent,|,undo,redo,|,link,unlink,anchor,image,cleanup,help,code",theme_advanced_buttons3:"hr,removeformat,visualaid,|,sub,sup,|,charmap",theme_advanced_blockformats:"p,address,pre,h1,h2,h3,h4,h5,h6",theme_advanced_toolbar_align:"center",theme_advanced_fonts:"Andale Mono=andale mono,times;Arial=arial,helvetica,sans-serif;Arial Black=arial black,avant garde;Book Antiqua=book antiqua,palatino;Comic Sans MS=comic sans ms,sans-serif;Courier New=courier new,courier;Georgia=georgia,palatino;Helvetica=helvetica;Impact=impact,chicago;Symbol=symbol;Tahoma=tahoma,arial,helvetica,sans-serif;Terminal=terminal,monaco;Times New Roman=times new roman,times;Trebuchet MS=trebuchet ms,geneva;Verdana=verdana,geneva;Webdings=webdings;Wingdings=wingdings,zapf dingbats",theme_advanced_more_colors:1,theme_advanced_row_height:23,theme_advanced_resize_horizontal:1,theme_advanced_resizing_use_cookie:1,theme_advanced_font_sizes:"1,2,3,4,5,6,7",readonly:j.settings.readonly},j.settings);if(!m.font_size_style_values){m.font_size_style_values="8pt,10pt,12pt,14pt,18pt,24pt,36pt"}if(e.is(m.theme_advanced_font_sizes,"string")){m.font_size_style_values=e.explode(m.font_size_style_values);m.font_size_classes=e.explode(m.font_size_classes||"");n={};j.settings.theme_advanced_font_sizes=m.theme_advanced_font_sizes;f(j.getParam("theme_advanced_font_sizes","","hash"),function(q,p){var o;if(p==q&&q>=1&&q<=7){p=q+" ("+l.sizes[q-1]+"pt)";if(j.settings.convert_fonts_to_spans){o=m.font_size_classes[q-1];q=m.font_size_style_values[q-1]||(l.sizes[q-1]+"pt")}}if(/^\s*\./.test(q)){o=q.replace(/\./g,"")}n[p]=o?{"class":o}:{fontSize:q}});m.theme_advanced_font_sizes=n}if((i=m.theme_advanced_path_location)&&i!="none"){m.theme_advanced_statusbar_location=m.theme_advanced_path_location}if(m.theme_advanced_statusbar_location=="none"){m.theme_advanced_statusbar_location=0}j.onInit.add(function(){j.onNodeChange.add(l._nodeChanged,l);if(j.settings.content_css!==false){j.dom.loadCSS(j.baseURI.toAbsolute("themes/advanced/skins/"+j.settings.skin+"/content.css"))}});j.onSetProgressState.add(function(q,o,r){var s,t=q.id,p;if(o){l.progressTimer=setTimeout(function(){s=q.getContainer();s=s.insertBefore(d.create("DIV",{style:"position:relative"}),s.firstChild);p=d.get(q.id+"_tbl");d.add(s,"div",{id:t+"_blocker","class":"mceBlocker",style:{width:p.clientWidth+2,height:p.clientHeight+2}});d.add(s,"div",{id:t+"_progress","class":"mceProgress",style:{left:p.clientWidth/2,top:p.clientHeight/2}})},r||0)}else{d.remove(t+"_blocker");d.remove(t+"_progress");clearTimeout(l.progressTimer)}});d.loadCSS(m.editor_css?j.documentBaseURI.toAbsolute(m.editor_css):k+"/skins/"+j.settings.skin+"/ui.css");if(m.skin_variant){d.loadCSS(k+"/skins/"+j.settings.skin+"/ui_"+m.skin_variant+".css")}},createControl:function(l,i){var j,k;if(k=i.createControl(l)){return k}switch(l){case"styleselect":return this._createStyleSelect();case"formatselect":return this._createBlockFormats();case"fontselect":return this._createFontSelect();case"fontsizeselect":return this._createFontSizeSelect();case"forecolor":return this._createForeColorMenu();case"backcolor":return this._createBackColorMenu()}if((j=this.controls[l])){return i.createButton(l,{title:"advanced."+j[0],cmd:j[1],ui:j[2],value:j[3]})}},execCommand:function(k,j,l){var i=this["_"+k];if(i){i.call(this,j,l);return true}return false},_importClasses:function(j){var i=this.editor,k=i.controlManager.get("styleselect");if(k.getLength()==0){f(i.dom.getClasses(),function(l){k.add(l["class"],l["class"])})}},_createStyleSelect:function(m){var j=this,i=j.editor,k=i.controlManager,l=k.createListBox("styleselect",{title:"advanced.style_select",onselect:function(n){if(l.selectedValue===n){i.execCommand("mceSetStyleInfo",0,{command:"removeformat"});l.select();return false}else{i.execCommand("mceSetCSSClass",0,n)}}});if(l){f(i.getParam("theme_advanced_styles","","hash"),function(o,n){if(o){l.add(j.editor.translate(n),o)}});l.onPostRender.add(function(o,p){if(!l.NativeListBox){b.add(p.id+"_text","focus",j._importClasses,j);b.add(p.id+"_text","mousedown",j._importClasses,j);b.add(p.id+"_open","focus",j._importClasses,j);b.add(p.id+"_open","mousedown",j._importClasses,j)}else{b.add(p.id,"focus",j._importClasses,j)}})}return l},_createFontSelect:function(){var k,j=this,i=j.editor;k=i.controlManager.createListBox("fontselect",{title:"advanced.fontdefault",cmd:"FontName"});if(k){f(i.getParam("theme_advanced_fonts",j.settings.theme_advanced_fonts,"hash"),function(m,l){k.add(i.translate(l),m,{style:m.indexOf("dings")==-1?"font-family:"+m:""})})}return k},_createFontSizeSelect:function(){var m=this,k=m.editor,n,l=0,j=[];n=k.controlManager.createListBox("fontsizeselect",{title:"advanced.font_size",onselect:function(i){if(i.fontSize){k.execCommand("FontSize",false,i.fontSize)}else{f(m.settings.theme_advanced_font_sizes,function(p,o){if(p["class"]){j.push(p["class"])}});k.editorCommands._applyInlineStyle("span",{"class":i["class"]},{check_classes:j})}}});if(n){f(m.settings.theme_advanced_font_sizes,function(o,i){var p=o.fontSize;if(p>=1&&p<=7){p=m.sizes[parseInt(p)-1]+"pt"}n.add(i,o,{style:"font-size:"+p,"class":"mceFontSize"+(l++)+(" "+(o["class"]||""))})})}return n},_createBlockFormats:function(){var k,i={p:"advanced.paragraph",address:"advanced.address",pre:"advanced.pre",h1:"advanced.h1",h2:"advanced.h2",h3:"advanced.h3",h4:"advanced.h4",h5:"advanced.h5",h6:"advanced.h6",div:"advanced.div",blockquote:"advanced.blockquote",code:"advanced.code",dt:"advanced.dt",dd:"advanced.dd",samp:"advanced.samp"},j=this;k=j.editor.controlManager.createListBox("formatselect",{title:"advanced.block",cmd:"FormatBlock"});if(k){f(j.editor.getParam("theme_advanced_blockformats",j.settings.theme_advanced_blockformats,"hash"),function(m,l){k.add(j.editor.translate(l!=m?l:i[m]),m,{"class":"mce_formatPreview mce_"+m})})}return k},_createForeColorMenu:function(){var m,j=this,k=j.settings,l={},i;if(k.theme_advanced_more_colors){l.more_colors_func=function(){j._mceColorPicker(0,{color:m.value,func:function(n){m.setColor(n)}})}}if(i=k.theme_advanced_text_colors){l.colors=i}if(k.theme_advanced_default_foreground_color){l.default_color=k.theme_advanced_default_foreground_color}l.title="advanced.forecolor_desc";l.cmd="ForeColor";l.scope=this;m=j.editor.controlManager.createColorSplitButton("forecolor",l);return m},_createBackColorMenu:function(){var m,j=this,k=j.settings,l={},i;if(k.theme_advanced_more_colors){l.more_colors_func=function(){j._mceColorPicker(0,{color:m.value,func:function(n){m.setColor(n)}})}}if(i=k.theme_advanced_background_colors){l.colors=i}if(k.theme_advanced_default_background_color){l.default_color=k.theme_advanced_default_background_color}l.title="advanced.backcolor_desc";l.cmd="HiliteColor";l.scope=this;m=j.editor.controlManager.createColorSplitButton("backcolor",l);return m},renderUI:function(k){var m,l,q,v=this,r=v.editor,w=v.settings,u,j,i;m=j=d.create("span",{id:r.id+"_parent","class":"mceEditor "+r.settings.skin+"Skin"+(w.skin_variant?" "+r.settings.skin+"Skin"+v._ufirst(w.skin_variant):"")});if(!d.boxModel){m=d.add(m,"div",{"class":"mceOldBoxModel"})}m=u=d.add(m,"table",{id:r.id+"_tbl","class":"mceLayout",cellSpacing:0,cellPadding:0});m=q=d.add(m,"tbody");switch((w.theme_advanced_layout_manager||"").toLowerCase()){case"rowlayout":l=v._rowLayout(w,q,k);break;case"customlayout":l=r.execCallback("theme_advanced_custom_layout",w,q,k,j);break;default:l=v._simpleLayout(w,q,k,j)}m=k.targetNode;i=d.stdMode?u.getElementsByTagName("tr"):u.rows;d.addClass(i[0],"mceFirst");d.addClass(i[i.length-1],"mceLast");f(d.select("tr",q),function(o){d.addClass(o.firstChild,"mceFirst");d.addClass(o.childNodes[o.childNodes.length-1],"mceLast")});if(d.get(w.theme_advanced_toolbar_container)){d.get(w.theme_advanced_toolbar_container).appendChild(j)}else{d.insertAfter(j,m)}b.add(r.id+"_path_row","click",function(n){n=n.target;if(n.nodeName=="A"){v._sel(n.className.replace(/^.*mcePath_([0-9]+).*$/,"$1"));return b.cancel(n)}});if(!r.getParam("accessibility_focus")){b.add(d.add(j,"a",{href:"#"},"<!-- IE -->"),"focus",function(){tinyMCE.get(r.id).focus()})}if(w.theme_advanced_toolbar_location=="external"){k.deltaHeight=0}v.deltaHeight=k.deltaHeight;k.targetNode=null;return{iframeContainer:l,editorContainer:r.id+"_parent",sizeContainer:u,deltaHeight:k.deltaHeight}},getInfo:function(){return{longname:"Advanced theme",author:"Moxiecode Systems AB",authorurl:"http://tinymce.moxiecode.com",version:e.majorVersion+"."+e.minorVersion}},resizeBy:function(i,j){var k=d.get(this.editor.id+"_tbl");this.resizeTo(k.clientWidth+i,k.clientHeight+j)},resizeTo:function(i,l){var j=this.editor,k=j.settings,n=d.get(j.id+"_tbl"),o=d.get(j.id+"_ifr"),m;i=Math.max(k.theme_advanced_resizing_min_width||100,i);l=Math.max(k.theme_advanced_resizing_min_height||100,l);i=Math.min(k.theme_advanced_resizing_max_width||65535,i);l=Math.min(k.theme_advanced_resizing_max_height||65535,l);m=n.clientHeight-o.clientHeight;d.setStyle(o,"height",l-m);d.setStyles(n,{width:i,height:l})},destroy:function(){var i=this.editor.id;b.clear(i+"_resize");b.clear(i+"_path_row");b.clear(i+"_external_close")},_simpleLayout:function(y,r,k,i){var x=this,u=x.editor,v=y.theme_advanced_toolbar_location,m=y.theme_advanced_statusbar_location,l,j,q,w;if(y.readonly){l=d.add(r,"tr");l=j=d.add(l,"td",{"class":"mceIframeContainer"});return j}if(v=="top"){x._addToolbars(r,k)}if(v=="external"){l=w=d.create("div",{style:"position:relative"});l=d.add(l,"div",{id:u.id+"_external","class":"mceExternalToolbar"});d.add(l,"a",{id:u.id+"_external_close",href:"javascript:;","class":"mceExternalClose"});l=d.add(l,"table",{id:u.id+"_tblext",cellSpacing:0,cellPadding:0});q=d.add(l,"tbody");if(i.firstChild.className=="mceOldBoxModel"){i.firstChild.appendChild(w)}else{i.insertBefore(w,i.firstChild)}x._addToolbars(q,k);u.onMouseUp.add(function(){var o=d.get(u.id+"_external");d.show(o);d.hide(g);var n=b.add(u.id+"_external_close","click",function(){d.hide(u.id+"_external");b.remove(u.id+"_external_close","click",n)});d.show(o);d.setStyle(o,"top",0-d.getRect(u.id+"_tblext").h-1);d.hide(o);d.show(o);o.style.filter="";g=u.id+"_external";o=null})}if(m=="top"){x._addStatusBar(r,k)}if(!y.theme_advanced_toolbar_container){l=d.add(r,"tr");l=j=d.add(l,"td",{"class":"mceIframeContainer"})}if(v=="bottom"){x._addToolbars(r,k)}if(m=="bottom"){x._addStatusBar(r,k)}return j},_rowLayout:function(w,m,k){var v=this,p=v.editor,u,x,i=p.controlManager,l,j,r,q;u=w.theme_advanced_containers_default_class||"";x=w.theme_advanced_containers_default_align||"center";f(c(w.theme_advanced_containers||""),function(s,o){var n=w["theme_advanced_container_"+s]||"";switch(n.toLowerCase()){case"mceeditor":l=d.add(m,"tr");l=j=d.add(l,"td",{"class":"mceIframeContainer"});break;case"mceelementpath":v._addStatusBar(m,k);break;default:q=(w["theme_advanced_container_"+s+"_align"]||x).toLowerCase();q="mce"+v._ufirst(q);l=d.add(d.add(m,"tr"),"td",{"class":"mceToolbar "+(w["theme_advanced_container_"+s+"_class"]||u)+" "+q||x});r=i.createToolbar("toolbar"+o);v._addControls(n,r);d.setHTML(l,r.renderHTML());k.deltaHeight-=w.theme_advanced_row_height}});return j},_addControls:function(j,i){var k=this,l=k.settings,m,n=k.editor.controlManager;if(l.theme_advanced_disable&&!k._disabled){m={};f(c(l.theme_advanced_disable),function(o){m[o]=1});k._disabled=m}else{m=k._disabled}f(c(j),function(p){var o;if(m&&m[p]){return}if(p=="tablecontrols"){f(["table","|","row_props","cell_props","|","row_before","row_after","delete_row","|","col_before","col_after","delete_col","|","split_cells","merge_cells"],function(q){q=k.createControl(q,n);if(q){i.add(q)}});return}o=k.createControl(p,n);if(o){i.add(o)}})},_addToolbars:function(w,k){var z=this,p,m,r=z.editor,A=z.settings,y,j=r.controlManager,u,l,q=[],x;x=A.theme_advanced_toolbar_align.toLowerCase();x="mce"+z._ufirst(x);l=d.add(d.add(w,"tr"),"td",{"class":"mceToolbar "+x});if(!r.getParam("accessibility_focus")){q.push(d.createHTML("a",{href:"#",onfocus:"tinyMCE.get('"+r.id+"').focus();"},"<!-- IE -->"))}q.push(d.createHTML("a",{href:"#",accesskey:"q",title:r.getLang("advanced.toolbar_focus")},"<!-- IE -->"));for(p=1;(y=A["theme_advanced_buttons"+p]);p++){m=j.createToolbar("toolbar"+p,{"class":"mceToolbarRow"+p});if(A["theme_advanced_buttons"+p+"_add"]){y+=","+A["theme_advanced_buttons"+p+"_add"]}if(A["theme_advanced_buttons"+p+"_add_before"]){y=A["theme_advanced_buttons"+p+"_add_before"]+","+y}z._addControls(y,m);q.push(m.renderHTML());k.deltaHeight-=A.theme_advanced_row_height}q.push(d.createHTML("a",{href:"#",accesskey:"z",title:r.getLang("advanced.toolbar_focus"),onfocus:"tinyMCE.getInstanceById('"+r.id+"').focus();"},"<!-- IE -->"));d.setHTML(l,q.join(""))},_addStatusBar:function(m,j){var k,v=this,p=v.editor,w=v.settings,i,q,u,l;k=d.add(m,"tr");k=l=d.add(k,"td",{"class":"mceStatusbar"});k=d.add(k,"div",{id:p.id+"_path_row"},w.theme_advanced_path?p.translate("advanced.path")+": ":"&#160;");d.add(k,"a",{href:"#",accesskey:"x"});if(w.theme_advanced_resizing){d.add(l,"a",{id:p.id+"_resize",href:"javascript:;",onclick:"return false;","class":"mceResize"});if(w.theme_advanced_resizing_use_cookie){p.onPostRender.add(function(){var n=a.getHash("TinyMCE_"+p.id+"_size"),r=d.get(p.id+"_tbl");if(!n){return}if(w.theme_advanced_resize_horizontal){r.style.width=Math.max(10,n.cw)+"px"}r.style.height=Math.max(10,n.ch)+"px";d.get(p.id+"_ifr").style.height=Math.max(10,parseInt(n.ch)+v.deltaHeight)+"px"})}p.onPostRender.add(function(){b.add(p.id+"_resize","mousedown",function(x){var z,t,o,s,y,r;z=d.get(p.id+"_tbl");o=z.clientWidth;s=z.clientHeight;miw=w.theme_advanced_resizing_min_width||100;mih=w.theme_advanced_resizing_min_height||100;maw=w.theme_advanced_resizing_max_width||65535;mah=w.theme_advanced_resizing_max_height||65535;t=d.add(d.get(p.id+"_parent"),"div",{"class":"mcePlaceHolder"});d.setStyles(t,{width:o,height:s});d.hide(z);d.show(t);i={x:x.screenX,y:x.screenY,w:o,h:s,dx:null,dy:null};q=b.add(d.doc,"mousemove",function(B){var n,A;i.dx=B.screenX-i.x;i.dy=B.screenY-i.y;n=Math.max(miw,i.w+i.dx);A=Math.max(mih,i.h+i.dy);n=Math.min(maw,n);A=Math.min(mah,A);if(w.theme_advanced_resize_horizontal){t.style.width=n+"px"}t.style.height=A+"px";return b.cancel(B)});u=b.add(d.doc,"mouseup",function(n){var A;b.remove(d.doc,"mousemove",q);b.remove(d.doc,"mouseup",u);z.style.display="";d.remove(t);if(i.dx===null){return}A=d.get(p.id+"_ifr");if(w.theme_advanced_resize_horizontal){z.style.width=Math.max(10,i.w+i.dx)+"px"}z.style.height=Math.max(10,i.h+i.dy)+"px";A.style.height=Math.max(10,A.clientHeight+i.dy)+"px";if(w.theme_advanced_resizing_use_cookie){a.setHash("TinyMCE_"+p.id+"_size",{cw:i.w+i.dx,ch:i.h+i.dy})}});return b.cancel(x)})})}j.deltaHeight-=21;k=m=null},_nodeChanged:function(l,u,k,q){var y=this,i,r=0,x,m,z=y.settings,w,j,o;if(z.readonly){return}e.each(y.stateControls,function(n){u.setActive(n,l.queryCommandState(y.controls[n][1]))});u.setActive("visualaid",l.hasVisual);u.setDisabled("undo",!l.undoManager.hasUndo()&&!l.typing);u.setDisabled("redo",!l.undoManager.hasRedo());u.setDisabled("outdent",!l.queryCommandState("Outdent"));i=d.getParent(k,"A");if(m=u.get("link")){if(!i||!i.name){m.setDisabled(!i&&q);m.setActive(!!i)}}if(m=u.get("unlink")){m.setDisabled(!i&&q);m.setActive(!!i&&!i.name)}if(m=u.get("anchor")){m.setActive(!!i&&i.name);if(e.isWebKit){i=d.getParent(k,"IMG");m.setActive(!!i&&d.getAttrib(i,"mce_name")=="a")}}i=d.getParent(k,"IMG");if(m=u.get("image")){m.setActive(!!i&&k.className.indexOf("mceItem")==-1)}if(m=u.get("styleselect")){if(k.className){y._importClasses();m.select(k.className)}else{m.select()}}if(m=u.get("formatselect")){i=d.getParent(k,d.isBlock);if(i){m.select(i.nodeName.toLowerCase())}}if(l.settings.convert_fonts_to_spans){l.dom.getParent(k,function(p){if(p.nodeName==="SPAN"){if(!w&&p.className){w=p.className}if(!j&&p.style.fontSize){j=p.style.fontSize}if(!o&&p.style.fontFamily){o=p.style.fontFamily.replace(/[\"\']+/g,"").replace(/^([^,]+).*/,"$1").toLowerCase()}}return false});if(m=u.get("fontselect")){m.select(function(n){return n.replace(/^([^,]+).*/,"$1").toLowerCase()==o})}if(m=u.get("fontsizeselect")){m.select(function(n){if(n.fontSize&&n.fontSize===j){return true}if(n["class"]&&n["class"]===w){return true}})}}else{if(m=u.get("fontselect")){m.select(l.queryCommandValue("FontName"))}if(m=u.get("fontsizeselect")){x=l.queryCommandValue("FontSize");m.select(function(n){return n.fontSize==x})}}if(z.theme_advanced_path&&z.theme_advanced_statusbar_location){i=d.get(l.id+"_path")||d.add(l.id+"_path_row","span",{id:l.id+"_path"});d.setHTML(i,"");l.dom.getParent(k,function(A){var p=A.nodeName.toLowerCase(),s,v,t="";if(A.nodeType!=1||A.nodeName==="BR"||(d.hasClass(A,"mceItemHidden")||d.hasClass(A,"mceItemRemoved"))){return}if(x=d.getAttrib(A,"mce_name")){p=x}if(e.isIE&&A.scopeName!=="HTML"){p=A.scopeName+":"+p}p=p.replace(/mce\:/g,"");switch(p){case"b":p="strong";break;case"i":p="em";break;case"img":if(x=d.getAttrib(A,"src")){t+="src: "+x+" "}break;case"a":if(x=d.getAttrib(A,"name")){t+="name: "+x+" ";p+="#"+x}if(x=d.getAttrib(A,"href")){t+="href: "+x+" "}break;case"font":if(z.convert_fonts_to_spans){p="span"}if(x=d.getAttrib(A,"face")){t+="font: "+x+" "}if(x=d.getAttrib(A,"size")){t+="size: "+x+" "}if(x=d.getAttrib(A,"color")){t+="color: "+x+" "}break;case"span":if(x=d.getAttrib(A,"style")){t+="style: "+x+" "}break}if(x=d.getAttrib(A,"id")){t+="id: "+x+" "}if(x=A.className){x=x.replace(/(webkit-[\w\-]+|Apple-[\w\-]+|mceItem\w+|mceVisualAid)/g,"");if(x&&x.indexOf("mceItem")==-1){t+="class: "+x+" ";if(d.isBlock(A)||p=="img"||p=="span"){p+="."+x}}}p=p.replace(/(html:)/g,"");p={name:p,node:A,title:t};y.onResolveName.dispatch(y,p);t=p.title;p=p.name;v=d.create("a",{href:"javascript:;",onmousedown:"return false;",title:t,"class":"mcePath_"+(r++)},p);if(i.hasChildNodes()){i.insertBefore(d.doc.createTextNode(" \u00bb "),i.firstChild);i.insertBefore(v,i.firstChild)}else{i.appendChild(v)}},l.getBody())}},_sel:function(i){this.editor.execCommand("mceSelectNodeDepth",false,i)},_mceInsertAnchor:function(k,j){var i=this.editor;i.windowManager.open({url:e.baseURL+"/themes/advanced/anchor.htm",width:320+parseInt(i.getLang("advanced.anchor_delta_width",0)),height:90+parseInt(i.getLang("advanced.anchor_delta_height",0)),inline:true},{theme_url:this.url})},_mceCharMap:function(){var i=this.editor;i.windowManager.open({url:e.baseURL+"/themes/advanced/charmap.htm",width:550+parseInt(i.getLang("advanced.charmap_delta_width",0)),height:250+parseInt(i.getLang("advanced.charmap_delta_height",0)),inline:true},{theme_url:this.url})},_mceHelp:function(){var i=this.editor;i.windowManager.open({url:e.baseURL+"/themes/advanced/about.htm",width:480,height:380,inline:true},{theme_url:this.url})},_mceColorPicker:function(k,j){var i=this.editor;j=j||{};i.windowManager.open({url:e.baseURL+"/themes/advanced/color_picker.htm",width:375+parseInt(i.getLang("advanced.colorpicker_delta_width",0)),height:250+parseInt(i.getLang("advanced.colorpicker_delta_height",0)),close_previous:false,inline:true},{input_color:j.color,func:j.func,theme_url:this.url})},_mceCodeEditor:function(j,k){var i=this.editor;i.windowManager.open({url:e.baseURL+"/themes/advanced/source_editor.htm",width:parseInt(i.getParam("theme_advanced_source_editor_width",720)),height:parseInt(i.getParam("theme_advanced_source_editor_height",580)),inline:true,resizable:true,maximizable:true},{theme_url:this.url})},_mceImage:function(j,k){var i=this.editor;if(i.dom.getAttrib(i.selection.getNode(),"class").indexOf("mceItem")!=-1){return}i.windowManager.open({url:e.baseURL+"/themes/advanced/image.htm",width:355+parseInt(i.getLang("advanced.image_delta_width",0)),height:275+parseInt(i.getLang("advanced.image_delta_height",0)),inline:true},{theme_url:this.url})},_mceLink:function(j,k){var i=this.editor;i.windowManager.open({url:e.baseURL+"/themes/advanced/link.htm",width:310+parseInt(i.getLang("advanced.link_delta_width",0)),height:200+parseInt(i.getLang("advanced.link_delta_height",0)),inline:true},{theme_url:this.url})},_mceNewDocument:function(){var i=this.editor;i.windowManager.confirm("advanced.newdocument",function(j){if(j){i.execCommand("mceSetContent",false,"")}})},_mceForeColor:function(){var i=this;this._mceColorPicker(0,{color:i.fgColor,func:function(j){i.fgColor=j;i.editor.execCommand("ForeColor",false,j)}})},_mceBackColor:function(){var i=this;this._mceColorPicker(0,{color:i.bgColor,func:function(j){i.bgColor=j;i.editor.execCommand("HiliteColor",false,j)}})},_ufirst:function(i){return i.substring(0,1).toUpperCase()+i.substring(1)}});e.ThemeManager.add("advanced",e.themes.AdvancedTheme)}(tinymce));
    \ No newline at end of file
    diff --git a/TinyMCE/tiny_mce/themes/advanced/image.htm b/TinyMCE/tiny_mce/themes/advanced/image.htm
    new file mode 100644
    index 0000000..c04afee
    --- /dev/null
    +++ b/TinyMCE/tiny_mce/themes/advanced/image.htm
    @@ -0,0 +1,85 @@
    +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
    +<html xmlns="http://www.w3.org/1999/xhtml">
    +<head>
    +	<title>{#advanced_dlg.image_title}</title>
    +	<script type="text/javascript" src="../../tiny_mce_popup.js"></script>
    +	<script type="text/javascript" src="../../utils/mctabs.js"></script>
    +	<script type="text/javascript" src="../../utils/form_utils.js"></script>
    +	<script type="text/javascript" src="js/image.js"></script>
    +</head>
    +<body id="image" style="display: none">
    +<form onsubmit="ImageDialog.update();return false;" action="#">
    +	<div class="tabs">
    +		<ul>
    +			<li id="general_tab" class="current"><span><a href="javascript:mcTabs.displayTab('general_tab','general_panel');" onmousedown="return false;">{#advanced_dlg.image_title}</a></span></li>
    +		</ul>
    +	</div>
    +
    +	<div class="panel_wrapper">
    +		<div id="general_panel" class="panel current">
    +     <table border="0" cellpadding="4" cellspacing="0">
    +          <tr>
    +            <td class="nowrap"><label for="src">{#advanced_dlg.image_src}</label></td>
    +            <td><table border="0" cellspacing="0" cellpadding="0">
    +                <tr>
    +                  <td><input id="src" name="src" type="text" class="mceFocus" value="" style="width: 200px" onchange="ImageDialog.getImageData();" /></td>
    +                  <td id="srcbrowsercontainer">&nbsp;</td>
    +                </tr>
    +              </table></td>
    +          </tr>
    +		  <tr>
    +			<td><label for="image_list">{#advanced_dlg.image_list}</label></td>
    +			<td><select id="image_list" name="image_list" onchange="document.getElementById('src').value=this.options[this.selectedIndex].value;document.getElementById('alt').value=this.options[this.selectedIndex].text;"></select></td>
    +		  </tr>
    +          <tr>
    +            <td class="nowrap"><label for="alt">{#advanced_dlg.image_alt}</label></td>
    +            <td><input id="alt" name="alt" type="text" value="" style="width: 200px" /></td>
    +          </tr>
    +          <tr>
    +            <td class="nowrap"><label for="align">{#advanced_dlg.image_align}</label></td>
    +            <td><select id="align" name="align" onchange="ImageDialog.updateStyle();">
    +                <option value="">{#not_set}</option>
    +                <option value="baseline">{#advanced_dlg.image_align_baseline}</option>
    +                <option value="top">{#advanced_dlg.image_align_top}</option>
    +                <option value="middle">{#advanced_dlg.image_align_middle}</option>
    +                <option value="bottom">{#advanced_dlg.image_align_bottom}</option>
    +                <option value="text-top">{#advanced_dlg.image_align_texttop}</option>
    +                <option value="text-bottom">{#advanced_dlg.image_align_textbottom}</option>
    +                <option value="left">{#advanced_dlg.image_align_left}</option>
    +                <option value="right">{#advanced_dlg.image_align_right}</option>
    +              </select></td>
    +          </tr>
    +          <tr>
    +            <td class="nowrap"><label for="width">{#advanced_dlg.image_dimensions}</label></td>
    +            <td><input id="width" name="width" type="text" value="" size="3" maxlength="5" />
    +              x
    +              <input id="height" name="height" type="text" value="" size="3" maxlength="5" /></td>
    +          </tr>
    +          <tr>
    +            <td class="nowrap"><label for="border">{#advanced_dlg.image_border}</label></td>
    +            <td><input id="border" name="border" type="text" value="" size="3" maxlength="3" onchange="ImageDialog.updateStyle();" /></td>
    +          </tr>
    +          <tr>
    +            <td class="nowrap"><label for="vspace">{#advanced_dlg.image_vspace}</label></td>
    +            <td><input id="vspace" name="vspace" type="text" value="" size="3" maxlength="3" onchange="ImageDialog.updateStyle();" /></td>
    +          </tr>
    +          <tr>
    +            <td class="nowrap"><label for="hspace">{#advanced_dlg.image_hspace}</label></td>
    +            <td><input id="hspace" name="hspace" type="text" value="" size="3" maxlength="3" onchange="ImageDialog.updateStyle();" /></td>
    +          </tr>
    +        </table>
    +		</div>
    +	</div>
    +
    +	<div class="mceActionPanel">
    +		<div style="float: left">
    +			<input type="submit" id="insert" name="insert" value="{#insert}" />
    +		</div>
    +
    +		<div style="float: right">
    +			<input type="button" id="cancel" name="cancel" value="{#cancel}" onclick="tinyMCEPopup.close();" />
    +		</div>
    +	</div>
    +</form>
    +</body>
    +</html>
    diff --git a/TinyMCE/tiny_mce/themes/advanced/img/colorpicker.jpg b/TinyMCE/tiny_mce/themes/advanced/img/colorpicker.jpg
    new file mode 100644
    index 0000000..b4c542d
    Binary files /dev/null and b/TinyMCE/tiny_mce/themes/advanced/img/colorpicker.jpg differ
    diff --git a/TinyMCE/tiny_mce/themes/advanced/img/icons.gif b/TinyMCE/tiny_mce/themes/advanced/img/icons.gif
    new file mode 100644
    index 0000000..ccac36f
    Binary files /dev/null and b/TinyMCE/tiny_mce/themes/advanced/img/icons.gif differ
    diff --git a/TinyMCE/tiny_mce/themes/advanced/js/about.js b/TinyMCE/tiny_mce/themes/advanced/js/about.js
    new file mode 100644
    index 0000000..7fc8ba2
    --- /dev/null
    +++ b/TinyMCE/tiny_mce/themes/advanced/js/about.js
    @@ -0,0 +1,72 @@
    +tinyMCEPopup.requireLangPack();
    +
    +function init() {
    +	var ed, tcont;
    +
    +	tinyMCEPopup.resizeToInnerSize();
    +	ed = tinyMCEPopup.editor;
    +
    +	// Give FF some time
    +	window.setTimeout(insertHelpIFrame, 10);
    +
    +	tcont = document.getElementById('plugintablecontainer');
    +	document.getElementById('plugins_tab').style.display = 'none';
    +
    +	var html = "";
    +	html += '<table id="plugintable">';
    +	html += '<thead>';
    +	html += '<tr>';
    +	html += '<td>' + ed.getLang('advanced_dlg.about_plugin') + '</td>';
    +	html += '<td>' + ed.getLang('advanced_dlg.about_author') + '</td>';
    +	html += '<td>' + ed.getLang('advanced_dlg.about_version') + '</td>';
    +	html += '</tr>';
    +	html += '</thead>';
    +	html += '<tbody>';
    +
    +	tinymce.each(ed.plugins, function(p, n) {
    +		var info;
    +
    +		if (!p.getInfo)
    +			return;
    +
    +		html += '<tr>';
    +
    +		info = p.getInfo();
    +
    +		if (info.infourl != null && info.infourl != '')
    +			html += '<td width="50%" title="' + n + '"><a href="' + info.infourl + '" target="_blank">' + info.longname + '</a></td>';
    +		else
    +			html += '<td width="50%" title="' + n + '">' + info.longname + '</td>';
    +
    +		if (info.authorurl != null && info.authorurl != '')
    +			html += '<td width="35%"><a href="' + info.authorurl + '" target="_blank">' + info.author + '</a></td>';
    +		else
    +			html += '<td width="35%">' + info.author + '</td>';
    +
    +		html += '<td width="15%">' + info.version + '</td>';
    +		html += '</tr>';
    +
    +		document.getElementById('plugins_tab').style.display = '';
    +
    +	});
    +
    +	html += '</tbody>';
    +	html += '</table>';
    +
    +	tcont.innerHTML = html;
    +
    +	tinyMCEPopup.dom.get('version').innerHTML = tinymce.majorVersion + "." + tinymce.minorVersion;
    +	tinyMCEPopup.dom.get('date').innerHTML = tinymce.releaseDate;
    +}
    +
    +function insertHelpIFrame() {
    +	var html;
    +
    +	if (tinyMCEPopup.getParam('docs_url')) {
    +		html = '<iframe width="100%" height="300" src="' + tinyMCEPopup.editor.baseURI.toAbsolute(tinyMCEPopup.getParam('docs_url')) + '"></iframe>';
    +		document.getElementById('iframecontainer').innerHTML = html;
    +		document.getElementById('help_tab').style.display = 'block';
    +	}
    +}
    +
    +tinyMCEPopup.onInit.add(init);
    diff --git a/TinyMCE/tiny_mce/themes/advanced/js/anchor.js b/TinyMCE/tiny_mce/themes/advanced/js/anchor.js
    new file mode 100644
    index 0000000..76f4f7d
    --- /dev/null
    +++ b/TinyMCE/tiny_mce/themes/advanced/js/anchor.js
    @@ -0,0 +1,37 @@
    +tinyMCEPopup.requireLangPack();
    +
    +var AnchorDialog = {
    +	init : function(ed) {
    +		var action, elm, f = document.forms[0];
    +
    +		this.editor = ed;
    +		elm = ed.dom.getParent(ed.selection.getNode(), 'A,IMG');
    +		v = ed.dom.getAttrib(elm, 'name');
    +
    +		if (v) {
    +			this.action = 'update';
    +			f.anchorName.value = v;
    +		}
    +
    +		f.insert.value = ed.getLang(elm ? 'update' : 'insert');
    +	},
    +
    +	update : function() {
    +		var ed = this.editor;
    +		
    +		tinyMCEPopup.restoreSelection();
    +
    +		if (this.action != 'update')
    +			ed.selection.collapse(1);
    +
    +		// Webkit acts weird if empty inline element is inserted so we need to use a image instead
    +		if (tinymce.isWebKit)
    +			ed.execCommand('mceInsertContent', 0, ed.dom.createHTML('img', {mce_name : 'a', name : document.forms[0].anchorName.value, 'class' : 'mceItemAnchor'}));
    +		else
    +			ed.execCommand('mceInsertContent', 0, ed.dom.createHTML('a', {name : document.forms[0].anchorName.value, 'class' : 'mceItemAnchor'}, ''));
    +
    +		tinyMCEPopup.close();
    +	}
    +};
    +
    +tinyMCEPopup.onInit.add(AnchorDialog.init, AnchorDialog);
    diff --git a/TinyMCE/tiny_mce/themes/advanced/js/charmap.js b/TinyMCE/tiny_mce/themes/advanced/js/charmap.js
    new file mode 100644
    index 0000000..d9fd8d3
    --- /dev/null
    +++ b/TinyMCE/tiny_mce/themes/advanced/js/charmap.js
    @@ -0,0 +1,325 @@
    +tinyMCEPopup.requireLangPack();
    +
    +var charmap = [
    +	['&nbsp;',    '&#160;',  true, 'no-break space'],
    +	['&amp;',     '&#38;',   true, 'ampersand'],
    +	['&quot;',    '&#34;',   true, 'quotation mark'],
    +// finance
    +	['&cent;',    '&#162;',  true, 'cent sign'],
    +	['&euro;',    '&#8364;', true, 'euro sign'],
    +	['&pound;',   '&#163;',  true, 'pound sign'],
    +	['&yen;',     '&#165;',  true, 'yen sign'],
    +// signs
    +	['&copy;',    '&#169;',  true, 'copyright sign'],
    +	['&reg;',     '&#174;',  true, 'registered sign'],
    +	['&trade;',   '&#8482;', true, 'trade mark sign'],
    +	['&permil;',  '&#8240;', true, 'per mille sign'],
    +	['&micro;',   '&#181;',  true, 'micro sign'],
    +	['&middot;',  '&#183;',  true, 'middle dot'],
    +	['&bull;',    '&#8226;', true, 'bullet'],
    +	['&hellip;',  '&#8230;', true, 'three dot leader'],
    +	['&prime;',   '&#8242;', true, 'minutes / feet'],
    +	['&Prime;',   '&#8243;', true, 'seconds / inches'],
    +	['&sect;',    '&#167;',  true, 'section sign'],
    +	['&para;',    '&#182;',  true, 'paragraph sign'],
    +	['&szlig;',   '&#223;',  true, 'sharp s / ess-zed'],
    +// quotations
    +	['&lsaquo;',  '&#8249;', true, 'single left-pointing angle quotation mark'],
    +	['&rsaquo;',  '&#8250;', true, 'single right-pointing angle quotation mark'],
    +	['&laquo;',   '&#171;',  true, 'left pointing guillemet'],
    +	['&raquo;',   '&#187;',  true, 'right pointing guillemet'],
    +	['&lsquo;',   '&#8216;', true, 'left single quotation mark'],
    +	['&rsquo;',   '&#8217;', true, 'right single quotation mark'],
    +	['&ldquo;',   '&#8220;', true, 'left double quotation mark'],
    +	['&rdquo;',   '&#8221;', true, 'right double quotation mark'],
    +	['&sbquo;',   '&#8218;', true, 'single low-9 quotation mark'],
    +	['&bdquo;',   '&#8222;', true, 'double low-9 quotation mark'],
    +	['&lt;',      '&#60;',   true, 'less-than sign'],
    +	['&gt;',      '&#62;',   true, 'greater-than sign'],
    +	['&le;',      '&#8804;', true, 'less-than or equal to'],
    +	['&ge;',      '&#8805;', true, 'greater-than or equal to'],
    +	['&ndash;',   '&#8211;', true, 'en dash'],
    +	['&mdash;',   '&#8212;', true, 'em dash'],
    +	['&macr;',    '&#175;',  true, 'macron'],
    +	['&oline;',   '&#8254;', true, 'overline'],
    +	['&curren;',  '&#164;',  true, 'currency sign'],
    +	['&brvbar;',  '&#166;',  true, 'broken bar'],
    +	['&uml;',     '&#168;',  true, 'diaeresis'],
    +	['&iexcl;',   '&#161;',  true, 'inverted exclamation mark'],
    +	['&iquest;',  '&#191;',  true, 'turned question mark'],
    +	['&circ;',    '&#710;',  true, 'circumflex accent'],
    +	['&tilde;',   '&#732;',  true, 'small tilde'],
    +	['&deg;',     '&#176;',  true, 'degree sign'],
    +	['&minus;',   '&#8722;', true, 'minus sign'],
    +	['&plusmn;',  '&#177;',  true, 'plus-minus sign'],
    +	['&divide;',  '&#247;',  true, 'division sign'],
    +	['&frasl;',   '&#8260;', true, 'fraction slash'],
    +	['&times;',   '&#215;',  true, 'multiplication sign'],
    +	['&sup1;',    '&#185;',  true, 'superscript one'],
    +	['&sup2;',    '&#178;',  true, 'superscript two'],
    +	['&sup3;',    '&#179;',  true, 'superscript three'],
    +	['&frac14;',  '&#188;',  true, 'fraction one quarter'],
    +	['&frac12;',  '&#189;',  true, 'fraction one half'],
    +	['&frac34;',  '&#190;',  true, 'fraction three quarters'],
    +// math / logical
    +	['&fnof;',    '&#402;',  true, 'function / florin'],
    +	['&int;',     '&#8747;', true, 'integral'],
    +	['&sum;',     '&#8721;', true, 'n-ary sumation'],
    +	['&infin;',   '&#8734;', true, 'infinity'],
    +	['&radic;',   '&#8730;', true, 'square root'],
    +	['&sim;',     '&#8764;', false,'similar to'],
    +	['&cong;',    '&#8773;', false,'approximately equal to'],
    +	['&asymp;',   '&#8776;', true, 'almost equal to'],
    +	['&ne;',      '&#8800;', true, 'not equal to'],
    +	['&equiv;',   '&#8801;', true, 'identical to'],
    +	['&isin;',    '&#8712;', false,'element of'],
    +	['&notin;',   '&#8713;', false,'not an element of'],
    +	['&ni;',      '&#8715;', false,'contains as member'],
    +	['&prod;',    '&#8719;', true, 'n-ary product'],
    +	['&and;',     '&#8743;', false,'logical and'],
    +	['&or;',      '&#8744;', false,'logical or'],
    +	['&not;',     '&#172;',  true, 'not sign'],
    +	['&cap;',     '&#8745;', true, 'intersection'],
    +	['&cup;',     '&#8746;', false,'union'],
    +	['&part;',    '&#8706;', true, 'partial differential'],
    +	['&forall;',  '&#8704;', false,'for all'],
    +	['&exist;',   '&#8707;', false,'there exists'],
    +	['&empty;',   '&#8709;', false,'diameter'],
    +	['&nabla;',   '&#8711;', false,'backward difference'],
    +	['&lowast;',  '&#8727;', false,'asterisk operator'],
    +	['&prop;',    '&#8733;', false,'proportional to'],
    +	['&ang;',     '&#8736;', false,'angle'],
    +// undefined
    +	['&acute;',   '&#180;',  true, 'acute accent'],
    +	['&cedil;',   '&#184;',  true, 'cedilla'],
    +	['&ordf;',    '&#170;',  true, 'feminine ordinal indicator'],
    +	['&ordm;',    '&#186;',  true, 'masculine ordinal indicator'],
    +	['&dagger;',  '&#8224;', true, 'dagger'],
    +	['&Dagger;',  '&#8225;', true, 'double dagger'],
    +// alphabetical special chars
    +	['&Agrave;',  '&#192;',  true, 'A - grave'],
    +	['&Aacute;',  '&#193;',  true, 'A - acute'],
    +	['&Acirc;',   '&#194;',  true, 'A - circumflex'],
    +	['&Atilde;',  '&#195;',  true, 'A - tilde'],
    +	['&Auml;',    '&#196;',  true, 'A - diaeresis'],
    +	['&Aring;',   '&#197;',  true, 'A - ring above'],
    +	['&AElig;',   '&#198;',  true, 'ligature AE'],
    +	['&Ccedil;',  '&#199;',  true, 'C - cedilla'],
    +	['&Egrave;',  '&#200;',  true, 'E - grave'],
    +	['&Eacute;',  '&#201;',  true, 'E - acute'],
    +	['&Ecirc;',   '&#202;',  true, 'E - circumflex'],
    +	['&Euml;',    '&#203;',  true, 'E - diaeresis'],
    +	['&Igrave;',  '&#204;',  true, 'I - grave'],
    +	['&Iacute;',  '&#205;',  true, 'I - acute'],
    +	['&Icirc;',   '&#206;',  true, 'I - circumflex'],
    +	['&Iuml;',    '&#207;',  true, 'I - diaeresis'],
    +	['&ETH;',     '&#208;',  true, 'ETH'],
    +	['&Ntilde;',  '&#209;',  true, 'N - tilde'],
    +	['&Ograve;',  '&#210;',  true, 'O - grave'],
    +	['&Oacute;',  '&#211;',  true, 'O - acute'],
    +	['&Ocirc;',   '&#212;',  true, 'O - circumflex'],
    +	['&Otilde;',  '&#213;',  true, 'O - tilde'],
    +	['&Ouml;',    '&#214;',  true, 'O - diaeresis'],
    +	['&Oslash;',  '&#216;',  true, 'O - slash'],
    +	['&OElig;',   '&#338;',  true, 'ligature OE'],
    +	['&Scaron;',  '&#352;',  true, 'S - caron'],
    +	['&Ugrave;',  '&#217;',  true, 'U - grave'],
    +	['&Uacute;',  '&#218;',  true, 'U - acute'],
    +	['&Ucirc;',   '&#219;',  true, 'U - circumflex'],
    +	['&Uuml;',    '&#220;',  true, 'U - diaeresis'],
    +	['&Yacute;',  '&#221;',  true, 'Y - acute'],
    +	['&Yuml;',    '&#376;',  true, 'Y - diaeresis'],
    +	['&THORN;',   '&#222;',  true, 'THORN'],
    +	['&agrave;',  '&#224;',  true, 'a - grave'],
    +	['&aacute;',  '&#225;',  true, 'a - acute'],
    +	['&acirc;',   '&#226;',  true, 'a - circumflex'],
    +	['&atilde;',  '&#227;',  true, 'a - tilde'],
    +	['&auml;',    '&#228;',  true, 'a - diaeresis'],
    +	['&aring;',   '&#229;',  true, 'a - ring above'],
    +	['&aelig;',   '&#230;',  true, 'ligature ae'],
    +	['&ccedil;',  '&#231;',  true, 'c - cedilla'],
    +	['&egrave;',  '&#232;',  true, 'e - grave'],
    +	['&eacute;',  '&#233;',  true, 'e - acute'],
    +	['&ecirc;',   '&#234;',  true, 'e - circumflex'],
    +	['&euml;',    '&#235;',  true, 'e - diaeresis'],
    +	['&igrave;',  '&#236;',  true, 'i - grave'],
    +	['&iacute;',  '&#237;',  true, 'i - acute'],
    +	['&icirc;',   '&#238;',  true, 'i - circumflex'],
    +	['&iuml;',    '&#239;',  true, 'i - diaeresis'],
    +	['&eth;',     '&#240;',  true, 'eth'],
    +	['&ntilde;',  '&#241;',  true, 'n - tilde'],
    +	['&ograve;',  '&#242;',  true, 'o - grave'],
    +	['&oacute;',  '&#243;',  true, 'o - acute'],
    +	['&ocirc;',   '&#244;',  true, 'o - circumflex'],
    +	['&otilde;',  '&#245;',  true, 'o - tilde'],
    +	['&ouml;',    '&#246;',  true, 'o - diaeresis'],
    +	['&oslash;',  '&#248;',  true, 'o slash'],
    +	['&oelig;',   '&#339;',  true, 'ligature oe'],
    +	['&scaron;',  '&#353;',  true, 's - caron'],
    +	['&ugrave;',  '&#249;',  true, 'u - grave'],
    +	['&uacute;',  '&#250;',  true, 'u - acute'],
    +	['&ucirc;',   '&#251;',  true, 'u - circumflex'],
    +	['&uuml;',    '&#252;',  true, 'u - diaeresis'],
    +	['&yacute;',  '&#253;',  true, 'y - acute'],
    +	['&thorn;',   '&#254;',  true, 'thorn'],
    +	['&yuml;',    '&#255;',  true, 'y - diaeresis'],
    +    ['&Alpha;',   '&#913;',  true, 'Alpha'],
    +	['&Beta;',    '&#914;',  true, 'Beta'],
    +	['&Gamma;',   '&#915;',  true, 'Gamma'],
    +	['&Delta;',   '&#916;',  true, 'Delta'],
    +	['&Epsilon;', '&#917;',  true, 'Epsilon'],
    +	['&Zeta;',    '&#918;',  true, 'Zeta'],
    +	['&Eta;',     '&#919;',  true, 'Eta'],
    +	['&Theta;',   '&#920;',  true, 'Theta'],
    +	['&Iota;',    '&#921;',  true, 'Iota'],
    +	['&Kappa;',   '&#922;',  true, 'Kappa'],
    +	['&Lambda;',  '&#923;',  true, 'Lambda'],
    +	['&Mu;',      '&#924;',  true, 'Mu'],
    +	['&Nu;',      '&#925;',  true, 'Nu'],
    +	['&Xi;',      '&#926;',  true, 'Xi'],
    +	['&Omicron;', '&#927;',  true, 'Omicron'],
    +	['&Pi;',      '&#928;',  true, 'Pi'],
    +	['&Rho;',     '&#929;',  true, 'Rho'],
    +	['&Sigma;',   '&#931;',  true, 'Sigma'],
    +	['&Tau;',     '&#932;',  true, 'Tau'],
    +	['&Upsilon;', '&#933;',  true, 'Upsilon'],
    +	['&Phi;',     '&#934;',  true, 'Phi'],
    +	['&Chi;',     '&#935;',  true, 'Chi'],
    +	['&Psi;',     '&#936;',  true, 'Psi'],
    +	['&Omega;',   '&#937;',  true, 'Omega'],
    +	['&alpha;',   '&#945;',  true, 'alpha'],
    +	['&beta;',    '&#946;',  true, 'beta'],
    +	['&gamma;',   '&#947;',  true, 'gamma'],
    +	['&delta;',   '&#948;',  true, 'delta'],
    +	['&epsilon;', '&#949;',  true, 'epsilon'],
    +	['&zeta;',    '&#950;',  true, 'zeta'],
    +	['&eta;',     '&#951;',  true, 'eta'],
    +	['&theta;',   '&#952;',  true, 'theta'],
    +	['&iota;',    '&#953;',  true, 'iota'],
    +	['&kappa;',   '&#954;',  true, 'kappa'],
    +	['&lambda;',  '&#955;',  true, 'lambda'],
    +	['&mu;',      '&#956;',  true, 'mu'],
    +	['&nu;',      '&#957;',  true, 'nu'],
    +	['&xi;',      '&#958;',  true, 'xi'],
    +	['&omicron;', '&#959;',  true, 'omicron'],
    +	['&pi;',      '&#960;',  true, 'pi'],
    +	['&rho;',     '&#961;',  true, 'rho'],
    +	['&sigmaf;',  '&#962;',  true, 'final sigma'],
    +	['&sigma;',   '&#963;',  true, 'sigma'],
    +	['&tau;',     '&#964;',  true, 'tau'],
    +	['&upsilon;', '&#965;',  true, 'upsilon'],
    +	['&phi;',     '&#966;',  true, 'phi'],
    +	['&chi;',     '&#967;',  true, 'chi'],
    +	['&psi;',     '&#968;',  true, 'psi'],
    +	['&omega;',   '&#969;',  true, 'omega'],
    +// symbols
    +	['&alefsym;', '&#8501;', false,'alef symbol'],
    +	['&piv;',     '&#982;',  false,'pi symbol'],
    +	['&real;',    '&#8476;', false,'real part symbol'],
    +	['&thetasym;','&#977;',  false,'theta symbol'],
    +	['&upsih;',   '&#978;',  false,'upsilon - hook symbol'],
    +	['&weierp;',  '&#8472;', false,'Weierstrass p'],
    +	['&image;',   '&#8465;', false,'imaginary part'],
    +// arrows
    +	['&larr;',    '&#8592;', true, 'leftwards arrow'],
    +	['&uarr;',    '&#8593;', true, 'upwards arrow'],
    +	['&rarr;',    '&#8594;', true, 'rightwards arrow'],
    +	['&darr;',    '&#8595;', true, 'downwards arrow'],
    +	['&harr;',    '&#8596;', true, 'left right arrow'],
    +	['&crarr;',   '&#8629;', false,'carriage return'],
    +	['&lArr;',    '&#8656;', false,'leftwards double arrow'],
    +	['&uArr;',    '&#8657;', false,'upwards double arrow'],
    +	['&rArr;',    '&#8658;', false,'rightwards double arrow'],
    +	['&dArr;',    '&#8659;', false,'downwards double arrow'],
    +	['&hArr;',    '&#8660;', false,'left right double arrow'],
    +	['&there4;',  '&#8756;', false,'therefore'],
    +	['&sub;',     '&#8834;', false,'subset of'],
    +	['&sup;',     '&#8835;', false,'superset of'],
    +	['&nsub;',    '&#8836;', false,'not a subset of'],
    +	['&sube;',    '&#8838;', false,'subset of or equal to'],
    +	['&supe;',    '&#8839;', false,'superset of or equal to'],
    +	['&oplus;',   '&#8853;', false,'circled plus'],
    +	['&otimes;',  '&#8855;', false,'circled times'],
    +	['&perp;',    '&#8869;', false,'perpendicular'],
    +	['&sdot;',    '&#8901;', false,'dot operator'],
    +	['&lceil;',   '&#8968;', false,'left ceiling'],
    +	['&rceil;',   '&#8969;', false,'right ceiling'],
    +	['&lfloor;',  '&#8970;', false,'left floor'],
    +	['&rfloor;',  '&#8971;', false,'right floor'],
    +	['&lang;',    '&#9001;', false,'left-pointing angle bracket'],
    +	['&rang;',    '&#9002;', false,'right-pointing angle bracket'],
    +	['&loz;',     '&#9674;', true,'lozenge'],
    +	['&spades;',  '&#9824;', false,'black spade suit'],
    +	['&clubs;',   '&#9827;', true, 'black club suit'],
    +	['&hearts;',  '&#9829;', true, 'black heart suit'],
    +	['&diams;',   '&#9830;', true, 'black diamond suit'],
    +	['&ensp;',    '&#8194;', false,'en space'],
    +	['&emsp;',    '&#8195;', false,'em space'],
    +	['&thinsp;',  '&#8201;', false,'thin space'],
    +	['&zwnj;',    '&#8204;', false,'zero width non-joiner'],
    +	['&zwj;',     '&#8205;', false,'zero width joiner'],
    +	['&lrm;',     '&#8206;', false,'left-to-right mark'],
    +	['&rlm;',     '&#8207;', false,'right-to-left mark'],
    +	['&shy;',     '&#173;',  false,'soft hyphen']
    +];
    +
    +tinyMCEPopup.onInit.add(function() {
    +	tinyMCEPopup.dom.setHTML('charmapView', renderCharMapHTML());
    +});
    +
    +function renderCharMapHTML() {
    +	var charsPerRow = 20, tdWidth=20, tdHeight=20, i;
    +	var html = '<table border="0" cellspacing="1" cellpadding="0" width="' + (tdWidth*charsPerRow) + '"><tr height="' + tdHeight + '">';
    +	var cols=-1;
    +
    +	for (i=0; i<charmap.length; i++) {
    +		if (charmap[i][2]==true) {
    +			cols++;
    +			html += ''
    +				+ '<td class="charmap">'
    +				+ '<a onmouseover="previewChar(\'' + charmap[i][1].substring(1,charmap[i][1].length) + '\',\'' + charmap[i][0].substring(1,charmap[i][0].length) + '\',\'' + charmap[i][3] + '\');" onfocus="previewChar(\'' + charmap[i][1].substring(1,charmap[i][1].length) + '\',\'' + charmap[i][0].substring(1,charmap[i][0].length) + '\',\'' + charmap[i][3] + '\');" href="javascript:void(0)" onclick="insertChar(\'' + charmap[i][1].substring(2,charmap[i][1].length-1) + '\');" onclick="return false;" onmousedown="return false;" title="' + charmap[i][3] + '">'
    +				+ charmap[i][1]
    +				+ '</a></td>';
    +			if ((cols+1) % charsPerRow == 0)
    +				html += '</tr><tr height="' + tdHeight + '">';
    +		}
    +	 }
    +
    +	if (cols % charsPerRow > 0) {
    +		var padd = charsPerRow - (cols % charsPerRow);
    +		for (var i=0; i<padd-1; i++)
    +			html += '<td width="' + tdWidth + '" height="' + tdHeight + '" class="charmap">&nbsp;</td>';
    +	}
    +
    +	html += '</tr></table>';
    +
    +	return html;
    +}
    +
    +function insertChar(chr) {
    +	tinyMCEPopup.execCommand('mceInsertContent', false, '&#' + chr + ';');
    +
    +	// Refocus in window
    +	if (tinyMCEPopup.isWindow)
    +		window.focus();
    +
    +	tinyMCEPopup.editor.focus();
    +	tinyMCEPopup.close();
    +}
    +
    +function previewChar(codeA, codeB, codeN) {
    +	var elmA = document.getElementById('codeA');
    +	var elmB = document.getElementById('codeB');
    +	var elmV = document.getElementById('codeV');
    +	var elmN = document.getElementById('codeN');
    +
    +	if (codeA=='#160;') {
    +		elmV.innerHTML = '__';
    +	} else {
    +		elmV.innerHTML = '&' + codeA;
    +	}
    +
    +	elmB.innerHTML = '&amp;' + codeA;
    +	elmA.innerHTML = '&amp;' + codeB;
    +	elmN.innerHTML = codeN;
    +}
    diff --git a/TinyMCE/tiny_mce/themes/advanced/js/color_picker.js b/TinyMCE/tiny_mce/themes/advanced/js/color_picker.js
    new file mode 100644
    index 0000000..c1a65db
    --- /dev/null
    +++ b/TinyMCE/tiny_mce/themes/advanced/js/color_picker.js
    @@ -0,0 +1,253 @@
    +tinyMCEPopup.requireLangPack();
    +
    +var detail = 50, strhex = "0123456789abcdef", i, isMouseDown = false, isMouseOver = false;
    +
    +var colors = [
    +	"#000000","#000033","#000066","#000099","#0000cc","#0000ff","#330000","#330033",
    +	"#330066","#330099","#3300cc","#3300ff","#660000","#660033","#660066","#660099",
    +	"#6600cc","#6600ff","#990000","#990033","#990066","#990099","#9900cc","#9900ff",
    +	"#cc0000","#cc0033","#cc0066","#cc0099","#cc00cc","#cc00ff","#ff0000","#ff0033",
    +	"#ff0066","#ff0099","#ff00cc","#ff00ff","#003300","#003333","#003366","#003399",
    +	"#0033cc","#0033ff","#333300","#333333","#333366","#333399","#3333cc","#3333ff",
    +	"#663300","#663333","#663366","#663399","#6633cc","#6633ff","#993300","#993333",
    +	"#993366","#993399","#9933cc","#9933ff","#cc3300","#cc3333","#cc3366","#cc3399",
    +	"#cc33cc","#cc33ff","#ff3300","#ff3333","#ff3366","#ff3399","#ff33cc","#ff33ff",
    +	"#006600","#006633","#006666","#006699","#0066cc","#0066ff","#336600","#336633",
    +	"#336666","#336699","#3366cc","#3366ff","#666600","#666633","#666666","#666699",
    +	"#6666cc","#6666ff","#996600","#996633","#996666","#996699","#9966cc","#9966ff",
    +	"#cc6600","#cc6633","#cc6666","#cc6699","#cc66cc","#cc66ff","#ff6600","#ff6633",
    +	"#ff6666","#ff6699","#ff66cc","#ff66ff","#009900","#009933","#009966","#009999",
    +	"#0099cc","#0099ff","#339900","#339933","#339966","#339999","#3399cc","#3399ff",
    +	"#669900","#669933","#669966","#669999","#6699cc","#6699ff","#999900","#999933",
    +	"#999966","#999999","#9999cc","#9999ff","#cc9900","#cc9933","#cc9966","#cc9999",
    +	"#cc99cc","#cc99ff","#ff9900","#ff9933","#ff9966","#ff9999","#ff99cc","#ff99ff",
    +	"#00cc00","#00cc33","#00cc66","#00cc99","#00cccc","#00ccff","#33cc00","#33cc33",
    +	"#33cc66","#33cc99","#33cccc","#33ccff","#66cc00","#66cc33","#66cc66","#66cc99",
    +	"#66cccc","#66ccff","#99cc00","#99cc33","#99cc66","#99cc99","#99cccc","#99ccff",
    +	"#cccc00","#cccc33","#cccc66","#cccc99","#cccccc","#ccccff","#ffcc00","#ffcc33",
    +	"#ffcc66","#ffcc99","#ffcccc","#ffccff","#00ff00","#00ff33","#00ff66","#00ff99",
    +	"#00ffcc","#00ffff","#33ff00","#33ff33","#33ff66","#33ff99","#33ffcc","#33ffff",
    +	"#66ff00","#66ff33","#66ff66","#66ff99","#66ffcc","#66ffff","#99ff00","#99ff33",
    +	"#99ff66","#99ff99","#99ffcc","#99ffff","#ccff00","#ccff33","#ccff66","#ccff99",
    +	"#ccffcc","#ccffff","#ffff00","#ffff33","#ffff66","#ffff99","#ffffcc","#ffffff"
    +];
    +
    +var named = {
    +	'#F0F8FF':'AliceBlue','#FAEBD7':'AntiqueWhite','#00FFFF':'Aqua','#7FFFD4':'Aquamarine','#F0FFFF':'Azure','#F5F5DC':'Beige',
    +	'#FFE4C4':'Bisque','#000000':'Black','#FFEBCD':'BlanchedAlmond','#0000FF':'Blue','#8A2BE2':'BlueViolet','#A52A2A':'Brown',
    +	'#DEB887':'BurlyWood','#5F9EA0':'CadetBlue','#7FFF00':'Chartreuse','#D2691E':'Chocolate','#FF7F50':'Coral','#6495ED':'CornflowerBlue',
    +	'#FFF8DC':'Cornsilk','#DC143C':'Crimson','#00FFFF':'Cyan','#00008B':'DarkBlue','#008B8B':'DarkCyan','#B8860B':'DarkGoldenRod',
    +	'#A9A9A9':'DarkGray','#A9A9A9':'DarkGrey','#006400':'DarkGreen','#BDB76B':'DarkKhaki','#8B008B':'DarkMagenta','#556B2F':'DarkOliveGreen',
    +	'#FF8C00':'Darkorange','#9932CC':'DarkOrchid','#8B0000':'DarkRed','#E9967A':'DarkSalmon','#8FBC8F':'DarkSeaGreen','#483D8B':'DarkSlateBlue',
    +	'#2F4F4F':'DarkSlateGray','#2F4F4F':'DarkSlateGrey','#00CED1':'DarkTurquoise','#9400D3':'DarkViolet','#FF1493':'DeepPink','#00BFFF':'DeepSkyBlue',
    +	'#696969':'DimGray','#696969':'DimGrey','#1E90FF':'DodgerBlue','#B22222':'FireBrick','#FFFAF0':'FloralWhite','#228B22':'ForestGreen',
    +	'#FF00FF':'Fuchsia','#DCDCDC':'Gainsboro','#F8F8FF':'GhostWhite','#FFD700':'Gold','#DAA520':'GoldenRod','#808080':'Gray','#808080':'Grey',
    +	'#008000':'Green','#ADFF2F':'GreenYellow','#F0FFF0':'HoneyDew','#FF69B4':'HotPink','#CD5C5C':'IndianRed','#4B0082':'Indigo','#FFFFF0':'Ivory',
    +	'#F0E68C':'Khaki','#E6E6FA':'Lavender','#FFF0F5':'LavenderBlush','#7CFC00':'LawnGreen','#FFFACD':'LemonChiffon','#ADD8E6':'LightBlue',
    +	'#F08080':'LightCoral','#E0FFFF':'LightCyan','#FAFAD2':'LightGoldenRodYellow','#D3D3D3':'LightGray','#D3D3D3':'LightGrey','#90EE90':'LightGreen',
    +	'#FFB6C1':'LightPink','#FFA07A':'LightSalmon','#20B2AA':'LightSeaGreen','#87CEFA':'LightSkyBlue','#778899':'LightSlateGray','#778899':'LightSlateGrey',
    +	'#B0C4DE':'LightSteelBlue','#FFFFE0':'LightYellow','#00FF00':'Lime','#32CD32':'LimeGreen','#FAF0E6':'Linen','#FF00FF':'Magenta','#800000':'Maroon',
    +	'#66CDAA':'MediumAquaMarine','#0000CD':'MediumBlue','#BA55D3':'MediumOrchid','#9370D8':'MediumPurple','#3CB371':'MediumSeaGreen','#7B68EE':'MediumSlateBlue',
    +	'#00FA9A':'MediumSpringGreen','#48D1CC':'MediumTurquoise','#C71585':'MediumVioletRed','#191970':'MidnightBlue','#F5FFFA':'MintCream','#FFE4E1':'MistyRose','#FFE4B5':'Moccasin',
    +	'#FFDEAD':'NavajoWhite','#000080':'Navy','#FDF5E6':'OldLace','#808000':'Olive','#6B8E23':'OliveDrab','#FFA500':'Orange','#FF4500':'OrangeRed','#DA70D6':'Orchid',
    +	'#EEE8AA':'PaleGoldenRod','#98FB98':'PaleGreen','#AFEEEE':'PaleTurquoise','#D87093':'PaleVioletRed','#FFEFD5':'PapayaWhip','#FFDAB9':'PeachPuff',
    +	'#CD853F':'Peru','#FFC0CB':'Pink','#DDA0DD':'Plum','#B0E0E6':'PowderBlue','#800080':'Purple','#FF0000':'Red','#BC8F8F':'RosyBrown','#4169E1':'RoyalBlue',
    +	'#8B4513':'SaddleBrown','#FA8072':'Salmon','#F4A460':'SandyBrown','#2E8B57':'SeaGreen','#FFF5EE':'SeaShell','#A0522D':'Sienna','#C0C0C0':'Silver',
    +	'#87CEEB':'SkyBlue','#6A5ACD':'SlateBlue','#708090':'SlateGray','#708090':'SlateGrey','#FFFAFA':'Snow','#00FF7F':'SpringGreen',
    +	'#4682B4':'SteelBlue','#D2B48C':'Tan','#008080':'Teal','#D8BFD8':'Thistle','#FF6347':'Tomato','#40E0D0':'Turquoise','#EE82EE':'Violet',
    +	'#F5DEB3':'Wheat','#FFFFFF':'White','#F5F5F5':'WhiteSmoke','#FFFF00':'Yellow','#9ACD32':'YellowGreen'
    +};
    +
    +function init() {
    +	var inputColor = convertRGBToHex(tinyMCEPopup.getWindowArg('input_color'));
    +
    +	tinyMCEPopup.resizeToInnerSize();
    +
    +	generatePicker();
    +
    +	if (inputColor) {
    +		changeFinalColor(inputColor);
    +
    +		col = convertHexToRGB(inputColor);
    +
    +		if (col)
    +			updateLight(col.r, col.g, col.b);
    +	}
    +}
    +
    +function insertAction() {
    +	var color = document.getElementById("color").value, f = tinyMCEPopup.getWindowArg('func');
    +
    +	tinyMCEPopup.restoreSelection();
    +
    +	if (f)
    +		f(color);
    +
    +	tinyMCEPopup.close();
    +}
    +
    +function showColor(color, name) {
    +	if (name)
    +		document.getElementById("colorname").innerHTML = name;
    +
    +	document.getElementById("preview").style.backgroundColor = color;
    +	document.getElementById("color").value = color.toLowerCase();
    +}
    +
    +function convertRGBToHex(col) {
    +	var re = new RegExp("rgb\\s*\\(\\s*([0-9]+).*,\\s*([0-9]+).*,\\s*([0-9]+).*\\)", "gi");
    +
    +	if (!col)
    +		return col;
    +
    +	var rgb = col.replace(re, "$1,$2,$3").split(',');
    +	if (rgb.length == 3) {
    +		r = parseInt(rgb[0]).toString(16);
    +		g = parseInt(rgb[1]).toString(16);
    +		b = parseInt(rgb[2]).toString(16);
    +
    +		r = r.length == 1 ? '0' + r : r;
    +		g = g.length == 1 ? '0' + g : g;
    +		b = b.length == 1 ? '0' + b : b;
    +
    +		return "#" + r + g + b;
    +	}
    +
    +	return col;
    +}
    +
    +function convertHexToRGB(col) {
    +	if (col.indexOf('#') != -1) {
    +		col = col.replace(new RegExp('[^0-9A-F]', 'gi'), '');
    +
    +		r = parseInt(col.substring(0, 2), 16);
    +		g = parseInt(col.substring(2, 4), 16);
    +		b = parseInt(col.substring(4, 6), 16);
    +
    +		return {r : r, g : g, b : b};
    +	}
    +
    +	return null;
    +}
    +
    +function generatePicker() {
    +	var el = document.getElementById('light'), h = '', i;
    +
    +	for (i = 0; i < detail; i++){
    +		h += '<div id="gs'+i+'" style="background-color:#000000; width:15px; height:3px; border-style:none; border-width:0px;"'
    +		+ ' onclick="changeFinalColor(this.style.backgroundColor)"'
    +		+ ' onmousedown="isMouseDown = true; return false;"'
    +		+ ' onmouseup="isMouseDown = false;"'
    +		+ ' onmousemove="if (isMouseDown && isMouseOver) changeFinalColor(this.style.backgroundColor); return false;"'
    +		+ ' onmouseover="isMouseOver = true;"'
    +		+ ' onmouseout="isMouseOver = false;"'
    +		+ '></div>';
    +	}
    +
    +	el.innerHTML = h;
    +}
    +
    +function generateWebColors() {
    +	var el = document.getElementById('webcolors'), h = '', i;
    +
    +	if (el.className == 'generated')
    +		return;
    +
    +	h += '<table border="0" cellspacing="1" cellpadding="0">'
    +		+ '<tr>';
    +
    +	for (i=0; i<colors.length; i++) {
    +		h += '<td bgcolor="' + colors[i] + '" width="10" height="10">'
    +			+ '<a href="javascript:insertAction();" onfocus="showColor(\'' + colors[i] +  '\');" onmouseover="showColor(\'' + colors[i] +  '\');" style="display:block;width:10px;height:10px;overflow:hidden;">'
    +			+ '</a></td>';
    +		if ((i+1) % 18 == 0)
    +			h += '</tr><tr>';
    +	}
    +
    +	h += '</table>';
    +
    +	el.innerHTML = h;
    +	el.className = 'generated';
    +}
    +
    +function generateNamedColors() {
    +	var el = document.getElementById('namedcolors'), h = '', n, v, i = 0;
    +
    +	if (el.className == 'generated')
    +		return;
    +
    +	for (n in named) {
    +		v = named[n];
    +		h += '<a href="javascript:insertAction();" onmouseover="showColor(\'' + n +  '\',\'' + v + '\');" style="background-color: ' + n + '"><!-- IE --></a>'
    +	}
    +
    +	el.innerHTML = h;
    +	el.className = 'generated';
    +}
    +
    +function dechex(n) {
    +	return strhex.charAt(Math.floor(n / 16)) + strhex.charAt(n % 16);
    +}
    +
    +function computeColor(e) {
    +	var x, y, partWidth, partDetail, imHeight, r, g, b, coef, i, finalCoef, finalR, finalG, finalB;
    +
    +	x = e.offsetX ? e.offsetX : (e.target ? e.clientX - e.target.x : 0);
    +	y = e.offsetY ? e.offsetY : (e.target ? e.clientY - e.target.y : 0);
    +
    +	partWidth = document.getElementById('colors').width / 6;
    +	partDetail = detail / 2;
    +	imHeight = document.getElementById('colors').height;
    +
    +	r = (x >= 0)*(x < partWidth)*255 + (x >= partWidth)*(x < 2*partWidth)*(2*255 - x * 255 / partWidth) + (x >= 4*partWidth)*(x < 5*partWidth)*(-4*255 + x * 255 / partWidth) + (x >= 5*partWidth)*(x < 6*partWidth)*255;
    +	g = (x >= 0)*(x < partWidth)*(x * 255 / partWidth) + (x >= partWidth)*(x < 3*partWidth)*255	+ (x >= 3*partWidth)*(x < 4*partWidth)*(4*255 - x * 255 / partWidth);
    +	b = (x >= 2*partWidth)*(x < 3*partWidth)*(-2*255 + x * 255 / partWidth) + (x >= 3*partWidth)*(x < 5*partWidth)*255 + (x >= 5*partWidth)*(x < 6*partWidth)*(6*255 - x * 255 / partWidth);
    +
    +	coef = (imHeight - y) / imHeight;
    +	r = 128 + (r - 128) * coef;
    +	g = 128 + (g - 128) * coef;
    +	b = 128 + (b - 128) * coef;
    +
    +	changeFinalColor('#' + dechex(r) + dechex(g) + dechex(b));
    +	updateLight(r, g, b);
    +}
    +
    +function updateLight(r, g, b) {
    +	var i, partDetail = detail / 2, finalCoef, finalR, finalG, finalB, color;
    +
    +	for (i=0; i<detail; i++) {
    +		if ((i>=0) && (i<partDetail)) {
    +			finalCoef = i / partDetail;
    +			finalR = dechex(255 - (255 - r) * finalCoef);
    +			finalG = dechex(255 - (255 - g) * finalCoef);
    +			finalB = dechex(255 - (255 - b) * finalCoef);
    +		} else {
    +			finalCoef = 2 - i / partDetail;
    +			finalR = dechex(r * finalCoef);
    +			finalG = dechex(g * finalCoef);
    +			finalB = dechex(b * finalCoef);
    +		}
    +
    +		color = finalR + finalG + finalB;
    +
    +		setCol('gs' + i, '#'+color);
    +	}
    +}
    +
    +function changeFinalColor(color) {
    +	if (color.indexOf('#') == -1)
    +		color = convertRGBToHex(color);
    +
    +	setCol('preview', color);
    +	document.getElementById('color').value = color;
    +}
    +
    +function setCol(e, c) {
    +	try {
    +		document.getElementById(e).style.backgroundColor = c;
    +	} catch (ex) {
    +		// Ignore IE warning
    +	}
    +}
    +
    +tinyMCEPopup.onInit.add(init);
    diff --git a/TinyMCE/tiny_mce/themes/advanced/js/image.js b/TinyMCE/tiny_mce/themes/advanced/js/image.js
    new file mode 100644
    index 0000000..7f07a86
    --- /dev/null
    +++ b/TinyMCE/tiny_mce/themes/advanced/js/image.js
    @@ -0,0 +1,245 @@
    +var ImageDialog = {
    +	preInit : function() {
    +		var url;
    +
    +		tinyMCEPopup.requireLangPack();
    +
    +		if (url = tinyMCEPopup.getParam("external_image_list_url"))
    +			document.write('<script language="javascript" type="text/javascript" src="' + tinyMCEPopup.editor.documentBaseURI.toAbsolute(url) + '"></script>');
    +	},
    +
    +	init : function() {
    +		var f = document.forms[0], ed = tinyMCEPopup.editor;
    +
    +		// Setup browse button
    +		document.getElementById('srcbrowsercontainer').innerHTML = getBrowserHTML('srcbrowser','src','image','theme_advanced_image');
    +		if (isVisible('srcbrowser'))
    +			document.getElementById('src').style.width = '180px';
    +
    +		e = ed.selection.getNode();
    +
    +		this.fillFileList('image_list', 'tinyMCEImageList');
    +
    +		if (e.nodeName == 'IMG') {
    +			f.src.value = ed.dom.getAttrib(e, 'src');
    +			f.alt.value = ed.dom.getAttrib(e, 'alt');
    +			f.border.value = this.getAttrib(e, 'border');
    +			f.vspace.value = this.getAttrib(e, 'vspace');
    +			f.hspace.value = this.getAttrib(e, 'hspace');
    +			f.width.value = ed.dom.getAttrib(e, 'width');
    +			f.height.value = ed.dom.getAttrib(e, 'height');
    +			f.insert.value = ed.getLang('update');
    +			this.styleVal = ed.dom.getAttrib(e, 'style');
    +			selectByValue(f, 'image_list', f.src.value);
    +			selectByValue(f, 'align', this.getAttrib(e, 'align'));
    +			this.updateStyle();
    +		}
    +	},
    +
    +	fillFileList : function(id, l) {
    +		var dom = tinyMCEPopup.dom, lst = dom.get(id), v, cl;
    +
    +		l = window[l];
    +
    +		if (l && l.length > 0) {
    +			lst.options[lst.options.length] = new Option('', '');
    +
    +			tinymce.each(l, function(o) {
    +				lst.options[lst.options.length] = new Option(o[0], o[1]);
    +			});
    +		} else
    +			dom.remove(dom.getParent(id, 'tr'));
    +	},
    +
    +	update : function() {
    +		var f = document.forms[0], nl = f.elements, ed = tinyMCEPopup.editor, args = {}, el;
    +
    +		tinyMCEPopup.restoreSelection();
    +
    +		if (f.src.value === '') {
    +			if (ed.selection.getNode().nodeName == 'IMG') {
    +				ed.dom.remove(ed.selection.getNode());
    +				ed.execCommand('mceRepaint');
    +			}
    +
    +			tinyMCEPopup.close();
    +			return;
    +		}
    +
    +		if (!ed.settings.inline_styles) {
    +			args = tinymce.extend(args, {
    +				vspace : nl.vspace.value,
    +				hspace : nl.hspace.value,
    +				border : nl.border.value,
    +				align : getSelectValue(f, 'align')
    +			});
    +		} else
    +			args.style = this.styleVal;
    +
    +		tinymce.extend(args, {
    +			src : f.src.value,
    +			alt : f.alt.value,
    +			width : f.width.value,
    +			height : f.height.value
    +		});
    +
    +		el = ed.selection.getNode();
    +
    +		if (el && el.nodeName == 'IMG') {
    +			ed.dom.setAttribs(el, args);
    +		} else {
    +			ed.execCommand('mceInsertContent', false, '<img id="__mce_tmp" />', {skip_undo : 1});
    +			ed.dom.setAttribs('__mce_tmp', args);
    +			ed.dom.setAttrib('__mce_tmp', 'id', '');
    +			ed.undoManager.add();
    +		}
    +
    +		tinyMCEPopup.close();
    +	},
    +
    +	updateStyle : function() {
    +		var dom = tinyMCEPopup.dom, st, v, f = document.forms[0];
    +
    +		if (tinyMCEPopup.editor.settings.inline_styles) {
    +			st = tinyMCEPopup.dom.parseStyle(this.styleVal);
    +
    +			// Handle align
    +			v = getSelectValue(f, 'align');
    +			if (v) {
    +				if (v == 'left' || v == 'right') {
    +					st['float'] = v;
    +					delete st['vertical-align'];
    +				} else {
    +					st['vertical-align'] = v;
    +					delete st['float'];
    +				}
    +			} else {
    +				delete st['float'];
    +				delete st['vertical-align'];
    +			}
    +
    +			// Handle border
    +			v = f.border.value;
    +			if (v || v == '0') {
    +				if (v == '0')
    +					st['border'] = '0';
    +				else
    +					st['border'] = v + 'px solid black';
    +			} else
    +				delete st['border'];
    +
    +			// Handle hspace
    +			v = f.hspace.value;
    +			if (v) {
    +				delete st['margin'];
    +				st['margin-left'] = v + 'px';
    +				st['margin-right'] = v + 'px';
    +			} else {
    +				delete st['margin-left'];
    +				delete st['margin-right'];
    +			}
    +
    +			// Handle vspace
    +			v = f.vspace.value;
    +			if (v) {
    +				delete st['margin'];
    +				st['margin-top'] = v + 'px';
    +				st['margin-bottom'] = v + 'px';
    +			} else {
    +				delete st['margin-top'];
    +				delete st['margin-bottom'];
    +			}
    +
    +			// Merge
    +			st = tinyMCEPopup.dom.parseStyle(dom.serializeStyle(st));
    +			this.styleVal = dom.serializeStyle(st);
    +		}
    +	},
    +
    +	getAttrib : function(e, at) {
    +		var ed = tinyMCEPopup.editor, dom = ed.dom, v, v2;
    +
    +		if (ed.settings.inline_styles) {
    +			switch (at) {
    +				case 'align':
    +					if (v = dom.getStyle(e, 'float'))
    +						return v;
    +
    +					if (v = dom.getStyle(e, 'vertical-align'))
    +						return v;
    +
    +					break;
    +
    +				case 'hspace':
    +					v = dom.getStyle(e, 'margin-left')
    +					v2 = dom.getStyle(e, 'margin-right');
    +					if (v && v == v2)
    +						return parseInt(v.replace(/[^0-9]/g, ''));
    +
    +					break;
    +
    +				case 'vspace':
    +					v = dom.getStyle(e, 'margin-top')
    +					v2 = dom.getStyle(e, 'margin-bottom');
    +					if (v && v == v2)
    +						return parseInt(v.replace(/[^0-9]/g, ''));
    +
    +					break;
    +
    +				case 'border':
    +					v = 0;
    +
    +					tinymce.each(['top', 'right', 'bottom', 'left'], function(sv) {
    +						sv = dom.getStyle(e, 'border-' + sv + '-width');
    +
    +						// False or not the same as prev
    +						if (!sv || (sv != v && v !== 0)) {
    +							v = 0;
    +							return false;
    +						}
    +
    +						if (sv)
    +							v = sv;
    +					});
    +
    +					if (v)
    +						return parseInt(v.replace(/[^0-9]/g, ''));
    +
    +					break;
    +			}
    +		}
    +
    +		if (v = dom.getAttrib(e, at))
    +			return v;
    +
    +		return '';
    +	},
    +
    +	resetImageData : function() {
    +		var f = document.forms[0];
    +
    +		f.width.value = f.height.value = "";	
    +	},
    +
    +	updateImageData : function() {
    +		var f = document.forms[0], t = ImageDialog;
    +
    +		if (f.width.value == "")
    +			f.width.value = t.preloadImg.width;
    +
    +		if (f.height.value == "")
    +			f.height.value = t.preloadImg.height;
    +	},
    +
    +	getImageData : function() {
    +		var f = document.forms[0];
    +
    +		this.preloadImg = new Image();
    +		this.preloadImg.onload = this.updateImageData;
    +		this.preloadImg.onerror = this.resetImageData;
    +		this.preloadImg.src = tinyMCEPopup.editor.documentBaseURI.toAbsolute(f.src.value);
    +	}
    +};
    +
    +ImageDialog.preInit();
    +tinyMCEPopup.onInit.add(ImageDialog.init, ImageDialog);
    diff --git a/TinyMCE/tiny_mce/themes/advanced/js/link.js b/TinyMCE/tiny_mce/themes/advanced/js/link.js
    new file mode 100644
    index 0000000..73b9f59
    --- /dev/null
    +++ b/TinyMCE/tiny_mce/themes/advanced/js/link.js
    @@ -0,0 +1,156 @@
    +tinyMCEPopup.requireLangPack();
    +
    +var LinkDialog = {
    +	preInit : function() {
    +		var url;
    +
    +		if (url = tinyMCEPopup.getParam("external_link_list_url"))
    +			document.write('<script language="javascript" type="text/javascript" src="' + tinyMCEPopup.editor.documentBaseURI.toAbsolute(url) + '"></script>');
    +	},
    +
    +	init : function() {
    +		var f = document.forms[0], ed = tinyMCEPopup.editor;
    +
    +		// Setup browse button
    +		document.getElementById('hrefbrowsercontainer').innerHTML = getBrowserHTML('hrefbrowser', 'href', 'file', 'theme_advanced_link');
    +		if (isVisible('hrefbrowser'))
    +			document.getElementById('href').style.width = '180px';
    +
    +		this.fillClassList('class_list');
    +		this.fillFileList('link_list', 'tinyMCELinkList');
    +		this.fillTargetList('target_list');
    +
    +		if (e = ed.dom.getParent(ed.selection.getNode(), 'A')) {
    +			f.href.value = ed.dom.getAttrib(e, 'href');
    +			f.linktitle.value = ed.dom.getAttrib(e, 'title');
    +			f.insert.value = ed.getLang('update');
    +			selectByValue(f, 'link_list', f.href.value);
    +			selectByValue(f, 'target_list', ed.dom.getAttrib(e, 'target'));
    +			selectByValue(f, 'class_list', ed.dom.getAttrib(e, 'class'));
    +		}
    +	},
    +
    +	update : function() {
    +		var f = document.forms[0], ed = tinyMCEPopup.editor, e, b;
    +
    +		tinyMCEPopup.restoreSelection();
    +		e = ed.dom.getParent(ed.selection.getNode(), 'A');
    +
    +		// Remove element if there is no href
    +		if (!f.href.value) {
    +			if (e) {
    +				tinyMCEPopup.execCommand("mceBeginUndoLevel");
    +				b = ed.selection.getBookmark();
    +				ed.dom.remove(e, 1);
    +				ed.selection.moveToBookmark(b);
    +				tinyMCEPopup.execCommand("mceEndUndoLevel");
    +				tinyMCEPopup.close();
    +				return;
    +			}
    +		}
    +
    +		tinyMCEPopup.execCommand("mceBeginUndoLevel");
    +
    +		// Create new anchor elements
    +		if (e == null) {
    +			ed.getDoc().execCommand("unlink", false, null);
    +			tinyMCEPopup.execCommand("CreateLink", false, "#mce_temp_url#", {skip_undo : 1});
    +
    +			tinymce.each(ed.dom.select("a"), function(n) {
    +				if (ed.dom.getAttrib(n, 'href') == '#mce_temp_url#') {
    +					e = n;
    +
    +					ed.dom.setAttribs(e, {
    +						href : f.href.value,
    +						title : f.linktitle.value,
    +						target : f.target_list ? getSelectValue(f, "target_list") : null,
    +						'class' : f.class_list ? getSelectValue(f, "class_list") : null
    +					});
    +				}
    +			});
    +		} else {
    +			ed.dom.setAttribs(e, {
    +				href : f.href.value,
    +				title : f.linktitle.value,
    +				target : f.target_list ? getSelectValue(f, "target_list") : null,
    +				'class' : f.class_list ? getSelectValue(f, "class_list") : null
    +			});
    +		}
    +
    +		// Don't move caret if selection was image
    +		if (e.childNodes.length != 1 || e.firstChild.nodeName != 'IMG') {
    +			ed.focus();
    +			ed.selection.select(e);
    +			ed.selection.collapse(0);
    +			tinyMCEPopup.storeSelection();
    +		}
    +
    +		tinyMCEPopup.execCommand("mceEndUndoLevel");
    +		tinyMCEPopup.close();
    +	},
    +
    +	checkPrefix : function(n) {
    +		if (n.value && Validator.isEmail(n) && !/^\s*mailto:/i.test(n.value) && confirm(tinyMCEPopup.getLang('advanced_dlg.link_is_email')))
    +			n.value = 'mailto:' + n.value;
    +
    +		if (/^\s*www\./i.test(n.value) && confirm(tinyMCEPopup.getLang('advanced_dlg.link_is_external')))
    +			n.value = 'http://' + n.value;
    +	},
    +
    +	fillFileList : function(id, l) {
    +		var dom = tinyMCEPopup.dom, lst = dom.get(id), v, cl;
    +
    +		l = window[l];
    +
    +		if (l && l.length > 0) {
    +			lst.options[lst.options.length] = new Option('', '');
    +
    +			tinymce.each(l, function(o) {
    +				lst.options[lst.options.length] = new Option(o[0], o[1]);
    +			});
    +		} else
    +			dom.remove(dom.getParent(id, 'tr'));
    +	},
    +
    +	fillClassList : function(id) {
    +		var dom = tinyMCEPopup.dom, lst = dom.get(id), v, cl;
    +
    +		if (v = tinyMCEPopup.getParam('theme_advanced_styles')) {
    +			cl = [];
    +
    +			tinymce.each(v.split(';'), function(v) {
    +				var p = v.split('=');
    +
    +				cl.push({'title' : p[0], 'class' : p[1]});
    +			});
    +		} else
    +			cl = tinyMCEPopup.editor.dom.getClasses();
    +
    +		if (cl.length > 0) {
    +			lst.options[lst.options.length] = new Option(tinyMCEPopup.getLang('not_set'), '');
    +
    +			tinymce.each(cl, function(o) {
    +				lst.options[lst.options.length] = new Option(o.title || o['class'], o['class']);
    +			});
    +		} else
    +			dom.remove(dom.getParent(id, 'tr'));
    +	},
    +
    +	fillTargetList : function(id) {
    +		var dom = tinyMCEPopup.dom, lst = dom.get(id), v;
    +
    +		lst.options[lst.options.length] = new Option(tinyMCEPopup.getLang('not_set'), '');
    +		lst.options[lst.options.length] = new Option(tinyMCEPopup.getLang('advanced_dlg.link_target_same'), '_self');
    +		lst.options[lst.options.length] = new Option(tinyMCEPopup.getLang('advanced_dlg.link_target_blank'), '_blank');
    +
    +		if (v = tinyMCEPopup.getParam('theme_advanced_link_targets')) {
    +			tinymce.each(v.split(','), function(v) {
    +				v = v.split('=');
    +				lst.options[lst.options.length] = new Option(v[0], v[1]);
    +			});
    +		}
    +	}
    +};
    +
    +LinkDialog.preInit();
    +tinyMCEPopup.onInit.add(LinkDialog.init, LinkDialog);
    diff --git a/TinyMCE/tiny_mce/themes/advanced/js/source_editor.js b/TinyMCE/tiny_mce/themes/advanced/js/source_editor.js
    new file mode 100644
    index 0000000..81063ec
    --- /dev/null
    +++ b/TinyMCE/tiny_mce/themes/advanced/js/source_editor.js
    @@ -0,0 +1,62 @@
    +tinyMCEPopup.requireLangPack();
    +tinyMCEPopup.onInit.add(onLoadInit);
    +
    +function saveContent() {
    +	tinyMCEPopup.editor.setContent(document.getElementById('htmlSource').value, {source_view : true});
    +	tinyMCEPopup.close();
    +}
    +
    +function onLoadInit() {
    +	tinyMCEPopup.resizeToInnerSize();
    +
    +	// Remove Gecko spellchecking
    +	if (tinymce.isGecko)
    +		document.body.spellcheck = tinyMCEPopup.editor.getParam("gecko_spellcheck");
    +
    +	document.getElementById('htmlSource').value = tinyMCEPopup.editor.getContent({source_view : true});
    +
    +	if (tinyMCEPopup.editor.getParam("theme_advanced_source_editor_wrap", true)) {
    +		setWrap('soft');
    +		document.getElementById('wraped').checked = true;
    +	}
    +
    +	resizeInputs();
    +}
    +
    +function setWrap(val) {
    +	var v, n, s = document.getElementById('htmlSource');
    +
    +	s.wrap = val;
    +
    +	if (!tinymce.isIE) {
    +		v = s.value;
    +		n = s.cloneNode(false);
    +		n.setAttribute("wrap", val);
    +		s.parentNode.replaceChild(n, s);
    +		n.value = v;
    +	}
    +}
    +
    +function toggleWordWrap(elm) {
    +	if (elm.checked)
    +		setWrap('soft');
    +	else
    +		setWrap('off');
    +}
    +
    +var wHeight=0, wWidth=0, owHeight=0, owWidth=0;
    +
    +function resizeInputs() {
    +	var el = document.getElementById('htmlSource');
    +
    +	if (!tinymce.isIE) {
    +		 wHeight = self.innerHeight - 65;
    +		 wWidth = self.innerWidth - 16;
    +	} else {
    +		 wHeight = document.body.clientHeight - 70;
    +		 wWidth = document.body.clientWidth - 16;
    +	}
    +
    +	el.style.height = Math.abs(wHeight) + 'px';
    +	el.style.width  = Math.abs(wWidth) + 'px';
    +}
    diff --git a/TinyMCE/tiny_mce/themes/advanced/langs/typecho.js b/TinyMCE/tiny_mce/themes/advanced/langs/typecho.js
    new file mode 100644
    index 0000000..5361afd
    --- /dev/null
    +++ b/TinyMCE/tiny_mce/themes/advanced/langs/typecho.js
    @@ -0,0 +1,2 @@
    +/** hack js laguage pack */
    +//tinymce.ScriptLoader.load(tinymce.baseURL + '/langs.php');
    diff --git a/TinyMCE/tiny_mce/themes/advanced/langs/typecho_dlg.js b/TinyMCE/tiny_mce/themes/advanced/langs/typecho_dlg.js
    new file mode 100644
    index 0000000..8b13789
    --- /dev/null
    +++ b/TinyMCE/tiny_mce/themes/advanced/langs/typecho_dlg.js
    @@ -0,0 +1 @@
    +
    diff --git a/TinyMCE/tiny_mce/themes/advanced/link.htm b/TinyMCE/tiny_mce/themes/advanced/link.htm
    new file mode 100644
    index 0000000..3c5e79d
    --- /dev/null
    +++ b/TinyMCE/tiny_mce/themes/advanced/link.htm
    @@ -0,0 +1,63 @@
    +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
    +<html xmlns="http://www.w3.org/1999/xhtml">
    +<head>
    +	<title>{#advanced_dlg.link_title}</title>
    +	<script type="text/javascript" src="../../tiny_mce_popup.js"></script>
    +	<script type="text/javascript" src="../../utils/mctabs.js"></script>
    +	<script type="text/javascript" src="../../utils/form_utils.js"></script>
    +	<script type="text/javascript" src="../../utils/validate.js"></script>
    +	<script type="text/javascript" src="js/link.js"></script>
    +</head>
    +<body id="link" style="display: none">
    +<form onsubmit="LinkDialog.update();return false;" action="#">
    +	<div class="tabs">
    +		<ul>
    +			<li id="general_tab" class="current"><span><a href="javascript:mcTabs.displayTab('general_tab','general_panel');" onmousedown="return false;">{#advanced_dlg.link_title}</a></span></li>
    +		</ul>
    +	</div>
    +
    +	<div class="panel_wrapper">
    +		<div id="general_panel" class="panel current">
    +
    +		<table border="0" cellpadding="4" cellspacing="0">
    +          <tr>
    +            <td class="nowrap"><label for="href">{#advanced_dlg.link_url}</label></td>
    +            <td><table border="0" cellspacing="0" cellpadding="0"> 
    +				  <tr> 
    +					<td><input id="href" name="href" type="text" class="mceFocus" value="" style="width: 200px" onchange="LinkDialog.checkPrefix(this);" /></td> 
    +					<td id="hrefbrowsercontainer">&nbsp;</td>
    +				  </tr> 
    +				</table></td>
    +          </tr>
    +		  <tr>
    +			<td><label for="link_list">{#advanced_dlg.link_list}</label></td>
    +			<td><select id="link_list" name="link_list" onchange="document.getElementById('href').value=this.options[this.selectedIndex].value;"></select></td>
    +		  </tr>
    +		<tr>
    +			<td><label id="targetlistlabel" for="targetlist">{#advanced_dlg.link_target}</label></td>
    +			<td><select id="target_list" name="target_list"></select></td>
    +		</tr>
    +          <tr>
    +            <td class="nowrap"><label for="linktitle">{#advanced_dlg.link_titlefield}</label></td>
    +            <td><input id="linktitle" name="linktitle" type="text" value="" style="width: 200px" /></td>
    +          </tr>
    +			<tr>
    +				<td><label for="class_list">{#class_name}</label></td>
    +				<td><select id="class_list" name="class_list"></select></td>
    +			</tr>
    +        </table>
    +		</div>
    +	</div>
    +
    +	<div class="mceActionPanel">
    +		<div style="float: left">
    +			<input type="submit" id="insert" name="insert" value="{#insert}" />
    +		</div>
    +
    +		<div style="float: right">
    +			<input type="button" id="cancel" name="cancel" value="{#cancel}" onclick="tinyMCEPopup.close();" />
    +		</div>
    +	</div>
    +</form>
    +</body>
    +</html>
    diff --git a/TinyMCE/tiny_mce/themes/advanced/skins/typecho/content.css b/TinyMCE/tiny_mce/themes/advanced/skins/typecho/content.css
    new file mode 100644
    index 0000000..9bd7334
    --- /dev/null
    +++ b/TinyMCE/tiny_mce/themes/advanced/skins/typecho/content.css
    @@ -0,0 +1,36 @@
    +body, td, pre {color:#000; font-family:Georgia,"Times New Roman","Bitstream Charter",Times,serif; font-size:13px; line-height: 18px; margin:8px;}
    +body {background:#FFF;}
    +body.mceForceColors {background:#FFF; color:#000;}
    +h1 {font-size: 2em}
    +h2 {font-size: 1.5em}
    +h3 {font-size: 1.17em}
    +h4 {font-size: 1em}
    +h5 {font-size: .83em}
    +h6 {font-size: .75em}
    +.mceItemTable, .mceItemTable td, .mceItemTable th, .mceItemTable caption, .mceItemVisualAid {border: 1px dashed #BBB;}
    +a.mceItemAnchor {width:12px; line-height:6px; overflow:hidden; padding-left:12px; background:url(img/items.gif) no-repeat bottom left;}
    +img.mceItemAnchor {width:12px; height:12px; background:url(img/items.gif) no-repeat;}
    +img {border:0;}
    +table {cursor:default}
    +table td, table th {cursor:text}
    +ins {border-bottom:1px solid green; text-decoration: none; color:green}
    +del {color:red; text-decoration:line-through}
    +cite {border-bottom:1px dashed blue}
    +acronym {border-bottom:1px dotted #CCC; cursor:help}
    +abbr, html\:abbr {border-bottom:1px dashed #CCC; cursor:help}
    +code {display: block; border: 1px solid #AAAAAA; background: #F1ECDD; color: #000; line-height: 16px; overflow: auto;
    +	font-family: 'andale mono','lucida console',monospace; font-size: 12px; padding: 10px; margin: 10px 0;}
    +pre {margin: 10px 0; padding: 5px; display: block; font-size: 12px;
    +	background: #EAF7FF; border: 1px solid #D5E7F0; font-family: "Courier New",Helvetica,sans-serif;}
    +
    +/* IE */
    +* html body {
    +scrollbar-3dlight-color:#F0F0EE;
    +scrollbar-arrow-color:#676662;
    +scrollbar-base-color:#F0F0EE;
    +scrollbar-darkshadow-color:#DDD;
    +scrollbar-face-color:#E0E0DD;
    +scrollbar-highlight-color:#F0F0EE;
    +scrollbar-shadow-color:#F0F0EE;
    +scrollbar-track-color:#F5F5F5;
    +}
    diff --git a/TinyMCE/tiny_mce/themes/advanced/skins/typecho/dialog.css b/TinyMCE/tiny_mce/themes/advanced/skins/typecho/dialog.css
    new file mode 100644
    index 0000000..b99339b
    --- /dev/null
    +++ b/TinyMCE/tiny_mce/themes/advanced/skins/typecho/dialog.css
    @@ -0,0 +1,130 @@
    +/* Generic */
    +body {
    +font-family:"Lucida Grande","Lucida Sans Unicode",Tahoma,Verdana; font-size:11px;
    +scrollbar-3dlight-color:#F0F0EE;
    +scrollbar-arrow-color:#676662;
    +scrollbar-base-color:#F0F0EE;
    +scrollbar-darkshadow-color:#DDDDDD;
    +scrollbar-face-color:#E0E0DD;
    +scrollbar-highlight-color:#F0F0EE;
    +scrollbar-shadow-color:#F0F0EE;
    +scrollbar-track-color:#F5F5F5;
    +background:#DEE4C5;
    +padding:0;
    +margin:8px 8px 0 8px;
    +}
    +
    +html {background:#DEE4C5;}
    +td {font-size:9pt; padding: 0 6px 0 0; line-height: 32px;}
    +textarea {resize:none;outline:none;}
    +a:link, a:visited {color:black;}
    +a:hover {color:#2B6FB6;}
    +
    +/* Forms */
    +fieldset {margin:0; padding:4px; border:none; font-family:"Lucida Grande","Lucida Sans Unicode",Tahoma,Verdana; font-size:10pt;}
    +legend {color:#BD6800; font-weight:bold; margin-top: 10px;}
    +label.msg {display:none;}
    +label.invalid {color:#EE0000; display:inline;}
    +input.invalid {border:1px solid #EE0000;}
    +input {background:#FFF; border:1px solid #CCC;}
    +input, select, textarea {font-family:Verdana, Arial, Helvetica, sans-serif; font-size:9pt;}
    +input, select, textarea {border:1px solid #808080;}
    +input.radio {border:1px none #000000; background:transparent; vertical-align:middle;}
    +input.checkbox {border:1px none #000000; background:transparent; vertical-align:middle;}
    +.input_noborder {border:0;}
    +
    +/* Buttons */
    +#insert, #cancel, input.button, .updateButton {
    +border:0; margin:0; padding:0;
    +font-weight:bold;
    +width:94px; height:26px;
    +background: #fff url(./img/sprite.gif) repeat-x scroll center top;
    +border:1px solid #AFBA7C;
    +cursor:pointer;
    +padding-bottom:2px;
    +-moz-border-radius-topleft: 3px; -moz-border-radius-topright: 3px; -moz-border-radius-bottomleft: 3px; -moz-border-radius-bottomright: 3px;
    +-webkit-border-top-left-radius: 3px; -webkit-border-top-right-radius: 3px; -webkit-border-bottom-left-radius: 3px; -webkit-border-bottom-right-radius: 3px;
    +/* hope IE support border radius, God save me! */
    +border-top-left-radius: 3px; border-top-right-radius: 3px; border-bottom-left-radius: 3px; border-bottom-right-radius: 3px;
    +}
    +
    +#insert:hover, #cancel:hover, input.button:hover, .updateButton:hover {
    +border:1px solid #545c30;
    +background: #fff url(./img/sprite.gif) bottom repeat-x;
    +}
    +
    +/* Browse */
    +a.browse span {display:block; width:20px; height:18px; background:url(../../img/icons.gif) -860px 0; border:1px solid #FFF; margin-left:1px;}
    +.mceOldBoxModel a.browse span {width:22px; height:20px;}
    +a.browse:hover span {border:1px solid #0A246A; background-color:#B2BBD0;}
    +a.browse span.disabled {border:1px solid white; opacity:0.3; -ms-filter:'alpha(opacity=30)'; filter:alpha(opacity=30)}
    +a.browse:hover span.disabled {border:1px solid white; background-color:transparent;}
    +a.pickcolor span {display:block; width:20px; height:16px; background:url(../../img/icons.gif) -840px 0; margin-left:2px;}
    +.mceOldBoxModel a.pickcolor span {width:21px; height:17px;}
    +a.pickcolor:hover span {background-color:#B2BBD0;}
    +a.pickcolor:hover span.disabled {}
    +
    +/* Charmap */
    +table.charmap {border:1px solid #AAA; text-align:center}
    +td.charmap, #charmap a {width:18px; height:18px; color:#000; border:1px solid #AAA; text-align:center; font-size:12px; vertical-align:middle; line-height: 18px;}
    +#charmap a {display:block; color:#000; text-decoration:none; border:0}
    +#charmap a:hover {background:#CCC;color:#2B6FB6}
    +#charmap #codeN {font-size:9pt; font-family:Arial,Helvetica,sans-serif; text-align:center}
    +#charmap #codeV {font-size:40px; height:80px; border:1px solid #AAA; text-align:center}
    +
    +/* Source */
    +.wordWrapCode {vertical-align:middle; border:1px none #000000; background:transparent;}
    +.mceActionPanel {margin-top:5px;}
    +
    +/* Tabs classes */
    +.tabs {width:100%; height:26px; line-height:normal; }
    +.tabs ul {margin:0; padding:0; list-style:none;}
    +.tabs li {float:left; margin:0 2px 0 0; line-height:25px; height:26px; display:block;
    +-moz-border-radius-topleft: 3px;
    +-moz-border-radius-topright: 3px;
    +-webkit-border-top-left-radius: 3px;
    +-webkit-border-top-right-radius: 3px;
    +
    +/* hope IE support border radius, God save me! */
    +border-bottom-top-radius: 3px;
    +border-bottom-top-radius: 3px;}
    +.tabs li.current {background:#f7fbe9 url(../../../../../../images/btn.png) top repeat-x;}
    +.tabs span {float:left; display:block; padding:0px 9pt;}
    +.tabs .current span {}
    +.tabs a {text-decoration:none; font-size:13px; font-weight:bold;}
    +.tabs a:link, .tabs a:visited, .tabs a:hover {color:black;}
    +
    +/* Panels */
    +.panel_wrapper div.panel {display:none;}
    +.panel_wrapper div.current {display:block; width:100%; height:300px; overflow:visible;}
    +.panel_wrapper {padding:9pt; padding-top:5px; clear:both; background:#f7fbe9;}
    +
    +/* Columns */
    +.column {float:left;}
    +.properties {width:100%;}
    +.properties .column1 {}
    +.properties .column2 {text-align:left;}
    +
    +/* Titles */
    +h1, h2, h3, h4 {color:#2B6FB6; margin:0; padding:0; padding-top:5px;}
    +h3 {font-size:14px;}
    +.title {font-size:12px; font-weight:bold; color:#BD6800;}
    +
    +/* Dialog specific */
    +#link .panel_wrapper, #link div.current {height:90px;}
    +#image .panel_wrapper, #image div.current {height:220px;}
    +#plugintable thead {font-weight:bold; background:#DDD;}
    +#plugintable, #about #plugintable td {border:1px solid #919B9C;}
    +#plugintable {width:96%; margin-top:9pt;}
    +#pluginscontainer {height:290px; overflow:auto;}
    +#colorpicker #preview {float:right; width:50px; height:14px;line-height:1px; border:1px solid black; margin-left:5px;}
    +#colorpicker #colors {float:left; border:1px solid gray; cursor:crosshair;}
    +#colorpicker #light {border:1px solid gray; margin-left:5px; float:left;width:15px; height:150px; cursor:crosshair;}
    +#colorpicker #light div {overflow:hidden;}
    +#colorpicker #previewblock {float:right; padding-left:9pt; height:20px;}
    +#colorpicker .panel_wrapper div.current {height:190px;}
    +#colorpicker td {padding:0;}
    +#colorpicker #namedcolors {width:150px;}
    +#colorpicker #namedcolors a {display:block; float:left; width:10px; height:10px; margin:1px 1px 0 0; overflow:hidden;}
    +#colorpicker #colornamecontainer {margin-top:5px;}
    +#colorpicker #picker_panel fieldset {margin:auto;width:325px;}
    diff --git a/TinyMCE/tiny_mce/themes/advanced/skins/typecho/img/items.gif b/TinyMCE/tiny_mce/themes/advanced/skins/typecho/img/items.gif
    new file mode 100644
    index 0000000..2eafd79
    Binary files /dev/null and b/TinyMCE/tiny_mce/themes/advanced/skins/typecho/img/items.gif differ
    diff --git a/TinyMCE/tiny_mce/themes/advanced/skins/typecho/img/menu_arrow.gif b/TinyMCE/tiny_mce/themes/advanced/skins/typecho/img/menu_arrow.gif
    new file mode 100644
    index 0000000..85e31df
    Binary files /dev/null and b/TinyMCE/tiny_mce/themes/advanced/skins/typecho/img/menu_arrow.gif differ
    diff --git a/TinyMCE/tiny_mce/themes/advanced/skins/typecho/img/menu_check.gif b/TinyMCE/tiny_mce/themes/advanced/skins/typecho/img/menu_check.gif
    new file mode 100644
    index 0000000..adfdddc
    Binary files /dev/null and b/TinyMCE/tiny_mce/themes/advanced/skins/typecho/img/menu_check.gif differ
    diff --git a/TinyMCE/tiny_mce/themes/advanced/skins/typecho/img/progress.gif b/TinyMCE/tiny_mce/themes/advanced/skins/typecho/img/progress.gif
    new file mode 100644
    index 0000000..5bb90fd
    Binary files /dev/null and b/TinyMCE/tiny_mce/themes/advanced/skins/typecho/img/progress.gif differ
    diff --git a/TinyMCE/tiny_mce/themes/advanced/skins/typecho/img/sprite.gif b/TinyMCE/tiny_mce/themes/advanced/skins/typecho/img/sprite.gif
    new file mode 100644
    index 0000000..6231207
    Binary files /dev/null and b/TinyMCE/tiny_mce/themes/advanced/skins/typecho/img/sprite.gif differ
    diff --git a/TinyMCE/tiny_mce/themes/advanced/skins/typecho/img/tabs.gif b/TinyMCE/tiny_mce/themes/advanced/skins/typecho/img/tabs.gif
    new file mode 100644
    index 0000000..ce4be63
    Binary files /dev/null and b/TinyMCE/tiny_mce/themes/advanced/skins/typecho/img/tabs.gif differ
    diff --git a/TinyMCE/tiny_mce/themes/advanced/skins/typecho/ui.css b/TinyMCE/tiny_mce/themes/advanced/skins/typecho/ui.css
    new file mode 100644
    index 0000000..223a3cb
    --- /dev/null
    +++ b/TinyMCE/tiny_mce/themes/advanced/skins/typecho/ui.css
    @@ -0,0 +1,228 @@
    +/* Reset */
    +.typechoSkin table, .typechoSkin tbody, .typechoSkin a, .typechoSkin img, .typechoSkin tr, .typechoSkin div, .typechoSkin td, .typechoSkin iframe, .typechoSkin span, .typechoSkin *, .typechoSkin .mceText {border:0; margin:0; padding:0; background:transparent; white-space:nowrap; text-decoration:none; font-weight:normal; cursor:default; color:#000; vertical-align:baseline; width:auto; border-collapse:separate; text-align:left}
    +.typechoSkin a:hover, .typechoSkin a:link, .typechoSkin a:visited, .typechoSkin a:active {text-decoration:none; font-weight:normal; cursor:default; color:#000}
    +.typechoSkin table td {vertical-align:middle}
    +
    +/* Containers */
    +.typechoSkin table {background:#D3DBB3}
    +.typechoSkin iframe {display:block; background:#FFF}
    +.typechoSkin .mceToolbar {height:26px}
    +.typechoSkin .mceLeft {text-align:left}
    +.typechoSkin .mceRight {text-align:right}
    +
    +/* External */
    +.typechoSkin .mceExternalToolbar {position:absolute; border:1px solid #CCC; border-bottom:0; display:none;}
    +.typechoSkin .mceExternalToolbar td.mceToolbar {padding-right:13px;}
    +.typechoSkin .mceExternalClose {position:absolute; top:3px; right:3px; width:7px; height:7px; background:url(../../img/icons.gif) -820px 0}
    +
    +/* Layout */
    +.typechoSkin table.mceLayout {border:0; border-left:1px solid #C1CD94; border-right:1px solid #C1CD94}
    +.typechoSkin table.mceLayout tr.mceFirst td {border-top:1px solid #C1CD94}
    +.typechoSkin table.mceLayout tr.mceLast td {border-bottom:1px solid #C1CD94}
    +.typechoSkin table.mceToolbar, .typechoSkin tr.mceFirst .mceToolbar tr td, .typechoSkin tr.mceLast .mceToolbar tr td {border:0; margin:0; padding: 2px 0;}
    +.typechoSkin td.mceToolbar {padding-top:1px; vertical-align:top}
    +.typechoSkin .mceIframeContainer {border-top:1px solid #C1CD94; border-bottom:1px solid #C1CD94}
    +.typechoSkin .mceStatusbar {font-family:'MS Sans Serif',sans-serif,Verdana,Arial; font-size:9pt; line-height:16px; overflow:visible; color:#000; display:block; height:20px}
    +.typechoSkin .mceStatusbar div {float:left; margin:2px}
    +.typechoSkin .mceStatusbar a.mceResize {display:block; float:right; background:url(../../img/icons.gif) -800px 0; width:20px; height:20px; cursor:se-resize}
    +.typechoSkin .mceStatusbar a:hover {text-decoration:underline}
    +.typechoSkin table.mceToolbar {margin-left:7px}
    +.typechoSkin span.mceIcon, .typechoSkin img.mceIcon {display:block; width:20px; height:20px}
    +.typechoSkin .mceIcon {background:url(../../img/icons.gif) no-repeat 20px 20px}
    +.typechoSkin td.mceCenter {text-align:center;}
    +.typechoSkin td.mceCenter table {margin:0 auto; text-align:left;}
    +.typechoSkin td.mceRight table {margin:0 0 0 auto;}
    +
    +/* Button */
    +.typechoSkin .mceButton {display:block; border:1px solid #AFBA7C; width:20px; height:20px; margin-right:1px; padding: 2px 3px;
    +background: #fff url(./img/sprite.gif) top repeat-x;
    +-moz-border-radius-topleft: 3px; -moz-border-radius-topright: 3px; -moz-border-radius-bottomleft: 3px; -moz-border-radius-bottomright: 3px;
    +-webkit-border-top-left-radius: 3px; -webkit-border-top-right-radius: 3px; -webkit-border-bottom-left-radius: 3px; -webkit-border-bottom-right-radius: 3px;
    +/* hope IE support border radius, God save me! */
    +border-top-left-radius: 3px; border-top-right-radius: 3px; border-bottom-left-radius: 3px; border-bottom-right-radius: 3px;}
    +.typechoSkin a.mceButtonEnabled:hover {border:1px solid #545c30; background: #fff url(./img/sprite.gif) bottom repeat-x; }
    +.typechoSkin a.mceButtonActive, .typechoSkin a.mceButtonSelected {border:1px solid #545c30; background: #fff url(./img/sprite.gif) bottom repeat-x; }
    +.typechoSkin .mceButtonDisabled .mceIcon {opacity:0.3; -ms-filter:'alpha(opacity=30)'; filter:alpha(opacity=30)}
    +.typechoSkin .mceButtonLabeled {width:auto}
    +.typechoSkin .mceButtonLabeled span.mceIcon {float:left}
    +.typechoSkin span.mceButtonLabel {display:block; font-size:10px; padding:4px 6px 0 22px; font-family:Tahoma,Verdana,Arial,Helvetica}
    +.typechoSkin .mceButtonDisabled .mceButtonLabel {color:#888}
    +
    +/* Separator */
    +.typechoSkin .mceSeparator {display:block; width:2px; height:20px; margin:2px 2px 0 4px}
    +
    +/* ListBox */
    +.typechoSkin .mceListBox {direction:ltr}
    +.typechoSkin .mceListBox, .typechoSkin .mceListBox a {display:block}
    +.typechoSkin .mceListBox .mceText {padding-left:4px; width:70px; text-align:left; border:1px solid #CCC; border-right:0; background:#FFF; font-family:Tahoma,Verdana,Arial,Helvetica; font-size:11px; height:20px; line-height:20px; overflow:hidden}
    +.typechoSkin .mceListBox .mceOpen {width:9px; height:20px; background:url(../../img/icons.gif) -741px 0; margin-right:2px; border:1px solid #CCC;}
    +.typechoSkin table.mceListBoxEnabled:hover .mceText, .typechoSkin .mceListBoxHover .mceText, .typechoSkin .mceListBoxSelected .mceText {border:1px solid #A2ABC0; border-right:0; background:#FFF}
    +.typechoSkin table.mceListBoxEnabled:hover .mceOpen, .typechoSkin .mceListBoxHover .mceOpen, .typechoSkin .mceListBoxSelected .mceOpen {background-color:#FFF; border:1px solid #A2ABC0}
    +.typechoSkin .mceListBoxDisabled a.mceText {color:gray; background-color:transparent;}
    +.typechoSkin .mceListBoxMenu {overflow:auto; overflow-x:hidden}
    +.typechoSkin .mceOldBoxModel .mceListBox .mceText {height:22px}
    +.typechoSkin .mceOldBoxModel .mceListBox .mceOpen {width:11px; height:22px;}
    +.typechoSkin select.mceNativeListBox {font-family:'MS Sans Serif',sans-serif,Verdana,Arial; font-size:7pt; background:#F0F0EE; border:1px solid gray; margin-right:2px;}
    +
    +/* SplitButton */
    +.typechoSkin .mceSplitButton {width:32px; height:20px; direction:ltr}
    +.typechoSkin .mceSplitButton a, .typechoSkin .mceSplitButton span {height:20px; display:block}
    +.typechoSkin .mceSplitButton a.mceAction {width:20px; border:1px solid #999; border-right:0; padding: 2px 3px; 
    +background: #fff url(./img/sprite.gif) top repeat-x;
    +-moz-border-radius-topleft: 3px; -moz-border-radius-bottomleft: 3px;
    +-webkit-border-top-left-radius: 3px; -webkit-border-bottom-left-radius: 3px;
    +/* hope IE support border radius, God save me! */
    +border-top-left-radius: 3px; border-bottom-left-radius: 3px;}
    +.typechoSkin .mceSplitButton span.mceAction {width:20px; background:url(../../img/icons.gif) 20px 20px;}
    +.typechoSkin .mceSplitButton a.mceOpen {width:9px; background: #fff url(./img/sprite.gif) top repeat-x; border:1px solid #999; padding: 2px 1px; margin-right: 1px;
    +-moz-border-radius-topright: 3px; -moz-border-radius-bottomright: 3px;
    +-webkit-border-top-right-radius: 3px; -webkit-border-bottom-right-radius: 3px;
    +/* hope IE support border radius, God save me! */
    +border-top-right-radius: 3px; border-bottom-right-radius: 3px;}
    +.typechoSkin .mceSplitButton span.mceOpen {background:url(../../img/icons.gif) -741px 0;}
    +.typechoSkin table.mceSplitButtonEnabled:hover a.mceAction, .typechoSkin .mceSplitButtonHover a.mceAction, .typechoSkin .mceSplitButtonSelected a.mceAction {border:1px solid #555; border-right:0; background: #fff url(./img/sprite.gif) bottom repeat-x;}
    +.typechoSkin table.mceSplitButtonEnabled:hover a.mceOpen, .typechoSkin .mceSplitButtonHover a.mceOpen, .typechoSkin .mceSplitButtonSelected a.mceOpen {background: #fff url(./img/sprite.gif) bottom repeat-x; border:1px solid #555;}
    +.typechoSkin .mceSplitButtonDisabled .mceAction, .typechoSkin .mceSplitButtonDisabled a.mceOpen {opacity:0.3; -ms-filter:'alpha(opacity=30)'; filter:alpha(opacity=30)}
    +.typechoSkin .mceSplitButtonActive a.mceAction {border:1px solid #0A246A; background-color:#C2CBE0}
    +.typechoSkin .mceSplitButtonActive a.mceOpen {border-left:0;}
    +
    +/* ColorSplitButton */
    +.typechoSkin div.mceColorSplitMenu table {background:#FFF; border:1px solid gray}
    +.typechoSkin .mceColorSplitMenu td {padding:2px}
    +.typechoSkin .mceColorSplitMenu a {display:block; width:9px; height:9px; overflow:hidden; border:1px solid #808080}
    +.typechoSkin .mceColorSplitMenu td.mceMoreColors {padding:1px 3px 1px 1px}
    +.typechoSkin .mceColorSplitMenu a.mceMoreColors {width:100%; height:auto; text-align:center; font-family:Tahoma,Verdana,Arial,Helvetica; font-size:11px; line-height:20px; border:1px solid #FFF}
    +.typechoSkin .mceColorSplitMenu a.mceMoreColors:hover {border:1px solid #0A246A; background-color:#B6BDD2}
    +.typechoSkin a.mceMoreColors:hover {border:1px solid #0A246A}
    +.typechoSkin .mceColorPreview {margin-left:2px; width:16px; height:4px; overflow:hidden; background:#9a9b9a}
    +.typechoSkin .mce_forecolor span.mceAction, .typechoSkin .mce_backcolor span.mceAction {overflow:hidden; height:16px}
    +
    +/* Menu */
    +.typechoSkin .mceMenu {position:absolute; left:0; top:0; z-index:1000; border:1px solid #D4D0C8}
    +.typechoSkin .mceNoIcons span.mceIcon {width:0;}
    +.typechoSkin .mceNoIcons a .mceText {padding-left:10px}
    +.typechoSkin .mceMenu table {background:#FFF}
    +.typechoSkin .mceMenu a, .typechoSkin .mceMenu span, .typechoSkin .mceMenu {display:block}
    +.typechoSkin .mceMenu td {height:20px}
    +.typechoSkin .mceMenu a {position:relative;padding:3px 0 4px 0}
    +.typechoSkin .mceMenu .mceText {position:relative; display:block; font-family:Tahoma,Verdana,Arial,Helvetica; color:#000; cursor:default; margin:0; padding:0 25px 0 25px; display:block}
    +.typechoSkin .mceMenu span.mceText, .typechoSkin .mceMenu .mcePreview {font-size:11px}
    +.typechoSkin .mceMenu pre.mceText {font-family:Monospace}
    +.typechoSkin .mceMenu .mceIcon {position:absolute; top:0; left:0; width:22px;}
    +.typechoSkin .mceMenu .mceMenuItemEnabled a:hover, .typechoSkin .mceMenu .mceMenuItemActive {background-color:#dbecf3}
    +.typechoSkin td.mceMenuItemSeparator {background:#DDD; height:1px}
    +.typechoSkin .mceMenuItemTitle a {border:0; background:#EEE; border-bottom:1px solid #DDD}
    +.typechoSkin .mceMenuItemTitle span.mceText {color:#000; font-weight:bold; padding-left:4px}
    +.typechoSkin .mceMenuItemDisabled .mceText {color:#888}
    +.typechoSkin .mceMenuItemSelected .mceIcon {background:url(img/menu_check.gif)}
    +.typechoSkin .mceNoIcons .mceMenuItemSelected a {background:url(img/menu_arrow.gif) no-repeat -6px center}
    +.typechoSkin .mceMenu span.mceMenuLine {display:none}
    +.typechoSkin .mceMenuItemSub a {background:url(img/menu_arrow.gif) no-repeat top right;}
    +
    +/* Progress,Resize */
    +.typechoSkin .mceBlocker {position:absolute; left:0; top:0; z-index:1000; opacity:0.5; -ms-filter:'alpha(opacity=50)'; filter:alpha(opacity=50); background:#FFF}
    +.typechoSkin .mceProgress {position:absolute; left:0; top:0; z-index:1001; background:url(img/progress.gif) no-repeat; width:32px; height:32px; margin:-16px 0 0 -16px}
    +.typechoSkin .mcePlaceHolder {border:1px dotted gray}
    +
    +/* Formats */
    +.typechoSkin .mce_formatPreview a {font-size:10px}
    +.typechoSkin .mce_p span.mceText {}
    +.typechoSkin .mce_address span.mceText {font-style:italic}
    +.typechoSkin .mce_pre span.mceText {font-family:monospace}
    +.typechoSkin .mce_h1 span.mceText {font-weight:bolder; font-size: 2em}
    +.typechoSkin .mce_h2 span.mceText {font-weight:bolder; font-size: 1.5em}
    +.typechoSkin .mce_h3 span.mceText {font-weight:bolder; font-size: 1.17em}
    +.typechoSkin .mce_h4 span.mceText {font-weight:bolder; font-size: 1em}
    +.typechoSkin .mce_h5 span.mceText {font-weight:bolder; font-size: .83em}
    +.typechoSkin .mce_h6 span.mceText {font-weight:bolder; font-size: .75em}
    +
    +/* Theme */
    +.typechoSkin span.mce_bold {background-position:0 0}
    +.typechoSkin span.mce_italic {background-position:-60px 0}
    +.typechoSkin span.mce_underline {background-position:-140px 0}
    +.typechoSkin span.mce_strikethrough {background-position:-120px 0}
    +.typechoSkin span.mce_undo {background-position:-160px 0}
    +.typechoSkin span.mce_redo {background-position:-100px 0}
    +.typechoSkin span.mce_cleanup {background-position:-40px 0}
    +.typechoSkin span.mce_bullist {background-position:-20px 0}
    +.typechoSkin span.mce_numlist {background-position:-80px 0}
    +.typechoSkin span.mce_justifyleft {background-position:-460px 0}
    +.typechoSkin span.mce_justifyright {background-position:-480px 0}
    +.typechoSkin span.mce_justifycenter {background-position:-420px 0}
    +.typechoSkin span.mce_justifyfull {background-position:-440px 0}
    +.typechoSkin span.mce_anchor {background-position:-200px 0}
    +.typechoSkin span.mce_indent {background-position:-400px 0}
    +.typechoSkin span.mce_outdent {background-position:-540px 0}
    +.typechoSkin span.mce_link {background-position:-500px 0}
    +.typechoSkin span.mce_unlink {background-position:-640px 0}
    +.typechoSkin span.mce_sub {background-position:-600px 0}
    +.typechoSkin span.mce_sup {background-position:-620px 0}
    +.typechoSkin span.mce_removeformat {background-position:-580px 0}
    +.typechoSkin span.mce_newdocument {background-position:-520px 0}
    +.typechoSkin span.mce_image {background-position:-380px 0}
    +.typechoSkin span.mce_help {background-position:-340px 0}
    +.typechoSkin span.mce_code {background-position:-260px 0}
    +.typechoSkin span.mce_hr {background-position:-360px 0}
    +.typechoSkin span.mce_visualaid {background-position:-660px 0}
    +.typechoSkin span.mce_charmap {background-position:-240px 0}
    +.typechoSkin span.mce_paste {background-position:-560px 0}
    +.typechoSkin span.mce_copy {background-position:-700px 0}
    +.typechoSkin span.mce_cut {background-position:-680px 0}
    +.typechoSkin span.mce_blockquote {background-position:-220px 0}
    +.typechoSkin .mce_forecolor span.mceAction {background-position:-720px 0}
    +.typechoSkin .mce_backcolor span.mceAction {background-position:-760px 0}
    +.typechoSkin span.mce_forecolorpicker {background-position:-720px 0}
    +.typechoSkin span.mce_backcolorpicker {background-position:-760px 0}
    +
    +/* Plugins */
    +.typechoSkin span.mce_advhr {background-position:-0px -20px}
    +.typechoSkin span.mce_ltr {background-position:-20px -20px}
    +.typechoSkin span.mce_rtl {background-position:-40px -20px}
    +.typechoSkin span.mce_emotions {background-position:-60px -20px}
    +.typechoSkin span.mce_fullpage {background-position:-80px -20px}
    +.typechoSkin span.mce_fullscreen {background-position:-100px -20px}
    +.typechoSkin span.mce_iespell {background-position:-120px -20px}
    +.typechoSkin span.mce_insertdate {background-position:-140px -20px}
    +.typechoSkin span.mce_inserttime {background-position:-160px -20px}
    +.typechoSkin span.mce_absolute {background-position:-180px -20px}
    +.typechoSkin span.mce_backward {background-position:-200px -20px}
    +.typechoSkin span.mce_forward {background-position:-220px -20px}
    +.typechoSkin span.mce_insert_layer {background-position:-240px -20px}
    +.typechoSkin span.mce_insertlayer {background-position:-260px -20px}
    +.typechoSkin span.mce_movebackward {background-position:-280px -20px}
    +.typechoSkin span.mce_moveforward {background-position:-300px -20px}
    +.typechoSkin span.mce_media {background-position:-320px -20px}
    +.typechoSkin span.mce_nonbreaking {background-position:-340px -20px}
    +.typechoSkin span.mce_pastetext {background-position:-360px -20px}
    +.typechoSkin span.mce_pasteword {background-position:-380px -20px}
    +.typechoSkin span.mce_selectall {background-position:-400px -20px}
    +.typechoSkin span.mce_preview {background-position:-420px -20px}
    +.typechoSkin span.mce_print {background-position:-440px -20px}
    +.typechoSkin span.mce_cancel {background-position:-460px -20px}
    +.typechoSkin span.mce_save {background-position:-480px -20px}
    +.typechoSkin span.mce_replace {background-position:-500px -20px}
    +.typechoSkin span.mce_search {background-position:-520px -20px}
    +.typechoSkin span.mce_styleprops {background-position:-560px -20px}
    +.typechoSkin span.mce_table {background-position:-580px -20px}
    +.typechoSkin span.mce_cell_props {background-position:-600px -20px}
    +.typechoSkin span.mce_delete_table {background-position:-620px -20px}
    +.typechoSkin span.mce_delete_col {background-position:-640px -20px}
    +.typechoSkin span.mce_delete_row {background-position:-660px -20px}
    +.typechoSkin span.mce_col_after {background-position:-680px -20px}
    +.typechoSkin span.mce_col_before {background-position:-700px -20px}
    +.typechoSkin span.mce_row_after {background-position:-720px -20px}
    +.typechoSkin span.mce_row_before {background-position:-740px -20px}
    +.typechoSkin span.mce_merge_cells {background-position:-760px -20px}
    +.typechoSkin span.mce_table_props {background-position:-980px -20px}
    +.typechoSkin span.mce_row_props {background-position:-780px -20px}
    +.typechoSkin span.mce_split_cells {background-position:-800px -20px}
    +.typechoSkin span.mce_template {background-position:-820px -20px}
    +.typechoSkin span.mce_visualchars {background-position:-840px -20px}
    +.typechoSkin span.mce_abbr {background-position:-860px -20px}
    +.typechoSkin span.mce_acronym {background-position:-880px -20px}
    +.typechoSkin span.mce_attribs {background-position:-900px -20px}
    +.typechoSkin span.mce_cite {background-position:-920px -20px}
    +.typechoSkin span.mce_del {background-position:-940px -20px}
    +.typechoSkin span.mce_ins {background-position:-960px -20px}
    +.typechoSkin span.mce_morebreak {background-position:0 -40px}
    +.typechoSkin .mce_spellchecker span.mceAction {background-position:-540px -20px}
    diff --git a/TinyMCE/tiny_mce/themes/advanced/source_editor.htm b/TinyMCE/tiny_mce/themes/advanced/source_editor.htm
    new file mode 100644
    index 0000000..acb0d08
    --- /dev/null
    +++ b/TinyMCE/tiny_mce/themes/advanced/source_editor.htm
    @@ -0,0 +1,31 @@
    +<html xmlns="http://www.w3.org/1999/xhtml">
    +<head>
    +	<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" />
    +	<title>{#advanced_dlg.code_title}</title>
    +	<script type="text/javascript" src="../../tiny_mce_popup.js"></script>
    +	<script type="text/javascript" src="js/source_editor.js"></script>
    +</head>
    +<body onresize="resizeInputs();" style="display:none; overflow:hidden;">
    +	<form name="source" onsubmit="saveContent();return false;" action="#">
    +		<div style="float: left" class="title">{#advanced_dlg.code_title}</div>
    +
    +		<div id="wrapline" style="float: right">
    +			<input type="checkbox" name="wraped" id="wraped" onclick="toggleWordWrap(this);" class="wordWrapCode" /><label for="wraped">{#advanced_dlg.code_wordwrap}</label>
    +		</div>
    +
    +		<br style="clear: both" />
    +
    +		<textarea name="htmlSource" id="htmlSource" rows="15" cols="100" style="width: 100%; height: 100%; font-family: 'Courier New',Courier,monospace; font-size: 12px;" dir="ltr" wrap="off" class="mceFocus"></textarea>
    +
    +		<div class="mceActionPanel">
    +			<div style="float: left">
    +				<input type="submit" name="insert" value="{#update}" id="insert" />
    +			</div>
    +
    +			<div style="float: right">
    +				<input type="button" name="cancel" value="{#cancel}" onclick="tinyMCEPopup.close();" id="cancel" />
    +			</div>
    +		</div>
    +	</form>
    +</body>
    +</html>
    diff --git a/TinyMCE/tiny_mce/tiny_mce.js b/TinyMCE/tiny_mce/tiny_mce.js
    new file mode 100644
    index 0000000..ccbb173
    --- /dev/null
    +++ b/TinyMCE/tiny_mce/tiny_mce.js
    @@ -0,0 +1 @@
    +var tinymce={majorVersion:"3",minorVersion:"2.7",releaseDate:"2009-09-22",_init:function(){var o=this,k=document,l=window,j=navigator,b=j.userAgent,h,a,g,f,e,m;o.isOpera=l.opera&&opera.buildNumber;o.isWebKit=/WebKit/.test(b);o.isIE=!o.isWebKit&&!o.isOpera&&(/MSIE/gi).test(b)&&(/Explorer/gi).test(j.appName);o.isIE6=o.isIE&&/MSIE [56]/.test(b);o.isGecko=!o.isWebKit&&/Gecko/.test(b);o.isMac=b.indexOf("Mac")!=-1;o.isAir=/adobeair/i.test(b);if(l.tinyMCEPreInit){o.suffix=tinyMCEPreInit.suffix;o.baseURL=tinyMCEPreInit.base;o.query=tinyMCEPreInit.query;return}o.suffix="";a=k.getElementsByTagName("base");for(h=0;h<a.length;h++){if(m=a[h].href){if(/^https?:\/\/[^\/]+$/.test(m)){m+="/"}f=m?m.match(/.*\//)[0]:""}}function c(d){if(d.src&&/tiny_mce(|_gzip|_jquery|_prototype)(_dev|_src)?.js/.test(d.src)){if(/_(src|dev)\.js/g.test(d.src)){o.suffix="_src"}if((e=d.src.indexOf("?"))!=-1){o.query=d.src.substring(e+1)}o.baseURL=d.src.substring(0,d.src.lastIndexOf("/"));if(f&&o.baseURL.indexOf("://")==-1&&o.baseURL.indexOf("/")!==0){o.baseURL=f+o.baseURL}return o.baseURL}return null}a=k.getElementsByTagName("script");for(h=0;h<a.length;h++){if(c(a[h])){return}}g=k.getElementsByTagName("head")[0];if(g){a=g.getElementsByTagName("script");for(h=0;h<a.length;h++){if(c(a[h])){return}}}return},is:function(b,a){var c=typeof(b);if(!a){return c!="undefined"}if(a=="array"&&(b.hasOwnProperty&&b instanceof Array)){return true}return c==a},each:function(d,a,c){var e,b;if(!d){return 0}c=c||d;if(typeof(d.length)!="undefined"){for(e=0,b=d.length;e<b;e++){if(a.call(c,d[e],e,d)===false){return 0}}}else{for(e in d){if(d.hasOwnProperty(e)){if(a.call(c,d[e],e,d)===false){return 0}}}}return 1},map:function(b,c){var d=[];tinymce.each(b,function(a){d.push(c(a))});return d},grep:function(b,c){var d=[];tinymce.each(b,function(a){if(!c||c(a)){d.push(a)}});return d},inArray:function(c,d){var e,b;if(c){for(e=0,b=c.length;e<b;e++){if(c[e]===d){return e}}}return -1},extend:function(f,d){var c,b=arguments;for(c=1;c<b.length;c++){d=b[c];tinymce.each(d,function(a,e){if(typeof(a)!=="undefined"){f[e]=a}})}return f},trim:function(a){return(a?""+a:"").replace(/^\s*|\s*$/g,"")},create:function(j,a){var i=this,b,e,f,g,d,h=0;j=/^((static) )?([\w.]+)(:([\w.]+))?/.exec(j);f=j[3].match(/(^|\.)(\w+)$/i)[2];e=i.createNS(j[3].replace(/\.\w+$/,""));if(e[f]){return}if(j[2]=="static"){e[f]=a;if(this.onCreate){this.onCreate(j[2],j[3],e[f])}return}if(!a[f]){a[f]=function(){};h=1}e[f]=a[f];i.extend(e[f].prototype,a);if(j[5]){b=i.resolve(j[5]).prototype;g=j[5].match(/\.(\w+)$/i)[1];d=e[f];if(h){e[f]=function(){return b[g].apply(this,arguments)}}else{e[f]=function(){this.parent=b[g];return d.apply(this,arguments)}}e[f].prototype[f]=e[f];i.each(b,function(c,k){e[f].prototype[k]=b[k]});i.each(a,function(c,k){if(b[k]){e[f].prototype[k]=function(){this.parent=b[k];return c.apply(this,arguments)}}else{if(k!=f){e[f].prototype[k]=c}}})}i.each(a["static"],function(c,k){e[f][k]=c});if(this.onCreate){this.onCreate(j[2],j[3],e[f].prototype)}},walk:function(c,b,d,a){a=a||this;if(c){if(d){c=c[d]}tinymce.each(c,function(f,e){if(b.call(a,f,e,d)===false){return false}tinymce.walk(f,b,d,a)})}},createNS:function(d,c){var b,a;c=c||window;d=d.split(".");for(b=0;b<d.length;b++){a=d[b];if(!c[a]){c[a]={}}c=c[a]}return c},resolve:function(d,c){var b,a;c=c||window;d=d.split(".");for(b=0,a=d.length;b<a;b++){c=c[d[b]];if(!c){break}}return c},addUnload:function(e,d){var c=this,a=window;e={func:e,scope:d||this};if(!c.unloads){function b(){var f=c.unloads,h,i;if(f){for(i in f){h=f[i];if(h&&h.func){h.func.call(h.scope,1)}}if(a.detachEvent){a.detachEvent("onbeforeunload",g);a.detachEvent("onunload",b)}else{if(a.removeEventListener){a.removeEventListener("unload",b,false)}}c.unloads=h=f=a=b=0;if(window.CollectGarbage){window.CollectGarbage()}}}function g(){var h=document;if(h.readyState=="interactive"){function f(){h.detachEvent("onstop",f);if(b){b()}h=0}if(h){h.attachEvent("onstop",f)}window.setTimeout(function(){if(h){h.detachEvent("onstop",f)}},0)}}if(a.attachEvent){a.attachEvent("onunload",b);a.attachEvent("onbeforeunload",g)}else{if(a.addEventListener){a.addEventListener("unload",b,false)}}c.unloads=[e]}else{c.unloads.push(e)}return e},removeUnload:function(c){var a=this.unloads,b=null;tinymce.each(a,function(e,d){if(e&&e.func==c){a.splice(d,1);b=c;return false}});return b},explode:function(a,b){return a?tinymce.map(a.split(b||","),tinymce.trim):a},_addVer:function(b){var a;if(!this.query){return b}a=(b.indexOf("?")==-1?"?":"&")+this.query;if(b.indexOf("#")==-1){return b+a}return b.replace("#",a+"#")}};window.tinymce=tinymce;tinymce._init();tinymce.create("tinymce.util.Dispatcher",{scope:null,listeners:null,Dispatcher:function(a){this.scope=a||this;this.listeners=[]},add:function(a,b){this.listeners.push({cb:a,scope:b||this.scope});return a},addToTop:function(a,b){this.listeners.unshift({cb:a,scope:b||this.scope});return a},remove:function(a){var b=this.listeners,c=null;tinymce.each(b,function(e,d){if(a==e.cb){c=a;b.splice(d,1);return false}});return c},dispatch:function(){var f,d=arguments,e,b=this.listeners,g;for(e=0;e<b.length;e++){g=b[e];f=g.cb.apply(g.scope,d);if(f===false){break}}return f}});(function(){var a=tinymce.each;tinymce.create("tinymce.util.URI",{URI:function(e,g){var f=this,h,d,c;e=tinymce.trim(e);g=f.settings=g||{};if(/^(mailto|tel|news|javascript|about|data):/i.test(e)||/^\s*#/.test(e)){f.source=e;return}if(e.indexOf("/")===0&&e.indexOf("//")!==0){e=(g.base_uri?g.base_uri.protocol||"http":"http")+"://mce_host"+e}if(!/^\w*:?\/\//.test(e)){e=(g.base_uri.protocol||"http")+"://mce_host"+f.toAbsPath(g.base_uri.path,e)}e=e.replace(/@@/g,"(mce_at)");e=/^(?:(?![^:@]+:[^:@\/]*@)([^:\/?#.]+):)?(?:\/\/)?((?:(([^:@]*):?([^:@]*))?@)?([^:\/?#]*)(?::(\d*))?)(((\/(?:[^?#](?![^?#\/]*\.[^?#\/.]+(?:[?#]|$)))*\/?)?([^?#\/]*))(?:\?([^#]*))?(?:#(.*))?)/.exec(e);a(["source","protocol","authority","userInfo","user","password","host","port","relative","path","directory","file","query","anchor"],function(b,j){var k=e[j];if(k){k=k.replace(/\(mce_at\)/g,"@@")}f[b]=k});if(c=g.base_uri){if(!f.protocol){f.protocol=c.protocol}if(!f.userInfo){f.userInfo=c.userInfo}if(!f.port&&f.host=="mce_host"){f.port=c.port}if(!f.host||f.host=="mce_host"){f.host=c.host}f.source=""}},setPath:function(c){var b=this;c=/^(.*?)\/?(\w+)?$/.exec(c);b.path=c[0];b.directory=c[1];b.file=c[2];b.source="";b.getURI()},toRelative:function(b){var c=this,d;if(b==="./"){return b}b=new tinymce.util.URI(b,{base_uri:c});if((b.host!="mce_host"&&c.host!=b.host&&b.host)||c.port!=b.port||c.protocol!=b.protocol){return b.getURI()}d=c.toRelPath(c.path,b.path);if(b.query){d+="?"+b.query}if(b.anchor){d+="#"+b.anchor}return d},toAbsolute:function(b,c){var b=new tinymce.util.URI(b,{base_uri:this});return b.getURI(this.host==b.host&&this.protocol==b.protocol?c:0)},toRelPath:function(g,h){var c,f=0,d="",e,b;g=g.substring(0,g.lastIndexOf("/"));g=g.split("/");c=h.split("/");if(g.length>=c.length){for(e=0,b=g.length;e<b;e++){if(e>=c.length||g[e]!=c[e]){f=e+1;break}}}if(g.length<c.length){for(e=0,b=c.length;e<b;e++){if(e>=g.length||g[e]!=c[e]){f=e+1;break}}}if(f==1){return h}for(e=0,b=g.length-(f-1);e<b;e++){d+="../"}for(e=f-1,b=c.length;e<b;e++){if(e!=f-1){d+="/"+c[e]}else{d+=c[e]}}return d},toAbsPath:function(e,f){var c,b=0,h=[],d,g;d=/\/$/.test(f)?"/":"";e=e.split("/");f=f.split("/");a(e,function(i){if(i){h.push(i)}});e=h;for(c=f.length-1,h=[];c>=0;c--){if(f[c].length==0||f[c]=="."){continue}if(f[c]==".."){b++;continue}if(b>0){b--;continue}h.push(f[c])}c=e.length-b;if(c<=0){g=h.reverse().join("/")}else{g=e.slice(0,c).join("/")+"/"+h.reverse().join("/")}if(g.indexOf("/")!==0){g="/"+g}if(d&&g.lastIndexOf("/")!==g.length-1){g+=d}return g},getURI:function(d){var c,b=this;if(!b.source||d){c="";if(!d){if(b.protocol){c+=b.protocol+"://"}if(b.userInfo){c+=b.userInfo+"@"}if(b.host){c+=b.host}if(b.port){c+=":"+b.port}}if(b.path){c+=b.path}if(b.query){c+="?"+b.query}if(b.anchor){c+="#"+b.anchor}b.source=c}return b.source}})})();(function(){var a=tinymce.each;tinymce.create("static tinymce.util.Cookie",{getHash:function(d){var b=this.get(d),c;if(b){a(b.split("&"),function(e){e=e.split("=");c=c||{};c[unescape(e[0])]=unescape(e[1])})}return c},setHash:function(j,b,g,f,i,c){var h="";a(b,function(e,d){h+=(!h?"":"&")+escape(d)+"="+escape(e)});this.set(j,h,g,f,i,c)},get:function(i){var h=document.cookie,g,f=i+"=",d;if(!h){return}d=h.indexOf("; "+f);if(d==-1){d=h.indexOf(f);if(d!=0){return null}}else{d+=2}g=h.indexOf(";",d);if(g==-1){g=h.length}return unescape(h.substring(d+f.length,g))},set:function(i,b,g,f,h,c){document.cookie=i+"="+escape(b)+((g)?"; expires="+g.toGMTString():"")+((f)?"; path="+escape(f):"")+((h)?"; domain="+h:"")+((c)?"; secure":"")},remove:function(e,b){var c=new Date();c.setTime(c.getTime()-1000);this.set(e,"",c,b,c)}})})();tinymce.create("static tinymce.util.JSON",{serialize:function(e){var c,a,d=tinymce.util.JSON.serialize,b;if(e==null){return"null"}b=typeof e;if(b=="string"){a="\bb\tt\nn\ff\rr\"\"''\\\\";return'"'+e.replace(/([\u0080-\uFFFF\x00-\x1f\"])/g,function(g,f){c=a.indexOf(f);if(c+1){return"\\"+a.charAt(c+1)}g=f.charCodeAt().toString(16);return"\\u"+"0000".substring(g.length)+g})+'"'}if(b=="object"){if(e.hasOwnProperty&&e instanceof Array){for(c=0,a="[";c<e.length;c++){a+=(c>0?",":"")+d(e[c])}return a+"]"}a="{";for(c in e){a+=typeof e[c]!="function"?(a.length>1?',"':'"')+c+'":'+d(e[c]):""}return a+"}"}return""+e},parse:function(s){try{return eval("("+s+")")}catch(ex){}}});tinymce.create("static tinymce.util.XHR",{send:function(g){var a,e,b=window,h=0;g.scope=g.scope||this;g.success_scope=g.success_scope||g.scope;g.error_scope=g.error_scope||g.scope;g.async=g.async===false?false:true;g.data=g.data||"";function d(i){a=0;try{a=new ActiveXObject(i)}catch(c){}return a}a=b.XMLHttpRequest?new XMLHttpRequest():d("Microsoft.XMLHTTP")||d("Msxml2.XMLHTTP");if(a){if(a.overrideMimeType){a.overrideMimeType(g.content_type)}a.open(g.type||(g.data?"POST":"GET"),g.url,g.async);if(g.content_type){a.setRequestHeader("Content-Type",g.content_type)}a.setRequestHeader("X-Requested-With","XMLHttpRequest");a.send(g.data);function f(){if(!g.async||a.readyState==4||h++>10000){if(g.success&&h<10000&&a.status==200){g.success.call(g.success_scope,""+a.responseText,a,g)}else{if(g.error){g.error.call(g.error_scope,h>10000?"TIMED_OUT":"GENERAL",a,g)}}a=null}else{b.setTimeout(f,10)}}if(!g.async){return f()}e=b.setTimeout(f,10)}}});(function(){var c=tinymce.extend,b=tinymce.util.JSON,a=tinymce.util.XHR;tinymce.create("tinymce.util.JSONRequest",{JSONRequest:function(d){this.settings=c({},d);this.count=0},send:function(f){var e=f.error,d=f.success;f=c(this.settings,f);f.success=function(h,g){h=b.parse(h);if(typeof(h)=="undefined"){h={error:"JSON Parse error."}}if(h.error){e.call(f.error_scope||f.scope,h.error,g)}else{d.call(f.success_scope||f.scope,h.result)}};f.error=function(h,g){e.call(f.error_scope||f.scope,h,g)};f.data=b.serialize({id:f.id||"c"+(this.count++),method:f.method,params:f.params});f.content_type="application/json";a.send(f)},"static":{sendRPC:function(d){return new tinymce.util.JSONRequest().send(d)}}})}());(function(c){var e=c.each,b=c.is;var d=c.isWebKit,a=c.isIE;c.create("tinymce.dom.DOMUtils",{doc:null,root:null,files:null,pixelStyles:/^(top|left|bottom|right|width|height|borderWidth)$/,props:{"for":"htmlFor","class":"className",className:"className",checked:"checked",disabled:"disabled",maxlength:"maxLength",readonly:"readOnly",selected:"selected",value:"value",id:"id",name:"name",type:"type"},DOMUtils:function(i,g){var f=this;f.doc=i;f.win=window;f.files={};f.cssFlicker=false;f.counter=0;f.boxModel=!c.isIE||i.compatMode=="CSS1Compat";f.stdMode=i.documentMode===8;f.settings=g=c.extend({keep_values:false,hex_colors:1,process_html:1},g);if(c.isIE6){try{i.execCommand("BackgroundImageCache",false,true)}catch(h){f.cssFlicker=true}}c.addUnload(f.destroy,f)},getRoot:function(){var f=this,g=f.settings;return(g&&f.get(g.root_element))||f.doc.body},getViewPort:function(g){var h,f;g=!g?this.win:g;h=g.document;f=this.boxModel?h.documentElement:h.body;return{x:g.pageXOffset||f.scrollLeft,y:g.pageYOffset||f.scrollTop,w:g.innerWidth||f.clientWidth,h:g.innerHeight||f.clientHeight}},getRect:function(i){var h,f=this,g;i=f.get(i);h=f.getPos(i);g=f.getSize(i);return{x:h.x,y:h.y,w:g.w,h:g.h}},getSize:function(j){var g=this,f,i;j=g.get(j);f=g.getStyle(j,"width");i=g.getStyle(j,"height");if(f.indexOf("px")===-1){f=0}if(i.indexOf("px")===-1){i=0}return{w:parseInt(f)||j.offsetWidth||j.clientWidth,h:parseInt(i)||j.offsetHeight||j.clientHeight}},getParent:function(i,h,g){return this.getParents(i,h,g,false)},getParents:function(p,k,i,m){var h=this,g,j=h.settings,l=[];p=h.get(p);m=m===undefined;if(j.strict_root){i=i||h.getRoot()}if(b(k,"string")){g=k;if(k==="*"){k=function(f){return f.nodeType==1}}else{k=function(f){return h.is(f,g)}}}while(p){if(p==i||!p.nodeType||p.nodeType===9){break}if(!k||k(p)){if(m){l.push(p)}else{return p}}p=p.parentNode}return m?l:null},get:function(f){var g;if(f&&this.doc&&typeof(f)=="string"){g=f;f=this.doc.getElementById(f);if(f&&f.id!==g){return this.doc.getElementsByName(g)[1]}}return f},getNext:function(g,f){return this._findSib(g,f,"nextSibling")},getPrev:function(g,f){return this._findSib(g,f,"previousSibling")},select:function(h,g){var f=this;return c.dom.Sizzle(h,f.get(g)||f.get(f.settings.root_element)||f.doc,[])},is:function(g,f){return c.dom.Sizzle.matches(f,g.nodeType?[g]:g).length>0},add:function(j,l,f,i,k){var g=this;return this.run(j,function(n){var m,h;m=b(l,"string")?g.doc.createElement(l):l;g.setAttribs(m,f);if(i){if(i.nodeType){m.appendChild(i)}else{g.setHTML(m,i)}}return !k?n.appendChild(m):m})},create:function(i,f,g){return this.add(this.doc.createElement(i),i,f,g,1)},createHTML:function(m,f,j){var l="",i=this,g;l+="<"+m;for(g in f){if(f.hasOwnProperty(g)){l+=" "+g+'="'+i.encode(f[g])+'"'}}if(c.is(j)){return l+">"+j+"</"+m+">"}return l+" />"},remove:function(h,f){var g=this;return this.run(h,function(m){var l,k,j;l=m.parentNode;if(!l){return null}if(f){for(j=m.childNodes.length-1;j>=0;j--){g.insertAfter(m.childNodes[j],m)}}if(g.fixPsuedoLeaks){l=m.cloneNode(true);f="IELeakGarbageBin";k=g.get(f)||g.add(g.doc.body,"div",{id:f,style:"display:none"});k.appendChild(m);k.innerHTML="";return l}return l.removeChild(m)})},setStyle:function(i,f,g){var h=this;return h.run(i,function(l){var k,j;k=l.style;f=f.replace(/-(\D)/g,function(n,m){return m.toUpperCase()});if(h.pixelStyles.test(f)&&(c.is(g,"number")||/^[\-0-9\.]+$/.test(g))){g+="px"}switch(f){case"opacity":if(a){k.filter=g===""?"":"alpha(opacity="+(g*100)+")";if(!i.currentStyle||!i.currentStyle.hasLayout){k.display="inline-block"}}k[f]=k["-moz-opacity"]=k["-khtml-opacity"]=g||"";break;case"float":a?k.styleFloat=g:k.cssFloat=g;break;default:k[f]=g||""}if(h.settings.update_styles){h.setAttrib(l,"mce_style")}})},getStyle:function(i,f,h){i=this.get(i);if(!i){return false}if(this.doc.defaultView&&h){f=f.replace(/[A-Z]/g,function(j){return"-"+j});try{return this.doc.defaultView.getComputedStyle(i,null).getPropertyValue(f)}catch(g){return null}}f=f.replace(/-(\D)/g,function(k,j){return j.toUpperCase()});if(f=="float"){f=a?"styleFloat":"cssFloat"}if(i.currentStyle&&h){return i.currentStyle[f]}return i.style[f]},setStyles:function(i,j){var g=this,h=g.settings,f;f=h.update_styles;h.update_styles=0;e(j,function(k,l){g.setStyle(i,l,k)});h.update_styles=f;if(h.update_styles){g.setAttrib(i,h.cssText)}},setAttrib:function(h,i,f){var g=this;if(!h||!i){return}if(g.settings.strict){i=i.toLowerCase()}return this.run(h,function(k){var j=g.settings;switch(i){case"style":if(!b(f,"string")){e(f,function(l,m){g.setStyle(k,m,l)});return}if(j.keep_values){if(f&&!g._isRes(f)){k.setAttribute("mce_style",f,2)}else{k.removeAttribute("mce_style",2)}}k.style.cssText=f;break;case"class":k.className=f||"";break;case"src":case"href":if(j.keep_values){if(j.url_converter){f=j.url_converter.call(j.url_converter_scope||g,f,i,k)}g.setAttrib(k,"mce_"+i,f,2)}break;case"shape":k.setAttribute("mce_style",f);break}if(b(f)&&f!==null&&f.length!==0){k.setAttribute(i,""+f,2)}else{k.removeAttribute(i,2)}})},setAttribs:function(g,h){var f=this;return this.run(g,function(i){e(h,function(j,k){f.setAttrib(i,k,j)})})},getAttrib:function(i,j,h){var f,g=this;i=g.get(i);if(!i||i.nodeType!==1){return false}if(!b(h)){h=""}if(/^(src|href|style|coords|shape)$/.test(j)){f=i.getAttribute("mce_"+j);if(f){return f}}if(a&&g.props[j]){f=i[g.props[j]];f=f&&f.nodeValue?f.nodeValue:f}if(!f){f=i.getAttribute(j,2)}if(/^(checked|compact|declare|defer|disabled|ismap|multiple|nohref|noshade|nowrap|readonly|selected)$/.test(j)){if(i[g.props[j]]===true&&f===""){return j}return f?j:""}if(i.nodeName==="FORM"&&i.getAttributeNode(j)){return i.getAttributeNode(j).nodeValue}if(j==="style"){f=f||i.style.cssText;if(f){f=g.serializeStyle(g.parseStyle(f));if(g.settings.keep_values&&!g._isRes(f)){i.setAttribute("mce_style",f)}}}if(d&&j==="class"&&f){f=f.replace(/(apple|webkit)\-[a-z\-]+/gi,"")}if(a){switch(j){case"rowspan":case"colspan":if(f===1){f=""}break;case"size":if(f==="+0"||f===20||f===0){f=""}break;case"width":case"height":case"vspace":case"checked":case"disabled":case"readonly":if(f===0){f=""}break;case"hspace":if(f===-1){f=""}break;case"maxlength":case"tabindex":if(f===32768||f===2147483647||f==="32768"){f=""}break;case"multiple":case"compact":case"noshade":case"nowrap":if(f===65535){return j}return h;case"shape":f=f.toLowerCase();break;default:if(j.indexOf("on")===0&&f){f=(""+f).replace(/^function\s+\w+\(\)\s+\{\s+(.*)\s+\}$/,"$1")}}}return(f!==undefined&&f!==null&&f!=="")?""+f:h},getPos:function(m,i){var g=this,f=0,l=0,j,k=g.doc,h;m=g.get(m);i=i||k.body;if(m){if(a&&!g.stdMode){m=m.getBoundingClientRect();j=g.boxModel?k.documentElement:k.body;f=g.getStyle(g.select("html")[0],"borderWidth");f=(f=="medium"||g.boxModel&&!g.isIE6)&&2||f;m.top+=g.win.self!=g.win.top?2:0;return{x:m.left+j.scrollLeft-f,y:m.top+j.scrollTop-f}}h=m;while(h&&h!=i&&h.nodeType){f+=h.offsetLeft||0;l+=h.offsetTop||0;h=h.offsetParent}h=m.parentNode;while(h&&h!=i&&h.nodeType){f-=h.scrollLeft||0;l-=h.scrollTop||0;h=h.parentNode}}return{x:f,y:l}},parseStyle:function(h){var i=this,j=i.settings,k={};if(!h){return k}function f(w,q,v){var o,u,m,n;o=k[w+"-top"+q];if(!o){return}u=k[w+"-right"+q];if(o!=u){return}m=k[w+"-bottom"+q];if(u!=m){return}n=k[w+"-left"+q];if(m!=n){return}k[v]=n;delete k[w+"-top"+q];delete k[w+"-right"+q];delete k[w+"-bottom"+q];delete k[w+"-left"+q]}function g(n,m,l,p){var o;o=k[m];if(!o){return}o=k[l];if(!o){return}o=k[p];if(!o){return}k[n]=k[m]+" "+k[l]+" "+k[p];delete k[m];delete k[l];delete k[p]}h=h.replace(/&(#?[a-z0-9]+);/g,"&$1_MCE_SEMI_");e(h.split(";"),function(m){var l,n=[];if(m){m=m.replace(/_MCE_SEMI_/g,";");m=m.replace(/url\([^\)]+\)/g,function(o){n.push(o);return"url("+n.length+")"});m=m.split(":");l=c.trim(m[1]);l=l.replace(/url\(([^\)]+)\)/g,function(p,o){return n[parseInt(o)-1]});l=l.replace(/rgb\([^\)]+\)/g,function(o){return i.toHex(o)});if(j.url_converter){l=l.replace(/url\([\'\"]?([^\)\'\"]+)[\'\"]?\)/g,function(o,p){return"url("+j.url_converter.call(j.url_converter_scope||i,i.decode(p),"style",null)+")"})}k[c.trim(m[0]).toLowerCase()]=l}});f("border","","border");f("border","-width","border-width");f("border","-color","border-color");f("border","-style","border-style");f("padding","","padding");f("margin","","margin");g("border","border-width","border-style","border-color");if(a){if(k.border=="medium none"){k.border=""}}return k},serializeStyle:function(g){var f="";e(g,function(i,h){if(h&&i){if(c.isGecko&&h.indexOf("-moz-")===0){return}switch(h){case"color":case"background-color":i=i.toLowerCase();break}f+=(f?" ":"")+h+": "+i+";"}});return f},loadCSS:function(f){var h=this,i=h.doc,g;if(!f){f=""}g=h.select("head")[0];e(f.split(","),function(j){var k;if(h.files[j]){return}h.files[j]=true;k=h.create("link",{rel:"stylesheet",href:c._addVer(j)});if(a&&i.documentMode){k.onload=function(){i.recalc();k.onload=null}}g.appendChild(k)})},addClass:function(f,g){return this.run(f,function(h){var i;if(!g){return 0}if(this.hasClass(h,g)){return h.className}i=this.removeClass(h,g);return h.className=(i!=""?(i+" "):"")+g})},removeClass:function(h,i){var f=this,g;return f.run(h,function(k){var j;if(f.hasClass(k,i)){if(!g){g=new RegExp("(^|\\s+)"+i+"(\\s+|$)","g")}j=k.className.replace(g," ");return k.className=c.trim(j!=" "?j:"")}return k.className})},hasClass:function(g,f){g=this.get(g);if(!g||!f){return false}return(" "+g.className+" ").indexOf(" "+f+" ")!==-1},show:function(f){return this.setStyle(f,"display","block")},hide:function(f){return this.setStyle(f,"display","none")},isHidden:function(f){f=this.get(f);return !f||f.style.display=="none"||this.getStyle(f,"display")=="none"},uniqueId:function(f){return(!f?"mce_":f)+(this.counter++)},setHTML:function(i,g){var f=this;return this.run(i,function(m){var h,k,j,q,l,h;g=f.processHTML(g);if(a){function o(){try{m.innerHTML="<br />"+g;m.removeChild(m.firstChild)}catch(n){while(m.firstChild){m.firstChild.removeNode()}h=f.create("div");h.innerHTML="<br />"+g;e(h.childNodes,function(r,p){if(p){m.appendChild(r)}})}}if(f.settings.fix_ie_paragraphs){g=g.replace(/<p><\/p>|<p([^>]+)><\/p>|<p[^\/+]\/>/gi,'<p$1 mce_keep="true">&nbsp;</p>')}o();if(f.settings.fix_ie_paragraphs){j=m.getElementsByTagName("p");for(k=j.length-1,h=0;k>=0;k--){q=j[k];if(!q.hasChildNodes()){if(!q.mce_keep){h=1;break}q.removeAttribute("mce_keep")}}}if(h){g=g.replace(/<p ([^>]+)>|<p>/ig,'<div $1 mce_tmp="1">');g=g.replace(/<\/p>/g,"</div>");o();if(f.settings.fix_ie_paragraphs){j=m.getElementsByTagName("DIV");for(k=j.length-1;k>=0;k--){q=j[k];if(q.mce_tmp){l=f.doc.createElement("p");q.cloneNode(false).outerHTML.replace(/([a-z0-9\-_]+)=/gi,function(p,n){var r;if(n!=="mce_tmp"){r=q.getAttribute(n);if(!r&&n==="class"){r=q.className}l.setAttribute(n,r)}});for(h=0;h<q.childNodes.length;h++){l.appendChild(q.childNodes[h].cloneNode(true))}q.swapNode(l)}}}}}else{m.innerHTML=g}return g})},processHTML:function(j){var g=this,i=g.settings,k=[];if(!i.process_html){return j}if(c.isGecko){j=j.replace(/<(\/?)strong>|<strong( [^>]+)>/gi,"<$1b$2>");j=j.replace(/<(\/?)em>|<em( [^>]+)>/gi,"<$1i$2>")}else{if(a){j=j.replace(/&apos;/g,"&#39;");j=j.replace(/\s+(disabled|checked|readonly|selected)\s*=\s*[\"\']?(false|0)[\"\']?/gi,"")}}j=j.replace(/<a( )([^>]+)\/>|<a\/>/gi,"<a$1$2></a>");if(i.keep_values){if(/<script|noscript|style/i.test(j)){function f(h){h=h.replace(/(<!--\[CDATA\[|\]\]-->)/g,"\n");h=h.replace(/^[\r\n]*|[\r\n]*$/g,"");h=h.replace(/^\s*(\/\/\s*<!--|\/\/\s*<!\[CDATA\[|<!--|<!\[CDATA\[)[\r\n]*/g,"");h=h.replace(/\s*(\/\/\s*\]\]>|\/\/\s*-->|\]\]>|-->|\]\]-->)\s*$/g,"");return h}j=j.replace(/<script([^>]+|)>([\s\S]*?)<\/script>/gi,function(h,m,l){if(!m){m=' type="text/javascript"'}m=m.replace(/src=\"([^\"]+)\"?/i,function(n,o){if(i.url_converter){o=g.encode(i.url_converter.call(i.url_converter_scope||g,g.decode(o),"src","script"))}return'mce_src="'+o+'"'});if(c.trim(l)){k.push(f(l));l="<!--\nMCE_SCRIPT:"+(k.length-1)+"\n// -->"}return"<mce:script"+m+">"+l+"</mce:script>"});j=j.replace(/<style([^>]+|)>([\s\S]*?)<\/style>/gi,function(h,m,l){if(l){k.push(f(l));l="<!--\nMCE_SCRIPT:"+(k.length-1)+"\n-->"}return"<mce:style"+m+">"+l+"</mce:style><style "+m+' mce_bogus="1">'+l+"</style>"});j=j.replace(/<noscript([^>]+|)>([\s\S]*?)<\/noscript>/g,function(h,m,l){return"<mce:noscript"+m+"><!--"+g.encode(l).replace(/--/g,"&#45;&#45;")+"--></mce:noscript>"})}j=j.replace(/<!\[CDATA\[([\s\S]+)\]\]>/g,"<!--[CDATA[$1]]-->");j=j.replace(/<([\w:]+) [^>]*(checked|compact|declare|defer|disabled|ismap|multiple|nohref|noshade|nowrap|readonly|selected)[^>]*>/gi,function(l){function h(o,m,n){if(n==="false"||n==="0"){return""}return" "+m+'="'+m+'"'}l=l.replace(/ (checked|compact|declare|defer|disabled|ismap|multiple|nohref|noshade|nowrap|readonly|selected)=[\"]([^\"]+)[\"]/gi,h);l=l.replace(/ (checked|compact|declare|defer|disabled|ismap|multiple|nohref|noshade|nowrap|readonly|selected)=[\']([^\']+)[\']/gi,h);l=l.replace(/ (checked|compact|declare|defer|disabled|ismap|multiple|nohref|noshade|nowrap|readonly|selected)=([^\s\"\'>]+)/gi,h);l=l.replace(/ (checked|compact|declare|defer|disabled|ismap|multiple|nohref|noshade|nowrap|readonly|selected)([\s>])/gi,' $1="$1"$2');return l});j=j.replace(/<([\w:]+) [^>]*(src|href|style|shape|coords)[^>]*>/gi,function(h,m){function l(o,n,q){var p=q;if(h.indexOf("mce_"+n)!=-1){return o}if(n=="style"){if(g._isRes(q)){return o}p=g.encode(g.serializeStyle(g.parseStyle(p)))}else{if(n!="coords"&&n!="shape"){if(i.url_converter){p=g.encode(i.url_converter.call(i.url_converter_scope||g,g.decode(q),n,m))}}}return" "+n+'="'+q+'" mce_'+n+'="'+p+'"'}h=h.replace(/ (src|href|style|coords|shape)=[\"]([^\"]+)[\"]/gi,l);h=h.replace(/ (src|href|style|coords|shape)=[\']([^\']+)[\']/gi,l);return h.replace(/ (src|href|style|coords|shape)=([^\s\"\'>]+)/gi,l)});j=j.replace(/MCE_SCRIPT:([0-9]+)/g,function(l,h){return k[h]})}return j},getOuterHTML:function(f){var g;f=this.get(f);if(!f){return null}if(f.outerHTML!==undefined){return f.outerHTML}g=(f.ownerDocument||this.doc).createElement("body");g.appendChild(f.cloneNode(true));return g.innerHTML},setOuterHTML:function(j,g,k){var f=this;function i(m,l,p){var q,o;o=p.createElement("body");o.innerHTML=l;q=o.lastChild;while(q){f.insertAfter(q.cloneNode(true),m);q=q.previousSibling}f.remove(m)}return this.run(j,function(l){l=f.get(l);if(l.nodeType==1){k=k||l.ownerDocument||f.doc;if(a){try{if(a&&l.nodeType==1){l.outerHTML=g}else{i(l,g,k)}}catch(h){i(l,g,k)}}else{i(l,g,k)}}})},decode:function(g){var h,i,f;if(/&[^;]+;/.test(g)){h=this.doc.createElement("div");h.innerHTML=g;i=h.firstChild;f="";if(i){do{f+=i.nodeValue}while(i.nextSibling)}return f||g}return g},encode:function(f){return f?(""+f).replace(/[<>&\"]/g,function(h,g){switch(h){case"&":return"&amp;";case'"':return"&quot;";case"<":return"&lt;";case">":return"&gt;"}return h}):f},insertAfter:function(h,g){var f=this;g=f.get(g);return this.run(h,function(k){var j,i;j=g.parentNode;i=g.nextSibling;if(i){j.insertBefore(k,i)}else{j.appendChild(k)}return k})},isBlock:function(f){if(f.nodeType&&f.nodeType!==1){return false}f=f.nodeName||f;return/^(H[1-6]|HR|P|DIV|ADDRESS|PRE|FORM|TABLE|LI|OL|UL|TH|TBODY|TR|TD|CAPTION|BLOCKQUOTE|CENTER|DL|DT|DD|DIR|FIELDSET|NOSCRIPT|NOFRAMES|MENU|ISINDEX|SAMP)$/.test(f)},replace:function(i,h,f){var g=this;if(b(h,"array")){i=i.cloneNode(true)}return g.run(h,function(j){if(f){e(j.childNodes,function(k){i.appendChild(k.cloneNode(true))})}if(g.fixPsuedoLeaks&&j.nodeType===1){j.parentNode.insertBefore(i,j);g.remove(j);return i}return j.parentNode.replaceChild(i,j)})},findCommonAncestor:function(h,f){var i=h,g;while(i){g=f;while(g&&i!=g){g=g.parentNode}if(i==g){break}i=i.parentNode}if(!i&&h.ownerDocument){return h.ownerDocument.documentElement}return i},toHex:function(f){var h=/^\s*rgb\s*?\(\s*?([0-9]+)\s*?,\s*?([0-9]+)\s*?,\s*?([0-9]+)\s*?\)\s*$/i.exec(f);function g(i){i=parseInt(i).toString(16);return i.length>1?i:"0"+i}if(h){f="#"+g(h[1])+g(h[2])+g(h[3]);return f}return f},getClasses:function(){var l=this,g=[],k,m={},n=l.settings.class_filter,j;if(l.classes){return l.classes}function o(f){e(f.imports,function(i){o(i)});e(f.cssRules||f.rules,function(i){switch(i.type||1){case 1:if(i.selectorText){e(i.selectorText.split(","),function(p){p=p.replace(/^\s*|\s*$|^\s\./g,"");if(/\.mce/.test(p)||!/\.[\w\-]+$/.test(p)){return}j=p;p=p.replace(/.*\.([a-z0-9_\-]+).*/i,"$1");if(n&&!(p=n(p,j))){return}if(!m[p]){g.push({"class":p});m[p]=1}})}break;case 3:o(i.styleSheet);break}})}try{e(l.doc.styleSheets,o)}catch(h){}if(g.length>0){l.classes=g}return g},run:function(j,i,h){var g=this,k;if(g.doc&&typeof(j)==="string"){j=g.get(j)}if(!j){return false}h=h||this;if(!j.nodeType&&(j.length||j.length===0)){k=[];e(j,function(l,f){if(l){if(typeof(l)=="string"){l=g.doc.getElementById(l)}k.push(i.call(h,l,f))}});return k}return i.call(h,j)},getAttribs:function(g){var f;g=this.get(g);if(!g){return[]}if(a){f=[];if(g.nodeName=="OBJECT"){return g.attributes}if(g.nodeName==="OPTION"&&this.getAttrib(g,"selected")){f.push({specified:1,nodeName:"selected"})}g.cloneNode(false).outerHTML.replace(/<\/?[\w:]+ ?|=[\"][^\"]+\"|=\'[^\']+\'|=\w+|>/gi,"").replace(/[\w:]+/gi,function(h){f.push({specified:1,nodeName:h})});return f}return g.attributes},destroy:function(g){var f=this;if(f.events){f.events.destroy()}f.win=f.doc=f.root=f.events=null;if(!g){c.removeUnload(f.destroy)}},createRng:function(){var f=this.doc;return f.createRange?f.createRange():new c.dom.Range(this)},split:function(l,k,o){var p=this,f=p.createRng(),m,j,n;function g(r,q){r=r[q];if(r&&r[q]&&r[q].nodeType==1&&i(r[q])){p.remove(r[q])}}function i(q){q=p.getOuterHTML(q);q=q.replace(/<(img|hr|table)/gi,"-");q=q.replace(/<[^>]+>/g,"");return q.replace(/[ \t\r\n]+|&nbsp;|&#160;/g,"")==""}function h(r){var q=0;while(r.previousSibling){q++;r=r.previousSibling}return q}if(l&&k){f.setStart(l.parentNode,h(l));f.setEnd(k.parentNode,h(k));m=f.extractContents();f=p.createRng();f.setStart(k.parentNode,h(k)+1);f.setEnd(l.parentNode,h(l)+1);j=f.extractContents();n=l.parentNode;g(m,"lastChild");if(!i(m)){n.insertBefore(m,l)}if(o){n.replaceChild(o,k)}else{n.insertBefore(k,l)}g(j,"firstChild");if(!i(j)){n.insertBefore(j,l)}p.remove(l);return o||k}},bind:function(j,f,i,h){var g=this;if(!g.events){g.events=new c.dom.EventUtils()}return g.events.add(j,f,i,h||this)},unbind:function(i,f,h){var g=this;if(!g.events){g.events=new c.dom.EventUtils()}return g.events.remove(i,f,h)},_findSib:function(j,g,h){var i=this,k=g;if(j){if(b(k,"string")){k=function(f){return i.is(f,g)}}for(j=j[h];j;j=j[h]){if(k(j)){return j}}}return null},_isRes:function(f){return/^(top|left|bottom|right|width|height)/i.test(f)||/;\s*(top|left|bottom|right|width|height)/i.test(f)}});c.DOM=new c.dom.DOMUtils(document,{process_html:0})})(tinymce);(function(f){var h=0,c=1,e=2,d=tinymce.extend;function g(m,k){var j,l;if(m.parentNode!=k){return -1}for(l=k.firstChild,j=0;l!=m;l=l.nextSibling){j++}return j}function b(k){var j=0;while(k.previousSibling){j++;k=k.previousSibling}return j}function i(j,k){var l;if(j.nodeType==3){return j}if(k<0){return j}l=j.firstChild;while(l!=null&&k>0){--k;l=l.nextSibling}if(l!=null){return l}return j}function a(k){var j=k.doc;d(this,{dom:k,startContainer:j,startOffset:0,endContainer:j,endOffset:0,collapsed:true,commonAncestorContainer:j,START_TO_START:0,START_TO_END:1,END_TO_END:2,END_TO_START:3})}d(a.prototype,{setStart:function(k,j){this._setEndPoint(true,k,j)},setEnd:function(k,j){this._setEndPoint(false,k,j)},setStartBefore:function(j){this.setStart(j.parentNode,b(j))},setStartAfter:function(j){this.setStart(j.parentNode,b(j)+1)},setEndBefore:function(j){this.setEnd(j.parentNode,b(j))},setEndAfter:function(j){this.setEnd(j.parentNode,b(j)+1)},collapse:function(k){var j=this;if(k){j.endContainer=j.startContainer;j.endOffset=j.startOffset}else{j.startContainer=j.endContainer;j.startOffset=j.endOffset}j.collapsed=true},selectNode:function(j){this.setStartBefore(j);this.setEndAfter(j)},selectNodeContents:function(j){this.setStart(j,0);this.setEnd(j,j.nodeType===1?j.childNodes.length:j.nodeValue.length)},compareBoundaryPoints:function(m,n){var l=this,p=l.startContainer,o=l.startOffset,k=l.endContainer,j=l.endOffset;if(m===0){return l._compareBoundaryPoints(p,o,p,o)}if(m===1){return l._compareBoundaryPoints(p,o,k,j)}if(m===2){return l._compareBoundaryPoints(k,j,k,j)}if(m===3){return l._compareBoundaryPoints(k,j,p,o)}},deleteContents:function(){this._traverse(e)},extractContents:function(){return this._traverse(h)},cloneContents:function(){return this._traverse(c)},insertNode:function(m){var j=this,l,k;if(m.nodeType===3||m.nodeType===4){l=j.startContainer.splitText(j.startOffset);j.startContainer.parentNode.insertBefore(m,l)}else{if(j.startContainer.childNodes.length>0){k=j.startContainer.childNodes[j.startOffset]}j.startContainer.insertBefore(m,k)}},surroundContents:function(l){var j=this,k=j.extractContents();j.insertNode(l);l.appendChild(k);j.selectNode(l)},cloneRange:function(){var j=this;return d(new a(j.dom),{startContainer:j.startContainer,startOffset:j.startOffset,endContainer:j.endContainer,endOffset:j.endOffset,collapsed:j.collapsed,commonAncestorContainer:j.commonAncestorContainer})},_isCollapsed:function(){return(this.startContainer==this.endContainer&&this.startOffset==this.endOffset)},_compareBoundaryPoints:function(m,p,k,o){var q,l,j,r,t,s;if(m==k){if(p==o){return 0}else{if(p<o){return -1}else{return 1}}}q=k;while(q&&q.parentNode!=m){q=q.parentNode}if(q){l=0;j=m.firstChild;while(j!=q&&l<p){l++;j=j.nextSibling}if(p<=l){return -1}else{return 1}}q=m;while(q&&q.parentNode!=k){q=q.parentNode}if(q){l=0;j=k.firstChild;while(j!=q&&l<o){l++;j=j.nextSibling}if(l<o){return -1}else{return 1}}r=this.dom.findCommonAncestor(m,k);t=m;while(t&&t.parentNode!=r){t=t.parentNode}if(!t){t=r}s=k;while(s&&s.parentNode!=r){s=s.parentNode}if(!s){s=r}if(t==s){return 0}j=r.firstChild;while(j){if(j==t){return -1}if(j==s){return 1}j=j.nextSibling}},_setEndPoint:function(k,q,p){var l=this,j,m;if(k){l.startContainer=q;l.startOffset=p}else{l.endContainer=q;l.endOffset=p}j=l.endContainer;while(j.parentNode){j=j.parentNode}m=l.startContainer;while(m.parentNode){m=m.parentNode}if(m!=j){l.collapse(k)}else{if(l._compareBoundaryPoints(l.startContainer,l.startOffset,l.endContainer,l.endOffset)>0){l.collapse(k)}}l.collapsed=l._isCollapsed();l.commonAncestorContainer=l.dom.findCommonAncestor(l.startContainer,l.endContainer)},_traverse:function(r){var s=this,q,m=0,v=0,k,o,l,n,j,u;if(s.startContainer==s.endContainer){return s._traverseSameContainer(r)}for(q=s.endContainer,k=q.parentNode;k!=null;q=k,k=k.parentNode){if(k==s.startContainer){return s._traverseCommonStartContainer(q,r)}++m}for(q=s.startContainer,k=q.parentNode;k!=null;q=k,k=k.parentNode){if(k==s.endContainer){return s._traverseCommonEndContainer(q,r)}++v}o=v-m;l=s.startContainer;while(o>0){l=l.parentNode;o--}n=s.endContainer;while(o<0){n=n.parentNode;o++}for(j=l.parentNode,u=n.parentNode;j!=u;j=j.parentNode,u=u.parentNode){l=j;n=u}return s._traverseCommonAncestors(l,n,r)},_traverseSameContainer:function(o){var r=this,q,u,j,k,l,p,m;if(o!=e){q=r.dom.doc.createDocumentFragment()}if(r.startOffset==r.endOffset){return q}if(r.startContainer.nodeType==3){u=r.startContainer.nodeValue;j=u.substring(r.startOffset,r.endOffset);if(o!=c){r.startContainer.deleteData(r.startOffset,r.endOffset-r.startOffset);r.collapse(true)}if(o==e){return null}q.appendChild(r.dom.doc.createTextNode(j));return q}k=i(r.startContainer,r.startOffset);l=r.endOffset-r.startOffset;while(l>0){p=k.nextSibling;m=r._traverseFullySelected(k,o);if(q){q.appendChild(m)}--l;k=p}if(o!=c){r.collapse(true)}return q},_traverseCommonStartContainer:function(j,p){var s=this,r,k,l,m,q,o;if(p!=e){r=s.dom.doc.createDocumentFragment()}k=s._traverseRightBoundary(j,p);if(r){r.appendChild(k)}l=g(j,s.startContainer);m=l-s.startOffset;if(m<=0){if(p!=c){s.setEndBefore(j);s.collapse(false)}return r}k=j.previousSibling;while(m>0){q=k.previousSibling;o=s._traverseFullySelected(k,p);if(r){r.insertBefore(o,r.firstChild)}--m;k=q}if(p!=c){s.setEndBefore(j);s.collapse(false)}return r},_traverseCommonEndContainer:function(m,p){var s=this,r,o,j,k,q,l;if(p!=e){r=s.dom.doc.createDocumentFragment()}j=s._traverseLeftBoundary(m,p);if(r){r.appendChild(j)}o=g(m,s.endContainer);++o;k=s.endOffset-o;j=m.nextSibling;while(k>0){q=j.nextSibling;l=s._traverseFullySelected(j,p);if(r){r.appendChild(l)}--k;j=q}if(p!=c){s.setStartAfter(m);s.collapse(true)}return r},_traverseCommonAncestors:function(p,j,s){var w=this,l,v,o,q,r,k,u,m;if(s!=e){v=w.dom.doc.createDocumentFragment()}l=w._traverseLeftBoundary(p,s);if(v){v.appendChild(l)}o=p.parentNode;q=g(p,o);r=g(j,o);++q;k=r-q;u=p.nextSibling;while(k>0){m=u.nextSibling;l=w._traverseFullySelected(u,s);if(v){v.appendChild(l)}u=m;--k}l=w._traverseRightBoundary(j,s);if(v){v.appendChild(l)}if(s!=c){w.setStartAfter(p);w.collapse(true)}return v},_traverseRightBoundary:function(p,q){var s=this,l=i(s.endContainer,s.endOffset-1),r,o,n,j,k;var m=l!=s.endContainer;if(l==p){return s._traverseNode(l,m,false,q)}r=l.parentNode;o=s._traverseNode(r,false,false,q);while(r!=null){while(l!=null){n=l.previousSibling;j=s._traverseNode(l,m,false,q);if(q!=e){o.insertBefore(j,o.firstChild)}m=true;l=n}if(r==p){return o}l=r.previousSibling;r=r.parentNode;k=s._traverseNode(r,false,false,q);if(q!=e){k.appendChild(o)}o=k}return null},_traverseLeftBoundary:function(p,q){var s=this,m=i(s.startContainer,s.startOffset);var n=m!=s.startContainer,r,o,l,j,k;if(m==p){return s._traverseNode(m,n,true,q)}r=m.parentNode;o=s._traverseNode(r,false,true,q);while(r!=null){while(m!=null){l=m.nextSibling;j=s._traverseNode(m,n,true,q);if(q!=e){o.appendChild(j)}n=true;m=l}if(r==p){return o}m=r.nextSibling;r=r.parentNode;k=s._traverseNode(r,false,true,q);if(q!=e){k.appendChild(o)}o=k}return null},_traverseNode:function(j,o,r,s){var u=this,m,l,p,k,q;if(o){return u._traverseFullySelected(j,s)}if(j.nodeType==3){m=j.nodeValue;if(r){k=u.startOffset;l=m.substring(k);p=m.substring(0,k)}else{k=u.endOffset;l=m.substring(0,k);p=m.substring(k)}if(s!=c){j.nodeValue=p}if(s==e){return null}q=j.cloneNode(false);q.nodeValue=l;return q}if(s==e){return null}return j.cloneNode(false)},_traverseFullySelected:function(l,k){var j=this;if(k!=e){return k==c?l.cloneNode(true):l}l.parentNode.removeChild(l);return null}});f.Range=a})(tinymce.dom);(function(){function a(e){var d=this,h="\uFEFF",b,g;function c(j,i){if(j&&i){if(j.item&&i.item&&j.item(0)===i.item(0)){return 1}if(j.isEqual&&i.isEqual&&i.isEqual(j)){return 1}}return 0}function f(){var m=e.dom,j=e.getRng(),s=m.createRng(),p,k,n,q,o,l;function i(v){var t=v.parentNode.childNodes,u;for(u=t.length-1;u>=0;u--){if(t[u]==v){return u}}return -1}function r(v){var t=j.duplicate(),B,y,u,w,x=0,z=0,A,C;t.collapse(v);B=t.parentElement();t.pasteHTML(h);u=B.childNodes;for(y=0;y<u.length;y++){w=u[y];if(y>0&&(w.nodeType!==3||u[y-1].nodeType!==3)){z++}if(w.nodeType===3){A=w.nodeValue.indexOf(h);if(A!==-1){x+=A;break}x+=w.nodeValue.length}else{x=0}}t.moveStart("character",-1);t.text="";return{index:z,offset:x,parent:B}}n=j.item?j.item(0):j.parentElement();if(n.ownerDocument!=m.doc){return s}if(j.item||!n.hasChildNodes()){s.setStart(n.parentNode,i(n));s.setEnd(s.startContainer,s.startOffset+1);return s}l=e.isCollapsed();p=r(true);k=r(false);p.parent.normalize();k.parent.normalize();q=p.parent.childNodes[Math.min(p.index,p.parent.childNodes.length-1)];if(q.nodeType!=3){s.setStart(p.parent,p.index)}else{s.setStart(p.parent.childNodes[p.index],p.offset)}o=k.parent.childNodes[Math.min(k.index,k.parent.childNodes.length-1)];if(o.nodeType!=3){if(!l){k.index++}s.setEnd(k.parent,k.index)}else{s.setEnd(k.parent.childNodes[k.index],k.offset)}if(!l){q=s.startContainer;if(q.nodeType==1){s.setStart(q,Math.min(s.startOffset,q.childNodes.length))}o=s.endContainer;if(o.nodeType==1){s.setEnd(o,Math.min(s.endOffset,o.childNodes.length))}}d.addRange(s);return s}this.addRange=function(j){var o,m=e.dom.doc.body,p,k,q,l,n,i;q=j.startContainer;l=j.startOffset;n=j.endContainer;i=j.endOffset;o=m.createTextRange();q=q.nodeType==1?q.childNodes[Math.min(l,q.childNodes.length-1)]:q;n=n.nodeType==1?n.childNodes[Math.min(l==i?i:i-1,n.childNodes.length-1)]:n;if(q==n&&q.nodeType==1){if(/^(IMG|TABLE)$/.test(q.nodeName)&&l!=i){o=m.createControlRange();o.addElement(q)}else{o=m.createTextRange();if(!q.hasChildNodes()&&q.canHaveHTML){q.innerHTML=h}o.moveToElementText(q);if(q.innerHTML==h){o.collapse(true);q.removeChild(q.firstChild)}}if(l==i){o.collapse(i<=j.endContainer.childNodes.length-1)}o.select();return}function r(t,v){var u,s,w;if(t.nodeType!=3){return -1}u=t.nodeValue;s=m.createTextRange();t.nodeValue=u.substring(0,v)+h+u.substring(v);s.moveToElementText(t.parentNode);s.findText(h);w=Math.abs(s.moveStart("character",-1048575));t.nodeValue=u;return w}if(j.collapsed){pos=r(q,l);o=m.createTextRange();o.move("character",pos);o.select();return}else{if(q==n&&q.nodeType==3){p=r(q,l);o=m.createTextRange();o.move("character",p);o.moveEnd("character",i-l);o.select();return}p=r(q,l);k=r(n,i);o=m.createTextRange();if(p==-1){o.moveToElementText(q);p=0}else{o.move("character",p)}tmpRng=m.createTextRange();if(k==-1){tmpRng.moveToElementText(n)}else{tmpRng.move("character",k)}o.setEndPoint("EndToEnd",tmpRng);o.select();return}};this.getRangeAt=function(){if(!b||!c(g,e.getRng())){b=f();g=e.getRng()}return b};this.destroy=function(){g=b=null}}tinymce.dom.TridentSelection=a})();(function(){var p=/((?:\((?:\([^()]+\)|[^()]+)+\)|\[(?:\[[^[\]]*\]|['"][^'"]*['"]|[^[\]'"]+)+\]|\\.|[^ >+~,(\[\\]+)+|[>+~])(\s*,\s*)?/g,i=0,d=Object.prototype.toString,n=false;var b=function(D,t,A,v){A=A||[];var e=t=t||document;if(t.nodeType!==1&&t.nodeType!==9){return[]}if(!D||typeof D!=="string"){return A}var B=[],C,y,G,F,z,s,r=true,w=o(t);p.lastIndex=0;while((C=p.exec(D))!==null){B.push(C[1]);if(C[2]){s=RegExp.rightContext;break}}if(B.length>1&&j.exec(D)){if(B.length===2&&f.relative[B[0]]){y=g(B[0]+B[1],t)}else{y=f.relative[B[0]]?[t]:b(B.shift(),t);while(B.length){D=B.shift();if(f.relative[D]){D+=B.shift()}y=g(D,y)}}}else{if(!v&&B.length>1&&t.nodeType===9&&!w&&f.match.ID.test(B[0])&&!f.match.ID.test(B[B.length-1])){var H=b.find(B.shift(),t,w);t=H.expr?b.filter(H.expr,H.set)[0]:H.set[0]}if(t){var H=v?{expr:B.pop(),set:a(v)}:b.find(B.pop(),B.length===1&&(B[0]==="~"||B[0]==="+")&&t.parentNode?t.parentNode:t,w);y=H.expr?b.filter(H.expr,H.set):H.set;if(B.length>0){G=a(y)}else{r=false}while(B.length){var u=B.pop(),x=u;if(!f.relative[u]){u=""}else{x=B.pop()}if(x==null){x=t}f.relative[u](G,x,w)}}else{G=B=[]}}if(!G){G=y}if(!G){throw"Syntax error, unrecognized expression: "+(u||D)}if(d.call(G)==="[object Array]"){if(!r){A.push.apply(A,G)}else{if(t&&t.nodeType===1){for(var E=0;G[E]!=null;E++){if(G[E]&&(G[E]===true||G[E].nodeType===1&&h(t,G[E]))){A.push(y[E])}}}else{for(var E=0;G[E]!=null;E++){if(G[E]&&G[E].nodeType===1){A.push(y[E])}}}}}else{a(G,A)}if(s){b(s,e,A,v);b.uniqueSort(A)}return A};b.uniqueSort=function(r){if(c){n=false;r.sort(c);if(n){for(var e=1;e<r.length;e++){if(r[e]===r[e-1]){r.splice(e--,1)}}}}};b.matches=function(e,r){return b(e,null,null,r)};b.find=function(x,e,y){var w,u;if(!x){return[]}for(var t=0,s=f.order.length;t<s;t++){var v=f.order[t],u;if((u=f.match[v].exec(x))){var r=RegExp.leftContext;if(r.substr(r.length-1)!=="\\"){u[1]=(u[1]||"").replace(/\\/g,"");w=f.find[v](u,e,y);if(w!=null){x=x.replace(f.match[v],"");break}}}}if(!w){w=e.getElementsByTagName("*")}return{set:w,expr:x}};b.filter=function(A,z,D,t){var s=A,F=[],x=z,v,e,w=z&&z[0]&&o(z[0]);while(A&&z.length){for(var y in f.filter){if((v=f.match[y].exec(A))!=null){var r=f.filter[y],E,C;e=false;if(x==F){F=[]}if(f.preFilter[y]){v=f.preFilter[y](v,x,D,F,t,w);if(!v){e=E=true}else{if(v===true){continue}}}if(v){for(var u=0;(C=x[u])!=null;u++){if(C){E=r(C,v,u,x);var B=t^!!E;if(D&&E!=null){if(B){e=true}else{x[u]=false}}else{if(B){F.push(C);e=true}}}}}if(E!==undefined){if(!D){x=F}A=A.replace(f.match[y],"");if(!e){return[]}break}}}if(A==s){if(e==null){throw"Syntax error, unrecognized expression: "+A}else{break}}s=A}return x};var f=b.selectors={order:["ID","NAME","TAG"],match:{ID:/#((?:[\w\u00c0-\uFFFF_-]|\\.)+)/,CLASS:/\.((?:[\w\u00c0-\uFFFF_-]|\\.)+)/,NAME:/\[name=['"]*((?:[\w\u00c0-\uFFFF_-]|\\.)+)['"]*\]/,ATTR:/\[\s*((?:[\w\u00c0-\uFFFF_-]|\\.)+)\s*(?:(\S?=)\s*(['"]*)(.*?)\3|)\s*\]/,TAG:/^((?:[\w\u00c0-\uFFFF\*_-]|\\.)+)/,CHILD:/:(only|nth|last|first)-child(?:\((even|odd|[\dn+-]*)\))?/,POS:/:(nth|eq|gt|lt|first|last|even|odd)(?:\((\d*)\))?(?=[^-]|$)/,PSEUDO:/:((?:[\w\u00c0-\uFFFF_-]|\\.)+)(?:\((['"]*)((?:\([^\)]+\)|[^\2\(\)]*)+)\2\))?/},attrMap:{"class":"className","for":"htmlFor"},attrHandle:{href:function(e){return e.getAttribute("href")}},relative:{"+":function(x,e,w){var u=typeof e==="string",y=u&&!/\W/.test(e),v=u&&!y;if(y&&!w){e=e.toUpperCase()}for(var t=0,s=x.length,r;t<s;t++){if((r=x[t])){while((r=r.previousSibling)&&r.nodeType!==1){}x[t]=v||r&&r.nodeName===e?r||false:r===e}}if(v){b.filter(e,x,true)}},">":function(w,r,x){var u=typeof r==="string";if(u&&!/\W/.test(r)){r=x?r:r.toUpperCase();for(var s=0,e=w.length;s<e;s++){var v=w[s];if(v){var t=v.parentNode;w[s]=t.nodeName===r?t:false}}}else{for(var s=0,e=w.length;s<e;s++){var v=w[s];if(v){w[s]=u?v.parentNode:v.parentNode===r}}if(u){b.filter(r,w,true)}}},"":function(t,r,v){var s=i++,e=q;if(!r.match(/\W/)){var u=r=v?r:r.toUpperCase();e=m}e("parentNode",r,s,t,u,v)},"~":function(t,r,v){var s=i++,e=q;if(typeof r==="string"&&!r.match(/\W/)){var u=r=v?r:r.toUpperCase();e=m}e("previousSibling",r,s,t,u,v)}},find:{ID:function(r,s,t){if(typeof s.getElementById!=="undefined"&&!t){var e=s.getElementById(r[1]);return e?[e]:[]}},NAME:function(s,v,w){if(typeof v.getElementsByName!=="undefined"){var r=[],u=v.getElementsByName(s[1]);for(var t=0,e=u.length;t<e;t++){if(u[t].getAttribute("name")===s[1]){r.push(u[t])}}return r.length===0?null:r}},TAG:function(e,r){return r.getElementsByTagName(e[1])}},preFilter:{CLASS:function(t,r,s,e,w,x){t=" "+t[1].replace(/\\/g,"")+" ";if(x){return t}for(var u=0,v;(v=r[u])!=null;u++){if(v){if(w^(v.className&&(" "+v.className+" ").indexOf(t)>=0)){if(!s){e.push(v)}}else{if(s){r[u]=false}}}}return false},ID:function(e){return e[1].replace(/\\/g,"")},TAG:function(r,e){for(var s=0;e[s]===false;s++){}return e[s]&&o(e[s])?r[1]:r[1].toUpperCase()},CHILD:function(e){if(e[1]=="nth"){var r=/(-?)(\d*)n((?:\+|-)?\d*)/.exec(e[2]=="even"&&"2n"||e[2]=="odd"&&"2n+1"||!/\D/.test(e[2])&&"0n+"+e[2]||e[2]);e[2]=(r[1]+(r[2]||1))-0;e[3]=r[3]-0}e[0]=i++;return e},ATTR:function(u,r,s,e,v,w){var t=u[1].replace(/\\/g,"");if(!w&&f.attrMap[t]){u[1]=f.attrMap[t]}if(u[2]==="~="){u[4]=" "+u[4]+" "}return u},PSEUDO:function(u,r,s,e,v){if(u[1]==="not"){if(u[3].match(p).length>1||/^\w/.test(u[3])){u[3]=b(u[3],null,null,r)}else{var t=b.filter(u[3],r,s,true^v);if(!s){e.push.apply(e,t)}return false}}else{if(f.match.POS.test(u[0])||f.match.CHILD.test(u[0])){return true}}return u},POS:function(e){e.unshift(true);return e}},filters:{enabled:function(e){return e.disabled===false&&e.type!=="hidden"},disabled:function(e){return e.disabled===true},checked:function(e){return e.checked===true},selected:function(e){e.parentNode.selectedIndex;return e.selected===true},parent:function(e){return !!e.firstChild},empty:function(e){return !e.firstChild},has:function(s,r,e){return !!b(e[3],s).length},header:function(e){return/h\d/i.test(e.nodeName)},text:function(e){return"text"===e.type},radio:function(e){return"radio"===e.type},checkbox:function(e){return"checkbox"===e.type},file:function(e){return"file"===e.type},password:function(e){return"password"===e.type},submit:function(e){return"submit"===e.type},image:function(e){return"image"===e.type},reset:function(e){return"reset"===e.type},button:function(e){return"button"===e.type||e.nodeName.toUpperCase()==="BUTTON"},input:function(e){return/input|select|textarea|button/i.test(e.nodeName)}},setFilters:{first:function(r,e){return e===0},last:function(s,r,e,t){return r===t.length-1},even:function(r,e){return e%2===0},odd:function(r,e){return e%2===1},lt:function(s,r,e){return r<e[3]-0},gt:function(s,r,e){return r>e[3]-0},nth:function(s,r,e){return e[3]-0==r},eq:function(s,r,e){return e[3]-0==r}},filter:{PSEUDO:function(w,s,t,x){var r=s[1],u=f.filters[r];if(u){return u(w,t,s,x)}else{if(r==="contains"){return(w.textContent||w.innerText||"").indexOf(s[3])>=0}else{if(r==="not"){var v=s[3];for(var t=0,e=v.length;t<e;t++){if(v[t]===w){return false}}return true}}}},CHILD:function(e,t){var w=t[1],r=e;switch(w){case"only":case"first":while(r=r.previousSibling){if(r.nodeType===1){return false}}if(w=="first"){return true}r=e;case"last":while(r=r.nextSibling){if(r.nodeType===1){return false}}return true;case"nth":var s=t[2],z=t[3];if(s==1&&z==0){return true}var v=t[0],y=e.parentNode;if(y&&(y.sizcache!==v||!e.nodeIndex)){var u=0;for(r=y.firstChild;r;r=r.nextSibling){if(r.nodeType===1){r.nodeIndex=++u}}y.sizcache=v}var x=e.nodeIndex-z;if(s==0){return x==0}else{return(x%s==0&&x/s>=0)}}},ID:function(r,e){return r.nodeType===1&&r.getAttribute("id")===e},TAG:function(r,e){return(e==="*"&&r.nodeType===1)||r.nodeName===e},CLASS:function(r,e){return(" "+(r.className||r.getAttribute("class"))+" ").indexOf(e)>-1},ATTR:function(v,t){var s=t[1],e=f.attrHandle[s]?f.attrHandle[s](v):v[s]!=null?v[s]:v.getAttribute(s),w=e+"",u=t[2],r=t[4];return e==null?u==="!=":u==="="?w===r:u==="*="?w.indexOf(r)>=0:u==="~="?(" "+w+" ").indexOf(r)>=0:!r?w&&e!==false:u==="!="?w!=r:u==="^="?w.indexOf(r)===0:u==="$="?w.substr(w.length-r.length)===r:u==="|="?w===r||w.substr(0,r.length+1)===r+"-":false},POS:function(u,r,s,v){var e=r[2],t=f.setFilters[e];if(t){return t(u,s,r,v)}}}};var j=f.match.POS;for(var l in f.match){f.match[l]=new RegExp(f.match[l].source+/(?![^\[]*\])(?![^\(]*\))/.source)}var a=function(r,e){r=Array.prototype.slice.call(r);if(e){e.push.apply(e,r);return e}return r};try{Array.prototype.slice.call(document.documentElement.childNodes)}catch(k){a=function(u,t){var r=t||[];if(d.call(u)==="[object Array]"){Array.prototype.push.apply(r,u)}else{if(typeof u.length==="number"){for(var s=0,e=u.length;s<e;s++){r.push(u[s])}}else{for(var s=0;u[s];s++){r.push(u[s])}}}return r}}var c;if(document.documentElement.compareDocumentPosition){c=function(r,e){var s=r.compareDocumentPosition(e)&4?-1:r===e?0:1;if(s===0){n=true}return s}}else{if("sourceIndex" in document.documentElement){c=function(r,e){var s=r.sourceIndex-e.sourceIndex;if(s===0){n=true}return s}}else{if(document.createRange){c=function(t,r){var s=t.ownerDocument.createRange(),e=r.ownerDocument.createRange();s.setStart(t,0);s.setEnd(t,0);e.setStart(r,0);e.setEnd(r,0);var u=s.compareBoundaryPoints(Range.START_TO_END,e);if(u===0){n=true}return u}}}}(function(){var r=document.createElement("div"),s="script"+(new Date).getTime();r.innerHTML="<a name='"+s+"'/>";var e=document.documentElement;e.insertBefore(r,e.firstChild);if(!!document.getElementById(s)){f.find.ID=function(u,v,w){if(typeof v.getElementById!=="undefined"&&!w){var t=v.getElementById(u[1]);return t?t.id===u[1]||typeof t.getAttributeNode!=="undefined"&&t.getAttributeNode("id").nodeValue===u[1]?[t]:undefined:[]}};f.filter.ID=function(v,t){var u=typeof v.getAttributeNode!=="undefined"&&v.getAttributeNode("id");return v.nodeType===1&&u&&u.nodeValue===t}}e.removeChild(r)})();(function(){var e=document.createElement("div");e.appendChild(document.createComment(""));if(e.getElementsByTagName("*").length>0){f.find.TAG=function(r,v){var u=v.getElementsByTagName(r[1]);if(r[1]==="*"){var t=[];for(var s=0;u[s];s++){if(u[s].nodeType===1){t.push(u[s])}}u=t}return u}}e.innerHTML="<a href='#'></a>";if(e.firstChild&&typeof e.firstChild.getAttribute!=="undefined"&&e.firstChild.getAttribute("href")!=="#"){f.attrHandle.href=function(r){return r.getAttribute("href",2)}}})();if(document.querySelectorAll){(function(){var e=b,s=document.createElement("div");s.innerHTML="<p class='TEST'></p>";if(s.querySelectorAll&&s.querySelectorAll(".TEST").length===0){return}b=function(w,v,t,u){v=v||document;if(!u&&v.nodeType===9&&!o(v)){try{return a(v.querySelectorAll(w),t)}catch(x){}}return e(w,v,t,u)};for(var r in e){b[r]=e[r]}})()}if(document.getElementsByClassName&&document.documentElement.getElementsByClassName){(function(){var e=document.createElement("div");e.innerHTML="<div class='test e'></div><div class='test'></div>";if(e.getElementsByClassName("e").length===0){return}e.lastChild.className="e";if(e.getElementsByClassName("e").length===1){return}f.order.splice(1,0,"CLASS");f.find.CLASS=function(r,s,t){if(typeof s.getElementsByClassName!=="undefined"&&!t){return s.getElementsByClassName(r[1])}}})()}function m(r,w,v,A,x,z){var y=r=="previousSibling"&&!z;for(var t=0,s=A.length;t<s;t++){var e=A[t];if(e){if(y&&e.nodeType===1){e.sizcache=v;e.sizset=t}e=e[r];var u=false;while(e){if(e.sizcache===v){u=A[e.sizset];break}if(e.nodeType===1&&!z){e.sizcache=v;e.sizset=t}if(e.nodeName===w){u=e;break}e=e[r]}A[t]=u}}}function q(r,w,v,A,x,z){var y=r=="previousSibling"&&!z;for(var t=0,s=A.length;t<s;t++){var e=A[t];if(e){if(y&&e.nodeType===1){e.sizcache=v;e.sizset=t}e=e[r];var u=false;while(e){if(e.sizcache===v){u=A[e.sizset];break}if(e.nodeType===1){if(!z){e.sizcache=v;e.sizset=t}if(typeof w!=="string"){if(e===w){u=true;break}}else{if(b.filter(w,[e]).length>0){u=e;break}}}e=e[r]}A[t]=u}}}var h=document.compareDocumentPosition?function(r,e){return r.compareDocumentPosition(e)&16}:function(r,e){return r!==e&&(r.contains?r.contains(e):true)};var o=function(e){return e.nodeType===9&&e.documentElement.nodeName!=="HTML"||!!e.ownerDocument&&e.ownerDocument.documentElement.nodeName!=="HTML"};var g=function(e,x){var t=[],u="",v,s=x.nodeType?[x]:x;while((v=f.match.PSEUDO.exec(e))){u+=v[0];e=e.replace(f.match.PSEUDO,"")}e=f.relative[e]?e+"*":e;for(var w=0,r=s.length;w<r;w++){b(e,s[w],t)}return b.filter(u,t)};window.tinymce.dom.Sizzle=b})();(function(d){var f=d.each,c=d.DOM,b=d.isIE,e=d.isWebKit,a;d.create("tinymce.dom.EventUtils",{EventUtils:function(){this.inits=[];this.events=[]},add:function(m,p,l,j){var g,h=this,i=h.events,k;if(p instanceof Array){k=[];f(p,function(o){k.push(h.add(m,o,l,j))});return k}if(m&&m.hasOwnProperty&&m instanceof Array){k=[];f(m,function(n){n=c.get(n);k.push(h.add(n,p,l,j))});return k}m=c.get(m);if(!m){return}g=function(n){if(h.disabled){return}n=n||window.event;if(n&&b){if(!n.target){n.target=n.srcElement}d.extend(n,h._stoppers)}if(!j){return l(n)}return l.call(j,n)};if(p=="unload"){d.unloads.unshift({func:g});return g}if(p=="init"){if(h.domLoaded){g()}else{h.inits.push(g)}return g}i.push({obj:m,name:p,func:l,cfunc:g,scope:j});h._add(m,p,g);return l},remove:function(l,m,k){var h=this,g=h.events,i=false,j;if(l&&l.hasOwnProperty&&l instanceof Array){j=[];f(l,function(n){n=c.get(n);j.push(h.remove(n,m,k))});return j}l=c.get(l);f(g,function(o,n){if(o.obj==l&&o.name==m&&(!k||(o.func==k||o.cfunc==k))){g.splice(n,1);h._remove(l,m,o.cfunc);i=true;return false}});return i},clear:function(l){var j=this,g=j.events,h,k;if(l){l=c.get(l);for(h=g.length-1;h>=0;h--){k=g[h];if(k.obj===l){j._remove(k.obj,k.name,k.cfunc);k.obj=k.cfunc=null;g.splice(h,1)}}}},cancel:function(g){if(!g){return false}this.stop(g);return this.prevent(g)},stop:function(g){if(g.stopPropagation){g.stopPropagation()}else{g.cancelBubble=true}return false},prevent:function(g){if(g.preventDefault){g.preventDefault()}else{g.returnValue=false}return false},destroy:function(){var g=this;f(g.events,function(j,h){g._remove(j.obj,j.name,j.cfunc);j.obj=j.cfunc=null});g.events=[];g=null},_add:function(h,i,g){if(h.attachEvent){h.attachEvent("on"+i,g)}else{if(h.addEventListener){h.addEventListener(i,g,false)}else{h["on"+i]=g}}},_remove:function(i,j,h){if(i){try{if(i.detachEvent){i.detachEvent("on"+j,h)}else{if(i.removeEventListener){i.removeEventListener(j,h,false)}else{i["on"+j]=null}}}catch(g){}}},_pageInit:function(h){var g=this;if(g.domLoaded){return}g.domLoaded=true;f(g.inits,function(i){i()});g.inits=[]},_wait:function(i){var g=this,h=i.document;if(i.tinyMCE_GZ&&tinyMCE_GZ.loaded){g.domLoaded=1;return}if(h.attachEvent){h.attachEvent("onreadystatechange",function(){if(h.readyState==="complete"){h.detachEvent("onreadystatechange",arguments.callee);g._pageInit(i)}});if(h.documentElement.doScroll&&i==i.top){(function(){if(g.domLoaded){return}try{h.documentElement.doScroll("left")}catch(j){setTimeout(arguments.callee,0);return}g._pageInit(i)})()}}else{if(h.addEventListener){g._add(i,"DOMContentLoaded",function(){g._pageInit(i)})}}g._add(i,"load",function(){g._pageInit(i)})},_stoppers:{preventDefault:function(){this.returnValue=false},stopPropagation:function(){this.cancelBubble=true}}});a=d.dom.Event=new d.dom.EventUtils();a._wait(window);d.addUnload(function(){a.destroy()})})(tinymce);(function(a){var b=a.each;a.create("tinymce.dom.Element",{Element:function(g,e){var c=this,f,d;e=e||{};c.id=g;c.dom=f=e.dom||a.DOM;c.settings=e;if(!a.isIE){d=c.dom.get(c.id)}b(["getPos","getRect","getParent","add","setStyle","getStyle","setStyles","setAttrib","setAttribs","getAttrib","addClass","removeClass","hasClass","getOuterHTML","setOuterHTML","remove","show","hide","isHidden","setHTML","get"],function(h){c[h]=function(){var j=[g],k;for(k=0;k<arguments.length;k++){j.push(arguments[k])}j=f[h].apply(f,j);c.update(h);return j}})},on:function(e,d,c){return a.dom.Event.add(this.id,e,d,c)},getXY:function(){return{x:parseInt(this.getStyle("left")),y:parseInt(this.getStyle("top"))}},getSize:function(){var c=this.dom.get(this.id);return{w:parseInt(this.getStyle("width")||c.clientWidth),h:parseInt(this.getStyle("height")||c.clientHeight)}},moveTo:function(c,d){this.setStyles({left:c,top:d})},moveBy:function(c,e){var d=this.getXY();this.moveTo(d.x+c,d.y+e)},resizeTo:function(c,d){this.setStyles({width:c,height:d})},resizeBy:function(c,e){var d=this.getSize();this.resizeTo(d.w+c,d.h+e)},update:function(d){var e=this,c,f=e.dom;if(a.isIE6&&e.settings.blocker){d=d||"";if(d.indexOf("get")===0||d.indexOf("has")===0||d.indexOf("is")===0){return}if(d=="remove"){f.remove(e.blocker);return}if(!e.blocker){e.blocker=f.uniqueId();c=f.add(e.settings.container||f.getRoot(),"iframe",{id:e.blocker,style:"position:absolute;",frameBorder:0,src:'javascript:""'});f.setStyle(c,"opacity",0)}else{c=f.get(e.blocker)}f.setStyle(c,"left",e.getStyle("left",1));f.setStyle(c,"top",e.getStyle("top",1));f.setStyle(c,"width",e.getStyle("width",1));f.setStyle(c,"height",e.getStyle("height",1));f.setStyle(c,"display",e.getStyle("display",1));f.setStyle(c,"zIndex",parseInt(e.getStyle("zIndex",1)||0)-1)}}})})(tinymce);(function(c){function e(f){return f.replace(/[\n\r]+/g,"")}var b=c.is,a=c.isIE,d=c.each;c.create("tinymce.dom.Selection",{Selection:function(i,h,g){var f=this;f.dom=i;f.win=h;f.serializer=g;d(["onBeforeSetContent","onBeforeGetContent","onSetContent","onGetContent"],function(j){f[j]=new c.util.Dispatcher(f)});if(!f.win.getSelection){f.tridentSel=new c.dom.TridentSelection(f)}c.addUnload(f.destroy,f)},getContent:function(g){var f=this,h=f.getRng(),l=f.dom.create("body"),j=f.getSel(),i,k,m;g=g||{};i=k="";g.get=true;g.format=g.format||"html";f.onBeforeGetContent.dispatch(f,g);if(g.format=="text"){return f.isCollapsed()?"":(h.text||(j.toString?j.toString():""))}if(h.cloneContents){m=h.cloneContents();if(m){l.appendChild(m)}}else{if(b(h.item)||b(h.htmlText)){l.innerHTML=h.item?h.item(0).outerHTML:h.htmlText}else{l.innerHTML=h.toString()}}if(/^\s/.test(l.innerHTML)){i=" "}if(/\s+$/.test(l.innerHTML)){k=" "}g.getInner=true;g.content=f.isCollapsed()?"":i+f.serializer.serialize(l,g)+k;f.onGetContent.dispatch(f,g);return g.content},setContent:function(i,g){var f=this,j=f.getRng(),l,k=f.win.document;g=g||{format:"html"};g.set=true;i=g.content=f.dom.processHTML(i);f.onBeforeSetContent.dispatch(f,g);i=g.content;if(j.insertNode){i+='<span id="__caret">_</span>';j.deleteContents();j.insertNode(f.getRng().createContextualFragment(i));l=f.dom.get("__caret");j=k.createRange();j.setStartBefore(l);j.setEndAfter(l);f.setRng(j);f.dom.remove("__caret")}else{if(j.item){k.execCommand("Delete",false,null);j=f.getRng()}j.pasteHTML(i)}f.onSetContent.dispatch(f,g)},getStart:function(){var f=this,g=f.getRng(),h;if(a){if(g.item){return g.item(0)}g=g.duplicate();g.collapse(1);h=g.parentElement();if(h&&h.nodeName=="BODY"){return h.firstChild}return h}else{h=g.startContainer;if(h.nodeName=="BODY"){return h.firstChild}return f.dom.getParent(h,"*")}},getEnd:function(){var f=this,g=f.getRng(),h;if(a){if(g.item){return g.item(0)}g=g.duplicate();g.collapse(0);h=g.parentElement();if(h&&h.nodeName=="BODY"){return h.lastChild}return h}else{h=g.endContainer;if(h.nodeName=="BODY"){return h.lastChild}return f.dom.getParent(h,"*")}},getBookmark:function(x){var j=this,m=j.getRng(),f,n,l,u=j.dom.getViewPort(j.win),v,p,z,o,w=-16777215,k,h=j.dom.getRoot(),g=0,i=0,y;n=u.x;l=u.y;if(x){return{rng:m,scrollX:n,scrollY:l}}if(a){if(m.item){v=m.item(0);d(j.dom.select(v.nodeName),function(s,r){if(v==s){p=r;return false}});return{tag:v.nodeName,index:p,scrollX:n,scrollY:l}}f=j.dom.doc.body.createTextRange();f.moveToElementText(h);f.collapse(true);z=Math.abs(f.move("character",w));f=m.duplicate();f.collapse(true);p=Math.abs(f.move("character",w));f=m.duplicate();f.collapse(false);o=Math.abs(f.move("character",w))-p;return{start:p-z,length:o,scrollX:n,scrollY:l}}v=j.getNode();k=j.getSel();if(!k){return null}if(v&&v.nodeName=="IMG"){return{scrollX:n,scrollY:l}}function q(A,D,t){var s=j.dom.doc.createTreeWalker(A,NodeFilter.SHOW_TEXT,null,false),E,B=0,C={};while((E=s.nextNode())!=null){if(E==D){C.start=B}if(E==t){C.end=B;return C}B+=e(E.nodeValue||"").length}return null}if(k.anchorNode==k.focusNode&&k.anchorOffset==k.focusOffset){v=q(h,k.anchorNode,k.focusNode);if(!v){return{scrollX:n,scrollY:l}}e(k.anchorNode.nodeValue||"").replace(/^\s+/,function(r){g=r.length});return{start:Math.max(v.start+k.anchorOffset-g,0),end:Math.max(v.end+k.focusOffset-g,0),scrollX:n,scrollY:l,beg:k.anchorOffset-g==0}}else{v=q(h,m.startContainer,m.endContainer);if(!v){return{scrollX:n,scrollY:l}}return{start:Math.max(v.start+m.startOffset-g,0),end:Math.max(v.end+m.endOffset-i,0),scrollX:n,scrollY:l,beg:m.startOffset-g==0}}},moveToBookmark:function(n){var o=this,g=o.getRng(),p=o.getSel(),j=o.dom.getRoot(),m,h,k;function i(q,t,D){var B=o.dom.doc.createTreeWalker(q,NodeFilter.SHOW_TEXT,null,false),x,s=0,A={},u,C,z,y;while((x=B.nextNode())!=null){z=y=0;k=x.nodeValue||"";h=e(k).length;s+=h;if(s>=t&&!A.startNode){u=t-(s-h);if(n.beg&&u>=h){continue}A.startNode=x;A.startOffset=u+y}if(s>=D){A.endNode=x;A.endOffset=D-(s-h)+y;return A}}return null}if(!n){return false}o.win.scrollTo(n.scrollX,n.scrollY);if(a){o.tridentSel.destroy();if(g=n.rng){try{g.select()}catch(l){}return true}o.win.focus();if(n.tag){g=j.createControlRange();d(o.dom.select(n.tag),function(r,q){if(q==n.index){g.addElement(r)}})}else{try{if(n.start<0){return true}g=p.createRange();g.moveToElementText(j);g.collapse(true);g.moveStart("character",n.start);g.moveEnd("character",n.length)}catch(f){return true}}try{g.select()}catch(l){}return true}if(!p){return false}if(n.rng){p.removeAllRanges();p.addRange(n.rng)}else{if(b(n.start)&&b(n.end)){try{m=i(j,n.start,n.end);if(m){g=o.dom.doc.createRange();g.setStart(m.startNode,m.startOffset);g.setEnd(m.endNode,m.endOffset);p.removeAllRanges();p.addRange(g)}if(!c.isOpera){o.win.focus()}}catch(l){}}}},select:function(g,l){var p=this,f=p.getRng(),q=p.getSel(),o,m,k,j=p.win.document;function h(u,t){var s,r;if(u){s=j.createTreeWalker(u,NodeFilter.SHOW_TEXT,null,false);while(u=s.nextNode()){r=u;if(c.trim(u.nodeValue).length!=0){if(t){return u}else{r=u}}}}return r}if(a){try{o=j.body;if(/^(IMG|TABLE)$/.test(g.nodeName)){f=o.createControlRange();f.addElement(g)}else{f=o.createTextRange();f.moveToElementText(g)}f.select()}catch(i){}}else{if(l){m=h(g,1)||p.dom.select("br:first",g)[0];k=h(g,0)||p.dom.select("br:last",g)[0];if(m&&k){f=j.createRange();if(m.nodeName=="BR"){f.setStartBefore(m)}else{f.setStart(m,0)}if(k.nodeName=="BR"){f.setEndBefore(k)}else{f.setEnd(k,k.nodeValue.length)}}else{f.selectNode(g)}}else{f.selectNode(g)}p.setRng(f)}return g},isCollapsed:function(){var f=this,h=f.getRng(),g=f.getSel();if(!h||h.item){return false}return !g||h.boundingWidth==0||h.collapsed},collapse:function(f){var g=this,h=g.getRng(),i;if(h.item){i=h.item(0);h=this.win.document.body.createTextRange();h.moveToElementText(i)}h.collapse(!!f);g.setRng(h)},getSel:function(){var g=this,f=this.win;return f.getSelection?f.getSelection():f.document.selection},getRng:function(j){var g=this,h,i;if(j&&g.tridentSel){return g.tridentSel.getRangeAt(0)}try{if(h=g.getSel()){i=h.rangeCount>0?h.getRangeAt(0):(h.createRange?h.createRange():g.win.document.createRange())}}catch(f){}if(!i){i=a?g.win.document.body.createTextRange():g.win.document.createRange()}return i},setRng:function(i){var h,g=this;if(!g.tridentSel){h=g.getSel();if(h){h.removeAllRanges();h.addRange(i)}}else{if(i.cloneRange){g.tridentSel.addRange(i);return}try{i.select()}catch(f){}}},setNode:function(g){var f=this;f.setContent(f.dom.getOuterHTML(g));return g},getNode:function(){var f=this,h=f.getRng(),g=f.getSel(),i;if(!a){if(!h){return f.dom.getRoot()}i=h.commonAncestorContainer;if(!h.collapsed){if(c.isWebKit&&g.anchorNode&&g.anchorNode.nodeType==1){return g.anchorNode.childNodes[g.anchorOffset]}if(h.startContainer==h.endContainer){if(h.startOffset-h.endOffset<2){if(h.startContainer.hasChildNodes()){i=h.startContainer.childNodes[h.startOffset]}}}}return f.dom.getParent(i,"*")}return h.item?h.item(0):h.parentElement()},getSelectedBlocks:function(g,f){var i=this,j=i.dom,m,h,l,k=[];m=j.getParent(g||i.getStart(),j.isBlock);h=j.getParent(f||i.getEnd(),j.isBlock);if(m){k.push(m)}if(m&&h&&m!=h){l=m;while((l=l.nextSibling)&&l!=h){if(j.isBlock(l)){k.push(l)}}}if(h&&m!=h){k.push(h)}return k},destroy:function(g){var f=this;f.win=null;if(f.tridentSel){f.tridentSel.destroy()}if(!g){c.removeUnload(f.destroy)}}})})(tinymce);(function(a){a.create("tinymce.dom.XMLWriter",{node:null,XMLWriter:function(c){function b(){var e=document.implementation;if(!e||!e.createDocument){try{return new ActiveXObject("MSXML2.DOMDocument")}catch(d){}try{return new ActiveXObject("Microsoft.XmlDom")}catch(d){}}else{return e.createDocument("","",null)}}this.doc=b();this.valid=a.isOpera||a.isWebKit;this.reset()},reset:function(){var b=this,c=b.doc;if(c.firstChild){c.removeChild(c.firstChild)}b.node=c.appendChild(c.createElement("html"))},writeStartElement:function(c){var b=this;b.node=b.node.appendChild(b.doc.createElement(c))},writeAttribute:function(c,b){if(this.valid){b=b.replace(/>/g,"%MCGT%")}this.node.setAttribute(c,b)},writeEndElement:function(){this.node=this.node.parentNode},writeFullEndElement:function(){var b=this,c=b.node;c.appendChild(b.doc.createTextNode(""));b.node=c.parentNode},writeText:function(b){if(this.valid){b=b.replace(/>/g,"%MCGT%")}this.node.appendChild(this.doc.createTextNode(b))},writeCDATA:function(b){this.node.appendChild(this.doc.createCDATASection(b))},writeComment:function(b){if(a.isIE){b=b.replace(/^\-|\-$/g," ")}this.node.appendChild(this.doc.createComment(b.replace(/\-\-/g," ")))},getContent:function(){var b;b=this.doc.xml||new XMLSerializer().serializeToString(this.doc);b=b.replace(/<\?[^?]+\?>|<html>|<\/html>|<html\/>|<!DOCTYPE[^>]+>/g,"");b=b.replace(/ ?\/>/g," />");if(this.valid){b=b.replace(/\%MCGT%/g,"&gt;")}return b}})})(tinymce);(function(a){a.create("tinymce.dom.StringWriter",{str:null,tags:null,count:0,settings:null,indent:null,StringWriter:function(b){this.settings=a.extend({indent_char:" ",indentation:0},b);this.reset()},reset:function(){this.indent="";this.str="";this.tags=[];this.count=0},writeStartElement:function(b){this._writeAttributesEnd();this.writeRaw("<"+b);this.tags.push(b);this.inAttr=true;this.count++;this.elementCount=this.count},writeAttribute:function(d,b){var c=this;c.writeRaw(" "+c.encode(d)+'="'+c.encode(b)+'"')},writeEndElement:function(){var b;if(this.tags.length>0){b=this.tags.pop();if(this._writeAttributesEnd(1)){this.writeRaw("</"+b+">")}if(this.settings.indentation>0){this.writeRaw("\n")}}},writeFullEndElement:function(){if(this.tags.length>0){this._writeAttributesEnd();this.writeRaw("</"+this.tags.pop()+">");if(this.settings.indentation>0){this.writeRaw("\n")}}},writeText:function(b){this._writeAttributesEnd();this.writeRaw(this.encode(b));this.count++},writeCDATA:function(b){this._writeAttributesEnd();this.writeRaw("<![CDATA["+b+"]]>");this.count++},writeComment:function(b){this._writeAttributesEnd();this.writeRaw("<!-- "+b+"-->");this.count++},writeRaw:function(b){this.str+=b},encode:function(b){return b.replace(/[<>&"]/g,function(c){switch(c){case"<":return"&lt;";case">":return"&gt;";case"&":return"&amp;";case'"':return"&quot;"}return c})},getContent:function(){return this.str},_writeAttributesEnd:function(b){if(!this.inAttr){return}this.inAttr=false;if(b&&this.elementCount==this.count){this.writeRaw(" />");return false}this.writeRaw(">");return true}})})(tinymce);(function(e){var g=e.extend,f=e.each,b=e.util.Dispatcher,d=e.isIE,a=e.isGecko;function c(h){return h.replace(/([?+*])/g,".$1")}e.create("tinymce.dom.Serializer",{Serializer:function(j){var i=this;i.key=0;i.onPreProcess=new b(i);i.onPostProcess=new b(i);try{i.writer=new e.dom.XMLWriter()}catch(h){i.writer=new e.dom.StringWriter()}i.settings=j=g({dom:e.DOM,valid_nodes:0,node_filter:0,attr_filter:0,invalid_attrs:/^(mce_|_moz_|sizset|sizcache)/,closed:/^(br|hr|input|meta|img|link|param|area)$/,entity_encoding:"named",entities:"160,nbsp,161,iexcl,162,cent,163,pound,164,curren,165,yen,166,brvbar,167,sect,168,uml,169,copy,170,ordf,171,laquo,172,not,173,shy,174,reg,175,macr,176,deg,177,plusmn,178,sup2,179,sup3,180,acute,181,micro,182,para,183,middot,184,cedil,185,sup1,186,ordm,187,raquo,188,frac14,189,frac12,190,frac34,191,iquest,192,Agrave,193,Aacute,194,Acirc,195,Atilde,196,Auml,197,Aring,198,AElig,199,Ccedil,200,Egrave,201,Eacute,202,Ecirc,203,Euml,204,Igrave,205,Iacute,206,Icirc,207,Iuml,208,ETH,209,Ntilde,210,Ograve,211,Oacute,212,Ocirc,213,Otilde,214,Ouml,215,times,216,Oslash,217,Ugrave,218,Uacute,219,Ucirc,220,Uuml,221,Yacute,222,THORN,223,szlig,224,agrave,225,aacute,226,acirc,227,atilde,228,auml,229,aring,230,aelig,231,ccedil,232,egrave,233,eacute,234,ecirc,235,euml,236,igrave,237,iacute,238,icirc,239,iuml,240,eth,241,ntilde,242,ograve,243,oacute,244,ocirc,245,otilde,246,ouml,247,divide,248,oslash,249,ugrave,250,uacute,251,ucirc,252,uuml,253,yacute,254,thorn,255,yuml,402,fnof,913,Alpha,914,Beta,915,Gamma,916,Delta,917,Epsilon,918,Zeta,919,Eta,920,Theta,921,Iota,922,Kappa,923,Lambda,924,Mu,925,Nu,926,Xi,927,Omicron,928,Pi,929,Rho,931,Sigma,932,Tau,933,Upsilon,934,Phi,935,Chi,936,Psi,937,Omega,945,alpha,946,beta,947,gamma,948,delta,949,epsilon,950,zeta,951,eta,952,theta,953,iota,954,kappa,955,lambda,956,mu,957,nu,958,xi,959,omicron,960,pi,961,rho,962,sigmaf,963,sigma,964,tau,965,upsilon,966,phi,967,chi,968,psi,969,omega,977,thetasym,978,upsih,982,piv,8226,bull,8230,hellip,8242,prime,8243,Prime,8254,oline,8260,frasl,8472,weierp,8465,image,8476,real,8482,trade,8501,alefsym,8592,larr,8593,uarr,8594,rarr,8595,darr,8596,harr,8629,crarr,8656,lArr,8657,uArr,8658,rArr,8659,dArr,8660,hArr,8704,forall,8706,part,8707,exist,8709,empty,8711,nabla,8712,isin,8713,notin,8715,ni,8719,prod,8721,sum,8722,minus,8727,lowast,8730,radic,8733,prop,8734,infin,8736,ang,8743,and,8744,or,8745,cap,8746,cup,8747,int,8756,there4,8764,sim,8773,cong,8776,asymp,8800,ne,8801,equiv,8804,le,8805,ge,8834,sub,8835,sup,8836,nsub,8838,sube,8839,supe,8853,oplus,8855,otimes,8869,perp,8901,sdot,8968,lceil,8969,rceil,8970,lfloor,8971,rfloor,9001,lang,9002,rang,9674,loz,9824,spades,9827,clubs,9829,hearts,9830,diams,338,OElig,339,oelig,352,Scaron,353,scaron,376,Yuml,710,circ,732,tilde,8194,ensp,8195,emsp,8201,thinsp,8204,zwnj,8205,zwj,8206,lrm,8207,rlm,8211,ndash,8212,mdash,8216,lsquo,8217,rsquo,8218,sbquo,8220,ldquo,8221,rdquo,8222,bdquo,8224,dagger,8225,Dagger,8240,permil,8249,lsaquo,8250,rsaquo,8364,euro",valid_elements:"*[*]",extended_valid_elements:0,valid_child_elements:0,invalid_elements:0,fix_table_elements:1,fix_list_elements:true,fix_content_duplication:true,convert_fonts_to_spans:false,font_size_classes:0,font_size_style_values:0,apply_source_formatting:0,indent_mode:"simple",indent_char:"\t",indent_levels:1,remove_linebreaks:1,remove_redundant_brs:1,element_format:"xhtml"},j);i.dom=j.dom;if(j.remove_redundant_brs){i.onPostProcess.add(function(k,l){l.content=l.content.replace(/(<br \/>\s*)+<\/(p|h[1-6]|div|li)>/gi,function(n,m,o){if(/^<br \/>\s*<\//.test(n)){return"</"+o+">"}return n})})}if(j.element_format=="html"){i.onPostProcess.add(function(k,l){l.content=l.content.replace(/<([^>]+) \/>/g,"<$1>")})}if(j.fix_list_elements){i.onPreProcess.add(function(v,s){var l,y,w=["ol","ul"],u,t,q,k=/^(OL|UL)$/,z;function m(r,x){var o=x.split(","),p;while((r=r.previousSibling)!=null){for(p=0;p<o.length;p++){if(r.nodeName==o[p]){return r}}}return null}for(y=0;y<w.length;y++){l=i.dom.select(w[y],s.node);for(u=0;u<l.length;u++){t=l[u];q=t.parentNode;if(k.test(q.nodeName)){z=m(t,"LI");if(!z){z=i.dom.create("li");z.innerHTML="&nbsp;";z.appendChild(t);q.insertBefore(z,q.firstChild)}else{z.appendChild(t)}}}}})}if(j.fix_table_elements){i.onPreProcess.add(function(k,l){if(!e.isOpera||opera.buildNumber()>=1767){f(i.dom.select("p table",l.node).reverse(),function(p){var o=i.dom.getParent(p.parentNode,"table,p");if(o.nodeName!="TABLE"){try{i.dom.split(o,p)}catch(m){}}})}})}},setEntities:function(p){var n=this,j,m,h={},o="",k;if(n.entityLookup){return}j=p.split(",");for(m=0;m<j.length;m+=2){k=j[m];if(k==34||k==38||k==60||k==62){continue}h[String.fromCharCode(j[m])]=j[m+1];k=parseInt(j[m]).toString(16);o+="\\u"+"0000".substring(k.length)+k}if(!o){n.settings.entity_encoding="raw";return}n.entitiesRE=new RegExp("["+o+"]","g");n.entityLookup=h},setValidChildRules:function(h){this.childRules=null;this.addValidChildRules(h)},addValidChildRules:function(k){var j=this,l,h,i;if(!k){return}l="A|BR|SPAN|BDO|MAP|OBJECT|IMG|TT|I|B|BIG|SMALL|EM|STRONG|DFN|CODE|Q|SAMP|KBD|VAR|CITE|ABBR|ACRONYM|SUB|SUP|#text|#comment";h="A|BR|SPAN|BDO|OBJECT|APPLET|IMG|MAP|IFRAME|TT|I|B|U|S|STRIKE|BIG|SMALL|FONT|BASEFONT|EM|STRONG|DFN|CODE|Q|SAMP|KBD|VAR|CITE|ABBR|ACRONYM|SUB|SUP|INPUT|SELECT|TEXTAREA|LABEL|BUTTON|#text|#comment";i="H[1-6]|P|DIV|ADDRESS|PRE|FORM|TABLE|LI|OL|UL|TD|CAPTION|BLOCKQUOTE|CENTER|DL|DT|DD|DIR|FIELDSET|FORM|NOSCRIPT|NOFRAMES|MENU|ISINDEX|SAMP";f(k.split(","),function(n){var o=n.split(/\[|\]/),m;n="";f(o[1].split("|"),function(p){if(n){n+="|"}switch(p){case"%itrans":p=h;break;case"%itrans_na":p=h.substring(2);break;case"%istrict":p=l;break;case"%istrict_na":p=l.substring(2);break;case"%btrans":p=i;break;case"%bstrict":p=i;break}n+=p});m=new RegExp("^("+n.toLowerCase()+")$","i");f(o[0].split("/"),function(p){j.childRules=j.childRules||{};j.childRules[p]=m})});k="";f(j.childRules,function(n,m){if(k){k+="|"}k+=m});j.parentElementsRE=new RegExp("^("+k.toLowerCase()+")$","i")},setRules:function(i){var h=this;h._setup();h.rules={};h.wildRules=[];h.validElements={};return h.addRules(i)},addRules:function(i){var h=this,j;if(!i){return}h._setup();f(i.split(","),function(m){var q=m.split(/\[|\]/),l=q[0].split("/"),r,k,o,n=[];if(j){k=e.extend([],j.attribs)}if(q.length>1){f(q[1].split("|"),function(u){var p={},t;k=k||[];u=u.replace(/::/g,"~");u=/^([!\-])?([\w*.?~_\-]+|)([=:<])?(.+)?$/.exec(u);u[2]=u[2].replace(/~/g,":");if(u[1]=="!"){r=r||[];r.push(u[2])}if(u[1]=="-"){for(t=0;t<k.length;t++){if(k[t].name==u[2]){k.splice(t,1);return}}}switch(u[3]){case"=":p.defaultVal=u[4]||"";break;case":":p.forcedVal=u[4];break;case"<":p.validVals=u[4].split("?");break}if(/[*.?]/.test(u[2])){o=o||[];p.nameRE=new RegExp("^"+c(u[2])+"$");o.push(p)}else{p.name=u[2];k.push(p)}n.push(u[2])})}f(l,function(v,u){var w=v.charAt(0),t=1,p={};if(j){if(j.noEmpty){p.noEmpty=j.noEmpty}if(j.fullEnd){p.fullEnd=j.fullEnd}if(j.padd){p.padd=j.padd}}switch(w){case"-":p.noEmpty=true;break;case"+":p.fullEnd=true;break;case"#":p.padd=true;break;default:t=0}l[u]=v=v.substring(t);h.validElements[v]=1;if(/[*.?]/.test(l[0])){p.nameRE=new RegExp("^"+c(l[0])+"$");h.wildRules=h.wildRules||{};h.wildRules.push(p)}else{p.name=l[0];if(l[0]=="@"){j=p}h.rules[v]=p}p.attribs=k;if(r){p.requiredAttribs=r}if(o){v="";f(n,function(s){if(v){v+="|"}v+="("+c(s)+")"});p.validAttribsRE=new RegExp("^"+v.toLowerCase()+"$");p.wildAttribs=o}})});i="";f(h.validElements,function(m,l){if(i){i+="|"}if(l!="@"){i+=l}});h.validElementsRE=new RegExp("^("+c(i.toLowerCase())+")$")},findRule:function(m){var j=this,l=j.rules,h,k;j._setup();k=l[m];if(k){return k}l=j.wildRules;for(h=0;h<l.length;h++){if(l[h].nameRE.test(m)){return l[h]}}return null},findAttribRule:function(h,l){var j,k=h.wildAttribs;for(j=0;j<k.length;j++){if(k[j].nameRE.test(l)){return k[j]}}return null},serialize:function(r,q){var m,k=this,p,i,j,l;k._setup();q=q||{};q.format=q.format||"html";k.processObj=q;if(d){l=[];f(r.getElementsByTagName("option"),function(o){var h=k.dom.getAttrib(o,"selected");l.push(h?h:null)})}r=r.cloneNode(true);if(d){f(r.getElementsByTagName("option"),function(o,h){k.dom.setAttrib(o,"selected",l[h])})}j=r.ownerDocument.implementation;if(j.createHTMLDocument&&(e.isOpera&&opera.buildNumber()>=1767)){p=j.createHTMLDocument("");f(r.nodeName=="BODY"?r.childNodes:[r],function(h){p.body.appendChild(p.importNode(h,true))});if(r.nodeName!="BODY"){r=p.body.firstChild}else{r=p.body}i=k.dom.doc;k.dom.doc=p}k.key=""+(parseInt(k.key)+1);if(!q.no_events){q.node=r;k.onPreProcess.dispatch(k,q)}k.writer.reset();k._serializeNode(r,q.getInner);q.content=k.writer.getContent();if(i){k.dom.doc=i}if(!q.no_events){k.onPostProcess.dispatch(k,q)}k._postProcess(q);q.node=null;return e.trim(q.content)},_postProcess:function(n){var i=this,k=i.settings,j=n.content,m=[],l;if(n.format=="html"){l=i._protect({content:j,patterns:[{pattern:/(<script[^>]*>)(.*?)(<\/script>)/g},{pattern:/(<noscript[^>]*>)(.*?)(<\/noscript>)/g},{pattern:/(<style[^>]*>)(.*?)(<\/style>)/g},{pattern:/(<pre[^>]*>)(.*?)(<\/pre>)/g,encode:1},{pattern:/(<!--\[CDATA\[)(.*?)(\]\]-->)/g}]});j=l.content;if(k.entity_encoding!=="raw"){j=i._encode(j)}if(!n.set){j=j.replace(/<p>\s+<\/p>|<p([^>]+)>\s+<\/p>/g,k.entity_encoding=="numeric"?"<p$1>&#160;</p>":"<p$1>&nbsp;</p>");if(k.remove_linebreaks){j=j.replace(/\r?\n|\r/g," ");j=j.replace(/(<[^>]+>)\s+/g,"$1 ");j=j.replace(/\s+(<\/[^>]+>)/g," $1");j=j.replace(/<(p|h[1-6]|blockquote|hr|div|table|tbody|tr|td|body|head|html|title|meta|style|pre|script|link|object) ([^>]+)>\s+/g,"<$1 $2>");j=j.replace(/<(p|h[1-6]|blockquote|hr|div|table|tbody|tr|td|body|head|html|title|meta|style|pre|script|link|object)>\s+/g,"<$1>");j=j.replace(/\s+<\/(p|h[1-6]|blockquote|hr|div|table|tbody|tr|td|body|head|html|title|meta|style|pre|script|link|object)>/g,"</$1>")}if(k.apply_source_formatting&&k.indent_mode=="simple"){j=j.replace(/<(\/?)(ul|hr|table|meta|link|tbody|tr|object|body|head|html|map)(|[^>]+)>\s*/g,"\n<$1$2$3>\n");j=j.replace(/\s*<(p|h[1-6]|blockquote|div|title|style|pre|script|td|li|area)(|[^>]+)>/g,"\n<$1$2>");j=j.replace(/<\/(p|h[1-6]|blockquote|div|title|style|pre|script|td|li)>\s*/g,"</$1>\n");j=j.replace(/\n\n/g,"\n")}}j=i._unprotect(j,l);j=j.replace(/<!--\[CDATA\[([\s\S]+)\]\]-->/g,"<![CDATA[$1]]>");if(k.entity_encoding=="raw"){j=j.replace(/<p>&nbsp;<\/p>|<p([^>]+)>&nbsp;<\/p>/g,"<p$1>\u00a0</p>")}j=j.replace(/<noscript([^>]+|)>([\s\S]*?)<\/noscript>/g,function(h,p,o){return"<noscript"+p+">"+i.dom.decode(o.replace(/<!--|-->/g,""))+"</noscript>"})}n.content=j},_serializeNode:function(D,o){var z=this,A=z.settings,x=z.writer,q,j,u,F,E,G,B,h,y,k,r,C,p,m;if(!A.node_filter||A.node_filter(D)){switch(D.nodeType){case 1:if(D.hasAttribute?D.hasAttribute("mce_bogus"):D.getAttribute("mce_bogus")){return}p=false;q=D.hasChildNodes();k=D.getAttribute("mce_name")||D.nodeName.toLowerCase();if(d){if(D.scopeName!=="HTML"&&D.scopeName!=="html"){k=D.scopeName+":"+k}}if(k.indexOf("mce:")===0){k=k.substring(4)}if(!z.validElementsRE||!z.validElementsRE.test(k)||(z.invalidElementsRE&&z.invalidElementsRE.test(k))||o){p=true;break}if(d){if(A.fix_content_duplication){if(D.mce_serialized==z.key){return}D.mce_serialized=z.key}if(k.charAt(0)=="/"){k=k.substring(1)}}else{if(a){if(D.nodeName==="BR"&&D.getAttribute("type")=="_moz"){return}}}if(z.childRules){if(z.parentElementsRE.test(z.elementName)){if(!z.childRules[z.elementName].test(k)){p=true;break}}z.elementName=k}r=z.findRule(k);k=r.name||k;m=A.closed.test(k);if((!q&&r.noEmpty)||(d&&!k)){p=true;break}if(r.requiredAttribs){G=r.requiredAttribs;for(F=G.length-1;F>=0;F--){if(this.dom.getAttrib(D,G[F])!==""){break}}if(F==-1){p=true;break}}x.writeStartElement(k);if(r.attribs){for(F=0,B=r.attribs,E=B.length;F<E;F++){G=B[F];y=z._getAttrib(D,G);if(y!==null){x.writeAttribute(G.name,y)}}}if(r.validAttribsRE){B=z.dom.getAttribs(D);for(F=B.length-1;F>-1;F--){h=B[F];if(h.specified){G=h.nodeName.toLowerCase();if(A.invalid_attrs.test(G)||!r.validAttribsRE.test(G)){continue}C=z.findAttribRule(r,G);y=z._getAttrib(D,C,G);if(y!==null){x.writeAttribute(G,y)}}}}if(k==="script"&&e.trim(D.innerHTML)){x.writeText("// ");x.writeCDATA(D.innerHTML.replace(/<!--|-->|<\[CDATA\[|\]\]>/g,""));q=false;break}if(r.padd){if(q&&(u=D.firstChild)&&u.nodeType===1&&D.childNodes.length===1){if(u.hasAttribute?u.hasAttribute("mce_bogus"):u.getAttribute("mce_bogus")){x.writeText("\u00a0")}}else{if(!q){x.writeText("\u00a0")}}}break;case 3:if(z.childRules&&z.parentElementsRE.test(z.elementName)){if(!z.childRules[z.elementName].test(D.nodeName)){return}}return x.writeText(D.nodeValue);case 4:return x.writeCDATA(D.nodeValue);case 8:return x.writeComment(D.nodeValue)}}else{if(D.nodeType==1){q=D.hasChildNodes()}}if(q&&!m){u=D.firstChild;while(u){z._serializeNode(u);z.elementName=k;u=u.nextSibling}}if(!p){if(!m){x.writeFullEndElement()}else{x.writeEndElement()}}},_protect:function(j){var i=this;j.items=j.items||[];function h(l){return l.replace(/[\r\n\\]/g,function(m){if(m==="\n"){return"\\n"}else{if(m==="\\"){return"\\\\"}}return"\\r"})}function k(l){return l.replace(/\\[\\rn]/g,function(m){if(m==="\\n"){return"\n"}else{if(m==="\\\\"){return"\\"}}return"\r"})}f(j.patterns,function(l){j.content=k(h(j.content).replace(l.pattern,function(n,o,m,p){m=k(m);if(l.encode){m=i._encode(m)}j.items.push(m);return o+"<!--mce:"+(j.items.length-1)+"-->"+p}))});return j},_unprotect:function(i,j){i=i.replace(/\<!--mce:([0-9]+)--\>/g,function(k,h){return j.items[parseInt(h)]});j.items=[];return i},_encode:function(m){var j=this,k=j.settings,i;if(k.entity_encoding!=="raw"){if(k.entity_encoding.indexOf("named")!=-1){j.setEntities(k.entities);i=j.entityLookup;m=m.replace(j.entitiesRE,function(h){var l;if(l=i[h]){h="&"+l+";"}return h})}if(k.entity_encoding.indexOf("numeric")!=-1){m=m.replace(/[\u007E-\uFFFF]/g,function(h){return"&#"+h.charCodeAt(0)+";"})}}return m},_setup:function(){var h=this,i=this.settings;if(h.done){return}h.done=1;h.setRules(i.valid_elements);h.addRules(i.extended_valid_elements);h.addValidChildRules(i.valid_child_elements);if(i.invalid_elements){h.invalidElementsRE=new RegExp("^("+c(i.invalid_elements.replace(/,/g,"|").toLowerCase())+")$")}if(i.attrib_value_filter){h.attribValueFilter=i.attribValueFilter}},_getAttrib:function(m,j,h){var l,k;h=h||j.name;if(j.forcedVal&&(k=j.forcedVal)){if(k==="{$uid}"){return this.dom.uniqueId()}return k}k=this.dom.getAttrib(m,h);switch(h){case"rowspan":case"colspan":if(k=="1"){k=""}break}if(this.attribValueFilter){k=this.attribValueFilter(h,k,m)}if(j.validVals){for(l=j.validVals.length-1;l>=0;l--){if(k==j.validVals[l]){break}}if(l==-1){return null}}if(k===""&&typeof(j.defaultVal)!="undefined"){k=j.defaultVal;if(k==="{$uid}"){return this.dom.uniqueId()}return k}else{if(h=="class"&&this.processObj.get){k=k.replace(/\s?mceItem\w+\s?/g,"")}}if(k===""){return null}return k}})})(tinymce);(function(tinymce){var each=tinymce.each,Event=tinymce.dom.Event;tinymce.create("tinymce.dom.ScriptLoader",{ScriptLoader:function(s){this.settings=s||{};this.queue=[];this.lookup={}},isDone:function(u){return this.lookup[u]?this.lookup[u].state==2:0},markDone:function(u){this.lookup[u]={state:2,url:u}},add:function(u,cb,s,pr){var t=this,lo=t.lookup,o;if(o=lo[u]){if(cb&&o.state==2){cb.call(s||this)}return o}o={state:0,url:u,func:cb,scope:s||this};if(pr){t.queue.unshift(o)}else{t.queue.push(o)}lo[u]=o;return o},load:function(u,cb,s){var t=this,o;if(o=t.lookup[u]){if(cb&&o.state==2){cb.call(s||t)}return o}function loadScript(u){if(Event.domLoaded||t.settings.strict_mode){tinymce.util.XHR.send({url:tinymce._addVer(u),error:t.settings.error,async:false,success:function(co){t.eval(co)}})}else{document.write('<script type="text/javascript" src="'+tinymce._addVer(u)+'"><\/script>')}}if(!tinymce.is(u,"string")){each(u,function(u){loadScript(u)});if(cb){cb.call(s||t)}}else{loadScript(u);if(cb){cb.call(s||t)}}},loadQueue:function(cb,s){var t=this;if(!t.queueLoading){t.queueLoading=1;t.queueCallbacks=[];t.loadScripts(t.queue,function(){t.queueLoading=0;if(cb){cb.call(s||t)}each(t.queueCallbacks,function(o){o.func.call(o.scope)})})}else{if(cb){t.queueCallbacks.push({func:cb,scope:s||t})}}},eval:function(co){var w=window;if(!w.execScript){try{eval.call(w,co)}catch(ex){eval(co,w)}}else{w.execScript(co)}},loadScripts:function(sc,cb,s){var t=this,lo=t.lookup;function done(o){o.state=2;if(o.func){o.func.call(o.scope||t)}}function allDone(){var l;l=sc.length;each(sc,function(o){o=lo[o.url];if(o.state===2){done(o);l--}else{load(o)}});if(l===0&&cb){cb.call(s||t);cb=0}}function load(o){if(o.state>0){return}o.state=1;tinymce.dom.ScriptLoader.loadScript(o.url,function(){done(o);allDone()})}each(sc,function(o){var u=o.url;if(!lo[u]){lo[u]=o;t.queue.push(o)}else{o=lo[u]}if(o.state>0){return}if(!Event.domLoaded&&!t.settings.strict_mode){var ix,ol="";if(cb||o.func){o.state=1;ix=tinymce.dom.ScriptLoader._addOnLoad(function(){done(o);allDone()});if(tinymce.isIE){ol=' onreadystatechange="'}else{ol=' onload="'}ol+="tinymce.dom.ScriptLoader._onLoad(this,'"+u+"',"+ix+');"'}document.write('<script type="text/javascript" src="'+tinymce._addVer(u)+'"'+ol+"><\/script>");if(!o.func){done(o)}}else{load(o)}});allDone()},"static":{_addOnLoad:function(f){var t=this;t._funcs=t._funcs||[];t._funcs.push(f);return t._funcs.length-1},_onLoad:function(e,u,ix){if(!tinymce.isIE||e.readyState=="complete"){this._funcs[ix].call(this)}},loadScript:function(u,cb){var id=tinymce.DOM.uniqueId(),e;function done(){Event.clear(id);tinymce.DOM.remove(id);if(cb){cb.call(document,u);cb=0}}if(tinymce.isIE){tinymce.util.XHR.send({url:tinymce._addVer(u),async:false,success:function(co){window.execScript(co);done()}})}else{e=tinymce.DOM.create("script",{id:id,type:"text/javascript",src:tinymce._addVer(u)});Event.add(e,"load",done);(document.getElementsByTagName("head")[0]||document.body).appendChild(e)}}}});tinymce.ScriptLoader=new tinymce.dom.ScriptLoader()})(tinymce);(function(c){var b=c.DOM,a=c.is;c.create("tinymce.ui.Control",{Control:function(e,d){this.id=e;this.settings=d=d||{};this.rendered=false;this.onRender=new c.util.Dispatcher(this);this.classPrefix="";this.scope=d.scope||this;this.disabled=0;this.active=0},setDisabled:function(d){var f;if(d!=this.disabled){f=b.get(this.id);if(f&&this.settings.unavailable_prefix){if(d){this.prevTitle=f.title;f.title=this.settings.unavailable_prefix+": "+f.title}else{f.title=this.prevTitle}}this.setState("Disabled",d);this.setState("Enabled",!d);this.disabled=d}},isDisabled:function(){return this.disabled},setActive:function(d){if(d!=this.active){this.setState("Active",d);this.active=d}},isActive:function(){return this.active},setState:function(f,d){var e=b.get(this.id);f=this.classPrefix+f;if(d){b.addClass(e,f)}else{b.removeClass(e,f)}},isRendered:function(){return this.rendered},renderHTML:function(){},renderTo:function(d){b.setHTML(d,this.renderHTML())},postRender:function(){var e=this,d;if(a(e.disabled)){d=e.disabled;e.disabled=-1;e.setDisabled(d)}if(a(e.active)){d=e.active;e.active=-1;e.setActive(d)}},remove:function(){b.remove(this.id);this.destroy()},destroy:function(){c.dom.Event.clear(this.id)}})})(tinymce);tinymce.create("tinymce.ui.Container:tinymce.ui.Control",{Container:function(b,a){this.parent(b,a);this.controls=[];this.lookup={}},add:function(a){this.lookup[a.id]=a;this.controls.push(a);return a},get:function(a){return this.lookup[a]}});tinymce.create("tinymce.ui.Separator:tinymce.ui.Control",{Separator:function(b,a){this.parent(b,a);this.classPrefix="mceSeparator"},renderHTML:function(){return tinymce.DOM.createHTML("span",{"class":this.classPrefix})}});(function(d){var c=d.is,b=d.DOM,e=d.each,a=d.walk;d.create("tinymce.ui.MenuItem:tinymce.ui.Control",{MenuItem:function(g,f){this.parent(g,f);this.classPrefix="mceMenuItem"},setSelected:function(f){this.setState("Selected",f);this.selected=f},isSelected:function(){return this.selected},postRender:function(){var f=this;f.parent();if(c(f.selected)){f.setSelected(f.selected)}}})})(tinymce);(function(d){var c=d.is,b=d.DOM,e=d.each,a=d.walk;d.create("tinymce.ui.Menu:tinymce.ui.MenuItem",{Menu:function(h,g){var f=this;f.parent(h,g);f.items={};f.collapsed=false;f.menuCount=0;f.onAddItem=new d.util.Dispatcher(this)},expand:function(g){var f=this;if(g){a(f,function(h){if(h.expand){h.expand()}},"items",f)}f.collapsed=false},collapse:function(g){var f=this;if(g){a(f,function(h){if(h.collapse){h.collapse()}},"items",f)}f.collapsed=true},isCollapsed:function(){return this.collapsed},add:function(f){if(!f.settings){f=new d.ui.MenuItem(f.id||b.uniqueId(),f)}this.onAddItem.dispatch(this,f);return this.items[f.id]=f},addSeparator:function(){return this.add({separator:true})},addMenu:function(f){if(!f.collapse){f=this.createMenu(f)}this.menuCount++;return this.add(f)},hasMenus:function(){return this.menuCount!==0},remove:function(f){delete this.items[f.id]},removeAll:function(){var f=this;a(f,function(g){if(g.removeAll){g.removeAll()}else{g.remove()}g.destroy()},"items",f);f.items={}},createMenu:function(g){var f=new d.ui.Menu(g.id||b.uniqueId(),g);f.onAddItem.add(this.onAddItem.dispatch,this.onAddItem);return f}})})(tinymce);(function(e){var d=e.is,c=e.DOM,f=e.each,a=e.dom.Event,b=e.dom.Element;e.create("tinymce.ui.DropMenu:tinymce.ui.Menu",{DropMenu:function(h,g){g=g||{};g.container=g.container||c.doc.body;g.offset_x=g.offset_x||0;g.offset_y=g.offset_y||0;g.vp_offset_x=g.vp_offset_x||0;g.vp_offset_y=g.vp_offset_y||0;if(d(g.icons)&&!g.icons){g["class"]+=" mceNoIcons"}this.parent(h,g);this.onShowMenu=new e.util.Dispatcher(this);this.onHideMenu=new e.util.Dispatcher(this);this.classPrefix="mceMenu"},createMenu:function(j){var h=this,i=h.settings,g;j.container=j.container||i.container;j.parent=h;j.constrain=j.constrain||i.constrain;j["class"]=j["class"]||i["class"];j.vp_offset_x=j.vp_offset_x||i.vp_offset_x;j.vp_offset_y=j.vp_offset_y||i.vp_offset_y;g=new e.ui.DropMenu(j.id||c.uniqueId(),j);g.onAddItem.add(h.onAddItem.dispatch,h.onAddItem);return g},update:function(){var i=this,j=i.settings,g=c.get("menu_"+i.id+"_tbl"),l=c.get("menu_"+i.id+"_co"),h,k;h=j.max_width?Math.min(g.clientWidth,j.max_width):g.clientWidth;k=j.max_height?Math.min(g.clientHeight,j.max_height):g.clientHeight;if(!c.boxModel){i.element.setStyles({width:h+2,height:k+2})}else{i.element.setStyles({width:h,height:k})}if(j.max_width){c.setStyle(l,"width",h)}if(j.max_height){c.setStyle(l,"height",k);if(g.clientHeight<j.max_height){c.setStyle(l,"overflow","hidden")}}},showMenu:function(p,n,r){var z=this,A=z.settings,o,g=c.getViewPort(),u,l,v,q,i=2,k,j,m=z.classPrefix;z.collapse(1);if(z.isMenuVisible){return}if(!z.rendered){o=c.add(z.settings.container,z.renderNode());f(z.items,function(h){h.postRender()});z.element=new b("menu_"+z.id,{blocker:1,container:A.container})}else{o=c.get("menu_"+z.id)}if(!e.isOpera){c.setStyles(o,{left:-65535,top:-65535})}c.show(o);z.update();p+=A.offset_x||0;n+=A.offset_y||0;g.w-=4;g.h-=4;if(A.constrain){u=o.clientWidth-i;l=o.clientHeight-i;v=g.x+g.w;q=g.y+g.h;if((p+A.vp_offset_x+u)>v){p=r?r-u:Math.max(0,(v-A.vp_offset_x)-u)}if((n+A.vp_offset_y+l)>q){n=Math.max(0,(q-A.vp_offset_y)-l)}}c.setStyles(o,{left:p,top:n});z.element.update();z.isMenuVisible=1;z.mouseClickFunc=a.add(o,"click",function(s){var h;s=s.target;if(s&&(s=c.getParent(s,"tr"))&&!c.hasClass(s,m+"ItemSub")){h=z.items[s.id];if(h.isDisabled()){return}k=z;while(k){if(k.hideMenu){k.hideMenu()}k=k.settings.parent}if(h.settings.onclick){h.settings.onclick(s)}return a.cancel(s)}});if(z.hasMenus()){z.mouseOverFunc=a.add(o,"mouseover",function(w){var h,t,s;w=w.target;if(w&&(w=c.getParent(w,"tr"))){h=z.items[w.id];if(z.lastMenu){z.lastMenu.collapse(1)}if(h.isDisabled()){return}if(w&&c.hasClass(w,m+"ItemSub")){t=c.getRect(w);h.showMenu((t.x+t.w-i),t.y-i,t.x);z.lastMenu=h;c.addClass(c.get(h.id).firstChild,m+"ItemActive")}}})}z.onShowMenu.dispatch(z);if(A.keyboard_focus){a.add(o,"keydown",z._keyHandler,z);c.select("a","menu_"+z.id)[0].focus();z._focusIdx=0}},hideMenu:function(j){var g=this,i=c.get("menu_"+g.id),h;if(!g.isMenuVisible){return}a.remove(i,"mouseover",g.mouseOverFunc);a.remove(i,"click",g.mouseClickFunc);a.remove(i,"keydown",g._keyHandler);c.hide(i);g.isMenuVisible=0;if(!j){g.collapse(1)}if(g.element){g.element.hide()}if(h=c.get(g.id)){c.removeClass(h.firstChild,g.classPrefix+"ItemActive")}g.onHideMenu.dispatch(g)},add:function(i){var g=this,h;i=g.parent(i);if(g.isRendered&&(h=c.get("menu_"+g.id))){g._add(c.select("tbody",h)[0],i)}return i},collapse:function(g){this.parent(g);this.hideMenu(1)},remove:function(g){c.remove(g.id);this.destroy();return this.parent(g)},destroy:function(){var g=this,h=c.get("menu_"+g.id);a.remove(h,"mouseover",g.mouseOverFunc);a.remove(h,"click",g.mouseClickFunc);if(g.element){g.element.remove()}c.remove(h)},renderNode:function(){var i=this,j=i.settings,l,h,k,g;g=c.create("div",{id:"menu_"+i.id,"class":j["class"],style:"position:absolute;left:0;top:0;z-index:200000"});k=c.add(g,"div",{id:"menu_"+i.id+"_co","class":i.classPrefix+(j["class"]?" "+j["class"]:"")});i.element=new b("menu_"+i.id,{blocker:1,container:j.container});if(j.menu_line){c.add(k,"span",{"class":i.classPrefix+"Line"})}l=c.add(k,"table",{id:"menu_"+i.id+"_tbl",border:0,cellPadding:0,cellSpacing:0});h=c.add(l,"tbody");f(i.items,function(m){i._add(h,m)});i.rendered=true;return g},_keyHandler:function(j){var i=this,h=j.keyCode;function g(m){var k=i._focusIdx+m,l=c.select("a","menu_"+i.id)[k];if(l){i._focusIdx=k;l.focus()}}switch(h){case 38:g(-1);return;case 40:g(1);return;case 13:return;case 27:return this.hideMenu()}},_add:function(j,h){var i,q=h.settings,p,l,k,m=this.classPrefix,g;if(q.separator){l=c.add(j,"tr",{id:h.id,"class":m+"ItemSeparator"});c.add(l,"td",{"class":m+"ItemSeparator"});if(i=l.previousSibling){c.addClass(i,"mceLast")}return}i=l=c.add(j,"tr",{id:h.id,"class":m+"Item "+m+"ItemEnabled"});i=k=c.add(i,"td");i=p=c.add(i,"a",{href:"#",onclick:"return false;",onmousedown:"return false;"});c.addClass(k,q["class"]);g=c.add(i,"span",{"class":"mceIcon"+(q.icon?" mce_"+q.icon:"")});if(q.icon_src){c.add(g,"img",{src:q.icon_src})}i=c.add(i,q.element||"span",{"class":"mceText",title:h.settings.title},h.settings.title);if(h.settings.style){c.setAttrib(i,"style",h.settings.style)}if(j.childNodes.length==1){c.addClass(l,"mceFirst")}if((i=l.previousSibling)&&c.hasClass(i,m+"ItemSeparator")){c.addClass(l,"mceFirst")}if(h.collapse){c.addClass(l,m+"ItemSub")}if(i=l.previousSibling){c.removeClass(i,"mceLast")}c.addClass(l,"mceLast")}})})(tinymce);(function(b){var a=b.DOM;b.create("tinymce.ui.Button:tinymce.ui.Control",{Button:function(d,c){this.parent(d,c);this.classPrefix="mceButton"},renderHTML:function(){var f=this.classPrefix,e=this.settings,d,c;c=a.encode(e.label||"");d='<a id="'+this.id+'" href="#" class="'+f+" "+f+"Enabled "+e["class"]+(c?" "+f+"Labeled":"")+'" onmousedown="return false;" onclick="return false;" title="'+a.encode(e.title)+'">';if(e.image){d+='<img class="mceIcon" src="'+e.image+'" />'+c+"</a>"}else{d+='<span class="mceIcon '+e["class"]+'"></span>'+(c?'<span class="'+f+'Label">'+c+"</span>":"")+"</a>"}return d},postRender:function(){var c=this,d=c.settings;b.dom.Event.add(c.id,"click",function(f){if(!c.isDisabled()){return d.onclick.call(d.scope,f)}})}})})(tinymce);(function(d){var c=d.DOM,b=d.dom.Event,e=d.each,a=d.util.Dispatcher;d.create("tinymce.ui.ListBox:tinymce.ui.Control",{ListBox:function(h,g){var f=this;f.parent(h,g);f.items=[];f.onChange=new a(f);f.onPostRender=new a(f);f.onAdd=new a(f);f.onRenderMenu=new d.util.Dispatcher(this);f.classPrefix="mceListBox"},select:function(h){var g=this,j,i;if(h==undefined){return g.selectByIndex(-1)}if(h&&h.call){i=h}else{i=function(f){return f==h}}if(h!=g.selectedValue){e(g.items,function(k,f){if(i(k.value)){j=1;g.selectByIndex(f);return false}});if(!j){g.selectByIndex(-1)}}},selectByIndex:function(f){var g=this,h,i;if(f!=g.selectedIndex){h=c.get(g.id+"_text");i=g.items[f];if(i){g.selectedValue=i.value;g.selectedIndex=f;c.setHTML(h,c.encode(i.title));c.removeClass(h,"mceTitle")}else{c.setHTML(h,c.encode(g.settings.title));c.addClass(h,"mceTitle");g.selectedValue=g.selectedIndex=null}h=0}},add:function(i,f,h){var g=this;h=h||{};h=d.extend(h,{title:i,value:f});g.items.push(h);g.onAdd.dispatch(g,h)},getLength:function(){return this.items.length},renderHTML:function(){var i="",f=this,g=f.settings,j=f.classPrefix;i='<table id="'+f.id+'" cellpadding="0" cellspacing="0" class="'+j+" "+j+"Enabled"+(g["class"]?(" "+g["class"]):"")+'"><tbody><tr>';i+="<td>"+c.createHTML("a",{id:f.id+"_text",href:"#","class":"mceText",onclick:"return false;",onmousedown:"return false;"},c.encode(f.settings.title))+"</td>";i+="<td>"+c.createHTML("a",{id:f.id+"_open",tabindex:-1,href:"#","class":"mceOpen",onclick:"return false;",onmousedown:"return false;"},"<span></span>")+"</td>";i+="</tr></tbody></table>";return i},showMenu:function(){var g=this,j,i,h=c.get(this.id),f;if(g.isDisabled()||g.items.length==0){return}if(g.menu&&g.menu.isMenuVisible){return g.hideMenu()}if(!g.isMenuRendered){g.renderMenu();g.isMenuRendered=true}j=c.getPos(this.settings.menu_container);i=c.getPos(h);f=g.menu;f.settings.offset_x=i.x;f.settings.offset_y=i.y;f.settings.keyboard_focus=!d.isOpera;if(g.oldID){f.items[g.oldID].setSelected(0)}e(g.items,function(k){if(k.value===g.selectedValue){f.items[k.id].setSelected(1);g.oldID=k.id}});f.showMenu(0,h.clientHeight);b.add(c.doc,"mousedown",g.hideMenu,g);c.addClass(g.id,g.classPrefix+"Selected")},hideMenu:function(g){var f=this;if(g&&g.type=="mousedown"&&(g.target.id==f.id+"_text"||g.target.id==f.id+"_open")){return}if(!g||!c.getParent(g.target,".mceMenu")){c.removeClass(f.id,f.classPrefix+"Selected");b.remove(c.doc,"mousedown",f.hideMenu,f);if(f.menu){f.menu.hideMenu()}}},renderMenu:function(){var g=this,f;f=g.settings.control_manager.createDropMenu(g.id+"_menu",{menu_line:1,"class":g.classPrefix+"Menu mceNoIcons",max_width:150,max_height:150});f.onHideMenu.add(g.hideMenu,g);f.add({title:g.settings.title,"class":"mceMenuItemTitle",onclick:function(){if(g.settings.onselect("")!==false){g.select("")}}});e(g.items,function(h){h.id=c.uniqueId();h.onclick=function(){if(g.settings.onselect(h.value)!==false){g.select(h.value)}};f.add(h)});g.onRenderMenu.dispatch(g,f);g.menu=f},postRender:function(){var f=this,g=f.classPrefix;b.add(f.id,"click",f.showMenu,f);b.add(f.id+"_text","focus",function(h){if(!f._focused){f.keyDownHandler=b.add(f.id+"_text","keydown",function(l){var i=-1,j,k=l.keyCode;e(f.items,function(m,n){if(f.selectedValue==m.value){i=n}});if(k==38){j=f.items[i-1]}else{if(k==40){j=f.items[i+1]}else{if(k==13){j=f.selectedValue;f.selectedValue=null;f.settings.onselect(j);return b.cancel(l)}}}if(j){f.hideMenu();f.select(j.value)}})}f._focused=1});b.add(f.id+"_text","blur",function(){b.remove(f.id+"_text","keydown",f.keyDownHandler);f._focused=0});if(d.isIE6||!c.boxModel){b.add(f.id,"mouseover",function(){if(!c.hasClass(f.id,g+"Disabled")){c.addClass(f.id,g+"Hover")}});b.add(f.id,"mouseout",function(){if(!c.hasClass(f.id,g+"Disabled")){c.removeClass(f.id,g+"Hover")}})}f.onPostRender.dispatch(f,c.get(f.id))},destroy:function(){this.parent();b.clear(this.id+"_text");b.clear(this.id+"_open")}})})(tinymce);(function(d){var c=d.DOM,b=d.dom.Event,e=d.each,a=d.util.Dispatcher;d.create("tinymce.ui.NativeListBox:tinymce.ui.ListBox",{NativeListBox:function(g,f){this.parent(g,f);this.classPrefix="mceNativeListBox"},setDisabled:function(f){c.get(this.id).disabled=f},isDisabled:function(){return c.get(this.id).disabled},select:function(h){var g=this,j,i;if(h==undefined){return g.selectByIndex(-1)}if(h&&h.call){i=h}else{i=function(f){return f==h}}if(h!=g.selectedValue){e(g.items,function(k,f){if(i(k.value)){j=1;g.selectByIndex(f);return false}});if(!j){g.selectByIndex(-1)}}},selectByIndex:function(f){c.get(this.id).selectedIndex=f+1;this.selectedValue=this.items[f]?this.items[f].value:null},add:function(j,g,f){var i,h=this;f=f||{};f.value=g;if(h.isRendered()){c.add(c.get(this.id),"option",f,j)}i={title:j,value:g,attribs:f};h.items.push(i);h.onAdd.dispatch(h,i)},getLength:function(){return c.get(this.id).options.length-1},renderHTML:function(){var g,f=this;g=c.createHTML("option",{value:""},"-- "+f.settings.title+" --");e(f.items,function(h){g+=c.createHTML("option",{value:h.value},h.title)});g=c.createHTML("select",{id:f.id,"class":"mceNativeListBox"},g);return g},postRender:function(){var g=this,h;g.rendered=true;function f(j){var i=g.items[j.target.selectedIndex-1];if(i&&(i=i.value)){g.onChange.dispatch(g,i);if(g.settings.onselect){g.settings.onselect(i)}}}b.add(g.id,"change",f);b.add(g.id,"keydown",function(j){var i;b.remove(g.id,"change",h);i=b.add(g.id,"blur",function(){b.add(g.id,"change",f);b.remove(g.id,"blur",i)});if(j.keyCode==13||j.keyCode==32){f(j);return b.cancel(j)}});g.onPostRender.dispatch(g,c.get(g.id))}})})(tinymce);(function(c){var b=c.DOM,a=c.dom.Event,d=c.each;c.create("tinymce.ui.MenuButton:tinymce.ui.Button",{MenuButton:function(f,e){this.parent(f,e);this.onRenderMenu=new c.util.Dispatcher(this);e.menu_container=e.menu_container||b.doc.body},showMenu:function(){var g=this,j,i,h=b.get(g.id),f;if(g.isDisabled()){return}if(!g.isMenuRendered){g.renderMenu();g.isMenuRendered=true}if(g.isMenuVisible){return g.hideMenu()}j=b.getPos(g.settings.menu_container);i=b.getPos(h);f=g.menu;f.settings.offset_x=i.x;f.settings.offset_y=i.y;f.settings.vp_offset_x=i.x;f.settings.vp_offset_y=i.y;f.settings.keyboard_focus=g._focused;f.showMenu(0,h.clientHeight);a.add(b.doc,"mousedown",g.hideMenu,g);g.setState("Selected",1);g.isMenuVisible=1},renderMenu:function(){var f=this,e;e=f.settings.control_manager.createDropMenu(f.id+"_menu",{menu_line:1,"class":this.classPrefix+"Menu",icons:f.settings.icons});e.onHideMenu.add(f.hideMenu,f);f.onRenderMenu.dispatch(f,e);f.menu=e},hideMenu:function(g){var f=this;if(g&&g.type=="mousedown"&&b.getParent(g.target,function(h){return h.id===f.id||h.id===f.id+"_open"})){return}if(!g||!b.getParent(g.target,".mceMenu")){f.setState("Selected",0);a.remove(b.doc,"mousedown",f.hideMenu,f);if(f.menu){f.menu.hideMenu()}}f.isMenuVisible=0},postRender:function(){var e=this,f=e.settings;a.add(e.id,"click",function(){if(!e.isDisabled()){if(f.onclick){f.onclick(e.value)}e.showMenu()}})}})})(tinymce);(function(c){var b=c.DOM,a=c.dom.Event,d=c.each;c.create("tinymce.ui.SplitButton:tinymce.ui.MenuButton",{SplitButton:function(f,e){this.parent(f,e);this.classPrefix="mceSplitButton"},renderHTML:function(){var i,f=this,g=f.settings,e;i="<tbody><tr>";if(g.image){e=b.createHTML("img ",{src:g.image,"class":"mceAction "+g["class"]})}else{e=b.createHTML("span",{"class":"mceAction "+g["class"]},"")}i+="<td>"+b.createHTML("a",{id:f.id+"_action",href:"#","class":"mceAction "+g["class"],onclick:"return false;",onmousedown:"return false;",title:g.title},e)+"</td>";e=b.createHTML("span",{"class":"mceOpen "+g["class"]});i+="<td>"+b.createHTML("a",{id:f.id+"_open",href:"#","class":"mceOpen "+g["class"],onclick:"return false;",onmousedown:"return false;",title:g.title},e)+"</td>";i+="</tr></tbody>";return b.createHTML("table",{id:f.id,"class":"mceSplitButton mceSplitButtonEnabled "+g["class"],cellpadding:"0",cellspacing:"0",onmousedown:"return false;",title:g.title},i)},postRender:function(){var e=this,f=e.settings;if(f.onclick){a.add(e.id+"_action","click",function(){if(!e.isDisabled()){f.onclick(e.value)}})}a.add(e.id+"_open","click",e.showMenu,e);a.add(e.id+"_open","focus",function(){e._focused=1});a.add(e.id+"_open","blur",function(){e._focused=0});if(c.isIE6||!b.boxModel){a.add(e.id,"mouseover",function(){if(!b.hasClass(e.id,"mceSplitButtonDisabled")){b.addClass(e.id,"mceSplitButtonHover")}});a.add(e.id,"mouseout",function(){if(!b.hasClass(e.id,"mceSplitButtonDisabled")){b.removeClass(e.id,"mceSplitButtonHover")}})}},destroy:function(){this.parent();a.clear(this.id+"_action");a.clear(this.id+"_open")}})})(tinymce);(function(d){var c=d.DOM,a=d.dom.Event,b=d.is,e=d.each;d.create("tinymce.ui.ColorSplitButton:tinymce.ui.SplitButton",{ColorSplitButton:function(h,g){var f=this;f.parent(h,g);f.settings=g=d.extend({colors:"000000,993300,333300,003300,003366,000080,333399,333333,800000,FF6600,808000,008000,008080,0000FF,666699,808080,FF0000,FF9900,99CC00,339966,33CCCC,3366FF,800080,999999,FF00FF,FFCC00,FFFF00,00FF00,00FFFF,00CCFF,993366,C0C0C0,FF99CC,FFCC99,FFFF99,CCFFCC,CCFFFF,99CCFF,CC99FF,FFFFFF",grid_width:8,default_color:"#888888"},f.settings);f.onShowMenu=new d.util.Dispatcher(f);f.onHideMenu=new d.util.Dispatcher(f);f.value=g.default_color},showMenu:function(){var f=this,g,j,i,h;if(f.isDisabled()){return}if(!f.isMenuRendered){f.renderMenu();f.isMenuRendered=true}if(f.isMenuVisible){return f.hideMenu()}i=c.get(f.id);c.show(f.id+"_menu");c.addClass(i,"mceSplitButtonSelected");h=c.getPos(i);c.setStyles(f.id+"_menu",{left:h.x,top:h.y+i.clientHeight,zIndex:200000});i=0;a.add(c.doc,"mousedown",f.hideMenu,f);f.onShowMenu.dispatch(f);if(f._focused){f._keyHandler=a.add(f.id+"_menu","keydown",function(k){if(k.keyCode==27){f.hideMenu()}});c.select("a",f.id+"_menu")[0].focus()}f.isMenuVisible=1},hideMenu:function(g){var f=this;if(g&&g.type=="mousedown"&&c.getParent(g.target,function(h){return h.id===f.id+"_open"})){return}if(!g||!c.getParent(g.target,".mceSplitButtonMenu")){c.removeClass(f.id,"mceSplitButtonSelected");a.remove(c.doc,"mousedown",f.hideMenu,f);a.remove(f.id+"_menu","keydown",f._keyHandler);c.hide(f.id+"_menu")}f.onHideMenu.dispatch(f);f.isMenuVisible=0},renderMenu:function(){var k=this,f,j=0,l=k.settings,p,h,o,g;g=c.add(l.menu_container,"div",{id:k.id+"_menu","class":l.menu_class+" "+l["class"],style:"position:absolute;left:0;top:-1000px;"});f=c.add(g,"div",{"class":l["class"]+" mceSplitButtonMenu"});c.add(f,"span",{"class":"mceMenuLine"});p=c.add(f,"table",{"class":"mceColorSplitMenu"});h=c.add(p,"tbody");j=0;e(b(l.colors,"array")?l.colors:l.colors.split(","),function(i){i=i.replace(/^#/,"");if(!j--){o=c.add(h,"tr");j=l.grid_width-1}p=c.add(o,"td");p=c.add(p,"a",{href:"#",style:{backgroundColor:"#"+i},mce_color:"#"+i})});if(l.more_colors_func){p=c.add(h,"tr");p=c.add(p,"td",{colspan:l.grid_width,"class":"mceMoreColors"});p=c.add(p,"a",{id:k.id+"_more",href:"#",onclick:"return false;","class":"mceMoreColors"},l.more_colors_title);a.add(p,"click",function(i){l.more_colors_func.call(l.more_colors_scope||this);return a.cancel(i)})}c.addClass(f,"mceColorSplitMenu");a.add(k.id+"_menu","click",function(i){var m;i=i.target;if(i.nodeName=="A"&&(m=i.getAttribute("mce_color"))){k.setColor(m)}return a.cancel(i)});return g},setColor:function(g){var f=this;c.setStyle(f.id+"_preview","backgroundColor",g);f.value=g;f.hideMenu();f.settings.onselect(g)},postRender:function(){var f=this,g=f.id;f.parent();c.add(g+"_action","div",{id:g+"_preview","class":"mceColorPreview"});c.setStyle(f.id+"_preview","backgroundColor",f.value)},destroy:function(){this.parent();a.clear(this.id+"_menu");a.clear(this.id+"_more");c.remove(this.id+"_menu")}})})(tinymce);tinymce.create("tinymce.ui.Toolbar:tinymce.ui.Container",{renderHTML:function(){var l=this,e="",g,j,b=tinymce.DOM,m=l.settings,d,a,f,k;k=l.controls;for(d=0;d<k.length;d++){j=k[d];a=k[d-1];f=k[d+1];if(d===0){g="mceToolbarStart";if(j.Button){g+=" mceToolbarStartButton"}else{if(j.SplitButton){g+=" mceToolbarStartSplitButton"}else{if(j.ListBox){g+=" mceToolbarStartListBox"}}}e+=b.createHTML("td",{"class":g},b.createHTML("span",null,"<!-- IE -->"))}if(a&&j.ListBox){if(a.Button||a.SplitButton){e+=b.createHTML("td",{"class":"mceToolbarEnd"},b.createHTML("span",null,"<!-- IE -->"))}}if(b.stdMode){e+='<td style="position: relative">'+j.renderHTML()+"</td>"}else{e+="<td>"+j.renderHTML()+"</td>"}if(f&&j.ListBox){if(f.Button||f.SplitButton){e+=b.createHTML("td",{"class":"mceToolbarStart"},b.createHTML("span",null,"<!-- IE -->"))}}}g="mceToolbarEnd";if(j.Button){g+=" mceToolbarEndButton"}else{if(j.SplitButton){g+=" mceToolbarEndSplitButton"}else{if(j.ListBox){g+=" mceToolbarEndListBox"}}}e+=b.createHTML("td",{"class":g},b.createHTML("span",null,"<!-- IE -->"));return b.createHTML("table",{id:l.id,"class":"mceToolbar"+(m["class"]?" "+m["class"]:""),cellpadding:"0",cellspacing:"0",align:l.settings.align||""},"<tbody><tr>"+e+"</tr></tbody>")}});(function(b){var a=b.util.Dispatcher,c=b.each;b.create("tinymce.AddOnManager",{items:[],urls:{},lookup:{},onAdd:new a(this),get:function(d){return this.lookup[d]},requireLangPack:function(f){var d,e=b.EditorManager.settings;if(e&&e.language){d=this.urls[f]+"/langs/"+e.language+".js";if(!b.dom.Event.domLoaded&&!e.strict_mode){b.ScriptLoader.load(d)}else{b.ScriptLoader.add(d)}}},add:function(e,d){this.items.push(d);this.lookup[e]=d;this.onAdd.dispatch(this,e,d);return d},load:function(h,e,d,g){var f=this;if(f.urls[h]){return}if(e.indexOf("/")!=0&&e.indexOf("://")==-1){e=b.baseURL+"/"+e}f.urls[h]=e.substring(0,e.lastIndexOf("/"));b.ScriptLoader.add(e,d,g)}});b.PluginManager=new b.AddOnManager();b.ThemeManager=new b.AddOnManager()}(tinymce));(function(f){var g=f.each,h=f.extend,e=f.DOM,a=f.dom.Event,c=f.ThemeManager,b=f.PluginManager,d=f.explode;f.create("static tinymce.EditorManager",{editors:{},i18n:{},activeEditor:null,preInit:function(){var i=this,j=window.location;f.documentBaseURL=j.href.replace(/[\?#].*$/,"").replace(/[\/\\][^\/]+$/,"");if(!/[\/\\]$/.test(f.documentBaseURL)){f.documentBaseURL+="/"}f.baseURL=new f.util.URI(f.documentBaseURL).toAbsolute(f.baseURL);f.EditorManager.baseURI=new f.util.URI(f.baseURL);i.onBeforeUnload=new f.util.Dispatcher(i);a.add(window,"beforeunload",function(k){i.onBeforeUnload.dispatch(i,k)})},init:function(q){var p=this,l,k=f.ScriptLoader,o,n,i=[],m;function j(u,v,r){var t=u[v];if(!t){return}if(f.is(t,"string")){r=t.replace(/\.\w+$/,"");r=r?f.resolve(r):0;t=f.resolve(t)}return t.apply(r||this,Array.prototype.slice.call(arguments,2))}q=h({theme:"simple",language:"en",strict_loading_mode:document.contentType=="application/xhtml+xml"},q);p.settings=q;if(!a.domLoaded&&!q.strict_loading_mode){if(q.language){k.add(f.baseURL+"/langs/"+q.language+".js")}if(q.theme&&q.theme.charAt(0)!="-"&&!c.urls[q.theme]){c.load(q.theme,"themes/"+q.theme+"/editor_template"+f.suffix+".js")}if(q.plugins){l=d(q.plugins);g(l,function(r){if(r&&r.charAt(0)!="-"&&!b.urls[r]){if(!f.isWebKit&&r=="safari"){return}b.load(r,"plugins/"+r+"/editor_plugin"+f.suffix+".js")}})}k.loadQueue()}a.add(document,"init",function(){var r,t;j(q,"onpageload");if(q.browsers){r=false;g(d(q.browsers),function(u){switch(u){case"ie":case"msie":if(f.isIE){r=true}break;case"gecko":if(f.isGecko){r=true}break;case"safari":case"webkit":if(f.isWebKit){r=true}break;case"opera":if(f.isOpera){r=true}break}});if(!r){return}}switch(q.mode){case"exact":r=q.elements||"";if(r.length>0){g(d(r),function(u){if(e.get(u)){m=new f.Editor(u,q);i.push(m);m.render(1)}else{o=0;g(document.forms,function(v){g(v.elements,function(w){if(w.name===u){u="mce_editor_"+o;e.setAttrib(w,"id",u);m=new f.Editor(u,q);i.push(m);m.render(1)}})})}})}break;case"textareas":case"specific_textareas":function s(v,u){return u.constructor===RegExp?u.test(v.className):e.hasClass(v,u)}g(e.select("textarea"),function(u){if(q.editor_deselector&&s(u,q.editor_deselector)){return}if(!q.editor_selector||s(u,q.editor_selector)){n=e.get(u.name);if(!u.id&&!n){u.id=u.name}if(!u.id||p.get(u.id)){u.id=e.uniqueId()}m=new f.Editor(u.id,q);i.push(m);m.render(1)}});break}if(q.oninit){r=t=0;g(i,function(u){t++;if(!u.initialized){u.onInit.add(function(){r++;if(r==t){j(q,"oninit")}})}else{r++}if(r==t){j(q,"oninit")}})}})},get:function(i){return this.editors[i]},getInstanceById:function(i){return this.get(i)},add:function(i){this.editors[i.id]=i;this._setActive(i);return i},remove:function(j){var i=this;if(!i.editors[j.id]){return null}delete i.editors[j.id];if(i.activeEditor==j){i._setActive(null);g(i.editors,function(k){i._setActive(k);return false})}j.destroy();return j},execCommand:function(o,m,l){var n=this,k=n.get(l),i;switch(o){case"mceFocus":k.focus();return true;case"mceAddEditor":case"mceAddControl":if(!n.get(l)){new f.Editor(l,n.settings).render()}return true;case"mceAddFrameControl":i=l.window;i.tinyMCE=tinyMCE;i.tinymce=f;f.DOM.doc=i.document;f.DOM.win=i;k=new f.Editor(l.element_id,l);k.render();if(f.isIE){function j(){k.destroy();i.detachEvent("onunload",j);i=i.tinyMCE=i.tinymce=null}i.attachEvent("onunload",j)}l.page_window=null;return true;case"mceRemoveEditor":case"mceRemoveControl":if(k){k.remove()}return true;case"mceToggleEditor":if(!k){n.execCommand("mceAddControl",0,l);return true}if(k.isHidden()){k.show()}else{k.hide()}return true}if(n.activeEditor){return n.activeEditor.execCommand(o,m,l)}return false},execInstanceCommand:function(m,l,k,j){var i=this.get(m);if(i){return i.execCommand(l,k,j)}return false},triggerSave:function(){g(this.editors,function(i){i.save()})},addI18n:function(k,l){var i,j=this.i18n;if(!f.is(k,"string")){g(k,function(n,m){g(n,function(q,p){g(q,function(s,r){if(p==="common"){j[m+"."+r]=s}else{j[m+"."+p+"."+r]=s}})})})}else{g(l,function(n,m){j[k+"."+m]=n})}},_setActive:function(i){this.selectedInstance=this.activeEditor=i}});f.EditorManager.preInit()})(tinymce);var tinyMCE=window.tinyMCE=tinymce.EditorManager;(function(n){var o=n.DOM,k=n.dom.Event,f=n.extend,l=n.util.Dispatcher;var j=n.each,a=n.isGecko,b=n.isIE,e=n.isWebKit;var d=n.is,h=n.ThemeManager,c=n.PluginManager,i=n.EditorManager;var p=n.inArray,m=n.grep,g=n.explode;n.create("tinymce.Editor",{Editor:function(u,r){var q=this;q.id=q.editorId=u;q.execCommands={};q.queryStateCommands={};q.queryValueCommands={};q.isNotDirty=false;q.plugins={};j(["onPreInit","onBeforeRenderUI","onPostRender","onInit","onRemove","onActivate","onDeactivate","onClick","onEvent","onMouseUp","onMouseDown","onDblClick","onKeyDown","onKeyUp","onKeyPress","onContextMenu","onSubmit","onReset","onPaste","onPreProcess","onPostProcess","onBeforeSetContent","onBeforeGetContent","onSetContent","onGetContent","onLoadContent","onSaveContent","onNodeChange","onChange","onBeforeExecCommand","onExecCommand","onUndo","onRedo","onVisualAid","onSetProgressState"],function(s){q[s]=new l(q)});q.settings=r=f({id:u,language:"en",docs_language:"en",theme:"simple",skin:"default",delta_width:0,delta_height:0,popup_css:"",plugins:"",document_base_url:n.documentBaseURL,add_form_submit_trigger:1,submit_patch:1,add_unload_trigger:1,convert_urls:1,relative_urls:1,remove_script_host:1,table_inline_editing:0,object_resizing:1,cleanup:1,accessibility_focus:1,custom_shortcuts:1,custom_undo_redo_keyboard_shortcuts:1,custom_undo_redo_restore_selection:1,custom_undo_redo:1,doctype:'<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">',visual_table_class:"mceItemTable",visual:1,inline_styles:true,convert_fonts_to_spans:true,font_size_style_values:"xx-small,x-small,small,medium,large,x-large,xx-large",apply_source_formatting:1,directionality:"ltr",forced_root_block:"p",valid_elements:"@[id|class|style|title|dir<ltr?rtl|lang|xml::lang|onclick|ondblclick|onmousedown|onmouseup|onmouseover|onmousemove|onmouseout|onkeypress|onkeydown|onkeyup],a[rel|rev|charset|hreflang|tabindex|accesskey|type|name|href|target|title|class|onfocus|onblur],strong/b,em/i,strike,u,#p,-ol[type|compact],-ul[type|compact],-li,br,img[longdesc|usemap|src|border|alt=|title|hspace|vspace|width|height|align],-sub,-sup,-blockquote[cite],-table[border|cellspacing|cellpadding|width|frame|rules|height|align|summary|bgcolor|background|bordercolor],-tr[rowspan|width|height|align|valign|bgcolor|background|bordercolor],tbody,thead,tfoot,#td[colspan|rowspan|width|height|align|valign|bgcolor|background|bordercolor|scope],#th[colspan|rowspan|width|height|align|valign|scope],caption,-div,-span,-code,-pre,address,-h1,-h2,-h3,-h4,-h5,-h6,hr[size|noshade],-font[face|size|color],dd,dl,dt,cite,abbr,acronym,del[datetime|cite],ins[datetime|cite],object[classid|width|height|codebase|*],param[name|value],embed[type|width|height|src|*],script[src|type],map[name],area[shape|coords|href|alt|target],bdo,button,col[align|char|charoff|span|valign|width],colgroup[align|char|charoff|span|valign|width],dfn,fieldset,form[action|accept|accept-charset|enctype|method],input[accept|alt|checked|disabled|maxlength|name|readonly|size|src|type|value|tabindex|accesskey],kbd,label[for],legend,noscript,optgroup[label|disabled],option[disabled|label|selected|value],q[cite],samp,select[disabled|multiple|name|size],small,textarea[cols|rows|disabled|name|readonly],tt,var,big",hidden_input:1,padd_empty_editor:1,render_ui:1,init_theme:1,force_p_newlines:1,indentation:"30px",keep_styles:1,fix_table_elements:1,removeformat_selector:"span,b,strong,em,i,font,u,strike"},r);q.documentBaseURI=new n.util.URI(r.document_base_url||n.documentBaseURL,{base_uri:tinyMCE.baseURI});q.baseURI=i.baseURI;q.execCallback("setup",q)},render:function(u){var v=this,w=v.settings,x=v.id,q=n.ScriptLoader;if(!k.domLoaded){k.add(document,"init",function(){v.render()});return}if(!u){w.strict_loading_mode=1;tinyMCE.settings=w}if(!v.getElement()){return}if(w.strict_loading_mode){q.settings.strict_mode=w.strict_loading_mode;n.DOM.settings.strict=1}if(!/TEXTAREA|INPUT/i.test(v.getElement().nodeName)&&w.hidden_input&&o.getParent(x,"form")){o.insertAfter(o.create("input",{type:"hidden",name:x}),x)}if(n.WindowManager){v.windowManager=new n.WindowManager(v)}if(w.encoding=="xml"){v.onGetContent.add(function(s,t){if(t.save){t.content=o.encode(t.content)}})}if(w.add_form_submit_trigger){v.onSubmit.addToTop(function(){if(v.initialized){v.save();v.isNotDirty=1}})}if(w.add_unload_trigger){v._beforeUnload=tinyMCE.onBeforeUnload.add(function(){if(v.initialized&&!v.destroyed&&!v.isHidden()){v.save({format:"raw",no_events:true})}})}n.addUnload(v.destroy,v);if(w.submit_patch){v.onBeforeRenderUI.add(function(){var s=v.getElement().form;if(!s){return}if(s._mceOldSubmit){return}if(!s.submit.nodeType&&!s.submit.length){v.formElement=s;s._mceOldSubmit=s.submit;s.submit=function(){i.triggerSave();v.isNotDirty=1;return v.formElement._mceOldSubmit(v.formElement)}}s=null})}function r(){if(w.language){q.add(n.baseURL+"/langs/"+w.language+".js")}if(w.theme&&w.theme.charAt(0)!="-"&&!h.urls[w.theme]){h.load(w.theme,"themes/"+w.theme+"/editor_template"+n.suffix+".js")}j(g(w.plugins),function(s){if(s&&s.charAt(0)!="-"&&!c.urls[s]){if(!e&&s=="safari"){return}c.load(s,"plugins/"+s+"/editor_plugin"+n.suffix+".js")}});q.loadQueue(function(){if(!v.removed){v.init()}})}r()},init:function(){var v,F=this,G=F.settings,C,z,B=F.getElement(),r,q,D,y,A,E;i.add(F);if(G.theme){G.theme=G.theme.replace(/-/,"");r=h.get(G.theme);F.theme=new r();if(F.theme.init&&G.init_theme){F.theme.init(F,h.urls[G.theme]||n.documentBaseURL.replace(/\/$/,""))}}j(g(G.plugins.replace(/\-/g,"")),function(w){var H=c.get(w),t=c.urls[w]||n.documentBaseURL.replace(/\/$/,""),s;if(H){s=new H(F,t);F.plugins[w]=s;if(s.init){s.init(F,t)}}});if(G.popup_css!==false){if(G.popup_css){G.popup_css=F.documentBaseURI.toAbsolute(G.popup_css)}else{G.popup_css=F.baseURI.toAbsolute("themes/"+G.theme+"/skins/"+G.skin+"/dialog.css")}}if(G.popup_css_add){G.popup_css+=","+F.documentBaseURI.toAbsolute(G.popup_css_add)}F.controlManager=new n.ControlManager(F);F.undoManager=new n.UndoManager(F);F.undoManager.onAdd.add(function(t,s){if(!s.initial){return F.onChange.dispatch(F,s,t)}});F.undoManager.onUndo.add(function(t,s){return F.onUndo.dispatch(F,s,t)});F.undoManager.onRedo.add(function(t,s){return F.onRedo.dispatch(F,s,t)});if(G.custom_undo_redo){F.onExecCommand.add(function(t,w,u,H,s){if(w!="Undo"&&w!="Redo"&&w!="mceRepaint"&&(!s||!s.skip_undo)){F.undoManager.add()}})}F.onExecCommand.add(function(s,t){if(!/^(FontName|FontSize)$/.test(t)){F.nodeChanged()}});if(a){function x(s,t){if(!t||!t.initial){F.execCommand("mceRepaint")}}F.onUndo.add(x);F.onRedo.add(x);F.onSetContent.add(x)}F.onBeforeRenderUI.dispatch(F,F.controlManager);if(G.render_ui){C=G.width||B.style.width||B.offsetWidth;z=G.height||B.style.height||B.offsetHeight;F.orgDisplay=B.style.display;E=/^[0-9\.]+(|px)$/i;if(E.test(""+C)){C=Math.max(parseInt(C)+(r.deltaWidth||0),100)}if(E.test(""+z)){z=Math.max(parseInt(z)+(r.deltaHeight||0),100)}r=F.theme.renderUI({targetNode:B,width:C,height:z,deltaWidth:G.delta_width,deltaHeight:G.delta_height});F.editorContainer=r.editorContainer}if(document.domain&&location.hostname!=document.domain){n.relaxedDomain=document.domain}o.setStyles(r.sizeContainer||r.editorContainer,{width:C,height:z});z=(r.iframeHeight||z)+(typeof(z)=="number"?(r.deltaHeight||0):"");if(z<100){z=100}F.iframeHTML=G.doctype+'<html><head xmlns="http://www.w3.org/1999/xhtml">';if(G.document_base_url!=n.documentBaseURL){F.iframeHTML+='<base href="'+F.documentBaseURI.getURI()+'" />'}F.iframeHTML+='<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />';if(n.relaxedDomain){F.iframeHTML+='<script type="text/javascript">document.domain = "'+n.relaxedDomain+'";<\/script>'}y=G.body_id||"tinymce";if(y.indexOf("=")!=-1){y=F.getParam("body_id","","hash");y=y[F.id]||y}A=G.body_class||"";if(A.indexOf("=")!=-1){A=F.getParam("body_class","","hash");A=A[F.id]||""}F.iframeHTML+='</head><body id="'+y+'" class="mceContentBody '+A+'"></body></html>';if(n.relaxedDomain){if(b||(n.isOpera&&parseFloat(opera.version())>=9.5)){D='javascript:(function(){document.open();document.domain="'+document.domain+'";var ed = window.parent.tinyMCE.get("'+F.id+'");document.write(ed.iframeHTML);document.close();ed.setupIframe();})()'}else{if(n.isOpera){D='javascript:(function(){document.open();document.domain="'+document.domain+'";document.close();ed.setupIframe();})()'}}}v=o.add(r.iframeContainer,"iframe",{id:F.id+"_ifr",src:D||'javascript:""',frameBorder:"0",style:{width:"100%",height:z}});F.contentAreaContainer=r.iframeContainer;o.get(r.editorContainer).style.display=F.orgDisplay;o.get(F.id).style.display="none";if(!b||!n.relaxedDomain){F.setupIframe()}B=v=r=null},setupIframe:function(){var z=this,A=z.settings,u=o.get(z.id),v=z.getDoc(),r,x;if(!b||!n.relaxedDomain){v.open();v.write(z.iframeHTML);v.close()}if(!b){try{if(!A.readonly){v.designMode="On"}}catch(w){}}if(b){x=z.getBody();o.hide(x);if(!A.readonly){x.contentEditable=true}o.show(x)}z.dom=new n.dom.DOMUtils(z.getDoc(),{keep_values:true,url_converter:z.convertURL,url_converter_scope:z,hex_colors:A.force_hex_style_colors,class_filter:A.class_filter,update_styles:1,fix_ie_paragraphs:1});z.serializer=new n.dom.Serializer(f(A,{valid_elements:A.verify_html===false?"*[*]":A.valid_elements,dom:z.dom}));z.selection=new n.dom.Selection(z.dom,z.getWin(),z.serializer);z.forceBlocks=new n.ForceBlocks(z,{forced_root_block:A.forced_root_block});z.editorCommands=new n.EditorCommands(z);z.serializer.onPreProcess.add(function(s,t){return z.onPreProcess.dispatch(z,t,s)});z.serializer.onPostProcess.add(function(s,t){return z.onPostProcess.dispatch(z,t,s)});z.onPreInit.dispatch(z);if(!A.gecko_spellcheck){z.getBody().spellcheck=0}if(!A.readonly){z._addEvents()}z.controlManager.onPostRender.dispatch(z,z.controlManager);z.onPostRender.dispatch(z);if(A.directionality){z.getBody().dir=A.directionality}if(A.nowrap){z.getBody().style.whiteSpace="nowrap"}if(A.custom_elements){function y(s,t){j(g(A.custom_elements),function(B){var C;if(B.indexOf("~")===0){B=B.substring(1);C="span"}else{C="div"}t.content=t.content.replace(new RegExp("<("+B+")([^>]*)>","g"),"<"+C+' mce_name="$1"$2>');t.content=t.content.replace(new RegExp("</("+B+")>","g"),"</"+C+">")})}z.onBeforeSetContent.add(y);z.onPostProcess.add(function(s,t){if(t.set){y(s,t)}})}if(A.handle_node_change_callback){z.onNodeChange.add(function(t,s,B){z.execCallback("handle_node_change_callback",z.id,B,-1,-1,true,z.selection.isCollapsed())})}if(A.save_callback){z.onSaveContent.add(function(s,B){var t=z.execCallback("save_callback",z.id,B.content,z.getBody());if(t){B.content=t}})}if(A.onchange_callback){z.onChange.add(function(t,s){z.execCallback("onchange_callback",z,s)})}if(A.convert_newlines_to_brs){z.onBeforeSetContent.add(function(s,t){if(t.initial){t.content=t.content.replace(/\r?\n/g,"<br />")}})}if(A.fix_nesting&&b){z.onBeforeSetContent.add(function(s,t){t.content=z._fixNesting(t.content)})}if(A.preformatted){z.onPostProcess.add(function(s,t){t.content=t.content.replace(/^\s*<pre.*?>/,"");t.content=t.content.replace(/<\/pre>\s*$/,"");if(t.set){t.content='<pre class="mceItemHidden">'+t.content+"</pre>"}})}if(A.verify_css_classes){z.serializer.attribValueFilter=function(D,B){var C,t;if(D=="class"){if(!z.classesRE){t=z.dom.getClasses();if(t.length>0){C="";j(t,function(s){C+=(C?"|":"")+s["class"]});z.classesRE=new RegExp("("+C+")","gi")}}return !z.classesRE||/(\bmceItem\w+\b|\bmceTemp\w+\b)/g.test(B)||z.classesRE.test(B)?B:""}return B}}if(A.convert_fonts_to_spans){z._convertFonts()}if(A.inline_styles){z._convertInlineElements()}if(A.cleanup_callback){z.onBeforeSetContent.add(function(s,t){t.content=z.execCallback("cleanup_callback","insert_to_editor",t.content,t)});z.onPreProcess.add(function(s,t){if(t.set){z.execCallback("cleanup_callback","insert_to_editor_dom",t.node,t)}if(t.get){z.execCallback("cleanup_callback","get_from_editor_dom",t.node,t)}});z.onPostProcess.add(function(s,t){if(t.set){t.content=z.execCallback("cleanup_callback","insert_to_editor",t.content,t)}if(t.get){t.content=z.execCallback("cleanup_callback","get_from_editor",t.content,t)}})}if(A.save_callback){z.onGetContent.add(function(s,t){if(t.save){t.content=z.execCallback("save_callback",z.id,t.content,z.getBody())}})}if(A.handle_event_callback){z.onEvent.add(function(s,t,B){if(z.execCallback("handle_event_callback",t,s,B)===false){k.cancel(t)}})}z.onSetContent.add(function(){z.addVisual(z.getBody())});if(A.padd_empty_editor){z.onPostProcess.add(function(s,t){t.content=t.content.replace(/^(<p[^>]*>(&nbsp;|&#160;|\s|\u00a0|)<\/p>[\r\n]*|<br \/>[\r\n]*)$/,"")})}if(a){function q(s,t){j(s.dom.select("a"),function(C){var B=C.parentNode;if(s.dom.isBlock(B)&&B.lastChild===C){s.dom.add(B,"br",{mce_bogus:1})}})}z.onExecCommand.add(function(s,t){if(t==="CreateLink"){q(s)}});z.onSetContent.add(z.selection.onSetContent.add(q));if(!A.readonly){try{v.designMode="Off";v.designMode="On"}catch(w){}}}setTimeout(function(){if(z.removed){return}z.load({initial:true,format:(A.cleanup_on_startup?"html":"raw")});z.startContent=z.getContent({format:"raw"});z.undoManager.add({initial:true});z.initialized=true;z.onInit.dispatch(z);z.execCallback("setupcontent_callback",z.id,z.getBody(),z.getDoc());z.execCallback("init_instance_callback",z);z.focus(true);z.nodeChanged({initial:1});if(A.content_css){n.each(g(A.content_css),function(s){z.dom.loadCSS(z.documentBaseURI.toAbsolute(s))})}if(A.auto_focus){setTimeout(function(){var s=i.get(A.auto_focus);s.selection.select(s.getBody(),1);s.selection.collapse(1);s.getWin().focus()},100)}},1);u=null},focus:function(r){var u,q=this,s=q.settings.content_editable;if(!r){if(!s&&(!b||q.selection.getNode().ownerDocument!=q.getDoc())){q.getWin().focus()}}if(i.activeEditor!=q){if((u=i.activeEditor)!=null){u.onDeactivate.dispatch(u,q)}q.onActivate.dispatch(q,u)}i._setActive(q)},execCallback:function(v){var q=this,u=q.settings[v],r;if(!u){return}if(q.callbackLookup&&(r=q.callbackLookup[v])){u=r.func;r=r.scope}if(d(u,"string")){r=u.replace(/\.\w+$/,"");r=r?n.resolve(r):0;u=n.resolve(u);q.callbackLookup=q.callbackLookup||{};q.callbackLookup[v]={func:u,scope:r}}return u.apply(r||q,Array.prototype.slice.call(arguments,1))},translate:function(q){var t=this.settings.language||"en",r=i.i18n;if(!q){return""}return r[t+"."+q]||q.replace(/{\#([^}]+)\}/g,function(u,s){return r[t+"."+s]||"{#"+s+"}"})},getLang:function(r,q){return i.i18n[(this.settings.language||"en")+"."+r]||(d(q)?q:"{#"+r+"}")},getParam:function(w,s,q){var t=n.trim,r=d(this.settings[w])?this.settings[w]:s,u;if(q==="hash"){u={};if(d(r,"string")){j(r.indexOf("=")>0?r.split(/[;,](?![^=;,]*(?:[;,]|$))/):r.split(","),function(x){x=x.split("=");if(x.length>1){u[t(x[0])]=t(x[1])}else{u[t(x[0])]=t(x)}})}else{u=r}return u}return r},nodeChanged:function(u){var q=this,r=q.selection,v=r.getNode()||q.getBody();if(q.initialized){q.onNodeChange.dispatch(q,u?u.controlManager||q.controlManager:q.controlManager,b&&v.ownerDocument!=q.getDoc()?q.getBody():v,r.isCollapsed(),u)}},addButton:function(u,r){var q=this;q.buttons=q.buttons||{};q.buttons[u]=r},addCommand:function(t,r,q){this.execCommands[t]={func:r,scope:q||this}},addQueryStateHandler:function(t,r,q){this.queryStateCommands[t]={func:r,scope:q||this}},addQueryValueHandler:function(t,r,q){this.queryValueCommands[t]={func:r,scope:q||this}},addShortcut:function(s,v,q,u){var r=this,w;if(!r.settings.custom_shortcuts){return false}r.shortcuts=r.shortcuts||{};if(d(q,"string")){w=q;q=function(){r.execCommand(w,false,null)}}if(d(q,"object")){w=q;q=function(){r.execCommand(w[0],w[1],w[2])}}j(g(s),function(t){var x={func:q,scope:u||this,desc:v,alt:false,ctrl:false,shift:false};j(g(t,"+"),function(y){switch(y){case"alt":case"ctrl":case"shift":x[y]=true;break;default:x.charCode=y.charCodeAt(0);x.keyCode=y.toUpperCase().charCodeAt(0)}});r.shortcuts[(x.ctrl?"ctrl":"")+","+(x.alt?"alt":"")+","+(x.shift?"shift":"")+","+x.keyCode]=x});return true},execCommand:function(x,w,z,q){var u=this,v=0,y,r;if(!/^(mceAddUndoLevel|mceEndUndoLevel|mceBeginUndoLevel|mceRepaint|SelectAll)$/.test(x)&&(!q||!q.skip_focus)){u.focus()}y={};u.onBeforeExecCommand.dispatch(u,x,w,z,y);if(y.terminate){return false}if(u.execCallback("execcommand_callback",u.id,u.selection.getNode(),x,w,z)){u.onExecCommand.dispatch(u,x,w,z,q);return true}if(y=u.execCommands[x]){r=y.func.call(y.scope,w,z);if(r!==true){u.onExecCommand.dispatch(u,x,w,z,q);return r}}j(u.plugins,function(s){if(s.execCommand&&s.execCommand(x,w,z)){u.onExecCommand.dispatch(u,x,w,z,q);v=1;return false}});if(v){return true}if(u.theme&&u.theme.execCommand&&u.theme.execCommand(x,w,z)){u.onExecCommand.dispatch(u,x,w,z,q);return true}if(n.GlobalCommands.execCommand(u,x,w,z)){u.onExecCommand.dispatch(u,x,w,z,q);return true}if(u.editorCommands.execCommand(x,w,z)){u.onExecCommand.dispatch(u,x,w,z,q);return true}u.getDoc().execCommand(x,w,z);u.onExecCommand.dispatch(u,x,w,z,q)},queryCommandState:function(w){var r=this,v,u;if(r._isHidden()){return}if(v=r.queryStateCommands[w]){u=v.func.call(v.scope);if(u!==true){return u}}v=r.editorCommands.queryCommandState(w);if(v!==-1){return v}try{return this.getDoc().queryCommandState(w)}catch(q){}},queryCommandValue:function(w){var r=this,v,u;if(r._isHidden()){return}if(v=r.queryValueCommands[w]){u=v.func.call(v.scope);if(u!==true){return u}}v=r.editorCommands.queryCommandValue(w);if(d(v)){return v}try{return this.getDoc().queryCommandValue(w)}catch(q){}},show:function(){var q=this;o.show(q.getContainer());o.hide(q.id);q.load()},hide:function(){var q=this,r=q.getDoc();if(b&&r){r.execCommand("SelectAll")}q.save();o.hide(q.getContainer());o.setStyle(q.id,"display",q.orgDisplay)},isHidden:function(){return !o.isHidden(this.id)},setProgressState:function(q,r,s){this.onSetProgressState.dispatch(this,q,r,s);return q},load:function(u){var q=this,s=q.getElement(),r;if(s){u=u||{};u.load=true;r=q.setContent(d(s.value)?s.value:s.innerHTML,u);u.element=s;if(!u.no_events){q.onLoadContent.dispatch(q,u)}u.element=s=null;return r}},save:function(v){var q=this,u=q.getElement(),r,s;if(!u||!q.initialized){return}v=v||{};v.save=true;if(!v.no_events){q.undoManager.typing=0;q.undoManager.add()}v.element=u;r=v.content=q.getContent(v);if(!v.no_events){q.onSaveContent.dispatch(q,v)}r=v.content;if(!/TEXTAREA|INPUT/i.test(u.nodeName)){u.innerHTML=r;if(s=o.getParent(q.id,"form")){j(s.elements,function(t){if(t.name==q.id){t.value=r;return false}})}}else{u.value=r}v.element=u=null;return r},setContent:function(r,s){var q=this;s=s||{};s.format=s.format||"html";s.set=true;s.content=r;if(!s.no_events){q.onBeforeSetContent.dispatch(q,s)}if(!n.isIE&&(r.length===0||/^\s+$/.test(r))){s.content=q.dom.setHTML(q.getBody(),'<br mce_bogus="1" />');s.format="raw"}s.content=q.dom.setHTML(q.getBody(),n.trim(s.content));if(s.format!="raw"&&q.settings.cleanup){s.getInner=true;s.content=q.dom.setHTML(q.getBody(),q.serializer.serialize(q.getBody(),s))}if(!s.no_events){q.onSetContent.dispatch(q,s)}return s.content},getContent:function(s){var q=this,r;s=s||{};s.format=s.format||"html";s.get=true;if(!s.no_events){q.onBeforeGetContent.dispatch(q,s)}if(s.format!="raw"&&q.settings.cleanup){s.getInner=true;r=q.serializer.serialize(q.getBody(),s)}else{r=q.getBody().innerHTML}r=r.replace(/^\s*|\s*$/g,"");s.content=r;if(!s.no_events){q.onGetContent.dispatch(q,s)}return s.content},isDirty:function(){var q=this;return n.trim(q.startContent)!=n.trim(q.getContent({format:"raw",no_events:1}))&&!q.isNotDirty},getContainer:function(){var q=this;if(!q.container){q.container=o.get(q.editorContainer||q.id+"_parent")}return q.container},getContentAreaContainer:function(){return this.contentAreaContainer},getElement:function(){return o.get(this.settings.content_element||this.id)},getWin:function(){var q=this,r;if(!q.contentWindow){r=o.get(q.id+"_ifr");if(r){q.contentWindow=r.contentWindow}}return q.contentWindow},getDoc:function(){var r=this,q;if(!r.contentDocument){q=r.getWin();if(q){r.contentDocument=q.document}}return r.contentDocument},getBody:function(){return this.bodyElement||this.getDoc().body},convertURL:function(q,x,w){var r=this,v=r.settings;if(v.urlconverter_callback){return r.execCallback("urlconverter_callback",q,w,true,x)}if(!v.convert_urls||(w&&w.nodeName=="LINK")||q.indexOf("file:")===0){return q}if(v.relative_urls){return r.documentBaseURI.toRelative(q)}q=r.documentBaseURI.toAbsolute(q,v.remove_script_host);return q},addVisual:function(u){var q=this,r=q.settings;u=u||q.getBody();if(!d(q.hasVisual)){q.hasVisual=r.visual}j(q.dom.select("table,a",u),function(t){var s;switch(t.nodeName){case"TABLE":s=q.dom.getAttrib(t,"border");if(!s||s=="0"){if(q.hasVisual){q.dom.addClass(t,r.visual_table_class)}else{q.dom.removeClass(t,r.visual_table_class)}}return;case"A":s=q.dom.getAttrib(t,"name");if(s){if(q.hasVisual){q.dom.addClass(t,"mceItemAnchor")}else{q.dom.removeClass(t,"mceItemAnchor")}}return}});q.onVisualAid.dispatch(q,u,q.hasVisual)},remove:function(){var q=this,r=q.getContainer();q.removed=1;q.hide();q.execCallback("remove_instance_callback",q);q.onRemove.dispatch(q);q.onExecCommand.listeners=[];i.remove(q);o.remove(r)},destroy:function(r){var q=this;if(q.destroyed){return}if(!r){n.removeUnload(q.destroy);tinyMCE.onBeforeUnload.remove(q._beforeUnload);if(q.theme&&q.theme.destroy){q.theme.destroy()}q.controlManager.destroy();q.selection.destroy();q.dom.destroy();if(!q.settings.content_editable){k.clear(q.getWin());k.clear(q.getDoc())}k.clear(q.getBody());k.clear(q.formElement)}if(q.formElement){q.formElement.submit=q.formElement._mceOldSubmit;q.formElement._mceOldSubmit=null}q.contentAreaContainer=q.formElement=q.container=q.settings.content_element=q.bodyElement=q.contentDocument=q.contentWindow=null;if(q.selection){q.selection=q.selection.win=q.selection.dom=q.selection.dom.doc=null}q.destroyed=1},_addEvents:function(){var w=this,v,y=w.settings,x={mouseup:"onMouseUp",mousedown:"onMouseDown",click:"onClick",keyup:"onKeyUp",keydown:"onKeyDown",keypress:"onKeyPress",submit:"onSubmit",reset:"onReset",contextmenu:"onContextMenu",dblclick:"onDblClick",paste:"onPaste"};function u(t,A){var s=t.type;if(w.removed){return}if(w.onEvent.dispatch(w,t,A)!==false){w[x[t.fakeType||t.type]].dispatch(w,t,A)}}j(x,function(t,s){switch(s){case"contextmenu":if(n.isOpera){w.dom.bind(w.getBody(),"mousedown",function(A){if(A.ctrlKey){A.fakeType="contextmenu";u(A)}})}else{w.dom.bind(w.getBody(),s,u)}break;case"paste":w.dom.bind(w.getBody(),s,function(A){u(A)});break;case"submit":case"reset":w.dom.bind(w.getElement().form||o.getParent(w.id,"form"),s,u);break;default:w.dom.bind(y.content_editable?w.getBody():w.getDoc(),s,u)}});w.dom.bind(y.content_editable?w.getBody():(a?w.getDoc():w.getWin()),"focus",function(s){w.focus(true)});if(n.isGecko){w.dom.bind(w.getDoc(),"DOMNodeInserted",function(t){var s;t=t.target;if(t.nodeType===1&&t.nodeName==="IMG"&&(s=t.getAttribute("mce_src"))){t.src=w.documentBaseURI.toAbsolute(s)}})}if(a){function q(){var B=this,D=B.getDoc(),C=B.settings;if(a&&!C.readonly){if(B._isHidden()){try{if(!C.content_editable){D.designMode="On"}}catch(A){}}try{D.execCommand("styleWithCSS",0,false)}catch(A){if(!B._isHidden()){try{D.execCommand("useCSS",0,true)}catch(A){}}}if(!C.table_inline_editing){try{D.execCommand("enableInlineTableEditing",false,false)}catch(A){}}if(!C.object_resizing){try{D.execCommand("enableObjectResizing",false,false)}catch(A){}}}}w.onBeforeExecCommand.add(q);w.onMouseDown.add(q)}w.onMouseUp.add(w.nodeChanged);w.onClick.add(w.nodeChanged);w.onKeyUp.add(function(s,t){var A=t.keyCode;if((A>=33&&A<=36)||(A>=37&&A<=40)||A==13||A==45||A==46||A==8||(n.isMac&&(A==91||A==93))||t.ctrlKey){w.nodeChanged()}});w.onReset.add(function(){w.setContent(w.startContent,{format:"raw"})});if(y.custom_shortcuts){if(y.custom_undo_redo_keyboard_shortcuts){w.addShortcut("ctrl+z",w.getLang("undo_desc"),"Undo");w.addShortcut("ctrl+y",w.getLang("redo_desc"),"Redo")}if(a){w.addShortcut("ctrl+b",w.getLang("bold_desc"),"Bold");w.addShortcut("ctrl+i",w.getLang("italic_desc"),"Italic");w.addShortcut("ctrl+u",w.getLang("underline_desc"),"Underline")}for(v=1;v<=6;v++){w.addShortcut("ctrl+"+v,"",["FormatBlock",false,"<h"+v+">"])}w.addShortcut("ctrl+7","",["FormatBlock",false,"<p>"]);w.addShortcut("ctrl+8","",["FormatBlock",false,"<div>"]);w.addShortcut("ctrl+9","",["FormatBlock",false,"<address>"]);function z(t){var s=null;if(!t.altKey&&!t.ctrlKey&&!t.metaKey){return s}j(w.shortcuts,function(A){if(n.isMac&&A.ctrl!=t.metaKey){return}else{if(!n.isMac&&A.ctrl!=t.ctrlKey){return}}if(A.alt!=t.altKey){return}if(A.shift!=t.shiftKey){return}if(t.keyCode==A.keyCode||(t.charCode&&t.charCode==A.charCode)){s=A;return false}});return s}w.onKeyUp.add(function(s,t){var A=z(t);if(A){return k.cancel(t)}});w.onKeyPress.add(function(s,t){var A=z(t);if(A){return k.cancel(t)}});w.onKeyDown.add(function(s,t){var A=z(t);if(A){A.func.call(A.scope);return k.cancel(t)}})}if(n.isIE){w.dom.bind(w.getDoc(),"controlselect",function(A){var t=w.resizeInfo,s;A=A.target;if(A.nodeName!=="IMG"){return}if(t){w.dom.unbind(t.node,t.ev,t.cb)}if(!w.dom.hasClass(A,"mceItemNoResize")){ev="resizeend";s=w.dom.bind(A,ev,function(C){var B;C=C.target;if(B=w.dom.getStyle(C,"width")){w.dom.setAttrib(C,"width",B.replace(/[^0-9%]+/g,""));w.dom.setStyle(C,"width","")}if(B=w.dom.getStyle(C,"height")){w.dom.setAttrib(C,"height",B.replace(/[^0-9%]+/g,""));w.dom.setStyle(C,"height","")}})}else{ev="resizestart";s=w.dom.bind(A,"resizestart",k.cancel,k)}t=w.resizeInfo={node:A,ev:ev,cb:s}});w.onKeyDown.add(function(s,t){switch(t.keyCode){case 8:if(w.selection.getRng().item){w.selection.getRng().item(0).removeNode();return k.cancel(t)}}})}if(n.isOpera){w.onClick.add(function(s,t){k.prevent(t)})}if(y.custom_undo_redo){function r(){w.undoManager.typing=0;w.undoManager.add()}if(n.isIE){w.dom.bind(w.getWin(),"blur",function(s){var t;if(w.selection){t=w.selection.getNode();if(!w.removed&&t.ownerDocument&&t.ownerDocument!=w.getDoc()){r()}}})}else{w.dom.bind(w.getDoc(),"blur",function(){if(w.selection&&!w.removed){r()}})}w.onMouseDown.add(r);w.onKeyUp.add(function(s,t){if((t.keyCode>=33&&t.keyCode<=36)||(t.keyCode>=37&&t.keyCode<=40)||t.keyCode==13||t.keyCode==45||t.ctrlKey){w.undoManager.typing=0;w.undoManager.add()}});w.onKeyDown.add(function(s,t){if((t.keyCode>=33&&t.keyCode<=36)||(t.keyCode>=37&&t.keyCode<=40)||t.keyCode==13||t.keyCode==45){if(w.undoManager.typing){w.undoManager.add();w.undoManager.typing=0}return}if(!w.undoManager.typing){w.undoManager.add();w.undoManager.typing=1}})}},_convertInlineElements:function(){var z=this,B=z.settings,r=z.dom,y,w,u,A,q;function x(s,t){if(!B.inline_styles){return}if(t.get){j(z.dom.select("table,u,strike",t.node),function(v){switch(v.nodeName){case"TABLE":if(y=r.getAttrib(v,"height")){r.setStyle(v,"height",y);r.setAttrib(v,"height","")}break;case"U":case"STRIKE":v.style.textDecoration=v.nodeName=="U"?"underline":"line-through";r.setAttrib(v,"mce_style","");r.setAttrib(v,"mce_name","span");break}})}else{if(t.set){j(z.dom.select("table,span",t.node).reverse(),function(v){if(v.nodeName=="TABLE"){if(y=r.getStyle(v,"height")){r.setAttrib(v,"height",y.replace(/[^0-9%]+/g,""))}}else{if(v.style.textDecoration=="underline"){u="u"}else{if(v.style.textDecoration=="line-through"){u="strike"}else{u=""}}if(u){v.style.textDecoration="";r.setAttrib(v,"mce_style","");w=r.create(u,{style:r.getAttrib(v,"style")});r.replace(w,v,1)}}})}}}z.onPreProcess.add(x);if(!B.cleanup_on_startup){z.onSetContent.add(function(s,t){if(t.initial){x(z,{node:z.getBody(),set:1})}})}},_convertFonts:function(){var w=this,x=w.settings,z=w.dom,v,r,q,u;if(!x.inline_styles){return}v=[8,10,12,14,18,24,36];r=["xx-small","x-small","small","medium","large","x-large","xx-large"];if(q=x.font_size_style_values){q=g(q)}if(u=x.font_size_classes){u=g(u)}function y(B){var C,A,t,s;if(!x.inline_styles){return}t=w.dom.select("font",B);for(s=t.length-1;s>=0;s--){C=t[s];A=z.create("span",{style:z.getAttrib(C,"style"),"class":z.getAttrib(C,"class")});z.setStyles(A,{fontFamily:z.getAttrib(C,"face"),color:z.getAttrib(C,"color"),backgroundColor:C.style.backgroundColor});if(C.size){if(q){z.setStyle(A,"fontSize",q[parseInt(C.size)-1])}else{z.setAttrib(A,"class",u[parseInt(C.size)-1])}}z.setAttrib(A,"mce_style","");z.replace(A,C,1)}}w.onPreProcess.add(function(s,t){if(t.get){y(t.node)}});w.onSetContent.add(function(s,t){if(t.initial){y(t.node)}})},_isHidden:function(){var q;if(!a){return 0}q=this.selection.getSel();return(!q||!q.rangeCount||q.rangeCount==0)},_fixNesting:function(r){var t=[],q;r=r.replace(/<(\/)?([^\s>]+)[^>]*?>/g,function(u,s,w){var v;if(s==="/"){if(!t.length){return""}if(w!==t[t.length-1].tag){for(q=t.length-1;q>=0;q--){if(t[q].tag===w){t[q].close=1;break}}return""}else{t.pop();if(t.length&&t[t.length-1].close){u=u+"</"+t[t.length-1].tag+">";t.pop()}}}else{if(/^(br|hr|input|meta|img|link|param)$/i.test(w)){return u}if(/\/>$/.test(u)){return u}t.push({tag:w})}return u});for(q=t.length-1;q>=0;q--){r+="</"+t[q].tag+">"}return r}})})(tinymce);(function(d){var f=d.each,c=d.isIE,a=d.isGecko,b=d.isOpera,e=d.isWebKit;d.create("tinymce.EditorCommands",{EditorCommands:function(g){this.editor=g},execCommand:function(k,j,l){var h=this,g=h.editor,i;switch(k){case"mceResetDesignMode":case"mceBeginUndoLevel":return true;case"unlink":h.UnLink();return true;case"JustifyLeft":case"JustifyCenter":case"JustifyRight":case"JustifyFull":h.mceJustify(k,k.substring(7).toLowerCase());return true;default:i=this[k];if(i){i.call(this,j,l);return true}}return false},Indent:function(){var g=this.editor,l=g.dom,j=g.selection,k,h,i;h=g.settings.indentation;i=/[a-z%]+$/i.exec(h);h=parseInt(h);if(g.settings.inline_styles&&(!this.queryStateInsertUnorderedList()&&!this.queryStateInsertOrderedList())){f(j.getSelectedBlocks(),function(m){l.setStyle(m,"paddingLeft",(parseInt(m.style.paddingLeft||0)+h)+i)});return}g.getDoc().execCommand("Indent",false,null);if(c){l.getParent(j.getNode(),function(m){if(m.nodeName=="BLOCKQUOTE"){m.dir=m.style.cssText=""}})}},Outdent:function(){var h=this.editor,m=h.dom,k=h.selection,l,g,i,j;i=h.settings.indentation;j=/[a-z%]+$/i.exec(i);i=parseInt(i);if(h.settings.inline_styles&&(!this.queryStateInsertUnorderedList()&&!this.queryStateInsertOrderedList())){f(k.getSelectedBlocks(),function(n){g=Math.max(0,parseInt(n.style.paddingLeft||0)-i);m.setStyle(n,"paddingLeft",g?g+j:"")});return}h.getDoc().execCommand("Outdent",false,null)},mceSetContent:function(h,g){this.editor.setContent(g)},mceToggleVisualAid:function(){var g=this.editor;g.hasVisual=!g.hasVisual;g.addVisual()},mceReplaceContent:function(h,g){var i=this.editor.selection;i.setContent(g.replace(/\{\$selection\}/g,i.getContent({format:"text"})))},mceInsertLink:function(i,h){var g=this.editor,j=g.selection,k=g.dom.getParent(j.getNode(),"a");if(d.is(h,"string")){h={href:h}}function l(m){f(h,function(o,n){g.dom.setAttrib(m,n,o)})}if(!k){g.execCommand("CreateLink",false,"javascript:mctmp(0);");f(g.dom.select("a[href=javascript:mctmp(0);]"),function(m){l(m)})}else{if(h.href){l(k)}else{g.dom.remove(k,1)}}},UnLink:function(){var g=this.editor,h=g.selection;if(h.isCollapsed()){h.select(h.getNode())}g.getDoc().execCommand("unlink",false,null);h.collapse(0)},FontName:function(i,h){var j=this,g=j.editor,k=g.selection,l;if(!h){if(k.isCollapsed()){k.select(k.getNode())}}else{if(g.settings.convert_fonts_to_spans){j._applyInlineStyle("span",{style:{fontFamily:h}})}else{g.getDoc().execCommand("FontName",false,h)}}},FontSize:function(j,i){var h=this.editor,l=h.settings,k,g;if(l.convert_fonts_to_spans&&i>=1&&i<=7){g=d.explode(l.font_size_style_values);k=d.explode(l.font_size_classes);if(k){i=k[i-1]||i}else{i=g[i-1]||i}}if(i>=1&&i<=7){h.getDoc().execCommand("FontSize",false,i)}else{this._applyInlineStyle("span",{style:{fontSize:i}})}},queryCommandValue:function(h){var g=this["queryValue"+h];if(g){return g.call(this,h)}return false},queryCommandState:function(h){var g;switch(h){case"JustifyLeft":case"JustifyCenter":case"JustifyRight":case"JustifyFull":return this.queryStateJustify(h,h.substring(7).toLowerCase());default:if(g=this["queryState"+h]){return g.call(this,h)}}return -1},_queryState:function(h){try{return this.editor.getDoc().queryCommandState(h)}catch(g){}},_queryVal:function(h){try{return this.editor.getDoc().queryCommandValue(h)}catch(g){}},queryValueFontSize:function(){var h=this.editor,g=0,i;if(i=h.dom.getParent(h.selection.getNode(),"span")){g=i.style.fontSize}if(!g&&(b||e)){if(i=h.dom.getParent(h.selection.getNode(),"font")){g=i.size}return g}return g||this._queryVal("FontSize")},queryValueFontName:function(){var h=this.editor,g=0,i;if(i=h.dom.getParent(h.selection.getNode(),"font")){g=i.face}if(i=h.dom.getParent(h.selection.getNode(),"span")){g=i.style.fontFamily.replace(/, /g,",").replace(/[\'\"]/g,"").toLowerCase()}if(!g){g=this._queryVal("FontName")}return g},mceJustify:function(o,p){var k=this.editor,m=k.selection,g=m.getNode(),q=g.nodeName,h,j,i=k.dom,l;if(k.settings.inline_styles&&this.queryStateJustify(o,p)){l=1}h=i.getParent(g,k.dom.isBlock);if(q=="IMG"){if(p=="full"){return}if(l){if(p=="center"){i.setStyle(h||g.parentNode,"textAlign","")}i.setStyle(g,"float","");this.mceRepaint();return}if(p=="center"){if(h&&/^(TD|TH)$/.test(h.nodeName)){h=0}if(!h||h.childNodes.length>1){j=i.create("p");j.appendChild(g.cloneNode(false));if(h){i.insertAfter(j,h)}else{i.insertAfter(j,g)}i.remove(g);g=j.firstChild;h=j}i.setStyle(h,"textAlign",p);i.setStyle(g,"float","")}else{i.setStyle(g,"float",p);i.setStyle(h||g.parentNode,"textAlign","")}this.mceRepaint();return}if(k.settings.inline_styles&&k.settings.forced_root_block){if(l){p=""}f(m.getSelectedBlocks(i.getParent(m.getStart(),i.isBlock),i.getParent(m.getEnd(),i.isBlock)),function(n){i.setAttrib(n,"align","");i.setStyle(n,"textAlign",p=="full"?"justify":p)});return}else{if(!l){k.getDoc().execCommand(o,false,null)}}if(k.settings.inline_styles){if(l){i.getParent(k.selection.getNode(),function(r){if(r.style&&r.style.textAlign){i.setStyle(r,"textAlign","")}});return}f(i.select("*"),function(s){var r=s.align;if(r){if(r=="full"){r="justify"}i.setStyle(s,"textAlign",r);i.setAttrib(s,"align","")}})}},mceSetCSSClass:function(h,g){this.mceSetStyleInfo(0,{command:"setattrib",name:"class",value:g})},getSelectedElement:function(){var w=this,o=w.editor,n=o.dom,s=o.selection,h=s.getRng(),l,k,u,p,j,g,q,i,x,v;if(s.isCollapsed()||h.item){return s.getNode()}v=o.settings.merge_styles_invalid_parents;if(d.is(v,"string")){v=new RegExp(v,"i")}if(c){l=h.duplicate();l.collapse(true);u=l.parentElement();k=h.duplicate();k.collapse(false);p=k.parentElement();if(u!=p){l.move("character",1);u=l.parentElement()}if(u==p){l=h.duplicate();l.moveToElementText(u);if(l.compareEndPoints("StartToStart",h)==0&&l.compareEndPoints("EndToEnd",h)==0){return v&&v.test(u.nodeName)?null:u}}}else{function m(r){return n.getParent(r,"*")}u=h.startContainer;p=h.endContainer;j=h.startOffset;g=h.endOffset;if(!h.collapsed){if(u==p){if(j-g<2){if(u.hasChildNodes()){i=u.childNodes[j];return v&&v.test(i.nodeName)?null:i}}}}if(u.nodeType!=3||p.nodeType!=3){return null}if(j==0){i=m(u);if(i&&i.firstChild!=u){i=null}}if(j==u.nodeValue.length){q=u.nextSibling;if(q&&q.nodeType==1){i=u.nextSibling}}if(g==0){q=p.previousSibling;if(q&&q.nodeType==1){x=q}}if(g==p.nodeValue.length){x=m(p);if(x&&x.lastChild!=p){x=null}}if(i==x){return v&&i&&v.test(i.nodeName)?null:i}}return null},mceSetStyleInfo:function(n,m){var q=this,h=q.editor,j=h.getDoc(),g=h.dom,i,k,r=h.selection,p=m.wrapper||"span",k=r.getBookmark(),o;function l(t,s){if(t.nodeType==1){switch(m.command){case"setattrib":return g.setAttrib(t,m.name,m.value);case"setstyle":return g.setStyle(t,m.name,m.value);case"removeformat":return g.setAttrib(t,"class","")}}}o=h.settings.merge_styles_invalid_parents;if(d.is(o,"string")){o=new RegExp(o,"i")}if((i=q.getSelectedElement())&&!h.settings.force_span_wrappers){l(i,1)}else{j.execCommand("FontName",false,"__");f(g.select("span,font"),function(u){var s,t;if(g.getAttrib(u,"face")=="__"||u.style.fontFamily==="__"){s=g.create(p,{mce_new:"1"});l(s);f(u.childNodes,function(v){s.appendChild(v.cloneNode(true))});g.replace(s,u)}})}f(g.select(p).reverse(),function(t){var s=t.parentNode;if(!g.getAttrib(t,"mce_new")){s=g.getParent(t,"*[mce_new]");if(s){g.remove(t,1)}}});f(g.select(p).reverse(),function(t){var s=t.parentNode;if(!s||!g.getAttrib(t,"mce_new")){return}if(h.settings.force_span_wrappers&&s.nodeName!="SPAN"){return}if(s.nodeName==p.toUpperCase()&&s.childNodes.length==1){return g.remove(s,1)}if(t.nodeType==1&&(!o||!o.test(s.nodeName))&&s.childNodes.length==1){l(s);g.setAttrib(t,"class","")}});f(g.select(p).reverse(),function(s){if(g.getAttrib(s,"mce_new")||(g.getAttribs(s).length<=1&&s.className==="")){if(!g.getAttrib(s,"class")&&!g.getAttrib(s,"style")){return g.remove(s,1)}g.setAttrib(s,"mce_new","")}});r.moveToBookmark(k)},queryStateJustify:function(k,h){var g=this.editor,j=g.selection.getNode(),i=g.dom;if(j&&j.nodeName=="IMG"){if(i.getStyle(j,"float")==h){return 1}return j.parentNode.style.textAlign==h}j=i.getParent(g.selection.getStart(),function(l){return l.nodeType==1&&l.style.textAlign});if(h=="full"){h="justify"}if(g.settings.inline_styles){return(j&&j.style.textAlign==h)}return this._queryState(k)},ForeColor:function(i,h){var g=this.editor;if(g.settings.convert_fonts_to_spans){this._applyInlineStyle("span",{style:{color:h}});return}else{g.getDoc().execCommand("ForeColor",false,h)}},HiliteColor:function(i,k){var h=this,g=h.editor,j=g.getDoc();if(g.settings.convert_fonts_to_spans){this._applyInlineStyle("span",{style:{backgroundColor:k}});return}function l(n){if(!a){return}try{j.execCommand("styleWithCSS",0,n)}catch(m){j.execCommand("useCSS",0,!n)}}if(a||b){l(true);j.execCommand("hilitecolor",false,k);l(false)}else{j.execCommand("BackColor",false,k)}},FormatBlock:function(n,h){var o=this,l=o.editor,p=l.selection,j=l.dom,g,k,m;function i(q){return/^(P|DIV|H[1-6]|ADDRESS|BLOCKQUOTE|PRE)$/.test(q.nodeName)}g=j.getParent(p.getNode(),function(q){return i(q)});if(g){if((c&&i(g.parentNode))||g.nodeName=="DIV"){k=l.dom.create(h);f(j.getAttribs(g),function(q){j.setAttrib(k,q.nodeName,j.getAttrib(g,q.nodeName))});m=p.getBookmark();j.replace(k,g,1);p.moveToBookmark(m);l.nodeChanged();return}}h=l.settings.forced_root_block?(h||"<p>"):h;if(h.indexOf("<")==-1){h="<"+h+">"}if(d.isGecko){h=h.replace(/<(div|blockquote|code|dt|dd|dl|samp)>/gi,"$1")}l.getDoc().execCommand("FormatBlock",false,h)},mceCleanup:function(){var h=this.editor,i=h.selection,g=i.getBookmark();h.setContent(h.getContent());i.moveToBookmark(g)},mceRemoveNode:function(j,k){var h=this.editor,i=h.selection,g,l=k||i.getNode();if(l==h.getBody()){return}g=i.getBookmark();h.dom.remove(l,1);i.moveToBookmark(g);h.nodeChanged()},mceSelectNodeDepth:function(i,j){var g=this.editor,h=g.selection,k=0;g.dom.getParent(h.getNode(),function(l){if(l.nodeType==1&&k++==j){h.select(l);g.nodeChanged();return false}},g.getBody())},mceSelectNode:function(h,g){this.editor.selection.select(g)},mceInsertContent:function(g,h){this.editor.selection.setContent(h)},mceInsertRawHTML:function(h,i){var g=this.editor;g.selection.setContent("tiny_mce_marker");g.setContent(g.getContent().replace(/tiny_mce_marker/g,i))},mceRepaint:function(){var i,g,j=this.editor;if(d.isGecko){try{i=j.selection;g=i.getBookmark(true);if(i.getSel()){i.getSel().selectAllChildren(j.getBody())}i.collapse(true);i.moveToBookmark(g)}catch(h){}}},queryStateUnderline:function(){var g=this.editor,h=g.selection.getNode();if(h&&h.nodeName=="A"){return false}return this._queryState("Underline")},queryStateOutdent:function(){var g=this.editor,h;if(g.settings.inline_styles){if((h=g.dom.getParent(g.selection.getStart(),g.dom.isBlock))&&parseInt(h.style.paddingLeft)>0){return true}if((h=g.dom.getParent(g.selection.getEnd(),g.dom.isBlock))&&parseInt(h.style.paddingLeft)>0){return true}}return this.queryStateInsertUnorderedList()||this.queryStateInsertOrderedList()||(!g.settings.inline_styles&&!!g.dom.getParent(g.selection.getNode(),"BLOCKQUOTE"))},queryStateInsertUnorderedList:function(){return this.editor.dom.getParent(this.editor.selection.getNode(),"UL")},queryStateInsertOrderedList:function(){return this.editor.dom.getParent(this.editor.selection.getNode(),"OL")},queryStatemceBlockQuote:function(){return !!this.editor.dom.getParent(this.editor.selection.getStart(),function(g){return g.nodeName==="BLOCKQUOTE"})},_applyInlineStyle:function(o,j,m){var q=this,n=q.editor,l=n.dom,i,p={},k,r;o=o.toUpperCase();if(m&&m.check_classes&&j["class"]){m.check_classes.push(j["class"])}function h(){f(l.select(o).reverse(),function(t){var s=0;f(l.getAttribs(t),function(u){if(u.nodeName.substring(0,1)!="_"&&l.getAttrib(t,u.nodeName)!=""){s++}});if(s==0){l.remove(t,1)}})}function g(){var s;f(l.select("span,font"),function(t){if(t.style.fontFamily=="mceinline"||t.face=="mceinline"){if(!s){s=n.selection.getBookmark()}j._mce_new="1";l.replace(l.create(o,j),t,1)}});f(l.select(o+"[_mce_new]"),function(u){function t(v){if(v.nodeType==1){f(j.style,function(x,w){l.setStyle(v,w,"")});if(j["class"]&&v.className&&m){f(m.check_classes,function(w){if(l.hasClass(v,w)){l.removeClass(v,w)}})}}}f(l.select(o,u),t);if(u.parentNode&&u.parentNode.nodeType==1&&u.parentNode.childNodes.length==1){t(u.parentNode)}l.getParent(u.parentNode,function(v){if(v.nodeType==1){if(j.style){f(j.style,function(y,x){var w;if(!p[x]&&(w=l.getStyle(v,x))){if(w===y){l.setStyle(u,x,"")}p[x]=1}})}if(j["class"]&&v.className&&m){f(m.check_classes,function(w){if(l.hasClass(v,w)){l.removeClass(u,w)}})}}return false});u.removeAttribute("_mce_new")});h();n.selection.moveToBookmark(s);return !!s}n.focus();n.getDoc().execCommand("FontName",false,"mceinline");g();if(k=q._applyInlineStyle.keyhandler){n.onKeyUp.remove(k);n.onKeyPress.remove(k);n.onKeyDown.remove(k);n.onSetContent.remove(q._applyInlineStyle.chandler)}if(n.selection.isCollapsed()){if(!c){f(l.getParents(n.selection.getNode(),"span"),function(s){f(j.style,function(u,t){var w;if(w=l.getStyle(s,t)){if(w==u){l.setStyle(s,t,"");r=2;return false}r=1;return false}});if(r){return false}});if(r==2){i=n.selection.getBookmark();h();n.selection.moveToBookmark(i);window.setTimeout(function(){n.nodeChanged()},1);return}}q._pendingStyles=d.extend(q._pendingStyles||{},j.style);q._applyInlineStyle.chandler=n.onSetContent.add(function(){delete q._pendingStyles});q._applyInlineStyle.keyhandler=k=function(s){if(q._pendingStyles){j.style=q._pendingStyles;delete q._pendingStyles}if(g()){n.onKeyDown.remove(q._applyInlineStyle.keyhandler);n.onKeyPress.remove(q._applyInlineStyle.keyhandler)}if(s.type=="keyup"){n.onKeyUp.remove(q._applyInlineStyle.keyhandler)}};n.onKeyDown.add(k);n.onKeyPress.add(k);n.onKeyUp.add(k)}else{q._pendingStyles=0}}})})(tinymce);(function(a){a.create("tinymce.UndoManager",{index:0,data:null,typing:0,UndoManager:function(c){var d=this,b=a.util.Dispatcher;d.editor=c;d.data=[];d.onAdd=new b(this);d.onUndo=new b(this);d.onRedo=new b(this)},add:function(d){var g=this,f,e=g.editor,c,h=e.settings,j;d=d||{};d.content=d.content||e.getContent({format:"raw",no_events:1});d.content=d.content.replace(/^\s*|\s*$/g,"");j=g.data[g.index>0&&(g.index==0||g.index==g.data.length)?g.index-1:g.index];if(!d.initial&&j&&d.content==j.content){return null}if(h.custom_undo_redo_levels){if(g.data.length>h.custom_undo_redo_levels){for(f=0;f<g.data.length-1;f++){g.data[f]=g.data[f+1]}g.data.length--;g.index=g.data.length}}if(h.custom_undo_redo_restore_selection&&!d.initial){d.bookmark=c=d.bookmark||e.selection.getBookmark()}if(g.index<g.data.length){g.index++}if(g.data.length===0&&!d.initial){return null}g.data.length=g.index+1;g.data[g.index++]=d;if(d.initial){g.index=0}if(g.data.length==2&&g.data[0].initial){g.data[0].bookmark=c}g.onAdd.dispatch(g,d);e.isNotDirty=0;return d},undo:function(){var e=this,c=e.editor,b=b,d;if(e.typing){e.add();e.typing=0}if(e.index>0){if(e.index==e.data.length&&e.index>1){d=e.index;e.typing=0;if(!e.add()){e.index=d}--e.index}b=e.data[--e.index];c.setContent(b.content,{format:"raw"});c.selection.moveToBookmark(b.bookmark);e.onUndo.dispatch(e,b)}return b},redo:function(){var d=this,c=d.editor,b=null;if(d.index<d.data.length-1){b=d.data[++d.index];c.setContent(b.content,{format:"raw"});c.selection.moveToBookmark(b.bookmark);d.onRedo.dispatch(d,b)}return b},clear:function(){var b=this;b.data=[];b.index=0;b.typing=0;b.add({initial:true})},hasUndo:function(){return this.index!=0||this.typing},hasRedo:function(){return this.index<this.data.length-1}})})(tinymce);(function(i){var h,c,a,b,g,f;h=i.dom.Event;c=i.isIE;a=i.isGecko;b=i.isOpera;g=i.each;f=i.extend;function e(k,l){var j=l.ownerDocument.createRange();j.setStart(k.endContainer,k.endOffset);j.setEndAfter(l);return j.cloneContents().textContent.length==0}function d(j){j=j.innerHTML;j=j.replace(/<(img|hr|table|input|select|textarea)[ \>]/gi,"-");j=j.replace(/<[^>]+>/g,"");return j.replace(/[ \t\r\n]+/g,"")==""}i.create("tinymce.ForceBlocks",{ForceBlocks:function(k){var l=this,m=k.settings,n;l.editor=k;l.dom=k.dom;n=(m.forced_root_block||"p").toLowerCase();m.element=n.toUpperCase();k.onPreInit.add(l.setup,l);l.reOpera=new RegExp("(\\u00a0|&#160;|&nbsp;)</"+n+">","gi");l.rePadd=new RegExp("<p( )([^>]+)><\\/p>|<p( )([^>]+)\\/>|<p( )([^>]+)>\\s+<\\/p>|<p><\\/p>|<p\\/>|<p>\\s+<\\/p>".replace(/p/g,n),"gi");l.reNbsp2BR1=new RegExp("<p( )([^>]+)>[\\s\\u00a0]+<\\/p>|<p>[\\s\\u00a0]+<\\/p>".replace(/p/g,n),"gi");l.reNbsp2BR2=new RegExp("<%p()([^>]+)>(&nbsp;|&#160;)<\\/%p>|<%p>(&nbsp;|&#160;)<\\/%p>".replace(/%p/g,n),"gi");l.reBR2Nbsp=new RegExp("<p( )([^>]+)>\\s*<br \\/>\\s*<\\/p>|<p>\\s*<br \\/>\\s*<\\/p>".replace(/p/g,n),"gi");function j(p,q){if(b){q.content=q.content.replace(l.reOpera,"</"+n+">")}q.content=q.content.replace(l.rePadd,"<"+n+"$1$2$3$4$5$6>\u00a0</"+n+">");if(!c&&!b&&q.set){q.content=q.content.replace(l.reNbsp2BR1,"<"+n+"$1$2><br /></"+n+">");q.content=q.content.replace(l.reNbsp2BR2,"<"+n+"$1$2><br /></"+n+">")}else{q.content=q.content.replace(l.reBR2Nbsp,"<"+n+"$1$2>\u00a0</"+n+">")}}k.onBeforeSetContent.add(j);k.onPostProcess.add(j);if(m.forced_root_block){k.onInit.add(l.forceRoots,l);k.onSetContent.add(l.forceRoots,l);k.onBeforeGetContent.add(l.forceRoots,l)}},setup:function(){var k=this,j=k.editor,l=j.settings;if(l.forced_root_block){j.onKeyUp.add(k.forceRoots,k);j.onPreProcess.add(k.forceRoots,k)}if(l.force_br_newlines){if(c){j.onKeyPress.add(function(o,q){var r,p=o.selection;if(q.keyCode==13&&p.getNode().nodeName!="LI"){p.setContent('<br id="__" /> ',{format:"raw"});r=o.dom.get("__");r.removeAttribute("id");p.select(r);p.collapse();return h.cancel(q)}})}return}if(!c&&l.force_p_newlines){j.onKeyPress.add(function(n,o){if(o.keyCode==13&&!o.shiftKey){if(!k.insertPara(o)){h.cancel(o)}}});if(a){j.onKeyDown.add(function(n,o){if((o.keyCode==8||o.keyCode==46)&&!o.shiftKey){k.backspaceDelete(o,o.keyCode==8)}})}}function m(o,n){var p=j.dom.create(n);g(o.attributes,function(q){if(q.specified&&q.nodeValue){p.setAttribute(q.nodeName.toLowerCase(),q.nodeValue)}});g(o.childNodes,function(q){p.appendChild(q.cloneNode(true))});o.parentNode.replaceChild(p,o);return p}j.onPreProcess.add(function(n,p){g(n.dom.select("p,h1,h2,h3,h4,h5,h6,div",p.node),function(o){if(d(o)){g(n.dom.select("span,em,strong,b,i",p.node),function(q){if(!q.hasChildNodes()){q.appendChild(n.getDoc().createTextNode("\u00a0"));return false}})}})});if(c){if(l.element!="P"){j.onKeyPress.add(function(n,o){k.lastElm=n.selection.getNode().nodeName});j.onKeyUp.add(function(p,r){var t,q=p.selection,s=q.getNode(),o=p.getBody();if(o.childNodes.length===1&&s.nodeName=="P"){s=m(s,l.element);q.select(s);q.collapse();p.nodeChanged()}else{if(r.keyCode==13&&!r.shiftKey&&k.lastElm!="P"){t=p.dom.getParent(s,"p");if(t){m(t,l.element);p.nodeChanged()}}}})}}},find:function(p,l,m){var k=this.editor,j=k.getDoc().createTreeWalker(p,4,null,false),o=-1;while(p=j.nextNode()){o++;if(l==0&&p==m){return o}if(l==1&&o==m){return p}}return -1},forceRoots:function(p,D){var u=this,p=u.editor,H=p.getBody(),E=p.getDoc(),K=p.selection,v=K.getSel(),w=K.getRng(),I=-2,o,B,j,k,F=-16777215;var G,l,J,A,x,m=H.childNodes,z,y,q;for(z=m.length-1;z>=0;z--){G=m[z];if(G.nodeType===3||(!u.dom.isBlock(G)&&G.nodeType!==8&&!/^(script|mce:script|style|mce:style)$/i.test(G.nodeName))){if(!l){if(G.nodeType!=3||/[^\s]/g.test(G.nodeValue)){if(I==-2&&w){if(!c){if(w.startContainer.nodeType==1&&(y=w.startContainer.childNodes[w.startOffset])&&y.nodeType==1){q=y.getAttribute("id");y.setAttribute("id","__mce")}else{if(p.dom.getParent(w.startContainer,function(n){return n===H})){B=w.startOffset;j=w.endOffset;I=u.find(H,0,w.startContainer);o=u.find(H,0,w.endContainer)}}}else{k=E.body.createTextRange();k.moveToElementText(H);k.collapse(1);J=k.move("character",F)*-1;k=w.duplicate();k.collapse(1);A=k.move("character",F)*-1;k=w.duplicate();k.collapse(0);x=(k.move("character",F)*-1)-A;I=A-J;o=x}}l=p.dom.create(p.settings.forced_root_block);G.parentNode.replaceChild(l,G);l.appendChild(G)}}else{if(l.hasChildNodes()){l.insertBefore(G,l.firstChild)}else{l.appendChild(G)}}}else{l=null}}if(I!=-2){if(!c){l=H.getElementsByTagName(p.settings.element)[0];w=E.createRange();if(I!=-1){w.setStart(u.find(H,1,I),B)}else{w.setStart(l,0)}if(o!=-1){w.setEnd(u.find(H,1,o),j)}else{w.setEnd(l,0)}if(v){v.removeAllRanges();v.addRange(w)}}else{try{w=v.createRange();w.moveToElementText(H);w.collapse(1);w.moveStart("character",I);w.moveEnd("character",o);w.select()}catch(C){}}}else{if(!c&&(y=p.dom.get("__mce"))){if(q){y.setAttribute("id",q)}else{y.removeAttribute("id")}w=E.createRange();w.setStartBefore(y);w.setEndBefore(y);K.setRng(w)}}},getParentBlock:function(k){var j=this.dom;return j.getParent(k,j.isBlock)},insertPara:function(N){var B=this,p=B.editor,J=p.dom,O=p.getDoc(),S=p.settings,C=p.selection.getSel(),D=C.getRangeAt(0),R=O.body;var G,H,E,L,K,m,k,o,u,j,z,Q,l,q,F,I=J.getViewPort(p.getWin()),x,A,w;G=O.createRange();G.setStart(C.anchorNode,C.anchorOffset);G.collapse(true);H=O.createRange();H.setStart(C.focusNode,C.focusOffset);H.collapse(true);E=G.compareBoundaryPoints(G.START_TO_END,H)<0;L=E?C.anchorNode:C.focusNode;K=E?C.anchorOffset:C.focusOffset;m=E?C.focusNode:C.anchorNode;k=E?C.focusOffset:C.anchorOffset;if(L===m&&/^(TD|TH)$/.test(L.nodeName)){if(L.firstChild.nodeName=="BR"){J.remove(L.firstChild)}if(L.childNodes.length==0){p.dom.add(L,S.element,null,"<br />");Q=p.dom.add(L,S.element,null,"<br />")}else{F=L.innerHTML;L.innerHTML="";p.dom.add(L,S.element,null,F);Q=p.dom.add(L,S.element,null,"<br />")}D=O.createRange();D.selectNodeContents(Q);D.collapse(1);p.selection.setRng(D);return false}if(L==R&&m==R&&R.firstChild&&p.dom.isBlock(R.firstChild)){L=m=L.firstChild;K=k=0;G=O.createRange();G.setStart(L,0);H=O.createRange();H.setStart(m,0)}L=L.nodeName=="HTML"?O.body:L;L=L.nodeName=="BODY"?L.firstChild:L;m=m.nodeName=="HTML"?O.body:m;m=m.nodeName=="BODY"?m.firstChild:m;o=B.getParentBlock(L);u=B.getParentBlock(m);j=o?o.nodeName:S.element;if(B.dom.getParent(o,"ol,ul,pre")){return true}if(o&&(o.nodeName=="CAPTION"||/absolute|relative|fixed/gi.test(J.getStyle(o,"position",1)))){j=S.element;o=null}if(u&&(u.nodeName=="CAPTION"||/absolute|relative|fixed/gi.test(J.getStyle(o,"position",1)))){j=S.element;u=null}if(/(TD|TABLE|TH|CAPTION)/.test(j)||(o&&j=="DIV"&&/left|right/gi.test(J.getStyle(o,"float",1)))){j=S.element;o=u=null}z=(o&&o.nodeName==j)?o.cloneNode(0):p.dom.create(j);Q=(u&&u.nodeName==j)?u.cloneNode(0):p.dom.create(j);Q.removeAttribute("id");if(/^(H[1-6])$/.test(j)&&e(D,o)){Q=p.dom.create(S.element)}F=l=L;do{if(F==R||F.nodeType==9||B.dom.isBlock(F)||/(TD|TABLE|TH|CAPTION)/.test(F.nodeName)){break}l=F}while((F=F.previousSibling?F.previousSibling:F.parentNode));F=q=m;do{if(F==R||F.nodeType==9||B.dom.isBlock(F)||/(TD|TABLE|TH|CAPTION)/.test(F.nodeName)){break}q=F}while((F=F.nextSibling?F.nextSibling:F.parentNode));if(l.nodeName==j){G.setStart(l,0)}else{G.setStartBefore(l)}G.setEnd(L,K);z.appendChild(G.cloneContents()||O.createTextNode(""));try{H.setEndAfter(q)}catch(M){}H.setStart(m,k);Q.appendChild(H.cloneContents()||O.createTextNode(""));D=O.createRange();if(!l.previousSibling&&l.parentNode.nodeName==j){D.setStartBefore(l.parentNode)}else{if(G.startContainer.nodeName==j&&G.startOffset==0){D.setStartBefore(G.startContainer)}else{D.setStart(G.startContainer,G.startOffset)}}if(!q.nextSibling&&q.parentNode.nodeName==j){D.setEndAfter(q.parentNode)}else{D.setEnd(H.endContainer,H.endOffset)}D.deleteContents();if(b){p.getWin().scrollTo(0,I.y)}if(z.firstChild&&z.firstChild.nodeName==j){z.innerHTML=z.firstChild.innerHTML}if(Q.firstChild&&Q.firstChild.nodeName==j){Q.innerHTML=Q.firstChild.innerHTML}if(d(z)){z.innerHTML="<br />"}function P(y,s){var r=[],U,T,t;y.innerHTML="";if(S.keep_styles){T=s;do{if(/^(SPAN|STRONG|B|EM|I|FONT|STRIKE|U)$/.test(T.nodeName)){U=T.cloneNode(false);J.setAttrib(U,"id","");r.push(U)}}while(T=T.parentNode)}if(r.length>0){for(t=r.length-1,U=y;t>=0;t--){U=U.appendChild(r[t])}r[0].innerHTML=b?"&nbsp;":"<br />";return r[0]}else{y.innerHTML=b?"&nbsp;":"<br />"}}if(d(Q)){w=P(Q,m)}if(b&&parseFloat(opera.version())<9.5){D.insertNode(z);D.insertNode(Q)}else{D.insertNode(Q);D.insertNode(z)}Q.normalize();z.normalize();function v(r){return O.createTreeWalker(r,NodeFilter.SHOW_TEXT,null,false).nextNode()||r}D=O.createRange();D.selectNodeContents(a?v(w||Q):w||Q);D.collapse(1);C.removeAllRanges();C.addRange(D);x=p.dom.getPos(Q).y;A=Q.clientHeight;if(x<I.y||x+A>I.y+I.h){p.getWin().scrollTo(0,x<I.y?x:x-I.h+25)}return false},backspaceDelete:function(o,x){var z=this,m=z.editor,s=m.getBody(),l=m.dom,k,p=m.selection,j=p.getRng(),q=j.startContainer,k,u,v;if(q&&m.dom.isBlock(q)&&!/^(TD|TH)$/.test(q.nodeName)&&x){if(q.childNodes.length==0||(q.childNodes.length==1&&q.firstChild.nodeName=="BR")){k=q;while((k=k.previousSibling)&&!m.dom.isBlock(k)){}if(k){if(q!=s.firstChild){u=m.dom.doc.createTreeWalker(k,NodeFilter.SHOW_TEXT,null,false);while(v=u.nextNode()){k=v}j=m.getDoc().createRange();j.setStart(k,k.nodeValue?k.nodeValue.length:0);j.setEnd(k,k.nodeValue?k.nodeValue.length:0);p.setRng(j);m.dom.remove(q)}return h.cancel(o)}}}function y(n){var r;n=n.target;if(n&&n.parentNode&&n.nodeName=="BR"&&(k=z.getParentBlock(n))){r=n.previousSibling;h.remove(s,"DOMNodeInserted",y);if(r&&r.nodeType==3&&/\s+$/.test(r.nodeValue)){return}if(n.previousSibling||n.nextSibling){m.dom.remove(n)}}}h._add(s,"DOMNodeInserted",y);window.setTimeout(function(){h._remove(s,"DOMNodeInserted",y)},1)}})})(tinymce);(function(c){var b=c.DOM,a=c.dom.Event,d=c.each,e=c.extend;c.create("tinymce.ControlManager",{ControlManager:function(f,j){var h=this,g;j=j||{};h.editor=f;h.controls={};h.onAdd=new c.util.Dispatcher(h);h.onPostRender=new c.util.Dispatcher(h);h.prefix=j.prefix||f.id+"_";h._cls={};h.onPostRender.add(function(){d(h.controls,function(i){i.postRender()})})},get:function(f){return this.controls[this.prefix+f]||this.controls[f]},setActive:function(h,f){var g=null;if(g=this.get(h)){g.setActive(f)}return g},setDisabled:function(h,f){var g=null;if(g=this.get(h)){g.setDisabled(f)}return g},add:function(g){var f=this;if(g){f.controls[g.id]=g;f.onAdd.dispatch(g,f)}return g},createControl:function(i){var h,g=this,f=g.editor;d(f.plugins,function(j){if(j.createControl){h=j.createControl(i,g);if(h){return false}}});switch(i){case"|":case"separator":return g.createSeparator()}if(!h&&f.buttons&&(h=f.buttons[i])){return g.createButton(i,h)}return g.add(h)},createDropMenu:function(f,n,h){var m=this,i=m.editor,j,g,k,l;n=e({"class":"mceDropDown",constrain:i.settings.constrain_menus},n);n["class"]=n["class"]+" "+i.getParam("skin")+"Skin";if(k=i.getParam("skin_variant")){n["class"]+=" "+i.getParam("skin")+"Skin"+k.substring(0,1).toUpperCase()+k.substring(1)}f=m.prefix+f;l=h||m._cls.dropmenu||c.ui.DropMenu;j=m.controls[f]=new l(f,n);j.onAddItem.add(function(r,q){var p=q.settings;p.title=i.getLang(p.title,p.title);if(!p.onclick){p.onclick=function(o){i.execCommand(p.cmd,p.ui||false,p.value)}}});i.onRemove.add(function(){j.destroy()});if(c.isIE){j.onShowMenu.add(function(){i.focus();g=i.selection.getBookmark(1)});j.onHideMenu.add(function(){if(g){i.selection.moveToBookmark(g);g=0}})}return m.add(j)},createListBox:function(m,i,l){var h=this,g=h.editor,j,k,f;if(h.get(m)){return null}i.title=g.translate(i.title);i.scope=i.scope||g;if(!i.onselect){i.onselect=function(n){g.execCommand(i.cmd,i.ui||false,n||i.value)}}i=e({title:i.title,"class":"mce_"+m,scope:i.scope,control_manager:h},i);m=h.prefix+m;if(g.settings.use_native_selects){k=new c.ui.NativeListBox(m,i)}else{f=l||h._cls.listbox||c.ui.ListBox;k=new f(m,i)}h.controls[m]=k;if(c.isWebKit){k.onPostRender.add(function(p,o){a.add(o,"mousedown",function(){g.bookmark=g.selection.getBookmark(1)});a.add(o,"focus",function(){g.selection.moveToBookmark(g.bookmark);g.bookmark=null})})}if(k.hideMenu){g.onMouseDown.add(k.hideMenu,k)}return h.add(k)},createButton:function(m,i,l){var h=this,g=h.editor,j,k,f;if(h.get(m)){return null}i.title=g.translate(i.title);i.label=g.translate(i.label);i.scope=i.scope||g;if(!i.onclick&&!i.menu_button){i.onclick=function(){g.execCommand(i.cmd,i.ui||false,i.value)}}i=e({title:i.title,"class":"mce_"+m,unavailable_prefix:g.getLang("unavailable",""),scope:i.scope,control_manager:h},i);m=h.prefix+m;if(i.menu_button){f=l||h._cls.menubutton||c.ui.MenuButton;k=new f(m,i);g.onMouseDown.add(k.hideMenu,k)}else{f=h._cls.button||c.ui.Button;k=new f(m,i)}return h.add(k)},createMenuButton:function(h,f,g){f=f||{};f.menu_button=1;return this.createButton(h,f,g)},createSplitButton:function(m,i,l){var h=this,g=h.editor,j,k,f;if(h.get(m)){return null}i.title=g.translate(i.title);i.scope=i.scope||g;if(!i.onclick){i.onclick=function(n){g.execCommand(i.cmd,i.ui||false,n||i.value)}}if(!i.onselect){i.onselect=function(n){g.execCommand(i.cmd,i.ui||false,n||i.value)}}i=e({title:i.title,"class":"mce_"+m,scope:i.scope,control_manager:h},i);m=h.prefix+m;f=l||h._cls.splitbutton||c.ui.SplitButton;k=h.add(new f(m,i));g.onMouseDown.add(k.hideMenu,k);return k},createColorSplitButton:function(f,n,h){var l=this,j=l.editor,i,k,m,g;if(l.get(f)){return null}n.title=j.translate(n.title);n.scope=n.scope||j;if(!n.onclick){n.onclick=function(o){if(c.isIE){g=j.selection.getBookmark(1)}j.execCommand(n.cmd,n.ui||false,o||n.value)}}if(!n.onselect){n.onselect=function(o){j.execCommand(n.cmd,n.ui||false,o||n.value)}}n=e({title:n.title,"class":"mce_"+f,menu_class:j.getParam("skin")+"Skin",scope:n.scope,more_colors_title:j.getLang("more_colors")},n);f=l.prefix+f;m=h||l._cls.colorsplitbutton||c.ui.ColorSplitButton;k=new m(f,n);j.onMouseDown.add(k.hideMenu,k);j.onRemove.add(function(){k.destroy()});if(c.isIE){k.onShowMenu.add(function(){j.focus();g=j.selection.getBookmark(1)});k.onHideMenu.add(function(){if(g){j.selection.moveToBookmark(g);g=0}})}return l.add(k)},createToolbar:function(k,h,j){var i,g=this,f;k=g.prefix+k;f=j||g._cls.toolbar||c.ui.Toolbar;i=new f(k,h);if(g.get(k)){return null}return g.add(i)},createSeparator:function(g){var f=g||this._cls.separator||c.ui.Separator;return new f()},setControlType:function(g,f){return this._cls[g.toLowerCase()]=f},destroy:function(){d(this.controls,function(f){f.destroy()});this.controls=null}})})(tinymce);(function(d){var a=d.util.Dispatcher,e=d.each,c=d.isIE,b=d.isOpera;d.create("tinymce.WindowManager",{WindowManager:function(f){var g=this;g.editor=f;g.onOpen=new a(g);g.onClose=new a(g);g.params={};g.features={}},open:function(z,h){var v=this,k="",n,m,i=v.editor.settings.dialog_type=="modal",q,o,j,g=d.DOM.getViewPort(),r;z=z||{};h=h||{};o=b?g.w:screen.width;j=b?g.h:screen.height;z.name=z.name||"mc_"+new Date().getTime();z.width=parseInt(z.width||320);z.height=parseInt(z.height||240);z.resizable=true;z.left=z.left||parseInt(o/2)-(z.width/2);z.top=z.top||parseInt(j/2)-(z.height/2);h.inline=false;h.mce_width=z.width;h.mce_height=z.height;h.mce_auto_focus=z.auto_focus;if(i){if(c){z.center=true;z.help=false;z.dialogWidth=z.width+"px";z.dialogHeight=z.height+"px";z.scroll=z.scrollbars||false}}e(z,function(p,f){if(d.is(p,"boolean")){p=p?"yes":"no"}if(!/^(name|url)$/.test(f)){if(c&&i){k+=(k?";":"")+f+":"+p}else{k+=(k?",":"")+f+"="+p}}});v.features=z;v.params=h;v.onOpen.dispatch(v,z,h);r=z.url||z.file;r=d._addVer(r);try{if(c&&i){q=1;window.showModalDialog(r,window,k)}else{q=window.open(r,z.name,k)}}catch(l){}if(!q){alert(v.editor.getLang("popup_blocked"))}},close:function(f){f.close();this.onClose.dispatch(this)},createInstance:function(i,h,g,m,l,k){var j=d.resolve(i);return new j(h,g,m,l,k)},confirm:function(h,f,i,g){g=g||window;f.call(i||this,g.confirm(this._decode(this.editor.getLang(h,h))))},alert:function(h,f,j,g){var i=this;g=g||window;g.alert(i._decode(i.editor.getLang(h,h)));if(f){f.call(j||i)}},_decode:function(f){return d.DOM.decode(f).replace(/\\n/g,"\n")}})}(tinymce));(function(a){a.CommandManager=function(){var c={},b={},d={};function e(i,h,g,f){if(typeof(h)=="string"){h=[h]}a.each(h,function(j){i[j.toLowerCase()]={func:g,scope:f}})}a.extend(this,{add:function(h,g,f){e(c,h,g,f)},addQueryStateHandler:function(h,g,f){e(b,h,g,f)},addQueryValueHandler:function(h,g,f){e(d,h,g,f)},execCommand:function(g,j,i,h,f){if(j=c[j.toLowerCase()]){if(j.func.call(g||j.scope,i,h,f)!==false){return true}}},queryCommandValue:function(){if(cmd=d[cmd.toLowerCase()]){return cmd.func.call(scope||cmd.scope,ui,value,args)}},queryCommandState:function(){if(cmd=b[cmd.toLowerCase()]){return cmd.func.call(scope||cmd.scope,ui,value,args)}}})};a.GlobalCommands=new a.CommandManager()})(tinymce);(function(b){function a(i,d,h,m){var j,g,e,l,f;function k(p,o){do{if(p.parentNode==o){return p}p=p.parentNode}while(p)}function c(o){m(o);b.walk(o,m,"childNodes")}j=i.findCommonAncestor(d,h);e=k(d,j)||d;l=k(h,j)||h;for(g=d;g&&g!=e;g=g.parentNode){for(f=g.nextSibling;f;f=f.nextSibling){c(f)}}if(e!=l){for(g=e.nextSibling;g&&g!=l;g=g.nextSibling){c(g)}}else{c(e)}for(g=h;g&&g!=l;g=g.parentNode){for(f=g.previousSibling;f;f=f.previousSibling){c(f)}}}b.GlobalCommands.add("RemoveFormat",function(){var m=this,l=m.dom,u=m.selection,d=u.getRng(1),e=[],h,f,j,q,g,o,c,i;function k(s){var r;l.getParent(s,function(v){if(l.is(v,m.getParam("removeformat_selector"))){r=v}return l.isBlock(v)},m.getBody());return r}function p(r){if(l.is(r,m.getParam("removeformat_selector"))){e.push(r)}}function t(r){p(r);b.walk(r,p,"childNodes")}h=u.getBookmark();q=d.startContainer;o=d.endContainer;g=d.startOffset;c=d.endOffset;q=q.nodeType==1?q.childNodes[Math.min(g,q.childNodes.length-1)]:q;o=o.nodeType==1?o.childNodes[Math.min(g==c?c:c-1,o.childNodes.length-1)]:o;if(q==o){f=k(q);if(q.nodeType==3){if(f&&f.nodeType==1){i=q.splitText(g);i.splitText(c-g);l.split(f,i);u.moveToBookmark(h)}return}t(l.split(f,q)||q)}else{f=k(q);j=k(o);if(f){if(q.nodeType==3){if(g==q.nodeValue.length){q.nodeValue+="\uFEFF"}q=q.splitText(g)}}if(j){if(o.nodeType==3){o.splitText(c)}}if(f&&f==j){l.replace(l.create("span",{id:"__end"},o.cloneNode(true)),o)}if(f){f=l.split(f,q)}else{f=q}if(i=l.get("__end")){o=i;j=k(o)}if(j){j=l.split(j,o)}else{j=o}a(l,f,j,p);if(q.nodeValue=="\uFEFF"){q.nodeValue=""}t(o);t(q)}b.each(e,function(r){l.remove(r,1)});l.remove("__end",1);u.moveToBookmark(h)})})(tinymce);(function(a){a.GlobalCommands.add("mceBlockQuote",function(){var j=this,o=j.selection,f=j.dom,l,k,e,d,p,c,m,h,b;function g(i){return f.getParent(i,function(q){return q.nodeName==="BLOCKQUOTE"})}l=f.getParent(o.getStart(),f.isBlock);k=f.getParent(o.getEnd(),f.isBlock);if(p=g(l)){if(l!=k||l.childNodes.length>1||(l.childNodes.length==1&&l.firstChild.nodeName!="BR")){d=o.getBookmark()}if(g(k)){m=p.cloneNode(false);while(e=k.nextSibling){m.appendChild(e.parentNode.removeChild(e))}}if(m){f.insertAfter(m,p)}b=o.getSelectedBlocks(l,k);for(h=b.length-1;h>=0;h--){f.insertAfter(b[h],p)}if(/^\s*$/.test(p.innerHTML)){f.remove(p,1)}if(m&&/^\s*$/.test(m.innerHTML)){f.remove(m,1)}if(!d){if(!a.isIE){c=j.getDoc().createRange();c.setStart(l,0);c.setEnd(l,0);o.setRng(c)}else{o.select(l);o.collapse(0);if(f.getParent(o.getStart(),f.isBlock)!=l){c=o.getRng();c.move("character",-1);c.select()}}}else{j.selection.moveToBookmark(d)}return}if(a.isIE&&!l&&!k){j.getDoc().execCommand("Indent");e=g(o.getNode());e.style.margin=e.dir="";return}if(!l||!k){return}if(l!=k||l.childNodes.length>1||(l.childNodes.length==1&&l.firstChild.nodeName!="BR")){d=o.getBookmark()}a.each(o.getSelectedBlocks(g(o.getStart()),g(o.getEnd())),function(i){if(i.nodeName=="BLOCKQUOTE"&&!p){p=i;return}if(!p){p=f.create("blockquote");i.parentNode.insertBefore(p,i)}if(i.nodeName=="BLOCKQUOTE"&&p){e=i.firstChild;while(e){p.appendChild(e.cloneNode(true));e=e.nextSibling}f.remove(i);return}p.appendChild(f.remove(i))});if(!d){if(!a.isIE){c=j.getDoc().createRange();c.setStart(l,0);c.setEnd(l,0);o.setRng(c)}else{o.select(l);o.collapse(1)}}else{o.moveToBookmark(d)}})})(tinymce);(function(a){a.each(["Cut","Copy","Paste"],function(b){a.GlobalCommands.add(b,function(){var c=this,e=c.getDoc();try{e.execCommand(b,false,null);if(!e.queryCommandEnabled(b)){throw"Error"}}catch(d){if(a.isGecko){c.windowManager.confirm(c.getLang("clipboard_msg"),function(f){if(f){open("http://www.mozilla.org/editor/midasdemo/securityprefs.html","_blank")}})}else{c.windowManager.alert(c.getLang("clipboard_no_support"))}}})})})(tinymce);(function(a){a.GlobalCommands.add("InsertHorizontalRule",function(){if(a.isOpera){return this.getDoc().execCommand("InsertHorizontalRule",false,"")}this.selection.setContent("<hr />")})})(tinymce);(function(){var a=tinymce.GlobalCommands;a.add(["mceEndUndoLevel","mceAddUndoLevel"],function(){this.undoManager.add()});a.add("Undo",function(){var b=this;if(b.settings.custom_undo_redo){b.undoManager.undo();b.nodeChanged();return true}return false});a.add("Redo",function(){var b=this;if(b.settings.custom_undo_redo){b.undoManager.redo();b.nodeChanged();return true}return false})})();
    \ No newline at end of file
    diff --git a/TinyMCE/tiny_mce/tiny_mce_popup.js b/TinyMCE/tiny_mce/tiny_mce_popup.js
    new file mode 100644
    index 0000000..c9bf1fe
    --- /dev/null
    +++ b/TinyMCE/tiny_mce/tiny_mce_popup.js
    @@ -0,0 +1,5 @@
    +
    +// Uncomment and change this document.domain value if you are loading the script cross subdomains
    +// document.domain = 'moxiecode.com';
    +
    +var tinymce=null,tinyMCEPopup,tinyMCE;tinyMCEPopup={init:function(){var b=this,a,c;a=b.getWin();tinymce=a.tinymce;tinyMCE=a.tinyMCE;b.editor=tinymce.EditorManager.activeEditor;b.params=b.editor.windowManager.params;b.features=b.editor.windowManager.features;b.dom=b.editor.windowManager.createInstance("tinymce.dom.DOMUtils",document);if(b.features.popup_css!==false){b.dom.loadCSS(b.features.popup_css||b.editor.settings.popup_css)}b.listeners=[];b.onInit={add:function(e,d){b.listeners.push({func:e,scope:d})}};b.isWindow=!b.getWindowArg("mce_inline");b.id=b.getWindowArg("mce_window_id");b.editor.windowManager.onOpen.dispatch(b.editor.windowManager,window)},getWin:function(){return(!window.frameElement&&window.dialogArguments)||opener||parent||top},getWindowArg:function(c,b){var a=this.params[c];return tinymce.is(a)?a:b},getParam:function(b,a){return this.editor.getParam(b,a)},getLang:function(b,a){return this.editor.getLang(b,a)},execCommand:function(d,c,e,b){b=b||{};b.skip_focus=1;this.restoreSelection();return this.editor.execCommand(d,c,e,b)},resizeToInnerSize:function(){var e=this,g,a=document.body,c=e.dom.getViewPort(window),d,f;d=e.getWindowArg("mce_width")-c.w;f=e.getWindowArg("mce_height")-c.h;if(e.isWindow){window.resizeBy(d,f)}else{e.editor.windowManager.resizeBy(d,f,e.id)}},executeOnLoad:function(s){this.onInit.add(function(){eval(s)})},storeSelection:function(){this.editor.windowManager.bookmark=tinyMCEPopup.editor.selection.getBookmark(1)},restoreSelection:function(){var a=tinyMCEPopup;if(!a.isWindow&&tinymce.isIE){a.editor.selection.moveToBookmark(a.editor.windowManager.bookmark)}},requireLangPack:function(){var b=this,a=b.getWindowArg("plugin_url")||b.getWindowArg("theme_url");if(a&&b.editor.settings.language&&b.features.translate_i18n!==false){a+="/langs/"+b.editor.settings.language+"_dlg.js";if(!tinymce.ScriptLoader.isDone(a)){document.write('<script type="text/javascript" src="'+tinymce._addVer(a)+'"><\/script>');tinymce.ScriptLoader.markDone(a)}}},pickColor:function(b,a){this.execCommand("mceColorPicker",true,{color:document.getElementById(a).value,func:function(e){document.getElementById(a).value=e;try{document.getElementById(a).onchange()}catch(d){}}})},openBrowser:function(a,c,b){tinyMCEPopup.restoreSelection();this.editor.execCallback("file_browser_callback",a,document.getElementById(a).value,c,window)},confirm:function(b,a,c){this.editor.windowManager.confirm(b,a,c,window)},alert:function(b,a,c){this.editor.windowManager.alert(b,a,c,window)},close:function(){var a=this;function b(){a.editor.windowManager.close(window);tinymce=tinyMCE=a.editor=a.params=a.dom=a.dom.doc=null}if(tinymce.isOpera){a.getWin().setTimeout(b,0)}else{b()}},_restoreSelection:function(){var a=window.event.srcElement;if(a.nodeName=="INPUT"&&(a.type=="submit"||a.type=="button")){tinyMCEPopup.restoreSelection()}},_onDOMLoaded:function(){var b=tinyMCEPopup,d=document.title,e,c,a;if(b.domLoaded){return}b.domLoaded=1;if(b.features.translate_i18n!==false){c=document.body.innerHTML;if(tinymce.isIE){c=c.replace(/ (value|title|alt)=([^"][^\s>]+)/gi,' $1="$2"')}document.dir=b.editor.getParam("directionality","");if((a=b.editor.translate(c))&&a!=c){document.body.innerHTML=a}if((a=b.editor.translate(d))&&a!=d){document.title=d=a}}document.body.style.display="";if(tinymce.isIE){document.attachEvent("onmouseup",tinyMCEPopup._restoreSelection);b.dom.add(b.dom.select("head")[0],"base",{target:"_self"})}b.restoreSelection();b.resizeToInnerSize();if(!b.isWindow){b.editor.windowManager.setTitle(window,d)}else{window.focus()}if(!tinymce.isIE&&!b.isWindow){tinymce.dom.Event._add(document,"focus",function(){b.editor.windowManager.focus(b.id)})}tinymce.each(b.dom.select("select"),function(f){f.onkeydown=tinyMCEPopup._accessHandler});tinymce.each(b.listeners,function(f){f.func.call(f.scope,b.editor)});if(b.getWindowArg("mce_auto_focus",true)){window.focus();tinymce.each(document.forms,function(g){tinymce.each(g.elements,function(f){if(b.dom.hasClass(f,"mceFocus")&&!f.disabled){f.focus();return false}})})}document.onkeyup=tinyMCEPopup._closeWinKeyHandler},_accessHandler:function(a){a=a||window.event;if(a.keyCode==13||a.keyCode==32){a=a.target||a.srcElement;if(a.onchange){a.onchange()}return tinymce.dom.Event.cancel(a)}},_closeWinKeyHandler:function(a){a=a||window.event;if(a.keyCode==27){tinyMCEPopup.close()}},_wait:function(){if(document.attachEvent){document.attachEvent("onreadystatechange",function(){if(document.readyState==="complete"){document.detachEvent("onreadystatechange",arguments.callee);tinyMCEPopup._onDOMLoaded()}});if(document.documentElement.doScroll&&window==window.top){(function(){if(tinyMCEPopup.domLoaded){return}try{document.documentElement.doScroll("left")}catch(a){setTimeout(arguments.callee,0);return}tinyMCEPopup._onDOMLoaded()})()}document.attachEvent("onload",tinyMCEPopup._onDOMLoaded)}else{if(document.addEventListener){window.addEventListener("DOMContentLoaded",tinyMCEPopup._onDOMLoaded,false);window.addEventListener("load",tinyMCEPopup._onDOMLoaded,false)}}}};tinyMCEPopup.init();tinyMCEPopup._wait();
    \ No newline at end of file
    diff --git a/TinyMCE/tiny_mce/utils/editable_selects.js b/TinyMCE/tiny_mce/utils/editable_selects.js
    new file mode 100644
    index 0000000..c1bfa73
    --- /dev/null
    +++ b/TinyMCE/tiny_mce/utils/editable_selects.js
    @@ -0,0 +1,69 @@
    +/**
    + * $Id: editable_selects.js 867 2008-06-09 20:33:40Z spocke $
    + *
    + * Makes select boxes editable.
    + *
    + * @author Moxiecode
    + * @copyright Copyright  2004-2008, Moxiecode Systems AB, All rights reserved.
    + */
    +
    +var TinyMCE_EditableSelects = {
    +	editSelectElm : null,
    +
    +	init : function() {
    +		var nl = document.getElementsByTagName("select"), i, d = document, o;
    +
    +		for (i=0; i<nl.length; i++) {
    +			if (nl[i].className.indexOf('mceEditableSelect') != -1) {
    +				o = new Option('(value)', '__mce_add_custom__');
    +
    +				o.className = 'mceAddSelectValue';
    +
    +				nl[i].options[nl[i].options.length] = o;
    +				nl[i].onchange = TinyMCE_EditableSelects.onChangeEditableSelect;
    +			}
    +		}
    +	},
    +
    +	onChangeEditableSelect : function(e) {
    +		var d = document, ne, se = window.event ? window.event.srcElement : e.target;
    +
    +		if (se.options[se.selectedIndex].value == '__mce_add_custom__') {
    +			ne = d.createElement("input");
    +			ne.id = se.id + "_custom";
    +			ne.name = se.name + "_custom";
    +			ne.type = "text";
    +
    +			ne.style.width = se.offsetWidth + 'px';
    +			se.parentNode.insertBefore(ne, se);
    +			se.style.display = 'none';
    +			ne.focus();
    +			ne.onblur = TinyMCE_EditableSelects.onBlurEditableSelectInput;
    +			ne.onkeydown = TinyMCE_EditableSelects.onKeyDown;
    +			TinyMCE_EditableSelects.editSelectElm = se;
    +		}
    +	},
    +
    +	onBlurEditableSelectInput : function() {
    +		var se = TinyMCE_EditableSelects.editSelectElm;
    +
    +		if (se) {
    +			if (se.previousSibling.value != '') {
    +				addSelectValue(document.forms[0], se.id, se.previousSibling.value, se.previousSibling.value);
    +				selectByValue(document.forms[0], se.id, se.previousSibling.value);
    +			} else
    +				selectByValue(document.forms[0], se.id, '');
    +
    +			se.style.display = 'inline';
    +			se.parentNode.removeChild(se.previousSibling);
    +			TinyMCE_EditableSelects.editSelectElm = null;
    +		}
    +	},
    +
    +	onKeyDown : function(e) {
    +		e = e || window.event;
    +
    +		if (e.keyCode == 13)
    +			TinyMCE_EditableSelects.onBlurEditableSelectInput();
    +	}
    +};
    diff --git a/TinyMCE/tiny_mce/utils/form_utils.js b/TinyMCE/tiny_mce/utils/form_utils.js
    new file mode 100644
    index 0000000..f6cd96a
    --- /dev/null
    +++ b/TinyMCE/tiny_mce/utils/form_utils.js
    @@ -0,0 +1,199 @@
    +/**
    + * $Id: form_utils.js 1184 2009-08-11 11:47:27Z spocke $
    + *
    + * Various form utilitiy functions.
    + *
    + * @author Moxiecode
    + * @copyright Copyright  2004-2008, Moxiecode Systems AB, All rights reserved.
    + */
    +
    +var themeBaseURL = tinyMCEPopup.editor.baseURI.toAbsolute('themes/' + tinyMCEPopup.getParam("theme"));
    +
    +function getColorPickerHTML(id, target_form_element) {
    +	var h = "";
    +
    +	h += '<a id="' + id + '_link" href="javascript:;" onclick="tinyMCEPopup.pickColor(event,\'' + target_form_element +'\');" onmousedown="return false;" class="pickcolor">';
    +	h += '<span id="' + id + '" title="' + tinyMCEPopup.getLang('browse') + '">&nbsp;</span></a>';
    +
    +	return h;
    +}
    +
    +function updateColor(img_id, form_element_id) {
    +	document.getElementById(img_id).style.backgroundColor = document.forms[0].elements[form_element_id].value;
    +}
    +
    +function setBrowserDisabled(id, state) {
    +	var img = document.getElementById(id);
    +	var lnk = document.getElementById(id + "_link");
    +
    +	if (lnk) {
    +		if (state) {
    +			lnk.setAttribute("realhref", lnk.getAttribute("href"));
    +			lnk.removeAttribute("href");
    +			tinyMCEPopup.dom.addClass(img, 'disabled');
    +		} else {
    +			if (lnk.getAttribute("realhref"))
    +				lnk.setAttribute("href", lnk.getAttribute("realhref"));
    +
    +			tinyMCEPopup.dom.removeClass(img, 'disabled');
    +		}
    +	}
    +}
    +
    +function getBrowserHTML(id, target_form_element, type, prefix) {
    +	var option = prefix + "_" + type + "_browser_callback", cb, html;
    +
    +	cb = tinyMCEPopup.getParam(option, tinyMCEPopup.getParam("file_browser_callback"));
    +
    +	if (!cb)
    +		return "";
    +
    +	html = "";
    +	html += '<a id="' + id + '_link" href="javascript:openBrowser(\'' + id + '\',\'' + target_form_element + '\', \'' + type + '\',\'' + option + '\');" onmousedown="return false;" class="browse">';
    +	html += '<span id="' + id + '" title="' + tinyMCEPopup.getLang('browse') + '">&nbsp;</span></a>';
    +
    +	return html;
    +}
    +
    +function openBrowser(img_id, target_form_element, type, option) {
    +	var img = document.getElementById(img_id);
    +
    +	if (img.className != "mceButtonDisabled")
    +		tinyMCEPopup.openBrowser(target_form_element, type, option);
    +}
    +
    +function selectByValue(form_obj, field_name, value, add_custom, ignore_case) {
    +	if (!form_obj || !form_obj.elements[field_name])
    +		return;
    +
    +	var sel = form_obj.elements[field_name];
    +
    +	var found = false;
    +	for (var i=0; i<sel.options.length; i++) {
    +		var option = sel.options[i];
    +
    +		if (option.value == value || (ignore_case && option.value.toLowerCase() == value.toLowerCase())) {
    +			option.selected = true;
    +			found = true;
    +		} else
    +			option.selected = false;
    +	}
    +
    +	if (!found && add_custom && value != '') {
    +		var option = new Option(value, value);
    +		option.selected = true;
    +		sel.options[sel.options.length] = option;
    +		sel.selectedIndex = sel.options.length - 1;
    +	}
    +
    +	return found;
    +}
    +
    +function getSelectValue(form_obj, field_name) {
    +	var elm = form_obj.elements[field_name];
    +
    +	if (elm == null || elm.options == null || elm.selectedIndex === -1)
    +		return "";
    +
    +	return elm.options[elm.selectedIndex].value;
    +}
    +
    +function addSelectValue(form_obj, field_name, name, value) {
    +	var s = form_obj.elements[field_name];
    +	var o = new Option(name, value);
    +	s.options[s.options.length] = o;
    +}
    +
    +function addClassesToList(list_id, specific_option) {
    +	// Setup class droplist
    +	var styleSelectElm = document.getElementById(list_id);
    +	var styles = tinyMCEPopup.getParam('theme_advanced_styles', false);
    +	styles = tinyMCEPopup.getParam(specific_option, styles);
    +
    +	if (styles) {
    +		var stylesAr = styles.split(';');
    +
    +		for (var i=0; i<stylesAr.length; i++) {
    +			if (stylesAr != "") {
    +				var key, value;
    +
    +				key = stylesAr[i].split('=')[0];
    +				value = stylesAr[i].split('=')[1];
    +
    +				styleSelectElm.options[styleSelectElm.length] = new Option(key, value);
    +			}
    +		}
    +	} else {
    +		tinymce.each(tinyMCEPopup.editor.dom.getClasses(), function(o) {
    +			styleSelectElm.options[styleSelectElm.length] = new Option(o.title || o['class'], o['class']);
    +		});
    +	}
    +}
    +
    +function isVisible(element_id) {
    +	var elm = document.getElementById(element_id);
    +
    +	return elm && elm.style.display != "none";
    +}
    +
    +function convertRGBToHex(col) {
    +	var re = new RegExp("rgb\\s*\\(\\s*([0-9]+).*,\\s*([0-9]+).*,\\s*([0-9]+).*\\)", "gi");
    +
    +	var rgb = col.replace(re, "$1,$2,$3").split(',');
    +	if (rgb.length == 3) {
    +		r = parseInt(rgb[0]).toString(16);
    +		g = parseInt(rgb[1]).toString(16);
    +		b = parseInt(rgb[2]).toString(16);
    +
    +		r = r.length == 1 ? '0' + r : r;
    +		g = g.length == 1 ? '0' + g : g;
    +		b = b.length == 1 ? '0' + b : b;
    +
    +		return "#" + r + g + b;
    +	}
    +
    +	return col;
    +}
    +
    +function convertHexToRGB(col) {
    +	if (col.indexOf('#') != -1) {
    +		col = col.replace(new RegExp('[^0-9A-F]', 'gi'), '');
    +
    +		r = parseInt(col.substring(0, 2), 16);
    +		g = parseInt(col.substring(2, 4), 16);
    +		b = parseInt(col.substring(4, 6), 16);
    +
    +		return "rgb(" + r + "," + g + "," + b + ")";
    +	}
    +
    +	return col;
    +}
    +
    +function trimSize(size) {
    +	return size.replace(/([0-9\.]+)px|(%|in|cm|mm|em|ex|pt|pc)/, '$1$2');
    +}
    +
    +function getCSSSize(size) {
    +	size = trimSize(size);
    +
    +	if (size == "")
    +		return "";
    +
    +	// Add px
    +	if (/^[0-9]+$/.test(size))
    +		size += 'px';
    +
    +	return size;
    +}
    +
    +function getStyle(elm, attrib, style) {
    +	var val = tinyMCEPopup.dom.getAttrib(elm, attrib);
    +
    +	if (val != '')
    +		return '' + val;
    +
    +	if (typeof(style) == 'undefined')
    +		style = attrib;
    +
    +	return tinyMCEPopup.dom.getStyle(elm, style);
    +}
    diff --git a/TinyMCE/tiny_mce/utils/mctabs.js b/TinyMCE/tiny_mce/utils/mctabs.js
    new file mode 100644
    index 0000000..1351806
    --- /dev/null
    +++ b/TinyMCE/tiny_mce/utils/mctabs.js
    @@ -0,0 +1,76 @@
    +/**
    + * $Id: mctabs.js 758 2008-03-30 13:53:29Z spocke $
    + *
    + * Moxiecode DHTML Tabs script.
    + *
    + * @author Moxiecode
    + * @copyright Copyright  2004-2008, Moxiecode Systems AB, All rights reserved.
    + */
    +
    +function MCTabs() {
    +	this.settings = [];
    +};
    +
    +MCTabs.prototype.init = function(settings) {
    +	this.settings = settings;
    +};
    +
    +MCTabs.prototype.getParam = function(name, default_value) {
    +	var value = null;
    +
    +	value = (typeof(this.settings[name]) == "undefined") ? default_value : this.settings[name];
    +
    +	// Fix bool values
    +	if (value == "true" || value == "false")
    +		return (value == "true");
    +
    +	return value;
    +};
    +
    +MCTabs.prototype.displayTab = function(tab_id, panel_id) {
    +	var panelElm, panelContainerElm, tabElm, tabContainerElm, selectionClass, nodes, i;
    +
    +	panelElm= document.getElementById(panel_id);
    +	panelContainerElm = panelElm ? panelElm.parentNode : null;
    +	tabElm = document.getElementById(tab_id);
    +	tabContainerElm = tabElm ? tabElm.parentNode : null;
    +	selectionClass = this.getParam('selection_class', 'current');
    +
    +	if (tabElm && tabContainerElm) {
    +		nodes = tabContainerElm.childNodes;
    +
    +		// Hide all other tabs
    +		for (i = 0; i < nodes.length; i++) {
    +			if (nodes[i].nodeName == "LI")
    +				nodes[i].className = '';
    +		}
    +
    +		// Show selected tab
    +		tabElm.className = 'current';
    +	}
    +
    +	if (panelElm && panelContainerElm) {
    +		nodes = panelContainerElm.childNodes;
    +
    +		// Hide all other panels
    +		for (i = 0; i < nodes.length; i++) {
    +			if (nodes[i].nodeName == "DIV")
    +				nodes[i].className = 'panel';
    +		}
    +
    +		// Show selected panel
    +		panelElm.className = 'current';
    +	}
    +};
    +
    +MCTabs.prototype.getAnchor = function() {
    +	var pos, url = document.location.href;
    +
    +	if ((pos = url.lastIndexOf('#')) != -1)
    +		return url.substring(pos + 1);
    +
    +	return "";
    +};
    +
    +// Global instance
    +var mcTabs = new MCTabs();
    diff --git a/TinyMCE/tiny_mce/utils/validate.js b/TinyMCE/tiny_mce/utils/validate.js
    new file mode 100644
    index 0000000..3fcd639
    --- /dev/null
    +++ b/TinyMCE/tiny_mce/utils/validate.js
    @@ -0,0 +1,219 @@
    +/**
    + * $Id: validate.js 758 2008-03-30 13:53:29Z spocke $
    + *
    + * Various form validation methods.
    + *
    + * @author Moxiecode
    + * @copyright Copyright  2004-2008, Moxiecode Systems AB, All rights reserved.
    + */
    +
    +/**
    +	// String validation:
    +
    +	if (!Validator.isEmail('myemail'))
    +		alert('Invalid email.');
    +
    +	// Form validation:
    +
    +	var f = document.forms['myform'];
    +
    +	if (!Validator.isEmail(f.myemail))
    +		alert('Invalid email.');
    +*/
    +
    +var Validator = {
    +	isEmail : function(s) {
    +		return this.test(s, '^[-!#$%&\'*+\\./0-9=?A-Z^_`a-z{|}~]+@[-!#$%&\'*+\\/0-9=?A-Z^_`a-z{|}~]+\.[-!#$%&\'*+\\./0-9=?A-Z^_`a-z{|}~]+$');
    +	},
    +
    +	isAbsUrl : function(s) {
    +		return this.test(s, '^(news|telnet|nttp|file|http|ftp|https)://[-A-Za-z0-9\\.]+\\/?.*$');
    +	},
    +
    +	isSize : function(s) {
    +		return this.test(s, '^[0-9]+(%|in|cm|mm|em|ex|pt|pc|px)?$');
    +	},
    +
    +	isId : function(s) {
    +		return this.test(s, '^[A-Za-z_]([A-Za-z0-9_])*$');
    +	},
    +
    +	isEmpty : function(s) {
    +		var nl, i;
    +
    +		if (s.nodeName == 'SELECT' && s.selectedIndex < 1)
    +			return true;
    +
    +		if (s.type == 'checkbox' && !s.checked)
    +			return true;
    +
    +		if (s.type == 'radio') {
    +			for (i=0, nl = s.form.elements; i<nl.length; i++) {
    +				if (nl[i].type == "radio" && nl[i].name == s.name && nl[i].checked)
    +					return false;
    +			}
    +
    +			return true;
    +		}
    +
    +		return new RegExp('^\\s*$').test(s.nodeType == 1 ? s.value : s);
    +	},
    +
    +	isNumber : function(s, d) {
    +		return !isNaN(s.nodeType == 1 ? s.value : s) && (!d || !this.test(s, '^-?[0-9]*\\.[0-9]*$'));
    +	},
    +
    +	test : function(s, p) {
    +		s = s.nodeType == 1 ? s.value : s;
    +
    +		return s == '' || new RegExp(p).test(s);
    +	}
    +};
    +
    +var AutoValidator = {
    +	settings : {
    +		id_cls : 'id',
    +		int_cls : 'int',
    +		url_cls : 'url',
    +		number_cls : 'number',
    +		email_cls : 'email',
    +		size_cls : 'size',
    +		required_cls : 'required',
    +		invalid_cls : 'invalid',
    +		min_cls : 'min',
    +		max_cls : 'max'
    +	},
    +
    +	init : function(s) {
    +		var n;
    +
    +		for (n in s)
    +			this.settings[n] = s[n];
    +	},
    +
    +	validate : function(f) {
    +		var i, nl, s = this.settings, c = 0;
    +
    +		nl = this.tags(f, 'label');
    +		for (i=0; i<nl.length; i++)
    +			this.removeClass(nl[i], s.invalid_cls);
    +
    +		c += this.validateElms(f, 'input');
    +		c += this.validateElms(f, 'select');
    +		c += this.validateElms(f, 'textarea');
    +
    +		return c == 3;
    +	},
    +
    +	invalidate : function(n) {
    +		this.mark(n.form, n);
    +	},
    +
    +	reset : function(e) {
    +		var t = ['label', 'input', 'select', 'textarea'];
    +		var i, j, nl, s = this.settings;
    +
    +		if (e == null)
    +			return;
    +
    +		for (i=0; i<t.length; i++) {
    +			nl = this.tags(e.form ? e.form : e, t[i]);
    +			for (j=0; j<nl.length; j++)
    +				this.removeClass(nl[j], s.invalid_cls);
    +		}
    +	},
    +
    +	validateElms : function(f, e) {
    +		var nl, i, n, s = this.settings, st = true, va = Validator, v;
    +
    +		nl = this.tags(f, e);
    +		for (i=0; i<nl.length; i++) {
    +			n = nl[i];
    +
    +			this.removeClass(n, s.invalid_cls);
    +
    +			if (this.hasClass(n, s.required_cls) && va.isEmpty(n))
    +				st = this.mark(f, n);
    +
    +			if (this.hasClass(n, s.number_cls) && !va.isNumber(n))
    +				st = this.mark(f, n);
    +
    +			if (this.hasClass(n, s.int_cls) && !va.isNumber(n, true))
    +				st = this.mark(f, n);
    +
    +			if (this.hasClass(n, s.url_cls) && !va.isAbsUrl(n))
    +				st = this.mark(f, n);
    +
    +			if (this.hasClass(n, s.email_cls) && !va.isEmail(n))
    +				st = this.mark(f, n);
    +
    +			if (this.hasClass(n, s.size_cls) && !va.isSize(n))
    +				st = this.mark(f, n);
    +
    +			if (this.hasClass(n, s.id_cls) && !va.isId(n))
    +				st = this.mark(f, n);
    +
    +			if (this.hasClass(n, s.min_cls, true)) {
    +				v = this.getNum(n, s.min_cls);
    +
    +				if (isNaN(v) || parseInt(n.value) < parseInt(v))
    +					st = this.mark(f, n);
    +			}
    +
    +			if (this.hasClass(n, s.max_cls, true)) {
    +				v = this.getNum(n, s.max_cls);
    +
    +				if (isNaN(v) || parseInt(n.value) > parseInt(v))
    +					st = this.mark(f, n);
    +			}
    +		}
    +
    +		return st;
    +	},
    +
    +	hasClass : function(n, c, d) {
    +		return new RegExp('\\b' + c + (d ? '[0-9]+' : '') + '\\b', 'g').test(n.className);
    +	},
    +
    +	getNum : function(n, c) {
    +		c = n.className.match(new RegExp('\\b' + c + '([0-9]+)\\b', 'g'))[0];
    +		c = c.replace(/[^0-9]/g, '');
    +
    +		return c;
    +	},
    +
    +	addClass : function(n, c, b) {
    +		var o = this.removeClass(n, c);
    +		n.className = b ? c + (o != '' ? (' ' + o) : '') : (o != '' ? (o + ' ') : '') + c;
    +	},
    +
    +	removeClass : function(n, c) {
    +		c = n.className.replace(new RegExp("(^|\\s+)" + c + "(\\s+|$)"), ' ');
    +		return n.className = c != ' ' ? c : '';
    +	},
    +
    +	tags : function(f, s) {
    +		return f.getElementsByTagName(s);
    +	},
    +
    +	mark : function(f, n) {
    +		var s = this.settings;
    +
    +		this.addClass(n, s.invalid_cls);
    +		this.markLabels(f, n, s.invalid_cls);
    +
    +		return false;
    +	},
    +
    +	markLabels : function(f, n, ic) {
    +		var nl, i;
    +
    +		nl = this.tags(f, "label");
    +		for (i=0; i<nl.length; i++) {
    +			if (nl[i].getAttribute("for") == n.id || nl[i].htmlFor == n.id)
    +				this.addClass(nl[i], ic);
    +		}
    +
    +		return null;
    +	}
    +};
    diff --git a/WordpressToTypecho/Action.php b/WordpressToTypecho/Action.php
    new file mode 100644
    index 0000000..df8a0d1
    --- /dev/null
    +++ b/WordpressToTypecho/Action.php
    @@ -0,0 +1,215 @@
    +<?php
    +
    +class WordpressToTypecho_Action extends Typecho_Widget implements Widget_Interface_Do
    +{
    +    public function doImport()
    +    {
    +        $options = $this->widget('Widget_Options');
    +        $dbConfig = $options->plugin('WordpressToTypecho');
    +
    +        /** 初始化一个db */
    +        if (Typecho_Db_Adapter_Mysql::isAvailable()) {
    +            $db = new Typecho_Db('Mysql', $dbConfig->prefix);
    +        } else {
    +            $db = new Typecho_Db('Pdo_Mysql', $dbConfig->prefix);
    +        }
    +        
    +        /** 只读即可 */
    +        $db->addServer(array (
    +          'host' => $dbConfig->host,
    +          'user' => $dbConfig->user,
    +          'password' => $dbConfig->password,
    +          'charset' => 'utf8',
    +          'port' => $dbConfig->port,
    +          'database' => $dbConfig->database
    +        ), Typecho_Db::READ);
    +        
    +        /** 删除当前内容 */
    +        $masterDb = Typecho_Db::get();
    +        $this->widget('Widget_Abstract_Contents')->to($contents)->delete($masterDb->sql()->where('1 = 1'));
    +        $this->widget('Widget_Abstract_Comments')->to($comments)->delete($masterDb->sql()->where('1 = 1'));
    +        $this->widget('Widget_Abstract_Metas')->to($metas)->delete($masterDb->sql()->where('1 = 1'));
    +        $this->widget('Widget_Contents_Post_Edit')->to($edit);
    +        $masterDb->query($masterDb->delete('table.relationships')->where('1 = 1'));
    +        $userId = $this->widget('Widget_User')->uid;
    +        
    +        /** 获取时区偏移 */
    +        $gmtOffset = idate('Z');
    +        
    +        /** 转换全局变量 */
    +		/** 
    +        $rows = $db->fetchAll($db->select()->from('table.statics'));
    +        $static = array();
    +        foreach ($rows as $row) {
    +            $static[$row['static_name']] = $row['static_value'];
    +        }*/
    +        
    +        /** 转换文件 */
    +        /**$files = $db->fetchAll($db->select()->from('table.files'));
    +        if (!is_dir(__TYPECHO_ROOT_DIR__ . '/usr/uploads/')) {
    +            mkdir(__TYPECHO_ROOT_DIR__ . '/usr/uploads/', 0766);
    +        }
    +        
    +        $pattern = array();
    +        $replace = array();
    +        foreach ($files as $file) {
    +            $path = __TYPECHO_ROOT_DIR__ . '/data/upload/' . substr($file['file_guid'], 0, 2) . '/' .
    +            substr($file['file_guid'], 2, 2) . '/' . $file['file_guid'];
    +            
    +            if (is_file($path)) {
    +                $file['file_time'] = empty($file['file_time']) ? $options->gmtTime : $file['file_time'];
    +                $year = idate('Y', $file['file_time']);
    +                $month = idate('m', $file['file_time']);
    +                $day = idate('d', $file['file_time']);
    +                
    +                if (!is_dir(__TYPECHO_ROOT_DIR__ . "/usr/uploads/{$year}")) {
    +                    mkdir(__TYPECHO_ROOT_DIR__ . "/usr/uploads/{$year}", 0766);
    +                }
    +                
    +                if (!is_dir(__TYPECHO_ROOT_DIR__ . "/usr/uploads/{$year}/{$month}")) {
    +                    mkdir(__TYPECHO_ROOT_DIR__ . "/usr/uploads/{$year}/{$month}", 0766);
    +                }
    +                
    +                if (!is_dir(__TYPECHO_ROOT_DIR__ . "/usr/uploads/{$year}/{$month}/{$day}")) {
    +                    mkdir(__TYPECHO_ROOT_DIR__ . "/usr/uploads/{$year}/{$month}/{$day}", 0766);
    +                }
    +                
    +                $parts = explode('.', $file['file_name']);
    +                $ext = array_pop($parts);
    +                copy($path, __TYPECHO_ROOT_DIR__ . "/usr/uploads/{$year}/{$month}/{$day}/{$file['file_id']}.{$ext}");
    +                
    +                $new = Typecho_Common::url("/usr/uploads/{$year}/{$month}/{$day}/{$file['file_id']}.{$ext}", $options->siteUrl);
    +                $old = Typecho_Common::url("/res/{$file['file_id']}/{$file['file_name']}", $static['siteurl'] . '/index.php');
    +                $pattern[] = '/' . str_replace('\/index\.php', '(\/index\.php)?', preg_quote($old, '/')) . '/is';
    +                $replace[] = $new;
    +            }
    +        }
    +        */
    +        /** 转换评论 */
    +        $i = 1;
    +        
    +        while (true) {
    +            $result = $db->query($db->select()->from('table.comments')
    +            ->order('comment_ID', Typecho_Db::SORT_ASC)->page($i, 100));
    +            $j = 0;
    +            
    +            while ($row = $db->fetchRow($result)) {
    +                $status = $row['comment_approved'];
    +                if ('spam' == $row['comment_approved']) {
    +                    $status = 'spam';
    +                } else if ('0' == $row['comment_approved']) {
    +                    $status = 'waiting';
    +                } else {
    +                    $status = 'approved';
    +                }
    +                
    +                $row['comment_content'] = preg_replace(
    +                array("/\s*<p>/is", "/\s*<\/p>\s*/is", "/\s*<br\s*\/>\s*/is",
    +                "/\s*<(div|blockquote|pre|table|ol|ul)>/is", "/<\/(div|blockquote|pre|table|ol|ul)>\s*/is"),
    +                array('', "\n\n", "\n", "\n\n<\\1>", "</\\1>\n\n"), 
    +                $row['comment_content']);
    +            
    +                $comments->insert(array(
    +                    'coid'      =>  $row['comment_ID'],
    +                    'cid'       =>  $row['comment_post_ID'],
    +                    'created'   =>  strtotime($row['comment_date_gmt']) + $gmtOffset,
    +                    'author'    =>  $row['comment_author'],
    +                    'authorId'  =>  $row['user_id'],
    +                    'ownerId'   =>  1,
    +                    'mail'      =>  $row['comment_author_email'],
    +                    'url'       =>  $row['comment_author_url'],
    +                    'ip'        =>  $row['comment_author_IP'],
    +                    'agent'     =>  $row['comment_agent'],
    +                    'text'      =>  $row['comment_content'],
    +                    'type'      =>  empty($row['comment_type']) ? 'comment' : $row['comment_type'],
    +                    'status'    =>  $status,
    +                    'parent'    =>  $row['comment_parent']
    +                ));
    +                $j ++;
    +                unset($row);
    +            }
    +            
    +            if ($j < 100) {
    +                break;
    +            }
    +            
    +            $i ++;
    +            unset($result);
    +        }
    +		
    +		/** 转换Wordpress的term_taxonomy表 */
    +		$terms = $db->fetchAll($db->select()->from('table.term_taxonomy')
    +        ->join('table.terms', 'table.term_taxonomy.term_id = table.terms.term_id')
    +        ->where('taxonomy = ? OR taxonomy = ?', 'category', 'post_tag'));
    +        foreach ($terms as $term) {
    +            $metas->insert(array(
    +                'mid'           =>  $term['term_taxonomy_id'],
    +                'name'          =>  $term['name'],
    +                'slug'          =>  'post_tag' == $term['taxonomy'] ? Typecho_Common::slugName($term['name']) : $term['slug'],
    +                'type'      	=>  'post_tag' == $term['taxonomy'] ? 'tag' : 'category',
    +                'description'   =>  $term['description'],
    +                'count'      	=>  $term['count'],
    +            ));
    +            
    +            /** 转换关系表 */
    +            $relationships = $db->fetchAll($db->select()->from('table.term_relationships')
    +            ->where('term_taxonomy_id = ?', $term['term_taxonomy_id']));
    +            foreach ($relationships as $relationship) {
    +                $masterDb->query($masterDb->insert('table.relationships')->rows(array(
    +                    'cid'      	=>  $relationship['object_id'],
    +                    'mid'   	=>  $relationship['term_taxonomy_id'],
    +                )));
    +            }
    +        }
    +		
    +        /** 转换内容 */
    +        $i = 1;
    +        
    +        while (true) {
    +            $result = $db->query($db->select()->from('table.posts')
    +            ->where('post_type = ? OR post_type = ?', 'post', 'page')
    +            ->order('ID', Typecho_Db::SORT_ASC)->page($i, 100));
    +            $j = 0;
    +            
    +            while ($row = $db->fetchRow($result)) {
    +                $contents->insert(array(
    +                    'cid'           =>  $row['ID'],
    +                    'title'         =>  $row['post_title'],
    +                    'slug'          =>  Typecho_Common::slugName(urldecode($row['post_name']), $row['ID'], 128),
    +                    'created'       =>  strtotime($row['post_date_gmt']) + $gmtOffset,
    +                    'modified'      =>  strtotime($row['post_modified_gmt']) + $gmtOffset,
    +                    'text'          =>  $row['post_content'],
    +                    'order'         =>  $row['menu_order'],
    +                    'authorId'      =>  $row['post_author'],
    +                    'template'      =>  NULL,
    +                    'type'          =>  'page' == $row['post_type'] ? 'page' : 'post',
    +                    'status'        =>  'publish' == $row['post_status'] ? 'publish' : 'draft',
    +                    'password'      =>  $row['post_password'],
    +                    'commentsNum'   =>  $row['comment_count'],
    +                    'allowComment'  =>  'open' == $row['comment_status']? '1' : '0',
    +                    'allowFeed'     =>  '1',
    +                    'allowPing'     =>  'open' == $row['ping_status']? '1' : '0',
    +                ));
    +                
    +                $j ++;
    +                unset($row);
    +            }
    +            
    +            if ($j < 100) {
    +                break;
    +            }
    +            
    +            $i ++;
    +            unset($result);
    +        }
    +        
    +        $this->widget('Widget_Notice')->set(_t("数据已经转换完成"), NULL, 'success');
    +        $this->response->goBack();
    +    }
    +
    +    public function action()
    +    {
    +        $this->widget('Widget_User')->pass('administrator');
    +        $this->on($this->request->isPost())->doImport();
    +    }
    +}
    diff --git a/WordpressToTypecho/Plugin.php b/WordpressToTypecho/Plugin.php
    new file mode 100644
    index 0000000..4487982
    --- /dev/null
    +++ b/WordpressToTypecho/Plugin.php
    @@ -0,0 +1,95 @@
    +<?php
    +/**
    + * 将 Wordpress 数据库中的数据转换为 Typecho
    + * 
    + * @package Wordpress to Typecho
    + * @author qining
    + * @version 1.0.3 Beta
    + * @link http://typecho.org
    + */
    +class WordpressToTypecho_Plugin implements Typecho_Plugin_Interface
    +{
    +    /**
    +     * 激活插件方法,如果激活失败,直接抛出异常
    +     * 
    +     * @access public
    +     * @return void
    +     * @throws Typecho_Plugin_Exception
    +     */
    +    public static function activate()
    +    {
    +        if (!Typecho_Db_Adapter_Mysql::isAvailable() && !Typecho_Db_Adapter_Pdo_Mysql::isAvailable()) {
    +            throw new Typecho_Plugin_Exception(_t('没有找到任何可用的 Mysql 适配器'));
    +        }
    +        
    +        /**$error = NULL;
    +        if ((!is_dir(__TYPECHO_ROOT_DIR__ . '/usr/uploads/') || !is_writeable(__TYPECHO_ROOT_DIR__ . '/usr/uploads/'))
    +        && !is_writeable(__TYPECHO_ROOT_DIR__ . '/usr/')) {
    +            $error = '<br /><strong>' . _t('%s 目录不可写, 可能会导致附件转换不成功', __TYPECHO_ROOT_DIR__ . '/usr/uploads/') . '</strong>';
    +        }
    +		*/
    +    
    +        Helper::addPanel(1, 'WordpressToTypecho/panel.php', _t('从Wordpress导入数据'), _t('从Wordpress导入数据'), 'administrator');
    +        Helper::addAction('wordpress-to-typecho', 'WordpressToTypecho_Action');
    +        return _t('请在插件设置里设置 Wordpress 所在的数据库参数') . $error;
    +    }
    +    
    +    /**
    +     * 禁用插件方法,如果禁用失败,直接抛出异常
    +     * 
    +     * @static
    +     * @access public
    +     * @return void
    +     * @throws Typecho_Plugin_Exception
    +     */
    +    public static function deactivate()
    +    {
    +        Helper::removeAction('wordpress-to-typecho');
    +        Helper::removePanel(1, 'WordpressToTypecho/panel.php');
    +    }
    +    
    +    /**
    +     * 获取插件配置面板
    +     * 
    +     * @access public
    +     * @param Typecho_Widget_Helper_Form $form 配置面板
    +     * @return void
    +     */
    +    public static function config(Typecho_Widget_Helper_Form $form)
    +    {
    +        $host = new Typecho_Widget_Helper_Form_Element_Text('host', NULL, 'localhost',
    +        _t('数据库地址'), _t('请填写 Wordpress 所在的数据库地址'));
    +        $form->addInput($host->addRule('required', _t('必须填写一个数据库地址')));
    +        
    +        $port = new Typecho_Widget_Helper_Form_Element_Text('port', NULL, '3306',
    +        _t('数据库端口'), _t('Wordpress 所在的数据库服务器端口'));
    +        $port->input->setAttribute('class', 'mini');
    +        $form->addInput($port->addRule('required', _t('必须填写数据库端口'))
    +        ->addRule('isInteger', _t('端口号必须是纯数字')));
    +        
    +        $user = new Typecho_Widget_Helper_Form_Element_Text('user', NULL, 'root',
    +        _t('数据库用户名'));
    +        $form->addInput($user->addRule('required', _t('必须填写数据库用户名')));
    +        
    +        $password = new Typecho_Widget_Helper_Form_Element_Password('password', NULL, NULL,
    +        _t('数据库密码'));
    +        $form->addInput($password);
    +        
    +        $database = new Typecho_Widget_Helper_Form_Element_Text('database', NULL, 'Wordpress',
    +        _t('数据库名称'), _t('Wordpress 所在的数据库名称'));
    +        $form->addInput($database->addRule('required', _t('您必须填写数据库名称')));
    +    
    +        $prefix = new Typecho_Widget_Helper_Form_Element_Text('prefix', NULL, 'wp_',
    +        _t('表前缀'), _t('所有 Wordpress 数据表的前缀'));
    +        $form->addInput($prefix->addRule('required', _t('您必须填写表前缀')));
    +    }
    +    
    +    /**
    +     * 个人用户的配置面板
    +     * 
    +     * @access public
    +     * @param Typecho_Widget_Helper_Form $form
    +     * @return void
    +     */
    +    public static function personalConfig(Typecho_Widget_Helper_Form $form){}
    +}
    diff --git a/WordpressToTypecho/panel.php b/WordpressToTypecho/panel.php
    new file mode 100644
    index 0000000..e49d7f8
    --- /dev/null
    +++ b/WordpressToTypecho/panel.php
    @@ -0,0 +1,73 @@
    +<?php
    +if (!defined('__TYPECHO_ROOT_DIR__')) {
    +    exit;
    +}
    +
    +$success = true;
    +try {
    +    $dbConfig = $options->plugin('WordpressToTypecho');
    +
    +    /** 初始化一个db */
    +    if (Typecho_Db_Adapter_Mysql::isAvailable()) {
    +        $wordpressDb = new Typecho_Db('Mysql', $dbConfig->prefix);
    +    } else {
    +        $wordpressDb = new Typecho_Db('Pdo_Mysql', $dbConfig->prefix);
    +    }
    +
    +    /** 只读即可 */
    +    $wordpressDb->addServer(array (
    +      'host' => $dbConfig->host,
    +      'user' => $dbConfig->user,
    +      'password' => $dbConfig->password,
    +      'charset' => 'utf8',
    +      'port' => $dbConfig->port,
    +      'database' => $dbConfig->database
    +    ), Typecho_Db::READ);
    +    
    +    $rows = $wordpressDb->fetchAll($wordpressDb->select()->from('table.options'));
    +    $static = array();
    +    foreach ($rows as $row) {
    +        $static[$row['option_name']] = $row['option_value'];
    +    }
    +} catch (Typecho_Db_Exception $e) {
    +    $success = false;
    +}
    +
    +include 'header.php';
    +include 'menu.php';
    +?>
    +<div class="main">
    +    <div class="body body-950">
    +        <?php include 'page-title.php'; ?>
    +        <div class="container typecho-page-main">
    +            <div class="column-22 start-02">
    +                <?php if ($success): ?>
    +                <div class="message notice typecho-radius-topleft typecho-radius-topright typecho-radius-bottomleft typecho-radius-bottomright">
    +                <form action="<?php $options->index('/action/wordpress-to-typecho'); ?>" method="post">
    +                    <?php _e('我们检测到了 Wordpress 系统信息, 点击下方的按钮开始数据转换, 数据转换可能会耗时较长.'); ?>
    +                    <blockquote>
    +                    <ul>
    +                        <li><strong><?php echo $static['blogname']; ?></strong></li>
    +                        <li><strong><?php echo $static['blogdescription']; ?></strong></li>
    +                        <li><strong><?php echo $static['siteurl']; ?></strong></li>
    +                    </ul>
    +                    </blockquote>
    +                    <br />
    +                    <p><button type="submit"><?php _e('开始数据转换 &raquo;'); ?></button></p>
    +                </form>
    +                </div>
    +                <?php else: ?>
    +                <div class="message error">
    +                    <?php _e('我们在连接到 Wordpress 的数据库时发生了错误, 请<a href="%s">重新设置</a>你的信息.', 
    +                    Typecho_Common::url('options-plugin.php?config=WordpressToTypecho', $options->adminUrl)); ?>
    +                </div>
    +                <?php endif; ?>
    +            </div>
    +        </div>
    +    </div>
    +</div>
    +<?php
    +include 'copyright.php';
    +include 'common-js.php';
    +include 'footer.php';
    +?>
    diff --git a/ZenCoding/Plugin.php b/ZenCoding/Plugin.php
    new file mode 100644
    index 0000000..ef2522c
    --- /dev/null
    +++ b/ZenCoding/Plugin.php
    @@ -0,0 +1,88 @@
    +<?php
    +/**
    + * Set of plugins for HTML and CSS hi-speed coding
    + * 
    + * @package Zen Coding
    + * @author qining
    + * @version 1.0.0
    + * @link http://code.google.com/p/zen-coding/
    + */
    +class ZenCoding_Plugin implements Typecho_Plugin_Interface
    +{
    +    /**
    +     * 激活插件方法,如果激活失败,直接抛出异常
    +     * 
    +     * @access public
    +     * @return void
    +     * @throws Typecho_Plugin_Exception
    +     */
    +    public static function activate()
    +    {
    +        Typecho_Plugin::factory('admin/write-post.php')->bottom = array('ZenCoding_Plugin', 'writeBottom');
    +        Typecho_Plugin::factory('admin/write-page.php')->bottom = array('ZenCoding_Plugin', 'writeBottom');
    +        Typecho_Plugin::factory('admin/theme-editor.php')->bottom = array('ZenCoding_Plugin', 'themeBottom');
    +    }
    +    
    +    /**
    +     * 禁用插件方法,如果禁用失败,直接抛出异常
    +     * 
    +     * @static
    +     * @access public
    +     * @return void
    +     * @throws Typecho_Plugin_Exception
    +     */
    +    public static function deactivate()
    +    {}
    +    
    +    /**
    +     * 获取插件配置面板
    +     * 
    +     * @access public
    +     * @param Typecho_Widget_Helper_Form $form 配置面板
    +     * @return void
    +     */
    +    public static function config(Typecho_Widget_Helper_Form $form){}
    +    
    +    /**
    +     * 个人用户的配置面板
    +     * 
    +     * @access public
    +     * @param Typecho_Widget_Helper_Form $form
    +     * @return void
    +     */
    +    public static function personalConfig(Typecho_Widget_Helper_Form $form){}
    +    
    +    /**
    +     * 插件实现方法
    +     * 
    +     * @access public
    +     * @return void
    +     */
    +    public static function themeBottom($files)
    +    {
    +        $options = Helper::options();
    +        $js = Typecho_Common::url('ZenCoding/zen_textarea.js', $options->pluginUrl);
    +        echo "<script type=\"text/javascript\" src=\"{$js}\"></script>
    +<script type=\"text/javascript\">
    +    $(document).getElement('#content').addClass('zc-use_tab-true zc-syntax-xsl zc-profile-xml');
    +    zen_textarea.setup({pretty_break: true});
    +</script>";
    +    }
    +    
    +    /**
    +     * 插件实现方法
    +     * 
    +     * @access public
    +     * @return void
    +     */
    +    public static function writeBottom($post)
    +    {
    +        $options = Helper::options();
    +        $js = Typecho_Common::url('ZenCoding/zen_textarea.js', $options->pluginUrl);
    +        echo "<script type=\"text/javascript\" src=\"{$js}\"></script>
    +<script type=\"text/javascript\">
    +    $(document).getElement('#text').addClass('zc-use_tab-true zc-syntax-xsl zc-profile-xml');
    +    zen_textarea.setup({pretty_break: true});
    +</script>";
    +    }
    +}
    diff --git a/ZenCoding/zen_textarea.js b/ZenCoding/zen_textarea.js
    new file mode 100644
    index 0000000..737ce80
    --- /dev/null
    +++ b/ZenCoding/zen_textarea.js
    @@ -0,0 +1,3113 @@
    +(function(){
    +/**
    + * Zen Coding settings
    + * @author Sergey Chikuyonok (serge.che@gmail.com)
    + * @link http://chikuyonok.ru
    + */
    +var zen_settings = {
    +	/** 
    +	 * Variables that can be placed inside snippets or abbreviations as ${variable}
    +	 * ${child} variable is reserved, don't use it 
    +	 */
    +	'variables': {
    +		'lang': 'en',
    +		'locale': 'en-US',
    +		'charset': 'UTF-8',
    +		'profile': 'xhtml',
    +		
    +		/** Inner element indentation */
    +		'indentation': '\t'     // TODO take from Aptana settings
    +	},
    +	
    +	'css': {
    +		'snippets': {
    +			"@i": "@import url(|);",
    +			"@m": "@media print {\n\t|\n}",
    +			"@f": "@font-face {\n\tfont-family:|;\n\tsrc:url(|);\n}",
    +			"!": "!important",
    +			"pos": "position:|;",
    +			"pos:s": "position:static;",
    +			"pos:a": "position:absolute;",
    +			"pos:r": "position:relative;",
    +			"pos:f": "position:fixed;",
    +			"t": "top:|;",
    +			"t:a": "top:auto;",
    +			"r": "right:|;",
    +			"r:a": "right:auto;",
    +			"b": "bottom:|;",
    +			"b:a": "bottom:auto;",
    +			"l": "left:|;",
    +			"l:a": "left:auto;",
    +			"z": "z-index:|;",
    +			"z:a": "z-index:auto;",
    +			"fl": "float:|;",
    +			"fl:n": "float:none;",
    +			"fl:l": "float:left;",
    +			"fl:r": "float:right;",
    +			"cl": "clear:|;",
    +			"cl:n": "clear:none;",
    +			"cl:l": "clear:left;",
    +			"cl:r": "clear:right;",
    +			"cl:b": "clear:both;",
    +			"d": "display:|;",
    +			"d:n": "display:none;",
    +			"d:b": "display:block;",
    +			"d:ib": "display:inline;",
    +			"d:li": "display:list-item;",
    +			"d:ri": "display:run-in;",
    +			"d:cp": "display:compact;",
    +			"d:tb": "display:table;",
    +			"d:itb": "display:inline-table;",
    +			"d:tbcp": "display:table-caption;",
    +			"d:tbcl": "display:table-column;",
    +			"d:tbclg": "display:table-column-group;",
    +			"d:tbhg": "display:table-header-group;",
    +			"d:tbfg": "display:table-footer-group;",
    +			"d:tbr": "display:table-row;",
    +			"d:tbrg": "display:table-row-group;",
    +			"d:tbc": "display:table-cell;",
    +			"d:rb": "display:ruby;",
    +			"d:rbb": "display:ruby-base;",
    +			"d:rbbg": "display:ruby-base-group;",
    +			"d:rbt": "display:ruby-text;",
    +			"d:rbtg": "display:ruby-text-group;",
    +			"v": "visibility:|;",
    +			"v:v": "visibility:visible;",
    +			"v:h": "visibility:hidden;",
    +			"v:c": "visibility:collapse;",
    +			"ov": "overflow:|;",
    +			"ov:v": "overflow:visible;",
    +			"ov:h": "overflow:hidden;",
    +			"ov:s": "overflow:scroll;",
    +			"ov:a": "overflow:auto;",
    +			"ovx": "overflow-x:|;",
    +			"ovx:v": "overflow-x:visible;",
    +			"ovx:h": "overflow-x:hidden;",
    +			"ovx:s": "overflow-x:scroll;",
    +			"ovx:a": "overflow-x:auto;",
    +			"ovy": "overflow-y:|;",
    +			"ovy:v": "overflow-y:visible;",
    +			"ovy:h": "overflow-y:hidden;",
    +			"ovy:s": "overflow-y:scroll;",
    +			"ovy:a": "overflow-y:auto;",
    +			"ovs": "overflow-style:|;",
    +			"ovs:a": "overflow-style:auto;",
    +			"ovs:s": "overflow-style:scrollbar;",
    +			"ovs:p": "overflow-style:panner;",
    +			"ovs:m": "overflow-style:move;",
    +			"ovs:mq": "overflow-style:marquee;",
    +			"zoo": "zoom:1;",
    +			"cp": "clip:|;",
    +			"cp:a": "clip:auto;",
    +			"cp:r": "clip:rect(|);",
    +			"bxz": "box-sizing:|;",
    +			"bxz:cb": "box-sizing:content-box;",
    +			"bxz:bb": "box-sizing:border-box;",
    +			"bxsh": "box-shadow:|;",
    +			"bxsh:n": "box-shadow:none;",
    +			"bxsh:w": "-webkit-box-shadow:0 0 0 #000;",
    +			"bxsh:m": "-moz-box-shadow:0 0 0 0 #000;",
    +			"m": "margin:|;",
    +			"m:a": "margin:auto;",
    +			"m:0": "margin:0;",
    +			"m:2": "margin:0 0;",
    +			"m:3": "margin:0 0 0;",
    +			"m:4": "margin:0 0 0 0;",
    +			"mt": "margin-top:|;",
    +			"mt:a": "margin-top:auto;",
    +			"mr": "margin-right:|;",
    +			"mr:a": "margin-right:auto;",
    +			"mb": "margin-bottom:|;",
    +			"mb:a": "margin-bottom:auto;",
    +			"ml": "margin-left:|;",
    +			"ml:a": "margin-left:auto;",
    +			"p": "padding:|;",
    +			"p:0": "padding:0;",
    +			"p:2": "padding:0 0;",
    +			"p:3": "padding:0 0 0;",
    +			"p:4": "padding:0 0 0 0;",
    +			"pt": "padding-top:|;",
    +			"pr": "padding-right:|;",
    +			"pb": "padding-bottom:|;",
    +			"pl": "padding-left:|;",
    +			"w": "width:|;",
    +			"w:a": "width:auto;",
    +			"h": "height:|;",
    +			"h:a": "height:auto;",
    +			"maw": "max-width:|;",
    +			"maw:n": "max-width:none;",
    +			"mah": "max-height:|;",
    +			"mah:n": "max-height:none;",
    +			"miw": "min-width:|;",
    +			"mih": "min-height:|;",
    +			"o": "outline:|;",
    +			"o:n": "outline:none;",
    +			"oo": "outline-offset:|;",
    +			"ow": "outline-width:|;",
    +			"os": "outline-style:|;",
    +			"oc": "outline-color:#000;",
    +			"oc:i": "outline-color:invert;",
    +			"bd": "border:|;",
    +			"bd+": "border:1px solid #000;",
    +			"bd:n": "border:none;",
    +			"bdbk": "border-break:|;",
    +			"bdbk:c": "border-break:close;",
    +			"bdcl": "border-collapse:|;",
    +			"bdcl:c": "border-collapse:collapse;",
    +			"bdcl:s": "border-collapse:separate;",
    +			"bdc": "border-color:#000;",
    +			"bdi": "border-image:url(|);",
    +			"bdi:n": "border-image:none;",
    +			"bdi:w": "-webkit-border-image:url(|) 0 0 0 0 stretch stretch;",
    +			"bdi:m": "-moz-border-image:url(|) 0 0 0 0 stretch stretch;",
    +			"bdti": "border-top-image:url(|);",
    +			"bdti:n": "border-top-image:none;",
    +			"bdri": "border-right-image:url(|);",
    +			"bdri:n": "border-right-image:none;",
    +			"bdbi": "border-bottom-image:url(|);",
    +			"bdbi:n": "border-bottom-image:none;",
    +			"bdli": "border-left-image:url(|);",
    +			"bdli:n": "border-left-image:none;",
    +			"bdci": "border-corner-image:url(|);",
    +			"bdci:n": "border-corner-image:none;",
    +			"bdci:c": "border-corner-image:continue;",
    +			"bdtli": "border-top-left-image:url(|);",
    +			"bdtli:n": "border-top-left-image:none;",
    +			"bdtli:c": "border-top-left-image:continue;",
    +			"bdtri": "border-top-right-image:url(|);",
    +			"bdtri:n": "border-top-right-image:none;",
    +			"bdtri:c": "border-top-right-image:continue;",
    +			"bdbri": "border-bottom-right-image:url(|);",
    +			"bdbri:n": "border-bottom-right-image:none;",
    +			"bdbri:c": "border-bottom-right-image:continue;",
    +			"bdbli": "border-bottom-left-image:url(|);",
    +			"bdbli:n": "border-bottom-left-image:none;",
    +			"bdbli:c": "border-bottom-left-image:continue;",
    +			"bdf": "border-fit:|;",
    +			"bdf:c": "border-fit:clip;",
    +			"bdf:r": "border-fit:repeat;",
    +			"bdf:sc": "border-fit:scale;",
    +			"bdf:st": "border-fit:stretch;",
    +			"bdf:ow": "border-fit:overwrite;",
    +			"bdf:of": "border-fit:overflow;",
    +			"bdf:sp": "border-fit:space;",
    +			"bdl": "border-length:|;",
    +			"bdl:a": "border-length:auto;",
    +			"bdsp": "border-spacing:|;",
    +			"bds": "border-style:|;",
    +			"bds:n": "border-style:none;",
    +			"bds:h": "border-style:hidden;",
    +			"bds:dt": "border-style:dotted;",
    +			"bds:ds": "border-style:dashed;",
    +			"bds:s": "border-style:solid;",
    +			"bds:db": "border-style:double;",
    +			"bds:dtds": "border-style:dot-dash;",
    +			"bds:dtdtds": "border-style:dot-dot-dash;",
    +			"bds:w": "border-style:wave;",
    +			"bds:g": "border-style:groove;",
    +			"bds:r": "border-style:ridge;",
    +			"bds:i": "border-style:inset;",
    +			"bds:o": "border-style:outset;",
    +			"bdw": "border-width:|;",
    +			"bdt": "border-top:|;",
    +			"bdt+": "border-top:1px solid #000;",
    +			"bdt:n": "border-top:none;",
    +			"bdtw": "border-top-width:|;",
    +			"bdts": "border-top-style:|;",
    +			"bdts:n": "border-top-style:none;",
    +			"bdtc": "border-top-color:#000;",
    +			"bdr": "border-right:|;",
    +			"bdr+": "border-right:1px solid #000;",
    +			"bdr:n": "border-right:none;",
    +			"bdrw": "border-right-width:|;",
    +			"bdrs": "border-right-style:|;",
    +			"bdrs:n": "border-right-style:none;",
    +			"bdrc": "border-right-color:#000;",
    +			"bdb": "border-bottom:|;",
    +			"bdb+": "border-bottom:1px solid #000;",
    +			"bdb:n": "border-bottom:none;",
    +			"bdbw": "border-bottom-width:|;",
    +			"bdbs": "border-bottom-style:|;",
    +			"bdbs:n": "border-bottom-style:none;",
    +			"bdbc": "border-bottom-color:#000;",
    +			"bdl": "border-left:|;",
    +			"bdl+": "border-left:1px solid #000;",
    +			"bdl:n": "border-left:none;",
    +			"bdlw": "border-left-width:|;",
    +			"bdls": "border-left-style:|;",
    +			"bdls:n": "border-left-style:none;",
    +			"bdlc": "border-left-color:#000;",
    +			"bdrs": "border-radius:|;",
    +			"bdtrrs": "border-top-right-radius:|;",
    +			"bdtlrs": "border-top-left-radius:|;",
    +			"bdbrrs": "border-bottom-right-radius:|;",
    +			"bdblrs": "border-bottom-left-radius:|;",
    +			"bg": "background:|;",
    +			"bg+": "background:#FFF url(|) 0 0 no-repeat;",
    +			"bg:n": "background:none;",
    +			"bg:ie": "filter:progid:DXImageTransform.Microsoft.AlphaImageLoader(src='|x.png');",
    +			"bgc": "background-color:#FFF;",
    +			"bgi": "background-image:url(|);",
    +			"bgi:n": "background-image:none;",
    +			"bgr": "background-repeat:|;",
    +			"bgr:n": "background-repeat:no-repeat;",
    +			"bgr:x": "background-repeat:repeat-x;",
    +			"bgr:y": "background-repeat:repeat-y;",
    +			"bga": "background-attachment:|;",
    +			"bga:f": "background-attachment:fixed;",
    +			"bga:s": "background-attachment:scroll;",
    +			"bgp": "background-position:0 0;",
    +			"bgpx": "background-position-x:|;",
    +			"bgpy": "background-position-y:|;",
    +			"bgbk": "background-break:|;",
    +			"bgbk:bb": "background-break:bounding-box;",
    +			"bgbk:eb": "background-break:each-box;",
    +			"bgbk:c": "background-break:continuous;",
    +			"bgcp": "background-clip:|;",
    +			"bgcp:bb": "background-clip:border-box;",
    +			"bgcp:pb": "background-clip:padding-box;",
    +			"bgcp:cb": "background-clip:content-box;",
    +			"bgcp:nc": "background-clip:no-clip;",
    +			"bgo": "background-origin:|;",
    +			"bgo:pb": "background-origin:padding-box;",
    +			"bgo:bb": "background-origin:border-box;",
    +			"bgo:cb": "background-origin:content-box;",
    +			"bgz": "background-size:|;",
    +			"bgz:a": "background-size:auto;",
    +			"bgz:ct": "background-size:contain;",
    +			"bgz:cv": "background-size:cover;",
    +			"c": "color:#000;",
    +			"tbl": "table-layout:|;",
    +			"tbl:a": "table-layout:auto;",
    +			"tbl:f": "table-layout:fixed;",
    +			"cps": "caption-side:|;",
    +			"cps:t": "caption-side:top;",
    +			"cps:b": "caption-side:bottom;",
    +			"ec": "empty-cells:|;",
    +			"ec:s": "empty-cells:show;",
    +			"ec:h": "empty-cells:hide;",
    +			"lis": "list-style:|;",
    +			"lis:n": "list-style:none;",
    +			"lisp": "list-style-position:|;",
    +			"lisp:i": "list-style-position:inside;",
    +			"lisp:o": "list-style-position:outside;",
    +			"list": "list-style-type:|;",
    +			"list:n": "list-style-type:none;",
    +			"list:d": "list-style-type:disc;",
    +			"list:c": "list-style-type:circle;",
    +			"list:s": "list-style-type:square;",
    +			"list:dc": "list-style-type:decimal;",
    +			"list:dclz": "list-style-type:decimal-leading-zero;",
    +			"list:lr": "list-style-type:lower-roman;",
    +			"list:ur": "list-style-type:upper-roman;",
    +			"lisi": "list-style-image:|;",
    +			"lisi:n": "list-style-image:none;",
    +			"q": "quotes:|;",
    +			"q:n": "quotes:none;",
    +			"q:ru": "quotes:'\00AB' '\00BB' '\201E' '\201C';",
    +			"q:en": "quotes:'\201C' '\201D' '\2018' '\2019';",
    +			"ct": "content:|;",
    +			"ct:n": "content:normal;",
    +			"ct:oq": "content:open-quote;",
    +			"ct:noq": "content:no-open-quote;",
    +			"ct:cq": "content:close-quote;",
    +			"ct:ncq": "content:no-close-quote;",
    +			"ct:a": "content:attr(|);",
    +			"ct:c": "content:counter(|);",
    +			"ct:cs": "content:counters(|);",
    +			"coi": "counter-increment:|;",
    +			"cor": "counter-reset:|;",
    +			"va": "vertical-align:|;",
    +			"va:sup": "vertical-align:super;",
    +			"va:t": "vertical-align:top;",
    +			"va:tt": "vertical-align:text-top;",
    +			"va:m": "vertical-align:middle;",
    +			"va:bl": "vertical-align:baseline;",
    +			"va:b": "vertical-align:bottom;",
    +			"va:tb": "vertical-align:text-bottom;",
    +			"va:sub": "vertical-align:sub;",
    +			"ta": "text-align:|;",
    +			"ta:l": "text-align:left;",
    +			"ta:c": "text-align:center;",
    +			"ta:r": "text-align:right;",
    +			"tal": "text-align-last:|;",
    +			"tal:a": "text-align-last:auto;",
    +			"tal:l": "text-align-last:left;",
    +			"tal:c": "text-align-last:center;",
    +			"tal:r": "text-align-last:right;",
    +			"td": "text-decoration:|;",
    +			"td:n": "text-decoration:none;",
    +			"td:u": "text-decoration:underline;",
    +			"td:o": "text-decoration:overline;",
    +			"td:l": "text-decoration:line-through;",
    +			"te": "text-emphasis:|;",
    +			"te:n": "text-emphasis:none;",
    +			"te:ac": "text-emphasis:accent;",
    +			"te:dt": "text-emphasis:dot;",
    +			"te:c": "text-emphasis:circle;",
    +			"te:ds": "text-emphasis:disc;",
    +			"te:b": "text-emphasis:before;",
    +			"te:a": "text-emphasis:after;",
    +			"th": "text-height:|;",
    +			"th:a": "text-height:auto;",
    +			"th:f": "text-height:font-size;",
    +			"th:t": "text-height:text-size;",
    +			"th:m": "text-height:max-size;",
    +			"ti": "text-indent:|;",
    +			"ti:-": "text-indent:-9999px;",
    +			"tj": "text-justify:|;",
    +			"tj:a": "text-justify:auto;",
    +			"tj:iw": "text-justify:inter-word;",
    +			"tj:ii": "text-justify:inter-ideograph;",
    +			"tj:ic": "text-justify:inter-cluster;",
    +			"tj:d": "text-justify:distribute;",
    +			"tj:k": "text-justify:kashida;",
    +			"tj:t": "text-justify:tibetan;",
    +			"to": "text-outline:|;",
    +			"to+": "text-outline:0 0 #000;",
    +			"to:n": "text-outline:none;",
    +			"tr": "text-replace:|;",
    +			"tr:n": "text-replace:none;",
    +			"tt": "text-transform:|;",
    +			"tt:n": "text-transform:none;",
    +			"tt:c": "text-transform:capitalize;",
    +			"tt:u": "text-transform:uppercase;",
    +			"tt:l": "text-transform:lowercase;",
    +			"tw": "text-wrap:|;",
    +			"tw:n": "text-wrap:normal;",
    +			"tw:no": "text-wrap:none;",
    +			"tw:u": "text-wrap:unrestricted;",
    +			"tw:s": "text-wrap:suppress;",
    +			"tsh": "text-shadow:|;",
    +			"tsh+": "text-shadow:0 0 0 #000;",
    +			"tsh:n": "text-shadow:none;",
    +			"lh": "line-height:|;",
    +			"whs": "white-space:|;",
    +			"whs:n": "white-space:normal;",
    +			"whs:p": "white-space:pre;",
    +			"whs:nw": "white-space:nowrap;",
    +			"whs:pw": "white-space:pre-wrap;",
    +			"whs:pl": "white-space:pre-line;",
    +			"whsc": "white-space-collapse:|;",
    +			"whsc:n": "white-space-collapse:normal;",
    +			"whsc:k": "white-space-collapse:keep-all;",
    +			"whsc:l": "white-space-collapse:loose;",
    +			"whsc:bs": "white-space-collapse:break-strict;",
    +			"whsc:ba": "white-space-collapse:break-all;",
    +			"wob": "word-break:|;",
    +			"wob:n": "word-break:normal;",
    +			"wob:k": "word-break:keep-all;",
    +			"wob:l": "word-break:loose;",
    +			"wob:bs": "word-break:break-strict;",
    +			"wob:ba": "word-break:break-all;",
    +			"wos": "word-spacing:|;",
    +			"wow": "word-wrap:|;",
    +			"wow:nm": "word-wrap:normal;",
    +			"wow:n": "word-wrap:none;",
    +			"wow:u": "word-wrap:unrestricted;",
    +			"wow:s": "word-wrap:suppress;",
    +			"lts": "letter-spacing:|;",
    +			"f": "font:|;",
    +			"f+": "font:1em Arial,sans-serif;",
    +			"fw": "font-weight:|;",
    +			"fw:n": "font-weight:normal;",
    +			"fw:b": "font-weight:bold;",
    +			"fw:br": "font-weight:bolder;",
    +			"fw:lr": "font-weight:lighter;",
    +			"fs": "font-style:|;",
    +			"fs:n": "font-style:normal;",
    +			"fs:i": "font-style:italic;",
    +			"fs:o": "font-style:oblique;",
    +			"fv": "font-variant:|;",
    +			"fv:n": "font-variant:normal;",
    +			"fv:sc": "font-variant:small-caps;",
    +			"fz": "font-size:|;",
    +			"fza": "font-size-adjust:|;",
    +			"fza:n": "font-size-adjust:none;",
    +			"ff": "font-family:|;",
    +			"ff:s": "font-family:serif;",
    +			"ff:ss": "font-family:sans-serif;",
    +			"ff:c": "font-family:cursive;",
    +			"ff:f": "font-family:fantasy;",
    +			"ff:m": "font-family:monospace;",
    +			"fef": "font-effect:|;",
    +			"fef:n": "font-effect:none;",
    +			"fef:eg": "font-effect:engrave;",
    +			"fef:eb": "font-effect:emboss;",
    +			"fef:o": "font-effect:outline;",
    +			"fem": "font-emphasize:|;",
    +			"femp": "font-emphasize-position:|;",
    +			"femp:b": "font-emphasize-position:before;",
    +			"femp:a": "font-emphasize-position:after;",
    +			"fems": "font-emphasize-style:|;",
    +			"fems:n": "font-emphasize-style:none;",
    +			"fems:ac": "font-emphasize-style:accent;",
    +			"fems:dt": "font-emphasize-style:dot;",
    +			"fems:c": "font-emphasize-style:circle;",
    +			"fems:ds": "font-emphasize-style:disc;",
    +			"fsm": "font-smooth:|;",
    +			"fsm:a": "font-smooth:auto;",
    +			"fsm:n": "font-smooth:never;",
    +			"fsm:aw": "font-smooth:always;",
    +			"fst": "font-stretch:|;",
    +			"fst:n": "font-stretch:normal;",
    +			"fst:uc": "font-stretch:ultra-condensed;",
    +			"fst:ec": "font-stretch:extra-condensed;",
    +			"fst:c": "font-stretch:condensed;",
    +			"fst:sc": "font-stretch:semi-condensed;",
    +			"fst:se": "font-stretch:semi-expanded;",
    +			"fst:e": "font-stretch:expanded;",
    +			"fst:ee": "font-stretch:extra-expanded;",
    +			"fst:ue": "font-stretch:ultra-expanded;",
    +			"op": "opacity:|;",
    +			"op:ie": "filter:progid:DXImageTransform.Microsoft.Alpha(Opacity=100);",
    +			"op:ms": "-ms-filter:'progid:DXImageTransform.Microsoft.Alpha(Opacity=100)';",
    +			"rz": "resize:|;",
    +			"rz:n": "resize:none;",
    +			"rz:b": "resize:both;",
    +			"rz:h": "resize:horizontal;",
    +			"rz:v": "resize:vertical;",
    +			"cur": "cursor:|;",
    +			"cur:a": "cursor:auto;",
    +			"cur:d": "cursor:default;",
    +			"cur:c": "cursor:crosshair;",
    +			"cur:ha": "cursor:hand;",
    +			"cur:he": "cursor:help;",
    +			"cur:m": "cursor:move;",
    +			"cur:p": "cursor:pointer;",
    +			"cur:t": "cursor:text;",
    +			"pgbb": "page-break-before:|;",
    +			"pgbb:au": "page-break-before:auto;",
    +			"pgbb:al": "page-break-before:always;",
    +			"pgbb:l": "page-break-before:left;",
    +			"pgbb:r": "page-break-before:right;",
    +			"pgbi": "page-break-inside:|;",
    +			"pgbi:au": "page-break-inside:auto;",
    +			"pgbi:av": "page-break-inside:avoid;",
    +			"pgba": "page-break-after:|;",
    +			"pgba:au": "page-break-after:auto;",
    +			"pgba:al": "page-break-after:always;",
    +			"pgba:l": "page-break-after:left;",
    +			"pgba:r": "page-break-after:right;",
    +			"orp": "orphans:|;",
    +			"wid": "widows:|;"
    +		}
    +	},
    +	
    +	'html': {
    +		'snippets': {
    +			'cc:ie6': '<!--[if lte IE 6]>\n\t${child}|\n<![endif]-->',
    +			'cc:ie': '<!--[if IE]>\n\t${child}|\n<![endif]-->',
    +			'cc:noie': '<!--[if !IE]><!-->\n\t${child}|\n<!--<![endif]-->',
    +			'html:4t': '<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">\n' +
    +					'<html lang="${lang}">\n' +
    +					'<head>\n' +
    +					'	<title></title>\n' +
    +					'	<meta http-equiv="Content-Type" content="text/html;charset=${charset}">\n' +
    +					'</head>\n' +
    +					'<body>\n\t${child}|\n</body>\n' +
    +					'</html>',
    +			
    +			'html:4s': '<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">\n' +
    +					'<html lang="${lang}">\n' +
    +					'<head>\n' +
    +					'	<title></title>\n' +
    +					'	<meta http-equiv="Content-Type" content="text/html;charset=${charset}">\n' +
    +					'</head>\n' +
    +					'<body>\n\t${child}|\n</body>\n' +
    +					'</html>',
    +			
    +			'html:xt': '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">\n' +
    +					'<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="${lang}">\n' +
    +					'<head>\n' +
    +					'	<title></title>\n' +
    +					'	<meta http-equiv="Content-Type" content="text/html;charset=${charset}" />\n' +
    +					'</head>\n' +
    +					'<body>\n\t${child}|\n</body>\n' +
    +					'</html>',
    +			
    +			'html:xs': '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">\n' +
    +					'<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="${lang}">\n' +
    +					'<head>\n' +
    +					'	<title></title>\n' +
    +					'	<meta http-equiv="Content-Type" content="text/html;charset=${charset}" />\n' +
    +					'</head>\n' +
    +					'<body>\n\t${child}|\n</body>\n' +
    +					'</html>',
    +			
    +			'html:xxs': '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">\n' +
    +					'<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="${lang}">\n' +
    +					'<head>\n' +
    +					'	<title></title>\n' +
    +					'	<meta http-equiv="Content-Type" content="text/html;charset=${charset}" />\n' +
    +					'</head>\n' +
    +					'<body>\n\t${child}|\n</body>\n' +
    +					'</html>',
    +			
    +			'html:5': '<!DOCTYPE HTML>\n' +
    +					'<html lang="${locale}">\n' +
    +					'<head>\n' +
    +					'	<title></title>\n' +
    +					'	<meta charset="${charset}">\n' +
    +					'</head>\n' +
    +					'<body>\n\t${child}|\n</body>\n' +
    +					'</html>',
    +            
    +            'more': '\n<!--more-->\n'
    +		},
    +		
    +		'abbreviations': {
    +			'a': '<a href=""></a>',
    +			'a:link': '<a href="http://|"></a>',
    +			'a:mail': '<a href="mailto:|"></a>',
    +			'abbr': '<abbr title=""></abbr>',
    +			'acronym': '<acronym title=""></acronym>',
    +			'base': '<base href="" />',
    +			'bdo': '<bdo dir=""></bdo>',
    +			'bdo:r': '<bdo dir="rtl"></bdo>',
    +			'bdo:l': '<bdo dir="ltr"></bdo>',
    +			'link:css': '<link rel="stylesheet" type="text/css" href="|style.css" media="all" />',
    +			'link:print': '<link rel="stylesheet" type="text/css" href="|print.css" media="print" />',
    +			'link:favicon': '<link rel="shortcut icon" type="image/x-icon" href="|favicon.ico" />',
    +			'link:touch': '<link rel="apple-touch-icon" href="|favicon.png" />',
    +			'link:rss': '<link rel="alternate" type="application/rss+xml" title="RSS" href="|rss.xml" />',
    +			'link:atom': '<link rel="alternate" type="application/atom+xml" title="Atom" href="atom.xml" />',
    +			'meta:utf': '<meta http-equiv="Content-Type" content="text/html;charset=UTF-8" />',
    +			'meta:win': '<meta http-equiv="Content-Type" content="text/html;charset=windows-1251" />',
    +			'meta:compat': '<meta http-equiv="X-UA-Compatible" content="IE=7" />',
    +			'style': '<style type="text/css"></style>',
    +			'script': '<script type="text/javascript"></script>',
    +			'script:src': '<script type="text/javascript" src=""></script>',
    +			'img': '<img src="" alt="" />',
    +			'iframe': '<iframe src="" frameborder="0"></iframe>',
    +			'embed': '<embed src="" type="" />',
    +			'object': '<object data="" type=""></object>',
    +			'param': '<param name="" value="" />',
    +			'map': '<map name=""></map>',
    +			'area': '<area shape="" coords="" href="" alt="" />',
    +			'area:d': '<area shape="default" href="" alt="" />',
    +			'area:c': '<area shape="circle" coords="" href="" alt="" />',
    +			'area:r': '<area shape="rect" coords="" href="" alt="" />',
    +			'area:p': '<area shape="poly" coords="" href="" alt="" />',
    +			'link': '<link rel="stylesheet" href="" />',
    +			'form': '<form action=""></form>',
    +			'form:get': '<form action="" method="get"></form>',
    +			'form:post': '<form action="" method="post"></form>',
    +			'label': '<label for=""></label>',
    +			'input': '<input type="" />',
    +			'input:hidden': '<input type="hidden" name="" />',
    +			'input:h': '<input type="hidden" name="" />',
    +			'input:text': '<input type="text" name="" id="" />',
    +			'input:t': '<input type="text" name="" id="" />',
    +			'input:search': '<input type="search" name="" id="" />',
    +			'input:email': '<input type="email" name="" id="" />',
    +			'input:url': '<input type="url" name="" id="" />',
    +			'input:password': '<input type="password" name="" id="" />',
    +			'input:p': '<input type="password" name="" id="" />',
    +			'input:datetime': '<input type="datetime" name="" id="" />',
    +			'input:date': '<input type="date" name="" id="" />',
    +			'input:datetime-local': '<input type="datetime-local" name="" id="" />',
    +			'input:month': '<input type="month" name="" id="" />',
    +			'input:week': '<input type="week" name="" id="" />',
    +			'input:time': '<input type="time" name="" id="" />',
    +			'input:number': '<input type="number" name="" id="" />',
    +			'input:color': '<input type="color" name="" id="" />',
    +			'input:checkbox': '<input type="checkbox" name="" id="" />',
    +			'input:c': '<input type="checkbox" name="" id="" />',
    +			'input:radio': '<input type="radio" name="" id="" />',
    +			'input:r': '<input type="radio" name="" id="" />',
    +			'input:range': '<input type="range" name="" id="" />',
    +			'input:file': '<input type="file" name="" id="" />',
    +			'input:f': '<input type="file" name="" id="" />',
    +			'input:submit': '<input type="submit" value="" />',
    +			'input:s': '<input type="submit" value="" />',
    +			'input:image': '<input type="image" src="" alt="" />',
    +			'input:i': '<input type="image" src="" alt="" />',
    +			'input:reset': '<input type="reset" value="" />',
    +			'input:button': '<input type="button" value="" />',
    +			'input:b': '<input type="button" value="" />',
    +			'select': '<select name="" id=""></select>',
    +			'option': '<option value=""></option>',
    +			'textarea': '<textarea name="" id="" cols="30" rows="10"></textarea>',
    +			'menu:context': '<menu type="context"></menu>',
    +			'menu:c': '<menu type="context"></menu>',
    +			'menu:toolbar': '<menu type="toolbar"></menu>',
    +			'menu:t': '<menu type="toolbar"></menu>',
    +			'video': '<video src=""></video>',
    +			'audio': '<audio src=""></audio>',
    +			'html:xml': '<html xmlns="http://www.w3.org/1999/xhtml"></html>',
    +			'bq': '<blockquote></blockquote>',
    +			'acr': '<acronym></acronym>',
    +			'fig': '<figure></figure>',
    +			'ifr': '<iframe></iframe>',
    +			'emb': '<embed></embed>',
    +			'obj': '<object></object>',
    +			'src': '<source></source>',
    +			'cap': '<caption></caption>',
    +			'colg': '<colgroup></colgroup>',
    +			'fst': '<fieldset></fieldset>',
    +			'btn': '<button></button>',
    +			'optg': '<optgroup></optgroup>',
    +			'opt': '<option></option>',
    +			'tarea': '<textarea></textarea>',
    +			'leg': '<legend></legend>',
    +			'sect': '<section></section>',
    +			'art': '<article></article>',
    +			'hdr': '<header></header>',
    +			'ftr': '<footer></footer>',
    +			'adr': '<address></address>',
    +			'dlg': '<dialog></dialog>',
    +			'str': '<strong></strong>',
    +			'prog': '<progress></progress>',
    +			'fset': '<fieldset></fieldset>',
    +			'datag': '<datagrid></datagrid>',
    +			'datal': '<datalist></datalist>',
    +			'kg': '<keygen></keygen>',
    +			'out': '<output></output>',
    +			'det': '<details></details>',
    +			'cmd': '<command></command>',
    +			
    +			// expandos
    +			'ol+': 'ol>li',
    +			'ul+': 'ul>li',
    +			'dl+': 'dl>dt+dd',
    +			'map+': 'map>area',
    +			'table+': 'table>tr>td',
    +			'colgroup+': 'colgroup>col',
    +			'colg+': 'colgroup>col',
    +			'tr+': 'tr>td',
    +			'select+': 'select>option',
    +			'optgroup+': 'optgroup>option',
    +			'optg+': 'optgroup>option'
    +
    +		},
    +		
    +		'element_types': {
    +			'empty': 'area,base,basefont,br,col,frame,hr,img,input,isindex,link,meta,param,embed,keygen,command',
    +			'block_level': 'address,applet,blockquote,button,center,dd,del,dir,div,dl,dt,fieldset,form,frameset,hr,iframe,ins,isindex,li,link,map,menu,noframes,noscript,object,ol,p,pre,script,table,tbody,td,tfoot,th,thead,tr,ul,h1,h2,h3,h4,h5,h6',
    +			'inline_level': 'a,abbr,acronym,applet,b,basefont,bdo,big,br,button,cite,code,del,dfn,em,font,i,iframe,img,input,ins,kbd,label,map,object,q,s,samp,script,select,small,span,strike,strong,sub,sup,textarea,tt,u,var'
    +		}
    +	},
    +	
    +	'xsl': {
    +		'extends': 'html', 
    +		'abbreviations': {
    +			'tm': '<xsl:template match="" mode=""></xsl:template>',
    +			'tmatch': 'tm',
    +			'tn': '<xsl:template name=""></xsl:template>',
    +			'tname': 'tn',
    +			'xsl:when': '<xsl:when test=""></xsl:when>',
    +			'wh': 'xsl:when',
    +			'var': '<xsl:variable name="">|</xsl:variable>',
    +			'vare': '<xsl:variable name="" select=""/>',
    +			'if': '<xsl:if test=""></xsl:if>',
    +			'call': '<xsl:call-template name=""/>',
    +			'attr': '<xsl:attribute name=""></xsl:attribute>',
    +			'wp': '<xsl:with-param name="" select=""/>',
    +			'par': '<xsl:param name="" select=""/>',
    +			'val': '<xsl:value-of select=""/>',
    +			'co': '<xsl:copy-of select=""/>',
    +			'each': '<xsl:for-each select=""></xsl:for-each>',
    +			'ap': '<xsl:apply-templates select="" mode=""/>',
    +			
    +			//expandos
    +			'choose+': 'xsl:choose>xsl:when+xsl:otherwise'
    +		}
    +	}
    +};/**
    + * @author Sergey Chikuyonok (serge.che@gmail.com)
    + * @link http://chikuyonok.ru
    + */
    (function(){
    +	// Regular Expressions for parsing tags and attributes
    +	var start_tag = /^<([\w\:\-]+)((?:\s+[\w\-:]+(?:\s*=\s*(?:(?:"[^"]*")|(?:'[^']*')|[^>\s]+))?)*)\s*(\/?)>/,
    +		end_tag = /^<\/([\w\:\-]+)[^>]*>/,
    +		attr = /([\w\-:]+)(?:\s*=\s*(?:(?:"((?:\\.|[^"])*)")|(?:'((?:\\.|[^'])*)')|([^>\s]+)))?/g;
    +		
    +	// Empty Elements - HTML 4.01
    +	var empty = makeMap("area,base,basefont,br,col,frame,hr,img,input,isindex,link,meta,param,embed");
    +
    +	// Block Elements - HTML 4.01
    +	var block = makeMap("address,applet,blockquote,button,center,dd,dir,div,dl,dt,fieldset,form,frameset,hr,iframe,isindex,li,map,menu,noframes,noscript,object,ol,p,pre,script,table,tbody,td,tfoot,th,thead,tr,ul");
    +
    +	// Inline Elements - HTML 4.01
    +	var inline = makeMap("a,abbr,acronym,applet,b,basefont,bdo,big,br,button,cite,code,del,dfn,em,font,i,iframe,img,input,ins,kbd,label,map,object,q,s,samp,select,small,span,strike,strong,sub,sup,textarea,tt,u,var");
    +
    +	// Elements that you can, intentionally, leave open
    +	// (and which close themselves)
    +	var close_self = makeMap("colgroup,dd,dt,li,options,p,td,tfoot,th,thead,tr");
    +	
    +	/** Last matched HTML pair */
    +	var last_match = {
    +		opening_tag: null, // tag() or comment() object
    +		closing_tag: null, // tag() or comment() object
    +		start_ix: -1,
    +		end_ix: -1
    +	};
    +	
    +	function tag(match, ix) {
    +		var name = match[1].toLowerCase();
    +		return  {
    +			name: name,
    +			full_tag: match[0],
    +			start: ix,
    +			end: ix + match[0].length,
    +			unary: Boolean(match[3]) || (name in empty),
    +			type: 'tag',
    +			close_self: (name in close_self)
    +		};
    +	}
    +	
    +	function comment(start, end) {
    +		return {
    +			start: start,
    +			end: end,
    +			type: 'comment'
    +		};
    +	}
    +	
    +	function makeMap(str){
    +		var obj = {}, items = str.split(",");
    +		for ( var i = 0; i < items.length; i++ )
    +			obj[ items[i] ] = true;
    +		return obj;
    +	}
    +	
    +	/**
    +	 * Makes selection ranges for matched tag pair
    +	 * @param {tag} opening_tag
    +	 * @param {tag} closing_tag
    +	 * @param {Number} ix
    +	 */
    +	function makeRange(opening_tag, closing_tag, ix) {
    +		ix = ix || 0;
    +		
    +		var start_ix = -1, 
    +			end_ix = -1;
    +		
    +		if (opening_tag && !closing_tag) { // unary element
    +			start_ix = opening_tag.start;
    +			end_ix = opening_tag.end;
    +		} else if (opening_tag && closing_tag) { // complete element
    +			if (
    +				(opening_tag.start < ix && opening_tag.end > ix) || 
    +				(closing_tag.start <= ix && closing_tag.end > ix)
    +			) {
    +				start_ix = opening_tag.start;
    +				end_ix = closing_tag.end;
    +			} else {
    +				start_ix = opening_tag.end;
    +				end_ix = closing_tag.start;
    +			}
    +		}
    +		
    +		return [start_ix, end_ix];
    +	}
    +	
    +	/**
    +	 * Save matched tag for later use and return found indexes
    +	 * @param {tag} opening_tag
    +	 * @param {tag} closing_tag
    +	 * @param {Number} ix
    +	 * @return {Array}
    +	 */
    +	function saveMatch(opening_tag, closing_tag, ix) {
    +		ix = ix || 0;
    +		last_match.opening_tag = opening_tag; 
    +		last_match.closing_tag = closing_tag;
    +		
    +		var range = makeRange(opening_tag, closing_tag, ix);
    +		last_match.start_ix = range[0];
    +		last_match.end_ix = range[1];
    +		
    +		return last_match.start_ix != -1 ? [last_match.start_ix, last_match.end_ix] : null;
    +	}
    +	
    +	/**
    +	 * Search for matching tags in <code>html</code>, starting from 
    +	 * <code>start_ix</code> position
    +	 * @param {String} html Code to search
    +	 * @param {Number} start_ix Character index where to start searching pair 
    +	 * (commonly, current caret position)
    +	 * @param {Function} action Function that creates selection range
    +	 * @return {Array|null}
    +	 */
    +	function findPair(html, start_ix, action) {
    +		action = action || makeRange;
    +		
    +		var forward_stack = [],
    +			backward_stack = [],
    +			/** @type {tag()} */
    +			opening_tag = null,
    +			/** @type {tag()} */
    +			closing_tag = null,
    +			range = null,
    +			html_len = html.length,
    +			m,
    +			ix,
    +			tmp_tag;
    +			
    +		forward_stack.last = backward_stack.last = function() {
    +			return this[this.length - 1];
    +		}
    +		
    +		function hasMatch(str, start) {
    +			if (arguments.length == 1)
    +				start = ix;
    +			return html.substr(start, str.length) == str;
    +		}
    +		
    +		function searchCommentStart(from) {
    +			while (from--) {
    +				if (html.charAt(from) == '<' && hasMatch('<!--', from))
    +					break;
    +			}
    +			
    +			return from;
    +		}
    +		
    +		// find opening tag
    +		ix = start_ix;
    +		while (ix-- && ix >= 0) {
    +			var ch = html.charAt(ix);
    +			if (ch == '<') {
    +				var check_str = html.substring(ix, html_len);
    +				
    +				if ( (m = check_str.match(end_tag)) ) { // found closing tag
    +					tmp_tag = tag(m, ix);
    +					if (tmp_tag.start < start_ix && tmp_tag.end > start_ix) // direct hit on searched closing tag
    +						closing_tag = tmp_tag;
    +					else
    +						backward_stack.push(tmp_tag);
    +				} else if ( (m = check_str.match(start_tag)) ) { // found opening tag
    +					tmp_tag = tag(m, ix);
    +					
    +					if (tmp_tag.unary) {
    +						if (tmp_tag.start < start_ix && tmp_tag.end > start_ix) // exact match
    +							return saveMatch(tmp_tag, null, start_ix);
    +					} else if (backward_stack.last() && backward_stack.last().name == tmp_tag.name) {
    +						backward_stack.pop();
    +					} else { // found nearest unclosed tag
    +						opening_tag = tmp_tag;
    +						break;
    +					}
    +				} else if (check_str.indexOf('<!--') == 0) { // found comment start
    +					var end_ix = check_str.search('-->') + ix + 3;
    +					if (ix < start_ix && end_ix >= start_ix)
    +						return saveMatch( comment(ix, end_ix) );
    +				}
    +			} else if (ch == '-' && hasMatch('-->')) { // found comment end
    +				// search left until comment start is reached
    +				ix = searchCommentStart(ix);
    +			}
    +		}
    +		
    +		if (!opening_tag)
    +			return action(null);
    +		
    +		// find closing tag
    +		if (!closing_tag) {
    +			for (ix = start_ix; ix < html_len; ix++) {
    +				var ch = html.charAt(ix);
    +				if (ch == '<') {
    +					var check_str = html.substring(ix, html_len);
    +					
    +					if ( (m = check_str.match(start_tag)) ) { // found opening tag
    +						tmp_tag = tag(m, ix);
    +						if (!tmp_tag.unary)
    +							forward_stack.push( tmp_tag );
    +					} else if ( (m = check_str.match(end_tag)) ) { // found closing tag
    +						var tmp_tag = tag(m, ix);
    +						if (forward_stack.last() && forward_stack.last().name == tmp_tag.name)
    +							forward_stack.pop();
    +						else { // found matched closing tag
    +							closing_tag = tmp_tag;
    +							break;
    +						}
    +					} else if (hasMatch('<!--')) { // found comment
    +						ix += check_str.search('-->') + 3;
    +					}
    +				} else if (ch == '-' && hasMatch('-->')) {
    +					// looks like cursor was inside comment with invalid HTML
    +					if (!forward_stack.last() || forward_stack.last().type != 'comment') {
    +						var end_ix = ix + 3;
    +						return action(comment( searchCommentStart(ix), end_ix ));
    +					}
    +				}
    +			}
    +		}
    +		
    +		return action(opening_tag, closing_tag, start_ix);
    +	}
    +	
    +	/**
    +	 * Search for matching tags in <code>html</code>, starting 
    +	 * from <code>start_ix</code> position. The result is automatically saved in 
    +	 * <code>last_match</code> property
    +	 * 
    +	 * @return {Array|null}
    +	 */
    +	var HTMLPairMatcher = this.HTMLPairMatcher = function(/* String */ html, /* Number */ start_ix){
    +		return findPair(html, start_ix, saveMatch);
    +	}
    +	
    +	HTMLPairMatcher.start_tag = start_tag;
    +	HTMLPairMatcher.end_tag = end_tag;
    +	
    +	/**
    +	 * Search for matching tags in <code>html</code>, starting from 
    +	 * <code>start_ix</code> position. The difference between 
    +	 * <code>HTMLPairMatcher</code> function itself is that <code>find</code> 
    +	 * method doesn't save matched result in <code>last_match</code> property.
    +	 * This method is generally used for lookups 
    +	 */
    +	HTMLPairMatcher.find = function(html, start_ix) {
    +		return findPair(html, start_ix);
    +	};
    +	
    +	/**
    +	 * Search for matching tags in <code>html</code>, starting from 
    +	 * <code>start_ix</code> position. The difference between 
    +	 * <code>HTMLPairMatcher</code> function itself is that <code>getTags</code> 
    +	 * method doesn't save matched result in <code>last_match</code> property 
    +	 * and returns array of opening and closing tags
    +	 * This method is generally used for lookups 
    +	 */
    +	HTMLPairMatcher.getTags = function(html, start_ix) {
    +		return findPair(html, start_ix, function(opening_tag, closing_tag){
    +			return [opening_tag, closing_tag];
    +		});
    +	};
    +	
    +	HTMLPairMatcher.last_match = last_match;
    +})();/**
    + * @author Sergey Chikuyonok (serge.che@gmail.com)
    + * @link http://chikuyonok.ru
    + * @include "settings.js"
    + * @include "/EclipseMonkey/scripts/monkey-doc.js"
    + */
    var zen_coding = (function(){
    +	
    +	var re_tag = /<\/?[\w:\-]+(?:\s+[\w\-:]+(?:\s*=\s*(?:(?:"[^"]*")|(?:'[^']*')|[^>\s]+))?)*\s*(\/?)>$/;
    +	
    +	var TYPE_ABBREVIATION = 'zen-tag',
    +		TYPE_EXPANDO = 'zen-expando',
    +	
    +		/** Reference to another abbreviation or tag */
    +		TYPE_REFERENCE = 'zen-reference',
    +		
    +		content_placeholder = '{%::zen-content::%}',
    +		newline = '\n';
    +		
    +	var default_profile = {
    +		tag_case: 'lower',
    +		attr_case: 'lower',
    +		attr_quotes: 'double',
    +		
    +		// each tag on new line
    +		tag_nl: 'decide',
    +		
    +		place_cursor: true,
    +		
    +		// indent tags
    +		indent: true,
    +		
    +		// use self-closing style for writing empty elements, e.g. <br /> or <br>
    +		self_closing_tag: 'xhtml'
    +	};
    +	
    +	var profiles = {};
    +	
    +	/**
    +	 * Проверяет, является ли символ допустимым в аббревиатуре
    +	 * @param {String} ch
    +	 * @return {Boolean}
    +	 */
    +	function isAllowedChar(ch) {
    +		var char_code = ch.charCodeAt(0),
    +			special_chars = '#.>+*:$-_!@';
    +		
    +		return (char_code > 64 && char_code < 91)       // uppercase letter
    +				|| (char_code > 96 && char_code < 123)  // lowercase letter
    +				|| (char_code > 47 && char_code < 58)   // number
    +				|| special_chars.indexOf(ch) != -1;     // special character
    +	}
    +	
    +	/**
    +	 * Возвращает символ перевода строки, используемый в редакторе
    +	 * @return {String}
    +	 */
    +	function getNewline() {
    +		return zen_coding.getNewline();
    +	}
    +	
    +	/**
    +	 * Split text into lines. Set <code>remove_empty</code> to true to filter
    +	 * empty lines
    +	 * @param {String} text
    +	 * @param {Boolean} [remove_empty]
    +	 * @return {Array}
    +	 */
    +	function splitByLines(text, remove_empty) {
    +		
    +		// IE fails to split string by regexp, 
    +		// need to normalize newlines first
    +		var lines = text.replace(/\r\n/g, '\n').replace(/\n\r/g, '\n').split('\n');
    +		
    +//		var nl = getNewline(), 
    +//			lines = text.split(new RegExp('\\r?\\n|\\n\\r|\\r|' + nl));
    +			
    +		if (remove_empty) {
    +			for (var i = lines.length; i >= 0; i--) {
    +				if (!trim(lines[i]))
    +					lines.splice(i, 1);
    +			}
    +		}
    +		
    +		return lines;
    +	}
    +	
    +	/**
    +	 * Trim whitespace from string
    +	 * @param {String} text
    +	 * @return {String}
    +	 */
    +	function trim(text) {
    +		return (text || "").replace( /^\s+|\s+$/g, "" );
    +	}
    +	
    +	function createProfile(options) {
    +		var result = {};
    +		for (var p in default_profile)
    +			result[p] = (p in options) ? options[p] : default_profile[p];
    +		
    +		return result;
    +	}
    +	
    +	function setupProfile(name, options) {
    +		profiles[name.toLowerCase()] = createProfile(options || {});
    +	}
    +	
    +	/**
    +	 * Helper function that transforms string into hash
    +	 * @return {Object}
    +	 */
    +	function stringToHash(str){
    +		var obj = {}, items = str.split(",");
    +		for ( var i = 0; i < items.length; i++ )
    +			obj[ items[i] ] = true;
    +		return obj;
    +	}
    +	
    +	/**
    +	 * Отбивает текст отступами
    +	 * @param {String} text Текст, который нужно отбить
    +	 * @param {String|Number} pad Количество отступов или сам отступ
    +	 * @return {String}
    +	 */
    +	function padString(text, pad, verbose) {
    +		var pad_str = '', result = '';
    +		if (typeof(pad) == 'number')
    +			for (var i = 0; i < pad; i++) 
    +				pad_str += zen_settings.variables.indentation;
    +		else
    +			pad_str = pad;
    +		
    +		var lines = splitByLines(text),
    +			nl = getNewline();
    +			
    +		result += lines[0];
    +		for (var j = 1; j < lines.length; j++) 
    +			result += nl + pad_str + lines[j];
    +			
    +		return result;
    +	}
    +	
    +	/**
    +	 * Check if passed abbreviation is snippet
    +	 * @param {String} abbr
    +	 * @param {String} type
    +	 * @return {Boolean}
    +	 */
    +	function isShippet(abbr, type) {
    +		return getSnippet(type, abbr) ? true : false;
    +	}
    +	
    +	/**
    +	 * Проверяет, закачивается ли строка полноценным тэгом. В основном 
    +	 * используется для проверки принадлежности символа '>' аббревиатуре 
    +	 * или тэгу
    +	 * @param {String} str
    +	 * @return {Boolean}
    +	 */
    +	function isEndsWithTag(str) {
    +		return re_tag.test(str);
    +	}
    +	
    +	/**
    +	 * Returns specified elements collection (like 'empty', 'block_level') from
    +	 * <code>resource</code>. If collections wasn't found, returns empty object
    +	 * @param {Object} resource
    +	 * @param {String} type
    +	 * @return {Object}
    +	 */
    +	function getElementsCollection(resource, type) {
    +		if (resource && resource.element_types)
    +			return resource.element_types[type] || {}
    +		else
    +			return {};
    +	}
    +	
    +	/**
    +	 * Replace variables like ${var} in string
    +	 * @param {String} str
    +	 * @param {Object} [vars] Variable set (default is <code>zen_settings.variables</code>) 
    +	 * @return {String}
    +	 */
    +	function replaceVariables(str, vars) {
    +		vars = vars || zen_settings.variables;
    +		return str.replace(/\$\{([\w\-]+)\}/g, function(str, p1){
    +			return (p1 in vars) ? vars[p1] : str;
    +		});
    +	}
    +	
    +	/**
    +	 * Тэг
    +	 * @class
    +	 * @param {String} name Имя тэга
    +	 * @param {Number} count Сколько раз вывести тэг (по умолчанию: 1)
    +	 * @param {String} type Тип тэга (html, xml)
    +	 */
    +	function Tag(name, count, type) {
    +		name = name.toLowerCase();
    +		type = type || 'html';
    +		
    +		var abbr = getAbbreviation(type, name);
    +		if (abbr && abbr.type == TYPE_REFERENCE)
    +			abbr = getAbbreviation(type, abbr.value);
    +		
    +		this.name = (abbr) ? abbr.value.name : name.replace('+', '');
    +		this.count = count || 1;
    +		this.children = [];
    +		this.attributes = [];
    +		this._attr_hash = {};
    +		this._abbr = abbr;
    +		this._res = zen_settings[type];
    +		this._content = '';
    +		this.repeat_by_lines = false;
    +		
    +		// add default attributes
    +		if (this._abbr && this._abbr.value.attributes) {
    +			var def_attrs = this._abbr.value.attributes;
    			if (def_attrs) {
    +				for (var i = 0; i < def_attrs.length; i++) {
    +					var attr = def_attrs[i];
    +					this.addAttribute(attr.name, attr.value);
    +				}
    +			}
    +		}
    +	}
    +	
    +	Tag.prototype = {
    +		/**
    +		 * Добавляет нового потомка
    +		 * @param {Tag} tag
    +		 */
    +		addChild: function(tag) {
    +			this.children.push(tag);
    +		},
    +		
    +		/**
    +		 * Добавляет атрибут
    +		 * @param {String} name Название атрибута
    +		 * @param {String} value Значение атрибута
    +		 */
    +		addAttribute: function(name, value) {
    +			var a;
    +			if (name in this._attr_hash) {
    +				// attribute already exists, decide what to do
    +				a = this._attr_hash[name];
    +				if (name == 'class') {
    +					// 'class' is a magic attribute
    +					a.value += ((a.value) ? ' ' : '') + value;
    +				} else {
    +					a.value = value;
    +				}
    +			} else {
    +				a = {name: name, value: value};
    +				this._attr_hash[name] = a
    +				this.attributes.push(a);
    +			}
    +			
    +		},
    +		
    +		/**
    +		 * Проверяет, является ли текущий элемент пустым
    +		 * @return {Boolean}
    +		 */
    +		isEmpty: function() {
    +			return (this._abbr && this._abbr.value.is_empty) || (this.name in getElementsCollection(this._res, 'empty'));
    +		},
    +		
    +		/**
    +		 * Проверяет, является ли текущий элемент строчным
    +		 * @return {Boolean}
    +		 */
    +		isInline: function() {
    +			return (this.name in getElementsCollection(this._res, 'inline_level'));
    +		},
    +		
    +		/**
    +		 * Проверяет, является ли текущий элемент блочным
    +		 * @return {Boolean}
    +		 */
    +		isBlock: function() {
    +			return (this.name in getElementsCollection(this._res, 'block_level'));
    +		},
    +		
    +		/**
    +		 * This function tests if current tags' content contains xHTML tags. 
    +		 * This function is mostly used for output formatting
    +		 */
    +		hasTagsInContent: function() {
    +			return this.getContent() && re_tag.test(this.getContent());
    +		},
    +		
    +		/**
    +		 * Проверяет, есть ли блочные потомки у текущего тэга. 
    +		 * Используется для форматирования
    +		 * @return {Boolean}
    +		 */
    +		hasBlockChildren: function() {
    +			if (this.hasTagsInContent() && this.isBlock()) {
    +				return true;
    +			}
    +			
    +			for (var i = 0; i < this.children.length; i++) {
    +				if (this.children[i].isBlock())
    +					return true;
    +			}
    +			
    +			return false;
    +		},
    +		
    +		/**
    +		 * Set textual content for tag
    +		 * @param {String} str Tag's content
    +		 */
    +		setContent: function(str) {
    +			this._content = str;
    +		},
    +		
    +		/**
    +		 * Returns tag's textual content
    +		 * @return {String}
    +		 */
    +		getContent: function() {
    +			return this._content;
    +		},
    +		
    +		/**
    +		 * Search for deepest and latest child of current element
    +		 * @return {Tag|null} Returns null if there's no children
    +		 */
    +		findDeepestChild: function() {
    +			if (!this.children.length)
    +				return null;
    +				
    +			var deepest_child = this;
    +			while (true) {
    +				deepest_child = deepest_child.children[ deepest_child.children.length - 1 ];
    +				if (!deepest_child.children.length)
    +					break;
    +			}
    +			
    +			return deepest_child;
    +		},
    +		
    +		/**
    +		 * Transforms and formats tag into string using profile
    +		 * @param {String} profile Profile name
    +		 * @return {String}
    +		 * TODO Function is too large, need refactoring
    +		 */
    +		toString: function(profile_name) {
    +			
    +			var result = [], 
    +				profile = (profile_name in profiles) ? profiles[profile_name] : profiles['plain'],
    +				attrs = '', 
    +				content = '', 
    +				start_tag = '', 
    +				end_tag = '',
    +				cursor = profile.place_cursor ? '|' : '',
    +				self_closing = '',
    +				attr_quote = profile.attr_quotes == 'single' ? "'" : '"',
    +				attr_name,
    +				
    +				is_empty = (this.isEmpty() && !this.children.length);
    +
    +			if (profile.self_closing_tag == 'xhtml')
    +				self_closing = ' /';
    +			else if (profile.self_closing_tag === true)
    +				self_closing = '/';
    +				
    +			function allowNewline(tag) {
    +				return (profile.tag_nl === true || (profile.tag_nl == 'decide' && tag.isBlock()))
    +			}
    +				
    +			// make attribute string
    +			for (var i = 0; i < this.attributes.length; i++) {
    +				var a = this.attributes[i];
    +				attr_name = (profile.attr_case == 'upper') ? a.name.toUpperCase() : a.name.toLowerCase();
    +				attrs += ' ' + attr_name + '=' + attr_quote + (a.value || cursor) + attr_quote;
    +			}
    +			
    +			var deepest_child = this.findDeepestChild();
    +			
    +			// output children
    +			if (!is_empty) {
    +				if (deepest_child && this.repeat_by_lines)
    +					deepest_child.setContent(content_placeholder);
    +				
    +				for (var j = 0; j < this.children.length; j++) {
    +//					
    +					content += this.children[j].toString(profile_name);
    +					if (
    +						(j != this.children.length - 1) &&
    +						( allowNewline(this.children[j]) || allowNewline(this.children[j + 1]) )
    +					)
    +						content += getNewline();
    +				}
    +			}
    +			
    +			// define opening and closing tags
    +			if (this.name) {
    +				var tag_name = (profile.tag_case == 'upper') ? this.name.toUpperCase() : this.name.toLowerCase();
    +				if (is_empty) {
    +					start_tag = '<' + tag_name + attrs + self_closing + '>';
    +				} else {
    +					start_tag = '<' + tag_name + attrs + '>';
    +					end_tag = '</' + tag_name + '>';
    +				}
    +			}
    +			
    +			// formatting output
    +			if (profile.tag_nl !== false) {
    +				if (
    +					this.name && 
    +					(
    +						profile.tag_nl === true || 
    +						this.hasBlockChildren() 
    +					)
    +				) {
    +					if (end_tag) { // non-empty tag: add indentation
    +						start_tag += getNewline() + zen_settings.variables.indentation;
    +						end_tag = getNewline() + end_tag;
    +					} else { // empty tag
    +						
    +					}
    +						
    +				}
    +				
    +				if (this.name) {
    +					if (content)
    +						content = padString(content, profile.indent ? 1 : 0);
    +					else if (!is_empty)
    +						start_tag += cursor;
    +				}
    +					
    +			}
    +			
    +			// repeat tag by lines count
    +			var cur_content = '';
    +			if (this.repeat_by_lines) {
    +				var lines = splitByLines( trim(this.getContent()) , true);
    +				for (var j = 0; j < lines.length; j++) {
    +					cur_content = deepest_child ? '' : content_placeholder;
    +					if (content && !deepest_child)
    +						cur_content += getNewline();
    +						
    +					var elem_str = start_tag.replace(/\$/g, j + 1) + cur_content + content + end_tag;
    +					result.push(elem_str.replace(content_placeholder, trim(lines[j])));
    +				}
    +			}
    +			
    +			// repeat tag output
    +			if (!result.length) {
    +				if (this.getContent()) {
    +					var pad = (profile.tag_nl === true || (this.hasTagsInContent() && this.isBlock())) ? 1 : 0;
    +					content = padString(this.getContent(), pad) + content;
    +				}
    +				
    +				for (var i = 0; i < this.count; i++) 
    +					result.push(start_tag.replace(/\$/g, i + 1) + content + end_tag);
    +			}
    +			
    +			var glue = '';
    +			if (allowNewline(this))
    +				glue = getNewline();
    +				
    +			return result.join(glue);
    +		}
    +	};
    +	
    +	// TODO inherit from Tag
    +	function Snippet(name, count, type) {
    +		/** @type {String} */
    +		this.name = name;
    +		this.count = count || 1;
    +		this.children = [];
    +		this._content = '';
    +		this.repeat_by_lines = false;
    +		this.attributes = {'id': '|', 'class': '|'};
    +		this.value = getSnippet(type, name);
    +	}
    +	
    +	Snippet.prototype = {
    +		/**
    +		 * Добавляет нового потомка
    +		 * @param {Tag} tag
    +		 */
    +		addChild: function(tag) {
    +			this.children.push(tag);
    +		},
    +		
    +		addAttribute: function(name, value){
    +			this.attributes[name] = value;
    +		},
    +		
    +		isBlock: function() {
    +			return true; 
    +		},
    +		
    +		/**
    +		 * Set textual content for snippet
    +		 * @param {String} str Tag's content
    +		 */
    +		setContent: function(str) {
    +			this._content = str;
    +		},
    +		
    +		/**
    +		 * Returns snippet's textual content
    +		 * @return {String}
    +		 */
    +		getContent: function() {
    +			return this._content;
    +		},
    +		
    +		/**
    +		 * Search for deepest and latest child of current element
    +		 * @return {Tag|null} Returns null if there's no children
    +		 */
    +		findDeepestChild: function() {
    +			if (!this.children.length)
    +				return null;
    +				
    +			var deepest_child = this;
    +			while (true) {
    +				deepest_child = deepest_child.children[ deepest_child.children.length - 1 ];
    +				if (!deepest_child.children.length)
    +					break;
    +			}
    +			
    +			return deepest_child;
    +		},
    +		
    +		toString: function(profile_name) {
    +			var content = '', 
    +				profile = (profile_name in profiles) ? profiles[profile_name] : profiles['plain'],
    +				result = [],
    +				data = this.value,
    +				begin = '',
    +				end = '',
    +				child_padding = '',
    +				child_token = '${child}';
    +			
    +			if (data) {
    +				if (profile.tag_nl !== false) {
    +					var nl = getNewline();
    +					data = data.replace(/\n/g, nl);
    +					// figuring out indentation for children
    +					var lines = data.split(nl), m;
    +					for (var j = 0; j < lines.length; j++) {
    +						if (lines[j].indexOf(child_token) != -1) {
    +							child_padding =  (m = lines[j].match(/(^\s+)/)) ? m[1] : '';
    +							break;
    +						}
    +					}
    +				}
    +				
    +				var parts = data.split(child_token);
    +				begin = parts[0] || '';
    +				end = parts[1] || '';
    +			}
    +			
    +			for (var i = 0; i < this.children.length; i++) {
    +				content += this.children[i].toString(profile_name);
    +				if (
    +					i != this.children.length - 1 &&
    +					(
    +						profile.tag_nl === true || 
    +						(profile.tag_nl == 'decide' && this.children[i].isBlock())
    +					)
    +				)
    +					content += getNewline();
    +			}
    +			
    +			if (child_padding)
    +				content = padString(content, child_padding);
    +			
    +			
    +			// substitute attributes
    +			begin = replaceVariables(begin, this.attributes);
    +			end = replaceVariables(end, this.attributes);
    +				
    +			if (this.getContent()) {
    +				content = padString(this.getContent(), 1) + content;
    +			}
    +			
    +			// выводим тэг нужное количество раз
    +			for (var i = 0; i < this.count; i++) 
    +				result.push(begin + content + end);
    +//				result.push(begin.replace(/\$(?!\{)/g, i + 1) + content + end);
    +			
    +			return result.join((profile.tag_nl !== false) ? getNewline() : '');
    +		}
    +	}
    +	
    +	/**
    +	 * Returns abbreviation value from data set
    +	 * @param {String} type Resource type (html, css, ...)
    +	 * @param {String} abbr Abbreviation name
    +	 * @return {Object|null}
    +	 */
    +	function getAbbreviation(type, abbr) {
    +		return getSettingsResource(type, abbr, 'abbreviations');
    +	}
    +	
    +	/**
    +	 * Returns snippet value from data set
    +	 * @param {String} type Resource type (html, css, ...)
    +	 * @param {String} snippet_name Snippet name
    +	 * @return {Object|null}
    +	 */
    +	function getSnippet(type, snippet_name) {
    +		return getSettingsResource(type, snippet_name, 'snippets');
    +	}
    +	
    +	/**
    +	 * Returns resurce value from data set with respect of inheritance
    +	 * @param {String} type Resource type (html, css, ...)
    +	 * @param {String} abbr Abbreviation name
    +	 * @param {String} res_name Resource name ('snippets' or 'abbreviation')
    +	 * @return {Object|null}
    +	 */
    +	function getSettingsResource(type, abbr, res_name) {
    +		var resource = zen_settings[type];
    +		
    +		if (resource) {
    +			if (res_name in resource && abbr in resource[res_name])
    +				return resource[res_name][abbr];
    +			else if ('extends' in resource) {
    +				// find abbreviation in ancestors
    +				for (var i = 0; i < resource['extends'].length; i++) {
    +					var type = resource['extends'][i];
    +					if (
    +						zen_settings[type] && 
    +						zen_settings[type][res_name] && 
    +						zen_settings[type][res_name][abbr]
    +					)
    +						return zen_settings[type][res_name][abbr];
    +				}
    +			}
    +		}
    +		
    +		
    +		return null;
    +	}
    +	
    +	// create default profiles
    +	setupProfile('xhtml');
    +	setupProfile('html', {self_closing_tag: false});
    +	setupProfile('xml', {self_closing_tag: true, tag_nl: true});
    +	setupProfile('plain', {tag_nl: false, indent: false, place_cursor: false});
    +	
    +	
    +	return {
    +		expandAbbreviation: function(abbr, type, profile) {
    +			var tree = this.parseIntoTree(abbr, type || 'html');
    +			return replaceVariables(tree ? tree.toString(profile) : '');
    +		},
    +		
    +		/**
    +		 * Extracts abbreviations from text stream, starting from the end
    +		 * @param {String} str
    +		 * @return {String} Abbreviation or empty string
    +		 */
    +		extractAbbreviation: function(str) {
    +			var cur_offset = str.length,
    +				start_index = -1;
    +			
    +			while (true) {
    +				cur_offset--;
    +				if (cur_offset < 0) {
    +					// дошли до начала строки
    +					start_index = 0;
    +					break;
    +				}
    +				
    +				var ch = str.charAt(cur_offset);
    +				
    +				if (!isAllowedChar(ch) || (ch == '>' && isEndsWithTag(str.substring(0, cur_offset + 1)))) {
    +					start_index = cur_offset + 1;
    +					break;
    +				}
    +			}
    +			
    +			if (start_index != -1) 
    +				// что-то нашли, возвращаем аббревиатуру
    +				return str.substring(start_index);
    +			else
    +				return '';
    +		},
    +		
    +		/**
    +		 * Parses abbreviation into a node set
    +		 * @param {String} abbr Abbreviation
    +		 * @param {String} type Document type (xsl, html, etc.)
    +		 * @return {Tag}
    +		 */
    +		parseIntoTree: function(abbr, type) {
    +			type = type || 'html';
    +			var root = new Tag('', 1, type),
    +				parent = root,
    +				last = null,
    +				multiply_elem = null,
    +				res = zen_settings[type],
    +				re = /([\+>])?([a-z@\!][a-z0-9:\-]*)(#[\w\-\$]+)?((?:\.[\w\-\$]+)*)(\*(\d*))?(\+$)?/ig;
    +			
    +			if (!abbr)
    +				return null;
    +			
    +			// replace expandos
    +			abbr = abbr.replace(/([a-z][\w\:\-]*)\+$/i, function(str){
    +				var a = getAbbreviation(type, str);
    +				return a ? a.value : str;
    +			});
    +			
    +			abbr = abbr.replace(re, function(str, operator, tag_name, id, class_name, has_multiplier, multiplier, has_expando){
    +				var multiply_by_lines = (has_multiplier && !multiplier);
    +				multiplier = multiplier ? parseInt(multiplier) : 1;
    +				
    +				if (has_expando)
    +					tag_name += '+';
    +				
    +				var current = isShippet(tag_name, type) ? new Snippet(tag_name, multiplier, type) : new Tag(tag_name, multiplier, type);
    +				if (id)
    +					current.addAttribute('id', id.substr(1));
    +				
    +				if (class_name) 
    +					current.addAttribute('class', class_name.substr(1).replace(/\./g, ' '));
    +				
    +				
    +				// dive into tree
    +				if (operator == '>' && last)
    +					parent = last;
    +					
    +				parent.addChild(current);
    +				
    +				last = current;
    +				
    +				if (multiply_by_lines)
    +					multiply_elem = current;
    +				
    +				return '';
    +			});
    +			
    +			root.last = last;
    +			root.multiply_elem = multiply_elem;
    +			
    +			// empty 'abbr' string means that abbreviation was successfully expanded,
    +			// if not — abbreviation wasn't valid 
    +			return (!abbr) ? root : null;
    +		},
    +		
    +		/**
    +		 * Отбивает текст отступами
    +		 * @param {String} text Текст, который нужно отбить
    +		 * @param {String|Number} pad Количество отступов или сам отступ
    +		 * @return {String}
    +		 */
    +		padString: padString,
    +		setupProfile: setupProfile,
    +		getNewline: function(){
    +			return newline;
    +		},
    +		
    +		setNewline: function(str) {
    +			newline = str;
    +		},
    +		
    +		/**
    +		 * Returns range for matched tag pair inside document
    +		 * @requires HTMLParser
    +		 * @param {String} html Full xHTML document
    +		 * @param {Number} cursor_pos Cursor position inside document
    +		 * @return {Object} Pair of indicies (<code>start</code> and <code>end</code>). 
    +		 * Returns 'null' if match wasn't found 
    +		 */
    +		getPairRange: function(html, cursor_pos) {
    +			var tags = {},
    +				ranges = [],
    +				result = null;
    +				
    +			function inRange(start, end) {
    +				return cursor_pos > start && cursor_pos < end;
    +			} 
    +			
    +			var handler = {
    +				start: function(name, attrs, unary, ix_start, ix_end) {
    +					if (unary && inRange(ix_start, ix_end)) {
    +						// this is the exact range for cursor position, stop searching
    +						result = {start: ix_start, end: ix_end};
    +						this.stop = true;
    +					} else {
    +						if (!tags.hasOwnProperty(name))
    +							tags[name] = [];
    +							
    +						tags[name].push(ix_start);
    +					}
    +				},
    +				
    +				end: function(name, ix_start, ix_end) {
    +					if (tags.hasOwnProperty(name)) {
    +						var start = tags[name].pop();
    +						if (inRange(start, ix_end))
    +							ranges.push({start: start, end: ix_end});
    +					}
    +				},
    +				
    +				comment: function(data, ix_start, ix_end) {
    +					if (inRange(ix_start, ix_end)) {
    +						// this is the exact range for cursor position, stop searching
    +						result = {start: ix_start, end: ix_end};
    +						this.stop = true;
    +					}
    +				}
    +			};
    +			
    +			// scan document
    +			try {
    +				HTMLParser(html, handler);
    +			} catch(e) {}
    +			
    +			if (!result && ranges.length) {
    +				// because we have overlaped ranges only, we have to sort array by 
    +				// length: the shorter range length, the most probable match
    +				result = ranges.sort(function(a, b){
    +					return (a.end - a.start) - (b.end - b.start);
    +				})[0];
    +			}
    +			
    +			return result;
    +		},
    +		
    +		/**
    +		 * Wraps passed text with abbreviation. Text will be placed inside last
    +		 * expanded element
    +		 * @param {String} abbr Abbreviation
    +		 * @param {String} text Text to wrap
    +		 * @param {String} [type] Document type (html, xml, etc.). Default is 'html'
    +		 * @param {String} [profile] Output profile's name. Default is 'plain'
    +		 * @return {String}
    +		 */
    +		wrapWithAbbreviation: function(abbr, text, type, profile) {
    +			var tree = this.parseIntoTree(abbr, type || 'html');
    +			if (tree) {
    +				var repeat_elem = tree.multiply_elem || tree.last;
    +				repeat_elem.setContent(text);
    +				repeat_elem.repeat_by_lines = !!tree.multiply_elem;
    +				return tree.toString(profile);
    +			} else {
    +				return null;
    +			}
    +		},
    +		
    +		splitByLines: splitByLines,
    +		
    +		/**
    +		 * Check if cursor is placed inside xHTML tag
    +		 * @param {String} html Contents of the document
    +		 * @param {Number} cursor_pos Current caret position inside tag
    +		 * @return {Boolean}
    +		 */
    +		isInsideTag: function(html, cursor_pos) {
    +			var re_tag = /^<\/?\w[\w\:\-]*.*?>/;
    +			
    +			// search left to find opening brace
    +			var pos = cursor_pos;
    +			while (pos > -1) {
    +				if (html.charAt(pos) == '<') 
    +					break;
    +				pos--;
    +			}
    +			
    +			if (pos != -1) {
    +				var m = re_tag.exec(html.substring(pos));
    +				if (m && cursor_pos > pos && cursor_pos < pos + m[0].length)
    +					return true;
    +			}
    +			
    +			return false;
    +		},
    +		
    +		settings_parser: (function(){
    +			/**
    +			 * Unified object for parsed data
    +			 */
    +			function entry(type, key, value) {
    +				return {
    +					type: type,
    +					key: key,
    +					value: value
    +				};
    +			}
    +			
    +			/** Regular expression for XML tag matching */
    +			var re_tag = /^<(\w+\:?[\w\-]*)((?:\s+[\w\:\-]+\s*=\s*(['"]).*?\3)*)\s*(\/?)>/,
    +				re_attrs = /([\w\-]+)\s*=\s*(['"])(.*?)\2/g;
    +			
    +			/**
    +			 * Make expando from string
    +			 * @param {String} key
    +			 * @param {String} value
    +			 * @return {Object}
    +			 */
    +			function makeExpando(key, value) {
    +				return entry(TYPE_EXPANDO, key, value);
    +			}
    +			
    +			/**
    +			 * Make abbreviation from string
    +			 * @param {String} key Abbreviation key
    +			 * @param {String} tag_name Expanded element's tag name
    +			 * @param {String} attrs Expanded element's attributes
    +			 * @param {Boolean} is_empty Is expanded element empty or not
    +			 * @return {Object}
    +			 */
    +			function makeAbbreviation(key, tag_name, attrs, is_empty) {
    +				var result = {
    +					name: tag_name,
    +					is_empty: Boolean(is_empty)
    +				};
    +				
    +				if (attrs) {
    +					var m;
    +					result.attributes = [];
    +					while (m = re_attrs.exec(attrs)) {
    +						result.attributes.push({
    +							name: m[1],
    +							value: m[3]
    +						});
    +					}
    +				}
    +				
    +				return entry(TYPE_ABBREVIATION, key, result);
    +			}
    +			
    +			/**
    +			 * Parses all abbreviations inside object
    +			 * @param {Object} obj
    +			 */
    +			function parseAbbreviations(obj) {
    +				for (var key in obj) {
    +					var value = obj[key], m;
    +					
    +					key = trim(key);
    +					if (key.substr(-1) == '+') {
    +						// this is expando, leave 'value' as is
    +						obj[key] = makeExpando(key, value);
    +					} else if (m = re_tag.exec(value)) {
    +						obj[key] = makeAbbreviation(key, m[1], m[2], m[4] == '/');
    +					} else {
    +						// assume it's reference to another abbreviation
    +						obj[key] = entry(TYPE_REFERENCE, key, value);
    +					}
    +					
    +				}
    +			}
    +			
    +			return {
    +				/**
    +				 * Parse user's settings
    +				 * @param {Object} settings
    +				 */
    +				parse: function(settings) {
    +					for (var p in settings) {
    +						if (p == 'abbreviations')
    +							parseAbbreviations(settings[p]);
    +						else if (p == 'extends') {
    +							var ar = settings[p].split(',');
    +							for (var i = 0; i < ar.length; i++) 
    +								ar[i] = trim(ar[i]);
    +							settings[p] = ar;
    +						}
    +						else if (typeof(settings[p]) == 'object')
    +							arguments.callee(settings[p]);
    +					}
    +				},
    +				
    +				extend: function(parent, child) {
    +					for (var p in child) {
    +						if (typeof(child[p]) == 'object' && parent.hasOwnProperty(p))
    +							arguments.callee(parent[p], child[p]);
    +						else
    +							parent[p] = child[p];
    +					}
    +				},
    +				
    +				/**
    +				 * Create hash maps on certain string properties
    +				 * @param {Object} obj
    +				 */
    +				createMaps: function(obj) {
    +					for (var p in obj) {
    +						if (p == 'element_types') {
    +							for (var k in obj[p]) 
    +								obj[p][k] = stringToHash(obj[p][k]);
    +						} else if (typeof(obj[p]) == 'object') {
    +							arguments.callee(obj[p]);
    +						}
    +					}
    +				},
    +				
    +				TYPE_ABBREVIATION: TYPE_ABBREVIATION,
    +				TYPE_EXPANDO: TYPE_EXPANDO,
    +				
    +				/** Reference to another abbreviation or tag */
    +				TYPE_REFERENCE: TYPE_REFERENCE
    +			}
    +		})()
    +	}
    +	
    +})();
    +
    +if ('zen_settings' in this || zen_settings) {
    +	// first we need to expand some strings into hashes
    +	zen_coding.settings_parser.createMaps(zen_settings);
    +	if ('my_zen_settings' in this) {
    +		// we need to extend default settings with user's
    +		zen_coding.settings_parser.createMaps(my_zen_settings);
    +		zen_coding.settings_parser.extend(zen_settings, my_zen_settings);
    +	}
    +	
    +	// now we need to parse final set of settings
    +	zen_coding.settings_parser.parse(zen_settings);
    +}/**
    + * High-level editor interface which communicates with other editor (like 
    + * TinyMCE, CKEditor, etc.) or browser.
    + * Before using any of editor's methods you should initialize it with
    + * <code>editor.setTarget(elem)</code> method and pass reference to 
    + * &lt;textarea&gt; element.
    + * @example
    + * var textarea = document.getElemenetsByTagName('textarea')[0];
    + * editor.setTarget(textarea);
    + * //now you are ready to use editor object
    + * editor.getSelectionRange() 
    + * 
    + * @author Sergey Chikuyonok (serge.che@gmail.com)
    + * @link http://chikuyonok.ru
    + * @include "../../aptana/lib/zen_coding.js"
    + */
    +var zen_editor = (function(){
    +	/** @param {Element} Source element */
    +	var target = null,
    +		/** Textual placeholder that identifies cursor position in pasted text */
    +		caret_placeholder = '|';
    +	
    +		
    +	// different browser uses different newlines, so we have to figure out
    +	// native browser newline and sanitize incoming text with them
    +	var tx = document.createElement('textarea');
    +	tx.value = '\n';
    +	zen_coding.setNewline(tx.value);
    +	tx = null;
    +	
    +	/**
    +	 * Returns content of current target element
    +	 */
    +	function getContent() {
    +		return target.value || '';
    +	}
    +	
    +	/**
    +	 * Returns selection indexes from element
    +	 */
    +	function getSelectionRange() {
    +		if ('selectionStart' in target) { // W3C's DOM
    +			var length = target.selectionEnd - target.selectionStart;
    +			return {
    +				start: target.selectionStart, 
    +				end: target.selectionEnd 
    +			};
    +		} else if (document.selection) { // IE
    +			target.focus();
    +	 
    +			var range = document.selection.createRange();
    +			
    +			if (range === null) {
    +				return {
    +					start: 0, 
    +					end: getContent().length
    +				};
    +			}
    +	 
    +			var re = target.createTextRange();
    +			var rc = re.duplicate();
    +			re.moveToBookmark(range.getBookmark());
    +			rc.setEndPoint('EndToStart', re);
    +	 
    +			return {
    +				start: rc.text.length, 
    +				end: rc.text.length + range.text.length
    +			};
    +		} else {
    +			return null;
    +		}
    +	}
    +	
    +	/**
    +	 * Creates text selection on target element
    +	 * @param {Number} start
    +	 * @param {Number} end
    +	 */
    +	function createSelection(start, end) {
    +		// W3C's DOM
    +		if (typeof(end) == 'undefined')
    +			end = start;
    +			
    +		if ('setSelectionRange' in target) {
    +			target.setSelectionRange(start, end);
    +		} else if ('createTextRange' in target) {
    +			var t = target.createTextRange();
    +			
    +			t.collapse(true);
    +			var delta = zen_coding.splitByLines(getContent().substring(0, start)).length - 1;
    +			
    +			// IE has an issue with handling newlines while creating selection,
    +			// so we need to adjust start and end indexes
    +			end -= delta + zen_coding.splitByLines(getContent().substring(start, end)).length - 1;
    +			start -= delta;
    +			
    +			t.moveStart('character', start);
    +			t.moveEnd('character', end - start);
    +			t.select();
    +		}
    +	}
    +	
    +	/**
    +	 * Find start and end index of text line for <code>from</code> index
    +	 * @param {String} text 
    +	 * @param {Number} from 
    +	 */
    +	function findNewlineBounds(text, from) {
    +		var len = text.length,
    +			start = 0,
    +			end = len - 1;
    +		
    +		// search left
    +		for (var i = from - 1; i > 0; i--) {
    +			var ch = text.charAt(i);
    +			if (ch == '\n' || ch == '\r') {
    +				start = i + 1;
    +				break;
    +			}
    +		}
    +		// search right
    +		for (var j = from; j < len; j++) {
    +			var ch = text.charAt(j);
    +			if (ch == '\n' || ch == '\r') {
    +				end = j;
    +				break;
    +			}
    +		}
    +		
    +		return {start: start, end: end};
    +	}
    +	
    +	/**
    +	 * Returns current caret position
    +	 */
    +	function getCaretPos() {
    +		var selection = getSelectionRange();
    +		return selection ? selection.start : null;
    +	}
    +	
    +	/**
    +	 * Returns whitrespace padding of string
    +	 * @param {String} str String line
    +	 * @return {String}
    +	 */
    +	function getStringPadding(str) {
    +		return (str.match(/^(\s+)/) || [''])[0];
    +	}
    +	
    +	return {
    +		setTarget: function(elem) {
    +			target = elem;
    +		},
    +		
    +		getSelectionRange: getSelectionRange,
    +		createSelection: createSelection,
    +		
    +		/**
    +		 * Returns current line's start and end indexes
    +		 */
    +		getCurrentLineRange: function() {
    +			var caret_pos = getCaretPos(),
    +				content = getContent();
    +			if (caret_pos === null) return null;
    +			
    +			return findNewlineBounds(content, caret_pos);
    +		},
    +		
    +		/**
    +		 * Returns current caret position
    +		 * @return {Number}
    +		 */
    +		getCaretPos: getCaretPos,
    +		
    +		/**
    +		 * Returns content of current line
    +		 * @return {String}
    +		 */
    +		getCurrentLine: function() {
    +			var range = this.getCurrentLineRange();
    +			return range.start < range.end ? this.getContent().substring(range.start, range.end) : '';
    +		},
    +		
    +		/**
    +		 * Replace editor's content or it's part (from <code>start</code> to 
    +		 * <code>end</code> index). If <code>value</code> contains 
    +		 * <code>caret_placeholder</code>, the editor will put caret into 
    +		 * this position. If you skip <code>start</code> and <code>end</code>
    +		 * arguments, the whole target's content will be replaced with 
    +		 * <code>value</code>. 
    +		 * 
    +		 * If you pass <code>start</code> argument only,
    +		 * the <code>value</code> will be placed at <code>start</code> string 
    +		 * index of current content. 
    +		 * 
    +		 * If you pass <code>start</code> and <code>end</code> arguments,
    +		 * the corresponding substring of current target's content will be 
    +		 * replaced with <code>value</code>. 
    +		 * @param {String} value Content you want to paste
    +		 * @param {Number} [start] Start index of editor's content
    +		 * @param {Number} [end] End index of editor's content
    +		 */
    +		replaceContent: function(value, start, end) {
    +			var content = getContent(),
    +				caret_pos = getCaretPos(),
    +				has_start = typeof(start) !== 'undefined',
    +				has_end = typeof(end) !== 'undefined';
    +				
    +			// indent new value
    +			value = zen_coding.padString(value, getStringPadding(this.getCurrentLine()));
    +			
    +			// find new caret position
    +			var new_pos = value.indexOf(caret_placeholder);
    +			if (new_pos != -1) {
    +				caret_pos = (start || 0) + new_pos;
    +				value = value.split(caret_placeholder).join('');
    +			} else {
    +				caret_pos += value.length;
    +			}
    +			
    +			try {
    +				if (has_start && has_end) {
    +					content = content.substring(0, start) + value + content.substring(end);
    +				} else if (has_start) {
    +					content = content.substring(0, start) + value + content.substring(start);
    +				}
    +				
    +				target.value = content;
    +				createSelection(caret_pos, caret_pos);
    +			} catch(e){}
    +		},
    +		
    +		/**
    +		 * Returns editor's content
    +		 * @return {String}
    +		 */
    +		getContent: getContent
    +	}
    +})();
    + /**
    + * Middleware layer that communicates between editor and Zen Coding.
    + * This layer describes all available Zen Coding actions, like 
    + * "Expand Abbreviation".
    + * @author Sergey Chikuyonok (serge.che@gmail.com)
    + * @link http://chikuyonok.ru
    + * 
    + * @include "editor.js"
    + * @include "../../aptana/lib/html_matcher.js"
    + * @include "../../aptana/lib/zen_coding.js"
    + */
    +
    +/**
    + * Search for abbreviation in editor from current caret position
    + * @param {zen_editor} editor Editor instance
    + * @return {String|null}
    + */
    +function findAbbreviation(editor) {
    +	var range = editor.getSelectionRange();
    +	if (range.start != range.end) {
    +		// abbreviation is selected by user
    +		return editor.getContent().substring(range.start, range.end);
    +	}
    +	
    +	// search for new abbreviation from current caret position
    +	var cur_line = editor.getCurrentLineRange();
    +	return zen_coding.extractAbbreviation(editor.getContent().substring(cur_line.start, range.start));
    +}
    +
    +/**
    + * Find from current caret position and expand abbreviation in editor
    + * @param {zen_editor} editor Editor instance
    + * @param {String} type Syntax type (html, css, etc.)
    + * @param {String} profile_name Output profile name (html, xml, xhtml)
    + * @return {Boolean} Returns <code>true</code> if abbreviation was expanded 
    + * successfully
    + */
    +function expandAbbreviation(editor, type, profile_name) {
    +	profile_name = profile_name || 'xhtml';
    +	
    +	var caret_pos = editor.getSelectionRange().end,
    +		abbr,
    +		content = '';
    +		
    +	if ( (abbr = findAbbreviation(editor)) ) {
    +		content = zen_coding.expandAbbreviation(abbr, type, profile_name);
    +		if (content) {
    +			editor.replaceContent(content, caret_pos - abbr.length, caret_pos);
    +			return true;
    +		}
    +	}
    +	
    +	return false;
    +}
    +
    +/**
    + * A special version of <code>expandAbbreviation</code> function: if it can't
    + * find abbreviation, it will place Tab character at caret position
    + * @param {zen_editor} editor Editor instance
    + * @param {String} type Syntax type (html, css, etc.)
    + * @param {String} profile_name Output profile name (html, xml, xhtml)
    + */
    +function expandAbbreviationWithTab(editor, type, profile_name) {
    +	if (!expandAbbreviation(editor, type, profile_name))
    +		editor.replaceContent('\t', editor.getCaretPos());
    +}
    +
    +/**
    + * Find and select HTML tag pair
    + * @param {zen_editor} editor Editor instance
    + * @param {String} [direction] Direction of pair matching: 'in' or 'out'. 
    + * Default is 'out'
    + */
    +function matchPair(editor, direction) {
    +	direction = (direction || 'out').toLowerCase();
    +	
    +	var range = editor.getSelectionRange(),
    +		cursor = range.end,
    +		range_start = range.start, 
    +		range_end = range.end,
    +//		content = zen_coding.splitByLines(editor.getContent()).join('\n'),
    +		content = editor.getContent(),
    +		range = null,
    +		_r,
    +	
    +		old_open_tag = HTMLPairMatcher.last_match['opening_tag'],
    +		old_close_tag = HTMLPairMatcher.last_match['closing_tag'];
    +		
    +	if (direction == 'in' && old_open_tag && range_start != range_end) {
    +//		user has previously selected tag and wants to move inward
    +		if (!old_close_tag) {
    +//			unary tag was selected, can't move inward
    +			return false;
    +		} else if (old_open_tag.start == range_start) {
    +			if (content[old_open_tag.end] == '<') {
    +//				test if the first inward tag matches the entire parent tag's content
    +				_r = HTMLPairMatcher.find(content, old_open_tag.end + 1);
    +				if (_r[0] == old_open_tag.end && _r[1] == old_close_tag.start) {
    +					range = HTMLPairMatcher(content, old_open_tag.end + 1);
    +				} else {
    +					range = [old_open_tag.end, old_close_tag.start];
    +				}
    +			} else {
    +				range = [old_open_tag.end, old_close_tag.start];
    +			}
    +		} else {
    +			var new_cursor = content.substring(0, old_close_tag.start).indexOf('<', old_open_tag.end);
    +			var search_pos = new_cursor != -1 ? new_cursor + 1 : old_open_tag.end;
    +			range = HTMLPairMatcher(content, search_pos);
    +		}
    +	} else {
    +		range = HTMLPairMatcher(content, cursor);
    +	}
    +	
    +	if (range !== null && range[0] != -1) {
    +//		alert(range[0] + ', '+ range[1]);
    +		editor.createSelection(range[0], range[1]);
    +		return true;
    +	} else {
    +		return false;
    +	}
    +}
    +
    +/**
    + * Wraps content with abbreviation
    + * @param {zen_editor} Editor instance
    + * @param {String} type Syntax type (html, css, etc.)
    + * @param {String} profile_name Output profile name (html, xml, xhtml)
    + */
    +function wrapWithAbbreviation(editor, abbr, type, profile_name) {
    +	profile_name = profile_name || 'xhtml';
    +	
    +	var range = editor.getSelectionRange(),
    +		start_offset = range.start,
    +		end_offset = range.end,
    +		content = editor.getContent();
    +		
    +		
    +	if (!abbr)
    +		return null; 
    +	
    +	if (start_offset == end_offset) {
    +		// no selection, find tag pair
    +		range = HTMLPairMatcher(content, start_offset);
    +		
    +		if (!range || range[0] == -1) // nothing to wrap
    +			return null;
    +			
    +		start_offset = range[0];
    +		end_offset = range[1];
    +			
    +		// narrow down selection until first non-space character
    +		var re_space = /\s|\n|\r/;
    +		function isSpace(ch) {
    +			return re_space.test(ch);
    +		}
    +		
    +		while (start_offset < end_offset) {
    +			if (!isSpace(content.charAt(start_offset)))
    +				break;
    +				
    +			start_offset++;
    +		}
    +		
    +		while (end_offset > start_offset) {
    +			end_offset--;
    +			if (!isSpace(content.charAt(end_offset))) {
    +				end_offset++;
    +				break;
    +			}
    +		}
    +			
    +	}
    +	
    +	var new_content = content.substring(start_offset, end_offset),
    +		result = zen_coding.wrapWithAbbreviation(abbr, unindent(editor, new_content), type, profile_name);
    +	
    +	if (result) {
    +		editor.createSelection(end_offset);
    +		editor.replaceContent(result, start_offset, end_offset);
    +	}
    +}
    +
    +/**
    + * Unindent content, thus preparing text for tag wrapping
    + * @param {zen_editor} Editor instance
    + * @param {String} text
    + * @return {String}
    + */
    +function unindent(editor, text) {
    +	var pad = getCurrentLinePadding(editor);
    +	var lines = zen_coding.splitByLines(text);
    +	for (var i = 0; i < lines.length; i++) {
    +		if (lines[i].search(pad) == 0)
    +			lines[i] = lines[i].substr(pad.length);
    +	}
    +	
    +	return lines.join(zen_coding.getNewline());
    +}
    +
    +/**
    + * Returns padding of current editor's line
    + * @param {zen_editor} Editor instance
    + * @return {String}
    + */
    +function getCurrentLinePadding(editor) {
    +	return (editor.getCurrentLine().match(/^(\s+)/) || [''])[0];
    +}
    +
    +/**
    + * Search for new caret insertion point
    + * @param {zen_editor} editor Editor instance
    + * @param {Number} Search increment: -1 — search left, 1 — search right
    + * @param {Number} Initial offset relative to current caret position
    + * @return {Number} Returns -1 if insertion point wasn't found
    + */
    +function findNewEditPoint(editor, inc, offset) {
    +	inc = inc || 1;
    +	offset = offset || 0;
    +	var cur_point = editor.getCaretPos() + offset,
    +		content = editor.getContent(),
    +		max_len = content.length,
    +		next_point = -1,
    +		re_empty_line = /^\s+$/;
    +	
    +	function ch(ix) {
    +		return content.charAt(ix);
    +	}
    +	
    +	function getLine(ix) {
    +		var start = ix;
    +		while (start >= 0) {
    +			var c = ch(start);
    +			if (c == '\n' || c == '\r')
    +				break;
    +			start--;
    +		}
    +		
    +		return content.substring(start, ix);
    +	}
    +		
    +	while (cur_point < max_len && cur_point > 0) {
    +		cur_point += inc;
    +		var cur_char = ch(cur_point),
    +			next_char = ch(cur_point + 1),
    +			prev_char = ch(cur_point - 1);
    +			
    +		switch (cur_char) {
    +			case '"':
    +			case '\'':
    +				if (next_char == cur_char && prev_char == '=') {
    +					// empty attribute
    +					next_point = cur_point + 1;
    +				}
    +				break;
    +			case '>':
    +				if (next_char == '<') {
    +					// between tags
    +					next_point = cur_point + 1;
    +				}
    +				break;
    +			case '\n':
    +			case '\r':
    +				// empty line
    +				if (re_empty_line.test(getLine(cur_point - 1))) {
    +					next_point = cur_point;
    +				}
    +				break;
    +		}
    +		
    +		if (next_point != -1)
    +			break;
    +	}
    +	
    +	return next_point;
    +}
    +
    +/**
    + * Move caret to previous edit point
    + * @param {zen_editor} editor Editor instance
    + */
    +function prevEditPoint(editor) {
    +	var cur_pos = editor.getCaretPos(),
    +		new_point = findNewEditPoint(editor, -1);
    +		
    +	if (new_point == cur_pos)
    +		// we're still in the same point, try searching from the other place
    +		new_point = findNewEditPoint(editor, -1, -2);
    +	
    +	if (new_point != -1) 
    +		editor.createSelection(new_point);
    +}
    +
    +/**
    + * Move caret to next edit point
    + * @param {zen_editor} editor Editor instance
    + */
    +function nextEditPoint(editor) {
    +	var new_point = findNewEditPoint(editor, 1);
    +	if (new_point != -1)
    +		editor.createSelection(new_point);
    +}
    +
    +/**
    + * Inserts newline character with proper indentation
    + * @param {zen_editor} editor Editor instance
    + * @param {String} mode Syntax mode (only 'html' is implemented)
    + */
    +function insertFormattedNewline(editor, mode) {
    +	mode = mode || 'html';
    +	var pad = getCurrentLinePadding(editor),
    +		caret_pos = editor.getCaretPos();
    +		
    +	function insert_nl() {
    +		editor.replaceContent('\n', caret_pos);
    +	}
    +	
    +	switch (mode) {
    +		case 'html':
    +			// let's see if we're breaking newly created tag
    +			var pair = HTMLPairMatcher.getTags(editor.getContent(), editor.getCaretPos());
    +			
    +			if (pair[0] && pair[1] && pair[0].type == 'tag' && pair[0].end == caret_pos && pair[1].start == caret_pos) {
    +				editor.replaceContent('\n\t|\n', caret_pos);
    +			} else {
    +				insert_nl();
    +			}
    +			break;
    +		default:
    +			insert_nl();
    +	}
    +}
    +
    +/**
    + * Select line under cursor
    + * @param {zen_editor} editor Editor instance
    + */
    +function selectLine(editor) {
    +	var range = editor.getCurrentLineRange();
    +	editor.createSelection(range.start, range.end);
    +}/**
    + * http://www.openjs.com/scripts/events/keyboard_shortcuts/
    + * Version : 2.01.B
    + * By Binny V A
    + * License : BSD
    + */
    +shortcut = {
    +	'all_shortcuts':{},//All the shortcuts are stored in this array
    +	'add': function(shortcut_combination,callback,opt) {
    +		var is_opera = !!window.opera,
    +			is_mac = /mac\s+os/i.test(navigator.userAgent);
    +		
    +		//Provide a set of default options
    +		var default_options = {
    +			'type':is_opera ? 'keypress' : 'keydown',
    +			'propagate':false,
    +			'disable_in_input':false,
    +			'target':document,
    +			'keycode':false
    +		}
    +		if(!opt) opt = default_options;
    +		else {
    +			for(var dfo in default_options) {
    +				if(typeof opt[dfo] == 'undefined') opt[dfo] = default_options[dfo];
    +			}
    +		}
    +
    +		var ele = opt.target;
    +		if(typeof opt.target == 'string') ele = document.getElementById(opt.target);
    +		var ths = this;
    +		shortcut_combination = shortcut_combination.toLowerCase();
    +
    +		//The function to be called at keypress
    +		var func = function(e) {
    +			e = e || window.event;
    +			var code;
    +			
    +			if(opt['disable_in_input']) { //Don't enable shortcut keys in Input, Textarea fields
    +				var element;
    +				if(e.target) element=e.target;
    +				else if(e.srcElement) element=e.srcElement;
    +				if(element.nodeType==3) element=element.parentNode;
    +
    +				if(element.tagName == 'INPUT' || element.tagName == 'TEXTAREA') return;
    +			}
    +	
    +			//Find Which key is pressed
    +			if (e.keyCode) code = e.keyCode;
    +			else if (e.which) code = e.which;
    +			var character = String.fromCharCode(code).toLowerCase();
    +			
    +			if(code == 188) character=","; //If the user presses , when the type is onkeydown
    +			if(code == 190) character="."; //If the user presses , when the type is onkeydown
    +
    +			var keys = shortcut_combination.split("+");
    +			//Key Pressed - counts the number of valid keypresses - if it is same as the number of keys, the shortcut function is invoked
    +			var kp = 0;
    +			
    +			//Work around for stupid Shift key bug created by using lowercase - as a result the shift+num combination was broken
    +			var shift_nums = {
    +				"`":"~",
    +				"1":"!",
    +				"2":"@",
    +				"3":"#",
    +				"4":"$",
    +				"5":"%",
    +				"6":"^",
    +				"7":"&",
    +				"8":"*",
    +				"9":"(",
    +				"0":")",
    +				"-":"_",
    +				"=":"+",
    +				";":":",
    +				"'":"\"",
    +				",":"<",
    +				".":">",
    +				"/":"?",
    +				"\\":"|"
    +			}
    +			//Special Keys - and their codes
    +			var special_keys = {
    +				'esc':27,
    +				'escape':27,
    +				'tab':9,
    +				'space':32,
    +				'return':13,
    +				'enter':13,
    +				'backspace':8,
    +	
    +				'scrolllock':145,
    +				'scroll_lock':145,
    +				'scroll':145,
    +				'capslock':20,
    +				'caps_lock':20,
    +				'caps':20,
    +				'numlock':144,
    +				'num_lock':144,
    +				'num':144,
    +				
    +				'pause':19,
    +				'break':19,
    +				
    +				'insert':45,
    +				'home':36,
    +				'delete':46,
    +				'end':35,
    +				
    +				'pageup':33,
    +				'page_up':33,
    +				'pu':33,
    +	
    +				'pagedown':34,
    +				'page_down':34,
    +				'pd':34,
    +				
    +				'plus': 187,
    +				'minus': 189,
    +	
    +				'left':37,
    +				'up':38,
    +				'right':39,
    +				'down':40,
    +	
    +				'f1':112,
    +				'f2':113,
    +				'f3':114,
    +				'f4':115,
    +				'f5':116,
    +				'f6':117,
    +				'f7':118,
    +				'f8':119,
    +				'f9':120,
    +				'f10':121,
    +				'f11':122,
    +				'f12':123
    +			}
    +	
    +			var modifiers = { 
    +				shift: { wanted:false, pressed:false},
    +				ctrl : { wanted:false, pressed:false},
    +				alt  : { wanted:false, pressed:false},
    +				meta : { wanted:false, pressed:false}	//Meta is Mac specific
    +			};
    +                        
    +			if(e.ctrlKey)	modifiers.ctrl.pressed = true;
    +			if(e.shiftKey)	modifiers.shift.pressed = true;
    +			if(e.altKey)	modifiers.alt.pressed = true;
    +			if(e.metaKey)   modifiers.meta.pressed = true;
    +            
    +			var k;
    +			for(var i=0; k=keys[i], i<keys.length; i++) {
    +				// Due to stupid Opera bug I have to swap Ctrl and Meta keys
    +				if (is_mac && is_opera) {
    +					if (k == 'ctrl' || k == 'control')
    +						k = 'meta';
    +					else if (k == 'meta')
    +						k = 'ctrl';
    +				} else if (!is_mac && k == 'meta') {
    +					k = 'ctrl';
    +				}
    +				
    +				//Modifiers
    +				if(k == 'ctrl' || k == 'control') {
    +					kp++;
    +					modifiers.ctrl.wanted = true;
    +
    +				} else if(k == 'shift') {
    +					kp++;
    +					modifiers.shift.wanted = true;
    +
    +				} else if(k == 'alt') {
    +					kp++;
    +					modifiers.alt.wanted = true;
    +				} else if(k == 'meta') {
    +					kp++;
    +					modifiers.meta.wanted = true;
    +				} else if(k.length > 1) { //If it is a special key
    +					if(special_keys[k] == code) kp++;
    +					
    +				} else if(opt['keycode']) {
    +					if(opt['keycode'] == code) kp++;
    +
    +				} else { //The special keys did not match
    +					if(character == k) kp++;
    +					else {
    +						if(shift_nums[character] && e.shiftKey) { //Stupid Shift key bug created by using lowercase
    +							character = shift_nums[character]; 
    +							if(character == k) kp++;
    +						}
    +					}
    +				}
    +			}
    +			
    +			if(kp == keys.length && 
    +						modifiers.ctrl.pressed == modifiers.ctrl.wanted &&
    +						modifiers.shift.pressed == modifiers.shift.wanted &&
    +						modifiers.alt.pressed == modifiers.alt.wanted &&
    +						modifiers.meta.pressed == modifiers.meta.wanted) {
    +				
    +				var result = callback(e);
    +	
    +				if(result !== true && !opt['propagate']) { //Stop the event
    +					//e.cancelBubble is supported by IE - this will kill the bubbling process.
    +					e.cancelBubble = true;
    +					e.returnValue = false;
    +	
    +					//e.stopPropagation works in Firefox.
    +					if (e.stopPropagation) {
    +						e.stopPropagation();
    +						e.preventDefault();
    +					}
    +					return false;
    +				}
    +			}
    +		}
    +		this.all_shortcuts[shortcut_combination] = {
    +			'callback':func, 
    +			'target':ele, 
    +			'event': opt['type']
    +		};
    +		//Attach the function with the event
    +		if(ele.addEventListener) ele.addEventListener(opt['type'], func, false);
    +		else if(ele.attachEvent) ele.attachEvent('on'+opt['type'], func);
    +		else ele['on'+opt['type']] = func;
    +	},
    +
    +	//Remove the shortcut - just specify the shortcut and I will remove the binding
    +	'remove':function(shortcut_combination) {
    +		shortcut_combination = shortcut_combination.toLowerCase();
    +		var binding = this.all_shortcuts[shortcut_combination];
    +		delete(this.all_shortcuts[shortcut_combination])
    +		if(!binding) return;
    +		var type = binding['event'];
    +		var ele = binding['target'];
    +		var callback = binding['callback'];
    +
    +		if(ele.detachEvent) ele.detachEvent('on'+type, callback);
    +		else if(ele.removeEventListener) ele.removeEventListener(type, callback, false);
    +		else ele['on'+type] = false;
    +	}
    +}/**
    + * Editor manager that handles all incoming events and runs Zen Coding actions.
    + * This manager is also used for setting up editor preferences
    + * @author Sergey Chikuyonok (serge.che@gmail.com)
    + * @link http://chikuyonok.ru
    + * 
    + * @include "actions.js"
    + * @include "editor.js"
    + * @include "shortcut.js"
    + */
    zen_textarea = (function(){ // should be global
    +	var default_options = {
    +		profile: 'xhtml',
    +		syntax: 'html',
    +		use_tab: false,
    +		pretty_break: false
    +	},
    +	
    +	mac_char_map = {
    +		'ctrl': '⌃',
    +		'control': '⌃',
    +		'meta': '⌘',
    +		'shift': '⇧',
    +		'alt': '⌥',
    +		'enter': '⏎',
    +		'tab': '⇥',
    +		'left': '←',
    +		'right': '→'
    +	},
    +	
    +	pc_char_map = {
    +		'left': '←',
    +		'right': '→'
    +	},
    +	
    +	shortcuts = {},
    +	is_mac = /mac\s+os/i.test(navigator.userAgent),
    +	
    +	/** Zen Coding parameter name/value regexp for getting options from element */
    +	re_param = /\bzc\-(\w+)\-(\w+)/g;
    +	
    +	/** @type {default_options} */
    +	var options = {};
    +	
    +	function copyOptions(opt) {
    +		opt = opt || {};
    +		var result = {};
    +		for (var p in default_options) if (default_options.hasOwnProperty(p)) {
    +			result[p] = (p in opt) ? opt[p] : default_options[p];
    +		}
    +		
    +		return result;
    +	}
    +	
    +	options = copyOptions();
    +	
    +	/**
    +	 * Makes first letter of string in uppercase
    +	 * @param {String} str
    +	 */
    +	function capitalize(str) {
    +		return str.charAt().toUpperCase() + str.substring(1);
    +	}
    +	
    +	function humanize(str) {
    +		return capitalize(str.replace(/_(\w)/g, function(s, p){return ' ' + p.toUpperCase()}));
    +	}
    +	
    +	function formatShortcut(char_map, glue) {
    +		var result = [];
    +		if (typeof(glue) == 'undefined')
    +			glue = '+';
    +			
    +		for (var p in shortcuts) if (shortcuts.hasOwnProperty(p)) {
    +			var keys = p.split('+'),
    +				ar = [],
    +				lp = p.toLowerCase();
    +				
    +			if (lp == 'tab' || lp == 'enter')
    +				continue;
    +				
    +			for (var i = 0; i < keys.length; i++) {
    +				var key = keys[i].toLowerCase();
    +				ar.push(key in char_map ? char_map[key] : capitalize(key));
    +			}
    +			
    +			result.push({
    +				'keystroke': ar.join(glue), 
    +				'action_name': humanize(shortcuts[p])
    +			});
    +		}
    +		
    +		return result;
    +	}
    +	
    +	
    +	/**
    +	 * Get Zen Coding options from element's class name
    +	 * @param {Element} elem
    +	 */
    +	function getOptionsFromElement(elem) {
    +		var param_str = elem.className || '',
    +			m,
    +			result = copyOptions(options);
    +			
    +		while ( (m = re_param.exec(param_str)) ) {
    +			var key = m[1].toLowerCase(),
    +				value = m[2].toLowerCase();
    +			
    +			if (value == 'true' || value == 'yes' || value == '1')
    +				value = true;
    +			else if (value == 'false' || value == 'no' || value == '0')
    +				value = false;
    +				
    +			result[key] = value;
    +		}
    +		
    +		return result;
    +	}
    +	
    +	/**
    +	 * Returns normalized action name
    +	 * @param {String} name Action name (like 'Expand Abbreviation')
    +	 * @return Normalized name for coding (like 'expand_abbreviation')
    +	 */
    +	function normalizeActionName(name) {
    +		return name
    +			.replace(/(^\s+|\s+$)/g, '') // remove trailing spaces
    +			.replace(/\s+/g, '_')
    +			.toLowerCase();
    +	}
    +	
    +	/**
    +	 * Runs actions called by user
    +	 * @param {String} name Normalized action name
    +	 * @param {Event} evt Event object
    +	 */
    +	function runAction(name, evt) {
    +		/** @type {Element} */
    +		var target_elem = evt.target || evt.srcElement,
    +			key_code = evt.keyCode || evt.which;
    +			
    +		if (target_elem && target_elem.nodeType == 1 && target_elem.nodeName == 'TEXTAREA') {
    +			zen_editor.setTarget(target_elem);
    +			
    +			var options = getOptionsFromElement(target_elem),
    +				syntax = options.syntax,
    +				profile_name = options.profile;
    +			
    +			switch (name) {
    +				case 'expand_abbreviation':
    +					if (key_code == 9) {
    +						if (options.use_tab)
    +							expandAbbreviationWithTab(zen_editor, syntax, profile_name);
    +						else
    +							// user pressed Tab key but it's forbidden in 
    +							// Zen Coding: bubble up event
    +							return true;
    +							
    +					} else {
    +						expandAbbreviation(zen_editor, syntax, profile_name);
    +					}
    +					break;
    +				case 'match_pair_inward':
    +				case 'balance_tag_inward':
    +					matchPair(zen_editor, 'in');
    +					break;
    +				case 'match_pair_outward':
    +				case 'balance_tag_outward':
    +					matchPair(zen_editor, 'out');
    +					break;
    +				case 'wrap_with_abbreviation':
    +					var abbr = prompt('Enter abbreviation', 'div');
    +					if (abbr)
    +						wrapWithAbbreviation(zen_editor, abbr, syntax, profile_name);
    +					break;
    +				case 'next_edit_point':
    +					nextEditPoint(zen_editor);
    +					break;
    +				case 'previous_edit_point':
    +				case 'prev_edit_point':
    +					prevEditPoint(zen_editor);
    +					break;
    +				case 'pretty_break':
    +				case 'format_line_break':
    +					if (key_code == 13) {
    +						if (options.pretty_break)
    +							insertFormattedNewline(zen_editor);
    +						else
    +							// user pressed Enter but it's forbidden in 
    +							// Zen Coding: bubble up event
    +							return true;
    +					} else {
    +						insertFormattedNewline(zen_editor);
    +					}
    +					break;
    +				case 'select_line':
    +					selectLine(zen_editor);
    +			}
    +		} else {
    +			// allow event bubbling
    +			return true;
    +		}
    +	}
    +	
    +	/**
    +	 * Bind shortcut to Zen Coding action
    +	 * @param {String} keystroke
    +	 * @param {String} action_name
    +	 */
    +	function addShortcut(keystroke, action_name) {
    +		action_name = normalizeActionName(action_name);
    +		shortcuts[keystroke.toLowerCase()] = action_name;
    +		shortcut.add(keystroke, function(evt){
    +			return runAction(action_name, evt);
    +		});
    +	}
    +	
    +	// add default shortcuts
    +	addShortcut('Meta+E', 'Expand Abbreviation');
    +	addShortcut('Tab', 'Expand Abbreviation');
    +	addShortcut('Meta+D', 'Balance Tag Outward');
    +	addShortcut('Shift+Meta+D', 'Balance Tag inward');
    +	addShortcut('Shift+Meta+A', 'Wrap with Abbreviation');
    +	addShortcut('Ctrl+RIGHT', 'Next Edit Point');
    +	addShortcut('Ctrl+LEFT', 'Previous Edit Point');
    +	addShortcut('Meta+L', 'Select Line');
    +	addShortcut('Enter', 'Format Line Break');
    +	
    +	
    +	return {
    +		shortcut: addShortcut,
    +		
    +		/**
    +		 * Removes shortcut binding
    +		 * @param {String} keystroke
    +		 */
    +		unbindShortcut: function(keystroke) {
    +			keystroke = keystroke.toLowerCase();
    +			if (keystroke in shortcuts)
    +				delete shortcuts[keystroke];
    +			shortcut.remove(keystroke);
    +		},
    +		
    +		/**
    +		 * Setup editor. Pass object with values defined in 
    +		 * <code>default_options</code>
    +		 */
    +		setup: function(opt) {
    +			options = copyOptions(opt);
    +		},
    +		
    +		/**
    +		 * Returns option value
    +		 */
    +		getOption: function(name) {
    +			return options[name];
    +		},
    +		
    +		/**
    +		 * Returns array of binded actions and their keystrokes
    +		 * @return {Array}
    +		 */
    +		getShortcuts: function() {
    +			return formatShortcut(is_mac ? mac_char_map : pc_char_map, is_mac ? '' : '+');
    +		},
    +		
    +		/**
    +		 * Show info window about Zen Coding
    +		 */
    +		showInfo: function() {
    +			var message = 'All textareas on this page are powered by Zen Coding project: ' +
    +					'a set of tools for fast HTML coding.\n\n' +
    +					'Available shortcuts:\n';
    +					
    +			var sh = this.getShortcuts(),
    +				actions = [];
    +				
    +			for (var i = 0; i < sh.length; i++) {
    +				actions.push(sh[i].keystroke + ' — ' + sh[i].action_name)
    +			}
    +			
    +			message += actions.join('\n') + '\n\n';
    +			message += 'More info on http://code.google.com/p/zen-coding/';
    +			
    +			alert(message);
    +			
    +		}
    +	}
    +})();	
    +})();