summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'plugins/jetpack/modules')
-rw-r--r--plugins/jetpack/modules/after-the-deadline.php245
-rw-r--r--plugins/jetpack/modules/after-the-deadline/atd-autoproofread.js89
-rw-r--r--plugins/jetpack/modules/after-the-deadline/atd-l10n.php40
-rw-r--r--plugins/jetpack/modules/after-the-deadline/atd-nonvis-editor-plugin.js189
-rw-r--r--plugins/jetpack/modules/after-the-deadline/atd.core.js535
-rw-r--r--plugins/jetpack/modules/after-the-deadline/atd.css105
-rw-r--r--plugins/jetpack/modules/after-the-deadline/button.gifbin0 -> 515 bytes
-rw-r--r--plugins/jetpack/modules/after-the-deadline/config-options.php130
-rw-r--r--plugins/jetpack/modules/after-the-deadline/config-unignore.php143
-rw-r--r--plugins/jetpack/modules/after-the-deadline/install_atd_l10n.js24
-rw-r--r--plugins/jetpack/modules/after-the-deadline/jquery.atd.js417
-rw-r--r--plugins/jetpack/modules/after-the-deadline/proxy.php69
-rw-r--r--plugins/jetpack/modules/after-the-deadline/tinymce/atdbuttontr.gifbin0 -> 123 bytes
-rw-r--r--plugins/jetpack/modules/after-the-deadline/tinymce/css/content.css18
-rw-r--r--plugins/jetpack/modules/after-the-deadline/tinymce/editor_plugin.js476
-rw-r--r--plugins/jetpack/modules/contact-form.php9
-rw-r--r--plugins/jetpack/modules/contact-form/admin.php513
-rw-r--r--plugins/jetpack/modules/contact-form/css/grunion.css9
-rw-r--r--plugins/jetpack/modules/contact-form/grunion-contact-form.php799
-rw-r--r--plugins/jetpack/modules/contact-form/grunion-form-view.php203
-rw-r--r--plugins/jetpack/modules/contact-form/images/blank-screen-akismet.pngbin0 -> 2456 bytes
-rw-r--r--plugins/jetpack/modules/contact-form/images/blank-screen-button.pngbin0 -> 1999 bytes
-rw-r--r--plugins/jetpack/modules/contact-form/images/grunion-form.pngbin0 -> 324 bytes
-rw-r--r--plugins/jetpack/modules/contact-form/images/grunion-menu-big.pngbin0 -> 447 bytes
-rw-r--r--plugins/jetpack/modules/contact-form/images/grunion-menu-hover.pngbin0 -> 399 bytes
-rw-r--r--plugins/jetpack/modules/contact-form/images/grunion-menu.pngbin0 -> 352 bytes
-rw-r--r--plugins/jetpack/modules/contact-form/images/grunion-remove-field-hover.gifbin0 -> 144 bytes
-rw-r--r--plugins/jetpack/modules/contact-form/images/grunion-remove-field.gifbin0 -> 139 bytes
-rw-r--r--plugins/jetpack/modules/contact-form/images/grunion-remove-option-hover.gifbin0 -> 73 bytes
-rw-r--r--plugins/jetpack/modules/contact-form/images/grunion-remove-option.gifbin0 -> 73 bytes
-rw-r--r--plugins/jetpack/modules/contact-form/js/grunion.js734
-rw-r--r--plugins/jetpack/modules/contact-form/js/jquery-ui-1.8.4.custom.min.js185
-rw-r--r--plugins/jetpack/modules/contact-form/readme.txt131
-rw-r--r--plugins/jetpack/modules/enhanced-distribution.php9
-rw-r--r--plugins/jetpack/modules/gravatar-hovercards.php270
-rw-r--r--plugins/jetpack/modules/latex.php103
-rw-r--r--plugins/jetpack/modules/module-info.php432
-rw-r--r--plugins/jetpack/modules/sharedaddy.php23
-rw-r--r--plugins/jetpack/modules/sharedaddy/admin-sharing.css353
-rw-r--r--plugins/jetpack/modules/sharedaddy/admin-sharing.js348
-rw-r--r--plugins/jetpack/modules/sharedaddy/images/custom.pngbin0 -> 1364 bytes
-rw-r--r--plugins/jetpack/modules/sharedaddy/images/designfloat.pngbin0 -> 870 bytes
-rw-r--r--plugins/jetpack/modules/sharedaddy/images/digg.pngbin0 -> 1449 bytes
-rw-r--r--plugins/jetpack/modules/sharedaddy/images/divider.pngbin0 -> 945 bytes
-rw-r--r--plugins/jetpack/modules/sharedaddy/images/draggy.pngbin0 -> 958 bytes
-rw-r--r--plugins/jetpack/modules/sharedaddy/images/email.pngbin0 -> 1380 bytes
-rw-r--r--plugins/jetpack/modules/sharedaddy/images/ember.pngbin0 -> 653 bytes
-rw-r--r--plugins/jetpack/modules/sharedaddy/images/facebook.pngbin0 -> 838 bytes
-rw-r--r--plugins/jetpack/modules/sharedaddy/images/feed.pngbin0 -> 805 bytes
-rw-r--r--plugins/jetpack/modules/sharedaddy/images/googleplus1.pngbin0 -> 1288 bytes
-rw-r--r--plugins/jetpack/modules/sharedaddy/images/linkedin-smart.pngbin0 -> 2566 bytes
-rw-r--r--plugins/jetpack/modules/sharedaddy/images/linkedin.pngbin0 -> 1150 bytes
-rw-r--r--plugins/jetpack/modules/sharedaddy/images/loading.gifbin0 -> 2530 bytes
-rw-r--r--plugins/jetpack/modules/sharedaddy/images/print.pngbin0 -> 1485 bytes
-rw-r--r--plugins/jetpack/modules/sharedaddy/images/reddit.pngbin0 -> 918 bytes
-rw-r--r--plugins/jetpack/modules/sharedaddy/images/rss.pngbin0 -> 907 bytes
-rw-r--r--plugins/jetpack/modules/sharedaddy/images/share-bg.pngbin0 -> 938 bytes
-rw-r--r--plugins/jetpack/modules/sharedaddy/images/sharing-hidden.pngbin0 -> 3696 bytes
-rw-r--r--plugins/jetpack/modules/sharedaddy/images/smart-digg.pngbin0 -> 2024 bytes
-rw-r--r--plugins/jetpack/modules/sharedaddy/images/smart-facebook.pngbin0 -> 2278 bytes
-rw-r--r--plugins/jetpack/modules/sharedaddy/images/smart-like.pngbin0 -> 2108 bytes
-rw-r--r--plugins/jetpack/modules/sharedaddy/images/smart-reddit.pngbin0 -> 2845 bytes
-rw-r--r--plugins/jetpack/modules/sharedaddy/images/smart-stumbleupon.pngbin0 -> 3199 bytes
-rw-r--r--plugins/jetpack/modules/sharedaddy/images/smart-twitter.pngbin0 -> 3018 bytes
-rw-r--r--plugins/jetpack/modules/sharedaddy/images/stumbleupon.pngbin0 -> 818 bytes
-rw-r--r--plugins/jetpack/modules/sharedaddy/images/twitter.pngbin0 -> 485 bytes
-rw-r--r--plugins/jetpack/modules/sharedaddy/images/wordpress.pngbin0 -> 1621 bytes
-rw-r--r--plugins/jetpack/modules/sharedaddy/readme.txt129
-rw-r--r--plugins/jetpack/modules/sharedaddy/screenshot-1.jpgbin0 -> 58951 bytes
-rw-r--r--plugins/jetpack/modules/sharedaddy/screenshot-2.jpgbin0 -> 27511 bytes
-rw-r--r--plugins/jetpack/modules/sharedaddy/sharedaddy.php126
-rw-r--r--plugins/jetpack/modules/sharedaddy/sharedaddy.pot404
-rw-r--r--plugins/jetpack/modules/sharedaddy/sharing-service.php529
-rw-r--r--plugins/jetpack/modules/sharedaddy/sharing-sources.php1076
-rw-r--r--plugins/jetpack/modules/sharedaddy/sharing.css278
-rw-r--r--plugins/jetpack/modules/sharedaddy/sharing.js234
-rw-r--r--plugins/jetpack/modules/sharedaddy/sharing.php417
-rw-r--r--plugins/jetpack/modules/shortcodes.php51
-rw-r--r--plugins/jetpack/modules/shortcodes/archives.php57
-rw-r--r--plugins/jetpack/modules/shortcodes/audio.php71
-rw-r--r--plugins/jetpack/modules/shortcodes/blip.php55
-rw-r--r--plugins/jetpack/modules/shortcodes/dailymotion.php116
-rw-r--r--plugins/jetpack/modules/shortcodes/diggthis.php40
-rw-r--r--plugins/jetpack/modules/shortcodes/flickr.php137
-rw-r--r--plugins/jetpack/modules/shortcodes/googlemaps.php69
-rw-r--r--plugins/jetpack/modules/shortcodes/googlevideo.php29
-rw-r--r--plugins/jetpack/modules/shortcodes/polldaddy.php167
-rw-r--r--plugins/jetpack/modules/shortcodes/scribd.php45
-rw-r--r--plugins/jetpack/modules/shortcodes/slide.php121
-rw-r--r--plugins/jetpack/modules/shortcodes/slideshare.php41
-rw-r--r--plugins/jetpack/modules/shortcodes/soundcloud.php60
-rw-r--r--plugins/jetpack/modules/shortcodes/videopress.php1325
-rw-r--r--plugins/jetpack/modules/shortcodes/vimeo.php109
-rw-r--r--plugins/jetpack/modules/shortcodes/youtube.php291
-rw-r--r--plugins/jetpack/modules/shortlinks.php83
-rw-r--r--plugins/jetpack/modules/stats.php1040
-rw-r--r--plugins/jetpack/modules/subscriptions.php610
-rw-r--r--plugins/jetpack/modules/vaultpress.php28
-rw-r--r--plugins/jetpack/modules/widgets.php33
-rw-r--r--plugins/jetpack/modules/widgets/facebook-likebox.php253
-rw-r--r--plugins/jetpack/modules/widgets/image-widget.php156
-rw-r--r--plugins/jetpack/modules/widgets/rsslinks-widget.php168
-rw-r--r--plugins/jetpack/modules/widgets/twitter-widget.php273
-rw-r--r--plugins/jetpack/modules/wpgroho.js33
104 files changed, 15255 insertions, 0 deletions
diff --git a/plugins/jetpack/modules/after-the-deadline.php b/plugins/jetpack/modules/after-the-deadline.php
new file mode 100644
index 00000000..f634be02
--- /dev/null
+++ b/plugins/jetpack/modules/after-the-deadline.php
@@ -0,0 +1,245 @@
+<?php
+/**
+ * Module Name: Spelling and Grammar
+ * Module Description: Improve your spelling, style, and grammar with the <a href="http://www.afterthedeadline.com/">After&nbsp;the&nbsp;Deadline</a> Proofreading service.
+ * Sort Order: 1
+ * First Introduced: 1.1
+ */
+
+add_action( 'jetpack_modules_loaded', 'AtD_load' );
+
+function AtD_load() {
+ Jetpack::enable_module_configurable( __FILE__ );
+ Jetpack::module_configuration_load( __FILE__, 'AtD_configuration_load' );
+}
+
+function AtD_configuration_load() {
+ wp_safe_redirect( admin_url( 'profile.php#atd' ) );
+ exit;
+}
+
+/*
+ * Load necessary include files
+ */
+include( 'after-the-deadline/config-options.php' );
+include( 'after-the-deadline/config-unignore.php' );
+include( 'after-the-deadline/proxy.php' );
+
+define('ATD_VERSION', '20120221');
+
+/**
+ * Update a user's After the Deadline Setting
+ */
+function AtD_update_setting( $user_id, $name, $value ) {
+ update_user_meta( $user_id, $name, $value );
+}
+
+/**
+ * Retrieve a user's After the Deadline Setting
+ */
+function AtD_get_setting( $user_id, $name, $single = true ) {
+ return get_user_meta( $user_id, $name, $single );
+}
+
+/*
+ * Display the AtD configuration options
+ */
+function AtD_config() {
+ AtD_display_options_form();
+ AtD_display_unignore_form();
+}
+
+/*
+ * Code to update the toolbar with the AtD Button and Install the AtD TinyMCE Plugin
+ */
+function AtD_addbuttons() {
+ /* Don't bother doing this stuff if the current user lacks permissions */
+ if ( ! AtD_is_allowed() )
+ return;
+
+ /* Add only in Rich Editor mode */
+ if ( get_user_option( 'rich_editing' ) == 'true' ) {
+ add_filter( 'mce_external_plugins', 'add_AtD_tinymce_plugin' );
+ add_filter( 'mce_buttons', 'register_AtD_button' );
+ }
+
+ add_action( 'personal_options_update', 'AtD_process_options_update' );
+ add_action( 'personal_options_update', 'AtD_process_unignore_update' );
+ add_action( 'profile_personal_options', 'AtD_config' );
+}
+
+/*
+ * Hook into the TinyMCE buttons and replace the current spellchecker
+ */
+function register_AtD_button( $buttons ) {
+
+ /* kill the spellchecker.. don't need no steenkin PHP spell checker */
+ foreach ( $buttons as $key => $button ) {
+ if ( $button == 'spellchecker' ) {
+ $buttons[$key] = 'AtD';
+ return $buttons;
+ }
+ }
+
+ /* hrm... ok add us last plz */
+ array_push( $buttons, '|', 'AtD' );
+ return $buttons;
+}
+
+/*
+ * Load the TinyMCE plugin : editor_plugin.js (wp2.5)
+ */
+function add_AtD_tinymce_plugin( $plugin_array ) {
+ $plugin_array['AtD'] = plugins_url( 'after-the-deadline/tinymce/editor_plugin.js?v=' . ATD_VERSION, __FILE__ );
+ return $plugin_array;
+}
+
+/*
+ * Update the TinyMCE init block with AtD specific settings
+ */
+function AtD_change_mce_settings( $init_array ) {
+ if ( ! AtD_is_allowed() )
+ return $init_array;
+
+ $user = wp_get_current_user();
+
+ $init_array['atd_rpc_url'] = admin_url( 'admin-ajax.php?action=proxy_atd&url=' );
+ $init_array['atd_ignore_rpc_url'] = admin_url( 'admin-ajax.php?action=atd_ignore&phrase=' );
+ $init_array['atd_rpc_id'] = 'WPORG-' . md5(get_bloginfo('wpurl'));
+ $init_array['atd_theme'] = 'wordpress';
+ $init_array['atd_ignore_enable'] = 'true';
+ $init_array['atd_strip_on_get'] = 'true';
+ $init_array['atd_ignore_strings'] = json_encode( explode( ',', AtD_get_setting( $user->ID, 'AtD_ignored_phrases' ) ) );
+ $init_array['atd_show_types'] = AtD_get_setting( $user->ID, 'AtD_options' );
+ $init_array['gecko_spellcheck'] = 'false';
+
+ return $init_array;
+}
+
+/*
+ * Sanitizes AtD AJAX data to acceptable chars, caller needs to make sure ' is escaped
+ */
+function AtD_sanitize( $untrusted ) {
+ return preg_replace( '/[^a-zA-Z0-9\-\',_ ]/i', "", $untrusted );
+}
+
+/*
+ * AtD HTML Editor Stuff
+ */
+function AtD_settings() {
+ $user = wp_get_current_user();
+
+ header( 'Content-Type: text/javascript' );
+
+ /* set the RPC URL for AtD */
+ echo "AtD.rpc = " . json_encode( esc_url_raw( admin_url( 'admin-ajax.php?action=proxy_atd&url=' ) ) ) . ";\n";
+
+ /* set the API key for AtD */
+ echo "AtD.api_key = " . json_encode( 'WPORG-' . md5( get_bloginfo( 'wpurl' ) ) ) . ";\n";
+
+ /* set the ignored phrases for AtD */
+ echo "AtD.setIgnoreStrings(" . json_encode( AtD_get_setting( $user->ID, 'AtD_ignored_phrases' ) ) . ");\n";
+
+ /* honor the types we want to show */
+ echo "AtD.showTypes(" . json_encode( AtD_get_setting( $user->ID, 'AtD_options' ) ) .");\n";
+
+ /* this is not an AtD/jQuery setting but I'm putting it in AtD to make it easy for the non-viz plugin to find it */
+ echo "AtD.rpc_ignore = " . json_encode( esc_url_raw( admin_url( 'admin-ajax.php?action=atd_ignore&phrase=' ) ) ) . ";\n";
+
+ die;
+}
+
+function AtD_load_javascripts() {
+ if ( AtD_should_load_on_page() ) {
+ wp_enqueue_script( 'AtD_core', plugins_url( '/after-the-deadline/atd.core.js', __FILE__ ), array(), ATD_VERSION );
+ wp_enqueue_script( 'AtD_quicktags', plugins_url( '/after-the-deadline/atd-nonvis-editor-plugin.js', __FILE__ ), array('quicktags'), ATD_VERSION );
+ wp_enqueue_script( 'AtD_jquery', plugins_url( '/after-the-deadline/jquery.atd.js', __FILE__ ), array('jquery'), ATD_VERSION );
+ wp_enqueue_script( 'AtD_settings', admin_url() . 'admin-ajax.php?action=atd_settings', array('AtD_jquery'), ATD_VERSION );
+ wp_enqueue_script( 'AtD_autoproofread', plugins_url( '/after-the-deadline/atd-autoproofread.js', __FILE__ ), array('AtD_jquery'), ATD_VERSION );
+ }
+}
+
+/* Spits out user options for auto-proofreading on publish/update */
+function AtD_load_submit_check_javascripts() {
+ global $pagenow;
+
+ $user = wp_get_current_user();
+ if ( ! $user || $user->ID == 0 )
+ return;
+
+ if ( AtD_should_load_on_page() ) {
+ $atd_check_when = AtD_get_setting( $user->ID, 'AtD_check_when' );
+
+ if ( !empty( $atd_check_when ) ) {
+ $check_when = array();
+ /* Set up the options in json */
+ foreach( explode( ',', $atd_check_when ) as $option ) {
+ $check_when[$option] = true;
+ }
+ echo '<script type="text/javascript">' . "\n";
+ echo 'AtD_check_when = ' . json_encode( (object) $check_when ) . ";\n";
+ echo '</script>' . "\n";
+ }
+ }
+}
+
+/*
+ * Check if a user is allowed to use AtD
+ */
+function AtD_is_allowed() {
+ $user = wp_get_current_user();
+ if ( ! $user || $user->ID == 0 )
+ return;
+
+ if ( ! current_user_can( 'edit_posts' ) && ! current_user_can( 'edit_pages' ) )
+ return;
+
+ return 1;
+}
+
+function AtD_load_css() {
+ if ( AtD_should_load_on_page() )
+ wp_enqueue_style( 'AtD_style', plugins_url( '/after-the-deadline/atd.css', __FILE__ ), null, ATD_VERSION, 'screen' );
+}
+
+/* Helper used to check if javascript should be added to page. Helps avoid bloat in admin */
+function AtD_should_load_on_page() {
+ global $pagenow, $current_screen;
+
+ $pages = array( 'post.php', 'post-new.php', 'page.php', 'page-new.php', 'admin.php', 'profile.php' );
+
+ if ( in_array( $pagenow, $pages ) ) {
+ if ( isset( $current_screen->post_type ) && $current_screen->post_type ) {
+ return post_type_supports( $current_screen->post_type, 'editor' );
+ }
+ return true;
+ }
+
+ return apply_filters( 'atd_load_scripts', false );
+}
+
+// add button to DFW
+add_filter( 'wp_fullscreen_buttons', 'AtD_fullscreen' );
+function AtD_fullscreen($buttons) {
+ $buttons['spellchecker'] = array( 'title' => __( 'Proofread Writing', 'jetpack' ), 'onclick' => "tinyMCE.execCommand('mceWritingImprovementTool');", 'both' => false );
+ return $buttons;
+}
+
+/* add some vars into the AtD plugin */
+add_filter( 'tiny_mce_before_init', 'AtD_change_mce_settings' );
+
+/* load some stuff for non-visual editor */
+add_action( 'admin_print_scripts', 'AtD_load_javascripts' );
+add_action( 'admin_print_scripts', 'AtD_load_submit_check_javascripts' );
+add_action( 'admin_print_styles', 'AtD_load_css' );
+
+/* init process for button control */
+add_action( 'init', 'AtD_addbuttons' );
+
+/* setup hooks for our PHP functions we want to make available via an AJAX call */
+add_action( 'wp_ajax_proxy_atd', 'AtD_redirect_call' );
+add_action( 'wp_ajax_atd_ignore', 'AtD_ignore_call' );
+add_action( 'wp_ajax_atd_settings', 'AtD_settings' );
+
+/* load and install the localization stuff */
+include( 'after-the-deadline/atd-l10n.php' );
diff --git a/plugins/jetpack/modules/after-the-deadline/atd-autoproofread.js b/plugins/jetpack/modules/after-the-deadline/atd-autoproofread.js
new file mode 100644
index 00000000..4a135bdf
--- /dev/null
+++ b/plugins/jetpack/modules/after-the-deadline/atd-autoproofread.js
@@ -0,0 +1,89 @@
+/* the AtD/jQuery and AtD/TinyMCE plugins check if this variable exists and increment it when a proofread check happens */
+var AtD_proofread_click_count = 0;
+
+/* This is function called when the publish/update button is pressed */
+function AtD_submit_check( e ) {
+ /* User has already checked their document... no need to hold up their submit */
+ if (AtD_proofread_click_count > 0)
+ return;
+
+ /* Let's not submit the form, shall we? */
+ e.preventDefault();
+
+ /* We'll call the AtD function based on which editor is currently active */
+ if (typeof(tinyMCE) != 'undefined' && tinyMCE.activeEditor != null && tinyMCE.activeEditor.isHidden() == false) {
+ /* Woo! We're running tinyMCE! */
+ tinyMCE.activeEditor.execCommand('mceWritingImprovementTool', AtD_submit_check_callback);
+ } else {
+ /* Go Go HTML editor! */
+ AtD_restore_if_proofreading();
+ AtD_check( AtD_submit_check_callback );
+ }
+}
+
+/* This is the callback function that runs after the publish/update button is pressed */
+function AtD_submit_check_callback(count) {
+ AtD_unbind_proofreader_listeners();
+
+ if ( 0 == count || 1 < AtD_proofread_click_count ) {
+ /* if no errors were found, submit form */
+ AtD_update_post();
+ } else if ( -1 == count ) {
+ /* If there was an error, alert the user and submit form */
+ alert( AtD.getLang('message_server_error', 'There was a problem communicating with the Proofreading service. Try again in one minute.') );
+ AtD_update_post();
+ } else {
+ var original_post_status = jQuery('#original_post_status').val()
+
+ /* Okay, the user has tried to publish/update already but there are still errors. Ask them what to do */
+ var message;
+ if ( original_post_status == 'publish' )
+ message = AtD.getLang('dialog_confirm_post_publish', 'The proofreader has suggestions for this post. Are you sure you want to publish it?\n\nPress OK to publish your post, or Cancel to view the suggestions and edit your post.');
+ else
+ message = AtD.getLang('dialog_confirm_post_update', 'The proofreader has suggestions for this post. Are you sure you want to update it?\n\nPress OK to update your post, or Cancel to view the suggestions and edit your post.');
+
+ if ( confirm( message ) ) {
+ AtD_update_post();
+ } else {
+ AtD_bind_proofreader_listeners();
+ AtD_kill_autoproofread();
+ }
+
+ /* Let's do some interface clean-up */
+ jQuery('#publish').removeClass('button-primary-disabled');
+ jQuery('#ajax-loading').hide();
+ }
+}
+
+/* Stop the proofreader from doing its auto proofread thing (activated when the proofread button is clicked) */
+function AtD_kill_autoproofread() {
+ jQuery('#publish').unbind('click', AtD_submit_check);
+}
+
+/* a function to force the post to be submitted */
+function AtD_update_post() {
+
+ if ( typeof(tinyMCE) == 'undefined' || tinyMCE.activeEditor == null || tinyMCE.activeEditor.isHidden() )
+ AtD_restore_if_proofreading();
+
+ jQuery('#publish')
+ .unbind('click', AtD_submit_check)
+ .click() // we're using click() because doing a form submit causes posts to not publish. I couldn't trace back the the cause :/
+ ;
+}
+
+/* init the autoproofread options */
+function atd_auto_proofread_init() {
+ var original_post_status = jQuery('#original_post_status').val();
+
+ /* check if auto-check is enabled && if #content exists */
+ if ( typeof AtD_check_when != 'undefined' && ((original_post_status != 'publish' && AtD_check_when.onpublish) || ((original_post_status == 'publish' || original_post_status == 'schedule') && AtD_check_when.onupdate)) && jQuery('#content').length != 0 )
+ jQuery('#publish').click( AtD_submit_check );
+}
+
+/* document.ready() does not execute in IE6 unless it's at the bottom of the page. oi! */
+if (navigator.appName == 'Microsoft Internet Explorer')
+ setTimeout( atd_auto_proofread_init, 2500 );
+else
+ jQuery( document ).ready( atd_auto_proofread_init );
+
diff --git a/plugins/jetpack/modules/after-the-deadline/atd-l10n.php b/plugins/jetpack/modules/after-the-deadline/atd-l10n.php
new file mode 100644
index 00000000..628baa4d
--- /dev/null
+++ b/plugins/jetpack/modules/after-the-deadline/atd-l10n.php
@@ -0,0 +1,40 @@
+<?php
+/*
+ * loads AtD localization strings (shared between Visual and HTML Editors)
+ */
+function AtD_init_l10n_js() {
+ if ( !AtD_should_load_on_page() ) {
+ return;
+ }
+
+ /* load localized strings for AtD */
+ wp_localize_script( 'AtD_settings', 'AtD_l10n_r0ar', array (
+ 'menu_title_spelling' => __( 'Spelling', 'jetpack' ),
+ 'menu_title_repeated_word' => __( 'Repeated Word', 'jetpack' ),
+
+ 'menu_title_no_suggestions' => __( 'No suggestions', 'jetpack' ),
+
+ 'menu_option_explain' => __( 'Explain...', 'jetpack' ),
+ 'menu_option_ignore_once' => __( 'Ignore suggestion', 'jetpack' ),
+ 'menu_option_ignore_always' => __( 'Ignore always', 'jetpack' ),
+ 'menu_option_ignore_all' => __( 'Ignore all', 'jetpack' ),
+
+ 'menu_option_edit_selection' => __( 'Edit Selection...', 'jetpack' ),
+
+ 'button_proofread' => __( 'proofread', 'jetpack' ),
+ 'button_edit_text' => __( 'edit text', 'jetpack' ),
+ 'button_proofread_tooltip' => __( 'Proofread Writing', 'jetpack' ),
+
+ 'message_no_errors_found' => __( 'No writing errors were found.', 'jetpack' ),
+ 'message_server_error' => __( 'There was a problem communicating with the Proofreading service. Try again in one minute.', 'jetpack' ),
+ 'message_server_error_short' => __( 'There was an error communicating with the proofreading service.', 'jetpack' ),
+
+ 'dialog_replace_selection' => __( 'Replace selection with:', 'jetpack' ),
+ 'dialog_confirm_post_publish' => __( "The proofreader has suggestions for this post. Are you sure you want to publish it?\n\nPress OK to publish your post, or Cancel to view the suggestions and edit your post.", 'jetpack' ),
+ 'dialog_confirm_post_update' => __( "The proofreader has suggestions for this post. Are you sure you want to update it?\n\nPress OK to update your post, or Cancel to view the suggestions and edit your post.", 'jetpack' ),
+ ) );
+
+ wp_enqueue_script( 'AtD_l10n', plugins_url( 'install_atd_l10n.js', __FILE__ ), array( 'AtD_settings', 'jquery' ), ATD_VERSION );
+}
+
+add_action( 'admin_print_scripts', 'AtD_init_l10n_js' );
diff --git a/plugins/jetpack/modules/after-the-deadline/atd-nonvis-editor-plugin.js b/plugins/jetpack/modules/after-the-deadline/atd-nonvis-editor-plugin.js
new file mode 100644
index 00000000..dde4eab2
--- /dev/null
+++ b/plugins/jetpack/modules/after-the-deadline/atd-nonvis-editor-plugin.js
@@ -0,0 +1,189 @@
+var AtD_qtbutton;
+/* convienence method to restore the text area from the preview div */
+function AtD_restore_text_area()
+{
+ /* clear the error HTML out of the preview div */
+ AtD.remove('content');
+
+ /* swap the preview div for the textarea, notice how I have to restore the appropriate class/id/style attributes */
+
+ var content;
+
+ if (navigator.appName == 'Microsoft Internet Explorer')
+ content = jQuery('#content').html().replace(/<BR.*?class.*?atd_remove_me.*?>/gi, "\n");
+ else
+ content = jQuery('#content').html();
+
+ jQuery('#content').replaceWith( AtD.content_canvas );
+ jQuery('#content').val( content.replace(/\&lt\;/g, '<').replace(/\&gt\;/g, '>').replace(/\&amp;/g, '&') );
+ jQuery('#content').height(AtD.height);
+
+ /* change the link text back to its original label */
+ jQuery(AtD_qtbutton).val( AtD.getLang('button_proofread', 'proofread') );
+ jQuery(AtD_qtbutton).css({ 'color' : '#464646' });
+
+ /* enable the toolbar buttons */
+ jQuery( AtD_qtbutton ).siblings('input').andSelf().attr( 'disabled', false );
+
+ /* restore autosave */
+ if (AtD.autosave != undefined)
+ autosave = AtD.autosave;
+};
+
+// add the AtD button properly to quicktags
+if ( typeof(QTags) != 'undefined' && QTags.addButton ) {
+ jQuery(document).ready(function($){
+ QTags.addButton( 'AtD', AtD_l10n_r0ar.button_proofread, AtD_check );
+ });
+} else {
+ edButtons[edButtons.length] = new edButton('ed_AtD', 'AtD', '', '', '');
+ jQuery(document).ready(function($){
+ $('#ed_AtD').replaceWith('<input type="button" id="ed_AtD" accesskey="" class="ed_button" onclick="AtD_check(this);" value="' + AtD_l10n_r0ar.button_proofread + '" />');
+ });
+}
+
+function AtD_restore_if_proofreading() {
+ if (jQuery(AtD_qtbutton).val() == AtD.getLang('button_edit_text', 'edit text'))
+ AtD_restore_text_area();
+}
+
+function AtD_unbind_proofreader_listeners() {
+ jQuery('#save-post, #post-preview, #publish, #edButtonPreview').unbind('focus', AtD_restore_if_proofreading );
+ jQuery('#add_poll, #add_image, #add_video, #add_audio, #add_media').unbind('click', AtD_restore_if_proofreading );
+ jQuery('#post').unbind('submit', AtD_restore_if_proofreading );
+}
+
+function AtD_bind_proofreader_listeners() {
+ jQuery('#save-post, #post-preview, #publish, #edButtonPreview').focus( AtD_restore_if_proofreading );
+ jQuery('#add_poll, #add_image, #add_video, #add_audio, #add_media').click( AtD_restore_if_proofreading );
+ jQuery('#post').submit( AtD_restore_if_proofreading );
+}
+
+/* where the magic happens, checks the spelling or restores the form */
+function AtD_check(button) {
+ var callback;
+ if ( jQuery.isFunction( button ) ) {
+ callback = button;
+
+ if ( !AtD_qtbutton ) {
+ AtD_qtbutton = jQuery( '#qt_content_AtD, #ed_AtD' ).get( 0 );
+ }
+ } else {
+ if ( !button.id )
+ button = button[0];
+
+ AtD_qtbutton = button;
+ }
+
+ if ( !jQuery('#content').size() ) {
+ if ( 'undefined' !== typeof callback ) {
+ callback( 0 );
+ }
+ AtD_restore_if_proofreading();
+ return;
+ }
+
+ /* If the text of the link says edit comment, then restore the textarea so the user can edit the text */
+
+ if ( jQuery(AtD_qtbutton).val() == AtD.getLang('button_edit_text', 'edit text') ) {
+ AtD_restore_text_area();
+ } else {
+ /* initialize some of the stuff related to this plugin */
+ if ( !AtD.height ) {
+
+ AtD.height = jQuery('#content').height();
+ AtD_bind_proofreader_listeners();
+
+ /* make it so clicking the Visual button works when AtD is active */
+
+ jQuery('#edButtonPreview').attr( 'onclick', null ).click( function() {
+ AtD_restore_if_proofreading();
+ switchEditors.go( 'content', 'tinymce' );
+ });
+
+ /* saved the textarea as we need to restore the original one for the toolbar to continue to function properly */
+ AtD.content_canvas = jQuery('#content');
+
+ /* store the autosave, we're going to make it empty during spellcheck to prevent auto saved text from being
+ over written with empty text */
+ AtD.autosave = autosave;
+ }
+
+ /* set the spell check link to a link that lets the user edit the text */
+ /* disable the button to prevent a race condition where content is deleted if proofread is clicked with a check
+ in progress. */
+ jQuery(AtD_qtbutton).css({ 'color' : 'red' }).val( AtD.getLang('button_edit_text', 'edit text') ).attr('disabled', true);
+
+ /* replace the div */
+ var text = jQuery('#content').val().replace(/\&/g, '&amp;').replace(/\</g, '&lt;').replace(/\>/g, '&gt;');
+
+ if (navigator.appName == 'Microsoft Internet Explorer') {
+ text = text.replace(/[\n\r\f]/gm, '<BR class="atd_remove_me">');
+ var node = jQuery('<div class="input" id="content" style="height: 170px">' + text + '</div>');
+ jQuery('#content').replaceWith(node);
+ node.css( { 'overflow' : 'auto', 'background-color' : 'white', 'color' : 'black' } );
+ } else {
+ jQuery('#content').replaceWith('<div class="input" id="content">' + text + '</div>');
+ jQuery('#content').css( { 'overflow' : 'auto', 'background-color' : 'white', 'color' : 'black', 'white-space' : 'pre-wrap' } );
+ jQuery('#content').height(AtD.height);
+ }
+
+ /* kill autosave... :) */
+ autosave = function() { };
+
+ /* disable the toolbar buttons */
+ jQuery( AtD_qtbutton ).siblings('input').andSelf().attr( 'disabled', true ); // using .arrt instead of .prop so it's compat with older WP and jQuery
+
+ /* check the writing in the textarea */
+ AtD.check('content', {
+ success: function(errorCount) {
+ if ( errorCount == 0 && typeof callback !== 'function' )
+ alert( AtD.getLang('message_no_errors_found', 'No writing errors were found') );
+ AtD_restore_if_proofreading();
+ },
+
+ ready: function(errorCount) {
+ jQuery(AtD_qtbutton).attr('disabled', false);
+
+ if ( typeof callback === 'function' )
+ callback( errorCount );
+ },
+
+ error: function(reason) {
+ jQuery(AtD_qtbutton).attr('disabled', false);
+
+ if ( typeof callback === 'function' )
+ callback( -1 );
+ else
+ alert( AtD.getLang('message_server_error', 'There was a problem communicating with the Proofreading service. Try again in one minute.') );
+
+ AtD_restore_if_proofreading();
+ },
+
+ editSelection: function(element) {
+ var text = prompt( AtD.getLang('dialog_replace_selection', 'Replace selection with:'), element.text() );
+
+ if ( text != null )
+ element.replaceWith( text );
+ },
+
+ explain: function(url) {
+ var left = (screen.width / 2) - (480 / 2);
+ var top = (screen.height / 2) - (380 / 2);
+ window.open( url, '', 'width=480,height=380,toolbar=0,status=0,resizable=0,location=0,menuBar=0,left=' + left + ',top=' + top).focus();
+ },
+
+ ignore: function(word) {
+ jQuery.ajax({
+ type : 'GET',
+ url : AtD.rpc_ignore + encodeURI( word ).replace( /&/g, '%26'),
+ format : 'raw',
+ error : function(XHR, status, error) {
+ if ( AtD.callback_f != undefined && AtD.callback_f.error != undefined )
+ AtD.callback_f.error(status + ": " + error);
+ }
+ });
+ }
+ });
+ }
+}
diff --git a/plugins/jetpack/modules/after-the-deadline/atd.core.js b/plugins/jetpack/modules/after-the-deadline/atd.core.js
new file mode 100644
index 00000000..0e3d1c4c
--- /dev/null
+++ b/plugins/jetpack/modules/after-the-deadline/atd.core.js
@@ -0,0 +1,535 @@
+/*
+ * atd.core.js - A building block to create a front-end for AtD
+ * Author : Raphael Mudge, Automattic
+ * License : LGPL
+ * Project : http://www.afterthedeadline.com/developers.slp
+ * Contact : raffi@automattic.com
+ */
+
+/* EXPORTED_SYMBOLS is set so this file can be a JavaScript Module */
+var EXPORTED_SYMBOLS = ['AtDCore'];
+
+function AtDCore() {
+ /* these are the categories of errors AtD should ignore */
+ this.ignore_types = ['Bias Language', 'Cliches', 'Complex Expression', 'Diacritical Marks', 'Double Negatives', 'Hidden Verbs', 'Jargon Language', 'Passive voice', 'Phrases to Avoid', 'Redundant Expression'];
+
+ /* these are the phrases AtD should ignore */
+ this.ignore_strings = {};
+
+ /* Localized strings */
+ this.i18n = {};
+};
+
+/*
+ * Internationalization Functions
+ */
+
+AtDCore.prototype.getLang = function(key, defaultk) {
+ if (this.i18n[key] == undefined)
+ return defaultk;
+
+ return this.i18n[key];
+};
+
+AtDCore.prototype.addI18n = function(localizations) {
+ this.i18n = localizations;
+};
+
+/*
+ * Setters
+ */
+
+AtDCore.prototype.setIgnoreStrings = function(string) {
+ var parent = this;
+
+ this.map(string.split(/,\s*/g), function(string) {
+ parent.ignore_strings[string] = 1;
+ });
+};
+
+AtDCore.prototype.showTypes = function(string) {
+ var show_types = string.split(/,\s*/g);
+ var types = {};
+
+ /* set some default types that we want to make optional */
+
+ /* grammar checker options */
+ types["Double Negatives"] = 1;
+ types["Hidden Verbs"] = 1;
+ types["Passive voice"] = 1;
+ types["Bias Language"] = 1;
+
+ /* style checker options */
+ types["Cliches"] = 1;
+ types["Complex Expression"] = 1;
+ types["Diacritical Marks"] = 1;
+ types["Jargon Language"] = 1;
+ types["Phrases to Avoid"] = 1;
+ types["Redundant Expression"] = 1;
+
+ var ignore_types = [];
+
+ this.map(show_types, function(string) {
+ types[string] = undefined;
+ });
+
+ this.map(this.ignore_types, function(string) {
+ if (types[string] != undefined)
+ ignore_types.push(string);
+ });
+
+ this.ignore_types = ignore_types;
+};
+
+/*
+ * Error Parsing Code
+ */
+
+AtDCore.prototype.makeError = function(error_s, tokens, type, seps, pre) {
+ var struct = new Object();
+ struct.type = type;
+ struct.string = error_s;
+ struct.tokens = tokens;
+
+ if (new RegExp("\\b" + error_s + "\\b").test(error_s)) {
+ struct.regexp = new RegExp("(?!"+error_s+"<)\\b" + error_s.replace(/\s+/g, seps) + "\\b");
+ }
+ else if (new RegExp(error_s + "\\b").test(error_s)) {
+ struct.regexp = new RegExp("(?!"+error_s+"<)" + error_s.replace(/\s+/g, seps) + "\\b");
+ }
+ else if (new RegExp("\\b" + error_s).test(error_s)) {
+ struct.regexp = new RegExp("(?!"+error_s+"<)\\b" + error_s.replace(/\s+/g, seps));
+ }
+ else {
+ struct.regexp = new RegExp("(?!"+error_s+"<)" + error_s.replace(/\s+/g, seps));
+ }
+
+ struct.used = false; /* flag whether we've used this rule or not */
+
+ return struct;
+};
+
+AtDCore.prototype.addToErrorStructure = function(errors, list, type, seps) {
+ var parent = this;
+
+ this.map(list, function(error) {
+ var tokens = error["word"].split(/\s+/);
+ var pre = error["pre"];
+ var first = tokens[0];
+
+ if (errors['__' + first] == undefined) {
+ errors['__' + first] = new Object();
+ errors['__' + first].pretoks = {};
+ errors['__' + first].defaults = new Array();
+ }
+
+ if (pre == "") {
+ errors['__' + first].defaults.push(parent.makeError(error["word"], tokens, type, seps, pre));
+ } else {
+ if (errors['__' + first].pretoks['__' + pre] == undefined)
+ errors['__' + first].pretoks['__' + pre] = new Array();
+
+ errors['__' + first].pretoks['__' + pre].push(parent.makeError(error["word"], tokens, type, seps, pre));
+ }
+ });
+};
+
+AtDCore.prototype.buildErrorStructure = function(spellingList, enrichmentList, grammarList) {
+ var seps = this._getSeparators();
+ var errors = {};
+
+ this.addToErrorStructure(errors, spellingList, "hiddenSpellError", seps);
+ this.addToErrorStructure(errors, grammarList, "hiddenGrammarError", seps);
+ this.addToErrorStructure(errors, enrichmentList, "hiddenSuggestion", seps);
+ return errors;
+};
+
+AtDCore.prototype._getSeparators = function() {
+ var re = '', i;
+ var str = '"s!#$%&()*+,./:;<=>?@[\]^_{|}';
+
+ // Build word separator regexp
+ for (i=0; i<str.length; i++)
+ re += '\\' + str.charAt(i);
+
+ return "(?:(?:[\xa0" + re + "])|(?:\\-\\-))+";
+};
+
+AtDCore.prototype.processXML = function(responseXML) {
+
+ /* types of errors to ignore */
+ var types = {};
+
+ this.map(this.ignore_types, function(type) {
+ types[type] = 1;
+ });
+
+ /* save suggestions in the editor object */
+ this.suggestions = [];
+
+ /* process through the errors */
+ var errors = responseXML.getElementsByTagName('error');
+
+ /* words to mark */
+ var grammarErrors = [];
+ var spellingErrors = [];
+ var enrichment = [];
+
+ for (var i = 0; i < errors.length; i++) {
+ if (errors[i].getElementsByTagName('string').item(0).firstChild != null) {
+ var errorString = errors[i].getElementsByTagName('string').item(0).firstChild.data;
+ var errorType = errors[i].getElementsByTagName('type').item(0).firstChild.data;
+ var errorDescription = errors[i].getElementsByTagName('description').item(0).firstChild.data;
+
+ var errorContext;
+
+ if (errors[i].getElementsByTagName('precontext').item(0).firstChild != null)
+ errorContext = errors[i].getElementsByTagName('precontext').item(0).firstChild.data;
+ else
+ errorContext = "";
+
+ /* create a hashtable with information about the error in the editor object, we will use this later
+ to populate a popup menu with information and suggestions about the error */
+
+ if (this.ignore_strings[errorString] == undefined) {
+ var suggestion = {};
+ suggestion["description"] = errorDescription;
+ suggestion["suggestions"] = [];
+
+ /* used to find suggestions when a highlighted error is clicked on */
+ suggestion["matcher"] = new RegExp('^' + errorString.replace(/\s+/, this._getSeparators()) + '$');
+
+ suggestion["context"] = errorContext;
+ suggestion["string"] = errorString;
+ suggestion["type"] = errorType;
+
+ this.suggestions.push(suggestion);
+
+ if (errors[i].getElementsByTagName('suggestions').item(0) != undefined) {
+ var suggestions = errors[i].getElementsByTagName('suggestions').item(0).getElementsByTagName('option');
+ for (var j = 0; j < suggestions.length; j++)
+ suggestion["suggestions"].push(suggestions[j].firstChild.data);
+ }
+
+ /* setup the more info url */
+ if (errors[i].getElementsByTagName('url').item(0) != undefined) {
+ var errorUrl = errors[i].getElementsByTagName('url').item(0).firstChild.data;
+ suggestion["moreinfo"] = errorUrl + '&theme=tinymce';
+ }
+
+ if (types[errorDescription] == undefined) {
+ if (errorType == "suggestion")
+ enrichment.push({ word: errorString, pre: errorContext });
+
+ if (errorType == "grammar")
+ grammarErrors.push({ word: errorString, pre: errorContext });
+ }
+
+ if (errorType == "spelling" || errorDescription == "Homophone")
+ spellingErrors.push({ word: errorString, pre: errorContext });
+
+ if (errorDescription == 'Cliches')
+ suggestion["description"] = 'Clich&eacute;s'; /* done here for backwards compatability with current user settings */
+
+ if (errorDescription == "Spelling")
+ suggestion["description"] = this.getLang('menu_title_spelling', 'Spelling');
+
+ if (errorDescription == "Repeated Word")
+ suggestion["description"] = this.getLang('menu_title_repeated_word', 'Repeated Word');
+
+ if (errorDescription == "Did you mean...")
+ suggestion["description"] = this.getLang('menu_title_confused_word', 'Did you mean...');
+ } // end if ignore[errorString] == undefined
+ } // end if
+ } // end for loop
+
+ var errorStruct;
+ var ecount = spellingErrors.length + grammarErrors.length + enrichment.length;
+
+ if (ecount > 0)
+ errorStruct = this.buildErrorStructure(spellingErrors, enrichment, grammarErrors);
+ else
+ errorStruct = undefined;
+
+ /* save some state in this object, for retrieving suggestions later */
+ return { errors: errorStruct, count: ecount, suggestions: this.suggestions };
+};
+
+AtDCore.prototype.findSuggestion = function(element) {
+ var text = element.innerHTML;
+ var context = ( this.getAttrib(element, 'pre') + "" ).replace(/[\\,!\\?\\."\s]/g, '');
+ if (this.getAttrib(element, 'pre') == undefined)
+ {
+ alert(element.innerHTML);
+ }
+
+ var errorDescription = undefined;
+ var len = this.suggestions.length;
+
+ for (var i = 0; i < len; i++) {
+ var key = this.suggestions[i]["string"];
+
+ if ((context == "" || context == this.suggestions[i]["context"]) && this.suggestions[i]["matcher"].test(text)) {
+ errorDescription = this.suggestions[i];
+ break;
+ }
+ }
+ return errorDescription;
+};
+
+/*
+ * TokenIterator class
+ */
+
+function TokenIterator(tokens) {
+ this.tokens = tokens;
+ this.index = 0;
+ this.count = 0;
+ this.last = 0;
+};
+
+TokenIterator.prototype.next = function() {
+ var current = this.tokens[this.index];
+ this.count = this.last;
+ this.last += current.length + 1;
+ this.index++;
+
+ /* strip single quotes from token, AtD does this when presenting errors */
+ if (current != "") {
+ if (current[0] == "'")
+ current = current.substring(1, current.length);
+
+ if (current[current.length - 1] == "'")
+ current = current.substring(0, current.length - 1);
+ }
+
+ return current;
+};
+
+TokenIterator.prototype.hasNext = function() {
+ return this.index < this.tokens.length;
+};
+
+TokenIterator.prototype.hasNextN = function(n) {
+ return (this.index + n) < this.tokens.length;
+};
+
+TokenIterator.prototype.skip = function(m, n) {
+ this.index += m;
+ this.last += n;
+
+ if (this.index < this.tokens.length)
+ this.count = this.last - this.tokens[this.index].length;
+};
+
+TokenIterator.prototype.getCount = function() {
+ return this.count;
+};
+
+TokenIterator.prototype.peek = function(n) {
+ var peepers = new Array();
+ var end = this.index + n;
+ for (var x = this.index; x < end; x++)
+ peepers.push(this.tokens[x]);
+ return peepers;
+};
+
+/*
+ * code to manage highlighting of errors
+ */
+AtDCore.prototype.markMyWords = function(container_nodes, errors) {
+ var seps = new RegExp(this._getSeparators());
+ var nl = new Array();
+ var ecount = 0; /* track number of highlighted errors */
+ var parent = this;
+
+ /* Collect all text nodes */
+ /* Our goal--ignore nodes that are already wrapped */
+
+ this._walk(container_nodes, function(n) {
+ if (n.nodeType == 3 && !parent.isMarkedNode(n))
+ nl.push(n);
+ });
+
+ /* walk through the relevant nodes */
+
+ var iterator;
+
+ this.map(nl, function(n) {
+ var v;
+
+ if (n.nodeType == 3) {
+ v = n.nodeValue; /* we don't want to mangle the HTML so use the actual encoded string */
+ var tokens = n.nodeValue.split(seps); /* split on the unencoded string so we get access to quotes as " */
+ var previous = "";
+
+ var doReplaces = [];
+
+ iterator = new TokenIterator(tokens);
+
+ while (iterator.hasNext()) {
+ var token = iterator.next();
+ var current = errors['__' + token];
+
+ var defaults;
+
+ if (current != undefined && current.pretoks != undefined) {
+ defaults = current.defaults;
+ current = current.pretoks['__' + previous];
+
+ var done = false;
+ var prev, curr;
+
+ prev = v.substr(0, iterator.getCount());
+ curr = v.substr(prev.length, v.length);
+
+ var checkErrors = function(error) {
+ if (error != undefined && !error.used && foundStrings['__' + error.string] == undefined && error.regexp.test(curr)) {
+ var oldlen = curr.length;
+
+ foundStrings['__' + error.string] = 1;
+ doReplaces.push([error.regexp, '<span class="'+error.type+'" pre="'+previous+'">$&</span>']);
+
+ error.used = true;
+ done = true;
+ }
+ };
+
+ var foundStrings = {};
+
+ if (current != undefined) {
+ previous = previous + ' ';
+ parent.map(current, checkErrors);
+ }
+
+ if (!done) {
+ previous = '';
+ parent.map(defaults, checkErrors);
+ }
+ }
+
+ previous = token;
+ } // end while
+
+ /* do the actual replacements on this span */
+ if (doReplaces.length > 0) {
+ newNode = n;
+
+ for (var x = 0; x < doReplaces.length; x++) {
+ var regexp = doReplaces[x][0], result = doReplaces[x][1];
+
+ /* it's assumed that this function is only being called on text nodes (nodeType == 3), the iterating is necessary
+ because eventually the whole thing gets wrapped in an mceItemHidden span and from there it's necessary to
+ handle each node individually. */
+ var bringTheHurt = function(node) {
+ if (node.nodeType == 3) {
+ ecount++;
+
+ /* sometimes IE likes to ignore the space between two spans, solution is to insert a placeholder span with
+ a non-breaking space. The markup removal code substitutes this span for a space later */
+ if (parent.isIE() && node.nodeValue.length > 0 && node.nodeValue.substr(0, 1) == ' ')
+ return parent.create('<span class="mceItemHidden">&nbsp;</span>' + node.nodeValue.substr(1, node.nodeValue.length - 1).replace(regexp, result), false);
+ else
+ return parent.create(node.nodeValue.replace(regexp, result), false);
+ }
+ else {
+ var contents = parent.contents(node);
+
+ for (var y = 0; y < contents.length; y++) {
+ if (contents[y].nodeType == 3 && regexp.test(contents[y].nodeValue)) {
+ var nnode;
+
+ if (parent.isIE() && contents[y].nodeValue.length > 0 && contents[y].nodeValue.substr(0, 1) == ' ')
+ nnode = parent.create('<span class="mceItemHidden">&nbsp;</span>' + contents[y].nodeValue.substr(1, contents[y].nodeValue.length - 1).replace(regexp, result), true);
+ else
+ nnode = parent.create(contents[y].nodeValue.replace(regexp, result), true);
+
+ parent.replaceWith(contents[y], nnode);
+ parent.removeParent(nnode);
+
+ ecount++;
+
+ return node; /* we did a replacement so we can call it quits, errors only get used once */
+ }
+ }
+
+ return node;
+ }
+ };
+
+ newNode = bringTheHurt(newNode);
+ }
+
+ parent.replaceWith(n, newNode);
+ }
+ }
+ });
+
+ return ecount;
+};
+
+AtDCore.prototype._walk = function(elements, f) {
+ var i;
+ for (i = 0; i < elements.length; i++) {
+ f.call(f, elements[i]);
+ this._walk(this.contents(elements[i]), f);
+ }
+};
+
+AtDCore.prototype.removeWords = function(node, w) {
+ var count = 0;
+ var parent = this;
+
+ this.map(this.findSpans(node).reverse(), function(n) {
+ if (n && (parent.isMarkedNode(n) || parent.hasClass(n, 'mceItemHidden') || parent.isEmptySpan(n)) ) {
+ if (n.innerHTML == '&nbsp;') {
+ var nnode = document.createTextNode(' '); /* hax0r */
+ parent.replaceWith(n, nnode);
+ }
+ else if (!w || n.innerHTML == w) {
+ parent.removeParent(n);
+ count++;
+ }
+ }
+ });
+
+ return count;
+};
+
+AtDCore.prototype.isEmptySpan = function(node) {
+ return (this.getAttrib(node, 'class') == "" && this.getAttrib(node, 'style') == "" && this.getAttrib(node, 'id') == "" && !this.hasClass(node, 'Apple-style-span') && this.getAttrib(node, 'mce_name') == "");
+};
+
+AtDCore.prototype.isMarkedNode = function(node) {
+ return (this.hasClass(node, 'hiddenGrammarError') || this.hasClass(node, 'hiddenSpellError') || this.hasClass(node, 'hiddenSuggestion'));
+};
+
+/*
+ * Context Menu Helpers
+ */
+AtDCore.prototype.applySuggestion = function(element, suggestion) {
+ if (suggestion == '(omit)') {
+ this.remove(element);
+ }
+ else {
+ var node = this.create(suggestion);
+ this.replaceWith(element, node);
+ this.removeParent(node);
+ }
+};
+
+/*
+ * Check for an error
+ */
+AtDCore.prototype.hasErrorMessage = function(xmlr) {
+ return (xmlr != undefined && xmlr.getElementsByTagName('message').item(0) != null);
+};
+
+AtDCore.prototype.getErrorMessage = function(xmlr) {
+ return xmlr.getElementsByTagName('message').item(0);
+};
+
+/* this should always be an error, alas... not practical */
+AtDCore.prototype.isIE = function() {
+ return navigator.appName == 'Microsoft Internet Explorer';
+};
diff --git a/plugins/jetpack/modules/after-the-deadline/atd.css b/plugins/jetpack/modules/after-the-deadline/atd.css
new file mode 100644
index 00000000..5014feac
--- /dev/null
+++ b/plugins/jetpack/modules/after-the-deadline/atd.css
@@ -0,0 +1,105 @@
+/* AtD error styles */
+
+.hiddenSpellError
+{
+ border-bottom: 2px solid red;
+ cursor: default;
+}
+
+.hiddenGrammarError
+{
+ border-bottom: 2px solid green;
+ cursor: default;
+}
+
+.hiddenSuggestion
+{
+ border-bottom: 2px solid blue;
+ cursor: default;
+}
+
+/* Menu styles derived from:
+ * jquery.spellchecker.js - a simple jQuery Spell Checker
+ * Copyright (c) 2008 Richard Willis
+ * MIT license : http://www.opensource.org/licenses/mit-license.php
+ * Project : http://jquery-spellchecker.googlecode.com
+ */
+
+#suggestmenu
+{
+ min-width: 122px;
+ background: #ebeaeb;
+ position: absolute;
+ display: none;
+ z-index: 9999;
+ overflow: none;
+ margin-top: 1px;
+ text-align: left;
+ font-size: 11px;
+ font-family: Tahoma, Verdana, Arial, Helvetica;
+}
+
+#suggestmenu strong
+{
+ background: #cccccc;
+ font-weight: bold;
+ padding:3px 6px 3px 6px;
+ display:block;
+ border:1px solid #dddddd;
+ border-bottom: 1px solid #aaaaaa;
+ color: black;
+}
+
+#suggestmenu em
+{
+ text-align:center;
+ padding:3px 6px 3px 6px;
+ display:block;
+ border-top:1px solid #ccc;
+ border-left:1px solid #ccc;
+}
+
+#suggestmenu a, #suggestmenu a:visited
+{
+ background: #ebeaeb;
+ border-left:1px solid #dddddd;
+ border-right:1px solid #dddddd;
+ padding:3px 6px 3px 6px;
+ display:block;
+ margin:0px;
+ text-decoration:none;
+ color: black;
+ outline:none
+}
+
+#suggestmenu a.first, #suggestmenu a.first:visited
+{
+ border-top:1px solid #dddddd;
+}
+
+.spell_sep_bottom
+{
+ border-bottom: 1px solid #dddddd;
+}
+
+.spell_sep_top
+{
+ border-top: 1px solid #aaaaaa;
+}
+
+#suggestmenu a:hover
+{
+ color:#000;
+ background: #f5f5f5;
+}
+
+#suggestmenu .foot
+{
+ border-top:1px solid #aaaaaa;
+ background:#fff
+}
+
+#suggestmenu .foot a, #suggestmenu .foot a:visited
+{
+ outline:none
+}
diff --git a/plugins/jetpack/modules/after-the-deadline/button.gif b/plugins/jetpack/modules/after-the-deadline/button.gif
new file mode 100644
index 00000000..279a75de
--- /dev/null
+++ b/plugins/jetpack/modules/after-the-deadline/button.gif
Binary files differ
diff --git a/plugins/jetpack/modules/after-the-deadline/config-options.php b/plugins/jetpack/modules/after-the-deadline/config-options.php
new file mode 100644
index 00000000..31cc4da3
--- /dev/null
+++ b/plugins/jetpack/modules/after-the-deadline/config-options.php
@@ -0,0 +1,130 @@
+<?php
+/*
+ * Display the configuration options for AtD
+ */
+
+/*
+ * A convienence function to display the HTML for an AtD option
+ */
+function AtD_print_option( $name, $value, $options ) {
+ // Attribute-safe version of $name
+ $attr_name = sanitize_title($name); // Using sanitize_title since there's no comparable function for attributes
+?>
+ <input type="checkbox" id="atd_<?php echo ($attr_name) ?>" name="<?php echo $options['name'] ?>[<?php echo $name; ?>]" value="1" <?php checked( '1', isset( $options[$name] ) ? $options[$name] : false ); ?>> <label for="atd_<?php echo $attr_name ?>"><?php echo $value; ?></label>
+<?php
+}
+
+/*
+ * Save AtD options
+ */
+function AtD_process_options_update() {
+
+ $user = wp_get_current_user();
+
+ if ( ! $user || $user->ID == 0 )
+ return;
+
+ AtD_update_options( $user->ID, 'AtD_options' );
+ AtD_update_options( $user->ID, 'AtD_check_when' );
+ AtD_update_options( $user->ID, 'AtD_guess_lang' );
+}
+
+/*
+ * Display the various AtD options
+ */
+function AtD_display_options_form() {
+
+ /* grab our user and validate their existence */
+ $user = wp_get_current_user();
+ if ( ! $user || $user->ID == 0 )
+ return;
+
+ $options_show_types = AtD_get_options( $user->ID, 'AtD_options' );
+ $options_check_when = AtD_get_options( $user->ID, 'AtD_check_when' );
+ $options_guess_lang = AtD_get_options( $user->ID, 'AtD_guess_lang' );
+?>
+ <table class="form-table">
+ <tr valign="top">
+ <th scope="row"> <a name="atd"></a> <?php _e( 'Proofreading', 'jetpack' ); ?></th>
+ <td>
+ <p><?php _e( 'Automatically proofread content when:', 'jetpack' ); ?>
+
+ <p><?php
+ AtD_print_option( 'onpublish', __('a post or page is first published', 'jetpack'), $options_check_when );
+ echo '<br />';
+ AtD_print_option( 'onupdate', __('a post or page is updated', 'jetpack'), $options_check_when );
+ ?></p>
+
+ <p style="font-weight: bold"><?php _e('English Options', 'jetpack'); ?></font>
+
+ <p><?php _e('Enable proofreading for the following grammar and style rules when writing posts and pages:', 'jetpack'); ?></p>
+
+ <p><?php
+ AtD_print_option( 'Bias Language', __('Bias Language', 'jetpack'), $options_show_types );
+ echo '<br />';
+ AtD_print_option( 'Cliches', __('Clich&eacute;s', 'jetpack'), $options_show_types );
+ echo '<br />';
+ AtD_print_option( 'Complex Expression', __('Complex Phrases', 'jetpack'), $options_show_types );
+ echo '<br />';
+ AtD_print_option( 'Diacritical Marks', __('Diacritical Marks', 'jetpack'), $options_show_types );
+ echo '<br />';
+ AtD_print_option( 'Double Negative', __('Double Negatives', 'jetpack'), $options_show_types );
+ echo '<br />';
+ AtD_print_option( 'Hidden Verbs', __('Hidden Verbs', 'jetpack'), $options_show_types );
+ echo '<br />';
+ AtD_print_option( 'Jargon Language', __('Jargon', 'jetpack'), $options_show_types );
+ echo '<br />';
+ AtD_print_option( 'Passive voice', __('Passive Voice', 'jetpack'), $options_show_types );
+ echo '<br />';
+ AtD_print_option( 'Phrases to Avoid', __('Phrases to Avoid', 'jetpack'), $options_show_types );
+ echo '<br />';
+ AtD_print_option( 'Redundant Expression', __('Redundant Phrases', 'jetpack'), $options_show_types );
+ ?></p>
+ <p><?php printf( __( '<a href="%s">Learn more</a> about these options.', 'jetpack' ), 'http://support.wordpress.com/proofreading/' );
+?></p>
+
+ <p style="font-weight: bold"><?php _e( 'Language', 'jetpack' ); ?></font>
+
+ <p><?php printf(
+ _x( 'The proofreader supports English, French, German, Portuguese, and Spanish. Your <a href="%1$s">%2%s</a> value is the default proofreading language.', '%1$s = http://codex.wordpress.org/Installing_WordPress_in_Your_Language, %2$s = WPLANG', 'jetpack' ),
+ 'http://codex.wordpress.org/Installing_WordPress_in_Your_Language',
+ 'WPLANG'
+ ); ?></p>
+
+ <p><?php
+ AtD_print_option( 'true', __('Use automatically detected language to proofread posts and pages', 'jetpack' ), $options_guess_lang );
+ ?></p>
+
+<?php
+}
+
+/*
+ * Returns an array of AtD user options specified by $name
+ */
+function AtD_get_options( $user_id, $name ) {
+ $options_raw = AtD_get_setting( $user_id, $name, 'single' );
+
+ $options = array();
+ $options['name'] = $name;
+
+ if ( $options_raw )
+ foreach ( explode( ',', $options_raw ) as $option )
+ $options[ $option ] = 1;
+
+ return $options;
+}
+
+/*
+ * Saves set of user options specified by $name from POST data
+ */
+function AtD_update_options( $user_id, $name ) {
+ /* We should probably run $_POST[name] through an esc_*() function... */
+ if ( isset( $_POST[$name] ) && is_array( $_POST[$name] ) ) {
+ $copy = array_map( 'strip_tags', array_keys( $_POST[$name] ) );
+ AtD_update_setting( $user_id, AtD_sanitize( $name ), implode( ',', $copy ) );
+ } else {
+ AtD_update_setting( $user_id, AtD_sanitize( $name ), '');
+ }
+
+ return;
+}
diff --git a/plugins/jetpack/modules/after-the-deadline/config-unignore.php b/plugins/jetpack/modules/after-the-deadline/config-unignore.php
new file mode 100644
index 00000000..9d49fa20
--- /dev/null
+++ b/plugins/jetpack/modules/after-the-deadline/config-unignore.php
@@ -0,0 +1,143 @@
+<?php
+/*
+ * Called by the TinyMCE plugin when Ignore Always is clicked (setup as an action through admin-ajax.php)
+ */
+function AtD_ignore_call() {
+
+ if ( ! AtD_is_allowed() )
+ return;
+
+ $user = wp_get_current_user();
+
+ if ( ! $user || $user->ID == 0 )
+ return;
+
+ $ignores = explode( ',', AtD_get_setting( $user->ID, 'AtD_ignored_phrases') );
+ array_push( $ignores, $_GET['phrase'] );
+
+ $ignores = array_filter( array_map( 'strip_tags', $ignores ) );
+
+ AtD_update_setting( $user->ID, 'AtD_ignored_phrases', implode( ',', $ignores ) );
+
+ header( 'Content-Type: text/xml' );
+ echo '<success></success>';
+ die();
+}
+
+/*
+ * Called when a POST occurs, used to save AtD ignored phrases
+ */
+function AtD_process_unignore_update() {
+
+ if ( ! AtD_is_allowed() )
+ return;
+
+ $user = wp_get_current_user();
+
+ if ( ! $user || $user->ID == 0 )
+ return;
+
+ $ignores = array_filter( array_map( 'strip_tags', explode( ',', $_POST['AtD_ignored_phrases'] ) ) );
+ AtD_update_setting( $user->ID, 'AtD_ignored_phrases', join( ',', $ignores ) );
+}
+
+/*
+ * Display the AtD unignore form on a page
+ */
+function AtD_display_unignore_form() {
+
+ if ( ! AtD_is_allowed() )
+ return;
+
+ $user = wp_get_current_user();
+
+ if ( ! $user || $user->ID == 0 )
+ return;
+
+ $ignores = AtD_get_setting( $user->ID, 'AtD_ignored_phrases' );
+?>
+<script>
+function atd_show_phrases( ignored )
+{
+ var element = jQuery( '#atd_ignores' ),
+ items = [],
+ delLink;
+
+ ignored.sort();
+
+ element.empty();
+ for ( var i = 0; i < ignored.length; i++ ) {
+ if ( ignored[i].length > 0 ) {
+ delLink = jQuery( '<span id="atd_' + i + '">&nbsp;</span>' );
+ delLink
+ .text( delLink.text() + ignored[i] )
+ .prepend( jQuery( '<a class="ntdelbutton">X</a>' ).data( 'ignored', ignored[i] ) );
+ element.append( delLink ).append( '<br />' );
+ }
+ }
+}
+
+function atd_unignore( phrase ) {
+ /* get the ignored values and remove the unwanted phrase */
+ var ignored = jQuery( '#AtD_ignored_phrases' ).val().split( /,/g );
+ ignored = jQuery.map(ignored, function(value, index) { return value == phrase ? null : value; });
+ jQuery( '#AtD_ignored_phrases' ).val( ignored.join(',') );
+
+ /* update the UI */
+ atd_show_phrases( ignored );
+
+ /* show a nifty message to the user */
+ jQuery( '#AtD_message' ).show();
+}
+
+function atd_ignore () {
+ /* get the ignored values and update the hidden field */
+ var ignored = jQuery( '#AtD_ignored_phrases' ).val().split( /,/g );
+
+ jQuery.map(jQuery( '#AtD_add_ignore' ).val().split(/,\s*/g), function(value, index) { ignored.push(value); });
+
+ jQuery( '#AtD_ignored_phrases' ).val( ignored.join(',') );
+
+ /* update the UI */
+ atd_show_phrases( ignored );
+ jQuery( '#AtD_add_ignore' ).val('');
+
+ /* show that nifteroo messaroo to the useroo */
+ jQuery( '#AtD_message' ).show();
+}
+
+function atd_ignore_init() {
+ jQuery( '#AtD_message' ).hide();
+ jQuery( '#atd_ignores' ).delegate( 'a', 'click', function() {
+ atd_unignore( jQuery(this).data( 'ignored' ) );
+ return false;
+ } );
+ atd_show_phrases( jQuery( '#AtD_ignored_phrases' ).val().split( /,/g ) );
+}
+
+/* document.ready() does not execute in IE6 unless it's at the bottom of the page. oi! */
+if (navigator.appName == 'Microsoft Internet Explorer')
+ setTimeout( atd_ignore_init, 2500 );
+else
+ jQuery( document ).ready( atd_ignore_init );
+</script>
+ <input type="hidden" name="AtD_ignored_phrases" id="AtD_ignored_phrases" value="<?php echo esc_attr( $ignores ); ?>">
+
+ <p style="font-weight: bold"><?php _e( 'Ignored Phrases', 'jetpack' ); ?></font>
+
+ <p><?php _e( 'Identify words and phrases to ignore while proofreading your posts and pages:', 'jetpack' ); ?></p>
+
+ <p><input type="text" id="AtD_add_ignore" name="AtD_add_ignore"> <input type="button" value="<?php _e( 'Add', 'jetpack' ); ?>" onclick="javascript:atd_ignore()"></p>
+
+ <div class="tagchecklist" id="atd_ignores"></div>
+
+ <div class="plugin-update-tr" id="AtD_message" style="display: none">
+ <div class="update-message"><strong><?php _e( 'Be sure to click "Update Profile" at the bottom of the screen to save your changes.', 'jetpack' ); ?></strong></div>
+ </div>
+
+ </td>
+ </tr>
+ </table>
+
+<?php
+}
diff --git a/plugins/jetpack/modules/after-the-deadline/install_atd_l10n.js b/plugins/jetpack/modules/after-the-deadline/install_atd_l10n.js
new file mode 100644
index 00000000..7d18bb9a
--- /dev/null
+++ b/plugins/jetpack/modules/after-the-deadline/install_atd_l10n.js
@@ -0,0 +1,24 @@
+/* a quick poor man's sprintf */
+function atd_sprintf(format, values) {
+ var result = format;
+ for (var x = 0; x < values.length; x++)
+ result = result.replace(new RegExp('%' + (x + 1) + '\\$', 'g'), values[x]);
+ return result;
+}
+
+/* init the autoproofread options */
+function install_atd_l10n() {
+ /* install L10n strings into TinyMCE if it's present */
+ if ( typeof( tinyMCE ) != 'undefined' && typeof( tinyMCEPreInit ) != 'undefined' )
+ tinyMCE.addI18n(tinyMCEPreInit.mceInit.language + '.AtD', AtD_l10n_r0ar);
+
+ /* set the AtD l10n instance */
+ AtD.addI18n(AtD_l10n_r0ar);
+}
+
+/* document.ready() does not execute in IE6 unless it's at the bottom of the page. oi! */
+if (navigator.appName == 'Microsoft Internet Explorer')
+ setTimeout( install_atd_l10n, 2500 );
+else
+ jQuery( document ).ready( install_atd_l10n );
+
diff --git a/plugins/jetpack/modules/after-the-deadline/jquery.atd.js b/plugins/jetpack/modules/after-the-deadline/jquery.atd.js
new file mode 100644
index 00000000..e5201079
--- /dev/null
+++ b/plugins/jetpack/modules/after-the-deadline/jquery.atd.js
@@ -0,0 +1,417 @@
+/*
+ * jquery.atd.js - jQuery powered writing check with After the Deadline
+ * Author : Raphael Mudge, Automattic Inc.
+ * License : LGPL or MIT License (take your pick)
+ * Project : http://www.afterthedeadline.com/development.slp
+ * Contact : raffi@automattic.com
+ *
+ * Derived from:
+ *
+ * jquery.spellchecker.js - a simple jQuery Spell Checker
+ * Copyright (c) 2008 Richard Willis
+ * MIT license : http://www.opensource.org/licenses/mit-license.php
+ * Project : http://jquery-spellchecker.googlecode.com
+ * Contact : willis.rh@gmail.com
+ */
+
+var AtD =
+{
+ rpc : '', /* see the proxy.php that came with the AtD/TinyMCE plugin */
+ rpc_css : 'http://www.polishmywriting.com/atd-jquery/server/proxycss.php?data=', /* you may use this, but be nice! */
+ rpc_css_lang : 'en',
+ api_key : '',
+ i18n : {},
+ listener : {}
+};
+
+AtD.getLang = function(key, defaultk) {
+ if (AtD.i18n[key] == undefined)
+ return defaultk;
+
+ return AtD.i18n[key];
+};
+
+AtD.addI18n = function(localizations) {
+ AtD.i18n = localizations;
+ AtD.core.addI18n(localizations);
+};
+
+AtD.setIgnoreStrings = function(string) {
+ AtD.core.setIgnoreStrings(string);
+};
+
+AtD.showTypes = function(string) {
+ AtD.core.showTypes(string);
+};
+
+AtD.checkCrossAJAX = function(container_id, callback_f) {
+ /* checks if a global var for click stats exists and increments it if it does... */
+ if (typeof AtD_proofread_click_count != "undefined")
+ AtD_proofread_click_count++;
+
+ AtD.callback_f = callback_f; /* remember the callback for later */
+ AtD.remove(container_id);
+ var container = jQuery('#' + container_id);
+
+ var html = container.html();
+ text = jQuery.trim(container.html());
+ text = text.replace(/\&lt;/g, '<').replace(/\&gt;/g, '>').replace(/\&amp;/g, '&');
+ text = encodeURIComponent( text.replace( /\%/g, '%25' ) ); /* % not being escaped here creates problems, I don't know why. */
+
+ /* do some sanity checks based on the browser */
+ if ((text.length > 2000 && navigator.appName == 'Microsoft Internet Explorer') || text.length > 7800) {
+ if (callback_f != undefined && callback_f.error != undefined)
+ callback_f.error("Maximum text length for this browser exceeded");
+
+ return;
+ }
+
+ /* do some cross-domain AJAX action with CSSHttpRequest */
+ CSSHttpRequest.get(AtD.rpc_css + text + "&lang=" + AtD.rpc_css_lang + "&nocache=" + (new Date().getTime()), function(response) {
+ /* do some magic to convert the response into an XML document */
+ var xml;
+ if (navigator.appName == 'Microsoft Internet Explorer') {
+ xml = new ActiveXObject("Microsoft.XMLDOM");
+ xml.async = false;
+ xml.loadXML(response);
+ }
+ else {
+ xml = (new DOMParser()).parseFromString(response, 'text/xml');
+ }
+
+ /* check for and display error messages from the server */
+ if (AtD.core.hasErrorMessage(xml)) {
+ if (AtD.callback_f != undefined && AtD.callback_f.error != undefined)
+ AtD.callback_f.error(AtD.core.getErrorMessage(xml));
+
+ return;
+ }
+
+ /* highlight the errors */
+
+ AtD.container = container_id;
+ var count = AtD.processXML(container_id, xml);
+
+ if (AtD.callback_f != undefined && AtD.callback_f.ready != undefined)
+ AtD.callback_f.ready(count);
+
+ if (count == 0 && AtD.callback_f != undefined && AtD.callback_f.success != undefined)
+ AtD.callback_f.success(count);
+
+ AtD.counter = count;
+ AtD.count = count;
+ });
+};
+
+/* check a div for any incorrectly spelled words */
+AtD.check = function(container_id, callback_f) {
+ /* checks if a global var for click stats exists and increments it if it does... */
+ if (typeof AtD_proofread_click_count != "undefined")
+ AtD_proofread_click_count++;
+
+ AtD.callback_f = callback_f; /* remember the callback for later */
+
+ AtD.remove(container_id);
+
+ var container = jQuery('#' + container_id);
+
+ var html = container.html();
+ text = jQuery.trim(container.html());
+ text = text.replace(/\&lt;/g, '<').replace(/\&gt;/g, '>').replace(/\&amp;/g, '&');
+ text = encodeURIComponent( text ); /* re-escaping % is not necessary here. don't do it */
+
+ jQuery.ajax({
+ type : "POST",
+ url : AtD.rpc + '/checkDocument',
+ data : 'key=' + AtD.api_key + '&data=' + text,
+ format : 'raw',
+ dataType : (jQuery.browser.msie) ? "text" : "xml",
+
+ error : function(XHR, status, error) {
+ if (AtD.callback_f != undefined && AtD.callback_f.error != undefined)
+ AtD.callback_f.error(status + ": " + error);
+ },
+
+ success : function(data) {
+ /* apparently IE likes to return XML as plain text-- work around from:
+ http://docs.jquery.com/Specifying_the_Data_Type_for_AJAX_Requests */
+
+ var xml;
+ if (typeof data == "string") {
+ xml = new ActiveXObject("Microsoft.XMLDOM");
+ xml.async = false;
+ xml.loadXML(data);
+ }
+ else {
+ xml = data;
+ }
+
+ if (AtD.core.hasErrorMessage(xml)) {
+ if (AtD.callback_f != undefined && AtD.callback_f.error != undefined)
+ AtD.callback_f.error(AtD.core.getErrorMessage(xml));
+
+ return;
+ }
+
+ /* on with the task of processing and highlighting errors */
+
+ AtD.container = container_id;
+ var count = AtD.processXML(container_id, xml);
+
+ if (AtD.callback_f != undefined && AtD.callback_f.ready != undefined)
+ AtD.callback_f.ready(count);
+
+ if (count == 0 && AtD.callback_f != undefined && AtD.callback_f.success != undefined)
+ AtD.callback_f.success(count);
+
+ AtD.counter = count;
+ AtD.count = count;
+ }
+ });
+};
+
+AtD.remove = function(container_id) {
+ AtD._removeWords(container_id, null);
+};
+
+AtD.clickListener = function(event) {
+ if (AtD.core.isMarkedNode(event.target))
+ AtD.suggest(event.target);
+};
+
+AtD.processXML = function(container_id, responseXML) {
+
+ var results = AtD.core.processXML(responseXML);
+
+ if (results.count > 0)
+ results.count = AtD.core.markMyWords(jQuery('#' + container_id).contents(), results.errors);
+
+ jQuery('#' + container_id).unbind('click', AtD.clickListener);
+ jQuery('#' + container_id).click(AtD.clickListener);
+
+ return results.count;
+};
+
+AtD.useSuggestion = function(word) {
+ this.core.applySuggestion(AtD.errorElement, word);
+
+ AtD.counter --;
+ if (AtD.counter == 0 && AtD.callback_f != undefined && AtD.callback_f.success != undefined)
+ AtD.callback_f.success(AtD.count);
+};
+
+AtD.editSelection = function() {
+ var parent = AtD.errorElement.parent();
+
+ if (AtD.callback_f != undefined && AtD.callback_f.editSelection != undefined)
+ AtD.callback_f.editSelection(AtD.errorElement);
+
+ if (AtD.errorElement.parent() != parent) {
+ AtD.counter --;
+ if (AtD.counter == 0 && AtD.callback_f != undefined && AtD.callback_f.success != undefined)
+ AtD.callback_f.success(AtD.count);
+ }
+};
+
+AtD.ignoreSuggestion = function() {
+ AtD.core.removeParent(AtD.errorElement);
+
+ AtD.counter --;
+ if (AtD.counter == 0 && AtD.callback_f != undefined && AtD.callback_f.success != undefined)
+ AtD.callback_f.success(AtD.count);
+};
+
+AtD.ignoreAll = function(container_id) {
+ var target = AtD.errorElement.text();
+ var removed = AtD._removeWords(container_id, target);
+
+ AtD.counter -= removed;
+
+ if (AtD.counter == 0 && AtD.callback_f != undefined && AtD.callback_f.success != undefined)
+ AtD.callback_f.success(AtD.count);
+
+ if (AtD.callback_f != undefined && AtD.callback_f.ignore != undefined) {
+ AtD.callback_f.ignore(target);
+ AtD.core.setIgnoreStrings(target);
+ }
+};
+
+AtD.explainError = function() {
+ if (AtD.callback_f != undefined && AtD.callback_f.explain != undefined)
+ AtD.callback_f.explain(AtD.explainURL);
+};
+
+AtD.suggest = function(element) {
+ /* construct the menu if it doesn't already exist */
+
+ if (jQuery('#suggestmenu').length == 0) {
+ var suggest = jQuery('<div id="suggestmenu"></div>');
+ suggest.prependTo('body');
+ }
+ else {
+ var suggest = jQuery('#suggestmenu');
+ suggest.hide();
+ }
+
+ /* find the correct suggestions object */
+
+ errorDescription = AtD.core.findSuggestion(element);
+
+ /* build up the menu y0 */
+
+ AtD.errorElement = jQuery(element);
+
+ suggest.empty();
+
+ if (errorDescription == undefined) {
+ suggest.append('<strong>' + AtD.getLang('menu_title_no_suggestions', 'No suggestions') + '</strong>');
+ }
+ else if (errorDescription["suggestions"].length == 0) {
+ suggest.append('<strong>' + errorDescription['description'] + '</strong>');
+ }
+ else {
+ suggest.append('<strong>' + errorDescription['description'] + '</strong>');
+
+ for (var i = 0; i < errorDescription["suggestions"].length; i++) {
+ (function(sugg) {
+ suggest.append('<a href="javascript:AtD.useSuggestion(\'' + sugg.replace(/'/, '\\\'') + '\')">' + sugg + '</a>');
+ })(errorDescription["suggestions"][i]);
+ }
+ }
+
+ /* do the explain menu if configured */
+
+ if (AtD.callback_f != undefined && AtD.callback_f.explain != undefined && errorDescription['moreinfo'] != undefined) {
+ suggest.append('<a href="javascript:AtD.explainError()" class="spell_sep_top">' + AtD.getLang('menu_option_explain', 'Explain...') + '</a>');
+ AtD.explainURL = errorDescription['moreinfo'];
+ }
+
+ /* do the ignore option */
+
+ suggest.append('<a href="javascript:AtD.ignoreSuggestion()" class="spell_sep_top">' + AtD.getLang('menu_option_ignore_once', 'Ignore suggestion') + '</a>');
+
+ /* add the edit in place and ignore always option */
+
+ if (AtD.callback_f != undefined && AtD.callback_f.editSelection != undefined) {
+ if (AtD.callback_f != undefined && AtD.callback_f.ignore != undefined)
+ suggest.append('<a href="javascript:AtD.ignoreAll(\'' + AtD.container + '\')">' + AtD.getLang('menu_option_ignore_always', 'Ignore always') + '</a>');
+ else
+ suggest.append('<a href="javascript:AtD.ignoreAll(\'' + AtD.container + '\')">' + AtD.getLang('menu_option_ignore_all', 'Ignore all') + '</a>');
+
+ suggest.append('<a href="javascript:AtD.editSelection(\'' + AtD.container + '\')" class="spell_sep_bottom spell_sep_top">' + AtD.getLang('menu_option_edit_selection', 'Edit Selection...') + '</a>');
+ }
+ else {
+ if (AtD.callback_f != undefined && AtD.callback_f.ignore != undefined)
+ suggest.append('<a href="javascript:AtD.ignoreAll(\'' + AtD.container + '\')" class="spell_sep_bottom">' + AtD.getLang('menu_option_ignore_always', 'Ignore always') + '</a>');
+ else
+ suggest.append('<a href="javascript:AtD.ignoreAll(\'' + AtD.container + '\')" class="spell_sep_bottom">' + AtD.getLang('menu_option_ignore_all', 'Ignore all') + '</a>');
+ }
+
+ /* show the menu */
+
+ var pos = jQuery(element).offset();
+ var width = jQuery(element).width();
+
+ /* a sanity check for Internet Explorer--my favorite browser in every possible way */
+ if (width > 100)
+ width = 50;
+
+ jQuery(suggest).css({ left: (pos.left + width) + 'px', top: pos.top + 'px' });
+
+ jQuery(suggest).fadeIn(200);
+
+ /* bind events to make the menu disappear when the user clicks outside of it */
+
+ AtD.suggestShow = true;
+
+ setTimeout(function() {
+ jQuery("body").bind("click", function() {
+ if (!AtD.suggestShow)
+ jQuery('#suggestmenu').fadeOut(200);
+ });
+ }, 1);
+
+ setTimeout(function() {
+ AtD.suggestShow = false;
+ }, 2);
+};
+
+AtD._removeWords = function(container_id, w) {
+ return this.core.removeWords(jQuery('#' + container_id), w);
+};
+
+/*
+ * Set prototypes used by AtD Core UI
+ */
+AtD.initCoreModule = function() {
+ var core = new AtDCore();
+
+ core.hasClass = function(node, className) {
+ return jQuery(node).hasClass(className);
+ };
+
+ core.map = jQuery.map;
+
+ core.contents = function(node) {
+ return jQuery(node).contents();
+ };
+
+ core.replaceWith = function(old_node, new_node) {
+ return jQuery(old_node).replaceWith(new_node);
+ };
+
+ core.findSpans = function(parent) {
+ return jQuery.makeArray(parent.find('span'));
+ };
+
+ core.create = function(string, isTextNode) {
+ // replace out all tags with &-equivalents so that we preserve tag text.
+ string = string.replace(/\&/g, '&amp;');
+ string = string.replace(/\</g, '&lt;').replace(/\>/g, '&gt;');
+
+ // find all instances of AtD-created spans
+ var matches = string.match(/\&lt;span class="hidden\w+?" pre="[^"]*"\&gt;.*?\&lt;\/span\&gt;/g);
+
+ // ... and fix the tags in those substrings.
+ if (matches) {
+ for (var x = 0; x < matches.length; x++) {
+ string = string.replace(matches[x], matches[x].replace(/\&lt;/gi, '<').replace(/\&gt;/gi, '>'));
+ };
+ }
+
+ if (core.isIE()) {
+ // and... one more round of corrections for our friends over at the Internet Explorer
+ matches = string.match(/\&lt;span class="mceItemHidden"\&gt;\&amp;nbsp;\&lt;\/span&gt;/g, string);
+ //|&lt;BR.*?class.*?atd_remove_me.*?\&gt;/gi, string);
+ if (matches) {
+ for (var x = 0; x < matches.length; x++) {
+ string = string.replace(matches[x], matches[x].replace(/\&lt;/gi, '<').replace(/\&gt;/gi, '>').replace(/\&amp;/gi, '&'));
+ };
+ }
+ }
+
+ node = jQuery('<span class="mceItemHidden"></span>');
+ node.html(string);
+ return node;
+ };
+
+ core.remove = function(node) {
+ return jQuery(node).remove();
+ };
+
+ core.removeParent = function(node) {
+ /* unwrap exists in jQuery 1.4+ only. Thankfully because replaceWith as-used here won't work in 1.4 */
+ if (jQuery(node).unwrap)
+ return jQuery(node).contents().unwrap();
+ else
+ return jQuery(node).replaceWith(jQuery(node).html());
+ };
+
+ core.getAttrib = function(node, name) {
+ return jQuery(node).attr(name);
+ };
+
+ return core;
+};
+
+AtD.core = AtD.initCoreModule();
diff --git a/plugins/jetpack/modules/after-the-deadline/proxy.php b/plugins/jetpack/modules/after-the-deadline/proxy.php
new file mode 100644
index 00000000..04bbd285
--- /dev/null
+++ b/plugins/jetpack/modules/after-the-deadline/proxy.php
@@ -0,0 +1,69 @@
+<?php
+/*
+ * This script redirects AtD AJAX requests to the AtD service
+ */
+
+/**
+ * Returns array with headers in $response[0] and body in $response[1]
+ * Based on a function from Akismet
+ */
+function AtD_http_post( $request, $host, $path, $port = 80 ) {
+ $http_args = array(
+ 'body' => $request,
+ 'headers' => array(
+ 'Content-Type' => 'application/x-www-form-urlencoded; charset=' . get_option( 'blog_charset' ),
+ 'Host' => $host,
+ 'User-Agent' => 'AtD/0.1'
+ ),
+ 'httpversion' => '1.0',
+ 'timeout' => apply_filters( 'atd_http_post_timeout', 15 ),
+ );
+ $AtD_url = "http://{$host}{$path}";
+ $response = wp_remote_post( $AtD_url, $http_args );
+ $code = (int) wp_remote_retrieve_response_code( $response );
+
+ if ( is_wp_error( $response ) ) {
+ do_action( 'atd_http_post_error', 'http-error' );
+ return array();
+ } elseif ( 200 != $code ) {
+ do_action( 'atd_http_post_error', $code );
+ }
+
+ return array(
+ wp_remote_retrieve_headers( $response ),
+ wp_remote_retrieve_body( $response ),
+ );
+}
+
+/*
+ * This function is called as an action handler to admin-ajax.php
+ */
+function AtD_redirect_call() {
+ if ( $_SERVER['REQUEST_METHOD'] === 'POST' )
+ $postText = trim( file_get_contents( 'php://input' ) );
+
+ $url = $_GET['url'];
+
+ $service = apply_filters( 'atd_service_domain', 'service.afterthedeadline.com' );
+ if ( defined('WPLANG') ) {
+ if ( strpos(WPLANG, 'pt') !== false )
+ $service = 'pt.service.afterthedeadline.com';
+ else if ( strpos(WPLANG, 'de') !== false )
+ $service = 'de.service.afterthedeadline.com';
+ else if ( strpos(WPLANG, 'es') !== false )
+ $service = 'es.service.afterthedeadline.com';
+ else if ( strpos(WPLANG, 'fr') !== false )
+ $service = 'fr.service.afterthedeadline.com';
+ }
+ $user = wp_get_current_user();
+ $guess = strcmp( AtD_get_setting( $user->ID, 'AtD_guess_lang' ), "true" ) == 0 ? "true" : "false";
+
+ $data = AtD_http_post( $postText . "&guess=$guess", defined('ATD_HOST') ? ATD_HOST : $service, $url, defined('ATD_PORT') ? ATD_PORT : 80 );
+
+ header( 'Content-Type: text/xml' );
+
+ if ( !empty($data[1]) )
+ echo $data[1];
+
+ die();
+}
diff --git a/plugins/jetpack/modules/after-the-deadline/tinymce/atdbuttontr.gif b/plugins/jetpack/modules/after-the-deadline/tinymce/atdbuttontr.gif
new file mode 100644
index 00000000..6c51d075
--- /dev/null
+++ b/plugins/jetpack/modules/after-the-deadline/tinymce/atdbuttontr.gif
Binary files differ
diff --git a/plugins/jetpack/modules/after-the-deadline/tinymce/css/content.css b/plugins/jetpack/modules/after-the-deadline/tinymce/css/content.css
new file mode 100644
index 00000000..8a667529
--- /dev/null
+++ b/plugins/jetpack/modules/after-the-deadline/tinymce/css/content.css
@@ -0,0 +1,18 @@
+.hiddenSpellError
+{
+ border-bottom: 2px solid red;
+ cursor: default;
+}
+
+.hiddenGrammarError
+{
+ border-bottom: 2px solid green;
+ cursor: default;
+}
+
+.hiddenSuggestion
+{
+ border-bottom: 2px solid blue;
+ cursor: default;
+}
+
diff --git a/plugins/jetpack/modules/after-the-deadline/tinymce/editor_plugin.js b/plugins/jetpack/modules/after-the-deadline/tinymce/editor_plugin.js
new file mode 100644
index 00000000..fc9f01fe
--- /dev/null
+++ b/plugins/jetpack/modules/after-the-deadline/tinymce/editor_plugin.js
@@ -0,0 +1,476 @@
+/*
+ * TinyMCE Writing Improvement Tool Plugin
+ * Author: Raphael Mudge (raffi@automattic.com)
+ *
+ * http://www.afterthedeadline.com
+ *
+ * Distributed under the LGPL
+ *
+ * Derived from:
+ * $Id: editor_plugin_src.js 425 2007-11-21 15:17:39Z spocke $
+ *
+ * @author Moxiecode
+ * @copyright Copyright (C) 2004-2008, Moxiecode Systems AB, All rights reserved.
+ *
+ * Moxiecode Spell Checker plugin released under the LGPL with TinyMCE
+ */
+
+(function()
+{
+ var JSONRequest = tinymce.util.JSONRequest, each = tinymce.each, DOM = tinymce.DOM;
+
+ tinymce.create('tinymce.plugins.AfterTheDeadlinePlugin',
+ {
+ getInfo : function()
+ {
+ return
+ ({
+ longname : 'After The Deadline',
+ author : 'Raphael Mudge',
+ authorurl : 'http://blog.afterthedeadline.com',
+ infourl : 'http://www.afterthedeadline.com',
+ version : tinymce.majorVersion + "." + tinymce.minorVersion
+ });
+ },
+
+ /* initializes the functions used by the AtD Core UI Module */
+ initAtDCore : function(editor, plugin)
+ {
+ var core = new AtDCore();
+
+ core.map = each;
+
+ core.getAttrib = function(node, key)
+ {
+ return editor.dom.getAttrib(node, key);
+ };
+
+ core.findSpans = function(parent)
+ {
+ if (parent == undefined)
+ return editor.dom.select('span');
+ else
+ return editor.dom.select('span', parent);
+ };
+
+ core.hasClass = function(node, className)
+ {
+ return editor.dom.hasClass(node, className);
+ };
+
+ core.contents = function(node)
+ {
+ return node.childNodes;
+ };
+
+ core.replaceWith = function(old_node, new_node)
+ {
+ return editor.dom.replace(new_node, old_node);
+ };
+
+ core.create = function(node_html)
+ {
+ return editor.dom.create('span', { 'class': 'mceItemHidden' }, node_html);
+ };
+
+ core.removeParent = function(node)
+ {
+ editor.dom.remove(node, 1);
+ return node;
+ };
+
+ core.remove = function(node)
+ {
+ editor.dom.remove(node);
+ };
+
+ core.getLang = function(key, defaultk)
+ {
+ return editor.getLang("AtD." + key, defaultk);
+ };
+
+ core.setIgnoreStrings(editor.getParam("atd_ignore_strings", [] ).join(','));
+ core.showTypes(editor.getParam("atd_show_types", ""));
+ return core;
+ },
+
+ /* called when the plugin is initialized */
+ init : function(ed, url)
+ {
+ if ( typeof(AtDCore) == 'undefined' )
+ return;
+
+ var t = this;
+ var plugin = this;
+ var editor = ed;
+ var core = this.initAtDCore(editor, plugin);
+
+ this.url = url;
+ this.editor = ed;
+ ed.core = core;
+
+ /* look at the atd_ignore variable and put that stuff into a hash */
+ var ignore = tinymce.util.Cookie.getHash('atd_ignore');
+
+ if (ignore == undefined)
+ {
+ ignore = {};
+ }
+
+ /* add a command to request a document check and process the results. */
+ editor.addCommand('mceWritingImprovementTool', function(callback)
+ {
+ /* checks if a global var for click stats exists and increments it if it does... */
+ if (typeof AtD_proofread_click_count != "undefined")
+ AtD_proofread_click_count++;
+
+ /* create the nifty spinny thing that says "hizzo, I'm doing something fo realz" */
+ plugin.editor.setProgressState(1);
+
+ /* remove the previous errors */
+ plugin._removeWords();
+
+ /* send request to our service */
+ plugin.sendRequest('checkDocument', ed.getContent({ format: 'raw' }), function(data, request, someObject)
+ {
+ /* turn off the spinning thingie */
+ plugin.editor.setProgressState(0);
+
+ /* if the server is not accepting requests, let the user know */
+ if ( request.status != 200 || request.responseText.substr(1, 4) == 'html' || !request.responseXML )
+ {
+ ed.windowManager.alert(
+ plugin.editor.getLang('AtD.message_server_error', 'There was a problem communicating with the Proofreading service. Try again in one minute.'),
+ callback ? function() { callback( 0 ); } : function() {}
+ );
+ return;
+ }
+
+ /* check to see if things are broken first and foremost */
+ if (request.responseXML.getElementsByTagName('message').item(0) != null)
+ {
+ ed.windowManager.alert(
+ request.responseXML.getElementsByTagName('message').item(0).firstChild.data,
+ callback ? function() { callback( 0 ); } : function() {}
+ );
+ return;
+ }
+
+ var results = core.processXML(request.responseXML);
+ var ecount = 0;
+
+ if (results.count > 0)
+ {
+ ecount = plugin.markMyWords(results.errors);
+ ed.suggestions = results.suggestions;
+ }
+
+ if (ecount == 0 && (!callback || callback == undefined))
+ ed.windowManager.alert(plugin.editor.getLang('AtD.message_no_errors_found', 'No writing errors were found.'));
+ else if (callback)
+ callback(ecount);
+ });
+ });
+
+ /* load cascading style sheet for this plugin */
+ editor.onInit.add(function()
+ {
+ /* loading the content.css file, why? I have no clue */
+ if (editor.settings.content_css !== false)
+ {
+ editor.dom.loadCSS(editor.getParam("atd_css_url", url + '/css/content.css'));
+ }
+ });
+
+ /* again showing a menu, I have no clue what */
+ editor.onClick.add(plugin._showMenu, plugin);
+
+ /* we're showing some sort of menu, no idea what */
+ editor.onContextMenu.add(plugin._showMenu, plugin);
+
+ /* strip out the markup before the contents is serialized (and do it on a copy of the markup so we don't affect the user experience) */
+ editor.onPreProcess.add(function(sender, object)
+ {
+ var dom = sender.dom;
+
+ each(dom.select('span', object.node).reverse(), function(n)
+ {
+ if (n && (dom.hasClass(n, 'hiddenGrammarError') || dom.hasClass(n, 'hiddenSpellError') || dom.hasClass(n, 'hiddenSuggestion') || dom.hasClass(n, 'mceItemHidden') || (dom.getAttrib(n, 'class') == "" && dom.getAttrib(n, 'style') == "" && dom.getAttrib(n, 'id') == "" && !dom.hasClass(n, 'Apple-style-span') && dom.getAttrib(n, 'mce_name') == "")))
+ {
+ dom.remove(n, 1);
+ }
+ });
+ });
+
+ /* cleanup the HTML before executing certain commands */
+ editor.onBeforeExecCommand.add(function(editor, command)
+ {
+ if (command == 'mceCodeEditor')
+ {
+ plugin._removeWords();
+ }
+ else if (command == 'mceFullScreen')
+ {
+ plugin._done();
+ }
+ });
+
+ ed.addButton('AtD', {
+ title: ed.getLang('AtD.button_proofread_tooltip', 'Proofread Writing'),
+ image: ed.getParam('atd_button_url', url + '/atdbuttontr.gif'),
+ cmd: 'mceWritingImprovementTool'
+ });
+ },
+
+ _removeWords : function(w)
+ {
+ var ed = this.editor, dom = ed.dom, se = ed.selection, b = se.getBookmark();
+
+ ed.core.removeWords(undefined, w);
+
+ /* force a rebuild of the DOM... even though the right elements are stripped, the DOM is still organized
+ as if the span were there and this breaks my code */
+
+ dom.setHTML(dom.getRoot(), dom.getRoot().innerHTML);
+
+ se.moveToBookmark(b);
+ },
+
+ markMyWords : function(errors)
+ {
+ var ed = this.editor;
+ var se = ed.selection, b = se.getBookmark();
+
+ var ecount = ed.core.markMyWords(ed.core.contents(this.editor.getBody()), errors);
+
+ se.moveToBookmark(b);
+ return ecount;
+ },
+
+ _showMenu : function(ed, e)
+ {
+ var t = this, ed = t.editor, m = t._menu, p1, dom = ed.dom, vp = dom.getViewPort(ed.getWin());
+ var plugin = this;
+
+ if (!m)
+ {
+ p1 = DOM.getPos(ed.getContentAreaContainer());
+ //p2 = DOM.getPos(ed.getContainer());
+
+ m = ed.controlManager.createDropMenu('spellcheckermenu',
+ {
+ offset_x : p1.x,
+ offset_y : p1.y,
+ 'class' : 'mceNoIcons'
+ });
+
+ t._menu = m;
+ }
+
+ if (ed.core.isMarkedNode(e.target))
+ {
+ /* remove these other lame-o elements */
+ m.removeAll();
+
+ /* find the correct suggestions object */
+ var errorDescription = ed.core.findSuggestion(e.target);
+
+ if (errorDescription == undefined)
+ {
+ m.add({title : plugin.editor.getLang('AtD.menu_title_no_suggestions', 'No suggestions'), 'class' : 'mceMenuItemTitle'}).setDisabled(1);
+ }
+ else if (errorDescription["suggestions"].length == 0)
+ {
+ m.add({title : errorDescription["description"], 'class' : 'mceMenuItemTitle'}).setDisabled(1);
+ }
+ else
+ {
+ m.add({ title : errorDescription["description"], 'class' : 'mceMenuItemTitle' }).setDisabled(1);
+
+ for (var i = 0; i < errorDescription["suggestions"].length; i++)
+ {
+ (function(sugg)
+ {
+ m.add({
+ title : sugg,
+ onclick : function()
+ {
+ ed.core.applySuggestion(e.target, sugg);
+ t._checkDone();
+ }
+ });
+ })(errorDescription["suggestions"][i]);
+ }
+
+ m.addSeparator();
+ }
+
+ if (errorDescription != undefined && errorDescription["moreinfo"] != null)
+ {
+ (function(url)
+ {
+ m.add({
+ title : plugin.editor.getLang('AtD.menu_option_explain', 'Explain...'),
+ onclick : function()
+ {
+ ed.windowManager.open({
+ url : url,
+ width : 480,
+ height : 380,
+ inline : true
+ }, { theme_url : this.url });
+ }
+ });
+ })(errorDescription["moreinfo"]);
+
+ m.addSeparator();
+ }
+
+ m.add({
+ title : plugin.editor.getLang('AtD.menu_option_ignore_once', 'Ignore suggestion'),
+ onclick : function()
+ {
+ dom.remove(e.target, 1);
+ t._checkDone();
+ }
+ });
+
+ if (String(this.editor.getParam("atd_ignore_enable", "false")) == "true")
+ {
+ m.add({
+ title : plugin.editor.getLang('AtD.menu_option_ignore_always', 'Ignore always'),
+ onclick : function()
+ {
+ var url = t.editor.getParam('atd_ignore_rpc_url', '{backend}');
+
+ if (url == '{backend}')
+ {
+ /* Default scheme is to save ignore preferences in a cookie */
+
+ var ignore = tinymce.util.Cookie.getHash('atd_ignore');
+ if (ignore == undefined) { ignore = {}; }
+ ignore[e.target.innerHTML] = 1;
+
+ tinymce.util.Cookie.setHash('atd_ignore', ignore, new Date( (new Date().getTime()) + 157680000000) );
+ }
+ else
+ {
+ /* Plugin is configured to send ignore preferences to server, do that */
+
+ var id = t.editor.getParam("atd_rpc_id", "12345678");
+
+ tinymce.util.XHR.send({
+ url : url + encodeURI(e.target.innerHTML).replace(/&/g, '%26') + "&key=" + id,
+ content_type : 'text/xml',
+ async : true,
+ type : 'GET',
+ success : function( type, req, o )
+ {
+ /* do nothing */
+ },
+ error : function( type, req, o )
+ {
+ alert( "Ignore preference save failed\n" + type + "\n" + req.status + "\nAt: " + o.url );
+ }
+ });
+
+ /* update atd_ignore_strings with the new value */
+ t.editor.core.setIgnoreStrings(e.target.innerHTML); /* this does an update */
+ }
+
+ t._removeWords(e.target.innerHTML);
+ t._checkDone();
+ }
+ });
+ }
+ else
+ {
+ m.add({
+ title : plugin.editor.getLang('menu_option_ignore_all', 'Ignore all'),
+ onclick : function()
+ {
+ t._removeWords(e.target.innerHTML);
+ t._checkDone();
+ }
+ });
+ }
+
+ /* show the menu please */
+ ed.selection.select(e.target);
+ p1 = dom.getPos(e.target);
+ m.showMenu(p1.x, p1.y + e.target.offsetHeight - vp.y);
+
+ return tinymce.dom.Event.cancel(e);
+ }
+ else
+ {
+ m.hideMenu();
+ }
+ },
+
+ /* loop through editor DOM, call _done if no mce tags exist. */
+ _checkDone : function()
+ {
+ var t = this, ed = t.editor, dom = ed.dom, o;
+
+ each(dom.select('span'), function(n)
+ {
+ if (n && dom.hasClass(n, 'mceItemHidden'))
+ {
+ o = true;
+ return false;
+ }
+ });
+
+ if (!o)
+ {
+ t._done();
+ }
+ },
+
+ /* remove all tags, hide the menu, and fire a dom change event */
+ _done : function()
+ {
+ var plugin = this;
+ plugin._removeWords();
+
+ if (plugin._menu)
+ {
+ plugin._menu.hideMenu();
+ }
+
+ plugin.editor.nodeChanged();
+ },
+
+ sendRequest : function(file, data, success)
+ {
+ var id = this.editor.getParam("atd_rpc_id", "12345678");
+ var url = this.editor.getParam("atd_rpc_url", "{backend}");
+ var plugin = this;
+
+ if (url == '{backend}' || id == '12345678')
+ {
+ this.editor.setProgressState(0);
+ alert('Please specify: atd_rpc_url and atd_rpc_id');
+ return;
+ }
+
+ tinymce.util.XHR.send({
+ url : url + "/" + file,
+ content_type : 'text/xml',
+ type : "POST",
+ data : "data=" + encodeURI(data).replace(/&/g, '%26') + "&key=" + id,
+ async : true,
+ success : success,
+ error : function( type, req, o )
+ {
+ plugin.editor.setProgressState(0);
+ alert( type + "\n" + req.status + "\nAt: " + o.url );
+ }
+ });
+ }
+ });
+
+ // Register plugin
+ tinymce.PluginManager.add('AtD', tinymce.plugins.AfterTheDeadlinePlugin);
+})();
diff --git a/plugins/jetpack/modules/contact-form.php b/plugins/jetpack/modules/contact-form.php
new file mode 100644
index 00000000..bff13e26
--- /dev/null
+++ b/plugins/jetpack/modules/contact-form.php
@@ -0,0 +1,9 @@
+<?php
+/**
+ * Module Name: Contact Form
+ * Module Description: Easily insert a contact form any where on your site.
+ * Sort Order: 5
+ * First Introduced: 1.3
+ */
+
+include dirname( __FILE__ ) . '/contact-form/grunion-contact-form.php';
diff --git a/plugins/jetpack/modules/contact-form/admin.php b/plugins/jetpack/modules/contact-form/admin.php
new file mode 100644
index 00000000..9ac40f9e
--- /dev/null
+++ b/plugins/jetpack/modules/contact-form/admin.php
@@ -0,0 +1,513 @@
+<?php
+
+// feedback specific css items
+add_action( 'admin_print_styles', 'grunion_admin_css' );
+function grunion_admin_css() {
+ global $current_screen;
+ if ( 'edit-feedback' != $current_screen->id )
+ return;
+
+ wp_enqueue_script( 'wp-lists' );
+?>
+
+<style type='text/css'>
+.add-new-h2, .view-switch, body.no-js .tablenav select[name^=action], body.no-js #doaction, body.no-js #doaction2 {
+ display: none
+}
+
+.column-feedback_from img {
+ float:left;
+ margin-right:10px;
+ margin-top:3px;
+}
+
+.widefat .column-feedback_from {
+ width: 17%;
+}
+.widefat .column-feedback_date {
+ width: 17%;
+}
+
+.spam a {
+ color: #BC0B0B;
+}
+
+.untrash a {
+ color: #D98500;
+}
+
+.unspam a {
+color: #D98500;
+}
+
+#icon-edit { background-position: -432px -5px; }
+
+#icon-edit, #icon-post { background: url("<?php echo GRUNION_PLUGIN_URL; ?>/images/grunion-menu-big.png") no-repeat !important; }
+</style>
+
+<?php
+}
+
+// remove admin UI parts that we don't support in feedback management
+add_action( 'admin_menu', 'grunion_admin_menu' );
+function grunion_admin_menu() {
+ global $menu, $submenu;
+ unset( $submenu['edit.php?post_type=feedback'] );
+}
+
+add_filter( 'bulk_actions-edit-feedback', 'grunion_admin_bulk_actions' );
+function grunion_admin_bulk_actions( $actions ) {
+ global $current_screen;
+ if ( 'edit-feedback' != $current_screen->id )
+ return $actions;
+
+ unset( $actions['edit'] );
+ return $actions;
+}
+
+add_filter( 'views_edit-feedback', 'grunion_admin_view_tabs' );
+function grunion_admin_view_tabs( $views ) {
+ global $current_screen;
+ if ( 'edit-feedback' != $current_screen->id )
+ return $actions;
+
+ unset( $views['publish'] );
+
+ preg_match( '|post_type=feedback\'( class="current")?\>(.*)\<span class=|', $views['all'], $match );
+ if ( !empty( $match[2] ) )
+ $views['all'] = str_replace( $match[2], 'Messages ', $views['all'] );
+
+ return $views;
+}
+
+add_filter( 'manage_feedback_posts_columns', 'grunion_post_type_columns_filter' );
+function grunion_post_type_columns_filter( $cols ) {
+ $cols = array(
+ 'cb' => '<input type="checkbox" />',
+ 'feedback_from' => __( 'From', 'jetpack' ),
+ 'feedback_message' => __( 'Message', 'jetpack' ),
+ 'feedback_date' => __( 'Date', 'jetpack' )
+ );
+
+ return $cols;
+}
+
+add_action( 'manage_posts_custom_column', 'grunion_manage_post_columns', 10, 2 );
+function grunion_manage_post_columns( $col, $post_id ) {
+ global $post;
+
+ switch ( $col ) {
+ case 'feedback_from':
+ $author_name = get_post_meta( $post_id, '_feedback_author', TRUE );
+ $author_email = get_post_meta( $post_id, '_feedback_author_email', TRUE );
+ $author_url = get_post_meta( $post_id, '_feedback_author_url', TRUE );
+ $author_ip = get_post_meta( $post_id, '_feedback_ip', TRUE );
+ $form_url = get_post_meta( $post_id, '_feedback_contact_form_url', TRUE );
+
+ $author_name_line = '';
+ if ( !empty( $author_name ) ) {
+ if ( !empty( $author_email ) )
+ $author_name_line = get_avatar( $author_email, 32 );
+
+ $author_name_line .= "<strong>{$author_name}</strong><br />";
+ }
+
+ $author_email_line = '';
+ if ( !empty( $author_email ) ) {
+ $author_email_line = "<a href='mailto:{$author_email}'>";
+ $author_email_line .= "{$author_email}</a><br />";
+ }
+
+ $author_url_line = '';
+ if ( !empty( $author_url ) ) {
+ $author_url_line = "<a href='{$author_url}'>";
+ $author_url_line .= "{$author_url}</a><br />";
+
+ }
+
+ echo $author_name_line;
+ echo $author_email_line;
+ echo $author_url_line;
+ echo "<a href='edit.php?post_type=feedback&s={$author_ip}";
+ echo "&mode=detail'>{$author_ip}</a><br />";
+ echo "<a href='{$form_url}'>{$form_url}</a>";
+ break;
+
+ case 'feedback_message':
+ $post = get_post( $post_id );
+ $post_type_object = get_post_type_object( $post->post_type );
+ echo '<strong>';
+ echo esc_html( get_post_meta( $post_id, '_feedback_subject', TRUE ) );
+ echo '</strong><br />';
+ echo sanitize_text_field( get_the_content( '' ) );
+ echo '<br />';
+
+ $extra_fields = get_post_meta( $post_id, '_feedback_extra_fields', TRUE );
+ if ( !empty( $extra_fields ) ) {
+ echo '<br /><hr />';
+ echo '<table cellspacing="0" cellpadding="0" style="">' . "\n";
+ foreach ( (array) $extra_fields as $k => $v ) {
+ echo "<tr><td align='right'><b>". esc_html( $k ) ."</b></td><td>". sanitize_text_field( $v ) ."</td></tr>\n";
+ }
+ echo '</table>';
+ }
+
+ echo '<div class="row-actions">';
+ if ( $post->post_status == 'trash' ) {
+ echo '<span class="untrash" id="feedback-restore-' . $post_id;
+ echo '"><a title="';
+ echo esc_attr__( 'Restore this item from the Trash', 'jetpack' );
+ echo '" href="' . wp_nonce_url( admin_url( sprintf( $post_type_object->_edit_link . '&amp;action=untrash', $post->ID ) ), 'untrash-' . $post->post_type . '_' . $post->ID );
+ echo '">' . __( 'Restore', 'jetpack' ) . '</a></span> | ';
+
+ echo "<span class='delete'> <a class='submitdelete' title='";
+ echo esc_attr( __( 'Delete this item permanently', 'jetpack' ) );
+ echo "' href='" . get_delete_post_link( $post->ID, '', true );
+ echo "'>" . __( 'Delete Permanently', 'jetpack' ) . "</a></span>";
+?>
+
+<script>
+jQuery(document).ready(function($) {
+$('#feedback-restore-<?php echo $post_id; ?>').click(function(e) {
+ e.preventDefault();
+ $.post(ajaxurl, {
+ action: 'grunion_ajax_spam',
+ post_id: '<?php echo $post_id; ?>',
+ make_it: 'publish',
+ sub_menu: jQuery('.subsubsub .current').attr('href'),
+ _ajax_nonce: '<?php echo wp_create_nonce( 'grunion-post-status-' . $post_id ); ?>'
+ },
+ function(r) {
+ $('#post-<?php echo $post_id; ?>')
+ .css({backgroundColor: '#59C859'})
+ .fadeOut(350, function() {
+ $(this).remove();
+ $('.subsubsub').html(r);
+ });
+ }
+ );
+});
+});
+</script>
+
+<?php
+ } elseif ( $post->post_status == 'publish' ) {
+ echo '<span class="spam" id="feedback-spam-' . $post_id;
+ echo '"><a title="';
+ echo __( 'Mark this message as spam', 'jetpack' );
+ echo '" href="' . wp_nonce_url( admin_url( 'admin-ajax.php?post_id=' . $post_id . '&amp;action=spam' ), 'spam-feedback_' . $post_id );
+ echo '">Spam</a></span>';
+ echo ' | ';
+
+ echo '<span class="delete" id="feedback-trash-' . $post_id;
+ echo '">';
+ echo '<a class="submitdelete" title="' . esc_attr__( 'Trash', 'jetpack' );
+ echo '" href="' . get_delete_post_link( $post_id );
+ echo '">' . __( 'Trash', 'jetpack' ) . '</a></span>';
+
+?>
+
+<script>
+jQuery(document).ready( function($) {
+ $('#feedback-spam-<?php echo $post_id; ?>').click( function(e) {
+ e.preventDefault();
+ $.post( ajaxurl, {
+ action: 'grunion_ajax_spam',
+ post_id: '<?php echo $post_id; ?>',
+ make_it: 'spam',
+ sub_menu: jQuery('.subsubsub .current').attr('href'),
+ _ajax_nonce: '<?php echo wp_create_nonce( 'grunion-post-status-' . $post_id ); ?>'
+ },
+ function( r ) {
+ $('#post-<?php echo $post_id; ?>')
+ .css( {backgroundColor:'#FF7979'} )
+ .fadeOut(350, function() {
+ $(this).remove();
+ $('.subsubsub').html(r);
+ });
+ });
+ });
+
+ $('#feedback-trash-<?php echo $post_id; ?>').click(function(e) {
+ e.preventDefault();
+ $.post(ajaxurl, {
+ action: 'grunion_ajax_spam',
+ post_id: '<?php echo $post_id; ?>',
+ make_it: 'trash',
+ sub_menu: jQuery('.subsubsub .current').attr('href'),
+ _ajax_nonce: '<?php echo wp_create_nonce( 'grunion-post-status-' . $post_id ); ?>'
+ },
+ function(r) {
+ $('#post-<?php echo $post_id; ?>')
+ .css({backgroundColor: '#FF7979'})
+ .fadeOut(350, function() {
+ $(this).remove();
+ $('.subsubsub').html(r);
+ });
+ }
+ );
+ });
+});
+</script>
+
+<?php
+ } elseif ( $post->post_status == 'spam' ) {
+ echo '<span class="unspam unapprove" id="feedback-ham-' . $post_id;
+ echo '"><a title="';
+ echo __( 'Mark this message as NOT spam', 'jetpack' );
+ echo '" href="">Not Spam</a></span>';
+ echo ' | ';
+
+ echo "<span class='delete' id='feedback-trash-" . $post_id;
+ echo "'> <a class='submitdelete' title='";
+ echo esc_attr( __( 'Delete this item permanently', 'jetpack' ) );
+ echo "' href='" . get_delete_post_link( $post->ID, '', true );
+ echo "'>" . __( 'Delete Permanently', 'jetpack' ) . "</a></span>";
+?>
+
+<script>
+jQuery(document).ready( function($) {
+ $('#feedback-ham-<?php echo $post_id; ?>').click( function(e) {
+ e.preventDefault();
+ $.post( ajaxurl, {
+ action: 'grunion_ajax_spam',
+ post_id: '<?php echo $post_id; ?>',
+ make_it: 'ham',
+ sub_menu: jQuery('.subsubsub .current').attr('href'),
+ _ajax_nonce: '<?php echo wp_create_nonce( 'grunion-post-status-' . $post_id ); ?>'
+ },
+ function( r ) {
+ $('#post-<?php echo $post_id; ?>')
+ .css( {backgroundColor:'#59C859'} )
+ .fadeOut(350, function() {
+ $(this).remove();
+ $('.subsubsub').html(r);
+ });
+ });
+ });
+});
+</script>
+
+<?php
+ }
+ break;
+
+ case 'feedback_date':
+ echo get_the_date( __( 'Y-m-d @ g:i:s A', 'jetpack' ) );
+ break;
+ }
+}
+
+function grunion_esc_attr( $attr ) {
+ $out = esc_attr( $attr );
+ // we also have to entity-encode square brackets so they don't interfere with the shortcode parser
+ // FIXME: do this better - just stripping out square brackets for now since they mysteriously keep reappearing
+ $out = str_replace( '[', '', $out );
+ $out = str_replace( ']', '', $out );
+ return $out;
+}
+
+function grunion_sort_objects( $a, $b ) {
+ if ( isset($a['order']) && isset($b['order']) )
+ return $a['order'] - $b['order'];
+ return 0;
+}
+
+// take an array of field types from the form builder, and construct a shortcode form
+// returns both the shortcode form, and HTML markup representing a preview of the form
+function grunion_ajax_shortcode() {
+ check_ajax_referer( 'grunion_shortcode' );
+
+ $atts = '';
+ if ( trim( $_POST['subject'] ) )
+ $atts .= ' subject="'.grunion_esc_attr($_POST['subject']).'"';
+ if ( trim( $_POST['to'] ) )
+ $atts .= ' to="'.grunion_esc_attr($_POST['to']).'"';
+
+ $shortcode = '[contact-form'.$atts.']';
+ $shortcode .= "\n";
+ if ( is_array( $_POST['fields'] ) ) {
+ usort( $_POST['fields'], 'grunion_sort_objects' );
+ foreach ( $_POST['fields'] as $field ) {
+ $req = $opts = '';
+ if ( $field['required'] == 'true' )
+ $req = ' required="true"';
+ if ( isset( $field['options'] ) && $field['options'] ) {
+ $opts = ' options="';
+ foreach ( $field['options'] as $option ) {
+ $option = wp_kses( $option, array() );
+ $option = grunion_esc_attr( $option );
+
+ # we need to be very specific about how we
+ # encode these values
+ $option = str_replace( ',', '&#x002c;', $option );
+ $option = str_replace( '"', '&#x0022;', $option );
+ $option = str_replace( "'", '&#x0027;', $option );
+ $option = str_replace( '&', '&#x0026;', $option );
+
+ $opts .= $option . ',';
+ }
+ $opts = rtrim( $opts, ',' ) . '"';
+ }
+
+ $field['label'] = wp_kses( $field['label'], array() );
+ $field['label'] = str_replace( '"', '&#x0022;', $field['label'] );
+
+ $shortcode .= '[contact-field label="'. $field['label'] .'" type="'.grunion_esc_attr($field['type']).'"' . $req . $opts .' /]'."\n";
+ }
+ }
+ $shortcode .= '[/contact-form]';
+
+ die( "\n$shortcode\n" );
+}
+
+// takes a post_id, extracts the contact-form shortcode from that post (if there is one), parses it,
+// and constructs a json object representing its contents and attributes
+function grunion_ajax_shortcode_to_json() {
+ global $post, $grunion_form;
+
+ check_ajax_referer( 'grunion_shortcode_to_json' );
+ if ( isset( $_POST['content'] ) && is_numeric( $_POST['post_id'] ) ) {
+ $content = stripslashes( $_POST['content'] );
+ $post = get_post( $_POST['post_id'] );
+ // does it look like a post with a [contact-form] already?
+ if ( strpos( $content, '[contact-form' ) !== false ) {
+ $out = do_shortcode($content);
+ global $contact_form_fields;
+ if ( is_array($contact_form_fields) && !empty($contact_form_fields) ) {
+ foreach ( $contact_form_fields as $field_id => $field ) {
+ # need to dig deeper on select field options
+ if ( preg_match( "|^(.*)\-select$|", $field_id ) ) {
+ foreach ( (array) $field['options'] as $opt_i => $opt ) {
+ $contact_form_fields[$field_id]['options'][$opt_i] = html_entity_decode( $opt );
+ }
+ }
+ $contact_form_fields[$field_id]['label'] = html_entity_decode( $contact_form_fields[$field_id]['label'] );
+ $contact_form_fields[$field_id]['label'] = wp_kses( $contact_form_fields[$field_id]['label'], array() );
+ }
+
+ $out = array( 'fields' => $contact_form_fields, 'to' => $grunion_form->to, 'subject' => $grunion_form->subject );
+ die( json_encode( $out ) );
+ }
+ }
+ die( '' );
+ }
+
+ die( -1 );
+}
+
+
+add_action( 'wp_ajax_grunion_shortcode', 'grunion_ajax_shortcode' );
+add_action( 'wp_ajax_grunion_shortcode_to_json', 'grunion_ajax_shortcode_to_json' );
+
+
+// process row-action spam/not spam clicks
+add_action( 'wp_ajax_grunion_ajax_spam', 'grunion_ajax_spam' );
+function grunion_ajax_spam() {
+ global $wpdb;
+
+ if ( empty( $_POST['make_it'] ) )
+ return;
+
+ $post_id = (int) $_POST['post_id'];
+ check_ajax_referer( 'grunion-post-status-' . $post_id );
+ if ( !current_user_can("edit_page", $post_id) )
+ wp_die( __( 'You are not allowed to manage this item.', 'jetpack' ) );
+
+ require_once dirname( __FILE__ ) . '/grunion-contact-form.php';
+
+ $current_menu = '';
+ if ( preg_match( '|post_type=feedback|', $_POST['sub_menu'] ) ) {
+ if ( preg_match( '|post_status=spam|', $_POST['sub_menu'] ) )
+ $current_menu = 'spam';
+ else if ( preg_match( '|post_status=trash|', $_POST['sub_menu'] ) )
+ $current_menu = 'trash';
+ else
+ $current_menu = 'messages';
+
+ }
+
+ $post = get_post( $post_id );
+ $post_type_object = get_post_type_object( $post->post_type );
+ $akismet_values = get_post_meta( $post_id, '_feedback_akismet_values', TRUE );
+ if ( $_POST['make_it'] == 'spam' ) {
+ $post->post_status = 'spam';
+ $status = wp_insert_post( $post );
+ wp_transition_post_status( 'spam', 'publish', $post );
+ do_action( 'contact_form_akismet', 'spam', $akismet_values );
+ } elseif ( $_POST['make_it'] == 'ham' ) {
+ $post->post_status = 'publish';
+ $status = wp_insert_post( $post );
+ wp_transition_post_status( 'publish', 'spam', $post );
+ do_action( 'contact_form_akismet', 'spam', $akismet_values );
+
+ // resend the original email
+ $email = get_post_meta( $post_id, '_feedback_email', TRUE );
+ wp_mail( $email['to'], $email['subject'], $email['message'], $email['headers'] );
+ } elseif( $_POST['make_it'] == 'publish' ) {
+ if ( !current_user_can($post_type_object->cap->delete_post, $post_id) )
+ wp_die( __( 'You are not allowed to move this item out of the Trash.', 'jetpack' ) );
+
+ if ( ! wp_untrash_post($post_id) )
+ wp_die( __( 'Error in restoring from Trash.', 'jetpack' ) );
+
+ } elseif( $_POST['make_it'] == 'trash' ) {
+ if ( !current_user_can($post_type_object->cap->delete_post, $post_id) )
+ wp_die( __( 'You are not allowed to move this item to the Trash.', 'jetpack' ) );
+
+ if ( ! wp_trash_post($post_id) )
+ wp_die( __( 'Error in moving to Trash.', 'jetpack' ) );
+
+ }
+
+ $sql = "
+ SELECT post_status,
+ COUNT( * ) AS post_count
+ FROM `{$wpdb->posts}`
+ WHERE post_type = 'feedback'
+ GROUP BY post_status
+ ";
+ $status_count = (array) $wpdb->get_results( $sql, ARRAY_A );
+
+ $status = array();
+ $status_html = '';
+ foreach ( $status_count as $i => $row ) {
+ $status[$row['post_status']] = $row['post_count'];
+ }
+
+ if ( isset( $status['publish'] ) ) {
+ $status_html .= '<li><a href="edit.php?post_type=feedback"';
+ if ( $current_menu == 'messages' )
+ $status_html .= ' class="current"';
+
+ $status_html .= '>' . __( 'Messages', 'jetpack' ) . ' <span class="count">';
+ $status_html .= '(' . number_format( $status['publish'] ) . ')';
+ $status_html .= '</span></a> |</li>';
+ }
+
+ if ( isset( $status['trash'] ) ) {
+ $status_html .= '<li><a href="edit.php?post_status=trash&amp;post_type=feedback"';
+ if ( $current_menu == 'trash' )
+ $status_html .= ' class="current"';
+
+ $status_html .= '>' . __( 'Trash', 'jetpack' ) . ' <span class="count">';
+ $status_html .= '(' . number_format( $status['trash'] ) . ')';
+ $status_html .= '</span></a>';
+ if ( isset( $status['spam'] ) )
+ $status_html .= ' |';
+ $status_html .= '</li>';
+ }
+
+ if ( isset( $status['spam'] ) ) {
+ $status_html .= '<li><a href="edit.php?post_status=spam&amp;post_type=feedback"';
+ if ( $current_menu == 'spam' )
+ $status_html .= ' class="current"';
+
+ $status_html .= '>' . __( 'Spam', 'jetpack' ) . ' <span class="count">';
+ $status_html .= '(' . number_format( $status['spam'] ) . ')';
+ $status_html .= '</span></a></li>';
+ }
+
+ echo $status_html;
+ exit;
+}
diff --git a/plugins/jetpack/modules/contact-form/css/grunion.css b/plugins/jetpack/modules/contact-form/css/grunion.css
new file mode 100644
index 00000000..a8f1651d
--- /dev/null
+++ b/plugins/jetpack/modules/contact-form/css/grunion.css
@@ -0,0 +1,9 @@
+.textwidget input[type='text'], .textwidget textarea { width: 100% !important; }
+.contact-form .clear-form { clear: both; }
+.contact-form input[type='text'] { width: 300px; margin-bottom: 13px; }
+.contact-form select { margin-bottom: 13px; }
+.contact-form textarea { height: 200px; width: 80%; float: none; margin-bottom: 13px; }
+.contact-form input[type='radio'], .contact-form input[type='checkbox'] { float: none; margin-bottom: 13px; }
+.contact-form label { margin-bottom: 3px; float: none; font-weight: bold; display: block; }
+.contact-form label.checkbox, .contact-form label.radio { margin-bottom: 3px; float: none; font-weight: bold; display: inline-block; }
+.contact-form label span { color: #AAA; margin-left: 4px; font-weight: normal; } \ No newline at end of file
diff --git a/plugins/jetpack/modules/contact-form/grunion-contact-form.php b/plugins/jetpack/modules/contact-form/grunion-contact-form.php
new file mode 100644
index 00000000..af81871a
--- /dev/null
+++ b/plugins/jetpack/modules/contact-form/grunion-contact-form.php
@@ -0,0 +1,799 @@
+<?php
+
+/*
+Plugin Name: Grunion Contact Form
+Description: Add a contact form to any post, page or text widget. Emails will be sent to the post's author by default, or any email address you choose. As seen on WordPress.com.
+Plugin URI: http://automattic.com/#
+AUthor: Automattic, Inc.
+Author URI: http://automattic.com/
+Version: 2.3
+License: GPLv2 or later
+*/
+
+define( 'GRUNION_PLUGIN_DIR', plugin_dir_path( __FILE__ ) );
+define( 'GRUNION_PLUGIN_URL', plugin_dir_url( __FILE__ ) );
+
+if ( is_admin() )
+ require_once GRUNION_PLUGIN_DIR . '/admin.php';
+
+// take the content of a contact-form shortcode and parse it into a list of field types
+function contact_form_parse( $content ) {
+ // first parse all the contact-field shortcodes into an array
+ global $contact_form_fields, $grunion_form;
+ $contact_form_fields = array();
+
+ if ( empty( $_REQUEST['action'] ) || $_REQUEST['action'] != 'grunion_shortcode_to_json' ) {
+ wp_print_styles( 'grunion.css' );
+ }
+
+ $out = do_shortcode( $content );
+
+ if ( empty($contact_form_fields) || !is_array($contact_form_fields) ) {
+ // default form: same as the original Grunion form
+ $default_form = '
+ [contact-field label="'.__( 'Name', 'jetpack' ).'" type="name" required="true" /]
+ [contact-field label="'.__( 'Email', 'jetpack' ).'" type="email" required="true" /]
+ [contact-field label="'.__( 'Website', 'jetpack' ).'" type="url" /]';
+ if ( 'yes' == strtolower($grunion_form->show_subject) ) {
+ $default_form .= '
+ [contact-field label="'.__( 'Subject', 'jetpack' ).'" type="subject" /]';
+ }
+ $default_form .= '
+ [contact-field label="'.__( 'Message', 'jetpack' ).'" type="textarea" /]';
+
+ $out = do_shortcode( $default_form );
+ }
+
+ return $out;
+}
+
+function contact_form_render_field( $field ) {
+ global $contact_form_last_id, $contact_form_errors, $contact_form_fields, $current_user, $user_identity;
+
+ $r = '';
+
+ $field_id = $field['id'];
+ if ( isset($_POST[ $field_id ]) ) {
+ $field_value = stripslashes( $_POST[ $field_id ] );
+ } elseif ( is_user_logged_in() ) {
+ // Special defaults for logged-in users
+ if ( $field['type'] == 'email' )
+ $field_value = $current_user->data->user_email;
+ elseif ( $field['type'] == 'name' )
+ $field_value = $user_identity;
+ elseif ( $field['type'] == 'url' )
+ $field_value = $current_user->data->user_url;
+ else
+ $field_value = $field['default'];
+ } else {
+ $field_value = $field['default'];
+ }
+
+ $field_value = wp_kses($field_value, array());
+
+ $field['label'] = html_entity_decode( $field['label'] );
+ $field['label'] = wp_kses( $field['label'], array() );
+
+ if ( $field['type'] == 'email' ) {
+ $r .= "\n<div>\n";
+ $r .= "\t\t<label for='".esc_attr($field_id)."' class='grunion-field-label ".esc_attr($field['type']) . ( contact_form_is_error($field_id) ? ' form-error' : '' ) . "'>" . htmlspecialchars( $field['label'] ) . ( $field['required'] ? '<span>'. __( "(required)", 'jetpack' ) . '</span>' : '' ) . "</label>\n";
+ $r .= "\t\t<input type='text' name='".esc_attr($field_id)."' id='".esc_attr($field_id)."' value='".esc_attr($field_value)."' class='".esc_attr($field['type'])."'/>\n";
+ $r .= "\t</div>\n";
+ } elseif ( $field['type'] == 'textarea' ) {
+ $r .= "\n<div>\n";
+ $r .= "\t\t<label class='".esc_attr($field['type']) . ( contact_form_is_error($field_id) ? ' form-error' : '' ) . "' for='contact-form-comment-" . esc_attr( $field_id ) . "'>" . htmlspecialchars( $field['label'] ) . ( $field['required'] ? '<span>'. __( "(required)", 'jetpack' ) . '</span>' : '' ) . "</label>\n";
+ $r .= "\t\t<textarea name='".esc_attr($field_id)."' id='contact-form-comment-".esc_attr($field_id)."' rows='20'>".htmlspecialchars($field_value)."</textarea>\n";
+ $r .= "\t</div>\n";
+ } elseif ( $field['type'] == 'radio' ) {
+ $r .= "\t<div><label class='". ( contact_form_is_error($field_id) ? ' form-error' : '' ) . "'>" . htmlspecialchars( $field['label'] ) . ( $field['required'] ? '<span>'. __( "(required)", 'jetpack' ) . '</span>' : '' ) . "</label>\n";
+ foreach ( $field['options'] as $option ) {
+ $r .= "\t\t<label class='" . esc_attr( $field['type'] ) . ( contact_form_is_error( $field_id ) ? ' form-error' : '' ) . "'>";
+ $r .= "<input type='radio' name='".esc_attr($field_id)."' value='".esc_attr($option)."' class='".esc_attr($field['type'])."' ".( $option == $field_value ? "checked='checked' " : "")." /> ";
+ $r .= htmlspecialchars( $option ) . "</label>\n";
+ $r .= "\t\t<div class='clear-form'></div>\n";
+ }
+ $r .= "\t\t</div>\n";
+ } elseif ( $field['type'] == 'checkbox' ) {
+ $r .= "\t<div>\n";
+ $r .= "\t\t<label class='".esc_attr($field['type']) . ( contact_form_is_error($field_id) ? ' form-error' : '' ) . "'>\n";
+ $r .= "\t\t<input type='checkbox' name='".esc_attr($field_id)."' value='".__( 'Yes', 'jetpack' )."' class='".esc_attr($field['type'])."' ".( $field_value ? "checked='checked' " : "")." /> \n";
+ $r .= "\t\t". htmlspecialchars( $field['label'] ) . ( $field['required'] ? '<span>'. __( "(required)", 'jetpack' ) . '</span>' : '' ) . "</label>\n";
+ $r .= "\t\t<div class='clear-form'></div>\n";
+ $r .= "\t</div>\n";
+ } elseif ( $field['type'] == 'select' ) {
+ $r .= "\n<div>\n";
+ $r .= "\t\t<label for='".esc_attr($field_id)."' class='".esc_attr($field['type']) . ( contact_form_is_error($field_id) ? ' form-error' : '' ) . "'>" . htmlspecialchars( $field['label'] ) . ( $field['required'] ? '<span>'. __( "(required)", 'jetpack' ) . '</span>' : '' ) . "</label>\n";
+ $r .= "\t<select name='".esc_attr($field_id)."' id='".esc_attr($field_id)."' value='".esc_attr($field_value)."' class='".esc_attr($field['type'])."'/>\n";
+ foreach ( $field['options'] as $option ) {
+ $option = html_entity_decode( $option );
+ $option = wp_kses( $option, array() );
+ $r .= "\t\t<option".( $option == $field_value ? " selected='selected'" : "").">". esc_html( $option ) ."</option>\n";
+ }
+ $r .= "\t</select>\n";
+ $r .= "\t</div>\n";
+ } else {
+ // default: text field
+ // note that any unknown types will produce a text input, so we can use arbitrary type names to handle
+ // input fields like name, email, url that require special validation or handling at POST
+ $r .= "\n<div>\n";
+ $r .= "\t\t<label for='".esc_attr($field_id)."' class='".esc_attr($field['type']) . ( contact_form_is_error($field_id) ? ' form-error' : '' ) . "'>" . htmlspecialchars( $field['label'] ) . ( $field['required'] ? '<span>'. __( "(required)", 'jetpack' ) . '</span>' : '' ) . "</label>\n";
+ $r .= "\t\t<input type='text' name='".esc_attr($field_id)."' id='".esc_attr($field_id)."' value='".esc_attr($field_value)."' class='".esc_attr($field['type'])."'/>\n";
+ $r .= "\t</div>\n";
+ }
+
+ return $r;
+}
+
+function contact_form_validate_field( $field ) {
+ global $contact_form_last_id, $contact_form_errors, $contact_form_values;
+
+ $field_id = $field['id'];
+ $field_value = isset($_POST[ $field_id ]) ? stripslashes($_POST[ $field_id ]) : '';
+
+ # pay special attention to required email fields
+ if ( $field['required'] && $field['type'] == 'email' ) {
+ if ( !is_email( $field_value ) ) {
+ if ( !is_wp_error( $contact_form_errors ) ) {
+ $contact_form_errors = new WP_Error();
+ }
+
+ $contact_form_errors->add( $field_id, sprintf( __( '%s requires a valid email address', 'jetpack' ), $field['label'] ) );
+ }
+ } elseif ( $field['required'] && !trim($field_value) ) {
+ if ( !is_wp_error($contact_form_errors) ) {
+ $contact_form_errors = new WP_Error();
+ }
+
+ $contact_form_errors->add( $field_id, sprintf( __( '%s is required', 'jetpack' ), $field['label'] ) );
+ }
+
+ $contact_form_values[ $field_id ] = $field_value;
+}
+
+function contact_form_is_error( $field_id ) {
+ global $contact_form_errors;
+
+ return ( is_wp_error( $contact_form_errors ) && $contact_form_errors->get_error_message( $field_id ) );
+}
+
+// generic shortcode that handles all of the major input types
+// this parses the field attributes into an array that is used by other functions for rendering, validation etc
+function contact_form_field( $atts, $content, $tag ) {
+ global $contact_form_fields, $contact_form_last_id, $grunion_form;
+
+ $field = shortcode_atts( array(
+ 'label' => null,
+ 'type' => 'text',
+ 'required' => false,
+ 'options' => array(),
+ 'id' => null,
+ 'default' => null,
+ ), $atts);
+
+ // special default for subject field
+ if ( $field['type'] == 'subject' && is_null($field['default']) )
+ $field['default'] = $grunion_form->subject;
+
+ // allow required=1 or required=true
+ if ( $field['required'] == '1' || strtolower($field['required']) == 'true' )
+ $field['required'] = true;
+ else
+ $field['required'] = false;
+
+ // parse out comma-separated options list
+ if ( !empty($field['options']) && is_string($field['options']) )
+ $field['options'] = array_map('trim', explode(',', $field['options']));
+
+ // make a unique field ID based on the label, with an incrementing number if needed to avoid clashes
+ $id = $field['id'];
+ if ( empty($id) ) {
+ $id = sanitize_title_with_dashes( $contact_form_last_id . '-' . $field['label'] );
+ $i = 0;
+ $max_tries = 12;
+ while ( isset( $contact_form_fields[ $id ] ) ) {
+ $i++;
+ $id = sanitize_title_with_dashes( $contact_form_last_id . '-' . $field['label'] . '-' . $i );
+
+ if ( $i > $max_tries ) {
+ break;
+ }
+ }
+ $field['id'] = $id;
+ }
+
+ $contact_form_fields[ $id ] = $field;
+
+ if ( isset( $_POST['contact-form-id'] ) && $_POST['contact-form-id'] == $contact_form_last_id )
+ contact_form_validate_field( $field );
+
+ return contact_form_render_field( $field );
+}
+
+add_shortcode('contact-field', 'contact_form_field');
+
+
+function contact_form_shortcode( $atts, $content ) {
+ global $post;
+
+ $default_to = get_option( 'admin_email' );
+ $default_subject = "[" . get_option( 'blogname' ) . "]";
+
+ if ( !empty( $atts['widget'] ) && $atts['widget'] ) {
+ $default_subject .= " Sidebar";
+ } elseif ( $post->ID ) {
+ $default_subject .= " ". wp_kses( $post->post_title, array() );
+ $post_author = get_userdata( $post->post_author );
+ $default_to = $post_author->user_email;
+ }
+
+ extract( shortcode_atts( array(
+ 'to' => $default_to,
+ 'subject' => $default_subject,
+ 'show_subject' => 'no', // only used in back-compat mode
+ 'widget' => 0 //This is not exposed to the user. Works with contact_form_widget_atts
+ ), $atts ) );
+
+ $widget = esc_attr( $widget );
+
+ if ( ( function_exists( 'faux_faux' ) && faux_faux() ) || is_feed() )
+ return '[contact-form]';
+
+ global $wp_query, $grunion_form, $contact_form_errors, $contact_form_values, $user_identity, $contact_form_last_id, $contact_form_message;
+
+ // used to store attributes, configuration etc for access by contact-field shortcodes
+ $grunion_form = new stdClass();
+ $grunion_form->to = $to;
+ $grunion_form->subject = $subject;
+ $grunion_form->show_subject = $show_subject;
+
+ if ( $widget )
+ $id = 'widget-' . $widget;
+ elseif ( is_singular() )
+ $id = $wp_query->get_queried_object_id();
+ else
+ $id = $GLOBALS['post']->ID;
+ if ( !$id ) // something terrible has happened
+ return '[contact-form]';
+
+ if ( $id == $contact_form_last_id )
+ return;
+ else
+ $contact_form_last_id = $id;
+
+ ob_start();
+ wp_nonce_field( 'contact-form_' . $id );
+ $nonce = ob_get_contents();
+ ob_end_clean();
+
+
+ $body = contact_form_parse( $content );
+
+ $r = "<div id='contact-form-$id'>\n";
+
+ $errors = array();
+ if ( is_wp_error( $contact_form_errors ) && $errors = (array) $contact_form_errors->get_error_codes() ) {
+ $r .= "<div class='form-error'>\n<h3>" . __( 'Error!', 'jetpack' ) . "</h3>\n<ul class='form-errors'>\n";
+ foreach ( $contact_form_errors->get_error_messages() as $message )
+ $r .= "\t<li class='form-error-message' style='color: red;'>$message</li>\n";
+ $r .= "</ul>\n</div>\n\n";
+ }
+
+ $action = apply_filters( 'grunion_contact_form_form_action', get_permalink( $post->ID ) . "#contact-form-$id", $post, $id );
+ $r .= "<form action='" . esc_url( $action ) . "' method='post' class='contact-form commentsblock'>\n";
+ $r .= $body;
+ $r .= "\t<p class='contact-submit'>\n";
+ $r .= "\t\t<input type='submit' value='" . __( "Submit &#187;", 'jetpack' ) . "' class='pushbutton-wide'/>\n";
+ $r .= "\t\t$nonce\n";
+ $r .= "\t\t<input type='hidden' name='contact-form-id' value='$id' />\n";
+ $r .= "\t</p>\n";
+ $r .= "</form>\n</div>";
+
+ if ( !isset( $_POST['contact-form-id'] ) || $_POST['contact-form-id'] != $contact_form_last_id )
+ return $r;
+
+
+ if ( is_wp_error($contact_form_errors) )
+ return $r;
+
+
+ $emails = str_replace( ' ', '', $to );
+ $emails = explode( ',', $emails );
+ foreach ( (array) $emails as $email ) {
+ if ( is_email( $email ) && ( !function_exists( 'is_email_address_unsafe' ) || !is_email_address_unsafe( $email ) ) )
+ $valid_emails[] = $email;
+ }
+
+ $to = ( $valid_emails ) ? $valid_emails : $default_to;
+
+ $message_sent = contact_form_send_message( $to, $subject, $widget );
+
+ if ( is_array( $contact_form_values ) )
+ extract( $contact_form_values );
+
+ if ( !isset( $comment_content ) )
+ $comment_content = '';
+ else
+ $comment_content = wp_kses( $comment_content, array() );
+
+
+ $r = "<div id='contact-form-$id'>\n";
+
+ $errors = array();
+ if ( is_wp_error( $contact_form_errors ) && $errors = (array) $contact_form_errors->get_error_codes() ) :
+ $r .= "<div class='form-error'>\n<h3>" . __( 'Error!', 'jetpack' ) . "</h3>\n<p>\n";
+ foreach ( $contact_form_errors->get_error_messages() as $message )
+ $r .= "\t$message<br />\n";
+ $r .= "</p>\n</div>\n\n";
+ else :
+ $r_success_message = "<h3>" . __( 'Message Sent', 'jetpack' ) . "</h3>\n\n";
+ $r_success_message .= wp_kses($contact_form_message, array('br' => array(), 'blockquote' => array()));
+
+ $r .= apply_filters( 'grunion_contact_form_success_message', $r_success_message );
+
+ $r .= "</div>";
+
+ // Reset for multiple contact forms. Hacky
+ $contact_form_values['comment_content'] = '';
+
+ return $r;
+ endif;
+
+ return $r;
+}
+add_shortcode( 'contact-form', 'contact_form_shortcode' );
+
+function contact_form_send_message( $to, $subject, $widget ) {
+ global $post;
+
+ if ( !isset( $_POST['contact-form-id'] ) )
+ return;
+
+ if ( ( $widget && 'widget-' . $widget != $_POST['contact-form-id'] ) || ( !$widget && $post->ID != $_POST['contact-form-id'] ) )
+ return;
+
+ if ( $widget )
+ check_admin_referer( 'contact-form_widget-' . $widget );
+ else
+ check_admin_referer( 'contact-form_' . $post->ID );
+
+ global $contact_form_values, $contact_form_errors, $current_user, $user_identity;
+ global $contact_form_fields, $contact_form_message;
+
+ // compact the fields and values into an array of Label => Value pairs
+ // also find values for comment_author_email and other significant fields
+ $all_values = $extra_values = array();
+
+ foreach ( $contact_form_fields as $id => $field ) {
+ if ( $field['type'] == 'email' && !isset( $comment_author_email ) ) {
+ $comment_author_email = $contact_form_values[ $id ];
+ $comment_author_email_label = $field['label'];
+ } elseif ( $field['type'] == 'name' && !isset( $comment_author ) ) {
+ $comment_author = $contact_form_values[ $id ];
+ $comment_author_label = $field['label'];
+ } elseif ( $field['type'] == 'url' && !isset( $comment_author_url ) ) {
+ $comment_author_url = $contact_form_values[ $id ];
+ $comment_author_url_label = $field['label'];
+ } elseif ( $field['type'] == 'subject' && !isset( $contact_form_subject ) ) {
+ $contact_form_subject = $contact_form_values[$id];
+ $contact_form_subject_label = $field['label'];
+ } elseif ( $field['type'] == 'textarea' && !isset( $comment_content ) ) {
+ $comment_content = $contact_form_values[ $id ];
+ $comment_content_label = $field['label'];
+ } else {
+ $extra_values[ $field['label'] ] = $contact_form_values[ $id ];
+ }
+
+ $all_values[ $field['label'] ] = $contact_form_values[ $id ];
+ }
+
+/*
+ $contact_form_values = array();
+ $contact_form_errors = new WP_Error();
+
+ list($comment_author, $comment_author_email, $comment_author_url) = is_user_logged_in() ?
+ add_magic_quotes( array( $user_identity, $current_user->data->user_email, $current_user->data->user_url ) ) :
+ array( $_POST['comment_author'], $_POST['comment_author_email'], $_POST['comment_author_url'] );
+*/
+
+ $comment_author = stripslashes( apply_filters( 'pre_comment_author_name', $comment_author ) );
+
+ if ( !empty( $comment_author_email ) ) {
+ $comment_author_email = stripslashes( apply_filters( 'pre_comment_author_email', $comment_author_email ) );
+ } else {
+ $comment_author_email = '';
+ $comment_author_email_label = '';
+ }
+
+ if ( !empty( $comment_author_url ) ) {
+ $comment_author_url = stripslashes( apply_filters( 'pre_comment_author_url', $comment_author_url ) );
+ if ( 'http://' == $comment_author_url ) {
+ $comment_author_url = '';
+ }
+ } else {
+ $comment_author_url = '';
+ $comment_author_url_label = '';
+ }
+
+ $comment_content = stripslashes( $comment_content );
+ $comment_content = trim( wp_kses( $comment_content, array() ) );
+
+ if ( empty( $contact_form_subject ) )
+ $contact_form_subject = trim( wp_kses( $subject, array() ) );
+ else
+ $contact_form_subject = trim( wp_kses( $contact_form_subject, array() ) );
+
+ $comment_author_IP = $_SERVER['REMOTE_ADDR'];
+
+ $vars = array( 'comment_author', 'comment_author_email', 'comment_author_url', 'contact_form_subject', 'comment_author_IP' );
+ foreach ( $vars as $var )
+ $$var = str_replace( array("\n", "\r" ), '', $$var ); // I don't know if it's possible to inject this
+ $vars[] = 'comment_content';
+
+ $contact_form_values = compact( $vars );
+
+ $spam = '';
+ $akismet_values = contact_form_prepare_for_akismet( $contact_form_values );
+ $is_spam = apply_filters( 'contact_form_is_spam', $akismet_values );
+ if ( is_wp_error( $is_spam ) )
+ return; // abort
+ else if ( $is_spam === TRUE )
+ $spam = '***SPAM*** ';
+
+ if ( !$comment_author )
+ $comment_author = $comment_author_email;
+
+ $to = apply_filters( 'contact_form_to', $to );
+ foreach ( (array) $to as $to_key => $to_value ) {
+ $to[$to_key] = wp_kses( $to_value, array() );
+ }
+
+ $from_email_addr = $to[0];
+ if ( !empty( $comment_author_email ) ) {
+ $from_email_addr = $comment_author_email;
+ }
+
+ $headers = 'From: ' . wp_kses( $comment_author, array() ) .
+ ' <' . wp_kses( $from_email_addr, array() ) . ">\r\n" .
+ 'Reply-To: ' . wp_kses( $from_email_addr, array() ) . "\r\n" .
+ "Content-Type: text/plain; charset=\"" . get_option('blog_charset') . "\"";
+ $subject = apply_filters( 'contact_form_subject', $contact_form_subject );
+ $subject = wp_kses( $subject, array() );
+
+ $time = date_i18n( __( 'l F j, Y \a\t g:i a', 'jetpack' ), current_time( 'timestamp' ) );
+
+ $extra_content = '';
+ $extra_content_br = '';
+
+ foreach ( $extra_values as $label => $value ) {
+ $extra_content .= $label . ': ' . trim($value) . "\n";
+ $extra_content_br .= wp_kses( $label, array() ) . ': ' . wp_kses( trim($value), array() ) . "<br />";
+ }
+
+ $message = "$comment_author_label: $comment_author\n";
+ if ( !empty( $comment_author_email ) ) {
+ $message .= "$comment_author_email_label: $comment_author_email\n";
+ }
+ if ( !empty( $comment_author_url ) ) {
+ $message .= "$comment_author_url_label: $comment_author_url\n";
+ }
+ $message .= "$comment_content_label: $comment_content\n";
+ $message .= $extra_content . "\n";
+
+ $message .= __( "Time:", 'jetpack' ) . " " . $time . "\n";
+ $message .= __( "IP Address:", 'jetpack' ) . " " . $comment_author_IP . "\n";
+ $message .= __( "Contact Form URL:", 'jetpack' ) . " " . get_permalink( $post->ID ) . "\n";
+
+
+ // Construct message that is returned to user
+ $contact_form_message = "<blockquote>";
+ if (isset($comment_author_label))
+ $contact_form_message .= wp_kses( $comment_author_label, array() ) . ": " . wp_kses( $comment_author, array() ) . "<br />";
+ if ( !empty( $comment_author_email ) )
+ $contact_form_message .= wp_kses( $comment_author_email_label, array() ) . ": " . wp_kses( $comment_author_email, array() ) . "<br />";
+ if ( !empty( $comment_author_url ) )
+ $contact_form_message .= wp_kses( $comment_author_url_label, array() ) . ": " . wp_kses( $comment_author_url, array() ) . "<br />";
+ if ( !empty( $contact_form_subject_label ) ) {
+ $contact_form_message .= wp_kses( $contact_form_subject_label, array() ) . ": " . wp_kses( $contact_form_subject, array() ) . "<br />";
+ }
+ if (isset($comment_content_label))
+ $contact_form_message .= wp_kses( $comment_content_label, array() ) . ": " . wp_kses( $comment_content, array() ) . "<br />";
+ if (isset($extra_content_br))
+ $contact_form_message .= $extra_content_br;
+ $contact_form_message .= "</blockquote><br /><br />";
+
+ if ( is_user_logged_in() ) {
+ $message .= "\n";
+ $message .= sprintf(
+ __( 'Sent by a verified %s user.', 'jetpack' ),
+ isset( $GLOBALS['current_site']->site_name ) && $GLOBALS['current_site']->site_name ? $GLOBALS['current_site']->site_name : '"' . get_option( 'blogname' ) . '"'
+ );
+ } else {
+ $message .= __( "Sent by an unverified visitor to your site.", 'jetpack' );
+ }
+
+ $message = apply_filters( 'contact_form_message', $message );
+ $message = wp_kses( $message, array() );
+
+ // keep a copy of the feedback as a custom post type
+ $feedback_mysql_time = current_time( 'mysql' );
+ $feedback_title = "{$comment_author} - {$feedback_mysql_time}";
+ $feedback_status = 'publish';
+ if ( $is_spam === TRUE )
+ $feedback_status = 'spam';
+
+ foreach ( (array) $akismet_values as $av_key => $av_value ) {
+ $akismet_values[$av_key] = wp_kses( $av_value, array() );
+ }
+
+ foreach ( (array) $all_values as $all_key => $all_value ) {
+ $all_values[$all_key] = wp_kses( $all_value, array() );
+ }
+
+ foreach ( (array) $extra_values as $ev_key => $ev_value ) {
+ $ev_values[$ev_key] = wp_kses( $ev_value, array() );
+ }
+
+ # We need to make sure that the post author is always zero for contact
+ # form submissions. This prevents export/import from trying to create
+ # new users based on form submissions from people who were logged in
+ # at the time.
+ #
+ # Unfortunately wp_insert_post() tries very hard to make sure the post
+ # author gets the currently logged in user id. That is how we ended up
+ # with this work around.
+ global $do_grunion_insert;
+ $do_grunion_insert = TRUE;
+ add_filter( 'wp_insert_post_data', 'grunion_insert_filter', 10, 2 );
+
+ $post_id = wp_insert_post( array(
+ 'post_date' => $feedback_mysql_time,
+ 'post_type' => 'feedback',
+ 'post_status' => $feedback_status,
+ 'post_parent' => $post->ID,
+ 'post_title' => wp_kses( $feedback_title, array() ),
+ 'post_content' => wp_kses($comment_content . "\n<!--more-->\n" . "AUTHOR: {$comment_author}\nAUTHOR EMAIL: {$comment_author_email}\nAUTHOR URL: {$comment_author_url}\nSUBJECT: {$contact_form_subject}\nIP: {$comment_author_IP}\n" . print_r( $all_values, TRUE ), array()), // so that search will pick up this data
+ 'post_name' => md5( $feedback_title )
+ ) );
+
+ # once insert has finished we don't need this filter any more
+ remove_filter( 'wp_insert_post_data', 'grunion_insert_filter' );
+ $do_grunion_insert = FALSE;
+
+ update_post_meta( $post_id, '_feedback_author', wp_kses( $comment_author, array() ) );
+ update_post_meta( $post_id, '_feedback_author_email', wp_kses( $comment_author_email, array() ) );
+ update_post_meta( $post_id, '_feedback_author_url', wp_kses( $comment_author_url, array() ) );
+ update_post_meta( $post_id, '_feedback_subject', wp_kses( $contact_form_subject, array() ) );
+ update_post_meta( $post_id, '_feedback_ip', wp_kses( $comment_author_IP, array() ) );
+ update_post_meta( $post_id, '_feedback_contact_form_url', wp_kses( get_permalink( $post->ID ), array() ) );
+ update_post_meta( $post_id, '_feedback_all_fields', $all_values );
+ update_post_meta( $post_id, '_feedback_extra_fields', $extra_values );
+ update_post_meta( $post_id, '_feedback_akismet_values', $akismet_values );
+ update_post_meta( $post_id, '_feedback_email', array( 'to' => $to, 'subject' => $subject, 'message' => $message, 'headers' => $headers ) );
+
+ do_action( 'grunion_pre_message_sent', $post_id, $all_values, $extra_values );
+
+ # schedule deletes of old spam feedbacks
+ if ( !wp_next_scheduled( 'grunion_scheduled_delete' ) ) {
+ wp_schedule_event( time() + 250, 'daily', 'grunion_scheduled_delete' );
+ }
+
+ if ( $is_spam !== TRUE )
+ return wp_mail( $to, "{$spam}{$subject}", $message, $headers );
+ elseif ( apply_filters( 'grunion_still_email_spam', FALSE ) == TRUE )
+ return wp_mail( $to, "{$spam}{$subject}", $message, $headers );
+
+}
+
+// populate an array with all values necessary to submit a NEW comment to Akismet
+// note that this includes the current user_ip etc, so this should only be called when accepting a new item via $_POST
+function contact_form_prepare_for_akismet( $form ) {
+
+ $form['comment_type'] = 'contact_form';
+ $form['user_ip'] = preg_replace( '/[^0-9., ]/', '', $_SERVER['REMOTE_ADDR'] );
+ $form['user_agent'] = $_SERVER['HTTP_USER_AGENT'];
+ $form['referrer'] = $_SERVER['HTTP_REFERER'];
+ $form['blog'] = get_option( 'home' );
+
+ $ignore = array( 'HTTP_COOKIE' );
+
+ foreach ( $_SERVER as $k => $value )
+ if ( !in_array( $k, $ignore ) && is_string( $value ) )
+ $form["$k"] = $value;
+
+ return $form;
+}
+
+// submit an array to Akismet. If you're accepting a new item via $_POST, run it through contact_form_prepare_for_akismet() first
+function contact_form_is_spam_akismet( $form ) {
+ if ( !function_exists( 'akismet_http_post' ) )
+ return false;
+
+ global $akismet_api_host, $akismet_api_port;
+
+ $query_string = '';
+ foreach ( array_keys( $form ) as $k )
+ $query_string .= $k . '=' . urlencode( $form[$k] ) . '&';
+
+ $response = akismet_http_post( $query_string, $akismet_api_host, '/1.1/comment-check', $akismet_api_port );
+ $result = false;
+ if ( 'true' == trim( $response[1] ) ) // 'true' is spam
+ $result = true;
+ return apply_filters( 'contact_form_is_spam_akismet', $result, $form );
+}
+
+// submit a comment as either spam or ham
+// $as should be a string (either 'spam' or 'ham'), $form should be the comment array
+function contact_form_akismet_submit( $as, $form ) {
+ global $akismet_api_host, $akismet_api_port;
+
+ if ( !in_array( $as, array( 'ham', 'spam' ) ) )
+ return false;
+
+ $query_string = '';
+ foreach ( array_keys( $form ) as $k )
+ $query_string .= $k . '=' . urlencode( $form[$k] ) . '&';
+
+ $response = akismet_http_post( $query_string, $akismet_api_host, '/1.1/submit-'.$as, $akismet_api_port );
+ return trim( $response[1] );
+}
+
+function contact_form_widget_atts( $text ) {
+ static $widget = 0;
+
+ $widget++;
+
+ return preg_replace( '/\[contact-form([^a-zA-Z_-])/', '[contact-form widget="' . $widget . '"\\1', $text );
+}
+add_filter( 'widget_text', 'contact_form_widget_atts', 0 );
+
+function contact_form_widget_shortcode_hack( $text ) {
+ if ( !preg_match( '/\[contact-form([^a-zA-Z_-])/', $text ) ) {
+ return $text;
+ }
+
+ $old = $GLOBALS['shortcode_tags'];
+ remove_all_shortcodes();
+ add_shortcode( 'contact-form', 'contact_form_shortcode' );
+ add_shortcode( 'contact-field', 'contact_form_field' );
+ $text = do_shortcode( $text );
+ $GLOBALS['shortcode_tags'] = $old;
+ return $text;
+}
+
+function contact_form_init() {
+ if ( function_exists( 'akismet_http_post' ) ) {
+ add_filter( 'contact_form_is_spam', 'contact_form_is_spam_akismet', 10 );
+ add_action( 'contact_form_akismet', 'contact_form_akismet_submit', 10, 2 );
+ }
+ if ( !has_filter( 'widget_text', 'do_shortcode' ) )
+ add_filter( 'widget_text', 'contact_form_widget_shortcode_hack', 5 );
+
+ // custom post type we'll use to keep copies of the feedback items
+ register_post_type( 'feedback', array(
+ 'labels' => array(
+ 'name' => __( 'Feedbacks', 'jetpack' ),
+ 'singular_name' => __( 'Feedback', 'jetpack' ),
+ 'search_items' => __( 'Search Feedback', 'jetpack' ),
+ 'not_found' => __( 'No feedback found', 'jetpack' ),
+ 'not_found_in_trash' => __( 'No feedback found', 'jetpack' )
+ ),
+ 'menu_icon' => GRUNION_PLUGIN_URL . '/images/grunion-menu.png',
+ 'show_ui' => TRUE,
+ 'show_in_admin_bar' => FALSE,
+ 'public' => FALSE,
+ 'rewrite' => FALSE,
+ 'query_var' => FALSE,
+ 'capability_type' => 'page'
+ ) );
+
+ register_post_status( 'spam', array(
+ 'label' => 'Spam',
+ 'public' => FALSE,
+ 'exclude_from_search' => TRUE,
+ 'show_in_admin_all_list' => FALSE,
+ 'label_count' => _n_noop( 'Spam <span class="count">(%s)</span>', 'Spam <span class="count">(%s)</span>', 'jetpack' ),
+ 'protected' => TRUE,
+ '_builtin' => FALSE
+ ) );
+
+ /* Can be dequeued by placing the following in wp-content/themes/yourtheme/functions.php
+ *
+ * function remove_grunion_style() {
+ * wp_deregister_style('grunion.css');
+ * }
+ * add_action('wp_print_styles', 'remove_grunion_style');
+ */
+
+ wp_register_style('grunion.css', GRUNION_PLUGIN_URL . 'css/grunion.css');
+}
+add_action( 'init', 'contact_form_init' );
+
+/**
+ * Add a contact form button to the post composition screen
+ */
+add_action( 'media_buttons', 'grunion_media_button', 999 );
+function grunion_media_button( ) {
+ global $post_ID, $temp_ID;
+ $iframe_post_id = (int) (0 == $post_ID ? $temp_ID : $post_ID);
+ $title = esc_attr( __( 'Add a custom form', 'jetpack' ) );
+ $plugin_url = esc_url( GRUNION_PLUGIN_URL );
+ $site_url = admin_url( "/admin-ajax.php?post_id=$iframe_post_id&amp;grunion=form-builder&amp;action=grunion_form_builder&amp;TB_iframe=true&amp;width=768" );
+
+ echo '<a href="' . $site_url . '&id=add_form" class="thickbox" title="' . $title . '"><img src="' . $plugin_url . '/images/grunion-form.png" alt="' . $title . '" width="13" height="12" /></a>';
+}
+
+
+if ( !empty( $_GET['grunion'] ) && $_GET['grunion'] == 'form-builder' ) {
+ add_action( 'parse_request', 'parse_wp_request' );
+ add_action( 'wp_ajax_grunion_form_builder', 'parse_wp_request' );
+}
+
+function parse_wp_request( $wp ) {
+ display_form_view( );
+ exit;
+}
+
+function display_form_view( ) {
+ require_once GRUNION_PLUGIN_DIR . 'grunion-form-view.php';
+}
+
+function menu_alter() {
+ echo '
+ <style>
+ #menu-posts-feedback .wp-menu-image img { display: none; }
+ #adminmenu .menu-icon-feedback:hover div.wp-menu-image, #adminmenu .menu-icon-feedback.wp-has-current-submenu div.wp-menu-image, #adminmenu .menu-icon-feedback.current div.wp-menu-image { background: url("' .GRUNION_PLUGIN_URL . '/images/grunion-menu-hover.png") no-repeat 6px 7px !important; }
+ #adminmenu .menu-icon-feedback div.wp-menu-image, #adminmenu .menu-icon-feedback div.wp-menu-image, #adminmenu .menu-icon-feedback div.wp-menu-image { background: url("' . GRUNION_PLUGIN_URL . '/images/grunion-menu.png") no-repeat 6px 7px !important; }
+ </style>';
+}
+
+add_action('admin_head', 'menu_alter');
+
+function grunion_insert_filter( $data, $postarr ) {
+ global $do_grunion_insert;
+
+ if ( $do_grunion_insert === TRUE ) {
+ if ( $data['post_type'] == 'feedback' ) {
+ if ( $postarr['post_type'] == 'feedback' ) {
+ $data['post_author'] = 0;
+ }
+ }
+ }
+
+ return $data;
+}
+
+add_action( 'grunion_scheduled_delete', 'grunion_delete_old_spam' );
+function grunion_delete_old_spam() {
+ global $wpdb;
+
+ $grunion_delete_limit = 100;
+
+ $now_gmt = current_time( 'mysql', 1 );
+ $sql = $wpdb->prepare( "
+ SELECT `ID`
+ FROM $wpdb->posts
+ WHERE DATE_SUB( %s, INTERVAL 15 DAY ) > `post_date_gmt`
+ AND `post_type` = 'feedback'
+ AND `post_status` = 'spam'
+ LIMIT %d
+ ", $now_gmt, $grunion_delete_limit );
+ $post_ids = $wpdb->get_col( $sql );
+
+ foreach ( (array) $post_ids as $post_id ) {
+ # force a full delete, skip the trash
+ wp_delete_post( $post_id, TRUE );
+ }
+
+ # Arbitrary check points for running OPTIMIZE
+ # nothing special about 5000 or 11
+ # just trying to periodically recover deleted rows
+ $random_num = mt_rand( 1, 5000 );
+ if ( apply_filters( 'grunion_optimize_table', ( $random_number == 11 ) ) ) {
+ $wpdb->query( "OPTIMIZE TABLE $wpdb->posts" );
+ }
+
+ # if we hit the max then schedule another run
+ if ( count( $post_ids ) >= $grunion_delete_limit ) {
+ wp_schedule_single_event( time() + 700, 'grunion_scheduled_delete' );
+ }
+}
diff --git a/plugins/jetpack/modules/contact-form/grunion-form-view.php b/plugins/jetpack/modules/contact-form/grunion-form-view.php
new file mode 100644
index 00000000..26577bb9
--- /dev/null
+++ b/plugins/jetpack/modules/contact-form/grunion-form-view.php
@@ -0,0 +1,203 @@
+<?php
+/**
+ * Template for form builder
+ */
+
+wp_register_script( 'grunion', GRUNION_PLUGIN_URL . 'js/grunion.js', array( 'jquery-ui-sortable', 'jquery-ui-draggable' ), JETPACK__VERSION );
+wp_localize_script( 'grunion', 'GrunionFB_i18n', array(
+ 'nameLabel' => esc_attr( _x( 'Name', 'Label for HTML form "Name" field in contact form builder', 'jetpack' ) ),
+ 'emailLabel' => esc_attr( _x( 'Email', 'Label for HTML form "Email" field in contact form builder', 'jetpack' ) ),
+ 'urlLabel' => esc_attr( _x( 'Website', 'Label for HTML form "URL/Website" field in contact form builder', 'jetpack' ) ),
+ 'commentLabel' => esc_attr( _x( 'Comment', 'Label for HTML form "Comment/Response" field in contact form builder', 'jetpack' ) ),
+ 'newLabel' => esc_attr( _x( 'New Field', 'Default label for new HTML form field in contact form builder', 'jetpack' ) ),
+ 'optionsLabel' => esc_attr( _x( 'Options', 'Label for the set of options to be included in a user-created dropdown in contact form builder', 'jetpack' ) ),
+ 'optionsLabel' => esc_attr( _x( 'Option', 'Label for an option to be included in a user-created dropdown in contact form builder', 'jetpack' ) ),
+ 'firstOptionLabel' => esc_attr( _x( 'First option', 'Default label for the first option to be included in a user-created dropdown in contact form builder', 'jetpack' ) ),
+ 'problemGeneratingForm' => esc_attr( _x( "Oops, there was a problem generating your form. You'll likely need to try again.", 'error message in contact form builder', 'jetpack' ) ),
+ 'moveInstructions' => esc_attr__( "Drag up or down\nto re-arrange", 'jetpack' ),
+ 'moveLabel' => esc_attr( _x( 'move', 'Label to drag HTML form fields around to change their order in contact form builder', 'jetpack' ) ),
+ 'editLabel' => esc_attr( _x( 'edit', 'Link to edit an HTML form field in contact form builder', 'jetpack' ) ),
+ 'savedMessage' => esc_attr__( 'Saved successfully', 'jetpack' ),
+ 'requiredLabel' => esc_attr( _x( '(required)', 'This HTML form field is marked as required by the user in contact form builder', 'jetpack' ) ),
+ 'exitConfirmMessage' => esc_attr__( 'Are you sure you want to exit the form editor without saving? Any changes you have made will be lost.', 'jetpack' ),
+) );
+
+?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+<title><?php esc_html_e( 'Contact Form', 'jetpack' ); ?></title>
+<script type="text/javascript">
+ var ajaxurl = '<?php echo admin_url('admin-ajax.php'); ?>';
+ var postId = <?php echo absint( $_GET['post_id'] ); ?>;
+ var ajax_nonce_shortcode = '<?php echo wp_create_nonce( 'grunion_shortcode' ); ?>';
+ var ajax_nonce_json = '<?php echo wp_create_nonce( 'grunion_shortcode_to_json' ); ?>';
+</script>
+<?php wp_print_scripts( 'grunion' ); ?>
+<script type="text/javascript">
+ jQuery(document).ready(function () {
+ FB.ContactForm.init();
+ FB.ContactForm.resizePop();
+ });
+ jQuery(window).resize(function() {
+ setTimeout(function () { FB.ContactForm.resizePop(); }, 50);
+ });
+</script>
+<style>
+ /* Reset */
+ html { height: 100%; }
+ body, div, ul, ol, li, h1, h2, h3, h4, h5, h6, form, fieldset, legend, input, button, textarea, p, blockquote, th, td { margin: 0; padding: 0; }
+ body { background: #F9F9F9; font-family:"Lucida Grande",Verdana,Arial,"Bitstream Vera Sans",sans-serif; font-size:12px; color: #333; line-height:1.5em; height: 100%; width: 100%; padding-bottom: 20px !important; }
+ a { color: #21759B; text-decoration: none; }
+ a:hover { text-decoration: underline; text-shadow: none !important; }
+ h1 { font-size: 21px; color:#5A5A5A; font-family:Georgia,"Times New Roman",Times,serif; font-weight:normal; margin-bottom: 21px; }
+ h3 { font-size: 13px; color: #666; margin-bottom: 18px; }
+ input { width: 301px; }
+ input[type='text'] { padding: 3px 5px; margin-right: 4px; -moz-border-radius:3px; border-radius:3px; -webkit-border-radius:3px; }
+ input[type='text']:focus { border: 2px solid #80B8D9; outline: 0 !important; }
+ input[type='checkbox'], input[type='radio'] { width: auto !important; float: left; margin-top: 3px; }
+ input[type='radio'] { margin-right: 8px; }
+ input.fieldError, select.fieldError, textarea.fieldError { border: 2px solid #D56F55; }
+ img { border: none; }
+ label { color: #222; font-weight: bold; display: block; margin-bottom: 4px; }
+ label.radio { width: auto; margin: -2px 0 0 5px; }
+ label span.label-required { color: #AAA; margin-left: 4px; font-weight: normal; }
+ td { vertical-align: top; }
+ select { width: 300px; }
+ textarea { height: 100px; width: 311px; }
+ /* Core */
+ #media-upload-header { border-bottom: 1px solid #DFDFDF; font-weight:bold; margin:0; padding:3px 5px 0 5px; position:relative; background: #FFF; }
+ #sidemenu { bottom:-1px; font-size:12px; list-style:none outside none; padding-left:10px; position:relative; left:0; margin:0 5px; overflow:hidden; }
+ #sidemenu a { text-decoration:none; border-top: 1px solid #FFF; display:block; float:left; line-height:28px; padding:0 13px; outline: none; }
+ #sidemenu a.current { background-color:#F9F9F9; border-color:#DFDFDF #DFDFDF #F9F9F9; color:#D54E21; -moz-border-radius:4px 4px 0 0; border-radius:4px 4px 0 0; -webkit-border-radius:4px 4px 0 0; border-style:solid; border-width:1px; font-weight:normal; }
+ #sidemenu li { display:inline; margin-bottom:6px; line-height:200%; list-style:none outside none; margin:0; padding:0; text-align:center; white-space:nowrap; }
+ .button { background-color:#FFFFFF; background:url("<?php echo get_bloginfo('url'); ?>/wp-admin/images/white-grad.png") repeat-x scroll left top #F2F2F2; border-color:#BBBBBB; min-width:80px; text-align:center; color:#464646; text-shadow:0 1px 0 #FFFFFF; border-style:solid; border-width:1px; cursor:pointer; width: auto; font-size:11px !important; line-height:13px; padding:3px 11px; margin-top: 12px; text-decoration:none; -moz-border-radius:11px; border-radius:11px; -webkit-border-radius:11px }
+ .button-primary { background-color:#FFFFFF; font-weight: bold; background: url('<?php echo get_bloginfo('url'); ?>/wp-admin/images/button-grad-active.png') repeat-x scroll left top #21759B; border-color:#298CBA; text-align:center; color:#EAF2FA; text-shadow:0 -1px 0 rgba(0, 0, 0, 0.3); border-style:solid; border-width:1px; cursor:pointer; width: auto; font-size:11px !important; line-height:13px; padding:3px 11px; margin-top: 21px; text-decoration:none; -moz-border-radius:11px; border-radius:11px; -webkit-border-radius:11px }
+ .clear { clear: both; }
+ .fb-add-field { padding-left: 10px; }
+ .fb-add-option { margin: 0 0 14px 100px; }
+ .fb-container { margin: 21px; padding-bottom: 20px; }
+ .fb-desc, #fb-add-field { margin-top: 34px; }
+ .fb-extra-fields { margin-bottom: 2px; }
+ .fb-form-case { background: #FFF; padding: 13px; border: 1px solid #E2E2E2; width: 336px; -moz-border-radius:4px; border-radius:4px; -webkit-border-radius:4px }
+ .fb-form-case a { outline: none; }
+ .fb-form-case input[type='text'], .fb-form-case textarea { background: #E1E1E1; }
+ .fb-radio-label { display: inline-block; margin-left: 8px; float: left; width: 290px; }
+ .fb-new-fields { position: relative; border: 1px dashed #FFF; background: #FFF; padding: 4px 10px 10px; cursor: default; }
+ .fb-new-fields:hover { border: 1px dashed #BBDBEA; background: #F7FBFD; }
+ .fb-options { width: 170px !important; }
+ .fb-remove { background: url('<?php echo GRUNION_PLUGIN_URL; ?>/images/grunion-remove-field.gif') no-repeat; position: absolute; cursor: pointer !important; right: -26px; top: 27px; width: 20px; height: 23px; }
+ .fb-remove:hover { background: url('<?php echo GRUNION_PLUGIN_URL; ?>/images/grunion-remove-field-hover.gif') no-repeat; }
+ .fb-remove-small { top: 2px !important; }
+ .fb-remove-option { position: absolute; top: 1px; right: 10px; width: 20px; height: 23px; background: url('<?php echo GRUNION_PLUGIN_URL; ?>/images/grunion-remove-option.gif') no-repeat; }
+ .fb-remove-option:hover { background: url('<?php echo GRUNION_PLUGIN_URL; ?>/images/grunion-remove-option-hover.gif') no-repeat; }
+ .fb-reorder { cursor: move; position: relative; }
+ .fb-reorder:hover div { display: block !important; width: 130px !important; position: absolute; top: 0; right: 0; z-index: 200; padding: 5px 10px; color: #555; font-size: 11px; background: #FFF; border: 1px solid #CCC; -moz-border-radius:4px; border-radius:4px; -webkit-border-radius:4px; }
+ .fb-right { position: absolute; right: 0; top: 0; width: 315px; margin: 57px 21px 0 0; }
+ .fb-right .fb-new-fields { border: none; background: #F9F9F9; padding: 0; }
+ .fb-right input[type='text'] { width: 195px; margin-bottom: 14px; }
+ .fb-right label { color: #444; width: 100px; float: left; font-weight: normal; }
+ .fb-right select { width: 150px !important; margin-bottom: 14px; }
+ .fb-right textarea { margin-bottom: 13px; }
+ .fb-right p { color: #999; line-height: 19px; }
+ .fb-settings input[type='text'], .fb-settings textarea { background-image: none !important; }
+ .fb-success { position: absolute; top: -3px; right: 100px; padding: 6px 23px 4px 23px; background: #FFFFE0; font-weight: normal; border: 1px solid #E6DB55; color: #333; -moz-border-radius:4px; border-radius:4px; -webkit-border-radius:4px; }
+ .right { float: right; }
+</style>
+</head>
+
+<body>
+ <div id="media-upload-header">
+ <div id="fb-success" class="fb-success" style="display: none;"><?php esc_html_e( 'Your new field was saved successfully', 'jetpack' ); ?></div>
+ <ul id="sidemenu">
+ <li id="tab-preview"><a class="current" href=""><?php esc_html_e( 'Form builder', 'jetpack' ); ?></a></li>
+ <li id="tab-settings"><a href=""><?php esc_html_e( 'Email notifications', 'jetpack' ); ?></a></li>
+ </ul>
+ </div>
+ <div class="fb-right">
+ <div id="fb-desc" class="fb-desc">
+ <h3><?php esc_html_e( 'How does this work?', 'jetpack' ); ?></h3>
+ <p><?php esc_html_e( 'By adding a contact form, your readers will be able to submit feedback to you. All feedback is automatically scanned for spam, and the legitimate feedback will be emailed to you.', 'jetpack' ); ?></p>
+ <h3 style="margin-top: 21px;"><?php esc_html_e( 'Can I add more fields?', 'jetpack' ); ?></h3>
+ <p><?php printf(
+ esc_html( _x( 'Sure thing. %1$s to add a new text box, textarea, radio, checkbox, or dropdown field.', '%1$s = "Click here" in an HTML link', 'jetpack' ) ),
+ '<a href="#" class="fb-add-field" style="padding-left: 0;">' . esc_html__( 'Click here', 'jetpack' ) . '</a>'
+ ); ?></p>
+ <h3 style="margin-top: 21px;"><?php esc_html_e( 'Can I view my feedback within WordPress?', 'jetpack' ); ?></h3>
+ <p><?php printf(
+ esc_html( _x( 'Yep, you can read your feedback at any time by clicking the "%1$s" link in the admin menu.', '%1$s = "Feedbacks" in an HTML link', 'jetpack' ) ),
+ '<a id="fb-feedback" href="' . admin_url( 'edit.php?post_type=feedback' ) . '">' . esc_html__( 'Feedbacks', 'jetpack' ) . '</a>'
+ ); ?></p>
+ <div class="clear"></div>
+ </div>
+ <div id="fb-email-desc" class="fb-desc" style="display: none;">
+ <h3><?php esc_html_e( 'Do I need to fill this out?', 'jetpack' ); ?></h3>
+ <p><?php esc_html_e( 'Nope. However, if you&#8217;d like to modify where your feedback is sent, or the subject line you can. If you don&#8217;t make any changes here, feedback will be sent to the author of the page/post and the subject will be the name of this page/post.', 'jetpack' ); ?></p>
+ <div class="clear"></div>
+ </div>
+ <div id="fb-add-field" style="display: none;">
+ <h3><?php esc_html_e( 'Edit this new field', 'jetpack' ); ?></h3>
+
+ <label for="fb-new-label"><?php esc_html_e( 'Label', 'jetpack' ); ?></label>
+ <input type="text" id="fb-new-label" value="<?php esc_attr_e( 'New field', 'jetpack' ); ?>" />
+
+ <label for="fb-new-label"><?php esc_html_e( 'Field type', 'jetpack' ); ?></label>
+ <select id="fb-new-type">
+ <option value="checkbox"><?php esc_html_e( 'Checkbox', 'jetpack' ); ?></option>
+ <option value="select"><?php esc_html_e( 'Drop down', 'jetpack' ); ?></option>
+ <option value="email"><?php esc_html_e( 'Email', 'jetpack' ); ?></option>
+ <option value="name"><?php esc_html_e( 'Name', 'jetpack' ); ?></option>
+ <option value="radio"><?php esc_html_e( 'Radio', 'jetpack' ); ?></option>
+ <option value="text" selected="selected"><?php esc_html_e( 'Text', 'jetpack' ); ?></option>
+ <option value="textarea"><?php esc_html_e( 'Textarea', 'jetpack' ); ?></option>
+ <option value="url"><?php esc_html_e( 'Website', 'jetpack' ); ?></option>
+ </select>
+ <div class="clear"></div>
+
+ <div id="fb-options" style="display: none;">
+ <div id="fb-new-options">
+ <label for="fb-option0"><?php esc_html_e( 'Options', 'jetpack' ); ?></label>
+ <input type="text" id="fb-option0" optionid="0" value="<?php esc_attr_e( 'First option', 'jetpack' ); ?>" class="fb-options" />
+ </div>
+ <div id="fb-add-option" class="fb-add-option">
+ <a href="#" id="fb-another-option"><?php esc_html_e( 'Add another option', 'jetpack' ); ?></a>
+ </div>
+ </div>
+
+ <div class="fb-required">
+ <label for="fb-new-label"></label>
+ <input type="checkbox" id="fb-new-required" />
+ <label for="fb-new-label" class="fb-radio-label"><?php esc_html_e( 'Required?', 'jetpack' ); ?></label>
+ <div class="clear"></div>
+ </div>
+
+ <input type="hidden" id="fb-field-id" />
+ <input type="submit" class="button" value="<?php esc_attr_e( 'Save this field', 'jetpack' ); ?>" id="fb-save-field" name="save">
+ </div>
+ </div>
+ <form id="fb-preview">
+ <div id="fb-preview-form" class="fb-container">
+ <h1><?php esc_html_e( 'Here&#8217;s what your form will look like', 'jetpack' ); ?></h1>
+ <div id="sortable" class="fb-form-case">
+
+ <div id="fb-extra-fields" class="fb-extra-fields"></div>
+
+ <a href="#" id="fb-new-field" class="fb-add-field"><?php esc_html_e( 'Add a new field', 'jetpack' ); ?></a>
+ </div>
+ <input type="submit" class="button-primary" tabindex="4" value="<?php esc_attr_e( 'Add this form to my post', 'jetpack' ); ?>" id="fb-save-form" name="save">
+ </div>
+ <div id="fb-email-settings" class="fb-container" style="display: none;">
+ <h1><?php esc_html_e( 'Email settings', 'jetpack' ); ?></h1>
+ <div class="fb-form-case fb-settings">
+ <label for="fb-fieldname"><?php esc_html_e( 'Enter your email address', 'jetpack' ); ?></label>
+ <input type="text" id="fb-field-my-email" style="background: #FFF !important;" />
+
+ <label for="fb-fieldemail" style="margin-top: 14px;"><?php esc_html_e( 'What should the subject line be?', 'jetpack' ); ?></label>
+ <input type="text" id="fb-field-subject" style="background: #FFF !important;" />
+ </div>
+ <input type="submit" class="button-primary" value="<?php esc_attr_e( 'Save and go back to form builder', 'jetpack' ); ?>" id="fb-prev-form" name="save">
+ </div>
+ </form>
+</body>
+</html>
diff --git a/plugins/jetpack/modules/contact-form/images/blank-screen-akismet.png b/plugins/jetpack/modules/contact-form/images/blank-screen-akismet.png
new file mode 100644
index 00000000..3d3f13d6
--- /dev/null
+++ b/plugins/jetpack/modules/contact-form/images/blank-screen-akismet.png
Binary files differ
diff --git a/plugins/jetpack/modules/contact-form/images/blank-screen-button.png b/plugins/jetpack/modules/contact-form/images/blank-screen-button.png
new file mode 100644
index 00000000..ab03ed10
--- /dev/null
+++ b/plugins/jetpack/modules/contact-form/images/blank-screen-button.png
Binary files differ
diff --git a/plugins/jetpack/modules/contact-form/images/grunion-form.png b/plugins/jetpack/modules/contact-form/images/grunion-form.png
new file mode 100644
index 00000000..75bd6253
--- /dev/null
+++ b/plugins/jetpack/modules/contact-form/images/grunion-form.png
Binary files differ
diff --git a/plugins/jetpack/modules/contact-form/images/grunion-menu-big.png b/plugins/jetpack/modules/contact-form/images/grunion-menu-big.png
new file mode 100644
index 00000000..56ce4f42
--- /dev/null
+++ b/plugins/jetpack/modules/contact-form/images/grunion-menu-big.png
Binary files differ
diff --git a/plugins/jetpack/modules/contact-form/images/grunion-menu-hover.png b/plugins/jetpack/modules/contact-form/images/grunion-menu-hover.png
new file mode 100644
index 00000000..e876e0e5
--- /dev/null
+++ b/plugins/jetpack/modules/contact-form/images/grunion-menu-hover.png
Binary files differ
diff --git a/plugins/jetpack/modules/contact-form/images/grunion-menu.png b/plugins/jetpack/modules/contact-form/images/grunion-menu.png
new file mode 100644
index 00000000..d4e005f4
--- /dev/null
+++ b/plugins/jetpack/modules/contact-form/images/grunion-menu.png
Binary files differ
diff --git a/plugins/jetpack/modules/contact-form/images/grunion-remove-field-hover.gif b/plugins/jetpack/modules/contact-form/images/grunion-remove-field-hover.gif
new file mode 100644
index 00000000..20d9e712
--- /dev/null
+++ b/plugins/jetpack/modules/contact-form/images/grunion-remove-field-hover.gif
Binary files differ
diff --git a/plugins/jetpack/modules/contact-form/images/grunion-remove-field.gif b/plugins/jetpack/modules/contact-form/images/grunion-remove-field.gif
new file mode 100644
index 00000000..55062664
--- /dev/null
+++ b/plugins/jetpack/modules/contact-form/images/grunion-remove-field.gif
Binary files differ
diff --git a/plugins/jetpack/modules/contact-form/images/grunion-remove-option-hover.gif b/plugins/jetpack/modules/contact-form/images/grunion-remove-option-hover.gif
new file mode 100644
index 00000000..9098b065
--- /dev/null
+++ b/plugins/jetpack/modules/contact-form/images/grunion-remove-option-hover.gif
Binary files differ
diff --git a/plugins/jetpack/modules/contact-form/images/grunion-remove-option.gif b/plugins/jetpack/modules/contact-form/images/grunion-remove-option.gif
new file mode 100644
index 00000000..ec491663
--- /dev/null
+++ b/plugins/jetpack/modules/contact-form/images/grunion-remove-option.gif
Binary files differ
diff --git a/plugins/jetpack/modules/contact-form/js/grunion.js b/plugins/jetpack/modules/contact-form/js/grunion.js
new file mode 100644
index 00000000..040710bf
--- /dev/null
+++ b/plugins/jetpack/modules/contact-form/js/grunion.js
@@ -0,0 +1,734 @@
+if (!window.FB) {
+ window.FB = {};
+}
+
+GrunionFB_i18n = jQuery.extend( {
+ nameLabel: 'Name',
+ emailLabel: 'Email',
+ urlLabel: 'Website',
+ commentLabel: 'Comment',
+ newLabel: 'New Field',
+ optionsLabel: 'Options',
+ optionLabel: 'Option',
+ firstOptionLabel: 'First option',
+ problemGeneratingForm: "Oops, there was a problem generating your form. You'll likely need to try again.",
+ moveInstructions: "Drag up or down\nto re-arrange",
+ moveLabel: 'move',
+ editLabel: 'edit',
+ savedMessage: 'Saved successfully',
+ requiredLabel: '(required)',
+ exitConfirmMessage: 'Are you sure you want to exit the form editor without saving? Any changes you have made will be lost.',
+}, GrunionFB_i18n );
+
+GrunionFB_i18n.moveInstructions = GrunionFB_i18n.moveInstructions.replace( "\n", '<br />' );
+
+FB.ContactForm = function() {
+ var fbForm = { // Main object that generated shortcode via AJAX call
+ 'action' : 'grunion_shortcode',
+ '_ajax_nonce' : ajax_nonce_shortcode,
+ 'to' : '',
+ 'subject' : '',
+ 'fields' : {}
+ };
+ var defaultFields = {
+ 'name': {
+ 'label' : GrunionFB_i18n.nameLabel,
+ 'type' : 'name',
+ 'required' : true,
+ 'options' : [],
+ 'order' : '1'
+ },
+ 'email': {
+ 'label' : GrunionFB_i18n.emailLabel,
+ 'type' : 'email',
+ 'required' : true,
+ 'options' : [],
+ 'order' : '2'
+ },
+ 'url': {
+ 'label' : GrunionFB_i18n.urlLabel,
+ 'type' : 'url',
+ 'required' : false,
+ 'options' : [],
+ 'order' : '3'
+ },
+ 'comment': {
+ 'label' : GrunionFB_i18n.commentLabel,
+ 'type' : 'textarea',
+ 'required' : true,
+ 'options' : [],
+ 'order' : '4'
+ }
+ };
+ var debug = false; // will print errors to log if true
+ var grunionNewCount = 0; // increment for new fields
+ var maxNewFields = 5; // Limits number of new fields available
+ var optionsCache = {};
+ var optionsCount = 0; // increment for options
+ var shortcode;
+
+ function addField () {
+ try {
+ grunionNewCount++;
+ if (grunionNewCount <= maxNewFields) {
+ // Add to preview
+ jQuery('#fb-extra-fields').append('<div id="fb-new-field' + grunionNewCount + '" fieldid="' + grunionNewCount + '" class="fb-new-fields"><div class="fb-fields"><div id="' + grunionNewCount + '" class="fb-remove"></div><label fieldid="' + grunionNewCount + '" for="fb-field' + grunionNewCount + '"><span class="label-text">' + GrunionFB_i18n.newLabel + '</span> </label><input type="text" id="fb-field' + grunionNewCount + '" disabled="disabled" /></div></div>');
+ // Add to form object
+ fbForm.fields[grunionNewCount] = {
+ 'label' : GrunionFB_i18n.newLabel,
+ 'type' : 'text',
+ 'required' : false,
+ 'options' : [],
+ 'order' : '5'
+ };
+ if (grunionNewCount === maxNewFields) {
+ jQuery('#fb-new-field').hide();
+ }
+ // Reset form for this new field
+ optionsCount = 0;
+ optionsCache = {};
+ jQuery('#fb-new-options').html('<label for="fb-option0">' + GrunionFB_i18n.optionsLabel + '</label><input type="text" id="fb-option0" optionid="0" value="' + GrunionFB_i18n.firstOptionLabel + '" class="fb-options" />');
+ jQuery('#fb-options').hide();
+ jQuery('#fb-new-label').val( GrunionFB_i18n.newLabel );
+ jQuery('#fb-new-type').val('text');
+ jQuery('#fb-field-id').val(grunionNewCount);
+ setTimeout(function () { jQuery('#fb-new-label').focus().select(); }, 100);
+ } else {
+ jQuery('#fb-new-field').hide();
+ }
+ } catch(e) {
+ if (debug) {
+ console.log("addField(): " + e);
+ }
+ }
+ }
+ function addOption () {
+ try {
+ optionsCount++;
+ var thisId = jQuery('#fb-field-id').val();
+ var thisType = jQuery('#fb-new-type').val();
+ if (thisType === "radio") {
+ // Add to right col
+ jQuery('#fb-new-options').append('<div id="fb-option-box-' + optionsCount + '" class="fb-new-fields"><span optionid="' + optionsCount + '" class="fb-remove-option"></span><label></label><input type="text" id="fb-option' + optionsCount + '" optionid="' + optionsCount + '" value="' + GrunionFB_i18n.optionLabel + '" class="fb-options" /><div>');
+ // Add to preview
+ jQuery('#fb-new-field' + thisId + ' .fb-fields').append('<div id="fb-radio-' + thisId + '-' + optionsCount + '"><input type="radio" disabled="disabled" id="fb-field' + thisId + '" name="radio-' + thisId + '" /><span>' + GrunionFB_i18n.optionLabel + '</span><div class="clear"></div></div>');
+ } else {
+ // Add to right col
+ jQuery('#fb-new-options').append('<div id="fb-option-box-' + optionsCount + '" class="fb-new-fields"><span optionid="' + optionsCount + '" class="fb-remove-option"></span><label></label><input type="text" id="fb-option' + optionsCount + '" optionid="' + optionsCount + '" value="" class="fb-options" /><div>');
+ // Add to preview
+ jQuery('#fb-field'+ thisId).append('<option id="fb-' + thisId + '-' + optionsCount + '" value="' + thisId + '-' + optionsCount + '"></option>');
+ }
+ // Add to fbForm object
+ fbForm.fields[thisId].options[optionsCount] = "";
+ // Add focus to new field
+ jQuery('#fb-option' + optionsCount).focus().select();
+ } catch(e) {
+ if (debug) {
+ console.log("addOption(): " + e);
+ }
+ }
+ }
+ function buildPreview () {
+ try {
+ if (fbForm.to) { jQuery('#fb-field-my-email').val(fbForm.to); }
+ if (fbForm.subject) { jQuery('#fb-field-subject').val(fbForm.subject); }
+ // Loop over and add fields
+ jQuery.each(fbForm.fields, function(index, value) {
+ jQuery('#fb-extra-fields').before('<div class="fb-new-fields ui-state-default" fieldid="' + index + '" id="fb-new-field' + index + '"><div class="fb-fields"></div></div>');
+ jQuery('#fb-field-id').val(index);
+ optionsCache[index] = {};
+ optionsCache[index].options = [];
+ if (value.type === "radio" || value.type === "select") {
+ jQuery.each(value.options, function(i, value) {
+ optionsCache[index].options[i] = value;
+ });
+ }
+ updateType(value.type, value.label, value.required);
+ });
+ } catch(e) {
+ if (debug) {
+ console.log("buildPreview(): " + e);
+ }
+ }
+ }
+ function customOptions (id, thisType) {
+ try {
+ var thisOptions = '';
+ for (i=0; i<optionsCache[id].options.length; i++) {
+ if (optionsCache[id].options[i] !== undefined) {
+ if (thisType === "radio") {
+ thisOptions = thisOptions + '<div id="fb-radio-' + id + '-' + i + '"><input type="radio" id="fb-field' + id + '" name="radio-' + id + '" /><span>' + optionsCache[id].options[i] + '</span><div class="clear"></div></div>';
+ } else {
+ thisOptions = thisOptions + '<option id="fb-' + id + '-' + i + '" value="' + id + '-' + i + '">' + optionsCache[id].options[i] + '</option>';
+ }
+ }
+ }
+ return thisOptions;
+ } catch(e) {
+ if (debug) {
+ console.log("customOptions(): " + e);
+ }
+ }
+ }
+ function deleteField (that) {
+ try {
+ grunionNewCount--;
+ var thisId = that.attr("id");
+ delete fbForm.fields[thisId];
+ jQuery("#"+thisId).parent().parent().remove();
+ if (grunionNewCount <= maxNewFields) {
+ jQuery('#fb-new-field').show();
+ }
+ } catch(e) {
+ if (debug) {
+ console.log("deleteField(): " + e);
+ }
+ }
+ }
+ function editField (that) {
+ try {
+ scroll(0,0);
+ setTimeout(function () { jQuery('#fb-new-label').focus().select(); }, 100);
+ var thisId = that.parent().attr('fieldid');
+ loadFieldEditor(thisId);
+ } catch(e) {
+ if (debug) {
+ console.log("editField(): " + e);
+ }
+ }
+ }
+ function grabShortcode () {
+ try {
+ // Takes fbForm object and returns shortcode syntax
+ jQuery.post(ajaxurl, fbForm, function(response) {
+ shortcode = response;
+ });
+ } catch(e) {
+ alert( GrunionFB_i18n.problemGeneratingForm );
+ if (debug) {
+ console.log("grabShortcode(): " + e);
+ }
+ }
+ }
+ function hideDesc () {
+ jQuery('#fb-desc').hide();
+ jQuery('#fb-add-field').show();
+ }
+ function hidePopup () {
+ try {
+ // copied from wp-includes/js/thickbox/thickbox.js
+ jQuery("#TB_imageOff", window.parent.document).unbind("click");
+ jQuery("#TB_closeWindowButton", window.parent.document).unbind("click");
+ jQuery("#TB_window", window.parent.document).fadeOut("fast");
+ jQuery('#TB_window,#TB_overlay,#TB_HideSelect', window.parent.document).trigger("unload").unbind().remove();
+ jQuery("#TB_load", window.parent.document).remove();
+ if (typeof window.parent.document.body.style.maxHeight == "undefined") {//if IE 6
+ jQuery("body","html", window.parent.document).css({height: "auto", width: "auto"});
+ jQuery("html", window.parent.document).css("overflow","");
+ }
+ window.parent.document.onkeydown = "";
+ window.parent.document.onkeyup = "";
+ return false;
+ } catch(e) {
+ if (debug) {
+ console.log("hidePopup(): " + e);
+ }
+ }
+ }
+ function hideShowEditLink (whichType, that) {
+ try {
+ if (whichType === "show") {
+ // Prevents showing links twice
+ if (jQuery(".fb-edit-field").is(":visible")) {
+ jQuery(".fb-edit-field").remove();
+ }
+ that.find('label').prepend('<span class="right fb-edit-field" style="font-weight: normal;"><a href="" class="fb-reorder"><div style="display: none;">' + GrunionFB_i18n.moveInstructions + '</div>' + GrunionFB_i18n.moveLabel + '</a>&nbsp;&nbsp;<span style="color: #C7D8DE;">|</span>&nbsp;&nbsp;<a href="" class="fb-edit">' + GrunionFB_i18n.editLabel + '</a></span>');
+ } else {
+ jQuery('.fb-edit-field').remove();
+ }
+ } catch(e) {
+ if (debug) {
+ console.log("hideShowEditLink(): " + e);
+ }
+ }
+ }
+ function loadFieldEditor (id) {
+ try {
+ var thisType = fbForm.fields[id].type;
+ jQuery('#fb-options').hide();
+ // Reset hidden field ID
+ jQuery('#fb-field-id').val(id);
+ // Load label
+ jQuery('#fb-new-label').val(fbForm.fields[id].label);
+ // Load type
+ jQuery('#fb-new-type').val(fbForm.fields[id].type);
+ // Load required
+ if (fbForm.fields[id].required) {
+ jQuery('#fb-new-required').prop("checked", true);
+ } else {
+ jQuery('#fb-new-required').prop("checked", false);
+ }
+ // Load options if there are any
+ if (thisType === "select" || thisType === "radio") {
+ var thisResult = '';
+ var thisOptions = fbForm.fields[id].options;
+ jQuery('#fb-options').show();
+ jQuery('#fb-new-options').html(""); // Clear it all out
+ for (i=0; i<thisOptions.length; i++) {
+ if (thisOptions[i] !== undefined) {
+ if (thisType === "radio") {
+ jQuery('#fb-new-options').append('<div id="fb-option-box-' + i + '" class="fb-new-fields"><span optionid="' + i + '" class="fb-remove-option"></span><label></label><input type="text" id="fb-option' + i + '" optionid="' + i + '" value="' + fbForm.fields[id].options[i] + '" class="fb-options" /><div>');
+ } else {
+ jQuery('#fb-new-options').append('<div id="fb-option-box-' + i + '" class="fb-new-fields"><span optionid="' + i + '" class="fb-remove-option"></span><label></label><input type="text" id="fb-option' + i + '" optionid="' + i + '" value="' + fbForm.fields[id].options[i] + '" class="fb-options" /><div>');
+ }
+ }
+ }
+ }
+ // Load editor & hide description
+ hideDesc();
+ } catch(e) {
+ if (debug) {
+ console.log("loadFieldEditor(): " + e);
+ }
+ }
+ }
+ function parseShortcode (data) {
+ try {
+ // Clean up fields by resetting them
+ fbForm.fields = {};
+ // Add new fields
+ if (!data) {
+ fbForm.fields = defaultFields;
+ } else {
+ jQuery.each(data.fields, function(index, value) {
+ fbForm.fields[index] = value;
+ });
+ fbForm.to = data.to;
+ fbForm.subject = data.subject;
+ }
+ } catch(e) {
+ if (debug) {
+ console.log("parseShortcode(): " + e);
+ }
+ }
+ }
+ function removeOption (optionId) {
+ try {
+ var thisId = jQuery('#fb-field-id').val();
+ var thisVal = jQuery('#fb-option' + optionId).val();
+ var thisType = jQuery('#fb-new-type').val();
+ // Remove from right
+ jQuery('#fb-option-box-' + optionId).remove();
+ // Remove from preview
+ if (thisType === "radio") {
+ jQuery('#fb-radio-' + thisId + '-' + optionId).remove();
+ } else {
+ jQuery('#fb-' + thisId + '-' + optionId).remove();
+ }
+ // Remove from fbForm object
+ var idx = fbForm.fields[thisId].options.indexOf(thisVal);
+ if (idx !== -1) { fbForm.fields[thisId].options.splice(idx, 1); }
+ } catch(e) {
+ if (debug) {
+ console.log("removeOption(): " + e);
+ }
+ }
+ }
+ function removeOptions () {
+ try {
+ var thisId = jQuery('#fb-field-id').val();
+ jQuery('#fb-options').hide();
+ if (optionsCache[thisId] === undefined) { optionsCache[thisId] = {}; }
+ optionsCache[thisId].options = fbForm.fields[thisId].options; // Save options in case they change their mind
+ fbForm.fields[thisId].options = []; // Removes all options
+ } catch(e) {
+ if (debug) {
+ console.log("removeOptions(): " + e);
+ }
+ }
+ }
+ function sendShortcodeToEditor () {
+ try {
+ // Serialize fields
+ jQuery('div#sortable div.fb-new-fields').each(function(index) {
+ var thisId = jQuery(this).attr('fieldid');
+ fbForm.fields[thisId].order = index;
+ });
+ // Export to WYSIWYG editor
+ jQuery.post(ajaxurl, fbForm, function(response) {
+ var isVisual = jQuery('#edButtonPreview', window.parent.document).hasClass('active');
+ /* WP 3.3+ */
+ if ( !isVisual ) {
+ isVisual = jQuery( '#wp-content-wrap', window.parent.document ).hasClass( 'tmce-active' );
+ }
+
+ var win = window.dialogArguments || opener || parent || top;
+ if (isVisual) {
+ var currentCode = win.tinyMCE.activeEditor.getContent();
+ } else {
+ var currentCode = jQuery('#editorcontainer textarea', window.parent.document).val();
+ /* WP 3.3+ */
+ if ( typeof currentCode != 'string' ) {
+ currentCode = jQuery( '.wp-editor-area', window.parent.document ).val();
+ }
+ }
+ var regexp = new RegExp("\\[contact-form\\b.*?\\/?\\](?:[\\s\\S]+?\\[\\/contact-form\\])?");
+
+ // Remove new lines that cause BR tags to show up
+ response = response.replace(/\n/g,' ');
+
+ // Add new shortcode
+ if (currentCode.match(regexp)) {
+ if (isVisual) {
+ win.tinyMCE.activeEditor.execCommand('mceSetContent', false, currentCode.replace(regexp, response));
+ } else {
+ // looks like the visual editor is disabled,
+ // update the contents of the post directly
+ jQuery( '#content', window.parent.document ).val( currentCode.replace( regexp, response ) );
+ }
+ } else {
+ if (isVisual) {
+ win.tinyMCE.activeEditor.execCommand('mceInsertContent', false, response);
+ } else {
+ // looks like the visual editor is disabled,
+ // update the contents of the post directly
+ jQuery( '#content', window.parent.document ).val( currentCode + response );
+ }
+ }
+ hidePopup();
+ });
+ } catch(e) {
+ if (debug) {
+ console.log("sendShortcodeToEditor(): " + e);
+ }
+ }
+ }
+ function showDesc () {
+ jQuery('#fb-desc').show();
+ jQuery('#fb-add-field').hide();
+ }
+ function showAndHideMessage (message) {
+ try {
+ var newMessage = (!message) ? GrunionFB_i18n.savedMessage : message;
+ jQuery('#fb-success').html(newMessage);
+ jQuery('#fb-success').slideDown('fast');
+ setTimeout(function () {
+ jQuery('#fb-success').slideUp('fast');
+ }, 2500);
+ } catch(e) {
+ if (debug) {
+ console.log("showAndHideMessage(): " + e);
+ }
+ }
+ }
+ function switchTabs (whichType) {
+ try {
+ if (whichType === "preview") {
+ jQuery('#tab-preview a').addClass('current');
+ jQuery('#tab-settings a').removeClass('current');
+ jQuery('#fb-preview-form, #fb-desc').show();
+ jQuery('#fb-email-settings, #fb-email-desc').hide();
+ } else {
+ jQuery('#tab-preview a').removeClass('current');
+ jQuery('#tab-settings a').addClass('current');
+ jQuery('#fb-preview-form, #fb-desc, #fb-add-field').hide();
+ jQuery('#fb-email-settings, #fb-email-desc').show();
+ jQuery('#fb-field-my-email').focus().select();
+ }
+ } catch(e) {
+ if (debug) {
+ console.log("switchTabs(): " + e);
+ }
+ }
+ }
+ function updateLabel () {
+ try {
+ var thisId = jQuery('#fb-field-id').val();
+ var thisLabel = jQuery('#fb-new-label').val();
+ // Update preview
+ if (thisLabel.length === 0) {
+ jQuery('#fb-new-field' + thisId + ' label .label-text').html("New field");
+ } else {
+ jQuery('#fb-new-field' + thisId + ' label .label-text').html(thisLabel);
+ }
+ // Update fbForm object
+ fbForm.fields[thisId].label = thisLabel;
+ } catch(e) {
+ if (debug) {
+ console.log("updateLabel(): " + e);
+ }
+ }
+ }
+ function updateMyEmail () {
+ try {
+ var thisEmail = jQuery('#fb-field-my-email').val();
+ fbForm.to = thisEmail;
+ } catch(e) {
+ if (debug) {
+ console.log("updateMyEmail(): " + e);
+ }
+ }
+ }
+ function updateOption (that) {
+ try {
+ var thisId = jQuery('#fb-field-id').val();
+ var thisOptionid = that.attr('optionid');
+ var thisOptionValue = that.val();
+ var thisType = jQuery('#fb-new-type').val();
+ // Update preview
+ if (thisType === "radio") {
+ jQuery('#fb-radio-' + thisId + '-' + thisOptionid + ' span').html(thisOptionValue);
+ } else {
+ jQuery('#fb-' + thisId + '-' + thisOptionid).text(thisOptionValue);
+ }
+ // Update fbForm object
+ fbForm.fields[thisId].options[thisOptionid] = thisOptionValue;
+ } catch(e) {
+ if (debug) {
+ console.log("updateOption(): " + e);
+ }
+ }
+ }
+ function updateRequired () {
+ try {
+ var thisId = jQuery('#fb-field-id').val();
+ var thisChecked = jQuery('#fb-new-required').is(':checked');
+ // Update object and preview
+ if (thisChecked) {
+ fbForm.fields[thisId].required = true;
+ jQuery('#fb-new-field' + thisId + ' label').append('<span class="label-required">' + GrunionFB_i18n.requiredLabel + '</span>');
+ } else {
+ fbForm.fields[thisId].required = false;
+ jQuery('#fb-new-field' + thisId + ' label .label-required').remove();
+ }
+ } catch(e) {
+ if (debug) {
+ console.log("updateRequired(): " + e);
+ }
+ }
+ }
+ function updateSubject () {
+ try {
+ var thisSubject = jQuery('#fb-field-subject').val();
+ fbForm.subject = thisSubject;
+ } catch(e) {
+ if (debug) {
+ console.log("updateSubject(): " + e);
+ }
+ }
+ }
+ function updateType(thisType, thisLabelText, thisRequired) {
+ try {
+ var isLoaded = thisType;
+ var thisId = jQuery('#fb-field-id').val();
+ if (!thisType) { var thisType = jQuery('#fb-new-type').val(); }
+ if (!thisLabelText) { var thisLabelText = jQuery('#fb-new-field' + thisId + ' label').html(); }
+ var isRequired = (thisRequired) ? '<span class="label-required">' + GrunionFB_i18n.requiredLabel + '</span>' : '';
+ var thisLabel = '<label fieldid="' + thisId + '" for="fb-field' + thisId + '"><span class="label-text">' + thisLabelText + '</span>' + isRequired + '</label>';
+ var thisRadio = '<input type="radio" name="radio-' + thisId + '" id="fb-field' + thisId + ' "disabled="disabled" />';
+ var thisRadioLabel = '<label fieldid="' + thisId + '" for="fb-field' + thisId + '" class="fb-radio-label"><span class="label-text">' + thisLabelText + '</span>' + isRequired + '</label>';
+ var thisRadioRemove = '<div class="fb-remove fb-remove-small" id="' + thisId + '"></div>';
+ var thisRemove = '<div class="fb-remove" id="' + thisId + '"></div>';
+ var thisCheckbox = '<input type="checkbox" id="fb-field' + thisId + '" "disabled="disabled" />';
+ var thisText = '<input type="text" id="fb-field' + thisId + '" "disabled="disabled" />';
+ var thisTextarea = '<textarea id="fb-field' + thisId + '" "disabled="disabled"></textarea>';
+ var thisClear = '<div class="clear"></div>';
+ var thisSelect = '<select id="fb-field' + thisId + '" fieldid="' + thisId + '"><option id="fb-' + thisId + '-' + optionsCount + '" value="' + thisId + '-' + optionsCount + '">' + GrunionFB_i18n.firstOptionLabel + '</option></select>';
+ switch (thisType) {
+ case "checkbox":
+ removeOptions();
+ jQuery('#fb-new-field' + thisId + ' .fb-fields').html(thisRadioRemove + thisCheckbox + thisRadioLabel + thisClear);
+ break;
+ case "email":
+ removeOptions();
+ jQuery('#fb-new-field' + thisId + ' .fb-fields').html(thisRemove + thisLabel + thisText);
+ break;
+ case "name":
+ removeOptions();
+ jQuery('#fb-new-field' + thisId + ' .fb-fields').html(thisRemove + thisLabel + thisText);
+ break;
+ case "radio":
+ jQuery('#fb-new-field' + thisId + ' .fb-fields').html(thisLabel + thisRadioRemove + '<div fieldid="' + thisId + '" id="fb-custom-radio' + thisId + '"></div>');
+ if (optionsCache[thisId] !== undefined && optionsCache[thisId].options.length !== 0) {
+ fbForm.fields[thisId].options = optionsCache[thisId].options;
+ jQuery('#fb-custom-radio' + thisId).append(customOptions(thisId, thisType));
+ } else {
+ jQuery('#fb-new-options').html('<label for="fb-option0">' + GrunionFB_i18n.optionsLabel + '</label><input type="text" id="fb-option0" optionid="0" value="' + GrunionFB_i18n.firstOptionLabel + '" class="fb-options" />');
+ jQuery('#fb-custom-radio' + thisId).append('<div id="fb-radio-' + thisId + '-0">' + thisRadio + '<span>' + GrunionFB_i18n.firstOptionLabel + '</span>' + thisClear + '</div>');
+ fbForm.fields[thisId].options[optionsCount] = GrunionFB_i18n.firstOptionLabel;
+ }
+ jQuery('#fb-options').show();
+ setTimeout(function () { jQuery('#fb-option0').focus().select(); }, 100);
+ break;
+ case "select":
+ jQuery('#fb-new-field' + thisId + ' .fb-fields').html(thisRemove + thisLabel + thisSelect);
+ if (optionsCache[thisId] !== undefined && optionsCache[thisId].options.length !== 0) {
+ fbForm.fields[thisId].options = optionsCache[thisId].options;
+ jQuery('#fb-field' + thisId).html(customOptions(thisId, thisType));
+ } else {
+ jQuery('#fb-new-options').html('<label for="fb-option0">' + GrunionFB_i18n.optionsLabel + '</label><input type="text" id="fb-option0" optionid="0" value="' + GrunionFB_i18n.firstOptionLabel + '" class="fb-options" />');
+ fbForm.fields[thisId].options[optionsCount] = GrunionFB_i18n.firstOptionLabel;
+ }
+ jQuery('#fb-options').show();
+ setTimeout(function () { jQuery('#fb-option0').focus().select(); }, 100);
+ break;
+ case "text":
+ removeOptions();
+ jQuery('#fb-new-field' + thisId + ' .fb-fields').html(thisRemove + thisLabel + thisText);
+ break;
+ case "textarea":
+ removeOptions();
+ jQuery('#fb-new-field' + thisId + ' .fb-fields').html(thisRemove + thisLabel + thisTextarea);
+ break;
+ case "url":
+ removeOptions();
+ jQuery('#fb-new-field' + thisId + ' .fb-fields').html(thisRemove + thisLabel + thisText);
+ break;
+ }
+ // update object
+ fbForm.fields[thisId].type = thisType;
+ } catch(e) {
+ if (debug) {
+ console.log("updateType(): " + e);
+ }
+ }
+ }
+ return {
+ resizePop: function () {
+ try {
+ //Thickbox won't resize for some reason, we are manually doing it here
+ var totalWidth = jQuery('body', window.parent.document).width();
+ var totalHeight = jQuery('body', window.parent.document).height();
+ var isIE6 = typeof document.body.style.maxHeight === "undefined";
+
+ jQuery('#TB_window, #TB_iframeContent', window.parent.document).css('width', '768px');
+ jQuery('#TB_window', window.parent.document).css({ left: (totalWidth-768)/2 + 'px', top: '23px', position: 'absolute', marginLeft: '0' });
+ if ( ! isIE6 ) { // take away IE6
+ jQuery('#TB_window, #TB_iframeContent', window.parent.document).css('height', (totalHeight-73) + 'px');
+ }
+ } catch(e) {
+ if (debug) {
+ console.log("resizePop(): " + e);
+ }
+ }
+ },
+ init: function () {
+ // Scroll to top of page
+ window.parent.scroll(0,0);
+ //Check for existing form data
+ if (jQuery('#edButtonPreview', window.parent.document).hasClass('active') || jQuery( '#wp-content-wrap', window.parent.document ).hasClass( 'tmce-active' ) ) {
+ var win = window.dialogArguments || opener || parent || top;
+ var contentSource = win.tinyMCE.activeEditor.getContent();
+ } else {
+ var contentSource = jQuery('#content', window.parent.document).val();
+ }
+ var data = {
+ action: 'grunion_shortcode_to_json',
+ '_ajax_nonce' : ajax_nonce_json,
+ post_id: postId,
+ content: contentSource
+ };
+
+ jQuery.post(ajaxurl, data, function(response) {
+ // Setup fbForm
+ parseShortcode(jQuery.parseJSON(response));
+ // Now build out the preview form
+ buildPreview();
+ });
+ // actions
+ jQuery('.fb-add-field').click(function () {
+ addField();
+ hideDesc();
+ return false;
+ });
+ jQuery('#fb-new-label').keyup(function () {
+ updateLabel();
+ });
+ jQuery('#fb-new-type').change(function () {
+ updateType();
+ });
+ jQuery('#fb-new-required').click(function () {
+ updateRequired();
+ });
+ jQuery('.fb-remove').live('click', function () {
+ showDesc();
+ deleteField(jQuery(this));
+ grabShortcode();
+ });
+ jQuery('#fb-preview').submit(function () {
+ sendShortcodeToEditor();
+ return false;
+ });
+ jQuery('#TB_overlay, #TB_closeWindowButton', window.parent.document).mousedown(function () {
+ if(confirm( GrunionFB_i18n.exitConfirmMessage )) {
+ hidePopup();
+ }
+ });
+ jQuery('#fb-another-option').live('click', function () {
+ addOption();
+ });
+ jQuery('.fb-options').live('keyup', function () {
+ updateOption(jQuery(this));
+ });
+ jQuery('.fb-remove-option').live('click', function () {
+ removeOption(jQuery(this).attr('optionid'));
+ });
+ jQuery('#tab-preview a').click(function () {
+ switchTabs('preview');
+ return false;
+ });
+ jQuery('#fb-prev-form').click(function () {
+ switchTabs('preview');
+ showAndHideMessage( GrunionFB_i18n.savedMessage );
+ return false;
+ });
+ jQuery('#tab-settings a').click(function () {
+ switchTabs();
+ return false;
+ });
+ jQuery('#fb-field-my-email').blur(function () {
+ updateMyEmail();
+ });
+ jQuery('#fb-field-subject').blur(function () {
+ updateSubject();
+ });
+ jQuery('.fb-form-case .fb-new-fields').live('mouseenter', function () {
+ hideShowEditLink('show', jQuery(this));
+ });
+ jQuery('.fb-form-case .fb-new-fields').live('mouseleave', function () {
+ hideShowEditLink('hide');
+ return false;
+ });
+ jQuery('.fb-edit-field').live('click', function () {
+ editField(jQuery(this));
+ return false;
+ });
+ jQuery('.fb-edit-field .fb-reorder').live('click', function () {
+ return false;
+ });
+ jQuery('#fb-save-field').live('click', function () {
+ showDesc();
+ showAndHideMessage();
+ return false;
+ });
+ jQuery('#fb-feedback').click(function () {
+ var thisHref = jQuery(this).attr('href');
+ window.parent.location = thisHref;
+ return false;
+ });
+ jQuery("#sortable").sortable({
+ axis: 'y',
+ handle: '.fb-reorder',
+ revert: true,
+ start: function() { jQuery('.fb-edit-field').hide(); }
+ });
+ jQuery("#draggable").draggable({
+ axis: 'y',
+ handle: '.fb-reorder',
+ connectToSortable: '#sortable',
+ helper: 'clone',
+ revert: 'invalid'
+ });
+ }
+ };
+}();
diff --git a/plugins/jetpack/modules/contact-form/js/jquery-ui-1.8.4.custom.min.js b/plugins/jetpack/modules/contact-form/js/jquery-ui-1.8.4.custom.min.js
new file mode 100644
index 00000000..11e456fe
--- /dev/null
+++ b/plugins/jetpack/modules/contact-form/js/jquery-ui-1.8.4.custom.min.js
@@ -0,0 +1,185 @@
+/*!
+ * jQuery UI 1.8.4
+ *
+ * Copyright 2010, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI
+ */
+(function(c,j){function k(a){return!c(a).parents().andSelf().filter(function(){return c.curCSS(this,"visibility")==="hidden"||c.expr.filters.hidden(this)}).length}c.ui=c.ui||{};if(!c.ui.version){c.extend(c.ui,{version:"1.8.4",plugin:{add:function(a,b,d){a=c.ui[a].prototype;for(var e in d){a.plugins[e]=a.plugins[e]||[];a.plugins[e].push([b,d[e]])}},call:function(a,b,d){if((b=a.plugins[b])&&a.element[0].parentNode)for(var e=0;e<b.length;e++)a.options[b[e][0]]&&b[e][1].apply(a.element,d)}},contains:function(a,
+b){return document.compareDocumentPosition?a.compareDocumentPosition(b)&16:a!==b&&a.contains(b)},hasScroll:function(a,b){if(c(a).css("overflow")==="hidden")return false;b=b&&b==="left"?"scrollLeft":"scrollTop";var d=false;if(a[b]>0)return true;a[b]=1;d=a[b]>0;a[b]=0;return d},isOverAxis:function(a,b,d){return a>b&&a<b+d},isOver:function(a,b,d,e,h,i){return c.ui.isOverAxis(a,d,h)&&c.ui.isOverAxis(b,e,i)},keyCode:{ALT:18,BACKSPACE:8,CAPS_LOCK:20,COMMA:188,COMMAND:91,COMMAND_LEFT:91,COMMAND_RIGHT:93,
+CONTROL:17,DELETE:46,DOWN:40,END:35,ENTER:13,ESCAPE:27,HOME:36,INSERT:45,LEFT:37,MENU:93,NUMPAD_ADD:107,NUMPAD_DECIMAL:110,NUMPAD_DIVIDE:111,NUMPAD_ENTER:108,NUMPAD_MULTIPLY:106,NUMPAD_SUBTRACT:109,PAGE_DOWN:34,PAGE_UP:33,PERIOD:190,RIGHT:39,SHIFT:16,SPACE:32,TAB:9,UP:38,WINDOWS:91}});c.fn.extend({_focus:c.fn.focus,focus:function(a,b){return typeof a==="number"?this.each(function(){var d=this;setTimeout(function(){c(d).focus();b&&b.call(d)},a)}):this._focus.apply(this,arguments)},enableSelection:function(){return this.attr("unselectable",
+"off").css("MozUserSelect","")},disableSelection:function(){return this.attr("unselectable","on").css("MozUserSelect","none")},scrollParent:function(){var a;a=c.browser.msie&&/(static|relative)/.test(this.css("position"))||/absolute/.test(this.css("position"))?this.parents().filter(function(){return/(relative|absolute|fixed)/.test(c.curCSS(this,"position",1))&&/(auto|scroll)/.test(c.curCSS(this,"overflow",1)+c.curCSS(this,"overflow-y",1)+c.curCSS(this,"overflow-x",1))}).eq(0):this.parents().filter(function(){return/(auto|scroll)/.test(c.curCSS(this,
+"overflow",1)+c.curCSS(this,"overflow-y",1)+c.curCSS(this,"overflow-x",1))}).eq(0);return/fixed/.test(this.css("position"))||!a.length?c(document):a},zIndex:function(a){if(a!==j)return this.css("zIndex",a);if(this.length){a=c(this[0]);for(var b;a.length&&a[0]!==document;){b=a.css("position");if(b==="absolute"||b==="relative"||b==="fixed"){b=parseInt(a.css("zIndex"));if(!isNaN(b)&&b!=0)return b}a=a.parent()}}return 0}});c.each(["Width","Height"],function(a,b){function d(f,g,l,m){c.each(e,function(){g-=
+parseFloat(c.curCSS(f,"padding"+this,true))||0;if(l)g-=parseFloat(c.curCSS(f,"border"+this+"Width",true))||0;if(m)g-=parseFloat(c.curCSS(f,"margin"+this,true))||0});return g}var e=b==="Width"?["Left","Right"]:["Top","Bottom"],h=b.toLowerCase(),i={innerWidth:c.fn.innerWidth,innerHeight:c.fn.innerHeight,outerWidth:c.fn.outerWidth,outerHeight:c.fn.outerHeight};c.fn["inner"+b]=function(f){if(f===j)return i["inner"+b].call(this);return this.each(function(){c.style(this,h,d(this,f)+"px")})};c.fn["outer"+
+b]=function(f,g){if(typeof f!=="number")return i["outer"+b].call(this,f);return this.each(function(){c.style(this,h,d(this,f,true,g)+"px")})}});c.extend(c.expr[":"],{data:function(a,b,d){return!!c.data(a,d[3])},focusable:function(a){var b=a.nodeName.toLowerCase(),d=c.attr(a,"tabindex");if("area"===b){b=a.parentNode;d=b.name;if(!a.href||!d||b.nodeName.toLowerCase()!=="map")return false;a=c("img[usemap=#"+d+"]")[0];return!!a&&k(a)}return(/input|select|textarea|button|object/.test(b)?!a.disabled:"a"==
+b?a.href||!isNaN(d):!isNaN(d))&&k(a)},tabbable:function(a){var b=c.attr(a,"tabindex");return(isNaN(b)||b>=0)&&c(a).is(":focusable")}})}})(jQuery);
+;/*!
+ * jQuery UI Widget 1.8.4
+ *
+ * Copyright 2010, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI/Widget
+ */
+(function(b,j){var k=b.fn.remove;b.fn.remove=function(a,c){return this.each(function(){if(!c)if(!a||b.filter(a,[this]).length)b("*",this).add([this]).each(function(){b(this).triggerHandler("remove")});return k.call(b(this),a,c)})};b.widget=function(a,c,d){var e=a.split(".")[0],f;a=a.split(".")[1];f=e+"-"+a;if(!d){d=c;c=b.Widget}b.expr[":"][f]=function(h){return!!b.data(h,a)};b[e]=b[e]||{};b[e][a]=function(h,g){arguments.length&&this._createWidget(h,g)};c=new c;c.options=b.extend(true,{},c.options);
+b[e][a].prototype=b.extend(true,c,{namespace:e,widgetName:a,widgetEventPrefix:b[e][a].prototype.widgetEventPrefix||a,widgetBaseClass:f},d);b.widget.bridge(a,b[e][a])};b.widget.bridge=function(a,c){b.fn[a]=function(d){var e=typeof d==="string",f=Array.prototype.slice.call(arguments,1),h=this;d=!e&&f.length?b.extend.apply(null,[true,d].concat(f)):d;if(e&&d.substring(0,1)==="_")return h;e?this.each(function(){var g=b.data(this,a),i=g&&b.isFunction(g[d])?g[d].apply(g,f):g;if(i!==g&&i!==j){h=i;return false}}):
+this.each(function(){var g=b.data(this,a);if(g){d&&g.option(d);g._init()}else b.data(this,a,new c(d,this))});return h}};b.Widget=function(a,c){arguments.length&&this._createWidget(a,c)};b.Widget.prototype={widgetName:"widget",widgetEventPrefix:"",options:{disabled:false},_createWidget:function(a,c){b.data(c,this.widgetName,this);this.element=b(c);this.options=b.extend(true,{},this.options,b.metadata&&b.metadata.get(c)[this.widgetName],a);var d=this;this.element.bind("remove."+this.widgetName,function(){d.destroy()});
+this._create();this._init()},_create:function(){},_init:function(){},destroy:function(){this.element.unbind("."+this.widgetName).removeData(this.widgetName);this.widget().unbind("."+this.widgetName).removeAttr("aria-disabled").removeClass(this.widgetBaseClass+"-disabled ui-state-disabled")},widget:function(){return this.element},option:function(a,c){var d=a,e=this;if(arguments.length===0)return b.extend({},e.options);if(typeof a==="string"){if(c===j)return this.options[a];d={};d[a]=c}b.each(d,function(f,
+h){e._setOption(f,h)});return e},_setOption:function(a,c){this.options[a]=c;if(a==="disabled")this.widget()[c?"addClass":"removeClass"](this.widgetBaseClass+"-disabled ui-state-disabled").attr("aria-disabled",c);return this},enable:function(){return this._setOption("disabled",false)},disable:function(){return this._setOption("disabled",true)},_trigger:function(a,c,d){var e=this.options[a];c=b.Event(c);c.type=(a===this.widgetEventPrefix?a:this.widgetEventPrefix+a).toLowerCase();d=d||{};if(c.originalEvent){a=
+b.event.props.length;for(var f;a;){f=b.event.props[--a];c[f]=c.originalEvent[f]}}this.element.trigger(c,d);return!(b.isFunction(e)&&e.call(this.element[0],c,d)===false||c.isDefaultPrevented())}}})(jQuery);
+;/*!
+ * jQuery UI Mouse 1.8.4
+ *
+ * Copyright 2010, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI/Mouse
+ *
+ * Depends:
+ * jquery.ui.widget.js
+ */
+(function(c){c.widget("ui.mouse",{options:{cancel:":input,option",distance:1,delay:0},_mouseInit:function(){var a=this;this.element.bind("mousedown."+this.widgetName,function(b){return a._mouseDown(b)}).bind("click."+this.widgetName,function(b){if(a._preventClickEvent){a._preventClickEvent=false;b.stopImmediatePropagation();return false}});this.started=false},_mouseDestroy:function(){this.element.unbind("."+this.widgetName)},_mouseDown:function(a){a.originalEvent=a.originalEvent||{};if(!a.originalEvent.mouseHandled){this._mouseStarted&&
+this._mouseUp(a);this._mouseDownEvent=a;var b=this,e=a.which==1,f=typeof this.options.cancel=="string"?c(a.target).parents().add(a.target).filter(this.options.cancel).length:false;if(!e||f||!this._mouseCapture(a))return true;this.mouseDelayMet=!this.options.delay;if(!this.mouseDelayMet)this._mouseDelayTimer=setTimeout(function(){b.mouseDelayMet=true},this.options.delay);if(this._mouseDistanceMet(a)&&this._mouseDelayMet(a)){this._mouseStarted=this._mouseStart(a)!==false;if(!this._mouseStarted){a.preventDefault();
+return true}}this._mouseMoveDelegate=function(d){return b._mouseMove(d)};this._mouseUpDelegate=function(d){return b._mouseUp(d)};c(document).bind("mousemove."+this.widgetName,this._mouseMoveDelegate).bind("mouseup."+this.widgetName,this._mouseUpDelegate);c.browser.safari||a.preventDefault();return a.originalEvent.mouseHandled=true}},_mouseMove:function(a){if(c.browser.msie&&!a.button)return this._mouseUp(a);if(this._mouseStarted){this._mouseDrag(a);return a.preventDefault()}if(this._mouseDistanceMet(a)&&
+this._mouseDelayMet(a))(this._mouseStarted=this._mouseStart(this._mouseDownEvent,a)!==false)?this._mouseDrag(a):this._mouseUp(a);return!this._mouseStarted},_mouseUp:function(a){c(document).unbind("mousemove."+this.widgetName,this._mouseMoveDelegate).unbind("mouseup."+this.widgetName,this._mouseUpDelegate);if(this._mouseStarted){this._mouseStarted=false;this._preventClickEvent=a.target==this._mouseDownEvent.target;this._mouseStop(a)}return false},_mouseDistanceMet:function(a){return Math.max(Math.abs(this._mouseDownEvent.pageX-
+a.pageX),Math.abs(this._mouseDownEvent.pageY-a.pageY))>=this.options.distance},_mouseDelayMet:function(){return this.mouseDelayMet},_mouseStart:function(){},_mouseDrag:function(){},_mouseStop:function(){},_mouseCapture:function(){return true}})})(jQuery);
+;/*
+ * jQuery UI Draggable 1.8.4
+ *
+ * Copyright 2010, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI/Draggables
+ *
+ * Depends:
+ * jquery.ui.core.js
+ * jquery.ui.mouse.js
+ * jquery.ui.widget.js
+ */
+(function(d){d.widget("ui.draggable",d.ui.mouse,{widgetEventPrefix:"drag",options:{addClasses:true,appendTo:"parent",axis:false,connectToSortable:false,containment:false,cursor:"auto",cursorAt:false,grid:false,handle:false,helper:"original",iframeFix:false,opacity:false,refreshPositions:false,revert:false,revertDuration:500,scope:"default",scroll:true,scrollSensitivity:20,scrollSpeed:20,snap:false,snapMode:"both",snapTolerance:20,stack:false,zIndex:false},_create:function(){if(this.options.helper==
+"original"&&!/^(?:r|a|f)/.test(this.element.css("position")))this.element[0].style.position="relative";this.options.addClasses&&this.element.addClass("ui-draggable");this.options.disabled&&this.element.addClass("ui-draggable-disabled");this._mouseInit()},destroy:function(){if(this.element.data("draggable")){this.element.removeData("draggable").unbind(".draggable").removeClass("ui-draggable ui-draggable-dragging ui-draggable-disabled");this._mouseDestroy();return this}},_mouseCapture:function(a){var b=
+this.options;if(this.helper||b.disabled||d(a.target).is(".ui-resizable-handle"))return false;this.handle=this._getHandle(a);if(!this.handle)return false;return true},_mouseStart:function(a){var b=this.options;this.helper=this._createHelper(a);this._cacheHelperProportions();if(d.ui.ddmanager)d.ui.ddmanager.current=this;this._cacheMargins();this.cssPosition=this.helper.css("position");this.scrollParent=this.helper.scrollParent();this.offset=this.positionAbs=this.element.offset();this.offset={top:this.offset.top-
+this.margins.top,left:this.offset.left-this.margins.left};d.extend(this.offset,{click:{left:a.pageX-this.offset.left,top:a.pageY-this.offset.top},parent:this._getParentOffset(),relative:this._getRelativeOffset()});this.originalPosition=this.position=this._generatePosition(a);this.originalPageX=a.pageX;this.originalPageY=a.pageY;b.cursorAt&&this._adjustOffsetFromHelper(b.cursorAt);b.containment&&this._setContainment();if(this._trigger("start",a)===false){this._clear();return false}this._cacheHelperProportions();
+d.ui.ddmanager&&!b.dropBehaviour&&d.ui.ddmanager.prepareOffsets(this,a);this.helper.addClass("ui-draggable-dragging");this._mouseDrag(a,true);return true},_mouseDrag:function(a,b){this.position=this._generatePosition(a);this.positionAbs=this._convertPositionTo("absolute");if(!b){b=this._uiHash();if(this._trigger("drag",a,b)===false){this._mouseUp({});return false}this.position=b.position}if(!this.options.axis||this.options.axis!="y")this.helper[0].style.left=this.position.left+"px";if(!this.options.axis||
+this.options.axis!="x")this.helper[0].style.top=this.position.top+"px";d.ui.ddmanager&&d.ui.ddmanager.drag(this,a);return false},_mouseStop:function(a){var b=false;if(d.ui.ddmanager&&!this.options.dropBehaviour)b=d.ui.ddmanager.drop(this,a);if(this.dropped){b=this.dropped;this.dropped=false}if(!this.element[0]||!this.element[0].parentNode)return false;if(this.options.revert=="invalid"&&!b||this.options.revert=="valid"&&b||this.options.revert===true||d.isFunction(this.options.revert)&&this.options.revert.call(this.element,
+b)){var c=this;d(this.helper).animate(this.originalPosition,parseInt(this.options.revertDuration,10),function(){c._trigger("stop",a)!==false&&c._clear()})}else this._trigger("stop",a)!==false&&this._clear();return false},cancel:function(){this.helper.is(".ui-draggable-dragging")?this._mouseUp({}):this._clear();return this},_getHandle:function(a){var b=!this.options.handle||!d(this.options.handle,this.element).length?true:false;d(this.options.handle,this.element).find("*").andSelf().each(function(){if(this==
+a.target)b=true});return b},_createHelper:function(a){var b=this.options;a=d.isFunction(b.helper)?d(b.helper.apply(this.element[0],[a])):b.helper=="clone"?this.element.clone():this.element;a.parents("body").length||a.appendTo(b.appendTo=="parent"?this.element[0].parentNode:b.appendTo);a[0]!=this.element[0]&&!/(fixed|absolute)/.test(a.css("position"))&&a.css("position","absolute");return a},_adjustOffsetFromHelper:function(a){if(typeof a=="string")a=a.split(" ");if(d.isArray(a))a={left:+a[0],top:+a[1]||
+0};if("left"in a)this.offset.click.left=a.left+this.margins.left;if("right"in a)this.offset.click.left=this.helperProportions.width-a.right+this.margins.left;if("top"in a)this.offset.click.top=a.top+this.margins.top;if("bottom"in a)this.offset.click.top=this.helperProportions.height-a.bottom+this.margins.top},_getParentOffset:function(){this.offsetParent=this.helper.offsetParent();var a=this.offsetParent.offset();if(this.cssPosition=="absolute"&&this.scrollParent[0]!=document&&d.ui.contains(this.scrollParent[0],
+this.offsetParent[0])){a.left+=this.scrollParent.scrollLeft();a.top+=this.scrollParent.scrollTop()}if(this.offsetParent[0]==document.body||this.offsetParent[0].tagName&&this.offsetParent[0].tagName.toLowerCase()=="html"&&d.browser.msie)a={top:0,left:0};return{top:a.top+(parseInt(this.offsetParent.css("borderTopWidth"),10)||0),left:a.left+(parseInt(this.offsetParent.css("borderLeftWidth"),10)||0)}},_getRelativeOffset:function(){if(this.cssPosition=="relative"){var a=this.element.position();return{top:a.top-
+(parseInt(this.helper.css("top"),10)||0)+this.scrollParent.scrollTop(),left:a.left-(parseInt(this.helper.css("left"),10)||0)+this.scrollParent.scrollLeft()}}else return{top:0,left:0}},_cacheMargins:function(){this.margins={left:parseInt(this.element.css("marginLeft"),10)||0,top:parseInt(this.element.css("marginTop"),10)||0}},_cacheHelperProportions:function(){this.helperProportions={width:this.helper.outerWidth(),height:this.helper.outerHeight()}},_setContainment:function(){var a=this.options;if(a.containment==
+"parent")a.containment=this.helper[0].parentNode;if(a.containment=="document"||a.containment=="window")this.containment=[0-this.offset.relative.left-this.offset.parent.left,0-this.offset.relative.top-this.offset.parent.top,d(a.containment=="document"?document:window).width()-this.helperProportions.width-this.margins.left,(d(a.containment=="document"?document:window).height()||document.body.parentNode.scrollHeight)-this.helperProportions.height-this.margins.top];if(!/^(document|window|parent)$/.test(a.containment)&&
+a.containment.constructor!=Array){var b=d(a.containment)[0];if(b){a=d(a.containment).offset();var c=d(b).css("overflow")!="hidden";this.containment=[a.left+(parseInt(d(b).css("borderLeftWidth"),10)||0)+(parseInt(d(b).css("paddingLeft"),10)||0)-this.margins.left,a.top+(parseInt(d(b).css("borderTopWidth"),10)||0)+(parseInt(d(b).css("paddingTop"),10)||0)-this.margins.top,a.left+(c?Math.max(b.scrollWidth,b.offsetWidth):b.offsetWidth)-(parseInt(d(b).css("borderLeftWidth"),10)||0)-(parseInt(d(b).css("paddingRight"),
+10)||0)-this.helperProportions.width-this.margins.left,a.top+(c?Math.max(b.scrollHeight,b.offsetHeight):b.offsetHeight)-(parseInt(d(b).css("borderTopWidth"),10)||0)-(parseInt(d(b).css("paddingBottom"),10)||0)-this.helperProportions.height-this.margins.top]}}else if(a.containment.constructor==Array)this.containment=a.containment},_convertPositionTo:function(a,b){if(!b)b=this.position;a=a=="absolute"?1:-1;var c=this.cssPosition=="absolute"&&!(this.scrollParent[0]!=document&&d.ui.contains(this.scrollParent[0],
+this.offsetParent[0]))?this.offsetParent:this.scrollParent,f=/(html|body)/i.test(c[0].tagName);return{top:b.top+this.offset.relative.top*a+this.offset.parent.top*a-(d.browser.safari&&d.browser.version<526&&this.cssPosition=="fixed"?0:(this.cssPosition=="fixed"?-this.scrollParent.scrollTop():f?0:c.scrollTop())*a),left:b.left+this.offset.relative.left*a+this.offset.parent.left*a-(d.browser.safari&&d.browser.version<526&&this.cssPosition=="fixed"?0:(this.cssPosition=="fixed"?-this.scrollParent.scrollLeft():
+f?0:c.scrollLeft())*a)}},_generatePosition:function(a){var b=this.options,c=this.cssPosition=="absolute"&&!(this.scrollParent[0]!=document&&d.ui.contains(this.scrollParent[0],this.offsetParent[0]))?this.offsetParent:this.scrollParent,f=/(html|body)/i.test(c[0].tagName),e=a.pageX,g=a.pageY;if(this.originalPosition){if(this.containment){if(a.pageX-this.offset.click.left<this.containment[0])e=this.containment[0]+this.offset.click.left;if(a.pageY-this.offset.click.top<this.containment[1])g=this.containment[1]+
+this.offset.click.top;if(a.pageX-this.offset.click.left>this.containment[2])e=this.containment[2]+this.offset.click.left;if(a.pageY-this.offset.click.top>this.containment[3])g=this.containment[3]+this.offset.click.top}if(b.grid){g=this.originalPageY+Math.round((g-this.originalPageY)/b.grid[1])*b.grid[1];g=this.containment?!(g-this.offset.click.top<this.containment[1]||g-this.offset.click.top>this.containment[3])?g:!(g-this.offset.click.top<this.containment[1])?g-b.grid[1]:g+b.grid[1]:g;e=this.originalPageX+
+Math.round((e-this.originalPageX)/b.grid[0])*b.grid[0];e=this.containment?!(e-this.offset.click.left<this.containment[0]||e-this.offset.click.left>this.containment[2])?e:!(e-this.offset.click.left<this.containment[0])?e-b.grid[0]:e+b.grid[0]:e}}return{top:g-this.offset.click.top-this.offset.relative.top-this.offset.parent.top+(d.browser.safari&&d.browser.version<526&&this.cssPosition=="fixed"?0:this.cssPosition=="fixed"?-this.scrollParent.scrollTop():f?0:c.scrollTop()),left:e-this.offset.click.left-
+this.offset.relative.left-this.offset.parent.left+(d.browser.safari&&d.browser.version<526&&this.cssPosition=="fixed"?0:this.cssPosition=="fixed"?-this.scrollParent.scrollLeft():f?0:c.scrollLeft())}},_clear:function(){this.helper.removeClass("ui-draggable-dragging");this.helper[0]!=this.element[0]&&!this.cancelHelperRemoval&&this.helper.remove();this.helper=null;this.cancelHelperRemoval=false},_trigger:function(a,b,c){c=c||this._uiHash();d.ui.plugin.call(this,a,[b,c]);if(a=="drag")this.positionAbs=
+this._convertPositionTo("absolute");return d.Widget.prototype._trigger.call(this,a,b,c)},plugins:{},_uiHash:function(){return{helper:this.helper,position:this.position,originalPosition:this.originalPosition,offset:this.positionAbs}}});d.extend(d.ui.draggable,{version:"1.8.4"});d.ui.plugin.add("draggable","connectToSortable",{start:function(a,b){var c=d(this).data("draggable"),f=c.options,e=d.extend({},b,{item:c.element});c.sortables=[];d(f.connectToSortable).each(function(){var g=d.data(this,"sortable");
+if(g&&!g.options.disabled){c.sortables.push({instance:g,shouldRevert:g.options.revert});g._refreshItems();g._trigger("activate",a,e)}})},stop:function(a,b){var c=d(this).data("draggable"),f=d.extend({},b,{item:c.element});d.each(c.sortables,function(){if(this.instance.isOver){this.instance.isOver=0;c.cancelHelperRemoval=true;this.instance.cancelHelperRemoval=false;if(this.shouldRevert)this.instance.options.revert=true;this.instance._mouseStop(a);this.instance.options.helper=this.instance.options._helper;
+c.options.helper=="original"&&this.instance.currentItem.css({top:"auto",left:"auto"})}else{this.instance.cancelHelperRemoval=false;this.instance._trigger("deactivate",a,f)}})},drag:function(a,b){var c=d(this).data("draggable"),f=this;d.each(c.sortables,function(){this.instance.positionAbs=c.positionAbs;this.instance.helperProportions=c.helperProportions;this.instance.offset.click=c.offset.click;if(this.instance._intersectsWith(this.instance.containerCache)){if(!this.instance.isOver){this.instance.isOver=
+1;this.instance.currentItem=d(f).clone().appendTo(this.instance.element).data("sortable-item",true);this.instance.options._helper=this.instance.options.helper;this.instance.options.helper=function(){return b.helper[0]};a.target=this.instance.currentItem[0];this.instance._mouseCapture(a,true);this.instance._mouseStart(a,true,true);this.instance.offset.click.top=c.offset.click.top;this.instance.offset.click.left=c.offset.click.left;this.instance.offset.parent.left-=c.offset.parent.left-this.instance.offset.parent.left;
+this.instance.offset.parent.top-=c.offset.parent.top-this.instance.offset.parent.top;c._trigger("toSortable",a);c.dropped=this.instance.element;c.currentItem=c.element;this.instance.fromOutside=c}this.instance.currentItem&&this.instance._mouseDrag(a)}else if(this.instance.isOver){this.instance.isOver=0;this.instance.cancelHelperRemoval=true;this.instance.options.revert=false;this.instance._trigger("out",a,this.instance._uiHash(this.instance));this.instance._mouseStop(a,true);this.instance.options.helper=
+this.instance.options._helper;this.instance.currentItem.remove();this.instance.placeholder&&this.instance.placeholder.remove();c._trigger("fromSortable",a);c.dropped=false}})}});d.ui.plugin.add("draggable","cursor",{start:function(){var a=d("body"),b=d(this).data("draggable").options;if(a.css("cursor"))b._cursor=a.css("cursor");a.css("cursor",b.cursor)},stop:function(){var a=d(this).data("draggable").options;a._cursor&&d("body").css("cursor",a._cursor)}});d.ui.plugin.add("draggable","iframeFix",{start:function(){var a=
+d(this).data("draggable").options;d(a.iframeFix===true?"iframe":a.iframeFix).each(function(){d('<div class="ui-draggable-iframeFix" style="background: #fff;"></div>').css({width:this.offsetWidth+"px",height:this.offsetHeight+"px",position:"absolute",opacity:"0.001",zIndex:1E3}).css(d(this).offset()).appendTo("body")})},stop:function(){d("div.ui-draggable-iframeFix").each(function(){this.parentNode.removeChild(this)})}});d.ui.plugin.add("draggable","opacity",{start:function(a,b){a=d(b.helper);b=d(this).data("draggable").options;
+if(a.css("opacity"))b._opacity=a.css("opacity");a.css("opacity",b.opacity)},stop:function(a,b){a=d(this).data("draggable").options;a._opacity&&d(b.helper).css("opacity",a._opacity)}});d.ui.plugin.add("draggable","scroll",{start:function(){var a=d(this).data("draggable");if(a.scrollParent[0]!=document&&a.scrollParent[0].tagName!="HTML")a.overflowOffset=a.scrollParent.offset()},drag:function(a){var b=d(this).data("draggable"),c=b.options,f=false;if(b.scrollParent[0]!=document&&b.scrollParent[0].tagName!=
+"HTML"){if(!c.axis||c.axis!="x")if(b.overflowOffset.top+b.scrollParent[0].offsetHeight-a.pageY<c.scrollSensitivity)b.scrollParent[0].scrollTop=f=b.scrollParent[0].scrollTop+c.scrollSpeed;else if(a.pageY-b.overflowOffset.top<c.scrollSensitivity)b.scrollParent[0].scrollTop=f=b.scrollParent[0].scrollTop-c.scrollSpeed;if(!c.axis||c.axis!="y")if(b.overflowOffset.left+b.scrollParent[0].offsetWidth-a.pageX<c.scrollSensitivity)b.scrollParent[0].scrollLeft=f=b.scrollParent[0].scrollLeft+c.scrollSpeed;else if(a.pageX-
+b.overflowOffset.left<c.scrollSensitivity)b.scrollParent[0].scrollLeft=f=b.scrollParent[0].scrollLeft-c.scrollSpeed}else{if(!c.axis||c.axis!="x")if(a.pageY-d(document).scrollTop()<c.scrollSensitivity)f=d(document).scrollTop(d(document).scrollTop()-c.scrollSpeed);else if(d(window).height()-(a.pageY-d(document).scrollTop())<c.scrollSensitivity)f=d(document).scrollTop(d(document).scrollTop()+c.scrollSpeed);if(!c.axis||c.axis!="y")if(a.pageX-d(document).scrollLeft()<c.scrollSensitivity)f=d(document).scrollLeft(d(document).scrollLeft()-
+c.scrollSpeed);else if(d(window).width()-(a.pageX-d(document).scrollLeft())<c.scrollSensitivity)f=d(document).scrollLeft(d(document).scrollLeft()+c.scrollSpeed)}f!==false&&d.ui.ddmanager&&!c.dropBehaviour&&d.ui.ddmanager.prepareOffsets(b,a)}});d.ui.plugin.add("draggable","snap",{start:function(){var a=d(this).data("draggable"),b=a.options;a.snapElements=[];d(b.snap.constructor!=String?b.snap.items||":data(draggable)":b.snap).each(function(){var c=d(this),f=c.offset();this!=a.element[0]&&a.snapElements.push({item:this,
+width:c.outerWidth(),height:c.outerHeight(),top:f.top,left:f.left})})},drag:function(a,b){for(var c=d(this).data("draggable"),f=c.options,e=f.snapTolerance,g=b.offset.left,n=g+c.helperProportions.width,m=b.offset.top,o=m+c.helperProportions.height,h=c.snapElements.length-1;h>=0;h--){var i=c.snapElements[h].left,k=i+c.snapElements[h].width,j=c.snapElements[h].top,l=j+c.snapElements[h].height;if(i-e<g&&g<k+e&&j-e<m&&m<l+e||i-e<g&&g<k+e&&j-e<o&&o<l+e||i-e<n&&n<k+e&&j-e<m&&m<l+e||i-e<n&&n<k+e&&j-e<o&&
+o<l+e){if(f.snapMode!="inner"){var p=Math.abs(j-o)<=e,q=Math.abs(l-m)<=e,r=Math.abs(i-n)<=e,s=Math.abs(k-g)<=e;if(p)b.position.top=c._convertPositionTo("relative",{top:j-c.helperProportions.height,left:0}).top-c.margins.top;if(q)b.position.top=c._convertPositionTo("relative",{top:l,left:0}).top-c.margins.top;if(r)b.position.left=c._convertPositionTo("relative",{top:0,left:i-c.helperProportions.width}).left-c.margins.left;if(s)b.position.left=c._convertPositionTo("relative",{top:0,left:k}).left-c.margins.left}var t=
+p||q||r||s;if(f.snapMode!="outer"){p=Math.abs(j-m)<=e;q=Math.abs(l-o)<=e;r=Math.abs(i-g)<=e;s=Math.abs(k-n)<=e;if(p)b.position.top=c._convertPositionTo("relative",{top:j,left:0}).top-c.margins.top;if(q)b.position.top=c._convertPositionTo("relative",{top:l-c.helperProportions.height,left:0}).top-c.margins.top;if(r)b.position.left=c._convertPositionTo("relative",{top:0,left:i}).left-c.margins.left;if(s)b.position.left=c._convertPositionTo("relative",{top:0,left:k-c.helperProportions.width}).left-c.margins.left}if(!c.snapElements[h].snapping&&
+(p||q||r||s||t))c.options.snap.snap&&c.options.snap.snap.call(c.element,a,d.extend(c._uiHash(),{snapItem:c.snapElements[h].item}));c.snapElements[h].snapping=p||q||r||s||t}else{c.snapElements[h].snapping&&c.options.snap.release&&c.options.snap.release.call(c.element,a,d.extend(c._uiHash(),{snapItem:c.snapElements[h].item}));c.snapElements[h].snapping=false}}}});d.ui.plugin.add("draggable","stack",{start:function(){var a=d(this).data("draggable").options;a=d.makeArray(d(a.stack)).sort(function(c,f){return(parseInt(d(c).css("zIndex"),
+10)||0)-(parseInt(d(f).css("zIndex"),10)||0)});if(a.length){var b=parseInt(a[0].style.zIndex)||0;d(a).each(function(c){this.style.zIndex=b+c});this[0].style.zIndex=b+a.length}}});d.ui.plugin.add("draggable","zIndex",{start:function(a,b){a=d(b.helper);b=d(this).data("draggable").options;if(a.css("zIndex"))b._zIndex=a.css("zIndex");a.css("zIndex",b.zIndex)},stop:function(a,b){a=d(this).data("draggable").options;a._zIndex&&d(b.helper).css("zIndex",a._zIndex)}})})(jQuery);
+;/*
+ * jQuery UI Droppable 1.8.4
+ *
+ * Copyright 2010, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI/Droppables
+ *
+ * Depends:
+ * jquery.ui.core.js
+ * jquery.ui.widget.js
+ * jquery.ui.mouse.js
+ * jquery.ui.draggable.js
+ */
+(function(d){d.widget("ui.droppable",{widgetEventPrefix:"drop",options:{accept:"*",activeClass:false,addClasses:true,greedy:false,hoverClass:false,scope:"default",tolerance:"intersect"},_create:function(){var a=this.options,b=a.accept;this.isover=0;this.isout=1;this.accept=d.isFunction(b)?b:function(c){return c.is(b)};this.proportions={width:this.element[0].offsetWidth,height:this.element[0].offsetHeight};d.ui.ddmanager.droppables[a.scope]=d.ui.ddmanager.droppables[a.scope]||[];d.ui.ddmanager.droppables[a.scope].push(this);
+a.addClasses&&this.element.addClass("ui-droppable")},destroy:function(){for(var a=d.ui.ddmanager.droppables[this.options.scope],b=0;b<a.length;b++)a[b]==this&&a.splice(b,1);this.element.removeClass("ui-droppable ui-droppable-disabled").removeData("droppable").unbind(".droppable");return this},_setOption:function(a,b){if(a=="accept")this.accept=d.isFunction(b)?b:function(c){return c.is(b)};d.Widget.prototype._setOption.apply(this,arguments)},_activate:function(a){var b=d.ui.ddmanager.current;this.options.activeClass&&
+this.element.addClass(this.options.activeClass);b&&this._trigger("activate",a,this.ui(b))},_deactivate:function(a){var b=d.ui.ddmanager.current;this.options.activeClass&&this.element.removeClass(this.options.activeClass);b&&this._trigger("deactivate",a,this.ui(b))},_over:function(a){var b=d.ui.ddmanager.current;if(!(!b||(b.currentItem||b.element)[0]==this.element[0]))if(this.accept.call(this.element[0],b.currentItem||b.element)){this.options.hoverClass&&this.element.addClass(this.options.hoverClass);
+this._trigger("over",a,this.ui(b))}},_out:function(a){var b=d.ui.ddmanager.current;if(!(!b||(b.currentItem||b.element)[0]==this.element[0]))if(this.accept.call(this.element[0],b.currentItem||b.element)){this.options.hoverClass&&this.element.removeClass(this.options.hoverClass);this._trigger("out",a,this.ui(b))}},_drop:function(a,b){var c=b||d.ui.ddmanager.current;if(!c||(c.currentItem||c.element)[0]==this.element[0])return false;var e=false;this.element.find(":data(droppable)").not(".ui-draggable-dragging").each(function(){var g=
+d.data(this,"droppable");if(g.options.greedy&&!g.options.disabled&&g.options.scope==c.options.scope&&g.accept.call(g.element[0],c.currentItem||c.element)&&d.ui.intersect(c,d.extend(g,{offset:g.element.offset()}),g.options.tolerance)){e=true;return false}});if(e)return false;if(this.accept.call(this.element[0],c.currentItem||c.element)){this.options.activeClass&&this.element.removeClass(this.options.activeClass);this.options.hoverClass&&this.element.removeClass(this.options.hoverClass);this._trigger("drop",
+a,this.ui(c));return this.element}return false},ui:function(a){return{draggable:a.currentItem||a.element,helper:a.helper,position:a.position,offset:a.positionAbs}}});d.extend(d.ui.droppable,{version:"1.8.4"});d.ui.intersect=function(a,b,c){if(!b.offset)return false;var e=(a.positionAbs||a.position.absolute).left,g=e+a.helperProportions.width,f=(a.positionAbs||a.position.absolute).top,h=f+a.helperProportions.height,i=b.offset.left,k=i+b.proportions.width,j=b.offset.top,l=j+b.proportions.height;
+switch(c){case "fit":return i<=e&&g<=k&&j<=f&&h<=l;case "intersect":return i<e+a.helperProportions.width/2&&g-a.helperProportions.width/2<k&&j<f+a.helperProportions.height/2&&h-a.helperProportions.height/2<l;case "pointer":return d.ui.isOver((a.positionAbs||a.position.absolute).top+(a.clickOffset||a.offset.click).top,(a.positionAbs||a.position.absolute).left+(a.clickOffset||a.offset.click).left,j,i,b.proportions.height,b.proportions.width);case "touch":return(f>=j&&f<=l||h>=j&&h<=l||f<j&&h>l)&&(e>=
+i&&e<=k||g>=i&&g<=k||e<i&&g>k);default:return false}};d.ui.ddmanager={current:null,droppables:{"default":[]},prepareOffsets:function(a,b){var c=d.ui.ddmanager.droppables[a.options.scope]||[],e=b?b.type:null,g=(a.currentItem||a.element).find(":data(droppable)").andSelf(),f=0;a:for(;f<c.length;f++)if(!(c[f].options.disabled||a&&!c[f].accept.call(c[f].element[0],a.currentItem||a.element))){for(var h=0;h<g.length;h++)if(g[h]==c[f].element[0]){c[f].proportions.height=0;continue a}c[f].visible=c[f].element.css("display")!=
+"none";if(c[f].visible){c[f].offset=c[f].element.offset();c[f].proportions={width:c[f].element[0].offsetWidth,height:c[f].element[0].offsetHeight};e=="mousedown"&&c[f]._activate.call(c[f],b)}}},drop:function(a,b){var c=false;d.each(d.ui.ddmanager.droppables[a.options.scope]||[],function(){if(this.options){if(!this.options.disabled&&this.visible&&d.ui.intersect(a,this,this.options.tolerance))c=c||this._drop.call(this,b);if(!this.options.disabled&&this.visible&&this.accept.call(this.element[0],a.currentItem||
+a.element)){this.isout=1;this.isover=0;this._deactivate.call(this,b)}}});return c},drag:function(a,b){a.options.refreshPositions&&d.ui.ddmanager.prepareOffsets(a,b);d.each(d.ui.ddmanager.droppables[a.options.scope]||[],function(){if(!(this.options.disabled||this.greedyChild||!this.visible)){var c=d.ui.intersect(a,this,this.options.tolerance);if(c=!c&&this.isover==1?"isout":c&&this.isover==0?"isover":null){var e;if(this.options.greedy){var g=this.element.parents(":data(droppable):eq(0)");if(g.length){e=
+d.data(g[0],"droppable");e.greedyChild=c=="isover"?1:0}}if(e&&c=="isover"){e.isover=0;e.isout=1;e._out.call(e,b)}this[c]=1;this[c=="isout"?"isover":"isout"]=0;this[c=="isover"?"_over":"_out"].call(this,b);if(e&&c=="isout"){e.isout=0;e.isover=1;e._over.call(e,b)}}}})}}})(jQuery);
+;/*
+ * jQuery UI Sortable 1.8.4
+ *
+ * Copyright 2010, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI/Sortables
+ *
+ * Depends:
+ * jquery.ui.core.js
+ * jquery.ui.mouse.js
+ * jquery.ui.widget.js
+ */
+(function(d){d.widget("ui.sortable",d.ui.mouse,{widgetEventPrefix:"sort",options:{appendTo:"parent",axis:false,connectWith:false,containment:false,cursor:"auto",cursorAt:false,dropOnEmpty:true,forcePlaceholderSize:false,forceHelperSize:false,grid:false,handle:false,helper:"original",items:"> *",opacity:false,placeholder:false,revert:false,scroll:true,scrollSensitivity:20,scrollSpeed:20,scope:"default",tolerance:"intersect",zIndex:1E3},_create:function(){this.containerCache={};this.element.addClass("ui-sortable");
+this.refresh();this.floating=this.items.length?/left|right/.test(this.items[0].item.css("float")):false;this.offset=this.element.offset();this._mouseInit()},destroy:function(){this.element.removeClass("ui-sortable ui-sortable-disabled").removeData("sortable").unbind(".sortable");this._mouseDestroy();for(var a=this.items.length-1;a>=0;a--)this.items[a].item.removeData("sortable-item");return this},_setOption:function(a,b){if(a==="disabled"){this.options[a]=b;this.widget()[b?"addClass":"removeClass"]("ui-sortable-disabled")}else d.Widget.prototype._setOption.apply(this,
+arguments)},_mouseCapture:function(a,b){if(this.reverting)return false;if(this.options.disabled||this.options.type=="static")return false;this._refreshItems(a);var c=null,e=this;d(a.target).parents().each(function(){if(d.data(this,"sortable-item")==e){c=d(this);return false}});if(d.data(a.target,"sortable-item")==e)c=d(a.target);if(!c)return false;if(this.options.handle&&!b){var f=false;d(this.options.handle,c).find("*").andSelf().each(function(){if(this==a.target)f=true});if(!f)return false}this.currentItem=
+c;this._removeCurrentsFromItems();return true},_mouseStart:function(a,b,c){b=this.options;var e=this;this.currentContainer=this;this.refreshPositions();this.helper=this._createHelper(a);this._cacheHelperProportions();this._cacheMargins();this.scrollParent=this.helper.scrollParent();this.offset=this.currentItem.offset();this.offset={top:this.offset.top-this.margins.top,left:this.offset.left-this.margins.left};this.helper.css("position","absolute");this.cssPosition=this.helper.css("position");d.extend(this.offset,
+{click:{left:a.pageX-this.offset.left,top:a.pageY-this.offset.top},parent:this._getParentOffset(),relative:this._getRelativeOffset()});this.originalPosition=this._generatePosition(a);this.originalPageX=a.pageX;this.originalPageY=a.pageY;b.cursorAt&&this._adjustOffsetFromHelper(b.cursorAt);this.domPosition={prev:this.currentItem.prev()[0],parent:this.currentItem.parent()[0]};this.helper[0]!=this.currentItem[0]&&this.currentItem.hide();this._createPlaceholder();b.containment&&this._setContainment();
+if(b.cursor){if(d("body").css("cursor"))this._storedCursor=d("body").css("cursor");d("body").css("cursor",b.cursor)}if(b.opacity){if(this.helper.css("opacity"))this._storedOpacity=this.helper.css("opacity");this.helper.css("opacity",b.opacity)}if(b.zIndex){if(this.helper.css("zIndex"))this._storedZIndex=this.helper.css("zIndex");this.helper.css("zIndex",b.zIndex)}if(this.scrollParent[0]!=document&&this.scrollParent[0].tagName!="HTML")this.overflowOffset=this.scrollParent.offset();this._trigger("start",
+a,this._uiHash());this._preserveHelperProportions||this._cacheHelperProportions();if(!c)for(c=this.containers.length-1;c>=0;c--)this.containers[c]._trigger("activate",a,e._uiHash(this));if(d.ui.ddmanager)d.ui.ddmanager.current=this;d.ui.ddmanager&&!b.dropBehaviour&&d.ui.ddmanager.prepareOffsets(this,a);this.dragging=true;this.helper.addClass("ui-sortable-helper");this._mouseDrag(a);return true},_mouseDrag:function(a){this.position=this._generatePosition(a);this.positionAbs=this._convertPositionTo("absolute");
+if(!this.lastPositionAbs)this.lastPositionAbs=this.positionAbs;if(this.options.scroll){var b=this.options,c=false;if(this.scrollParent[0]!=document&&this.scrollParent[0].tagName!="HTML"){if(this.overflowOffset.top+this.scrollParent[0].offsetHeight-a.pageY<b.scrollSensitivity)this.scrollParent[0].scrollTop=c=this.scrollParent[0].scrollTop+b.scrollSpeed;else if(a.pageY-this.overflowOffset.top<b.scrollSensitivity)this.scrollParent[0].scrollTop=c=this.scrollParent[0].scrollTop-b.scrollSpeed;if(this.overflowOffset.left+
+this.scrollParent[0].offsetWidth-a.pageX<b.scrollSensitivity)this.scrollParent[0].scrollLeft=c=this.scrollParent[0].scrollLeft+b.scrollSpeed;else if(a.pageX-this.overflowOffset.left<b.scrollSensitivity)this.scrollParent[0].scrollLeft=c=this.scrollParent[0].scrollLeft-b.scrollSpeed}else{if(a.pageY-d(document).scrollTop()<b.scrollSensitivity)c=d(document).scrollTop(d(document).scrollTop()-b.scrollSpeed);else if(d(window).height()-(a.pageY-d(document).scrollTop())<b.scrollSensitivity)c=d(document).scrollTop(d(document).scrollTop()+
+b.scrollSpeed);if(a.pageX-d(document).scrollLeft()<b.scrollSensitivity)c=d(document).scrollLeft(d(document).scrollLeft()-b.scrollSpeed);else if(d(window).width()-(a.pageX-d(document).scrollLeft())<b.scrollSensitivity)c=d(document).scrollLeft(d(document).scrollLeft()+b.scrollSpeed)}c!==false&&d.ui.ddmanager&&!b.dropBehaviour&&d.ui.ddmanager.prepareOffsets(this,a)}this.positionAbs=this._convertPositionTo("absolute");if(!this.options.axis||this.options.axis!="y")this.helper[0].style.left=this.position.left+
+"px";if(!this.options.axis||this.options.axis!="x")this.helper[0].style.top=this.position.top+"px";for(b=this.items.length-1;b>=0;b--){c=this.items[b];var e=c.item[0],f=this._intersectsWithPointer(c);if(f)if(e!=this.currentItem[0]&&this.placeholder[f==1?"next":"prev"]()[0]!=e&&!d.ui.contains(this.placeholder[0],e)&&(this.options.type=="semi-dynamic"?!d.ui.contains(this.element[0],e):true)){this.direction=f==1?"down":"up";if(this.options.tolerance=="pointer"||this._intersectsWithSides(c))this._rearrange(a,
+c);else break;this._trigger("change",a,this._uiHash());break}}this._contactContainers(a);d.ui.ddmanager&&d.ui.ddmanager.drag(this,a);this._trigger("sort",a,this._uiHash());this.lastPositionAbs=this.positionAbs;return false},_mouseStop:function(a,b){if(a){d.ui.ddmanager&&!this.options.dropBehaviour&&d.ui.ddmanager.drop(this,a);if(this.options.revert){var c=this;b=c.placeholder.offset();c.reverting=true;d(this.helper).animate({left:b.left-this.offset.parent.left-c.margins.left+(this.offsetParent[0]==
+document.body?0:this.offsetParent[0].scrollLeft),top:b.top-this.offset.parent.top-c.margins.top+(this.offsetParent[0]==document.body?0:this.offsetParent[0].scrollTop)},parseInt(this.options.revert,10)||500,function(){c._clear(a)})}else this._clear(a,b);return false}},cancel:function(){var a=this;if(this.dragging){this._mouseUp();this.options.helper=="original"?this.currentItem.css(this._storedCSS).removeClass("ui-sortable-helper"):this.currentItem.show();for(var b=this.containers.length-1;b>=0;b--){this.containers[b]._trigger("deactivate",
+null,a._uiHash(this));if(this.containers[b].containerCache.over){this.containers[b]._trigger("out",null,a._uiHash(this));this.containers[b].containerCache.over=0}}}this.placeholder[0].parentNode&&this.placeholder[0].parentNode.removeChild(this.placeholder[0]);this.options.helper!="original"&&this.helper&&this.helper[0].parentNode&&this.helper.remove();d.extend(this,{helper:null,dragging:false,reverting:false,_noFinalSort:null});this.domPosition.prev?d(this.domPosition.prev).after(this.currentItem):
+d(this.domPosition.parent).prepend(this.currentItem);return this},serialize:function(a){var b=this._getItemsAsjQuery(a&&a.connected),c=[];a=a||{};d(b).each(function(){var e=(d(a.item||this).attr(a.attribute||"id")||"").match(a.expression||/(.+)[-=_](.+)/);if(e)c.push((a.key||e[1]+"[]")+"="+(a.key&&a.expression?e[1]:e[2]))});!c.length&&a.key&&c.push(a.key+"=");return c.join("&")},toArray:function(a){var b=this._getItemsAsjQuery(a&&a.connected),c=[];a=a||{};b.each(function(){c.push(d(a.item||this).attr(a.attribute||
+"id")||"")});return c},_intersectsWith:function(a){var b=this.positionAbs.left,c=b+this.helperProportions.width,e=this.positionAbs.top,f=e+this.helperProportions.height,g=a.left,h=g+a.width,i=a.top,k=i+a.height,j=this.offset.click.top,l=this.offset.click.left;j=e+j>i&&e+j<k&&b+l>g&&b+l<h;return this.options.tolerance=="pointer"||this.options.forcePointerForContainers||this.options.tolerance!="pointer"&&this.helperProportions[this.floating?"width":"height"]>a[this.floating?"width":"height"]?j:g<b+
+this.helperProportions.width/2&&c-this.helperProportions.width/2<h&&i<e+this.helperProportions.height/2&&f-this.helperProportions.height/2<k},_intersectsWithPointer:function(a){var b=d.ui.isOverAxis(this.positionAbs.top+this.offset.click.top,a.top,a.height);a=d.ui.isOverAxis(this.positionAbs.left+this.offset.click.left,a.left,a.width);b=b&&a;a=this._getDragVerticalDirection();var c=this._getDragHorizontalDirection();if(!b)return false;return this.floating?c&&c=="right"||a=="down"?2:1:a&&(a=="down"?
+2:1)},_intersectsWithSides:function(a){var b=d.ui.isOverAxis(this.positionAbs.top+this.offset.click.top,a.top+a.height/2,a.height);a=d.ui.isOverAxis(this.positionAbs.left+this.offset.click.left,a.left+a.width/2,a.width);var c=this._getDragVerticalDirection(),e=this._getDragHorizontalDirection();return this.floating&&e?e=="right"&&a||e=="left"&&!a:c&&(c=="down"&&b||c=="up"&&!b)},_getDragVerticalDirection:function(){var a=this.positionAbs.top-this.lastPositionAbs.top;return a!=0&&(a>0?"down":"up")},
+_getDragHorizontalDirection:function(){var a=this.positionAbs.left-this.lastPositionAbs.left;return a!=0&&(a>0?"right":"left")},refresh:function(a){this._refreshItems(a);this.refreshPositions();return this},_connectWith:function(){var a=this.options;return a.connectWith.constructor==String?[a.connectWith]:a.connectWith},_getItemsAsjQuery:function(a){var b=[],c=[],e=this._connectWith();if(e&&a)for(a=e.length-1;a>=0;a--)for(var f=d(e[a]),g=f.length-1;g>=0;g--){var h=d.data(f[g],"sortable");if(h&&h!=
+this&&!h.options.disabled)c.push([d.isFunction(h.options.items)?h.options.items.call(h.element):d(h.options.items,h.element).not(".ui-sortable-helper").not(".ui-sortable-placeholder"),h])}c.push([d.isFunction(this.options.items)?this.options.items.call(this.element,null,{options:this.options,item:this.currentItem}):d(this.options.items,this.element).not(".ui-sortable-helper").not(".ui-sortable-placeholder"),this]);for(a=c.length-1;a>=0;a--)c[a][0].each(function(){b.push(this)});return d(b)},_removeCurrentsFromItems:function(){for(var a=
+this.currentItem.find(":data(sortable-item)"),b=0;b<this.items.length;b++)for(var c=0;c<a.length;c++)a[c]==this.items[b].item[0]&&this.items.splice(b,1)},_refreshItems:function(a){this.items=[];this.containers=[this];var b=this.items,c=[[d.isFunction(this.options.items)?this.options.items.call(this.element[0],a,{item:this.currentItem}):d(this.options.items,this.element),this]],e=this._connectWith();if(e)for(var f=e.length-1;f>=0;f--)for(var g=d(e[f]),h=g.length-1;h>=0;h--){var i=d.data(g[h],"sortable");
+if(i&&i!=this&&!i.options.disabled){c.push([d.isFunction(i.options.items)?i.options.items.call(i.element[0],a,{item:this.currentItem}):d(i.options.items,i.element),i]);this.containers.push(i)}}for(f=c.length-1;f>=0;f--){a=c[f][1];e=c[f][0];h=0;for(g=e.length;h<g;h++){i=d(e[h]);i.data("sortable-item",a);b.push({item:i,instance:a,width:0,height:0,left:0,top:0})}}},refreshPositions:function(a){if(this.offsetParent&&this.helper)this.offset.parent=this._getParentOffset();for(var b=this.items.length-1;b>=
+0;b--){var c=this.items[b],e=this.options.toleranceElement?d(this.options.toleranceElement,c.item):c.item;if(!a){c.width=e.outerWidth();c.height=e.outerHeight()}e=e.offset();c.left=e.left;c.top=e.top}if(this.options.custom&&this.options.custom.refreshContainers)this.options.custom.refreshContainers.call(this);else for(b=this.containers.length-1;b>=0;b--){e=this.containers[b].element.offset();this.containers[b].containerCache.left=e.left;this.containers[b].containerCache.top=e.top;this.containers[b].containerCache.width=
+this.containers[b].element.outerWidth();this.containers[b].containerCache.height=this.containers[b].element.outerHeight()}return this},_createPlaceholder:function(a){var b=a||this,c=b.options;if(!c.placeholder||c.placeholder.constructor==String){var e=c.placeholder;c.placeholder={element:function(){var f=d(document.createElement(b.currentItem[0].nodeName)).addClass(e||b.currentItem[0].className+" ui-sortable-placeholder").removeClass("ui-sortable-helper")[0];if(!e)f.style.visibility="hidden";return f},
+update:function(f,g){if(!(e&&!c.forcePlaceholderSize)){g.height()||g.height(b.currentItem.innerHeight()-parseInt(b.currentItem.css("paddingTop")||0,10)-parseInt(b.currentItem.css("paddingBottom")||0,10));g.width()||g.width(b.currentItem.innerWidth()-parseInt(b.currentItem.css("paddingLeft")||0,10)-parseInt(b.currentItem.css("paddingRight")||0,10))}}}}b.placeholder=d(c.placeholder.element.call(b.element,b.currentItem));b.currentItem.after(b.placeholder);c.placeholder.update(b,b.placeholder)},_contactContainers:function(a){for(var b=
+null,c=null,e=this.containers.length-1;e>=0;e--)if(!d.ui.contains(this.currentItem[0],this.containers[e].element[0]))if(this._intersectsWith(this.containers[e].containerCache)){if(!(b&&d.ui.contains(this.containers[e].element[0],b.element[0]))){b=this.containers[e];c=e}}else if(this.containers[e].containerCache.over){this.containers[e]._trigger("out",a,this._uiHash(this));this.containers[e].containerCache.over=0}if(b)if(this.containers.length===1){this.containers[c]._trigger("over",a,this._uiHash(this));
+this.containers[c].containerCache.over=1}else if(this.currentContainer!=this.containers[c]){b=1E4;e=null;for(var f=this.positionAbs[this.containers[c].floating?"left":"top"],g=this.items.length-1;g>=0;g--)if(d.ui.contains(this.containers[c].element[0],this.items[g].item[0])){var h=this.items[g][this.containers[c].floating?"left":"top"];if(Math.abs(h-f)<b){b=Math.abs(h-f);e=this.items[g]}}if(e||this.options.dropOnEmpty){this.currentContainer=this.containers[c];e?this._rearrange(a,e,null,true):this._rearrange(a,
+null,this.containers[c].element,true);this._trigger("change",a,this._uiHash());this.containers[c]._trigger("change",a,this._uiHash(this));this.options.placeholder.update(this.currentContainer,this.placeholder);this.containers[c]._trigger("over",a,this._uiHash(this));this.containers[c].containerCache.over=1}}},_createHelper:function(a){var b=this.options;a=d.isFunction(b.helper)?d(b.helper.apply(this.element[0],[a,this.currentItem])):b.helper=="clone"?this.currentItem.clone():this.currentItem;a.parents("body").length||
+d(b.appendTo!="parent"?b.appendTo:this.currentItem[0].parentNode)[0].appendChild(a[0]);if(a[0]==this.currentItem[0])this._storedCSS={width:this.currentItem[0].style.width,height:this.currentItem[0].style.height,position:this.currentItem.css("position"),top:this.currentItem.css("top"),left:this.currentItem.css("left")};if(a[0].style.width==""||b.forceHelperSize)a.width(this.currentItem.width());if(a[0].style.height==""||b.forceHelperSize)a.height(this.currentItem.height());return a},_adjustOffsetFromHelper:function(a){if(typeof a==
+"string")a=a.split(" ");if(d.isArray(a))a={left:+a[0],top:+a[1]||0};if("left"in a)this.offset.click.left=a.left+this.margins.left;if("right"in a)this.offset.click.left=this.helperProportions.width-a.right+this.margins.left;if("top"in a)this.offset.click.top=a.top+this.margins.top;if("bottom"in a)this.offset.click.top=this.helperProportions.height-a.bottom+this.margins.top},_getParentOffset:function(){this.offsetParent=this.helper.offsetParent();var a=this.offsetParent.offset();if(this.cssPosition==
+"absolute"&&this.scrollParent[0]!=document&&d.ui.contains(this.scrollParent[0],this.offsetParent[0])){a.left+=this.scrollParent.scrollLeft();a.top+=this.scrollParent.scrollTop()}if(this.offsetParent[0]==document.body||this.offsetParent[0].tagName&&this.offsetParent[0].tagName.toLowerCase()=="html"&&d.browser.msie)a={top:0,left:0};return{top:a.top+(parseInt(this.offsetParent.css("borderTopWidth"),10)||0),left:a.left+(parseInt(this.offsetParent.css("borderLeftWidth"),10)||0)}},_getRelativeOffset:function(){if(this.cssPosition==
+"relative"){var a=this.currentItem.position();return{top:a.top-(parseInt(this.helper.css("top"),10)||0)+this.scrollParent.scrollTop(),left:a.left-(parseInt(this.helper.css("left"),10)||0)+this.scrollParent.scrollLeft()}}else return{top:0,left:0}},_cacheMargins:function(){this.margins={left:parseInt(this.currentItem.css("marginLeft"),10)||0,top:parseInt(this.currentItem.css("marginTop"),10)||0}},_cacheHelperProportions:function(){this.helperProportions={width:this.helper.outerWidth(),height:this.helper.outerHeight()}},
+_setContainment:function(){var a=this.options;if(a.containment=="parent")a.containment=this.helper[0].parentNode;if(a.containment=="document"||a.containment=="window")this.containment=[0-this.offset.relative.left-this.offset.parent.left,0-this.offset.relative.top-this.offset.parent.top,d(a.containment=="document"?document:window).width()-this.helperProportions.width-this.margins.left,(d(a.containment=="document"?document:window).height()||document.body.parentNode.scrollHeight)-this.helperProportions.height-
+this.margins.top];if(!/^(document|window|parent)$/.test(a.containment)){var b=d(a.containment)[0];a=d(a.containment).offset();var c=d(b).css("overflow")!="hidden";this.containment=[a.left+(parseInt(d(b).css("borderLeftWidth"),10)||0)+(parseInt(d(b).css("paddingLeft"),10)||0)-this.margins.left,a.top+(parseInt(d(b).css("borderTopWidth"),10)||0)+(parseInt(d(b).css("paddingTop"),10)||0)-this.margins.top,a.left+(c?Math.max(b.scrollWidth,b.offsetWidth):b.offsetWidth)-(parseInt(d(b).css("borderLeftWidth"),
+10)||0)-(parseInt(d(b).css("paddingRight"),10)||0)-this.helperProportions.width-this.margins.left,a.top+(c?Math.max(b.scrollHeight,b.offsetHeight):b.offsetHeight)-(parseInt(d(b).css("borderTopWidth"),10)||0)-(parseInt(d(b).css("paddingBottom"),10)||0)-this.helperProportions.height-this.margins.top]}},_convertPositionTo:function(a,b){if(!b)b=this.position;a=a=="absolute"?1:-1;var c=this.cssPosition=="absolute"&&!(this.scrollParent[0]!=document&&d.ui.contains(this.scrollParent[0],this.offsetParent[0]))?
+this.offsetParent:this.scrollParent,e=/(html|body)/i.test(c[0].tagName);return{top:b.top+this.offset.relative.top*a+this.offset.parent.top*a-(d.browser.safari&&this.cssPosition=="fixed"?0:(this.cssPosition=="fixed"?-this.scrollParent.scrollTop():e?0:c.scrollTop())*a),left:b.left+this.offset.relative.left*a+this.offset.parent.left*a-(d.browser.safari&&this.cssPosition=="fixed"?0:(this.cssPosition=="fixed"?-this.scrollParent.scrollLeft():e?0:c.scrollLeft())*a)}},_generatePosition:function(a){var b=
+this.options,c=this.cssPosition=="absolute"&&!(this.scrollParent[0]!=document&&d.ui.contains(this.scrollParent[0],this.offsetParent[0]))?this.offsetParent:this.scrollParent,e=/(html|body)/i.test(c[0].tagName);if(this.cssPosition=="relative"&&!(this.scrollParent[0]!=document&&this.scrollParent[0]!=this.offsetParent[0]))this.offset.relative=this._getRelativeOffset();var f=a.pageX,g=a.pageY;if(this.originalPosition){if(this.containment){if(a.pageX-this.offset.click.left<this.containment[0])f=this.containment[0]+
+this.offset.click.left;if(a.pageY-this.offset.click.top<this.containment[1])g=this.containment[1]+this.offset.click.top;if(a.pageX-this.offset.click.left>this.containment[2])f=this.containment[2]+this.offset.click.left;if(a.pageY-this.offset.click.top>this.containment[3])g=this.containment[3]+this.offset.click.top}if(b.grid){g=this.originalPageY+Math.round((g-this.originalPageY)/b.grid[1])*b.grid[1];g=this.containment?!(g-this.offset.click.top<this.containment[1]||g-this.offset.click.top>this.containment[3])?
+g:!(g-this.offset.click.top<this.containment[1])?g-b.grid[1]:g+b.grid[1]:g;f=this.originalPageX+Math.round((f-this.originalPageX)/b.grid[0])*b.grid[0];f=this.containment?!(f-this.offset.click.left<this.containment[0]||f-this.offset.click.left>this.containment[2])?f:!(f-this.offset.click.left<this.containment[0])?f-b.grid[0]:f+b.grid[0]:f}}return{top:g-this.offset.click.top-this.offset.relative.top-this.offset.parent.top+(d.browser.safari&&this.cssPosition=="fixed"?0:this.cssPosition=="fixed"?-this.scrollParent.scrollTop():
+e?0:c.scrollTop()),left:f-this.offset.click.left-this.offset.relative.left-this.offset.parent.left+(d.browser.safari&&this.cssPosition=="fixed"?0:this.cssPosition=="fixed"?-this.scrollParent.scrollLeft():e?0:c.scrollLeft())}},_rearrange:function(a,b,c,e){c?c[0].appendChild(this.placeholder[0]):b.item[0].parentNode.insertBefore(this.placeholder[0],this.direction=="down"?b.item[0]:b.item[0].nextSibling);this.counter=this.counter?++this.counter:1;var f=this,g=this.counter;window.setTimeout(function(){g==
+f.counter&&f.refreshPositions(!e)},0)},_clear:function(a,b){this.reverting=false;var c=[];!this._noFinalSort&&this.currentItem[0].parentNode&&this.placeholder.before(this.currentItem);this._noFinalSort=null;if(this.helper[0]==this.currentItem[0]){for(var e in this._storedCSS)if(this._storedCSS[e]=="auto"||this._storedCSS[e]=="static")this._storedCSS[e]="";this.currentItem.css(this._storedCSS).removeClass("ui-sortable-helper")}else this.currentItem.show();this.fromOutside&&!b&&c.push(function(f){this._trigger("receive",
+f,this._uiHash(this.fromOutside))});if((this.fromOutside||this.domPosition.prev!=this.currentItem.prev().not(".ui-sortable-helper")[0]||this.domPosition.parent!=this.currentItem.parent()[0])&&!b)c.push(function(f){this._trigger("update",f,this._uiHash())});if(!d.ui.contains(this.element[0],this.currentItem[0])){b||c.push(function(f){this._trigger("remove",f,this._uiHash())});for(e=this.containers.length-1;e>=0;e--)if(d.ui.contains(this.containers[e].element[0],this.currentItem[0])&&!b){c.push(function(f){return function(g){f._trigger("receive",
+g,this._uiHash(this))}}.call(this,this.containers[e]));c.push(function(f){return function(g){f._trigger("update",g,this._uiHash(this))}}.call(this,this.containers[e]))}}for(e=this.containers.length-1;e>=0;e--){b||c.push(function(f){return function(g){f._trigger("deactivate",g,this._uiHash(this))}}.call(this,this.containers[e]));if(this.containers[e].containerCache.over){c.push(function(f){return function(g){f._trigger("out",g,this._uiHash(this))}}.call(this,this.containers[e]));this.containers[e].containerCache.over=
+0}}this._storedCursor&&d("body").css("cursor",this._storedCursor);this._storedOpacity&&this.helper.css("opacity",this._storedOpacity);if(this._storedZIndex)this.helper.css("zIndex",this._storedZIndex=="auto"?"":this._storedZIndex);this.dragging=false;if(this.cancelHelperRemoval){if(!b){this._trigger("beforeStop",a,this._uiHash());for(e=0;e<c.length;e++)c[e].call(this,a);this._trigger("stop",a,this._uiHash())}return false}b||this._trigger("beforeStop",a,this._uiHash());this.placeholder[0].parentNode.removeChild(this.placeholder[0]);
+this.helper[0]!=this.currentItem[0]&&this.helper.remove();this.helper=null;if(!b){for(e=0;e<c.length;e++)c[e].call(this,a);this._trigger("stop",a,this._uiHash())}this.fromOutside=false;return true},_trigger:function(){d.Widget.prototype._trigger.apply(this,arguments)===false&&this.cancel()},_uiHash:function(a){var b=a||this;return{helper:b.helper,placeholder:b.placeholder||d([]),position:b.position,originalPosition:b.originalPosition,offset:b.positionAbs,item:b.currentItem,sender:a?a.element:null}}});
+d.extend(d.ui.sortable,{version:"1.8.4"})})(jQuery);
+; \ No newline at end of file
diff --git a/plugins/jetpack/modules/contact-form/readme.txt b/plugins/jetpack/modules/contact-form/readme.txt
new file mode 100644
index 00000000..ff4958f1
--- /dev/null
+++ b/plugins/jetpack/modules/contact-form/readme.txt
@@ -0,0 +1,131 @@
+=== Grunion Contact Form ===
+Contributors: mdawaffe, automattic, nickmomrik
+Tags: WordPress.com, contact form, email
+Stable tag: 2.3
+Requires at least: 3.0
+Tested up to: 3.3
+
+Add a contact form to any post, page or text widget. Messages will be sent to any email address you choose. As seen on WordPress.com.
+
+== Description ==
+
+Add a contact form to any post or page by inserting `[contact-form]` in the post. Messages will be sent to the post's author or any email address you choose.
+
+Or add a contact form ta a text widget. Messages will be sent to the email address set in your Settings -> General admin panel or any email address you choose.
+
+Your email address is never shown, and the sender never learns it (unless you reply to the email).
+
+As seen on WordPress.com.
+
+= Configuration =
+
+The `[contact-form]` shortcode has the following parameters:
+
+* `to`: A comma separated list of email addresses to which the messages will be sent.
+ If you leave this blank: contact forms in posts and pages will send messages to the post or page's author; and
+ contact forms in text widgets will send messages to the email address set in Settings -> General.
+
+ Example: `[contact-form to="you@me.com"]`
+
+ Example: `[contact-form to="you@me.com,me@you.com,us@them.com"]`
+
+* `subject`: The e-mail subject of the message defaults to `[{Blog Title}] {Sidebar}` for text widgets
+ and `[{Blog Title}] {Post Title}` for posts and pages. Set your own default with the subject option.
+
+ Example: `[contact-form subject="My Contact Form"]`
+
+* `show_subject`: You can allow the user to edit the subject by showing a new field on the form. The
+ field will be populated with the default subject or the subject you have set with the previous option.
+
+ Example: `[contact-form subject="My Contact Form" show_subject="yes"]`
+
+== Frequently Asked Questions ==
+
+= What's a Grunion? =
+
+The plugin was written in Southern California, home of an unusual fish call the [Grunion](http://en.wikipedia.org/wiki/Grunion).
+There's no correlation between fish and contact forms as far as I can tell; it's just a fun sounding word that's geographically apropos.
+
+= What about spam? Will I get a lot from the contact form? =
+
+If you have [Akismet](http://akismet.com/) installed on your blog, you shouldn't get much spam.
+All the messages people send to you through the contact form will be filtered through Akismet.
+
+= Anyone can put whatever they want in the name and email boxes. How can I know who's really sending the message? =
+
+If a logged member of your site sends you a message, the end of the email will let you know that the message was sent by a verified user.
+Otherwise, you can't trust anything... just like a blog comment.
+
+Anonymity is both a curse and a blessing :)
+
+= My blog has multiple authors. Who gets the email? =
+
+By default, the email is sent to the author of the post with the contact form in it. So each author on your blog can have his or her own contact form.
+
+In the contact form shortcode, you can specify what email address(es) messages should be sent to with the `to` parameter.
+
+= Great! But how will my visitors know who they're sending a message to? =
+
+Just make the title of your post "Contact Mary" or put "Hey, drop John a line with the form below" in the body of your post.
+
+== Changelog ==
+
+= 2.4 =
+* Support forms with no email address
+* Don't include *** SPAM *** in the subject line of unspammed items
+* Fix form processing when there is more than one form on a page (Koff)
+
+= 2.3 =
+* Fix for Chrome going 'oh snap' when inserting a new contact form
+* Fix for export/import issue that created users incorrectly
+* Add 'grunion_pre_message_sent' action (dimadin)
+* Updates to work with WordPress 3.3+ changes to wp-admin UI
+
+= 2.2 =
+* Only load grunion.css on pages with a contact form
+* Be better about checking for valid email addresses
+* Track the permalink for the contact form that was used and provide that detail in the email and Feedbacks page
+* Fix for the problem where every feedback would be marked as spam if the Akismet plugin wasn't activated
+* Make sure wp_kses only gets used on strings
+* New filter: grunion_still_email_spam, for those that want to still gets emails even when a feedback has been flagged as spam
+
+= 2.1 =
+* Fix error where the form builder wouldn't load correctly when using mapped domains
+* Don't attempt to check form submission for spam unless the Akismet plugin is active
+* Fixed form styles so that they wouldn't affect forms outside grunion container
+* Fixed "Add form to post" button in IE7
+* Fixed "re-arrange" in drag and drop so that it doesn't hard return
+* Hid "re-arrange" once you start dragging
+* Fixed issue where if you had content in the post, and added a form the content disappeared
+* Fixed form field width for when form is in the sidebar
+* Fixed First textarea label is always missing in email
+* Fixed Name, Email, Web site are always shown in default labels
+* Fixed Result page only shows the first textarea message
+* Fixed Results missing in the email notification
+* Limit form field submissions to valid text
+* Fixed broken path to button-grad-active.png
+* Encode drop down options so that quotes and commas work correctly
+* Encode quotes in field labels so that they show up correctly
+* Fix the from address formatting in the email headers
+* Use the correct bulk action filter to limit the list to supported actions only
+* Removed inline styles
+* Moved basic form CSS to include
+* Changed HTML shortcode insert from .html() to .val()
+* Fixed HTML encoding of option values for select fields
+
+= 1.2 =
+* Fix a PHP Warning in some CGI evironments.
+
+= 1.1 =
+* Move to shortcode API.
+* Add `to`, `subject` and `show-subject` options.
+* Allow use in text widgets.
+* Move spam check to a filter.
+
+== Upgrade Notice ==
+
+= 1.2 =
+Fixes a PHP Warning.
+
+= 1.1 =
+Now with more options!
diff --git a/plugins/jetpack/modules/enhanced-distribution.php b/plugins/jetpack/modules/enhanced-distribution.php
new file mode 100644
index 00000000..c3bc7aaf
--- /dev/null
+++ b/plugins/jetpack/modules/enhanced-distribution.php
@@ -0,0 +1,9 @@
+<?php
+/**
+ * Module Name: Enhanced Distribution
+ * Module Description: Share your public posts and comments to search engines and other services in real-time.
+ * Sort Order: 100
+ * First Introduced: 1.2
+ */
+
+// Stub
diff --git a/plugins/jetpack/modules/gravatar-hovercards.php b/plugins/jetpack/modules/gravatar-hovercards.php
new file mode 100644
index 00000000..e3e8e66e
--- /dev/null
+++ b/plugins/jetpack/modules/gravatar-hovercards.php
@@ -0,0 +1,270 @@
+<?php
+/**
+ * Module Name: Gravatar Hovercards
+ * Module Description: Show a pop-up business card of your users' gravatar profiles in comments.
+ * Sort Order: 5
+ * First Introduced: 1.1
+ */
+
+define( 'GROFILES__CACHE_BUSTER', 'aa' ); // Break CDN cache, increment when gravatar.com/js/gprofiles.js changes
+
+function grofiles_hovercards_init() {
+ add_filter( 'get_avatar', 'grofiles_get_avatar', 10, 2 );
+ add_action( 'wp_footer', 'grofiles_attach_cards' );
+ add_action( 'wp_footer', 'grofiles_extra_data' );
+ add_action( 'admin_init', 'grofiles_add_settings' );
+
+ add_action( 'load-index.php', 'grofiles_admin_cards' );
+ add_action( 'load-users.php', 'grofiles_admin_cards' );
+ add_action( 'load-edit-comments.php', 'grofiles_admin_cards' );
+ add_action( 'load-options-discussion.php', 'grofiles_admin_cards_forced' );
+
+ Jetpack::enable_module_configurable( __FILE__ );
+ Jetpack::module_configuration_load( __FILE__, 'gravatar_hovercards_configuration_load' );
+}
+
+function gravatar_hovercards_configuration_load() {
+ wp_safe_redirect( admin_url( 'options-discussion.php#gravatar-hovercard-options' ) );
+ exit;
+}
+
+add_action( 'jetpack_modules_loaded', 'grofiles_hovercards_init' );
+
+/* Hovercard Settings */
+
+/**
+ * Adds Gravatar Hovercard setting
+ *
+ * @todo - always print HTML, hide via CSS/JS if !show_avatars
+ */
+function grofiles_add_settings() {
+ if ( !get_option( 'show_avatars' ) )
+ return;
+
+ add_settings_field( 'gravatar_disable_hovercards', __( 'Gravatar Hovercards', 'jetpack' ), 'grofiles_setting_callback', 'discussion', 'avatars' );
+ register_setting( 'discussion', 'gravatar_disable_hovercards', 'grofiles_hovercard_option_sanitize' );
+}
+
+/**
+ * HTML for Gravatar Hovercard setting
+ */
+function grofiles_setting_callback() {
+ global $current_user;
+
+ $checked = 'disabled' == get_option( 'gravatar_disable_hovercards' ) ? '' : 'checked="checked" ';
+
+ echo "<label id='gravatar-hovercard-options'><input {$checked}name='gravatar_disable_hovercards' id='gravatar_disable_hovercards' type='checkbox' value='enabled' class='code' /> " . __( "View people's profiles when you mouse over their Gravatars", 'jetpack' ) . "</label>";
+?>
+<style type="text/css">
+#grav-profile-example img {
+ float: left;
+}
+#grav-profile-example span {
+ padding: 0 1em;
+}
+</style>
+<script type="text/javascript">
+// <![CDATA[
+jQuery( function($) {
+ var tr = $( '#gravatar_disable_hovercards' ).change( function() {
+ if ( $( this ).is( ':checked' ) ) {
+ $( '#grav-profile-example' ).slideDown( 'fast' );
+ } else {
+ $( '#grav-profile-example' ).slideUp( 'fast' );
+ }
+ } ).parents( 'tr' );
+ var ftr = tr.parents( 'table' ).find( 'tr:first' );
+ if ( ftr.size() && !ftr.find( '#gravatar_disable_hovercards' ).size() ) {
+ ftr.after( tr );
+ }
+} );
+// ]]>
+</script>
+ <p id="grav-profile-example" class="hide-if-no-js"<?php if ( !$checked ) echo ' style="display:none"'; ?>><?php echo get_avatar( $current_user->ID, 64 ); ?> <span><?php _e( 'Put your mouse over your Gravatar to check out your profile.', 'jetpack' ); ?> <br class="clear" /></span></p>
+<?php
+}
+
+/**
+ * Sanitation filter for Gravatar Hovercard setting
+ */
+function grofiles_hovercard_option_sanitize( $val ) {
+ if ( 'disabled' == $val ) {
+ return $val;
+ }
+
+ return $val ? 'enabled' : 'disabled';
+}
+
+
+/* Hovercard Display */
+
+/**
+ * Stores the gravatars' users that need extra profile data attached.
+ *
+ * Getter/Setter
+ *
+ * @param int|string|null $author Setter: User ID or email address. Getter: null.
+ *
+ * @return mixed Setter: void. Getter: array of user IDs and email addresses.
+ */
+function grofiles_gravatars_to_append( $author = null ) {
+ static $authors = array();
+
+ // Get
+ if ( is_null( $author ) ) {
+ return array_keys( $authors );
+ }
+
+ // Set
+
+ if ( is_numeric( $author ) ) {
+ $author = (int) $author;
+ }
+
+ $authors[$author] = true;
+}
+
+/**
+ * Stores the user ID or email address for each gravatar generated.
+ *
+ * Attached to the 'get_avatar' filter.
+ *
+ * @param string $avatar The <img/> element of the avatar.
+ * @param mixed $author User ID, email address, user login, comment object, user object, post object
+ *
+ * @return The <img/> element of the avatar.
+ */
+function grofiles_get_avatar( $avatar, $author ) {
+ if ( is_numeric( $author ) ) {
+ grofiles_gravatars_to_append( $author );
+ } else if ( is_string( $author ) ) {
+ if ( false !== strpos( $author, '@' ) ) {
+ grofiles_gravatars_to_append( $author );
+ } else {
+ if ( $user = get_userdatabylogin( $author ) )
+ grofiles_gravatars_to_append( $user->ID );
+ }
+ } else if ( isset( $author->comment_type ) ) {
+ if ( '' != $author->comment_type && 'comment' != $author->comment_type )
+ return $avatar;
+ if ( $author->user_id )
+ grofiles_gravatars_to_append( $author->user_id );
+ else
+ grofiles_gravatars_to_append( $author->comment_author_email );
+ } else if ( isset( $author->user_login ) ) {
+ grofiles_gravatars_to_append( $author->ID );
+ } else if ( isset( $author->post_author ) ) {
+ grofiles_gravatars_to_append( $author->post_author );
+ }
+
+ return $avatar;
+}
+
+/**
+ * Loads Gravatar Hovercard script.
+ *
+ * @todo is_singular() only?
+ */
+function grofiles_attach_cards() {
+ global $blog_id;
+
+ if ( 'disabled' == get_option( 'gravatar_disable_hovercards' ) )
+ return;
+
+ wp_enqueue_script( 'grofiles-cards', ( is_ssl() ? 'https://secure' : 'http://s' ) . '.gravatar.com/js/gprofiles.js?' . GROFILES__CACHE_BUSTER, array( 'jquery' ) );
+ wp_enqueue_script( 'wpgroho', plugins_url( 'wpgroho.js', __FILE__ ), array( 'grofiles-cards' ) );
+ if ( is_user_logged_in() ) {
+ $cu = wp_get_current_user();
+ $my_hash = md5( $cu->user_email );
+ } else if ( !empty( $_COOKIE['comment_author_email_' . COOKIEHASH] ) ) {
+ $my_hash = md5( $_COOKIE['comment_author_email_' . COOKIEHASH] );
+ } else {
+ $my_hash = '';
+ }
+ wp_localize_script( 'wpgroho', 'WPGroHo', compact( 'my_hash' ) );
+ wp_print_scripts( 'wpgroho' );
+}
+
+function grofiles_attach_cards_forced() {
+ add_filter( 'pre_option_gravatar_disable_hovercards', 'grofiles_force_gravatar_enable_hovercards' );
+ grofiles_attach_cards();
+}
+
+function grofiles_force_gravatar_enable_hovercards() {
+ return 'enabled';
+}
+
+function grofiles_admin_cards_forced() {
+ add_action( 'admin_footer', 'grofiles_attach_cards_forced' );
+}
+
+function grofiles_admin_cards() {
+ add_action( 'admin_footer', 'grofiles_attach_cards' );
+}
+
+function grofiles_extra_data() {
+?>
+ <div style="display:none">
+<?php
+ foreach ( grofiles_gravatars_to_append() as $author )
+ grofiles_hovercards_data_html( $author );
+?>
+ </div>
+<?php
+}
+
+/**
+ * Echoes the data from grofiles_hovercards_data() as HTML elements.
+ *
+ * @param int|string $author User ID or email address
+ */
+function grofiles_hovercards_data_html( $author ) {
+ $data = grofiles_hovercards_data( $author );
+ if ( is_numeric( $author ) ) {
+ $user = get_userdata( $author );
+ $hash = md5( $user->user_email );
+ } else {
+ $hash = md5( $author );
+ }
+?>
+ <div class="grofile-hash-map-<?php echo $hash; ?>">
+<?php foreach ( $data as $key => $value ) : ?>
+ <span class="<?php echo esc_attr( $key ); ?>"><?php echo esc_html( $value ); ?></span>
+<?php endforeach; ?>
+ </div>
+<?php
+}
+
+
+/* API */
+
+/**
+ * Returns the PHP callbacks for data sources.
+ *
+ * 'grofiles_hovercards_data_callbacks' filter
+ *
+ * @return array( data_key => data_callback, ... )
+ */
+function grofiles_hovercards_data_callbacks() {
+ return apply_filters( 'grofiles_hovercards_data_callbacks', array() );
+}
+
+/**
+ * Keyed JSON object containing all profile data provided by registered callbacks
+ *
+ * @param int|strung $author User ID or email address
+ *
+ * @return array( data_key => data, ... )
+ */
+function grofiles_hovercards_data( $author ) {
+ $r = array();
+ foreach ( grofiles_hovercards_data_callbacks() as $key => $callback ) {
+ if ( !is_callable( $callback ) )
+ continue;
+ $data = call_user_func( $callback, $author, $key );
+ if ( !is_null( $data ) )
+ $r[$key] = $data;
+ }
+
+ return $r;
+}
diff --git a/plugins/jetpack/modules/latex.php b/plugins/jetpack/modules/latex.php
new file mode 100644
index 00000000..fe20647d
--- /dev/null
+++ b/plugins/jetpack/modules/latex.php
@@ -0,0 +1,103 @@
+<?php
+/**
+ * Module Name: Beautiful Math
+ * Module Description: Mark up your posts with the <img src="http://l.wordpress.com/latex.php?latex=%5CLaTeX&amp;bg=transparent&amp;fg=000&amp;s=-2" alt="LaTeX logo" title="LaTeX" style="vertical-align: -25%" /> markup language, perfect for complex mathematical equations and other &#252;ber-geekery.
+ * Sort Order: 15
+ * First Introduced: 1.1
+ */
+
+/**
+ * LaTeX support.
+ *
+ * Backward compatibility requires support for both "[latex][/latex]", and
+ * "$latex $" shortcodes.
+ *
+ * $latex e^{\i \pi} + 1 = 0$ -> [latex]e^{\i \pi} + 1 = 0[/latex]
+ * $latex [a, b]$ -> [latex][a, b][/latex]
+ */
+
+function latex_markup( $content ) {
+ $regex = '%
+ \$latex(?:=\s*|\s+)
+ ((?:
+ [^$]+ # Not a dollar
+ |
+ (?<=(?<!\\\\)\\\\)\$ # Dollar preceded by exactly one slash
+ )+)
+ (?<!\\\\)\$ # Dollar preceded by zero slashes
+ %ix';
+ return preg_replace_callback( $regex, 'latex_src', $content );
+}
+
+function latex_src( $matches ) {
+ $latex = $matches[1];
+
+ $bg = latex_get_default_color( 'bg' );
+ $fg = latex_get_default_color( 'text', '000' );
+ $s = 0;
+
+
+ $latex = latex_entity_decode( $latex );
+ if ( preg_match( '/.+(&fg=[0-9a-f]{6}).*/i', $latex, $fg_matches ) ) {
+ $fg = substr( $fg_matches[1], 4 );
+ $latex = str_replace( $fg_matches[1], '', $latex );
+ }
+ if ( preg_match( '/.+(&bg=[0-9a-f]{6}).*/i', $latex, $bg_matches ) ) {
+ $bg = substr( $bg_matches[1], 4 );
+ $latex = str_replace( $bg_matches[1], '', $latex );
+ }
+ if ( preg_match( '/.+(&s=[0-9-]{1,2}).*/i', $latex, $s_matches ) ) {
+ $s = (int) substr( $s_matches[1], 3 );
+ $latex = str_replace( $s_matches[1], '', $latex );
+ }
+
+ return latex_render( $latex, $fg, $bg, $s );
+}
+
+function latex_get_default_color( $color, $default_color = 'ffffff' ) {
+ global $themecolors;
+ return isset($themecolors[$color]) ? $themecolors[$color] : $default_color;
+}
+
+function latex_entity_decode( $latex ) {
+ return str_replace( array( '&lt;', '&gt;', '&quot;', '&#039;', '&#038;', '&amp;', "\n", "\r" ), array( '<', '>', '"', "'", '&', '&', ' ', ' ' ), $latex );
+}
+
+function latex_render( $latex, $fg, $bg, $s = 0 ) {
+ $url = ( is_ssl() ? 'https://s-ssl.wordpress.com' : 'http://s0.wp.com' ) . "/latex.php?latex=" . urlencode( $latex ) . "&bg=$bg&fg=$fg&s=$s";
+ $url = esc_url( $url );
+ $alt = str_replace( '\\', '&#92;', esc_attr( $latex ) );
+
+ return "<img src='$url' alt='$alt' title='$alt' class='latex' />";
+}
+
+/**
+ * The shortcode way. The attributes are the same as the old ones - 'fg' and 'bg', instead of foreground
+ * and background, and 's' is for the font size.
+ *
+ * Example: [latex s=4 bg=00f fg=ff0]\LaTeX[/latex]
+ */
+function latex_shortcode( $atts, $content = '' ) {
+ extract( shortcode_atts( array(
+ 's' => 0,
+ 'bg' => latex_get_default_color( 'bg' ),
+ 'fg' => latex_get_default_color( 'text', '000' )
+ ), $atts ) );
+
+ return latex_render( latex_entity_decode( $content ), $fg, $bg, $s );
+}
+
+/**
+ * LaTeX needs to be untexturized
+ */
+function latex_no_texturize( $shortcodes ) {
+ $shortcodes[] = 'latex';
+ return $shortcodes;
+}
+
+add_filter( 'no_texturize_shortcodes', 'latex_no_texturize' );
+
+add_filter( 'the_content', 'latex_markup', 9 ); // before wptexturize
+add_filter( 'comment_text', 'latex_markup', 9 ); // before wptexturize
+add_shortcode( 'latex', 'latex_shortcode' );
+
diff --git a/plugins/jetpack/modules/module-info.php b/plugins/jetpack/modules/module-info.php
new file mode 100644
index 00000000..e6140161
--- /dev/null
+++ b/plugins/jetpack/modules/module-info.php
@@ -0,0 +1,432 @@
+<?php
+/**
+ * "Learn More" information blocks for all modules live in this file.
+ *
+ * jetpack_module_more_info_<module-slug> hooks are for pre-connection information
+ * jetpack_module_more_info_connected_<module-slug> hooks are used once the user
+ * is connected to show them links to admin panels, usage info etc.
+ */
+
+// VaultPress (stub)
+
+function vaultpress_jetpack_more_info() {
+ if ( function_exists( 'is_multisite' ) && is_multisite() ) {
+ $vaultpress_url = 'http://vaultpress.com/jetpack-ms/';
+ } else {
+ $vaultpress_url = 'http://vaultpress.com/jetpack/';
+ }
+ ?>
+
+ <div class="jp-info-img">
+ <a href="<?php echo $vaultpress_url?>">
+ <img class="jp-info-img" src="<?php echo plugins_url( basename( dirname( dirname( __FILE__ ) ) ) . '/_inc/images/vaultpress.png' ) ?>" alt="<?php esc_attr_e( 'VaultPress', 'jetpack' ) ?>" width="300" height="155" />
+ </a>
+ </div>
+
+ <h4><?php esc_html_e( 'VaultPress', 'jetpack' ) ?></h4>
+<?php if ( class_exists( 'VaultPress' ) || function_exists( 'vaultpress_contact_service' ) ) : ?>
+ <p><?php esc_html_e( 'Your WordPress installation is currently being protected with the world&#8217;s best security, backup, and support.', 'jetpack' ); ?></p>
+ <p><?php printf( _x( 'To check your backups, see any security alerts, or check your VaultPress Vitality, visit your %s.', 'Visit your _VaultPress_dashboard_.', 'jetpack' ), '<a href="https://dashboard.vaultpress.com/">' . esc_html__( 'VaultPress dashboard', 'jetpack' ) . '</a>' ); ?></a></p>
+<?php else : ?>
+ <p><?php esc_html_e( 'With a monthly subscription, the VaultPress plugin will backup your site&#8217;s content, themes, and plugins in real-time, as well as perform regular security scans for common threats and attacks.', 'jetpack' ); ?></p>
+ <p><?php printf( _x( 'View %s.', 'View _Plans_&_Pricing_. (VaultPress)', 'jetpack' ), '<a href="' . $vaultpress_url . '">' . esc_html__( 'Plans & Pricing', 'jetpack' ) . '</a>' ); ?></a></p>
+<?php endif;
+}
+add_action( 'jetpack_module_more_info_vaultpress', 'vaultpress_jetpack_more_info' );
+add_action( 'jetpack_module_more_info_connected_vaultpress', 'vaultpress_jetpack_more_info' );
+
+function vaultpress_jetpack_load_more_link() {
+ if ( function_exists( 'is_multisite' ) && is_multisite() ) {
+ $vaultpress_url = 'http://vaultpress.com/jetpack-ms/';
+ } else {
+ $vaultpress_url = 'http://vaultpress.com/jetpack/';
+ }
+
+ echo '<a class="button more-info-link" href="' . $vaultpress_url . '">' . __( "Learn More", 'jetpack' ) . '</a>';
+}
+add_filter( 'jetpack_learn_more_button_vaultpress', 'vaultpress_jetpack_load_more_link' );
+
+// Gravatar Hovercards
+function grofiles_more_info() { ?>
+ <div class="jp-info-img">
+ <a href="http://blog.gravatar.com/2010/10/06/gravatar-hovercards-on-wordpress-com/">
+ <img class="jp-info-img" src="<?php echo plugins_url( basename( dirname( dirname( __FILE__ ) ) ) . '/_inc/images/hovercard.png' ) ?>" alt="<?php esc_attr_e( 'Gravatar Hovercard', 'jetpack' ) ?>" width="320" height="165" />
+ </a>
+ </div>
+
+ <h4><?php esc_html_e( 'Gravatar Hovercards', 'jetpack' ) ?></h4>
+ <h5><?php esc_html_e( "What&#8217;s a Hovercard?", 'jetpack' ) ?></h5>
+ <p><?php esc_html_e( 'Hovercards enhance plain Gravatar images with information about a person: name, bio, pictures, their contact info, and other services they use on the web like Twitter, Facebook, or LinkedIn.', 'jetpack' ); ?></p>
+ <p><?php esc_html_e( 'Hovercards offer a great way to show your internet presence and help people find your own blog.', 'jetpack' ); ?></p>
+<?php
+}
+add_action( 'jetpack_module_more_info_gravatar-hovercards', 'grofiles_more_info' );
+
+function grofiles_more_info_connected() { ?>
+ <div class="jp-info-img">
+ <a href="http://blog.gravatar.com/2010/10/06/gravatar-hovercards-on-wordpress-com/">
+ <img class="jp-info-img" src="<?php echo plugins_url( basename( dirname( dirname( __FILE__ ) ) ) . '/_inc/images/hovercard.png' ) ?>" alt="<?php esc_attr_e( 'Gravatar Hovercard', 'jetpack' ) ?>" width="320" height="165" />
+ </a>
+ </div>
+
+ <h4><?php esc_html_e( 'Gravatar Hovercards', 'jetpack' ) ?></h4>
+ <h5><?php esc_html_e( "What&#8217;s a Hovercard?", 'jetpack' ) ?></h5>
+ <p><?php esc_html_e( 'Hovercards enhance plain Gravatar images with information about a person: name, bio, pictures, their contact info, and other services.', 'jetpack' ); ?></p>
+ <p><?php esc_html_e( 'To see hovercards, look at any blog post on your blog that has comments. If the commenter has a hovercard associated with their gravatar, mouse over their image and the hovercard will appear. To turn hovercards off, click the Deactivate button above.', 'jetpack' ); ?></p>
+<?php
+}
+add_action( 'jetpack_module_more_info_connected_gravatar-hovercards', 'grofiles_more_info_connected' );
+
+function grofiles_load_more_link() {
+ echo '<a class="button more-info-link" href="http://blog.gravatar.com/2010/10/06/gravatar-hovercards-on-wordpress-com/">' . esc_html__( 'Learn More', 'jetpack' ) . '</a>';
+}
+add_filter( 'jetpack_learn_more_button_gravatar-hovercards', 'grofiles_load_more_link' );
+
+
+// Shortcodes
+function jetpack_shortcodes_more_info() { ?>
+ <div class="jp-info-img">
+ <a href="http://en.support.wordpress.com/shortcodes/">
+ <img class="jp-info-img" src="<?php echo plugins_url( basename( dirname( dirname( __FILE__ ) ) ) . '/_inc/images/shortcodes.png' ) ?>" alt="<?php esc_attr_e( 'Shortcode Embeds', 'jetpack' ) ?>" width="300" height="135" />
+ </a>
+ </div>
+
+ <h4><?php esc_html_e( 'Shortcode Embeds', 'jetpack' ) ?></h4>
+ <p><?php esc_html_e( 'Shortcodes allow you to easily and safely embed media from other places in your site. With just one simple code, you can tell WordPress to embed YouTube, Flickr, and other media.', 'jetpack' ) ?></p>
+<?php
+}
+add_action( 'jetpack_module_more_info_shortcodes', 'jetpack_shortcodes_more_info' );
+
+function jetpack_shortcodes_more_info_connected() { ?>
+ <div class="jp-info-img">
+ <a href="http://en.support.wordpress.com/shortcodes/">
+ <img class="jp-info-img" src="<?php echo plugins_url( basename( dirname( dirname( __FILE__ ) ) ) . '/_inc/images/shortcodes.png' ) ?>" alt="<?php esc_attr_e( 'Shortcode Embeds', 'jetpack' ) ?>" width="300" height="135" />
+ </a>
+ </div>
+
+ <h4><?php esc_html_e( 'Shortcode Embeds', 'jetpack' ) ?></h4>
+ <p><?php esc_html_e( 'Shortcodes allow you to easily and safely embed media from other places in your site. With just one simple code, you can tell WordPress to embed YouTube, Flickr, and other media.', 'jetpack' ) ?></p>
+ <p><?php esc_html_e( 'Enter a shortcode directly into the Post/Page editor to embed media. For specific instructions follow the links below.', 'jetpack' ) ?></p>
+ <?php
+ $codes = array( 'archives' => 'http://support.wordpress.com/archives-shortcode/',
+ 'audio' => 'http://support.wordpress.com/audio/',
+ 'blip.tv' => 'http://support.wordpress.com/videos/bliptv/',
+ 'dailymotion' => 'http://support.wordpress.com/videos/dailymotion/',
+ 'digg' => 'http://support.wordpress.com/digg/',
+ 'flickr' => 'http://support.wordpress.com/videos/flickr-video/',
+ 'googlevideo' => 'http://support.wordpress.com/videos/google-video/',
+ 'scribd' => 'http://support.wordpress.com/scribd/',
+ 'slide' => 'http://support.wordpress.com/slideshows/slide/',
+ 'slideshare' => 'http://support.wordpress.com/slideshows/slideshare/',
+ 'soundcloud' => 'http://support.wordpress.com/audio/soundcloud-audio-player/',
+ 'vimeo' => 'http://support.wordpress.com/videos/vimeo/',
+ 'youtube' => 'http://support.wordpress.com/videos/youtube/',
+ 'polldaddy' => 'http://support.polldaddy.com/wordpress-shortcodes/',
+ );
+
+ if ( version_compare( PHP_VERSION, 5, '>=' ) ) {
+ $codes['wpvideo (VideoPress)'] = 'http://en.support.wordpress.com/videopress/';
+ }
+
+ $available = '';
+ foreach ( $codes as $code => $url ) {
+ $available[] = '<a href="' . $url . '" target="_blank">[' . $code . ']</a>';
+
+ }
+ ?>
+ <p><?php echo wp_sprintf( esc_html__( 'Available shortcodes are: %l.', 'jetpack' ), $available ); ?></p>
+<?php
+}
+add_action( 'jetpack_module_more_info_connected_shortcodes', 'jetpack_shortcodes_more_info_connected' );
+
+function jetpack_shortcodes_load_more_link( $description ) {
+ echo '<a class="button more-info-link" href="http://en.support.wordpress.com/shortcodes/">' . esc_html__( 'Learn More' , 'jetpack' ) . '</a>';
+}
+add_filter( 'jetpack_learn_more_button_shortcodes', 'jetpack_shortcodes_load_more_link' );
+
+
+// Shortlinks
+function wpme_more_info() { ?>
+ <div class="jp-info-img">
+ <a href="http://wp.me/sf2B5-shorten">
+ <img class="jp-info-img" src="<?php echo plugins_url( basename( dirname( dirname( __FILE__ ) ) ) . '/_inc/images/shortlinks.gif' ) ?>" alt="<?php esc_attr_e( 'WP.me Shortlinks', 'jetpack' ) ?>" width="300" height="154" />
+ </a>
+ </div>
+
+ <h4><?php esc_html_e( 'WP.me Shortlinks' , 'jetpack' ); ?></h4>
+ <p><?php esc_html_e( "Instead of typing or copy-pasting long URLs, you can now get a short and simple link to your posts and pages. This uses the super compact wp.me domain name, and gives you a unique URL you can use that will be safe and reliable.", 'jetpack' ) ?></p>
+ <p><?php esc_html_e( "It&#8217;s perfect for use on Twitter, Facebook, and cell phone text messages where every character counts.", 'jetpack' ) ?></p>
+<?php
+}
+add_action( 'jetpack_module_more_info_shortlinks', 'wpme_more_info' );
+
+function wpme_more_info_connected() { ?>
+ <div class="jp-info-img">
+ <a href="http://wp.me/sf2B5-shorten">
+ <img class="jp-info-img" src="<?php echo plugins_url( basename( dirname( dirname( __FILE__ ) ) ) . '/_inc/images/shortlinks.gif' ) ?>" alt="<?php esc_attr_e( 'WP.me Shortlinks', 'jetpack' ) ?>" width="300" height="154" />
+ </a>
+ </div>
+
+ <h4><?php esc_html_e( 'WP.me Shortlinks' , 'jetpack' ); ?></h4>
+ <p><?php esc_html_e( "Instead of typing or copy-pasting long URLs, you can now get a short and simple link to your posts and pages. This uses the super compact wp.me domain name, and gives you a unique URL you can use that will be safe and reliable.", 'jetpack' ) ?></p>
+ <p><?php esc_html_e( "To use shortlinks, go to any already published post (or publish something new!). A &#8220;Get Shortlink&#8221; button will be visible under the Post title. When you click it, a dialog box will appear with the shortlink and you can copy and paste to Twitter, Facebook or wherever your heart desires.", 'jetpack' ) ?></p>
+<?php
+}
+add_action( 'jetpack_module_more_info_connected_shortlinks', 'wpme_more_info_connected' );
+
+function wpme_load_more_link( $description ) {
+ echo '<a class="button more-info-link" href="http://wp.me/sf2B5-shorten">' . esc_html__( 'Learn More', 'jetpack' ) . '</a>';
+}
+add_filter( 'jetpack_learn_more_button_shortlinks', 'wpme_load_more_link' );
+
+
+// WordPress.com Stats
+function stats_more_info() { ?>
+ <div class="jp-info-img">
+ <a href="http://en.support.wordpress.com/stats/">
+ <img class="jp-info-img" src="<?php echo plugins_url( basename( dirname( dirname( __FILE__ ) ) ) . '/_inc/images/stats.gif' ) ?>" alt="<?php esc_attr_e( 'WordPress.com Stats', 'jetpack' ) ?>" width="300" height="144" />
+ </a>
+ </div>
+
+ <h4><?php esc_html_e( 'WordPress.com Stats' , 'jetpack' ); ?></h4>
+ <p><?php esc_html_e( 'There are many plugins and services that provide statistics, but data can be overwhelming. WordPress.com Stats makes the most popular metrics easy to understand through a clear and attractive interface.', 'jetpack' ) ?></p>
+<?php
+}
+add_action( 'jetpack_module_more_info_stats', 'stats_more_info' );
+
+function stats_more_info_connected() { ?>
+ <div class="jp-info-img">
+ <a href="http://en.support.wordpress.com/stats/">
+ <img class="jp-info-img" src="<?php echo plugins_url( basename( dirname( dirname( __FILE__ ) ) ) . '/_inc/images/stats.gif' ) ?>" alt="<?php esc_attr_e( 'WordPress.com Stats', 'jetpack' ) ?>" width="300" height="144" />
+ </a>
+ </div>
+
+ <h4><?php esc_html_e( 'WordPress.com Stats' , 'jetpack' ); ?></h4>
+ <p><?php esc_html_e( 'There are many plugins and services that provide statistics, but data can be overwhelming. WordPress.com Stats makes the most popular metrics easy to understand through a clear and attractive interface.', 'jetpack' ) ?></p>
+ <p><?php printf( __( 'You can <a href="%s">view your stats dashboard here</a>.', 'jetpack' ), admin_url( 'admin.php?page=stats' ) ); ?></p>
+<?php
+}
+add_action( 'jetpack_module_more_info_connected_stats', 'stats_more_info_connected' );
+
+function stats_load_more_link( $description ) {
+ echo '<a class="button more-info-link" href="http://en.support.wordpress.com/stats/">' . esc_html__( 'Learn More', 'jetpack' ) . '</a>';
+}
+add_filter( 'jetpack_learn_more_button_stats', 'stats_load_more_link' );
+
+
+// LaTeX
+function latex_more_info() { ?>
+ <div class="jp-info-img">
+ <a href="http://support.wordpress.com/latex/">
+ <img class="jp-info-img" src="<?php echo plugins_url( basename( dirname( dirname( __FILE__ ) ) ) . '/_inc/images/latex.gif' ) ?>" alt="<?php esc_attr_e( 'LaTeX', 'jetpack' ) ?>" width="300" height="155" />
+ </a>
+ </div>
+
+ <h4><img src="http://l.wordpress.com/latex.php?latex=%5CLaTeX&amp;bg=transparent&amp;fg=000&amp;s=1" alt="LaTeX logo" title="LaTeX" style="vertical-align: -27%" /> Makes Beautiful Math</h4>
+ <p><?php printf( esc_html__( '%s is a powerful markup language for writing complex mathematical equations, formulas, etc.', 'jetpack' ), '<a href="http://www.latex-project.org/" target="_blank"><img src="http://l.wordpress.com/latex.php?latex=%5CLaTeX&amp;bg=transparent&amp;fg=000&amp;s=-1" alt="LaTeX logo" title="LaTeX" style="vertical-align: -25%" /></a>' ); ?></p>
+ <p><?php printf( esc_html__( 'Jetpack combines the power of %s and the simplicity of WordPress to give you the ultimate in math blogging platforms.', 'jetpack' ), '<img src="http://l.wordpress.com/latex.php?latex=%5CLaTeX&amp;bg=transparent&amp;fg=000&amp;s=-1" alt="LaTeX logo" title="LaTeX" style="vertical-align: -25%" />' ); ?></p>
+ <p><?php esc_html_e( 'Wow, that sounds nerdy.', 'jetpack' ) ?></p>
+<?php
+}
+add_action( 'jetpack_module_more_info_latex', 'latex_more_info' );
+
+function latex_more_info_connected() { ?>
+ <div class="jp-info-img">
+ <a href="http://support.wordpress.com/latex/">
+ <img class="jp-info-img" src="<?php echo plugins_url( basename( dirname( dirname( __FILE__ ) ) ) . '/_inc/images/latex.gif' ) ?>" alt="<?php esc_attr_e( 'LaTeX', 'jetpack' ) ?>" width="300" height="155" />
+ </a>
+ </div>
+
+ <h4><img src="http://l.wordpress.com/latex.php?latex=%5CLaTeX&amp;bg=transparent&amp;fg=000&amp;s=1" alt="LaTeX logo" title="LaTeX" style="vertical-align: -27%;" /> Makes Beautiful Math</h4>
+ <p><?php printf( esc_html__( '%s is a powerful markup language for writing complex mathematical equations, formulas, etc.', 'jetpack' ), '<a href="http://www.latex-project.org/" target="_blank"><img src="http://l.wordpress.com/latex.php?latex=%5CLaTeX&amp;bg=transparent&amp;fg=000&amp;s=-1" alt="LaTeX logo" title="LaTeX" style="vertical-align: -25%" /></a>' ); ?></p>
+ <p><?php printf( __( 'Use <code>$latex your latex code here$</code> or <code>[latex]your latex code here[/latex]</code> to include %s in your posts and comments. There are <a href="%s" target="_blank">all sorts of options</a> available.', 'jetpack' ), '<img src="http://l.wordpress.com/latex.php?latex=%5CLaTeX&amp;bg=transparent&amp;fg=000&amp;s=-1" alt="LaTeX logo" title="LaTeX" style="vertical-align: -25%" />', 'http://support.wordpress.com/latex/' ); ?></p>
+<?php
+}
+add_action( 'jetpack_module_more_info_connected_latex', 'latex_more_info_connected' );
+
+function latex_load_more_link( $description ) {
+ echo '<a class="button more-info-link" href="http://support.wordpress.com/latex/">' . esc_html__( 'Learn More', 'jetpack' ) . '</a>';
+}
+add_filter( 'jetpack_learn_more_button_latex', 'latex_load_more_link' );
+
+
+// Sharedaddy
+function sharedaddy_more_info() { ?>
+ <div class="jp-info-img">
+ <a href="http://en.support.wordpress.com/sharing/">
+ <img class="jp-info-img" src="<?php echo plugins_url( basename( dirname( dirname( __FILE__ ) ) ) . '/_inc/images/sharedaddy.gif' ) ?>" alt="<?php esc_attr_e( 'Sharing', 'jetpack' ) ?>" width="300" height="155" />
+ </a>
+ </div>
+ <h4><?php esc_html_e( 'Sharing' , 'jetpack' ); ?></h4>
+ <p><?php esc_html_e( 'Share your posts with Twitter, Facebook, and a host of other services. You can configure services to appear as icons, text, or both. Some services have additional options to display smart buttons, such as Twitter, which will update the number of times the post has been shared.', 'jetpack' ); ?></p>
+
+ <p><?php
+ if ( is_multisite() ) {
+ esc_html_e( 'The following services are included: Twitter, Facebook, Reddit, StumbleUpon, PressThis, Digg, LinkedIn, Google +1, Print, and Email.' , 'jetpack' );
+ } else {
+ esc_html_e( 'The following services are included: Twitter, Facebook, Reddit, StumbleUpon, Digg, LinkedIn, Google +1, Print, and Email.' , 'jetpack' );
+ }
+ ?></p>
+
+ <p><?php esc_html_e( 'Additionally you can define your own custom services.', 'jetpack' ); ?></p>
+<?php
+}
+add_action( 'jetpack_module_more_info_sharedaddy', 'sharedaddy_more_info' );
+
+function sharedaddy_more_info_connected() { ?>
+ <div class="jp-info-img">
+ <embed type="application/x-shockwave-flash" src="http://s0.videopress.com/player.swf?v=1.02" height="190" wmode="transparent" seamlesstabbing="true" allowfullscreen="true" allowscriptaccess="always" overstretch="true" flashvars="guid=WV0JOwY2"></embed>
+ </div>
+
+ <h4><?php esc_html_e( 'Sharing' , 'jetpack' ); ?></h4>
+ <?php
+ if ( class_exists( 'Sharing_Admin' ) ) {
+ ?>
+
+ <p><?php printf( __( 'To configure your sharing settings, go to the Settings &rarr; <a href="%s">Sharing</a> menu.', 'jetpack' ), 'options-general.php?page=sharing' ); ?></p>
+ <p><?php esc_html_e( 'Drag and drop sharing services into the enabled section to have them show up on your site, and drag them into the hidden section to have them hidden behind a button.', 'jetpack' ); ?>
+
+ <?php
+ }
+ ?>
+
+ <p><?php printf( __( 'Full details can be found on the <a href="%s">Sharing support page</a>. This video also gives a swish run-down of how to use the Sharing feature. Watch it in HD for extra snazz!', 'jetpack' ), 'http://support.wordpress.com/sharing/' ); ?></p>
+<?php
+}
+add_action( 'jetpack_module_more_info_connected_sharedaddy', 'sharedaddy_more_info_connected' );
+
+function sharedaddy_load_more_link( $description ) {
+ echo '<a class="button more-info-link" href="http://support.wordpress.com/sharing/">' . esc_html__( 'Learn More', 'jetpack' ) . '</a>';
+}
+add_filter( 'jetpack_learn_more_button_sharedaddy', 'sharedaddy_load_more_link' );
+
+
+// After The Deadline
+function jpatd_more_info() { ?>
+ <div class="jp-info-img">
+ <a href="http://en.support.wordpress.com/proofreading/">
+ <img class="jp-info-img" src="<?php echo plugins_url( basename( dirname( dirname( __FILE__ ) ) ) . '/_inc/images/after-the-deadline.gif' ) ?>" alt="<?php esc_attr_e( 'Spelling and Grammar', 'jetpack' ) ?>" width="300" height="155" />
+ </a>
+ </div>
+
+ <h4><?php esc_html_e( 'Spelling and Grammar' , 'jetpack' ); ?></h4>
+
+ <p><?php printf( __( "The <a href='%s'>After&nbsp;the&nbsp;Deadline</a> Proofreading service improves your writing by using artificial intelligence to find your errors and offer smart suggestions.", 'jetpack' ), 'http://www.afterthedeadline.com/' ); ?></p>
+ <p><?php printf( __( 'After the Deadline provides a number of <a href="%s">customization options</a>, which you can edit in your profile.', 'jetpack' ), esc_url( get_edit_profile_url( get_current_user_id() ) ) . '#atd' ); ?></p>
+<?php
+}
+add_action( 'jetpack_module_more_info_after-the-deadline', 'jpatd_more_info' );
+
+function jpatd_load_more_link( $description ) {
+ echo '<a class="button more-info-link" href="http://en.support.wordpress.com/proofreading/">' . esc_html__( 'Learn More', 'jetpack' ) . '</a>';
+}
+add_filter( 'jetpack_learn_more_button_after-the-deadline', 'jpatd_load_more_link' );
+
+
+// RSS Links Widget, Image Widget, Twitter Widget
+function jetpack_widgets_more_info() { ?>
+ <div class="jp-info-img">
+ <img class="jp-info-img" src="<?php echo plugins_url( basename( dirname( dirname( __FILE__ ) ) ) . '/_inc/images/widgets.png' ) ?>" alt="<?php esc_attr_e( 'Widgets Screenshot', 'jetpack' ) ?>" width="320" height="119" />
+ </div>
+
+ <h4><?php esc_html_e( 'Extra Sidebar Widgets' , 'jetpack' ); ?></h4>
+
+ <p><strong><?php esc_html_e( 'The RSS Links Widget ', 'jetpack' ); ?></strong> <?php esc_html_e( "allows you to add links to your blog&#8217;s post and comment RSS feeds in your sidebar. This makes it easy for your readers to stay updated when you post new content or receive new comments.", 'jetpack' ) ?></p>
+ <p><strong><?php esc_html_e( 'The Twitter Widget ', 'jetpack' ); ?></strong> <?php esc_html_e( "shows your latest tweets within a sidebar on your theme. It&#8217;s an easy way to add more activity to your site. There are also a number of customization options.", 'jetpack' ) ?></p>
+ <p><strong><?php esc_html_e( 'The Facebook Like Box Widget ', 'jetpack' ); ?></strong> <?php esc_html_e( "shows your Facebook Like Box within a sidebar on your theme. It&#8217;s a great way to let your readers show their support.", 'jetpack' ) ?></p>
+ <p><strong><?php esc_html_e( 'The Image Widget ', 'jetpack' ); ?></strong><?php esc_html_e( "allows you to easily add images to widget areas in your theme. It&#8217;s an easy way to add more visual interest to your site.", 'jetpack' ) ?></p>
+
+<?php
+}
+add_action( 'jetpack_module_more_info_widgets', 'jetpack_widgets_more_info' );
+
+function jetpack_widgets_more_info_connected() { ?>
+ <div class="jp-info-img">
+ <img class="jp-info-img" src="<?php echo plugins_url( basename( dirname( dirname( __FILE__ ) ) ) . '/_inc/images/widgets.png' ) ?>" alt="<?php esc_attr_e( 'Widgets Screenshot', 'jetpack' ) ?>" width="320" height="119" />
+ </div>
+
+ <h4><?php esc_html_e( 'Extra Sidebar Widgets' , 'jetpack' ); ?></h4>
+
+ <p><strong><?php esc_html_e( 'The RSS Links Widget', 'jetpack' ); ?></strong> <?php esc_html_e( 'lets you easily add post and comment RSS feeds to a sidebar on your theme.', 'jetpack' ) ?></p>
+ <p><strong><?php esc_html_e( 'The Twitter Widget', 'jetpack' ); ?></strong> <?php esc_html_e( 'shows your latest tweets within a sidebar on your theme.', 'jetpack' ) ?></p>
+ <p><strong><?php esc_html_e( 'The Facebook Like Box Widget', 'jetpack' ); ?></strong> <?php esc_html_e( 'shows your Facebook Like Box within a sidebar on your theme.', 'jetpack' ) ?></p>
+ <p><strong><?php esc_html_e( 'The Image Widget', 'jetpack' ); ?></strong> <?php esc_html_e( 'lets you easily add images to a sidebar on your theme.', 'jetpack' ) ?></p>
+
+ <p><?php esc_html_e( 'Each of these widgets has a number of customization options.', 'jetpack' ); ?> <?php printf( __( 'To use the widgets, go to Appearance &#8594; <a href="%s">Widgets</a>. Drag them into one of your sidebars and configure away.', 'jetpack' ), admin_url( 'widgets.php' ) ); ?></p>
+<?php
+}
+add_action( 'jetpack_module_more_info_connected_widgets', 'jetpack_widgets_more_info_connected' );
+
+function jetpack_widgets_load_more_link( $description ) {
+ echo '<a class="button more-info-link" href="http://en.support.wordpress.com/widgets/">' . esc_html__( 'Learn More', 'jetpack' ) . '</a>';
+}
+add_filter( 'jetpack_learn_more_button_widgets', 'jetpack_widgets_load_more_link' );
+
+// Subscriptions
+function jetpack_subscriptions_more_info() { ?>
+ <div class="jp-info-img">
+ <img class="jp-info-img" src="<?php echo plugins_url( basename( dirname( dirname( __FILE__ ) ) ) . '/_inc/images/subscriptions.png' ) ?>" alt="<?php esc_attr_e( 'Subsriptions Screenshot', 'jetpack' ) ?>" width="320" height="119" />
+ </div>
+
+ <h4><?php esc_html_e( 'Subscriptions' , 'jetpack' ); ?></h4>
+
+ <p><?php esc_html_e( 'Easily allow any visitor to subscribe to all of your posts via email through a widget in your blog&#8217;s sidebar. Every time you publish a post, WordPress.com will send a notification to all your subscribers.', 'jetpack' ); ?></p>
+ <p><?php esc_html_e( 'When leaving comments, your visitors can also subscribe to a post&#8217;s comments to keep up with the conversation.', 'jetpack' ); ?></p>
+
+<?php
+
+ if ( 'jetpack_module_more_info_connected_subscriptions' == current_filter() )
+ printf( '<p>' . __( 'To use the Subscriptions widget, go to Appearance &#8594; <a href="%s">Widgets</a>. Drag the widget labeled &#8220;Blog Subscriptions (Jetpack)&#8221; into one of your sidebars and configure away.', 'jetpack' ) . '</p>', admin_url( 'widgets.php' ) );
+}
+add_action( 'jetpack_module_more_info_subscriptions', 'jetpack_subscriptions_more_info' );
+add_action( 'jetpack_module_more_info_connected_subscriptions', 'jetpack_subscriptions_more_info' );
+
+function jetpack_subscriptions_load_more_link() {
+ echo '<a class="button more-info-link" href="http://en.support.wordpress.com/following/">' . esc_html__( 'Learn More', 'jetpack' ) . '</a>';
+}
+add_action( 'jetpack_learn_more_button_subscriptions', 'jetpack_subscriptions_load_more_link' );
+
+// Enhanced Distribution
+
+function jetpack_enhanced_distribution_more_info() { ?>
+ <h4><?php esc_html_e( 'Enhanced Distribution' , 'jetpack' ); ?></h4>
+
+ <p><?php esc_html_e( 'Jetpack will automatically take the great published content from your blog or website and share it instantly with third party services like search engines, increasing your reach and traffic.', 'jetpack' ); ?></p>
+
+<?php
+}
+
+add_action( 'jetpack_module_more_info_enhanced-distribution', 'jetpack_enhanced_distribution_more_info' );
+add_action( 'jetpack_module_more_info_connected_enhanced-distribution', 'jetpack_enhanced_distribution_more_info' );
+
+function jetpack_enhanced_distribution_more_link() {
+ echo '<a class="button more-info-link" href="http://en.wordpress.com/firehose/">' . esc_html__( 'Learn More', 'jetpack' ) . '</a>';
+}
+add_action( 'jetpack_learn_more_button_enhanced-distribution', 'jetpack_enhanced_distribution_more_link' );
+
+// Contact Form: START
+function jetpack_contact_form_learn_more_button() {
+ echo '<a class="button more-info-link" href="http://support.wordpress.com/contact-form/">' . __( 'Learn More', 'jetpack' ) . '</a>';
+}
+
+function jetpack_contact_form_more_info() {
+ echo '<div class="jp-info-img">';
+ echo '<a href="http://support.wordpress.com/contact-form/">';
+ echo '<img class="jp-info-img" src="' . plugins_url( basename( dirname( dirname( __FILE__ ) ) ) . '/_inc/images/contact-form.jpg' ) . '" alt="' . esc_attr__( 'Contact Form', 'jetpack' ) . '" width="194" height="148" />';
+ echo '</a>';
+ echo '</div>';
+
+ echo '<h4>' . esc_html__( 'Contact Form', 'jetpack' ) . '</h4>';
+
+ echo '<p>';
+ _e( 'A contact form is a great way to offer your readers the ability to get in touch, without giving out your personal email address.', 'jetpack' );
+ echo '</p>';
+
+ echo '<p>'; _e( 'Each contact form can easily be customized to fit your needs. When a user submits your contact form, the feedback will be filtered through <a href="http://akismet.com/">Akismet</a> (if it is active on your site) to make sure it’s not spam. Any legitimate feedback will then be emailed to you, and added to your feedback management area.', 'jetpack' );
+ echo '</p>';
+}
+
+add_action( 'jetpack_learn_more_button_contact-form', 'jetpack_contact_form_learn_more_button' );
+add_action( 'jetpack_module_more_info_contact-form', 'jetpack_contact_form_more_info' );
+add_action( 'jetpack_module_more_info_connected_contact-form', 'jetpack_contact_form_more_info' );
+// Contact Form: STOP
diff --git a/plugins/jetpack/modules/sharedaddy.php b/plugins/jetpack/modules/sharedaddy.php
new file mode 100644
index 00000000..97f359c5
--- /dev/null
+++ b/plugins/jetpack/modules/sharedaddy.php
@@ -0,0 +1,23 @@
+<?php
+/**
+ * Module Name: Sharing
+ * Module Description: The most super duper sharing tool on the interwebs. Share content with Facebook, Twitter, and many more.
+ * Sort Order: 1
+ * First Introduced: 1.1
+ * Major Changes In: 1.2
+ */
+
+if ( !function_exists( 'sharing_init' ) )
+ include dirname( __FILE__ ).'/sharedaddy/sharedaddy.php';
+
+add_action( 'jetpack_modules_loaded', 'sharedaddy_loaded' );
+
+function sharedaddy_loaded() {
+ Jetpack::enable_module_configurable( __FILE__ );
+ Jetpack::module_configuration_load( __FILE__, 'sharedaddy_configuration_load' );
+}
+
+function sharedaddy_configuration_load() {
+ wp_safe_redirect( menu_page_url( 'sharing', false ) );
+ exit;
+}
diff --git a/plugins/jetpack/modules/sharedaddy/admin-sharing.css b/plugins/jetpack/modules/sharedaddy/admin-sharing.css
new file mode 100644
index 00000000..80c4fb89
--- /dev/null
+++ b/plugins/jetpack/modules/sharedaddy/admin-sharing.css
@@ -0,0 +1,353 @@
+#services-config {
+ background: #f1f1f1;
+ border: 1px #e3e3e3 solid;
+ border-radius: 6px;
+ -moz-border-radius: 6px;
+ -webkit-border-radius: 6px;
+ margin-top:15px;
+ min-width: 700px;
+ width: 100%;
+ float: left;
+ margin-bottom: 25px;
+}
+
+#available-services,#enabled-services,#live-preview{
+ padding:20px 20px 0px 20px;
+ width: 100%;
+}
+
+#enabled-services {
+ padding: 20px;
+}
+
+#live-preview {
+ padding: 20px 20px 10px 20px;
+}
+
+#available-services{
+ border-bottom: 1px #e3e3e3 solid;
+}
+
+#available-services h3,#enabled-services h3,#live-preview h3{
+ padding: 0px;
+ margin-top: 0px;
+}
+
+.description{
+ width: 180px;
+ vertical-align: top;
+}
+
+.description p{
+ font-size: 11px;
+}
+
+.services {
+ padding: 0px 20px;
+ vertical-align: top;
+}
+
+.services ul{
+}
+
+.services ul li {
+ float: left;
+ border: 1px #e3e3e3 solid;
+ border-radius: 6px;
+ -moz-border-radius: 6px;
+ -webkit-border-radius: 6px;
+ padding: 5px 10px 5px 24px;
+ margin-right: 10px;
+ cursor: move;
+}
+
+.services ul li.divider {
+ border: none;
+ padding: 0;
+ background: none;
+ cursor: default;
+}
+
+.preview {
+ float: left !important;
+}
+
+
+.services ul li#facebook, .preview-facebook div.option-smart-off{background: #FFF url(images/facebook.png) no-repeat 4px 5px;}
+.services ul li#twitter, .preview-twitter div.option-smart-off{background: #FFF url(images/twitter.png) no-repeat 4px 5px;}
+.services ul li#wordpress, .preview-wordpress{background: #FFF url(images/wordpress.png) no-repeat 4px 5px;}
+.services ul li#digg, .preview-digg div.option-smart-off{background: #FFF url(images/digg.png) no-repeat 4px 5px;}
+.services ul li#reddit, .preview-reddit div.option-smart-off{background: #FFF url(images/reddit.png) no-repeat 4px 5px;}
+.services ul li#stumbleupon, .preview-stumbleupon div.option-smart-off{background: #FFF url(images/stumbleupon.png) no-repeat 4px 5px;}
+.services ul li#email, .preview-email{background: #FFF url(images/email.png) no-repeat 4px 5px; padding-right: 10px;}
+.services ul li#print, .preview-print{background: #FFF url(images/print.png) no-repeat 4px 5px; padding-right: 10px;}
+.services ul li#press-this, .preview-press-this{background: #FFF url(images/wordpress.png) no-repeat 4px 5px; padding-right: 10px;}
+.services ul li#linkedin, .preview-linkedin div.option-smart-off{background: #FFF url(images/linkedin.png) no-repeat 4px 5px;}
+.services ul li#google-plus-1,.preview-google-plus-1{background: #FFF url(images/googleplus1.png) no-repeat 4px 5px; padding-right: 10px;}
+.services ul li.share-custom, .preview-custom{background: #FFF url(images/custom.png) no-repeat 4px 5px; no-repeat 4px 5px; padding-right: 10px;}
+
+/****************************************************************************/
+
+.preview li.preview-item {
+ margin: 0 15px 0 0;
+ background-position: 0px 5px;
+ padding: 5px 0px 5px 20px;
+ border: none;
+ cursor: default;
+}
+
+.preview li.preview-digg, .preview li.preview-reddit, .preview li.preview-stumbleupon, .preview li.preview-facebook, .preview li.preview-twitter, .preview li.preview-linkedin {
+ padding: 0;
+}
+
+.preview-digg .option-smart-off, .preview-reddit .option-smart-off, .preview-stumbleupon .option-smart-off, .preview-facebook .option-smart-off, .preview-twitter .option-smart-off, .preview-linkedin .option-smart-off {
+ background-position: 0px 5px !important;
+ padding: 5px 0px 5px 20px;
+}
+
+.preview-digg .option-smart-on {
+ background: #FFF url(images/smart-digg.png) no-repeat top left;
+ width:76px;
+ height:17px;
+ margin-top: 5px;
+}
+
+.preview-reddit .option-smart-on {
+ background: #FFF url(images/smart-reddit.png) no-repeat top left;
+ width:104px;
+ height:21px;
+ margin-top: 3px;
+}
+
+.preview-stumbleupon .option-smart-on {
+ background: #FFF url(images/smart-stumbleupon.png) no-repeat top left;
+ width: 74px;
+ height: 18px;
+ margin-top: 4px;
+}
+
+.preview-facebook .option-smart-like {
+ background: #FFF url(images/smart-like.png) no-repeat top left;
+ width:50px;
+ height:20px;
+ margin-top: 3px;
+}
+
+.preview-facebook .option-smart-on {
+ background: #FFF url(images/smart-facebook.png) no-repeat top left;
+ width:60px;
+ height:18px;
+ margin-top: 4px;
+}
+
+.preview-twitter .option-smart-on {
+ background: #FFF url(images/smart-twitter.png) no-repeat top left;
+ width:92px;
+ height:20px;
+ margin-top: 3px;
+}
+
+.preview-linkedin .option-smart-on {
+ background: #FFF url(images/linkedin-smart.png) no-repeat top center;
+ width:99px;
+ height:22px;
+ margin-top: 3px;
+}
+
+.preview-google-plus-1 .option-smart-on {
+ max-width: 70px;
+ width: 15px;
+ height: 15px;
+ margin-top: 4px;
+}
+
+.services .preview li.share-custom {
+ border-radius: 6px;
+ -moz-border-radius: 6px;
+ -webkit-border-radius: 6px;
+ background:url("images/sharing-hidden.png") no-repeat scroll 0px center #FFFFFF;
+ border: 1px solid #D8D8D8;
+ float: left;
+ line-height: 22px;
+ padding: 0 8px 0 21px;
+ margin: 1px 0 0 0;
+}
+
+.services .preview li.share-custom a {
+ color: #333;
+ text-decoration: none;
+}
+
+.services ul li.end-fix {
+ clear:both;
+ float:none;
+ visibility:hidden;
+ padding:0;
+ margin:0;
+ height:20px;
+ width:0;
+}
+
+#enabled-services{
+ border-top: 1px #FFF solid;
+ border-bottom: 1px #e3e3e3 solid;
+}
+
+#enabled-services h2{
+ font-size:20px;
+ padding-top:0px;
+ font-weight: normal !important;
+ color: #999;
+}
+
+#live-preview h2{
+ font-size:20px;
+ font-weight: normal !important;
+ color: #e3e3e3;
+}
+
+#live-preview{
+ background: #FFF;
+ border-bottom-left-radius:6px 6px;
+ border-bottom-right-radius:6px 6px;
+ -moz-border-radius-bottomleft: 6px;
+ -moz-border-radius-bottomright: 6px;
+}
+
+.clearing{
+ clear: both;
+}
+
+.options-toggle{
+}
+ #available-services .options-toggle {
+ display: none;
+ }
+
+ .services ul li.options{
+ padding-right: 0px;
+ }
+
+ .options .options-left{
+ float: left;
+ }
+
+ .options .options-toggle{
+ float: left;
+ width: 24px;
+ height: 26px;
+ margin-top:-6px;
+ margin-left: 5px;
+ margin-bottom:-5px;
+ }
+
+ html>body .options .options-toggle{
+ float: right;
+ }
+
+ .options-open .options-toggle{
+ margin-top:-5px;
+ }
+
+ .input label{
+ font-size: 11px;
+ line-height: 16px;
+ }
+
+ .advanced-form {
+ padding: 10px 14px 8px 10px;
+ margin-left: -24px;
+ display: none;
+ border-top: 1px #e3e3e3 solid;
+ margin-top:4px;
+ }
+
+
+ .services ul li.options-open .advanced-form{
+ display: block;
+ }
+
+ .utility{
+ float: right;
+ padding-top:10px;
+ padding-right: 10px;
+ font-size: 10px;
+ }
+
+ .advanced input[type=submit]{
+ float: left;
+ margin-top:10px;
+ margin-right: 10px;
+ }
+
+.services li.dropzone {
+ border: 1px dashed #999;
+ background: #e3e3e3;
+}
+
+.advanced-form .form-table th {
+ width: auto !important;
+}
+
+.advanced-form .button-secondary {
+ margin-top: 0 !important;
+}
+
+#share-drop-target {
+}
+
+#hidden-drop-target {
+ background: #e1e1e1;
+ border: 1px solid #cdcdcd;
+ border-radius: 6px;
+ -moz-border-radius: 6px;
+ -webkit-border-radius: 6px;
+ width: 29%;
+ padding: 10px;
+ vertical-align: top;
+}
+
+#hidden-drop-target p {
+ font-size: 10px;
+ margin: 0 0 10px 0;
+
+}
+
+.sharing-hidden .inner {
+ position: absolute;
+ border: 2px solid #6e6e6e;
+ padding: 15px 0px;
+ background: #fff;
+}
+
+
+.sharing-hidden ul {
+ background: #fff url('images/share-bg.png') repeat-y center center;
+ margin: 0 !important;
+}
+
+.services .sharing-hidden li {
+ margin: 0px 10px 0px 10px;
+ background-color: transparent;
+ width: 100px;
+}
+
+.sharing-hidden .preview-digg, .sharing-hidden .preview-reddit, .sharing-hidden .preview-stumbleupon, .sharing-hidden .preview-facebook, .sharing-hidden .preview-twitter, .sharing-hidden .preview-linkedin, .sharing-hidden .preview-google-plus-1 {
+ width: 120px !important;
+}
+
+.sharing-hidden li.share-end {
+ clear: both;
+ height: 0;
+ padding: 0px !important;
+ margin: 0px !important;
+ width: 0;
+ visibility: hidden;
+ float: none;
+}
+
+.preview .sharing-label {
+ font-weight: bold;
+ border: 0;
+ padding: 4px 6px 0 0;
+}
diff --git a/plugins/jetpack/modules/sharedaddy/admin-sharing.js b/plugins/jetpack/modules/sharedaddy/admin-sharing.js
new file mode 100644
index 00000000..4a93eab8
--- /dev/null
+++ b/plugins/jetpack/modules/sharedaddy/admin-sharing.js
@@ -0,0 +1,348 @@
+(function($) {
+ $( document ).ready(function() {
+ function enable_share_button() {
+ $( '.preview a.sharing-anchor' ).unbind( 'mouseenter mouseenter' ).hover( function() {
+ if ( $( this ).data( 'hasappeared' ) !== true ) {
+ var item = $( this ).parents( 'li:first' ).find( '.inner' );
+ var original = $( this ).parents( '.share-custom' );
+
+ // Create a timer to make the area appear if the mouse hovers for a period
+ var timer = setTimeout( function() {
+
+ $( item ).css( {
+ left: $( original ).position().left + 'px',
+ top: $( original ).position().top + $( original ).height() + 3 + 'px'
+ } ).slideDown( 200, function() {
+ // Mark the item as have being appeared by the hover
+ $( original ).data( 'hasappeared', true ).data( 'hasoriginal', true ).data( 'hasitem', false );
+
+ // Remove all special handlers
+ $( item ).mouseleave( handler_item_leave ).mouseenter( handler_item_enter );
+ $( original ).mouseleave( handler_original_leave ).mouseenter( handler_original_enter );
+
+ // Add a special handler to quickly close the item
+ $( original ).click( close_it );
+ } );
+
+ // The following handlers take care of the mouseenter/mouseleave for the share button and the share area - if both are left then we close the share area
+ var handler_item_leave = function() {
+ $( original ).data( 'hasitem', false );
+
+ if ( $( original ).data( 'hasoriginal' ) === false ) {
+ var timer = setTimeout( close_it, 800 );
+ $( original ).data( 'timer2', timer );
+ }
+ };
+
+ var handler_item_enter = function() {
+ $( original ).data( 'hasitem', true );
+ clearTimeout( $( original ).data( 'timer2' ) );
+ }
+
+ var handler_original_leave = function() {
+ $( original ).data( 'hasoriginal', false );
+
+ if ( $( original ).data( 'hasitem' ) === false ) {
+ var timer = setTimeout( close_it, 800 );
+ $( original ).data( 'timer2', timer );
+ }
+ };
+
+ var handler_original_enter = function() {
+ $( original ).data( 'hasoriginal', true );
+ clearTimeout( $( original ).data( 'timer2' ) );
+ };
+
+ var close_it = function() {
+ item.slideUp( 200 );
+
+ // Clear all hooks
+ $( original ).unbind( 'mouseleave', handler_original_leave ).unbind( 'mouseenter', handler_original_enter );
+ $( item ).unbind( 'mouseleave', handler_item_leave ).unbind( 'mouseenter', handler_item_leave );
+ $( original ).data( 'hasappeared', false );
+ $( original ).unbind( 'click', close_it );
+ return false;
+ };
+ }, 200 );
+
+ // Remember the timer so we can detect it on the mouseout
+ $( this ).data( 'timer', timer );
+ }
+ }, function() {
+ // Mouse out - remove any timer
+ clearTimeout( $( this ).data( 'timer' ) );
+ $( this ).data( 'timer', false );
+ } );
+ }
+
+ function update_preview() {
+ var item;
+
+ // Clear the live preview
+ $( '#live-preview ul.preview li' ).remove();
+
+ // Add label
+ if ( $( '#save-enabled-shares input[name=visible]' ).val() != '' || $( '#save-enabled-shares input[name=hidden]' ).val() != '' )
+ $( '#live-preview ul.preview' ).append( $( '#live-preview ul.archive .sharing-label' ).clone() );
+
+ // Re-insert all the enabled items
+ $( 'ul.services-enabled li' ).each( function() {
+ if ( $( this ).hasClass( 'service' ) ) {
+ var service = $( this ).attr( 'id' );
+
+ $( '#live-preview ul.preview' ).append( $( '#live-preview ul.archive .preview-' + service ).clone() );
+ }
+ } );
+
+ // Add any preview items
+ if ( $( '#save-enabled-shares input[name=hidden]' ).val() != '' ) {
+ // Add share button
+ $( '#live-preview ul.preview' ).append( $( '#live-preview ul.archive .share-custom' ).clone() );
+ $( '#live-preview ul.preview li.share-custom ul li' ).remove();
+
+ // Add rest of the items
+ $( 'ul.services-hidden li' ).each( function( pos, item ) {
+ if ( $( this ).hasClass( 'service' ) ) {
+ var service = $( this ).attr( 'id' );
+
+ $( '#live-preview ul.preview li.share-custom ul' ).append( $( '#live-preview ul.archive .preview-' + service ).clone() );
+
+ if ( pos % 2 == 1 )
+ $( '#live-preview ul.preview li.share-custom ul' ).append( '<li class="share-end"></div>' );
+ }
+ } );
+
+ enable_share_button();
+ }
+
+ // Button style
+ if ( $( 'select[name=button_style]' ).val() == 'icon' )
+ $( '#live-preview ul.preview .option' ).html( '&nbsp;' ); // Remove the text
+ else if ( $( 'select[name=button_style]' ).val() == 'text' ) {
+ $( '#live-preview ul.preview li.advanced' ).each( function() {
+ if ( $( this ).find( '.option' ).hasClass( 'option-smart-on' ) === false && $( this ).find( '.option' ).hasClass( 'option-smart-like' ) === false )
+ $( this ).attr( 'class', 'advanced preview-item' );
+ } );
+ }
+ }
+
+ function sharing_option_changed() {
+ var item = this;
+
+ // Loading icon
+ $( this ).parents( 'li:first' ).css( 'backgroundImage', 'url("' + sharing_loading_icon + '")' );
+
+ // Save
+ $( this ).parents( 'form' ).ajaxSubmit( function( response ) {
+ if ( response.indexOf( '<!---' ) >= 0 ) {
+ var button = response.substring( 0, response.indexOf( '<!--->' ) );
+ var preview = response.substring( response.indexOf( '<!--->' ) + 6 );
+
+ if ( $( item ).is( ':submit' ) === true ) {
+ // Update the DOM using a bit of cut/paste technology
+
+ $( item ).parents( 'li:first' ).replaceWith( button );
+
+ init_handlers();
+ }
+
+ $( '#live-preview ul.archive li.preview-' + $( item ).parents( 'form' ).find( 'input[name=service]' ).val() ).replaceWith( preview );
+ }
+
+ // Update preview
+ update_preview();
+
+ // Restore the icon
+ $( item ).parents( 'li:first' ).removeAttr( 'style' );
+ } );
+
+ if ( $( item ).is( ':submit' ) === true )
+ return false;
+ return true;
+ }
+
+ function save_services() {
+ $( '#enabled-services h3 img' ).show();
+
+ // Update the display to reflect the changes
+ $( '#enabled-services li' ).addClass( 'options' );
+ $( '#available-services li' ).removeClass( 'options' );
+
+ // Toggle various dividers/help texts
+ if ( $( '#enabled-services ul.services-enabled li.service' ).length > 0 ) {
+ $( '#drag-instructions' ).hide();
+ }
+ else {
+ $( '#drag-instructions' ).show();
+ }
+
+ if ( $( '#enabled-services li.service' ).length > 0 ) {
+ $( '#live-preview .services h2' ).hide();
+ }
+ else {
+ $( '#live-preview .services h2' ).show();
+ }
+
+ // Gather the modules
+ var visible = [], hidden = [];
+
+ $( 'ul.services-enabled li' ).each( function() {
+ if ( $( this ).hasClass( 'service' ) ) {
+ // Ready for saving
+ visible[visible.length] = $( this ).attr( 'id' );
+ }
+ } );
+
+ $( 'ul.services-hidden li' ).each( function() {
+ if ( $( this ).hasClass( 'service' ) ) {
+ // Ready for saving
+ hidden[hidden.length] = $( this ).attr( 'id' );
+ }
+ } );
+
+ // Set the hidden element values
+ $( '#save-enabled-shares input[name=visible]' ).val( visible.join( ',' ) );
+ $( '#save-enabled-shares input[name=hidden]' ).val( hidden.join( ',' ) );
+
+ update_preview();
+
+ // Save it
+ $( '#save-enabled-shares' ).ajaxSubmit( function() {
+ $( '#enabled-services h3 img' ).hide();
+ } );
+ }
+
+ $( '#enabled-services .services ul' ).sortable( {
+ receive: function( event, ui ) {
+ save_services();
+ },
+ stop: function() {
+ save_services();
+ $( 'li.service' ).enableSelection(); // Fixes a problem with Chrome
+ },
+ over: function( event, ui ) {
+ $( this ).find( 'ul' ).addClass( 'dropping' );
+
+ // Ensure the 'end-fix' is at the end
+ $( '#enabled-services li.end-fix' ).remove()
+ $( '#enabled-services ul' ).append( '<li class="end-fix"></li>' );
+ },
+ out: function( event, ui ) {
+ $( this ).find( 'ul' ).removeClass( 'dropping' );
+
+ // Ensure the 'end-fix' is at the end
+ $( '#enabled-services li.end-fix' ).remove()
+ $( '#enabled-services ul' ).append( '<li class="end-fix"></li>' );
+ },
+ helper: function( event, ui ) {
+ ui.find( '.advanced-form' ).hide();
+
+ return ui.clone();
+ },
+ start: function( event, ui ) {
+ // Make sure that the advanced section is closed
+ $( '.advanced-form' ).hide();
+ $( 'li.service' ).disableSelection(); // Fixes a problem with Chrome
+ },
+ placeholder: 'dropzone',
+ opacity: 0.8,
+ delay: 150,
+ forcePlaceholderSize: true,
+ items: 'li',
+ connectWith: '#available-services ul, #enabled-services .services ul',
+ cancel: '.advanced-form'
+ } );
+
+ $( '#available-services ul' ).sortable( {
+ opacity: 0.8,
+ delay: 150,
+ cursor: 'move',
+ connectWith: '#enabled-services .services ul',
+ placeholder: 'dropzone',
+ forcePlaceholderSize: true,
+ start: function() {
+ $( '.advanced-form' ).hide();
+ }
+ } );
+
+ // Advanced options toggle
+ $( '.options-toggle' ).live( 'click', function() {
+ var was_visible = $( this ).parents( 'li:first' ).find( '.advanced-form' ).is( ':visible' );
+
+ // Hide everything
+ $( '.advanced-form' ).slideUp( 200 );
+
+ if ( !was_visible )
+ $( this ).parents( 'li:first' ).find( '.advanced-form' ).slideDown( 200 );
+ } );
+
+ // Live preview 'hidden' button
+ $( '.preview-hidden a' ).click( function() {
+ $( this ).parent().find( '.preview' ).toggle();
+ return false;
+ } );
+
+ // Add service
+ $( '#new-service form' ).ajaxForm( {
+ beforeSubmit: function() {
+ $( '#new-service-form .error' ).hide();
+ $( '#new-service-form img' ).show();
+ $( '#new-service-form input[type=submit]' ).attr( 'disabled', true );
+ },
+ success: function( response ) {
+ $( '#new-service-form img' ).hide();
+
+ if ( response == '1' ) {
+ $( '#new-service-form .inerror' ).removeClass( 'inerror' ).addClass( 'error' );
+ $( '#new-service-form .error' ).show();
+ $( '#new-service-form input[type=submit]' ).attr( 'disabled', false );
+ }
+ else {
+ document.location.reload();
+ }
+ }
+ }
+ );
+
+ function init_handlers() {
+ // Hook up all advanced options
+ $( '.advanced-form form input[type=checkbox]' ).unbind( 'click' ).click( sharing_option_changed );
+ $( '.advanced-form form select' ).unbind( 'change' ).change( sharing_option_changed );
+ $( '.advanced-form form input[type=submit]' ).unbind( 'click' ).click( sharing_option_changed );
+
+ $( '.advanced-form form a.remove' ).unbind( 'click' ).click( function() {
+ var form = $( this ).parents( 'form' );
+
+ form.find( 'input[name=action]' ).val( 'sharing_delete_service' );
+
+ // Loading icon
+ $( this ).parents( 'li:first' ).css( 'backgroundImage', 'url("' + sharing_loading_icon + '")' );
+
+ // Save
+ $( this ).parents( 'form' ).ajaxSubmit( function( response ) {
+ // Remove the item
+ form.parents( 'li:first' ).fadeOut( function() {
+ $( this ).remove();
+
+ // Update preview
+ update_preview();
+ } );
+ } );
+
+ return false;
+ } );
+ }
+
+ $( 'select[name=button_style]' ).change( function() {
+ update_preview();
+ return true;
+ } );
+
+ $( 'input[name=sharing_label]' ).blur( function() {
+ $('#live-preview ul.preview li.sharing-label').html( $( '<div/>' ).text( $( this ).val() ).html() );
+ } );
+
+ init_handlers();
+ enable_share_button();
+ } );
+})( jQuery );
diff --git a/plugins/jetpack/modules/sharedaddy/images/custom.png b/plugins/jetpack/modules/sharedaddy/images/custom.png
new file mode 100644
index 00000000..6d2f495e
--- /dev/null
+++ b/plugins/jetpack/modules/sharedaddy/images/custom.png
Binary files differ
diff --git a/plugins/jetpack/modules/sharedaddy/images/designfloat.png b/plugins/jetpack/modules/sharedaddy/images/designfloat.png
new file mode 100644
index 00000000..62e8f32d
--- /dev/null
+++ b/plugins/jetpack/modules/sharedaddy/images/designfloat.png
Binary files differ
diff --git a/plugins/jetpack/modules/sharedaddy/images/digg.png b/plugins/jetpack/modules/sharedaddy/images/digg.png
new file mode 100644
index 00000000..8524fa53
--- /dev/null
+++ b/plugins/jetpack/modules/sharedaddy/images/digg.png
Binary files differ
diff --git a/plugins/jetpack/modules/sharedaddy/images/divider.png b/plugins/jetpack/modules/sharedaddy/images/divider.png
new file mode 100644
index 00000000..709e7f97
--- /dev/null
+++ b/plugins/jetpack/modules/sharedaddy/images/divider.png
Binary files differ
diff --git a/plugins/jetpack/modules/sharedaddy/images/draggy.png b/plugins/jetpack/modules/sharedaddy/images/draggy.png
new file mode 100644
index 00000000..fcdd3989
--- /dev/null
+++ b/plugins/jetpack/modules/sharedaddy/images/draggy.png
Binary files differ
diff --git a/plugins/jetpack/modules/sharedaddy/images/email.png b/plugins/jetpack/modules/sharedaddy/images/email.png
new file mode 100644
index 00000000..2ea4d7b0
--- /dev/null
+++ b/plugins/jetpack/modules/sharedaddy/images/email.png
Binary files differ
diff --git a/plugins/jetpack/modules/sharedaddy/images/ember.png b/plugins/jetpack/modules/sharedaddy/images/ember.png
new file mode 100644
index 00000000..1a285a2a
--- /dev/null
+++ b/plugins/jetpack/modules/sharedaddy/images/ember.png
Binary files differ
diff --git a/plugins/jetpack/modules/sharedaddy/images/facebook.png b/plugins/jetpack/modules/sharedaddy/images/facebook.png
new file mode 100644
index 00000000..8850a80b
--- /dev/null
+++ b/plugins/jetpack/modules/sharedaddy/images/facebook.png
Binary files differ
diff --git a/plugins/jetpack/modules/sharedaddy/images/feed.png b/plugins/jetpack/modules/sharedaddy/images/feed.png
new file mode 100644
index 00000000..b0f72ffe
--- /dev/null
+++ b/plugins/jetpack/modules/sharedaddy/images/feed.png
Binary files differ
diff --git a/plugins/jetpack/modules/sharedaddy/images/googleplus1.png b/plugins/jetpack/modules/sharedaddy/images/googleplus1.png
new file mode 100644
index 00000000..22cb1df8
--- /dev/null
+++ b/plugins/jetpack/modules/sharedaddy/images/googleplus1.png
Binary files differ
diff --git a/plugins/jetpack/modules/sharedaddy/images/linkedin-smart.png b/plugins/jetpack/modules/sharedaddy/images/linkedin-smart.png
new file mode 100644
index 00000000..a828ad6f
--- /dev/null
+++ b/plugins/jetpack/modules/sharedaddy/images/linkedin-smart.png
Binary files differ
diff --git a/plugins/jetpack/modules/sharedaddy/images/linkedin.png b/plugins/jetpack/modules/sharedaddy/images/linkedin.png
new file mode 100644
index 00000000..a5e0340e
--- /dev/null
+++ b/plugins/jetpack/modules/sharedaddy/images/linkedin.png
Binary files differ
diff --git a/plugins/jetpack/modules/sharedaddy/images/loading.gif b/plugins/jetpack/modules/sharedaddy/images/loading.gif
new file mode 100644
index 00000000..85b99d46
--- /dev/null
+++ b/plugins/jetpack/modules/sharedaddy/images/loading.gif
Binary files differ
diff --git a/plugins/jetpack/modules/sharedaddy/images/print.png b/plugins/jetpack/modules/sharedaddy/images/print.png
new file mode 100644
index 00000000..19a245fa
--- /dev/null
+++ b/plugins/jetpack/modules/sharedaddy/images/print.png
Binary files differ
diff --git a/plugins/jetpack/modules/sharedaddy/images/reddit.png b/plugins/jetpack/modules/sharedaddy/images/reddit.png
new file mode 100644
index 00000000..25a849f8
--- /dev/null
+++ b/plugins/jetpack/modules/sharedaddy/images/reddit.png
Binary files differ
diff --git a/plugins/jetpack/modules/sharedaddy/images/rss.png b/plugins/jetpack/modules/sharedaddy/images/rss.png
new file mode 100644
index 00000000..0f06c7f1
--- /dev/null
+++ b/plugins/jetpack/modules/sharedaddy/images/rss.png
Binary files differ
diff --git a/plugins/jetpack/modules/sharedaddy/images/share-bg.png b/plugins/jetpack/modules/sharedaddy/images/share-bg.png
new file mode 100644
index 00000000..f74ed039
--- /dev/null
+++ b/plugins/jetpack/modules/sharedaddy/images/share-bg.png
Binary files differ
diff --git a/plugins/jetpack/modules/sharedaddy/images/sharing-hidden.png b/plugins/jetpack/modules/sharedaddy/images/sharing-hidden.png
new file mode 100644
index 00000000..96072621
--- /dev/null
+++ b/plugins/jetpack/modules/sharedaddy/images/sharing-hidden.png
Binary files differ
diff --git a/plugins/jetpack/modules/sharedaddy/images/smart-digg.png b/plugins/jetpack/modules/sharedaddy/images/smart-digg.png
new file mode 100644
index 00000000..ce654de4
--- /dev/null
+++ b/plugins/jetpack/modules/sharedaddy/images/smart-digg.png
Binary files differ
diff --git a/plugins/jetpack/modules/sharedaddy/images/smart-facebook.png b/plugins/jetpack/modules/sharedaddy/images/smart-facebook.png
new file mode 100644
index 00000000..18de01f7
--- /dev/null
+++ b/plugins/jetpack/modules/sharedaddy/images/smart-facebook.png
Binary files differ
diff --git a/plugins/jetpack/modules/sharedaddy/images/smart-like.png b/plugins/jetpack/modules/sharedaddy/images/smart-like.png
new file mode 100644
index 00000000..98cfc345
--- /dev/null
+++ b/plugins/jetpack/modules/sharedaddy/images/smart-like.png
Binary files differ
diff --git a/plugins/jetpack/modules/sharedaddy/images/smart-reddit.png b/plugins/jetpack/modules/sharedaddy/images/smart-reddit.png
new file mode 100644
index 00000000..4788a507
--- /dev/null
+++ b/plugins/jetpack/modules/sharedaddy/images/smart-reddit.png
Binary files differ
diff --git a/plugins/jetpack/modules/sharedaddy/images/smart-stumbleupon.png b/plugins/jetpack/modules/sharedaddy/images/smart-stumbleupon.png
new file mode 100644
index 00000000..8851447c
--- /dev/null
+++ b/plugins/jetpack/modules/sharedaddy/images/smart-stumbleupon.png
Binary files differ
diff --git a/plugins/jetpack/modules/sharedaddy/images/smart-twitter.png b/plugins/jetpack/modules/sharedaddy/images/smart-twitter.png
new file mode 100644
index 00000000..e14da3e8
--- /dev/null
+++ b/plugins/jetpack/modules/sharedaddy/images/smart-twitter.png
Binary files differ
diff --git a/plugins/jetpack/modules/sharedaddy/images/stumbleupon.png b/plugins/jetpack/modules/sharedaddy/images/stumbleupon.png
new file mode 100644
index 00000000..af4e9564
--- /dev/null
+++ b/plugins/jetpack/modules/sharedaddy/images/stumbleupon.png
Binary files differ
diff --git a/plugins/jetpack/modules/sharedaddy/images/twitter.png b/plugins/jetpack/modules/sharedaddy/images/twitter.png
new file mode 100644
index 00000000..1734a8a4
--- /dev/null
+++ b/plugins/jetpack/modules/sharedaddy/images/twitter.png
Binary files differ
diff --git a/plugins/jetpack/modules/sharedaddy/images/wordpress.png b/plugins/jetpack/modules/sharedaddy/images/wordpress.png
new file mode 100644
index 00000000..6b560450
--- /dev/null
+++ b/plugins/jetpack/modules/sharedaddy/images/wordpress.png
Binary files differ
diff --git a/plugins/jetpack/modules/sharedaddy/readme.txt b/plugins/jetpack/modules/sharedaddy/readme.txt
new file mode 100644
index 00000000..b2fd2bd3
--- /dev/null
+++ b/plugins/jetpack/modules/sharedaddy/readme.txt
@@ -0,0 +1,129 @@
+=== Sharedaddy ===
+Contributors: eoigal, johnny5, donncha, polldaddy, automattic
+Tags: share, sharing, email, twitter, facebook, print, reddit, stumbleupon, digg
+Requires at least: 3.0
+Tested up to: 3.1
+Stable tag: trunk
+
+Share your posts with Twitter, Facebook, and a host of other services
+
+== Description ==
+
+Share your posts with Twitter, Facebook, and a host of other services. You can configure services to appear as icons, text, or both. Some services have additional options to display smart buttons, such as Twitter, which will update the number of times the post has been shared.
+
+The following services are included:
+
+* Twitter
+* Facebook
+* Reddit
+* StumbleUpon
+* PressThis
+* Digg
+* Print
+* Email
+
+Additionally you can define your own custom services.
+
+[wpvideo WV0JOwY2]
+
+The plugin is available in the following languages:
+
+* English
+* Japanese, thanks to Naoko McCracken
+* Portuguese, thanks to WordPress Portugal
+* Spanish, thanks to elarequi
+* German, thanks to Jott und die Welt
+* French, thanks to Dario Spagnolo / Aurélie Rochelle
+* Brazilian Portuguese, thanks to Gabriel Reguly
+* Dutch, thanks to Chantal Coolsma
+* Serbian, thanks to Milan Dinić
+
+If you have a translation please send it us and we would be glad to include it for everyone to use!
+
+The following plugins extend Sharedaddy:
+
+* [Mixi Check](http://wordpress.org/extend/plugins/mixi-check/) - support for Mixi
+
+For more detailed information about using this plugin you can refer to these pages:
+
+* http://support.wordpress.com/sharing/
+* http://ryanmarkel.com/2010/08/26/adding-a-custom-sharing-service-to-wordpress-com/
+* http://ryanmarkel.com/2010/08/31/adding-specific-sharing-services-to-sharedaddy/
+* http://wpgarage.com/tips/how-to-add-a-linkedin-share-button-to-sharedaddy/
+
+Note: You will need PHP5 to use this plugin
+
+== Installation ==
+
+Upload the plugin to your blog and activate it. Configure your sharing services from the Settings > Sharing dashboard page
+
+== Screenshots ==
+
+1. Manage sharing services
+2. Share posts
+
+== Changelog ==
+= 0.1 =
+* Initial release
+
+= 0.2 =
+* Fix incorrect link in plugin page
+* Remove debug from JS code
+
+= 0.2.1 =
+* Add Japanese translation, thanks Naoko!
+
+= 0.2.2 =
+* Add Portuguese translation, thanks WordPress Portugal!
+
+= 0.2.3 =
+* Add Spanish, thanks to elarequi!
+
+= 0.2.4 =
+* Fix incorrect icon reference
+
+= 0.2.5 =
+* Add German, thanks to Jott und die Welt!
+* Optimize loading of Digg JS
+
+= 0.2.6 =
+* Add French, thanks to Dario Spagnolo / Aurélie Rochelle!
+
+= 0.2.7 =
+* Add Brazilian Portuguese, thanks to Gabriel
+* Add Dutch, thanks to Chantal
+
+= 0.2.8 =
+* Update Spanish translation (thanks to elarequi)
+* Change CSS link to use wp_enqueue (props to Barry)
+* Add %post_full_url% to custom service tags
+* Fixed removal of sharing option in quickedit (props to dimadin)
+* Add service ID to sharing_permalink filter (props to dimadin)
+* Email service loading.gif is included in the plugin
+* Better RTL support, cleanup style issues in some themes (props to Lance)
+
+= 0.2.9 =
+* Add Serbian translation (thanks to Milan Dinić)
+* Fix double = in Facebook share and rawurlencode like button for better theme compat (props to Lance)
+* Add %post_tags% to custom service tags
+* Allow Facebook like button width to change
+* Language domain fixes (props to dimadin)
+* Add language context to service names
+* Add 'sharing_show' filter to allow custom determination of whether to show sharing links
+* Add option to disable CSS and JS (so it can be moved in theme, if required)
+* Better support for non-multibyte blogs
+
+= 0.2.10 =
+* Restore fixes to Twitter link
+
+= 0.2.11 =
+* Updated German language (thanks to infected)
+* Further improvements to localisation (thanks to Milan Dinić)
+
+= 0.2.12 =
+* Theme placement improvements
+* Add Danish width to Facebook
+* Fix invalid HTML in Twitter iframe
+
+= 0.2.13 =
+* Add Google+1 button
diff --git a/plugins/jetpack/modules/sharedaddy/screenshot-1.jpg b/plugins/jetpack/modules/sharedaddy/screenshot-1.jpg
new file mode 100644
index 00000000..ec13343c
--- /dev/null
+++ b/plugins/jetpack/modules/sharedaddy/screenshot-1.jpg
Binary files differ
diff --git a/plugins/jetpack/modules/sharedaddy/screenshot-2.jpg b/plugins/jetpack/modules/sharedaddy/screenshot-2.jpg
new file mode 100644
index 00000000..2679d8b5
--- /dev/null
+++ b/plugins/jetpack/modules/sharedaddy/screenshot-2.jpg
Binary files differ
diff --git a/plugins/jetpack/modules/sharedaddy/sharedaddy.php b/plugins/jetpack/modules/sharedaddy/sharedaddy.php
new file mode 100644
index 00000000..a7146019
--- /dev/null
+++ b/plugins/jetpack/modules/sharedaddy/sharedaddy.php
@@ -0,0 +1,126 @@
+<?php
+/*
+Plugin Name: Sharedaddy
+Description: The most super duper sharing tool on the interwebs.
+Version: 0.2.12
+Author: Automattic, Inc.
+Author URI: http://automattic.com/
+Plugin URI: http://en.blog.wordpress.com/2010/08/24/more-ways-to-share/
+*/
+
+require_once plugin_dir_path( __FILE__ ).'sharing.php';
+
+function sharing_email_send_post( $data ) {
+ $content = sprintf( __( '%1$s (%2$s) thinks you may be interested in the following post:'."\n\n", 'jetpack' ), $data['name'], $data['source'] );
+ $content .= $data['post']->post_title."\n";
+ $content .= get_permalink( $data['post']->ID )."\n";
+
+ wp_mail( $data['target'], '['.__( 'Shared Post', 'jetpack' ).'] '.$data['post']->post_title, $content );
+}
+
+function sharing_add_meta_box() {
+ $post_types = get_post_types( array( 'public' => true ) );
+
+ foreach( $post_types as $post_type ) {
+ add_meta_box( 'sharing_meta', __( 'Sharing', 'jetpack' ), 'sharing_meta_box_content', $post_type, 'advanced', 'high' );
+ }
+}
+
+function sharing_meta_box_content( $post ) {
+ $sharing_checked = get_post_meta( $post->ID, 'sharing_disabled', false );
+
+ if ( empty( $sharing_checked ) || $sharing_checked === false )
+ $sharing_checked = ' checked="checked"';
+ else
+ $sharing_checked = '';
+
+ echo '<p><label for="enable_post_sharing"><input name="enable_post_sharing" id="enable_post_sharing" value="1"' . $sharing_checked . ' type="checkbox"> ' . __( 'Show sharing buttons on this post.', 'jetpack' ) . '</label><input type="hidden" name="sharing_status_hidden" value="1" /></p>';
+}
+
+function sharing_meta_box_save( $post_id ) {
+ if ( defined('DOING_AUTOSAVE') && DOING_AUTOSAVE )
+ return $post_id;
+
+ // Record sharing disable
+ if ( current_user_can( 'edit_post', $post_id ) ) {
+ if ( isset( $_POST['sharing_status_hidden'] ) ) {
+ if ( !isset( $_POST['enable_post_sharing'] ) ) {
+ update_post_meta( $post_id, 'sharing_disabled', 1 );
+ } else {
+ delete_post_meta( $post_id, 'sharing_disabled' );
+ }
+ }
+ }
+
+ return $post_id;
+}
+
+function sharing_meta_box_protected( $protected, $meta_key, $meta_type ) {
+ if ( 'sharing_disabled' == $meta_key )
+ $protected = true;
+
+ return $protected;
+}
+
+add_filter( 'is_protected_meta', 'sharing_meta_box_protected', 10, 3 );
+
+function sharing_plugin_settings( $links ) {
+ $settings_link = '<a href="options-general.php?page=sharing.php">'.__( 'Settings', 'jetpack' ).'</a>';
+ array_unshift( $links, $settings_link );
+ return $links;
+}
+
+function sharing_add_plugin_settings($links, $file) {
+ if ( $file == basename( dirname( __FILE__ ) ).'/'.basename( __FILE__ ) ) {
+ $links[] = '<a href="options-general.php?page=sharing.php">' . __( 'Settings', 'jetpack' ) . '</a>';
+ $links[] = '<a href="http://support.wordpress.com/sharing/">' . __( 'Support', 'jetpack' ) . '</a>';
+ }
+
+ return $links;
+}
+
+function sharing_restrict_to_single( $services ) {
+ // This removes Press This from non-multisite blogs - doesnt make much sense
+ if ( is_multisite() === false ) {
+ unset( $services['press-this'] );
+ }
+
+ return $services;
+}
+
+function sharing_init() {
+ if ( get_option( 'sharedaddy_disable_resources' ) ) {
+ add_filter( 'sharing_js', 'sharing_disable_js' );
+ remove_action( 'wp_head', 'sharing_add_header', 1 );
+ }
+}
+
+function sharing_disable_js() {
+ return false;
+}
+
+function sharing_global_resources() {
+ $disable = get_option( 'sharedaddy_disable_resources' );
+?>
+<tr valign="top">
+ <th scope="row"><label for="disable_css"><?php _e( 'Disable CSS and JS', 'jetpack' ); ?></label></th>
+ <td>
+ <input id="disable_css" type="checkbox" name="disable_resourcse" <?php if ( $disable == 1 ) echo ' checked="checked"'; ?>/> <small><em><?php _e( 'Advanced. If this option is checked, you must include these files in your theme manually for the sharing links to work.', 'jetpack' ); ?></em></small>
+ </td>
+</tr>
+<?php
+}
+
+function shareing_global_resources_save() {
+ update_option( 'sharedaddy_disable_resources', isset( $_POST['disable_resourcse'] ) ? 1 : 0 );
+}
+
+add_action( 'init', 'sharing_init' );
+add_action( 'admin_init', 'sharing_add_meta_box' );
+add_action( 'save_post', 'sharing_meta_box_save' );
+add_action( 'sharing_email_send_post', 'sharing_email_send_post' );
+add_action( 'sharing_global_options', 'sharing_global_resources' );
+add_action( 'sharing_admin_update', 'shareing_global_resources_save' );
+add_filter( 'sharing_services', 'sharing_restrict_to_single' );
+add_action( 'plugin_action_links_'.basename( dirname( __FILE__ ) ).'/'.basename( __FILE__ ), 'sharing_plugin_settings', 10, 4 );
+add_filter( 'plugin_row_meta', 'sharing_add_plugin_settings', 10, 2 );
diff --git a/plugins/jetpack/modules/sharedaddy/sharedaddy.pot b/plugins/jetpack/modules/sharedaddy/sharedaddy.pot
new file mode 100644
index 00000000..7af247cb
--- /dev/null
+++ b/plugins/jetpack/modules/sharedaddy/sharedaddy.pot
@@ -0,0 +1,404 @@
+# Translation of the WordPress plugin Sharedaddy 0.2.9 by Automattic, Inc..
+# Copyright (C) 2010 Automattic, Inc.
+# This file is distributed under the same license as the Sharedaddy package.
+# FIRST AUTHOR <EMAIL@ADDRESS>, 2010.
+#
+#, fuzzy
+msgid ""
+msgstr ""
+"Project-Id-Version: Sharedaddy 0.2.9\n"
+"Report-Msgid-Bugs-To: http://wordpress.org/tag/sharedaddy\n"
+"POT-Creation-Date: 2010-12-16 16:35+0000\n"
+"PO-Revision-Date: 2010-MO-DA HO:MI+ZONE\n"
+"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
+"Language-Team: LANGUAGE <LL@li.org>\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=utf-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+
+#: sharedaddy.php:14
+#, php-format
+msgid ""
+"%1$s (%2$s) thinks you may be interested in the following post:\n"
+"\n"
+msgstr ""
+
+#: sharedaddy.php:18
+msgid "Shared Post"
+msgstr ""
+
+#: sharedaddy.php:22 sharedaddy.php:23 sharing.php:49
+msgid "Sharing"
+msgstr ""
+
+#: sharedaddy.php:34
+msgid "Show sharing buttons on this post."
+msgstr ""
+
+#: sharedaddy.php:57 sharedaddy.php:64
+msgid "Settings"
+msgstr ""
+
+#: sharedaddy.php:65
+msgid "Support"
+msgstr ""
+
+#: sharedaddy.php:97
+msgid "Disable CSS and JS"
+msgstr ""
+
+#: sharedaddy.php:99
+msgid "Advanced - you must include these in your theme for Sharedaddy to work"
+msgstr ""
+
+#: sharing-service.php:194
+msgid "Share this:"
+msgstr ""
+
+#: sharing-service.php:434 sharing-sources.php:620 sharing.php:230
+#: sharing.php:271
+msgid "Share"
+msgstr ""
+
+#: sharing-sources.php:127
+msgid "Email"
+msgstr ""
+
+#: sharing-sources.php:169
+msgid "This post has been shared!"
+msgstr ""
+
+#: sharing-sources.php:170
+#, php-format
+msgid "You have shared this post with %s"
+msgstr ""
+
+#: sharing-sources.php:171
+msgid "Close"
+msgstr ""
+
+#: sharing-sources.php:193
+msgctxt "share to"
+msgid "Email"
+msgstr ""
+
+#: sharing-sources.php:193
+msgid "Click to email this to a friend"
+msgstr ""
+
+#: sharing-sources.php:207
+msgid "Send to Email Address"
+msgstr ""
+
+#: sharing-sources.php:215
+msgid "Your Name"
+msgstr ""
+
+#: sharing-sources.php:218
+msgid "Your Email Address"
+msgstr ""
+
+#: sharing-sources.php:226
+msgid "Send Email"
+msgstr ""
+
+#: sharing-sources.php:227
+msgid "Cancel"
+msgstr ""
+
+#: sharing-sources.php:230
+msgid "Post was not sent - check your email addresses!"
+msgstr ""
+
+#: sharing-sources.php:234
+msgid "Email check failed, please try again"
+msgstr ""
+
+#: sharing-sources.php:238
+msgid "Sorry, your blog cannot share posts by email."
+msgstr ""
+
+#: sharing-sources.php:257
+msgid "Twitter"
+msgstr ""
+
+#: sharing-sources.php:264
+msgctxt "share to"
+msgid "Twitter"
+msgstr ""
+
+#: sharing-sources.php:264
+msgid "Click to share on Twitter"
+msgstr ""
+
+#: sharing-sources.php:334 sharing-sources.php:411 sharing-sources.php:462
+#: sharing-sources.php:568
+msgid "Use smart button"
+msgstr ""
+
+#: sharing-sources.php:352
+msgid "StumbleUpon"
+msgstr ""
+
+#: sharing-sources.php:363
+msgctxt "share to"
+msgid "StumbleUpon"
+msgstr ""
+
+#: sharing-sources.php:363
+msgid "Click to share on StumbleUpon"
+msgstr ""
+
+#: sharing-sources.php:429 sharing-sources.php:436
+msgid "Reddit"
+msgstr ""
+
+#: sharing-sources.php:436
+msgid "Click to share on Reddit"
+msgstr ""
+
+#: sharing-sources.php:506
+msgid "Digg"
+msgstr ""
+
+#: sharing-sources.php:515 sharing-sources.php:519
+msgid "Click to Digg this post"
+msgstr ""
+
+#: sharing-sources.php:519
+msgctxt "share to"
+msgid "Digg"
+msgstr ""
+
+#: sharing-sources.php:601
+msgid "Facebook"
+msgstr ""
+
+#: sharing-sources.php:651
+msgctxt "share to"
+msgid "Facebook"
+msgstr ""
+
+#: sharing-sources.php:651
+msgid "Share on Facebook"
+msgstr ""
+
+#: sharing-sources.php:673
+msgid "Default button"
+msgstr ""
+
+#: sharing-sources.php:674
+msgid "Share button"
+msgstr ""
+
+#: sharing-sources.php:675
+msgid "Like button"
+msgstr ""
+
+#: sharing-sources.php:720
+msgid "Print"
+msgstr ""
+
+#: sharing-sources.php:724
+msgctxt "share to"
+msgid "Print"
+msgstr ""
+
+#: sharing-sources.php:724
+msgid "Click to print"
+msgstr ""
+
+#: sharing-sources.php:730
+msgid "Press This"
+msgstr ""
+
+#: sharing-sources.php:758
+msgctxt "share to"
+msgid "Press This"
+msgstr ""
+
+#: sharing-sources.php:758
+msgid "Click to Press This!"
+msgstr ""
+
+#: sharing-sources.php:789
+msgid "Click to share"
+msgstr ""
+
+#: sharing-sources.php:839
+msgid "Label"
+msgstr ""
+
+#: sharing-sources.php:844
+msgid "URL"
+msgstr ""
+
+#: sharing-sources.php:849
+msgid "Icon"
+msgstr ""
+
+#: sharing-sources.php:856
+msgid "Save"
+msgstr ""
+
+#: sharing-sources.php:857
+msgid "Remove Service"
+msgstr ""
+
+#: sharing.php:49 sharing.php:154
+msgid "Sharing Settings"
+msgstr ""
+
+#: sharing.php:144
+msgid "Warning! Multibyte support missing!"
+msgstr ""
+
+#: sharing.php:145
+#, php-format
+msgid ""
+"This plugin will work without it, but multibyte support is used <a href=\"%s"
+"\">if available</a>. You may see minor problems with Tweets and other "
+"sharing services."
+msgstr ""
+
+#: sharing.php:149
+msgid "Settings have been saved"
+msgstr ""
+
+#: sharing.php:160
+msgid "Available Services"
+msgstr ""
+
+#: sharing.php:161
+msgid "Drag and drop the services you'd like to enable into the box below."
+msgstr ""
+
+#: sharing.php:162
+msgid "Add a new service"
+msgstr ""
+
+#: sharing.php:182
+msgid "Enabled Services"
+msgstr ""
+
+#: sharing.php:185
+msgid "Services dragged here will appear individually."
+msgstr ""
+
+#: sharing.php:188
+msgid "Drag and drop available services here"
+msgstr ""
+
+#: sharing.php:199
+msgid "Services dragged here will be hidden behind a share button."
+msgstr ""
+
+#: sharing.php:214
+msgid "Live Preview"
+msgstr ""
+
+#: sharing.php:217
+msgid "Sharing is off. Please add services above to enable"
+msgstr ""
+
+#: sharing.php:299
+msgid "Default button style"
+msgstr ""
+
+#: sharing.php:302
+msgid "Icon + text"
+msgstr ""
+
+#: sharing.php:303
+msgid "Icon only"
+msgstr ""
+
+#: sharing.php:304
+msgid "Text only"
+msgstr ""
+
+#: sharing.php:309
+msgid "Sharing label"
+msgstr ""
+
+#: sharing.php:315
+msgid "Open links in"
+msgstr ""
+
+#: sharing.php:318
+msgid "New window"
+msgstr ""
+
+#: sharing.php:319
+msgid "Same window"
+msgstr ""
+
+#: sharing.php:324
+msgid "Show sharing buttons on"
+msgstr ""
+
+#: sharing.php:327
+msgid "Posts, pages, and index pages"
+msgstr ""
+
+#: sharing.php:328
+msgid "Posts and pages only"
+msgstr ""
+
+#: sharing.php:329
+msgid "Index pages only"
+msgstr ""
+
+#: sharing.php:339
+msgid "Save Changes"
+msgstr ""
+
+#: sharing.php:350
+msgid "Service name"
+msgstr ""
+
+#: sharing.php:356
+msgid "Sharing URL"
+msgstr ""
+
+#: sharing.php:360
+msgid "You can add the following variables to your service sharing URL:"
+msgstr ""
+
+#: sharing.php:365
+msgid "Icon URL"
+msgstr ""
+
+#: sharing.php:368
+msgid "Enter the URL of a 16x16px icon you want to use for this service."
+msgstr ""
+
+#: sharing.php:374
+msgid "Create Share"
+msgstr ""
+
+#: sharing.php:384
+msgid ""
+"An error occurred creating your new sharing service - please check you gave "
+"valid details."
+msgstr ""
+
+#. Plugin Name of the plugin/theme
+msgid "Sharedaddy"
+msgstr ""
+
+#. Plugin URI of the plugin/theme
+msgid "http://en.blog.wordpress.com/2010/08/24/more-ways-to-share/"
+msgstr ""
+
+#. Description of the plugin/theme
+msgid "The most super duper sharing tool on the interwebs."
+msgstr ""
+
+#. Author of the plugin/theme
+msgid "Automattic, Inc."
+msgstr ""
+
+#. Author URI of the plugin/theme
+msgid "http://automattic.com/"
+msgstr ""
diff --git a/plugins/jetpack/modules/sharedaddy/sharing-service.php b/plugins/jetpack/modules/sharedaddy/sharing-service.php
new file mode 100644
index 00000000..073044d4
--- /dev/null
+++ b/plugins/jetpack/modules/sharedaddy/sharing-service.php
@@ -0,0 +1,529 @@
+<?php
+
+include_once dirname( __FILE__ ).'/sharing-sources.php';
+
+define( 'WP_SHARING_PLUGIN_VERSION', '0.3' );
+
+class Sharing_Service {
+ private $global = false;
+
+ /**
+ * Gets a generic list of all services, without any config
+ */
+ public function get_all_services_blog() {
+ $options = get_option( 'sharing-options' );
+
+ $all = $this->get_all_services();
+ $services = array();
+
+ foreach ( $all AS $id => $name ) {
+ if ( isset( $all[$id] ) ) {
+ $config = array();
+
+ // Pre-load custom modules otherwise they won't know who they are
+ if ( substr( $id, 0, 7 ) == 'custom-' && is_array( $options[$id] ) )
+ $config = $options[$id];
+
+ $services[$id] = new $all[$id]( $id, $config );
+ }
+ }
+
+ return $services;
+ }
+
+ /**
+ * Gets a list of all available service names and classes
+ */
+ private function get_all_services() {
+ // Default services
+ $services = array(
+ 'email' => 'Share_Email',
+ 'print' => 'Share_Print',
+ 'digg' => 'Share_Digg',
+ 'facebook' => 'Share_Facebook',
+ 'linkedin' => 'Share_LinkedIn',
+ 'reddit' => 'Share_Reddit',
+ 'stumbleupon' => 'Share_Stumbleupon',
+ 'twitter' => 'Share_Twitter',
+ 'press-this' => 'Share_PressThis',
+ 'google-plus-1' => 'Share_GooglePlus1'
+ );
+
+ // Add any custom services in
+ $options = $this->get_global_options();
+ foreach ( (array)$options['custom'] AS $custom_id ) {
+ $services[$custom_id] = 'Share_Custom';
+ }
+
+ return apply_filters( 'sharing_services', $services );
+ }
+
+ public function new_service( $label, $url, $icon ) {
+ // Validate
+ $label = trim( wp_html_excerpt( wp_kses( $label, array() ), 30 ) );
+ $url = trim( esc_url_raw( $url ) );
+ $icon = trim( esc_url_raw( $icon ) );
+
+ if ( $label && $url && $icon ) {
+ $options = get_option( 'sharing-options' );
+ if ( !is_array( $options ) )
+ $options = array();
+
+ $service_id = 'custom-'.time();
+
+ // Add a new custom service
+ $options['global']['custom'][] = $service_id;
+
+ update_option( 'sharing-options', $options );
+
+ // Create a custom service and set the options for it
+ $service = new Share_Custom( $service_id, array( 'name' => $label, 'url' => $url, 'icon' => $icon ) );
+ $this->set_service( $service_id, $service );
+
+ // Return the service
+ return $service;
+ }
+
+ return false;
+ }
+
+ public function delete_service( $service_id ) {
+ $service = $this->get_service( $service_id );
+
+ if ( $service ) {
+ $options = get_option( 'sharing-options' );
+ if ( isset( $options[$service_id] ) )
+ unset( $options[$service_id] );
+
+ $key = array_search( $service_id, $options['global']['custom'] );
+ if ( $key !== false )
+ unset( $options['global']['custom'][$key] );
+
+ update_option( 'sharing-options', $options );
+ return true;
+ }
+
+ return false;
+ }
+
+ public function set_blog_services( array $visible, array $hidden ) {
+ $services = $this->get_all_services();
+ // Validate the services
+ $available = array_keys( $services );
+
+ // Only allow services that we have defined
+ $hidden = array_intersect( $hidden, $available );
+ $visible = array_intersect( $visible, $available );
+
+ // Ensure we don't have the same ones in hidden and visible
+ $hidden = array_diff( $hidden, $visible );
+
+ do_action( 'sharing_get_services_state', array(
+ 'services' => $services,
+ 'available' => $available,
+ 'hidden' => $hidden,
+ 'visible' => $visible,
+ 'currently_enabled' => $this->get_blog_services()
+ ) );
+
+ update_option( 'sharing-services', array( 'visible' => $visible, 'hidden' => $hidden ) );
+ }
+
+ public function get_blog_services() {
+ $options = get_option( 'sharing-options' );
+ $enabled = get_option( 'sharing-services' );
+ $services = $this->get_all_services();
+
+ if ( !is_array( $options ) )
+ $options = array( 'global' => $this->get_global_options() );
+
+ $global = $options['global'];
+
+ // Default services
+ if ( !is_array( $enabled ) ) {
+ $enabled = array(
+ 'visible' => array(),
+ 'hidden' => array()
+ );
+
+ $enabled = apply_filters( 'sharing_default_services', $enabled );
+ }
+
+ // Cleanup after any filters that may have produced duplicate services
+ $enabled['visible'] = array_unique( $enabled['visible'] );
+ $enabled['hidden'] = array_unique( $enabled['hidden'] );
+
+ // Form the enabled services
+ $blog = array( 'visible' => array(), 'hidden' => array() );
+
+ foreach ( $blog AS $area => $stuff ) {
+ foreach ( (array)$enabled[$area] AS $service ) {
+ if ( isset( $services[$service] ) ) {
+ $blog[$area][$service] = new $services[$service]( $service, array_merge( $global, isset( $options[$service] ) ? $options[$service] : array() ) );
+ }
+ }
+ }
+
+ $blog = apply_filters( 'sharing_services_enabled', $blog );
+
+ // Convenience for checking if a service is present
+ $blog['all'] = array_flip( array_merge( array_keys( $blog['visible'] ), array_keys( $blog['hidden'] ) ) );
+ return $blog;
+ }
+
+ public function get_service( $service_name ) {
+ $services = $this->get_blog_services();
+
+ if ( isset( $services['visible'][$service_name] ) )
+ return $services['visible'][$service_name];
+
+ if ( isset( $services['hidden'][$service_name] ) )
+ return $services['hidden'][$service_name];
+
+ return false;
+ }
+
+ public function set_global_options( $data ) {
+ $options = get_option( 'sharing-options' );
+
+ // No options yet
+ if ( ! is_array( $options ) )
+ $options = array();
+
+ // Defaults
+ $options['global'] = array(
+ 'button_style' => 'icon-text',
+ 'sharing_label' => __( 'Share this:', 'jetpack' ),
+ 'open_links' => 'same',
+ 'show' => array( 'post', 'page' ),
+ 'custom' => isset( $options['global']['custom'] ) ? $options['global']['custom'] : array()
+ );
+
+ $options['global'] = apply_filters( 'sharing_default_global', $options['global'] );
+
+ // Validate options and set from our data
+ if ( isset( $data['button_style'] ) && in_array( $data['button_style'], array( 'icon-text', 'icon', 'text' ) ) )
+ $options['global']['button_style'] = $data['button_style'];
+
+ if ( isset( $data['sharing_label'] ) )
+ $options['global']['sharing_label'] = trim( wp_kses( stripslashes( $data['sharing_label'] ), array() ) );
+
+ if ( isset( $data['open_links'] ) && in_array( $data['open_links'], array( 'new', 'same' ) ) )
+ $options['global']['open_links'] = $data['open_links'];
+
+ $shows = array_values( get_post_types( array( 'public' => true ) ) );
+ $shows[] = 'index';
+
+ if ( isset( $data['show'] ) ) {
+ if ( is_scalar( $data['show'] ) ) {
+ switch ( $data['show'] ) {
+ case 'posts' :
+ $data['show'] = array( 'post', 'page' );
+ break;
+ case 'index' :
+ $data['show'] = array( 'index' );
+ break;
+ case 'posts-index' :
+ $data['show'] = array( 'post', 'page', 'index' );
+ break;
+ }
+ }
+
+ if ( $data['show'] = array_intersect( $data['show'], $shows ) ) {
+ $options['global']['show'] = $data['show'];
+ }
+ } else {
+ $options['global']['show'] = array();
+ }
+
+ update_option( 'sharing-options', $options );
+ return $options['global'];
+ }
+
+ public function get_global_options() {
+ if ( $this->global === false ) {
+ $options = get_option( 'sharing-options' );
+
+ if ( is_array( $options ) && isset( $options['global'] ) )
+ $this->global = $options['global'];
+ else
+ $this->global = $this->set_global_options( $options['global'] );
+ }
+
+ if ( ! isset( $this->global['show'] ) ) {
+ $this->global['show'] = array( 'post', 'page' );
+ } elseif ( is_scalar( $this->global['show'] ) ) {
+ switch ( $this->global['show'] ) {
+ case 'posts' :
+ $this->global['show'] = array( 'post', 'page' );
+ break;
+ case 'index' :
+ $this->global['show'] = array( 'index' );
+ break;
+ case 'posts-index' :
+ $this->global['show'] = array( 'post', 'page', 'index' );
+ break;
+ }
+ }
+ return $this->global;
+ }
+
+ public function set_service( $id, Sharing_Source $service ) {
+ // Update the options for this service
+ $options = get_option( 'sharing-options' );
+
+ // No options yet
+ if ( !is_array( $options ) )
+ $options = array();
+
+ do_action( 'sharing_get_button_state', array( 'id' => $id, 'options' => $options, 'service' => $service ) );
+
+ $options[$id] = $service->get_options();
+
+ update_option( 'sharing-options', array_filter( $options ) );
+ }
+
+ // Soon to come to a .org plugin near you!
+ public function get_total( $service_name = false, $post_id = false ) {
+ global $wpdb, $blog_id;
+ if ( $service_name == false ) {
+ if ( $post_id > 0 ) {
+ // total number of shares for this post
+ return (int) $wpdb->get_var( $wpdb->prepare( "SELECT SUM( count ) FROM sharing_stats WHERE blog_id = %d AND post_id = %d", $blog_id, $post_id ) );
+ } else {
+ // total number of shares for this blog
+ return (int) $wpdb->get_var( $wpdb->prepare( "SELECT SUM( count ) FROM sharing_stats WHERE blog_id = %d", $blog_id ) );
+ }
+ }
+
+ if ( $post_id > 0 )
+ return (int) $wpdb->get_var( $wpdb->prepare( "SELECT SUM( count ) FROM sharing_stats WHERE blog_id = %d AND post_id = %d AND share_service = %s", $blog_id, $post_id, $service_name ) );
+ else
+ return (int) $wpdb->get_var( $wpdb->prepare( "SELECT SUM( count ) FROM sharing_stats WHERE blog_id = %d AND share_service = %s", $blog_id, $service_name ) );
+ }
+
+ public function get_services_total( $post_id = false ) {
+ $totals = array();
+ $services = $this->get_blog_services();
+
+ if ( !empty( $services ) && isset( $services[ 'all' ] ) )
+ foreach( $services[ 'all' ] as $key => $value ) {
+ $totals[$key] = new Sharing_Service_Total( $key, $this->get_total( $key, $post_id ) );
+ }
+ usort( $totals, array( 'Sharing_Service_Total', 'cmp' ) );
+
+ return $totals;
+ }
+
+ public function get_posts_total() {
+ $totals = array();
+ global $wpdb, $blog_id;
+
+ $my_data = $wpdb->get_results( $wpdb->prepare( "SELECT post_id as id, SUM( count ) as total FROM sharing_stats WHERE blog_id = %d GROUP BY post_id ORDER BY count DESC ", $blog_id ) );
+
+ if ( !empty( $my_data ) )
+ foreach( $my_data as $row )
+ $totals[] = new Sharing_Post_Total( $row->id, $row->total );
+
+ usort( $totals, array( 'Sharing_Post_Total', 'cmp' ) );
+
+ return $totals;
+ }
+}
+
+class Sharing_Service_Total {
+ var $id = '';
+ var $name = '';
+ var $service = '';
+ var $total = 0;
+
+ public function Sharing_Service_Total( $id, $total ) {
+ $services = new Sharing_Service();
+ $this->id = esc_html( $id );
+ $this->service = $services->get_service( $id );
+ $this->total = (int) $total;
+
+ $this->name = $this->service->get_name();
+ }
+
+ static function cmp( $a, $b ) {
+ if ( $a->total == $b->total )
+ return $a->name < $b->name;
+ return $a->total < $b->total;
+ }
+}
+
+class Sharing_Post_Total {
+ var $id = 0;
+ var $total = 0;
+ var $title = '';
+ var $url = '';
+
+ public function Sharing_Post_Total( $id, $total ) {
+ $this->id = (int) $id;
+ $this->total = (int) $total;
+ $this->title = get_the_title( $this->id );
+ $this->url = get_permalink( $this->id );
+ }
+
+ static function cmp( $a, $b ) {
+ if ( $a->total == $b->total )
+ return $a->id < $b->id;
+ return $a->total < $b->total;
+ }
+}
+
+function sharing_add_footer() {
+ if ( apply_filters( 'sharing_js', true ) )
+ wp_print_scripts( 'sharing-js' );
+
+ $sharer = new Sharing_Service();
+ $enabled = $sharer->get_blog_services();
+ foreach ( array_merge( $enabled['visible'], $enabled['hidden'] ) AS $service ) {
+ $service->display_footer();
+ }
+}
+
+function sharing_add_header() {
+ $sharer = new Sharing_Service();
+ $enabled = $sharer->get_blog_services();
+
+ foreach ( array_merge( $enabled['visible'], $enabled['hidden'] ) AS $service ) {
+ $service->display_header();
+ }
+
+ if ( count( $enabled['all'] ) > 0 )
+ wp_enqueue_style( 'sharedaddy', plugin_dir_url( __FILE__ ) .'sharing.css' );
+}
+
+function sharing_process_requests() {
+ global $post;
+
+ // Only process if: single post and share=X defined
+ if ( ( is_page() || is_single() ) && isset( $_GET['share'] ) ) {
+ $sharer = new Sharing_Service();
+
+ $service = $sharer->get_service( $_GET['share'] );
+ if ( $service ) {
+ $service->process_request( $post, $_POST );
+ }
+ }
+}
+
+function sharing_display( $text = '' ) {
+ global $post, $wp_current_filter;
+
+ if ( is_preview() ) {
+ return $text;
+ }
+
+ if ( in_array( 'get_the_excerpt', (array) $wp_current_filter ) ) {
+ return $text;
+ }
+
+ $sharer = new Sharing_Service();
+ $global = $sharer->get_global_options();
+
+ $show = false;
+ if ( !is_feed() ) {
+ if ( is_singular() && in_array( get_post_type(), $global['show'] ) ) {
+ $show = true;
+ } elseif ( in_array( 'index', $global['show'] ) && ( is_home() || is_archive() || is_search() ) ) {
+ $show = true;
+ }
+ }
+
+ // Pass through a filter for final say so
+ $show = apply_filters( 'sharing_show', $show, $post );
+
+ // Disabled for this post?
+ $switched_status = get_post_meta( $post->ID, 'sharing_disabled', false );
+
+ if ( !empty( $switched_status ) )
+ $show = false;
+
+ $sharing_content = '';
+
+ if ( $show ) {
+ $enabled = $sharer->get_blog_services();
+
+ if ( count( $enabled['all'] ) > 0 ) {
+ global $post;
+
+ $dir = get_option( 'text_direction' );
+
+ // Wrapper
+ $sharing_content .= '<div class="snap_nopreview sharing robots-nocontent">';
+ $sharing_content .= '<ul>';
+
+ // Visible items
+ $visible = '';
+ foreach ( $enabled['visible'] AS $id => $service ) {
+ // Individual HTML for sharing service
+ $visible .= '<li class="share-'.$service->get_class().' share-regular">';
+ $visible .= $service->get_display( $post );
+ $visible .= '</li>';
+ }
+
+ $parts = array();
+ if ( $global['sharing_label'] != '' )
+ $parts[] = '<li class="sharing_label">'.$global['sharing_label'].'</li>';
+
+ $parts[] = $visible;
+ if ( count( $enabled['hidden'] ) > 0 )
+ $parts[] = '<li class="share-custom"><a href="#" class="sharing-anchor">'._x( 'Share', 'dropdown button', 'jetpack' ).'</a></li>';
+
+ if ( $dir == 'rtl' )
+ $parts = array_reverse( $parts );
+
+ $sharing_content .= implode( '', $parts );
+ $sharing_content .= '<li class="share-end"></li></ul>';
+
+ if ( count( $enabled['hidden'] ) > 0 ) {
+ $sharing_content .= '<div class="sharing-hidden"><div class="inner" style="display: none;';
+
+ if ( count( $enabled['hidden'] ) == 1 )
+ $sharing_content .= 'width:150px;';
+
+ $sharing_content .= '">';
+
+ if ( count( $enabled['hidden'] ) == 1 )
+ $sharing_content .= '<ul style="background-image:none;">';
+ else
+ $sharing_content .= '<ul>';
+
+ $count = 1;
+ foreach ( $enabled['hidden'] AS $id => $service ) {
+ // Individual HTML for sharing service
+ $sharing_content .= '<li class="share-'.$service->get_class().'">';
+ $sharing_content .= $service->get_display( $post );
+ $sharing_content .= '</li>';
+
+ if ( ( $count % 2 ) == 0 )
+ $sharing_content .= '<li class="share-end"></li>';
+
+ $count ++;
+ }
+
+ // End of wrapper
+ $sharing_content .= '<li class="share-end"></li></ul></div></div>';
+ }
+
+ $sharing_content .= '<div class="sharing-clear"></div></div>';
+
+ // Register our JS
+ wp_register_script( 'sharing-js', plugin_dir_url( __FILE__ ).'sharing.js', array( 'jquery' ), '0.1' );
+ add_action( 'wp_footer', 'sharing_add_footer' );
+ }
+ }
+
+ return $text.$sharing_content;
+}
+
+add_filter( 'the_content', 'sharing_display', 19 );
+add_filter( 'the_excerpt', 'sharing_display', 19 );
+
+// Register our CSS
+add_action( 'wp_head', 'sharing_add_header', 1 );
+
+add_action( 'template_redirect', 'sharing_process_requests' );
diff --git a/plugins/jetpack/modules/sharedaddy/sharing-sources.php b/plugins/jetpack/modules/sharedaddy/sharing-sources.php
new file mode 100644
index 00000000..1eb18722
--- /dev/null
+++ b/plugins/jetpack/modules/sharedaddy/sharing-sources.php
@@ -0,0 +1,1076 @@
+<?php
+
+abstract class Sharing_Source {
+ public $button_style;
+ protected $open_links;
+ protected $id;
+
+ public function __construct( $id, array $settings ) {
+ $this->id = $id;
+
+ if ( isset( $settings['button_style'] ) )
+ $this->button_style = $settings['button_style'];
+
+ if ( isset( $settings['open_links'] ) )
+ $this->open_links = $settings['open_links'];
+ }
+
+ public function get_id() {
+ return $this->id;
+ }
+
+ public function get_class() {
+ return $this->id;
+ }
+
+ public function has_custom_button_style() {
+ return false;
+ }
+
+ public function get_link( $url, $text, $title, $query = '' ) {
+ $klasses = array( 'share-'.$this->get_class() );
+
+ if ( $this->button_style == 'icon' || $this->button_style == 'icon-text' )
+ $klasses[] = 'share-icon';
+
+ if ( $this->button_style == 'icon' ) {
+ $text = '';
+ $klasses[] = 'no-text';
+ }
+
+ if ( !empty( $query ) ) {
+ if ( stripos( $url, '?' ) === false )
+ $url .= '?'.$query;
+ else
+ $url .= '&amp;'.$query;
+ }
+
+ if ( $this->button_style == 'text' )
+ $klasses[] = 'no-icon';
+
+ return sprintf( '<a rel="nofollow" class="%s" href="%s"%s title="%s">%s</a>', implode( ' ', $klasses ), $url, ( $this->open_links == 'new' ) ? ' target="_blank"' : '', $title, $text );
+ }
+
+ abstract public function get_name();
+ abstract public function get_display( $post );
+
+ public function display_header() {
+ }
+
+ public function display_footer() {
+ }
+
+ public function has_advanced_options() {
+ return false;
+ }
+
+ public function display_preview() {
+ echo '<div class="option">';
+
+ if ( $this->button_style == 'text' || $this->button_style == 'icon-text' )
+ echo $this->get_name();
+ else
+ echo '&nbsp;';
+
+ echo '</div>';
+ }
+
+ public function get_total( $post = false ) {
+ global $wpdb, $blog_id;
+
+ $name = strtolower( $this->get_id() );
+
+ if ( $post == false ) {
+ // get total number of shares for service
+ return (int) $wpdb->get_var( $wpdb->prepare( "SELECT SUM( count ) FROM sharing_stats WHERE blog_id = %d AND share_service = %s", $blog_id, $name ) );
+ }
+
+ //get total shares for a post
+ return (int) $wpdb->get_var( $wpdb->prepare( "SELECT count FROM sharing_stats WHERE blog_id = %d AND post_id = %d AND share_service = %s", $blog_id, $post->ID, $name ) );
+ }
+
+ public function get_posts_total() {
+ global $wpdb, $blog_id;
+
+ $totals = array();
+ $name = strtolower( $this->get_id() );
+
+ $my_data = $wpdb->get_results( $wpdb->prepare( "SELECT post_id as id, SUM( count ) as total FROM sharing_stats WHERE blog_id = %d AND share_service = %s GROUP BY post_id ORDER BY count DESC ", $blog_id, $name ) );
+
+ if ( !empty( $my_data ) )
+ foreach( $my_data as $row )
+ $totals[] = new Sharing_Post_Total( $row->id, $row->total );
+
+ usort( $totals, array( 'Sharing_Post_Total', 'cmp' ) );
+
+ return $totals;
+ }
+
+ public function process_request( $post, array $post_data ) {
+ do_action( 'sharing_bump_stats', array( 'service' => $this, 'post' => $post ) );
+ }
+}
+
+abstract class Sharing_Advanced_Source extends Sharing_Source {
+ public function has_advanced_options() {
+ return true;
+ }
+
+ abstract public function display_options();
+ abstract public function update_options( array $data );
+ abstract public function get_options();
+}
+
+
+class Share_Email extends Sharing_Source {
+ public function get_name() {
+ return __( 'Email', 'jetpack' );
+ }
+
+ // Default does nothing
+ public function process_request( $post, array $post_data ) {
+ $ajax = false;
+ if ( isset( $_SERVER['HTTP_X_REQUESTED_WITH'] ) && strtolower( $_SERVER['HTTP_X_REQUESTED_WITH'] ) == 'xmlhttprequest' )
+ $ajax = true;
+
+ $source_email = $target_email = $source_name = false;
+
+ if ( isset( $post_data['source_email'] ) && is_email( $post_data['source_email'] ) )
+ $source_email = $post_data['source_email'];
+
+ if ( isset( $post_data['target_email'] ) && is_email( $post_data['target_email'] ) )
+ $target_email = $post_data['target_email'];
+
+ if ( isset( $post_data['source_name'] ) )
+ $source_name = $post_data['source_name'];
+
+ // Test email
+ $error = 1; // Failure in data
+ if ( $source_email && $target_email && $source_name ) {
+ if ( apply_filters( 'sharing_email_check', true, $post, $post_data ) ) {
+ $data = array(
+ 'post' => $post,
+ 'source' => $source_email,
+ 'target' => $target_email,
+ 'name' => $source_name
+ );
+
+ if ( ( $data = apply_filters( 'sharing_email_can_send', $data ) ) !== false ) {
+ // Record stats
+ parent::process_request( $data['post'], $post_data );
+
+ do_action( 'sharing_email_send_post', $data );
+ }
+
+ // Return a positive regardless of whether the user is subscribed or not
+ if ( $ajax ) {
+?>
+<div class="response">
+ <div class="response-title"><?php _e( 'This post has been shared!', 'jetpack' ); ?></div>
+ <div class="response-sub"><?php printf( __( 'You have shared this post with %s', 'jetpack' ), esc_html( $target_email ) ); ?></div>
+ <div class="response-close"><a href="#" class="sharing_cancel"><?php _e( 'Close', 'jetpack' ); ?></a></div>
+</div>
+<?php
+ }
+ else
+ wp_safe_redirect( get_permalink( $post->ID ).'?shared=email' );
+
+ die();
+ }
+ else
+ $error = 2; // Email check failed
+ }
+
+ if ( $ajax )
+ echo $error;
+ else
+ wp_safe_redirect( get_permalink( $post->ID ).'?shared=email&msg=fail' );
+
+ die();
+ }
+
+ public function get_display( $post ) {
+ return $this->get_link( get_permalink( $post->ID ), _x( 'Email', 'share to', 'jetpack' ), __( 'Click to email this to a friend', 'jetpack' ), 'share=email' );
+ }
+
+ /**
+ * Outputs the hidden email dialog
+ */
+
+ public function display_footer() {
+ global $current_user;
+
+ $visible = $status = false;
+?>
+ <div id="sharing_email" style="<?php if ( $visible === false ) echo 'display: none;'; ?>">
+ <form action="" method="post">
+ <label for="target_email"><?php _e( 'Send to Email Address', 'jetpack' ) ?></label>
+ <input type="text" name="target_email" id="target_email" value="" />
+
+ <?php if ( is_user_logged_in() ) : ?>
+ <input type="hidden" name="source_name" value="<?php echo esc_attr( $current_user->display_name ); ?>" />
+ <input type="hidden" name="source_email" value="<?php echo esc_attr( $current_user->user_email ); ?>" />
+ <?php else : ?>
+
+ <label for="source_name"><?php _e( 'Your Name', 'jetpack' ) ?></label>
+ <input type="text" name="source_name" id="source_name" value="" />
+
+ <label for="source_email"><?php _e( 'Your Email Address', 'jetpack' ) ?></label>
+ <input type="text" name="source_email" id="source_email" value="" />
+
+ <?php endif; ?>
+
+ <?php do_action( 'sharing_email_dialog', 'sharedaddy' ); ?>
+
+ <img style="float: right; display: none" class="loading" src="<?php echo plugin_dir_url( __FILE__ ); ?>images/loading.gif" alt="loading" width="16" height="16" />
+ <input type="submit" value="<?php _e( 'Send Email', 'jetpack' ); ?>" class="sharing_send" />
+ <a href="#cancel" class="sharing_cancel"><?php _e( 'Cancel', 'jetpack' ); ?></a>
+
+ <div class="errors errors-1" style="display: none;">
+ <?php _e( 'Post was not sent - check your email addresses!', 'jetpack' ); ?>
+ </div>
+
+ <div class="errors errors-2" style="display: none;">
+ <?php _e( 'Email check failed, please try again', 'jetpack' ); ?>
+ </div>
+
+ <div class="errors errors-3" style="display: none;">
+ <?php _e( 'Sorry, your blog cannot share posts by email.', 'jetpack' ); ?>
+ </div>
+ </form>
+ </div>
+<?php
+ }
+}
+
+class Share_Twitter extends Sharing_Advanced_Source {
+ private $smart = true;
+
+ public function __construct( $id, array $settings ) {
+ parent::__construct( $id, $settings );
+
+ if ( isset( $settings['smart'] ) )
+ $this->smart = $settings['smart'];
+ }
+
+ public function get_name() {
+ return __( 'Twitter', 'jetpack' );
+ }
+
+ public function get_display( $post ) {
+ if ( $this->smart == 'smart' )
+ return '<div class="twitter_button"><iframe allowtransparency="true" frameborder="0" scrolling="no" src="http://platform.twitter.com/widgets/tweet_button.html?url=' . rawurlencode( apply_filters( 'sharing_permalink', get_permalink( $post->ID ), $post->ID, $this->id ) ) . '&amp;counturl=' . rawurlencode( str_replace( 'https://', 'http://', get_permalink( $post->ID ) ) ) . '&amp;count=horizontal&amp;text=' . rawurlencode( apply_filters( 'sharing_post_title', $post->post_title, $post->ID, $this->id ) ) . ': " style="width:97px; height:20px;"></iframe></div>';
+ else
+ return $this->get_link( get_permalink( $post->ID ), _x( 'Twitter', 'share to', 'jetpack' ), __( 'Click to share on Twitter', 'jetpack' ), 'share=twitter' );
+ }
+
+ public function process_request( $post, array $post_data ) {
+ $post_title = apply_filters( 'sharing_post_title', $post->post_title, $post->ID, $this->id );
+ $post_link = apply_filters( 'sharing_permalink', get_permalink( $post->ID ), $post->ID, $this->id );
+
+ $twitter_url = '';
+ if ( function_exists( 'mb_stripos' ) )
+ $mb = true;
+ else
+ $mb = false;
+
+ if ( ( $mb && ( mb_strlen( $post_title ) + 1 + mb_strlen( $post_link ) ) > 140 ) || ( !$mb && ( strlen( $post_title ) + 1 + strlen( $post_link ) ) > 140 ) ) {
+ if ( $mb ) {
+ $twitter_url = 'http://twitter.com/?status=' . rawurlencode( ( mb_substr( $post_title, 0, (140 - mb_strlen ( $post_link ) - 4 ) ) ) . '... ' . $post_link );
+ } else {
+ $twitter_url = 'http://twitter.com/?status=' . rawurlencode( ( substr( $post_title, 0, (140 - strlen ( $post_link ) - 4 ) ) ) . '... ' . $post_link );
+ }
+ }
+ else {
+ $twitter_url = 'http://twitter.com/?status=' . rawurlencode( $post_title . ' ' . $post_link );
+ }
+
+ // Record stats
+ parent::process_request( $post, $post_data );
+
+ // Redirect to Twitter
+ wp_redirect( $twitter_url );
+ die();
+ }
+
+ public function has_custom_button_style() {
+ return $this->smart;
+ }
+
+ public function display_preview() {
+?>
+ <div class="option option-smart-<?php echo $this->smart ? 'on' : 'off'; ?>">
+ <?php
+ if ( !$this->smart ) {
+ if ( $this->button_style == 'text' || $this->button_style == 'icon-text' )
+ echo $this->get_name();
+ else
+ echo '&nbsp;';
+ }
+ ?>
+ </div>
+<?php
+ }
+
+ public function update_options( array $data ) {
+ $this->smart = false;
+
+ if ( isset( $data['smart'] ) )
+ $this->smart = true;
+ }
+
+ public function get_options() {
+ return array(
+ 'smart' => $this->smart
+ );
+ }
+
+ public function display_options() {
+?>
+ <div class="input">
+ <label>
+ <input name="smart" type="checkbox"<?php if ( $this->smart ) echo ' checked="checked"'; ?>/>
+
+ <?php _e( 'Use smart button', 'jetpack' ); ?>
+ </label>
+ </div>
+<?php
+ }
+}
+
+class Share_Stumbleupon extends Sharing_Advanced_Source {
+ private $smart = false;
+
+ public function __construct( $id, array $settings ) {
+ parent::__construct( $id, $settings );
+
+ if ( isset( $settings['smart'] ) )
+ $this->smart = $settings['smart'];
+ }
+
+ public function get_name() {
+ return __( 'StumbleUpon', 'jetpack' );
+ }
+
+ public function has_custom_button_style() {
+ return $this->smart;
+ }
+
+ public function get_display( $post ) {
+ if ( $this->smart == 'smart' )
+ return '<div class="stumbleupon_button"><iframe src="http://www.stumbleupon.com/badge/embed/1/?url=' . urlencode( apply_filters( 'sharing_permalink', get_permalink( $post->ID ), $post->ID, $this->id ) ) . '&amp;title=' . urlencode( apply_filters( 'sharing_post_title', $post->post_title, $post->ID, $this->id ) ) . '" scrolling="no" frameborder="0" style="border:none; overflow:hidden; width:74px; height: 18px;" allowTransparency="true"></iframe></div>';
+ else
+ return $this->get_link( get_permalink( $post->ID ), _x( 'StumbleUpon', 'share to', 'jetpack' ), __( 'Click to share on StumbleUpon', 'jetpack' ), 'share=stumbleupon' );
+ }
+
+ public function display_preview() {
+?>
+ <div class="option option-smart-<?php echo $this->smart ? 'on' : 'off'; ?>">
+ <?php
+ if ( !$this->smart ) {
+ if ( $this->button_style == 'text' || $this->button_style == 'icon-text' )
+ echo $this->get_name();
+ else
+ echo '&nbsp;';
+ }
+ ?>
+ </div>
+<?php
+ }
+
+ public function process_request( $post, array $post_data ) {
+ $stumbleupon_url = 'http://www.stumbleupon.com/submit?url=' . urlencode( apply_filters( 'sharing_permalink', get_permalink( $post->ID ), $post->ID, $this->id ) ) . '&title=' . urlencode( apply_filters( 'sharing_post_title', $post->post_title, $post->ID, $this->id ) );
+
+ // Record stats
+ parent::process_request( $post, $post_data );
+
+ // Redirect to Stumbleupon
+ wp_redirect( $stumbleupon_url );
+ die();
+ }
+
+ public function update_options( array $data ) {
+ $this->smart = false;
+
+ if ( isset( $data['smart'] ) )
+ $this->smart = true;
+ }
+
+ public function get_options() {
+ return array(
+ 'smart' => $this->smart
+ );
+ }
+
+ public function display_options() {
+?>
+ <div class="input">
+ <label>
+ <input name="smart" type="checkbox"<?php if ( $this->smart ) echo ' checked="checked"'; ?>/>
+
+ <?php _e( 'Use smart button', 'jetpack' ); ?>
+ </label>
+ </div>
+<?php
+ }
+}
+
+class Share_Reddit extends Sharing_Advanced_Source {
+ private $smart = false;
+
+ public function __construct( $id, array $settings ) {
+ parent::__construct( $id, $settings );
+
+ if ( isset( $settings['smart'] ) )
+ $this->smart = $settings['smart'];
+ }
+
+ public function get_name() {
+ return __( 'Reddit', 'jetpack' );
+ }
+
+ public function get_display( $post ) {
+ if ( $this->smart == 'smart' )
+ return '<div class="reddit_button"><iframe src="http://www.reddit.com/static/button/button1.html?width=120&amp;url=' . urlencode( apply_filters( 'sharing_permalink', get_permalink( $post->ID ), $post->ID, $this->id ) ) . '&amp;title=' . rawurlencode( apply_filters( 'sharing_post_title', $post->post_title, $post->ID, $this->id ) ) . '" height="22" width="120" scrolling="no" frameborder="0"></iframe></div>';
+ else
+ return $this->get_link( get_permalink( $post->ID ), __( 'Reddit', 'share to', 'jetpack' ), __( 'Click to share on Reddit', 'jetpack' ), 'share=reddit' );
+ }
+
+ public function update_options( array $data ) {
+ $this->smart = false;
+
+ if ( isset( $data['smart'] ) )
+ $this->smart = true;
+ }
+
+ public function has_custom_button_style() {
+ return $this->smart;
+ }
+
+ public function get_options() {
+ return array(
+ 'smart' => $this->smart
+ );
+ }
+
+ public function display_options() {
+?>
+ <div class="input">
+ <label>
+ <input name="smart" type="checkbox"<?php if ( $this->smart ) echo ' checked="checked"'; ?>/>
+
+ <?php _e( 'Use smart button', 'jetpack' ); ?>
+ </label>
+ </div>
+<?php
+ }
+
+ public function display_preview() {
+?>
+ <div class="option option-smart-<?php echo $this->smart ? 'on' : 'off'; ?>">
+ <?php
+ if ( !$this->smart ) {
+ if ( $this->button_style == 'text' || $this->button_style == 'icon-text' )
+ echo $this->get_name();
+ else
+ echo '&nbsp;';
+ }
+ ?>
+ </div>
+<?php
+ }
+
+ public function process_request( $post, array $post_data ) {
+ $reddit_url = 'http://reddit.com/submit?url=' . urlencode( apply_filters( 'sharing_permalink', get_permalink( $post->ID ), $post->ID, $this->id ) ) . '&title=' . urlencode( apply_filters( 'sharing_post_title', $post->post_title, $post->ID, $this->id ) );
+
+ // Record stats
+ parent::process_request( $post, $post_data );
+
+ // Redirect to Reddit
+ wp_redirect( $reddit_url );
+ die();
+ }
+}
+
+class Share_Digg extends Sharing_Advanced_Source {
+ private $smart = false;
+
+ public function __construct( $id, array $settings ) {
+ parent::__construct( $id, $settings );
+
+ if ( isset( $settings['smart'] ) )
+ $this->smart = $settings['smart'];
+ }
+
+ public function get_name() {
+ return __( 'Digg', 'jetpack' );
+ }
+
+ public function has_custom_button_style() {
+ return $this->smart;
+ }
+
+ public function get_display( $post ) {
+ if ( $this->smart ) {
+ $url = $this->get_link( 'http://digg.com/submit?url='. urlencode( apply_filters( 'sharing_permalink', get_permalink( $post->ID ), $post->ID, $this->id ) ) . '&amp;title=' . urlencode( apply_filters( 'sharing_post_title', $post->post_title, $post->ID, $this->id ) ), 'Digg', __( 'Click to Digg this post', 'jetpack' ) );
+ return '<div class="digg_button">' . str_replace( 'class="', 'class="DiggThisButton DiggCompact ', $url ) . '</div>';
+ }
+ else
+ return $this->get_link( get_permalink( $post->ID ), _x( 'Digg', 'share to', 'jetpack' ), __( 'Click to Digg this post', 'jetpack' ), 'share=digg' );
+ }
+
+ public function process_request( $post, array $post_data ) {
+ $digg_url = 'http://digg.com/submit?url=' . urlencode( apply_filters( 'sharing_permalink', get_permalink( $post->ID ), $post->ID, $this->id ) ) . '&title=' . urlencode( apply_filters( 'sharing_post_title', $post->post_title, $post->ID, $this->id ) );
+
+ // Record stats
+ parent::process_request( $post, $post_data );
+
+ // Redirect to Digg
+ wp_redirect( $digg_url );
+ die();
+ }
+
+ public function display_header() {
+ if ( $this->smart ) {
+?>
+<script type="text/javascript">
+(function() {
+ var s = document.createElement('SCRIPT'), s1 = document.getElementsByTagName('SCRIPT')[0];
+ s.type = 'text/javascript';
+ s.async = true;
+ s.src = 'http://widgets.digg.com/buttons.js';
+ s1.parentNode.insertBefore(s, s1);
+})();
+</script>
+<?php
+ }
+ }
+
+ public function update_options( array $data ) {
+ $this->smart = false;
+
+ if ( isset( $data['smart'] ) )
+ $this->smart = true;
+ }
+
+ public function get_options() {
+ return array(
+ 'smart' => $this->smart
+ );
+ }
+
+ public function display_options() {
+?>
+ <div class="input">
+ <label>
+ <input name="smart" type="checkbox"<?php if ( $this->smart ) echo ' checked="checked"'; ?>/>
+
+ <?php _e( 'Use smart button', 'jetpack' ); ?>
+ </label>
+ </div>
+<?php
+ }
+
+ public function display_preview() {
+?>
+ <div class="option option-smart-<?php echo $this->smart ? 'on' : 'off'; ?>">
+ <?php
+ if ( !$this->smart ) {
+ if ( $this->button_style == 'text' || $this->button_style == 'icon-text' )
+ echo $this->get_name();
+ else
+ echo '&nbsp;';
+ }
+ ?>
+ </div>
+<?php
+ }
+}
+
+class Share_LinkedIn extends Sharing_Advanced_Source {
+ private $smart = true;
+
+ public function __construct( $id, array $settings ) {
+ parent::__construct( $id, $settings );
+
+ if ( isset( $settings['smart'] ) )
+ $this->smart = (bool) $settings['smart'];
+ }
+
+ public function get_name() {
+ return __( 'LinkedIn', 'jetpack' );
+ }
+
+ public function has_custom_button_style() {
+ return (bool) $this->smart;
+ }
+
+ public function display_header() {
+ }
+
+ public function get_display( $post ) {
+ static $added_linkedin_js = false;
+ $proto = ( is_ssl() ) ? 'https://' : 'http://';
+ $permalink = get_permalink( $post->ID );
+ $display = '';
+
+ if( $this->smart ) {
+
+ // So we don't spit out the linkedin js for each post on index pages
+ if( ! $added_linkedin_js ) {
+ $display .= sprintf( '<script type="text/javascript" src="%splatform.linkedin.com/in.js"></script>', $proto );
+ $added_linkedin_js = true;
+ }
+
+ $display .= sprintf( '<div class="linkedin_button"><script type="in/share" data-url="%s" data-counter="right"></script></div>', esc_url( $permalink ) );
+
+ } else {
+
+ $display = $this->get_link( $permalink, _x( 'LinkedIn', 'share to', 'jetpack' ), __( 'Click to share on LinkedIn', 'jetpack' ), 'share=linkedin' );
+
+ }
+ return $display;
+ }
+
+ public function process_request( $post, array $post_data ) {
+ $post_link = apply_filters( 'sharing_permalink', get_permalink( $post->ID ), $post->ID, $this->id );
+
+ // http://www.linkedin.com/shareArticle?mini=true&url={articleUrl}&title={articleTitle}&summary={articleSummary}&source={articleSource}
+
+ $encoded_title = rawurlencode( $post->post_title );
+ if( strlen( $encoded_title ) > 200 )
+ $encoded_title = substr( $encoded_title, 0, 197 ) . '...';
+
+ $encoded_summary = rawurlencode( get_the_excerpt() );
+ if( strlen( $encoded_summary ) > 256 )
+ $encoded_summary = substr( $encoded_summary, 0, 253 ) . '...';
+
+ $source = get_bloginfo( 'name' );
+
+ $query = add_query_arg( array(
+ 'title' => $encoded_title,
+ 'url' => rawurlencode( $post_link ),
+ 'source' => rawurlencode( $source ),
+ 'summary' => $encoded_summary,
+ ) );
+
+ $linkedin_url = 'http://www.linkedin.com/shareArticle?mini=true' . $query;
+
+ // Record stats
+ parent::process_request( $post, $post_data );
+
+ // Redirect to LinkedIn
+ wp_redirect( $linkedin_url );
+ die();
+ }
+
+ public function update_options( array $data ) {
+ $this->smart = false;
+
+ if ( isset( $data['smart'] ) )
+ $this->smart = true;
+ }
+
+ public function get_options() {
+ return array(
+ 'smart' => $this->smart
+ );
+ }
+
+ public function display_options() {
+ ?><div class="input">
+ <label>
+ <input name="smart" type="checkbox"<?php checked( $this->smart ); ?>/>
+ <?php _e( 'Use smart button', 'jetpack' ); ?>
+ </label>
+ </div><?php
+ }
+
+ public function display_preview() {
+ ?>
+ <div class="option option-smart-<?php echo $this->smart ? 'on' : 'off'; ?>">
+ <?php
+ if ( ! $this->smart ) {
+ if ( $this->button_style == 'text' || $this->button_style == 'icon-text' )
+ echo $this->get_name();
+ else
+ echo '&nbsp;';
+ } ?>
+ </div><?php
+ }
+}
+
+class Share_Facebook extends Sharing_Advanced_Source {
+ private $share_type = 'default';
+
+ public function __construct( $id, array $settings ) {
+ parent::__construct( $id, $settings );
+
+ if ( isset( $settings['share_type'] ) )
+ $this->share_type = $settings['share_type'];
+ }
+
+ public function get_name() {
+ return __( 'Facebook', 'jetpack' );
+ }
+
+ public function has_custom_button_style() {
+ return $this->share_type != 'default';
+ }
+
+ public function display_header() {
+ if ( $this->share_type == 'share' ) {
+ // Set the open graph description, otherwise Facebook may pick up some random text from the page
+ global $post;
+
+ if ( $post && $post->ID > 0 )
+ echo '<meta property="og:description" content="'.esc_attr( apply_filters( 'sharing_post_title', $post->post_title, $post->ID, $this->id ) ).'" />';
+ }
+ }
+
+ function guess_locale_from_lang( $lang ) {
+ $lang = strtolower( str_replace( '-', '_', $lang ) );
+ if ( 5 == strlen( $lang ) )
+ $lang = substr( $lang, 0, 3 ) . strtoupper( substr( $lang, 3, 2 ) ); // Already in xx_xx, just make sure it's uppered
+ else if ( 3 == strlen( $lang ) )
+ $lang = $lang; // Don't know what to do with these
+ else
+ $lang = $lang . '_' . strtoupper( $lang ); // Sometimes this gives a workable locale
+ return $lang;
+ }
+
+ public function get_display( $post ) {
+ if ( $this->share_type == 'share' ) {
+ return '<div class="facebook_button"><a name="fb_share" rel="nofollow" type="button" share_url="' . apply_filters( 'sharing_permalink', get_permalink( $post->ID ), $post->ID, $this->id ) . '" href="http://www.facebook.com/sharer.php?u=' . rawurlencode( get_permalink( $post->ID ) ) . '&t=' . rawurlencode( apply_filters( 'sharing_post_title', $post->post_title, $post->ID, $this->id ) ) . '">'.__( 'Share' , 'jetpack' ).'</a><script src="http://static.ak.fbcdn.net/connect.php/js/FB.Share" type="text/javascript"></script></div>';
+ } else if ( $this->share_type == 'like' ) {
+ $url = 'http://www.facebook.com/plugins/like.php?href=' . rawurlencode( get_permalink( $post->ID ) ) . '&amp;layout=button_count&amp;show_faces=false&amp;action=like&amp;colorscheme=light&amp;height=21';
+
+ // Default widths to suit English
+ $inner_w = 90;
+
+ // Locale-specific widths/overrides
+ $widths = array(
+ 'de' => array( 'width' => 100, 'locale' => 'de_DE' ),
+ 'da' => array( 'width' => 120, 'locale' => 'da_DK' ),
+ 'fi' => array( 'width' => 100, 'locale' => 'fi_FI' ),
+ );
+
+ $widths = apply_filters( 'sharing_facebook_like_widths', $widths );
+
+ // Fix the button to the blogs locale and then adjust the width
+ $locale = str_replace( '-', '_', get_locale() );
+
+ if ( isset( $widths[substr( $locale, 0, 2 )] ) ) {
+ $inner_w = $widths[substr( $locale, 0, 2 )]['width'];
+ $locale = $widths[substr( $locale, 0, 2 )]['locale'];
+ } else {
+ $locale = $this->guess_locale_from_lang( get_locale() );
+ }
+
+ if ( $locale && 'en_US' != $locale )
+ $url .= '&amp;locale=' . $locale;
+
+ $url .= '&amp;width='.$inner_w;
+ return '<div class="like_button"><iframe src="'.$url.'" scrolling="no" frameborder="0" style="border:none; overflow:hidden; width:'.( $inner_w + 6 ).'px; height:21px;" allowTransparency="true"></iframe></div>';
+ }
+
+ return $this->get_link( get_permalink( $post->ID ), _x( 'Facebook', 'share to', 'jetpack' ), __( 'Share on Facebook', 'jetpack' ), 'share=facebook' );
+ }
+
+
+ public function update_options( array $data ) {
+ $this->share_type = 'default';
+
+ if ( isset( $data['share_type'] ) && in_array( $data['share_type'], array( 'default', 'like', 'share' ) ) )
+ $this->share_type = $data['share_type'];
+ }
+
+ public function get_options() {
+ return array(
+ 'share_type' => $this->share_type
+ );
+ }
+
+ public function display_options() {
+?>
+ <div class="input">
+ <label>
+ <select name="share_type">
+ <option value="default"<?php if ( $this->share_type == 'default' ) echo ' selected="selected"'; ?>><?php _e( 'Default button', 'jetpack' ); ?></option>
+ <option value="share"<?php if ( $this->share_type == 'share' ) echo ' selected="selected"'; ?>><?php _e( 'Share button', 'jetpack' ); ?></option>
+ <option value="like"<?php if ( $this->share_type == 'like' ) echo ' selected="selected"'; ?>><?php _e( 'Like button', 'jetpack' ); ?></option>
+ </select>
+ </label>
+ </div>
+<?php
+ }
+
+ public function display_preview() {
+?>
+ <div class="option option-smart-<?php
+
+ if ( $this->share_type == 'share' ) {
+ echo ( 'on">' );
+ echo '&nbsp;';
+ }
+ elseif ( $this->share_type == 'like' ) {
+ echo ( 'like">' );
+ echo '&nbsp;';
+ }
+ else {
+ echo ( 'off">' );
+ if ( $this->button_style == 'text' || $this->button_style == 'icon-text' )
+ echo $this->get_name();
+ else
+ echo '&nbsp;';
+ }
+ ?>
+ </div>
+<?php
+ }
+
+ public function process_request( $post, array $post_data ) {
+ $fb_url = 'http://www.facebook.com/sharer.php?u=' . urlencode( apply_filters( 'sharing_permalink', get_permalink( $post->ID ), $post->ID, $this->id ) ) . '&t=' . urlencode( apply_filters( 'sharing_post_title', $post->post_title, $post->ID, $this->id ) );
+
+ // Record stats
+ parent::process_request( $post, $post_data );
+
+ // Redirect to Facebook
+ wp_redirect( $fb_url );
+ die();
+ }
+}
+
+class Share_Print extends Sharing_Source {
+ public function get_name() {
+ return __( 'Print', 'jetpack' );
+ }
+
+ public function get_display( $post ) {
+ return $this->get_link( get_permalink( $post->ID ). ( ( is_single() || is_page() ) ? '#print': '' ), _x( 'Print', 'share to', 'jetpack' ), __( 'Click to print', 'jetpack' ) );
+ }
+}
+
+class Share_PressThis extends Sharing_Source {
+ public function get_name() {
+ return __( 'Press This', 'jetpack' );
+ }
+
+ public function process_request( $post, array $post_data ) {
+ global $current_user;
+
+ $blogs = get_blogs_of_user( $current_user->ID );
+ if ( empty( $blogs ) ) {
+ wp_safe_redirect( get_permalink( $post->ID ) );
+ die();
+ }
+
+ $blog = current( $blogs );
+
+ $url = $blog->siteurl.'/wp-admin/press-this.php?u='.urlencode( apply_filters( 'sharing_permalink', get_permalink( $post->ID ), $post->ID, $this->id ) ).'&t='.urlencode( apply_filters( 'sharing_post_title', $post->post_title, $post->ID, $this->id ) ).'&v=4';
+
+ if ( isset( $_GET['sel'] ) )
+ $url .= '&s='.urlencode( $_GET['sel'] );
+
+ // Record stats
+ parent::process_request( $post, $post_data );
+
+ // Redirect to Press This
+ wp_safe_redirect( $url );
+ die();
+ }
+
+ public function get_display( $post ) {
+ return $this->get_link( get_permalink( $post->ID ), _x( 'Press This', 'share to', 'jetpack' ), __( 'Click to Press This!', 'jetpack' ), 'share=press-this' );
+ }
+}
+
+class Share_GooglePlus1 extends Sharing_Source {
+ private $state = false;
+
+ public function get_name() {
+ return __( 'Google +1', 'jetpack' );
+ }
+
+ public function get_display( $post ) {
+ return '<div class="googleplus1_button"><div class="g-plusone" data-size="medium" data-callback="sharing_plusone" data-href="' . esc_attr( get_permalink( $post->ID ) ) . '"></div></div>';
+ }
+
+ public function display_preview() {
+?>
+ <div class="option option-smart-on"></div>
+<?php
+ }
+
+ public function get_state() {
+ return $this->state;
+ }
+
+ public function process_request( $post, array $post_data ) {
+
+ if ( isset( $post_data['state'] ) ) {
+ $this->state = $post_data['state'];
+ }
+ // Record stats
+ parent::process_request( $post, $post_data );
+ die();
+ }
+
+ public function display_footer() {
+ global $post;
+?>
+ <script type="text/javascript" charset="utf-8">
+ function sharing_plusone( obj ) {
+ jQuery.ajax( {
+ url: '<?php echo get_permalink( $post->ID ) . '?share=google-plus-1'; ?>',
+ type: 'POST',
+ data: obj
+ } );
+ }
+ </script>
+ <script type="text/javascript" src="http://apis.google.com/js/plusone.js"></script>
+<?php
+ }
+
+ public function get_total( $post = false ) {
+ global $wpdb, $blog_id;
+
+ $name = strtolower( $this->get_id() );
+
+ if ( $post == false ) {
+ // get total number of shares for service
+ return $wpdb->get_var( $wpdb->prepare( "SELECT SUM( count ) FROM sharing_stats WHERE blog_id = %d AND share_service = %s", $blog_id, $name ) );
+ }
+
+ //get total shares for a post
+ return $wpdb->get_var( $wpdb->prepare( "SELECT count FROM sharing_stats WHERE blog_id = %d AND post_id = %d AND share_service = %s", $blog_id, $post->ID, $name ) );
+ }
+}
+
+class Share_Custom extends Sharing_Advanced_Source {
+ private $name;
+ private $icon;
+ private $url;
+
+ public function get_class() {
+ return 'custom';
+ }
+
+ public function __construct( $id, array $settings ) {
+ parent::__construct( $id, $settings );
+
+ if ( isset( $settings['name'] ) )
+ $this->name = $settings['name'];
+
+ if ( isset( $settings['icon'] ) )
+ $this->icon = $settings['icon'];
+
+ if ( isset( $settings['url'] ) )
+ $this->url = $settings['url'];
+ }
+
+ public function get_name() {
+ return $this->name;
+ }
+
+ public function get_display( $post ) {
+ $str = $this->get_link( get_permalink( $post->ID ), esc_html( $this->name ), __( 'Click to share', 'jetpack' ), 'share='.$this->id );
+ return str_replace( 'class="', 'style="background:url(' . esc_url( $this->icon ) . ') no-repeat center left;" class="', $str );
+ }
+
+ public function process_request( $post, array $post_data ) {
+ $url = $this->url;
+ $url = str_replace( '%post_url%', urlencode( apply_filters( 'sharing_permalink', get_permalink( $post->ID ), $post->ID, $this->id ) ), $url );
+ $url = str_replace( '%post_full_url%', urlencode( get_permalink( $post->ID ) ), $url );
+ $url = str_replace( '%post_title%', urlencode( apply_filters( 'sharing_post_title', $post->post_title, $post->ID, $this->id ) ), $url );
+
+ if ( strpos( $url, '%post_tags%' ) !== false ) {
+ $tags = get_the_tags( $post->ID );
+ $tagged = '';
+
+ if ( $tags ) {
+ foreach ( $tags AS $tag ) {
+ $tagged[] = urlencode( $tag->name );
+ }
+
+ $tagged = implode( ',', $tagged );
+ }
+
+ $url = str_replace( '%post_tags%', $tagged, $url );
+ }
+
+ if ( strpos( $url, '%post_excerpt%' ) !== false ) {
+ $url_excerpt = $post->post_excerpt;
+ if ( empty( $url_excerpt ) )
+ $url_excerpt = $post->post_content;
+
+ $url_excerpt = strip_tags( strip_shortcodes( $url_excerpt ) );
+ $url_excerpt = wp_html_excerpt( $url_excerpt, 100 );
+ $url_excerpt = rtrim( preg_replace( '/[^ .]*$/', '', $url_excerpt ) );
+ $url = str_replace( '%post_excerpt%', urlencode( $url_excerpt ), $url );
+ }
+
+ // Record stats
+ parent::process_request( $post, $post_data );
+
+ // Redirect
+ wp_redirect( $url );
+ die();
+ }
+
+ public function display_options() {
+?>
+<div class="input">
+ <table class="form-table">
+ <tbody>
+ <tr>
+ <th scope="row"><?php _e( 'Label', 'jetpack' ); ?></th>
+ <td><input type="text" name="name" value="<?php echo esc_attr( $this->name ); ?>" /></td>
+ </tr>
+
+ <tr>
+ <th scope="row"><?php _e( 'URL', 'jetpack' ); ?></th>
+ <td><input type="text" name="url" value="<?php echo esc_attr( $this->url ); ?>" /></td>
+ </tr>
+
+ <tr>
+ <th scope="row"><?php _e( 'Icon', 'jetpack' ); ?></th>
+ <td><input type="text" name="icon" value="<?php echo esc_attr( $this->icon ); ?>" /></td>
+ </tr>
+
+ <tr>
+ <th scope="row"></th>
+ <td>
+ <input class="button-secondary" type="submit"value="<?php _e( 'Save', 'jetpack' ); ?>" />
+ <a href="#" class="remove"><small><?php _e( 'Remove Service', 'jetpack' ); ?></small></a>
+ </td>
+ </tr>
+ </tbody>
+ </table>
+</div>
+<?php
+ }
+
+ public function update_options( array $data ) {
+ $name = trim( wp_html_excerpt( wp_kses( stripslashes( $data['name'] ), array() ), 30 ) );
+ $url = trim( esc_url_raw( $data['url'] ) );
+ $icon = trim( esc_url_raw( $data['icon'] ) );
+
+ if ( $name )
+ $this->name = $name;
+
+ if ( $url )
+ $this->url = $url;
+
+ if ( $icon )
+ $this->icon = $icon;
+ }
+
+ public function get_options() {
+ return array(
+ 'name' => $this->name,
+ 'icon' => $this->icon,
+ 'url' => $this->url,
+ );
+ }
+}
diff --git a/plugins/jetpack/modules/sharedaddy/sharing.css b/plugins/jetpack/modules/sharedaddy/sharing.css
new file mode 100644
index 00000000..e5874f29
--- /dev/null
+++ b/plugins/jetpack/modules/sharedaddy/sharing.css
@@ -0,0 +1,278 @@
+.sharing {
+ padding: 0 0 10px 0;
+}
+
+.sharing_label {
+ line-height: 24px;
+ padding: 10px 10px 0 0;
+ float: left;
+ font-weight: bold;
+}
+
+.sharing ul, .sharing-hidden ul {
+ list-style: none outside none !important;
+ padding: 0 !important;
+ margin: 0 !important;
+ float: left;
+ text-indent: 0 !important;
+}
+
+.sharing li, .sharing-hidden li {
+ display: list-item !important;
+ list-style: none outside none !important;
+ float: left;
+ padding: 10px 0 0 0 !important;
+ margin: 0 10px 0 0 !important;
+ background: none !important;
+}
+
+.sharing li div, .sharing-hidden li div {
+ margin: 0 !important;
+}
+
+.sharing li:before, .sharing-hidden li:before {
+ content: none !important;
+}
+
+
+.sharing li a, .sharing-hidden li a {
+ padding: 0px 0 0 20px;
+ line-height: 24px;
+ display: block;
+}
+
+.sharing li.share-regular a, .sharing-hidden li a, .sharing li.share-regular a:hover, .sharing-hidden li a:hover {
+ border: none !important;
+}
+
+.share-custom a.sharing-anchor{
+ color: #666;
+ font-size:11px;
+ font-family: arial, tahoma, verdana, sans-serif;
+ text-decoration: none;
+}
+
+.sharing li.share-custom{
+
+}
+
+.sharing-hidden a{
+ color: #666;
+}
+
+.sharing a.share-twitter,.sharing-hidden a.share-twitter { background: url('images/twitter.png') no-repeat center left; }
+.sharing a.share-facebook,.sharing-hidden a.share-facebook { background: url('images/facebook.png') no-repeat center left; }
+.sharing a.share-email,.sharing-hidden a.share-email { background: url('images/email.png') no-repeat center left; }
+.sharing a.share-digg,.sharing-hidden a.share-digg { background: url('images/digg.png') no-repeat center left; }
+.sharing a.share-stumbleupon,.sharing-hidden a.share-stumbleupon { background: url('images/stumbleupon.png') no-repeat center left; }
+.sharing a.share-reddit,.sharing-hidden a.share-reddit { background: url('images/reddit.png') no-repeat center left; }
+.sharing a.share-print,.sharing-hidden a.share-print { background: url('images/print.png') no-repeat center left; }
+.sharing a.share-press-this,.sharing-hidden a.share-press-this { background: url('images/wordpress.png') no-repeat center left; }
+.sharing a.share-linkedin,.sharing-hidden a.share-linkedin { background: url('images/linkedin.png') no-repeat center left; }
+.sharing a.share-google-plus-1,.sharing-hidden a.share-google-plus-1 { background: url('images/googleplus1.png') no-repeat center left; }
+
+.sharing div.twitter_button { padding: 4px 0; }
+.sharing div.reddit_button { padding: 4px 0 0 0; }
+.sharing div.stumbleupon_button { padding: 4px; }
+.sharing div.digg_button { font-size: 0px; padding: 0 0 0 0; }
+.sharing div.facebook_button { font-size: 0px; padding: 5px 0; height: 18px;}
+.sharing div.like_button { font-size: 0px; padding: 4px 0; height: 18px;}
+.sharing div.linkedin_button { padding-top: 4px; line-height: 16px; }
+.sharing div.googleplus1_button { padding-top: 4px; line-height: 16px; }
+
+.sharing-hidden li {
+ width: 130px;
+}
+
+.sharing-hidden div.twitter_button { padding: 0; }
+.sharing-hidden div.reddit_button { padding: 0; }
+.sharing-hidden div.stumbleupon_button { padding: 0; }
+.sharing-hidden div.digg_button { font-size: 0px; padding: 0; }
+.sharing-hidden div.facebook_button { font-size: 0px; padding: 0;}
+.sharing-hidden div.like_button { font-size: 0px; padding: 0;}
+.sharing-hidden div.linkedin_button { padding: 0; }
+.sharing-hidden div.googleplus1_button { padding: 0; }
+
+
+.sharing div.facebook_button a, .sharing-hidden.facebook_button a {
+ line-height: none;
+ padding: 0;
+}
+
+#sharing_email {
+ background-color: #fff;
+ padding: 15px;
+ width: 312px;
+ position: absolute;
+ border: 2px solid #6e6e6e;
+ z-index: 1001;
+ text-align: left;
+}
+
+#sharing_email .errors {
+ color: #fff;
+ background-color: #771a09;
+ font-size: 11px;
+ padding: 5px 8px;
+ line-height: 11px;
+ margin: 10px 0 0 0;
+}
+
+#sharing_email label {
+ font-size: 11px;
+ color: #333;
+ font-weight: bold;
+ display: block;
+ padding: 0 0 4px 0;
+ text-align: left;
+}
+
+#sharing_email input[type="text"] {
+ width: 100%;
+ margin-bottom: 12px;
+}
+
+#sharing_email .sharing_send {
+}
+
+#sharing_email .sharing_cancel {
+ padding: 0 0 0 10px;
+ font-size: 11px;
+}
+
+#sharing_email .recaptcha {
+ width: 312px;
+ height: 123px;
+ margin: 10px 0 14px 0;
+}
+
+#sharing_background {
+ position: fixed;
+ top: 0;
+ left: 0;
+ width: 100%;
+ height: 100%;
+ background-color: black;
+ z-index: 1000;
+}
+
+.sharing-hidden {
+ padding: 10px 0 0 0;
+}
+
+.sharing-hidden .inner {
+ border: 2px solid #6e6e6e;
+ padding: 15px 0 15px 0;
+ background: #fff;
+ position: absolute;
+ top: 0;
+ left: 0;
+ margin: 0px !important;
+ width: 300px;
+ z-index: 1000;
+}
+
+.sharing-hidden ul {
+ background: white url('images/share-bg.png') repeat-y center center;
+ margin: 0 !important;
+}
+
+.sharing-hidden li {
+ padding-left:10px !important;
+ padding-right:10px !important;
+ padding-top:0px !important;
+ margin: 0 !important;
+ margin-bottom:10px !important;
+}
+
+.sharing-hidden li a {
+ padding-right: 20px;
+ font-size: 11px;
+ line-height: 16px;
+ display: block;
+ text-decoration: none !important;
+ border-bottom: 0px !important;
+
+}
+
+.sharing-hidden li a.no-text{
+ width: 16px;
+ height:16px;
+}
+
+.sharing li a.no-text{
+ width: 16px;
+ height: 16px;
+ margin-top: 4px;
+ padding: 0px !important;
+}
+
+.sharing li a.no-icon {
+ background: none !important;
+ padding-left: 0 !important;
+}
+
+.sharing li.share-end, .sharing-hidden li.share-end {
+ clear: both;
+ height: 0;
+ padding: 0px !important;
+ margin: 0px !important;
+ width: 0;
+ visibility: hidden;
+ float: none;
+}
+
+.sharing .sharing-anchor {
+ border-radius: 3px;
+ -moz-border-radius: 3px;
+ -webkit-border-radius: 3px;
+ border: 1px solid #d8d8d8;
+ float: left;
+ line-height: 20px;
+ padding: 0 8px 0 21px;
+ background: #fff url('images/sharing-hidden.png') no-repeat 0px center;
+ font-weight: normal;
+}
+
+.sharing-clear {
+ clear: left;
+}
+
+.response {
+}
+
+.response-title {
+ font-size: 12px;
+ line-height: 18px;
+ font-weight: bold;
+}
+
+.response-sub {
+ font-size: 11px;
+ line-height: 24px;
+}
+
+.response-close .sharing_cancel {
+ padding: 0px !important;
+}
+
+li.share-email, li.share-custom a.sharing-anchor {
+ display: none !important;
+}
+
+li.share-service-visible {
+ display: list-item !important;
+}
+
+li.share-custom a.sharing-anchor.share-service-visible {
+ display: inline !important;
+}
+
+/* =RTL
+-------------------------------------------------------------- */
+body.rtl .sharing ul {
+ float: right;
+}
+body.rtl .sharing li {
+ margin: 0 0 0 10px !important;
+}
diff --git a/plugins/jetpack/modules/sharedaddy/sharing.js b/plugins/jetpack/modules/sharedaddy/sharing.js
new file mode 100644
index 00000000..90b19e8e
--- /dev/null
+++ b/plugins/jetpack/modules/sharedaddy/sharing.js
@@ -0,0 +1,234 @@
+(function($){
+ $.fn.extend( {
+ share_is_email: function( value ) {
+ return /^((([a-z]|\d|[!#\$%&'\*\+\-\/=\?\^_`{\|}~]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+(\.([a-z]|\d|[!#\$%&'\*\+\-\/=\?\^_`{\|}~]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+)*)|((\x22)((((\x20|\x09)*(\x0d\x0a))?(\x20|\x09)+)?(([\x01-\x08\x0b\x0c\x0e-\x1f\x7f]|\x21|[\x23-\x5b]|[\x5d-\x7e]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(\\([\x01-\x09\x0b\x0c\x0d-\x7f]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]))))*(((\x20|\x09)*(\x0d\x0a))?(\x20|\x09)+)?(\x22)))@((([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.)+(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.?$/i.test( this.val() );
+ }
+ } );
+
+ $( document ).ready(function() {
+ $( '.sharing a.sharing-anchor' ).click( function() {
+ return false;
+ } );
+
+ $( '.sharing a' ).each( function() {
+ if ( $( this ).attr( 'href' ) && $( this ).attr( 'href' ).indexOf( 'share=' ) != -1 )
+ $( this ).attr( 'href', $( this ).attr( 'href' ) + '&nb=1' );
+ } );
+
+ // Show hidden buttons
+ $( '.sharing a.sharing-anchor' ).hover( function() {
+ if ( $( this ).data( 'hasappeared' ) !== true ) {
+ var item = $( this ).parents( 'div:first' ).find( '.inner' );
+ var original = $( this );
+
+ // Create a timer to make the area appear if the mouse hovers for a period
+ var timer = setTimeout( function() {
+ $( '#sharing_email' ).slideUp( 200 );
+
+ $( item ).css( {
+ left: $( original ).position().left + 'px',
+ top: $( original ).position().top + $( original ).height() + 3 + 'px'
+ } ).slideDown( 200, function() {
+ // Mark the item as have being appeared by the hover
+ $( original ).data( 'hasappeared', true ).data( 'hasoriginal', true ).data( 'hasitem', false );
+
+ // Remove all special handlers
+ $( item ).mouseleave( handler_item_leave ).mouseenter( handler_item_enter );
+ $( original ).mouseleave( handler_original_leave ).mouseenter( handler_original_enter );
+
+ // Add a special handler to quickly close the item
+ $( original ).click( close_it );
+ } );
+
+ // The following handlers take care of the mouseenter/mouseleave for the share button and the share area - if both are left then we close the share area
+ var handler_item_leave = function() {
+ $( original ).data( 'hasitem', false );
+
+ if ( $( original ).data( 'hasoriginal' ) === false ) {
+ var timer = setTimeout( close_it, 800 );
+ $( original ).data( 'timer2', timer );
+ }
+ };
+
+ var handler_item_enter = function() {
+ $( original ).data( 'hasitem', true );
+ clearTimeout( $( original ).data( 'timer2' ) );
+ }
+
+ var handler_original_leave = function() {
+ $( original ).data( 'hasoriginal', false );
+
+ if ( $( original ).data( 'hasitem' ) === false ) {
+ var timer = setTimeout( close_it, 800 );
+ $( original ).data( 'timer2', timer );
+ }
+ };
+
+ var handler_original_enter = function() {
+ $( original ).data( 'hasoriginal', true );
+ clearTimeout( $( original ).data( 'timer2' ) );
+ };
+
+ var close_it = function() {
+ item.slideUp( 200 );
+
+ // Clear all hooks
+ $( original ).unbind( 'mouseleave', handler_original_leave ).unbind( 'mouseenter', handler_original_enter );
+ $( item ).unbind( 'mouseleave', handler_item_leave ).unbind( 'mouseenter', handler_item_leave );
+ $( original ).data( 'hasappeared', false );
+ $( original ).unbind( 'click', close_it );
+ return false;
+ };
+ }, 200 );
+
+ // Remember the timer so we can detect it on the mouseout
+ $( this ).data( 'timer', timer );
+ }
+ }, function() {
+ // Mouse out - remove any timer
+ clearTimeout( $( this ).data( 'timer' ) );
+ $( this ).data( 'timer', false );
+ } );
+
+ // Add click functionality
+ $( '.sharing ul' ).each( function( item ) {
+ printUrl = function ( uniqueId, urlToPrint ) {
+ $( 'body:first' ).append( '<iframe style="position:fixed;top:100;left:100;height:1px;width:1px;border:none;" id="printFrame-' + uniqueId + '" name="printFrame-' + uniqueId + '" src="' + urlToPrint + '" onload="frames[\'printFrame-' + uniqueId + '\'].focus();frames[\'printFrame-' + uniqueId + '\'].print();"></iframe>' )
+ };
+
+ // Print button
+ $( this ).find( '.share-print a' ).click( function() {
+ ref = $( this ).attr( 'href' );
+
+ var do_print = function() {
+ if ( ref.indexOf( '#print' ) == -1 ) {
+ uid = new Date().getTime();
+ printUrl( uid , ref );
+ }
+ else
+ print();
+ }
+
+ // Is the button in a dropdown?
+ if ( $( this ).parents( '.sharing-hidden' ).length > 0 ) {
+ $( this ).parents( '.inner' ).slideUp( 0, function() {
+ do_print();
+ } );
+ }
+ else
+ do_print();
+
+ return false;
+ } );
+
+ // Press This button
+ $( this ).find( '.share-press-this a' ).click( function() {
+ var s = '';
+
+ if ( window.getSelection )
+ s = window.getSelection();
+ else if( document.getSelection )
+ s = document.getSelection();
+ else if( document.selection )
+ s = document.selection.createRange().text;
+
+ if ( s )
+ $( this ).attr( 'href', $( this ).attr( 'href' ) + '&sel=' + encodeURI( s ) );
+
+ if ( !window.open( $( this ).attr( 'href' ), 't', 'toolbar=0,resizable=1,scrollbars=1,status=1,width=720,height=570' ) )
+ document.location.href = $( this ).attr( 'href' );
+
+ return false;
+ } );
+
+ // Email button
+ $( this ).find( '.share-email a' ).click( function() {
+ var url = $( this ).attr( 'href' );
+
+ if ( $( '#sharing_email' ).is( ':visible' ) )
+ $( '#sharing_email' ).slideUp( 200 );
+ else {
+ $( '.sharing .inner' ).slideUp();
+
+ $( '#sharing_email .response' ).remove();
+ $( '#sharing_email form' ).show();
+ $( '#sharing_email form input[type=submit]' ).removeAttr( 'disabled' );
+ $( '#sharing_email form a.sharing_cancel' ).show();
+
+ // Show dialog
+ $( '#sharing_email' ).css( {
+ left: $( this ).offset().left + 'px',
+ top: $( this ).offset().top + $( this ).height() + 'px'
+ } ).slideDown( 200 );
+
+ // Hook up other buttons
+ $( '#sharing_email a.sharing_cancel' ).unbind( 'click' ).click( function() {
+ $( '#sharing_email .errors' ).hide();
+ $( '#sharing_email' ).slideUp( 200 );
+ $( '#sharing_background' ).fadeOut();
+ return false;
+ } );
+
+ // Submit validation
+ $( '#sharing_email input[type=submit]' ).unbind( 'click' ).click( function() {
+ var form = $( this ).parents( 'form' );
+
+ // Disable buttons + enable loading icon
+ $( this ).attr( 'disabled', 'disabled' );
+ form.find( 'a.sharing_cancel' ).hide();
+ form.find( 'img.loading' ).show();
+
+ $( '#sharing_email .errors' ).hide();
+ $( '#sharing_email .error' ).removeClass( 'error' );
+
+ if ( $( '#sharing_email input[name=source_email]' ).share_is_email() == false )
+ $( '#sharing_email input[name=source_email]' ).addClass( 'error' );
+
+ if ( $( '#sharing_email input[name=target_email]' ).share_is_email() == false )
+ $( '#sharing_email input[name=target_email]' ).addClass( 'error' );
+
+ if ( $( '#sharing_email .error' ).length == 0 ) {
+ // AJAX send the form
+ $.ajax( {
+ url: url,
+ type: 'POST',
+ data: form.serialize(),
+ success: function( response ) {
+ form.find( 'img.loading' ).hide();
+
+ if ( response == '1' || response == '2' || response == '3' ) {
+ $( '#sharing_email .errors-' + response ).show();
+ form.find( 'input[type=submit]' ).removeAttr( 'disabled' );
+ form.find( 'a.sharing_cancel' ).show();
+ }
+ else {
+ $( '#sharing_email form' ).hide();
+ $( '#sharing_email' ).append( response );
+ $( '#sharing_email a.sharing_cancel' ).click( function() {
+ $( '#sharing_email' ).slideUp( 200 );
+ $( '#sharing_background' ).fadeOut();
+ return false;
+ } );
+ }
+ }
+ } );
+
+ return false;
+ }
+
+ form.find( 'img.loading' ).hide();
+ form.find( 'input[type=submit]' ).removeAttr( 'disabled' );
+ form.find( 'a.sharing_cancel' ).show();
+ $( '#sharing_email .errors-1' ).show();
+
+ return false;
+ } );
+ }
+
+ return false;
+ } );
+ } );
+
+ $( 'li.share-email, li.share-custom a.sharing-anchor' ).addClass( 'share-service-visible' );
+ } );
+})( jQuery );
diff --git a/plugins/jetpack/modules/sharedaddy/sharing.php b/plugins/jetpack/modules/sharedaddy/sharing.php
new file mode 100644
index 00000000..26422edc
--- /dev/null
+++ b/plugins/jetpack/modules/sharedaddy/sharing.php
@@ -0,0 +1,417 @@
+<?php
+
+class Sharing_Admin {
+ public function __construct() {
+ if ( !defined( 'WP_SHARING_PLUGIN_URL' ) ) {
+ define( 'WP_SHARING_PLUGIN_URL', plugin_dir_url( __FILE__ ) );
+ define( 'WP_SHARING_PLUGIN_DIR', plugin_dir_path( __FILE__ ) );
+ }
+
+ require_once WP_SHARING_PLUGIN_DIR.'sharing-service.php';
+
+ add_action( 'admin_init', array( $this, 'admin_init' ) );
+ add_action( 'admin_menu', array( $this, 'subscription_menu' ) );
+
+ // Catch AJAX
+ add_action( 'wp_ajax_sharing_save_services', array( $this, 'ajax_save_services' ) );
+ add_action( 'wp_ajax_sharing_save_options', array( $this, 'ajax_save_options' ) );
+ add_action( 'wp_ajax_sharing_new_service', array( $this, 'ajax_new_service' ) );
+ add_action( 'wp_ajax_sharing_delete_service', array( $this, 'ajax_delete_service' ) );
+ }
+
+ public function sharing_head() {
+ wp_enqueue_script( 'sharing-js', WP_SHARING_PLUGIN_URL.'admin-sharing.js', array( 'jquery-ui-draggable', 'jquery-ui-droppable', 'jquery-ui-sortable', 'jquery-form' ), 1 );
+ wp_enqueue_style( 'sharing', WP_SHARING_PLUGIN_URL.'admin-sharing.css', false, WP_SHARING_PLUGIN_VERSION );
+
+ add_thickbox();
+ }
+
+ public function admin_init() {
+ if ( isset( $_GET['page'] ) && ( $_GET['page'] == 'sharing.php' || $_GET['page'] == 'sharing' ) )
+ $this->process_requests();
+ }
+
+ public function process_requests() {
+ if ( isset( $_POST['_wpnonce'] ) && wp_verify_nonce( $_POST['_wpnonce'], 'sharing-options' ) ) {
+ $sharer = new Sharing_Service();
+ $sharer->set_global_options( $_POST );
+ do_action( 'sharing_admin_update' );
+
+ wp_safe_redirect( admin_url( 'options-general.php?page=sharing&update=saved' ) );
+ die();
+ }
+ }
+
+ public function subscription_menu( $user ) {
+ $hook = add_submenu_page( 'options-general.php', __( 'Sharing Settings', 'jetpack' ), __( 'Sharing', 'jetpack' ), 'manage_options', 'sharing', array( $this, 'management_page' ) );
+
+ // Insert our CSS and JS
+ add_action( "load-$hook", array( $this, 'sharing_head' ) );
+ }
+
+ public function ajax_save_services() {
+ if ( isset( $_POST['_wpnonce'] ) && wp_verify_nonce( $_POST['_wpnonce'], 'sharing-options' ) && isset( $_POST['hidden'] ) && isset( $_POST['visible'] ) ) {
+ $sharer = new Sharing_Service();
+
+ $sharer->set_blog_services( explode( ',', $_POST['visible'] ), explode( ',', $_POST['hidden'] ) );
+ die();
+ }
+ }
+
+ public function ajax_new_service() {
+ if ( isset( $_POST['_wpnonce'] ) && isset( $_POST['sharing_name'] ) && isset( $_POST['sharing_url'] ) && isset( $_POST['sharing_icon'] ) && wp_verify_nonce( $_POST['_wpnonce'], 'sharing-new_service' ) ) {
+ $sharer = new Sharing_Service();
+ if ( $service = $sharer->new_service( $_POST['sharing_name'], $_POST['sharing_url'], $_POST['sharing_icon'] ) ) {
+ $this->output_service( $service->get_id(), $service );
+ echo '<!--->';
+ $service->button_style = 'icon-text';
+ $this->output_preview( $service );
+
+ die();
+ }
+ }
+
+ // Fail
+ die( '1' );
+ }
+
+ public function ajax_delete_service() {
+ if ( isset( $_POST['_wpnonce'] ) && isset( $_POST['service'] ) && wp_verify_nonce( $_POST['_wpnonce'], 'sharing-options_'.$_POST['service'] ) ) {
+ $sharer = new Sharing_Service();
+ $sharer->delete_service( $_POST['service'] );
+ }
+ }
+
+ public function ajax_save_options() {
+ if ( isset( $_POST['_wpnonce'] ) && isset( $_POST['service'] ) && wp_verify_nonce( $_POST['_wpnonce'], 'sharing-options_'.$_POST['service'] ) ) {
+ $sharer = new Sharing_Service();
+ $service = $sharer->get_service( $_POST['service'] );
+
+ if ( $service && $service instanceof Sharing_Advanced_Source ) {
+ $service->update_options( $_POST );
+
+ $sharer->set_service( $_POST['service'], $service );
+ }
+
+ $this->output_service( $service->get_id(), $service, true );
+ echo '<!--->';
+ $service->button_style = 'icon-text';
+ $this->output_preview( $service );
+ die();
+ }
+ }
+
+ public function output_preview( $service ) {
+ $klasses = array( 'advanced', 'preview-item');
+
+ if ( $service->button_style != 'text' || $service->has_custom_button_style() ) {
+ $klasses[] = 'preview-'.$service->get_class();
+
+ if ( $service->get_class() != $service->get_id() )
+ $klasses[] = 'preview-'.$service->get_id();
+ }
+
+ echo '<li class="'.implode( ' ', $klasses ).'">';
+ $service->display_preview();
+ echo '</li>';
+ }
+
+ public function output_service( $id, $service, $show_dropdown = false ) {
+?>
+ <li class="service advanced<?php if ( $show_dropdown ) echo ' options'; ?> share-<?php echo $service->get_class(); ?>" id="<?php echo $service->get_id(); ?>">
+ <span class="options-left"><?php echo esc_html( $service->get_name() ); ?></span><?php if ( $service->has_advanced_options() ) : ?><span class="options-toggle" style="background: url(<?php echo admin_url( '/images/menu-bits.gif' ); ?>) no-repeat 0px -110px;">&nbsp;</span>
+ <br style="clear:both;" />
+ <div class="advanced-form">
+ <form method="post" action="<?php echo admin_url( 'admin-ajax.php' ); ?>">
+ <?php $service->display_options(); ?>
+
+ <input type="hidden" name="action" value="sharing_save_options" />
+ <input type="hidden" name="service" value="<?php echo esc_attr( $id ); ?>" />
+
+ <input type="hidden" name="_wpnonce" value="<?php echo wp_create_nonce( 'sharing-options_'.$id );?>" />
+ </form>
+ </div>
+ <?php endif; ?>
+ </li>
+<?php
+ }
+
+ public function management_page() {
+ $sharer = new Sharing_Service();
+ $enabled = $sharer->get_blog_services();
+ $global = $sharer->get_global_options();
+
+ $shows = array_values( get_post_types( array( 'public' => true ) ) );
+ array_unshift( $shows, 'index' );
+
+ if ( false == function_exists( 'mb_stripos' ) ) {
+ echo '<div id="message" class="updated fade"><h3>' . __( 'Warning! Multibyte support missing!', 'jetpack' ) . '</h3>';
+ echo "<p>" . sprintf( __( 'This plugin will work without it, but multibyte support is used <a href="%s">if available</a>. You may see minor problems with Tweets and other sharing services.', 'jetpack' ), "http://www.php.net/manual/en/mbstring.installation.php" ) . '</p></div>';
+ }
+
+ if ( isset( $_GET['update'] ) && $_GET['update'] == 'saved' )
+ echo '<div class="updated"><p>'.__( 'Settings have been saved', 'jetpack' ).'</p></div>';
+?>
+
+ <div class="wrap">
+ <div class="icon32" id="icon-options-general"><br /></div>
+ <h2><?php _e( 'Sharing Settings', 'jetpack' ); ?></h2>
+
+ <div id="services-config">
+ <table id="available-services">
+ <tr>
+ <td class="description">
+ <h3><?php _e( 'Available Services', 'jetpack' ); ?></h3>
+ <p><?php _e( "Drag and drop the services you'd like to enable into the box below.", 'jetpack' ); ?></p>
+ <p><a href="#TB_inline?height=395&amp;width=600&amp;inlineId=new-service" title="<?php echo esc_attr( __( 'Add a new service', 'jetpack' ) ); ?>" class="thickbox"><?php _e( 'Add a new service', 'jetpack' ); ?></a></p>
+ </td>
+ <td class="services">
+ <ul class="services-available" style="height: 100px;">
+ <?php foreach ( $sharer->get_all_services_blog() AS $id => $service ) : ?>
+ <?php
+ if ( !isset( $enabled['all'][$id] ) )
+ $this->output_service( $id, $service );
+ ?>
+ <?php endforeach; ?>
+ </ul>
+ <br class="clearing" />
+ </td>
+ </tr>
+ </table>
+
+ <table id="enabled-services">
+ <tr>
+ <td class="description">
+ <h3>
+ <?php _e( 'Enabled Services', 'jetpack' ); ?>
+ <img src="<?php echo admin_url( 'images/loading.gif' ); ?>" width="16" height="16" alt="loading" style="vertical-align: middle; display: none" />
+ </h3>
+ <p><?php _e( 'Services dragged here will appear individually.', 'jetpack' ); ?></p>
+ </td>
+ <td class="services" id="share-drop-target">
+ <h2 id="drag-instructions" <?php if ( count( $enabled['visible'] ) > 0 ) echo ' style="display: none"'; ?>><?php _e( 'Drag and drop available services here', 'jetpack' ); ?></h2>
+
+ <ul class="services-enabled">
+ <?php foreach ( $enabled['visible'] AS $id => $service ) : ?>
+ <?php $this->output_service( $id, $service, true ); ?>
+ <?php endforeach; ?>
+
+ <li class="end-fix"></li>
+ </ul>
+ </td>
+ <td id="hidden-drop-target" class="services">
+ <p><?php _e( 'Services dragged here will be hidden behind a share button.', 'jetpack' ); ?></p>
+
+ <ul class="services-hidden">
+ <?php foreach ( $enabled['hidden'] AS $id => $service ) : ?>
+ <?php $this->output_service( $id, $service, true ); ?>
+ <?php endforeach; ?>
+ <li class="end-fix"></li>
+ </ul>
+ </td>
+ </tr>
+ </table>
+
+ <table id="live-preview">
+ <tr>
+ <td class="description">
+ <h3><?php _e( 'Live Preview', 'jetpack' ); ?></h3>
+ </td>
+ <td class="services">
+ <h2<?php if ( count( $enabled['all'] ) > 0 ) echo ' style="display: none"'; ?>><?php _e( 'Sharing is off. Please add services above to enable', 'jetpack' ); ?></h2>
+
+ <ul class="preview">
+ <?php if ( count( $enabled['all'] ) > 0 ) : ?>
+ <li class="sharing-label"><?php echo esc_html( $global['sharing_label'] ); ?></li>
+ <?php endif; ?>
+
+ <?php foreach ( $enabled['visible'] AS $id => $service ) : ?>
+ <?php $this->output_preview( $service ); ?>
+ <?php endforeach; ?>
+
+ <?php if ( count( $enabled['hidden'] ) > 0 ) : ?>
+ <li class="share-custom">
+ <a href="#" class="sharing-anchor"><?php _ex( 'Share', 'dropdown button', 'jetpack' ); ?></a>
+
+ <div class="sharing-hidden">
+ <div class="inner" style="display: none;">
+ <ul>
+ <?php
+ $count = 1;
+
+ foreach ( $enabled['hidden'] AS $id => $service ) {
+ $this->output_preview( $service );
+
+ if ( ( $count % 2 ) == 0 )
+ echo '<li class="share-end"></li>';
+
+ $count++;
+ }
+ ?>
+ <li class="share-end"></li>
+ </ul>
+ </div>
+ </div>
+ </li>
+ <?php endif; ?>
+ </ul>
+
+ <ul class="archive" style="display: none">
+ <li class="sharing-label"><?php echo esc_html( $global['sharing_label'] ); ?></li>
+
+ <?php foreach ( $sharer->get_all_services_blog() AS $id => $service ) : ?>
+ <?php
+ if ( isset( $enabled['visible'][$id] ) )
+ $service = $enabled['visible'][$id];
+ elseif ( isset( $enabled['hidden'][$id] ) )
+ $service = $enabled['hidden'][$id];
+
+ $service->button_style = 'icon-text'; // The archive needs the full text, which is removed in JS later
+ $this->output_preview( $service );
+ ?>
+ <?php endforeach; ?>
+
+ <li class="share-custom">
+ <a href="#" class="sharing-anchor"><?php _ex( 'Share', 'dropdown button', 'jetpack' ); ?></a>
+
+ <div class="sharing-hidden">
+ <div class="inner" style="display: none;">
+ <ul>
+ <li/>
+ </ul>
+ </div>
+ </div>
+ </li>
+ </ul>
+ <br class="clearing" />
+ </td>
+ </tr>
+ </table>
+
+ <form method="post" action="<?php echo admin_url( 'admin-ajax.php' ); ?>" id="save-enabled-shares">
+ <input type="hidden" name="action" value="sharing_save_services" />
+ <input type="hidden" name="visible" value="<?php echo implode( ',', array_keys( $enabled['visible'] ) ); ?>" />
+ <input type="hidden" name="hidden" value="<?php echo implode( ',', array_keys( $enabled['hidden'] ) ); ?>" />
+ <input type="hidden" name="_wpnonce" value="<?php echo wp_create_nonce( 'sharing-options' );?>" />
+ </form>
+ </div>
+
+ <form method="post" action="">
+ <table class="form-table">
+ <tbody>
+ <tr valign="top">
+ <th scope="row"><label><?php _e( 'Default button style', 'jetpack' ); ?></label></th>
+ <td>
+ <select name="button_style">
+ <option<?php if ( $global['button_style'] == 'icon-text' ) echo ' selected="selected"';?> value="icon-text"><?php _e( 'Icon + text', 'jetpack' ); ?></option>
+ <option<?php if ( $global['button_style'] == 'icon' ) echo ' selected="selected"';?> value="icon"><?php _e( 'Icon only', 'jetpack' ); ?></option>
+ <option<?php if ( $global['button_style'] == 'text' ) echo ' selected="selected"';?> value="text"><?php _e( 'Text only', 'jetpack' ); ?></option>
+ </select>
+ </td>
+ </tr>
+ <tr valign="top">
+ <th scope="row"><label><?php _e( 'Sharing label', 'jetpack' ); ?></label></th>
+ <td>
+ <input type="text" name="sharing_label" value="<?php echo esc_attr( $global['sharing_label'] ); ?>" />
+ </td>
+ </tr>
+ <tr valign="top">
+ <th scope="row"><label><?php _e( 'Open links in', 'jetpack' ); ?></label></th>
+ <td>
+ <select name="open_links">
+ <option<?php if ( $global['open_links'] == 'new' ) echo ' selected="selected"';?> value="new"><?php _e( 'New window', 'jetpack' ); ?></option>
+ <option<?php if ( $global['open_links'] == 'same' ) echo ' selected="selected"';?> value="same"><?php _e( 'Same window', 'jetpack' ); ?></option>
+ </select>
+ </td>
+ </tr>
+ <tr valign="top">
+ <th scope="row"><label><?php _e( 'Show sharing buttons on', 'jetpack' ); ?></label></th>
+ <td>
+ <?php
+ $br = false;
+ foreach ( $shows as $show ) :
+ if ( 'index' == $show ) {
+ $label = __( 'Front Page, Archive Pages, and Search Results', 'jetpack' );
+ } else {
+ $post_type_object = get_post_type_object( $show );
+ $label = $post_type_object->labels->name;
+ }
+ ?>
+ <?php if ( $br ) echo '<br />'; ?><label><input type="checkbox"<?php checked( in_array( $show, $global['show'] ) ); ?> name="show[]" value="<?php echo esc_attr( $show ); ?>" /> <?php echo esc_html( $label ); ?></label>
+ <?php $br = true; endforeach; ?>
+ </td>
+ </tr>
+
+ <?php do_action( 'sharing_global_options' ); ?>
+ </tbody>
+ </table>
+
+ <p class="submit">
+ <input type="submit" name="submit" class="button-primary" value="<?php _e( 'Save Changes', 'jetpack' ); ?>" />
+ </p>
+
+ <input type="hidden" name="_wpnonce" value="<?php echo wp_create_nonce( 'sharing-options' );?>" />
+ </form>
+
+ <div id="new-service" style="display: none">
+ <form method="post" action="<?php echo admin_url( 'admin-ajax.php' ); ?>" id="new-service-form">
+ <table class="form-table">
+ <tbody>
+ <tr valign="top">
+ <th scope="row" width="100"><label><?php _e( 'Service name', 'jetpack' ); ?></label></th>
+ <td>
+ <input type="text" name="sharing_name" size="40" />
+ </td>
+ </tr>
+ <tr valign="top">
+ <th scope="row" width="100"><label><?php _e( 'Sharing URL', 'jetpack' ); ?></label></th>
+ <td>
+ <input type="text" name="sharing_url" size="40" />
+
+ <p><?php _e( 'You can add the following variables to your service sharing URL:', 'jetpack' ); ?><br/>
+ <code>%post_title%</code>, <code>%post_url%</code>, <code>%post_full_url%</code>, <code>%post_excerpt%</code>, <code>%post_full_url%</code>, <code>%post_tags%</code></p>
+ </td>
+ </tr>
+ <tr valign="top">
+ <th scope="row" width="100"><label><?php _e( 'Icon URL', 'jetpack' ); ?></label></th>
+ <td>
+ <input type="text" name="sharing_icon" size="40" />
+ <p><?php _e( 'Enter the URL of a 16x16px icon you want to use for this service.', 'jetpack' ); ?></p>
+ </td>
+ </tr>
+ <tr valign="top" width="100">
+ <th scope="row"></th>
+ <td>
+ <input type="submit" class="button-secondary" value="<?php _e( 'Create Share', 'jetpack' ); ?>" />
+ <img src="<?php echo admin_url( 'images/loading.gif' ); ?>" width="16" height="16" alt="loading" style="vertical-align: middle; display: none" />
+ </td>
+ </tr>
+
+ <?php do_action( 'sharing_new_service_form' ); ?>
+ </tbody>
+ </table>
+
+ <div class="inerror" style="display: none; margin-top: 15px">
+ <p><?php _e( 'An error occurred creating your new sharing service - please check you gave valid details.', 'jetpack' ); ?></p>
+ </div>
+
+ <input type="hidden" name="action" value="sharing_new_service" />
+ <input type="hidden" name="_wpnonce" value="<?php echo wp_create_nonce( 'sharing-new_service' );?>" />
+ </form>
+ </div>
+ </div>
+
+ <script type="text/javascript">
+ var sharing_loading_icon = '<?php echo esc_js( admin_url( "/images/loading.gif" ) ); ?>';
+ </script>
+<?php
+ }
+}
+
+function sharing_admin_init() {
+ global $sharing_admin;
+
+ $sharing_admin = new Sharing_Admin();
+}
+
+add_action( 'init', 'sharing_admin_init' );
diff --git a/plugins/jetpack/modules/shortcodes.php b/plugins/jetpack/modules/shortcodes.php
new file mode 100644
index 00000000..614658c2
--- /dev/null
+++ b/plugins/jetpack/modules/shortcodes.php
@@ -0,0 +1,51 @@
+<?php
+
+/**
+ * Module Name: Shortcode Embeds
+ * Module Description: Easily embed videos and more from sites like YouTube, Vimeo, and SlideShare.
+ * First Introduced: 1.1
+ * Major Changes In: 1.2
+ */
+
+/**
+ * Transforms the $atts array into a string that the old functions expected
+ *
+ * The old way was:
+ * [shortcode a=1&b=2&c=3] or [shortcode=1]
+ * This is parsed as array( a => '1&b=2&c=3' ) and array( 0 => '=1' ), which is useless
+ *
+ * @param Array $params
+ * @param Bool $old_format_support true if [shortcode=foo] format is possible.
+ * @return String $params
+ */
+function shortcode_new_to_old_params( $params, $old_format_support = false ) {
+ $str = '';
+
+ if ( $old_format_support && isset( $params[0] ) ) {
+ $str = ltrim( $params[0], '=' );
+ } elseif ( is_array( $params ) ) {
+ foreach ( array_keys( $params ) as $key ) {
+ if ( ! is_numeric( $key ) )
+ $str = $key . '=' . $params[$key];
+ }
+ }
+
+ return str_replace( array( '&amp;', '&#038;' ), '&', $str );
+}
+
+function jetpack_load_shortcodes() {
+ if ( version_compare( PHP_VERSION, 5, '<' ) ) {
+ $php5_only = array( 'videopress.php' => true );
+ } else {
+ $php5_only = array();
+ }
+
+ foreach ( Jetpack::glob_php( dirname( __FILE__ ) . '/shortcodes' ) as $file ) {
+ if ( isset( $php5_only[basename( $file )] ) ) {
+ continue;
+ }
+ include $file;
+ }
+}
+
+jetpack_load_shortcodes();
diff --git a/plugins/jetpack/modules/shortcodes/archives.php b/plugins/jetpack/modules/shortcodes/archives.php
new file mode 100644
index 00000000..b4c78f3c
--- /dev/null
+++ b/plugins/jetpack/modules/shortcodes/archives.php
@@ -0,0 +1,57 @@
+<?php
+
+/*
+ * Archives shortcode
+ * @author bubel & nickmomrik
+ * [archives limit=10]
+ */
+
+add_shortcode( 'archives', 'archives_shortcode' );
+
+function archives_shortcode( $attr ) {
+ if ( is_feed() )
+ return '[archives]';
+
+ global $allowedposttags;
+
+ $default_atts = array(
+ 'type' => 'postbypost',
+ 'limit' => '',
+ 'format' => 'html',
+ 'showcount' => false,
+ 'before' => '',
+ 'after' => '',
+ 'order' => 'desc',
+ );
+ extract( shortcode_atts( $default_atts, $attr ) );
+
+ if ( !in_array( $type, array( 'yearly', 'monthly', 'daily', 'weekly', 'postbypost' ) ) )
+ $type = 'postbypost';
+
+ if ( !in_array( $format, array( 'html', 'option', 'custom' ) ) )
+ $format = 'html';
+
+ if ( '' != $limit )
+ $limit = (int)$limit;
+
+ $showcount = (bool)$showcount;
+ $before = wp_kses( $before, $allowedposttags );
+ $after = wp_kses( $after, $allowedposttags );
+
+ // Get the archives
+ $archives = wp_get_archives( 'type=' . $type . '&limit=' . $limit . '&format=' . $format . '&echo=0&show_post_count=' . $showcount . '&before=' . $before . '&after=' . $after );
+
+ if ( 'asc' == $order )
+ $archives = implode( "\n", array_reverse( explode( "\n", $archives ) ) );
+
+
+ // Check to see if there are any archives
+ if ( empty( $archives ) )
+ $archives = '<p>' . __( 'Your blog does not currently have any published posts.' , 'jetpack' ) . '</p>';
+ elseif ( 'option' == $format )
+ $archives = "<select name='archive-dropdown' onchange='document.location.href=this.options[this.selectedIndex].value;'><option value='" . get_permalink() . "'>--</option>" . $archives . "</select>";
+ elseif ( 'html' == $format )
+ $archives = '<ul>' . $archives . '</ul>';
+
+ return $archives;
+}
diff --git a/plugins/jetpack/modules/shortcodes/audio.php b/plugins/jetpack/modules/shortcodes/audio.php
new file mode 100644
index 00000000..ec50436a
--- /dev/null
+++ b/plugins/jetpack/modules/shortcodes/audio.php
@@ -0,0 +1,71 @@
+<?php
+
+/**
+ * Shortcode for audio
+ * [audio http://wpcom.files.wordpress.com/2007/01/mattmullenweg-interview.mp3|width=180|titles=1|artists=2]
+ *
+ * The important question here is whether the shortcode applies to widget_text:
+ * add_filter('widget_text', 'do_shortcode');
+ * */
+
+function audio_shortcode( $atts ) {
+ global $ap_playerID;
+
+ if ( ! isset( $atts[0] ) )
+ return '';
+
+ if ( count( $atts ) )
+ $atts[0] = strip_tags( join( ' ', $atts ) );
+
+ $src = ltrim( $atts[0], '=' );
+
+ $ap_options = apply_filters( 'audio_player_default_colors', array( "bg" => "0xf8f8f8", "leftbg" => "0xeeeeee", "lefticon" => "0x666666", "rightbg" => "0xcccccc", "rightbghover" => "0x999999", "righticon" => "0x666666", "righticonhover" => "0xffffff", "text" => "0x666666", "slider" => "0x666666", "track" => "0xFFFFFF", "border" => "0x666666", "loader" => "0x9FFFB8" ) );
+
+ if ( isset( $ap_playerID ) == false )
+ $ap_playerID = 1;
+ else
+ $ap_playerID++;
+
+ $src = trim( $src, ' "' );
+
+ if ( strpos( '|', $src ) )
+ $options = explode( '|', $src );
+ else
+ $options = array();
+
+ $data = preg_split( "/[\|]/", $src );
+ $flashvars = "playerID={$ap_playerID}";
+
+ for ( $i = 1; $i < count( $data ); $i++ ) {
+ $pair = explode( "=", $data[$i] );
+ if( strtolower( $pair[0] ) != 'autostart' )
+ $options[$pair[0]] = $pair[1];
+ }
+
+ // Merge runtime options to default colour options (runtime options overwrite default options)
+ $options = array_merge( $ap_options, $options );
+ $options['soundFile'] = $data[0];
+ $flash_vars = '';
+ foreach ( $options as $key => $value ) {
+ $flash_vars .= '&amp;' . rawurlencode( $key ) . '=' . rawurlencode( $value );
+ }
+ $flash_vars = esc_attr( $flash_vars );
+
+ if ( isset( $options['bgcolor'] ) )
+ $bgcolor = esc_attr( $options['bgcolor'] );
+ else
+ $bgcolor = '#FFFFFF';
+
+ if ( isset( $options['width'] ) )
+ $width = intval( $options['width'] );
+ else
+ $width = 290;
+
+ $swfurl = apply_filters( 'jetpack_static_url', 'http://en.wordpress.com/wp-content/plugins/audio-player/player.swf' );
+
+ $obj = "<p><object type='application/x-shockwave-flash' data='$swfurl' width='$width' height='24' id='audioplayer1'><param name='movie' value='$swfurl' /><param name='FlashVars' value='{$flash_vars}' /><param name='quality' value='high' /><param name='menu' value='false' /><param name='bgcolor' value='$bgcolor' /><param name='wmode' value='opaque' /></object></p>";
+
+ return "<span style='text-align:left;display:block;'>$obj</span>";
+}
+
+add_shortcode( 'audio', 'audio_shortcode' );
diff --git a/plugins/jetpack/modules/shortcodes/blip.php b/plugins/jetpack/modules/shortcodes/blip.php
new file mode 100644
index 00000000..e757dc4b
--- /dev/null
+++ b/plugins/jetpack/modules/shortcodes/blip.php
@@ -0,0 +1,55 @@
+<?php
+
+/**
+ * Blip.tv embed code:
+ * <embed src="http://blip.tv/play/g8sVgpfaCgI%2Em4v" type="application/x-shockwave-flash" width="480" height="255" allowscriptaccess="always" allowfullscreen="true"></embed>
+ * Blip.tv shortcode is: [blip.tv url-or-something-else]
+ * */
+
+function blip_embed_to_shortcode( $content ) {
+ if ( false === stripos( $content, '/blip.tv/play/' ) )
+ return $content;
+
+ $regexp = '!<embed((?:\s+\w+="[^"]*")*)\s+src="http(?:\:|&#0*58;)//(blip\.tv/play/[^"]*)"((?:\s+\w+="[^"]*")*)\s*(?:/>|>\s*</embed>)!';
+ $regexp_ent = str_replace( '&amp;#0*58;', '&amp;#0*58;|&#0*58;', htmlspecialchars( $regexp, ENT_NOQUOTES ) );
+
+ foreach ( array( 'regexp', 'regexp_ent' ) as $reg ) {
+ if ( !preg_match_all( $$reg, $content, $matches, PREG_SET_ORDER ) )
+ continue;
+
+ foreach ( $matches as $match ) {
+ $src = 'http://' . html_entity_decode( $match[2] );
+ $params = $match[1] . $match[3];
+ if ( 'regexp_ent' == $reg ) {
+ $src = html_entity_decode( $src );
+ $params = html_entity_decode( $params );
+ }
+ $params = wp_kses_hair( $params, array( 'http' ) );
+ if ( ! isset( $params['type'] ) || 'application/x-shockwave-flash' != $params['type']['value'] )
+ continue;
+
+ $content = str_replace( $match[0], "[blip.tv $src]", $content );
+ }
+ }
+ return $content;
+}
+add_filter( 'pre_kses', 'blip_embed_to_shortcode' );
+
+// [blip.tv ?posts_id=4060324&dest=-1]
+// [blip.tv http://blip.tv/play/hpZTgffqCAI%2Em4v] // WLS
+
+function blip_shortcode( $atts ) {
+ if ( ! isset( $atts[0] ) )
+ return '';
+ $src = $atts[0];
+
+ if ( preg_match( '/^\?posts_id=(\d+)&[^d]*dest=(-?\d+)$/', $src, $matches ) )
+ return "<script type='text/javascript' src='http://blip.tv/syndication/write_player?skin=js&posts_id={$matches[1]}&cross_post_destination={$matches[2]}&view=full_js'></script>";
+ elseif ( preg_match( '|^http://blip.tv/play/[.\w]+$|', urldecode( $src ) ) ) // WLS
+ return "<embed src='$src' type='application/x-shockwave-flash' width='480' height='300' allowscriptaccess='never' allowfullscreen='true'></embed>";
+
+
+ return "<!--blip.tv pattern not matched -->";
+}
+
+add_shortcode( 'blip.tv', 'blip_shortcode' );
diff --git a/plugins/jetpack/modules/shortcodes/dailymotion.php b/plugins/jetpack/modules/shortcodes/dailymotion.php
new file mode 100644
index 00000000..a6daed9d
--- /dev/null
+++ b/plugins/jetpack/modules/shortcodes/dailymotion.php
@@ -0,0 +1,116 @@
+<?php
+
+/**
+ * Dailymotion code
+ * */
+
+/**
+ * Original codes:
+ *
+ * <embed height="270" type="application/x-shockwave-flash" width="480" src="http&#58;//www.dailymotion.com/swf/video/xekmrq?additionalInfos=0" wmode="opaque" pluginspage="http&#58;//www.macromedia.com/go/getflashplayer" allowscriptaccess="never" allownetworking="internal" />
+ *
+ * <object width="480" height="240"><param name="movie" value="http://www.dailymotion.com/swf/video/xen4ms_ghinzu-cold-love-mirror-mirror_music?additionalInfos=0"></param><param name="allowFullScreen" value="true"></param><param name="allowScriptAccess" value="always"></param>
+ * <embed type="application/x-shockwave-flash" src="http://www.dailymotion.com/swf/video/xen4ms_ghinzu-cold-love-mirror-mirror_music?additionalInfos=0" width="480" height="240" allowfullscreen="true" allowscriptaccess="always"></embed>
+ * </object><br /><b><a href="http://www.dailymotion.com/video/xen4ms_ghinzu-cold-love-mirror-mirror_music">Ghinzu - Cold Love (Mirror Mirror)</a></b><br /><i>Uploaded by <a href="http://www.dailymotion.com/GhinzuTV">GhinzuTV</a>. - <a href="http://www.dailymotion.com/us/channel/music">Watch more music videos, in HD!</a></i>
+ *
+ * Code as of 01.01.11:
+ * <object width="560" height="421"><param name="movie" value="http://www.dailymotion.com/swf/video/xaose5?width=560&theme=denim&foreground=%2392ADE0&highlight=%23A2ACBF&background=%23202226&start=&animatedTitle=&iframe=0&additionalInfos=0&autoPlay=0&hideInfos=0"></param><param name="allowFullScreen" value="true"></param><param name="allowScriptAccess" value="always"></param><embed type="application/x-shockwave-flash" src="http://www.dailymotion.com/swf/video/xaose5?width=560&theme=denim&foreground=%2392ADE0&highlight=%23A2ACBF&background=%23202226&start=&animatedTitle=&iframe=0&additionalInfos=0&autoPlay=0&hideInfos=0" width="560" height="421" allowfullscreen="true" allowscriptaccess="always"></embed></object><br /><b><a href="http://www.dailymotion.com/video/xaose5_sexy-surprise_na">Sexy Surprise</a></b><br /><i>Uploaded by <a href="http://www.dailymotion.com/GilLavie">GilLavie</a>. - <a target="_self" href="http://www.dailymotion.com/channel/sexy/featured/1">Find more steamy, sexy videos.</a></i>
+ * movie param enforces anti-xss protection
+ */
+
+function dailymotion_embed_to_shortcode( $content ) {
+ if ( false === stripos( $content, 'www.dailymotion.com/swf/' ) )
+ return $content;
+
+ $regexp = '!<object.*>\s*(<param.*></param>\s*)*<embed((?:\s+\w+="[^"]*")*)\s+src="http(?:\:|&#0*58;)//(www\.dailymotion\.com/swf/[^"]*)"((?:\s+\w+="[^"]*")*)\s*(?:/>|>\s*</embed>)\s*</object><br /><b><a .*>.*</a></b><br /><i>.*</i>!';
+ $regexp_ent = str_replace( '&amp;#0*58;', '&amp;#0*58;|&#0*58;', htmlspecialchars( $regexp, ENT_NOQUOTES ) );
+
+ foreach ( array( 'regexp', 'regexp_ent' ) as $reg ) {
+ if ( ! preg_match_all( $$reg, $content, $matches, PREG_SET_ORDER ) )
+ continue;
+
+ foreach ( $matches as $match ) {
+ $src = html_entity_decode( $match[3] );
+ $params = $match[2] . $match[4];
+ if ( 'regexp_ent' == $reg ) {
+ $src = html_entity_decode( $src );
+ $params = html_entity_decode( $params );
+ }
+ $params = wp_kses_hair( $params, array( 'http' ) );
+ if ( !isset( $params['type'] ) || 'application/x-shockwave-flash' != $params['type']['value'] )
+ continue;
+
+ $id = basename( substr( $src, strlen( 'www.dailymotion.com/swf' ) ) );
+ $id = preg_replace( '/[^a-z0-9].*$/i', '', $id );
+
+ $content = str_replace( $match[0], "[dailymotion id=$id]", $content );
+ do_action( 'jetpack_embed_to_shortcode', 'dailymotion', $id );
+ }
+ }
+ return $content;
+}
+add_filter( 'pre_kses', 'dailymotion_embed_to_shortcode' );
+
+/**
+ * DailyMotion shortcode
+ *
+ * The documented shortcode is:
+ * [dailymotion id=x8oma9]
+ *
+ * Possibilities, according to the old parsing regexp:
+ * [dailymotion x8oma9]
+ * [dailymotion=x8oma9]
+ *
+ * Hypothetical option, according to the old shortcode function is
+ * [dailymotion id=1&title=2&user=3&video=4]
+ *
+ * The new style is now:
+ * [dailymotion id=x8oma9 title=2 user=3 video=4]
+ *
+ * @param array $atts
+ * @return string html
+ *
+ */
+
+function dailymotion_shortcode( $atts ) {
+ global $content_width;
+
+ if ( isset( $atts[0] ) ) {
+ $id = ltrim( $atts[0], '=' );
+ $atts['id'] = $id;
+ } else {
+ $params = shortcode_new_to_old_params( $atts );
+ parse_str( $params, $atts );
+ }
+
+ if ( isset( $atts['id'] ) )
+ $id = $atts['id'];
+ else
+ return '<!--Dailymotion error: bad or missing ID-->';
+
+ if ( !empty( $content_width ) )
+ $width = min( 425, intval( $content_width ) );
+ else
+ $width = 425;
+
+ $height = ( 425 == $width ) ? 334 : ( $width / 425 ) * 334;
+ $id = urlencode( $id );
+
+ $output = '<object width="' . $width . '" height="' . $height . '"><param name="movie" value="http://www.dailymotion.com/swf/';
+ $after = '';
+ if ( preg_match( '/^[a-z0-9]+$/', $id ) ) {
+ $output .= $id;
+
+ if ( array_key_exists( 'video', $atts ) && $video = preg_replace( '/[^-a-z0-9_]/i', '', $atts['video'] ) && array_key_exists( 'title', $atts ) && $title = wp_kses( $atts['title'], array() ) )
+ $after .= '<br /><strong><a href="http://www.dailymotion.com/video/' . $video . '">' . $title . '</a></strong>';
+
+ if ( array_key_exists( 'user', $atts ) && $user = preg_replace( '/[^-a-z0-9_]/i', '', $atts['user'] ) )
+ $after .= '<br /><em>Uploaded by <a href="http://www.dailymotion.com/' . $user . '">' . $user . '</a></em>';
+ }
+
+ $output .= '"></param><param name="allowfullscreen" value="true"></param><param name="wmode" value="opaque"></param><embed src="http://www.dailymotion.com/swf/' . $id . '" width="' . $width . '" height="' . $height . '" allowfullscreen="true" wmode="opaque"></embed></object>';
+
+ return $output . $after;
+}
+
+add_shortcode( 'dailymotion', 'dailymotion_shortcode' );
diff --git a/plugins/jetpack/modules/shortcodes/diggthis.php b/plugins/jetpack/modules/shortcodes/diggthis.php
new file mode 100644
index 00000000..ee1195fa
--- /dev/null
+++ b/plugins/jetpack/modules/shortcodes/diggthis.php
@@ -0,0 +1,40 @@
+<?php
+
+/**
+ * Digg changed their button API.
+ *
+ * The old style button was something like this:
+ * [digg=http://digg.com/some-digg-permalink] - uses digg permalink as id.
+ *
+ * The new style is:
+ * [digg class="wide"] # The class options are: 'wide', 'medium', 'compact', 'icon'
+ * and uses get_permalink() as id.
+ *
+ * @author Veselin Nikolov
+ */
+
+function digg_shortcode_js() {
+ echo '
+<script type="text/javascript">
+(function(){var s=document.createElement("SCRIPT"),s1=document.getElementsByTagName("SCRIPT")[0];s.type="text/javascript";s.async=true;s.src="http://widgets.digg.com/buttons.js";s1.parentNode.insertBefore(s,s1);})();
+</script>
+';
+}
+
+function digg_shortcode( $atts ) {
+ static $printed_digg_code = false;
+
+ if ( ! $printed_digg_code ) {
+ add_action( 'wp_footer', 'digg_shortcode_js' );
+ $printed_digg_code = true;
+ }
+
+ if ( isset( $atts['class']) && in_array( $atts['class'], array( 'wide', 'medium', 'compact', 'icon' ) ) )
+ $class = ucfirst( $atts['class'] );
+ else
+ $class = 'Medium';
+
+ return '<a class="DiggThisButton Digg' . $class . '" href="http://digg.com/submit?url=' . urlencode( get_permalink() ) . '&amp;title=' . urlencode( get_the_title() ) . '"></a>';
+}
+
+add_shortcode( 'digg', 'digg_shortcode' );
diff --git a/plugins/jetpack/modules/shortcodes/flickr.php b/plugins/jetpack/modules/shortcodes/flickr.php
new file mode 100644
index 00000000..6a85f4cb
--- /dev/null
+++ b/plugins/jetpack/modules/shortcodes/flickr.php
@@ -0,0 +1,137 @@
+<?php
+
+/*
+ Flickr Short Code
+ Author: kellan
+ License: BSD/GPL/public domain (take your pick)
+
+[flickr video=http://flickr.com/photos/revdancatt/2345938910/]
+[flickr video=2345938910]
+[flickr video=2345938910 show_info=true w=400 h=300]
+[flickr video=2345938910 show_info=true w=400 h=300 secret=846d9c1be9]
+
+*/
+
+/*
+ * <object type="application/x-shockwave-flash" width="400" height="300" data="http://www.flickr.com/apps/video/stewart.swf?v=71377" classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000"> <param name="flashvars" value="intl_lang=en-us&photo_secret=846d9c1be9&photo_id=2345938910"></param> <param name="movie" value="http://www.flickr.com/apps/video/stewart.swf?v=71377"></param> <param name="bgcolor" value="#000000"></param> <param name="allowFullScreen" value="true"></param><embed type="application/x-shockwave-flash" src="http://www.flickr.com/apps/video/stewart.swf?v=71377" bgcolor="#000000" allowfullscreen="true" flashvars="intl_lang=en-us&photo_secret=846d9c1be9&photo_id=2345938910" height="300" width="400"></embed></object>
+ */
+
+function flickr_embed_to_shortcode( $content ) {
+ if ( false === stripos( $content, '/www.flickr.com/apps/video/stewart.swf' ) )
+ return $content;
+
+ $regexp = '%(<object.*?(?:<(?!/?(?:object|embed)\s+).*?)*?)?<embed((?:\s+\w+="[^"]*")*)\s+src="http(?:\:|&#0*58;)//www.flickr.com/apps/video/stewart.swf[^"]*"((?:\s+\w+="[^"]*")*)\s*(?:/>|>\s*</embed>)(?(1)\s*</object>)%';
+ $regexp_ent = str_replace(
+ array(
+ '&amp;#0*58;',
+ '[^&gt;]*',
+ '[^&lt;]*',
+ ),
+ array(
+ '&amp;#0*58;|&#0*58;',
+ '[^&]*(?:&(?!gt;)[^&]*)*',
+ '[^&]*(?:&(?!lt;)[^&]*)*',
+ ),
+ htmlspecialchars( $regexp, ENT_NOQUOTES )
+ );
+
+ foreach ( array( 'regexp', 'regexp_ent' ) as $reg ) {
+ if ( !preg_match_all( $$reg, $content, $matches, PREG_SET_ORDER ) )
+ continue;
+ foreach ( $matches as $match ) {
+ $params = $match[2] . $match[3];
+
+ if ( 'regexp_ent' == $reg )
+ $params = html_entity_decode( $params );
+
+ $params = wp_kses_hair( $params, array( 'http' ) );
+ if ( ! isset( $params['type'] ) || 'application/x-shockwave-flash' != $params['type']['value'] || ! isset( $params['flashvars'] ) )
+ continue;
+
+ wp_parse_str( html_entity_decode( $params['flashvars']['value'] ), $flashvars );
+
+ if ( ! isset( $flashvars['photo_id'] ) )
+ continue;
+
+ $code_atts = array( 'video' => $flashvars['photo_id'], );
+
+ if ( isset( $flashvars['flickr_show_info_box'] ) && 'true' == $flashvars['flickr_show_info_box'] )
+ $code_atts['show_info'] = 'true';
+
+ if ( ! empty( $flashvars['photo_secret'] ) )
+ $code_atts['secret'] = $flashvars['photo_secret'] ;
+
+ if ( ! empty( $params['width']['value'] ) )
+ $code_atts['w'] = (int) $params['width']['value'];
+
+ if ( ! empty( $params['height']['value'] ) )
+ $code_atts['h'] = (int) $params['height']['value'];
+
+ $code = '[flickr';
+ foreach ( $code_atts as $k => $v )
+ $code .= " $k=$v";
+ $code .= ']';
+
+ $content = str_replace( $match[0], $code, $content );
+ do_action( 'jetpack_embed_to_shortcode', 'flickr_video', $flashvars['photo_id'] );
+ }
+ }
+
+ return $content;
+}
+add_filter( 'pre_kses', 'flickr_embed_to_shortcode' );
+
+function flickr_shortcode_handler( $atts ) {
+ $atts = shortcode_atts( array(
+ 'video' => 0,
+ 'photo' => 0,
+ 'show_info' => 0,
+ 'w' => 400,
+ 'h' => 300,
+ 'secret' => 0,
+ 'size' => 0,
+ ), $atts );
+
+ if ( isset( $atts['video'] ) ) {
+ $showing = 'video';
+ $src = $atts['video'];
+ } elseif ( isset( $atts['photo'] ) ) {
+ $showing = 'photo';
+ $src = $atts['photo'];
+ } else {
+ return '';
+ }
+
+ if ( preg_match( "!photos/(([0-9a-zA-Z-_]+)|([0-9]+@N[0-9]+))/([0-9]+)/?$!", $src, $m ) )
+ $atts['photo_id'] = $m[4];
+ else
+ $atts['photo_id'] = $atts['video'];
+
+ if ( $showing == 'video' ) {
+ if ( ! isset( $atts['show_info'] ) || in_array( $atts['show_info'], array('yes', 'true') ) )
+ $atts['show_info'] = 'true';
+ elseif ( in_array( $atts['show_info'], array( 'false', 'no' ) ) )
+ $atts['show_info'] = 'false';
+
+ if ( isset( $atts['secret'] ) )
+ $atts['secret'] = preg_replace( '![^\w]+!i', '', $atts['secret'] );
+
+ return flickr_shortcode_video_markup( $atts );
+ } else {
+ return '';
+ }
+}
+
+function flickr_shortcode_video_markup( $atts ) {
+ $atts = array_map( 'esc_attr', $atts );
+
+ $photo_vars = "photo_id=$atts[photo_id]";
+ if ( isset( $atts['secret'] ) )
+ $photo_vars .= "&amp;photo_secret=$atts[secret]";
+
+ return <<<EOD
+<object type="application/x-shockwave-flash" width="$atts[w]" height="$atts[h]" data="http://www.flickr.com/apps/video/stewart.swf?v=1.161" classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000"> <param name="flashvars" value="$photo_vars&amp;flickr_show_info_box=$atts[show_info]"></param><param name="movie" value="http://www.flickr.com/apps/video/stewart.swf?v=1.161"></param><param name="bgcolor" value="#000000"></param><param name="allowFullScreen" value="true"></param><param name="wmode" value="opaque"></param><embed type="application/x-shockwave-flash" src="http://www.flickr.com/apps/video/stewart.swf?v=1.161" bgcolor="#000000" allowfullscreen="true" flashvars="$photo_vars&amp;flickr_show_info_box=$atts[show_info]" wmode="opaque" height="$atts[h]" width="$atts[w]"></embed></object>
+EOD;
+}
+
+add_shortcode( 'flickr', 'flickr_shortcode_handler' );
diff --git a/plugins/jetpack/modules/shortcodes/googlemaps.php b/plugins/jetpack/modules/shortcodes/googlemaps.php
new file mode 100644
index 00000000..deadac38
--- /dev/null
+++ b/plugins/jetpack/modules/shortcodes/googlemaps.php
@@ -0,0 +1,69 @@
+<?php
+
+/*
+ * Google maps iframe - transforms code that looks like that:
+ * <iframe width="425" height="350" frameborder="0" scrolling="no" marginheight="0" marginwidth="0" src="http://maps.google.com/maps?f=q&amp;source=s_q&amp;hl=bg&amp;geocode=&amp;q=%D0%9C%D0%BB%D0%B0%D0%B4%D0%BE%D1%81%D1%82+1,+%D0%A1%D0%BE%D1%84%D0%B8%D1%8F,+%D0%91%D1%8A%D0%BB%D0%B3%D0%B0%D1%80%D0%B8%D1%8F&amp;sll=37.0625,-95.677068&amp;sspn=40.545434,79.013672&amp;ie=UTF8&amp;hq=&amp;hnear=%D0%9C%D0%BB%D0%B0%D0%B4%D0%BE%D1%81%D1%82+1&amp;ll=42.654446,23.372061&amp;spn=0.036864,0.077162&amp;t=h&amp;z=14&amp;output=embed"></iframe><br /><small><a href="http://maps.google.com/maps?f=q&amp;source=embed&amp;hl=bg&amp;geocode=&amp;q=%D0%9C%D0%BB%D0%B0%D0%B4%D0%BE%D1%81%D1%82+1,+%D0%A1%D0%BE%D1%84%D0%B8%D1%8F,+%D0%91%D1%8A%D0%BB%D0%B3%D0%B0%D1%80%D0%B8%D1%8F&amp;sll=37.0625,-95.677068&amp;sspn=40.545434,79.013672&amp;ie=UTF8&amp;hq=&amp;hnear=%D0%9C%D0%BB%D0%B0%D0%B4%D0%BE%D1%81%D1%82+1&amp;ll=42.654446,23.372061&amp;spn=0.036864,0.077162&amp;t=h&amp;z=14" style="color:#0000FF;text-align:left">Вижте по-голяма карта</a></small>
+ * into the [googlemaps http://...] shortcode format
+ */
+function jetpack_googlemaps_embed_to_short_code( $content ) {
+ if ( false === strpos( $content, 'maps.google.' ) && false === strpos( $content, 'google.com/maps' ) )
+ return $content;
+
+ // IE and TinyMCE format things differently
+ if ( strpos( $content, 'src="<a href="' ) !== false ) {
+ $content = preg_replace_callback( '!&lt;iframe width="(\d+)" height="(\d+)" frameborder="0" scrolling="no" marginheight="0" marginwidth="0" src="<a href="http://.*\.google\.(.*)/(.*)\?(.+)&quot;&gt;&lt;/iframe&gt;&lt;br">http://.*\.google\..*/(.*)\?(.+)"&gt;&lt;/iframe&gt;&lt;br</a> /&gt;&lt;small&gt;(.*)&lt;/small&gt;!i', 'jetpack_googlemaps_embed_to_short_code_callback', $content );
+ return $content;
+ }
+
+ $content = preg_replace_callback( '!\<iframe width="(\d+)" height="(\d+)" frameborder="0" scrolling="no" marginheight="0" marginwidth="0" src="http://.*\.google\.(.*)/(.*)\?(.+)"\>\</iframe\>\<br /\>\<small\>(.*)\</small\>!i', 'jetpack_googlemaps_embed_to_short_code_callback', $content );
+
+ $content = preg_replace_callback( '!&lt;iframe width="(\d+)" height="(\d+)" frameborder="0" scrolling="no" marginheight="0" marginwidth="0" src="http://.*\.google\.(.*)/(.*)\?(.+)"&gt;&lt;/iframe&gt;&lt;br /&gt;&lt;small&gt;(.*)&lt;/small&gt;!i', 'jetpack_googlemaps_embed_to_short_code_callback', $content );
+
+ return $content;
+}
+
+function jetpack_googlemaps_embed_to_short_code_callback( $match ) {
+ $url = "http://maps.google.{$match[3]}/{$match[4]}?{$match[5]}&amp;w={$match[1]}&amp;h={$match[2]}";
+
+ do_action( 'jetpack_embed_to_shortcode', 'googlemaps', $url );
+
+ return "[googlemaps $url]";
+}
+
+add_filter( 'pre_kses', 'jetpack_googlemaps_embed_to_short_code' );
+
+function jetpack_googlemaps_shortcode( $atts ) {
+ if ( !isset($atts[0]) || apply_filters( 'jetpack_bail_on_shortcode', false, 'googlemaps' ) )
+ return '';
+
+ $params = ltrim( $atts[0], '=' );
+
+ $width = 425;
+ $height = 350;
+
+ if ( preg_match( '!^http://maps\.google(\.co|\.com)?(\.[a-z]+)?/.*?(\?.+)!i', $params, $match ) ) {
+ $params = str_replace( '&amp;amp;', '&amp;', $params );
+ $params = str_replace( '&amp;', '&', $params );
+ parse_str( $params, $arg );
+
+ if ( isset( $arg['hq'] ) )
+ unset( $arg['hq'] );
+
+ $url = '';
+ foreach ( (array) $arg as $key => $value ) {
+ if ( 'w' == $key ) {
+ $width = (int) $value;
+ } elseif ( 'h' == $key ) {
+ $height = (int) $value;
+ } else {
+ $key = str_replace( '_', '.', $key );
+ $url .= esc_attr( "$key=$value&amp;" );
+ }
+ }
+ $url = substr( $url, 0, -5 );
+ $link_url = preg_replace( '!output=embed!', 'source=embed', $url );
+
+ return '<iframe width="' . $width . '" height="' . $height . '" frameborder="0" scrolling="no" marginheight="0" marginwidth="0" src="' . $url . '"></iframe><br /><small><a href="' . $link_url . '" style="text-align:left">View Larger Map</a></small>';
+ }
+}
+add_shortcode( 'googlemaps', 'jetpack_googlemaps_shortcode' );
diff --git a/plugins/jetpack/modules/shortcodes/googlevideo.php b/plugins/jetpack/modules/shortcodes/googlevideo.php
new file mode 100644
index 00000000..83074fbf
--- /dev/null
+++ b/plugins/jetpack/modules/shortcodes/googlevideo.php
@@ -0,0 +1,29 @@
+<?php
+
+/**
+ * google video is replaced by youtube, but its embeds will probably continue working indefinitely.
+ * [googlevideo=http://video.google.com/googleplayer.swf?docId=-6006084025483872237]
+ */
+
+function googlevideo_shortcode( $atts ) {
+ if ( !isset( $atts[0] ) )
+ return '';
+
+ $src = ltrim( $atts[0], '=' );
+
+ if ( 0 !== strpos( $src, 'http://video.google.com/googleplayer.swf' ) ) {
+ if ( !preg_match( '|^http://(video\.google\.[a-z]{2,3}(?:.[a-z]{2})?)/|', $src ) || !preg_match( '|.*docid=([0-9-]+).*|i', $src, $match ) || !is_numeric( $match[1] ) )
+ return '<!--Google Video Error: bad URL entered-->';
+
+ $src = 'http://video.google.com/googleplayer.swf?docId=' . $match[1];
+ }
+
+ // default width should be 400 unless the theme's content width is smaller than that
+ global $content_width;
+ $default_width = intval( !empty( $content_width ) ? min( $content_width, 400 ) : 400 );
+ $height = intval( 0.825 * $default_width );
+ $src = esc_attr( $src );
+
+ return "<span style='text-align:center;display:block;'><object width='{$default_width}' height='{$height}' type='application/x-shockwave-flash' data='{$src}'><param name='allowScriptAccess' value='never' /><param name='movie' value='$src'/><param name='quality' value='best'/><param name='bgcolor' value='#ffffff' /><param name='scale' value='noScale' /><param name='wmode' value='opaque' /></object></span>";
+}
+add_shortcode( 'googlevideo', 'googlevideo_shortcode' );
diff --git a/plugins/jetpack/modules/shortcodes/polldaddy.php b/plugins/jetpack/modules/shortcodes/polldaddy.php
new file mode 100644
index 00000000..63b2f66d
--- /dev/null
+++ b/plugins/jetpack/modules/shortcodes/polldaddy.php
@@ -0,0 +1,167 @@
+<?php
+
+/*
+ polldaddy.com
+ [polldaddy poll="139742"]
+ */
+
+if ( !function_exists( 'polldaddy_shortcode_handler' ) ) {
+
+ function polldaddy_shortcode_handler_set_data() {
+ $resource = wp_remote_get( 'http://polldaddy.com/xml/keywords.xml' );
+ $body = wp_remote_retrieve_body( $resource );
+ $keywords_xml = simplexml_load_string ( $body );
+ $keywords = array();
+ $keywords['generated'] = time();
+
+ foreach ( $keywords_xml->keyword as $keyword_xml ){
+ $keywords[] = array( 'keyword' => (string) $keyword_xml, 'url' => (string) $keyword_xml['url'] );
+ }
+ wp_cache_set( 'pd-keywords', $keywords, 'site-options', 864000 );
+
+ return $keywords;
+ }
+
+ function polldaddy_add_rating_js() {
+ wp_print_scripts( 'polldaddy-rating-js' );
+ }
+
+ function polldaddy_shortcode_handler( $atts, $content = null ) {
+ global $post;
+
+ extract( shortcode_atts( array(
+ 'survey' => null,
+ 'link_text' => 'View Survey',
+ 'poll' => 'empty',
+ 'rating' => 'empty',
+ 'unique_id' => null,
+ 'title' => null,
+ 'permalink' => null,
+ 'cb' => 0,
+ 'type' => null,
+ 'body' => '',
+ 'button' => '',
+ 'text_color' => 'FFFFFF',
+ 'back_color' => '000000',
+ 'align' => '',
+ 'style' => ''
+ ), $atts ) );
+
+ $survey = esc_attr( str_replace( "'", "", $survey ) );
+ $link_text = esc_attr( $link_text );
+
+ if ( null != $survey ) {
+
+ // This is the new survey embed
+ if ( $type != null ) {
+ $title = preg_replace( '/&amp;(\w*);/', '&$1;', esc_js( esc_attr( $title ) ) );
+ $type = preg_replace( '/&amp;(\w*);/', '&$1;', esc_js( esc_attr( $type ) ) );
+ $body = preg_replace( '/&amp;(\w*);/', '&$1;', esc_js( esc_attr( $body ) ) );
+ $button = preg_replace( '/&amp;(\w*);/', '&$1;', esc_js( esc_attr( $button ) ) );
+ $text_color = preg_replace( '/&amp;(\w*);/', '&$1;', esc_js( esc_attr( $text_color ) ) );
+ $back_color = preg_replace( '/&amp;(\w*);/', '&$1;', esc_js( esc_attr( $back_color ) ) );
+ $align = preg_replace( '/&amp;(\w*);/', '&$1;', esc_js( esc_attr( $align ) ) );
+ $style = preg_replace( '/&amp;(\w*);/', '&$1;', esc_js( esc_attr( $style ) ) );
+
+ return "
+ <script type='text/javascript' src='http://i0.poll.fm/survey.js' charset='UTF-8'></script>
+ <noscript><a href='http://polldaddy.com/s/$survey'>$title</a></noscript>
+ <script type='text/javascript'>
+ polldaddy.add( {
+ title: '$title',
+ type: '$type',
+ body: '$body',
+ button: '$button',
+ text_color: '$text_color',
+ back_color: '$back_color',
+ align: '$align',
+ style: '$style',
+ id: '$survey'
+ } );
+ </script>
+ ";
+
+ } else {
+ return "
+ <script language='javascript' type='text/javascript'>
+ var PDF_surveyID = '$survey';
+ var PDF_openText = '$link_text';
+ </script>
+ <script type='text/javascript' language='javascript' src='http://www.polldaddy.com/s.js'></script>
+ <noscript><a href='http://surveys.polldaddy.com/s/$survey/'>$link_text</a></noscript>
+
+ ";
+ }
+ }
+
+ $poll = (int) $poll;
+ $rating = (int) $rating;
+ $cb = (int) $cb;
+
+ if ( $rating > 0 ) {
+ if ( null != $unique_id ) {
+ $unique_id = wp_specialchars( $unique_id );
+ } else {
+ $unique_id = is_page() ? 'wp-page-' : 'wp-post-';
+ $unique_id .= $post->ID;
+ }
+
+ if ( null != $title )
+ $title = wp_specialchars( $title );
+ else
+ $title = urlencode( $post->post_title );
+
+ if ( null != $permalink )
+ $permalink = clean_url( $permalink );
+ else
+ $permalink = urlencode( get_permalink( $post->ID ) );
+
+ wp_register_script( 'polldaddy-rating-js', 'http://i.polldaddy.com/ratings/rating.js' );
+ add_filter( 'wp_footer', 'polldaddy_add_rating_js' );
+
+ return '<div id="pd_rating_holder_' . $rating . '"></div>
+ <script language="javascript">
+ PDRTJS_settings_' . $rating . ' = {
+ "id" : "' . $rating . '",
+ "unique_id" : "' . $unique_id . '",
+ "title" : "' . $title . '",
+ "permalink" : "' . $permalink . '"
+ };
+ </script>';
+ } elseif ( $poll > 0 ) {
+ $cb = ( $cb == 1 ? '?cb=' . mktime() : '' );
+ $keywords = wp_cache_get( 'pd-keywords', 'site-options' );
+ if ( ! $keywords || $keywords['generated'] <= ( time() - 300 ) ) {
+ if ( ! wp_cache_get( 'pd-keywords-fetching', 'site-options' ) ) {
+ wp_cache_set( 'pd-keywords-fetching', 1, 'site-options', 30 );
+ $keywords = polldaddy_shortcode_handler_set_data();
+ }
+ }
+
+ if ( !$keywords )
+ $keywords = array();
+
+ $mod = ( $poll % ( count( $keywords ) - 1 ) );
+
+ return '<a name="pd_a_' . $poll . '"></a><div class="PDS_Poll" id="PDI_container' . $poll . '" style="display:inline-block;"></div><script type="text/javascript" language="javascript" charset="utf-8" src="http://static.polldaddy.com/p/' . $poll . '.js' . $cb . '"></script>
+ <noscript>
+ <a href="http://answers.polldaddy.com/poll/' . $poll . '/">View This Poll</a><br/><span style="font-size:10px;"><a href="' . $keywords[ $mod ][ 'url' ] . '">' . $keywords[ $mod ][ 'keyword' ] . '</a></span>
+ </noscript>';
+ }
+
+ return '<!-- no polldaddy output -->';
+ }
+
+ add_shortcode( 'polldaddy', 'polldaddy_shortcode_handler' );
+
+ // http://answers.polldaddy.com/poll/1562975/?view=results&msg=voted
+ function polldaddy_link( $content ) {
+ return preg_replace( '!(?:\n|\A)http://answers.polldaddy.com/poll/([0-9]+?)/(.+)?(?:\n|\Z)!i', "\n<script type='text/javascript' language='javascript' charset='utf-8' src='http://s3.polldaddy.com/p/$1.js'></script><noscript> <a href='http://answers.polldaddy.com/poll/$1/'>View Poll</a></noscript>\n", $content );
+ }
+
+ // higher priority because we need it before auto-link and autop get to it
+ add_filter( 'the_content', 'polldaddy_link', 1 );
+ add_filter( 'the_content_rss', 'polldaddy_link', 1 );
+ add_filter( 'comment_text', 'polldaddy_link', 1 );
+
+} \ No newline at end of file
diff --git a/plugins/jetpack/modules/shortcodes/scribd.php b/plugins/jetpack/modules/shortcodes/scribd.php
new file mode 100644
index 00000000..d8677c91
--- /dev/null
+++ b/plugins/jetpack/modules/shortcodes/scribd.php
@@ -0,0 +1,45 @@
+<?php
+
+/* Scribd Short Code
+Author: Nick Momrik
+
+[scribd id=DOCUMENT_ID key=DOCUMENT_KEY mode=MODE]
+DOCUMENT_ID is an integer (also used as an object_id)
+DOCUMENT_KEY is an alphanumeric hash ('-' character as well)
+MODE can be 'list', 'book', 'slide', 'slideshow', or 'tile'
+
+[scribd id=39027960 key=key-3kaiwcjqhtipf25m8tw mode=list]
+*/
+
+function scribd_shortcode_handler( $atts ) {
+ $atts = shortcode_atts( array(
+ 'id' => 0,
+ 'key' => 0,
+ 'mode' => "",
+ ), $atts );
+
+ $modes = array( 'list', 'book', 'slide', 'slideshow', 'tile' );
+
+ $atts['id'] = (int) $atts['id'];
+ if ( preg_match( '/^[A-Za-z0-9-]+$/', $atts['key'], $m ) ) {
+ $atts['key'] = $m[0];
+
+ if ( !in_array( $atts['mode'], $modes ) )
+ $atts['mode'] = '';
+
+ return scribd_shortcode_markup( $atts );
+ } else {
+ return '';
+ }
+}
+
+function scribd_shortcode_markup( $atts ) {
+ $markup = <<<EOD
+<iframe class="scribd_iframe_embed" src="http://www.scribd.com/embeds/$atts[id]/content?start_page=1&view_mode=$atts[mode]&access_key=$atts[key]" data-auto-height="true" scrolling="no" id="scribd_$atts[id]" width="100%" height="500" frameborder="0"></iframe>
+<div style="font-size:10px;text-align:center;width:100%"><a href="http://www.scribd.com/doc/$atts[id]">View this document on Scribd</a></div>
+EOD;
+
+ return $markup;
+}
+
+add_shortcode( 'scribd', 'scribd_shortcode_handler' );
diff --git a/plugins/jetpack/modules/shortcodes/slide.php b/plugins/jetpack/modules/shortcodes/slide.php
new file mode 100644
index 00000000..ea668de2
--- /dev/null
+++ b/plugins/jetpack/modules/shortcodes/slide.php
@@ -0,0 +1,121 @@
+<?php
+
+/**
+ * slideshow and slideguest shortcodes for slide.com
+ * [slideshow id=2233785415202545677&w=426&h=320]
+ */
+function jetpack_slide_embed_to_short_code( $content ) {
+ global $content_width;
+
+ if ( false === strpos( $content, 'slide.com/widgets' ) )
+ return $content;
+
+ $regexp = '!<div><embed((?:\s+\w+="[^"]*")*)\s+src="http://widget[^"]+slide\.com/widgets/slideticker\.swf"((?:\s+\w+="[^"]*")*)\s*(?:/?>|>\s*</embed>)\s*<div(?:\s+[^>]+).*?slide\.com/p1/.*?slide\.com/p2.*?</div>\s*</div>!i';
+ $regexp_ent = htmlspecialchars( $regexp, ENT_NOQUOTES );
+
+ foreach ( array( 'regexp', 'regexp_ent' ) as $reg ) {
+ if ( !preg_match_all( $$reg, $content, $matches, PREG_SET_ORDER ) )
+ continue;
+
+ foreach ( $matches as $match ) {
+ $params = $match[1] . $match[2];
+ if ( 'regexp_ent' == $reg )
+ $params = html_entity_decode( $params );
+
+ $params = wp_kses_hair( $params, array( 'http' ) );
+ if ( !isset( $params['type'] ) || 'application/x-shockwave-flash' != $params['type']['value'] || !isset( $params['flashvars'] ) )
+ continue;
+
+ wp_parse_str( html_entity_decode( $params['flashvars']['value'] ), $flashvars );
+
+ if ( empty( $flashvars['channel'] ) )
+ continue;
+
+ $id = $flashvars['channel'];
+
+ $width = 400;
+ if ( ! empty( $params['width']['value'] ) )
+ $width = (int) $params['width']['value'];
+ elseif ( ! empty( $params['style']['value'] ) && preg_match( '/width\s*:\s*(\d+)/i', $params['style']['value'], $width_match ) )
+ $width = (int) $width_match[1];
+
+ $height = 300;
+ if ( ! empty( $params['height']['value'] ) )
+ $height = (int) $params['height']['value'];
+ elseif ( ! empty( $params['style']['value'] ) && preg_match( '/height\s*:\s*(\d+)/i', $params['style']['value'], $height_match ) )
+ $height = (int) $height_match[1];
+
+ if ( $content_width && $width > $content_width ) {
+ $height = intval( $height * $content_width / $width );
+ $width = $content_width;
+ }
+
+ $content = str_replace( $match[0], "[slideshow id={$id}&amp;w={$width}&amp;h={$height}]", $content );
+
+ do_action( 'jetpack_embed_to_shortcode', 'slideshow', $id );
+ }
+ }
+
+ return $content;
+}
+add_filter( 'pre_kses', 'jetpack_slide_embed_to_short_code' );
+
+function jetpack_slideshow_shortcode( $atts, $content, $shortcode ) {
+ if (
+ 'slideshow' == $shortcode
+ &&
+ ( empty( $atts['id'] ) || false === strpos( $atts['id'], '&' ) )
+ &&
+ ( $previous_slideshow_shortcode = jetpack_slide_shortcodes( true ) )
+ &&
+ is_callable( $previous_slideshow_shortcode )
+ ) {
+ return call_user_func( $previous_slideshow_shortcode, $atts, $content, $shortcode );
+ }
+
+ return jetpack_slide_embed( $shortcode, $atts );
+}
+
+function jetpack_slide_embed( $type, $atts ) {
+ $param = shortcode_new_to_old_params( $atts );
+
+ if ( ctype_digit( $param ) ) {
+ $id = $param;
+ $w = 426;
+ $h = 320;
+ } else {
+ parse_str( $param, $params );
+ if ( count( $params ) != 3 || !isset( $params['id'] ) || !isset( $params['w'] ) || !isset( $params['h'] ) )
+ return '<!-- Slide.com error: provide id, w, h -->';
+
+ extract( $params );
+ if ( !ctype_digit( $id ) || !ctype_digit( $w ) || !ctype_digit( $h ) )
+ return '<!-- Slide.com error: provide integers -->';
+ }
+
+ $partition = sprintf( '%02x', $id % 256 );
+
+ if ( 'slideshow' == $type )
+ return "<div><embed src='http://widget-$partition.slide.com/widgets/slideticker.swf' type='application/x-shockwave-flash' quality='high' scale='noscale' salign='l' wmode='transparent' flashvars='site=widget-$partition.slide.com&channel=$id&cy=wp&il=1' width='$w' height='$h' name='flashticker' align='middle' /><div style='width: {$w}px;text-align:left;'><a href='http://www.slide.com/pivot?ad=0&tt=0&sk=0&cy=wp&th=0&id=$id&map=1' target='_blank'><img src='http://widget-$partition.slide.com/p1/$id/wp_t000_v000_a000_f00/images/xslide1.gif' border='0' ismap='ismap' /></a> <a href='http://www.slide.com/pivot?ad=0&tt=0&sk=0&cy=wp&th=0&id=$id&map=2' target='_blank'><img src='http://widget-$partition.slide.com/p2/$id/wp_t000_v000_a000_f00/images/xslide2.gif' border='0' ismap='ismap' /></a></div></div>";
+ else
+ return "<div><embed src='http://widget-$partition.slide.com/widgets/slidemap.swf' type='application/x-shockwave-flash' quality='high' scale='noscale' salign='l' wmode='transparent' flashvars='site=widget-$partition.slide.com&channel=$id&cy=wp&il=1' width='$w' height='$h' name='flashticker' align='middle' /><div style='width:{$w}px;text-align:left;'><a href='http://www.slide.com/pivot?ad=0&tt=0&sk=0&cy=wp&th=0&id=$id&map=5' target='_blank'><img src='http://widget-$partition.slide.com/c1/$id/wp_t000_v000_a000_f00/images/xslide1.gif' border='0' ismap='ismap' /></a> <a href='http://www.slide.com/pivot?ad=0&tt=0&sk=0&cy=wp&th=0&id=$id&map=6' target='_blank'><img src='http://widget-$partition.slide.com/c2/$id/wp_t000_v000_a000_f00/images/xslide6.gif' border='0' ismap='ismap' /></a></div></div>";
+
+}
+
+function jetpack_slide_shortcodes( $get_previous = false ) {
+ global $shortcode_tags;
+ static $previous = false;
+
+ if ( $get_previous ) {
+ return $previous;
+ }
+
+ if ( isset( $shortcode_tags['slideshow'] ) ) {
+ $previous = $shortcode_tags['slideshow'];
+ }
+
+ add_shortcode( 'slideguest', 'jetpack_slideshow_shortcode' );
+ add_shortcode( 'slideshow', 'jetpack_slideshow_shortcode' );
+}
+
+jetpack_slide_shortcodes();
diff --git a/plugins/jetpack/modules/shortcodes/slideshare.php b/plugins/jetpack/modules/shortcodes/slideshare.php
new file mode 100644
index 00000000..012b9c7b
--- /dev/null
+++ b/plugins/jetpack/modules/shortcodes/slideshare.php
@@ -0,0 +1,41 @@
+<?php
+/*
+ * Slideshare shortcode format:
+ * [slideshare id=5342235&doc=camprock-101002163655-phpapp01&w=300&h=200]
+ **/
+
+function slideshare_shortcode( $atts ) {
+ global $content_width;
+
+ $params = shortcode_new_to_old_params( $atts );
+ parse_str( $params, $arguments );
+
+ if ( empty( $arguments ) )
+ return "<!-- SlideShare error: no arguments -->";
+
+ extract( $arguments );
+
+ $pattern = '/[^-_a-zA-Z0-9]/';
+ if ( empty( $id ) || preg_match( $pattern, $id ) )
+ return "<!-- SlideShare error: id is missing or has illegal characters -->";
+
+ if ( empty( $doc ) || preg_match( $pattern, $doc ) )
+ return "<!-- SlideShare error: doc is missing or has illegal characters -->";
+
+ if ( empty( $w ) && !empty( $content_width ) )
+ $w = intval( $content_width );
+ elseif ( ! ( $w = intval( $w ) ) || $w < 300 || $w > 1600 )
+ $w = 425;
+ else
+ $w = intval( $w );
+
+ $h = ceil( $w * 348 / 425 );
+
+ $player = "<object type='application/x-shockwave-flash' wmode='opaque' data='http://static.slideshare.net/swf/ssplayer2.swf?id=$id&doc=$doc' width='$w' height='$h'><param name='movie' value='http://static.slideshare.net/swf/ssplayer2.swf?id=$id&doc=$doc' /><param name='allowFullScreen' value='true' /></object>";
+ if ( !empty( $type ) && $type == 'd' )
+ $player = "<object style='margin: 0px;' width='$w' height='$h'><param name='movie' value='http://static.slidesharecdn.com/swf/ssplayerd.swf?doc=$doc' /><param name='allowFullScreen' value='true' /><param name='wmode' value='opaque' /><embed src='http://static.slidesharecdn.com/swf/ssplayerd.swf?doc=$doc' type='application/x-shockwave-flash' allowfullscreen='true' wmode='opaque' width='$w' height='$h'></embed></object>";
+
+ return $player;
+}
+
+add_shortcode( 'slideshare', 'slideshare_shortcode' );
diff --git a/plugins/jetpack/modules/shortcodes/soundcloud.php b/plugins/jetpack/modules/shortcodes/soundcloud.php
new file mode 100644
index 00000000..8aa35380
--- /dev/null
+++ b/plugins/jetpack/modules/shortcodes/soundcloud.php
@@ -0,0 +1,60 @@
+<?php
+
+/*
+Plugin Name: SoundCloud Shortcode
+Plugin URI: http://www.soundcloud.com
+Description: SoundCloud Shortcode. Usage in your posts: [soundcloud]http://soundcloud.com/TRACK_PERMALINK[/soundcloud] . Works also with set or group instead of track. You can provide optional parameters height/width/params like that [soundcloud height="82" params="auto_play=true"]http....
+Version: 1.1.5
+Author: Johannes Wagener <johannes@soundcloud.com> added to wpcom by tott
+Author URI: http://johannes.wagener.cc
+
+[soundcloud url="http://api.soundcloud.com/tracks/9408008"]
+<object height="81" width="100%"> <param name="movie" value="http://player.soundcloud.com/player.swf?url=http%3A%2F%2Fapi.soundcloud.com%2Ftracks%2F8781356"></param> <param name="allowscriptaccess" value="always"></param> <embed allowscriptaccess="always" height="81" src="http://player.soundcloud.com/player.swf?url=http%3A%2F%2Fapi.soundcloud.com%2Ftracks%2F8781356" type="application/x-shockwave-flash" width="100%"></embed> </object> <span><a href="http://soundcloud.com/robokopbeats/robokop-we-move-at-midnight-preview-forthcoming-on-mwm-recordings">Robokop - We move at midnight preview ( FORTHCOMING ON MWM recordings)</a> by <a href="http://soundcloud.com/robokopbeats">Robokop</a></span>
+*/
+
+add_filter( "pre_kses", "soundcloud_reverse_shortcode" );
+
+function soundcloud_reverse_shortcode_preg_replace_callback( $a ) {
+ $pattern = '/([a-zA-Z0-9\-_%=&]*)&?url=([^&]+)&?([a-zA-Z0-9\-_%&=]*)/';
+ preg_match( $pattern, str_replace( "&amp;", "&", $a[3] ), $params );
+
+ return '[soundcloud width="' . esc_attr( $a[2] ) . '" height="' . esc_attr( $a[1] ) . '" params="' . esc_attr( $params[1] . $params[3] ) . '" url="' . urldecode( $params[2] ) . '"]';
+}
+
+function soundcloud_reverse_shortcode( $content ){
+ if ( false === stripos( $content, 'http://player.soundcloud.com/player.swf' ) )
+ return $content;
+
+ $pattern = '!<object\s*height="(\d+%?)"\s*width="(\d+%?)".*?src="http://.*?soundcloud\.com/player.swf\?([^"]+)".*?</object>.*?</span>!';
+ $pattern_ent = str_replace( '&amp;#0*58;', '&amp;#0*58;|&#0*58;', htmlspecialchars( $pattern, ENT_NOQUOTES ) );
+
+ if ( preg_match( $pattern_ent, $content ) )
+ return( preg_replace_callback( $pattern_ent, 'soundcloud_reverse_shortcode_preg_replace_callback', $content ) );
+ else
+ return( preg_replace_callback( $pattern, 'soundcloud_reverse_shortcode_preg_replace_callback', $content ) );
+}
+
+add_shortcode( "soundcloud", "soundcloud_shortcode" );
+
+function soundcloud_shortcode( $atts, $url = '' ) {
+ if ( empty( $url ) )
+ extract( shortcode_atts( array( 'url' => '', 'params' => '', 'height' => '', 'width' => '100%' ), $atts ) );
+ else
+ extract( shortcode_atts( array( 'params' => '', 'height' => '', 'width' => '100%' ), $atts ) );
+
+ $encoded_url = urlencode( $url );
+ if ( $url = parse_url( $url ) ) {
+ $splitted_url = split( "/", $url['path'] );
+ $media_type = $splitted_url[ count( $splitted_url ) - 2 ];
+
+ if ( '' == $height ){
+ if ( in_array( $media_type, array( 'groups', 'sets' ) ) )
+ $height = 225;
+ else
+ $height = 81;
+ }
+ $player_params = "url=$encoded_url&g=1&$params";
+
+ return '<object height="' . esc_attr( $height ) . '" width="' . esc_attr( $width ) . '"><param name="movie" value="' . esc_url( "http://player.soundcloud.com/player.swf?$player_params" ) . '"></param><embed height="' . esc_attr( $height ) . '" src="' . esc_url( "http://player.soundcloud.com/player.swf?$player_params" ) . '" type="application/x-shockwave-flash" width="' . esc_attr( $width ) . '"> </embed> </object>';
+ }
+}
diff --git a/plugins/jetpack/modules/shortcodes/videopress.php b/plugins/jetpack/modules/shortcodes/videopress.php
new file mode 100644
index 00000000..aa87fbd5
--- /dev/null
+++ b/plugins/jetpack/modules/shortcodes/videopress.php
@@ -0,0 +1,1325 @@
+<?php
+/**
+ * @package video
+ * @category video
+ * @author Automattic Inc
+ * @link http://automattic.com/wordpress-plugins/#videopress VideoPress
+ * @version 1.5
+ * @license http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
+ */
+
+/*
+Plugin Name: VideoPress
+Plugin URI: http://wordpress.org/extend/plugins/video/
+Description: Upload new videos to <a href="http://videopress.com/">VideoPress</a>, edit metadata, and easily insert VideoPress videos into posts and pages using shortcodes. Requires a <a href="http://wordpress.com/">WordPress.com</a> account and a WordPress.com blog with the <a href="http://en.wordpress.com/products/#videopress">VideoPress upgrade</a> to store and serve uploaded videos.
+Author: Automattic, Niall Kennedy, Joseph Scott, Gary Pendergast
+Contributor: Hailin Wu
+Author URI: http://automattic.com/wordpress-plugins/#videopress
+Version: 1.5
+Stable tag: 1.5
+License: GPL v2 - http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
+ */
+
+if ( ! class_exists( 'VideoPress' ) ):
+
+/**
+ * VideoPress main handler.
+ * Attach actions and filters. Handle shortcodes. Add video button to rich text editor.
+ * @since 1.3
+ */
+class VideoPress {
+ /**
+ * Plugin version in PHP-addressable form
+ * @var string
+ * @since 1.3
+ */
+ const version = '1.5';
+
+ /**
+ * Minimum allowed width. We don't expect videos viewed below this width to be useful; we drop small values to help save publishers from themselves.
+ * @var int
+ * @since 1.3
+ */
+ const min_width = 60;
+
+ /**
+ * Remember if videopress.js and dependencies have already been loaded
+ * @var bool
+ * @since 1.5
+ */
+ var $js_loaded;
+
+ /**
+ * Remember all of the videos loaded on this page
+ * @var array
+ * @since 1.5
+ */
+ var $shown;
+
+ /**
+ * Attach actions, filters, and shortcode handlers
+ * @since 1.3
+ */
+ public function __construct() {
+ /**
+ * json_decode should be initialized by compat.php. It's a PHP extension that might not be turned on, or could not be compatible with older version of PHP. We won't be able to unpack the server response without it, so let's fail early.
+ */
+ if ( ! function_exists( 'json_decode' ) )
+ return;
+
+ add_action( 'wp_head', array( $this, 'html_head' ), -1 ); // load before enqueue_scripts action
+
+ //allow either [videopress xyz] or [wpvideo xyz] for backward compatibility
+ add_shortcode( 'videopress', array( $this, 'shortcode' ) );
+ add_shortcode( 'wpvideo', array( $this, 'shortcode' ) );
+
+ // set default values
+ $this->js_loaded = false;
+ $this->shown = array();
+ }
+
+ /**
+ * PHP 4 constructor compatibility
+ *
+ * @since 1.5
+ * @todo remove when targeting PHP 5 (WordPress 3.2 requirement) or above.
+ */
+ public function VideoPress() {
+ $this->__construct();
+ }
+
+ /**
+ * Validate user-supplied guid values against expected inputs
+ *
+ * @since 1.1
+ * @param string $guid video identifier
+ * @return bool true if passes validation test
+ */
+ public static function is_valid_guid( $guid ) {
+ if ( ! empty( $guid ) && strlen( $guid ) === 8 && ctype_alnum( $guid ) )
+ return true;
+ else
+ return false;
+ }
+
+ /**
+ * Search a given content string for VideoPress shortcodes. Return an array of shortcodes with guid and attribute values.
+ *
+ * @since 1.2
+ * @see do_shortcode()
+ * @param string $content post content string
+ * @return array Array of shortcode data. GUID as the key and other customization parameters as value. empty array if no matches found.
+ */
+ public static function find_all_shortcodes( $content ) {
+ $r = preg_match_all( '/(.?)\[(wpvideo|videopress)\b(.*?)(?:(\/))?\](?:(.+?)\[\/\2\])?(.?)/s', $content, $matches, PREG_SET_ORDER );
+
+ if ( $r === false || $r === 0 )
+ return array();
+
+ $guids = array();
+ foreach ( $matches as $m ) {
+ // allow [[foo]] syntax for escaping a tag
+ if ( $m[1] === '[' && $m[6] === ']' )
+ continue;
+ $attr = shortcode_parse_atts( $m[3] );
+ if ( self::is_valid_guid( $attr[0] ) ) {
+ $guid = $attr[0];
+ unset( $attr[0] );
+ $guids[$guid] = $attr;
+ }
+ }
+
+ return $guids;
+ }
+
+
+ /**
+ * Insert video handlers into HTML <head> if posts with video shortcodes exist.
+ * If video posts are present then queue VideoPress JavaScript files.
+ * If a video is present and is single post or page then add Open Graph protocol markup for first video found
+ *
+ * @since 1.3
+ */
+ public function html_head() {
+ if ( is_feed() || ! have_posts() )
+ return;
+
+ $guid = '';
+ while ( have_posts() ) {
+ the_post();
+ $guids = self::find_all_shortcodes( get_the_content() );
+ if ( ! empty( $guids ) ) {
+ $guid = trim( key( $guids ) );
+ break;
+ }
+ unset( $guids );
+ }
+ rewind_posts();
+
+ if ( ! empty( $guid ) )
+ add_action( 'wp_enqueue_scripts', array( $this, 'enqueue_scripts' ), 20 );
+ }
+
+ /**
+ * Add VideoPress JavaScript files to the script queue.
+ * A blog with the video_player_freedom option set to true may still require the VideoPress JS for stats purposes and therefore is not a reason for exclusion.
+ *
+ * @uses wp_enqueue_script()
+ * @since 1.3
+ * @return bool true if queued; else false
+ */
+ public function enqueue_scripts() {
+ if ( $this->js_loaded === true )
+ return false;
+
+ $jquery = '://ajax.googleapis.com/ajax/libs/jquery/1.6.2/jquery.min.js';
+ $swfobject = '://ajax.googleapis.com/ajax/libs/swfobject/2.2/swfobject.js';
+ if ( is_ssl() ) {
+ $vpjs = 'https://v0.wordpress.com/js/videopress.js';
+ $swfobject = 'https' . $swfobject;
+ $jquery = 'https' . $jquery;
+ } else {
+ $vpjs = 'http://s0.videopress.com/js/videopress.js';
+ $swfobject = 'http' . $swfobject;
+ $jquery = 'http' . $jquery;
+ }
+
+ wp_enqueue_script( 'swfobject', $swfobject, false. '2.2' );
+ wp_enqueue_script( 'jquery', $jquery, false, '1.4.4' );
+ wp_enqueue_script( 'videopress', $vpjs, array( 'jquery','swfobject' ), '1.07' );
+
+ $this->js_loaded = true;
+ return true;
+ }
+
+ /**
+ * Print the VideoPress JS files now.
+ * Used to load the JS in the footer, if it hasn't already been loaded in the header.
+ *
+ * @uses wp_enqueue_script()
+ * @uses wp_print_scripts()
+ * @since 1.5
+ */
+ public function print_scripts() {
+ if ( $this->enqueue_scripts() === true )
+ wp_print_scripts( array( 'swfobject', 'videopress' ) );
+ }
+
+ /**
+ * Translate a 'videopress' or 'wpvideo' shortcode and arguments into a video player display.
+ *
+ * @link http://codex.wordpress.org/Shortcode_API Shortcode API
+ * @param array $attr shortcode attributes
+ * @return string HTML markup or blank string on fail
+ */
+ public function shortcode( $attr ) {
+ global $content_width;
+
+ $guid = $attr[0];
+ if ( ! self::is_valid_guid( $guid ) )
+ return '';
+
+ if ( array_key_exists( $guid, $this->shown ) )
+ $this->shown[$guid]++;
+ else
+ $this->shown[$guid] = 1;
+
+ extract( shortcode_atts( array(
+ 'w' => 0,
+ 'freedom' => false,
+ 'flashonly' => false,
+ 'autoplay' => false
+ ), $attr ) );
+
+ $freedom = (bool) $freedom;
+
+ $width = absint($w);
+ unset($w);
+
+ if ( $width < self::min_width )
+ $width = 0;
+ elseif ( isset($content_width) && $content_width > self::min_width && $width > $content_width )
+ $width = 0;
+
+ if ( $width === 0 && isset( $content_width ) && $content_width > self::min_width )
+ $width = $content_width;
+
+ if ( ($width % 2) === 1 )
+ $width--;
+
+ $options = array(
+ 'freedom' => $freedom,
+ 'force_flash' => (bool) $flashonly,
+ 'autoplay' => (bool) $autoplay
+ );
+ unset( $freedom );
+ unset( $flashonly );
+
+ add_action( 'wp_footer', array( $this, 'print_scripts' ), -1 );
+
+ $player = new VideoPress_Player( $guid, $width, $options );
+ if ( $player instanceOf VideoPress_Player ) {
+ if ( is_feed() )
+ return $player->asXML();
+ else
+ return $player->asHTML();
+ } else {
+ return 'error';
+ }
+ }
+
+ /**
+ * Add a video button above the post composition screen linking to a thickbox view of WordPress.com videos
+ *
+ * @since 0.1.0
+ */
+ public function media_button() {
+ echo '<a href="https://public-api.wordpress.com/videopress-plugin.php?page=video-plugin&amp;video_plugin=1&amp;iframe&amp;TB_iframe=true" id="add_video" class="thickbox" title="VideoPress"><img src="' . esc_url( plugins_url( ) . '/' . dirname( plugin_basename( __FILE__ ) ) . '/camera-video.png' ) . '" alt="VideoPress" width="16" height="16" /></a>';
+ }
+}
+
+/**
+ * VideoPress video object retrieved from VideoPress servers and parsed.
+ * @since 1.3
+ */
+class VideoPress_Video {
+ /**
+ * Manifest version returned by remote service.
+ *
+ * @var string
+ * @since 1.3
+ */
+ const manifest_version = '1.5';
+
+ /**
+ * Expiration of the video expressed in Unix time
+ *
+ * @var int
+ * @since 1.3
+ */
+ public $expires;
+
+ /**
+ * VideoPress unique identifier
+ *
+ * @var string
+ * @since 1.3
+ */
+ public $guid;
+
+ /**
+ * WordPress.com blog identifier
+ *
+ * @var int
+ * @since 1.5
+ */
+ public $blog_id;
+
+ /**
+ * Remote blog attachment identifier
+ *
+ * @var int
+ * @since 1.5
+ */
+ public $post_id;
+
+ /**
+ * Maximum desired width.
+ *
+ * @var int
+ * @since 1.3
+ */
+ public $maxwidth;
+
+ /**
+ * Video width calculated based on original video dimensions and the requested maxwidth
+ *
+ * @var int
+ * @since 1.3
+ */
+ public $calculated_width;
+
+ /**
+ * Video height calculated based on original video dimensions and the requested maxwidth
+ *
+ * @var int
+ * @since 1.3
+ */
+ public $calculated_height;
+
+ /**
+ * Video title
+ *
+ * @var string
+ * @since 1.3
+ */
+ public $title;
+
+ /**
+ * Directionality of title text. ltr or rtl
+ *
+ * @var string
+ * @since 1.3
+ */
+ public $text_direction;
+
+ /**
+ * Text and audio language as ISO 639-2 language code
+ *
+ * @var string
+ * @since 1.3
+ */
+ public $language;
+
+ /**
+ * Video duration in whole seconds
+ *
+ * @var int
+ * @since 1.3
+ */
+ public $duration;
+
+ /**
+ * Recommended minimum age of the viewer.
+ *
+ * @var int
+ * @since 1.3
+ */
+ public $age_rating;
+
+ /**
+ * Video author has restricted video embedding or sharing
+ *
+ * @var bool
+ * @since 1.3
+ */
+ public $restricted_embed;
+
+ /**
+ * Poster frame image URI for the given video guid and calculated dimensions.
+ *
+ * @var string
+ * @since 1.3
+ */
+ public $poster_frame_uri;
+
+ /**
+ * Video files associated with the given guid for the calculated dimensions.
+ *
+ * @var stdClass
+ * @since 1.3
+ */
+ public $videos;
+
+ /**
+ * Video player information
+ *
+ * @var stdClass
+ * @since 1.3
+ */
+ public $players;
+
+ /**
+ * Video player skinning preferences including background color and watermark
+ *
+ * @var array
+ * @since 1.5
+ */
+ public $skin;
+
+ /**
+ * Closed captions if available for the given video. Associative array of ISO 639-2 language code and a WebVTT URI
+ *
+ * @var array
+ * @since 1.5
+ */
+ public $captions;
+
+ /**
+ * Setup the object.
+ * Request video information from VideoPress servers and process the response.
+ *
+ * @since 1.3
+ * @var string $guid VideoPress unique identifier
+ * @var int $maxwidth maximum requested video width. final width and height are calculated on VideoPress servers based on the aspect ratio of the original video upload.
+ */
+ public function __construct( $guid, $maxwidth=0 ) {
+ if ( VideoPress::is_valid_guid( $guid ) )
+ $this->guid = $guid;
+
+ $maxwidth = absint( $maxwidth );
+ if ( $maxwidth > 0 )
+ $this->maxwidth = $maxwidth;
+
+ $data = $this->get_data();
+ if ( is_wp_error( $data ) || empty( $data ) ) {
+ $this->error = $data;
+ return;
+ }
+
+ if ( isset( $data->blog_id ) )
+ $this->blog_id = absint( $data->blog_id );
+
+ if ( isset( $data->post_id ) )
+ $this->post_id = absint( $data->post_id );
+
+ if ( isset( $data->title ) && $data->title !== '' )
+ $this->title = trim( str_replace( '&nbsp;', ' ', $data->title ) );
+
+ if ( isset( $data->text_direction ) && $data->text_direction === 'rtl' )
+ $this->text_direction = 'rtl';
+ else
+ $this->text_direction = 'ltr';
+
+ if ( isset( $data->language ) )
+ $this->language = $data->language;
+
+ if ( isset( $data->duration ) && $data->duration > 0 )
+ $this->duration = absint( $data->duration );
+
+ if ( isset( $data->width ) && $data->width > 0 )
+ $this->calculated_width = absint( $data->width );
+
+ if ( isset( $data->height ) && $data->height > 0 )
+ $this->calculated_height = absint( $data->height );
+
+ if ( isset( $data->age_rating ) )
+ $this->age_rating = absint( $this->age_rating );
+
+ if ( isset( $data->restricted_embed ) && $data->restricted_embed === true )
+ $this->restricted_embed = true;
+ else
+ $this->restricted_embed = false;
+
+ if ( isset( $data->posterframe ) && $data->posterframe !== '' )
+ $this->poster_frame_uri = esc_url_raw( $data->posterframe, array( 'http', 'https' ) );
+
+ if ( isset( $data->mp4 ) || isset( $data->ogv ) ) {
+ $this->videos = new stdClass();
+ if ( isset( $data->mp4 ) )
+ $this->videos->mp4 = $data->mp4;
+ if ( isset( $data->ogv ) )
+ $this->videos->ogv = $data->ogv;
+ }
+
+ if ( isset( $data->swf ) ) {
+ if ( ! isset( $this->players ) )
+ $this->players = new stdClass();
+ $this->players->swf = $data->swf;
+ }
+
+ if ( isset( $data->skin ) )
+ $this->skin = $data->skin;
+
+ if ( isset( $data->captions ) )
+ $this->captions = (array) $data->captions;
+ }
+
+ /**
+ * PHP 4 constructor compatibility
+ *
+ * @since 1.5
+ * @todo remove when targeting PHP 5 (WordPress 3.2 requirement) or above.
+ */
+ public function VideoPress_Video( $guid, $maxwidth=0 ) {
+ $this->__construct( $guid, $maxwidth );
+ }
+
+ /**
+ * Convert an Expires HTTP header value into Unix time for use in WP Cache
+ *
+ * @since 1.3
+ * @var string $expires_header
+ * @return int|bool Unix time or false
+ */
+ public static function calculate_expiration( $expires_header ) {
+ if ( empty( $expires_header ) || ! is_string( $expires_header ) )
+ return false;
+
+ if ( class_exists( 'DateTime' ) && class_exists( 'DateTimeZone' ) ) {
+ $expires_date = DateTime::createFromFormat( 'D, d M Y H:i:s T', $expires_header, new DateTimeZone( 'UTC' ) );
+ if ( $expires_date instanceOf DateTime )
+ return date_format( $expires_date, 'U' );
+ } else {
+ $expires_array = strptime( $expires_header, '%a, %d %b %Y %H:%M:%S %Z' );
+ if ( is_array( $expires_array ) && isset( $expires_array['tm_hour'] ) && isset( $expires_array['tm_min'] ) && isset( $expires_array['tm_sec'] ) && isset( $expires_array['tm_mon'] ) && isset( $expires_array['tm_mday'] ) && isset( $expires_array['tm_year'] ) )
+ return gmmktime( $expires_array['tm_hour'], $expires_array['tm_min'], $expires_array['tm_sec'], 1 + $expires_array['tm_mon'], $expires_array['tm_mday'], 1900 + $expires_array['tm_year'] );
+ }
+ return false;
+ }
+
+ /**
+ * Extract the site's host domain for statistics and comparison against an allowed site list in the case of restricted embeds.
+ *
+ * @since 1.2
+ * @param string $url absolute URL
+ * @return bool|string host component of the URL, or false if none found
+ */
+ public static function hostname( $url ) {
+ if ( empty($url) || ! function_exists('parse_url') )
+ return false;
+
+ // PHP 5.3.3 or newer can throw a warning on a bad input URI. catch that occurance just in case
+ try {
+ // use the component parameter of parse_url if current version of PHP supports
+ if ( version_compare(PHP_VERSION, '5.1.2', '>=') ) {
+ return parse_url( $url, PHP_URL_HOST );
+ } else {
+ $url_parts = parse_url( $url );
+ if ( $url_parts !== false && isset( $url_parts['host'] ) )
+ return $url_parts['host'];
+ }
+ } catch (Exception $e){}
+ return false;
+ }
+
+
+ /**
+ * Request data from WordPress.com for the given guid, maxwidth, and calculated blog hostname.
+ *
+ * @since 1.3
+ * @return stdClass|WP_Error parsed JSON response or WP_Error if request unsuccessful
+ */
+ private function get_data() {
+ global $wp_version;
+
+ $domain = self::hostname( home_url() );
+ $request_params = array( 'guid' => $this->guid, 'domain' => $domain );
+ if ( isset( $this->maxwidth ) && $this->maxwidth > 0 )
+ $request_params['maxwidth'] = $this->maxwidth;
+
+ $url = 'http://videopress.com/data/wordpress.json';
+ if ( is_ssl() )
+ $url = 'https://v.wordpress.com/data/wordpress.json';
+
+ $response = wp_remote_get( $url . '?' . http_build_query( $request_params, null, '&' ), array(
+ 'httpversion' => '1.1',
+ 'redirection' => 1,
+ 'user-agent' => 'VideoPress plugin ' . VideoPress::version . '; WordPress ' . $wp_version . ' (' . home_url('/') . ')'
+ ) );
+ unset( $request_params );
+ unset( $url );
+ $response_body = wp_remote_retrieve_body( $response );
+ $response_code = absint( wp_remote_retrieve_response_code( $response ) );
+
+ if ( is_wp_error( $response ) ) {
+ return $response;
+ } elseif ( $response_code === 400 ) {
+ return new WP_Error( 'bad_config', __( 'The VideoPress plugin could not communicate with the VideoPress servers. This error is most likely caused by a misconfigured plugin. Please reinstall or upgrade.', 'jetpack' ) );
+ } elseif ( $response_code === 403 ) {
+ return new WP_Error( 'http_forbidden', '<p>' . sprintf( __( '<strong>%s</strong> is not an allowed embed site.' , 'jetpack' ), esc_html( $domain ) ) . '</p><p>' . __( 'Publisher limits playback of video embeds.', 'jetpack' ) . '</p>' );
+ } elseif ( $response_code === 404 ) {
+ return new WP_Error( 'http_not_found', '<p>' . sprintf( __( 'No data found for VideoPress identifier: <strong>%s</strong>.', 'jetpack' ), $this->guid ) . '</p>' );
+ } elseif ( $response_code !== 200 || empty( $response_body ) ) {
+ return;
+ } else {
+ $expires_header = wp_remote_retrieve_header( $response, 'Expires' );
+ if ( ! empty( $expires_header ) ) {
+ $expires = self::calculate_expiration( $expires_header );
+ if ( ! empty( $expires ) )
+ $this->expires = $expires;
+ }
+ return json_decode( $response_body );
+ }
+ }
+}
+
+/**
+ * VideoPress playback module markup generator.
+ *
+ * @since 1.3
+ */
+class VideoPress_Player {
+ /**
+ * Video data for the requested guid and maximum width
+ *
+ * @since 1.3
+ * @var VideoPress_Video
+ */
+ protected $video;
+
+ /**
+ * DOM identifier of the video container
+ *
+ * @var string
+ * @since 1.3
+ */
+ protected $video_container_id;
+
+ /**
+ * DOM identifier of the video element (video, object, embed)
+ *
+ * @var string
+ * @since 1.3
+ */
+ protected $video_id;
+
+ /**
+ * Array of playback options: force_flash or freedom
+ *
+ * @var array
+ * @since 1.3
+ */
+ protected $options;
+
+ /**
+ * Initiate a player object based on shortcode values and possible blog-level option overrides
+ *
+ * @since 1.3
+ * @var string $guid VideoPress unique identifier
+ * @var int $maxwidth maximum desired width of the video player if specified
+ * @var array $options player customizations
+ */
+ public function __construct( $guid, $maxwidth = 0, $options = array() ) {
+ global $videopress;
+ $this->video_container_id = 'v-' . $guid . '-' . $videopress->shown[$guid];
+ $this->video_id = $this->video_container_id . '-video';
+
+ if ( is_array( $options ) )
+ $this->options = $options;
+ else
+ $this->options = array();
+
+ // set up the video
+ $cache_key = null;
+
+ // disable cache in debug mode
+ if ( defined('WP_DEBUG') && WP_DEBUG === true ) {
+ $cached_video = null;
+ } else {
+ $cache_key_pieces = array( 'video' );
+ if ( is_multisite() && is_subdomain_install() ) {
+ /**
+ * Compatibility wrapper for less than WordPress 3.1
+ *
+ * @todo remove when targeting WordPress 3.2 or above.
+ */
+ if ( function_exists( 'get_current_blog_id' ) )
+ $cache_key_pieces[] = get_current_blog_id();
+ elseif ( isset( $GLOBALS ) && isset( $GLOBALS['blog_id'] ) )
+ $cache_key_pieces[] = absint( $GLOBALS['blog_id'] );
+ else
+ $cache_key_pieces[] = 1;
+ }
+ $cache_key_pieces[] = $guid;
+ if ( $width > 0 )
+ $cache_key_pieces[] = $maxwidth;
+ if ( is_ssl() )
+ $cache_key_pieces[] = 'ssl';
+ $cache_key = implode( '-', $cache_key_pieces );
+ unset( $cache_key_pieces );
+ $cached_video = wp_cache_get( $cache_key, 'video' );
+ }
+ if ( empty( $cached_video ) ) {
+ $video = new VideoPress_Video( $guid, $maxwidth );
+ if ( empty( $video ) ) {
+ return;
+ } elseif ( isset( $video->error ) ) {
+ $this->video = $video->error;
+ return;
+ } elseif ( is_wp_error( $video ) ) {
+ $this->video = $video;
+ return;
+ }
+
+ $this->video = $video;
+ unset( $video );
+
+ if ( ! defined( 'WP_DEBUG' ) || WP_DEBUG !== true ) {
+ $expire = 3600;
+ if ( isset( $video->expires ) && is_int( $video->expires ) ) {
+ $expires_diff = time() - $video->expires;
+ if ( $expires_diff > 0 && $expires_diff < 86400 ) // allowed range: 1 second to 1 day
+ $expire = $expires_diff;
+ unset( $expires_diff );
+ }
+
+ wp_cache_set( $cache_key, serialize($this->video), 'video', $expire );
+ unset( $expire );
+ }
+ } else {
+ $this->video = unserialize( $cached_video );
+ }
+ unset( $cache_key );
+ unset( $cached_video );
+ }
+
+ /**
+ * PHP 4 constructor compatibility
+ *
+ * @since 1.5
+ * @todo remove when targeting PHP 5 (WordPress 3.2 min requirement) or above.
+ */
+ public function VideoPress_Player( $guid, $maxwidth = 0, $options = array() ) {
+ $this->__construct( $guid, $maxwidth, $options );
+ }
+
+ /**
+ * Wrap output in a VideoPress player container
+ *
+ * @since 1.3
+ * @var string $content HTML string
+ * @return string HTML string or blank string if nothing to wrap
+ */
+ private function html_wrapper( $content ) {
+ if ( empty( $content ) )
+ return '';
+ else
+ return '<div id="' . esc_attr( $this->video_container_id ) . '" class="video-player">' . $content . '</div>';
+ }
+
+ /**
+ * Output content suitable for a feed reader displaying RSS or Atom feeds
+ * We do not display error messages in the feed view due to caching concerns.
+ * Flash content presented using <embed> markup for feed reader compatibility.
+ *
+ * @since 1.3
+ * @return string HTML string or empty string if error
+ */
+ public function asXML() {
+ if ( empty( $this->video ) || is_wp_error( $this->video ) )
+ return '';
+
+ if ( isset( $this->options['freedom'] ) && $this->options['freedom'] === true )
+ $content = $this->html5_static();
+ else
+ $content = $this->flash_embed();
+
+ return $this->html_wrapper( $content );
+ }
+
+ /**
+ * Video player markup for best matching the current request and publisher options
+ * @since 1.3
+ * @return string HTML markup string or empty string if no video property found
+ */
+ public function asHTML() {
+ if ( empty( $this->video ) ) {
+ $content = '';
+ } elseif ( is_wp_error( $this->video ) ) {
+ $content = $this->error_message( $this->video );
+ } elseif ( ( isset( $this->video->restricted_embed ) && $this->video->restricted_embed === true ) || ( isset( $this->options['force_flash'] ) && $this->options['force_flash'] === true ) ) {
+ $content = $this->flash_object();
+ } elseif ( isset( $this->options['freedom'] ) && $this->options['freedom'] === true ) {
+ $content = $this->html5_static();
+ } elseif ( ! in_the_loop() ) {
+ $content = $this->flash_object();
+ } else {
+ $content = $this->html5_dynamic();
+ }
+ return $this->html_wrapper( $content );
+ }
+
+ /**
+ * Display an error message to users capable of doing something about the error
+ *
+ * @since 1.3
+ * @uses current_user_can() to test if current user has edit_posts capability
+ * @var WP_Error $error WordPress error
+ * @return string HTML string
+ */
+ private function error_message( $error ) {
+ if ( ! current_user_can( 'edit_posts' ) || empty( $error ) )
+ return '';
+
+ $html = '<div class="videopress-error" style="background-color:rgb(255,0,0);color:rgb(255,255,255);font-family:font-family:\'Helvetica Neue\',Arial,Helvetica,\'Nimbus Sans L\',sans-serif;font-size:140%;min-height:10em;padding-top:1.5em;padding-bottom:1.5em">';
+ $html .= '<h1 style="font-size:180%;font-style:bold;line-height:130%;text-decoration:underline">' . esc_html( sprintf( __( '%s Error', 'jetpack' ), 'VideoPress' ) ) . '</h1>';
+ foreach( $error->get_error_messages() as $message ) {
+ $html .= $message;
+ }
+ $html .= '</div>';
+ return $html;
+ }
+
+ /**
+ * Rating agencies and industry associations require a potential viewer verify his or her age before a video or its poster frame are displayed.
+ * Content rated for audiences 17 years of age or older requires such verification across multiple rating agencies and industry associations
+ *
+ * @since 1.3
+ * @return bool true if video requires the viewer verify he or she is 17 years of age or older
+ */
+ private function age_gate_required() {
+ if ( isset( $this->video->age_rating ) && $this->video->age_rating >= 17 )
+ return true;
+ else
+ return false;
+ }
+
+ /**
+ * Select a date of birth using HTML form elements.
+ *
+ * @since 1.5
+ * @return string HTML markup
+ */
+ private function html_age_gate() {
+ $text_align = 'left';
+ if ( $this->video->text_direction === 'rtl' )
+ $text_align = 'right';
+
+ $html = '<div class="videopress-age-gate" style="margin:0 60px">';
+ $html .= '<p class="instructions" style="color:rgb(255, 255, 255);font-size:21px;padding-top:60px;padding-bottom:20px;text-align:' . $text_align . '">' . esc_html( __( 'This video is intended for mature audiences.', 'jetpack' ) ) . '<br />' . esc_html( __( 'Please verify your birthday.', 'jetpack' ) ) . '</p>';
+ $html .= '<fieldset id="birthday" style="border:0 none;text-align:' . $text_align . ';padding:0;">';
+ $inputs_style = 'border:1px solid #444;margin-';
+ if ( $this->video->text_direction === 'rtl' )
+ $inputs_style .= 'left';
+ else
+ $inputs_style .= 'right';
+ $inputs_style .= ':10px;background-color:rgb(0, 0, 0);font-size:14px;color:rgb(255,255,255);padding:4px 6px;line-height: 2em;vertical-align: middle';
+
+ /**
+ * Display a list of months in the Gregorian calendar.
+ * Set values to 0-based to match JavaScript Date.
+ * @link https://developer.mozilla.org/en/JavaScript/Reference/global_objects/date Mozilla JavaScript Reference: Date
+ */
+ $html .= '<select name="month" style="' . $inputs_style . '">';
+
+ $months = array( __('January', 'jetpack'), __('February', 'jetpack'), __('March', 'jetpack'), __('April', 'jetpack'), __('May', 'jetpack'), __('June', 'jetpack'), __('July', 'jetpack'), __('August', 'jetpack'), __('September', 'jetpack'), __('October', 'jetpack'), __('November', 'jetpack'), __('December', 'jetpack') );
+ for( $i=0; $i<12; $i++ ) {
+ $html .= '<option value="' . esc_attr( $i ) . '">' . esc_html( $months[$i] ) . '</option>';
+ }
+ $html .= '</select>';
+ unset( $months );
+
+ /**
+ * todo: numdays variance by month
+ */
+ $html .= '<select name="day" style="' . $inputs_style . '">';
+ for ( $i=1; $i<32; $i++ ) {
+ $html .= '<option>' . $i . '</option>';
+ }
+ $html .= '</select>';
+
+ /**
+ * Current record for human life is 122. Go back 130 years and no one is left out.
+ * Don't ask infants younger than 2 for their birthday
+ * Default to 13
+ */
+ $html .= '<select name="year" style="' . $inputs_style . '">';
+ $start_year = date('Y') - 2;
+ $default_year = $start_year - 11;
+ $end_year = $start_year - 128;
+ for ( $year=$start_year; $year>$end_year; $year-- ) {
+ $html .= '<option';
+ if ( $year === $default_year )
+ $html .= ' selected="selected"';
+ $html .= '>' . $year . '</option>';
+ }
+ unset( $start_year );
+ unset( $default_year );
+ unset( $end_year );
+ $html .= '</select>';
+
+ $html .= '<input type="submit" value="' . __( 'Submit', 'jetpack' ) . '" style="cursor:pointer;border-radius: 1em;border:1px solid #333;background-color:#333;background:-webkit-gradient( linear, left top, left bottom, color-stop(0.0, #444), color-stop(1, #111) );background:-moz-linear-gradient(center top, #444 0%, #111 100%);font-size:13px;padding:4px 10px 5px;line-height:1em;vertical-align:top;color:white;text-decoration:none;margin:0" />';
+
+ $html .= '</fieldset>';
+ $html .= '<p style="padding-top:20px;padding-bottom:60px;text-align:' . $text_align . ';"><a rel="nofollow" href="http://videopress.com/" style="color:rgb(128,128,128);text-decoration:underline;font-size:15px">' . __( 'More information', 'jetpack' ) . '</a></p>';
+
+ $html .= '</div>';
+ return $html;
+ }
+
+ /**
+ * Return HTML5 video static markup for the given video parameters.
+ * Use default browser player controls.
+ * No Flash fallback.
+ *
+ * @since 1.2
+ * @link http://www.whatwg.org/specs/web-apps/current-work/multipage/video.html HTML5 video
+ * @return string HTML5 video element and children
+ */
+ private function html5_static() {
+ $thumbnail = esc_url( $this->video->poster_frame_uri );
+ $html = "<video id=\"{$this->video_id}\" width=\"{$this->video->calculated_width}\" height=\"{$this->video->calculated_height}\" poster=\"$thumbnail\" controls=\"true\"";
+ if ( isset( $this->options['autoplay'] ) && $this->options['autoplay'] === true )
+ $html .= ' autoplay="true"';
+ else
+ $html .= ' preload="metadata"';
+ if ( isset( $this->video->text_direction ) )
+ $html .= ' dir="' . esc_attr( $this->video->text_direction ) . '"';
+ if ( isset( $this->video->language ) )
+ $html .= ' lang="' . esc_attr( $this->video->language ) . '"';
+ $html .= '>';
+ if ( ! isset( $this->options['freedom'] ) || $this->options['freedom'] === false ) {
+ $mp4 = $this->video->videos->mp4->url;
+ if ( ! empty( $mp4 ) )
+ $html .= '<source src="' . esc_url( $mp4 ) . '" type="video/mp4; codecs=&quot;' . esc_attr( $this->video->videos->mp4->codecs ) . '&quot;" />';
+ unset( $mp4 );
+ }
+ $ogg = $this->video->videos->ogv->url;
+ if ( ! empty( $ogg ) )
+ $html .= '<source src="' . esc_url( $ogg ) . '" type="video/ogg; codecs=&quot;' . esc_attr( $this->video->videos->ogv->codecs ) . '&quot;" />';
+ unset( $ogg );
+
+ $html .= '<div><img alt="';
+ if ( isset( $this->video->title ) )
+ $html .= esc_attr( $this->video->title );
+ $html .= '" src="' . $thumbnail . '" width="' . $this->video->calculated_width . '" height="' . $this->video->calculated_height . '" /></div>';
+ if ( isset( $this->options['freedom'] ) && $this->options['freedom'] === true )
+ $html .= '<p class="robots-nocontent">' . sprintf( __( 'You do not have sufficient <a rel="nofollow" href="%s">freedom levels</a> to view this video. Support free software and upgrade.', 'jetpack' ), 'http://www.gnu.org/philosophy/free-sw.html' ) . '</p>';
+ elseif ( isset( $this->video->title ) )
+ $html .= '<p>' . esc_html( $this->video->title ) . '</p>';
+ $html .= '</video>';
+ return $html;
+ }
+
+ /**
+ * Click to play dynamic HTML5-capable player.
+ * The player displays a video preview section including poster frame, video title, play button and watermark on the original page load and calculates the playback capabilities of the browser. The video player is loaded when the visitor clicks on the video preview area.
+ * If Flash Player 10 or above is available the browser will display the Flash version of the video. If HTML5 video appears to be supported and the browser may be capable of MP4 (H.264, AAC) or OGV (Theora, Vorbis) playback the browser will display its native HTML5 player.
+ *
+ * @since 1.5
+ * @return string HTML markup
+ */
+ private function html5_dynamic() {
+ global $videopress;
+
+ $video_placeholder_id = $this->video_container_id . '-placeholder';
+ $age_gate_required = $this->age_gate_required();
+ $width = absint( $this->video->calculated_width );
+ $height = absint( $this->video->calculated_height );
+
+ $html = '<div id="' . $video_placeholder_id . '" class="videopress-placeholder" style="';
+ if ( $age_gate_required )
+ $html .= "min-width:{$width}px;min-height:{$height}px";
+ else
+ $html .= "width:{$width}px;height:{$height}px";
+ $html .= ';display:none;cursor:pointer !important;position:relative;';
+ if ( isset( $this->video->skin ) && isset( $this->video->skin->background_color ) )
+ $html .= 'background-color:' . esc_attr( $this->video->skin->background_color ) . ';';
+ $html .= 'font-family: \'Helvetica Neue\',Arial,Helvetica,\'Nimbus Sans L\',sans-serif;font-weight:bold;font-size:18px">' . PHP_EOL;
+
+ /**
+ * Do not display a poster frame, title, or any other content hints for mature content.
+ */
+ if ( ! $age_gate_required ) {
+ if ( ! empty( $this->video->title ) ) {
+ $html .= '<div class="videopress-title" style="display:inline;position:absolute;margin:20px 20px 0 20px;padding:4px 8px;vertical-align:top;text-align:';
+ if ( $this->video->text_direction === 'rtl' )
+ $html .= 'right" dir="rtl"';
+ else
+ $html .= 'left" dir="ltr"';
+ if ( isset( $this->video->language ) )
+ $html .= ' lang="' . esc_attr( $this->video->language ) . '"';
+ $html .= '><span style="padding:3px 0;line-height:1.5em;';
+ if ( isset( $this->video->skin ) && isset( $this->video->skin->background_color ) ) {
+ $html .= 'background-color:';
+ if ( $this->video->skin->background_color === 'rgb(0,0,0)' )
+ $html .= 'rgba(0,0,0,0.8)';
+ else
+ $html .= esc_attr( $this->video->skin->background_color );
+ $html .= ';';
+ }
+ $html .= 'color:rgb(255,255,255)">' . esc_html( $this->video->title ) . '</span></div>';
+ }
+ $html .= '<img class="videopress-poster" alt="';
+ if ( ! empty( $this->video->title ) )
+ $html .= esc_attr( $this->video->title ) . '" title="' . esc_attr( sprintf( _x( 'Watch: %s', 'watch a video title', 'jetpack' ), $this->video->title ) );
+ $html .= '" src="' . esc_url( $this->video->poster_frame_uri, array( 'http', 'https' ) ) . '" width=' . $width . '" height="' . $height . '" />' . PHP_EOL;
+
+ //style a play button hovered over the poster frame
+ $html .= '<div class="play-button"><span style="z-index:2;display:block;position:absolute;top:50%;left:50%;text-align:center;vertical-align:middle;color:rgb(255,255,255);opacity:0.9;margin:0 0 0 -0.45em;padding:0;line-height:0;font-size:500%;text-shadow:0 0 40px rgba(0,0,0,0.5)">&#9654;</span></div>' . PHP_EOL;
+
+ // watermark
+ if ( isset( $this->video->skin ) && isset( $this->video->skin->watermark ) ) {
+ $html .= '<div style="position:relative;margin-top:-40px;height:25px;margin-bottom:35px;';
+ if ( $this->video->text_direction === 'rtl' )
+ $html .= 'margin-left:20px;text-align:left;';
+ else
+ $html .= 'margin-right:20px;text-align:right;';
+ $html .= 'vertical-align:bottom;z-index:3">';
+ $html .= '<img alt="" src="' . esc_url( $this->video->skin->watermark, array( 'http', 'https' ) ) . '" width="90" height="13" style="background-color:transparent;background-image:none;background-repeat:no-repeat;border:none;margin:0;padding:0"/>';
+ $html .= '</div>' . PHP_EOL;
+ }
+ }
+
+ $data = array(
+ 'blog' => absint( $this->video->blog_id ),
+ 'post' => absint( $this->video->post_id ),
+ 'duration'=> absint( $this->video->duration ),
+ 'poster' => esc_url_raw( $this->video->poster_frame_uri, array( 'http', 'https' ) )
+ );
+ if ( isset( $this->video->videos ) ) {
+ if ( isset( $this->video->videos->mp4 ) && isset( $this->video->videos->mp4->url ) )
+ $data['mp4'] = array( 'size' => $this->video->videos->mp4->format, 'uri' => esc_url_raw( $this->video->videos->mp4->url, array( 'http', 'https' ) ) );
+ if ( isset( $this->video->videos->ogv ) && isset( $this->video->videos->ogv->url ) )
+ $data['ogv'] = array( 'size' => 'std', 'uri' => esc_url_raw( $this->video->videos->ogv->url, array( 'http', 'https' ) ) );
+ }
+ $locale = array( 'dir' => $this->video->text_direction );
+ if ( isset( $this->video->language ) )
+ $locale['lang'] = $this->video->language;
+ $data['locale'] = $locale;
+ unset( $locale );
+
+ $guid = $this->video->guid;
+ $guid_js = json_encode( $guid );
+ $html .= '<script type="text/javascript">' . PHP_EOL;
+
+ // Only need to wait until document is ready if the JS is being loaded in the footer
+ if ( ! $videopress->js_loaded )
+ $html .= 'jQuery(document).ready(function() {';
+
+ $html .= 'if ( !jQuery.VideoPress.data[' . json_encode($guid) . '] ) { jQuery.VideoPress.data[' . json_encode($guid) . '] = new Array(); }' . PHP_EOL;
+ $html .= 'jQuery.VideoPress.data[' . json_encode( $guid ) . '][' . $videopress->shown[$guid] . ']=' . json_encode($data) . ';' . PHP_EOL;
+ unset( $data );
+
+ $jq_container = json_encode( '#' . $this->video_container_id );
+ $jq_placeholder = json_encode( '#' . $video_placeholder_id );
+ $player_config = "{width:{$width},height:{$height},";
+ if ( isset( $this->options['freedom'] ) && $this->options['freedom'] === true )
+ $player_config .= 'freedom:"true",';
+ $player_config .= 'container:jQuery(' . $jq_container . ')}';
+
+ $html .= "jQuery({$jq_placeholder}).show(0,function(){jQuery.VideoPress.analytics.impression({$guid_js})});" . PHP_EOL;
+
+ if ( $age_gate_required ) {
+ $html .= 'if ( jQuery.VideoPress.support.flash() ) {' . PHP_EOL;
+ /**
+ * @link http://code.google.com/p/swfobject/wiki/api#swfobject.embedSWF(swfUrlStr,_replaceElemIdStr,_widthStr,_height
+ */
+ $html .= 'swfobject.embedSWF(' . implode( ',', array(
+ 'jQuery.VideoPress.video.flash.player_uri',
+ json_encode( $this->video_container_id ),
+ json_encode( $width ),
+ json_encode( $height ),
+ 'jQuery.VideoPress.video.flash.min_version',
+ 'jQuery.VideoPress.video.flash.expressinstall', // attempt to upgrade the Flash player if less than min_version. requires a 310x137 container or larger but we will always try to include
+ '{guid:' . $guid_js . '}', // FlashVars
+ 'jQuery.VideoPress.video.flash.params',
+ 'null', // no attributes
+ 'jQuery.VideoPress.video.flash.embedCallback' // error fallback
+ ) ) . ');';
+ $html .= '} else {' . PHP_EOL;
+ $html .= "if ( jQuery.VideoPress.video.prepare({$guid_js},{$player_config}," . $videopress->shown[$guid] . ') ) {' . PHP_EOL;
+ $html .= 'if ( jQuery(' . $jq_container . ').data( "player" ) === "flash" ){jQuery.VideoPress.video.play(jQuery(' . json_encode('#' . $this->video_container_id) . '));}else{';
+ $html .= 'jQuery(' . $jq_placeholder . ').html(' . json_encode( $this->html_age_date() ) . ');' . PHP_EOL;
+ $html .= 'jQuery(' . json_encode( '#' . $video_placeholder_id . ' input[type=submit]' ) . ').one("click", function(event){jQuery.VideoPress.requirements.isSufficientAge(jQuery(' . $jq_container . '),' . absint( $this->video->age_rating ) . ')});' . PHP_EOL;
+ $html .= '}}}' . PHP_EOL;
+ } else {
+ $html .= "if ( jQuery.VideoPress.video.prepare({$guid_js}, {$player_config}," . $videopress->shown[$guid] . ') ) {' . PHP_EOL;
+ if ( isset( $this->options['autoplay'] ) && $this->options['autoplay'] === true )
+ $html .= "jQuery.VideoPress.video.play(jQuery({$jq_container}));";
+ else
+ $html .= 'jQuery(' . $jq_placeholder . ').one("click",function(){jQuery.VideoPress.video.play(jQuery(' . $jq_container . '))});';
+ $html .= '}';
+
+ // close the jQuery(document).ready() function
+ if ( !$videopress->js_loaded )
+ $html .= '});';
+ }
+ $html .= '</script>' . PHP_EOL;
+ $html .= '</div>' . PHP_EOL;
+
+ /*
+ * JavaScript required
+ */
+ $noun = __( 'this video', 'jetpack' );
+ if ( ! $age_gate_required ) {
+ $vid_type = '';
+ if ( ( isset( $this->options['freedom'] ) && $this->options['freedom'] === true ) && ( isset( $this->video->videos->ogv ) && isset( $this->video->videos->ogv->url ) ) )
+ $vid_type = 'ogv';
+ elseif ( isset( $this->video->videos->mp4 ) && isset( $this->video->videos->mp4->url ) )
+ $vid_type = 'mp4';
+ elseif ( isset( $this->video->videos->ogv ) && isset( $this->video->videos->ogv->url ) )
+ $vid_type = 'ogv';
+
+ if ( $vid_type !== '' ) {
+ $noun = '<a ';
+ if ( isset( $this->video->language ) )
+ $noun .= 'hreflang="' . esc_attr( $this->video->language ) . '" ';
+ if ( $vid_type === 'mp4' )
+ $noun .= 'type="video/mp4" href="' . esc_url( $this->video->videos->mp4->url, array( 'http', 'https' ) );
+ elseif ( $vid_type === 'ogv' )
+ $noun .= 'type="video/ogv" href="' . esc_url( $this->video->videos->ogv->url, array( 'http', 'https' ) );
+ $noun .= '">';
+ if ( isset( $this->video->title ) )
+ $noun .= esc_html( $this->video->title );
+ else
+ $noun .= __( 'this video', 'jetpack' );
+ $noun .= '</a>';
+ } elseif ( ! empty( $this->title ) ) {
+ $noun = esc_html( $this->title );
+ }
+ unset( $vid_type );
+ }
+ $html .= '<noscript><p>' . sprintf( _x( 'JavaScript required to play %s.', 'Play as in playback or view a movie', 'jetpack' ), $noun ) . '</p></noscript>';
+
+ return $html;
+ }
+
+ /**
+ * Only allow legitimate Flash parameters and their values
+ *
+ * @since 1.2
+ * @link http://kb2.adobe.com/cps/127/tn_12701.html Flash object and embed attributes
+ * @link http://kb2.adobe.com/cps/133/tn_13331.html devicefont
+ * @link http://kb2.adobe.com/cps/164/tn_16494.html allowscriptaccess
+ * @link http://www.adobe.com/devnet/flashplayer/articles/full_screen_mode.html full screen mode
+ * @link http://livedocs.adobe.com/flash/9.0/main/wwhelp/wwhimpl/common/html/wwhelp.htm?context=LiveDocs_Parts&file=00001079.html allownetworking
+ * @param array $flash_params Flash parameters expressed in key-value form
+ * @return array validated Flash parameters
+ */
+ public static function esc_flash_params( $flash_params ) {
+ $allowed_params = array(
+ 'swliveconnect' => array('true', 'false'),
+ 'play' => array('true', 'false'),
+ 'loop' => array('true', 'false'),
+ 'menu' => array('true', 'false'),
+ 'quality' => array('low', 'autolow', 'autohigh', 'medium', 'high', 'best'),
+ 'scale' => array('default', 'noborder', 'exactfit', 'noscale'),
+ 'align' => array('l', 'r', 't'),
+ 'salign' => array('l', 'r', 't', 'tl', 'tr', 'bl', 'br'),
+ 'wmode' => array('window', 'opaque', 'transparent','direct','gpu'),
+ 'devicefont' => array('_sans', '_serif', '_typewriter'),
+ 'allowscriptaccess' => array('always', 'samedomain', 'never'),
+ 'allownetworking' => array('all','internal', 'none'),
+ 'seamlesstabbing' => array('true', 'false'),
+ 'allowfullscreen' => array('true', 'false'),
+ 'fullScreenAspectRatio' => array('portrait', 'landscape'),
+ 'base',
+ 'bgcolor',
+ 'flashvars'
+ );
+
+ $allowed_params_keys = array_keys( $allowed_params );
+
+ $filtered_params = array();
+ foreach( $flash_params as $param=>$value ) {
+ if ( empty($param) || empty($value) )
+ continue;
+ $param = strtolower($param);
+ if ( in_array($param, $allowed_params_keys) ) {
+ if ( isset( $allowed_params[$param] ) && is_array( $allowed_params[$param] ) ) {
+ $value = strtolower($value);
+ if ( in_array( $value, $allowed_params[$param] ) )
+ $filtered_params[$param] = $value;
+ } else {
+ $filtered_params[$param] = $value;
+ }
+ }
+ }
+ unset( $allowed_params_keys );
+
+ /**
+ * Flash specifies sameDomain, not samedomain. change from lowercase value for preciseness
+ */
+ if ( isset( $filtered_params['allowscriptaccess'] ) && $filtered_params['allowscriptaccess'] === 'samedomain' )
+ $filtered_params['allowscriptaccess'] = 'sameDomain';
+
+ return $filtered_params;
+ }
+
+ /**
+ * Filter Flash variables from the response, taking into consideration player options.
+ *
+ * @since 1.3
+ * @return array Flash variable key value pairs
+ */
+ private function get_flash_variables() {
+ if ( ! isset( $this->video->players->swf->vars ) )
+ return array();
+
+ $flashvars = (array) $this->video->players->swf->vars;
+ if ( isset( $this->options['autoplay'] ) && $this->options['autoplay'] === true )
+ $flashvars['autoPlay'] = 'true';
+ return $flashvars;
+ }
+
+ /**
+ * Validate and filter Flash parameters
+ *
+ * @since 1.3
+ * @return array Flash parameters passed through key and value validation
+ */
+ private function get_flash_parameters() {
+ if ( ! isset( $this->video->players->swf->params ) )
+ return array();
+ else
+ return self::esc_flash_params( apply_filters( 'video_flash_params', (array) $this->video->players->swf->params, 10, 1 ) );
+ }
+
+ /**
+ * Flash player markup in a HTML embed element.
+ *
+ * @since 1.1
+ * @link http://www.whatwg.org/specs/web-apps/current-work/multipage/the-iframe-element.html#the-embed-element embed element
+ * @link http://www.google.com/support/reader/bin/answer.py?answer=70664 Google Reader markup support
+ * @return string HTML markup. Embed element with no children
+ */
+ private function flash_embed() {
+ if ( ! isset( $this->video->players->swf ) || ! isset( $this->video->players->swf->url ) )
+ return '';
+
+ $embed = array(
+ 'id' => $this->video_id,
+ 'src' => esc_url_raw( $this->video->players->swf->url . '&' . http_build_query( $this->get_flash_variables(), null, '&' ) , array( 'http', 'https' ) ),
+ 'type' => 'application/x-shockwave-flash',
+ 'width' => $this->video->calculated_width,
+ 'height' => $this->video->calculated_height
+ );
+ if ( isset( $this->video->title ) )
+ $embed['title'] = $this->video->title;
+ $embed = array_merge( $embed, $this->get_flash_parameters() );
+
+ $html = '<embed';
+ foreach ( $embed as $attribute => $value ) {
+ $html .= ' ' . esc_html( $attribute ) . '="' . esc_attr( $value ) . '"';
+ }
+ unset( $embed );
+ $html .= '></embed>';
+ return $html;
+ }
+
+ /**
+ * Double-baked Flash object markup for Internet Explorer and more standards-friendly consuming agents.
+ *
+ * @since 1.1
+ * @return HTML markup. Object and children.
+ */
+ private function flash_object() {
+ if ( ! isset( $this->video->players->swf ) || ! isset( $this->video->players->swf->url ) )
+ return '';
+
+ $thumbnail_html = '<img alt="';
+ if ( isset( $this->video->title ) )
+ $thumbnail_html .= esc_attr( $this->video->title );
+ $thumbnail_html .= '" src="' . esc_url( $this->video->poster_frame_uri, array( 'http', 'https' ) ) . '" width="' . $this->video->calculated_width . '" height="' . $this->video->calculated_height . '" />';
+ $flash_vars = esc_attr( http_build_query( $this->get_flash_variables(), null, '&' ) );
+ $flash_params = '';
+ foreach ( $this->get_flash_parameters() as $attribute => $value ) {
+ $flash_params .= '<param name="' . esc_attr( $attribute ) . '" value="' . esc_attr( $value ) . '" />';
+ }
+ $flash_help = sprintf( __( 'This video requires <a rel="nofollow" href="%s">Adobe Flash</a> for playback.', 'jetpack' ), 'http://www.adobe.com/go/getflashplayer');
+ $flash_player_url = esc_url( $this->video->players->swf->url, array( 'http', 'https' ) );
+ $description = '';
+ if ( isset( $this->video->title ) ) {
+ $standby = $this->video->title;
+ $description = '<p><strong>' . esc_html( $this->video->title ) . '</strong></p>';
+ } else {
+ $standby = __( 'Loading video...', 'jetpack' );
+ }
+ $standby = ' standby="' . esc_attr( $standby ) . '"';
+ return <<<OBJECT
+<script type="text/javascript">if(typeof swfobject!=="undefined"){swfobject.registerObject("{$this->video_id}", "{$this->video->players->swf->version}");}</script>
+<object classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000" width="{$this->video->calculated_width}" height="{$this->video->calculated_height}" id="{$this->video_id}"{$standby}>
+ <param name="movie" value="{$flash_player_url}" />
+ {$flash_params}
+ <param name="flashvars" value="{$flash_vars}" />
+ <!--[if !IE]>-->
+ <object type="application/x-shockwave-flash" data="{$flash_player_url}" width="{$this->video->calculated_width}" height="{$this->video->calculated_height}"{$standby}>
+ {$flash_params}
+ <param name="flashvars" value="{$flash_vars}" />
+ <!--<![endif]-->
+ {$thumbnail_html}{$description}<p class="robots-nocontent">{$flash_help}</p>
+ <!--[if !IE]>-->
+ </object>
+ <!--<![endif]-->
+</object>
+OBJECT;
+ }
+}
+
+global $videopress;
+$videopress = new VideoPress();
+
+endif;
+?>
diff --git a/plugins/jetpack/modules/shortcodes/vimeo.php b/plugins/jetpack/modules/shortcodes/vimeo.php
new file mode 100644
index 00000000..2225a5fe
--- /dev/null
+++ b/plugins/jetpack/modules/shortcodes/vimeo.php
@@ -0,0 +1,109 @@
+<?php
+
+/*
+[vimeo 141358]
+[vimeo http://vimeo.com/141358]
+[vimeo 141358 h=500&w=350]
+[vimeo id=141358 width=350 height=500]
+
+<iframe src="http://player.vimeo.com/video/18427511" width="400" height="225" frameborder="0"></iframe><p><a href="http://vimeo.com/18427511">Eskmo 'We Got More' (Official Video)</a> from <a href="http://vimeo.com/ninjatune">Ninja Tune</a> on <a href="http://vimeo.com">Vimeo</a>.</p>
+*/
+
+/**
+ * Convert a Vimeo shortcode into an embed code.
+ *
+ * @param array $atts An array of shortcode attributes.
+ * @return string The embed code for the Vimeo video.
+ */
+function vimeo_shortcode( $atts ) {
+ global $content_width;
+
+ extract( array_map( 'intval', shortcode_atts( array(
+ 'id' => 0,
+ 'width' => 400,
+ 'height' => 300
+ ), $atts ) ) );
+
+ if ( isset( $atts[0] ) ) {
+ $atts[0] = trim( $atts[0] , '=' );
+ if ( is_numeric( $atts[0] ) )
+ $id = (int) $atts[0];
+ elseif ( preg_match( '|vimeo\.com/(\d+)/?$|i', $atts[0], $match ) )
+ $id = (int) $match[1];
+ }
+
+ // [vimeo 141358 h=500&w=350]
+ $params = shortcode_new_to_old_params( $atts ); // h=500&w=350
+ $params = str_replace( array( '&amp;', '&#038;' ), '&', $params );
+ parse_str( $params, $args );
+
+ if ( isset( $args['w'] ) ) {
+ $width = (int) $args['w'];
+
+ if ( ! isset( $args['h'] ) ) {
+ // The case where w=300 is specified without h=200, otherwise $height
+ // will always equal the default of 300, no matter what w was set to.
+ $height = round( ( $width / 640 ) * 360 );
+ }
+ }
+
+ if ( isset( $args['h'] ) ) {
+ $height = (int) $args['h'];
+
+ if ( ! isset( $args['w'] ) ) {
+ $width = round( ( $height / 360 ) * 640 );
+ }
+ }
+
+ if ( ! $width )
+ $width = absint( $content_width );
+
+ if ( ! $height )
+ $height = round( ( $width / 640 ) * 360 );
+
+ if ( ! $id ) return "<!-- vimeo error: not a vimeo video -->";
+
+ $html = "<div class='embed-vimeo' style='text-align:center;'><iframe src='http://player.vimeo.com/video/$id' width='$width' height='$height' frameborder='0'></iframe></div>";
+ $html = apply_filters( 'video_embed_html', $html );
+ return $html;
+}
+
+add_shortcode( 'vimeo', 'vimeo_shortcode' );
+
+function vimeo_embed_to_shortcode( $content ) {
+ if ( false === stripos( $content, 'player.vimeo.com/video/' ) )
+ return $content;
+
+ $regexp = '!<iframe\s+src=[\'"]http://player\.vimeo\.com/video/(\d+)[\'"]((?:\s+\w+=[\'"][^\'"]*[\'"])*)></iframe>!i';
+ $regexp_ent = str_replace( '&amp;#0*58;', '&amp;#0*58;|&#0*58;', htmlspecialchars( $regexp, ENT_NOQUOTES ) );
+
+ foreach ( array( 'regexp', 'regexp_ent' ) as $reg ) {
+ if ( !preg_match_all( $$reg, $content, $matches, PREG_SET_ORDER ) )
+ continue;
+
+ foreach ( $matches as $match ) {
+ $id = (int) $match[1];
+
+ $params = $match[2];
+
+ if ( 'regexp_ent' == $reg )
+ $params = html_entity_decode( $params );
+
+ $params = wp_kses_hair( $params, array( 'http' ) );
+
+ $width = isset( $params['width'] ) ? (int) $params['width']['value'] : 0;
+ $height = isset( $params['height'] ) ? (int) $params['height']['value'] : 0;
+
+ $wh = '';
+ if ( $width && $height )
+ $wh = ' w=' . $width . ' h=' . $height;
+
+ $shortcode = '[vimeo ' . $id . $wh . ']';
+ $content = str_replace( $match[0], $shortcode, $content );
+ }
+ }
+
+ return $content;
+}
+
+add_filter( 'pre_kses', 'vimeo_embed_to_shortcode' );
diff --git a/plugins/jetpack/modules/shortcodes/youtube.php b/plugins/jetpack/modules/shortcodes/youtube.php
new file mode 100644
index 00000000..139fd9e3
--- /dev/null
+++ b/plugins/jetpack/modules/shortcodes/youtube.php
@@ -0,0 +1,291 @@
+<?php
+
+/**
+ * youtube shortcode
+ *
+ * Contains shortcode + some improvements over the Embeds syntax @
+ * http://codex.wordpress.org/Embeds
+ *
+ * @example [youtube=http://www.youtube.com/watch?v=wq0rXGLs0YM&amp;fs=1&amp;hl=bg_BG]
+ */
+
+/**
+ * Replaces YouTube embeds with YouTube shortcodes.
+ *
+ * @param string $content HTML content.
+ * @return string The content with YouTube embeds replaced with YouTube shortcodes.
+ */
+// 2008-07-15:
+//<object width="425" height="344"><param name="movie" value="http://www.youtube.com/v/bZBHZT3a-FA&hl=en&fs=1"></param><param name="allowFullScreen" value="true"></param><embed src="http://www.youtube.com/v/bZBHZT3a-FA&hl=en&fs=1" type="application/x-shockwave-flash" allowfullscreen="true" width="425" height="344"></embed></object>
+// around 2008-06-06 youtube changed their old embed code to this:
+//<object width="425" height="344"><param name="movie" value="http://www.youtube.com/v/M1D30gS7Z8U&hl=en"></param><embed src="http://www.youtube.com/v/M1D30gS7Z8U&hl=en" type="application/x-shockwave-flash" width="425" height="344"></embed></object>
+// old style was:
+// <object width="425" height="344"><param name="movie" value="http://www.youtube.com/v/dGY28Qbj76A&rel=0"></param><param name="wmode" value="transparent"></param><embed src="http://www.youtube.com/v/dGY28Qbj76A&rel=0" type="application/x-shockwave-flash" wmode="transparent" width="425" height="344"></embed></object>
+// 12-2010:
+// <object width="640" height="385"><param name="movie" value="http://www.youtube.com/v/3H8bnKdf654?fs=1&amp;hl=en_GB"></param><param name="allowFullScreen" value="true"></param><param name="allowscriptaccess" value="always"></param><embed src="http://www.youtube.com/v/3H8bnKdf654?fs=1&amp;hl=en_GB" type="application/x-shockwave-flash" allowscriptaccess="always" allowfullscreen="true" width="640" height="385"></embed></object>
+// 01-2011:
+// <iframe title="YouTube video player" class="youtube-player" type="text/html" width="640" height="390" src="http://www.youtube.com/embed/Qq9El3ki0_g" frameborder="0" allowFullScreen></iframe>
+// <iframe class="youtube-player" type="text/html" width="640" height="385" src="http://www.youtube.com/embed/VIDEO_ID" frameborder="0"></iframe>
+
+function youtube_embed_to_short_code( $content ) {
+ if ( false === strpos( $content, 'youtube.com' ) )
+ return $content;
+
+ //older codes
+ $regexp = '!<object width="\d+" height="\d+"><param name="movie" value="https?://www\.youtube\.com/v/([^"]+)"></param>(?:<param name="\w+" value="[^"]*"></param>)*<embed src="https?://www\.youtube\.com/v/(.+)" type="application/x-shockwave-flash"(?: \w+="[^"]*")* width="\d+" height="\d+"></embed></object>!i';
+ $regexp_ent = htmlspecialchars( $regexp, ENT_NOQUOTES );
+ $old_regexp = '!<embed(?:\s+\w+="[^"]*")*\s+src="https?(?:\:|&#0*58;)//www\.youtube\.com/v/([^"]+)"(?:\s+\w+="[^"]*")*\s*(?:/>|>\s*</embed>)!';
+ $old_regexp_ent = str_replace( '&amp;#0*58;', '&amp;#0*58;|&#0*58;', htmlspecialchars( $old_regexp, ENT_NOQUOTES ) );
+
+ //new code
+ $ifr_regexp = '!<iframe((?:\s+\w+="[^"]*")*?)\s+src="https?://(?:www\.)*youtube.com/embed/([^"]+)".*?</iframe>!i';
+ $ifr_regexp_ent = str_replace( '&amp;#0*58;', '&amp;#0*58;|&#0*58;', htmlspecialchars( $ifr_regexp, ENT_NOQUOTES ) );
+
+ foreach ( array( 'regexp', 'regexp_ent', 'old_regexp', 'old_regexp_ent', 'ifr_regexp', 'ifr_regexp_ent' ) as $reg ) {
+ if ( !preg_match_all( $$reg, $content, $matches, PREG_SET_ORDER ) )
+ continue;
+
+ foreach ( $matches as $match ) {
+ // Hack, but '?' should only ever appear once, and
+ // it should be for the 1st field-value pair in query string,
+ // if it is present
+ // YouTube changed their embed code.
+ // Example of how it is now:
+//<object width="640" height="385"><param name="movie" value="http://www.youtube.com/v/aP9AaD4tgBY?fs=1&amp;hl=en_US"></param><param name="allowFullScreen" value="true"></param><param name="allowscriptaccess" value="always"></param><embed src="http://www.youtube.com/v/aP9AaD4tgBY?fs=1&amp;hl=en_US" type="application/x-shockwave-flash" allowscriptaccess="always" allowfullscreen="true" width="640" height="385"></embed></object>
+ // As shown at the start of function, previous YouTube didn't '?'
+ // the 1st field-value pair.
+ if ( in_array ( $reg, array( 'ifr_regexp', 'ifr_regexp_ent' ) ) ) {
+ $params = $match[1];
+
+ if ( 'ifr_regexp_ent' == $reg )
+ $params = html_entity_decode( $params );
+
+ $params = wp_kses_hair( $params, array( 'http' ) );
+
+ $width = isset( $params['width'] ) ? (int) $params['width']['value'] : 0;
+ $height = isset( $params['height'] ) ? (int) $params['height']['value'] : 0;
+ $wh = '';
+ if ( $width && $height )
+ $wh = "&w=$width&h=$height";
+
+ $url = esc_url_raw( "http://www.youtube.com/watch?v={$match[2]}{$wh}" );
+ } else {
+ $match[1] = str_replace( '?', '&', $match[1] );
+
+ $url = esc_url_raw( 'http://www.youtube.com/watch?v=' . html_entity_decode( $match[1] ) );
+ }
+
+ $content = str_replace( $match[0], "[youtube $url]", $content );
+
+ do_action( 'jetpack_embed_to_shortcode', 'youtube', $url );
+ }
+ }
+
+ return $content;
+}
+add_filter('pre_kses', 'youtube_embed_to_short_code');
+
+/**
+ * Replaces plain-text links to YouTube videos with YouTube embeds.
+ *
+ * @param string $content HTML content
+ * @return string The content with embeds instead of URLs
+ */
+function youtube_link( $content ) {
+ return preg_replace_callback( '!(?:\n|\A)http://(?:www\.)?(?:youtube.com/(?:v/|watch[/\#?])|youtu\.be/)[^\s]+?(?:\n|\Z)!i', 'youtube_link_callback', $content );
+}
+
+/**
+ * Callback function for the regex that replaces YouTube URLs with
+ * YouTube embeds.
+ */
+function youtube_link_callback( $matches ) {
+ return "\n" . youtube_id( $matches[0] ) . "\n";
+}
+
+/**
+ * Normalizes a YouTube URL to include a v= parameter and a query string free of encoded ampersands.
+ *
+ * @param string $url
+ * @return string The normalized URL
+ */
+function youtube_sanitize_url( $url ) {
+ $url = trim( $url, ' "' );
+ $url = trim( $url );
+ $url = str_replace( array( 'youtu.be/', '/v/', '#!v=', '&amp;', '&#038;' ), array( 'youtu.be/?v=', '/?v=', '?v=', '&', '&' ), $url );
+
+ // Replace any extra question marks with ampersands - the result of a URL like "http://www.youtube.com/v/9FhMMmqzbD8?fs=1&hl=en_US" being passed in.
+ $query_string_start = strpos( $url, "?" );
+
+ if ( false !== $query_string_start ) {
+ $url = substr( $url, 0, $query_string_start + 1 ) . str_replace( "?", "&", substr( $url, $query_string_start + 1 ) );
+ }
+
+ return $url;
+}
+
+/*
+ * url can be:
+ * http://www.youtube.com/watch#!v=H2Ncxw1xfck
+ * http://www.youtube.com/watch?v=H2Ncxw1xfck
+ * http://www.youtube.com/watch?v=H2Ncxw1xfck&w=320&h=240&fmt=1&rel=0&showsearch=1&hd=0
+ * http://www.youtube.com/v/jF-kELmmvgA
+ * http://www.youtube.com/v/9FhMMmqzbD8?fs=1&hl=en_US
+ * http://youtu.be/Rrohlqeir5E
+ */
+
+function get_youtube_id( $url ) {
+ $url = youtube_sanitize_url( $url );
+ $url = parse_url( $url );
+
+ if ( !isset( $url['query'] ) )
+ return false;
+
+ parse_str( $url['query'], $qargs );
+
+ if ( !isset( $qargs['v'] ) )
+ return false;
+
+ $id = preg_replace( '|[^_a-z0-9-]|i', '', $qargs['v'] );
+
+ return $id;
+}
+
+/**
+ * Converts a YouTube URL into an embedded YouTube video.
+ */
+function youtube_id( $url ) {
+ if ( apply_filters( 'jetpack_bail_on_shortcode', false, 'youtube' ) )
+ return '';
+
+ if ( !$id = get_youtube_id( $url ) )
+ return '<!--YouTube Error: bad URL entered-->';
+
+ $url = youtube_sanitize_url( $url );
+ $url = parse_url( $url );
+
+ if ( !isset( $url['query'] ) )
+ return false;
+
+ parse_str( $url['query'], $qargs );
+
+ $agent = $_SERVER['HTTP_USER_AGENT'];
+ // Bloglines & Google Reader handle YouTube well now, instead of
+ // big blank space of yester year, so they can skip this treatment
+ if ( is_feed() && !preg_match( '#' . apply_filters( 'jetpack_shortcode_youtube_whitelist_user_agents', 'Bloglines|FeedFetcher-Google|feedburner' ) . '#i', $agent ) )
+ return '<span style="text-align:center; display: block;"><a href="' . get_permalink() . '"><img src="http://img.youtube.com/vi/' . $id . '/2.jpg" alt="" /></a></span>';
+
+ // calculate the width and height, taken content_width into consideration
+ global $content_width;
+
+ $input_w = ( isset( $qargs['w'] ) && intval( $qargs['w'] ) ) ? intval( $qargs['w'] ) : 0;
+ $input_h = ( isset( $qargs['h'] ) && intval( $qargs['h'] ) ) ? intval( $qargs['h'] ) : 0;
+
+ $default_width = 640;
+
+ if ( $input_w > 0 && $input_h > 0 ) {
+ $w = $input_w;
+ $h = $input_h;
+ } elseif ( 0 == $input_w && 0 == $input_h ) {
+ if ( isset( $qargs['fmt'] ) && intval( $qargs['fmt'] ) )
+ $w = ( !empty( $content_width ) ? min( $content_width, 480 ) : 480 );
+ else
+ $w = ( !empty( $content_width ) ? min( $content_width, $default_width ) : $default_width );
+
+ $h = ceil( ( $w / 16 ) * 9 ) + 30;
+ } elseif ( $input_w > 0 ) {
+ $w = $input_w;
+ $h = ceil( ( $w / 16 ) * 9 ) + 30;
+ } else {
+ if ( isset( $qargs['fmt'] ) && intval( $qargs['fmt'] ) )
+ $w = ( !empty( $content_width ) ? min( $content_width, 480 ) : 480 );
+ else
+ $w = ( !empty( $content_width ) ? min( $content_width, $default_width ) : $default_width );
+
+ $h = $input_h;
+ }
+
+ $w = (int) apply_filters( 'youtube_width', $w );
+ $h = (int) apply_filters( 'youtube_height', $h );
+
+ $fmt = '';
+ if ( isset( $qargs['fmt'] ) && intval( $qargs['fmt'] ) )
+ $fmt = '&fmt=' . (int) $qargs['fmt'];
+
+ if ( isset( $qargs['rel'] ) && 0 == $qargs['rel'] )
+ $rel = 0;
+ else
+ $rel = 1;
+
+ if ( isset( $qargs['showsearch'] ) && 1 == $qargs['showsearch'] )
+ $search = 1;
+ else
+ $search = 0;
+
+ if ( isset( $qargs['showinfo'] ) && 0 == $qargs['showinfo'] )
+ $info = 0;
+ else
+ $info = 1;
+
+ if ( isset( $qargs['iv_load_policy'] ) && 3 == $qargs['iv_load_policy'] )
+ $iv = 3;
+ else
+ $iv = 1;
+
+ $start = '';
+ if ( isset( $qargs['start'] ) && intval( $qargs['start'] ) )
+ $start = '&start=' . (int) $qargs['start'];
+
+ $hd = '';
+ if ( isset( $qargs['hd'] ) && intval( $qargs['hd'] ) )
+ $hd = '&hd=' . (int) $qargs['hd'];
+
+ $alignmentcss = 'text-align:center;';
+ if ( isset( $qargs['align'] ) ) {
+ switch ( $qargs['align'] ) {
+ case 'left':
+ $alignmentcss = "float:left; width:{$w}px; height:{$h}px; margin-right:10px; margin-bottom: 10px;";
+ break;
+ case 'right':
+ $alignmentcss = "float:right; width:{$w}px; height:{$h}px; margin-left:10px; margin-bottom: 10px;";
+ break;
+ }
+ }
+
+ if ( isset( $qargs['wmode'] ) && in_array( strtolower( $qargs['wmode'] ), array( 'opaque', 'window', 'transparent' ) ) )
+ $wmode = $qargs['wmode'];
+ else
+ $wmode = 'transparent';
+
+ $html = "<span class='embed-youtube' style='$alignmentcss display: block;'><iframe class='youtube-player' type='text/html' width='$w' height='$h' src='" . esc_attr( "http://www.youtube.com/embed/$id?version=3&rel=$rel&fs=1$fmt&showsearch=$search&showinfo=$info&iv_load_policy=$iv$start$hd&wmode=$wmode" ) . "' frameborder='0'></iframe></span>";
+ $html = apply_filters( 'video_embed_html', $html );
+ return $html;
+}
+
+function youtube_shortcode( $atts ) {
+ if ( isset ( $atts[0] ) )
+ $src = ltrim( $atts[0] , '=' );
+ else
+ $src = shortcode_new_to_old_params( $atts );
+
+ return youtube_id( $src );
+}
+add_shortcode( 'youtube', 'youtube_shortcode' );
+
+/**
+ * For bare URLs on their own line of the form
+ * http://www.youtube.com/v/9FhMMmqzbD8?fs=1&hl=en_US
+ */
+function wpcom_youtube_embed_crazy_url( $matches, $attr, $url ) {
+ return youtube_id( $url );
+}
+
+function wpcom_youtube_embed_crazy_url_init() {
+ wp_embed_register_handler( 'wpcom_youtube_embed_crazy_url', '#http://(?:www\.)?(?:youtube.com/(?:v/|watch[/\#?])|youtu\.be/).*#i', 'wpcom_youtube_embed_crazy_url' );
+}
+add_action( 'init', 'wpcom_youtube_embed_crazy_url_init' );
+
+// higher priority because we need it before auto-link and autop get to it
+if ( get_option('embed_autourls') ) {
+ add_filter( 'comment_text', 'youtube_link', 1 );
+}
diff --git a/plugins/jetpack/modules/shortlinks.php b/plugins/jetpack/modules/shortlinks.php
new file mode 100644
index 00000000..72c336b9
--- /dev/null
+++ b/plugins/jetpack/modules/shortlinks.php
@@ -0,0 +1,83 @@
+<?php
+/**
+ * Module Name: WP.me Shortlinks
+ * Module Description: Enable WP.me-powered shortlinks for all of your Posts and Pages for easier sharing.
+ * Sort Order 1
+ * First Introduced: 1.1
+ */
+
+add_filter( 'get_shortlink', 'wpme_get_shortlink_handler', 1, 4 );
+
+if ( !function_exists( 'wpme_dec2sixtwo' ) ) {
+ function wpme_dec2sixtwo( $num ) {
+ $index = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
+ $out = "";
+
+ if ( $num < 0 ) {
+ $out = '-';
+ $num = abs( $num );
+ }
+
+ for ( $t = floor( log10( $num ) / log10( 62 ) ); $t >= 0; $t-- ) {
+ $a = floor( $num / pow( 62, $t ) );
+ $out = $out . substr( $index, $a, 1 );
+ $num = $num - ( $a * pow( 62, $t ) );
+ }
+
+ return $out;
+ }
+}
+
+function wpme_get_shortlink( $id = 0, $context = 'post', $allow_slugs = true ) {
+ global $wp_query;
+
+ $blog_id = Jetpack::get_option( 'id' );
+
+ if ( 'query' == $context ) {
+ if ( is_singular() ) {
+ $id = $wp_query->get_queried_object_id();
+ $context = 'post';
+ } elseif ( is_front_page() ) {
+ $context = 'blog';
+ } else {
+ return '';
+ }
+ }
+
+ if ( 'blog' == $context ) {
+ if ( empty( $id ) )
+ $id = $blog_id;
+ return 'http://wp.me/' . wpme_dec2sixtwo( $id );
+ }
+
+ $post = get_post( $id );
+
+ if ( empty( $post ) )
+ return '';
+
+ $post_id = $post->ID;
+ $type = '';
+
+ if ( $allow_slugs && 'publish' == $post->post_status && 'post' == $post->post_type && strlen( $post->post_name ) <= 8 && false === strpos( $post->post_name, '%' )
+ && false === strpos( $post->post_name, '-' ) ) {
+ $id = $post->post_name;
+ $type = 's';
+ } else {
+ $id = wpme_dec2sixtwo( $post_id );
+ if ( 'page' == $post->post_type )
+ $type = 'P';
+ elseif ( 'post' == $post->post_type )
+ $type = 'p';
+ elseif ( 'attachment' == $post->post_type )
+ $type = 'a';
+ }
+
+ if ( empty( $type ) )
+ return '';
+
+ return 'http://wp.me/' . $type . wpme_dec2sixtwo( $blog_id ) . '-' . $id;
+}
+
+function wpme_get_shortlink_handler( $shortlink, $id, $context, $allow_slugs ) {
+ return wpme_get_shortlink( $id, $context, $allow_slugs );
+}
diff --git a/plugins/jetpack/modules/stats.php b/plugins/jetpack/modules/stats.php
new file mode 100644
index 00000000..fa1feb32
--- /dev/null
+++ b/plugins/jetpack/modules/stats.php
@@ -0,0 +1,1040 @@
+<?php
+/**
+ * Module Name: WordPress.com Stats
+ * Module Description: Simple, concise site stats with no additional load on your server.
+ * Sort Order: 1
+ * First Introduced: 1.1
+ */
+
+if ( defined( 'STATS_VERSION' ) ) {
+ return;
+}
+
+define( 'STATS_VERSION', '7' );
+defined( 'STATS_DASHBOARD_SERVER' ) or define( 'STATS_DASHBOARD_SERVER', 'dashboard.wordpress.com' );
+
+add_action( 'jetpack_modules_loaded', 'stats_load' );
+
+function stats_load() {
+ global $wp_roles;
+
+ Jetpack::enable_module_configurable( __FILE__ );
+ Jetpack::module_configuration_load( __FILE__, 'stats_configuration_load' );
+ Jetpack::module_configuration_head( __FILE__, 'stats_configuration_head' );
+ Jetpack::module_configuration_screen( __FILE__, 'stats_configuration_screen' );
+
+ // Generate the tracking code after wp() has queried for posts.
+ add_action( 'template_redirect', 'stats_template_redirect', 1 );
+
+ add_action( 'wp_head', 'stats_admin_bar_head', 100 );
+
+ add_action( 'jetpack_admin_menu', 'stats_admin_menu' );
+
+ add_action( 'wp_dashboard_setup', 'stats_register_dashboard_widget' );
+
+ // Tell HQ about changed settings
+ add_action( 'update_option_home', 'stats_update_blog' );
+ add_action( 'update_option_siteurl', 'stats_update_blog' );
+ add_action( 'update_option_blogname', 'stats_update_blog' );
+ add_action( 'update_option_blogdescription', 'stats_update_blog' );
+ add_action( 'update_option_timezone_string', 'stats_update_blog' );
+ add_action( 'add_option_timezone_string', 'stats_update_blog' );
+ add_action( 'update_option_gmt_offset', 'stats_update_blog' );
+ add_action( 'update_option_page_on_front', 'stats_update_blog' );
+ add_action( 'update_option_permalink_structure', 'stats_update_blog' );
+ add_action( 'update_option_category_base', 'stats_update_blog' );
+ add_action( 'update_option_tag_base', 'stats_update_blog' );
+
+ // Tell HQ about changed posts
+ add_action( 'save_post', 'stats_update_post', 10, 1 );
+
+ add_filter( 'jetpack_xmlrpc_methods', 'stats_xmlrpc_methods' );
+
+ // Map stats caps
+ add_filter( 'map_meta_cap', 'stats_map_meta_caps', 10, 4 );
+
+ add_filter( 'pre_option_db_version', 'stats_ignore_db_version' );
+}
+
+/**
+ * Prevent sparkline img requests being redirected to upgrade.php.
+ * See wp-admin/admin.php where it checks $wp_db_version.
+ */
+function stats_ignore_db_version( $version ) {
+ if (
+ is_admin() &&
+ isset( $_GET['page'] ) && $_GET['page'] == 'stats' &&
+ isset( $_GET['chart'] ) && $_GET['chart'] == 'admin-bar-hours'
+ ) {
+ global $wp_db_version;
+ return $wp_db_version;
+ }
+ return $version;
+}
+
+/**
+ * Maps view_stats cap to read cap as needed
+ *
+ * @return array Possibly mapped capabilities for meta capability
+ */
+function stats_map_meta_caps( $caps, $cap, $user_id, $args ) {
+
+ // Map view_stats to exists
+ if ( 'view_stats' == $cap ) {
+ $user = new WP_User( $user_id );
+ $user_role = array_shift( $user->roles );
+ $stats_roles = stats_get_option( 'roles' );
+
+ // Is the users role in the available stats roles?
+ if ( in_array( $user_role, $stats_roles ) ) {
+ $caps = array( 'read' );
+ }
+ }
+
+ return $caps;
+}
+
+function stats_template_redirect() {
+ global $wp_the_query, $current_user, $stats_footer;
+
+ if ( is_feed() || is_robots() || is_trackback() )
+ return;
+
+ $options = stats_get_options();
+ // Ensure this is always setup for the check below
+ $options['reg_users'] = empty( $options['reg_users'] ) ? false : true;
+
+ if ( !$options['reg_users'] && !empty( $current_user->ID ) )
+ return;
+
+ add_action( 'wp_footer', 'stats_footer', 101 );
+ add_action( 'wp_head', 'stats_add_shutdown_action' );
+
+ $blog = Jetpack::get_option( 'id' );
+ $v = 'ext';
+ $j = sprintf( '%s:%s', JETPACK__API_VERSION, JETPACK__VERSION );
+ if ( $wp_the_query->is_single || $wp_the_query->is_page || $wp_the_query->is_posts_page ) {
+ // Store and reset the queried_object and queried_object_id
+ // Otherwise, redirect_canonical() will redirect to home_url( '/' ) for show_on_front = page sites where home_url() is not all lowercase.
+ // Repro:
+ // 1. Set home_url = http://ExamPle.com/
+ // 2. Set show_on_front = page
+ // 3. Set page_on_front = something
+ // 4. Visit http://example.com/
+
+ $queried_object = ( isset( $wp_the_query->queried_object ) ) ? $wp_the_query->queried_object : null;
+ $queried_object_id = ( isset( $wp_the_query->queried_object_id ) ) ? $wp_the_query->queried_object_id : null;
+ $post = $wp_the_query->get_queried_object_id();
+ $wp_the_query->queried_object = $queried_object;
+ $wp_the_query->queried_object_id = $queried_object_id;
+ } else {
+ $post = '0';
+ }
+
+ $http = is_ssl() ? 'https' : 'http';
+ $week = gmdate( 'YW' );
+
+ $data = stats_array( compact( 'v', 'j', 'blog', 'post' ) );
+
+ $stats_footer = <<<END
+
+ <script src="$http://stats.wordpress.com/e-$week.js" type="text/javascript"></script>
+ <script type="text/javascript">
+ st_go({{$data}});
+ var load_cmc = function(){linktracker_init($blog,$post,2);};
+ if ( typeof addLoadEvent != 'undefined' ) addLoadEvent(load_cmc);
+ else load_cmc();
+ </script>
+END;
+ if ( isset( $options['hide_smile'] ) && $options['hide_smile'] ) {
+ $stats_footer .= "\n<style type='text/css'>img#wpstats{display:none}</style>";
+ }
+}
+
+function stats_add_shutdown_action() {
+ // just in case wp_footer isn't in your theme
+ add_action( 'shutdown', 'stats_footer', 101 );
+}
+
+function stats_footer() {
+ global $stats_footer;
+ print $stats_footer;
+ $stats_footer = '';
+}
+
+function stats_get_options() {
+ $options = get_option( 'stats_options' );
+
+ if ( !isset( $options['version'] ) || $options['version'] < STATS_VERSION )
+ $options = stats_upgrade_options( $options );
+
+ return $options;
+}
+
+function stats_get_option( $option ) {
+ $options = stats_get_options();
+
+ if ( $option == 'blog_id' )
+ return Jetpack::get_option( 'id' );
+
+ if ( isset( $options[$option] ) )
+ return $options[$option];
+
+ return null;
+}
+
+function stats_set_option( $option, $value ) {
+ $options = stats_get_options();
+
+ $options[$option] = $value;
+
+ stats_set_options($options);
+}
+
+function stats_set_options($options) {
+ update_option( 'stats_options', $options );
+}
+
+function stats_upgrade_options( $options ) {
+ $defaults = array(
+ 'admin_bar' => true,
+ 'roles' => array( 'administrator' ),
+ 'blog_id' => Jetpack::get_option( 'id' ),
+ 'do_not_track' => true, // @todo
+ 'hide_smile' => false,
+ );
+
+ if ( is_array( $options ) && !empty( $options ) )
+ $new_options = array_merge( $defaults, $options );
+ else
+ $new_options = $defaults;
+
+ $new_options['version'] = STATS_VERSION;
+
+ stats_set_options( $new_options );
+
+ stats_update_blog();
+
+ return $new_options;
+}
+
+function stats_array( $kvs ) {
+ $kvs = apply_filters( 'stats_array', $kvs );
+ $kvs = array_map( 'addslashes', $kvs );
+ foreach ( $kvs as $k => $v )
+ $jskvs[] = "$k:'$v'";
+ return join( ',', $jskvs );
+}
+
+/**
+ * Admin Pages
+ */
+function stats_admin_menu() {
+ global $pagenow;
+
+ // If we're at an old Stats URL, redirect to the new one.
+ // Don't even bother with caps, menu_page_url(), etc. Just do it.
+ if ( 'index.php' == $pagenow && isset( $_GET['page'] ) && 'stats' == $_GET['page'] ) {
+ $redirect_url = str_replace( array( '/wp-admin/index.php?', '/wp-admin/?' ), '/wp-admin/admin.php?', $_SERVER['REQUEST_URI'] );
+ $relative_pos = strpos( $redirect_url, '/wp-admin/' );
+ if ( false !== $relative_pos ) {
+ wp_safe_redirect( admin_url( substr( $redirect_url, $relative_pos + 10 ) ) );
+ exit;
+ }
+ }
+
+ $hook = add_submenu_page( 'jetpack', __( 'Site Stats', 'jetpack' ), __( 'Site Stats', 'jetpack' ), 'view_stats', 'stats', 'stats_reports_page' );
+ add_action( "load-$hook", 'stats_reports_load' );
+}
+
+function stats_admin_path() {
+ return Jetpack::module_configuration_url( __FILE__ );
+}
+
+function stats_reports_load() {
+ wp_enqueue_script( 'jquery' );
+ wp_enqueue_script( 'postbox' );
+
+ add_action( 'admin_print_styles', 'stats_reports_css' );
+
+ if ( isset( $_GET['nojs'] ) && $_GET['nojs'] ) {
+ $parsed = parse_url( admin_url() );
+ // Remember user doesn't want JS
+ setcookie( 'stnojs', '1', time() + 172800, $parsed['path'] ); // 2 days
+ }
+
+ if ( isset( $_COOKIE['stnojs'] ) && $_COOKIE['stnojs'] ) {
+ // Detect if JS is on. If so, remove cookie so next page load is via JS
+ add_action( 'admin_print_footer_scripts', 'stats_js_remove_stnojs_cookie' );
+ } else if ( !isset( $_GET['noheader'] ) && empty( $_GET['nojs'] ) ) {
+ // Normal page load. Load page content via JS.
+ add_action( 'admin_print_footer_scripts', 'stats_js_load_page_via_ajax' );
+ }
+}
+
+function stats_reports_css() {
+?>
+<style type="text/css">
+#stats-loading-wrap p {
+ text-align: center;
+ font-size: 2em;
+ margin: 7.5em 15px 0 0;
+ height: 64px;
+ line-height: 64px;
+}
+</style>
+<?php
+}
+
+// Detect if JS is on. If so, remove cookie so next page load is via JS.
+function stats_js_remove_stnojs_cookie() {
+ $parsed = parse_url( admin_url() );
+?>
+<script type="text/javascript">
+/* <![CDATA[ */
+document.cookie = 'stnojs=0; expires=Wed, 9 Mar 2011 16:55:50 UTC; path=<?php echo esc_js( $parsed['path'] ); ?>';
+/* ]]> */
+</script>
+<?php
+}
+
+// Normal page load. Load page content via JS.
+function stats_js_load_page_via_ajax() {
+?>
+<script type="text/javascript">
+/* <![CDATA[ */
+if ( -1 == document.location.href.indexOf( 'noheader' ) ) {
+ jQuery( function( $ ) {
+ $.get( document.location.href + '&noheader', function( responseText ) {
+ $( '#stats-loading-wrap' ).replaceWith( responseText );
+ } );
+ } );
+}
+/* ]]> */
+</script>
+<?php
+}
+
+function stats_reports_page() {
+ if ( isset( $_GET['dashboard'] ) )
+ return stats_dashboard_widget_content();
+
+ if ( !isset( $_GET['noheader'] ) && empty( $_GET['nojs'] ) && empty( $_COOKIE['stnojs'] ) ) {
+ $nojs_url = add_query_arg( 'nojs', '1' );
+ if ( 'classic' != $color = get_user_option( 'admin_color' ) ) {
+ $color = 'fresh';
+ }
+ $http = is_ssl() ? 'https' : 'http';
+ // Loading message
+ // No JS fallback message
+?>
+<div id="stats-loading-wrap" class="wrap">
+<p class="hide-if-no-js"><img alt="<?php esc_attr_e( 'Loading&hellip;', 'jetpack' ); ?>" src="<?php echo esc_url( "$http://" . STATS_DASHBOARD_SERVER . "/i/loading/$color-64.gif" ); ?>" /></p>
+<p class="hide-if-js"><?php esc_html_e( 'Your Site Stats work better with Javascript enabled.', 'jetpack' ); ?><br />
+<a href="<?php echo esc_url( $nojs_url ); ?>"><?php esc_html_e( 'View Site Stats without Javascript', 'jetpack' ); ?></a>.</p>
+</div>
+<?php
+ return;
+ }
+
+ $blog_id = stats_get_option( 'blog_id' );
+ $day = isset( $_GET['day'] ) && preg_match( '/^\d{4}-\d{2}-\d{2}$/', $_GET['day'] ) ? $_GET['day'] : false;
+ $q = array(
+ 'noheader' => 'true',
+ 'proxy' => '',
+ 'page' => 'stats',
+ 'day' => $day,
+ 'blog' => $blog_id,
+ 'charset' => get_option( 'blog_charset' ),
+ 'color' => get_user_option( 'admin_color' ),
+ 'ssl' => is_ssl(),
+ 'j' => sprintf( '%s:%s', JETPACK__API_VERSION, JETPACK__VERSION ),
+ );
+ $args = array(
+ 'view' => array( 'referrers', 'postviews', 'searchterms', 'clicks', 'post', 'table' ),
+ 'numdays' => 'int',
+ 'day' => 'date',
+ 'unit' => array( 1, 7, 31, 'human' ),
+ 'humanize' => array( 'true' ),
+ 'num' => 'int',
+ 'summarize' => null,
+ 'post' => 'int',
+ 'width' => 'int',
+ 'height' => 'int',
+ 'data' => 'data',
+ 'blog_subscribers' => 'int',
+ 'comment_subscribers' => null,
+ 'type' => array( 'email', 'pending' ),
+ 'pagenum' => 'int',
+ );
+ foreach ( $args as $var => $vals ) {
+ if ( !isset( $_REQUEST[$var] ) )
+ continue;
+ if ( is_array( $vals ) ) {
+ if ( in_array( $_REQUEST[$var], $vals ) )
+ $q[$var] = $_REQUEST[$var];
+ } elseif ( $vals == 'int' ) {
+ $q[$var] = intval( $_REQUEST[$var] );
+ } elseif ( $vals == 'date' ) {
+ if ( preg_match( '/^\d{4}-\d{2}-\d{2}$/', $_REQUEST[$var] ) )
+ $q[$var] = $_REQUEST[$var];
+ } elseif ( $vals == null ) {
+ $q[$var] = '';
+ } elseif ( $vals == 'data' ) {
+ if ( substr( $_REQUEST[$var], 0, 9 ) == 'index.php' )
+ $q[$var] = $_REQUEST[$var];
+ }
+ }
+
+ if ( isset( $_REQUEST['chart'] ) ) {
+ if ( preg_match( '/^[a-z0-9-]+$/', $_REQUEST['chart'] ) )
+ $url = 'http://' . STATS_DASHBOARD_SERVER . "/wp-includes/charts/{$_GET['chart']}.php";
+ } else {
+ $url = 'http://' . STATS_DASHBOARD_SERVER . "/wp-admin/index.php";
+ }
+
+ $url = add_query_arg( $q, $url );
+ $method = 'GET';
+ $timeout = 90;
+ $user_id = 1; // means send the wp.com user_id, not 1
+
+ $get = Jetpack_Client::remote_request( compact( 'url', 'method', 'timeout', 'user_id' ) );
+ $get_code = wp_remote_retrieve_response_code( $get );
+ $get_code_type = intval( $get_code / 100 );
+ if ( is_wp_error( $get ) || ( 2 != $get_code_type && 304 != $get_code ) ) {
+ // @todo nicer looking error
+ if ( 3 == $get_code_type ) {
+ echo '<p>' . __( 'We were unable to get your stats just now (too many redirects). Please try again.', 'jetpack' ) . '</p>';
+ } else {
+ echo '<p>' . __( 'We were unable to get your stats just now. Please try again.', 'jetpack' ) . '</p>';
+ }
+ } else {
+ if ( !empty( $get['headers']['content-type'] ) ) {
+ $type = $get['headers']['content-type'];
+ if ( substr( $type, 0, 5 ) == 'image' ) {
+ header( 'Content-Type: ' . $type );
+ die( $get['body'] );
+ }
+ }
+ $body = stats_convert_post_titles( $get['body'] );
+ $body = stats_convert_chart_urls( $body );
+ $body = stats_convert_image_urls( $body );
+ $body = stats_convert_admin_urls( $body );
+ echo $body;
+ }
+ if ( isset( $_GET['noheader'] ) )
+ die;
+}
+
+function stats_convert_admin_urls( $html ) {
+ return str_replace( 'index.php?page=stats', 'admin.php?page=stats', $html );
+}
+
+function stats_convert_image_urls( $html ) {
+ $url = ( is_ssl() ? 'https' : 'http' ) . '://' . STATS_DASHBOARD_SERVER;
+ $html = preg_replace( '|(["\'])(/i/stats.+)\\1|', '$1' . $url . '$2$1', $html );
+ return $html;
+}
+
+function stats_convert_chart_urls( $html ) {
+ $html = preg_replace( '|https?://[-.a-z0-9]+/wp-includes/charts/([-.a-z0-9]+).php|', 'admin.php?page=stats&noheader&chart=$1', $html );
+ return $html;
+}
+
+function stats_convert_post_titles( $html ) {
+ global $wpdb, $stats_posts;
+ $pattern = "<span class='post-(\d+)-link'>.*?</span>";
+ if ( !preg_match_all( "!$pattern!", $html, $matches ) )
+ return $html;
+ $posts = get_posts( array(
+ 'include' => implode( ',', $matches[1] ),
+ 'post_type' => 'any',
+ 'post_status' => 'any',
+ 'numberposts' => -1,
+ ));
+ foreach ( $posts as $post )
+ $stats_posts[$post->ID] = $post;
+ $html = preg_replace_callback( "!$pattern!", 'stats_convert_post_title', $html );
+ return $html;
+}
+
+function stats_convert_post_title( $matches ) {
+ global $stats_posts;
+ $post_id = $matches[1];
+ if ( isset( $stats_posts[$post_id] ) )
+ return '<a href="' . get_permalink( $post_id ) . '" target="_blank">' . get_the_title( $post_id ) . '</a>';
+ return $matches[0];
+}
+
+function stats_configuration_load() {
+ if ( isset( $_POST['action'] ) && $_POST['action'] == 'save_options' && $_POST['_wpnonce'] == wp_create_nonce( 'stats' ) ) {
+ $options = stats_get_options();
+ $options['admin_bar'] = isset( $_POST['admin_bar'] ) && $_POST['admin_bar'];
+ $options['reg_users'] = isset( $_POST['reg_users'] ) && $_POST['reg_users'];
+ $options['hide_smile'] = isset( $_POST['hide_smile'] ) && $_POST['hide_smile'];
+
+ $options['roles'] = array( 'administrator' );
+ foreach ( get_editable_roles() as $role => $details )
+ if ( isset( $_POST["role_$role"] ) && $_POST["role_$role"] )
+ $options['roles'][] = $role;
+
+ stats_set_options( $options );
+ stats_update_blog();
+ Jetpack::state( 'message', 'module_configured' );
+ wp_safe_redirect( Jetpack::module_configuration_url( 'stats' ) );
+ exit;
+ }
+}
+
+function stats_configuration_head() {
+ ?>
+ <style type="text/css">
+ #statserror {
+ border: 1px solid #766;
+ background-color: #d22;
+ padding: 1em 3em;
+ }
+ .stats-smiley {
+ vertical-align: 1px;
+ }
+ </style>
+ <?php
+}
+
+function stats_configuration_screen() {
+ global $wp_version;
+ $options = stats_get_options();
+ $options['reg_users'] = empty( $options['reg_users'] ) ? false : true;
+ ?>
+ <div class="narrow">
+ <p><?php printf( __( 'Visit <a href="%s">Site Stats</a> to see your stats.', 'jetpack' ), esc_url( menu_page_url( 'stats', false ) ) ); ?></p>
+ <form method="post">
+ <input type='hidden' name='action' value='save_options' />
+ <?php wp_nonce_field( 'stats' ); ?>
+ <table id="menu" class="form-table">
+ <?php if ( version_compare( $wp_version, '3.1-RC', '>=' ) ) : ?>
+ <tr valign="top"><th scope="row"><label for="admin_bar"><?php _e( 'Admin bar' , 'jetpack' ); ?></label></th>
+ <td><label><input type='checkbox'<?php checked( $options['admin_bar'] ); ?> name='admin_bar' id='admin_bar' /> <?php _e( "Put a chart showing 48 hours of views in the admin bar.", 'jetpack' ); ?></label></td></tr>
+ <?php endif; ?>
+ <tr valign="top"><th scope="row"><label for="reg_users"><?php _e( 'Registered users', 'jetpack' ); ?></label></th>
+ <td><label><input type='checkbox'<?php checked( $options['reg_users'] ); ?> name='reg_users' id='reg_users' /> <?php _e( "Count the page views of registered users who are logged in.", 'jetpack' ); ?></label></td></tr>
+ <tr valign="top"><th scope="row"><?php _e( 'Smiley' , 'jetpack' ); ?></th>
+ <td><label><input type='checkbox'<?php checked( isset( $options['hide_smile'] ) && $options['hide_smile'] ); ?> name='hide_smile' id='hide_smile' /> <?php _e( 'Hide the stats smiley face image.', 'jetpack' ); ?></label><br /> <span class="description"><?php _e( 'The image helps collect stats and <strong>makes the world a better place</strong> but should still work when hidden', 'jetpack' ); ?> <img class="stats-smiley" alt="<?php esc_attr_e( 'Smiley face', 'jetpack' ); ?>" src="<?php echo esc_url( plugins_url( '_inc/images/stats-smiley.gif', dirname( __FILE__ ) ) ); ?>" width="6" height="5" /></span></td></tr>
+ <tr valign="top"><th scope="row"><?php _e( 'Report visibility' , 'jetpack' ); ?></th>
+ <td>
+ <?php _e( 'Select the roles that will be able to view stats reports.', 'jetpack' ); ?><br/>
+ <?php
+ $stats_roles = stats_get_option( 'roles' );
+ foreach ( get_editable_roles() as $role => $details ) {
+ ?>
+ <label><input type='checkbox' <?php if ( $role == 'administrator' ) echo "disabled='disabled' "; ?>name='role_<?php echo $role; ?>'<?php checked( $role == 'administrator' || in_array( $role, $stats_roles ) ); ?> /> <?php echo translate_user_role( $details['name'] ); ?></label><br/>
+ <?php
+ }
+ ?>
+ </tr>
+ </table>
+ <p class="submit"><input type='submit' class='button-primary' value='<?php echo esc_attr( __( 'Save configuration', 'jetpack' ) ); ?>' /></p>
+ </form>
+ </div>
+ <?php
+}
+
+function stats_admin_bar_head() {
+ if ( !stats_get_option( 'admin_bar' ) )
+ return;
+
+ if ( !current_user_can( 'view_stats' ) )
+ return;
+
+ if ( function_exists( 'is_admin_bar_showing' ) && !is_admin_bar_showing() ) {
+ return;
+ }
+
+ add_action( 'admin_bar_menu', 'stats_admin_bar_menu', 100 );
+ ?>
+
+<style type='text/css'>
+#wpadminbar .quicklinks li#wp-admin-bar-stats {height:28px}
+#wpadminbar .quicklinks li#wp-admin-bar-stats a {height:28px;padding:0}
+#wpadminbar .quicklinks li#wp-admin-bar-stats a img {padding:4px 11px}
+</style>
+<?php
+}
+
+function stats_admin_bar_menu( &$wp_admin_bar ) {
+ $blog_id = stats_get_option( 'blog_id' );
+
+ $url = add_query_arg( 'page', 'stats', admin_url( 'admin.php' ) ); // no menu_page_url() blog-side.
+
+ $img_src = add_query_arg( array( 'noheader'=>'', 'proxy'=>'', 'chart'=>'admin-bar-hours', 'height'=>20, 'hours'=>48 ), $url );
+
+ $title = __( 'Views over 48 hours. Click for more Site Stats.', 'jetpack' );
+
+ $menu = array( 'id' => 'stats', 'title' => "<img style='width:95px;height:20px' src='$img_src' alt='$title' title='$title' />", 'href' => $url );
+
+ $wp_admin_bar->add_menu( $menu );
+}
+
+function stats_update_blog() {
+ Jetpack::xmlrpc_async_call( 'jetpack.updateBlog', stats_get_blog() );
+}
+
+function stats_update_post( $post ) {
+ if ( !$stats_post = stats_get_post( $post ) )
+ return;
+
+ $jetpack = Jetpack::init();
+ $jetpack->sync->post( $stats_post->ID, array_keys( get_object_vars( $stats_post ) ) );
+}
+
+function stats_get_blog() {
+ $home = parse_url( trailingslashit( get_option( 'home' ) ) );
+ $blog = array(
+ 'host' => $home['host'],
+ 'path' => $home['path'],
+ 'blogname' => get_option( 'blogname' ),
+ 'blogdescription' => get_option( 'blogdescription' ),
+ 'siteurl' => get_option( 'siteurl' ),
+ 'gmt_offset' => get_option( 'gmt_offset' ),
+ 'timezone_string' => get_option( 'timezone_string' ),
+ 'stats_version' => STATS_VERSION,
+ 'stats_api' => 'jetpack',
+ 'page_on_front' => get_option( 'page_on_front' ),
+ 'permalink_structure' => get_option( 'permalink_structure' ),
+ 'category_base' => get_option( 'category_base' ),
+ 'tag_base' => get_option( 'tag_base' ),
+ );
+ $blog = array_merge( stats_get_options(), $blog );
+ unset( $blog['roles'], $blog['blog_id'] );
+ return array_map( 'esc_html', $blog );
+}
+
+function stats_get_posts( $args ) {
+ list( $post_ids ) = $args;
+ $post_ids = array_map( 'intval', (array) $post_ids );
+ $r = array(
+ 'include' => $post_ids,
+ 'post_type' => array_values( get_post_types( array( 'public' => true ) ) ),
+ 'post_status' => array_values( get_post_stati( array( 'public' => true ) ) ),
+ );
+ $posts = get_posts( $r );
+ foreach ( $posts as $i => $post )
+ $posts[$i] = stats_get_post( $post );
+ return $posts;
+}
+
+function stats_get_post( $post ) {
+ if ( !$post = get_post( $post ) ) {
+ return null;
+ }
+
+ $stats_post = wp_clone( $post );
+ $stats_post->permalink = get_permalink( $post );
+ foreach ( array( 'post_content', 'post_excerpt', 'post_content_filtered', 'post_password' ) as $do_not_want )
+ unset( $stats_post->$do_not_want );
+ return $stats_post;
+}
+
+function stats_xmlrpc_methods( $methods ) {
+ $my_methods = array(
+ 'jetpack.getBlog' => 'stats_get_blog',
+ 'jetpack.getPosts' => 'stats_get_posts',
+ );
+
+ return array_merge( $methods, $my_methods );
+}
+
+function stats_register_dashboard_widget() {
+ if ( ! current_user_can( 'view_stats' ) )
+ return;
+
+ // wp_dashboard_empty: we load in the content after the page load via JS
+ wp_add_dashboard_widget( 'dashboard_stats', __( 'Site Stats', 'jetpack' ), 'wp_dashboard_empty', 'stats_dashboard_widget_control' );
+
+ add_action( 'admin_head', 'stats_dashboard_head' );
+}
+
+function stats_dashboard_widget_options() {
+ $defaults = array( 'chart' => 1, 'top' => 1, 'search' => 7 );
+ if ( ( !$options = get_option( 'stats_dashboard_widget' ) ) || !is_array( $options ) )
+ $options = array();
+
+ // Ignore obsolete option values
+ $intervals = array( 1, 7, 31, 90, 365 );
+ foreach ( array( 'top', 'search' ) as $key )
+ if ( isset( $options[$key] ) && !in_array( $options[$key], $intervals ) )
+ unset( $options[$key] );
+
+ return array_merge( $defaults, $options );
+}
+
+function stats_dashboard_widget_control() {
+ $periods = array(
+ '1' => __( 'day', 'jetpack' ),
+ '7' => __( 'week', 'jetpack' ),
+ '31' => __( 'month', 'jetpack' ),
+ );
+ $intervals = array(
+ '1' => __( 'the past day', 'jetpack' ),
+ '7' => __( 'the past week', 'jetpack' ),
+ '31' => __( 'the past month', 'jetpack' ),
+ '90' => __( 'the past quarter', 'jetpack' ),
+ '365' => __( 'the past year', 'jetpack' ),
+ );
+ $defaults = array(
+ 'top' => 1,
+ 'search' => 7,
+ );
+
+ $options = stats_dashboard_widget_options();
+
+ if ( 'post' == strtolower( $_SERVER['REQUEST_METHOD'] ) && isset( $_POST['widget_id'] ) && 'dashboard_stats' == $_POST['widget_id'] ) {
+ if ( isset( $periods[ $_POST['chart'] ] ) )
+ $options['chart'] = $_POST['chart'];
+ foreach ( array( 'top', 'search' ) as $key ) {
+ if ( isset( $intervals[ $_POST[$key] ] ) )
+ $options[$key] = $_POST[$key];
+ else
+ $options[$key] = $defaults[$key];
+ }
+ update_option( 'stats_dashboard_widget', $options );
+ }
+ ?>
+ <p>
+ <label for="chart"><?php _e( 'Chart stats by' , 'jetpack' ); ?></label>
+ <select id="chart" name="chart">
+ <?php
+ foreach ( $periods as $val => $label ) {
+ ?>
+ <option value="<?php echo $val; ?>"<?php selected( $val, $options['chart'] ); ?>><?php echo esc_html( $label ); ?></option>
+ <?php
+ }
+ ?>
+ </select>.
+ </p>
+
+ <p>
+ <label for="top"><?php _e( 'Show top posts over', 'jetpack'); ?></label>
+ <select id="top" name="top">
+ <?php
+ foreach ( $intervals as $val => $label ) {
+ ?>
+ <option value="<?php echo $val; ?>"<?php selected( $val, $options['top'] ); ?>><?php echo esc_html( $label ); ?></option>
+ <?php
+ }
+ ?>
+ </select>.
+ </p>
+
+ <p>
+ <label for="search"><?php _e( 'Show top search terms over', 'jetpack'); ?></label>
+ <select id="search" name="search">
+ <?php
+ foreach ( $intervals as $val => $label ) {
+ ?>
+ <option value="<?php echo $val; ?>"<?php selected( $val, $options['search'] ); ?>><?php echo esc_html( $label ); ?></option>
+ <?php
+ }
+ ?>
+ </select>.
+ </p>
+ <?php
+}
+
+// Javascript and CSS for dashboard widget
+function stats_dashboard_head() { ?>
+<script type="text/javascript">
+/* <![CDATA[ */
+jQuery(window).load( function() {
+ jQuery( function($) {
+ var dashStats = $( '#dashboard_stats.postbox div.inside' );
+
+ if ( dashStats.find( '.dashboard-widget-control-form' ).size() ) {
+ return;
+ }
+
+ if ( ! dashStats.size() ) {
+ dashStats = $( '#dashboard_stats div.dashboard-widget-content' );
+ var h = parseInt( dashStats.parent().height() ) - parseInt( dashStats.prev().height() );
+ var args = 'width=' + dashStats.width() + '&height=' + h.toString();
+ } else {
+ var args = 'width=' + ( dashStats.prev().width() * 2 ).toString();
+ }
+
+ dashStats.not( '.dashboard-widget-control' ).load( 'admin.php?page=stats&noheader&dashboard&' + args );
+ } );
+} );
+/* ]]> */
+</script>
+<style type="text/css">
+/* <![CDATA[ */
+#stat-chart {
+ background: none !important;
+}
+#dashboard_stats .inside {
+ margin: 10px 0 0 0 !important;
+}
+#dashboard_stats #stats-graph {
+ margin: 0;
+}
+#stats-info {
+ border-top: 1px solid #dfdfdf;
+ margin: 7px -10px 0 -10px;
+ padding: 10px;
+ background: #fcfcfc;
+ -moz-box-shadow:inset 0 1px 0 #fff;
+ -webkit-box-shadow:inset 0 1px 0 #fff;
+ box-shadow:inset 0 1px 0 #fff;
+ overflow: hidden;
+ border-radius: 0 0 2px 2px;
+ -webkit-border-radius: 0 0 2px 2px;
+ -moz-border-radius: 0 0 2px 2px;
+ -khtml-border-radius: 0 0 2px 2px;
+}
+#stats-info #top-posts, #stats-info #top-search {
+ float: left;
+ width: 50%;
+}
+#top-posts .stats-section-inner p {
+ white-space: nowrap;
+ overflow: hidden;
+}
+#top-posts .stats-section-inner p a {
+ overflow: hidden;
+ text-overflow: ellipsis;
+}
+#stats-info div#active {
+ border-top: 1px solid #dfdfdf;
+ margin: 0 -10px;
+ padding: 10px 10px 0 10px;
+ -moz-box-shadow:inset 0 1px 0 #fff;
+ -webkit-box-shadow:inset 0 1px 0 #fff;
+ box-shadow:inset 0 1px 0 #fff;
+ overflow: hidden;
+}
+#top-search p {
+ color: #999;
+}
+#stats-info h4 {
+ font-size: 1em;
+ margin: 0 0 .5em 0 !important;
+}
+#stats-info p {
+ margin: 0 0 .25em;
+ color: #999;
+}
+#stats-info p.widget-loading {
+ margin: 1em 0 0;
+ color: #333;
+}
+#stats-info p a {
+ display: block;
+}
+#stats-info p a.button {
+ display: inline;
+}
+/* ]]> */
+</style>
+<?php
+}
+
+function stats_dashboard_widget_content() {
+ if ( !isset( $_GET['width'] ) || ( !$width = (int) ( $_GET['width'] / 2 ) ) || $width < 250 )
+ $width = 370;
+ if ( !isset( $_GET['height'] ) || ( !$height = (int) $_GET['height'] - 36 ) || $height < 230 )
+ $height = 180;
+
+ $_width = $width - 5;
+ $_height = $height - ( $GLOBALS['is_winIE'] ? 16 : 5 ); // hack!
+
+ $options = stats_dashboard_widget_options();
+ $blog_id = Jetpack::get_option( 'id' );
+
+ $q = array(
+ 'noheader' => 'true',
+ 'proxy' => '',
+ 'blog' => $blog_id,
+ 'page' => 'stats',
+ 'chart' => '',
+ 'unit' => $options['chart'],
+ 'color' => get_user_option( 'admin_color' ),
+ 'width' => $_width,
+ 'height' => $_height,
+ 'ssl' => is_ssl(),
+ 'j' => sprintf( '%s:%s', JETPACK__API_VERSION, JETPACK__VERSION ),
+ );
+
+ $url = 'http://' . STATS_DASHBOARD_SERVER . "/wp-admin/index.php";
+
+ $url = add_query_arg( $q, $url );
+ $method = 'GET';
+ $timeout = 90;
+ $user_id = 1; // means send the wp.com user_id, not 1
+
+ $get = Jetpack_Client::remote_request( compact( 'url', 'method', 'timeout', 'user_id' ) );
+ $get_code = wp_remote_retrieve_response_code( $get );
+ $get_code_type = intval( $get_code / 100 );
+ if ( is_wp_error( $get ) || ( 2 != $get_code_type && 304 != $get_code ) || empty( $get['body'] ) ) {
+ // @todo
+ if ( 3 == $get_code_type ) {
+ echo '<p>' . __( 'We were unable to get your stats just now (too many redirects). Please try again.', 'jetpack' ) . '</p>';
+ } else {
+ echo '<p>' . __( 'We were unable to get your stats just now. Please try again.', 'jetpack' ) . '</p>';
+ }
+ } else {
+ $body = stats_convert_post_titles($get['body']);
+ $body = stats_convert_chart_urls($body);
+ $body = stats_convert_image_urls($body);
+ echo $body;
+ }
+
+ $post_ids = array();
+
+ $csv_args = array( 'top' => '&limit=8', 'search' => '&limit=5' );
+ /* translators: Stats dashboard widget postviews list: "$post_title $views Views" */
+ $printf = __( '%1$s %2$s Views' , 'jetpack' );
+
+ foreach ( $top_posts = stats_get_csv( 'postviews', "days=$options[top]$csv_args[top]" ) as $post )
+ $post_ids[] = $post['post_id'];
+
+ // cache
+ get_posts( array( 'include' => join( ',', array_unique( $post_ids ) ) ) );
+
+ $searches = array();
+ foreach ( $search_terms = stats_get_csv( 'searchterms', "days=$options[search]$csv_args[search]" ) as $search_term )
+ $searches[] = esc_html( $search_term['searchterm'] );
+
+?>
+<a class="button" href="admin.php?page=stats"><?php _e( 'View All', 'jetpack' ); ?></a>
+<div id="stats-info">
+ <div id="top-posts" class='stats-section'>
+ <div class="stats-section-inner">
+ <h4 class="heading"><?php _e( 'Top Posts' , 'jetpack' ); ?></h4>
+ <?php
+ if ( empty( $top_posts ) ) {
+ ?>
+ <p class="nothing"><?php _e( 'Sorry, nothing to report.', 'jetpack' ); ?></p>
+ <?php
+ } else {
+ foreach ( $top_posts as $post ) {
+ if ( !get_post( $post['post_id'] ) )
+ continue;
+ ?>
+ <p><?php printf(
+ $printf,
+ '<a href="' . get_permalink( $post['post_id'] ) . '">' . get_the_title( $post['post_id'] ) . '</a>',
+ number_format_i18n( $post['views'] )
+ ); ?></p>
+ <?php
+ }
+ }
+ ?>
+ </div>
+ </div>
+ <div id="top-search" class='stats-section'>
+ <div class="stats-section-inner">
+ <h4 class="heading"><?php _e( 'Top Searches' , 'jetpack' ); ?></h4>
+ <?php
+ if ( empty( $searches ) ) {
+ ?>
+ <p class="nothing"><?php _e( 'Sorry, nothing to report.', 'jetpack' ); ?></p>
+ <?php
+ } else {
+ ?>
+ <p><?php echo join( ',&nbsp; ', $searches );?></p>
+ <?php
+ }
+ ?>
+ </div>
+ </div>
+</div>
+<div class="clear"></div>
+<?php
+ exit;
+}
+
+function stats_get_csv( $table, $args = null ) {
+ $defaults = array( 'end' => false, 'days' => false, 'limit' => 3, 'post_id' => false, 'summarize' => '' );
+
+ $args = wp_parse_args( $args, $defaults );
+ $args['table'] = $table;
+ $args['blog_id'] = Jetpack::get_option( 'id' );
+
+ $stats_csv_url = add_query_arg( $args, 'http://stats.wordpress.com/csv.php' );
+
+ $key = md5( $stats_csv_url );
+
+ // Get cache
+ $stats_cache = get_option( 'stats_cache' );
+ if ( !$stats_cache || !is_array( $stats_cache ) )
+ $stats_cache = array();
+
+ // Return or expire this key
+ if ( isset( $stats_cache[$key] ) ) {
+ $time = key( $stats_cache[$key] );
+ if ( time() - $time < 300 )
+ return $stats_cache[$key][$time];
+ unset( $stats_cache[$key] );
+ }
+
+ $stats_rows = array();
+ do {
+ if ( !$stats = stats_get_remote_csv( $stats_csv_url ) )
+ break;
+
+ $labels = array_shift( $stats );
+
+ if ( 0 === stripos( $labels[0], 'error' ) )
+ break;
+
+ $stats_rows = array();
+ for ( $s = 0; isset( $stats[$s] ); $s++ ) {
+ $row = array();
+ foreach ( $labels as $col => $label )
+ $row[$label] = $stats[$s][$col];
+ $stats_rows[] = $row;
+ }
+ } while( 0 );
+
+ // Expire old keys
+ foreach ( $stats_cache as $k => $cache )
+ if ( !is_array( $cache ) || 300 < time() - key($cache) )
+ unset( $stats_cache[$k] );
+
+ // Set cache
+ $stats_cache[$key] = array( time() => $stats_rows );
+ update_option( 'stats_cache', $stats_cache );
+
+ return $stats_rows;
+}
+
+function stats_get_remote_csv( $url ) {
+ $method = 'GET';
+ $timeout = 90;
+ $user_id = 1; // means send the wp.com user_id, not 1
+
+ $get = Jetpack_Client::remote_request( compact( 'url', 'method', 'timeout', 'user_id' ) );
+ $get_code = wp_remote_retrieve_response_code( $get );
+ if ( is_wp_error( $get ) || ( 2 != intval( $get_code / 100 ) && 304 != $get_code ) || empty( $get['body'] ) ) {
+ return array(); // @todo: return an error?
+ } else {
+ return stats_str_getcsv( $get['body'] );
+ }
+}
+
+// rather than parsing the csv and its special cases, we create a new file and do fgetcsv on it.
+function stats_str_getcsv( $csv ) {
+ if ( !$temp = tmpfile() ) // tmpfile() automatically unlinks
+ return false;
+
+ $data = array();
+
+ fwrite( $temp, $csv, strlen( $csv ) );
+ fseek( $temp, 0 );
+ while ( false !== $row = fgetcsv( $temp, 2000 ) )
+ $data[] = $row;
+ fclose( $temp );
+
+ return $data;
+}
+
diff --git a/plugins/jetpack/modules/subscriptions.php b/plugins/jetpack/modules/subscriptions.php
new file mode 100644
index 00000000..f1ca95e6
--- /dev/null
+++ b/plugins/jetpack/modules/subscriptions.php
@@ -0,0 +1,610 @@
+<?php
+/**
+ * Module Name: Subscriptions
+ * Module Description: Allow users to subscribe to your posts and comments to receive a notification via email.
+ * Sort Order: 1
+ * First Introduced: 1.2
+ */
+class Jetpack_Subscriptions {
+ var $jetpack = false;
+
+ /**
+ * Singleton
+ * @static
+ */
+ function &init() {
+ static $instance = false;
+
+ if ( !$instance ) {
+ $instance = new Jetpack_Subscriptions;
+ }
+
+ return $instance;
+ }
+
+ function Jetpack_Subscriptions() {
+ $this->jetpack = Jetpack::init();
+
+ add_filter( 'jetpack_xmlrpc_methods', array( $this, 'xmlrpc_methods' ) );
+
+ // @todo remove sync from subscriptions and move elsewhere...
+
+ // Handle Posts
+ add_action( 'transition_post_status', array( $this, 'transition_post_status' ), 10, 3 );
+ add_action( 'trashed_post', array( $this, 'delete_post' ) );
+ add_action( 'delete_post', array( $this, 'delete_post' ) );
+
+ // Handle Taxonomy
+ add_action( 'created_term', array( $this, 'save_taxonomy'), 10, 3);
+ add_action( 'edited_term', array( $this, 'save_taxonomy'), 10, 3 );
+ add_action( 'delete_term', array( $this, 'delete_taxonomy'), 10, 3 );
+
+ // Handle Comments
+ add_action( 'wp_insert_comment', array( $this, 'save_comment' ), 10, 2 );
+ add_action( 'transition_comment_status', array( $this, 'transition_comment_status' ), 10, 3 );
+ add_action( 'trashed_comment', array( $this, 'delete_comment' ) );
+ add_action( 'delete_comment', array( $this, 'delete_comment' ) );
+
+ // Set up the subscription widget.
+ add_action( 'widgets_init', array( $this, 'widget_init' ) );
+
+ // Catch subscription widget submits
+ if ( isset( $_REQUEST['jetpack_subscriptions_widget'] ) )
+ add_action( 'template_redirect', array( $this, 'widget_submit' ) );
+
+ // Set up the comment subscription checkboxes
+ add_action( 'comment_form', array( $this, 'comment_subscribe_init' ) );
+
+ // Catch comment posts and check for subscriptions.
+ add_action( 'comment_post', array( $this, 'comment_subscribe_submit' ), 50, 2 );
+ }
+
+ function post_is_public( $the_post ) {
+ if ( !$post = get_post( $the_post ) ) {
+ return false;
+ }
+
+ return 'publish' === $post->post_status && strlen( (string) $post->post_password ) < 1;
+ }
+
+ function transition_post_status( $new, $old, $the_post ) {
+ if ( 'publish' == $old && 'publish' != $new ) {
+ // A published post was trashed or something else
+ $this->delete_post( $the_post->ID );
+ return;
+ }
+
+ clean_post_cache( $the_post->ID );
+
+ // Publish a new post
+ if (
+ 'publish' != $old
+ &&
+ $this->post_is_public( $the_post->ID )
+ &&
+ ( 'post' == $the_post->post_type || 'page' == $the_post->post_type )
+ ) {
+ $this->jetpack->sync->post( $the_post->ID );
+ }
+ }
+
+ function save_taxonomy( $term, $tt_id, $taxonomy = null ) {
+ if ( is_null( $taxonomy ) )
+ return;
+
+ $tax = get_term_by( 'id', $term, $taxonomy );
+ $this->jetpack->sync->taxonomy( $tax->slug, true, $taxonomy );
+ }
+
+ function delete_taxonomy( $term, $tt_id, $taxonomy ) {
+ $tags = get_terms( $taxonomy, array( 'hide_empty' => 0 ) ); // since we can't figure out what the slug is... we will do an array comparison on the remote site and remove old taxonomy...
+ $this->jetpack->sync->delete_taxonomy( $tags, $taxonomy );
+ }
+
+ function delete_post( $id ) {
+ $the_post = get_post( $id );
+ if ( 'post' == $the_post->post_type || 'page' == $the_post->post_type )
+ $this->jetpack->sync->delete_post( $id );
+ }
+
+ function save_comment( $id, $comment ) {
+ if ( !$this->post_is_public( $comment->comment_post_ID ) ) {
+ return;
+ }
+
+ if ( 1 == $comment->comment_approved ) {
+ $this->jetpack->sync->comment( $id );
+ }
+ }
+
+ function transition_comment_status( $new, $old, $the_comment ) {
+ if ( !$this->post_is_public( $the_comment->comment_post_ID ) ) {
+ return;
+ }
+
+ if ( 'approved' == $new ) {
+ $this->jetpack->sync->comment( $the_comment->comment_ID );
+ } else if ( 'approved' == $old && 'approved' != $new ) {
+ // Delete comments that are changing to anything but approved
+ $this->jetpack->sync->delete_comment( $the_comment->comment_ID );
+ }
+ }
+
+ function delete_comment( $id ) {
+ $this->jetpack->sync->delete_comment( $id );
+ }
+
+ /**
+ * Jetpack_Subscriptions::xmlrpc_methods()
+ *
+ * Register subscriptions methods with the Jetpack XML-RPC server.
+ * @param array $methods
+ */
+ function xmlrpc_methods( $methods ) {
+ return array_merge( $methods, array(
+ 'jetpack.subscriptions.subscribe' => array( $this, 'subscribe' ),
+ ) );
+ }
+
+ /**
+ * Jetpack_Subscriptions::configure()
+ *
+ * Jetpack Subscriptions configuration screen.
+ */
+ function configure() {
+ echo '<p>This is the configuration page for the Subscriptions Module.</p>';
+ }
+
+ /**
+ * Jetpack_Subscriptions::subscribe()
+ *
+ * Send a synchronous XML-RPC subscribe to blog posts or subscribe to post comments request.
+ *
+ * @param string $email
+ * @param array $post_ids (optional) defaults to 0 for blog posts only: array of post IDs to subscribe to blog's posts
+ * @param bool $async (optional) Should the subscription be performed asynchronously? Defaults to true.
+ *
+ * @return true|Jetpack_Error true on success
+ * invalid_email : not a valid email address
+ * invalid_post_id : not a valid post ID
+ * unknown_post_id : unknown post
+ * not_subscribed : strange error. Jetpack servers at WordPress.com could subscribe the email.
+ * disabled : Site owner has disabled subscriptions.
+ * active : Already subscribed.
+ * unknown : strange error. Jetpack servers at WordPress.com returned something malformed.
+ * unknown_status : strange error. Jetpack servers at WordPress.com returned something I didn't understand.
+ */
+ function subscribe( $email, $post_ids = 0, $async = true ) {
+ if ( !is_email( $email ) ) {
+ return new Jetpack_Error( 'invalid_email' );
+ }
+
+ if ( !$async ) {
+ Jetpack::load_xml_rpc_client();
+ $xml = new Jetpack_IXR_ClientMulticall();
+ }
+
+ foreach( (array) $post_ids as $post_id ) {
+ $post_id = (int) $post_id;
+ if ( $post_id < 0 ) {
+ return new Jetpack_Error( 'invalid_post_id' );
+ } else if ( $post_id && !$post = get_post( $post_id ) ) {
+ return new Jetpack_Error( 'unknown_post_id' );
+ }
+
+ if ( $async ) {
+ Jetpack::xmlrpc_async_call( 'jetpack.subscribeToSite', $email, $post_id );
+ } else {
+ $xml->addCall( 'jetpack.subscribeToSite', $email, $post_id );
+ }
+ }
+
+ if ( $async ) {
+ return;
+ }
+
+ // Call
+ $xml->query();
+
+ if ( $xml->isError() ) {
+ return $xml->get_jetpack_error();
+ }
+
+ $responses = $xml->getResponse();
+
+ $r = array();
+ foreach( (array) $responses as $response ) {
+ if ( isset( $response['faultCode'] ) || isset( $response['faultString'] ) ) {
+ $r[] = $xml->get_jetpack_error( $response['faultCode'], $response['faultString'] );
+ continue;
+ }
+
+ if ( !is_array( $response[0] ) || empty( $response[0]['status'] ) ) {
+ $r[] = new Jetpack_Error( 'unknown' );
+ continue;
+ }
+
+ switch ( $response[0]['status'] ) {
+ case 'error' :
+ $r[] = new Jetpack_Error( 'not_subscribed' );
+ continue 2;
+ case 'disabled' :
+ $r[] = new Jetpack_Error( 'disabled' );
+ continue 2;
+ case 'active' :
+ $r[] = new Jetpack_Error( 'active' );
+ continue 2;
+ case 'pending' :
+ $r[] = true;
+ continue 2;
+ default :
+ $r[] = new Jetpack_Error( 'unknown_status', (string) $response[0]['status'] );
+ continue 2;
+ }
+ }
+
+ return $r;
+ }
+
+ /**
+ * Jetpack_Subscriptions::widget_init()
+ *
+ * Initialize and register the Jetpack Subscriptions widget.
+ */
+ function widget_init() {
+ register_widget( 'Jetpack_Subscriptions_Widget' );
+ }
+
+ /**
+ * Jetpack_Subscriptions::widget_submit()
+ *
+ * When a user submits their email via the blog subscription widget, check the details and call the subsribe() method.
+ */
+ function widget_submit() {
+ // Check the nonce.
+ check_admin_referer( 'blogsub_subscribe_' . get_current_blog_id() );
+
+ if ( empty( $_REQUEST['email'] ) )
+ return false;
+
+ $redirect_fragment = false;
+ if ( isset( $_REQUEST['redirect_fragment'] ) ) {
+ $redirect_fragment = preg_replace( '/[^a-z0-9_-]/i', '', $_REQUEST['redirect_fragment'] );
+ }
+ if ( !$redirect_fragment ) {
+ $redirect_fragment = 'subscribe-blog';
+ }
+
+ $subscribe = Jetpack_Subscriptions::subscribe( $_REQUEST['email'], 0, false );
+
+ if ( is_wp_error( $subscribe ) ) {
+ $error = $subscribe->get_error_code();
+ } else {
+ $error = false;
+ foreach ( $subscribe as $response ) {
+ if ( is_wp_error( $response ) ) {
+ $error = $response->get_error_code();
+ break;
+ }
+ }
+ }
+
+ if ( $error ) {
+ switch( $error ) {
+ case 'invalid_email':
+ $redirect = add_query_arg( 'subscribe', 'invalid_email' );
+ break;
+ case 'active': case 'pending':
+ $redirect = add_query_arg( 'subscribe', 'already' );
+ break;
+ default:
+ $redirect = add_query_arg( 'subscribe', 'error' );
+ break;
+ }
+ } else {
+ $redirect = add_query_arg( 'subscribe', 'success' );
+ }
+
+ wp_safe_redirect( "$redirect#$redirect_fragment" );
+ exit;
+ }
+
+ /**
+ * Jetpack_Subscriptions::comment_subscribe_init()
+ *
+ * Set up and add the comment subscription checkbox to the comment form.
+ */
+ function comment_subscribe_init() {
+ global $post;
+
+ $comments_checked = '';
+ $blog_checked = '';
+
+ // Check for a comment / blog submission and set a cookie to retain the setting and check the boxes.
+ if ( isset( $_COOKIE[ 'jetpack_comments_subscribe_' . COOKIEHASH ] ) && $_COOKIE[ 'jetpack_comments_subscribe_' . COOKIEHASH ] == $post->ID )
+ $comments_checked = ' checked="checked"';
+
+ if ( isset( $_COOKIE[ 'jetpack_blog_subscribe_' . COOKIEHASH ] ) )
+ $blog_checked = ' checked="checked"';
+
+ // Some themes call this function, don't show the checkbox again
+ remove_action( 'comment_form', 'subscription_comment_form' );
+
+ // Check if Mark Jaquith's Subscribe to Comments plugin is active - if so, suppress Jetpack checkbox
+
+ $str = '';
+
+ if ( FALSE === has_filter( 'comment_form', 'show_subscription_checkbox' ) ) {
+ // Subscribe to comments checkbox
+ $str .= '<p class="comment-subscription-form"><input type="checkbox" name="subscribe_comments" id="subscribe_comments" value="subscribe" style="width: auto; -moz-appearance: checkbox; -webkit-appearance: checkbox;"' . $comments_checked . ' /> ';
+ $str .= '<label class="subscribe-label" id="subscribe-label" for="subscribe_comments">' . __( 'Notify me of follow-up comments by email.', 'jetpack' ) . '</label>';
+ $str .= '</p>';
+ }
+
+ // Subscribe to blog checkbox
+ $str .= '<p class="comment-subscription-form"><input type="checkbox" name="subscribe_blog" id="subscribe_blog" value="subscribe" style="width: auto; -moz-appearance: checkbox; -webkit-appearance: checkbox;"' . $blog_checked . ' /> ';
+ $str .= '<label class="subscribe-label" id="subscribe-blog-label" for="subscribe_blog">' . __( 'Notify me of new posts by email.', 'jetpack' ) . '</label>';
+ $str .= '</p>';
+
+ echo apply_filters( 'jetpack_comment_subscription_form', $str );
+ }
+
+ /**
+ * Jetpack_Subscriptions::comment_subscribe_init()
+ *
+ * When a user checks the comment subscribe box and submits a comment, subscribe them to the comment thread.
+ */
+ function comment_subscribe_submit( $comment_id, $approved ) {
+ if ( 'spam' === $approved ) {
+ return;
+ }
+
+ // Set cookies for this post/comment
+ $this->set_cookies( isset( $_REQUEST['subscribe_comments'] ), isset( $_REQUEST['subscribe_blog'] ) );
+
+ if ( !isset( $_REQUEST['subscribe_comments'] ) && !isset( $_REQUEST['subscribe_blog'] ) )
+ return;
+
+ $comment = get_comment( $comment_id );
+ $post_ids = array();
+
+ if ( isset( $_REQUEST['subscribe_comments'] ) )
+ $post_ids[] = $comment->comment_post_ID;
+
+ if ( isset( $_REQUEST['subscribe_blog'] ) )
+ $post_ids[] = 0;
+
+ Jetpack_Subscriptions::subscribe( $comment->comment_author_email, $post_ids );
+ }
+
+ /**
+ * Jetpack_Subscriptions::set_cookies()
+ *
+ * Set a cookie to save state on the comment and post subscription checkboxes.
+ */
+ function set_cookies( $comments = true, $posts = true ) {
+ global $post;
+
+ $cookie_lifetime = apply_filters( 'comment_cookie_lifetime', 30000000 );
+
+ if ( $comments )
+ setcookie( 'jetpack_comments_subscribe_' . COOKIEHASH, $post->ID, time() + $cookie_lifetime, COOKIEPATH, COOKIE_DOMAIN );
+ else
+ setcookie( 'jetpack_comments_subscribe_' . COOKIEHASH, '', time() - 3600, COOKIEPATH, COOKIE_DOMAIN );
+
+ if ( $posts )
+ setcookie( 'jetpack_blog_subscribe_' . COOKIEHASH, 1, time() + $cookie_lifetime, COOKIEPATH, COOKIE_DOMAIN );
+ else
+ setcookie( 'jetpack_blog_subscribe_' . COOKIEHASH, '', time() - 3600, COOKIEPATH, COOKIE_DOMAIN );
+ }
+}
+
+Jetpack_Subscriptions::init();
+
+
+/***
+ * Blog Subscription Widget
+ */
+
+class Jetpack_Subscriptions_Widget extends WP_Widget {
+ function Jetpack_Subscriptions_Widget() {
+ $widget_ops = array( 'classname' => 'jetpack_subscription_widget', 'description' => __( 'Add an email signup form to allow people to subscribe to your blog.', 'jetpack' ) );
+ $control_ops = array( 'width' => 300 );
+
+ $this->WP_Widget( 'blog_subscription', __( 'Blog Subscriptions (Jetpack)', 'jetpack' ), $widget_ops, $control_ops );
+ }
+
+ function widget( $args, $instance ) {
+ global $current_user;
+
+ $source = 'widget';
+
+ extract( $args );
+
+ $instance = wp_parse_args( (array) $instance, $this->defaults() );
+ $title = stripslashes( $instance['title'] );
+ $subscribe_text = stripslashes( $instance['subscribe_text'] );
+ $subscribe_button = stripslashes( $instance['subscribe_button'] );
+ $subscribe_logged_in = stripslashes( $instance['subscribe_logged_in'] );
+ $show_subscribers_total = (bool) $instance['show_subscribers_total'];
+ $subscribers_total = $this->fetch_subscriber_count();
+
+ if ( ! is_array( $subscribers_total ) )
+ $show_subscribers_total = FALSE;
+
+ echo $before_widget;
+ echo $before_title . '<label for="subscribe-field">' . esc_attr( $instance['title'] ) . '</label>' . $after_title . "\n";
+
+ $referer = ( is_ssl() ? 'https' : 'http' ) . '://' . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI'];
+
+ // Check for subscription confirmation.
+ if ( isset( $_GET['subscribe'] ) && 'success' == $_GET['subscribe'] ) {
+ ?>
+
+ <div class="success">
+ <p><?php _e( 'An email was just sent to confirm your subscription. Please find the email now and click activate to start subscribing.', 'jetpack' ); ?></p>
+ </div>
+
+ <?php
+ }
+
+ // Display any errors
+ if ( isset( $_GET['subscribe'] ) ) :
+ switch ( $_GET['subscribe'] ) :
+ case 'invalid_email' : ?>
+ <p class="error"><?php _e( 'The email you entered was invalid, please check and try again.', 'jetpack' ); ?></p>
+ <?php break;
+ case 'already' : ?>
+ <p class="error"><?php _e( 'You have already subscribed to this site, please check your inbox.', 'jetpack' ); ?></p>
+ <?php break;
+ case 'success' :
+
+ echo wpautop( $subscribe_text );
+ break;
+ default : ?>
+ <p class="error"><?php _e( 'There was an error when subscribing, please try again.', 'jetpack' ) ?></p>
+ <?php break;
+ endswitch;
+ endif;
+
+ $email_address = '';
+ if ( ! empty( $current_user->user_email ) )
+ $email_address = $current_user->user_email;
+
+ // Display a subscribe form ?>
+ <a name="subscribe-blog"></a>
+ <form action="" method="post" accept-charset="utf-8" id="subscribe-blog">
+ <?php
+ if ( ! isset ( $_GET['subscribe'] ) ) {
+ ?><p><?php echo $subscribe_text ?></p><?php
+ }
+
+ if ( $show_subscribers_total && $subscribers_total['value'] > 0 ) {
+ echo wpautop( sprintf( _n( 'Join %s other subscriber', 'Join %s other subscribers', $subscribers_total['value'], 'jetpack' ), number_format_i18n( $subscribers_total['value'] ) ) );
+ }
+ ?>
+
+ <p><input type="text" name="email" style="width: 95%; padding: 1px 2px" value="<?php if ( !empty( $email_address ) ) { echo $email_address; } else { _e( 'Email Address', 'jetpack' ); } ?>" id="subscribe-field" onclick="if ( this.value == '<?php _e( 'Email Address', 'jetpack' ) ?>' ) { this.value = ''; }" onblur="if ( this.value == '' ) { this.value = '<?php _e( 'Email Address', 'jetpack' ) ?>'; }" /></p>
+
+ <p>
+ <input type="hidden" name="action" value="subscribe" />
+ <input type="hidden" name="source" value="<?php echo esc_url( $referer ); ?>" />
+ <input type="hidden" name="sub-type" value="<?php echo esc_attr( $source ); ?>" />
+ <input type="hidden" name="redirect_fragment" value="<?php echo esc_attr( $widget_id ); ?>" />
+ <?php wp_nonce_field( 'blogsub_subscribe_'. get_current_blog_id(), '_wpnonce', false ); ?>
+ <input type="submit" value="<?php echo esc_attr( $subscribe_button ); ?>" name="jetpack_subscriptions_widget" />
+ </p>
+ </form>
+
+ <?php
+
+ echo "\n" . $after_widget;
+ }
+
+ function increment_subscriber_count( $current_subs_array = array() ) {
+ $current_subs_array['value']++;
+
+ set_transient( 'wpcom_subscribers_total', $current_subs_array, 3600 ); // try to cache the result for at least 1 hour
+
+ return $current_subs_array;
+ }
+
+ function fetch_subscriber_count() {
+ $subs_count = get_transient( 'wpcom_subscribers_total' );
+
+ if ( FALSE === $subs_count || 'failed' == $subs_count['status'] ) {
+ Jetpack:: load_xml_rpc_client();
+
+ $xml = new Jetpack_IXR_Client( array(
+ 'user_id' => $GLOBALS['current_user']->ID
+ ) );
+
+ $xml->query( 'jetpack.fetchSubscriberCount' );
+
+ if ( $xml->isError() ) { // if we get an error from .com, set the status to failed so that we will try again next time the data is requested
+ $subs_count = array(
+ 'status' => 'failed',
+ 'code' => $xml->getErrorCode(),
+ 'message' => $xml->getErrorMessage(),
+ 'value' => ( isset( $subs_count['value'] ) ) ? $subs_count['value'] : 0,
+ );
+ } else {
+ $subs_count = array(
+ 'status' => 'success',
+ 'value' => $xml->getResponse(),
+ );
+ }
+
+ set_transient( 'wpcom_subscribers_total', $subs_count, 3600 ); // try to cache the result for at least 1 hour
+ }
+
+ return $subs_count;
+ }
+
+ function update( $new_instance, $old_instance ) {
+ $instance = $old_instance;
+
+ $instance['title'] = strip_tags( stripslashes( $new_instance['title'] ) );
+ $instance['subscribe_text'] = wp_filter_post_kses( stripslashes( $new_instance['subscribe_text'] ) );
+ $instance['subscribe_logged_in'] = wp_filter_post_kses( stripslashes( $new_instance['subscribe_logged_in'] ) );
+ $instance['subscribe_button'] = strip_tags( stripslashes( $new_instance['subscribe_button'] ) );
+ $instance['show_subscribers_total'] = isset( $new_instance['show_subscribers_total'] ) && $new_instance['show_subscribers_total'];
+
+ return $instance;
+ }
+
+ function defaults() {
+ return array(
+ 'title' => __( 'Subscribe to Blog via Email', 'jetpack' ),
+ 'subscribe_text' => __( 'Enter your email address to subscribe to this blog and receive notifications of new posts by email.', 'jetpack' ),
+ 'subscribe_button' => __( 'Subscribe', 'jetpack' ),
+ 'subscribe_logged_in' => __( 'Click to subscribe to this blog and receive notifications of new posts by email.', 'jetpack' ),
+ 'show_subscribers_total' => true,
+ );
+ }
+
+ function form( $instance ) {
+ $instance = wp_parse_args( (array) $instance, $this->defaults() );
+
+ $title = esc_attr( stripslashes( $instance['title'] ) );
+ $subscribe_text = esc_attr( stripslashes( $instance['subscribe_text'] ) );
+ $subscribe_button = esc_attr( stripslashes( $instance['subscribe_button'] ) );
+ $show_subscribers_total = checked( $instance['show_subscribers_total'], true, false );
+
+ $subs_fetch = $this->fetch_subscriber_count();
+
+ if ( 'failed' == $subs_fetch['status'] ) {
+ printf( '<div class="error inline"><p>' . __( '%s: %s', 'jetpack' ) . '</p></div>', esc_html( $subs_fetch['code'] ), esc_html( $subs_fetch['message'] ) );
+
+ }
+ $subscribers_total = number_format_i18n( $subs_fetch['value'] );
+
+?>
+<p>
+ <label for="<?php echo $this->get_field_id( 'title' ); ?>">
+ <?php _e( 'Widget title:', 'jetpack' ); ?>
+ <input class="widefat" id="<?php echo $this->get_field_id( 'title' ); ?>" name="<?php echo $this->get_field_name( 'title' ); ?>" type="text" value="<?php echo esc_attr( $title ); ?>" />
+ </label>
+</p>
+<p>
+ <label for="<?php echo $this->get_field_id( 'subscribe_text' ); ?>">
+ <?php _e( 'Optional text to display to your readers:', 'jetpack' ); ?>
+ <textarea style="width: 95%" id="<?php echo $this->get_field_id( 'subscribe_text' ); ?>" name="<?php echo $this->get_field_name( 'subscribe_text' ); ?>" type="text"><?php echo esc_html( $subscribe_text ); ?></textarea>
+ </label>
+</p>
+<p>
+ <label for="<?php echo $this->get_field_id( 'subscribe_button' ); ?>">
+ <?php _e( 'Subscribe Button:', 'jetpack' ); ?>
+ <input class="widefat" id="<?php echo $this->get_field_id( 'subscribe_button' ); ?>" name="<?php echo $this->get_field_name( 'subscribe_button' ); ?>" type="text" value="<?php echo esc_attr( $subscribe_button ); ?>" />
+ </label>
+</p>
+<p>
+ <label for="<?php echo $this->get_field_id( 'show_subscribers_total' ); ?>">
+ <input type="checkbox" id="<?php echo $this->get_field_id( 'show_subscribers_total' ); ?>" name="<?php echo $this->get_field_name( 'show_subscribers_total' ); ?>" value="1"<?php echo $show_subscribers_total; ?> />
+ <?php echo esc_html( sprintf( _n( 'Show total number of subscribers? (%s subscriber)', 'Show total number of subscribers? (%s subscribers)', $subscribers_total, 'jetpack' ), number_format_i18n( $subscribers_total ) ) ); ?>
+ </label>
+</p>
+<?php
+ }
+}
+
diff --git a/plugins/jetpack/modules/vaultpress.php b/plugins/jetpack/modules/vaultpress.php
new file mode 100644
index 00000000..4b6b70e1
--- /dev/null
+++ b/plugins/jetpack/modules/vaultpress.php
@@ -0,0 +1,28 @@
+<?php
+/**
+ * Module Name: VaultPress
+ * Module Description: Realtime backup and security scanning for your WordPress site.
+ * First Introduced: 0:1.2
+ * Sort Order: 2
+ * Deactivate: false
+ * Free: false
+ */
+
+add_action( 'jetpack_modules_loaded', 'vaultpress_jetpack_stub' );
+
+function vaultpress_jetpack_stub() {
+ if ( class_exists( 'VaultPress' ) || function_exists( 'vaultpress_contact_service' ) ) {
+ Jetpack::enable_module_configurable( __FILE__ );
+ Jetpack::module_configuration_load( __FILE__, 'vaultpress_jetpack_configure' );
+ add_filter( 'jetpack_module_free_text_vaultpress', 'vaultpress_jetpack_module_free_text' );
+ }
+}
+
+function vaultpress_jetpack_module_free_text() {
+ return __( 'Active', 'jetpack' );
+}
+
+function vaultpress_jetpack_configure() {
+ wp_safe_redirect( menu_page_url( 'vaultpress', false ) );
+ exit;
+}
diff --git a/plugins/jetpack/modules/widgets.php b/plugins/jetpack/modules/widgets.php
new file mode 100644
index 00000000..d12c8b67
--- /dev/null
+++ b/plugins/jetpack/modules/widgets.php
@@ -0,0 +1,33 @@
+<?php
+/**
+ * Module Name: Extra Sidebar Widgets
+ * Module Description: Easily add images, Twitter updates, and your site's RSS links to your theme's sidebar.
+ * Sort Order: 20
+ * First Introduced: 1.2
+ */
+
+function jetpack_load_widgets() {
+ foreach ( Jetpack::glob_php( dirname( __FILE__ ) . '/widgets' ) as $file ) {
+ include $file;
+ }
+}
+
+add_action( 'jetpack_modules_loaded', 'jetpack_widgets_loaded' );
+
+function jetpack_widgets_loaded() {
+ Jetpack::enable_module_configurable( __FILE__ );
+ Jetpack::module_configuration_load( __FILE__, 'jetpack_widgets_configuration_load' );
+}
+
+function jetpack_widgets_configuration_load() {
+ wp_safe_redirect( admin_url( 'widgets.php' ) );
+ exit;
+}
+
+jetpack_load_widgets();
+
+add_action( 'widgets_init', 'facebook_likebox_widget_init' );
+
+function facebook_likebox_widget_init() {
+ register_widget( 'WPCOM_Widget_Facebook_LikeBox' );
+}
diff --git a/plugins/jetpack/modules/widgets/facebook-likebox.php b/plugins/jetpack/modules/widgets/facebook-likebox.php
new file mode 100644
index 00000000..ebf6e9b5
--- /dev/null
+++ b/plugins/jetpack/modules/widgets/facebook-likebox.php
@@ -0,0 +1,253 @@
+<?php
+
+/**
+ * Facebook Like Box widget class
+ * Display a Facebook Like Box as a widget
+ * http://developers.facebook.com/docs/reference/plugins/like-box/
+ */
+class WPCOM_Widget_Facebook_LikeBox extends WP_Widget {
+
+ private $default_height = 432;
+ private $default_width = 200;
+ private $max_width = 400;
+ private $min_width = 100;
+ private $default_colorscheme = 'light';
+ private $allowed_colorschemes = array( 'light', 'dark' );
+
+ function __construct() {
+ parent::__construct( 'facebook-likebox', __( 'Facebook Like Box', 'jetpack' ), array( 'classname' => 'widget_facebook_likebox', 'description' => __( 'Display a Facebook Like Box to connect visitors to your Facebook Page', 'jetpack' ) ) );
+ }
+
+ function widget( $args, $instance ) {
+
+ extract( $args );
+
+ $like_args = $this->normalize_facebook_args( $instance['like_args'] );
+
+ if( empty( $like_args['href'] ) || ! $this->is_valid_facebook_url( $like_args['href'] ) ) {
+ echo '<!-- Invalid Facebook Page URL -->';
+ return;
+ }
+
+
+ $title = apply_filters( 'widget_title', $instance['title'] );
+ $page_url = ( is_ssl() ) ? str_replace( 'http://', 'https://', $like_args['href'] ) : $like_args['href'];
+
+ // Calculate the height based on the features enabled
+ if( $like_args['show_faces'] && $like_args['stream'] ) {
+ $like_args['height'] = 580;
+ } else if( ! $like_args['show_faces'] && ! $like_args['stream'] ) {
+ $like_args['height'] = 110;
+ } else {
+ $like_args['height'] = 432;
+ }
+
+ $like_args['show_faces'] = (bool) $like_args['show_faces'] ? 'true' : 'false';
+ $like_args['stream'] = (bool) $like_args['stream'] ? 'true' : 'false';
+ $like_args['force_wall'] = (bool) $like_args['force_wall'] ? 'true' : 'false';
+ $like_args['header'] = (bool) $like_args['header'] ? 'true' : 'false';
+ $like_bg_colour = ( 'dark' == $like_args['colorscheme'] ) ? '#000' : '#fff';
+
+ $locale = $this->get_locale();
+ if( $locale && 'en_US' != $locale )
+ $like_args['locale'] = $locale;
+
+ $like_args = urlencode_deep( $like_args );
+ $like_url = add_query_arg( $like_args, sprintf( '%swww.facebook.com/plugins/likebox.php', ( is_ssl() ) ? 'https://' : 'http://' ) );
+
+ echo $before_widget;
+
+ if( ! empty( $title ) ) :
+ echo $before_title;
+ ?><a href="<?php echo esc_url( $page_url ); ?>"><?php esc_html_e( $title , 'jetpack' ); ?></a><?php
+ echo $after_title;
+ endif;
+
+ ?><iframe src="<?php echo esc_url( $like_url ); ?>" scrolling="no" frameborder="0" style="border: none; overflow: hidden; width: <?php esc_html_e( $like_args['width'] , 'jetpack' ); ?>px; height: <?php esc_html_e( $like_args['height'] , 'jetpack' ); ?>px; background: <?php esc_html_e( $like_bg_colour , 'jetpack' ); ?>"></iframe><?php
+
+ echo $after_widget;
+
+ do_action( 'jetpack_stats_extra', 'widget', 'facebook-likebox' );
+ }
+
+ function update( $new_instance, $old_instance ) {
+ $instance = array(
+ 'title' => '',
+ 'like_args' => $this->get_default_args(),
+ );
+
+ $instance['title'] = trim( strip_tags( stripslashes( $new_instance['title'] ) ) );
+
+ // Set up widget values
+ $instance['like_args'] = array(
+ 'href' => trim( strip_tags( stripslashes( $new_instance['href'] ) ) ),
+ 'width' => (int) $new_instance['width'],
+ 'height' => (int) $new_instance['height'],
+ 'colorscheme' => $new_instance['colorscheme'],
+ 'show_faces' => (bool) $new_instance['show_faces'],
+ 'stream' => (bool) $new_instance['stream'],
+ 'header' => false, // The header just displays "Find us on Facebook"; it's redundant with the title
+ 'force_wall' => (bool) $new_instance['force_wall'],
+ );
+
+ $instance['like_args'] = $this->normalize_facebook_args( $instance['like_args'] );
+
+ return $instance;
+ }
+
+ function form( $instance ) {
+ $instance = wp_parse_args( (array) $instance, array(
+ 'title' => '',
+ 'like_args' => $this->get_default_args()
+ ) );
+ $like_args = $this->normalize_facebook_args( $instance['like_args'] );
+ ?>
+
+ <p>
+ <label for="<?php echo $this->get_field_id( 'title' ); ?>">
+ <?php _e( 'Title', 'jetpack' ); ?>
+ <input type="text" name="<?php echo $this->get_field_name( 'title' ); ?>" id="<?php echo $this->get_field_id( 'title' ); ?>" value="<?php echo esc_attr( $instance['title'] ); ?>" class="widefat" />
+ </label>
+ </p>
+
+ <p>
+ <label for="<?php echo $this->get_field_id( 'href' ); ?>">
+ <?php _e( 'Facebook Page URL', 'jetpack' ); ?>
+ <input type="text" name="<?php echo $this->get_field_name( 'href' ); ?>" id="<?php echo $this->get_field_id( 'href' ); ?>" value="<?php echo esc_url( $like_args['href'] ); ?>" class="widefat" />
+ <br />
+ <small><?php _e( 'The Like Box only works with <a href="http://www.facebook.com/help/?faq=174987089221178">Facebook Pages</a>.', 'jetpack' ); ?></small>
+ </label>
+ </p>
+
+ <p>
+ <label for="<?php echo $this->get_field_id( 'width' ); ?>">
+ <?php _e( 'Width', 'jetpack' ); ?>
+ <input type="text" maxlength="3" name="<?php echo $this->get_field_name( 'width' ); ?>" id="<?php echo $this->get_field_id( 'width' ); ?>" value="<?php echo esc_attr( $like_args['width'] ); ?>" style="width: 30px; text-align: center;" />
+ </label>
+ </p>
+
+ <p>
+ <label for="<?php echo $this->get_field_id( 'colorscheme' ); ?>">
+ <?php _e( 'Color Scheme', 'jetpack' ); ?>
+ <select name="<?php echo $this->get_field_name( 'colorscheme' ); ?>" id="<?php echo $this->get_field_id( 'colorscheme' ); ?>">
+ <option value="light" <?php selected( $like_args['colorscheme'], 'light' ); ?>><?php _e( 'Light', 'jetpack' ); ?></option>
+ <option value="dark" <?php selected( $like_args['colorscheme'], 'dark' ); ?>><?php _e( 'Dark', 'jetpack' ); ?></option>
+ </select>
+ </label>
+ </p>
+
+
+ <p>
+ <label for="<?php echo $this->get_field_id( 'show_faces' ); ?>">
+ <input type="checkbox" name="<?php echo $this->get_field_name( 'show_faces' ); ?>" id="<?php echo $this->get_field_id( 'show_faces' ); ?>" <?php checked( $like_args['show_faces'] ); ?> />
+ <?php _e( 'Show Faces', 'jetpack' ); ?>
+ <br />
+ <small><?php _e( 'Show profile photos in the plugin.', 'jetpack' ); ?></small>
+ </label>
+ </p>
+
+ <p>
+ <label for="<?php echo $this->get_field_id( 'stream' ); ?>">
+ <input type="checkbox" name="<?php echo $this->get_field_name( 'stream' ); ?>" id="<?php echo $this->get_field_id( 'stream' ); ?>" <?php checked( $like_args['stream'] ); ?> />
+ <?php _e( 'Show Stream', 'jetpack' ); ?>
+ <br />
+ <small><?php _e( 'Show the profile stream for the public profile.', 'jetpack' ); ?></small>
+ </label>
+ </p>
+
+ <p>
+ <label for="<?php echo $this->get_field_id( 'force_wall' ); ?>">
+ <input type="checkbox" name="<?php echo $this->get_field_name( 'force_wall' ); ?>" id="<?php echo $this->get_field_id( 'force_wall' ); ?>" <?php checked( $like_args['force_wall'] ); ?> />
+ <?php _e( 'Show Wall', 'jetpack' ); ?>
+ <br />
+ <small><?php _e( 'Show the wall for a Places page rather than friend activity.', 'jetpack' ); ?></small>
+ </label>
+ </p>
+
+ <?php
+ }
+
+ function get_default_args() {
+ $defaults = array(
+ 'href' => '',
+ 'width' => $this->default_width,
+ 'height' => $this->default_height,
+ 'colorscheme' => $this->default_colorscheme,
+ 'show_faces' => true,
+ 'stream' => false,
+ 'header' => false,
+ 'force_wall' => false,
+ );
+
+ return apply_filters( 'jetpack_facebook_likebox_defaults', $defaults );
+ }
+
+ function normalize_facebook_args( $args ) {
+ $args = wp_parse_args( (array) $args, $this->get_default_args() );
+
+ // Validate the Facebook Page URL
+ if ( $this->is_valid_facebook_url( $args['href'] ) ) {
+ $temp = explode( '?', $args['href'] );
+ $args['href'] = str_replace( array( 'http://facebook.com', 'https://facebook.com' ), array( 'http://www.facebook.com', 'https://www.facebook.com' ), $temp[0] );
+ } else {
+ $args['href'] = '';
+ }
+
+ $args['width'] = $this->normalize_int_value( (int) $args['width'], $this->default_width, $this->max_width, $this->min_width );
+ $args['colorscheme'] = $this->normalize_text_value( $args['colorscheme'], $this->default_colorscheme, $this->allowed_colorschemes );
+ $args['show_faces'] = (bool) $args['show_faces'];
+ $args['stream'] = (bool) $args['stream'];
+ $args['force_wall'] = (bool) $args['force_wall'];
+
+ return $args;
+ }
+
+ function is_valid_facebook_url( $url ) {
+ return ( FALSE !== strpos( $url, 'facebook.com' ) ) ? TRUE : FALSE;
+ }
+
+ function normalize_int_value( $value, $default = 0, $max = 0, $min = 0 ) {
+ $value = (int) $value;
+
+ if( ! $value || $max < $value || $min > $value )
+ $value = $default;
+
+ return (int) $value;
+ }
+
+ function normalize_text_value( $value, $default = '', $allowed = array() ) {
+ $allowed = (array) $allowed;
+
+ if( empty( $value ) || ( ! empty( $allowed ) && ! in_array( $value, $allowed ) ) )
+ $value = $default;
+
+ return $value;
+ }
+
+ function guess_locale_from_lang( $lang ) {
+ $lang = strtolower( str_replace( '-', '_', $lang ) );
+
+ if ( 5 == strlen( $lang ) ) {
+ $lang = substr( $lang, 0, 3 ) . strtoupper( substr( $lang, 3, 2 ) );
+ } else if ( 3 == strlen( $lang ) ) {
+ $lang = $lang;
+ } else {
+ $lang = $lang . '_' . strtoupper( $lang );
+ }
+
+ if ( 'en_EN' == $lang ) {
+ $lang = 'en_US';
+ } else if ( 'he_HE' == $lang ) {
+ $lang = 'he_IL';
+ } else if ( 'ja_JA' == $lang )
+ $lang = 'ja_JP';
+
+ return $lang;
+ }
+
+ function get_locale() {
+ return $this->guess_locale_from_lang( get_locale() );
+ }
+}
+
+// END
diff --git a/plugins/jetpack/modules/widgets/image-widget.php b/plugins/jetpack/modules/widgets/image-widget.php
new file mode 100644
index 00000000..0e7e227b
--- /dev/null
+++ b/plugins/jetpack/modules/widgets/image-widget.php
@@ -0,0 +1,156 @@
+<?php
+/**
+ * Module Name: Image Widget
+ * Module Description: Easily add images to your theme's sidebar.
+ * Sort Order: 20
+ * First Introduced: 1.2
+ */
+
+class Jetpack_Image_Widget extends WP_Widget {
+
+ function Jetpack_Image_Widget() {
+ $widget_ops = array( 'classname' => 'widget_image', 'description' => __( "Display an image in your sidebar", 'jetpack' ) );
+ $control_ops = array( 'width' => 400 );
+ $this->WP_Widget( 'image', __( 'Image (Jetpack)', 'jetpack' ), $widget_ops, $control_ops );
+ }
+
+ function widget( $args, $instance ) {
+ extract( $args );
+
+ echo $before_widget;
+
+ $title = apply_filters( 'widget_title', $instance['title'] );
+
+ if ( $title )
+ echo $before_title . esc_html( $title ) . $after_title;
+
+ if ( '' != $instance['img_url'] ) {
+
+ $output = '<img src="' . esc_attr( $instance['img_url'] ) .'" ';
+ if ( '' != $instance['alt_text'] )
+ $output .= 'alt="' . esc_attr( $instance['alt_text'] ) .'" ';
+ if ( '' != $instance['img_title'] )
+ $output .= 'title="' . esc_attr( $instance['img_title'] ) .'" ';
+ if ( '' == $instance['caption'] )
+ $output .= 'class="align' . esc_attr( $instance['align'] ) . '" ';
+ if ( '' != $instance['img_width'] )
+ $output .= 'width="' . esc_attr( $instance['img_width'] ) .'" ';
+ if ( '' != $instance['img_height'] )
+ $output .= 'height="' . esc_attr( $instance['img_height'] ) .'" ';
+ $output .= '/>';
+ if ( '' != $instance['link'] )
+ $output = '<a href="' . esc_attr( $instance['link'] ) . '">' . $output . '</a>';
+ if ( '' != $instance['caption'] )
+ $output = '[caption align="align' . esc_attr( $instance['align'] ) . '" width="' . esc_attr( $instance['img_width'] ) .'" caption="' . esc_attr( $instance['caption'] ) . '"]' . $output . '[/caption]';
+
+ echo '<div style="overflow:hidden;">' . do_shortcode( $output ) . '</div>';
+ }
+
+ echo "\n" . $after_widget;
+ }
+
+ function update( $new_instance, $old_instance ) {
+ $instance = $old_instance;
+
+ $instance['title'] = strip_tags( $new_instance['title'] );
+ $instance['img_url'] = esc_url( $new_instance['img_url'], null, 'display' );
+ $instance['alt_text'] = strip_tags( $new_instance['alt_text'] );
+ $instance['img_title'] = strip_tags( $new_instance['img_title'] );
+ $instance['caption'] = strip_tags( $new_instance['caption'] );
+ $instance['align'] = $new_instance['align'];
+ $instance['img_width'] = absint( $new_instance['img_width'] );
+ $instance['img_height'] = absint( $new_instance['img_height'] );
+ $instance['link'] = esc_url( $new_instance['link'], null, 'display' );
+
+ return $instance;
+ }
+
+ function form( $instance ) {
+ // Defaults
+ $instance = wp_parse_args( (array) $instance, array( 'title' => '', 'img_url' => '', 'alt_text' => '', 'img_title' => '', 'caption' => '', 'align' => 'none', 'img_width' => '', 'img_height' => '', 'link' => '' ) );
+
+ $title = esc_attr( $instance['title'] );
+ $img_url = esc_url( $instance['img_url'], null, 'display' );
+ $alt_text = esc_attr( $instance['alt_text'] );
+ $img_title = esc_attr( $instance['img_title'] );
+ $caption = esc_attr( $instance['caption'] );
+ $align = esc_attr( $instance['align'] );
+ $img_width = esc_attr( $instance['img_width'] );
+ $img_height = esc_attr( $instance['img_height'] );
+
+ if ( !empty( $instance['img_url'] ) ) {
+ // Download the url to a local temp file and then process it with getimagesize so we can filter out domains which are blocking us
+ $tmp_file = download_url( $instance['img_url'], 30 );
+ if ( ! is_wp_error( $tmp_file ) ) {
+ $size = getimagesize( $tmp_file );
+
+ if ( '' == $instance['img_width'] ) {
+ $width = $size[0];
+ $img_width = $width;
+ } else {
+ $img_width = absint( $instance['img_width'] );
+ }
+
+ if ( '' == $instance['img_height'] ) {
+ $height = $size[1];
+ $img_height = $height;
+ } else {
+ $img_height = absint( $instance['img_height'] );
+ }
+
+ unlink( $tmp_file );
+ }
+ }
+
+ $link = esc_url( $instance['link'], null, 'display' );
+
+ echo '<p><label for="' . $this->get_field_id( 'title' ) . '">' . esc_html__( 'Widget title:', 'jetpack' ) . '
+ <input class="widefat" id="' . $this->get_field_id( 'title' ) . '" name="' . $this->get_field_name( 'title' ) . '" type="text" value="' . $title . '" />
+ </label></p>
+ <p><label for="' . $this->get_field_id( 'img_url' ) . '">' . esc_html__( 'Image URL:', 'jetpack' ) . '
+ <input class="widefat" id="' . $this->get_field_id( 'img_url' ) . '" name="' . $this->get_field_name( 'img_url' ) . '" type="text" value="' . $img_url . '" />
+ </label></p>
+ <p><label for="' . $this->get_field_id( 'alt_text' ) . '">' . esc_html__( 'Alternate text:', 'jetpack' ) . ' <a href="http://support.wordpress.com/widgets/image-widget/#image-widget-alt-text" target="_blank">( ? )</a>
+ <input class="widefat" id="' . $this->get_field_id( 'alt_text' ) . '" name="' . $this->get_field_name( 'alt_text' ) . '" type="text" value="' . $alt_text . '" />
+ </label></p>
+ <p><label for="' . $this->get_field_id( 'img_title' ) . '">' . esc_html__( 'Image title:', 'jetpack' ) . ' <a href="http://support.wordpress.com/widgets/image-widget/#image-widget-title" target="_blank">( ? )</a>
+ <input class="widefat" id="' . $this->get_field_id( 'img_title' ) . '" name="' . $this->get_field_name( 'img_title' ) . '" type="text" value="' . $img_title . '" />
+ </label></p>
+ <p><label for="' . $this->get_field_id( 'caption' ) . '">' . esc_html__( 'Caption:', 'jetpack' ) . ' <a href="http://support.wordpress.com/widgets/image-widget/#image-widget-caption" target="_blank">( ? )</a>
+ <input class="widefat" id="' . $this->get_field_id( 'caption' ) . '" name="' . $this->get_field_name( 'caption' ) . '" type="text" value="' . $caption . '" />
+ </label></p>';
+
+ $alignments = array(
+ 'none' => __( 'None', 'jetpack' ),
+ 'left' => __( 'Left', 'jetpack' ),
+ 'center' => __( 'Center', 'jetpack' ),
+ 'right' => __( 'Right', 'jetpack' ),
+ );
+ echo '<p><label for="' . $this->get_field_id( 'align' ) . '">' . esc_html__( 'Image Alignment:', 'jetpack' ) . '
+ <select id="' . $this->get_field_id( 'align' ) . '" name="' . $this->get_field_name( 'align' ) . '">';
+ foreach ( $alignments as $alignment => $alignment_name ) {
+ echo '<option value="' . esc_attr( $alignment ) . '" ';
+ if ( $alignment == $align )
+ echo 'selected="selected" ';
+ echo '>' . esc_html($alignment_name) . "</option>\n";
+ }
+ echo '</select></label></p>';
+
+ echo '<p><label for="' . $this->get_field_id( 'img_width' ) . '">' . esc_html__( 'Width:', 'jetpack' ) . '
+ <input size="3" id="' . $this->get_field_id( 'img_width' ) . '" name="' . $this->get_field_name( 'img_width' ) . '" type="text" value="' . $img_width . '" />
+ </label>
+ <label for="' . $this->get_field_id( 'img_height' ) . '">' . esc_html__( 'Height:', 'jetpack' ) . '
+ <input size="3" id="' . $this->get_field_id( 'img_height' ) . '" name="' . $this->get_field_name( 'img_height' ) . '" type="text" value="' . $img_height . '" />
+ </label><br />
+ <small>' . esc_html__( "If empty, we will attempt to determine the image size.", 'jetpack' ) . '</small></p>
+ <p><label for="' . $this->get_field_id( 'link' ) . '">' . esc_html__( 'Link URL (when the image is clicked):', 'jetpack' ) . '
+ <input class="widefat" id="' . $this->get_field_id( 'link' ) . '" name="' . $this->get_field_name( 'link' ) . '" type="text" value="' . $link . '" />
+ </label></p>';
+ }
+
+} //Class Jetpack_Image_Widget
+
+function jetpack_image_widget_init() {
+ register_widget( 'Jetpack_Image_Widget' );
+}
+add_action( 'widgets_init', 'jetpack_image_widget_init' );
diff --git a/plugins/jetpack/modules/widgets/rsslinks-widget.php b/plugins/jetpack/modules/widgets/rsslinks-widget.php
new file mode 100644
index 00000000..15309151
--- /dev/null
+++ b/plugins/jetpack/modules/widgets/rsslinks-widget.php
@@ -0,0 +1,168 @@
+<?php
+/**
+ * Module Name: RSS Links Widget
+ * Module Description: Easily add RSS links to your theme's sidebar.
+ * Sort Order: 20
+ * First Introduced: 1.2
+ */
+
+class Jetpack_RSS_Links_Widget extends WP_Widget {
+
+ function Jetpack_RSS_Links_Widget() {
+ $widget_ops = array('classname' => 'widget_rss_links', 'description' => __("Links to your blog's RSS feeds", 'jetpack') );
+ $this->WP_Widget('rss_links', __('RSS Links (Jetpack)', 'jetpack'), $widget_ops);
+ }
+
+ function widget($args, $instance) {
+ extract( $args );
+
+ $title = apply_filters( 'widget_title', $instance['title'] );
+ echo $before_widget;
+
+ if ( $title )
+ echo $before_title . stripslashes( $title ) . $after_title;
+
+ if ( 'text' == $instance['format'] ) echo '<ul>';
+
+ if ( 'posts' == $instance['display'] ) {
+ $this->_rss_link('posts', $instance);
+ } elseif ( 'comments' == $instance['display'] ) {
+ $this->_rss_link('comments', $instance);
+ } elseif ( 'posts-comments' == $instance['display'] ) {
+ $this->_rss_link('posts', $instance);
+ $this->_rss_link('comments', $instance);
+ }
+
+ if ( 'text' == $instance['format'] ) echo '</ul>';
+
+ echo "\n" . $after_widget;
+ }
+
+ function update($new_instance, $old_instance) {
+ $instance = $old_instance;
+
+ $instance['title'] = wp_filter_nohtml_kses( $new_instance['title'] );
+ $instance['display'] = $new_instance['display'];
+ $instance['format'] = $new_instance['format'];
+ $instance['imagesize'] = $new_instance['imagesize'];
+ $instance['imagecolor'] = $new_instance['imagecolor'];
+
+ return $instance;
+ }
+
+ function form($instance) {
+ $instance = wp_parse_args( (array) $instance, array('title' => '', 'display' => 'posts-comments', 'format' => 'text') );
+
+ $title = stripslashes( $instance['title'] );
+ $display = $instance['display'];
+ $format = $instance['format'];
+ $image_size = isset( $instance['imagesize'] ) ? $instance['imagesize'] : 0 ;
+ $image_color = isset( $instance['imagecolor'] ) ? $instance['imagecolor'] : 'red';
+
+ echo '<p><label for="' . $this->get_field_id('title') . '">' . esc_html__('Title:', 'jetpack') . '
+ <input class="widefat" id="' . $this->get_field_id('title') . '" name="' . $this->get_field_name('title') . '" type="text" value="' . esc_attr($title) . '" />
+ </label></p>';
+
+ $displays = array(
+ 'posts' => __('Posts', 'jetpack'),
+ 'comments' => __('Comments', 'jetpack'),
+ 'posts-comments' => __('Posts & Comments', 'jetpack')
+ );
+ echo '<p><label for="' . $this->get_field_id('display') . '">' . esc_html__('Feed(s) to Display:', 'jetpack') . '
+ <select class="widefat" id="' . $this->get_field_id('display') . '" name="' . $this->get_field_name('display') . '">';
+ foreach ( $displays as $display_option => $label ) {
+ echo '<option value="' . esc_attr($display_option) . '"';
+ if ( $display_option == $display ) echo ' selected="selected"';
+ echo '>' . esc_html($label) . '</option>' . "\n";
+ }
+ echo '</select></label></p>';
+
+ $formats = array(
+ 'text' => __('Text Link', 'jetpack'),
+ 'image' => __('Image Link', 'jetpack'),
+ 'text-image' => __('Text & Image Links', 'jetpack')
+ );
+ echo '<p><label for="' . $this->get_field_id('format') . '">' . __('Format:', 'jetpack') . '
+ <select class="widefat" id="' . $this->get_field_id('format') . '" name="' . $this->get_field_name('format') . '" onchange="if ( this.value == \'text\' ) jQuery( \'#' . $this->get_field_id('image-settings') . '\' ).fadeOut(); else jQuery( \'#' . $this->get_field_id('image-settings') . '\' ).fadeIn();">';
+ foreach ( $formats as $format_option => $label ) {
+ echo '<option value="' . esc_attr($format_option) . '"';
+ if ( $format_option == $format ) echo ' selected="selected"';
+ echo '>' . esc_html($label) . '</option>' . "\n";
+ }
+ echo '</select></label></p>';
+
+ echo '<div id="' . $this->get_field_id('image-settings') . '"';
+ if ( 'text' == $format ) echo ' style="display: none;"';
+ echo '><h3>' . esc_html__('Image Settings:', 'jetpack') . '</h3>';
+
+ $sizes = array(
+ 'small' => __('Small', 'jetpack'),
+ 'medium' => __('Medium', 'jetpack'),
+ 'large' => __('Large', 'jetpack')
+ );
+ echo '<p><label for="' . $this->get_field_id('imagesize') . '">' . esc_html__('Image Size:', 'jetpack') . '
+ <select class="widefat" id="' . $this->get_field_id('imagesize') . '" name="' . $this->get_field_name('imagesize') . '">';
+ foreach ( $sizes as $size => $label ) {
+ echo '<option value="' . esc_attr($size) . '"';
+ if ( $size == $image_size ) echo ' selected="selected"';
+ echo '>' . esc_html($label) . '</option>' . "\n";
+ }
+ echo '</select></label></p>';
+
+ $colors = array(
+ 'red' => __('Red', 'jetpack'),
+ 'orange' => __('Orange', 'jetpack'),
+ 'green' => __('Green', 'jetpack'),
+ 'blue' => __('Blue', 'jetpack'),
+ 'purple' => __('Purple', 'jetpack'),
+ 'pink' => __('Pink', 'jetpack'),
+ 'silver' => __('Silver', 'jetpack'),
+ );
+ echo '<p><label for="' . $this->get_field_id('imagecolor') . '">' . esc_html__('Image Color:', 'jetpack') . '
+ <select class="widefat" id="' . $this->get_field_id('imagecolor') . '" name="' . $this->get_field_name('imagecolor') . '">';
+ foreach ( $colors as $color => $label ) {
+ echo '<option value="' . esc_attr($color) . '"';
+ if ( $color == $image_color ) echo ' selected="selected"';
+ echo '>' . esc_html($label) . '</option>' . "\n";
+ }
+ echo '</select></label></p></div>';
+ }
+
+ function _rss_link( $type = 'posts', $args ) {
+ if ( 'posts' == $type ) {
+ $type_text = __( 'Posts', 'jetpack' );
+ $rss_type = 'rss2_url';
+ } elseif ( 'comments' == $type ) {
+ $type_text = __( 'Comments', 'jetpack' );
+ $rss_type = 'comments_rss2_url';
+ }
+
+ $subscribe_to = sprintf( __( 'Subscribe to %s', 'jetpack'), $type_text );
+
+ $link_item = '';
+ $format = $args['format'];
+ if ( 'image' == $format || 'text-image' == $format )
+ $link_item = '<a href="' . get_bloginfo($rss_type) . '" title="' . esc_attr( $subscribe_to ) . '"><img src="' . esc_url( plugins_url( '_inc/images/rss/' . $args['imagecolor'] . '-' . $args['imagesize'] . '.png', dirname( dirname( __FILE__ ) ) ) ) . '" alt="RSS Feed" /></a>';
+ if ( 'text-image' == $format )
+ $link_item .= '&nbsp;<a href="' . get_bloginfo($rss_type) . '" title="' . esc_attr( $subscribe_to ) . '">' . esc_html__('RSS - ' . $type_text, 'jetpack'). '</a>';
+ if ( 'text' == $format )
+ $link_item = '<a href="' . get_bloginfo($rss_type) . '" title="' . esc_attr( $subscribe_to ) . '">' . esc_html__('RSS - ' . $type_text, 'jetpack'). '</a>';
+
+ if ( 'text' == $format )
+ echo '<li>';
+ else
+ echo '<p>';
+ echo $link_item;
+ if ( 'text' == $format )
+ echo '</li>';
+ else
+ echo '</p>';
+
+ }
+} //Class Jetpack_RSS_Links_Widget
+
+function jetpack_rss_links_widget_init() {
+ register_widget('Jetpack_RSS_Links_Widget');
+}
+add_action( 'widgets_init', 'jetpack_rss_links_widget_init' );
+?>
diff --git a/plugins/jetpack/modules/widgets/twitter-widget.php b/plugins/jetpack/modules/widgets/twitter-widget.php
new file mode 100644
index 00000000..a7f359cd
--- /dev/null
+++ b/plugins/jetpack/modules/widgets/twitter-widget.php
@@ -0,0 +1,273 @@
+<?php
+/**
+ * Module Name: Twitter Widget
+ * Module Description: Display the latest updates from a Twitter user inside your theme's widgets.
+ * Sort Order: 20
+ * First Introduced: 1.1
+ */
+
+/*
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+*/
+
+if ( !function_exists('wpcom_time_since') ) :
+/*
+ * Time since function taken from WordPress.com
+ */
+
+function wpcom_time_since( $original, $do_more = 0 ) {
+ // array of time period chunks
+ $chunks = array(
+ array( 60 * 60 * 24 * 365 , 'year' ),
+ array( 60 * 60 * 24 * 30 , 'month' ),
+ array( 60 * 60 * 24 * 7, 'week' ),
+ array( 60 * 60 * 24 , 'day' ),
+ array( 60 * 60 , 'hour' ),
+ array( 60 , 'minute' ),
+ );
+
+ $today = time();
+ $since = $today - $original;
+
+ for ( $i = 0, $j = count( $chunks ); $i < $j; $i++ ) {
+ $seconds = $chunks[$i][0];
+ $name = $chunks[$i][1];
+
+ if ( ( $count = floor( $since / $seconds ) ) != 0 )
+ break;
+ }
+
+ $print = ( $count == 1 ) ? '1 ' . $name : "$count {$name}s";
+
+ if ( $i + 1 < $j ) {
+ $seconds2 = $chunks[$i + 1][0];
+ $name2 = $chunks[$i + 1][1];
+
+ // add second item if it's greater than 0
+ if ( ( ( $count2 = floor( ( $since - ( $seconds * $count ) ) / $seconds2 ) ) != 0 ) && $do_more )
+ $print .= ( $count2 == 1 ) ? ', 1 ' . $name2 : ", $count2 {$name2}s";
+ }
+ return $print;
+}
+endif;
+
+class Wickett_Twitter_Widget extends WP_Widget {
+
+ function Wickett_Twitter_Widget() {
+ $widget_ops = array( 'classname' => 'widget_twitter', 'description' => __( 'Display your tweets from Twitter', 'jetpack' ) );
+ parent::WP_Widget( 'twitter', __( 'Twitter (Jetpack)', 'jetpack' ), $widget_ops );
+ }
+
+ function widget( $args, $instance ) {
+ extract( $args );
+
+ $account = trim( urlencode( $instance['account'] ) );
+ if ( empty($account) ) return;
+ $title = apply_filters( 'widget_title', $instance['title'] );
+ if ( empty( $title ) ) $title = __( 'Twitter Updates', 'jetpack' );
+ $show = absint( $instance['show'] ); // # of Updates to show
+ if ( $show > 200 ) // Twitter paginates at 200 max tweets. update() should not have accepted greater than 20
+ $show = 200;
+ $hidereplies = (bool) $instance['hidereplies'];
+ $include_retweets = (bool) $instance['includeretweets'];
+
+ echo "{$before_widget}{$before_title}<a href='" . esc_url( "http://twitter.com/{$account}" ) . "'>" . esc_html($title) . "</a>{$after_title}";
+
+ if ( false === ( $tweets = get_transient( 'widget-twitter-' . $this->number ) ) ) {
+ $params = array(
+ 'screen_name'=>$account, // Twitter account name
+ 'trim_user'=>true, // only basic user data (slims the result)
+ 'include_entities' => true
+ );
+
+ /**
+ * The exclude_replies parameter filters out replies on the server. If combined with count it only filters that number of tweets (not all tweets up to the requested count)
+ * If we are not filtering out replies then we should specify our requested tweet count
+ */
+ if ( $hidereplies )
+ $params['exclude_replies'] = true;
+ else
+ $params['count'] = $show;
+ if ( $include_retweets )
+ $params['include_rts'] = true;
+ $twitter_json_url = esc_url_raw( 'http://api.twitter.com/1/statuses/user_timeline.json?' . http_build_query( $params ), array( 'http', 'https' ) );
+ unset( $params );
+ $response = wp_remote_get( $twitter_json_url, array( 'User-Agent' => 'WordPress.com Twitter Widget' ) );
+ $response_code = wp_remote_retrieve_response_code( $response );
+ if ( 200 == $response_code ) {
+ $tweets = wp_remote_retrieve_body( $response );
+ $tweets = json_decode( $tweets, true );
+ $expire = 900;
+ if ( !is_array( $tweets ) || isset( $tweets['error'] ) ) {
+ $tweets = 'error';
+ $expire = 300;
+ }
+ } else {
+ $tweets = 'error';
+ $expire = 300;
+ set_transient( 'widget-twitter-response-code-' . $this->number, $response_code, $expire );
+ }
+
+ set_transient( 'widget-twitter-' . $this->number, $tweets, $expire );
+ }
+
+ if ( 'error' != $tweets ) :
+ $before_timesince = ' ';
+ if ( isset( $instance['beforetimesince'] ) && !empty( $instance['beforetimesince'] ) )
+ $before_timesince = esc_html( $instance['beforetimesince'] );
+ $before_tweet = '';
+ if ( isset( $instance['beforetweet'] ) && !empty( $instance['beforetweet'] ) )
+ $before_tweet = stripslashes( wp_filter_post_kses( $instance['beforetweet'] ) );
+
+ echo '<ul class="tweets">' . "\n";
+
+ $tweets_out = 0;
+
+ foreach ( (array) $tweets as $tweet ) {
+ if ( $tweets_out >= $show )
+ break;
+
+ if ( empty( $tweet['text'] ) )
+ continue;
+
+ $text = esc_html( $tweet['text'] );
+
+ // expand t.co links
+ if ( !empty( $tweet['entities']['urls'] ) ) {
+ foreach ( $tweet['entities']['urls'] as $entity_url ) {
+ if ( !empty( $entity_url['expanded_url'] ) ) {
+ $expanded = '<a href="' . esc_url( $entity_url['expanded_url'] ) . '"> ' . esc_html( $entity_url['display_url'] ) . '</a>';
+ $text = str_replace( $entity_url['url'], $expanded, $text );
+ }
+ }
+ }
+
+ $text = make_clickable( $text );
+
+ /*
+ * Create links from plain text based on Twitter patterns
+ * @link http://github.com/mzsanford/twitter-text-rb/blob/master/lib/regex.rb Official Twitter regex
+ */
+ $text = preg_replace_callback( '/(^|[^0-9A-Z&\/]+)(#|\xef\xbc\x83)([0-9A-Z_]*[A-Z_]+[a-z0-9_\xc0-\xd6\xd8-\xf6\xf8\xff]*)/iu', array( $this, '_wpcom_widget_twitter_hashtag' ), $text );
+ $text = preg_replace_callback( '/([^a-zA-Z0-9_]|^)([@\xef\xbc\xa0]+)([a-zA-Z0-9_]{1,20})(\/[a-zA-Z][a-zA-Z0-9\x80-\xff-]{0,79})?/u', array( $this, '_wpcom_widget_twitter_username' ), $text );
+ if ( isset( $tweet['id_str'] ) )
+ $tweet_id = urlencode( $tweet['id_str'] );
+ else
+ $tweet_id = urlencode( $tweet['id'] );
+ $created_at = str_replace( '+0000', '', $tweet['created_at'] ) . ' UTC'; // Twitter's datetime format is strange, refactor for the sake of PHP4
+ echo "<li>{$before_tweet}{$text}{$before_timesince}<a href=\"" . esc_url( "http://twitter.com/{$account}/statuses/{$tweet_id}" ) . '" class="timesince">' . str_replace( ' ', '&nbsp;', wpcom_time_since( strtotime( $created_at ) ) ) . "&nbsp;ago</a></li>\n";
+ unset( $tweet_id );
+ $tweets_out++;
+ }
+
+ echo "</ul>\n";
+ else :
+ if ( 401 == get_transient( 'widget-twitter-response-code-' . $this->number ) )
+ echo '<p>' . wp_kses( sprintf( __( 'Error: Please make sure the Twitter account is <a href="%s">public</a>.', 'jetpack' ), 'http://support.twitter.com/forums/10711/entries/14016' ), array( 'a' => array( 'href' => true ) ) ) . '</p>';
+ else
+ echo '<p>' . esc_html__( 'Error: Twitter did not respond. Please wait a few minutes and refresh this page.', 'jetpack' ) . '</p>';
+ endif;
+
+ echo $after_widget;
+ }
+
+ function update( $new_instance, $old_instance ) {
+ $instance = $old_instance;
+
+ $instance['account'] = trim( strip_tags( stripslashes( $new_instance['account'] ) ) );
+ $instance['account'] = str_replace( 'http://twitter.com/', '', $instance['account'] );
+ $instance['account'] = str_replace( '/', '', $instance['account'] );
+ $instance['account'] = str_replace( '@', '', $instance['account'] );
+ $instance['account'] = str_replace( '#!', '', $instance['account'] ); // account for the Ajax URI
+ $instance['title'] = strip_tags( stripslashes( $new_instance['title'] ) );
+ $instance['show'] = absint( $new_instance['show'] );
+ $instance['hidereplies'] = isset( $new_instance['hidereplies'] );
+ $instance['includeretweets'] = isset( $new_instance['includeretweets'] );
+ $instance['beforetimesince'] = $new_instance['beforetimesince'];
+
+ delete_transient( 'widget-twitter-' . $this->number );
+ delete_transient( 'widget-twitter-response-code-' . $this->number );
+
+ return $instance;
+ }
+
+ function form( $instance ) {
+ //Defaults
+ $instance = wp_parse_args( (array) $instance, array( 'account' => '', 'title' => '', 'show' => 5, 'hidereplies' => false, 'includeretweets' => false, 'beforetimesince' => '' ) );
+
+ $account = esc_attr( $instance['account'] );
+ $title = esc_attr( $instance['title'] );
+ $show = absint( $instance['show'] );
+ if ( $show < 1 || 20 < $show )
+ $show = 5;
+ $hidereplies = (bool) $instance['hidereplies'];
+ $include_retweets = (bool) $instance['includeretweets'];
+ $before_timesince = esc_attr( $instance['beforetimesince'] );
+
+ echo '<p><label for="' . $this->get_field_id( 'title' ) . '">' . esc_html__( 'Title:', 'jetpack' ) . '
+ <input class="widefat" id="' . $this->get_field_id( 'title' ) . '" name="' . $this->get_field_name( 'title' ) . '" type="text" value="' . $title . '" />
+ </label></p>
+ <p><label for="' . $this->get_field_id( 'account' ) . '">' . esc_html__( 'Twitter username:', 'jetpack' ) . ' <a href="http://support.wordpress.com/widgets/twitter-widget/#twitter-username" target="_blank">( ? )</a>
+ <input class="widefat" id="' . $this->get_field_id( 'account' ) . '" name="' . $this->get_field_name( 'account' ) . '" type="text" value="' . $account . '" />
+ </label></p>
+ <p><label for="' . $this->get_field_id( 'show' ) . '">' . esc_html__( 'Maximum number of tweets to show:', 'jetpack' ) . '
+ <select id="' . $this->get_field_id( 'show' ) . '" name="' . $this->get_field_name( 'show' ) . '">';
+
+ for ( $i = 1; $i <= 20; ++$i )
+ echo "<option value='$i' " . ( $show == $i ? "selected='selected'" : '' ) . ">$i</option>";
+
+ echo ' </select>
+ </label></p>
+ <p><label for="' . $this->get_field_id( 'hidereplies' ) . '"><input id="' . $this->get_field_id( 'hidereplies' ) . '" class="checkbox" type="checkbox" name="' . $this->get_field_name( 'hidereplies' ) . '"';
+ if ( $hidereplies )
+ echo ' checked="checked"';
+ echo ' /> ' . esc_html__( 'Hide replies', 'jetpack' ) . '</label></p>';
+
+ echo '<p><label for="' . $this->get_field_id( 'includeretweets' ) . '"><input id="' . $this->get_field_id( 'includeretweets' ) . '" class="checkbox" type="checkbox" name="' . $this->get_field_name( 'includeretweets' ) . '"';
+ if ( $include_retweets )
+ echo ' checked="checked"';
+ echo ' /> ' . esc_html__( 'Include retweets', 'jetpack' ) . '</label></p>';
+
+ echo '<p><label for="' . $this->get_field_id( 'beforetimesince' ) . '">' . esc_html__( 'Text to display between tweet and timestamp:', 'jetpack' ) . '
+ <input class="widefat" id="' . $this->get_field_id( 'beforetimesince' ) . '" name="' . $this->get_field_name( 'beforetimesince' ) . '" type="text" value="' . $before_timesince . '" />
+ </label></p>';
+ }
+
+ /**
+ * Link a Twitter user mentioned in the tweet text to the user's page on Twitter.
+ *
+ * @param array $matches regex match
+ * @return string Tweet text with inserted @user link
+ */
+ function _wpcom_widget_twitter_username( $matches ) { // $matches has already been through wp_specialchars
+ return "$matches[1]@<a href='" . esc_url( 'http://twitter.com/' . urlencode( $matches[3] ) ) . "'>$matches[3]</a>";
+ }
+
+ /**
+ * Link a Twitter hashtag with a search results page on Twitter.com
+ *
+ * @param array $matches regex match
+ * @return string Tweet text with inserted #hashtag link
+ */
+ function _wpcom_widget_twitter_hashtag( $matches ) { // $matches has already been through wp_specialchars
+ return "$matches[1]<a href='" . esc_url( 'http://twitter.com/search?q=%23' . urlencode( $matches[3] ) ) . "'>#$matches[3]</a>";
+ }
+
+}
+
+add_action( 'widgets_init', 'wickett_twitter_widget_init' );
+function wickett_twitter_widget_init() {
+ register_widget( 'Wickett_Twitter_Widget' );
+}
diff --git a/plugins/jetpack/modules/wpgroho.js b/plugins/jetpack/modules/wpgroho.js
new file mode 100644
index 00000000..24e161b7
--- /dev/null
+++ b/plugins/jetpack/modules/wpgroho.js
@@ -0,0 +1,33 @@
+WPGroHo = jQuery.extend( {
+ my_hash: '',
+ data: {},
+ renderers: {},
+ syncProfileData: function( hash, id ) {
+ if ( !WPGroHo.data[hash] ) {
+ WPGroHo.data[hash] = {};
+ a = jQuery( 'div.grofile-hash-map-' + hash + ' span' ).each( function() {
+ WPGroHo.data[hash][this.className] = jQuery( this ).text();
+ } );
+ }
+
+ WPGroHo.appendProfileData( WPGroHo.data[hash], hash, id );
+ },
+ appendProfileData: function( data, hash, id ) {
+ for ( var key in data ) {
+ if ( jQuery.isFunction( WPGroHo.renderers[key] ) ) {
+ return WPGroHo.renderers[key]( data[key], hash, id, key );
+ }
+
+ jQuery( '#' + id ).find( 'h4' ).after( jQuery( '<p class="grav-extra ' + key + '" />' ).html( data[key] ) );
+ }
+ }
+}, WPGroHo );
+
+jQuery( document ).ready( function( $ ) {
+ Gravatar.profile_cb = function( h, d ) {
+ WPGroHo.syncProfileData( h, d );
+ };
+
+ Gravatar.my_hash = WPGroHo.my_hash;
+ Gravatar.init( 'body', '#wpadminbar' );
+} );