diff --git a/wp-content/plugins/akismet/.htaccess b/wp-content/plugins/akismet/.htaccess new file mode 100644 index 0000000..6d8b4ec --- /dev/null +++ b/wp-content/plugins/akismet/.htaccess @@ -0,0 +1,11 @@ +Order Deny,Allow +Deny from all + + + Allow from all + + +#allow access to any image + + Allow from all + \ No newline at end of file diff --git a/wp-content/plugins/akismet/_inc/akismet.css b/wp-content/plugins/akismet/_inc/akismet.css index e1d6fd5..ff076aa 100644 --- a/wp-content/plugins/akismet/_inc/akismet.css +++ b/wp-content/plugins/akismet/_inc/akismet.css @@ -75,8 +75,8 @@ h2.ak-header { display: inline-block !important; } .checkforspam-spinner { - display: none; - margin-top: 10px; + display: inline-block; + margin-top: 7px; } .config-wrap { diff --git a/wp-content/plugins/akismet/_inc/akismet.js b/wp-content/plugins/akismet/_inc/akismet.js index ed65220..aa0aab4 100644 --- a/wp-content/plugins/akismet/_inc/akismet.js +++ b/wp-content/plugins/akismet/_inc/akismet.js @@ -125,7 +125,7 @@ jQuery( function ( $ ) { }); $('.checkforspam:not(.button-disabled)').click( function(e) { $('.checkforspam:not(.button-disabled)').addClass('button-disabled'); - $('.checkforspam-spinner').show(); + $('.checkforspam-spinner').addClass( 'spinner' ); akismet_check_for_spam(0, 100); e.preventDefault(); }); diff --git a/wp-content/plugins/akismet/_inc/form.js b/wp-content/plugins/akismet/_inc/form.js index ec5e56e..3a5be8a 100644 --- a/wp-content/plugins/akismet/_inc/form.js +++ b/wp-content/plugins/akismet/_inc/form.js @@ -1,17 +1,30 @@ -jQuery( function ( $ ) { - var ak_js = $( '#ak_js' ); +var ak_js = document.getElementById( "ak_js" ); - // If the form field already exists just use that - if ( ak_js.length == 0 ) { - ak_js = $( '' ); +if ( ! ak_js ) { + ak_js = document.createElement( 'input' ); + ak_js.setAttribute( 'id', 'ak_js' ); + ak_js.setAttribute( 'name', 'ak_js' ); + ak_js.setAttribute( 'type', 'hidden' ); +} +else { + ak_js.parentNode.removeChild( ak_js ); +} + +ak_js.setAttribute( 'value', ( new Date() ).getTime() ); + +var commentForm = document.getElementById( 'commentform' ); + +if ( commentForm ) { + commentForm.appendChild( ak_js ); +} +else { + var replyRowContainer = document.getElementById( 'replyrow' ); + + if ( replyRowContainer ) { + var children = replyRowContainer.getElementsByTagName( 'td' ); + + if ( children.length > 0 ) { + children[0].appendChild( ak_js ); + } } - else { - ak_js.remove(); - } - - ak_js.val( ( new Date() ).getTime() ); - - // single page, front-end comment form - // inline comment reply, wp-admin - $( '#commentform, #replyrow td:first' ).append( ak_js ); -} ); +} \ No newline at end of file diff --git a/wp-content/plugins/akismet/akismet.php b/wp-content/plugins/akismet/akismet.php index 811ab71..7fc1e9d 100644 --- a/wp-content/plugins/akismet/akismet.php +++ b/wp-content/plugins/akismet/akismet.php @@ -6,7 +6,7 @@ Plugin Name: Akismet Plugin URI: http://akismet.com/ Description: Used by millions, Akismet is quite possibly the best way in the world to protect your blog from comment and trackback spam. It keeps your site protected from spam even while you sleep. To get started: 1) Click the "Activate" link to the left of this description, 2) Sign up for an Akismet API key, and 3) Go to your Akismet configuration page, and save your API key. -Version: 3.0.0 +Version: 3.0.1 Author: Automattic Author URI: http://automattic.com/wordpress-plugins/ License: GPLv2 or later @@ -35,8 +35,8 @@ if ( !function_exists( 'add_action' ) ) { exit; } -define( 'AKISMET_VERSION', '3.0.0' ); -define( 'AKISMET__MINIMUM_WP_VERSION', '3.0' ); +define( 'AKISMET_VERSION', '3.0.1' ); +define( 'AKISMET__MINIMUM_WP_VERSION', '3.1' ); define( 'AKISMET__PLUGIN_URL', plugin_dir_url( __FILE__ ) ); define( 'AKISMET__PLUGIN_DIR', plugin_dir_path( __FILE__ ) ); define( 'AKISMET_DELETE_LIMIT', 100000 ); diff --git a/wp-content/plugins/akismet/class.akismet-admin.php b/wp-content/plugins/akismet/class.akismet-admin.php index 15f5d47..7a62ccb 100644 --- a/wp-content/plugins/akismet/class.akismet-admin.php +++ b/wp-content/plugins/akismet/class.akismet-admin.php @@ -33,7 +33,6 @@ class Akismet_Admin { add_action( 'activity_box_end', array( 'Akismet_Admin', 'dashboard_stats' ) ); add_action( 'rightnow_end', array( 'Akismet_Admin', 'rightnow_stats' ) ); add_action( 'manage_comments_nav', array( 'Akismet_Admin', 'check_for_spam_button' ) ); - add_action( 'transition_comment_status', array( 'Akismet_Admin', 'transition_comment_status' ), 10, 3 ); add_action( 'admin_action_akismet_recheck_queue', array( 'Akismet_Admin', 'recheck_queue' ) ); add_action( 'wp_ajax_akismet_recheck_queue', array( 'Akismet_Admin', 'recheck_queue' ) ); add_action( 'wp_ajax_comment_author_deurl', array( 'Akismet_Admin', 'remove_comment_author_url' ) ); @@ -44,6 +43,8 @@ class Akismet_Admin { add_filter( 'comment_text', array( 'Akismet_Admin', 'text_add_link_class' ) ); add_filter( 'plugin_action_links_'.plugin_basename( plugin_dir_path( __FILE__ ) . 'akismet.php'), array( 'Akismet_Admin', 'admin_plugin_settings_link' ) ); + + add_filter( 'wxr_export_skip_commentmeta', array( 'Akismet_Admin', 'exclude_commentmeta_from_export' ), 10, 3 ); } public static function admin_init() { @@ -64,7 +65,7 @@ class Akismet_Admin { } public static function admin_plugin_settings_link( $links ) { - $settings_link = ''.__('Settings', 'akismet').''; + $settings_link = ''.__('Settings', 'akismet').''; array_unshift( $links, $settings_link ); return $links; } @@ -331,51 +332,7 @@ class Akismet_Admin { else $link = add_query_arg( array( 'page' => 'akismet-admin', 'recheckqueue' => 'true', 'noheader' => 'true' ), admin_url( 'edit-comments.php' ) ); - echo '
' . esc_html__('Check for Spam', 'akismet') . ''; - echo ''; - } - - public static function transition_comment_status( $new_status, $old_status, $comment ) { - if ( $new_status == $old_status ) - return; - - # we don't need to record a history item for deleted comments - if ( $new_status == 'delete' ) - return; - - if ( !is_admin() ) - return; - - if ( !current_user_can( 'edit_post', $comment->comment_post_ID ) && !current_user_can( 'moderate_comments' ) ) - return; - - if ( defined('WP_IMPORTING') && WP_IMPORTING == true ) - return; - - // if this is present, it means the status has been changed by a re-check, not an explicit user action - if ( get_comment_meta( $comment->comment_ID, 'akismet_rechecking' ) ) - return; - - global $current_user; - $reporter = ''; - if ( is_object( $current_user ) ) - $reporter = $current_user->user_login; - - // Assumption alert: - // We want to submit comments to Akismet only when a moderator explicitly spams or approves it - not if the status - // is changed automatically by another plugin. Unfortunately WordPress doesn't provide an unambiguous way to - // determine why the transition_comment_status action was triggered. And there are several different ways by which - // to spam and unspam comments: bulk actions, ajax, links in moderation emails, the dashboard, and perhaps others. - // We'll assume that this is an explicit user action if POST or GET has an 'action' key. - if ( isset($_POST['action']) || isset($_GET['action']) ) { - if ( $new_status == 'spam' && ( $old_status == 'approved' || $old_status == 'unapproved' || !$old_status ) ) { - return self::submit_spam_comment( $comment->comment_ID ); - } elseif ( $old_status == 'spam' && ( $new_status == 'approved' || $new_status == 'unapproved' ) ) { - return self::submit_nonspam_comment( $comment->comment_ID ); - } - } - - Akismet::update_comment_history( $comment->comment_ID, sprintf( __('%1$s changed the comment status to %2$s', 'akismet'), $reporter, $new_status ), 'status-' . $new_status ); + echo '
' . esc_html__('Check for Spam', 'akismet') . ''; } public static function recheck_queue() { @@ -410,7 +367,7 @@ class Akismet_Admin { add_comment_meta( $c['comment_ID'], 'akismet_rechecking', true ); - $response = Akismet::http_post( http_build_query( $c ), 'comment-check' ); + $response = Akismet::http_post( build_query( $c ), 'comment-check' ); if ( 'true' == $response[1] ) { wp_set_comment_status( $c['comment_ID'], 'spam' ); update_comment_meta( $c['comment_ID'], 'akismet_result', 'true' ); @@ -514,7 +471,10 @@ class Akismet_Admin { if ( $desc ) echo ''.esc_html( $desc ).''; - if ( apply_filters( 'akismet_show_user_comments_approved', get_option('akismet_show_user_comments_approved') ) ) { + $show_user_comments = apply_filters( 'akismet_show_user_comments_approved', get_option('akismet_show_user_comments_approved') ); + $show_user_comments = $show_user_comments === 'false' ? false : $show_user_comments; //option used to be saved as 'false' / 'true' + + if ( $show_user_comments ) { $comment_count = Akismet::get_user_comments_approved( $comment->user_id, $comment->comment_author_email, $comment->comment_author, $comment->comment_author_url ); $comment_count = intval( $comment_count ); echo ''; @@ -557,102 +517,6 @@ class Akismet_Admin { return preg_replace_callback( '#]*)href="([^"]+)"([^>]*)>(.*?)#i', array( 'Akismet_Admin', 'text_add_link_callback' ), $comment_text ); } - public static function submit_spam_comment( $comment_id ) { - global $wpdb, $current_user, $current_site; - - $comment_id = (int) $comment_id; - - $comment = $wpdb->get_row( $wpdb->prepare( "SELECT * FROM {$wpdb->comments} WHERE comment_ID = %d", $comment_id ) ); - - if ( !$comment ) // it was deleted - return; - - if ( 'spam' != $comment->comment_approved ) - return; - - // use the original version stored in comment_meta if available - $as_submitted = get_comment_meta( $comment_id, 'akismet_as_submitted', true); - - if ( $as_submitted && is_array( $as_submitted ) && isset( $as_submitted['comment_content'] ) ) - $comment = (object) array_merge( (array)$comment, $as_submitted ); - - $comment->blog = get_bloginfo('url'); - $comment->blog_lang = get_locale(); - $comment->blog_charset = get_option('blog_charset'); - $comment->permalink = get_permalink($comment->comment_post_ID); - - if ( is_object($current_user) ) - $comment->reporter = $current_user->user_login; - - if ( is_object($current_site) ) - $comment->site_domain = $current_site->domain; - - $comment->user_role = ''; - if ( isset( $comment->user_ID ) ) - $comment->user_role = Akismet::get_user_roles( $comment->user_ID ); - - if ( Akismet::is_test_mode() ) - $comment->is_test = 'true'; - - $post = get_post( $comment->comment_post_ID ); - $comment->comment_post_modified_gmt = $post->post_modified_gmt; - - $response = Akismet::http_post( http_build_query( $comment ), 'submit-spam' ); - if ( $comment->reporter ) { - Akismet::update_comment_history( $comment_id, sprintf( __('%s reported this comment as spam', 'akismet'), $comment->reporter ), 'report-spam' ); - update_comment_meta( $comment_id, 'akismet_user_result', 'true' ); - update_comment_meta( $comment_id, 'akismet_user', $comment->reporter ); - } - - do_action('akismet_submit_spam_comment', $comment_id, $response[1]); - } - - public static function submit_nonspam_comment( $comment_id ) { - global $wpdb, $current_user, $current_site; - - $comment_id = (int) $comment_id; - - $comment = $wpdb->get_row( $wpdb->prepare( "SELECT * FROM {$wpdb->comments} WHERE comment_ID = %d", $comment_id ) ); - if ( !$comment ) // it was deleted - return; - - // use the original version stored in comment_meta if available - $as_submitted = get_comment_meta( $comment_id, 'akismet_as_submitted', true); - - if ( $as_submitted && is_array($as_submitted) && isset($as_submitted['comment_content']) ) - $comment = (object) array_merge( (array)$comment, $as_submitted ); - - $comment->blog = get_bloginfo('url'); - $comment->blog_lang = get_locale(); - $comment->blog_charset = get_option('blog_charset'); - $comment->permalink = get_permalink( $comment->comment_post_ID ); - $comment->user_role = ''; - - if ( is_object($current_user) ) - $comment->reporter = $current_user->user_login; - - if ( is_object($current_site) ) - $comment->site_domain = $current_site->domain; - - if ( isset( $comment->user_ID ) ) - $comment->user_role = Akismet::get_user_roles($comment->user_ID); - - if ( Akismet::is_test_mode() ) - $comment->is_test = 'true'; - - $post = get_post( $comment->comment_post_ID ); - $comment->comment_post_modified_gmt = $post->post_modified_gmt; - - $response = Akismet::http_post( http_build_query( $comment ), 'submit-ham' ); - if ( $comment->reporter ) { - Akismet::update_comment_history( $comment_id, sprintf( __('%s reported this comment as not spam', 'akismet'), $comment->reporter ), 'report-ham' ); - update_comment_meta( $comment_id, 'akismet_user_result', 'false' ); - update_comment_meta( $comment_id, 'akismet_user', $comment->reporter ); - } - - do_action('akismet_submit_nonspam_comment', $comment_id, $response[1]); - } - // Total spam in queue // get_option( 'akismet_spam_count' ) is the total caught ever public static function get_spam_count( $type = false ) { @@ -672,53 +536,73 @@ class Akismet_Admin { return $count; } elseif ( 'comments' == $type || 'comment' == $type ) { // comments $type = ''; - } else { // pingback, trackback, ... - $type = $wpdb->escape( $type ); } - return (int) $wpdb->get_var("SELECT COUNT(comment_ID) FROM {$wpdb->comments} WHERE comment_approved = 'spam' AND comment_type='$type'"); + return (int) $wpdb->get_var( $wpdb->prepare( "SELECT COUNT(comment_ID) FROM {$wpdb->comments} WHERE comment_approved = 'spam' AND comment_type = %s", $type ) ); } // Check connectivity between the WordPress blog and Akismet's servers. // Returns an associative array of server IP addresses, where the key is the IP address, and value is true (available) or false (unable to connect). - public static function check_server_connectivity() { - $test_host = 'rest.akismet.com'; + public static function check_server_ip_connectivity() { + + $servers = $ips = array(); - // Some web hosts may disable one or both functions - if ( !function_exists('fsockopen') || !function_exists('gethostbynamel') ) - return array(); - - $ips = gethostbynamel( $test_host ); - if ( !$ips || !is_array($ips) || !count($ips) ) - return array(); - - $api_key = Akismet::get_api_key(); - - $servers = array(); - foreach ( $ips as $ip ) { - $response = Akismet::verify_key( $api_key, $ip ); - // even if the key is invalid, at least we know we have connectivity - if ( $response == 'valid' || $response == 'invalid' ) - $servers[$ip] = true; - else - $servers[$ip] = false; + // Some web hosts may disable this function + if ( function_exists('gethostbynamel') ) { + + $ips = gethostbynamel( 'rest.akismet.com' ); + if ( $ips && is_array($ips) && count($ips) ) { + $api_key = Akismet::get_api_key(); + + foreach ( $ips as $ip ) { + $response = Akismet::verify_key( $api_key, $ip ); + // even if the key is invalid, at least we know we have connectivity + if ( $response == 'valid' || $response == 'invalid' ) + $servers[$ip] = 'connected'; + else + $servers[$ip] = $response ? $response : 'unable to connect'; + } + } } + return $servers; } - - // Check the server connectivity and store the results in an option. - // Cached results will be used if not older than the specified timeout in seconds; use $cache_timeout = 0 to force an update. - // Returns the same associative array as check_server_connectivity() - public static function get_server_connectivity( $cache_timeout = 86400 ) { + + // Simpler connectivity check + public static function check_server_connectivity($cache_timeout = 86400) { + + $debug = array(); + $debug[ 'PHP_VERSION' ] = PHP_VERSION; + $debug[ 'WORDPRESS_VERSION' ] = $GLOBALS['wp_version']; + $debug[ 'AKISMET_VERSION' ] = AKISMET_VERSION; + $debug[ 'AKISMET__PLUGIN_DIR' ] = AKISMET__PLUGIN_DIR; + $debug[ 'SITE_URL' ] = site_url(); + $debug[ 'HOME_URL' ] = home_url(); + $servers = get_option('akismet_available_servers'); - if ( (time() - get_option('akismet_connectivity_time') < $cache_timeout) && $servers !== false ) - return $servers; + if ( (time() - get_option('akismet_connectivity_time') < $cache_timeout) && $servers !== false ) { + $servers = self::check_server_ip_connectivity(); + update_option('akismet_available_servers', $servers); + update_option('akismet_connectivity_time', time()); + } + + $response = wp_remote_get( 'http://rest.akismet.com/1.1/test' ); + + $debug[ 'gethostbynamel' ] = function_exists('gethostbynamel') ? 'exists' : 'not here'; + $debug[ 'Servers' ] = $servers; + $debug[ 'Test Connection' ] = $response; + + Akismet::log( $debug ); + + if ( $response && 'connected' == wp_remote_retrieve_body( $response ) ) + return true; + + return false; + } - // There's a race condition here but the effect is harmless. - $servers = self::check_server_connectivity(); - update_option('akismet_available_servers', $servers); - update_option('akismet_connectivity_time', time()); - return $servers; + // Check the server connectivity and store the available servers in an option. + public static function get_server_connectivity($cache_timeout = 86400) { + return self::check_server_connectivity( $cache_timeout ); } public static function get_number_spam_waiting() { @@ -741,7 +625,7 @@ class Akismet_Admin { } public static function get_akismet_user( $api_key ) { - $akismet_user = Akismet::http_post( http_build_query( array( 'key' => $api_key ) ), 'get-subscription' ); + $akismet_user = Akismet::http_post( build_query( array( 'key' => $api_key ) ), 'get-subscription' ); if ( ! empty( $akismet_user[1] ) ) $akismet_user = json_decode( $akismet_user[1] ); @@ -755,7 +639,7 @@ class Akismet_Admin { $stat_totals = array(); foreach( array( '6-months', 'all' ) as $interval ) { - $response = Akismet::http_post( http_build_query( array( 'blog' => urlencode( get_bloginfo('url') ), 'key' => $api_key, 'from' => $interval ) ), 'get-stats' ); + $response = Akismet::http_post( build_query( array( 'blog' => urlencode( get_bloginfo('url') ), 'key' => $api_key, 'from' => $interval ) ), 'get-stats' ); if ( ! empty( $response[1] ) ) { $stat_totals[$interval] = json_decode( $response[1] ); @@ -765,7 +649,7 @@ class Akismet_Admin { } public static function verify_wpcom_key( $api_key, $user_id, $token = '' ) { - $akismet_account = Akismet::http_post( http_build_query( array( + $akismet_account = Akismet::http_post( build_query( array( 'user_id' => $user_id, 'api_key' => $api_key, 'token' => $token, @@ -790,9 +674,11 @@ class Akismet_Admin { public static function display_spam_check_warning() { Akismet::fix_scheduled_recheck(); + + $link_text = apply_filters( 'akismet_spam_check_warning_link_text', sprintf( __( 'Please check your Akismet configuration and contact your web host if problems persist.', 'akismet'), esc_url( self::get_page_url() ) ) ); if ( self::get_number_spam_waiting() > 0 && wp_next_scheduled('akismet_schedule_cron_recheck') > time() ) - Akismet::view( 'notice', array( 'type' => 'spam-check' ) ); + Akismet::view( 'notice', array( 'type' => 'spam-check', 'link_text' => $link_text ) ); } public static function display_invalid_version() { @@ -869,7 +755,7 @@ class Akismet_Admin { if ( empty( self::$notices ) ) { //show status - if ( $akismet_user->status == 'active' && $akismet_user->account_type == 'free-api-key' ) { + if ( ! empty( $stat_totals['all'] ) && isset( $stat_totals['all']->time_saved ) && $akismet_user->status == 'active' && $akismet_user->account_type == 'free-api-key' ) { $time_saved = false; @@ -920,16 +806,11 @@ class Akismet_Admin { } public static function display_status() { - $servers = self::get_server_connectivity(); - $fail_count = count( $servers ) - count( array_filter( $servers ) ); - $type = ''; + $type = ''; - if ( empty( $servers ) || $fail_count > 0 ) + if ( !self::get_server_connectivity() ) $type = 'servers-be-down'; - if ( !function_exists('fsockopen') || !function_exists('gethostbynamel') ) - $type = 'missing-functions'; - if ( !empty( $type ) ) Akismet::view( 'notice', compact( 'type' ) ); elseif ( !empty( self::$notices ) ) { @@ -961,4 +842,20 @@ class Akismet_Admin { } return false; } + + /** + * Some commentmeta isn't useful in an export file. Suppress it (when supported). + * + * @param bool $exclude + * @param string $key The meta key + * @param object $meta The meta object + * @return bool Whether to exclude this meta entry from the export. + */ + public static function exclude_commentmeta_from_export( $exclude, $key, $meta ) { + if ( in_array( $key, array( 'akismet_as_submitted', 'akismet_rechecking', 'akismet_delayed_moderation_email' ) ) ) { + return true; + } + + return $exclude; + } } \ No newline at end of file diff --git a/wp-content/plugins/akismet/class.akismet.php b/wp-content/plugins/akismet/class.akismet.php index 7cf813d..17f6b00 100644 --- a/wp-content/plugins/akismet/class.akismet.php +++ b/wp-content/plugins/akismet/class.akismet.php @@ -33,12 +33,14 @@ class Akismet { if ( $akismet_comment_nonce_option == 'true' || $akismet_comment_nonce_option == '' ) add_action( 'comment_form', array( 'Akismet', 'add_comment_nonce' ), 1 ); - add_action( 'admin_footer-edit-comments.php', array( 'Akismet', 'load_form_js' ) ); + add_action( 'admin_head-edit-comments.php', array( 'Akismet', 'load_form_js' ) ); add_action( 'comment_form', array( 'Akismet', 'load_form_js' ) ); add_action( 'comment_form', array( 'Akismet', 'inject_ak_js' ) ); add_filter( 'comment_moderation_recipients', array( 'Akismet', 'disable_moderation_emails_if_unreachable' ), 1000, 2 ); add_filter( 'pre_comment_approved', array( 'Akismet', 'last_comment_status' ), 10, 2 ); + + add_action( 'transition_comment_status', array( 'Akismet', 'transition_comment_status' ), 10, 3 ); if ( '3.0.5' == $GLOBALS['wp_version'] ) { remove_filter( 'comment_text', 'wp_kses_data' ); @@ -48,11 +50,11 @@ class Akismet { } public static function get_api_key() { - return defined('WPCOM_API_KEY') ? constant('WPCOM_API_KEY') : get_option('wordpress_api_key'); + return apply_filters( 'akismet_get_api_key', defined('WPCOM_API_KEY') ? constant('WPCOM_API_KEY') : get_option('wordpress_api_key') ); } public static function check_key_status( $key, $ip = null ) { - return self::http_post( http_build_query( array( 'key' => $key, 'blog' => get_option('home') ) ), 'verify-key', $ip ); + return self::http_post( build_query( array( 'key' => $key, 'blog' => get_option('home') ) ), 'verify-key', $ip ); } public static function verify_key( $key, $ip = null ) { @@ -115,19 +117,24 @@ class Akismet { $post = get_post( $comment['comment_post_ID'] ); $comment[ 'comment_post_modified_gmt' ] = $post->post_modified_gmt; - $response = self::http_post( http_build_query( $comment ), 'comment-check' ); + $response = self::http_post( build_query( $comment ), 'comment-check' ); do_action( 'akismet_comment_check_response', $response ); self::update_alert( $response ); - $commentdata['comment_as_submitted'] = $comment; + $commentdata['comment_as_submitted'] = array_intersect_key( $comment, array( 'blog' => '', 'blog_charset' => '', 'blog_lang' => '', 'blog_ua' => '', 'comment_agent' => '', 'comment_author' => '', 'comment_author_IP' => '', 'comment_author_email' => '', 'comment_author_url' => '', 'comment_content' => '', 'comment_date_gmt' => '', 'comment_tags' => '', 'comment_type' => '', 'guid' => '', 'is_test' => '', 'permalink' => '', 'reporter' => '', 'site_domain' => '', 'submit_referer' => '', 'submit_uri' => '', 'user_ID' => '', 'user_agent' => '', 'user_id' => '', 'user_ip' => '' ) ); $commentdata['akismet_result'] = $response[1]; if ( isset( $response[0]['x-akismet-pro-tip'] ) ) $commentdata['akismet_pro_tip'] = $response[0]['x-akismet-pro-tip']; - if ( 'true' == $response[1] ) { + if ( isset( $response[0]['x-akismet-error'] ) ) { + // An error occurred that we anticipated (like a suspended key) and want the user to act on. + // Send to moderation. + self::$last_comment_result = '0'; + } + else if ( 'true' == $response[1] ) { // akismet_spam_count will be incremented later by comment_is_spam() self::$last_comment_result = 'spam'; @@ -363,10 +370,155 @@ class Akismet { if ( self::is_test_mode() ) $c['is_test'] = 'true'; - $response = self::http_post( http_build_query( $c ), 'comment-check' ); + $response = self::http_post( build_query( $c ), 'comment-check' ); return ( is_array( $response ) && ! empty( $response[1] ) ) ? $response[1] : false; } + + + + public static function transition_comment_status( $new_status, $old_status, $comment ) { + + if ( $new_status == $old_status ) + return; + + # we don't need to record a history item for deleted comments + if ( $new_status == 'delete' ) + return; + + if ( !current_user_can( 'edit_post', $comment->comment_post_ID ) && !current_user_can( 'moderate_comments' ) ) + return; + + if ( defined('WP_IMPORTING') && WP_IMPORTING == true ) + return; + + // if this is present, it means the status has been changed by a re-check, not an explicit user action + if ( get_comment_meta( $comment->comment_ID, 'akismet_rechecking' ) ) + return; + + global $current_user; + $reporter = ''; + if ( is_object( $current_user ) ) + $reporter = $current_user->user_login; + + // Assumption alert: + // We want to submit comments to Akismet only when a moderator explicitly spams or approves it - not if the status + // is changed automatically by another plugin. Unfortunately WordPress doesn't provide an unambiguous way to + // determine why the transition_comment_status action was triggered. And there are several different ways by which + // to spam and unspam comments: bulk actions, ajax, links in moderation emails, the dashboard, and perhaps others. + // We'll assume that this is an explicit user action if certain POST/GET variables exist. + if ( ( isset( $_POST['status'] ) && in_array( $_POST['status'], array( 'spam', 'unspam' ) ) ) || + ( isset( $_POST['spam'] ) && (int) $_POST['spam'] == 1 ) || + ( isset( $_POST['unspam'] ) && (int) $_POST['unspam'] == 1 ) || + ( isset( $_POST['comment_status'] ) && in_array( $_POST['comment_status'], array( 'spam', 'unspam' ) ) ) || + ( isset( $_GET['action'] ) && in_array( $_GET['action'], array( 'spam', 'unspam' ) ) ) || + ( isset( $_POST['action'] ) && in_array( $_POST['action'], array( 'editedcomment' ) ) ) + ) { + if ( $new_status == 'spam' && ( $old_status == 'approved' || $old_status == 'unapproved' || !$old_status ) ) { + return self::submit_spam_comment( $comment->comment_ID ); + } elseif ( $old_status == 'spam' && ( $new_status == 'approved' || $new_status == 'unapproved' ) ) { + return self::submit_nonspam_comment( $comment->comment_ID ); + } + } + + self::update_comment_history( $comment->comment_ID, sprintf( __('%1$s changed the comment status to %2$s', 'akismet'), $reporter, $new_status ), 'status-' . $new_status ); + } + + public static function submit_spam_comment( $comment_id ) { + global $wpdb, $current_user, $current_site; + + $comment_id = (int) $comment_id; + + $comment = $wpdb->get_row( $wpdb->prepare( "SELECT * FROM {$wpdb->comments} WHERE comment_ID = %d", $comment_id ) ); + + if ( !$comment ) // it was deleted + return; + + if ( 'spam' != $comment->comment_approved ) + return; + + // use the original version stored in comment_meta if available + $as_submitted = get_comment_meta( $comment_id, 'akismet_as_submitted', true); + + if ( $as_submitted && is_array( $as_submitted ) && isset( $as_submitted['comment_content'] ) ) + $comment = (object) array_merge( (array)$comment, $as_submitted ); + + $comment->blog = get_bloginfo('url'); + $comment->blog_lang = get_locale(); + $comment->blog_charset = get_option('blog_charset'); + $comment->permalink = get_permalink($comment->comment_post_ID); + + if ( is_object($current_user) ) + $comment->reporter = $current_user->user_login; + + if ( is_object($current_site) ) + $comment->site_domain = $current_site->domain; + + $comment->user_role = ''; + if ( isset( $comment->user_ID ) ) + $comment->user_role = Akismet::get_user_roles( $comment->user_ID ); + + if ( self::is_test_mode() ) + $comment->is_test = 'true'; + + $post = get_post( $comment->comment_post_ID ); + $comment->comment_post_modified_gmt = $post->post_modified_gmt; + + $response = Akismet::http_post( build_query( $comment ), 'submit-spam' ); + if ( $comment->reporter ) { + self::update_comment_history( $comment_id, sprintf( __('%s reported this comment as spam', 'akismet'), $comment->reporter ), 'report-spam' ); + update_comment_meta( $comment_id, 'akismet_user_result', 'true' ); + update_comment_meta( $comment_id, 'akismet_user', $comment->reporter ); + } + + do_action('akismet_submit_spam_comment', $comment_id, $response[1]); + } + + public static function submit_nonspam_comment( $comment_id ) { + global $wpdb, $current_user, $current_site; + + $comment_id = (int) $comment_id; + + $comment = $wpdb->get_row( $wpdb->prepare( "SELECT * FROM {$wpdb->comments} WHERE comment_ID = %d", $comment_id ) ); + if ( !$comment ) // it was deleted + return; + + // use the original version stored in comment_meta if available + $as_submitted = get_comment_meta( $comment_id, 'akismet_as_submitted', true); + + if ( $as_submitted && is_array($as_submitted) && isset($as_submitted['comment_content']) ) + $comment = (object) array_merge( (array)$comment, $as_submitted ); + + $comment->blog = get_bloginfo('url'); + $comment->blog_lang = get_locale(); + $comment->blog_charset = get_option('blog_charset'); + $comment->permalink = get_permalink( $comment->comment_post_ID ); + $comment->user_role = ''; + + if ( is_object($current_user) ) + $comment->reporter = $current_user->user_login; + + if ( is_object($current_site) ) + $comment->site_domain = $current_site->domain; + + if ( isset( $comment->user_ID ) ) + $comment->user_role = Akismet::get_user_roles($comment->user_ID); + + if ( Akismet::is_test_mode() ) + $comment->is_test = 'true'; + + $post = get_post( $comment->comment_post_ID ); + $comment->comment_post_modified_gmt = $post->post_modified_gmt; + + $response = self::http_post( build_query( $comment ), 'submit-ham' ); + if ( $comment->reporter ) { + self::update_comment_history( $comment_id, sprintf( __('%s reported this comment as not spam', 'akismet'), $comment->reporter ), 'report-ham' ); + update_comment_meta( $comment_id, 'akismet_user_result', 'false' ); + update_comment_meta( $comment_id, 'akismet_user', $comment->reporter ); + } + + do_action('akismet_submit_nonspam_comment', $comment_id, $response[1]); + } public static function cron_recheck() { global $wpdb; @@ -670,7 +822,14 @@ class Akismet { } public static function load_form_js() { - wp_enqueue_script( 'akismet-form', AKISMET__PLUGIN_URL . '_inc/form.js', array( 'jquery' ), AKISMET_VERSION ); + // WP < 3.3 can't enqueue a script this late in the game and still have it appear in the footer. + // Once we drop support for everything pre-3.3, this can change back to a single enqueue call. + wp_register_script( 'akismet-form', AKISMET__PLUGIN_URL . '_inc/form.js', array(), AKISMET_VERSION, true ); + add_action( 'wp_footer', array( 'Akismet', 'print_form_js' ) ); + add_action( 'admin_footer', array( 'Akismet', 'print_form_js' ) ); + } + + public static function print_form_js() { wp_print_scripts( 'akismet-form' ); } diff --git a/wp-content/plugins/akismet/readme.txt b/wp-content/plugins/akismet/readme.txt index 4360476..c9dce67 100644 --- a/wp-content/plugins/akismet/readme.txt +++ b/wp-content/plugins/akismet/readme.txt @@ -1,27 +1,26 @@ === Akismet === Contributors: matt, ryan, andy, mdawaffe, tellyworth, josephscott, lessbloat, eoigal, cfinke, automattic Tags: akismet, comments, spam -Requires at least: 3.0 -Tested up to: 3.9 -Stable tag: 3.0.0 +Requires at least: 3.1 +Tested up to: 3.9.1 +Stable tag: 3.0.1 License: GPLv2 or later -Akismet checks your comments against the Akismet web service to see if they look like spam or not. +Akismet checks your comments against the Akismet Web service to see if they look like spam or not. == Description == -Akismet checks your comments against the Akismet web service to see if they look like spam or not and lets you -review the spam it catches under your blog's "Comments" admin screen. +Akismet checks your comments against the Akismet Web service to see if they look like spam or not and lets you review the spam it catches under your blog's "Comments" admin screen. -Major new features in Akismet 2.5 include: +Major features in Akismet include: -* A comment status history, so you can easily see which comments were caught or cleared by Akismet, and which were spammed or unspammed by a moderator -* Links are highlighted in the comment body, to reveal hidden or misleading links -* If your web host is unable to reach Akismet's servers, the plugin will automatically retry when your connection is back up -* Moderators can see the number of approved comments for each user -* Spam and Unspam reports now include more information, to help improve accuracy +* Automatically checks all comments and filters out the ones that look like spam. +* Each comment has a status history, so you can easily see which comments were caught or cleared by Akismet and which were spammed or unspammed by a moderator. +* URLs are shown in the comment body to reveal hidden or misleading links. +* Moderators can see the number of approved comments for each user. +* A discard feature that outright blocks the worst spam, saving you disk space and speeding up your site. -PS: You'll need an [Akismet.com API key](http://akismet.com/get/) to use it. Keys are free for personal blogs, with paid subscriptions available for businesses and commercial sites. +PS: You'll need an [Akismet.com API key](http://akismet.com/get/) to use it. Keys are free for personal blogs; paid subscriptions are available for businesses and commercial sites. == Installation == @@ -31,6 +30,13 @@ Upload the Akismet plugin to your blog, Activate it, then enter your [Akismet.co == Changelog == += 3.0.1 = +* Removed dependency on PHP's fsockopen function +* Fix spam/ham reports to work when reported outside of the WP dashboard, e.g., from Notifications or the WP app +* Remove jQuery dependency for comment form JavaScript +* Remove unnecessary data from some Akismet comment meta +* Suspended keys will now result in all comments being put in moderation, not spam. + = 3.0.0 = * Move Akismet to Settings menu * Drop Akismet Stats menu @@ -38,7 +44,7 @@ Upload the Akismet plugin to your blog, Activate it, then enter your [Akismet.co * Add Akismet subscription details and status to Akismet settings * Add contextual help for each page * Improve Akismet setup to use Jetpack to automate plugin setup -* Fix Update Check for Spam to use ajax to avoid page timing out +* Fix "Check for Spam" to use AJAX to avoid page timing out * Fix Akismet settings page to be responsive * Drop legacy code * Tidy up CSS and Javascript diff --git a/wp-content/plugins/akismet/views/notice.php b/wp-content/plugins/akismet/views/notice.php index f613c37..0476929 100644 --- a/wp-content/plugins/akismet/views/notice.php +++ b/wp-content/plugins/akismet/views/notice.php @@ -34,7 +34,7 @@

-

fsockopen or gethostbynamel functions. Akismet cannot work correctly until this is fixed. Please contact your web host or firewall administrator and give them this information about Akismet’s system requirements.', 'akismet'), 'https://blog.akismet.com/akismet-hosting-faq/'); ?>

+

gethostbynamel functions. Akismet cannot work correctly until this is fixed. Please contact your web host or firewall administrator and give them this information about Akismet’s system requirements.', 'akismet'), 'https://blog.akismet.com/akismet-hosting-faq/'); ?>

diff --git a/wp-content/plugins/akismet/wrapper.php b/wp-content/plugins/akismet/wrapper.php index 71d5abf..12641c7 100644 --- a/wp-content/plugins/akismet/wrapper.php +++ b/wp-content/plugins/akismet/wrapper.php @@ -15,7 +15,7 @@ function akismet_test_mode() { function akismet_http_post( $request, $host, $path, $port = 80, $ip = null ) { _deprecated_function( __FUNCTION__, '3.0', 'Akismet::http_post()' ); - $path = str_ireplace( '/1.1/', '', $path ); + $path = str_replace( '/1.1/', '', $path ); return Akismet::http_post( $request, $path, $ip ); } @@ -120,19 +120,19 @@ function akismet_check_for_spam_button( $comment_status ) { return Akismet_Admin::check_for_spam_button( $comment_status ); } function akismet_submit_nonspam_comment( $comment_id ) { - _deprecated_function( __FUNCTION__, '3.0', 'Akismet_Admin::submit_nonspam_comment()' ); + _deprecated_function( __FUNCTION__, '3.0', 'Akismet::submit_nonspam_comment()' ); - return Akismet_Admin::submit_nonspam_comment( $comment_id ); + return Akismet::submit_nonspam_comment( $comment_id ); } function akismet_submit_spam_comment( $comment_id ) { - _deprecated_function( __FUNCTION__, '3.0', 'Akismet_Admin::submit_spam_comment()' ); + _deprecated_function( __FUNCTION__, '3.0', 'Akismet::submit_spam_comment()' ); - return Akismet_Admin::submit_spam_comment( $comment_id ); + return Akismet::submit_spam_comment( $comment_id ); } function akismet_transition_comment_status( $new_status, $old_status, $comment ) { - _deprecated_function( __FUNCTION__, '3.0', 'Akismet_Admin::transition_comment_status()' ); + _deprecated_function( __FUNCTION__, '3.0', 'Akismet::transition_comment_status()' ); - return Akismet_Admin::transition_comment_status( $new_status, $old_status, $comment ); + return Akismet::transition_comment_status( $new_status, $old_status, $comment ); } function akismet_spam_count( $type = false ) { _deprecated_function( __FUNCTION__, '3.0', 'Akismet_Admin::get_spam_count()' ); @@ -290,4 +290,4 @@ function akismet_kill_proxy_check( $option ) { _deprecated_function( __FUNCTION__, '3.0' ); return 0; -} +} \ No newline at end of file diff --git a/wp-content/plugins/google-calendar-events/admin/add.php b/wp-content/plugins/google-calendar-events/admin/add.php new file mode 100644 index 0000000..1c58e04 --- /dev/null +++ b/wp-content/plugins/google-calendar-events/admin/add.php @@ -0,0 +1,376 @@ + +

+ + + + + +
+ + + http://www.google.com/calendar/feeds/your-email@gmail.com/public/basic. +
+ http://www.google.com/calendar/feeds/your-email@gmail.com/private-d65741b037h695ff274247f90746b2ty/basic. +
+ + + + + + + +
+ + + + +
+ + + + +
+ + + PHP date format. Leave this blank if you\'d rather stick with the default format for your blog.', GCE_TEXT_DOMAIN ); ?> +
+ + + PHP date format. Again, leave this blank to stick with the default.', GCE_TEXT_DOMAIN ); ?> +
+ + Default', '', $timezone_list ); + ?> + +
+ + + +
+ + + +
+ +

+ +

+ + +
+ + +

+ + +

+ + +
+

+ + ) +

+

+ +

+ +

+

+ +

+

+ + +

+ + +
+ +

+ +
+ + + +
+ +

+ +
+ + + +
+ + + + +

+ +
+ + + + +

+ +
+ +

+ +
+ + + + +
+ + +

+ +
+ + \ No newline at end of file diff --git a/wp-content/plugins/google-calendar-events/admin/delete.php b/wp-content/plugins/google-calendar-events/admin/delete.php new file mode 100644 index 0000000..1e43e73 --- /dev/null +++ b/wp-content/plugins/google-calendar-events/admin/delete.php @@ -0,0 +1,39 @@ + +

+ + + + + + \ No newline at end of file diff --git a/wp-content/plugins/google-calendar-events/admin/edit.php b/wp-content/plugins/google-calendar-events/admin/edit.php new file mode 100644 index 0000000..c90a542 --- /dev/null +++ b/wp-content/plugins/google-calendar-events/admin/edit.php @@ -0,0 +1,389 @@ + +

+ + + + + +
+ + + http://www.google.com/calendar/feeds/your-email@gmail.com/public/full.', GCE_TEXT_DOMAIN); ?> +
+ http://www.google.com/calendar/feeds/your-email@gmail.com/private-d65741b037h695ff274247f90746b2ty/basic.', GCE_TEXT_DOMAIN); ?> +
+ + + UNIX timestamp in the text-box.', GCE_TEXT_DOMAIN); ?> +
+ + + + +
+ + + + +
+ + + PHP date format. Leave this blank if you\'d rather stick with the default format for your blog.', GCE_TEXT_DOMAIN); ?> +
+ + + PHP date format. Again, leave this blank to stick with the default.', GCE_TEXT_DOMAIN); ?> +
+ + + +
+ + + +
+ + + +
+ /> +

+ +

+ + +
+ + +

event display builder guide.', GCE_TEXT_DOMAIN); ?>

+ + +
+

event display builder guide)', GCE_TEXT_DOMAIN); ?>

+

+ +

+ +

+

+ +

+

+ + +

+ + +
+ +

+ +
+ + + +
+ +

+ +
+ + + +
+ + + value="on" /> + +

+ +
+ + + value="on" /> + +

+ +
+ +

+ +
+ + + value="on" /> + +
+ value="on" /> + +

+ +
+ + \ No newline at end of file diff --git a/wp-content/plugins/google-calendar-events/admin/main.php b/wp-content/plugins/google-calendar-events/admin/main.php new file mode 100644 index 0000000..4c2bcf0 --- /dev/null +++ b/wp-content/plugins/google-calendar-events/admin/main.php @@ -0,0 +1,112 @@ +
+

+ + + +

+

+ + + +

+ + + + + + + + + + + + + + + + + + + + + + + + $event){ ?> + + + + + + + + + +
+  |  |  +
+ + + +
+

+ + + + + + + + + + + + + + + + + + + + + +
+ google-calendar-events directory) and then enter its URL below.', GCE_TEXT_DOMAIN); ?> +
+ +
+ +
+ value="on" /> +
+ +
+ +
+ +
+ +
+ +
+ value="on" /> +
+ +
+ value="on" /> +
+ +
+ + +
\ No newline at end of file diff --git a/wp-content/plugins/google-calendar-events/admin/refresh.php b/wp-content/plugins/google-calendar-events/admin/refresh.php new file mode 100644 index 0000000..b74c32a --- /dev/null +++ b/wp-content/plugins/google-calendar-events/admin/refresh.php @@ -0,0 +1,40 @@ + +

+

+ + + + + + \ No newline at end of file diff --git a/wp-content/plugins/google-calendar-events/admin/timezone-choices.php b/wp-content/plugins/google-calendar-events/admin/timezone-choices.php new file mode 100644 index 0000000..cc3d613 --- /dev/null +++ b/wp-content/plugins/google-calendar-events/admin/timezone-choices.php @@ -0,0 +1,429 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + '; +} +?> \ No newline at end of file diff --git a/wp-content/plugins/google-calendar-events/css/gce-old-style.css b/wp-content/plugins/google-calendar-events/css/gce-old-style.css new file mode 100644 index 0000000..75e343d --- /dev/null +++ b/wp-content/plugins/google-calendar-events/css/gce-old-style.css @@ -0,0 +1,28 @@ +.gce-page-grid .gce-calendar th{ + padding:0 !important; +} + +.gce-page-grid .gce-calendar th abbr, +.gce-widget-grid .gce-calendar th abbr{ + border-bottom:none !important; +} + +.gce-page-list .gce-list p{ + margin:0 !important; +} + +.gce-page-list .gce-list ul, +.gce-widget-list .gce-list ul{ + margin:0 !important; + padding:0 !important; +} + +.gce-event-info .gce-tooltip-title{ + margin:5px !important; +} + +.gce-event-info ul{ + padding:0 !important; + margin:5px !important; + list-style-type:none !important; +} \ No newline at end of file diff --git a/wp-content/plugins/google-calendar-events/css/gce-style.css b/wp-content/plugins/google-calendar-events/css/gce-style.css new file mode 100644 index 0000000..fd36360 --- /dev/null +++ b/wp-content/plugins/google-calendar-events/css/gce-style.css @@ -0,0 +1,203 @@ +/* PAGE GRID */ + +.gce-page-grid .gce-calendar .gce-caption{ /* Caption at top of calendar */ + color:#333333; + text-align:center; +} + +.gce-page-grid .gce-calendar{ /* Main calendar table */ + width:100%; + border-collapse:collapse; + border:1px solid #CCCCCC; + color:#CCCCCC; +} + +.gce-page-grid .gce-calendar th{ /* Day headings (S, M etc.) */ + border:1px solid #CCCCCC; + text-align:center; + width:14.29%; + padding:0; +} + +.gce-page-grid .gce-calendar td{ /* Day table cells */ + border:1px solid #CCCCCC; + text-align:center; + height:80px; + vertical-align:middle; + padding:0; +} + +.gce-page-grid .gce-calendar .gce-has-events{ /* Table cells with events */ + color:#333333; + cursor:pointer; +} + +.gce-page-grid .gce-calendar .gce-event-info{ /* Event information */ + display:none; /* Important! */ +} + +.gce-page-grid .gce-calendar .gce-day-number{ /* Day number span */ + font-size:2em; +} + +.gce-page-grid .gce-calendar .gce-today{ /* Table cell that represents today */ + background-color:#DDDDDD; +} + +.gce-page-grid .gce-calendar .gce-next, +.gce-page-grid .gce-calendar .gce-prev{ /* Previous and next month links */ + cursor:pointer; + display:inline-block; + width:3%; +} + +.gce-page-grid .gce-calendar .gce-month-title{ /* Month title */ + display:inline-block; + width:90%; +} + +.gce-page-grid .gce-calendar th abbr{ /* Day letter abbreviation */ + border-bottom:none; +} + +/* PAGE LIST */ + +.gce-page-list .gce-list p{ /* Each piece of information in the list */ + margin:0; +} + +.gce-page-list .gce-list p span, +.gce-page-list .gce-list div span{ /* The text displayed before each piece of info, 'Starts:' for example */ + color:#999999; +} + +.gce-page-list .gce-list .gce-list-event{ /* The event title */ + background-color:#DDDDDD; +} + +.gce-page-list .gce-list .gce-list-title{ /* The title (not the same as event title) */ + font-weight:bold; +} + +.gce-page-list .gce-list ul{ + list-style-type:none; + margin:0; + padding:0; +} + +/* WIDGET GRID */ + +.gce-widget-grid .gce-calendar .gce-caption{ + text-align:center; +} + +.gce-widget-grid .gce-calendar{ /* Main calendar table */ + width:100%; + border:1px solid #CCCCCC; + border-collapse:collapse; +} + +.gce-widget-grid .gce-calendar th{ /* Day headings (S, M etc.) */ + width:14.29%; + border:1px solid #CCCCCC; + text-align:center; +} + +.gce-widget-grid .gce-calendar td{ /* Day table cells */ + color:#CCCCCC; + width:14.29%; + border:1px solid #CCCCCC; + text-align:center; +} + +.gce-widget-grid .gce-calendar .gce-has-events{ /* Table cells with events */ + cursor:pointer; + color:#666666; +} + +.gce-widget-grid .gce-calendar .gce-today{ /* Table cell that represents today */ + background-color:#DDDDDD; +} + +.gce-widget-grid .gce-calendar .gce-event-info{ /* Event information */ + display:none; /* Important! */ +} + +.gce-widget-grid .gce-calendar .gce-next, +.gce-widget-grid .gce-calendar .gce-prev{ /* Prev and next month links */ + cursor:pointer; + display:inline-block; + width:5%; +} + +.gce-widget-grid .gce-calendar .gce-month-title{ /* Month title in caption at top of table */ + display:inline-block; + width:80%; +} + +.gce-widget-grid .gce-calendar th abbr{ /* Day name abbreviations */ + border-bottom:none; +} + +/* WIDGET LIST */ + +.gce-widget-list .gce-list p{ /* Each piece of information in the list */ + margin:0; +} + +.gce-widget-list .gce-list p span, +.gce-widget-list .gce-list div span{ /* The text displayed before each piece of info, 'Starts:' for example */ + color:#999999; +} + +.gce-widget-list .gce-list .gce-list-event{ /* The event title */ + background-color:#DDDDDD; +} + +.gce-widget-list .gce-list .gce-list-title{ /* The title (not the same as event title) */ + font-weight:bold; +} + +.gce-widget-list .gce-list ul{ + list-style-type:none; + margin:0; + padding:0; +} + +/* TOOLTIP */ + +.gce-event-info{ /* Tooltip container */ + background-color:#FFFFFF; + border:1px solid #333333; + max-width:300px; +} + +.gce-event-info .gce-tooltip-title{ /* 'Events on...' text */ + margin:5px; + font-weight:bold; + font-size:1.2em; +} + +.gce-event-info ul{ /* Events list */ + padding:0; + margin:5px; + list-style-type:none; +} + +.gce-event-info ul li{ /* Event list item */ + margin:10px 0 0 0; +} + +.gce-event-info ul li p{ /* Each piece of information */ + margin:0; +} + +.gce-event-info ul li p span, +.gce-event-info ul li div span{ /* The text displayed before each piece of info, 'Starts:' for example */ + color:#999999; +} + +.gce-event-info .gce-tooltip-event{ /* The event title */ + background-color:#DDDDDD; + font-weight:bold; +} \ No newline at end of file diff --git a/wp-content/plugins/google-calendar-events/google-calendar-events.php b/wp-content/plugins/google-calendar-events/google-calendar-events.php new file mode 100644 index 0000000..103b2ef --- /dev/null +++ b/wp-content/plugins/google-calendar-events/google-calendar-events.php @@ -0,0 +1,697 @@ +have this requirement. You should get in touch with your web hosting provider and ask them to update PHP.

Back to Plugins' ); + } + } + } + + //If any new options have been added between versions, this will update any saved feeds with defaults for new options (shouldn't overwrite anything saved) + function update_settings() { + //If there are some plugin options in the database, but no version info, then this must be an upgrade from version 0.5 or below, so add flag that will provide user with option to clear old transients + if ( get_option( GCE_OPTIONS_NAME ) && ! get_option( 'gce_version' ) ) + add_option( 'gce_clear_old_transients', true ); + + add_option( 'gce_version', GCE_VERSION ); + + add_option( GCE_OPTIONS_NAME ); + add_option( GCE_GENERAL_OPTIONS_NAME ); + + //Get feed options + $options = get_option( GCE_OPTIONS_NAME ); + + if ( ! empty( $options ) ) { + foreach ( $options as $key => $saved_feed_options ) { + $defaults = array( + 'id' => 1, + 'title' => '', + 'url' => '', + 'retrieve_from' => 'today', + 'retrieve_from_value' => 0, + 'retrieve_until' => 'any', + 'retrieve_until_value' => 0, + 'max_events' => 25, + 'date_format' => '', + 'time_format' => '', + 'timezone' => 'default', + 'cache_duration' => 43200, + 'multiple_day' => 'false', + 'display_start' => 'time', + 'display_end' => 'time-date', + 'display_location' => '', + 'display_desc' => '', + 'display_link' => 'on', + 'display_start_text' => 'Starts:', + 'display_end_text' => 'Ends:', + 'display_location_text' => 'Location:', + 'display_desc_text' => 'Description:', + 'display_desc_limit' => '', + 'display_link_text' => 'More details', + 'display_link_target' => '', + 'display_separator' => ', ', + 'use_builder' => 'false', + 'builder' => '' + ); + + //If necessary, copy saved behaviour of old show_past_events and day_limit options into the new from / until options + if ( isset( $saved_feed_options['show_past_events'] ) ) { + if ( 'true' == $saved_feed_options['show_past_events'] ) { + $saved_feed_options['retrieve_from'] = 'month-start'; + } else { + $saved_feed_options['retrieve_from'] = 'today'; + } + } + + if ( isset( $saved_feed_options['day_limit'] ) && '' != $saved_feed_options['day_limit'] ) { + $saved_feed_options['retrieve_until'] = 'today'; + $saved_feed_options['retrieve_until_value'] = (int) $saved_feed_options['day_limit'] * 86400; + } + + //Update old display_start / display_end values + if ( ! isset( $saved_feed_options['display_start'] ) ) + $saved_feed_options['display_start'] = 'none'; + elseif ( 'on' == $saved_feed_options['display_start'] ) + $saved_feed_options['display_start'] = 'time'; + + if( ! isset( $saved_feed_options['display_end'] ) ) + $saved_feed_options['display_end'] = 'none'; + elseif ( 'on' == $saved_feed_options['display_end'] ) + $saved_feed_options['display_end'] = 'time-date'; + + //Merge saved options with defaults + foreach ( $saved_feed_options as $option_name => $option ) { + $defaults[$option_name] = $saved_feed_options[$option_name]; + } + + $options[$key] = $defaults; + } + } + + //Save feed options + update_option( GCE_OPTIONS_NAME, $options ); + + //Get general options + $options = get_option( GCE_GENERAL_OPTIONS_NAME ); + + $defaults = array( + 'stylesheet' => '', + 'javascript' => false, + 'loading' => 'Loading...', + 'error' => 'Events cannot currently be displayed, sorry! Please check back later.', + 'fields' => true, + 'old_stylesheet' => false + ); + + $old_stylesheet_option = get_option( 'gce_stylesheet' ); + + //If old custom stylesheet option was set, add it to general options, then delete old option + if( false !== $old_stylesheet_option ) { + $defaults['stylesheet'] = $old_stylesheet_option; + delete_option( 'gce_stylesheet' ); + } elseif ( isset($options['stylesheet'] ) ) { + $defaults['stylesheet'] = $options['stylesheet']; + } + + if ( isset($options['javascript'] ) ) + $defaults['javascript'] = $options['javascript']; + + if ( isset( $options['loading'] ) ) + $defaults['loading'] = $options['loading']; + + if ( isset($options['error'] ) ) + $defaults['error'] = $options['error']; + + if ( isset($options['fields'] ) ) + $defaults['fields'] = $options['fields']; + + if( isset( $options['old_stylesheet'] ) ) + $defaults['old_stylesheet'] = $options['old_stylesheet']; + + //Save general options + update_option( GCE_GENERAL_OPTIONS_NAME, $defaults ); + } + + function init_plugin() { + //Load text domain for i18n + load_plugin_textdomain( GCE_TEXT_DOMAIN, false, dirname( plugin_basename( __FILE__ ) ) . '/languages' ); + } + + //Adds 'Settings' link to main WordPress Plugins page + function add_settings_link( $links ) { + array_unshift( $links, '' . __( 'Settings', GCE_TEXT_DOMAIN ) . '' ); + return $links; + } + + //Setup admin settings page + function setup_admin(){ + global $gce_settings_page; + + $gce_settings_page = add_options_page( 'Google Calendar Events', 'Google Calendar Events', 'manage_options', basename( __FILE__ ), array( $this, 'admin_page' ) ); + + add_action( 'admin_enqueue_scripts', array( $this, 'enqueue_admin_scripts' ) ); + } + + //Add admin JavaScript (to GCE settings page only) + function enqueue_admin_scripts( $hook_suffix ) { + global $gce_settings_page; + + if ( $gce_settings_page == $hook_suffix ) + wp_enqueue_script( 'gce_scripts', WP_PLUGIN_URL . '/' . GCE_PLUGIN_NAME . '/js/gce-admin-script.js', array( 'jquery' ) ); + } + + //Prints admin settings page + function admin_page() { + ?> +
+

+ +

+ + +
+

+

+

+

+
+ + +
+

+

+

+

+

+
+
+ update_settings(); + + //If the message about old transients was displayed, check authority and intention, and then either clear transients or clear flag + if ( isset( $_GET['gce_action'] ) && current_user_can( 'manage_options' ) ) { + switch ( $_GET['gce_action'] ) { + case 'clear_old_transients': + check_admin_referer( 'gce_action_clear_old_transients' ); + $this->clear_old_transients(); + add_settings_error( 'gce_options', 'gce_cleared_old_transients', __( 'Old cached data cleared.', GCE_TEXT_DOMAIN ), 'updated' ); + break; + case 'ignore_old_transients': + check_admin_referer( 'gce_action_ignore_old_transients' ); + delete_option( 'gce_clear_old_transients' ); + } + } + + register_setting( 'gce_options', 'gce_options', array( $this, 'validate_feed_options' ) ); + register_setting( 'gce_general', 'gce_general', array( $this, 'validate_general_options' ) ); + + require_once 'admin/add.php'; + require_once 'admin/edit.php'; + require_once 'admin/delete.php'; + require_once 'admin/refresh.php'; + } + + //Clears any expired transients from the database + function clear_old_transients() { + global $wpdb; + + //Retrieve names of all transients + $transients = $wpdb->get_results( "SELECT option_name FROM $wpdb->options WHERE option_name LIKE '%transient%' AND option_name NOT LIKE '%transient_timeout%'" ); + + if ( ! empty( $transients ) ) { + foreach ( $transients as $transient ) { + //Attempt to retrieve the transient. If it has expired, it will be deleted + get_transient( str_replace( '_transient_', '', $transient->option_name ) ); + } + } + + //Remove the flag + delete_option( 'gce_clear_old_transients' ); + } + + //Register the widget + function add_widget() { + require_once 'widget/gce-widget.php'; + return register_widget( 'GCE_Widget' ); + } + + //Check / validate submitted feed options data before being stored + function validate_feed_options( $input ) { + //Get saved options + $options = get_option( GCE_OPTIONS_NAME ); + + if ( isset( $input['submit_delete'] ) ) { + //If delete button was clicked, delete feed from options array and remove associated transients + unset( $options[$input['id']] ); + $this->delete_feed_transients( (int) $input['id'] ); + add_settings_error( 'gce_options', 'gce_deleted', __( sprintf('Feed %s deleted.', absint( $input['id'] ) ), GCE_TEXT_DOMAIN ), 'updated' ); + } elseif ( isset($input['submit_refresh'] ) ) { + //If refresh button was clicked, delete transients associated with feed + $this->delete_feed_transients( (int) $input['id'] ); + add_settings_error( 'gce_options', 'gce_refreshed', __( sprintf('Cached data for feed %s cleared.', absint( $input['id'] ) ), GCE_TEXT_DOMAIN ), 'updated' ); + } else { + //Otherwise, validate options and add / update them + + //Check id is positive integer + $id = absint( $input['id'] ); + //Escape title text + $title = esc_html( $input['title'] ); + //Escape feed url + $url = esc_url( $input['url'] ); + + //Array of valid options for retrieve_from and retrieve_until settings + $valid_retrieve_options = array( 'now', 'today', 'week', 'month-start', 'month-end', 'any', 'date' ); + + $retrieve_from = 'today'; + $retrieve_from_value = 0; + + //Ensure retrieve_from is valid + if( in_array( $input['retrieve_from'], $valid_retrieve_options ) ) { + $retrieve_from = $input['retrieve_from']; + $retrieve_from_value = (int) $input['retrieve_from_value']; + } + + $retrieve_until = 'any'; + $retrieve_until_value = 0; + + //Ensure retrieve_until is valid + if ( in_array( $input['retrieve_until'], $valid_retrieve_options ) ) { + $retrieve_until = $input['retrieve_until']; + $retrieve_until_value = (int) $input['retrieve_until_value']; + } + + //Check max events is a positive integer. If absint returns 0, reset to default (25) + $max_events = ( 0 == absint($input['max_events'] ) ) ? 25 : absint( $input['max_events'] ); + + $date_format = wp_filter_kses( $input['date_format'] ); + $time_format = wp_filter_kses( $input['time_format'] ); + + //Escape timezone + $timezone = esc_html( $input['timezone'] ); + + //Make sure cache duration is a positive integer or 0. If user has typed 0, leave as 0 but if 0 is returned from absint, set to default (43200) + $cache_duration = $input['cache_duration']; + if ( '0' != $cache_duration ) + $cache_duration = ( 0 == absint( $cache_duration ) ) ? 43200 : absint( $cache_duration ); + + $multiple_day = ( isset( $input['multiple_day'] ) ) ? 'true' : 'false'; + + $display_start = esc_html( $input['display_start'] ); + $display_end = esc_html( $input['display_end'] ); + + //Display options must be 'on' or null + $display_location = ( isset( $input['display_location'] ) ) ? 'on' : null; + $display_desc = ( isset( $input['display_desc'] ) ) ? 'on' : null; + $display_link = ( isset( $input['display_link'] ) ) ? 'on' : null; + $display_link_target = ( isset( $input['display_link_target'] ) ) ? 'on' : null; + + //Filter display text + $display_start_text = wp_filter_kses( $input['display_start_text'] ); + $display_end_text = wp_filter_kses( $input['display_end_text'] ); + $display_location_text = wp_filter_kses( $input['display_location_text'] ); + $display_desc_text = wp_filter_kses( $input['display_desc_text'] ); + $display_link_text = wp_filter_kses( $input['display_link_text'] ); + + $display_separator = wp_filter_kses( $input['display_separator'] ); + + $display_desc_limit = ( 0 == absint( $input['display_desc_limit'] ) ) ? '' : absint( $input['display_desc_limit'] ); + + $use_builder = ( 'false' == $input['use_builder'] ) ? 'false' : 'true'; + $builder = wp_kses_post( $input['builder'] ); + + //Fill options array with validated values + $options[$id] = array( + 'id' => $id, + 'title' => $title, + 'url' => $url, + 'retrieve_from' => $retrieve_from, + 'retrieve_until' => $retrieve_until, + 'retrieve_from_value' => $retrieve_from_value, + 'retrieve_until_value' => $retrieve_until_value, + 'max_events' => $max_events, + 'date_format' => $date_format, + 'time_format' => $time_format, + 'timezone' => $timezone, + 'cache_duration' => $cache_duration, + 'multiple_day' => $multiple_day, + 'display_start' => $display_start, + 'display_end' => $display_end, + 'display_location' => $display_location, + 'display_desc' => $display_desc, + 'display_link' => $display_link, + 'display_start_text' => $display_start_text, + 'display_end_text' => $display_end_text, + 'display_location_text' => $display_location_text, + 'display_desc_text' => $display_desc_text, + 'display_desc_limit' => $display_desc_limit, + 'display_link_text' => $display_link_text, + 'display_link_target' => $display_link_target, + 'display_separator' => $display_separator, + 'use_builder' => $use_builder, + 'builder' => $builder + ); + + if ( isset( $input['submit_add'] ) ) + add_settings_error( 'gce_options', 'gce_added', __( sprintf( 'Feed %s added.', absint( $input['id'] ) ), GCE_TEXT_DOMAIN ), 'updated' ); + else + add_settings_error( 'gce_options', 'gce_edited', __( sprintf( 'Settings for feed %s updated.', absint( $input['id'] ) ), GCE_TEXT_DOMAIN ), 'updated' ); + } + + return $options; + } + + //Validate submitted general options + function validate_general_options( $input ) { + $options = get_option(GCE_GENERAL_OPTIONS_NAME); + + $options['stylesheet'] = esc_url( $input['stylesheet'] ); + $options['javascript'] = ( isset( $input['javascript'] ) ) ? true : false; + $options['loading'] = esc_html( $input['loading'] ); + $options['error'] = wp_filter_kses( $input['error'] ); + $options['fields'] = ( isset( $input['fields'] ) ) ? true : false; + $options['old_stylesheet'] = ( isset( $input['old_stylesheet'] ) ) ? true : false; + + add_settings_error( 'gce_general', 'gce_general_updated', __( 'General options updated.', GCE_TEXT_DOMAIN ), 'updated' ); + + return $options; + } + + //Delete all transients (cached feed data) associated with feed specified + function delete_feed_transients( $id ) { + delete_transient( 'gce_feed_' . $id ); + delete_transient( 'gce_feed_' . $id . '_url' ); + } + + //Handles the shortcode stuff + function shortcode_handler( $atts ) { + $options = get_option( GCE_OPTIONS_NAME ); + + //Check that any feeds have been added + if ( is_array( $options ) && ! empty( $options ) ) { + extract( shortcode_atts( array( + 'id' => '', + 'type' => 'grid', + 'title' => null, + 'max' => 0, + 'order' => 'asc' + ), $atts ) ); + + $no_feeds_exist = true; + $feed_ids = array(); + + if ( '' != $id ) { + //Break comma delimited list of feed ids into array + $feed_ids = explode( ',', str_replace( ' ', '', $id ) ); + + //Check each id is an integer, if not, remove it from the array + foreach ( $feed_ids as $key => $feed_id ) { + if ( 0 == absint( $feed_id ) ) + unset( $feed_ids[$key] ); + } + + //If at least one of the feed ids entered exists, set no_feeds_exist to false + foreach ( $feed_ids as $feed_id ) { + if ( isset($options[$feed_id] ) ) + $no_feeds_exist = false; + } + } else { + foreach ( $options as $feed ) { + $feed_ids[] = $feed['id']; + } + + $no_feeds_exist = false; + } + + //Ensure max events is a positive integer + $max_events = absint( $max ); + + //Ensure sort order is asc or desc + $sort_order = ( 'desc' == $order ) ? 'desc' : 'asc'; + + //Check that at least one valid feed id has been entered + if ( empty( $feed_ids ) || $no_feeds_exist ) { + return __( 'No valid Feed IDs have been entered for this shortcode. Please check that you have entered the IDs correctly and that the Feeds have not been deleted.', GCE_TEXT_DOMAIN ); + } else { + //Turns feed_ids back into string of feed ids delimited by '-' ('1-2-3-4' for example) + $feed_ids = implode( '-', $feed_ids ); + + //If title has been omitted from shortcode, set title_text to null, otherwise set to title (even if empty string) + $title_text = ( false === $title ) ? null : $title; + + switch ( $type ) { + case 'grid': + return gce_print_grid( $feed_ids, $title_text, $max_events ); + case 'ajax': + return gce_print_grid( $feed_ids, $title_text, $max_events, true ); + case 'list': + return gce_print_list( $feed_ids, $title_text, $max_events, $sort_order ); + case 'list-grouped': + return gce_print_list( $feed_ids, $title_text, $max_events, $sort_order, true ); + } + } + } else { + return __( 'No feeds have been added yet. You can add a feed in the Google Calendar Events settings.', GCE_TEXT_DOMAIN ); + } + } + + //Adds the required CSS + function add_styles() { + wp_enqueue_style( 'gce_styles', WP_PLUGIN_URL . '/' . GCE_PLUGIN_NAME . '/css/gce-style.css' ); + + $options = get_option( GCE_GENERAL_OPTIONS_NAME ); + + //If old stylesheet option is enabled, enqueue old styles + if ( $options['old_stylesheet'] ) + wp_enqueue_style( 'gce_old_styles', WP_PLUGIN_URL . '/' . GCE_PLUGIN_NAME . '/css/gce-old-style.css' ); + + //If user has entered a URL to a custom stylesheet, enqueue it too + if( '' != $options['stylesheet'] ) + wp_enqueue_style( 'gce_custom_styles', $options['stylesheet'] ); + } + + //Adds the required scripts + function add_scripts() { + $options = get_option( GCE_GENERAL_OPTIONS_NAME ); + $add_to_footer = (bool) $options['javascript']; + + wp_enqueue_script( 'jquery' ); + wp_enqueue_script( 'gce_jquery_qtip', WP_PLUGIN_URL . '/' . GCE_PLUGIN_NAME . '/js/jquery-qtip.js', array( 'jquery' ), null, $add_to_footer ); + wp_enqueue_script( 'gce_scripts', WP_PLUGIN_URL . '/' . GCE_PLUGIN_NAME . '/js/gce-script.js', array( 'jquery' ), null, $add_to_footer ); + wp_localize_script( 'gce_scripts', 'GoogleCalendarEvents', array( + 'ajaxurl' => admin_url( 'admin-ajax.php', is_ssl() ? 'https' : 'http' ), + 'loading' => $options['loading'] + ) ); + } + + //AJAX stuffs + function gce_ajax() { + if ( isset( $_GET['gce_feed_ids'] ) ) { + $ids = $_GET['gce_feed_ids']; + $title = $_GET['gce_title_text']; + $max = $_GET['gce_max_events']; + $month = $_GET['gce_month']; + $year = $_GET['gce_year']; + + $title = ( 'null' == $title ) ? null : $title; + + if ( 'page' == $_GET['gce_type'] ) { + //The page grid markup to be returned via AJAX + echo gce_print_grid( $ids, $title, $max, true, $month, $year ); + } elseif ( 'widget' == $_GET['gce_type'] ) { + $widget = esc_html( $_GET['gce_widget_id'] ); + + //The widget grid markup to be returned via AJAX + gce_widget_content_grid( $ids, $title, $max, $widget, true, $month, $year ); + } + } + die(); + } + } +} + +function gce_print_list( $feed_ids, $title_text, $max_events, $sort_order, $grouped = false ) { + require_once 'inc/gce-parser.php'; + + $ids = explode( '-', $feed_ids ); + + //Create new GCE_Parser object, passing array of feed id(s) + $list = new GCE_Parser( $ids, $title_text, $max_events, $sort_order ); + + $num_errors = $list->get_num_errors(); + + //If there are less errors than feeds parsed, at least one feed must have parsed successfully so continue to display the list + if ( $num_errors < count( $ids ) ) { + $markup = '
' . $list->get_list( $grouped ) . '
'; + + //If there was at least one error, return the list markup with error messages (for admins only) + if ( $num_errors > 0 && current_user_can( 'manage_options' ) ) + return $list->error_messages() . $markup; + + //Otherwise just return the list markup + return $markup; + } else { + //If current user is an admin, display an error message explaining problem(s). Otherwise, display a 'nice' error messsage + if ( current_user_can( 'manage_options' ) ) { + return $list->error_messages(); + } else { + $options = get_option( GCE_GENERAL_OPTIONS_NAME ); + return wp_kses_post( $options['error'] ); + } + } +} + +function gce_print_grid( $feed_ids, $title_text, $max_events, $ajaxified = false, $month = null, $year = null ) { + require_once 'inc/gce-parser.php'; + + $ids = explode( '-', $feed_ids ); + + //Create new GCE_Parser object, passing array of feed id(s) returned from gce_get_feed_ids() + $grid = new GCE_Parser( $ids, $title_text, $max_events ); + + $num_errors = $grid->get_num_errors(); + + //If there are less errors than feeds parsed, at least one feed must have parsed successfully so continue to display the grid + if ( $num_errors < count( $ids ) ) { + $feed_ids = esc_attr( $feed_ids ); + $title_text = isset( $title_text ) ? esc_html( $title_text) : 'null'; + + $markup = '
'; + + //Add AJAX script if required + if ( $ajaxified ) + $markup .= ''; + + $markup .= $grid->get_grid( $year, $month, $ajaxified ) . '
'; + + //If there was at least one error, return the grid markup with an error message (for admins only) + if ( $num_errors > 0 && current_user_can( 'manage_options' ) ) + return $grid->error_messages() . $markup; + + //Otherwise just return the grid markup + return $markup; + } else { + //If current user is an admin, display an error message explaining problem. Otherwise, display a 'nice' error messsage + if ( current_user_can( 'manage_options' ) ) { + return $grid->error_messages(); + } else { + $options = get_option( GCE_GENERAL_OPTIONS_NAME ); + return wp_kses_post( $options['error'] ); + } + } +} + +$gce = new Google_Calendar_Events(); +?> \ No newline at end of file diff --git a/wp-content/plugins/google-calendar-events/inc/gce-event.php b/wp-content/plugins/google-calendar-events/inc/gce-event.php new file mode 100644 index 0000000..e0765b5 --- /dev/null +++ b/wp-content/plugins/google-calendar-events/inc/gce-event.php @@ -0,0 +1,527 @@ +id = $id; + $this->title = $title; + $this->description = $description; + $this->location = $location; + $this->start_time = $start_time; + $this->end_time = $end_time; + $this->link = $link; + + //Calculate which day type this event is (SWD = single whole day, SPD = single part day, MWD = multiple whole day, MPD = multiple part day) + if ( ( $start_time + 86400 ) <= $end_time ) { + if ( ( $start_time + 86400 ) == $end_time ) { + $this->day_type = 'SWD'; + } else { + if ( ( '12:00 am' == date( 'g:i a', $start_time ) ) && ( '12:00 am' == date( 'g:i a', $end_time ) ) ) { + $this->day_type = 'MWD'; + } else { + $this->day_type = 'MPD'; + } + } + } else { + $this->day_type = 'SPD'; + } + } + + function set_feed( $feed ) { + $this->feed = $feed; + } + + function get_feed() { + return $this->feed; + } + + function get_start_time() { + return $this->start_time; + } + + function get_end_time() { + return $this->end_time; + } + + function get_day_type() { + return $this->day_type; + } + + //Returns an array of days (as UNIX timestamps) that this events spans + function get_days() { + //Round start date to nearest day + $start_time = mktime( 0, 0, 0, date( 'm', $this->start_time ), date( 'd', $this->start_time ) , date( 'Y', $this->start_time ) ); + + $days = array(); + + //If multiple day events should be handled, and this event is a multi-day event, add multiple day event to required days + if ( $this->feed->get_multi_day() && ( 'MPD' == $this->day_type || 'MWD' == $this->day_type ) ) { + $on_next_day = true; + $next_day = $start_time; + + while ( $on_next_day ) { + //If the end time of the event is after 00:00 on the next day (therefore, not doesn't end on this day) + if ( $this->end_time > $next_day ) { + //If $next_day is within the event retrieval date range (specified by retrieve events from / until settings) + if ( $next_day >= $this->feed->get_feed_start() && $next_day < $this->feed->get_feed_end() ) { + $days[] = $next_day; + } + } else { + $on_next_day = false; + } + $next_day += 86400; + } + } else { + //Add event into array of events for that day + $days[] = $start_time; + } + + return $days; + } + + //Returns the markup for this event, so that it can be used in the construction of a grid / list + function get_event_markup( $display_type, $num_in_day, $num ) { + //Set the display type (either tooltip or list) + $this->type = $display_type; + + //Set which number event this is in day (first in day etc) + $this->num_in_day = $num_in_day; + + //Set the position of this event in array of events currently being processed + $this->pos = $num; + + $this->time_now = current_time( 'timestamp' ); + + //Use the builder or the old display options to create the markup, depending on user choice + if ( $this->feed->get_use_builder() ) + return $this->use_builder(); + + return $this->use_old_display_options(); + } + + //Return the event markup using the builder + function use_builder() { + //Array of valid shortcodes + $shortcodes = array( + //Event / feed information shortcodes + + 'event-title', //The event title + 'start-time', //The start time of the event (uses the time format from the feed options, if it is set. Otherwise uses the default WordPress time format) + 'start-date', //The start date of the event (uses the date format from the feed options, if it is set. Otherwise uses the default WordPress date format) + 'start-custom', //The start time / date of the event (uses a custom PHP date format, specified in the 'format' attribute) + 'start-human', //The difference between the start time of the event and the time now, in human-readable format, such as '1 hour', '4 days', '15 mins' + 'end-time', //The end time of the event (uses the time format from the feed options, if it is set. Otherwise uses the default WordPress time format) + 'end-date', //The end date of the event (uses the date format from the feed options, if it is set. Otherwise uses the default WordPress date format) + 'end-custom', //The end time / date of the event (uses a custom PHP date format, specified in the 'format' attribute) + 'end-human', //The difference between the end time of the event and the time now, in human-readable format, such as '1 hour', '4 days', '15 mins' + 'location', //The event location + 'description', //The event deescription (number of words can be limited by the 'limit' attribute) + 'link', //Anything within this shortcode (including further shortcodes) will be linked to the Google Calendar page for this event + 'url', //The raw link URL to the Google Calendar page for this event (can be used to construct more customized links) + 'feed-id', //The ID of this feed (Can be useful for constructing feed specific CSS classes) + 'feed-title', //The feed title + 'maps-link', //Anything within this shortcode (including further shortcodes) will be linked to a Google Maps page based on whatever is specified for the event location + 'length', //How long the events lasts, in human-readable format + 'event-num', //The position of the event in the current list, or the position of the event in the current month (for grids) + 'event-id', //The event UID (unique identifier assigned by Google) + 'cal-id', //The calendar ID + + //Anything between the opening and closing tags of the following logical shortcodes (including further shortcodes) will only be displayed if: + + 'if-all-day', //This is an all-day event + 'if-not-all-day', //This is not an all-day event + 'if-title', //The event has a title + 'if-description', //The event has a description + 'if-location', //The event has a location + 'if-tooltip', //The current display type is 'tooltip' + 'if-list', //The current display type is 'list' + 'if-now', //The event is taking place now (after the start time, but before the end time) + 'if-not-now', //The event is not taking place now (may have ended or not yet started) + 'if-started', //The event has started (and even if it has ended) + 'if-not-started', //The event has not yet started + 'if-ended', //The event has ended + 'if-not-ended', //The event has not ended (and even if it hasn't started) + 'if-first', //The event is the first in the day + 'if-not-first', //The event is not the first in the day + 'if-multi-day', //The event spans multiple days + 'if-single-day' //The event does not span multiple days + ); + + $this->regex = '/(.?)\[(' . implode( '|', $shortcodes ) . ')\b(.*?)(?:(\/))?\](?:(.+?)\[\/\2\])?(.?)/s'; + + return $this->look_for_shortcodes( $this->feed->get_builder() ); + } + + //Look through the EDB markup for shortcodes + function look_for_shortcodes( $markup ) { + return preg_replace_callback( $this->regex, array( $this, 'process_shortcode' ), $markup ); + } + + //Parse a shortcode, returning the appropriate event information + //Much of this code is 'borrowed' from WordPress' own shortcode handling stuff! + function process_shortcode( $m ) { + if ( '[' == $m[1] && ']' == $m[6] ) + return substr( $m[0], 1, -1 ); + + //Extract any attributes contained in the shortcode + extract( shortcode_atts( array( + 'newwindow' => 'false', + 'format' => '', + 'limit' => '0', + 'html' => 'false', + 'markdown' => 'false', + 'precision' => '1', + 'offset' => '0', + 'autolink' => 'true' + ), shortcode_parse_atts( $m[3] ) ) ); + + //Sanitize the attributes + $newwindow = ( 'true' === $newwindow ); + $format = esc_attr( $format ); + $limit = absint( $limit ); + $html = ( 'true' === $html ); + $markdown = ( 'true' === $markdown ); + $precision = absint( $precision ); + $offset = intval( $offset ); + $autolink = ( 'true' === $autolink ); + + //Do the appropriate stuff depending on which shortcode we're looking at. See valid shortcode list (above) for explanation of each shortcode + switch ( $m[2] ) { + case 'event-title': + $title = esc_html( trim( $this->title ) ); + + if ( $markdown && function_exists( 'Markdown' ) ) + $title = Markdown( $title ); + + if ( $html ) + $title = wp_kses_post( html_entity_decode( $title ) ); + + return $m[1] . $title . $m[6]; + + case 'start-time': + return $m[1] . date_i18n( $this->feed->get_time_format(), $this->start_time + $offset ) . $m[6]; + + case 'start-date': + return $m[1] . date_i18n( $this->feed->get_date_format(), $this->start_time + $offset ) . $m[6]; + + case 'start-custom': + return $m[1] . date_i18n( $format, $this->start_time + $offset ) . $m[6]; + + case 'start-human': + return $m[1] . $this->gce_human_time_diff( $this->start_time + $offset, $this->time_now, $precision ) . $m[6]; + + case 'end-time': + return $m[1] . date_i18n( $this->feed->get_time_format(), $this->end_time + $offset ) . $m[6]; + + case 'end-date': + return $m[1] . date_i18n( $this->feed->get_date_format(), $this->end_time + $offset ) . $m[6]; + + case 'end-custom': + return $m[1] . date_i18n( $format, $this->end_time + $offset ) . $m[6]; + + case 'end-human': + return $m[1] . $this->gce_human_time_diff( $this->end_time + $offset, $this->time_now, $precision ) . $m[6]; + + case 'location': + $location = esc_html( trim( $this->location ) ); + + if ( $markdown && function_exists( 'Markdown' ) ) + $location = Markdown( $location ); + + if ( $html ) + $location = wp_kses_post( html_entity_decode( $location ) ); + + return $m[1] . $location . $m[6]; + + case 'description': + $description = esc_html( trim( $this->description ) ); + + //If a word limit has been set, trim the description to the required length + if ( 0 != $limit ) { + preg_match( '/([\S]+\s*){0,' . $limit . '}/', esc_html( $this->description ), $description ); + $description = trim( $description[0] ); + } + + if ( $markdown || $html ) { + if ( $markdown && function_exists( 'Markdown' ) ) + $description = Markdown( $description ); + + if ( $html ) + $description = wp_kses_post( html_entity_decode( $description ) ); + }else{ + //Otherwise, preserve line breaks + $description = nl2br( $description ); + + //Make URLs clickable if required + if ( $autolink ) + $description = make_clickable( $description ); + } + + return $m[1] . $description . $m[6]; + + case 'link': + $new_window = ( $newwindow ) ? ' target="_blank"' : ''; + return $m[1] . '' . $this->look_for_shortcodes( $m[5] ) . '' . $m[6]; + + case 'url': + return $m[1] . esc_url( $this->link . '&ctz=' . $this->feed->get_timezone() ) . $m[6]; + + case 'feed-id': + return $m[1] . intval( $this->feed->get_feed_id() ) . $m[6]; + + case 'feed-title': + return $m[1] . esc_html( $this->feed->get_feed_title() ) . $m[6]; + + case 'maps-link': + $new_window = ( $newwindow ) ? ' target="_blank"' : ''; + return $m[1] . '' . $this->look_for_shortcodes( $m[5] ) . '' . $m[6]; + + case 'length': + return $m[1] . $this->gce_human_time_diff( $this->start_time, $this->end_time, $precision ) . $m[6]; + + case 'event-num': + return $m[1] . intval( $this->pos ) . $m[6]; + + case 'event-id': + return $m[1] . esc_html( $this->id ) . $m[6]; + + case 'cal-id': + $cal_id = explode( '/', $this->feed->get_feed_url() ); + return $m[1] . esc_html( $cal_id[5] ) . $m[6]; + + case 'if-all-day': + if ( 'SWD' == $this->day_type || 'MWD' == $this->day_type ) + return $m[1] . $this->look_for_shortcodes( $m[5] ) . $m[6]; + + return ''; + + case 'if-not-all-day': + if ( 'SPD' == $this->day_type || 'MPD' == $this->day_type ) + return $m[1] . $this->look_for_shortcodes( $m[5] ) . $m[6]; + + return ''; + + case 'if-title': + if ( '' != $this->title ) + return $m[1] . $this->look_for_shortcodes( $m[5] ) . $m[6]; + + return ''; + + case 'if-description': + if ( '' != $this->description ) + return $m[1] . $this->look_for_shortcodes( $m[5] ) . $m[6]; + + return ''; + + case 'if-location': + if ( '' != $this->location ) + return $m[1] . $this->look_for_shortcodes( $m[5] ) . $m[6]; + + return ''; + + case 'if-tooltip': + if ( 'tooltip' == $this->type ) + return $m[1] . $this->look_for_shortcodes( $m[5] ) . $m[6]; + + return ''; + + case 'if-list': + if ( 'list' == $this->type ) + return $m[1] . $this->look_for_shortcodes( $m[5] ) . $m[6]; + + return ''; + + case 'if-now': + if ( $this->time_now >= $this->start_time && $this->time_now < $this->end_time ) + return $m[1] . $this->look_for_shortcodes( $m[5] ) . $m[6]; + + return ''; + + case 'if-not-now': + if ( $this->end_time < $this->time_now || $this->start_time > $this->time_now ) + return $m[1] . $this->look_for_shortcodes( $m[5] ) . $m[6]; + + return ''; + + case 'if-started': + if ( $this->start_time < $this->time_now ) + return $m[1] . $this->look_for_shortcodes( $m[5] ) . $m[6]; + + return ''; + + case 'if-not-started': + if ( $this->start_time > $this->time_now ) + return $m[1] . $this->look_for_shortcodes( $m[5] ) . $m[6]; + + return ''; + + case 'if-ended': + if ( $this->end_time < $this->time_now ) + return $m[1] . $this->look_for_shortcodes( $m[5] ) . $m[6]; + + return ''; + + case 'if-not-ended': + if ( $this->end_time > $this->time_now ) + return $m[1] . $this->look_for_shortcodes( $m[5] ) . $m[6]; + + return ''; + + case 'if-first': + if ( 0 == $this->num_in_day ) + return $m[1] . $this->look_for_shortcodes( $m[5] ) . $m[6]; + + return ''; + + case 'if-not-first': + if ( 0 != $this->num_in_day ) + return $m[1] . $this->look_for_shortcodes( $m[5] ) . $m[6]; + + return ''; + + case 'if-multi-day': + if ( 'MPD' == $this->day_type || 'MWD' == $this->day_type ) + return $m[1] . $this->look_for_shortcodes( $m[5] ) . $m[6]; + + return ''; + + case 'if-single-day': + if ( 'SPD' == $this->day_type || 'SWD' == $this->day_type ) + return $m[1] . $this->look_for_shortcodes( $m[5] ) . $m[6]; + + return ''; + } + } + + //Return the event markup using the old display options + function use_old_display_options() { + $display_options = $this->feed->get_display_options(); + + $markup = '

' . esc_html( $this->title ) . '

'; + + $start_end = array(); + + //If start date / time should be displayed, set up array of start date and time + if ( 'none' != $display_options['display_start'] ) { + $sd = $this->start_time; + $start_end['start'] = array( + 'time' => date_i18n( $this->feed->get_time_format(), $sd ), + 'date' => date_i18n( $this->feed->get_date_format(), $sd ) + ); + } + + //If end date / time should be displayed, set up array of end date and time + if ( 'none' != $display_options['display_end'] ) { + $ed = $this->end_time; + $start_end['end'] = array( + 'time' => date_i18n( $this->feed->get_time_format(), $ed ), + 'date' => date_i18n( $this->feed->get_date_format(), $ed ) + ); + } + + //Add the correct start / end, date / time information to $markup + foreach ( $start_end as $start_or_end => $info ) { + $markup .= '

' . esc_html( $display_options['display_' . $start_or_end . '_text'] ) . ' '; + + switch ( $display_options['display_' . $start_or_end] ) { + case 'time': $markup .= esc_html( $info['time'] ); + break; + case 'date': $markup .= esc_html( $info['date'] ); + break; + case 'time-date': $markup .= esc_html( $info['time'] . $display_options['display_separator'] . $info['date'] ); + break; + case 'date-time': $markup .= esc_html( $info['date'] . $display_options['display_separator'] . $info['time'] ); + } + + $markup .= '

'; + } + + //If location should be displayed (and is not empty) add to $markup + if ( isset( $display_options['display_location'] ) ) { + $event_location = $this->location; + if ( '' != $event_location ) + $markup .= '

' . esc_html( $display_options['display_location_text'] ) . ' ' . esc_html( $event_location ) . '

'; + } + + //If description should be displayed (and is not empty) add to $markup + if ( isset($display_options['display_desc'] ) ) { + $event_desc = $this->description; + + if ( '' != $event_desc ) { + //Limit number of words of description to display, if required + if ( '' != $display_options['display_desc_limit'] ) { + preg_match( '/([\S]+\s*){0,' . $display_options['display_desc_limit'] . '}/', $this->description, $event_desc ); + $event_desc = trim( $event_desc[0] ); + } + + $markup .= '

' . $display_options['display_desc_text'] . ' ' . make_clickable( nl2br( esc_html( $event_desc ) ) ) . '

'; + } + } + + //If link should be displayed add to $markup + if ( isset($display_options['display_link'] ) ) + $markup .= ''; + + return $markup; + } + + //Returns the difference between two times in human-readable format. Based on a patch for human_time_diff posted in the WordPress trac (http://core.trac.wordpress.org/ticket/9272) by Viper007Bond + function gce_human_time_diff( $from, $to = '', $limit = 1 ) { + $units = array( + 31556926 => array( __( '%s year', GCE_TEXT_DOMAIN ), __( '%s years', GCE_TEXT_DOMAIN ) ), + 2629744 => array( __( '%s month', GCE_TEXT_DOMAIN ), __( '%s months', GCE_TEXT_DOMAIN ) ), + 604800 => array( __( '%s week', GCE_TEXT_DOMAIN ), __( '%s weeks', GCE_TEXT_DOMAIN ) ), + 86400 => array( __( '%s day', GCE_TEXT_DOMAIN ), __( '%s days', GCE_TEXT_DOMAIN ) ), + 3600 => array( __( '%s hour', GCE_TEXT_DOMAIN ), __( '%s hours', GCE_TEXT_DOMAIN ) ), + 60 => array( __( '%s min', GCE_TEXT_DOMAIN ), __( '%s mins', GCE_TEXT_DOMAIN ) ), + ); + + if ( empty( $to ) ) + $to = time(); + + $from = (int) $from; + $to = (int) $to; + $diff = (int) abs( $to - $from ); + + $items = 0; + $output = array(); + + foreach ( $units as $unitsec => $unitnames ) { + if ( $items >= $limit ) + break; + + if ( $diff < $unitsec ) + continue; + + $numthisunits = floor( $diff / $unitsec ); + $diff = $diff - ( $numthisunits * $unitsec ); + $items++; + + if ( $numthisunits > 0 ) + $output[] = sprintf( _n( $unitnames[0], $unitnames[1], $numthisunits ), $numthisunits ); + } + + $seperator = _x( ', ', 'human_time_diff' ); + + if ( ! empty( $output ) ) { + return implode( $seperator, $output ); + } else { + $smallest = array_pop( $units ); + return sprintf( $smallest[0], 1 ); + } + } +} +?> \ No newline at end of file diff --git a/wp-content/plugins/google-calendar-events/inc/gce-feed.php b/wp-content/plugins/google-calendar-events/inc/gce-feed.php new file mode 100644 index 0000000..d7a277b --- /dev/null +++ b/wp-content/plugins/google-calendar-events/inc/gce-feed.php @@ -0,0 +1,248 @@ +feed_url ); + + $scheme_and_host = $url_parts['scheme'] . '://' . $url_parts['host']; + + //Remove the exisitng projection from the path, and replace it with '/full-noattendees' + $path = substr( $url_parts['path'], 0, strrpos( $url_parts['path'], '/' ) ) . '/full-noattendees'; + + //Add the default parameters to the querystring (retrieving JSON, not XML) + $query = '?alt=json&singleevents=true&sortorder=ascending&orderby=starttime'; + + $gmt_offset = get_option( 'gmt_offset' ) * 3600; + + //Append the feed specific parameters to the querystring + $query .= '&start-min=' . date( 'Y-m-d\TH:i:s', $this->feed_start - $gmt_offset ); + $query .= '&start-max=' . date( 'Y-m-d\TH:i:s', $this->feed_end - $gmt_offset ); + $query .= '&max-results=' . $this->max_events; + + if ( ! empty( $this->timezone ) ) + $query .= '&ctz=' . $this->timezone; + + //If enabled, use experimental 'fields' parameter of Google Data API, so that only necessary data is retrieved. This *significantly* reduces amount of data to retrieve and process + $general_options = get_option( GCE_GENERAL_OPTIONS_NAME ); + if ( $general_options['fields'] ) + $query .= '&fields=entry(title,link[@rel="alternate"],content,gd:where,gd:when,gCal:uid)'; + + //Put the URL back together + $url = $scheme_and_host . $path . $query; + + //Attempt to retrieve the cached feed data + $this->events = get_transient( 'gce_feed_' . $this->feed_id ); + + //If the cached feed data isn't valid any more (has expired), or the URL has changed (settings have changed), then the feed data needs to be retrieved and decoded again + if ( false === $this->events || 0 == $this->cache_duration || get_transient( 'gce_feed_' . $this->feed_id . '_url' ) != $url ) { + $this->events = array(); + + //Retrieve the feed data + $raw_data = wp_remote_get( $url, array( + 'sslverify' => false, //sslverify is set to false to ensure https URLs work reliably. Data source is Google's servers, so is trustworthy + 'timeout' => 10 //Increase timeout from the default 5 seconds to ensure even large feeds are retrieved successfully + ) ); + + //If $raw_data is a WP_Error, something went wrong + if ( ! is_wp_error( $raw_data ) ) { + //If response code isn't 200, something went wrong + if ( 200 == $raw_data['response']['code'] ) { + //Attempt to convert the returned JSON into an array + $raw_data = json_decode( $raw_data['body'], true ); + + //If decoding was successful + if ( ! empty( $raw_data ) ) { + //If there are some entries (events) to process + if ( isset( $raw_data['feed']['entry'] ) ) { + //Loop through each event, extracting the relevant information + foreach ( $raw_data['feed']['entry'] as $event ) { + $id = esc_html( substr( $event['gCal$uid']['value'], 0, strpos( $event['gCal$uid']['value'], '@' ) ) ); + $title = esc_html( $event['title']['$t'] ); + $description = esc_html( $event['content']['$t'] ); + $link = esc_url( $event['link'][0]['href'] ); + $location = esc_html( $event['gd$where'][0]['valueString'] ); + $start_time = $this->iso_to_ts( $event['gd$when'][0]['startTime'] ); + $end_time = $this->iso_to_ts( $event['gd$when'][0]['endTime'] ); + + //Create a GCE_Event using the above data. Add it to the array of events + $this->events[] = new GCE_Event( $id, $title, $description, $location, $start_time, $end_time, $link ); + } + } + + if ( 0 != $this->cache_duration ) { + //Cache the feed data + set_transient( 'gce_feed_' . $this->feed_id, $this->events, $this->cache_duration ); + set_transient( 'gce_feed_' . $this->feed_id . '_url', $url, $this->cache_duration ); + } + } else { + //json_decode failed + $this->error = __( 'Some data was retrieved, but could not be parsed successfully. Please ensure your feed URL is correct.', GCE_TEXT_DOMAIN ); + } + } else { + //The response code wasn't 200, so generate a helpful(ish) error message depending on error code + switch ( $raw_data['response']['code'] ) { + case 404: + $this->error = __( 'The feed could not be found (404). Please ensure your feed URL is correct.', GCE_TEXT_DOMAIN ); + break; + case 403: + $this->error = __( 'Access to this feed was denied (403). Please ensure you have public sharing enabled for your calendar.', GCE_TEXT_DOMAIN ); + break; + default: + $this->error = sprintf( __( 'The feed data could not be retrieved. Error code: %s. Please ensure your feed URL is correct.', GCE_TEXT_DOMAIN ), $raw_data['response']['code'] ); + } + } + }else{ + //Generate an error message from the returned WP_Error + $this->error = $raw_data->get_error_message() . ' Please ensure your feed URL is correct.'; + } + } + + //Makes sure each event knows it came from this feed + foreach ( $this->events as $event ) { + $event->set_feed( $this ); + } + } + + //Convert an ISO date/time to a UNIX timestamp + function iso_to_ts( $iso ) { + sscanf( $iso, "%u-%u-%uT%u:%u:%uZ", $year, $month, $day, $hour, $minute, $second ); + return mktime( $hour, $minute, $second, $month, $day, $year ); + } + + //Return error message, or false if no error occurred + function error() { + return $this->error; + } + + //Setters + + function set_feed_id( $v ) { + $this->feed_id = $v; + } + + function set_feed_title( $v ) { + $this->feed_title = $v; + } + + function set_feed_url( $v ) { + $this->feed_url = $v; + } + + function set_max_events( $v ) { + $this->max_events = $v; + } + + function set_cache_duration( $v ) { + $this->cache_duration = $v; + } + + function set_date_format( $v ) { + $this->date_format = $v; + } + + function set_time_format( $v ) { + $this->time_format = $v; + } + + function set_timezone( $v ) { + $this->timezone = $v; + } + + function set_display_options( $v ) { + $this->display_opts = $v; + } + + function set_multi_day( $v ) { + $this->multi_day = $v; + } + + function set_feed_start( $v ) { + $this->feed_start = $v; + } + + function set_feed_end( $v ) { + $this->feed_end = $v; + } + + function set_use_builder( $v ) { + $this->use_builder = $v; + } + + function set_builder( $v ) { + $this->builder = $v; + } + + //Getters + + function get_events() { + return $this->events; + } + + function get_feed_id() { + return $this->feed_id; + } + + function get_feed_title() { + return $this->feed_title; + } + + function get_feed_url() { + return $this->feed_url; + } + + function get_date_format() { + return $this->date_format; + } + + function get_time_format() { + return $this->time_format; + } + + function get_display_options() { + return $this->display_opts; + } + + function get_multi_day() { + return $this->multi_day; + } + + function get_feed_start() { + return $this->feed_start; + } + + function get_feed_end() { + return $this->feed_end; + } + + function get_timezone() { + return $this->timezone; + } + + function get_use_builder() { + return $this->use_builder; + } + + function get_builder() { + return $this->builder; + } +} +?> \ No newline at end of file diff --git a/wp-content/plugins/google-calendar-events/inc/gce-parser.php b/wp-content/plugins/google-calendar-events/inc/gce-parser.php new file mode 100644 index 0000000..bff4c3f --- /dev/null +++ b/wp-content/plugins/google-calendar-events/inc/gce-parser.php @@ -0,0 +1,355 @@ +title = $title_text; + $this->max_events_display = $max_events; + $this->sort_order = $sort_order; + + //Get the feed options + $options = get_option( GCE_OPTIONS_NAME ); + + $this->start_of_week = get_option( 'start_of_week' ); + + foreach ( $feed_ids as $single_feed ) { + //Get the options for this particular feed + if ( isset( $options[$single_feed] ) ) { + $feed_options = $options[$single_feed]; + + $feed = new GCE_Feed(); + + $feed->set_feed_id( $feed_options['id'] ); + $feed->set_feed_title( $feed_options['title'] ); + $feed->set_feed_url( $feed_options['url'] ); + $feed->set_max_events( $feed_options['max_events'] ); + $feed->set_cache_duration( $feed_options['cache_duration'] ); + + //Set the timezone if anything other than default + if ( 'default' != $feed_options['timezone'] ) + $feed->set_timezone( $feed_options['timezone'] ); + + //Set the start date to the appropriate value based on the retrieve_from option + switch ( $feed_options['retrieve_from'] ) { + //Don't just use time() for 'now', as this will effectively make cache duration 1 second. Instead set to previous minute. Events in Google Calendar cannot be set to precision of seconds anyway + case 'now': + $feed->set_feed_start( mktime( date( 'H' ), date( 'i' ), 0, date( 'm' ), date( 'j' ), date( 'Y' ) ) + $feed_options['retrieve_from_value'] ); + break; + case 'today': + $feed->set_feed_start( mktime( 0, 0, 0, date( 'm' ), date( 'j' ), date( 'Y' ) ) + $feed_options['retrieve_from_value'] ); + break; + case 'week': + $feed->set_feed_start( mktime( 0, 0, 0, date( 'm' ), ( date( 'j' ) - date( 'w' ) + $this->start_of_week ), date( 'Y' ) ) + $feed_options['retrieve_from_value'] ); + break; + case 'month-start': + $feed->set_feed_start( mktime( 0, 0, 0, date( 'm' ), 1, date( 'Y' ) ) + $feed_options['retrieve_from_value'] ); + break; + case 'month-end': + $feed->set_feed_start( mktime( 0, 0, 0, date( 'm' ) + 1, 1, date( 'Y' ) ) + $feed_options['retrieve_from_value'] ); + break; + case 'date': + $feed->set_feed_start( $feed_options['retrieve_from_value'] ); + break; + default: + $feed->set_feed_start( 0 ); //any - 1970-01-01 00:00 + } + + //Set the end date to the appropriate value based on the retrieve_until option + switch ( $feed_options['retrieve_until'] ) { + case 'now': + $feed->set_feed_end( mktime( date( 'H' ), date( 'i' ), 0, date( 'm' ), date( 'j' ), date( 'Y' ) ) + $feed_options['retrieve_until_value'] ); + break; + case 'today': + $feed->set_feed_end( mktime( 0, 0, 0, date( 'm' ), date( 'j' ), date( 'Y' ) ) + $feed_options['retrieve_until_value'] ); + break; + case 'week': + $feed->set_feed_end( mktime( 0, 0, 0, date( 'm' ), ( date( 'j' ) - date( 'w' ) + $this->start_of_week ), date( 'Y' ) ) + $feed_options['retrieve_until_value'] ); + break; + case 'month-start': + $feed->set_feed_end( mktime( 0, 0, 0, date( 'm' ), 1, date( 'Y' ) ) + $feed_options['retrieve_until_value'] ); + break; + case 'month-end': + $feed->set_feed_end( mktime( 0, 0, 0, date( 'm' ) + 1, 1, date( 'Y' ) ) + $feed_options['retrieve_until_value'] ); + break; + case 'date': + $feed->set_feed_end( $feed_options['retrieve_until_value'] ); + break; + case 'any': + $feed->set_feed_end( 2145916800 ); //any - 2038-01-01 00:00 + } + + //Set date and time formats. If they have not been set by user, set to global WordPress formats + $feed->set_date_format( ( empty( $feed_options['date_format'] ) ) ? get_option( 'date_format' ) : $feed_options['date_format'] ); + $feed->set_time_format( ( empty( $feed_options['time_format'] ) ) ? get_option( 'time_format' ) : $feed_options['time_format'] ); + //Set whether to handle multiple day events + $feed->set_multi_day( ( 'true' == $feed_options['multiple_day'] ) ? true : false ); + + //Sets all display options + $feed->set_display_options( array( + 'display_start' => $feed_options['display_start'], + 'display_end' => $feed_options['display_end'], + 'display_location' => $feed_options['display_location'], + 'display_desc' => $feed_options['display_desc'], + 'display_link' => $feed_options['display_link'], + 'display_start_text' => $feed_options['display_start_text'], + 'display_end_text' => $feed_options['display_end_text'], + 'display_location_text' => $feed_options['display_location_text'], + 'display_desc_text' => $feed_options['display_desc_text'], + 'display_desc_limit' => $feed_options['display_desc_limit'], + 'display_link_text' => $feed_options['display_link_text'], + 'display_link_target' => $feed_options['display_link_target'], + 'display_separator' => $feed_options['display_separator'] + ) ); + + $feed->set_use_builder( ( 'true' == $feed_options['use_builder'] ) ? true : false ); + $feed->set_builder( $feed_options['builder'] ); + + //Parse the feed + $feed->init(); + + //Add feed object to array of feeds + $this->feeds[$single_feed] = $feed; + } + } + + $this->merged_feed_data = array(); + + //Merge the feeds together into one array of events + foreach ( $this->feeds as $feed_id => $feed ) { + $errors_occurred = $feed->error(); + + if ( false === $errors_occurred ) + $this->merged_feed_data = array_merge( $this->merged_feed_data, $feed->get_events() ); + else + $this->errors[$feed_id] = $errors_occurred; + } + + //Sort the items into date order + if ( ! empty( $this->merged_feed_data ) ) + usort( $this->merged_feed_data, array( $this, 'compare' ) ); + } + + //Comparison function for use when sorting merged feed data (with usort) + function compare( $event1, $event2 ) { + //Sort ascending or descending + if ( 'asc' == $this->sort_order ) + return $event1->get_start_time() - $event2->get_start_time(); + + return $event2->get_start_time() - $event1->get_start_time(); + } + + //Returns number of errors that have occurred + function get_num_errors() { + return count( $this->errors ); + } + + //Outputs a message describing each error that has occurred + function error_messages() { + $message = '

' . __( '1 or more of your feeds could not be displayed. The following errors occurred:', GCE_TEXT_DOMAIN ) . '

'; + } + + //Returns array of days with events, with sub-arrays of events for that day + function get_event_days() { + $event_days = array(); + + //Total number of events retrieved + $count = count( $this->merged_feed_data ); + + //If maximum events to display is 0 (unlimited) set $max to 1, otherwise use maximum of events specified by user + $max = ( 0 == $this->max_events_display ) ? 1 : $this->max_events_display; + + //Loop through entire array of events, or until maximum number of events to be displayed has been reached + for ( $i = 0; $i < $count && $max > 0; $i++ ) { + $event = $this->merged_feed_data[$i]; + + //Check that event ends, or starts (or both) within the required date range. This prevents all-day events from before / after date range from showing up. + if ( $event->get_end_time() > $event->get_feed()->get_feed_start() && $event->get_start_time() < $event->get_feed()->get_feed_end() ) { + foreach ( $event->get_days() as $day ) { + $event_days[$day][] = $event; + } + + //If maximum events to display isn't 0 (unlimited) decrement $max counter + if ( 0 != $this->max_events_display ) + $max--; + } + } + + return $event_days; + } + + //Returns grid markup + function get_grid ( $year = null, $month = null, $ajaxified = false ) { + require_once 'php-calendar.php'; + + $time_now = current_time( 'timestamp' ); + + //If year and month have not been passed as paramaters, use current month and year + if( ! isset( $year ) ) + $year = date( 'Y', $time_now ); + + if( ! isset( $month ) ) + $month = date( 'm', $time_now ); + + //Get timestamps for the start and end of current month + $current_month_start = mktime( 0, 0, 0, date( 'm', $time_now ), 1, date( 'Y', $time_now ) ); + $current_month_end = mktime( 0, 0, 0, date( 'm', $time_now ) + 1, 1, date( 'Y', $time_now ) ); + + //Get timestamps for the start and end of the month to be displayed in the grid + $display_month_start = mktime( 0, 0, 0, $month, 1, $year ); + $display_month_end = mktime( 0, 0, 0, $month + 1, 1, $year ); + + //It should always be possible to navigate to the current month, even if it doesn't have any events + //So, if the display month is before the current month, set $nav_next to true, otherwise false + //If the display month is after the current month, set $nav_prev to true, otherwise false + $nav_next = ( $display_month_start < $current_month_start ); + $nav_prev = ( $display_month_start >= $current_month_end ); + + //Get events data + $event_days = $this->get_event_days(); + + //If event_days is empty, then there are no events in the feed(s), so set ajaxified to false (Prevents AJAX calendar from allowing to endlessly click through months with no events) + if ( empty( $event_days ) ) + $ajaxified = false; + + $today = mktime( 0, 0, 0, date( 'm', $time_now ), date( 'd', $time_now ), date( 'Y', $time_now ) ); + + $i = 1; + + foreach ( $event_days as $key => $event_day ) { + //If event day is in the month and year specified (by $month and $year) + if ( $key >= $display_month_start && $key < $display_month_end ) { + //Create array of CSS classes. Add gce-has-events + $css_classes = array( 'gce-has-events' ); + + //Create markup for display + $markup = '
'; + + //If title option has been set for display, add it + if ( isset( $this->title ) ) + $markup .= '
' . esc_html( $this->title ) . ' ' . date_i18n( $event_day[0]->get_feed()->get_date_format(), $key ) . '
'; + + $markup .= '
    '; + + foreach ( $event_day as $num_in_day => $event ) { + $feed_id = absint( $event->get_feed()->get_feed_id() ); + $markup .= '
  • ' . $event->get_event_markup( 'tooltip', $num_in_day, $i ) . '
  • '; + + //Add CSS class for the feed from which this event comes. If there are multiple events from the same feed on the same day, the CSS class will only be added once. + $css_classes['feed-' . $feed_id] = 'gce-feed-' . $feed_id; + + $i++; + } + + $markup .= '
'; + + //If number of CSS classes is greater than 2 ('gce-has-events' plus one specific feed class) then there must be events from multiple feeds on this day, so add gce-multiple CSS class + if ( count( $css_classes ) > 2 ) + $css_classes[] = 'gce-multiple'; + + //If event day is today, add gce-today CSS class, otherwise add past or future class + if ( $key == $today ) + $css_classes[] = 'gce-today gce-today-has-events'; + elseif ( $key < $today ) + $css_classes[] = 'gce-day-past'; + else + $css_classes[] = 'gce-day-future'; + + //Change array entry to array of link href, CSS classes, and markup for use in gce_generate_calendar (below) + $event_days[$key] = array( null, implode( ' ', $css_classes ), $markup ); + } elseif ( $key < $display_month_start ) { + //This day is before the display month, so set $nav_prev to true. Remove the day from $event_days, as it's no use for displaying this month + $nav_prev = true; + unset( $event_days[$key] ); + } else { + //This day is after the display month, so set $nav_next to true. Remove the day from $event_days, as it's no use for displaying this month + $nav_next = true; + unset( $event_days[$key] ); + } + } + + //Ensures that gce-today CSS class is added even if there are no events for 'today'. A bit messy :( + if ( ! isset( $event_days[$today] ) ) + $event_days[$today] = array( null, 'gce-today gce-today-no-events', null ); + + $pn = array(); + + //Only add previous / next functionality if AJAX grid is enabled + if ( $ajaxified ) { + //If there are events to display in a previous month, add previous month link + $prev_key = ( $nav_prev ) ? '«' : ' '; + $prev = ( $nav_prev ) ? date( 'm-Y', mktime( 0, 0, 0, $month - 1, 1, $year ) ) : null; + + //If there are events to display in a future month, add next month link + $next_key = ( $nav_next ) ? '»' : ' '; + $next = ( $nav_next ) ? date( 'm-Y', mktime( 0, 0, 0, $month + 1, 1, $year ) ) : null; + + //Array of previous and next link stuff for use in gce_generate_calendar (below) + $pn = array( $prev_key => $prev, $next_key => $next ); + } + + //Generate the calendar markup and return it + return gce_generate_calendar( $year, $month, $event_days, 1, null, $this->start_of_week, $pn ); + } + + function get_list( $grouped = false ) { + $time_now = current_time( 'timestamp' ); + + $event_days = $this->get_event_days(); + + //If event_days is empty, there are no events in the feed(s), so return a message indicating this + if( empty( $event_days) ) + return '

' . __( 'There are currently no events to display.', GCE_TEXT_DOMAIN ) . '

'; + + $today = mktime( 0, 0, 0, date( 'm', $time_now ), date( 'd', $time_now ), date( 'Y', $time_now ) ); + + $i = 1; + + $markup = ''; + + return $markup; + } +} +?> \ No newline at end of file diff --git a/wp-content/plugins/google-calendar-events/inc/php-calendar.php b/wp-content/plugins/google-calendar-events/inc/php-calendar.php new file mode 100644 index 0000000..87fc946 --- /dev/null +++ b/wp-content/plugins/google-calendar-events/inc/php-calendar.php @@ -0,0 +1,83 @@ +get_weekday_initial( date_i18n( 'l', $t, true ) ); + } + + list( $month, $year, $month_name, $weekday ) = explode( ',', date_i18n( 'm, Y, F, w', $first_of_month ) ); + $weekday = ( $weekday + 7 - $first_day ) % 7; #adjust for $first_day + $title = esc_html( $month_name ) . ' ' . $year; #note that some locales don't capitalize month and day names + + #Begin calendar. Uses a real . See http://diveintomark.org/archives/2002/07/03 + list( $p, $pl ) = each( $pn ); + list( $n, $nl ) = each( $pn ); #previous and next links, if applicable + + if ( $p ) + $p = '' . ( ( $pl ) ? ( '' . $p . '' ) : $p ) . ' '; + if ( $n ) + $n = ' ' . ( ( $nl ) ? ( '' . $n . '' ) : $n ) . ''; + + $calendar = '' . "\n" . '\n"; + + if ( $day_name_length ) { #if the day names should be shown ($day_name_length > 0) + #if day_name_length is >3, the full name of the day will be printed + foreach ( $day_names as $d ) { + $calendar .= ''; + } + + $calendar .= "\n"; + } + + $time_now = current_time( 'timestamp' ); + $today = mktime( 0, 0, 0, date( 'm', $time_now ), date( 'd', $time_now ), date( 'Y', $time_now ) ); + + if ( $weekday > 0 ) $calendar .= ''; #initial 'empty' days + for ( $day = 1, $days_in_month = date( 't', $first_of_month ); $day <= $days_in_month; $day++, $weekday++ ) { + if ( 7 == $weekday ) { + $weekday = 0; #start a new week + $calendar .= "\n"; + } + + $timestamp = mktime( 0, 0, 0, $month, $day, $year ); + + if ( isset( $days[$timestamp] ) && is_array( $days[$timestamp] ) ) { + list( $link, $classes, $content ) = $days[$timestamp]; + $calendar .= '' ) : '>' ) . ( ( $link ) ? ( '' . $day . '' . $content ) : '' . $day . '' . $content ) . ''; + }else{ + $css_class = ( $timestamp < $time_now ) ? 'gce-day-past' : 'gce-day-future'; + $calendar .= ''; + } + } + + if ( 7 != $weekday ) $calendar .= ''; #remaining "empty" days + + return $calendar . "\n
' . $p . '' . ( ( $month_href ) ? ( '' . $title . '' ) : $title ) . '' . $n . "
' . esc_html( $d['initial'] ) . '
 
' . $day . ' 
\n"; +} +?> \ No newline at end of file diff --git a/wp-content/plugins/google-calendar-events/js/gce-admin-script.js b/wp-content/plugins/google-calendar-events/js/gce-admin-script.js new file mode 100644 index 0000000..94ecd1d --- /dev/null +++ b/wp-content/plugins/google-calendar-events/js/gce-admin-script.js @@ -0,0 +1,29 @@ +function correct_options(){ + val = jQuery('select[name="gce_options[use_builder]"]').val(); + + if(val == 'true'){ + jQuery('.gce-simple-display-options').next('.form-table').hide(); + jQuery('.gce-simple-display-options').hide(); + jQuery('.gce-simple-display-options').prev('h3').hide(); + + jQuery('.gce-event-builder').next('.form-table').show(); + jQuery('.gce-event-builder').show(); + jQuery('.gce-event-builder').prev('h3').show(); + }else{ + jQuery('.gce-simple-display-options').next('.form-table').show(); + jQuery('.gce-simple-display-options').show(); + jQuery('.gce-simple-display-options').prev('h3').show(); + + jQuery('.gce-event-builder').next('.form-table').hide(); + jQuery('.gce-event-builder').hide(); + jQuery('.gce-event-builder').prev('h3').hide(); + } +} + +jQuery(document).ready(function(){ + correct_options(); + + jQuery('select[name="gce_options[use_builder]"]').change(function(e){ + correct_options(); + }); +}); \ No newline at end of file diff --git a/wp-content/plugins/google-calendar-events/js/gce-script.js b/wp-content/plugins/google-calendar-events/js/gce-script.js new file mode 100644 index 0000000..5f5ae87 --- /dev/null +++ b/wp-content/plugins/google-calendar-events/js/gce-script.js @@ -0,0 +1,45 @@ +function gce_ajaxify(target, feed_ids, max_events, title_text, type){ + //Add click event to change month links + jQuery('#' + target + ' .gce-change-month').click(function(){ + //Extract month and year + var month_year = jQuery(this).attr('name').split('-', 2); + //Add loading text to table caption + jQuery('#' + target + ' caption').html(GoogleCalendarEvents.loading); + //Send AJAX request + jQuery.get(GoogleCalendarEvents.ajaxurl,{ + action:'gce_ajax', + gce_type:type, + gce_feed_ids:feed_ids, + gce_title_text:title_text, + gce_widget_id:target, + gce_max_events:max_events, + gce_month:month_year[0], + gce_year:month_year[1] + }, function(data){ + //Replace existing data with returned AJAX data + if(type == 'widget'){ + jQuery('#' + target).html(data); + }else{ + jQuery('#' + target).replaceWith(data); + } + gce_tooltips('#' + target + ' .gce-has-events'); + }); + }); +} + +function gce_tooltips(target_items){ + jQuery(target_items).each(function(){ + //Add qtip to all target items + jQuery(this).qtip({ + content: jQuery(this).children('.gce-event-info'), + position: { corner: { target: 'center', tooltip: 'bottomLeft' }, adjust: { screen: true } }, + hide: { fixed: true, delay: 100, effect: { length: 0 } }, + show: { solo: true, delay: 0, effect: { length: 0 } }, + style: { padding: "0", classes: { tooltip: 'gce-qtip', tip: 'gce-qtip-tip', title: 'gce-qtip-title', content: 'gce-qtip-content', active: 'gce-qtip-active' }, border: { width: 0 } } + }); + }); +} + +jQuery(document).ready(function(){ + gce_tooltips('.gce-has-events'); +}); \ No newline at end of file diff --git a/wp-content/plugins/google-calendar-events/js/jquery-qtip.js b/wp-content/plugins/google-calendar-events/js/jquery-qtip.js new file mode 100644 index 0000000..26a8c77 --- /dev/null +++ b/wp-content/plugins/google-calendar-events/js/jquery-qtip.js @@ -0,0 +1,15 @@ +/*! + * jquery.qtip. The jQuery tooltip plugin + * + * Copyright (c) 2009 Craig Thompson + * http://craigsworks.com + * + * Licensed under MIT + * http://www.opensource.org/licenses/mit-license.php + * + * Launch : February 2009 + * Version : 1.0.0-rc3 + * Released: Tuesday 12th May, 2009 - 00:00 + * Debug: jquery.qtip.debug.js + */ +(function(f){f.fn.qtip=function(B,u){var y,t,A,s,x,w,v,z;if(typeof B=="string"){if(typeof f(this).data("qtip")!=="object"){f.fn.qtip.log.error.call(self,1,f.fn.qtip.constants.NO_TOOLTIP_PRESENT,false)}if(B=="api"){return f(this).data("qtip").interfaces[f(this).data("qtip").current]}else{if(B=="interfaces"){return f(this).data("qtip").interfaces}}}else{if(!B){B={}}if(typeof B.content!=="object"||(B.content.jquery&&B.content.length>0)){B.content={text:B.content}}if(typeof B.content.title!=="object"){B.content.title={text:B.content.title}}if(typeof B.position!=="object"){B.position={corner:B.position}}if(typeof B.position.corner!=="object"){B.position.corner={target:B.position.corner,tooltip:B.position.corner}}if(typeof B.show!=="object"){B.show={when:B.show}}if(typeof B.show.when!=="object"){B.show.when={event:B.show.when}}if(typeof B.show.effect!=="object"){B.show.effect={type:B.show.effect}}if(typeof B.hide!=="object"){B.hide={when:B.hide}}if(typeof B.hide.when!=="object"){B.hide.when={event:B.hide.when}}if(typeof B.hide.effect!=="object"){B.hide.effect={type:B.hide.effect}}if(typeof B.style!=="object"){B.style={name:B.style}}B.style=c(B.style);s=f.extend(true,{},f.fn.qtip.defaults,B);s.style=a.call({options:s},s.style);s.user=f.extend(true,{},B)}return f(this).each(function(){if(typeof B=="string"){w=B.toLowerCase();A=f(this).qtip("interfaces");if(typeof A=="object"){if(u===true&&w=="destroy"){while(A.length>0){A[A.length-1].destroy()}}else{if(u!==true){A=[f(this).qtip("api")]}for(y=0;y0))}if(typeof s.options.show.solo=="object"){z=f(s.options.show.solo)}else{if(s.options.show.solo===true){z=f("div.qtip").not(s.elements.tooltip)}}if(z){z.each(function(){if(f(this).qtip("api").status.rendered===true){f(this).qtip("api").hide()}})}if(typeof s.options.show.effect.type=="function"){s.options.show.effect.type.call(s.elements.tooltip,s.options.show.effect.length);s.elements.tooltip.queue(function(){w();f(this).dequeue()})}else{switch(s.options.show.effect.type.toLowerCase()){case"fade":s.elements.tooltip.fadeIn(s.options.show.effect.length,w);break;case"slide":s.elements.tooltip.slideDown(s.options.show.effect.length,function(){w();if(s.options.position.type!=="static"){s.updatePosition(y,true)}});break;case"grow":s.elements.tooltip.show(s.options.show.effect.length,w);break;default:s.elements.tooltip.show(null,w);break}s.elements.tooltip.addClass(s.options.style.classes.active)}return f.fn.qtip.log.error.call(s,1,f.fn.qtip.constants.EVENT_SHOWN,"show")},hide:function(y){var x;if(!s.status.rendered){return f.fn.qtip.log.error.call(s,2,f.fn.qtip.constants.TOOLTIP_NOT_RENDERED,"hide")}else{if(s.elements.tooltip.css("display")==="none"){return s}}clearTimeout(s.timers.show);s.elements.tooltip.stop(true,false);x=s.beforeHide.call(s,y);if(x===false){return s}function w(){s.onHide.call(s,y)}s.cache.toggle=0;if(typeof s.options.hide.effect.type=="function"){s.options.hide.effect.type.call(s.elements.tooltip,s.options.hide.effect.length);s.elements.tooltip.queue(function(){w();f(this).dequeue()})}else{switch(s.options.hide.effect.type.toLowerCase()){case"fade":s.elements.tooltip.fadeOut(s.options.hide.effect.length,w);break;case"slide":s.elements.tooltip.slideUp(s.options.hide.effect.length,w);break;case"grow":s.elements.tooltip.hide(s.options.hide.effect.length,w);break;default:s.elements.tooltip.hide(null,w);break}s.elements.tooltip.removeClass(s.options.style.classes.active)}return f.fn.qtip.log.error.call(s,1,f.fn.qtip.constants.EVENT_HIDDEN,"hide")},updatePosition:function(w,x){var C,G,L,J,H,E,y,I,B,D,K,A,F,z;if(!s.status.rendered){return f.fn.qtip.log.error.call(s,2,f.fn.qtip.constants.TOOLTIP_NOT_RENDERED,"updatePosition")}else{if(s.options.position.type=="static"){return f.fn.qtip.log.error.call(s,1,f.fn.qtip.constants.CANNOT_POSITION_STATIC,"updatePosition")}}G={position:{left:0,top:0},dimensions:{height:0,width:0},corner:s.options.position.corner.target};L={position:s.getPosition(),dimensions:s.getDimensions(),corner:s.options.position.corner.tooltip};if(s.options.position.target!=="mouse"){if(s.options.position.target.get(0).nodeName.toLowerCase()=="area"){J=s.options.position.target.attr("coords").split(",");for(C=0;CG.dimensions.width){G.dimensions.width=J[C]}if(J[C]G.dimensions.height){G.dimensions.height=J[C]}if(J[C]0){if(L.corner.search(/Left/)!==-1){y.left-=s.options.style.border.radius}else{if(L.corner.search(/Right/)!==-1){y.left+=s.options.style.border.radius}}if(L.corner.search(/Top/)!==-1){y.top-=s.options.style.border.radius}else{if(L.corner.search(/Bottom/)!==-1){y.top+=s.options.style.border.radius}}}if(I){if(L.corner.search(/top/)!==-1){y.top-=I}else{if(L.corner.search(/bottom/)!==-1){y.top+=I}}if(L.corner.search(/left/)!==-1){y.left-=I}else{if(L.corner.search(/right/)!==-1){y.left+=I}}if(L.corner.search(/leftMiddle|rightMiddle/)!==-1){y.top-=1}}if(s.options.position.adjust.screen===true){y=o.call(s,y,G,L)}if(s.options.position.target==="mouse"&&s.options.position.adjust.mouse===true){if(s.options.position.adjust.screen===true&&s.elements.tip){K=s.elements.tip.attr("rel")}else{K=s.options.position.corner.tooltip}y.left+=(K.search(/right/i)!==-1)?-6:6;y.top+=(K.search(/bottom/i)!==-1)?-6:6}if(!s.elements.bgiframe&&f.browser.msie&&parseInt(f.browser.version.charAt(0))==6){f("select, object").each(function(){A=f(this).offset();A.bottom=A.top+f(this).height();A.right=A.left+f(this).width();if(y.top+L.dimensions.height>=A.top&&y.left+L.dimensions.width>=A.left){k.call(s)}})}y.left+=s.options.position.adjust.x;y.top+=s.options.position.adjust.y;F=s.getPosition();if(y.left!=F.left||y.top!=F.top){z=s.beforePositionUpdate.call(s,w);if(z===false){return s}s.cache.position=y;if(x===true){s.status.animated=true;s.elements.tooltip.animate(y,200,"swing",function(){s.status.animated=false})}else{s.elements.tooltip.css(y)}s.onPositionUpdate.call(s,w);if(typeof w!=="undefined"&&w.type&&w.type!=="mousemove"){f.fn.qtip.log.error.call(s,1,f.fn.qtip.constants.EVENT_POSITION_UPDATED,"updatePosition")}}return s},updateWidth:function(w){var x;if(!s.status.rendered){return f.fn.qtip.log.error.call(s,2,f.fn.qtip.constants.TOOLTIP_NOT_RENDERED,"updateWidth")}else{if(w&&typeof w!=="number"){return f.fn.qtip.log.error.call(s,2,"newWidth must be of type number","updateWidth")}}x=s.elements.contentWrapper.siblings().add(s.elements.tip).add(s.elements.button);if(!w){if(typeof s.options.style.width.value=="number"){w=s.options.style.width.value}else{s.elements.tooltip.css({width:"auto"});x.hide();if(f.browser.msie){s.elements.wrapper.add(s.elements.contentWrapper.children()).css({zoom:"normal"})}w=s.getDimensions().width+1;if(!s.options.style.width.value){if(w>s.options.style.width.max){w=s.options.style.width.max}if(w").get(0).getContext){z=s.elements.tooltip.find(".qtip-tip canvas:first");x=z.get(0).getContext("2d");x.clearRect(0,0,300,300);y=z.parent("div[rel]:first").attr("rel");B=b(y,s.options.style.tip.size.width,s.options.style.tip.size.height);h.call(s,z,B,s.options.style.tip.color||s.options.style.border.color)}else{if(f.browser.msie){z=s.elements.tooltip.find('.qtip-tip [nodeName="shape"]');z.attr("fillcolor",s.options.style.tip.color||s.options.style.border.color)}}}if(s.options.style.border.radius>0){s.elements.tooltip.find(".qtip-betweenCorners").css({backgroundColor:s.options.style.border.color});if(f("").get(0).getContext){A=g(s.options.style.border.radius);s.elements.tooltip.find(".qtip-wrapper canvas").each(function(){x=f(this).get(0).getContext("2d");x.clearRect(0,0,300,300);y=f(this).parent("div[rel]:first").attr("rel");r.call(s,f(this),A[y],s.options.style.border.radius,s.options.style.border.color)})}else{if(f.browser.msie){s.elements.tooltip.find('.qtip-wrapper [nodeName="arc"]').each(function(){f(this).attr("fillcolor",s.options.style.border.color)})}}}return f.fn.qtip.log.error.call(s,1,f.fn.qtip.constants.EVENT_STYLE_UPDATED,"updateStyle")},updateContent:function(A,y){var z,x,w;if(!s.status.rendered){return f.fn.qtip.log.error.call(s,2,f.fn.qtip.constants.TOOLTIP_NOT_RENDERED,"updateContent")}else{if(!A){return f.fn.qtip.log.error.call(s,2,f.fn.qtip.constants.NO_CONTENT_PROVIDED,"updateContent")}}z=s.beforeContentUpdate.call(s,A);if(typeof z=="string"){A=z}else{if(z===false){return}}if(f.browser.msie){s.elements.contentWrapper.children().css({zoom:"normal"})}if(A.jquery&&A.length>0){A.clone(true).appendTo(s.elements.content).show()}else{s.elements.content.html(A)}x=s.elements.content.find("img[complete=false]");if(x.length>0){w=0;x.each(function(C){f('').load(function(){if(++w==x.length){B()}})})}else{B()}function B(){s.updateWidth();if(y!==false){if(s.options.position.type!=="static"){s.updatePosition(s.elements.tooltip.is(":visible"),true)}if(s.options.style.tip.corner!==false){n.call(s)}}}s.onContentUpdate.call(s);return f.fn.qtip.log.error.call(s,1,f.fn.qtip.constants.EVENT_CONTENT_UPDATED,"loadContent")},loadContent:function(w,z,A){var y;if(!s.status.rendered){return f.fn.qtip.log.error.call(s,2,f.fn.qtip.constants.TOOLTIP_NOT_RENDERED,"loadContent")}y=s.beforeContentLoad.call(s);if(y===false){return s}if(A=="post"){f.post(w,z,x)}else{f.get(w,z,x)}function x(B){s.onContentLoad.call(s);f.fn.qtip.log.error.call(s,1,f.fn.qtip.constants.EVENT_CONTENT_LOADED,"loadContent");s.updateContent(B)}return s},updateTitle:function(w){if(!s.status.rendered){return f.fn.qtip.log.error.call(s,2,f.fn.qtip.constants.TOOLTIP_NOT_RENDERED,"updateTitle")}else{if(!w){return f.fn.qtip.log.error.call(s,2,f.fn.qtip.constants.NO_CONTENT_PROVIDED,"updateTitle")}}returned=s.beforeTitleUpdate.call(s);if(returned===false){return s}if(s.elements.button){s.elements.button=s.elements.button.clone(true)}s.elements.title.html(w);if(s.elements.button){s.elements.title.prepend(s.elements.button)}s.onTitleUpdate.call(s);return f.fn.qtip.log.error.call(s,1,f.fn.qtip.constants.EVENT_TITLE_UPDATED,"updateTitle")},focus:function(A){var y,x,w,z;if(!s.status.rendered){return f.fn.qtip.log.error.call(s,2,f.fn.qtip.constants.TOOLTIP_NOT_RENDERED,"focus")}else{if(s.options.position.type=="static"){return f.fn.qtip.log.error.call(s,1,f.fn.qtip.constants.CANNOT_FOCUS_STATIC,"focus")}}y=parseInt(s.elements.tooltip.css("z-index"));x=6000+f("div.qtip[qtip]").length-1;if(!s.status.focused&&y!==x){z=s.beforeFocus.call(s,A);if(z===false){return s}f("div.qtip[qtip]").not(s.elements.tooltip).each(function(){if(f(this).qtip("api").status.rendered===true){w=parseInt(f(this).css("z-index"));if(typeof w=="number"&&w>-1){f(this).css({zIndex:parseInt(f(this).css("z-index"))-1})}f(this).qtip("api").status.focused=false}});s.elements.tooltip.css({zIndex:x});s.status.focused=true;s.onFocus.call(s,A);f.fn.qtip.log.error.call(s,1,f.fn.qtip.constants.EVENT_FOCUSED,"focus")}return s},disable:function(w){if(!s.status.rendered){return f.fn.qtip.log.error.call(s,2,f.fn.qtip.constants.TOOLTIP_NOT_RENDERED,"disable")}if(w){if(!s.status.disabled){s.status.disabled=true;f.fn.qtip.log.error.call(s,1,f.fn.qtip.constants.EVENT_DISABLED,"disable")}else{f.fn.qtip.log.error.call(s,1,f.fn.qtip.constants.TOOLTIP_ALREADY_DISABLED,"disable")}}else{if(s.status.disabled){s.status.disabled=false;f.fn.qtip.log.error.call(s,1,f.fn.qtip.constants.EVENT_ENABLED,"disable")}else{f.fn.qtip.log.error.call(s,1,f.fn.qtip.constants.TOOLTIP_ALREADY_ENABLED,"disable")}}return s},destroy:function(){var w,x,y;x=s.beforeDestroy.call(s);if(x===false){return s}if(s.status.rendered){s.options.show.when.target.unbind("mousemove.qtip",s.updatePosition);s.options.show.when.target.unbind("mouseout.qtip",s.hide);s.options.show.when.target.unbind(s.options.show.when.event+".qtip");s.options.hide.when.target.unbind(s.options.hide.when.event+".qtip");s.elements.tooltip.unbind(s.options.hide.when.event+".qtip");s.elements.tooltip.unbind("mouseover.qtip",s.focus);s.elements.tooltip.remove()}else{s.options.show.when.target.unbind(s.options.show.when.event+".qtip-create")}if(typeof s.elements.target.data("qtip")=="object"){y=s.elements.target.data("qtip").interfaces;if(typeof y=="object"&&y.length>0){for(w=0;w0){s.elements.target.data("qtip").current=y.length-1}else{s.elements.target.removeData("qtip")}s.onDestroy.call(s);f.fn.qtip.log.error.call(s,1,f.fn.qtip.constants.EVENT_DESTROYED,"destroy");return s.elements.target},getPosition:function(){var w,x;if(!s.status.rendered){return f.fn.qtip.log.error.call(s,2,f.fn.qtip.constants.TOOLTIP_NOT_RENDERED,"getPosition")}w=(s.elements.tooltip.css("display")!=="none")?false:true;if(w){s.elements.tooltip.css({visiblity:"hidden"}).show()}x=s.elements.tooltip.offset();if(w){s.elements.tooltip.css({visiblity:"visible"}).hide()}return x},getDimensions:function(){var w,x;if(!s.status.rendered){return f.fn.qtip.log.error.call(s,2,f.fn.qtip.constants.TOOLTIP_NOT_RENDERED,"getDimensions")}w=(!s.elements.tooltip.is(":visible"))?true:false;if(w){s.elements.tooltip.css({visiblity:"hidden"}).show()}x={height:s.elements.tooltip.outerHeight(),width:s.elements.tooltip.outerWidth()};if(w){s.elements.tooltip.css({visiblity:"visible"}).hide()}return x}})}function p(){var s,w,u,t,v,y,x;s=this;s.beforeRender.call(s);s.status.rendered=true;s.elements.tooltip='';s.elements.tooltip=f(s.elements.tooltip);s.elements.tooltip.appendTo(s.options.position.container);s.elements.tooltip.data("qtip",{current:0,interfaces:[s]});s.elements.wrapper=s.elements.tooltip.children("div:first");s.elements.contentWrapper=s.elements.wrapper.children("div:first").css({background:s.options.style.background});s.elements.content=s.elements.contentWrapper.children("div:first").css(q(s.options.style));if(f.browser.msie){s.elements.wrapper.add(s.elements.content).css({zoom:1})}if(s.options.hide.when.event=="unfocus"){s.elements.tooltip.attr("unfocus",true)}if(typeof s.options.style.width.value=="number"){s.updateWidth()}if(f("").get(0).getContext||f.browser.msie){if(s.options.style.border.radius>0){m.call(s)}else{s.elements.contentWrapper.css({border:s.options.style.border.width+"px solid "+s.options.style.border.color})}if(s.options.style.tip.corner!==false){e.call(s)}}else{s.elements.contentWrapper.css({border:s.options.style.border.width+"px solid "+s.options.style.border.color});s.options.style.border.radius=0;s.options.style.tip.corner=false;f.fn.qtip.log.error.call(s,2,f.fn.qtip.constants.CANVAS_VML_NOT_SUPPORTED,"render")}if((typeof s.options.content.text=="string"&&s.options.content.text.length>0)||(s.options.content.text.jquery&&s.options.content.text.length>0)){u=s.options.content.text}else{if(typeof s.elements.target.attr("title")=="string"&&s.elements.target.attr("title").length>0){u=s.elements.target.attr("title").replace("\\n","
");s.elements.target.attr("title","")}else{if(typeof s.elements.target.attr("alt")=="string"&&s.elements.target.attr("alt").length>0){u=s.elements.target.attr("alt").replace("\\n","
");s.elements.target.attr("alt","")}else{u=" ";f.fn.qtip.log.error.call(s,1,f.fn.qtip.constants.NO_VALID_CONTENT,"render")}}}if(s.options.content.title.text!==false){j.call(s)}s.updateContent(u);l.call(s);if(s.options.show.ready===true){s.show()}if(s.options.content.url!==false){t=s.options.content.url;v=s.options.content.data;y=s.options.content.method||"get";s.loadContent(t,v,y)}s.onRender.call(s);f.fn.qtip.log.error.call(s,1,f.fn.qtip.constants.EVENT_RENDERED,"render")}function m(){var F,z,t,B,x,E,u,G,D,y,w,C,A,s,v;F=this;F.elements.wrapper.find(".qtip-borderBottom, .qtip-borderTop").remove();t=F.options.style.border.width;B=F.options.style.border.radius;x=F.options.style.border.color||F.options.style.tip.color;E=g(B);u={};for(z in E){u[z]='
';if(f("").get(0).getContext){u[z]+=''}else{if(f.browser.msie){G=B*2+3;u[z]+=''}}u[z]+="
"}D=F.getDimensions().width-(Math.max(t,B)*2);y='
';w='
'+u.topLeft+u.topRight+y;F.elements.wrapper.prepend(w);C='
'+u.bottomLeft+u.bottomRight+y;F.elements.wrapper.append(C);if(f("").get(0).getContext){F.elements.wrapper.find("canvas").each(function(){A=E[f(this).parent("[rel]:first").attr("rel")];r.call(F,f(this),A,B,x)})}else{if(f.browser.msie){F.elements.tooltip.append('')}}s=Math.max(B,(B+(t-B)));v=Math.max(t-B,0);F.elements.contentWrapper.css({border:"0px solid "+x,borderWidth:v+"px "+s+"px"})}function r(u,w,s,t){var v=u.get(0).getContext("2d");v.fillStyle=t;v.beginPath();v.arc(w[0],w[1],s,0,Math.PI*2,false);v.fill()}function e(v){var t,s,x,u,w;t=this;if(t.elements.tip!==null){t.elements.tip.remove()}s=t.options.style.tip.color||t.options.style.border.color;if(t.options.style.tip.corner===false){return}else{if(!v){v=t.options.style.tip.corner}}x=b(v,t.options.style.tip.size.width,t.options.style.tip.size.height);t.elements.tip='
';if(f("").get(0).getContext){t.elements.tip+=''}else{if(f.browser.msie){u=t.options.style.tip.size.width+","+t.options.style.tip.size.height;w="m"+x[0][0]+","+x[0][1];w+=" l"+x[1][0]+","+x[1][1];w+=" "+x[2][0]+","+x[2][1];w+=" xe";t.elements.tip+='';t.elements.tip+='';t.elements.contentWrapper.css("position","relative")}}t.elements.tooltip.prepend(t.elements.tip+"
");t.elements.tip=t.elements.tooltip.find("."+t.options.style.classes.tip).eq(0);if(f("").get(0).getContext){h.call(t,t.elements.tip.find("canvas:first"),x,s)}if(v.search(/top/)!==-1&&f.browser.msie&&parseInt(f.browser.version.charAt(0))===6){t.elements.tip.css({marginTop:-4})}n.call(t,v)}function h(t,v,s){var u=t.get(0).getContext("2d");u.fillStyle=s;u.beginPath();u.moveTo(v[0][0],v[0][1]);u.lineTo(v[1][0],v[1][1]);u.lineTo(v[2][0],v[2][1]);u.fill()}function n(u){var t,w,s,x,v;t=this;if(t.options.style.tip.corner===false||!t.elements.tip){return}if(!u){u=t.elements.tip.attr("rel")}w=positionAdjust=(f.browser.msie)?1:0;t.elements.tip.css(u.match(/left|right|top|bottom/)[0],0);if(u.search(/top|bottom/)!==-1){if(f.browser.msie){if(parseInt(f.browser.version.charAt(0))===6){positionAdjust=(u.search(/top/)!==-1)?-3:1}else{positionAdjust=(u.search(/top/)!==-1)?1:2}}if(u.search(/Middle/)!==-1){t.elements.tip.css({left:"50%",marginLeft:-(t.options.style.tip.size.width/2)})}else{if(u.search(/Left/)!==-1){t.elements.tip.css({left:t.options.style.border.radius-w})}else{if(u.search(/Right/)!==-1){t.elements.tip.css({right:t.options.style.border.radius+w})}}}if(u.search(/top/)!==-1){t.elements.tip.css({top:-positionAdjust})}else{t.elements.tip.css({bottom:positionAdjust})}}else{if(u.search(/left|right/)!==-1){if(f.browser.msie){positionAdjust=(parseInt(f.browser.version.charAt(0))===6)?1:((u.search(/left/)!==-1)?1:2)}if(u.search(/Middle/)!==-1){t.elements.tip.css({top:"50%",marginTop:-(t.options.style.tip.size.height/2)})}else{if(u.search(/Top/)!==-1){t.elements.tip.css({top:t.options.style.border.radius-w})}else{if(u.search(/Bottom/)!==-1){t.elements.tip.css({bottom:t.options.style.border.radius+w})}}}if(u.search(/left/)!==-1){t.elements.tip.css({left:-positionAdjust})}else{t.elements.tip.css({right:positionAdjust})}}}s="padding-"+u.match(/left|right|top|bottom/)[0];x=t.options.style.tip.size[(s.search(/left|right/)!==-1)?"width":"height"];t.elements.tooltip.css("padding",0);t.elements.tooltip.css(s,x);if(f.browser.msie&&parseInt(f.browser.version.charAt(0))==6){v=parseInt(t.elements.tip.css("margin-top"))||0;v+=parseInt(t.elements.content.css("margin-top"))||0;t.elements.tip.css({marginTop:v})}}function j(){var s=this;if(s.elements.title!==null){s.elements.title.remove()}s.elements.title=f('
').css(q(s.options.style.title,true)).css({zoom:(f.browser.msie)?1:0}).prependTo(s.elements.contentWrapper);if(s.options.content.title.text){s.updateTitle.call(s,s.options.content.title.text)}if(s.options.content.title.button!==false&&typeof s.options.content.title.button=="string"){s.elements.button=f('').css(q(s.options.style.button,true)).html(s.options.content.title.button).prependTo(s.elements.title).click(function(t){if(!s.status.disabled){s.hide(t)}})}}function l(){var t,v,u,s;t=this;v=t.options.show.when.target;u=t.options.hide.when.target;if(t.options.hide.fixed){u=u.add(t.elements.tooltip)}if(t.options.hide.when.event=="inactive"){s=["click","dblclick","mousedown","mouseup","mousemove","mouseout","mouseenter","mouseleave","mouseover"];function y(z){if(t.status.disabled===true){return}clearTimeout(t.timers.inactive);t.timers.inactive=setTimeout(function(){f(s).each(function(){u.unbind(this+".qtip-inactive");t.elements.content.unbind(this+".qtip-inactive")});t.hide(z)},t.options.hide.delay)}}else{if(t.options.hide.fixed===true){t.elements.tooltip.bind("mouseover.qtip",function(){if(t.status.disabled===true){return}clearTimeout(t.timers.hide)})}}function x(z){if(t.status.disabled===true){return}if(t.options.hide.when.event=="inactive"){f(s).each(function(){u.bind(this+".qtip-inactive",y);t.elements.content.bind(this+".qtip-inactive",y)});y()}clearTimeout(t.timers.show);clearTimeout(t.timers.hide);t.timers.show=setTimeout(function(){t.show(z)},t.options.show.delay)}function w(z){if(t.status.disabled===true){return}if(t.options.hide.fixed===true&&t.options.hide.when.event.search(/mouse(out|leave)/i)!==-1&&f(z.relatedTarget).parents("div.qtip[qtip]").length>0){z.stopPropagation();z.preventDefault();clearTimeout(t.timers.hide);return false}clearTimeout(t.timers.show);clearTimeout(t.timers.hide);t.elements.tooltip.stop(true,true);t.timers.hide=setTimeout(function(){t.hide(z)},t.options.hide.delay)}if((t.options.show.when.target.add(t.options.hide.when.target).length===1&&t.options.show.when.event==t.options.hide.when.event&&t.options.hide.when.event!=="inactive")||t.options.hide.when.event=="unfocus"){t.cache.toggle=0;v.bind(t.options.show.when.event+".qtip",function(z){if(t.cache.toggle==0){x(z)}else{w(z)}})}else{v.bind(t.options.show.when.event+".qtip",x);if(t.options.hide.when.event!=="inactive"){u.bind(t.options.hide.when.event+".qtip",w)}}if(t.options.position.type.search(/(fixed|absolute)/)!==-1){t.elements.tooltip.bind("mouseover.qtip",t.focus)}if(t.options.position.target==="mouse"&&t.options.position.type!=="static"){v.bind("mousemove.qtip",function(z){t.cache.mouse={x:z.pageX,y:z.pageY};if(t.status.disabled===false&&t.options.position.adjust.mouse===true&&t.options.position.type!=="static"&&t.elements.tooltip.css("display")!=="none"){t.updatePosition(z)}})}}function o(u,v,A){var z,s,x,y,t,w;z=this;if(A.corner=="center"){return v.position}s=f.extend({},u);y={x:false,y:false};t={left:(s.left=f.fn.qtip.cache.screen.width+f.fn.qtip.cache.screen.scroll.left),top:(s.top=f.fn.qtip.cache.screen.height+f.fn.qtip.cache.screen.scroll.top)};x={left:(t.left&&(A.corner.search(/right/i)!=-1||(A.corner.search(/right/i)==-1&&!t.right))),right:(t.right&&(A.corner.search(/left/i)!=-1||(A.corner.search(/left/i)==-1&&!t.left))),top:(t.top&&A.corner.search(/top/i)==-1),bottom:(t.bottom&&A.corner.search(/bottom/i)==-1)};if(x.left){if(z.options.position.target!=="mouse"){s.left=v.position.left+v.dimensions.width}else{s.left=z.cache.mouse.x}y.x="Left"}else{if(x.right){if(z.options.position.target!=="mouse"){s.left=v.position.left-A.dimensions.width}else{s.left=z.cache.mouse.x-A.dimensions.width}y.x="Right"}}if(x.top){if(z.options.position.target!=="mouse"){s.top=v.position.top+v.dimensions.height}else{s.top=z.cache.mouse.y}y.y="top"}else{if(x.bottom){if(z.options.position.target!=="mouse"){s.top=v.position.top-A.dimensions.height}else{s.top=z.cache.mouse.y-A.dimensions.height}y.y="bottom"}}if(s.left<0){s.left=u.left;y.x=false}if(s.top<0){s.top=u.top;y.y=false}if(z.options.style.tip.corner!==false){s.corner=new String(A.corner);if(y.x!==false){s.corner=s.corner.replace(/Left|Right|Middle/,y.x)}if(y.y!==false){s.corner=s.corner.replace(/top|bottom/,y.y)}if(s.corner!==z.elements.tip.attr("rel")){e.call(z,s.corner)}}return s}function q(u,t){var v,s;v=f.extend(true,{},u);for(s in v){if(t===true&&s.search(/(tip|classes)/i)!==-1){delete v[s]}else{if(!t&&s.search(/(width|border|tip|title|classes|user)/i)!==-1){delete v[s]}}}return v}function c(s){if(typeof s.tip!=="object"){s.tip={corner:s.tip}}if(typeof s.tip.size!=="object"){s.tip.size={width:s.tip.size,height:s.tip.size}}if(typeof s.border!=="object"){s.border={width:s.border}}if(typeof s.width!=="object"){s.width={value:s.width}}if(typeof s.width.max=="string"){s.width.max=parseInt(s.width.max.replace(/([0-9]+)/i,"$1"))}if(typeof s.width.min=="string"){s.width.min=parseInt(s.width.min.replace(/([0-9]+)/i,"$1"))}if(typeof s.tip.size.x=="number"){s.tip.size.width=s.tip.size.x;delete s.tip.size.x}if(typeof s.tip.size.y=="number"){s.tip.size.height=s.tip.size.y;delete s.tip.size.y}return s}function a(){var s,t,u,x,v,w;s=this;u=[true,{}];for(t=0;t0){v.tip.size.width+=1}if(v.tip.size.height%2>0){v.tip.size.height+=1}if(v.tip.corner===true){v.tip.corner=(s.options.position.corner.tooltip==="center")?false:s.options.position.corner.tooltip}return v}function b(v,u,t){var s={bottomRight:[[0,0],[u,t],[u,0]],bottomLeft:[[0,0],[u,0],[0,t]],topRight:[[0,t],[u,0],[u,t]],topLeft:[[0,0],[0,t],[u,t]],topMiddle:[[0,t],[u/2,0],[u,t]],bottomMiddle:[[0,0],[u,0],[u/2,t]],rightMiddle:[[0,0],[u,t/2],[0,t]],leftMiddle:[[u,0],[u,t],[0,t/2]]};s.leftTop=s.bottomRight;s.rightTop=s.bottomLeft;s.leftBottom=s.topRight;s.rightBottom=s.topLeft;return s[v]}function g(s){var t;if(f("").get(0).getContext){t={topLeft:[s,s],topRight:[0,s],bottomLeft:[s,0],bottomRight:[0,0]}}else{if(f.browser.msie){t={topLeft:[-90,90,0],topRight:[-90,90,-s],bottomLeft:[90,270,0],bottomRight:[90,270,-s]}}}return t}function k(){var s,t,u;s=this;u=s.getDimensions();t='