summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTheo Chatzimichos <tampakrap@gentoo.org>2013-10-03 21:53:38 +0200
committerTheo Chatzimichos <tampakrap@gentoo.org>2013-10-03 21:53:38 +0200
commit61f7269ffabd11b7de56507c69191be42d7cfa60 (patch)
tree2ad81bec8d0c124019b23c841d80882c303801bc /plugins/jetpack/modules
parentforgot to include new files of jetpack (diff)
downloadblogs-gentoo-61f7269ffabd11b7de56507c69191be42d7cfa60.tar.gz
blogs-gentoo-61f7269ffabd11b7de56507c69191be42d7cfa60.tar.bz2
blogs-gentoo-61f7269ffabd11b7de56507c69191be42d7cfa60.zip
update jetpack
Diffstat (limited to 'plugins/jetpack/modules')
-rw-r--r--plugins/jetpack/modules/after-the-deadline.php1
-rw-r--r--plugins/jetpack/modules/after-the-deadline/config-unignore.php3
-rw-r--r--plugins/jetpack/modules/carousel.php1
-rw-r--r--plugins/jetpack/modules/carousel/jetpack-carousel.js10
-rw-r--r--plugins/jetpack/modules/carousel/jetpack-carousel.php3
-rw-r--r--plugins/jetpack/modules/carousel/jquery.spin.js86
-rw-r--r--plugins/jetpack/modules/carousel/spin.js301
-rw-r--r--plugins/jetpack/modules/comments.php1
-rw-r--r--plugins/jetpack/modules/comments/base.php14
-rw-r--r--plugins/jetpack/modules/comments/comments.php6
-rw-r--r--plugins/jetpack/modules/contact-form.php1
-rw-r--r--plugins/jetpack/modules/contact-form/admin.php7
-rw-r--r--plugins/jetpack/modules/contact-form/css/menu-alter.css4
-rw-r--r--plugins/jetpack/modules/contact-form/css/rtl/menu-alter-rtl.css10
-rw-r--r--plugins/jetpack/modules/contact-form/grunion-contact-form.php39
-rw-r--r--plugins/jetpack/modules/contact-form/js/jquery-ui-1.8.4.custom.min.js185
-rw-r--r--plugins/jetpack/modules/custom-css.php1
-rw-r--r--plugins/jetpack/modules/custom-css/custom-css.php19
-rw-r--r--plugins/jetpack/modules/custom-css/custom-css/blank.css1
-rw-r--r--plugins/jetpack/modules/custom-css/custom-css/css/css-editor.css3
-rw-r--r--plugins/jetpack/modules/custom-css/custom-css/preprocessors/lessc.inc.php561
-rw-r--r--plugins/jetpack/modules/custom-css/custom-css/preprocessors/scss.inc.php1064
-rw-r--r--plugins/jetpack/modules/custom-post-types/comics.php472
-rw-r--r--plugins/jetpack/modules/custom-post-types/comics/admin.css8
-rw-r--r--plugins/jetpack/modules/custom-post-types/comics/comics.css30
-rw-r--r--plugins/jetpack/modules/custom-post-types/comics/comics.js118
-rw-r--r--plugins/jetpack/modules/custom-post-types/comics/rtl/comics-rtl.css32
-rw-r--r--plugins/jetpack/modules/custom-post-types/testimonial.php286
-rw-r--r--plugins/jetpack/modules/custom-post-types/testimonials.php0
-rw-r--r--plugins/jetpack/modules/debug.php6
-rw-r--r--plugins/jetpack/modules/enhanced-distribution.php3
-rw-r--r--plugins/jetpack/modules/gplus-authorship.php206
-rw-r--r--plugins/jetpack/modules/gplus-authorship/admin/connect.js42
-rw-r--r--plugins/jetpack/modules/gplus-authorship/admin/listener.js57
-rw-r--r--plugins/jetpack/modules/gplus-authorship/admin/style.css51
-rw-r--r--plugins/jetpack/modules/gplus-authorship/admin/ui.php270
-rw-r--r--plugins/jetpack/modules/gplus-authorship/style.css17
-rw-r--r--plugins/jetpack/modules/gravatar-hovercards.php1
-rw-r--r--plugins/jetpack/modules/holiday-snow.php1
-rw-r--r--plugins/jetpack/modules/infinite-scroll.php5
-rw-r--r--plugins/jetpack/modules/json-api.php1
-rw-r--r--plugins/jetpack/modules/latex.php3
-rw-r--r--plugins/jetpack/modules/likes.php42
-rw-r--r--plugins/jetpack/modules/likes/post-count-jetpack.js18
-rw-r--r--plugins/jetpack/modules/likes/post-count.js20
-rw-r--r--plugins/jetpack/modules/likes/style.css2
-rw-r--r--plugins/jetpack/modules/minileven.php1
-rw-r--r--plugins/jetpack/modules/minileven/theme/pub/minileven/functions.php10
-rw-r--r--plugins/jetpack/modules/minileven/theme/pub/minileven/style.css4
-rw-r--r--plugins/jetpack/modules/mobile-push.php1
-rw-r--r--plugins/jetpack/modules/module-extras.php59
-rw-r--r--plugins/jetpack/modules/module-info.php90
-rw-r--r--plugins/jetpack/modules/notes.php1
-rw-r--r--plugins/jetpack/modules/omnisearch.php1
-rw-r--r--plugins/jetpack/modules/omnisearch/omnisearch-core.php5
-rw-r--r--plugins/jetpack/modules/omnisearch/omnisearch-media.php46
-rw-r--r--plugins/jetpack/modules/omnisearch/omnisearch.css19
-rw-r--r--plugins/jetpack/modules/omnisearch/rtl/omnisearch-rtl.css21
-rw-r--r--plugins/jetpack/modules/photon.php1
-rw-r--r--plugins/jetpack/modules/post-by-email.php1
-rw-r--r--plugins/jetpack/modules/publicize.php1
-rw-r--r--plugins/jetpack/modules/publicize/assets/facebook-logo.pngbin37624 -> 18719 bytes
-rw-r--r--plugins/jetpack/modules/publicize/assets/path-logo.pngbin0 -> 5708 bytes
-rw-r--r--plugins/jetpack/modules/publicize/assets/publicize.css27
-rw-r--r--plugins/jetpack/modules/publicize/assets/publicize.js2
-rw-r--r--plugins/jetpack/modules/publicize/assets/rtl/publicize-rtl.css29
-rw-r--r--plugins/jetpack/modules/publicize/assets/yahoo-logo.pngbin9675 -> 0 bytes
-rw-r--r--plugins/jetpack/modules/publicize/publicize-jetpack.php25
-rw-r--r--plugins/jetpack/modules/publicize/ui.php34
-rw-r--r--plugins/jetpack/modules/random-redirect.php43
-rw-r--r--plugins/jetpack/modules/sharedaddy.php1
-rw-r--r--plugins/jetpack/modules/sharedaddy/admin-sharing.css4
-rw-r--r--plugins/jetpack/modules/sharedaddy/images/googleplus1.pngbin722 -> 660 bytes
-rw-r--r--plugins/jetpack/modules/sharedaddy/images/googleplus1@2x.pngbin1333 -> 1187 bytes
-rw-r--r--plugins/jetpack/modules/sharedaddy/images/icon-facebook-2x.pngbin1036 -> 1080 bytes
-rw-r--r--plugins/jetpack/modules/sharedaddy/images/icon-facebook.pngbin568 -> 634 bytes
-rw-r--r--plugins/jetpack/modules/sharedaddy/images/smart-googleplus1.pngbin926 -> 6719 bytes
-rw-r--r--plugins/jetpack/modules/sharedaddy/images/smart-googleplus1@2x.pngbin2394 -> 7795 bytes
-rw-r--r--plugins/jetpack/modules/sharedaddy/sharing-service.php5
-rw-r--r--plugins/jetpack/modules/sharedaddy/sharing-sources.php27
-rw-r--r--plugins/jetpack/modules/sharedaddy/sharing.php3
-rw-r--r--plugins/jetpack/modules/shortcodes.php1
-rw-r--r--plugins/jetpack/modules/shortcodes/css/rtl/slideshow-shortcode-rtl.css3
-rw-r--r--plugins/jetpack/modules/shortcodes/css/slideshow-shortcode.css1
-rw-r--r--plugins/jetpack/modules/shortcodes/css/style.css187
-rw-r--r--plugins/jetpack/modules/shortcodes/facebook.php44
-rw-r--r--plugins/jetpack/modules/shortcodes/googleplus.php41
-rw-r--r--plugins/jetpack/modules/shortcodes/images/collapse.pngbin0 -> 4811 bytes
-rw-r--r--plugins/jetpack/modules/shortcodes/images/expand.pngbin0 -> 4778 bytes
-rw-r--r--plugins/jetpack/modules/shortcodes/images/slide-nav.pngbin0 -> 5741 bytes
-rw-r--r--plugins/jetpack/modules/shortcodes/js/jmpress.js2721
-rw-r--r--plugins/jetpack/modules/shortcodes/js/jmpress.min.js13
-rw-r--r--plugins/jetpack/modules/shortcodes/js/main.js255
-rw-r--r--plugins/jetpack/modules/shortcodes/presentations.php431
-rw-r--r--plugins/jetpack/modules/shortcodes/twitter-timeline.php31
-rw-r--r--plugins/jetpack/modules/shortcodes/videopress.php1334
-rw-r--r--plugins/jetpack/modules/shortcodes/vine.php65
-rw-r--r--plugins/jetpack/modules/shortlinks.php3
-rw-r--r--plugins/jetpack/modules/stats.php11
-rw-r--r--plugins/jetpack/modules/subscriptions.php144
-rw-r--r--plugins/jetpack/modules/theme-tools.php64
-rw-r--r--plugins/jetpack/modules/tiled-gallery.php1
-rw-r--r--plugins/jetpack/modules/tiled-gallery/tiled-gallery.php14
-rw-r--r--plugins/jetpack/modules/tiled-gallery/tiled-gallery/rtl/tiled-gallery-rtl.css4
-rw-r--r--plugins/jetpack/modules/tiled-gallery/tiled-gallery/tiled-gallery.css2
-rw-r--r--plugins/jetpack/modules/tonesque.php10
-rw-r--r--plugins/jetpack/modules/vaultpress.php1
-rw-r--r--plugins/jetpack/modules/videopress.php14
-rw-r--r--plugins/jetpack/modules/videopress/class.videopress-player.php682
-rw-r--r--plugins/jetpack/modules/videopress/class.videopress-video.php324
-rw-r--r--plugins/jetpack/modules/videopress/shortcode.php105
-rw-r--r--plugins/jetpack/modules/videopress/videopress-admin.css97
-rw-r--r--plugins/jetpack/modules/videopress/videopress-admin.js458
-rw-r--r--plugins/jetpack/modules/videopress/videopress.php734
-rw-r--r--plugins/jetpack/modules/widget-visibility.php11
-rw-r--r--plugins/jetpack/modules/widget-visibility/widget-conditions.php379
-rw-r--r--plugins/jetpack/modules/widget-visibility/widget-conditions/rtl/widget-conditions-rtl.css41
-rw-r--r--plugins/jetpack/modules/widget-visibility/widget-conditions/widget-conditions.css39
-rw-r--r--plugins/jetpack/modules/widget-visibility/widget-conditions/widget-conditions.js112
-rw-r--r--plugins/jetpack/modules/widgets.php1
-rw-r--r--plugins/jetpack/modules/widgets/gallery.php392
-rw-r--r--plugins/jetpack/modules/widgets/gallery/css/admin.css11
-rw-r--r--plugins/jetpack/modules/widgets/gallery/css/rtl/admin-rtl.css13
-rw-r--r--plugins/jetpack/modules/widgets/gallery/js/admin.js207
-rw-r--r--plugins/jetpack/modules/widgets/gallery/js/gallery.js12
-rw-r--r--plugins/jetpack/modules/widgets/gallery/templates/form.php89
-rw-r--r--plugins/jetpack/modules/widgets/top-posts.php2
-rw-r--r--plugins/jetpack/modules/widgets/twitter-timeline.php15
-rw-r--r--plugins/jetpack/modules/widgets/twitter-widget.php273
-rw-r--r--plugins/jetpack/modules/widgets/twitter.php407
-rw-r--r--plugins/jetpack/modules/wpcc.php24
-rw-r--r--plugins/jetpack/modules/wpcc/wpcc-sign-on.css11
-rw-r--r--plugins/jetpack/modules/wpcc/wpcc-sign-on.js4
-rw-r--r--plugins/jetpack/modules/wpcc/wpcc-sign-on.php437
134 files changed, 11498 insertions, 3250 deletions
diff --git a/plugins/jetpack/modules/after-the-deadline.php b/plugins/jetpack/modules/after-the-deadline.php
index cf98ec0c..61e32f01 100644
--- a/plugins/jetpack/modules/after-the-deadline.php
+++ b/plugins/jetpack/modules/after-the-deadline.php
@@ -5,6 +5,7 @@
* Sort Order: 6
* First Introduced: 1.1
* Requires Connection: Yes
+ * Auto Activate: Yes
*/
add_action( 'jetpack_modules_loaded', 'AtD_load' );
diff --git a/plugins/jetpack/modules/after-the-deadline/config-unignore.php b/plugins/jetpack/modules/after-the-deadline/config-unignore.php
index 84b18ff6..d8394d7b 100644
--- a/plugins/jetpack/modules/after-the-deadline/config-unignore.php
+++ b/plugins/jetpack/modules/after-the-deadline/config-unignore.php
@@ -32,6 +32,9 @@ function AtD_process_unignore_update() {
if ( ! AtD_is_allowed() )
return;
+ if ( ! isset( $_POST['AtD_ignored_phrases'] ) )
+ return;
+
$user = wp_get_current_user();
if ( ! $user || $user->ID == 0 )
diff --git a/plugins/jetpack/modules/carousel.php b/plugins/jetpack/modules/carousel.php
index e8db24b7..77f186b7 100644
--- a/plugins/jetpack/modules/carousel.php
+++ b/plugins/jetpack/modules/carousel.php
@@ -6,6 +6,7 @@
* Sort Order: 4
* First Introduced: 1.5
* Requires Connection: No
+ * Auto Activate: No
*/
include dirname( __FILE__ ) . '/carousel/jetpack-carousel.php';
diff --git a/plugins/jetpack/modules/carousel/jetpack-carousel.js b/plugins/jetpack/modules/carousel/jetpack-carousel.js
index f101dbf5..2b334fac 100644
--- a/plugins/jetpack/modules/carousel/jetpack-carousel.js
+++ b/plugins/jetpack/modules/carousel/jetpack-carousel.js
@@ -709,11 +709,11 @@ jQuery(document).ready(function($) {
$('#jp-carousel-comment-post-results').slideUp();
- // $('<div />').html(sometext).text() is a trick to go to HTML to plain text (including HTML emntities decode, etc)
+ // $('<div />').text(sometext).html() is a trick to go to HTML to plain text (including HTML entities decode, etc)
if ( current.data('caption') ) {
- if ( $('<div />').html(current.data('caption')).text() == $('<div />').html(current.data('title')).text() )
+ if ( $('<div />').text(current.data('caption')).html() == $('<div />').text(current.data('title')).html() )
$('.jp-carousel-titleanddesc-title').fadeOut('fast').empty();
- if ( $('<div />').html(current.data('caption')).text() == $('<div />').html(current.data('desc')).text() )
+ if ( $('<div />').text(current.data('caption')).html() == $('<div />').text(current.data('desc')).html() )
$('.jp-carousel-titleanddesc-desc').fadeOut('fast').empty();
caption.html( current.data('caption') ).fadeIn('slow');
} else {
@@ -1035,8 +1035,8 @@ jQuery(document).ready(function($) {
desc = gallery.jp_carousel('parseTitleDesc', data.desc) || '';
if ( title.length || desc.length ) {
- // $('<div />').html(sometext).text() is a trick to go to HTML to plain text (including HTML emntities decode, etc)
- if ( $('<div />').html(title).text() == $('<div />').html(desc).text() )
+ // $('<div />').text(sometext).html() is a trick to go to HTML to plain text (including HTML entities decode, etc)
+ if ( $('<div />').text(title).html() == $('<div />').text(desc).html() )
title = '';
markup = ( title.length ) ? '<div class="jp-carousel-titleanddesc-title">' + title + '</div>' : '';
diff --git a/plugins/jetpack/modules/carousel/jetpack-carousel.php b/plugins/jetpack/modules/carousel/jetpack-carousel.php
index 787f177a..f1d1babb 100644
--- a/plugins/jetpack/modules/carousel/jetpack-carousel.php
+++ b/plugins/jetpack/modules/carousel/jetpack-carousel.php
@@ -240,8 +240,7 @@ class Jetpack_Carousel {
if ( defined( 'IS_WPCOM' ) && IS_WPCOM ) {
$likes_blog_id = $blog_id;
} else {
- $jetpack = Jetpack::init();
- $likes_blog_id = $jetpack->get_option( 'id' );
+ $likes_blog_id = Jetpack_Options::get_option( 'id' );
}
$extra_data = array(
diff --git a/plugins/jetpack/modules/carousel/jquery.spin.js b/plugins/jetpack/modules/carousel/jquery.spin.js
deleted file mode 100644
index 4642af13..00000000
--- a/plugins/jetpack/modules/carousel/jquery.spin.js
+++ /dev/null
@@ -1,86 +0,0 @@
-/*
- * Matt Husby https://github.com/matthusby/spin.js
- * Based on the jquery plugin by Bradley Smith
- * https://gist.github.com/1290439
- */
-
-/*
-Add spin to the jQuery object
-If color is not passed the spinner will be black
-You can now create a spinner using any of the variants below:
-$("#el").spin(); // Produces default Spinner
-$("#el").spin("small"); // Produces a 'small' Spinner
-$("#el").spin("large", "white"); // Produces a 'large' Spinner in white (or any valid CSS color).
-$("#el").spin({ ... }); // Produces a Spinner using your custom settings.
-$("#el").spin("small-right"); // Pin the small spinner to the right edge
-$("#el").spin("{small, medium, large}-{left, right, top, bottom}"); // All options for where to pin
-$("#el").spin(false); // Kills the spinner.
-*/
-
-( function( $ ) {
- $.fn.spin = function( opts, color ) {
- var presets = {
- "small": { lines: 8, length: 2, width: 2, radius: 3, trail: 60, speed: 1.3 },
- "medium": { lines: 8, length: 4, width: 3, radius: 5, trail: 60, speed: 1.3 },
- "large": { lines: 10, length: 6, width: 4, radius: 7, trail: 60, speed: 1.3 }
- };
- if ( Spinner ) {
- return this.each( function() {
- var $this = $( this ),
- data = $this.data();
-
- if ( data.spinner ) {
- data.spinner.stop();
- delete data.spinner;
- }
- if ( opts !== false ) {
- var spinner_options;
- if ( typeof opts === "string" ) {
- var spinner_base = opts.indexOf( '-' );
- if( spinner_base == -1 ) {
- spinner_base = opts;
- } else {
- spinner_base = opts.substring( 0, spinner_base );
- }
- if ( spinner_base in presets ) {
- spinner_options = presets[spinner_base];
- } else {
- spinner_options = {};
- }
- var padding;
- if ( opts.indexOf( "-right" ) != -1 ) {
- padding = jQuery( this ).css( 'padding-left' );
- if( typeof padding === "undefined" ) {
- padding = 0;
- } else {
- padding = padding.replace( 'px', '' );
- }
- spinner_options.left = jQuery( this ).outerWidth() - ( 2 * ( spinner_options.length + spinner_options.width + spinner_options.radius ) ) - padding - 5;
- }
- if ( opts.indexOf( '-left' ) != -1 ) {
- spinner_options.left = 5;
- }
- if ( opts.indexOf( '-top' ) != -1 ) {
- spinner_options.top = 5;
- }
- if ( opts.indexOf( '-bottom' ) != -1 ) {
- padding = jQuery( this ).css( 'padding-top' );
- if( typeof padding === "undefined" ) {
- padding = 0;
- } else {
- padding = padding.replace( 'px', '' );
- }
- spinner_options.top = jQuery( this ).outerHeight() - ( 2 * ( spinner_options.length + spinner_options.width + spinner_options.radius ) ) - padding - 5;
- }
- }
- if( color ){
- spinner_options.color = color;
- }
- data.spinner = new Spinner( spinner_options ).spin( this );
- }
- });
- } else {
- throw "Spinner class not available.";
- }
- };
-})( jQuery ); \ No newline at end of file
diff --git a/plugins/jetpack/modules/carousel/spin.js b/plugins/jetpack/modules/carousel/spin.js
deleted file mode 100644
index f506cd2b..00000000
--- a/plugins/jetpack/modules/carousel/spin.js
+++ /dev/null
@@ -1,301 +0,0 @@
-//fgnass.github.com/spin.js#v1.2.4
-(function(window, document, undefined) {
-
-/**
- * Copyright (c) 2011 Felix Gnass [fgnass at neteye dot de]
- * Licensed under the MIT license
- */
-
- var prefixes = ['webkit', 'Moz', 'ms', 'O']; /* Vendor prefixes */
- var animations = {}; /* Animation rules keyed by their name */
- var useCssAnimations;
-
- /**
- * Utility function to create elements. If no tag name is given,
- * a DIV is created. Optionally properties can be passed.
- */
- function createEl(tag, prop) {
- var el = document.createElement(tag || 'div');
- var n;
-
- for(n in prop) {
- el[n] = prop[n];
- }
- return el;
- }
-
- /**
- * Appends children and returns the parent.
- */
- function ins(parent /* child1, child2, ...*/) {
- for (var i=1, n=arguments.length; i<n; i++) {
- parent.appendChild(arguments[i]);
- }
- return parent;
- }
-
- /**
- * Insert a new stylesheet to hold the @keyframe or VML rules.
- */
- var sheet = function() {
- var el = createEl('style');
- ins(document.getElementsByTagName('head')[0], el);
- return el.sheet || el.styleSheet;
- }();
-
- /**
- * Creates an opacity keyframe animation rule and returns its name.
- * Since most mobile Webkits have timing issues with animation-delay,
- * we create separate rules for each line/segment.
- */
- function addAnimation(alpha, trail, i, lines) {
- var name = ['opacity', trail, ~~(alpha*100), i, lines].join('-');
- var start = 0.01 + i/lines*100;
- var z = Math.max(1-(1-alpha)/trail*(100-start) , alpha);
- var prefix = useCssAnimations.substring(0, useCssAnimations.indexOf('Animation')).toLowerCase();
- var pre = prefix && '-'+prefix+'-' || '';
-
- if (!animations[name]) {
- sheet.insertRule(
- '@' + pre + 'keyframes ' + name + '{' +
- '0%{opacity:'+z+'}' +
- start + '%{opacity:'+ alpha + '}' +
- (start+0.01) + '%{opacity:1}' +
- (start+trail)%100 + '%{opacity:'+ alpha + '}' +
- '100%{opacity:'+ z + '}' +
- '}', 0);
- animations[name] = 1;
- }
- return name;
- }
-
- /**
- * Tries various vendor prefixes and returns the first supported property.
- **/
- function vendor(el, prop) {
- var s = el.style;
- var pp;
- var i;
-
- if(s[prop] !== undefined) return prop;
- prop = prop.charAt(0).toUpperCase() + prop.slice(1);
- for(i=0; i<prefixes.length; i++) {
- pp = prefixes[i]+prop;
- if(s[pp] !== undefined) return pp;
- }
- }
-
- /**
- * Sets multiple style properties at once.
- */
- function css(el, prop) {
- for (var n in prop) {
- el.style[vendor(el, n)||n] = prop[n];
- }
- return el;
- }
-
- /**
- * Fills in default values.
- */
- function merge(obj) {
- for (var i=1; i < arguments.length; i++) {
- var def = arguments[i];
- for (var n in def) {
- if (obj[n] === undefined) obj[n] = def[n];
- }
- }
- return obj;
- }
-
- /**
- * Returns the absolute page-offset of the given element.
- */
- function pos(el) {
- var o = {x:el.offsetLeft, y:el.offsetTop};
- while((el = el.offsetParent)) {
- o.x+=el.offsetLeft;
- o.y+=el.offsetTop;
- }
- return o;
- }
-
- var defaults = {
- lines: 12, // The number of lines to draw
- length: 7, // The length of each line
- width: 5, // The line thickness
- radius: 10, // The radius of the inner circle
- color: '#000', // #rgb or #rrggbb
- speed: 1, // Rounds per second
- trail: 100, // Afterglow percentage
- opacity: 1/4, // Opacity of the lines
- fps: 20, // Frames per second when using setTimeout()
- zIndex: 2e9, // Use a high z-index by default
- className: 'spinner', // CSS class to assign to the element
- top: 'auto', // center vertically
- left: 'auto' // center horizontally
- };
-
- /** The constructor */
- var Spinner = function Spinner(o) {
- if (!this.spin) return new Spinner(o);
- this.opts = merge(o || {}, Spinner.defaults, defaults);
- };
-
- Spinner.defaults = {};
- Spinner.prototype = {
- spin: function(target) {
- this.stop();
- var self = this;
- var o = self.opts;
- var el = self.el = css(createEl(0, {className: o.className}), {position: 'relative', zIndex: o.zIndex});
- var mid = o.radius+o.length+o.width;
- var ep; // element position
- var tp; // target position
-
- if (target) {
- target.insertBefore(el, target.firstChild||null);
- tp = pos(target);
- ep = pos(el);
- css(el, {
- left: (o.left == 'auto' ? tp.x-ep.x + (target.offsetWidth >> 1) : o.left+mid) + 'px',
- top: (o.top == 'auto' ? tp.y-ep.y + (target.offsetHeight >> 1) : o.top+mid) + 'px'
- });
- }
-
- el.setAttribute('aria-role', 'progressbar');
- self.lines(el, self.opts);
-
- if (!useCssAnimations) {
- // No CSS animation support, use setTimeout() instead
- var i = 0;
- var fps = o.fps;
- var f = fps/o.speed;
- var ostep = (1-o.opacity)/(f*o.trail / 100);
- var astep = f/o.lines;
-
- !function anim() {
- i++;
- for (var s=o.lines; s; s--) {
- var alpha = Math.max(1-(i+s*astep)%f * ostep, o.opacity);
- self.opacity(el, o.lines-s, alpha, o);
- }
- self.timeout = self.el && setTimeout(anim, ~~(1000/fps));
- }();
- }
- return self;
- },
- stop: function() {
- var el = this.el;
- if (el) {
- clearTimeout(this.timeout);
- if (el.parentNode) el.parentNode.removeChild(el);
- this.el = undefined;
- }
- return this;
- },
- lines: function(el, o) {
- var i = 0;
- var seg;
-
- function fill(color, shadow) {
- return css(createEl(), {
- position: 'absolute',
- width: (o.length+o.width) + 'px',
- height: o.width + 'px',
- background: color,
- boxShadow: shadow,
- transformOrigin: 'left',
- transform: 'rotate(' + ~~(360/o.lines*i) + 'deg) translate(' + o.radius+'px' +',0)',
- borderRadius: (o.width>>1) + 'px'
- });
- }
- for (; i < o.lines; i++) {
- seg = css(createEl(), {
- position: 'absolute',
- top: 1+~(o.width/2) + 'px',
- transform: o.hwaccel ? 'translate3d(0,0,0)' : '',
- opacity: o.opacity,
- animation: useCssAnimations && addAnimation(o.opacity, o.trail, i, o.lines) + ' ' + 1/o.speed + 's linear infinite'
- });
- if (o.shadow) ins(seg, css(fill('#000', '0 0 4px ' + '#000'), {top: 2+'px'}));
- ins(el, ins(seg, fill(o.color, '0 0 1px rgba(0,0,0,.1)')));
- }
- return el;
- },
- opacity: function(el, i, val) {
- if (i < el.childNodes.length) el.childNodes[i].style.opacity = val;
- }
- };
-
- /////////////////////////////////////////////////////////////////////////
- // VML rendering for IE
- /////////////////////////////////////////////////////////////////////////
-
- /**
- * Check and init VML support
- */
- !function() {
- var s = css(createEl('group'), {behavior: 'url(#default#VML)'});
- var i;
-
- if (!vendor(s, 'transform') && s.adj) {
-
- // VML support detected. Insert CSS rules ...
- for (i=4; i--;) sheet.addRule(['group', 'roundrect', 'fill', 'stroke'][i], 'behavior:url(#default#VML)');
-
- Spinner.prototype.lines = function(el, o) {
- var r = o.length+o.width;
- var s = 2*r;
-
- function grp() {
- return css(createEl('group', {coordsize: s +' '+s, coordorigin: -r +' '+-r}), {width: s, height: s});
- }
-
- var margin = -(o.width+o.length)*2+'px';
- var g = css(grp(), {position: 'absolute', top: margin, left: margin});
-
- var i;
-
- function seg(i, dx, filter) {
- ins(g,
- ins(css(grp(), {rotation: 360 / o.lines * i + 'deg', left: ~~dx}),
- ins(css(createEl('roundrect', {arcsize: 1}), {
- width: r,
- height: o.width,
- left: o.radius,
- top: -o.width>>1,
- filter: filter
- }),
- createEl('fill', {color: o.color, opacity: o.opacity}),
- createEl('stroke', {opacity: 0}) // transparent stroke to fix color bleeding upon opacity change
- )
- )
- );
- }
-
- if (o.shadow) {
- for (i = 1; i <= o.lines; i++) {
- seg(i, -2, 'progid:DXImageTransform.Microsoft.Blur(pixelradius=2,makeshadow=1,shadowopacity=.3)');
- }
- }
- for (i = 1; i <= o.lines; i++) seg(i);
- return ins(el, g);
- };
- Spinner.prototype.opacity = function(el, i, val, o) {
- var c = el.firstChild;
- o = o.shadow && o.lines || 0;
- if (c && i+o < c.childNodes.length) {
- c = c.childNodes[i+o]; c = c && c.firstChild; c = c && c.firstChild;
- if (c) c.opacity = val;
- }
- };
- }
- else {
- useCssAnimations = vendor(s, 'animation');
- }
- }();
-
- window.Spinner = Spinner;
-
-})(window, document); \ No newline at end of file
diff --git a/plugins/jetpack/modules/comments.php b/plugins/jetpack/modules/comments.php
index 26bc504f..524db982 100644
--- a/plugins/jetpack/modules/comments.php
+++ b/plugins/jetpack/modules/comments.php
@@ -6,6 +6,7 @@
* First Introduced: 1.4
* Sort Order: 2
* Requires Connection: Yes
+ * Auto Activate: No
*/
require dirname( __FILE__ ) . '/comments/comments.php';
diff --git a/plugins/jetpack/modules/comments/base.php b/plugins/jetpack/modules/comments/base.php
index 858b6e3e..93e59f00 100644
--- a/plugins/jetpack/modules/comments/base.php
+++ b/plugins/jetpack/modules/comments/base.php
@@ -185,7 +185,7 @@ class Highlander_Comments_Base {
* @return If no
*/
function allow_logged_out_user_to_comment_as_external() {
- if ( !$this->is_highlander_comment_post( 'facebook', 'twitter' ) ) {
+ if ( !$this->is_highlander_comment_post( 'facebook', 'twitter', 'googleplus' ) ) {
return;
}
@@ -213,7 +213,7 @@ class Highlander_Comments_Base {
}
// Bail if this is not a guest or external service credentialed request
- if ( !$this->is_highlander_comment_post( 'guest', 'facebook', 'twitter' ) ) {
+ if ( !$this->is_highlander_comment_post( 'guest', 'facebook', 'twitter', 'googleplus' ) ) {
return $comment_data;
}
@@ -233,12 +233,18 @@ class Highlander_Comments_Base {
}
}
+ $author_change = false;
foreach ( array( 'comment_author' => 'author', 'comment_author_email' => 'email', 'comment_author_url' => 'url' ) as $comment_field => $post_field ) {
+ if ( $comment_data[$comment_field] != $_POST[$post_field] && 'url' != $post_field ) {
+ $author_change = true;
+ }
$comment_data[$comment_field] = $_POST[$post_field];
}
- // Mark as guest comment
- $comment_data['user_id'] = $comment_data['user_ID'] = 0;
+ // Mark as guest comment if name or email were changed
+ if ( $author_change ) {
+ $comment_data['user_id'] = $comment_data['user_ID'] = 0;
+ }
return $comment_data;
}
diff --git a/plugins/jetpack/modules/comments/comments.php b/plugins/jetpack/modules/comments/comments.php
index fa156bb6..ad2d50b7 100644
--- a/plugins/jetpack/modules/comments/comments.php
+++ b/plugins/jetpack/modules/comments/comments.php
@@ -200,7 +200,7 @@ class Jetpack_Comments extends Highlander_Comments_Base {
}
$params = array(
- 'blogid' => Jetpack::get_option( 'id' ),
+ 'blogid' => Jetpack_Options::get_option( 'id' ),
'postid' => get_the_ID(),
'comment_registration' => ( get_option( 'comment_registration' ) ? '1' : '0' ), // Need to explicitly send a '1' or a '0' for these
'require_name_email' => ( get_option( 'require_name_email' ) ? '1' : '0' ),
@@ -227,7 +227,7 @@ class Jetpack_Comments extends Highlander_Comments_Base {
$params['_wp_unfiltered_html_comment'] = wp_create_nonce( 'unfiltered-html-comment_' . get_the_ID() );
}
- $signature = Jetpack_Comments::sign_remote_comment_parameters( $params, Jetpack::get_option( 'blog_token' ) );
+ $signature = Jetpack_Comments::sign_remote_comment_parameters( $params, Jetpack_Options::get_option( 'blog_token' ) );
if ( is_wp_error( $signature ) ) {
$signature = 'error';
}
@@ -377,7 +377,7 @@ class Jetpack_Comments extends Highlander_Comments_Base {
if ( FALSE !== strpos( $post_array['hc_avatar'], '.gravatar.com' ) )
$post_array['hc_avatar'] = htmlentities( $post_array['hc_avatar'] );
- $check = Jetpack_Comments::sign_remote_comment_parameters( $post_array, Jetpack::get_option( 'blog_token' ) );
+ $check = Jetpack_Comments::sign_remote_comment_parameters( $post_array, Jetpack_Options::get_option( 'blog_token' ) );
if ( is_wp_error( $check ) ) {
wp_die( $check );
}
diff --git a/plugins/jetpack/modules/contact-form.php b/plugins/jetpack/modules/contact-form.php
index 21f409ff..64c5600f 100644
--- a/plugins/jetpack/modules/contact-form.php
+++ b/plugins/jetpack/modules/contact-form.php
@@ -5,6 +5,7 @@
* Sort Order: 9
* First Introduced: 1.3
* Requires Connection: No
+ * Auto Activate: Yes
*/
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
index 5a6af06b..96d49ed3 100644
--- a/plugins/jetpack/modules/contact-form/admin.php
+++ b/plugins/jetpack/modules/contact-form/admin.php
@@ -402,7 +402,12 @@ jQuery(document).ready( function($) {
break;
case 'feedback_date':
- echo get_the_date( __( 'Y-m-d @ g:i:s A', 'jetpack' ) );
+
+ $date_time_format = _x( '%1$s \a\t %2$s', '{$date_format} \a\t {$time_format}', 'jetpack' );
+ $date_time_format = sprintf( $date_time_format, get_option( 'date_format' ), get_option( 'time_format' ) );
+ $time = date_i18n( $date_time_format, get_the_time( 'U' ) );
+
+ echo $time;
break;
}
}
diff --git a/plugins/jetpack/modules/contact-form/css/menu-alter.css b/plugins/jetpack/modules/contact-form/css/menu-alter.css
index d0b175e0..1ba4a609 100644
--- a/plugins/jetpack/modules/contact-form/css/menu-alter.css
+++ b/plugins/jetpack/modules/contact-form/css/menu-alter.css
@@ -12,11 +12,11 @@
background: url(../images/grunion-menu.png) no-repeat 7px 7px !important;
}
-body.admin-color-mp6 #adminmenu .menu-icon-feedback div.wp-menu-image {
+body.mp6 #adminmenu .menu-icon-feedback div.wp-menu-image {
background: none !important;
}
-body.admin-color-mp6 #adminmenu .menu-icon-feedback div.wp-menu-image:before {
+body.mp6 #adminmenu .menu-icon-feedback div.wp-menu-image:before {
content: '\f175';
margin-left: -1px;
}
diff --git a/plugins/jetpack/modules/contact-form/css/rtl/menu-alter-rtl.css b/plugins/jetpack/modules/contact-form/css/rtl/menu-alter-rtl.css
index fe824007..f625fa48 100644
--- a/plugins/jetpack/modules/contact-form/css/rtl/menu-alter-rtl.css
+++ b/plugins/jetpack/modules/contact-form/css/rtl/menu-alter-rtl.css
@@ -1,4 +1,4 @@
-/* This file was automatically generated on Jun 19 2013 15:20:57 */
+/* This file was automatically generated on Sep 05 2013 19:17:52 */
#menu-posts-feedback .wp-menu-image img {
display: none;
@@ -14,15 +14,11 @@
background: url(../../images/grunion-menu.png) no-repeat 7px 7px !important;
}
-body.admin-color-mp6 #adminmenu .toplevel_page_feedback div.wp-menu-image img {
- display: none;
-}
-
-body.admin-color-mp6 #adminmenu .menu-icon-feedback div.wp-menu-image, body.admin-color-mp6 #adminmenu .toplevel_page_feedback div.wp-menu-image {
+body.mp6 #adminmenu .menu-icon-feedback div.wp-menu-image {
background: none !important;
}
-body.admin-color-mp6 #adminmenu .menu-icon-feedback div.wp-menu-image:before, body.admin-color-mp6 #adminmenu .toplevel_page_feedback div.wp-menu-image:before {
+body.mp6 #adminmenu .menu-icon-feedback div.wp-menu-image:before {
content: '\f175';
margin-right: -1px;
}
diff --git a/plugins/jetpack/modules/contact-form/grunion-contact-form.php b/plugins/jetpack/modules/contact-form/grunion-contact-form.php
index 76e43be2..e85c3fc2 100644
--- a/plugins/jetpack/modules/contact-form/grunion-contact-form.php
+++ b/plugins/jetpack/modules/contact-form/grunion-contact-form.php
@@ -72,9 +72,8 @@ class Grunion_Contact_Form_Plugin {
// Export to CSV feature
if ( is_admin() ) {
- add_action( 'admin_init', array( $this, 'download_feedback_as_csv' ) );
- add_action( 'load-edit.php', array( $this, 'export_form' ) );
- add_action( 'admin_footer', array( $this, 'move_export_form_to_bottom' ) );
+ add_action( 'admin_init', array( $this, 'download_feedback_as_csv' ) );
+ add_action( 'admin_footer-edit.php', array( $this, 'export_form' ) );
}
// custom post type we'll use to keep copies of the feedback items
@@ -342,28 +341,6 @@ class Grunion_Contact_Form_Plugin {
}
/**
- * There aren't any usable actions in core to output the "export feedback" form in the correct place,
- * so this inline JS moves it from the top of the page to the bottom.
- */
- function move_export_form_to_bottom() {
- if ( get_current_screen()->id != 'edit-feedback' )
- return;
-
- // if there aren't any feedbacks, bail out
- if ( ! (int) wp_count_posts( 'feedback' )->publish )
- return;
-
- echo "
- <script type='text/javascript'>
- var menu = document.getElementById( 'feedback-export' ),
- wrapper = document.getElementsByClassName( 'wrap' )[0];
- wrapper.appendChild(menu);
- menu.style.display = 'block';
- </script>
- ";
- }
-
- /**
* Prints the menu
*/
function export_form() {
@@ -382,7 +359,7 @@ class Grunion_Contact_Form_Plugin {
<?php wp_nonce_field( 'feedback_export','feedback_export_nonce' ); ?>
<input name="action" value="feedback_export" type="hidden">
- <label for="post"><? _e( 'Select feedback to download', 'jetpack' ) ?></label>
+ <label for="post"><?php _e( 'Select feedback to download', 'jetpack' ) ?></label>
<select name="post">
<option value="all"><?php esc_html_e( 'All posts', 'jetpack' ) ?></option>
<?php echo $this->get_feedbacks_as_options() ?>
@@ -394,6 +371,16 @@ class Grunion_Contact_Form_Plugin {
</div>
<?php
+ // There aren't any usable actions in core to output the "export feedback" form in the correct place,
+ // so this inline JS moves it from the top of the page to the bottom.
+ ?>
+ <script type='text/javascript'>
+ var menu = document.getElementById( 'feedback-export' ),
+ wrapper = document.getElementsByClassName( 'wrap' )[0];
+ wrapper.appendChild(menu);
+ menu.style.display = 'block';
+ </script>
+ <?php
}
/**
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
deleted file mode 100644
index 11e456fe..00000000
--- a/plugins/jetpack/modules/contact-form/js/jquery-ui-1.8.4.custom.min.js
+++ /dev/null
@@ -1,185 +0,0 @@
-/*!
- * 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/custom-css.php b/plugins/jetpack/modules/custom-css.php
index 051976a3..55d5e484 100644
--- a/plugins/jetpack/modules/custom-css.php
+++ b/plugins/jetpack/modules/custom-css.php
@@ -6,6 +6,7 @@
* Sort Order: 11
* First Introduced: 1.7
* Requires Connection: No
+ * Auto Activate: Yes
*/
function jetpack_load_custom_css() {
diff --git a/plugins/jetpack/modules/custom-css/custom-css.php b/plugins/jetpack/modules/custom-css/custom-css.php
index 02fa7586..9c3e7e7c 100644
--- a/plugins/jetpack/modules/custom-css/custom-css.php
+++ b/plugins/jetpack/modules/custom-css/custom-css.php
@@ -901,14 +901,25 @@ class Jetpack_Custom_CSS {
/**
* Render metabox listing CSS revisions and the themes that correspond to the revisions.
- * Called by afecss_admin *
- * @param array $safecss_post
+ * Called by safecss_admin
+ *
* @global $post
- * @uses WP_Query, wp_post_revision_title, esc_html, add_query_arg, menu_page_url, wp_reset_query
+ * @param array $safecss_post
+ * @uses wp_revisions_to_keep
+ * @uses WP_Query
+ * @uses wp_post_revision_title
+ * @uses esc_html
+ * @uses add_query_arg
+ * @uses menu_page_url
+ * @uses wp_reset_query
* @return string
*/
static function revisions_meta_box( $safecss_post ) {
- $max_revisions = defined( 'WP_POST_REVISIONS' ) && is_numeric( WP_POST_REVISIONS ) ? (int) WP_POST_REVISIONS : 25;
+ if ( function_exists( 'wp_revisions_to_keep' ) )
+ $max_revisions = wp_revisions_to_keep( $safecss_post );
+ else
+ $max_revisions = defined( 'WP_POST_REVISIONS' ) && is_numeric( WP_POST_REVISIONS ) ? (int) WP_POST_REVISIONS : 25;
+
$posts_per_page = isset( $_GET['show_all_rev'] ) ? $max_revisions : 6;
$revisions = new WP_Query( array(
diff --git a/plugins/jetpack/modules/custom-css/custom-css/blank.css b/plugins/jetpack/modules/custom-css/custom-css/blank.css
deleted file mode 100644
index 79a9626b..00000000
--- a/plugins/jetpack/modules/custom-css/custom-css/blank.css
+++ /dev/null
@@ -1 +0,0 @@
-/* */
diff --git a/plugins/jetpack/modules/custom-css/custom-css/css/css-editor.css b/plugins/jetpack/modules/custom-css/custom-css/css/css-editor.css
index 4dec4125..230072c6 100644
--- a/plugins/jetpack/modules/custom-css/custom-css/css/css-editor.css
+++ b/plugins/jetpack/modules/custom-css/custom-css/css/css-editor.css
@@ -21,4 +21,7 @@
.misc-pub-section > div {
margin-top: 3px;
+}
+#safecss-ace .ace_gutter {
+ z-index: 1;
} \ No newline at end of file
diff --git a/plugins/jetpack/modules/custom-css/custom-css/preprocessors/lessc.inc.php b/plugins/jetpack/modules/custom-css/custom-css/preprocessors/lessc.inc.php
index c42147c5..8bdfa3fb 100644
--- a/plugins/jetpack/modules/custom-css/custom-css/preprocessors/lessc.inc.php
+++ b/plugins/jetpack/modules/custom-css/custom-css/preprocessors/lessc.inc.php
@@ -1,7 +1,7 @@
<?php
/**
- * lessphp v0.3.8
+ * lessphp v0.4.0
* http://leafo.net/lessphp
*
* LESS css compiler, adapted from http://lesscss.org
@@ -38,7 +38,7 @@
* handling things like indentation.
*/
class lessc {
- static public $VERSION = "v0.3.8";
+ static public $VERSION = "v0.4.0";
static protected $TRUE = array("keyword", "true");
static protected $FALSE = array("keyword", "false");
@@ -55,6 +55,8 @@ class lessc {
protected $numberPrecision = null;
+ protected $allParsedFiles = array();
+
// set to the parser that generated the current line when compiling
// so we know how to create error messages
protected $sourceParser = null;
@@ -103,12 +105,17 @@ class lessc {
if (substr_compare($url, '.css', -4, 4) === 0) return false;
$realPath = $this->findImport($url);
+
if ($realPath === null) return false;
if ($this->importDisabled) {
return array(false, "/* import disabled */");
}
+ if (isset($this->allParsedFiles[realpath($realPath)])) {
+ return array(false, null);
+ }
+
$this->addParsedFile($realPath);
$parser = $this->makeParser($realPath);
$root = $parser->parse(file_get_contents($realPath));
@@ -276,6 +283,8 @@ class lessc {
foreach ($this->sortProps($block->props) as $prop) {
$this->compileProp($prop, $block, $out);
}
+
+ $out->lines = array_values(array_unique($out->lines));
}
protected function sortProps($props, $split = false) {
@@ -327,6 +336,9 @@ class lessc {
$parts[] = "($q[1])";
}
break;
+ case "variable":
+ $parts[] = $this->compileValue($this->reduce($q));
+ break;
}
}
@@ -434,7 +446,7 @@ class lessc {
foreach ($selectors as $s) {
if (is_array($s)) {
list(, $value) = $s;
- $out[] = $this->compileValue($this->reduce($value));
+ $out[] = trim($this->compileValue($this->reduce($value)));
} else {
$out[] = $s;
}
@@ -447,7 +459,7 @@ class lessc {
return $left == $right;
}
- protected function patternMatch($block, $callingArgs) {
+ protected function patternMatch($block, $orderedArgs, $keywordArgs) {
// match the guards if it has them
// any one of the groups must have all its guards pass for a match
if (!empty($block->guards)) {
@@ -455,7 +467,7 @@ class lessc {
foreach ($block->guards as $guardGroup) {
foreach ($guardGroup as $guard) {
$this->pushEnv();
- $this->zipSetArgs($block->args, $callingArgs);
+ $this->zipSetArgs($block->args, $orderedArgs, $keywordArgs);
$negate = false;
if ($guard[0] == "negate") {
@@ -484,24 +496,34 @@ class lessc {
}
}
- $numCalling = count($callingArgs);
-
if (empty($block->args)) {
- return $block->isVararg || $numCalling == 0;
+ return $block->isVararg || empty($orderedArgs) && empty($keywordArgs);
+ }
+
+ $remainingArgs = $block->args;
+ if ($keywordArgs) {
+ $remainingArgs = array();
+ foreach ($block->args as $arg) {
+ if ($arg[0] == "arg" && isset($keywordArgs[$arg[1]])) {
+ continue;
+ }
+
+ $remainingArgs[] = $arg;
+ }
}
$i = -1; // no args
// try to match by arity or by argument literal
- foreach ($block->args as $i => $arg) {
+ foreach ($remainingArgs as $i => $arg) {
switch ($arg[0]) {
case "lit":
- if (empty($callingArgs[$i]) || !$this->eq($arg[1], $callingArgs[$i])) {
+ if (empty($orderedArgs[$i]) || !$this->eq($arg[1], $orderedArgs[$i])) {
return false;
}
break;
case "arg":
// no arg and no default value
- if (!isset($callingArgs[$i]) && !isset($arg[2])) {
+ if (!isset($orderedArgs[$i]) && !isset($arg[2])) {
return false;
}
break;
@@ -516,14 +538,19 @@ class lessc {
} else {
$numMatched = $i + 1;
// greater than becuase default values always match
- return $numMatched >= $numCalling;
+ return $numMatched >= count($orderedArgs);
}
}
- protected function patternMatchAll($blocks, $callingArgs) {
+ protected function patternMatchAll($blocks, $orderedArgs, $keywordArgs, $skip=array()) {
$matches = null;
foreach ($blocks as $block) {
- if ($this->patternMatch($block, $callingArgs)) {
+ // skip seen blocks that don't have arguments
+ if (isset($skip[$block->id]) && !isset($block->args)) {
+ continue;
+ }
+
+ if ($this->patternMatch($block, $orderedArgs, $keywordArgs)) {
$matches[] = $block;
}
}
@@ -532,7 +559,7 @@ class lessc {
}
// attempt to find blocks matched by path and args
- protected function findBlocks($searchIn, $path, $args, $seen=array()) {
+ protected function findBlocks($searchIn, $path, $orderedArgs, $keywordArgs, $seen=array()) {
if ($searchIn == null) return null;
if (isset($seen[$searchIn->id])) return null;
$seen[$searchIn->id] = true;
@@ -542,7 +569,7 @@ class lessc {
if (isset($searchIn->children[$name])) {
$blocks = $searchIn->children[$name];
if (count($path) == 1) {
- $matches = $this->patternMatchAll($blocks, $args);
+ $matches = $this->patternMatchAll($blocks, $orderedArgs, $keywordArgs, $seen);
if (!empty($matches)) {
// This will return all blocks that match in the closest
// scope that has any matching block, like lessjs
@@ -552,7 +579,7 @@ class lessc {
$matches = array();
foreach ($blocks as $subBlock) {
$subMatches = $this->findBlocks($subBlock,
- array_slice($path, 1), $args, $seen);
+ array_slice($path, 1), $orderedArgs, $keywordArgs, $seen);
if (!is_null($subMatches)) {
foreach ($subMatches as $sm) {
@@ -564,39 +591,51 @@ class lessc {
return count($matches) > 0 ? $matches : null;
}
}
-
if ($searchIn->parent === $searchIn) return null;
- return $this->findBlocks($searchIn->parent, $path, $args, $seen);
+ return $this->findBlocks($searchIn->parent, $path, $orderedArgs, $keywordArgs, $seen);
}
// sets all argument names in $args to either the default value
// or the one passed in through $values
- protected function zipSetArgs($args, $values) {
- $i = 0;
+ protected function zipSetArgs($args, $orderedValues, $keywordValues) {
$assignedValues = array();
- foreach ($args as $a) {
+
+ $i = 0;
+ foreach ($args as $a) {
if ($a[0] == "arg") {
- if ($i < count($values) && !is_null($values[$i])) {
- $value = $values[$i];
+ if (isset($keywordValues[$a[1]])) {
+ // has keyword arg
+ $value = $keywordValues[$a[1]];
+ } elseif (isset($orderedValues[$i])) {
+ // has ordered arg
+ $value = $orderedValues[$i];
+ $i++;
} elseif (isset($a[2])) {
+ // has default value
$value = $a[2];
- } else $value = null;
+ } else {
+ $this->throwError("Failed to assign arg " . $a[1]);
+ $value = null; // :(
+ }
$value = $this->reduce($value);
$this->set($a[1], $value);
$assignedValues[] = $value;
+ } else {
+ // a lit
+ $i++;
}
- $i++;
}
// check for a rest
$last = end($args);
if ($last[0] == "rest") {
- $rest = array_slice($values, count($args) - 1);
+ $rest = array_slice($orderedValues, count($args) - 1);
$this->set($last[1], $this->reduce(array("list", " ", $rest)));
}
- $this->env->arguments = $assignedValues;
+ // wow is this the only true use of PHP's + operator for arrays?
+ $this->env->arguments = $assignedValues + $orderedValues;
}
// compile a prop and update $lines or $blocks appropriately
@@ -621,8 +660,28 @@ class lessc {
case 'mixin':
list(, $path, $args, $suffix) = $prop;
- $args = array_map(array($this, "reduce"), (array)$args);
- $mixins = $this->findBlocks($block, $path, $args);
+ $orderedArgs = array();
+ $keywordArgs = array();
+ foreach ((array)$args as $arg) {
+ $argval = null;
+ switch ($arg[0]) {
+ case "arg":
+ if (!isset($arg[2])) {
+ $orderedArgs[] = $this->reduce(array("variable", $arg[1]));
+ } else {
+ $keywordArgs[$arg[1]] = $this->reduce($arg[2]);
+ }
+ break;
+
+ case "lit":
+ $orderedArgs[] = $this->reduce($arg[1]);
+ break;
+ default:
+ $this->throwError("Unknown arg type: " . $arg[0]);
+ }
+ }
+
+ $mixins = $this->findBlocks($block, $path, $orderedArgs, $keywordArgs);
if ($mixins === null) {
// fwrite(STDERR,"failed to find block: ".implode(" > ", $path)."\n");
@@ -630,6 +689,10 @@ class lessc {
}
foreach ($mixins as $mixin) {
+ if ($mixin === $block && !$orderedArgs) {
+ continue;
+ }
+
$haveScope = false;
if (isset($mixin->parent->scope)) {
$haveScope = true;
@@ -641,7 +704,7 @@ class lessc {
if (isset($mixin->args)) {
$haveArgs = true;
$this->pushEnv();
- $this->zipSetArgs($mixin->args, $args);
+ $this->zipSetArgs($mixin->args, $orderedArgs, $keywordArgs);
}
$oldParent = $mixin->parent;
@@ -698,7 +761,9 @@ class lessc {
list(,$importId) = $prop;
$import = $this->env->imports[$importId];
if ($import[0] === false) {
- $out->lines[] = $import[1];
+ if (isset($import[1])) {
+ $out->lines[] = $import[1];
+ }
} else {
list(, $bottom, $parser, $importDir) = $import;
$this->compileImportedProps($bottom, $block, $out, $parser, $importDir);
@@ -786,6 +851,60 @@ class lessc {
}
}
+ protected function lib_pow($args) {
+ list($base, $exp) = $this->assertArgs($args, 2, "pow");
+ return pow($this->assertNumber($base), $this->assertNumber($exp));
+ }
+
+ protected function lib_pi() {
+ return pi();
+ }
+
+ protected function lib_mod($args) {
+ list($a, $b) = $this->assertArgs($args, 2, "mod");
+ return $this->assertNumber($a) % $this->assertNumber($b);
+ }
+
+ protected function lib_tan($num) {
+ return tan($this->assertNumber($num));
+ }
+
+ protected function lib_sin($num) {
+ return sin($this->assertNumber($num));
+ }
+
+ protected function lib_cos($num) {
+ return cos($this->assertNumber($num));
+ }
+
+ protected function lib_atan($num) {
+ $num = atan($this->assertNumber($num));
+ return array("number", $num, "rad");
+ }
+
+ protected function lib_asin($num) {
+ $num = asin($this->assertNumber($num));
+ return array("number", $num, "rad");
+ }
+
+ protected function lib_acos($num) {
+ $num = acos($this->assertNumber($num));
+ return array("number", $num, "rad");
+ }
+
+ protected function lib_sqrt($num) {
+ return sqrt($this->assertNumber($num));
+ }
+
+ protected function lib_extract($value) {
+ list($list, $idx) = $this->assertArgs($value, 2, "extract");
+ $idx = $this->assertNumber($idx);
+ // 1 indexed
+ if ($list[0] == "list" && isset($list[2][$idx - 1])) {
+ return $list[2][$idx - 1];
+ }
+ }
+
protected function lib_isnumber($value) {
return $this->toBool($value[0] == "number");
}
@@ -814,6 +933,10 @@ class lessc {
return $this->toBool($value[0] == "number" && $value[2] == "em");
}
+ protected function lib_isrem($value) {
+ return $this->toBool($value[0] == "number" && $value[2] == "rem");
+ }
+
protected function lib_rgbahex($color) {
$color = $this->coerceColor($color);
if (is_null($color))
@@ -890,6 +1013,16 @@ class lessc {
return array("number", round($value), $arg[2]);
}
+ protected function lib_unit($arg) {
+ if ($arg[0] == "list") {
+ list($number, $newUnit) = $arg[2];
+ return array("number", $this->assertNumber($number),
+ $this->compileValue($this->lib_e($newUnit)));
+ } else {
+ return array("number", $this->assertNumber($arg), "");
+ }
+ }
+
/**
* Helper function to get arguments for color manipulation functions.
* takes a list that contains a color like thing and a percentage
@@ -996,19 +1129,24 @@ class lessc {
}
// mixes two colors by weight
- // mix(@color1, @color2, @weight);
+ // mix(@color1, @color2, [@weight: 50%]);
// http://sass-lang.com/docs/yardoc/Sass/Script/Functions.html#mix-instance_method
protected function lib_mix($args) {
- if ($args[0] != "list" || count($args[2]) < 3)
+ if ($args[0] != "list" || count($args[2]) < 2)
$this->throwError("mix expects (color1, color2, weight)");
- list($first, $second, $weight) = $args[2];
+ list($first, $second) = $args[2];
$first = $this->assertColor($first);
$second = $this->assertColor($second);
$first_a = $this->lib_alpha($first);
$second_a = $this->lib_alpha($second);
- $weight = $weight[1] / 100.0;
+
+ if (isset($args[2][2])) {
+ $weight = $args[2][2][1] / 100.0;
+ } else {
+ $weight = 0.5;
+ }
$w = $weight * 2 - 1;
$a = $first_a - $second_a;
@@ -1029,6 +1167,25 @@ class lessc {
return $this->fixColor($new);
}
+ protected function lib_contrast($args) {
+ if ($args[0] != 'list' || count($args[2]) < 3) {
+ return array(array('color', 0, 0, 0), 0);
+ }
+
+ list($inputColor, $darkColor, $lightColor) = $args[2];
+
+ $inputColor = $this->assertColor($inputColor);
+ $darkColor = $this->assertColor($darkColor);
+ $lightColor = $this->assertColor($lightColor);
+ $hsl = $this->toHSL($inputColor);
+
+ if ($hsl[3] > 50) {
+ return $darkColor;
+ }
+
+ return $lightColor;
+ }
+
protected function assertColor($value, $error = "expected color value") {
$color = $this->coerceColor($value);
if (is_null($color)) $this->throwError($error);
@@ -1040,6 +1197,25 @@ class lessc {
$this->throwError($error);
}
+ protected function assertArgs($value, $expectedArgs, $name="") {
+ if ($expectedArgs == 1) {
+ return $value;
+ } else {
+ if ($value[0] !== "list" || $value[1] != ",") $this->throwError("expecting list");
+ $values = $value[2];
+ $numValues = count($values);
+ if ($expectedArgs != $numValues) {
+ if ($name) {
+ $name = $name . ": ";
+ }
+
+ $this->throwError("${name}expecting $expectedArgs arguments, got $numValues");
+ }
+
+ return $values;
+ }
+ }
+
protected function toHSL($color) {
if ($color[0] == 'hsl') return $color;
@@ -1091,7 +1267,7 @@ class lessc {
* Expects H to be in range of 0 to 360, S and L in 0 to 100
*/
protected function toRGB($color) {
- if ($color == 'color') return $color;
+ if ($color[0] == 'color') return $color;
$H = $color[1] / 360;
$S = $color[2] / 100;
@@ -1179,6 +1355,18 @@ class lessc {
protected function reduce($value, $forExpression = false) {
switch ($value[0]) {
+ case "interpolate":
+ $reduced = $this->reduce($value[1]);
+ $var = $this->compileValue($reduced);
+ $res = $this->reduce(array("variable", $this->vPrefix . $var));
+
+ if ($res[0] == "raw_color") {
+ $res = $this->coerceColor($res);
+ }
+
+ if (empty($value[2])) $res = $this->lib_e($res);
+
+ return $res;
case "variable":
$key = $value[1];
if (is_array($key)) {
@@ -1299,8 +1487,12 @@ class lessc {
case 'keyword':
$name = $value[1];
if (isset(self::$cssColors[$name])) {
- list($r, $g, $b) = explode(',', self::$cssColors[$name]);
- return array('color', $r, $g, $b);
+ $rgba = explode(',', self::$cssColors[$name]);
+
+ if(isset($rgba[3]))
+ return array('color', $rgba[0], $rgba[1], $rgba[2], $rgba[3]);
+
+ return array('color', $rgba[0], $rgba[1], $rgba[2]);
}
return null;
}
@@ -1445,6 +1637,34 @@ class lessc {
return $this->fixColor($out);
}
+ function lib_red($color){
+ $color = $this->coerceColor($color);
+ if (is_null($color)) {
+ $this->throwError('color expected for red()');
+ }
+
+ return $color[1];
+ }
+
+ function lib_green($color){
+ $color = $this->coerceColor($color);
+ if (is_null($color)) {
+ $this->throwError('color expected for green()');
+ }
+
+ return $color[2];
+ }
+
+ function lib_blue($color){
+ $color = $this->coerceColor($color);
+ if (is_null($color)) {
+ $this->throwError('color expected for blue()');
+ }
+
+ return $color[3];
+ }
+
+
// operator on two numbers
protected function op_number_number($op, $left, $right) {
$unit = empty($left[2]) ? $right[2] : $left[2];
@@ -1605,7 +1825,6 @@ class lessc {
$this->importDir = (array)$this->importDir;
$this->importDir[] = $pi['dirname'].'/';
- $this->allParsedFiles = array();
$this->addParsedFile($fname);
$out = $this->compile(file_get_contents($fname), $fname);
@@ -1945,6 +2164,7 @@ class lessc {
'teal' => '0,128,128',
'thistle' => '216,191,216',
'tomato' => '255,99,71',
+ 'transparent' => '0,0,0,0',
'turquoise' => '64,224,208',
'violet' => '238,130,238',
'wheat' => '245,222,179',
@@ -1988,7 +2208,7 @@ class lessc_parser {
static protected $supressDivisionProps =
array('/border-radius$/i', '/^font$/i');
- protected $blockDirectives = array("font-face", "keyframes", "page", "-moz-document");
+ protected $blockDirectives = array("font-face", "keyframes", "page", "-moz-document", "viewport", "-moz-viewport", "-o-viewport", "-ms-viewport");
protected $lineDirectives = array("charset");
/**
@@ -2227,7 +2447,7 @@ class lessc_parser {
// mixin
if ($this->mixinTags($tags) &&
- ($this->argumentValues($argv) || true) &&
+ ($this->argumentDef($argv, $isVararg) || true) &&
($this->keyword($suffix) || true) && $this->end())
{
$tags = $this->fixTags($tags);
@@ -2519,6 +2739,9 @@ class lessc_parser {
$out = array("mediaExp", $feature);
if ($value) $out[] = $value;
return true;
+ } elseif ($this->variable($variable)) {
+ $out = array('variable', $variable);
+ return true;
}
$this->seek($s);
@@ -2572,12 +2795,10 @@ class lessc_parser {
continue;
}
- if (in_array($tok, $rejectStrs)) {
- $count = null;
+ if (!empty($rejectStrs) && in_array($tok, $rejectStrs)) {
break;
}
-
$content[] = $tok;
$this->count+= strlen($tok);
}
@@ -2652,10 +2873,10 @@ class lessc_parser {
$s = $this->seek();
if ($this->literal("@{") &&
- $this->keyword($var) &&
+ $this->openString("}", $interp, null, array("'", '"', ";")) &&
$this->literal("}", false))
{
- $out = array("variable", $this->lessc->vPrefix . $var);
+ $out = array("interpolate", $interp);
$this->eatWhiteDefault = $oldWhite;
if ($this->eatWhiteDefault) $this->whitespace();
return true;
@@ -2694,38 +2915,18 @@ class lessc_parser {
return false;
}
- // consume a list of property values delimited by ; and wrapped in ()
- protected function argumentValues(&$args, $delim = ',') {
- $s = $this->seek();
- if (!$this->literal('(')) return false;
-
- $values = array();
- while (true) {
- if ($this->expressionList($value)) $values[] = $value;
- if (!$this->literal($delim)) break;
- else {
- if ($value == null) $values[] = null;
- $value = null;
- }
- }
-
- if (!$this->literal(')')) {
- $this->seek($s);
- return false;
- }
-
- $args = $values;
- return true;
- }
-
// consume an argument definition list surrounded by ()
// each argument is a variable name with optional value
// or at the end a ... or a variable named followed by ...
- protected function argumentDef(&$args, &$isVararg, $delim = ',') {
+ // arguments are separated by , unless a ; is in the list, then ; is the
+ // delimiter.
+ protected function argumentDef(&$args, &$isVararg) {
$s = $this->seek();
if (!$this->literal('(')) return false;
$values = array();
+ $delim = ",";
+ $method = "expressionList";
$isVararg = false;
while (true) {
@@ -2734,28 +2935,77 @@ class lessc_parser {
break;
}
- if ($this->variable($vname)) {
- $arg = array("arg", $vname);
- $ss = $this->seek();
- if ($this->assign() && $this->expressionList($value)) {
- $arg[] = $value;
- } else {
- $this->seek($ss);
- if ($this->literal("...")) {
- $arg[0] = "rest";
- $isVararg = true;
+ if ($this->$method($value)) {
+ if ($value[0] == "variable") {
+ $arg = array("arg", $value[1]);
+ $ss = $this->seek();
+
+ if ($this->assign() && $this->$method($rhs)) {
+ $arg[] = $rhs;
+ } else {
+ $this->seek($ss);
+ if ($this->literal("...")) {
+ $arg[0] = "rest";
+ $isVararg = true;
+ }
}
+
+ $values[] = $arg;
+ if ($isVararg) break;
+ continue;
+ } else {
+ $values[] = array("lit", $value);
}
- $values[] = $arg;
- if ($isVararg) break;
- continue;
}
- if ($this->value($literal)) {
- $values[] = array("lit", $literal);
- }
- if (!$this->literal($delim)) break;
+ if (!$this->literal($delim)) {
+ if ($delim == "," && $this->literal(";")) {
+ // found new delim, convert existing args
+ $delim = ";";
+ $method = "propertyValue";
+
+ // transform arg list
+ if (isset($values[1])) { // 2 items
+ $newList = array();
+ foreach ($values as $i => $arg) {
+ switch($arg[0]) {
+ case "arg":
+ if ($i) {
+ $this->throwError("Cannot mix ; and , as delimiter types");
+ }
+ $newList[] = $arg[2];
+ break;
+ case "lit":
+ $newList[] = $arg[1];
+ break;
+ case "rest":
+ $this->throwError("Unexpected rest before semicolon");
+ }
+ }
+
+ $newList = array("list", ", ", $newList);
+
+ switch ($values[0][0]) {
+ case "arg":
+ $newArg = array("arg", $values[0][1], $newList);
+ break;
+ case "lit":
+ $newArg = array("lit", $newList);
+ break;
+ }
+
+ } elseif ($values) { // 1 item
+ $newArg = $values[0];
+ }
+
+ if ($newArg) {
+ $values = array($newArg);
+ }
+ } else {
+ break;
+ }
+ }
}
if (!$this->literal(')')) {
@@ -2797,70 +3047,136 @@ class lessc_parser {
}
// a bracketed value (contained within in a tag definition)
- protected function tagBracket(&$value) {
+ protected function tagBracket(&$parts, &$hasExpression) {
// speed shortcut
if (isset($this->buffer[$this->count]) && $this->buffer[$this->count] != "[") {
return false;
}
$s = $this->seek();
- if ($this->literal('[') && $this->to(']', $c, true) && $this->literal(']', false)) {
- $value = '['.$c.']';
- // whitespace?
- if ($this->whitespace()) $value .= " ";
- // escape parent selector, (yuck)
- $value = str_replace($this->lessc->parentSelector, "$&$", $value);
- return true;
- }
+ $hasInterpolation = false;
- $this->seek($s);
- return false;
- }
+ if ($this->literal("[", false)) {
+ $attrParts = array("[");
+ // keyword, string, operator
+ while (true) {
+ if ($this->literal("]", false)) {
+ $this->count--;
+ break; // get out early
+ }
- protected function tagExpression(&$value) {
- $s = $this->seek();
- if ($this->literal("(") && $this->expression($exp) && $this->literal(")")) {
- $value = array('exp', $exp);
- return true;
+ if ($this->match('\s+', $m)) {
+ $attrParts[] = " ";
+ continue;
+ }
+ if ($this->string($str)) {
+ // escape parent selector, (yuck)
+ foreach ($str[2] as &$chunk) {
+ $chunk = str_replace($this->lessc->parentSelector, "$&$", $chunk);
+ }
+
+ $attrParts[] = $str;
+ $hasInterpolation = true;
+ continue;
+ }
+
+ if ($this->keyword($word)) {
+ $attrParts[] = $word;
+ continue;
+ }
+
+ if ($this->interpolation($inter, false)) {
+ $attrParts[] = $inter;
+ $hasInterpolation = true;
+ continue;
+ }
+
+ // operator, handles attr namespace too
+ if ($this->match('[|-~\$\*\^=]+', $m)) {
+ $attrParts[] = $m[0];
+ continue;
+ }
+
+ break;
+ }
+
+ if ($this->literal("]", false)) {
+ $attrParts[] = "]";
+ foreach ($attrParts as $part) {
+ $parts[] = $part;
+ }
+ $hasExpression = $hasExpression || $hasInterpolation;
+ return true;
+ }
+ $this->seek($s);
}
$this->seek($s);
return false;
}
- // a single tag
+ // a space separated list of selectors
protected function tag(&$tag, $simple = false) {
if ($simple)
- $chars = '^,:;{}\][>\(\) "\'';
+ $chars = '^@,:;{}\][>\(\) "\'';
else
- $chars = '^,;{}["\'';
+ $chars = '^@,;{}["\'';
- if (!$simple && $this->tagExpression($tag)) {
- return true;
- }
+ $s = $this->seek();
- $tag = '';
- while ($this->tagBracket($first)) $tag .= $first;
+ $hasExpression = false;
+ $parts = array();
+ while ($this->tagBracket($parts, $hasExpression));
+
+ $oldWhite = $this->eatWhiteDefault;
+ $this->eatWhiteDefault = false;
while (true) {
if ($this->match('(['.$chars.'0-9]['.$chars.']*)', $m)) {
- $tag .= $m[1];
+ $parts[] = $m[1];
if ($simple) break;
- while ($this->tagBracket($brack)) $tag .= $brack;
+ while ($this->tagBracket($parts, $hasExpression));
continue;
- } elseif ($this->unit($unit)) { // for keyframes
- $tag .= $unit[1] . $unit[2];
+ }
+
+ if (isset($this->buffer[$this->count]) && $this->buffer[$this->count] == "@") {
+ if ($this->interpolation($interp)) {
+ $hasExpression = true;
+ $interp[2] = true; // don't unescape
+ $parts[] = $interp;
+ continue;
+ }
+
+ if ($this->literal("@")) {
+ $parts[] = "@";
+ continue;
+ }
+ }
+
+ if ($this->unit($unit)) { // for keyframes
+ $parts[] = $unit[1];
+ $parts[] = $unit[2];
continue;
}
+
break;
}
+ $this->eatWhiteDefault = $oldWhite;
+ if (!$parts) {
+ $this->seek($s);
+ return false;
+ }
- $tag = trim($tag);
- if ($tag == '') return false;
+ if ($hasExpression) {
+ $tag = array("exp", array("string", "", $parts));
+ } else {
+ $tag = trim(implode($parts));
+ }
+ $this->whitespace();
return true;
}
@@ -2948,7 +3264,7 @@ class lessc_parser {
protected function end() {
if ($this->literal(';')) {
return true;
- } elseif ($this->count == strlen($this->buffer) || $this->buffer{$this->count} == '}') {
+ } elseif ($this->count == strlen($this->buffer) || $this->buffer[$this->count] == '}') {
// if there is end of file or a closing block next then we don't need a ;
return true;
}
@@ -3212,7 +3528,7 @@ class lessc_parser {
break;
case '"':
case "'":
- if (preg_match('/'.$min[0].'.*?'.$min[0].'/', $text, $m, 0, $count))
+ if (preg_match('/'.$min[0].'.*?(?<!\\\\)'.$min[0].'/', $text, $m, 0, $count))
$count += strlen($m[0]) - 1;
break;
case '//':
@@ -3356,4 +3672,3 @@ class lessc_formatter_lessjs extends lessc_formatter_classic {
public $selectorSeparator = ",";
}
-
diff --git a/plugins/jetpack/modules/custom-css/custom-css/preprocessors/scss.inc.php b/plugins/jetpack/modules/custom-css/custom-css/preprocessors/scss.inc.php
index 5ad188cc..ff0493a7 100644
--- a/plugins/jetpack/modules/custom-css/custom-css/preprocessors/scss.inc.php
+++ b/plugins/jetpack/modules/custom-css/custom-css/preprocessors/scss.inc.php
@@ -1,7 +1,49 @@
<?php
-
+/**
+ * SCSS compiler written in PHP
+ *
+ * @copyright 2012-2013 Leaf Corcoran
+ *
+ * @license http://opensource.org/licenses/gpl-license GPL-3.0
+ * @license http://opensource.org/licenses/MIT MIT
+ *
+ * @link http://leafo.net/scssphp
+ */
+
+/**
+ * The scss compiler and parser.
+ *
+ * Converting SCSS to CSS is a three stage process. The incoming file is parsed
+ * by `scssc_parser` into a syntax tree, then it is compiled into another tree
+ * representing the CSS structure by `scssc`. The CSS tree is fed into a
+ * formatter, like `scssc_formatter` which then outputs CSS as a string.
+ *
+ * During the first compile, all values are *reduced*, which means that their
+ * types are brought to the lowest form before being dump as strings. This
+ * handles math equations, variable dereferences, and the like.
+ *
+ * The `parse` function of `scssc` is the entry point.
+ *
+ * In summary:
+ *
+ * The `scssc` class creates an instance of the parser, feeds it SCSS code,
+ * then transforms the resulting tree to a CSS tree. This class also holds the
+ * evaluation context, such as all available mixins and variables at any given
+ * time.
+ *
+ * The `scssc_parser` class is only concerned with parsing its input.
+ *
+ * The `scssc_formatter` takes a CSS tree, and dumps it to a formatted string,
+ * handling things like indentation.
+ */
+
+/**
+ * SCSS compiler
+ *
+ * @author Leaf Corcoran <leafot@gmail.com>
+ */
class scssc {
- static public $VERSION = "v0.0.4";
+ static public $VERSION = "v0.0.7";
static protected $operatorNames = array(
'+' => "add",
@@ -25,7 +67,7 @@ class scssc {
"function" => "^",
);
- static protected $numberPrecision = 3;
+ static protected $numberPrecision = 5;
static protected $unitTable = array(
"in" => array(
"in" => 1,
@@ -39,6 +81,7 @@ class scssc {
static public $true = array("keyword", "true");
static public $false = array("keyword", "false");
+ static public $null = array("null");
static public $defaultValue = array("keyword", "");
static public $selfSelector = array("self");
@@ -69,17 +112,28 @@ class scssc {
$this->scope = null;
$this->compileRoot($tree);
- $this->flattenSelectors($this->scope);
- ob_start();
- $this->formatter->block($this->scope);
- $out = ob_get_clean();
+ $out = $this->formatter->format($this->scope);
setlocale(LC_NUMERIC, $locale);
return $out;
}
+ protected function isSelfExtend($target, $origin) {
+ foreach ($origin as $sel) {
+ if (in_array($target, $sel)) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
protected function pushExtends($target, $origin) {
+ if ($this->isSelfExtend($target, $origin)) {
+ return;
+ }
+
$i = count($this->extends);
$this->extends[] = array($target, $origin);
@@ -93,7 +147,7 @@ class scssc {
}
protected function makeOutputBlock($type, $selectors = null) {
- $out = new stdclass;
+ $out = new stdClass;
$out->type = $type;
$out->lines = array();
$out->children = array();
@@ -104,7 +158,7 @@ class scssc {
return $out;
}
- protected function matchExtendsSingle($single, &$out_origin, &$out_rem) {
+ protected function matchExtendsSingle($single, &$outOrigin) {
$counts = array();
foreach ($single as $part) {
if (!is_string($part)) return false; // hmm
@@ -117,20 +171,30 @@ class scssc {
}
}
+ $outOrigin = array();
+ $found = false;
+
foreach ($counts as $idx => $count) {
list($target, $origin) = $this->extends[$idx];
+
// check count
if ($count != count($target)) continue;
+
// check if target is subset of single
if (array_diff(array_intersect($single, $target), $target)) continue;
- $out_origin = $origin;
- $out_rem = array_diff($single, $target);
+ $rem = array_diff($single, $target);
- return true;
+ foreach ($origin as $j => $new) {
+ $origin[$j][count($origin[$j]) - 1] = $this->combineSelectorSingle(end($new), $rem);
+ }
+
+ $outOrigin = array_merge($outOrigin, $origin);
+
+ $found = true;
}
- return false;
+ return $found;
}
protected function combineSelectorSingle($base, $other) {
@@ -139,7 +203,7 @@ class scssc {
foreach (array($base, $other) as $single) {
foreach ($single as $part) {
- if (preg_match('/^[^.#:]/', $part)) {
+ if (preg_match('/^[^\[.#:]/', $part)) {
$tag = $part;
} else {
$out[] = $part;
@@ -158,15 +222,13 @@ class scssc {
foreach ($selector as $i => $part) {
if ($i < $from) continue;
- if ($this->matchExtendsSingle($part, $origin, $rem)) {
+ if ($this->matchExtendsSingle($part, $origin)) {
$before = array_slice($selector, 0, $i);
$after = array_slice($selector, $i + 1);
foreach ($origin as $new) {
- $new[count($new) - 1] =
- $this->combineSelectorSingle(end($new), $rem);
-
$k = 0;
+
// remove shared parts
if ($initial) {
foreach ($before as $k => $val) {
@@ -203,7 +265,7 @@ class scssc {
}
}
- protected function flattenSelectors($block) {
+ protected function flattenSelectors($block, $parentKey = null) {
if ($block->selectors) {
$selectors = array();
foreach ($block->selectors as $s) {
@@ -215,19 +277,34 @@ class scssc {
}
}
- $selectors = array_map(array($this, "compileSelector"), $selectors);
- $block->selectors = $selectors;
+ $block->selectors = array();
+ $placeholderSelector = false;
+ foreach ($selectors as $selector) {
+ if ($this->hasSelectorPlaceholder($selector)) {
+ $placeholderSelector = true;
+ continue;
+ }
+ $block->selectors[] = $this->compileSelector($selector);
+ }
+
+ if ($placeholderSelector && 0 == count($block->selectors) && null !== $parentKey) {
+ unset($block->parent->children[$parentKey]);
+ return;
+ }
}
- foreach ($block->children as $child) {
- $this->flattenSelectors($child);
+ foreach ($block->children as $key => $child) {
+ $this->flattenSelectors($child, $key);
}
}
protected function compileRoot($rootBlock) {
$this->pushEnv($rootBlock);
$this->scope = $this->makeOutputBlock("root");
+
$this->compileChildren($rootBlock->children, $this->scope);
+ $this->flattenSelectors($this->scope);
+
$this->popEnv();
}
@@ -241,6 +318,24 @@ class scssc {
$parentScope->children[] = $this->scope;
+ // top level properties in a media cause it to be wrapped
+ $needsWrap = false;
+ foreach ($media->children as $child) {
+ $type = $child[0];
+ if ($type !== 'block' && $type !== 'media' && $type !== 'directive') {
+ $needsWrap = true;
+ break;
+ }
+ }
+
+ if ($needsWrap) {
+ $wrapped = (object)array(
+ "selectors" => array(),
+ "children" => $media->children
+ );
+ $media->children = array(array("block", $wrapped));
+ }
+
$this->compileChildren($media->children, $this->scope);
$this->scope = $this->scope->parent;
@@ -270,6 +365,24 @@ class scssc {
$this->popEnv();
}
+ /**
+ * Recursively compiles a block.
+ *
+ * A block is analogous to a CSS block in most cases. A single SCSS document
+ * is encapsulated in a block when parsed, but it does not have parent tags
+ * so all of its children appear on the root level when compiled.
+ *
+ * Blocks are made up of selectors and children.
+ *
+ * The children of a block are just all the blocks that are defined within.
+ *
+ * Compiling the block involves pushing a fresh environment on the stack,
+ * and iterating through the props, compiling each one.
+ *
+ * @see scss::compileChild()
+ *
+ * @param \StdClass $block
+ */
protected function compileBlock($block) {
$env = $this->pushEnv($block);
@@ -289,7 +402,7 @@ class scssc {
foreach ($single as $part) {
if (empty($joined) ||
!is_string($part) ||
- preg_match('/[.:#]/', $part))
+ preg_match('/[\[.:#%]/', $part))
{
$joined[] = $part;
continue;
@@ -318,6 +431,9 @@ class scssc {
case "interpolate":
$p = $this->compileValue($p);
break;
+ case "string":
+ $p = $this->compileValue($p);
+ break;
}
}
@@ -350,6 +466,21 @@ class scssc {
return implode($piece);
}
+ protected function hasSelectorPlaceholder($selector)
+ {
+ if (!is_array($selector)) return false;
+
+ foreach ($selector as $parts) {
+ foreach ($parts as $part) {
+ if ('%' == $part[0]) {
+ return true;
+ }
+ }
+ }
+
+ return false;
+ }
+
protected function compileChildren($stms, $out) {
foreach ($stms as $stm) {
$ret = $this->compileChild($stm, $out);
@@ -365,13 +496,13 @@ class scssc {
foreach ($query as $q) {
switch ($q[0]) {
case "mediaType":
- $parts[] = implode(" ", array_slice($q, 1));
+ $parts[] = implode(" ", array_map(array($this, "compileValue"), array_slice($q, 1)));
break;
case "mediaExp":
if (isset($q[2])) {
- $parts[] = "($q[1]" . $this->formatter->assignSeparator . $this->compileValue($q[2]) . ")";
+ $parts[] = "(". $this->compileValue($q[1]) . $this->formatter->assignSeparator . $this->compileValue($q[2]) . ")";
} else {
- $parts[] = "($q[1])";
+ $parts[] = "(" . $this->compileValue($q[1]) . ")";
}
break;
}
@@ -398,7 +529,8 @@ class scssc {
return true;
}
return false;
- } if ($rawPath[0] == "list") {
+ }
+ if ($rawPath[0] == "list") {
// handle a list of strings
if (count($rawPath[2]) == 0) return false;
foreach ($rawPath[2] as $path) {
@@ -417,6 +549,9 @@ class scssc {
// return a value to halt execution
protected function compileChild($child, $out) {
+ $this->sourcePos = isset($child[-1]) ? $child[-1] : -1;
+ $this->sourceParser = isset($child[-2]) ? $child[-2] : $this->parser;
+
switch ($child[0]) {
case "import":
list(,$rawPath) = $child;
@@ -446,15 +581,31 @@ class scssc {
list(,$name, $value) = $child;
if ($name[0] == "var") {
$isDefault = !empty($child[3]);
- if (!$isDefault || $this->get($name[1], true) === true) {
+
+ if ($isDefault) {
+ $existingValue = $this->get($name[1], true);
+ $shouldSet = $existingValue === true || $existingValue == self::$null;
+ }
+
+ if (!$isDefault || $shouldSet) {
$this->set($name[1], $this->reduce($value));
}
break;
}
+ // if the value reduces to null from something else then
+ // the property should be discarded
+ if ($value[0] != "null") {
+ $value = $this->reduce($value);
+ if ($value[0] == "null") {
+ break;
+ }
+ }
+
+ $compiledValue = $this->compileValue($value);
$out->lines[] = $this->formatter->property(
- $this->compileValue($child[1]),
- $this->compileValue($child[2]));
+ $this->compileValue($name),
+ $compiledValue);
break;
case "comment":
$out->lines[] = $child[1];
@@ -474,12 +625,12 @@ class scssc {
break;
case "if":
list(, $if) = $child;
- if ($this->reduce($if->cond, true) != self::$false) {
+ if ($this->isTruthy($this->reduce($if->cond, true))) {
return $this->compileChildren($if->children, $out);
} else {
foreach ($if->cases as $case) {
if ($case->type == "else" ||
- $case->type == "elseif" && ($this->reduce($case->cond) != self::$false))
+ $case->type == "elseif" && $this->isTruthy($this->reduce($case->cond)))
{
return $this->compileChildren($case->children, $out);
}
@@ -490,7 +641,7 @@ class scssc {
return $this->reduce($child[1], true);
case "each":
list(,$each) = $child;
- $list = $this->reduce($this->coerceList($each->list));
+ $list = $this->coerceList($this->reduce($each->list));
foreach ($list[2] as $item) {
$this->pushEnv();
$this->set($each->var, $item);
@@ -501,7 +652,7 @@ class scssc {
break;
case "while":
list(,$while) = $child;
- while ($this->reduce($while->cond, true) != self::$false) {
+ while ($this->isTruthy($this->reduce($while->cond, true))) {
$ret = $this->compileChildren($while->children, $out);
if ($ret) return $ret;
}
@@ -547,12 +698,17 @@ class scssc {
case "include": // including a mixin
list(,$name, $argValues, $content) = $child;
$mixin = $this->get(self::$namespaces["mixin"] . $name, false);
- if (!$mixin) break; // throw error?
+ if (!$mixin) {
+ $this->throwError("Undefined mixin $name");
+ }
$callingScope = $this->env;
// push scope, apply args
$this->pushEnv();
+ if ($this->env->depth > 0) {
+ $this->env->depth--;
+ }
if (!is_null($content)) {
$content->scope = $callingScope;
@@ -573,12 +729,14 @@ class scssc {
case "mixin_content":
$content = $this->get(self::$namespaces["special"] . "content");
if (is_null($content)) {
- throw new \Exception("Unexpected @content inside of mixin");
+ $this->throwError("Expected @content inside of mixin");
}
- $this->storeEnv = $content->scope;
-
foreach ($content->children as $child) {
+ $this->storeEnv = ($child[0] == 'include' || $child[0] == 'block')
+ ? null
+ : $content->scope;
+
$this->compileChild($child, $out);
}
@@ -591,20 +749,24 @@ class scssc {
fwrite(STDERR, "Line $line DEBUG: $value\n");
break;
default:
- throw new exception("unknown child type: $child[0]");
+ $this->throwError("unknown child type: $child[0]");
}
}
protected function expToString($exp) {
list(, $op, $left, $right, $inParens, $whiteLeft, $whiteRight) = $exp;
- $content = array($left);
+ $content = array($this->reduce($left));
if ($whiteLeft) $content[] = " ";
$content[] = $op;
if ($whiteRight) $content[] = " ";
- $content[] = $right;
+ $content[] = $this->reduce($right);
return array("string", "", $content);
}
+ protected function isTruthy($value) {
+ return $value != self::$false && $value != self::$null;
+ }
+
// should $value cause its operand to eval
protected function shouldEval($value) {
switch ($value[0]) {
@@ -662,7 +824,7 @@ class scssc {
$left[0] == "number" && $right[0] == "number")
{
if ($opName == "mod" && $right[2] != "") {
- throw new \Exception(sprintf('Cannot modulo by a number with units: %s%s.', $right[1], $right[2]));
+ $this->throwError("Cannot modulo by a number with units: $right[1]$right[2].");
}
$unitChange = true;
@@ -796,6 +958,27 @@ class scssc {
}
}
+ public function normalizeValue($value) {
+ $value = $this->coerceForExpression($this->reduce($value));
+ list($type) = $value;
+
+ switch ($type) {
+ case "list":
+ $value = $this->extractInterpolation($value);
+ if ($value[0] != "list") {
+ return array("keyword", $this->compileValue($value));
+ }
+ foreach ($value[2] as $key => $item) {
+ $value[2][$key] = $this->normalizeValue($item);
+ }
+ return $value;
+ case "number":
+ return $this->normalizeNumber($value);
+ default:
+ return $value;
+ }
+ }
+
// just does physical lengths for now
protected function normalizeNumber($number) {
list(, $value, $unit) = $number;
@@ -887,12 +1070,16 @@ class scssc {
break;
case '/':
if ($rval == 0) {
- throw new exception("color: Can't divide by zero");
+ $this->throwError("color: Can't divide by zero");
}
$out[] = $lval / $rval;
break;
+ case "==":
+ return $this->op_eq($left, $right);
+ case "!=":
+ return $this->op_neq($left, $right);
default:
- throw new exception("color: unknow op $op");
+ $this->throwError("color: unknown op $op");
}
}
@@ -948,6 +1135,19 @@ class scssc {
return $thing ? self::$true : self::$false;
}
+ /**
+ * Compiles a primitive value into a CSS property value.
+ *
+ * Values in scssphp are typed by being wrapped in arrays, their format is
+ * typically:
+ *
+ * array(type, contents [, additional_contents]*)
+ *
+ * The input is expected to be reduced. This function will not work on
+ * things like expressions and variables.
+ *
+ * @param array $value
+ */
protected function compileValue($value) {
$value = $this->reduce($value);
@@ -990,10 +1190,14 @@ class scssc {
if ($value[0] != "list") return $this->compileValue($value);
list(, $delim, $items) = $value;
- foreach ($items as &$item) {
- $item = $this->compileValue($item);
+
+ $filtered = array();
+ foreach ($items as $item) {
+ if ($item[0] == "null") continue;
+ $filtered[] = $this->compileValue($item);
}
- return implode("$delim ", $items);
+
+ return implode("$delim ", $filtered);
case "interpolated": # node created by extractInterpolation
list(, $interpolate, $left, $right) = $value;
list(,, $whiteLeft, $whiteRight) = $interpolate;
@@ -1011,14 +1215,20 @@ class scssc {
// strip quotes if it's a string
$reduced = $this->reduce($exp);
- if ($reduced[0] == "string") {
- $reduced = array("keyword",
- $this->compileStringContent($reduced));
+ switch ($reduced[0]) {
+ case "string":
+ $reduced = array("keyword",
+ $this->compileStringContent($reduced));
+ break;
+ case "null":
+ $reduced = array("keyword", "");
}
return $this->compileValue($reduced);
+ case "null":
+ return "null";
default:
- throw new exception("unknown value type: $type");
+ $this->throwError("unknown value type: $type");
}
}
@@ -1049,28 +1259,28 @@ class scssc {
}
// find the final set of selectors
- protected function multiplySelectors($env, $childSelectors = null) {
- if (is_null($env)) {
- return $childSelectors;
- }
-
- // skip env, has no selectors
- if (empty($env->selectors)) {
- return $this->multiplySelectors($env->parent, $childSelectors);
- }
+ protected function multiplySelectors($env) {
+ $envs = array();
+ while (null !== $env) {
+ if (!empty($env->selectors)) {
+ $envs[] = $env;
+ }
+ $env = $env->parent;
+ };
- if (is_null($childSelectors)) {
- $selectors = $env->selectors;
- } else {
+ $selectors = array();
+ $parentSelectors = array(array());
+ while ($env = array_pop($envs)) {
$selectors = array();
- foreach ($env->selectors as $parent) {
- foreach ($childSelectors as $child) {
- $selectors[] = $this->joinSelectors($parent, $child);
+ foreach ($env->selectors as $selector) {
+ foreach ($parentSelectors as $parent) {
+ $selectors[] = $this->joinSelectors($parent, $selector);
}
}
+ $parentSelectors = $selectors;
}
- return $this->multiplySelectors($env->parent, $selectors);
+ return $selectors;
}
// looks for & to replace, or append parent before child
@@ -1142,31 +1352,69 @@ class scssc {
}
protected function applyArguments($argDef, $argValues) {
- $argValues = (array)$argValues;
+ $hasVariable = false;
+ $args = array();
+ foreach ($argDef as $i => $arg) {
+ list($name, $default, $isVariable) = $argDef[$i];
+ $args[$name] = array($i, $name, $default, $isVariable);
+ $hasVariable |= $isVariable;
+ }
$keywordArgs = array();
+ $deferredKeywordArgs = array();
$remaining = array();
-
// assign the keyword args
- foreach ($argValues as $arg) {
+ foreach ((array) $argValues as $arg) {
if (!empty($arg[0])) {
- $keywordArgs[$arg[0][1]] = $arg[1];
+ if (!isset($args[$arg[0][1]])) {
+ if ($hasVariable) {
+ $deferredKeywordArgs[$arg[0][1]] = $arg[1];
+ } else {
+ $this->throwError("Mixin or function doesn't have an argument named $%s.", $arg[0][1]);
+ }
+ } elseif ($args[$arg[0][1]][0] < count($remaining)) {
+ $this->throwError("The argument $%s was passed both by position and by name.", $arg[0][1]);
+ } else {
+ $keywordArgs[$arg[0][1]] = $arg[1];
+ }
+ } elseif (count($keywordArgs)) {
+ $this->throwError('Positional arguments must come before keyword arguments.');
+ } elseif ($arg[2] == true) {
+ $val = $this->reduce($arg[1], true);
+ if ($val[0] == "list") {
+ foreach ($val[2] as $name => $item) {
+ if (!is_numeric($name)) {
+ $keywordArgs[$name] = $item;
+ } else {
+ $remaining[] = $item;
+ }
+ }
+ } else {
+ $remaining[] = $val;
+ }
} else {
$remaining[] = $arg[1];
}
}
- foreach ($argDef as $i => $arg) {
- list($name, $default) = $arg;
-
- if (isset($remaining[$i])) {
+ foreach ($args as $arg) {
+ list($i, $name, $default, $isVariable) = $arg;
+ if ($isVariable) {
+ $val = array("list", ",", array());
+ for ($count = count($remaining); $i < $count; $i++) {
+ $val[2][] = $remaining[$i];
+ }
+ foreach ($deferredKeywordArgs as $itemName => $item) {
+ $val[2][$itemName] = $item;
+ }
+ } elseif (isset($remaining[$i])) {
$val = $remaining[$i];
} elseif (isset($keywordArgs[$name])) {
$val = $keywordArgs[$name];
} elseif (!empty($default)) {
$val = $default;
} else {
- $val = self::$defaultValue;
+ $this->throwError("Missing argument $$name");
}
$this->set($name, $this->reduce($val, true), true);
@@ -1174,7 +1422,7 @@ class scssc {
}
protected function pushEnv($block=null) {
- $env = new stdclass;
+ $env = new stdClass;
$env->parent = $this->env;
$env->store = array();
$env->block = $block;
@@ -1194,6 +1442,7 @@ class scssc {
protected function set($name, $value, $shadow=false) {
$name = $this->normalizeName($name);
+
if ($shadow) {
$this->setRaw($name, $value);
} else {
@@ -1226,7 +1475,7 @@ class scssc {
if (isset($env->store[$name])) {
return $env->store[$name];
- } elseif (!is_null($env->parent)) {
+ } elseif (isset($env->parent)) {
return $this->get($name, $defaultValue, $env->parent);
}
@@ -1270,7 +1519,7 @@ class scssc {
$tree = $this->importCache[$realPath];
} else {
$code = file_get_contents($path);
- $parser = new scss_parser($path);
+ $parser = new scss_parser($path, false);
$tree = $parser->parse($code);
$this->parsedFiles[] = $path;
@@ -1336,7 +1585,7 @@ class scssc {
$val = $this->reduce($val, true);
}
$returnValue = call_user_func($f, $sorted, $this);
- } else if (isset($this->userFunctions[$name])) {
+ } elseif (isset($this->userFunctions[$name])) {
// see if we can find a user function
$fn = $this->userFunctions[$name];
@@ -1420,7 +1669,7 @@ class scssc {
$name = $value[1];
if (isset(self::$cssColors[$name])) {
list($r, $g, $b) = explode(',', self::$cssColors[$name]);
- return array('color', $r, $g, $b);
+ return array('color', (int) $r, (int) $g, (int) $b);
}
return null;
}
@@ -1438,14 +1687,20 @@ class scssc {
return null;
}
+ protected function assertList($value) {
+ if ($value[0] != "list")
+ $this->throwError("expecting list");
+ return $value;
+ }
+
protected function assertColor($value) {
if ($color = $this->coerceColor($value)) return $color;
- throw new exception("expecting color");
+ $this->throwError("expecting color");
}
protected function assertNumber($value) {
if ($value[0] != "number")
- throw new exception("expecting number");
+ $this->throwError("expecting number");
return $value[1];
}
@@ -1469,74 +1724,71 @@ class scssc {
return $c;
}
- function toHSL($r, $g, $b) {
- $r = $r / 255;
- $g = $g / 255;
- $b = $b / 255;
+ function toHSL($red, $green, $blue) {
+ $r = $red / 255;
+ $g = $green / 255;
+ $b = $blue / 255;
$min = min($r, $g, $b);
$max = max($r, $g, $b);
+ $d = $max - $min;
+ $l = ($min + $max) / 2;
- $L = ($min + $max) / 2;
if ($min == $max) {
- $S = $H = 0;
+ $s = $h = 0;
} else {
- if ($L < 0.5)
- $S = ($max - $min)/($max + $min);
+ if ($l < 0.5)
+ $s = $d / (2 * $l);
else
- $S = ($max - $min)/(2.0 - $max - $min);
-
- if ($r == $max) $H = ($g - $b)/($max - $min);
- elseif ($g == $max) $H = 2.0 + ($b - $r)/($max - $min);
- elseif ($b == $max) $H = 4.0 + ($r - $g)/($max - $min);
+ $s = $d / (2 - 2 * $l);
+ if ($r == $max)
+ $h = 60 * ($g - $b) / $d;
+ elseif ($g == $max)
+ $h = 60 * ($b - $r) / $d + 120;
+ elseif ($b == $max)
+ $h = 60 * ($r - $g) / $d + 240;
}
- return array('hsl',
- ($H < 0 ? $H + 6 : $H)*60,
- $S*100,
- $L*100,
- );
+ return array('hsl', fmod($h, 360), $s * 100, $l * 100);
}
- function toRGB_helper($comp, $temp1, $temp2) {
- if ($comp < 0) $comp += 1.0;
- elseif ($comp > 1) $comp -= 1.0;
+ function hueToRGB($m1, $m2, $h) {
+ if ($h < 0)
+ $h += 1;
+ elseif ($h > 1)
+ $h -= 1;
- if (6 * $comp < 1) return $temp1 + ($temp2 - $temp1) * 6 * $comp;
- if (2 * $comp < 1) return $temp2;
- if (3 * $comp < 2) return $temp1 + ($temp2 - $temp1)*((2/3) - $comp) * 6;
+ if ($h * 6 < 1)
+ return $m1 + ($m2 - $m1) * $h * 6;
- return $temp1;
- }
+ if ($h * 2 < 1)
+ return $m2;
- // H from 0 to 360, S and L from 0 to 100
- function toRGB($H, $S, $L) {
- $H = $H % 360;
- if ($H < 0) $H += 360;
+ if ($h * 3 < 2)
+ return $m1 + ($m2 - $m1) * (2/3 - $h) * 6;
- $S = min(100, max(0, $S));
- $L = min(100, max(0, $L));
+ return $m1;
+ }
- $H = $H / 360;
- $S = $S / 100;
- $L = $L / 100;
+ // H from 0 to 360, S and L from 0 to 100
+ function toRGB($hue, $saturation, $lightness) {
+ if ($hue < 0) {
+ $hue += 360;
+ }
- if ($S == 0) {
- $r = $g = $b = $L;
- } else {
- $temp2 = $L < 0.5 ?
- $L*(1.0 + $S) :
- $L + $S - $L * $S;
+ $h = $hue / 360;
+ $s = min(100, max(0, $saturation)) / 100;
+ $l = min(100, max(0, $lightness)) / 100;
- $temp1 = 2.0 * $L - $temp2;
+ $m2 = $l <= 0.5 ? $l * ($s + 1) : $l + $s - $l * $s;
+ $m1 = $l * 2 - $m2;
- $r = $this->toRGB_helper($H + 1/3, $temp1, $temp2);
- $g = $this->toRGB_helper($H, $temp1, $temp2);
- $b = $this->toRGB_helper($H - 1/3, $temp1, $temp2);
- }
+ $r = $this->hueToRGB($m1, $m2, $h + 1/3) * 255;
+ $g = $this->hueToRGB($m1, $m2, $h) * 255;
+ $b = $this->hueToRGB($m1, $m2, $h - 1/3) * 255;
- $out = array('color', $r*255, $g*255, $b*255);
+ $out = array('color', $r, $g, $b);
return $out;
}
@@ -1549,6 +1801,20 @@ class scssc {
return $t;
}
+ protected static $lib_index = array("list", "value");
+ protected function lib_index($args) {
+ list($list, $value) = $args;
+ $list = $this->assertList($list);
+
+ $values = array();
+ foreach ($list[2] as $item) {
+ $values[] = $this->normalizeValue($item);
+ }
+ $key = array_search($this->normalizeValue($value), $values);
+
+ return false === $key ? false : $key + 1;
+ }
+
protected static $lib_rgb = array("red", "green", "blue");
protected function lib_rgb($args) {
list($r,$g,$b) = $args;
@@ -1687,11 +1953,13 @@ class scssc {
}
// this might be the IE function, so return value unchanged
- return array("function", "alpha", array("list", ",", $args));
+ return null;
}
protected static $lib_opacity = array("color");
protected function lib_opacity($args) {
+ $value = $args[0];
+ if ($value[0] === 'number') return null;
return $this->lib_alpha($args);
}
@@ -1708,11 +1976,11 @@ class scssc {
$weight = $this->coercePercent($weight);
}
- $first_a = isset($first[4]) ? $first[4] : 1;
- $second_a = isset($second[4]) ? $second[4] : 1;
+ $firstAlpha = isset($first[4]) ? $first[4] : 1;
+ $secondAlpha = isset($second[4]) ? $second[4] : 1;
$w = $weight * 2 - 1;
- $a = $first_a - $second_a;
+ $a = $firstAlpha - $secondAlpha;
$w1 = (($w * $a == -1 ? $w : ($w + $a)/(1 + $w * $a)) + 1) / 2.0;
$w2 = 1.0 - $w1;
@@ -1723,8 +1991,8 @@ class scssc {
$w1 * $first[3] + $w2 * $second[3],
);
- if ($first_a != 1.0 || $second_a != 1.0) {
- $new[] = $first_a * $weight + $second_a * ($weight - 1);
+ if ($firstAlpha != 1.0 || $secondAlpha != 1.0) {
+ $new[] = $firstAlpha * $weight + $secondAlpha * ($weight - 1);
}
return $this->fixColor($new);
@@ -1766,7 +2034,6 @@ class scssc {
return array("number", $hsl[3], "%");
}
-
protected function adjustHsl($color, $idx, $amount) {
$hsl = $this->toHSL($color[1], $color[2], $color[3]);
$hsl[$idx] += $amount;
@@ -1798,7 +2065,9 @@ class scssc {
protected static $lib_saturate = array("color", "amount");
protected function lib_saturate($args) {
- $color = $this->assertColor($args[0]);
+ $value = $args[0];
+ if ($value[0] === 'number') return null;
+ $color = $this->assertColor($value);
$amount = 100*$this->coercePercent($args[1]);
return $this->adjustHsl($color, 2, $amount);
}
@@ -1812,7 +2081,9 @@ class scssc {
protected static $lib_grayscale = array("color");
protected function lib_grayscale($args) {
- return $this->adjustHsl($this->assertColor($args[0]), 2, -100);
+ $value = $args[0];
+ if ($value[0] === 'number') return null;
+ return $this->adjustHsl($this->assertColor($value), 2, -100);
}
protected static $lib_complement = array("color");
@@ -1822,14 +2093,15 @@ class scssc {
protected static $lib_invert = array("color");
protected function lib_invert($args) {
- $color = $this->assertColor($args[0]);
+ $value = $args[0];
+ if ($value[0] === 'number') return null;
+ $color = $this->assertColor($value);
$color[1] = 255 - $color[1];
$color[2] = 255 - $color[2];
$color[3] = 255 - $color[3];
return $color;
}
-
// increases opacity by amount
protected static $lib_opacify = array("color", "amount");
protected function lib_opacify($args) {
@@ -1942,14 +2214,14 @@ class scssc {
$numbers = array();
foreach ($args as $key => $item) {
if ('number' != $item[0]) {
- throw new Exception(sprintf('%s is not a number', $item[0]));
+ $this->throwError("%s is not a number", $item[0]);
}
$number = $this->normalizeNumber($item);
if (null === $unit) {
$unit = $number[2];
} elseif ($unit !== $number[2]) {
- throw new \Exception(sprintf('Incompatible units: "%s" and "%s".', $originalUnit, $item[2]));
+ $this->throwError('Incompatible units: "%s" and "%s".', $originalUnit, $item[2]);
}
$originalUnit = $item[2];
@@ -2002,6 +2274,27 @@ class scssc {
return array("list", $sep, array_merge($list1[2], array($value)));
}
+ protected function lib_zip($args) {
+ foreach ($args as $arg) {
+ $this->assertList($arg);
+ }
+
+ $lists = array();
+ $firstList = array_shift($args);
+ foreach ($firstList[2] as $key => $item) {
+ $list = array("list", "", array($item));
+ foreach ($args as $arg) {
+ if (isset($arg[2][$key])) {
+ $list[2][] = $arg[2][$key];
+ } else {
+ break 2;
+ }
+ }
+ $lists[] = $list;
+ }
+
+ return array("list", ",", $lists);
+ }
protected static $lib_type_of = array("value");
protected function lib_type_of($args) {
@@ -2037,10 +2330,29 @@ class scssc {
return $value[0] == "number" && empty($value[2]);
}
-
protected static $lib_comparable = array("number-1", "number-2");
protected function lib_comparable($args) {
- return true; // TODO: THIS
+ list($number1, $number2) = $args;
+ if (!isset($number1[0]) || $number1[0] != "number" || !isset($number2[0]) || $number2[0] != "number") {
+ $this->throwError('Invalid argument(s) for "comparable"');
+ }
+
+ $number1 = $this->normalizeNumber($number1);
+ $number2 = $this->normalizeNumber($number2);
+
+ return $number1[2] == $number2[2] || $number1[2] == "" || $number2[2] == "";
+ }
+
+ protected function throwError($msg = null) {
+ if (func_num_args() > 1) {
+ $msg = call_user_func_array("sprintf", func_get_args());
+ }
+
+ if ($this->sourcePos >= 0 && isset($this->sourceParser)) {
+ $this->sourceParser->throwParseError($msg, $this->sourcePos);
+ }
+
+ throw new Exception($msg);
}
static protected $cssColors = array(
@@ -2194,6 +2506,11 @@ class scssc {
);
}
+/**
+ * SCSS parser
+ *
+ * @author Leaf Corcoran <leafot@gmail.com>
+ */
class scss_parser {
static protected $precedence = array(
"or" => 0,
@@ -2225,8 +2542,9 @@ class scss_parser {
static protected $commentMultiLeft = "/*";
static protected $commentMultiRight = "*/";
- function __construct($sourceName = null) {
+ function __construct($sourceName = null, $rootParser = true) {
$this->sourceName = $sourceName;
+ $this->rootParser = $rootParser;
if (empty(self::$operatorStr)) {
self::$operatorStr = $this->makeOperatorStr(self::$operators);
@@ -2268,6 +2586,45 @@ class scss_parser {
return $this->env;
}
+ /**
+ * Parse a single chunk off the head of the buffer and append it to the
+ * current parse environment.
+ *
+ * Returns false when the buffer is empty, or when there is an error.
+ *
+ * This function is called repeatedly until the entire document is
+ * parsed.
+ *
+ * This parser is most similar to a recursive descent parser. Single
+ * functions represent discrete grammatical rules for the language, and
+ * they are able to capture the text that represents those rules.
+ *
+ * Consider the function scssc::keyword(). (All parse functions are
+ * structured the same.)
+ *
+ * The function takes a single reference argument. When calling the
+ * function it will attempt to match a keyword on the head of the buffer.
+ * If it is successful, it will place the keyword in the referenced
+ * argument, advance the position in the buffer, and return true. If it
+ * fails then it won't advance the buffer and it will return false.
+ *
+ * All of these parse functions are powered by scssc::match(), which behaves
+ * the same way, but takes a literal regular expression. Sometimes it is
+ * more convenient to use match instead of creating a new function.
+ *
+ * Because of the format of the functions, to parse an entire string of
+ * grammatical rules, you can chain them together using &&.
+ *
+ * But, if some of the rules in the chain succeed before one fails, then
+ * the buffer position will be left at an invalid state. In order to
+ * avoid this, scssc::seek() is used to remember and set buffer positions.
+ *
+ * Before parsing a chain, use $s = $this->seek() to remember the current
+ * position into $s. Then if a chain fails, use $this->seek($s) to
+ * go back where we started.
+ *
+ * @return boolean
+ */
protected function parseChunk() {
$s = $this->seek();
@@ -2309,7 +2666,7 @@ class scss_parser {
$include = $this->pushSpecialBlock("include");
$include->child = $child;
} else {
- $this->append($child);
+ $this->append($child, $s);
}
return true;
@@ -2321,7 +2678,7 @@ class scss_parser {
$this->valueList($importPath) &&
$this->end())
{
- $this->append(array("import", $importPath));
+ $this->append(array("import", $importPath), $s);
return true;
} else {
$this->seek($s);
@@ -2331,19 +2688,19 @@ class scss_parser {
$this->selectors($selector) &&
$this->end())
{
- $this->append(array("extend", $selector));
+ $this->append(array("extend", $selector), $s);
return true;
} else {
$this->seek($s);
}
if ($this->literal("@function") &&
- $this->keyword($fn_name) &&
+ $this->keyword($fnName) &&
$this->argumentDef($args) &&
$this->literal("{"))
{
$func = $this->pushSpecialBlock("function");
- $func->name = $fn_name;
+ $func->name = $fnName;
$func->args = $args;
return true;
} else {
@@ -2351,7 +2708,7 @@ class scss_parser {
}
if ($this->literal("@return") && $this->valueList($retVal) && $this->end()) {
- $this->append(array("return", $retVal));
+ $this->append(array("return", $retVal), $s);
return true;
} else {
$this->seek($s);
@@ -2413,14 +2770,14 @@ class scss_parser {
if (($this->literal("@debug") || $this->literal("@warn")) &&
$this->valueList($value) &&
$this->end()) {
- $this->append(array("debug", $value, $s));
+ $this->append(array("debug", $value, $s), $s);
return true;
} else {
$this->seek($s);
}
if ($this->literal("@content") && $this->end()) {
- $this->append(array("mixin_content"));
+ $this->append(array("mixin_content"), $s);
return true;
} else {
$this->seek($s);
@@ -2450,7 +2807,7 @@ class scss_parser {
if ($this->literal("@charset") &&
$this->valueList($charset) && $this->end())
{
- $this->append(array("charset", $charset));
+ $this->append(array("charset", $charset), $s);
return true;
} else {
$this->seek($s);
@@ -2479,7 +2836,7 @@ class scss_parser {
$this->end())
{
$name = array("string", "", array($name));
- $this->append(array("assign", $name, $value));
+ $this->append(array("assign", $name, $value), $s);
return true;
} else {
$this->seek($s);
@@ -2490,17 +2847,9 @@ class scss_parser {
$this->literal(":") &&
$this->valueList($value) && $this->end())
{
- $defaultVar = false;
// check for !default
- if ($value[0] == "list") {
- $def = end($value[2]);
- if ($def[0] == "keyword" && $def[1] == "!default") {
- array_pop($value[2]);
- $value = $this->flattenList($value);
- $defaultVar = true;
- }
- }
- $this->append(array("assign", $name, $value, $defaultVar));
+ $defaultVar = $value[0] == "list" && $this->stripDefault($value);
+ $this->append(array("assign", $name, $value, $defaultVar), $s);
return true;
} else {
$this->seek($s);
@@ -2527,7 +2876,7 @@ class scss_parser {
if ($this->propertyName($name) && $this->literal(":")) {
$foundSomething = false;
if ($this->valueList($value)) {
- $this->append(array("assign", $name, $value));
+ $this->append(array("assign", $name, $value), $s);
$foundSomething = true;
}
@@ -2555,10 +2904,10 @@ class scss_parser {
$include = $block->child;
unset($block->child);
$include[3] = $block;
- $this->append($include);
- } else if (empty($block->dontAppend)) {
+ $this->append($include, $s);
+ } elseif (empty($block->dontAppend)) {
$type = isset($block->type) ? $block->type : "block";
- $this->append(array($type, $block));
+ $this->append(array($type, $block), $s);
}
return true;
}
@@ -2573,19 +2922,35 @@ class scss_parser {
return false;
}
+ protected function stripDefault(&$value) {
+ $def = end($value[2]);
+ if ($def[0] == "keyword" && $def[1] == "!default") {
+ array_pop($value[2]);
+ $value = $this->flattenList($value);
+ return true;
+ }
+
+ if ($def[0] == "list") {
+ return $this->stripDefault($value[2][count($value[2]) - 1]);
+ }
+
+ return false;
+ }
+
protected function literal($what, $eatWhitespace = null) {
if (is_null($eatWhitespace)) $eatWhitespace = $this->eatWhiteDefault;
- // this is here mainly prevent notice from { } string accessor
- if ($this->count >= strlen($this->buffer)) return false;
-
// shortcut on single letter
- if (!$eatWhitespace && strlen($what) == 1) {
- if ($this->buffer{$this->count} == $what) {
- $this->count++;
- return true;
+ if (!isset($what[1]) && isset($this->buffer[$this->count])) {
+ if ($this->buffer[$this->count] == $what) {
+ if (!$eatWhitespace) {
+ $this->count++;
+ return true;
+ }
+ // goes below...
+ } else {
+ return false;
}
- else return false;
}
return $this->match($this->preg_quote($what), $m, $eatWhitespace);
@@ -2594,7 +2959,7 @@ class scss_parser {
// tree builders
protected function pushBlock($selectors) {
- $b = new stdclass;
+ $b = new stdClass;
$b->parent = $this->env; // not sure if we need this yet
$b->selectors = $selectors;
@@ -2621,7 +2986,11 @@ class scss_parser {
return $old;
}
- protected function append($statement) {
+ protected function append($statement, $pos=null) {
+ if ($pos !== null) {
+ $statement[-1] = $pos;
+ if (!$this->rootParser) $statement[-2] = $this;
+ }
$this->env->children[] = $statement;
}
@@ -2644,20 +3013,23 @@ class scss_parser {
$expressions = null;
$parts = array();
- if (($this->literal("only") && ($only = true) || $this->literal("not") && ($not = true) || true) && $this->keyword($mediaType)) {
+ if (($this->literal("only") && ($only = true) || $this->literal("not") && ($not = true) || true) && $this->mixedKeyword($mediaType)) {
$prop = array("mediaType");
- if (isset($only)) $prop[] = "only";
- if (isset($not)) $prop[] = "not";
- $prop[] = $mediaType;
+ if (isset($only)) $prop[] = array("keyword", "only");
+ if (isset($not)) $prop[] = array("keyword", "not");
+ $media = array("list", "", array());
+ foreach ((array)$mediaType as $type) {
+ if (is_array($type)) {
+ $media[2][] = $type;
+ } else {
+ $media[2][] = array("keyword", $type);
+ }
+ }
+ $prop[] = $media;
$parts[] = $prop;
- } else {
- $this->seek($s);
}
-
- if (!empty($mediaType) && !$this->literal("and")) {
- // ~
- } else {
+ if (empty($parts) || $this->literal("and")) {
$this->genericList($expressions, "mediaExpression", "and", false);
if (is_array($expressions)) $parts = array_merge($parts, $expressions[2]);
}
@@ -2670,7 +3042,7 @@ class scss_parser {
$s = $this->seek();
$value = null;
if ($this->literal("(") &&
- $this->keyword($feature) &&
+ $this->expression($feature) &&
($this->literal(":") && $this->expression($value) || true) &&
$this->literal(")"))
{
@@ -2701,7 +3073,13 @@ class scss_parser {
}
if ($this->genericList($value, "expression")) {
- $out = array($keyword, $value);
+ $out = array($keyword, $value, false);
+ $s = $this->seek();
+ if ($this->literal("...")) {
+ $out[2] = true;
+ } else {
+ $this->seek($s);
+ }
return true;
}
@@ -2710,11 +3088,11 @@ class scss_parser {
protected function valueList(&$out) {
- return $this->genericList($out, "commaList");
+ return $this->genericList($out, "spaceList", ",");
}
- protected function commaList(&$out) {
- return $this->genericList($out, "expression", ",");
+ protected function spaceList(&$out) {
+ return $this->genericList($out, "expression");
}
protected function genericList(&$out, $parseItem, $delim="", $flatten=true) {
@@ -2838,7 +3216,11 @@ class scss_parser {
if ($this->progid($out)) return true;
if ($this->keyword($keyword)) {
- $out = array("keyword", $keyword);
+ if ($keyword == "null") {
+ $out = array("null");
+ } else {
+ $out = array("keyword", $keyword);
+ }
return true;
}
@@ -2890,7 +3272,12 @@ class scss_parser {
if ($this->keyword($name, false) &&
$this->literal("("))
{
- if ($name != "expression" && false == preg_match("/^(-[a-z]+-)?calc$/", $name)) {
+ if ($name == "alpha" && $this->argumentList($args)) {
+ $func = array("function", $name, array("string", "", $args));
+ return true;
+ }
+
+ if ($name != "expression" && !preg_match("/^(-[a-z]+-)?calc$/", $name)) {
$ss = $this->seek();
if ($this->argValues($args) && $this->literal(")")) {
$func = array("fncall", $name, $args);
@@ -2916,21 +3303,64 @@ class scss_parser {
return false;
}
+ protected function argumentList(&$out) {
+ $s = $this->seek();
+ $this->literal("(");
+
+ $args = array();
+ while ($this->keyword($var)) {
+ $ss = $this->seek();
+
+ if ($this->literal("=") && $this->expression($exp)) {
+ $args[] = array("string", "", array($var."="));
+ $arg = $exp;
+ } else {
+ break;
+ }
+
+ $args[] = $arg;
+
+ if (!$this->literal(",")) break;
+
+ $args[] = array("string", "", array(", "));
+ }
+
+ if (!$this->literal(")")) {
+ $this->seek($s);
+ return false;
+ }
+
+ $out = $args;
+ return true;
+ }
+
protected function argumentDef(&$out) {
$s = $this->seek();
$this->literal("(");
$args = array();
while ($this->variable($var)) {
- $arg = array($var[1], null);
+ $arg = array($var[1], null, false);
$ss = $this->seek();
- if ($this->literal(":") && $this->expression($defaultVal)) {
+ if ($this->literal(":") && $this->genericList($defaultVal, "expression")) {
$arg[1] = $defaultVal;
} else {
$this->seek($ss);
}
+ $ss = $this->seek();
+ if ($this->literal("...")) {
+ $sss = $this->seek();
+ if (!$this->literal(")")) {
+ $this->throwParseError("... has to be after the final argument");
+ }
+ $arg[2] = true;
+ $this->seek($sss);
+ } else {
+ $this->seek($ss);
+ }
+
$args[] = $arg;
if (!$this->literal(",")) break;
}
@@ -2990,15 +3420,10 @@ class scss_parser {
}
$content = array();
-
- // look for either ending delim , escape, or string interpolation
- $patt = '([^\n]*?)(#\{|\\\\|' .
- $this->preg_quote($delim).')';
-
$oldWhite = $this->eatWhiteDefault;
$this->eatWhiteDefault = false;
- while ($this->match($patt, $m, false)) {
+ while ($this->matchString($m, $delim)) {
$content[] = $m[1];
if ($m[2] == "#{") {
$this->count -= strlen($m[2]);
@@ -3056,6 +3481,10 @@ class scss_parser {
if (count($parts) == 0) return false;
+ if ($this->eatWhiteDefault) {
+ $this->whitespace();
+ }
+
$out = $parts;
return true;
}
@@ -3075,7 +3504,7 @@ class scss_parser {
$content = array();
while ($this->match($patt, $m, false)) {
- if (!empty($m[1])) {
+ if (isset($m[1]) && $m[1] !== '') {
$content[] = $m[1];
if ($nestingOpen) {
$nestingLevel += substr_count($m[1], $nestingOpen);
@@ -3209,7 +3638,7 @@ class scss_parser {
return true;
}
- // whitepsace separated list of selectorSingle
+ // whitespace separated list of selectorSingle
protected function selector(&$out) {
$selector = array();
@@ -3219,6 +3648,8 @@ class scss_parser {
} elseif ($this->selectorSingle($part)) {
$selector[] = $part;
$this->whitespace();
+ } elseif ($this->match('\/[^\/]+\/', $m)) {
+ $selector[] = array($m[0]);
} else {
break;
}
@@ -3234,7 +3665,7 @@ class scss_parser {
}
// the parts that make up
- // div[yes=no]#something.hello.world:nth-child(-2n+1)
+ // div[yes=no]#something.hello.world:nth-child(-2n+1)%placeholder
protected function selectorSingle(&$out) {
$oldWhite = $this->eatWhiteDefault;
$this->eatWhiteDefault = false;
@@ -3285,6 +3716,12 @@ class scss_parser {
continue;
}
+ if ($this->literal('%', false) && $this->placeholder($placeholder)) {
+ $parts[] = '%';
+ $parts[] = $placeholder;
+ continue;
+ }
+
if ($this->literal("#", false)) {
$parts[] = "#";
continue;
@@ -3395,11 +3832,19 @@ class scss_parser {
return false;
}
+ protected function placeholder(&$placeholder) {
+ if ($this->match('([\w\-_]+)', $m)) {
+ $placeholder = $m[1];
+ return true;
+ }
+ return false;
+ }
+
// consume an end of statement delimiter
protected function end() {
if ($this->literal(';')) {
return true;
- } elseif ($this->count == strlen($this->buffer) || $this->buffer{$this->count} == '}') {
+ } elseif ($this->count == strlen($this->buffer) || $this->buffer[$this->count] == '}') {
// if there is end of file or a closing block next then we don't need a ;
return true;
}
@@ -3421,7 +3866,7 @@ class scss_parser {
return true;
}
- protected function throwParseError($msg = "parse error", $count = null) {
+ public function throwParseError($msg = "parse error", $count = null) {
$count = is_null($count) ? $this->count : $count;
$line = $this->getLineNo($count);
@@ -3433,9 +3878,9 @@ class scss_parser {
}
if ($this->peek("(.*?)(\n|$)", $m, $count)) {
- throw new exception("$msg: failed at `$m[1]` $loc");
+ throw new Exception("$msg: failed at `$m[1]` $loc");
} else {
- throw new exception("$msg: $loc");
+ throw new Exception("$msg: $loc");
}
}
@@ -3443,6 +3888,48 @@ class scss_parser {
return 1 + substr_count(substr($this->buffer, 0, $pos), "\n");
}
+ /**
+ * Match string looking for either ending delim, escape, or string interpolation
+ *
+ * {@internal This is a workaround for preg_match's 250K string match limit. }}
+ *
+ * @param array $m Matches (passed by reference)
+ * @param string $delim Delimeter
+ *
+ * @return boolean True if match; false otherwise
+ */
+ protected function matchString(&$m, $delim) {
+ $token = null;
+
+ $end = strpos($this->buffer, "\n", $this->count);
+ if ($end === false) {
+ $end = strlen($this->buffer);
+ }
+
+ // look for either ending delim, escape, or string interpolation
+ foreach (array('#{', '\\', $delim) as $lookahead) {
+ $pos = strpos($this->buffer, $lookahead, $this->count);
+ if ($pos !== false && $pos < $end) {
+ $end = $pos;
+ $token = $lookahead;
+ }
+ }
+
+ if (!isset($token)) {
+ return false;
+ }
+
+ $match = substr($this->buffer, $this->count, $end - $this->count);
+ $m = array(
+ $match . $token,
+ $match,
+ $token
+ );
+ $this->count = $end + strlen($token);
+
+ return true;
+ }
+
// try to match something on head of buffer
protected function match($regex, &$out, $eatWhitespace = null) {
if (is_null($eatWhitespace)) $eatWhitespace = $this->eatWhiteDefault;
@@ -3507,6 +3994,11 @@ class scss_parser {
}
}
+/**
+ * SCSS base formatter
+ *
+ * @author Leaf Corcoran <leafot@gmail.com>
+ */
class scss_formatter {
public $indentChar = " ";
@@ -3528,7 +4020,7 @@ class scss_formatter {
return $name . $this->assignSeparator . $value . ";";
}
- public function block($block) {
+ protected function block($block) {
if (empty($block->lines) && empty($block->children)) return;
$inner = $pre = $this->indentStr();
@@ -3559,9 +4051,21 @@ class scss_formatter {
echo $pre . $this->close . $this->break;
}
}
-}
+ public function format($block) {
+ ob_start();
+ $this->block($block);
+ $out = ob_get_clean();
+
+ return $out;
+ }
+}
+/**
+ * SCSS nested formatter
+ *
+ * @author Leaf Corcoran <leafot@gmail.com>
+ */
class scss_formatter_nested extends scss_formatter {
public $close = " }";
@@ -3578,6 +4082,21 @@ class scss_formatter_nested extends scss_formatter {
}
$children[] = $child;
}
+
+ $count = count($children);
+ for ($i = 0; $i < $count; $i++) {
+ $depth = $children[$i]->depth;
+ $j = $i + 1;
+ if (isset($children[$j]) && $depth < $children[$j]->depth) {
+ $childDepth = $children[$j]->depth;
+ for (; $j < $count; $j++) {
+ if ($depth < $children[$j]->depth && $childDepth >= $children[$j]->depth) {
+ $children[$j]->depth = $depth + 1;
+ }
+ }
+ }
+ }
+
$block->children = $children;
// make relative to parent
@@ -3587,7 +4106,7 @@ class scss_formatter_nested extends scss_formatter {
}
}
- public function block($block) {
+ protected function block($block) {
if ($block->type == "root") {
$this->adjustAllChildren($block);
}
@@ -3633,6 +4152,11 @@ class scss_formatter_nested extends scss_formatter {
}
}
+/**
+ * SCSS compressed formatter
+ *
+ * @author Leaf Corcoran <leafot@gmail.com>
+ */
class scss_formatter_compressed extends scss_formatter {
public $open = "{";
public $tagSeparator = ",";
@@ -3644,37 +4168,86 @@ class scss_formatter_compressed extends scss_formatter {
}
}
+/**
+ * SCSS server
+ *
+ * @author Leaf Corcoran <leafot@gmail.com>
+ */
class scss_server {
-
+ /**
+ * Join path components
+ *
+ * @param string $left Path component, left of the directory separator
+ * @param string $right Path component, right of the directory separator
+ *
+ * @return string
+ */
protected function join($left, $right) {
- return rtrim($left, "/") . "/" . ltrim($right, "/");
+ return rtrim($left, '/\\') . DIRECTORY_SEPARATOR . ltrim($right, '/\\');
}
+ /**
+ * Get name of requested .scss file
+ *
+ * @return string|null
+ */
protected function inputName() {
- if (isset($_GET["p"])) return $_GET["p"];
-
- if (isset($_SERVER["PATH_INFO"])) return $_SERVER["PATH_INFO"];
- if (isset($_SERVER["DOCUMENT_URI"])) {
- return substr($_SERVER["DOCUMENT_URI"], strlen($_SERVER["SCRIPT_NAME"]));
+ switch (true) {
+ case isset($_GET['p']):
+ return $_GET['p'];
+ case isset($_SERVER['PATH_INFO']):
+ return $_SERVER['PATH_INFO'];
+ case isset($_SERVER['DOCUMENT_URI']):
+ return substr($_SERVER['DOCUMENT_URI'], strlen($_SERVER['SCRIPT_NAME']));
}
}
+ /**
+ * Get path to requested .scss file
+ *
+ * @return string
+ */
protected function findInput() {
- if ($input = $this->inputName()) {
+ if ($input = $this->inputName()
+ && strpos($input, '..') === false
+ && substr($input, -5) === '.scss'
+ ) {
$name = $this->join($this->dir, $input);
- if (is_readable($name)) return $name;
+
+ if (is_file($name) && is_readable($name)) {
+ return $name;
+ }
}
+
return false;
}
+ /**
+ * Get path to cached .css file
+ *
+ * @return string
+ */
protected function cacheName($fname) {
- return $this->join($this->cacheDir, md5($fname) . ".css");
+ return $this->join($this->cacheDir, md5($fname) . '.css');
}
+ /**
+ * Get path to cached imports
+ *
+ * @return string
+ */
protected function importsCacheName($out) {
- return $out . ".imports";
+ return $out . '.imports';
}
+ /**
+ * Determine whether .scss file needs to be re-compiled.
+ *
+ * @param string $in Input path
+ * @param string $out Output path
+ *
+ * @return boolean True if compile required.
+ */
protected function needsCompile($in, $out) {
if (!is_file($out)) return true;
@@ -3692,13 +4265,21 @@ class scss_server {
return false;
}
+ /**
+ * Compile .scss file
+ *
+ * @param string $in Input path (.scss)
+ * @param string $out Output path (.css)
+ *
+ * @return string
+ */
protected function compile($in, $out) {
$start = microtime(true);
$css = $this->scss->compile(file_get_contents($in), $in);
$elapsed = round((microtime(true) - $start), 4);
$v = scssc::$VERSION;
- $t = date("r");
+ $t = date('r');
$css = "/* compiled by scssphp $v on $t (${elapsed}s) */\n\n" . $css;
file_put_contents($out, $css);
@@ -3707,17 +4288,20 @@ class scss_server {
return $css;
}
+ /**
+ * Compile requested scss and serve css. Outputs HTTP response.
+ */
public function serve() {
if ($input = $this->findInput()) {
$output = $this->cacheName($input);
- header("Content-type: text/css");
+ header('Content-type: text/css');
if ($this->needsCompile($input, $output)) {
try {
echo $this->compile($input, $output);
- } catch (exception $e) {
+ } catch (Exception $e) {
header('HTTP/1.1 500 Internal Server Error');
- echo "Parse error: " . $e->getMessage() . "\n";
+ echo 'Parse error: ' . $e->getMessage() . "\n";
}
} else {
header('X-SCSS-Cache: true');
@@ -3728,16 +4312,23 @@ class scss_server {
}
header('HTTP/1.0 404 Not Found');
- header("Content-type: text");
+ header('Content-type: text');
$v = scssc::$VERSION;
echo "/* INPUT NOT FOUND scss $v */\n";
}
+ /**
+ * Constructor
+ *
+ * @param string $dir Root directory to .scss files
+ * @param string $cacheDir Cache directory
+ * @param \scssc|null $scss SCSS compiler instance
+ */
public function __construct($dir, $cacheDir=null, $scss=null) {
$this->dir = $dir;
if (is_null($cacheDir)) {
- $cacheDir = $this->join($dir, "scss_cache");
+ $cacheDir = $this->join($dir, 'scss_cache');
}
$this->cacheDir = $cacheDir;
@@ -3750,10 +4341,13 @@ class scss_server {
$this->scss = $scss;
}
+ /**
+ * Helper method to serve compiled scss
+ *
+ * @param string $path Root path
+ */
static public function serveFrom($path) {
$server = new self($path);
$server->serve();
}
}
-
-
diff --git a/plugins/jetpack/modules/custom-post-types/comics.php b/plugins/jetpack/modules/custom-post-types/comics.php
new file mode 100644
index 00000000..a91b7e56
--- /dev/null
+++ b/plugins/jetpack/modules/custom-post-types/comics.php
@@ -0,0 +1,472 @@
+<?php
+
+class Jetpack_Comic {
+ const POST_TYPE = 'jetpack-comic';
+
+ static function init() {
+ static $instance = false;
+
+ if ( ! $instance )
+ $instance = new Jetpack_Comic;
+
+ return $instance;
+ }
+
+ /**
+ * Conditionally hook into WordPress.
+ *
+ * Themes must declare that they support this module by adding
+ * add_theme_support( 'jetpack-comic' ); during after_setup_theme.
+ *
+ * If no theme support is found there is no need to hook into
+ * WordPress. We'll just return early instead.
+ */
+ function __construct() {
+ // Return early if theme does not support Jetpack Comic.
+ if ( ! ( $this->site_supports_comics() ) )
+ return;
+
+ $this->register_post_types();
+
+ add_action( 'pre_get_posts', array( $this, 'add_posts_to_loop' ) );
+
+ // In order for the Feedbag job to find Comic posts, we need to circumvent any pretty
+ // URLs in the RSS feed given to Feedbag in favor of /?p=123&post_type=jetpack-comic
+ add_filter( 'the_permalink_rss', array( $this, 'custom_permalink_for_feedbag' ) );
+
+ // There are some cases (like when Feedbag is fetching posts) that the comics
+ // post type needs to be registered no matter what, but none of the UI needs to be
+ // available.
+
+ // Enable Omnisearch for Comic posts.
+ // @see http://themedevp2.wordpress.com/2013/06/21/howdy-cainm-id-like-to // @wpcom
+ if ( class_exists( 'Jetpack_Omnisearch_Posts' ) )
+ new Jetpack_Omnisearch_Posts( self::POST_TYPE );
+
+ add_filter( 'post_updated_messages', array( $this, 'updated_messages' ) );
+
+ if ( function_exists( 'queue_publish_post' ) ) {
+ add_action( 'publish_jetpack-comic', 'queue_publish_post', 10, 2 );
+ } else {
+ add_action( 'publish_jetpack-comic', 'publish_post', 10, 2 );
+ }
+
+ add_action( 'pre_get_posts', array( $this, 'include_in_feeds' ) );
+
+ add_action( 'admin_enqueue_scripts', array( $this, 'admin_enqueue_scripts' ) );
+
+ add_filter( 'manage_' . self::POST_TYPE . '_posts_columns', array( $this, 'manage_posts_columns' ) );
+ add_action( 'manage_' . self::POST_TYPE . '_posts_custom_column', array( $this, 'manage_posts_custom_column' ), 10, 2 );
+ add_image_size( 'jetpack-comic-thumb', 150, 0, false );
+
+ // Enable front-end uploading for users special enough.
+ if ( current_user_can( 'upload_files' ) && current_user_can( 'edit_posts' ) ) {
+ add_action( 'wp_ajax_jetpack_comic_upload', array( $this, 'upload' ) );
+ add_action( 'wp_enqueue_scripts', array( $this, 'register_scripts' ) );
+ }
+
+ /**
+ * Add a "Convert to Comic" and "Convert to Post" option to the bulk
+ * edit dropdowns.
+ */
+ add_action( 'admin_footer-edit.php', array( $this, 'admin_footer' ) );
+ add_action( 'load-edit.php', array( $this, 'bulk_edit' ) );
+ add_action( 'admin_notices', array( $this, 'bulk_edit_notices' ) );
+ }
+
+ public function admin_footer() {
+ $post_type = get_post_type();
+
+ ?>
+ <script type="text/javascript">
+ jQuery( document ).ready( function( $ ) {
+ <?php if ( ! $post_type || 'post' == $post_type ) { ?>
+ $( '<option>' )
+ .val( 'post2comic' )
+ .text( <?php echo json_encode( __( 'Convert to Comic', 'jetpack' ) ); ?> )
+ .appendTo( "select[name='action'], select[name='action2']" );
+ <?php } ?>
+ <?php if ( ! $post_type || self::POST_TYPE == $post_type ) { ?>
+ $( '<option>' )
+ .val( 'comic2post' )
+ .text( <?php echo json_encode( __( 'Convert to Post', 'jetpack' ) ); ?> )
+ .appendTo( "select[name='action'], select[name='action2']" );
+ <?php } ?>
+
+ $( '#message.jetpack-comic-post-type-conversion' ).remove().insertAfter( $( '.wrap h2:first' ) ).show();
+ });
+ </script>
+ <?php
+ }
+
+ /**
+ * Handle the "Convert to [Post|Comic]" bulk action.
+ */
+ public function bulk_edit() {
+ if ( empty( $_REQUEST['post'] ) )
+ return;
+
+ $wp_list_table = _get_list_table( 'WP_Posts_List_Table' );
+ $action = $wp_list_table->current_action();
+
+ check_admin_referer( 'bulk-posts' );
+
+ if ( 'post2comic' == $action || 'comic2post' == $action ) {
+ if ( ! current_user_can( 'publish_posts' ) )
+ wp_die( __( 'You are not allowed to make this change.', 'jetpack' ) );
+
+ $post_ids = array_map( 'intval', $_REQUEST['post'] );
+
+ $modified_count = 0;
+
+ foreach ( $post_ids as $post_id ) {
+ $destination_post_type = ( $action == 'post2comic' ) ? self::POST_TYPE : 'post';
+ $origin_post_type = ( $destination_post_type == 'post' ) ? self::POST_TYPE : 'post';
+
+ if ( current_user_can( 'edit_post', $post_id ) ) {
+ $post = get_post( $post_id );
+
+ // Only convert posts that are post => comic or comic => post.
+ // (e.g., Ignore comic => comic, page => post, etc. )
+ if ( $post->post_type != $destination_post_type && $post->post_type == $origin_post_type ) {
+ $post_type_object = get_post_type_object( $destination_post_type );
+
+ if ( current_user_can( $post_type_object->cap->publish_posts ) ) {
+ set_post_type( $post_id, $destination_post_type );
+ $modified_count++;
+ }
+ }
+ }
+ }
+
+ $sendback = remove_query_arg( array( 'exported', 'untrashed', 'deleted', 'ids' ), wp_get_referer() );
+
+ if ( ! $sendback )
+ $sendback = add_query_arg( array( 'post_type', get_post_type() ), admin_url( 'edit.php' ) );
+
+ $pagenum = $wp_list_table->get_pagenum();
+ $sendback = add_query_arg( array( 'paged' => $pagenum, 'post_type_changed' => $modified_count ), $sendback );
+
+ wp_safe_redirect( $sendback );
+ exit();
+ }
+ }
+
+ /**
+ * Show the post conversion success notice.
+ */
+ public function bulk_edit_notices() {
+ global $pagenow;
+
+ if ( 'edit.php' == $pagenow && ! empty( $_GET['post_type_changed'] ) ) {
+ ?><div id="message" class="updated below-h2 jetpack-comic-post-type-conversion" style="display: none;"><p><?php
+ printf( _n( 'Post converted.', '%s posts converted', $_GET['post_type_changed'], 'jetpack' ), number_format_i18n( $_GET['post_type_changed'] ) );
+ ?></p></div><?php
+ }
+ }
+
+ public function register_scripts() {
+ wp_enqueue_style( 'jetpack-comics-style', plugins_url( 'comics/comics.css', __FILE__ ) );
+ wp_enqueue_script( 'jetpack-comics', plugins_url( 'comics/comics.js', __FILE__ ), array( 'jquery', 'jquery.spin' ) );
+
+ $options = array(
+ 'nonce' => wp_create_nonce( 'jetpack_comic_upload_nonce' ),
+ 'writeURL' => admin_url( 'admin-ajax.php?action=jetpack_comic_upload' ),
+ 'labels' => array(
+ 'dragging' => __( 'Drop images to upload', 'jetpack' ),
+ 'uploading' => __( 'Uploading...', 'jetpack' ),
+ 'processing' => __( 'Processing...', 'jetpack' ),
+ 'unsupported' => __( "Sorry, your browser isn't supported. Upgrade at browsehappy.com.", 'jetpack' ),
+ 'invalidUpload' => __( 'Only images can be uploaded here.', 'jetpack' ),
+ 'error' => __( "Your upload didn't complete; try again later or cross your fingers and try again right now.", 'jetpack' ),
+ )
+ );
+
+ wp_localize_script( 'jetpack-comics', 'Jetpack_Comics_Options', $options );
+ }
+
+ public function admin_enqueue_scripts() {
+ wp_enqueue_style( 'jetpack-comics-admin', plugins_url( 'comics/admin.css', __FILE__ ) );
+ }
+
+ function register_post_types() {
+ register_post_type( self::POST_TYPE, array(
+ 'description' => __( 'Comics', 'jetpack' ),
+ 'labels' => array(
+ 'name' => esc_html__( 'Comics', 'jetpack' ),
+ 'singular_name' => esc_html__( 'Comic', 'jetpack' ),
+ 'menu_name' => esc_html__( 'Comics', 'jetpack' ),
+ 'all_items' => esc_html__( 'All Comics', 'jetpack' ),
+ 'add_new' => esc_html__( 'Add New', 'jetpack' ),
+ 'add_new_item' => esc_html__( 'Add New Comic', 'jetpack' ),
+ 'edit_item' => esc_html__( 'Edit Comic', 'jetpack' ),
+ 'new_item' => esc_html__( 'New Comic', 'jetpack' ),
+ 'view_item' => esc_html__( 'View Comic', 'jetpack' ),
+ 'search_items' => esc_html__( 'Search Comics', 'jetpack' ),
+ 'not_found' => esc_html__( 'No Comics found', 'jetpack' ),
+ 'not_found_in_trash' => esc_html__( 'No Comics found in Trash', 'jetpack' ),
+ ),
+ 'supports' => array(
+ 'title',
+ 'editor',
+ 'thumbnail',
+ 'comments',
+ 'revisions',
+ 'publicize', // Jetpack
+ 'subscriptions', // wpcom
+ 'shortlinks', // Jetpack
+ ),
+ 'rewrite' => array(
+ 'slug' => 'comic',
+ 'with_front' => false,
+ ),
+ 'taxonomies' => array(
+ 'category',
+ 'post_tag',
+ ),
+ // Only make the type public for sites that support Comics.
+ 'public' => true,
+ 'menu_position' => 5, // below Posts
+ 'map_meta_cap' => true,
+ 'has_archive' => true,
+ 'query_var' => 'comic',
+ ) );
+ }
+
+ public function manage_posts_columns( $columns ) {
+ $new_columns = array(
+ 'preview-jetpack-comic' => __( 'Preview', 'jetpack' ),
+ );
+ return array_merge( array_slice( $columns, 0, 2 ), $new_columns, array_slice( $columns, 2 ) );
+ }
+
+ public function manage_posts_custom_column( $column_name, $post_ID ) {
+ if ( 'preview-jetpack-comic' == $column_name && has_post_thumbnail( $post_ID ) ) {
+ echo get_the_post_thumbnail( $post_ID, 'jetpack-comic-thumb' );
+ }
+ }
+
+ /**
+ * The function url_to_postid() doesn't handle pretty permalinks
+ * for CPTs very well. When we're generating an RSS feed to be consumed
+ * for Feedbag (the Reader's feed storage mechanism), eschew
+ * a pretty URL for one that will get the post into the Reader.
+ *
+ * @see http://core.trac.wordpress.org/ticket/19744
+ * @param string $permalink The existing (possibly pretty) permalink.
+ */
+ public function custom_permalink_for_feedbag( $permalink ) {
+ global $post;
+
+ if ( ! empty( $GLOBALS['is_feedbag_rss_script'] ) && self::POST_TYPE == $post->post_type ) {
+ $permalink = home_url( add_query_arg( array( 'p' => $post->ID, 'post_type' => self::POST_TYPE ), '?' ) );
+ }
+
+ return $permalink;
+ }
+
+ /*
+ * Update messages for the Comic admin.
+ */
+ function updated_messages( $messages ) {
+ global $post;
+
+ $messages['jetpack-comic'] = array(
+ 0 => '', // Unused. Messages start at index 1.
+ 1 => sprintf( __( 'Comic updated. <a href="%s">View comic</a>', 'jetpack'), esc_url( get_permalink( $post->ID ) ) ),
+ 2 => esc_html__( 'Custom field updated.', 'jetpack' ),
+ 3 => esc_html__( 'Custom field deleted.', 'jetpack' ),
+ 4 => esc_html__( 'Comic updated.', 'jetpack' ),
+ /* translators: %s: date and time of the revision */
+ 5 => isset( $_GET['revision'] ) ? sprintf( esc_html__( 'Comic restored to revision from %s', 'jetpack'), wp_post_revision_title( (int) $_GET['revision'], false ) ) : false,
+ 6 => sprintf( __( 'Comic published. <a href="%s">View comic</a>', 'jetpack' ), esc_url( get_permalink( $post->ID ) ) ),
+ 7 => esc_html__( 'Comic saved.', 'jetpack' ),
+ 8 => sprintf( __( 'Comic submitted. <a target="_blank" href="%s">Preview comic</a>', 'jetpack'), esc_url( add_query_arg( 'preview', 'true', get_permalink( $post->ID ) ) ) ),
+ 9 => sprintf( __( 'Comic scheduled for: <strong>%1$s</strong>. <a target="_blank" href="%2$s">Preview comic</a>', 'jetpack' ),
+ // translators: Publish box date format, see http://php.net/date
+ date_i18n( __( 'M j, Y @ G:i', 'jetpack' ), strtotime( $post->post_date ) ), esc_url( get_permalink($post->ID) ) ),
+ 10 => sprintf( __( 'Comic draft updated. <a target="_blank" href="%s">Preview comic</a>', 'jetpack' ), esc_url( add_query_arg( 'preview', 'true', get_permalink( $post->ID ) ) ) ),
+ );
+
+ return $messages;
+ }
+
+ public function site_supports_comics() {
+ if ( 'blog-rss.php' == substr( $_SERVER['PHP_SELF'], -12 ) && count( $_SERVER['argv'] ) > 1 ) {
+ // blog-rss.php isn't run in the context of the target blog when the init action fires,
+ // so check manually whether the target blog supports comics.
+ switch_to_blog( $_SERVER['argv'][1] );
+ // The add_theme_support( 'jetpack-comic' ) won't fire on switch_to_blog, so check for Panel manually.
+ $supports_comics = $this->_site_supports_comics() || get_stylesheet() == 'pub/panel';
+ restore_current_blog();
+ return $supports_comics;
+ }
+ else {
+ return $this->_site_supports_comics();
+ }
+ }
+
+ private function _site_supports_comics() {
+ return ( ( function_exists( 'site_vertical' ) && 'comics' == site_vertical() ) || current_theme_supports( self::POST_TYPE ) );
+ }
+
+ /**
+ * Anywhere that a feed is displaying posts, show comics too.
+ *
+ * @param WP_Query $query
+ */
+ public function include_in_feeds( $query ) {
+ if ( ! $query->is_feed() )
+ return;
+
+ // Don't modify the query if the post type isn't public.
+ if ( ! get_post_type_object( 'jetpack-comic' )->public )
+ return;
+
+ $query_post_types = $query->get( 'post_type' );
+
+ if ( empty( $query_post_types ) )
+ $query_post_types = 'post';
+
+ if ( ! is_array( $query_post_types ) )
+ $query_post_types = array( $query_post_types );
+
+ if ( in_array( 'post', $query_post_types ) ) {
+ $query_post_types[] = self::POST_TYPE;
+ $query->set( 'post_type', $query_post_types );
+ }
+ }
+
+ /**
+ * API endpoint for front-end image uploading.
+ */
+ public function upload() {
+ global $content_width;
+
+ header( 'Content-Type: application/json' );
+
+ if ( ! wp_verify_nonce( $_REQUEST['nonce'], 'jetpack_comic_upload_nonce' ) )
+ die( json_encode( array( 'error' => __( 'Invalid or expired nonce.', 'jetpack' ) ) ) );
+
+ $_POST['action'] = 'wp_handle_upload';
+
+ $image_id_arr = array();
+ $image_error_arr = array();
+
+ $i = 0;
+
+ while ( isset( $_FILES['image_' . $i ] ) ) {
+ // Create attachment for the image.
+ $image_id = media_handle_upload( "image_$i", 0 );
+
+ if ( is_wp_error( $image_id ) ) {
+ $error = array( $image_id, $image_id->get_error_message() );
+ array_push( $image_error_arr, $error );
+ } else {
+ array_push( $image_id_arr, $image_id );
+ }
+
+ $i++;
+ }
+
+ if ( count( $image_id_arr ) == 0 ) {
+ // All image uploads failed.
+ $rv = array( 'error' => '' );
+
+ foreach ( $image_error_arr as $error )
+ $rv['error'] .= $error[1] . "\n";
+ }
+ else {
+ if ( count( $image_id_arr ) == 1 ) {
+ $image_id = $image_id_arr[0];
+
+ // Get the image
+ $image_src = get_the_guid( $image_id );
+ $image_dims = wp_get_attachment_image_src( $image_id, 'full' );
+
+ // Take off 10px of width to account for padding and border. @todo make this smarter.
+ if ( $content_width )
+ $image_width = $content_width - 10;
+ else
+ $image_width = $image_dims[1] - 10;
+
+ $post_content = '<a href="' . esc_attr( $image_src ) .'"><img src="' . esc_attr( $image_src ) . '?w=' . esc_attr( $image_width ) . '" alt="' . esc_attr( $_FILES['image_0']['name'] ) . '" class="size-full wp-image alignnone" id="i-' . esc_attr( $image_id ) . '" data-filename="' . esc_attr( $_FILES['image_0']['name'] ) . '" /></a>';
+ }
+ else {
+ $post_content = '[gallery ids="' . esc_attr( implode( ',', $image_id_arr ) ) . '"]';
+ }
+
+ // Create a new post with the image(s)
+ $post_id = wp_insert_post( array(
+ 'post_content' => $post_content,
+ 'post_type' => 'jetpack-comic',
+ 'post_status' => 'draft',
+ ),
+ true
+ );
+
+ if ( is_wp_error( $post_id, 'WP_Error' ) ) {
+ // Failed to create the post.
+ $rv = array( 'error' => $post_id->get_error_message() );
+
+ // Delete the uploaded images.
+ foreach ( $image_id_arr as $image_id ) {
+ wp_delete_post( $image_id, true );
+ }
+ }
+ else {
+ foreach ( $image_id_arr as $image_id ) {
+ wp_update_post( array(
+ 'ID' => $image_id,
+ 'post_parent' => $post_id
+ ) );
+ }
+
+ if ( current_theme_supports( 'post-thumbnails' ) && count( $image_id_arr ) == 1 )
+ set_post_thumbnail( $post_id, $image_id_arr[0] );
+
+ $rv = array( 'url' => add_query_arg( array( 'post' => $post_id, 'action' => 'edit' ), admin_url( 'post.php' ) ) );
+ }
+ }
+
+ die( json_encode( $rv ) );
+ }
+
+ public function add_posts_to_loop( $query ) {
+ // Add comic posts to the tag and category pages.
+ if ( ! is_admin() && $query->is_main_query() && ( $query->is_category() || $query->is_tag() ) ) {
+ $post_types = $query->get( 'post_type' );
+
+ if ( ! $post_types || 'post' == $post_types )
+ $post_types = array( 'post', self::POST_TYPE );
+ else if ( is_array( $post_types ) )
+ $post_types[] = self::POST_TYPE;
+
+ $query->set( 'post_type', $post_types );
+ }
+
+ return $query;
+ }
+
+}
+
+add_action( 'init', array( 'Jetpack_Comic', 'init' ) );
+
+
+function comics_welcome_email( $welcome_email, $blog_id, $user_id, $password, $title, $meta ) {
+ if ( ( isset( $meta['vertical'] ) && 'comics' == $meta['vertical'] ) || has_blog_sticker( 'vertical-comics', $blog_id ) ) {
+ return __( "Welcome! Ready to publish your first strip?
+
+Your webcomic's new site is ready to go. Get started by <a href=\"BLOG_URLwp-admin/customize.php#title\">setting your comic's title and tagline</a> so your readers know what it's all about.
+
+Looking for more help with setting up your site? Check out the WordPress.com <a href=\"http://learn.wordpress.com/\">beginner's tutorial</a> and the <a href=\"http://en.support.wordpress.com/comics/\">guide to comics on WordPress.com</a>. Dive right in by <a href=\"BLOG_URLwp-admin/customize.php#title\">publishing your first strip!</a>
+
+Lots of laughs,
+The WordPress.com Team", 'jetpack' );
+ }
+
+ return $welcome_email;
+}
+
+add_filter( 'update_welcome_email_pre_replacement', 'comics_welcome_email', 10, 6 );
+
diff --git a/plugins/jetpack/modules/custom-post-types/comics/admin.css b/plugins/jetpack/modules/custom-post-types/comics/admin.css
new file mode 100644
index 00000000..4e1ac316
--- /dev/null
+++ b/plugins/jetpack/modules/custom-post-types/comics/admin.css
@@ -0,0 +1,8 @@
+
+#adminmenu #menu-posts-jetpack-comic .menu-icon-post div.wp-menu-image:before {
+ content: '\f125';
+}
+
+.edit-php .column-preview-jetpack-comic {
+ width: 150px;
+}
diff --git a/plugins/jetpack/modules/custom-post-types/comics/comics.css b/plugins/jetpack/modules/custom-post-types/comics/comics.css
new file mode 100644
index 00000000..6e5cf110
--- /dev/null
+++ b/plugins/jetpack/modules/custom-post-types/comics/comics.css
@@ -0,0 +1,30 @@
+#jetpack-comic-drop-zone .dragging, #jetpack-comic-drop-zone .uploading {
+ margin-top: 200px;
+ display: none;
+}
+
+/* Display the appropriate loading message for each upload state. */
+body.dragging #jetpack-comic-drop-zone .dragging, body.uploading #jetpack-comic-drop-zone .uploading {
+ display: block;
+}
+
+body.uploading #jetpack-comic-drop-zone .uploading .spinner {
+ display: inline-block;
+ width: 60px;
+}
+
+/* Add the drop zone overlay. */
+body.dragging #jetpack-comic-drop-zone, body.uploading #jetpack-comic-drop-zone {
+ background: rgba( 0, 86, 132, 0.9 );
+ border: 1px dashed #fff;
+ color: #fff;
+ display: block;
+ font-size: 30px;
+ position: fixed;
+ top: 10px;
+ left: 10px;
+ right: 10px;
+ bottom: 10px;
+ text-align: center;
+ z-index: 99999;
+} \ No newline at end of file
diff --git a/plugins/jetpack/modules/custom-post-types/comics/comics.js b/plugins/jetpack/modules/custom-post-types/comics/comics.js
new file mode 100644
index 00000000..2576c019
--- /dev/null
+++ b/plugins/jetpack/modules/custom-post-types/comics/comics.js
@@ -0,0 +1,118 @@
+jQuery( function ( $ ) {
+ /**
+ * Enable front-end uploading of images for Comics users.
+ */
+ var Jetpack_Comics = {
+ init : function () {
+ $( document ).on( 'dragover.jetpack-comics', 'body, #jetpack-comic-drop-zone', this.onDragOver );
+ $( document ).on( 'dragleave.jetpack-comics', 'body, #jetpack-comic-drop-zone', this.onDragLeave );
+ $( document ).on( 'drop.jetpack-comics', 'body, #jetpack-comic-drop-zone', this.onDrop );
+
+ $( 'body' ).append( $( '<div id="jetpack-comic-drop-zone"><p class="dragging" /><p class="uploading" /></div>' ) );
+ $( '#jetpack-comic-drop-zone' )
+ .find( '.dragging' )
+ .text( Jetpack_Comics_Options.labels.dragging )
+ .end()
+ .find( '.uploading' )
+ .text( Jetpack_Comics_Options.labels.uploading )
+ .prepend( $( '<span class="spinner"/>' ) );
+
+ if ( ! ( 'FileReader' in window && 'File' in window ) ) {
+ $( '#jetpack-comic-drop-zone .dragging' ).text( Jetpack_Comics_Options.labels.unsupported );
+ $( document ).off( 'drop.jetpack-comics' ).on( 'drop.jetpack-comics', 'body, #jetpack-comic-drop-zone', this.onDragLeave );
+ }
+ },
+
+ /**
+ * Only upload image files.
+ */
+ filterImageFiles : function ( files ) {
+ var validFiles = [];
+
+ for ( var i = 0, _len = files.length; i < _len; i++ ) {
+ if ( files[i].type.match( /^image\//i ) ) {
+ validFiles.push( files[i] );
+ }
+ }
+
+ return validFiles;
+ },
+
+ dragTimeout : null,
+
+ onDragOver: function ( event ) {
+ event.preventDefault();
+
+ clearTimeout( Jetpack_Comics.dragTimeout );
+
+ $( 'body' ).addClass( 'dragging' );
+ },
+
+ onDragLeave: function ( event ) {
+ clearTimeout( Jetpack_Comics.dragTimeout );
+
+ // In Chrome, the screen flickers because we're moving the drop zone in front of 'body'
+ // so the dragover/dragleave events happen frequently.
+ Jetpack_Comics.dragTimeout = setTimeout( function () {
+ $( 'body' ).removeClass( 'dragging' );
+ }, 100 );
+ },
+
+ onDrop: function ( event ) {
+ event.preventDefault();
+ event.stopPropagation();
+
+ // recent chrome bug requires this, see stackoverflow thread: http://bit.ly/13BU7b5
+ event.originalEvent.stopPropagation();
+ event.originalEvent.preventDefault();
+
+ var files = Jetpack_Comics.filterImageFiles( event.originalEvent.dataTransfer.files );
+
+ $( 'body' ).removeClass( 'dragging' );
+
+ if ( files.length == 0 ) {
+ alert( Jetpack_Comics_Options.labels.invalidUpload );
+ return;
+ }
+
+ $( 'body' ).addClass( 'uploading' );
+
+ var formData = new FormData();
+
+ for ( var i = 0, fl = files.length; i < fl; i++ ) {
+ formData.append( 'image_' + i, files[ i ] ); // won't work as image[]
+ }
+
+ $( '#jetpack-comic-drop-zone .uploading .spinner' ).spin();
+
+ $.ajax( {
+ url: Jetpack_Comics_Options.writeURL + '&nonce=' + Jetpack_Comics_Options.nonce,
+ data: formData,
+ processData: false,
+ contentType: false,
+ type: 'POST',
+ dataType: 'json',
+ xhrFields: {
+ withCredentials: true
+ }
+ } )
+ .done( function( data ) {
+ $( '#jetpack-comic-drop-zone .uploading' ).text( Jetpack_Comics_Options.labels.processing );
+
+ if ( 'url' in data ) {
+ document.location.href = data.url;
+ }
+ else if ( 'error' in data ) {
+ alert( data.error );
+
+ $( 'body' ).removeClass( 'uploading' );
+ }
+ } )
+ .fail( function ( req ) {
+ alert( Jetpack_Comics_Options.labels.error );
+ } );
+ }
+ };
+
+ Jetpack_Comics.init();
+} );
diff --git a/plugins/jetpack/modules/custom-post-types/comics/rtl/comics-rtl.css b/plugins/jetpack/modules/custom-post-types/comics/rtl/comics-rtl.css
new file mode 100644
index 00000000..773fd99d
--- /dev/null
+++ b/plugins/jetpack/modules/custom-post-types/comics/rtl/comics-rtl.css
@@ -0,0 +1,32 @@
+/* This file was automatically generated on Jul 09 2013 05:18:56 */
+
+#jetpack-comic-drop-zone .dragging, #jetpack-comic-drop-zone .uploading {
+ margin-top: 200px;
+ display: none;
+}
+
+/* Display the appropriate loading message for each upload state. */
+body.dragging #jetpack-comic-drop-zone .dragging, body.uploading #jetpack-comic-drop-zone .uploading {
+ display: block;
+}
+
+body.uploading #jetpack-comic-drop-zone .uploading .spinner {
+ display: inline-block;
+ width: 60px;
+}
+
+/* Add the drop zone overlay. */
+body.dragging #jetpack-comic-drop-zone, body.uploading #jetpack-comic-drop-zone {
+ background: rgba( 0, 86, 132, 0.9 );
+ border: 1px dashed #fff;
+ color: #fff;
+ display: block;
+ font-size: 30px;
+ position: fixed;
+ top: 10px;
+ right: 10px;
+ left: 10px;
+ bottom: 10px;
+ text-align: center;
+ z-index: 99999;
+} \ No newline at end of file
diff --git a/plugins/jetpack/modules/custom-post-types/testimonial.php b/plugins/jetpack/modules/custom-post-types/testimonial.php
new file mode 100644
index 00000000..6cd6b9d7
--- /dev/null
+++ b/plugins/jetpack/modules/custom-post-types/testimonial.php
@@ -0,0 +1,286 @@
+<?php
+/*
+ * Plugin Name: Jetpack Testimonial
+ * Plugin URI:
+ * Author: Automattic
+ * Version: 0.1
+ * License: GPL v2 or later
+ * Text Domain: jetpack
+ * Domain Path: /languages/
+ */
+
+class Jetpack_Testimonial {
+ const TESTIMONIAL_POST_TYPE = 'jetpack-testimonial';
+
+ var $version = '0.1';
+
+ static function init() {
+ static $instance = false;
+
+ if ( ! $instance )
+ $instance = new Jetpack_Testimonial;
+
+ return $instance;
+ }
+
+ /**
+ * Conditionally hook into WordPress.
+ *
+ * Themes must declare that they support this module by adding
+ * add_theme_support( 'jetpack-testimonial' ); during after_setup_theme.
+ *
+ * If no theme support is found there is no need to hook into
+ * WordPress. We'll just return early instead.
+ */
+ function __construct() {
+ // Return early if theme does not support Jetpack Testimonial.
+ if ( ! current_theme_supports( self::TESTIMONIAL_POST_TYPE ) )
+ return;
+
+ $this->register_post_types();
+ add_filter( 'enter_title_here', array( $this, 'change_default_title' ) );
+ add_filter( 'manage_jetpack-testimonial_posts_columns', array( $this, 'edit_title_column_label' ) );
+ add_filter( 'post_updated_messages', array( $this, 'updated_messages' ) );
+ add_action( 'customize_register', array( $this, 'customize_register' ) );
+
+ $num_testimonials = self::count_testimonials();
+ if ( ! empty( $num_testimonials ) )
+ add_action( 'admin_menu', array( $this, 'add_customize_page' ) );
+ }
+
+ /* Setup */
+ function register_post_types() {
+ register_post_type( self::TESTIMONIAL_POST_TYPE, array(
+ 'description' => __( 'Customer Testimonials', 'jetpack' ),
+ 'labels' => array(
+ 'name' => esc_html__( 'Testimonials', 'jetpack' ),
+ 'singular_name' => esc_html__( 'Testimonial', 'jetpack' ),
+ 'menu_name' => esc_html__( 'Testimonials', 'jetpack' ),
+ 'all_items' => esc_html__( 'All Testimonials', 'jetpack' ),
+ 'add_new' => esc_html__( 'Add New', 'jetpack' ),
+ 'add_new_item' => esc_html__( 'Add New Testimonial', 'jetpack' ),
+ 'edit_item' => esc_html__( 'Edit Testimonial', 'jetpack' ),
+ 'new_item' => esc_html__( 'New Testimonial', 'jetpack' ),
+ 'view_item' => esc_html__( 'View Testimonial', 'jetpack' ),
+ 'search_items' => esc_html__( 'Search Testimonials', 'jetpack' ),
+ 'not_found' => esc_html__( 'No Testimonials found', 'jetpack' ),
+ 'not_found_in_trash' => esc_html__( 'No Testimonials found in Trash', 'jetpack' ),
+ ),
+ 'supports' => array(
+ 'title',
+ 'editor',
+ 'thumbnail',
+ 'page-attributes',
+ ),
+ 'rewrite' => array(
+ 'slug' => 'testimonial',
+ 'with_front' => false,
+ 'feeds' => false,
+ 'pages' => false,
+ ),
+ 'public' => true,
+ 'show_ui' => true, // set to false to replace with custom UI
+ 'menu_position' => 20, // below Pages
+ 'capability_type' => 'page',
+ 'map_meta_cap' => true,
+ 'has_archive' => true,
+ 'query_var' => 'testimonial',
+ ) );
+ }
+
+ /**
+ * Change ‘Enter Title Here’ text for the Testimonial.
+ */
+ function change_default_title( $title ) {
+ $screen = get_current_screen();
+
+ if ( 'jetpack-testimonial' == $screen->post_type )
+ $title = esc_html__( "Enter the customer's name here", 'jetpack' );
+
+ return $title;
+ }
+
+ /**
+ * Change ‘Title’ column label on all Testimonials page.
+ */
+ function edit_title_column_label( $columns ) {
+ $columns['title'] = esc_html__( 'Customer Name', 'jetpack' );
+
+ return $columns;
+ }
+
+ /**
+ * Update messages for the Testimonial admin.
+ */
+ function updated_messages( $messages ) {
+ global $post;
+
+ $messages['jetpack-testimonial'] = array(
+ 0 => '', // Unused. Messages start at index 1.
+ 1 => sprintf( __( 'Testimonial updated. <a href="%s">View testimonial</a>', 'jetpack'), esc_url( get_permalink( $post->ID ) ) ),
+ 2 => esc_html__( 'Custom field updated.', 'jetpack' ),
+ 3 => esc_html__( 'Custom field deleted.', 'jetpack' ),
+ 4 => esc_html__( 'Testimonial updated.', 'jetpack' ),
+ /* translators: %s: date and time of the revision */
+ 5 => isset( $_GET['revision'] ) ? sprintf( esc_html__( 'Testimonial restored to revision from %s', 'jetpack'), wp_post_revision_title( (int) $_GET['revision'], false ) ) : false,
+ 6 => sprintf( __( 'Testimonial published. <a href="%s">View testimonial</a>', 'jetpack' ), esc_url( get_permalink( $post->ID ) ) ),
+ 7 => esc_html__( 'Testimonial saved.', 'jetpack' ),
+ 8 => sprintf( __( 'Testimonial submitted. <a target="_blank" href="%s">Preview testimonial</a>', 'jetpack'), esc_url( add_query_arg( 'preview', 'true', get_permalink( $post->ID ) ) ) ),
+ 9 => sprintf( __( 'Testimonial scheduled for: <strong>%1$s</strong>. <a target="_blank" href="%2$s">Preview testimonial</a>', 'jetpack' ),
+ // translators: Publish box date format, see http://php.net/date
+ date_i18n( __( 'M j, Y @ G:i', 'jetpack' ), strtotime( $post->post_date ) ), esc_url( get_permalink($post->ID) ) ),
+ 10 => sprintf( __( 'Testimonial draft updated. <a target="_blank" href="%s">Preview testimonial</a>', 'jetpack' ), esc_url( add_query_arg( 'preview', 'true', get_permalink( $post->ID ) ) ) ),
+ );
+
+ return $messages;
+ }
+
+
+ function set_testimonial_option() {
+ $testimonials_option = get_option( 'jetpack_testimonial' );
+
+ $testimonials = wp_count_posts( 'jetpack-testimonial' );
+ $published_testimonials = $testimonials->publish;
+
+ update_option( 'jetpack_testimonial', $published_testimonials );
+ }
+
+ function count_testimonials() {
+ $testimonials = get_transient( 'jetpack-testimonial-count-cache' );
+
+ if ( false === $testimonials ) {
+ $testimonials = (int) wp_count_posts( 'jetpack-testimonial' )->publish;
+
+ if ( ! empty( $testimonials ) ) {
+ set_transient( 'jetpack-testimonial-count-cache', $testimonials, 60*60*12 );
+ }
+ }
+
+ return $testimonials;
+ }
+
+ /**
+ * Adds a submenu link to the Customizer.
+ */
+ function add_customize_page() {
+ add_submenu_page(
+ 'edit.php?post_type=jetpack-testimonial',
+ esc_html__( 'Customize Testimonials Archive', 'jetpack' ),
+ esc_html__( 'Customize', 'jetpack' ),
+ 'edit_theme_options',
+ add_query_arg( array( 'url' => urlencode( home_url( 'testimonial' ) ) ), 'customize.php' ) . '#accordion-section-jetpack_testimonials'
+ );
+ }
+
+ /**
+ * Adds testimonial section to the Customizer.
+ */
+ function customize_register( $wp_customize ) {
+ jetpack_testimonial_custom_control_classes();
+
+ $wp_customize->add_section( 'jetpack_testimonials', array(
+ 'title' => esc_html__( 'Testimonials', 'jetpack' ),
+ 'theme_supports' => 'jetpack-testimonial',
+ ) );
+
+ $wp_customize->add_setting( 'jetpack_testimonials[page-title]', array(
+ 'default' => esc_html__( 'Testimonials', 'jetpack' ),
+ 'sanitize_callback' => array( 'Jetpack_Testimonial_Title_Control', 'sanitize_content' ),
+ 'sanitize_js_callback' => array( 'Jetpack_Testimonial_Title_Control', 'sanitize_content' ),
+ ) );
+ $wp_customize->add_control( 'jetpack_testimonials[page-title]', array(
+ 'section' => 'jetpack_testimonials',
+ 'label' => esc_html__( 'Testimonial Page Title', 'jetpack' ),
+ 'type' => 'text',
+ ) );
+
+ $wp_customize->add_setting( 'jetpack_testimonials[page-content]', array(
+ 'default' => '',
+ 'sanitize_callback' => array( 'Jetpack_Testimonial_Textarea_Control', 'sanitize_content' ),
+ 'sanitize_js_callback' => array( 'Jetpack_Testimonial_Textarea_Control', 'sanitize_content' ),
+ ) );
+ $wp_customize->add_control( new Jetpack_Testimonial_Textarea_Control( $wp_customize, 'jetpack_testimonials[page-content]', array(
+ 'section' => 'jetpack_testimonials',
+ 'settings' => 'jetpack_testimonials[page-content]',
+ 'label' => esc_html__( 'Testimonial Page Content', 'jetpack' ),
+ ) ) );
+
+ if ( current_theme_supports( 'post-thumbnails' ) ) {
+ $wp_customize->add_setting( 'jetpack_testimonials[featured-image]', array(
+ 'default' => '',
+ 'sanitize_callback' => array( 'Jetpack_Testimonial_Image_Control', 'attachment_guid_to_id' ),
+ 'sanitize_js_callback' => array( 'Jetpack_Testimonial_Image_Control', 'attachment_guid_to_id' ),
+ ) );
+ $wp_customize->add_control( new Jetpack_Testimonial_Image_Control( $wp_customize, 'jetpack_testimonials[featured-image]', array(
+ 'section' => 'jetpack_testimonials',
+ 'label' => esc_html__( 'Testimonial Page Featured Image', 'jetpack' ),
+ ) ) );
+ }
+ }
+}
+
+function jetpack_testimonial_custom_control_classes() {
+ class Jetpack_Testimonial_Title_Control extends WP_Customize_Control {
+ public function sanitize_content( $value ) {
+ if ( '' != $value )
+ $value = trim( convert_chars( wptexturize( $value ) ) );
+
+ return $value;
+ }
+ }
+
+ class Jetpack_Testimonial_Textarea_Control extends WP_Customize_Control {
+ public $type = 'textarea';
+
+ public function render_content() {
+ ?>
+ <label>
+ <span class="customize-control-title"><?php echo esc_html( $this->label ); ?></span>
+ <textarea rows="5" style="width:100%;" <?php $this->link(); ?>><?php echo esc_textarea( $this->value() ); ?></textarea>
+ </label>
+ <?php
+ }
+
+ public function sanitize_content( $value ) {
+ if ( ! empty( $value ) )
+ $value = apply_filters( 'the_content', $value );
+
+ $value = preg_replace( '@<div id="jp-post-flair"([^>]+)?>(.+)?</div>@is', '', $value ); // Strip WPCOM and Jetpack post flair if included in content
+
+ return $value;
+ }
+ }
+
+ /**
+ * Need to extend WP_Customize_Image_Control to return attachment ID instead of url
+ */
+ class Jetpack_Testimonial_Image_Control extends WP_Customize_Image_Control {
+ public $context = 'custom_image';
+
+ public function __construct( $manager, $id, $args ) {
+ $this->get_url = array( $this, 'get_img_url' );
+ parent::__construct( $manager, $id, $args );
+ }
+
+ public function get_img_url( $attachment_id = 0 ) {
+ if ( is_numeric( $attachment_id ) && wp_attachment_is_image( $attachment_id ) )
+ list( $image, $x, $y ) = wp_get_attachment_image_src( $attachment_id );
+
+ return ! empty( $image ) ? $image : $attachment_id;
+ }
+
+ public function attachment_guid_to_id( $value ) {
+ if ( is_numeric( $value ) )
+ return $value;
+
+ $matches = get_posts( array( 'post_type' => 'attachment', 'guid' => $value ) );
+
+ if ( empty( $matches ) )
+ return false;
+
+ return $matches[0]->ID; // this is the match we want
+ }
+ }
+}
+
+add_action( 'init', array( 'Jetpack_Testimonial', 'init' ) );
diff --git a/plugins/jetpack/modules/custom-post-types/testimonials.php b/plugins/jetpack/modules/custom-post-types/testimonials.php
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/plugins/jetpack/modules/custom-post-types/testimonials.php
diff --git a/plugins/jetpack/modules/debug.php b/plugins/jetpack/modules/debug.php
new file mode 100644
index 00000000..88c13090
--- /dev/null
+++ b/plugins/jetpack/modules/debug.php
@@ -0,0 +1,6 @@
+<?php
+/**
+ * Deprecated. No longer needed.
+ *
+ * @package Jetpack
+ */
diff --git a/plugins/jetpack/modules/enhanced-distribution.php b/plugins/jetpack/modules/enhanced-distribution.php
index daad2eb2..42495201 100644
--- a/plugins/jetpack/modules/enhanced-distribution.php
+++ b/plugins/jetpack/modules/enhanced-distribution.php
@@ -5,6 +5,7 @@
* Sort Order: 100
* First Introduced: 1.2
* Requires Connection: Yes
+ * Auto Activate: Public
*/
Jetpack_Sync::sync_posts( __FILE__ );
@@ -17,7 +18,7 @@ function jetpack_enhanced_distribution_activate() {
// In case it's active prior to upgrading to 1.9
function jetpack_enhanced_distribution_before_activate_default_modules() {
- $old_version = Jetpack::get_option( 'old_version' );
+ $old_version = Jetpack_Options::get_option( 'old_version' );
list( $old_version ) = explode( ':', $old_version );
if ( version_compare( $old_version, '1.9-something', '>=' ) ) {
diff --git a/plugins/jetpack/modules/gplus-authorship.php b/plugins/jetpack/modules/gplus-authorship.php
new file mode 100644
index 00000000..aae851b7
--- /dev/null
+++ b/plugins/jetpack/modules/gplus-authorship.php
@@ -0,0 +1,206 @@
+<?php
+/**
+* Module Name: Google+ Profile
+* Module Description: Show a link to your Google+ in the sharing area of your posts and add your blog URL to your Google+ profile.
+* Sort Order: 10
+* First Introduced: 2.5
+* Requires Connection: Yes
+* Auto Activate: Yes
+*/
+function jetpack_init_gplus_authorship() {
+ new GPlus_Authorship;
+}
+add_action( 'init', 'jetpack_init_gplus_authorship' );
+
+class GPlus_Authorship {
+
+ function __construct() {
+ $this->in_jetpack = ( defined( 'IS_WPCOM' ) && IS_WPCOM ) ? false : true;
+ if ( $this->in_jetpack ) {
+ require "gplus-authorship/admin/ui.php";
+ $gplus_admin = new GPlus_Authorship_Admin;
+ add_action( 'save_post', array( 'GPlus_Authorship_Admin', 'save_post_meta' ) );
+ add_action( 'wp_ajax_save_gplus_profile_data', array( $this, 'save_profile_data' ) );
+ add_filter( 'the_content', array( $this, 'post_output_wrapper' ), 22, 1 );
+ } else {
+ add_filter( 'post_flair', array( $this, 'post_output_wrapper' ), 22 );
+ }
+ add_action( 'wp_head', array( $this, 'link_tag_styles_and_scripts' ) );
+ add_filter( 'the_author', array( $this, 'overwrite_the_author' ) );
+ add_filter( 'the_content', array( $this, 'overwrite_rel_attrs' ) );
+ }
+
+ function show_on_this_post() {
+ global $post;
+ $show = apply_filters( 'gplus_authorship_show', true, $post );
+ $author = $this->information( $post->post_author );
+ if ( empty( $author ) )
+ $show = false;
+ $meta = get_post_meta( $post->ID, 'gplus_authorship_disabled', true );
+ if ( isset( $meta ) && true == $meta )
+ $show = false;
+ return $show;
+ }
+
+ function information( $author ) {
+ $authors = get_option( 'gplus_authors', array() );
+ return ( empty( $authors[ $author ] ) ? array() : $authors[ $author ] );
+ }
+
+ /**
+ * Both overwrite_rel_attrs and rel_callback remove 'author' from the rel attribute of a link if it is found
+ * and if the post is being viewed on a single page, with G+ authorship enabled.
+ * based on prior art from privatize_link in wp-content/mu-plugins/private-blog.php
+ */
+ function overwrite_rel_attrs( $content ) {
+ if ( !is_single() ) // don't bother unless we are on a page where the G+ authorship link is actually being displayed
+ return $content;
+ if ( !$this->show_on_this_post() ) // G+ isn't enabled
+ return $content;
+ return preg_replace_callback( '#<a\s+[^>]+>#i', array( $this, 'rel_callback' ), $content );
+ }
+
+ function rel_callback( $link ) {
+ $link = $link[0]; // preg replace returns as array
+
+ $dom = new DOMDocument;
+ $link = mb_convert_encoding( $link, 'HTML-ENTITIES', 'UTF-8' );
+ @$dom->loadHTML( "<html><body>$link</a></body></html>" );
+ $link_node = false;
+ foreach ( $dom->childNodes as $child ) {
+ if ( XML_ELEMENT_NODE === $child->nodeType && 'html' === strtolower( $child->tagName ) ) {
+ $link_node = $child->firstChild->firstChild;
+ break;
+ }
+ }
+
+ // Don't bother if it's not actually a link (pointing to another document) or if there is no rel attribute.
+ if ( !$link_node )
+ return $link;
+ if ( !$link_node->hasAttribute( 'href' ) )
+ return $link;
+ if ( !$link_node->hasAttribute( 'rel' ) )
+ return $link;
+
+ $rels = explode( ' ', $link_node->getAttribute( 'rel' ) );
+
+ // delete 'author' from the list
+ if ( ( $key = array_search( 'author', $rels ) ) !== false ) {
+ unset( $rels[$key] );
+ }
+
+ // if there was more then one part of the attribute, set the new value, otherwise just get rid of the attribute all together
+ if ( count( $rels ) > 0 )
+ $link_node->setAttribute( 'rel', join( ' ', $rels ) );
+ else
+ $link_node->removeAttribute( 'rel' );
+
+ $link = $dom->saveXML( $link_node );
+ $link = rtrim( $link, '/>' ) . '>';
+ return $link;
+ }
+
+ /**
+ * Jetpack Only
+ */
+ function save_profile_data() {
+ global $current_user;
+ check_ajax_referer( 'gplus-connect', 'state' );
+ if ( !is_numeric( $_POST['id'] ) )
+ return;
+ $connections = get_option( 'gplus_authors', array() );
+ $connections[ $current_user->ID ]['name'] = $_POST['name'];
+ $connections[ $current_user->ID ]['id'] = $_POST['id'];
+ $connections[ $current_user->ID ]['url'] = esc_url_raw( $_POST['url'] );
+ $connections[ $current_user->ID ]['profile_image'] = esc_url_raw( $_POST['profile_image'] );
+ update_option( 'gplus_authors', $connections );
+ }
+
+ /**
+ * Google+ insits we don't have two different bylines..
+ * so we will display their G+ username in the byline here too
+ */
+ function overwrite_the_author( $author_name ) {
+ global $post;
+
+ if ( !$this->show_on_this_post() )
+ return $author_name;
+
+ $author = $this->information( $post->post_author );
+ return esc_html( $author['name'] );
+ }
+
+ function byline( $post ) {
+ $author = $this->information( $post->post_author );
+ $image = '<img src="' . esc_url( $author['profile_image'] ) . '?sz=40" alt="" width="20" height="20" align="absmiddle" /> ';
+ $byline = sprintf( '<a href="%1$s">%2$s</a><a rel="author" href="%1$s" class="gplus-profile">%3$s</a>', esc_url( $author['url'] ), $image, esc_html( $author['name'] ) );
+ return apply_filters( 'gplus_authorship_byline', $byline, $post );
+ }
+
+ function link_tag_styles_and_scripts() {
+ if ( !is_single() )
+ return;
+ if ( get_post_type() != 'post' )
+ return;
+ if ( !$this->show_on_this_post() )
+ return;
+
+ global $post;
+ $author = $this->information( $post->post_author );
+ echo '<link rel="author" href="'. esc_url( $author['url'] ) .'" title="' . esc_attr( $author['name'] ) . ' ' . __( 'on Google+', 'jetpack' ) .'" /> ' . "\n";
+ if ( $this->in_jetpack )
+ $css = plugins_url( 'gplus-authorship/style.css', __FILE__ );
+ else
+ $css = plugins_url( 'gplus/style.css', __FILE__ );
+ wp_enqueue_style( 'gplus', $css );
+ wp_enqueue_script( 'plusone', '//apis.google.com/js/plusone.js' );
+ }
+
+ function follow_button( $post ) {
+ $author = $this->information( $post->post_author );
+ return '<span class="g-follow-wrapper"><span class="g-follow" data-href="' . esc_url( $author['url'] ) . '" data-rel="author" data-height="15"></span></span>';
+ }
+
+ function post_output_wrapper( $text = '', $echo = false ) {
+ global $post, $wp_current_filter;
+
+ if ( !is_single() )
+ return $text;
+ if ( get_post_type() != 'post' )
+ return $text;
+ $author = $this->information( $post->post_author );
+ if ( empty( $author ) )
+ return $text;
+
+ // Don't allow G+ to be added to the_content more than once (prevent infinite loops)
+ $done = false;
+ foreach ( $wp_current_filter as $filter ) {
+ if ( 'the_content' == $filter ) {
+ if ( $done )
+ return $text;
+ else
+ $done = true;
+ }
+ }
+
+ if ( !$this->show_on_this_post())
+ return $text;
+
+ $output = '';
+ $output .= '<div class="sharedaddy sd-block sd-social sd-gplus">';
+ $output .= '<h3 class="sd-title">' . __( 'Google+', 'jetpack' ) . '</h3>';
+ $output .= '<div class="sd-content">';
+ $output .= $this->byline( $post );
+ $output .= $this->follow_button( $post );
+ $output .= '</div>';
+ $output .= '</div>';
+
+ if ( $echo )
+ echo $text . $output;
+ else
+ return $text . $output;
+ }
+
+}
+
+?>
diff --git a/plugins/jetpack/modules/gplus-authorship/admin/connect.js b/plugins/jetpack/modules/gplus-authorship/admin/connect.js
new file mode 100644
index 00000000..72798ba2
--- /dev/null
+++ b/plugins/jetpack/modules/gplus-authorship/admin/connect.js
@@ -0,0 +1,42 @@
+function googlePlusSignInCallback( authResult ) {
+ var blogID = jQuery( '#current-blog-id' ).attr( 'data-value' );
+ if ( authResult['code'] ) {
+ jQuery.ajax( {
+ type: 'POST',
+ url: 'index.php?page=gplus-authorship&blog_id=' + blogID + '&store_token=' + authResult['code'] + '&state=' + state,
+ contentType: 'application/json; charset=utf-8',
+ processData: false,
+
+ success: function(result) {
+ if ( 'undefined' == typeof result.error ) {
+ googlePlusSendToParent( { success: true } );
+ } else {
+ googlePlusSendToParent( { error: result.error } );
+ }
+ },
+
+ error: function( result ) {
+ if ( 'undefined' != typeof result.error ) {
+ googlePlusSendToParent( { error: result.error } );
+ } else {
+ googlePlusSendToParent( { error: 'unknown' } );
+ }
+ }
+ } );
+ } else if ( authResult['error'] ) {
+ googlePlusSendToParent( { error: authResult['error'] } );
+ } else {
+ googlePlusSendToParent( { error: 'unknown' } );
+ }
+}
+
+ function googlePlusSendToParent( data ) {
+ if ( 'immediate_failed' == data.error )
+ return;
+ pm( {
+ target: window.parent,
+ type: 'googlePlusSignInMessage',
+ data: data,
+ origin: '*'
+ } );
+} \ No newline at end of file
diff --git a/plugins/jetpack/modules/gplus-authorship/admin/listener.js b/plugins/jetpack/modules/gplus-authorship/admin/listener.js
new file mode 100644
index 00000000..01005a2b
--- /dev/null
+++ b/plugins/jetpack/modules/gplus-authorship/admin/listener.js
@@ -0,0 +1,57 @@
+jQuery( function( $ ) {
+
+ pm.bind( 'googlePlusSignInMessage', function( message ) {
+ if ( 'undefined' != typeof message.error)
+ GooglePlusMessageHandler.error( message.error );
+ else if ( 'undefined' != typeof message.success && 'undefined' != typeof message.result )
+ GooglePlusMessageHandler.success( message.result );
+ else
+ GooglePlusMessageHandler.unknownMessage( message );
+ } );
+
+ var GooglePlusMessageHandler = {
+
+ outputContainer: '#result',
+
+ success: function( result ) {
+ $.post( './admin-ajax.php',
+ {
+ action: 'save_gplus_profile_data',
+ name: result.name,
+ url: result.url,
+ profile_image: result.profile_image,
+ id: result.id,
+ state: result.state
+ }, function() {
+ $( GooglePlusMessageHandler.outputContainer ).text( GPlusL10n.connected );
+ window.location.href = 'options-general.php?page=sharing&r=' + Math.round( Math.random()*100000 ) + '#gplus';
+ }
+ );
+ },
+
+ error: function( error ) {
+ if ( 'unknown' == error ) {
+ $( GooglePlusMessageHandler.outputContainer ).text( GPlusL10n.unknownError );
+ } else if ( 'access_denied' == error ) {
+ $( GooglePlusMessageHandler.outputContainer ).text( GPlusL10n.accessDenied );
+ } else {
+ $( GooglePlusMessageHandler.outputContainer ).text( error );
+ }
+ },
+
+ unknownMessage: function( message ) {
+ console.log( 'DEBUG: An unknown message was passed via postMessage:' );
+ console.log( message );
+ GooglePlusMessageHandler.error( 'unknown' );
+ },
+
+ };
+
+ $( '#disconnect-gplus' ).click( function() {
+ var ays = confirm( 'Are you sure you want to disconnect your Google+ profile? If you have any Publicize accounts connected to this profile they will also be disconnected.' );
+ if ( ! ays ) {
+ return false;
+ }
+ } );
+
+} );
diff --git a/plugins/jetpack/modules/gplus-authorship/admin/style.css b/plugins/jetpack/modules/gplus-authorship/admin/style.css
new file mode 100644
index 00000000..04a16a37
--- /dev/null
+++ b/plugins/jetpack/modules/gplus-authorship/admin/style.css
@@ -0,0 +1,51 @@
+#gplus-connection-details {
+ margin-top: 10px;
+ padding: 15px;
+ background: #fff;
+ border: none !important;
+ -webkit-box-shadow: 0 1px 3px rgba(0,0,0,0.13);
+ box-shadow: 0 1px 3px rgba(0,0,0,0.13);
+ width: 400px;
+}
+
+#gplus-connection-details p {
+ padding: 0;
+}
+
+#gplus-connection-details img {
+ float: left;
+ margin-right: .5em;
+}
+
+.gplus-clear {
+ clear: both;
+}
+
+.gplus-connected {
+ float: right;
+ font-weight: bold;
+ color: #78AB46;
+}
+
+.gplus-disconnect {
+ float:right;
+ position: relative;
+ top: 13px;
+ right: -5px;
+}
+
+.gplus-disconnect a {
+ color: #c21c19;
+ font-size: 110%;
+}
+
+.gplus-post-meta-box img {
+ vertical-align: middle;
+ margin-bottom: 5px;
+ height: 20px;
+ width: 20px;
+}
+
+.gplus-sharing-screen {
+ margin: 10px 0 45px 0;
+} \ No newline at end of file
diff --git a/plugins/jetpack/modules/gplus-authorship/admin/ui.php b/plugins/jetpack/modules/gplus-authorship/admin/ui.php
new file mode 100644
index 00000000..ad05eb62
--- /dev/null
+++ b/plugins/jetpack/modules/gplus-authorship/admin/ui.php
@@ -0,0 +1,270 @@
+<?php
+function jetpack_init_gplus_authorship_admin() {
+ $gplus_admin = new GPlus_Authorship_Admin;
+ add_action( 'save_post', array( 'GPlus_Authorship_Admin', 'save_post_meta' ) );
+}
+add_action( 'init', 'jetpack_init_gplus_authorship_admin' );
+
+class GPlus_Authorship_Admin {
+
+ public function __construct() {
+ $this->in_jetpack = ( defined( 'IS_WPCOM' ) && IS_WPCOM ) ? false : true;
+
+ if ( $this->in_jetpack ) {
+ $active = Jetpack::get_active_modules();
+ if ( ! in_array( 'sharedaddy', $active ) && ! in_array( 'publicize', $active ) && ! in_array( 'likes', $active ) ) {
+ add_action( 'admin_menu', array( $this, 'sharing_menu' ) ); // we don't have a sharing page yet
+ }
+
+ add_action( 'jetpack_activate_module_likes', array( $this, 'module_toggle' ) );
+ add_action( 'jetpack_deactivate_module_likes', array( $this, 'module_toggle' ) );
+
+ Jetpack::enable_module_configurable( __FILE__ );
+ Jetpack::module_configuration_load( __FILE__, array( $this, 'configuration_redirect' ) );
+ }
+
+ // The visible UI elements for the user
+ add_action( 'load-settings_page_sharing', array( $this, 'load_management_script_assets' ) );
+ add_action( 'pre_admin_screen_sharing', array( $this, 'connection_screen' ), 15 );
+ add_action( 'admin_init', array( $this, 'add_meta_box' ) );
+ add_action( 'do_meta_boxes', array( $this, 'should_we_show_the_meta_box' ) );
+
+ if ( $this->in_jetpack )
+ add_action( 'pre_admin_screen_sharing', array( $this, 'jetpack_disconnect' ), 10 );
+ }
+
+ function module_toggle() {
+ $jetpack = Jetpack::init();
+ $jetpack->sync->register( 'noop' );
+ }
+
+ /**
+ * Redirects to the likes section of the sharing page.
+ */
+ function configuration_redirect() {
+ wp_safe_redirect( admin_url( 'options-general.php?page=sharing#gplus' ) );
+ die();
+ }
+
+ /**
+ * Adds the 'sharing' menu to the settings menu.
+ * Only ran if sharedaddy, publicize, and likes are not already active.
+ */
+ function sharing_menu() {
+ add_submenu_page( 'options-general.php', esc_html__( 'Sharing Settings', 'jetpack' ), esc_html__( 'Sharing', 'jetpack' ), 'manage_options', 'sharing', array( $this, 'sharing_page' ) );
+ }
+
+ /**
+ * Provides a sharing page with the sharing_global_options hook
+ * so we can display the setting.
+ * Only ran if sharedaddy and publicize are not already active.
+ */
+ function sharing_page() { ?>
+ <div class="wrap">
+ <div class="icon32" id="icon-options-general"><br /></div>
+ <h2><?php esc_html_e( 'Sharing Settings', 'jetpack' ); ?></h2>
+ <?php do_action( 'pre_admin_screen_sharing' ) ?>
+ </div><?php
+ }
+
+
+ public function load_management_script_assets() {
+ if ( $this->in_jetpack )
+ $pm = plugins_url( '_inc/postmessage.js', dirname( dirname ( dirname(__FILE__) ) ) );
+ else
+ $pm = '/wp-content/js/postmessage.js';
+ wp_enqueue_script( 'postmessage', $pm, array( 'jquery' ) );
+ wp_enqueue_script( 'gplus-listener', plugins_url( 'listener.js', __FILE__ ), array( 'jquery', 'postmessage' ) );
+ wp_localize_script( 'gplus-listener', 'GPlusL10n', array(
+ 'connected' => __( 'Your Google+ account has been connected.', 'jetpack' ),
+ 'unknownError' => __( 'There was a problem connecting your Google+ account. Please try again.', 'jetpack' ),
+ 'accessDenied' => __( "You must click 'Accept' in the Google+ dialog to connect your account.", 'jetpack' ),
+ ) );
+ wp_enqueue_style( 'gplus', plugins_url( 'style.css', __FILE__ ) );
+ }
+
+ public function jetpack_disconnect() {
+ if ( empty( $_GET['disconnect'] ) || 'gplus' != $_GET['disconnect'] )
+ return;
+
+ global $current_user;
+ // security check - did we actually want to disconnect?
+ $nonce = $_GET['_wpnonce'];
+ if ( !wp_verify_nonce( $nonce, 'disconnect-gplus' ) )
+ return;
+
+ $connections = get_option( 'gplus_authors', array() );
+
+ Jetpack::load_xml_rpc_client();
+ $xml = new Jetpack_IXR_Client();
+ $xml->query( 'jetpack.disconnectGooglePlus', $connections[ $current_user->ID ]['id'] );
+
+ if ( !$xml->isError() ) {
+ unset( $connections[ $current_user->ID ] );
+ update_option( 'gplus_authors', $connections );
+ } else {
+ // @todo error
+ }
+ }
+
+ public function connection_screen() { ?>
+ <div id="gplus"></div>
+ <div class="gplus-sharing-screen">
+ <h3><?php _e( 'Google+ Profile', 'jetpack' ) ?></h3><?php
+ $this->disconnected_message();
+ if ( GPlus_Authorship_Utils::is_current_user_connected() ) {
+ $this->connected_message();
+ } else {
+ $this->connect_button();
+ ?><div id="result"></div><?php // where output from Javascript (success/error messages) are placed
+ } ?>
+ </div><?php
+ }
+
+ private function disconnect_button() { ?>
+ <a href="<?php echo wp_nonce_url( 'options-general.php?page=sharing&disconnect=gplus', 'disconnect-gplus' ); ?>" id="disconnect-gplus" class="pub-disconnect-button" title="<?php _e( 'Disconnect', 'jetpack'); ?>">&times;</a><?php
+ }
+
+ private function connect_button() { ?>
+ <?php _e( 'Connect your WordPress account to Google+ to add this blog to your Google+ profile and improve the visibility of your blog posts on Google.', 'jetpack' ) ?></p>
+ <iframe src="//dashboard.wordpress.com/wp-admin/options-general.php?page=gplus-authorship&amp;gplus=frame&amp;blog_id=<?php echo intval( GPlus_Authorship_Utils::get_blog_id() ); ?>&amp;jpstate=<?php echo wp_create_nonce( 'gplus-connect' ); ?>" width="400" height="45" style="overflow:none;" scrolling="no"></iframe>
+ <p><small><a href="http://support.wordpress.com/google-plus-profile/" target="_blank"><?php esc_html_e( 'Need help?', 'jetpack' ); ?></a></small></p><?php
+ }
+
+ private function disconnected_message() {
+ global $publicize;
+ if ( !empty( $_GET['disconnect'] ) && 'gplus' == $_GET['disconnect'] ) {
+ if ( isset( $publicize->disconnected_from_authorship ) && $publicize->disconnected_from_authorship ) { ?>
+ <div class="updated"><p><?php echo sprintf( __( "Your Google+ profile and WordPress.com accounts have been disconnected, including your Publicize connections. If you no longer wish to be associated with this blog on Google we recommend that you also remove the blog URL from your <a href='%s' target='_blank'>Google+ profile</a>.", 'jetpack' ), 'http://plus.google.com/me/about/edit/co' ); ?></p></div><?php
+ } else { ?>
+ <div class="updated"><p><?php echo sprintf( __( "Your Google+ profile and WordPress.com accounts have been disconnected. If you no longer wish to be associated with this blog on Google we recommend that you also remove the blog URL from your <a href='%s' target='_blank'>Google+ profile</a>.", 'jetpack' ), 'http://plus.google.com/me/about/edit/co' ); ?></p></div><?php
+ }
+ }
+ }
+
+ private function connected_message() {
+ $users_gplus_info = GPlus_Authorship_Utils::get_current_users_gplus_info(); ?>
+ <p><?php _e( 'Your Google+ profile name and URL will be displayed in the sharing area of your posts.', 'jetpack' ); ?></p>
+ <div id="gplus-connection-details">
+ <div class="gplus-disconnect">
+ <?php $this->disconnect_button(); ?>
+ </div>
+ <img src="<?php echo esc_url( $users_gplus_info['profile_image'] ); ?>?sz=50" alt="" />
+ <p>
+ <span class="gplus-user"><a href="<?php echo esc_url( $users_gplus_info['url'] ); ?>"><?php echo esc_html( $users_gplus_info['name'] ); ?></a></span>
+ <span class="gplus-connected"><?php esc_html_e( 'Connected', 'jetpack' ); ?></span>
+ </p>
+ <div class="gplus-clear"></div>
+ </div><?php
+ }
+
+ function add_meta_box() {
+ $gplus_connections = GPlus_Authorship_Utils::get_all_gplus_authors();
+ if ( empty( $gplus_connections ) || count( $gplus_connections ) < 1 )
+ return;
+ add_meta_box( 'gplus_authorship', __( 'Google+', 'jetpack' ), array( $this, 'post_screen_meta_box' ), 'post', 'advanced', 'high' );
+ }
+
+ function should_we_show_the_meta_box( $page ) {
+ if ( 'post' != $page )
+ return;
+
+ global $post;
+ $gplus_connections = GPlus_Authorship_Utils::get_all_gplus_authors();
+ if ( empty( $gplus_connections ) || count( $gplus_connections ) < 1 )
+ remove_meta_box( 'gplus_authorship', 'post', 'advanced' );
+ $users_gplus_info = GPlus_Authorship_Utils::get_post_authors_gplus_info( $post );
+ if ( empty( $users_gplus_info ) )
+ remove_meta_box( 'gplus_authorship', 'post', 'advanced' );
+ }
+
+ function post_screen_meta_box( $post = '' ) {
+ wp_enqueue_style( 'gplus', plugins_url( 'style.css', __FILE__ ) );
+ $enabled_on_post = true;
+ $meta = get_post_meta( $post->ID, 'gplus_authorship_disabled', true );
+ if ( isset( $meta ) && true == $meta )
+ $enabled_on_post = false;
+ $users_gplus_info = GPlus_Authorship_Utils::get_post_authors_gplus_info( $post );?>
+ <p>
+ <label for="gplus_authorship_enable">
+ <input type="checkbox" name="gplus_authorship" id="gplus_authorship_enable" value="1" <?php checked( $enabled_on_post ); ?>>
+ <?php esc_html_e( 'Show Google+ infomation with this post', 'jetpack' ); ?> <br />
+ <?php if ( !empty( $users_gplus_info ) ) { ?>
+ <div class="gplus-post-meta-box">
+ <img src="<?php echo esc_url( $users_gplus_info['profile_image'] ); ?>?sz=25" alt="" />
+ <em><?php echo __( sprintf( 'Profile: <a href="%s" target="_blank">%s</a>', esc_url( $users_gplus_info['url'] ), esc_html( $users_gplus_info['name'] ) ), 'jetpack' ); ?></em>
+ </div>
+ <?php } ?>
+ </label>
+ </p><?php
+ }
+
+ static function save_post_meta( $post_id ) {
+ if ( defined('DOING_AUTOSAVE') && DOING_AUTOSAVE )
+ return $post_id;
+ global $post;
+ $authors = get_option( 'gplus_authors', array() );
+ if ( empty( $authors ) || empty( $post ) )
+ return $post_id;
+ if ( empty( $authors[ $post->post_author ] ) )
+ return $post_id;
+ if ( isset( $_POST['post_type'] ) && ( 'post' == $_POST['post_type'] ) ) {
+ if ( empty( $_POST['gplus_authorship'] ) ) {
+ update_post_meta( $post_id, 'gplus_authorship_disabled', 1 );
+ } else {
+ delete_post_meta( $post_id, 'gplus_authorship_disabled' );
+ }
+ }
+
+ return $post_id;
+ }
+
+}
+
+class GPlus_Authorship_Utils {
+
+ static function get_blog_id() {
+ if ( defined( 'IS_WPCOM' ) && IS_WPCOM ) {
+ $blog_id = get_current_blog_id();
+ } else {
+ $jetpack = Jetpack::init();
+ $blog_id = $jetpack->get_option( 'id' );
+ }
+ return $blog_id;
+ }
+
+ static function can_current_user_connect( $_blog_id = false ) {
+ global $current_user;
+ if ( !$_blog_id )
+ $_blog_id = GPlus_Authorship_Utils::get_blog_id();
+ if ( is_user_member_of_blog( $current_user->ID, $_blog_id ) || is_super_admin( $current_user->ID ) )
+ return true;
+ return false;
+ }
+
+ static function is_current_user_connected( $_blog_id = false ) {
+ if ( $_blog_id )
+ switch_to_blog( $_blog_id );
+ $gplus = self::get_current_users_gplus_info();
+ if ( $_blog_id )
+ restore_current_blog();
+ return (bool) !empty( $gplus );
+ }
+
+ static function get_all_gplus_authors() {
+ return get_option( 'gplus_authors', array() );
+ }
+
+ static function get_current_users_gplus_info() {
+ global $current_user;
+ $all = self::get_all_gplus_authors();
+ return ( empty( $all[ $current_user->ID ] ) ? array() : $all[ $current_user->ID ] );
+ }
+
+ static function get_post_authors_gplus_info( $post ) {
+ $id = $post->post_author;
+ $all = self::get_all_gplus_authors();
+ return ( empty( $all[ $id ] ) ? array() : $all[ $id ] );
+ }
+
+}
diff --git a/plugins/jetpack/modules/gplus-authorship/style.css b/plugins/jetpack/modules/gplus-authorship/style.css
new file mode 100644
index 00000000..3fada404
--- /dev/null
+++ b/plugins/jetpack/modules/gplus-authorship/style.css
@@ -0,0 +1,17 @@
+.g-follow-wrapper {
+ display: inline-block;
+ position: relative;
+ top: 3px;
+}
+
+.sd-gplus img {
+ vertical-align: middle;
+ margin-bottom: 5px;
+ height: 20px;
+ width: 20px;
+ display: inline;
+}
+
+.gplus-profile {
+ margin-right: 10px;
+} \ No newline at end of file
diff --git a/plugins/jetpack/modules/gravatar-hovercards.php b/plugins/jetpack/modules/gravatar-hovercards.php
index 980317cd..1c111fc7 100644
--- a/plugins/jetpack/modules/gravatar-hovercards.php
+++ b/plugins/jetpack/modules/gravatar-hovercards.php
@@ -5,6 +5,7 @@
* Sort Order: 8
* First Introduced: 1.1
* Requires Connection: No
+ * Auto Activate: Yes
*/
define( 'GROFILES__CACHE_BUSTER', gmdate( 'YM' ) . 'aa' ); // Break CDN cache, increment when gravatar.com/js/gprofiles.js changes
diff --git a/plugins/jetpack/modules/holiday-snow.php b/plugins/jetpack/modules/holiday-snow.php
index 51bf3ed6..0ab74cad 100644
--- a/plugins/jetpack/modules/holiday-snow.php
+++ b/plugins/jetpack/modules/holiday-snow.php
@@ -6,6 +6,7 @@
* Not a module that is activated/deactivated
* First Introduced: 2.0.3 ??
* Requires Connection: No
+ * Auto Activate: Yes
*/
class Jetpack_Holiday_Snow_Settings {
diff --git a/plugins/jetpack/modules/infinite-scroll.php b/plugins/jetpack/modules/infinite-scroll.php
index 2a94bf42..51a98988 100644
--- a/plugins/jetpack/modules/infinite-scroll.php
+++ b/plugins/jetpack/modules/infinite-scroll.php
@@ -5,6 +5,7 @@
* Sort Order: 14
* First Introduced: 2.0
* Requires Connection: No
+ * Auto Activate: No
*/
/**
@@ -137,7 +138,7 @@ class Jetpack_Infinite_Scroll_Extras {
/**
* Modify Infinite Scroll configuration information
*
- * @uses Jetpack::get_active_modules, is_user_logged_in, stats_get_options, Jetpack::get_option, get_option, JETPACK__API_VERSION, JETPACK__VERSION
+ * @uses Jetpack::get_active_modules, is_user_logged_in, stats_get_options, Jetpack_Options::get_option, get_option, JETPACK__API_VERSION, JETPACK__VERSION
* @filter infinite_scroll_js_settings
* @return array
*/
@@ -155,7 +156,7 @@ class Jetpack_Infinite_Scroll_Extras {
}
// We made it this far, so gather the data needed to track IS views
- $settings['stats'] = 'blog=' . Jetpack::get_option( 'id' ) . '&host=' . parse_url( get_option( 'home' ), PHP_URL_HOST ) . '&v=ext&j=' . JETPACK__API_VERSION . ':' . JETPACK__VERSION;
+ $settings['stats'] = 'blog=' . Jetpack_Options::get_option( 'id' ) . '&host=' . parse_url( get_option( 'home' ), PHP_URL_HOST ) . '&v=ext&j=' . JETPACK__API_VERSION . ':' . JETPACK__VERSION;
// Pagetype parameter
$settings['stats'] .= '&x_pagetype=infinite';
diff --git a/plugins/jetpack/modules/json-api.php b/plugins/jetpack/modules/json-api.php
index e0722317..233d56a9 100644
--- a/plugins/jetpack/modules/json-api.php
+++ b/plugins/jetpack/modules/json-api.php
@@ -5,6 +5,7 @@
* Sort Order: 100
* First Introduced: 1.9
* Requires Connection: Yes
+ * Auto Activate: Public
*/
function jetpack_json_api_toggle() {
diff --git a/plugins/jetpack/modules/latex.php b/plugins/jetpack/modules/latex.php
index eb58f797..2c90ba8b 100644
--- a/plugins/jetpack/modules/latex.php
+++ b/plugins/jetpack/modules/latex.php
@@ -4,7 +4,8 @@
* Module Description: Mark up your posts with the <img src="//s0.wp.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: 12
* First Introduced: 1.1
- * Requires Connection: Yes
+ * Requires Connection: No
+ * Auto Activate: Yes
*/
/**
diff --git a/plugins/jetpack/modules/likes.php b/plugins/jetpack/modules/likes.php
index ab8b7b7a..24e83ae2 100644
--- a/plugins/jetpack/modules/likes.php
+++ b/plugins/jetpack/modules/likes.php
@@ -5,6 +5,7 @@
* First Introduced: 2.2
* Sort Order: 4
* Requires Connection: Yes
+ * Auto Activate: No
*/
class Jetpack_Likes {
var $version = '20130620a';
@@ -520,11 +521,11 @@ class Jetpack_Likes {
.fixed .column-likes .post-com-count { background-image: none; }
.fixed .column-likes .comment-count { background-color: #888; }
.fixed .column-likes .comment-count:hover { background-color: #D54E21; }
- .admin-color-mp6 .fixed .column-likes .post-com-count::after { border: none !important; }
- .admin-color-mp6 .fixed .column-likes .comment-count { background-color: #bbb; }
- .admin-color-mp6 .fixed .column-likes .comment-count:hover { background-color: #2ea2cc; }
- .admin-color-mp6 .fixed .column-likes .vers img { display: none; }
- .admin-color-mp6 .fixed .column-likes .vers:before {font:20px/1 dashicons;content: '\f155';-webkit-font-smoothing:antialiased;}
+ .mp6 .fixed .column-likes .post-com-count::after { border: none !important; }
+ .mp6 .fixed .column-likes .comment-count { background-color: #bbb; }
+ .mp6 .fixed .column-likes .comment-count:hover { background-color: #2ea2cc; }
+ .mp6 .fixed .column-likes .vers img { display: none; }
+ .mp6 .fixed .column-likes .vers:before {font:20px/1 dashicons;content: '\f155';-webkit-font-smoothing:antialiased;}
</style> <?php
}
@@ -533,10 +534,14 @@ class Jetpack_Likes {
*/
function enqueue_admin_scripts() {
if ( empty( $_GET['post_type'] ) || 'post' == $_GET['post_type'] || 'page' == $_GET['post_type'] ) {
- if ( $this->in_jetpack )
+ if ( $this->in_jetpack ) {
wp_enqueue_script( 'likes-post-count', plugins_url( 'modules/likes/post-count.js', dirname( __FILE__ ) ), array( 'jquery' ), JETPACK__VERSION );
- else
+ wp_enqueue_script( 'likes-post-count-jetpack', plugins_url( 'modules/likes/post-count-jetpack.js', dirname( __FILE__ ) ), array( 'likes-post-count' ), JETPACK__VERSION );
+ } else {
+ wp_enqueue_script( 'jquery.wpcom-proxy-request', "/wp-content/js/jquery/jquery.wpcom-proxy-request.js", array('jquery'), NULL, true );
wp_enqueue_script( 'likes-post-count', plugins_url( 'likes/post-count.js', dirname( __FILE__ ) ), array( 'jquery' ), JETPACK__VERSION );
+ wp_enqueue_script( 'likes-post-count-wpcom', plugins_url( 'likes/post-count-wpcom.js', dirname( __FILE__ ) ), array( 'likes-post-count', 'jquery.wpcom-proxy-request' ), JETPACK__VERSION );
+ }
}
}
@@ -552,8 +557,7 @@ class Jetpack_Likes {
if ( defined( 'IS_WPCOM' ) && IS_WPCOM ) {
$blog_id = get_current_blog_id();
} else {
- $jetpack = Jetpack::init();
- $blog_id = $jetpack->get_option( 'id' );
+ $blog_id = Jetpack_Options::get_option( 'id' );
}
$permalink = get_permalink( get_the_ID() ); ?>
@@ -586,17 +590,12 @@ class Jetpack_Likes {
if ( ! $this->is_likes_visible() )
return $content;
- $protocol = 'http';
- if ( is_ssl() )
- $protocol = 'https';
-
if ( defined( 'IS_WPCOM' ) && IS_WPCOM ) {
$blog_id = get_current_blog_id();
$bloginfo = get_blog_details( (int) $blog_id );
$domain = $bloginfo->domain;
} else {
- $jetpack = Jetpack::init();
- $blog_id = $jetpack->get_option( 'id' );
+ $blog_id = Jetpack_Options::get_option( 'id' );
$url = home_url();
$url_parts = parse_url( $url );
$domain = $url_parts['host'];
@@ -610,7 +609,7 @@ class Jetpack_Likes {
*/
$uniqid = uniqid();
- $src = sprintf( '%1$s://widgets.wp.com/likes/#blog_id=%2$d&amp;post_id=%3$d&amp;origin=%1$s://%4$s&amp;obj_id=%2$d-%3$d-%5$s', $protocol, $blog_id, $post->ID, $domain, $uniqid );
+ $src = sprintf( '//widgets.wp.com/likes/#blog_id=%1$d&amp;post_id=%2$d&amp;origin=%3$s&amp;obj_id=%1$d-%2$d-%4$s', $blog_id, $post->ID, $domain, $uniqid );
$name = sprintf( 'like-post-frame-%1$d-%2$d-%3$s', $blog_id, $post->ID, $uniqid );
$wrapper = sprintf( 'like-post-wrapper-%1$d-%2$d-%3$s', $blog_id, $post->ID, $uniqid );
@@ -638,8 +637,7 @@ class Jetpack_Likes {
$bloginfo = get_blog_details( (int) $blog_id );
$domain = $bloginfo->domain;
} else {
- $jetpack = Jetpack::init();
- $blog_id = $jetpack->get_option( 'id' );
+ $blog_id = Jetpack_Options::get_option( 'id' );
$url = home_url();
$url_parts = parse_url( $url );
$domain = $url_parts['host'];
@@ -678,8 +676,7 @@ class Jetpack_Likes {
$bloginfo = get_blog_details( (int) $blog_id );
$domain = $bloginfo->domain;
} else {
- $jetpack = Jetpack::init();
- $blog_id = $jetpack->get_option( 'id' );
+ $blog_id = Jetpack_Options::get_option( 'id' );
$url = home_url();
$url_parts = parse_url( $url );
$domain = $url_parts['host'];
@@ -1029,6 +1026,11 @@ class Jetpack_Likes {
}
}
+ // Check that the post is a public, published post.
+ if ( 'publish' != $post->post_status ) {
+ $enabled = false;
+ }
+
// Run through the sharing filters
$enabled = apply_filters( 'sharing_show', $enabled, $post );
diff --git a/plugins/jetpack/modules/likes/post-count-jetpack.js b/plugins/jetpack/modules/likes/post-count-jetpack.js
new file mode 100644
index 00000000..1c53362a
--- /dev/null
+++ b/plugins/jetpack/modules/likes/post-count-jetpack.js
@@ -0,0 +1,18 @@
+var wpPostLikeCount = wpPostLikeCount || {};
+
+(function($) {
+
+ wpPostLikeCount = jQuery.extend( wpPostLikeCount, {
+ request: function( options ) {
+ return $.ajax( {
+ type: 'GET',
+ url: wpPostLikeCount.jsonAPIbase + options.path,
+ dataType : "jsonp",
+ data: options.data,
+ success: function( response ) { options.success( response ); },
+ error: function( response ) { options.error( response ); }
+ } );
+ }
+ } );
+
+})(jQuery);
diff --git a/plugins/jetpack/modules/likes/post-count.js b/plugins/jetpack/modules/likes/post-count.js
index d93b8859..935c0957 100644
--- a/plugins/jetpack/modules/likes/post-count.js
+++ b/plugins/jetpack/modules/likes/post-count.js
@@ -1,8 +1,8 @@
-var wpPostLikeCount;
+var wpPostLikeCount = wpPostLikeCount || {};
(function($) {
- wpPostLikeCount = {
+ wpPostLikeCount = jQuery.extend( wpPostLikeCount, {
jsonAPIbase: 'https://public-api.wordpress.com/rest/v1',
APIqueue: [],
@@ -47,23 +47,11 @@ var wpPostLikeCount;
}
wpPostLikeCount.request( batchRequest );
- },
-
- request: function( options ) {
- return $.ajax( {
- type: 'GET',
- url: wpPostLikeCount.jsonAPIbase + options.path,
- dataType : "jsonp",
- data: options.data,
- success: function( response ) { options.success( response ) },
- error: function( response ) { options.error( response ) }
- } )
}
-
- };
+ } );
})(jQuery);
jQuery(document).ready(function($) {
wpPostLikeCount.wpPostLikeCount();
-}); \ No newline at end of file
+});
diff --git a/plugins/jetpack/modules/likes/style.css b/plugins/jetpack/modules/likes/style.css
index ee34f435..525d3817 100644
--- a/plugins/jetpack/modules/likes/style.css
+++ b/plugins/jetpack/modules/likes/style.css
@@ -1,7 +1,7 @@
#wpadminbar li#wp-admin-bar-admin-bar-likes-widget {
width: 61px;
+ overflow: hidden;
}
-
#wpadminbar iframe.admin-bar-likes-widget {
width: 61px;
height: 28px;
diff --git a/plugins/jetpack/modules/minileven.php b/plugins/jetpack/modules/minileven.php
index 89a97d46..f3cb8f75 100644
--- a/plugins/jetpack/modules/minileven.php
+++ b/plugins/jetpack/modules/minileven.php
@@ -6,6 +6,7 @@
* Sort Order: 11
* First Introduced: 1.8
* Requires Connection: No
+ * Auto Activate: No
*/
function jetpack_load_minileven() {
diff --git a/plugins/jetpack/modules/minileven/theme/pub/minileven/functions.php b/plugins/jetpack/modules/minileven/theme/pub/minileven/functions.php
index 6bd7ee00..b33de783 100644
--- a/plugins/jetpack/modules/minileven/theme/pub/minileven/functions.php
+++ b/plugins/jetpack/modules/minileven/theme/pub/minileven/functions.php
@@ -40,6 +40,11 @@ function minileven_setup() {
*/
require( get_template_directory() . '/inc/tweaks.php' );
+ /**
+ * Implement the Custom Header functions
+ */
+ require( get_template_directory() . '/inc/custom-header.php' );
+
/* Make Minileven available for translation.
* Translations can be added to the /languages/ directory.
* If you're building a theme based on Minileven, use a find and replace
@@ -177,11 +182,6 @@ function minileven_get_background() {
}
/**
- * Implement the Custom Header functions
- */
-require( get_template_directory() . '/inc/custom-header.php' );
-
-/**
* If the user has set a static front page, show all posts on the front page, instead of a static page.
*/
if ( '1' == get_option( 'wp_mobile_static_front_page' ) )
diff --git a/plugins/jetpack/modules/minileven/theme/pub/minileven/style.css b/plugins/jetpack/modules/minileven/theme/pub/minileven/style.css
index e70415be..0def5169 100644
--- a/plugins/jetpack/modules/minileven/theme/pub/minileven/style.css
+++ b/plugins/jetpack/modules/minileven/theme/pub/minileven/style.css
@@ -1,8 +1,8 @@
/*
Theme Name: Minileven
-Theme URI: http://automattic.com
+Theme URI: http://theme.wordpress.com
Author: Automattic
-Author URI: http://automattic.com
+Author URI: http://theme.wordpress.com
Description: The Minileven theme is a clean, lightweight mobile experience for your blog based on Twenty Eleven.
Version: 2.0-wpcom
License: GNU General Public License
diff --git a/plugins/jetpack/modules/mobile-push.php b/plugins/jetpack/modules/mobile-push.php
index b073348c..2d28653e 100644
--- a/plugins/jetpack/modules/mobile-push.php
+++ b/plugins/jetpack/modules/mobile-push.php
@@ -5,6 +5,7 @@
* Sort Order: 100
* First Introduced: 1.9
* Requires Connection: Yes
+ * Auto Activate: Yes
*/
Jetpack_Sync::sync_comments( __FILE__, array(
diff --git a/plugins/jetpack/modules/module-extras.php b/plugins/jetpack/modules/module-extras.php
index 22fdb924..86bcbc5d 100644
--- a/plugins/jetpack/modules/module-extras.php
+++ b/plugins/jetpack/modules/module-extras.php
@@ -4,57 +4,16 @@
* For example, if a module shouldn't be activatable unless certain conditions are met, the code belongs in this file.
*/
-/**
- * INFINITE SCROLL
- */
-
-/**
- * Load theme's infinite scroll annotation file, if present in the IS plugin.
- * The `setup_theme` action is used because the annotation files should be using `after_setup_theme` to register support for IS.
- *
- * As released in Jetpack 2.0, a child theme's parent wasn't checked for in the plugin's bundled support, hence the convoluted way the parent is checked for now.
- *
- * @uses is_admin, wp_get_theme, get_theme, get_current_theme, apply_filters
- * @action setup_theme
- * @return null
- */
-function jetpack_load_infinite_scroll_annotation() {
- if ( is_admin() && isset( $_GET['page'] ) && 'jetpack' == $_GET['page'] ) {
- $theme = function_exists( 'wp_get_theme' ) ? wp_get_theme() : get_theme( get_current_theme() );
-
- if ( ! is_a( $theme, 'WP_Theme' ) && ! is_array( $theme ) )
- return;
-
- $customization_file = apply_filters( 'infinite_scroll_customization_file', dirname( __FILE__ ) . "/infinite-scroll/themes/{$theme['Stylesheet']}.php", $theme['Stylesheet'] );
-
- if ( is_readable( $customization_file ) ) {
- require_once( $customization_file );
- }
- elseif ( ! empty( $theme['Template'] ) ) {
- $customization_file = dirname( __FILE__ ) . "/infinite-scroll/themes/{$theme['Template']}.php";
-
- if ( is_readable( $customization_file ) )
- require_once( $customization_file );
- }
- }
-}
-add_action( 'setup_theme', 'jetpack_load_infinite_scroll_annotation' );
-
-/**
- * Prevent IS from being activated if theme doesn't support it
- *
- * @param bool $can_activate
- * @filter jetpack_can_activate_infinite-scroll
- * @return bool
- */
-function jetpack_can_activate_infinite_scroll( $can_activate ) {
- return (bool) current_theme_supports( 'infinite-scroll' );
-}
-add_filter( 'jetpack_can_activate_infinite-scroll', 'jetpack_can_activate_infinite_scroll' );
-
// Happy Holidays!
require_once( dirname( __FILE__ ) . '/holiday-snow.php' );
-require_once( dirname( __FILE__ ) . '/featured-content/featured-content.php' );
+// Include extra tools that aren't modules, in a filterable way
+$jetpack_tools_to_include = apply_filters( 'jetpack-tools-to-include', array( 'theme-tools.php' ) );
-require_once( dirname( __FILE__ ) . '/social-links.php' );
+if ( ! empty( $jetpack_tools_to_include ) ) {
+ foreach ( $jetpack_tools_to_include as $tool ) {
+ if ( file_exists( JETPACK__PLUGIN_DIR . '/modules/' . $tool ) ) {
+ require_once( JETPACK__PLUGIN_DIR . '/modules/' . $tool );
+ }
+ }
+} \ No newline at end of file
diff --git a/plugins/jetpack/modules/module-info.php b/plugins/jetpack/modules/module-info.php
index cb5257f0..f4b8ac8c 100644
--- a/plugins/jetpack/modules/module-info.php
+++ b/plugins/jetpack/modules/module-info.php
@@ -777,30 +777,35 @@ function jetpack_likes_more_link() {
add_action( 'jetpack_learn_more_button_likes', 'jetpack_likes_more_link' );
// Likes: STOP
-// Debug: START
-function jetpack_debug_more_info() { ?>
+// Google+ Profile: START
+function jetpack_gplus_authorship_more_info() { ?>
- <h4><?php esc_html_e( 'Debug' , 'jetpack' ); ?></h4>
+ <div class="jp-info-img">
+ <a href="http://jetpack.me/support/google-plus/">
+ <img class="jp-info-img" src="<?php echo plugins_url( basename( dirname( dirname( __FILE__ ) ) ) . '/_inc/images/screenshots/google-plus.png' ) ?>" alt="<?php esc_attr_e( 'Google+ Profile', 'jetpack' ) ?>" width="350" height="33" />
+ </a>
+ </div>
- <p><?php esc_html_e( "A debugging platform for the Jetpack plugin. Find out why Jetpack isn't working for you and submit a help request direct from your Dashboard.", 'jetpack' ); ?></p>
- <?php if ( Jetpack::is_module_active( 'debug' ) ) : ?>
- <p><a href="<?php echo admin_url( 'admin.php?page=jetpack-debugger' ); ?>"><?php esc_html_e( "Click here to start debugging.", 'jetpack' ); ?></a></p>
+ <h4><?php esc_html_e( 'Google+ Profile' , 'jetpack' ); ?></h4>
+
+ <p><?php esc_html_e( 'The Google+ profile module allows you to connect your blog and Google+ accounts.', 'jetpack' ) ?></p>
+ <p><?php esc_html_e( 'Displayed below your posts will be a link back to your Google+ profile and a Google+ follow button. A link will also be added to your Google+ profile.', 'jetpack' ); ?></p>
+
+ <p>&rarr; <a href="http://jetpack.me/support/google-plus/"><?php esc_html_e( 'More information on using Google+ Profile.', 'jetpack' ); ?></a></p>
- <?php endif; ?>
<?php
}
+add_action( 'jetpack_module_more_info_gplus-authorship', 'jetpack_gplus_authorship_more_info' );
-function jetpack_debug_more_link() {
- echo '<a class="button-secondary more-info-link" href="http://jetpack.me/support/debug/">' . __( 'Learn More', 'jetpack' ) . '</a>';
+function jetpack_gplus_authorship_more_link() {
+ echo '<a class="button-secondary more-info-link" href="#">' . __( 'Learn More', 'jetpack' ) . '</a>';
}
-
-add_action( 'jetpack_module_more_info_debug', 'jetpack_debug_more_info' );
-add_action( 'jetpack_module_more_info_connected_debug', 'jetpack_debug_more_info' );
-add_action( 'jetpack_learn_more_button_debug', 'jetpack_debug_more_link' );
-// Debug: STOP
+add_action( 'jetpack_learn_more_button_gplus-authorship', 'jetpack_gplus_authorship_more_link' );
+// Google+ Profile: STOP
// Omnisearch: START
-function jetpack_omnisearch_more_info() { ?>
+function jetpack_omnisearch_more_info() {
+ ?>
<h4><?php esc_html_e( 'Omnisearch' , 'jetpack' ); ?></h4>
@@ -812,7 +817,7 @@ function jetpack_omnisearch_more_info() { ?>
<?php echo Jetpack_Omnisearch::get_omnisearch_form(); ?>
<?php endif; ?>
-<?php
+ <?php
}
function jetpack_omnisearch_more_link() {
@@ -822,3 +827,56 @@ function jetpack_omnisearch_more_link() {
add_action( 'jetpack_module_more_info_omnisearch', 'jetpack_omnisearch_more_info' );
add_action( 'jetpack_learn_more_button_omnisearch', 'jetpack_omnisearch_more_link' );
// Omnisearch: STOP
+
+// Widget Visibility: START
+function jetpack_widget_visibility_more_info() { ?>
+ <h4><?php esc_html_e( 'Widget Visibility', 'jetpack' ); ?></h4>
+
+ <p><?php esc_html_e( 'Control which pages your widgets appear on with Widget Visibility.', 'jetpack' ); ?></p>
+ <p><?php esc_html_e( 'To control visibility, expand the widget and click the Visibility button next to the Save button, and then, choose a set of visibility options.', 'jetpack' ); ?></p>
+ <p><?php esc_html_e( 'For example, if you wanted the Archives widget to only appear on category archives and error pages, choose "Show" from the first dropdown and then add two rules: "Page is 404 Error Page" and "Category is All Category Pages."', 'jetpack' ); ?></p>
+ <p><?php esc_html_e( 'You can also hide widgets based on the current page. For example, if you don\'t want the Archives widget to appear on search results pages, choose "Hide" and "Page is Search results."', 'jetpack' ); ?></p>
+<?php
+}
+
+function jetpack_widget_visibility_more_link() {
+ echo '<a class="button-secondary more-info-link" href="http://jetpack.me/support/widget-visibility/">' . __( 'Learn More', 'jetpack' ) . '</a>';
+}
+
+add_action( 'jetpack_module_more_info_widget-visibility', 'jetpack_widget_visibility_more_info' );
+add_action( 'jetpack_learn_more_button_widget-visibility', 'jetpack_widget_visibility_more_link' );
+// Widget Visibility: STOP
+
+
+// WordPress.com Connect: START
+function jetpack_wpcc_more_info() { ?>
+ <h4><?php esc_html_e( 'WordPress.com Connect' , 'jetpack' ); ?></h4>
+
+ <p><?php esc_html_e( 'With WordPress.com Connect, your users will be able to log into your WordPress admin with the same credentials they use to log into WordPress.com. It\'s safe and secure.' , 'jetpack' ); ?></p>
+ <p><?php esc_html_e( 'Once enabled, a "Connect with WordPress.com" option will be added to your existing log-in form.' , 'jetpack' ); ?></p>
+
+<?php
+}
+
+function jetpack_wpcc_more_link() {
+ echo '<a class="button-secondary more-info-link" href="http://jetpack.me/support/wpcc/">' . __( 'Learn More', 'jetpack' ) . '</a>';
+}
+
+add_action( 'jetpack_module_more_info_wpcc', 'jetpack_wpcc_more_info' );
+add_action( 'jetpack_learn_more_button_wpcc', 'jetpack_wpcc_more_link' );
+// WordPress.com Connect: STOP
+
+// VideoPress: START
+function jetpack_videopress_more_info() {
+ ?>
+ <h4><?php esc_html_e( 'VideoPress', 'jetpack' ); ?></h4>
+ <p><?php _e( 'With the VideoPress module you can easily upload videos to your WordPress site and embed them in your posts and pages. This module requires a WordPress.com account with an active <a href="http://store.wordpress.com/premium-upgrades/videopress/" target="_blank">VideoPress subscription</a>.', 'jetpack' ); ?></p>
+ <?php
+}
+add_action( 'jetpack_module_more_info_videopress', 'jetpack_videopress_more_info' );
+
+function jetpack_videopress_more_link() {
+ echo '<a class="button-secondary more-info-link" href="#">' . __( 'Learn More', 'jetpack' ) . '</a>';
+}
+add_action( 'jetpack_learn_more_button_videopress', 'jetpack_videopress_more_link' );
+// VideoPress: STOP
diff --git a/plugins/jetpack/modules/notes.php b/plugins/jetpack/modules/notes.php
index 6022138a..c103e80a 100644
--- a/plugins/jetpack/modules/notes.php
+++ b/plugins/jetpack/modules/notes.php
@@ -5,6 +5,7 @@
* Sort Order: 1
* First Introduced: 1.9
* Requires Connection: Yes
+ * Auto Activate: Yes
*/
if ( !defined( 'JETPACK_NOTES__CACHE_BUSTER' ) ) define( 'JETPACK_NOTES__CACHE_BUSTER', JETPACK__VERSION . '-' . gmdate( 'oW' ) );
diff --git a/plugins/jetpack/modules/omnisearch.php b/plugins/jetpack/modules/omnisearch.php
index a5db2a5d..31577d75 100644
--- a/plugins/jetpack/modules/omnisearch.php
+++ b/plugins/jetpack/modules/omnisearch.php
@@ -6,6 +6,7 @@
* Sort Order: 8
* First Introduced: 2.3
* Requires Connection: No
+ * Auto Activate: Yes
*/
// Only do Jetpack Omnisearch if there isn't already a Core WP_Omnisearch Class.
diff --git a/plugins/jetpack/modules/omnisearch/omnisearch-core.php b/plugins/jetpack/modules/omnisearch/omnisearch-core.php
index 1b19a466..f0d3fb84 100644
--- a/plugins/jetpack/modules/omnisearch/omnisearch-core.php
+++ b/plugins/jetpack/modules/omnisearch/omnisearch-core.php
@@ -27,6 +27,11 @@ class Jetpack_Omnisearch {
require_once( dirname(__FILE__) . '/omnisearch-comments.php' );
new Jetpack_Omnisearch_Comments;
+ if ( current_user_can( 'upload_files' ) ) {
+ require_once( dirname(__FILE__) . '/omnisearch-media.php' );
+ new Jetpack_Omnisearch_Media;
+ }
+
if ( current_user_can( 'install_plugins' ) ) {
require_once( dirname(__FILE__) . '/omnisearch-plugins.php' );
new Jetpack_Omnisearch_Plugins;
diff --git a/plugins/jetpack/modules/omnisearch/omnisearch-media.php b/plugins/jetpack/modules/omnisearch/omnisearch-media.php
new file mode 100644
index 00000000..17899a0a
--- /dev/null
+++ b/plugins/jetpack/modules/omnisearch/omnisearch-media.php
@@ -0,0 +1,46 @@
+<?php
+
+if( ! class_exists( 'WP_Media_List_Table' ) )
+ require_once( ABSPATH . 'wp-admin/includes/class-wp-media-list-table.php' );
+
+class Jetpack_Omnisearch_Media extends WP_Media_List_Table {
+ static $instance;
+
+ function __construct() {
+ self::$instance = $this;
+ add_filter( 'omnisearch_results', array( $this, 'search'), 10, 2 );
+ }
+
+ function search( $results, $search_term ) {
+ $search_url = esc_url( add_query_arg( 's', $search_term, admin_url( 'upload.php' ) ) );
+ $search_link = sprintf( ' <a href="%s" class="add-new-h2">%s</a>', $search_url, esc_html__('Search Media', 'jetpack') );
+ $html = '<h2>' . esc_html__('Media', 'jetpack') . $search_link . '</h2>';
+ parent::__construct();
+
+ ob_start();
+ $this->prepare_items();
+ $columns = $this->get_columns();
+ unset( $columns['cb'] );
+ $this->_column_headers = array( $columns, array(), array() );
+ $this->display();
+ $html .= ob_get_clean();
+
+ $label = __( 'Media', 'jetpack' );
+ $results[ $label ] = $html;
+ return $results;
+ }
+
+ function get_sortable_columns() {
+ return array();
+ }
+
+ function get_bulk_actions() {
+ return array();
+ }
+
+ function pagination( $which ) {}
+
+ function extra_tablenav( $which ) {}
+}
+
+
diff --git a/plugins/jetpack/modules/omnisearch/omnisearch.css b/plugins/jetpack/modules/omnisearch/omnisearch.css
index f0265b70..6dc1784f 100644
--- a/plugins/jetpack/modules/omnisearch/omnisearch.css
+++ b/plugins/jetpack/modules/omnisearch/omnisearch.css
@@ -19,7 +19,7 @@ input.omnisearch {
width: 100%;
}
-.admin-color-mp6 input.omnisearch {
+.mp6 input.omnisearch {
line-height: 1.35;
}
@@ -40,7 +40,7 @@ button.omnisearch-submit {
bottom: 0;
}
-.admin-color-mp6 button.omnisearch-submit {
+.mp6 button.omnisearch-submit {
padding: 0.3em 0.5em 0.1em;
}
@@ -113,10 +113,22 @@ button.omnisearch-submit span {
width: 15%;
}
-.comments .column-author {
+.wp-list-table.comments .column-author {
width: 20%;
}
+.wp-list-table.media th {
+ white-space: nowrap;
+}
+
+.wp-list-table.media .column-parent {
+ width: 15%;
+}
+
+.wp-list-table.media .column-comments span.vers {
+ display: block;
+}
+
.tablenav {
height: 0;
}
@@ -124,3 +136,4 @@ button.omnisearch-submit span {
.omnisearch-results .tablenav.top {
margin: 5px 0;
}
+
diff --git a/plugins/jetpack/modules/omnisearch/rtl/omnisearch-rtl.css b/plugins/jetpack/modules/omnisearch/rtl/omnisearch-rtl.css
index 2a5de7e1..50cb1d6f 100644
--- a/plugins/jetpack/modules/omnisearch/rtl/omnisearch-rtl.css
+++ b/plugins/jetpack/modules/omnisearch/rtl/omnisearch-rtl.css
@@ -1,4 +1,4 @@
-/* This file was automatically generated on Jun 19 2013 20:38:31 */
+/* This file was automatically generated on Sep 05 2013 19:17:53 */
h2.page-title small {
@@ -21,7 +21,7 @@ input.omnisearch {
width: 100%;
}
-.admin-color-mp6 input.omnisearch {
+.mp6 input.omnisearch {
line-height: 1.35;
}
@@ -42,7 +42,7 @@ button.omnisearch-submit {
bottom: 0;
}
-.admin-color-mp6 button.omnisearch-submit {
+.mp6 button.omnisearch-submit {
padding: 0.3em 0.5em 0.1em;
}
@@ -115,10 +115,22 @@ button.omnisearch-submit span {
width: 15%;
}
-.comments .column-author {
+.wp-list-table.comments .column-author {
width: 20%;
}
+.wp-list-table.media th {
+ white-space: nowrap;
+}
+
+.wp-list-table.media .column-parent {
+ width: 15%;
+}
+
+.wp-list-table.media .column-comments span.vers {
+ display: block;
+}
+
.tablenav {
height: 0;
}
@@ -126,3 +138,4 @@ button.omnisearch-submit span {
.omnisearch-results .tablenav.top {
margin: 5px 0;
}
+
diff --git a/plugins/jetpack/modules/photon.php b/plugins/jetpack/modules/photon.php
index 23fa614f..e6ddc098 100644
--- a/plugins/jetpack/modules/photon.php
+++ b/plugins/jetpack/modules/photon.php
@@ -5,6 +5,7 @@
* Sort Order: 15
* First Introduced: 2.0
* Requires Connection: Yes
+ * Auto Activate: No
*/
Jetpack_Photon::instance(); \ No newline at end of file
diff --git a/plugins/jetpack/modules/post-by-email.php b/plugins/jetpack/modules/post-by-email.php
index bbc58ff2..41e19397 100644
--- a/plugins/jetpack/modules/post-by-email.php
+++ b/plugins/jetpack/modules/post-by-email.php
@@ -6,6 +6,7 @@
* First Introduced: 2.0
* Sort Order: 4
* Requires Connection: Yes
+ * Auto Activate: Yes
*/
add_action( 'jetpack_modules_loaded', array( 'Jetpack_Post_By_Email', 'init' ) );
diff --git a/plugins/jetpack/modules/publicize.php b/plugins/jetpack/modules/publicize.php
index c1265cb3..3c8baa48 100644
--- a/plugins/jetpack/modules/publicize.php
+++ b/plugins/jetpack/modules/publicize.php
@@ -5,6 +5,7 @@
* Sort Order: 1
* First Introduced: 2.0
* Requires Connection: Yes
+ * Auto Activate: Yes
*/
class Jetpack_Publicize {
diff --git a/plugins/jetpack/modules/publicize/assets/facebook-logo.png b/plugins/jetpack/modules/publicize/assets/facebook-logo.png
index b9181cc4..785d0850 100644
--- a/plugins/jetpack/modules/publicize/assets/facebook-logo.png
+++ b/plugins/jetpack/modules/publicize/assets/facebook-logo.png
Binary files differ
diff --git a/plugins/jetpack/modules/publicize/assets/path-logo.png b/plugins/jetpack/modules/publicize/assets/path-logo.png
new file mode 100644
index 00000000..7e9bba25
--- /dev/null
+++ b/plugins/jetpack/modules/publicize/assets/path-logo.png
Binary files differ
diff --git a/plugins/jetpack/modules/publicize/assets/publicize.css b/plugins/jetpack/modules/publicize/assets/publicize.css
index 64569ae9..bb40425a 100644
--- a/plugins/jetpack/modules/publicize/assets/publicize.css
+++ b/plugins/jetpack/modules/publicize/assets/publicize.css
@@ -17,10 +17,12 @@ span.pub-logos {
vertical-align: top;
}
-span#facebook { background: url( facebook-logo.png ) 50% 19px no-repeat; background-size: 125px 47px; }
+span#facebook { background: url( facebook-logo.png ) 50% 19px no-repeat; background-size: 125px 41px; }
span#twitter { background: url( twitter-logo.png ) 50% 19px no-repeat; background-size: 125px 47px; }
span#linkedin { background: url( linkedin-logo.png ) 50% 19px no-repeat; background-size: 125px 47px; }
span#tumblr { background: url( tumblr-logo.png ) 50% 19px no-repeat; background-size: 125px 47px; }
+span#path { background: url( path-logo.png ) 50% 19px no-repeat; background-size: 85px 32px; }
+span#google_plus { background: url( gplus.png ) 50% 19px no-repeat; background-size: 125px; }
a.publicize-profile-link, a.publicize-profile-link:visited {
text-decoration: none;
@@ -31,8 +33,6 @@ a.publicize-profile-link:hover {
}
a.publicize-add-connection, a.publicize-add-connection:visited {
- display: block;
- vertical-align: middle;
text-decoration: none;
}
@@ -73,6 +73,17 @@ div.publicize-service-right li {
list-style-type: none;
}
+.publicize-info {
+ display: inline-block;
+ width: 16px;
+ height: 16px;
+ background-image: url( info-2x.png );
+ background-size: 16px 16px;
+ text-indent: -99999px;
+ float: right;
+ cursor: help;
+}
+
.pub-disconnect-button {
-webkit-border-image: none;
border-bottom-color: #CCC;
@@ -146,25 +157,25 @@ table#option-profile td.details {
font-weight: bold; color: #333333
}
-table#option-fb-fanpage td {
+table#option-fb-fanpage td, table#option-fanpage td {
font-family: "Lucida Grande",Verdana,Arial,sans-serif;
vertical-align: middle;
}
-table#option-fb-fanpage td.thumbnail {
+table#option-fb-fanpage td.thumbnail, table#option-fanpage td.thumbnail {
padding: 5px 20px 5px 20px;
}
-table#option-fb-fanpage td.details {
+table#option-fb-fanpage td.details, table#option-fanpage td.details {
width: 130px;
padding-right: 10px;
}
-table#option-fb-fanpage td.details span.name {
+table#option-fb-fanpage td.details span.name, table#option-fanpage td.details span.name {
font-weight: bold; color: #333333;
}
-table#option-fb-fanpage td.details span.category {
+table#option-fb-fanpage td.details span.category, table#option-fanpage td.details span.category {
font-size: 10px; color: #888888;
}
diff --git a/plugins/jetpack/modules/publicize/assets/publicize.js b/plugins/jetpack/modules/publicize/assets/publicize.js
index 4ae37fb7..63e76540 100644
--- a/plugins/jetpack/modules/publicize/assets/publicize.js
+++ b/plugins/jetpack/modules/publicize/assets/publicize.js
@@ -70,7 +70,7 @@ jQuery( function( $ ) {
$.post( ajaxurl, 'action=publicize_'+ service + '_options_save&connection=' + connection + '&selected_id=' + id + '&token=' + token + '&type=' + type + '&_wpnonce=' + nonce + '&global=' + global_conn + '&global_nonce=' + global_nonce, function( response ) {
tb_remove();
- window.location = 'options-general.php?page=sharing';
+ top.location = 'options-general.php?page=sharing';
} );
} );
diff --git a/plugins/jetpack/modules/publicize/assets/rtl/publicize-rtl.css b/plugins/jetpack/modules/publicize/assets/rtl/publicize-rtl.css
index 0acaa428..509f8367 100644
--- a/plugins/jetpack/modules/publicize/assets/rtl/publicize-rtl.css
+++ b/plugins/jetpack/modules/publicize/assets/rtl/publicize-rtl.css
@@ -1,4 +1,4 @@
-/* This file was automatically generated on Jun 24 2013 19:39:49 */
+/* This file was automatically generated on Sep 07 2013 23:01:10 */
div#publicize-services-block {
display: inline-block;
@@ -19,10 +19,12 @@ span.pub-logos {
vertical-align: top;
}
-span#facebook { background: url( ../facebook-logo.png ) 50% 19px no-repeat; background-size: 125px 47px; }
+span#facebook { background: url( ../facebook-logo.png ) 50% 19px no-repeat; background-size: 125px 41px; }
span#twitter { background: url( ../twitter-logo.png ) 50% 19px no-repeat; background-size: 125px 47px; }
span#linkedin { background: url( ../linkedin-logo.png ) 50% 19px no-repeat; background-size: 125px 47px; }
span#tumblr { background: url( ../tumblr-logo.png ) 50% 19px no-repeat; background-size: 125px 47px; }
+span#path { background: url( ../path-logo.png ) 50% 19px no-repeat; background-size: 85px 32px; }
+span#google_plus { background: url( ../gplus.png ) 50% 19px no-repeat; background-size: 125px; }
a.publicize-profile-link, a.publicize-profile-link:visited {
text-decoration: none;
@@ -33,8 +35,6 @@ a.publicize-profile-link:hover {
}
a.publicize-add-connection, a.publicize-add-connection:visited {
- display: block;
- vertical-align: middle;
text-decoration: none;
}
@@ -75,6 +75,17 @@ div.publicize-service-right li {
list-style-type: none;
}
+.publicize-info {
+ display: inline-block;
+ width: 16px;
+ height: 16px;
+ background-image: url( ../info-2x.png );
+ background-size: 16px 16px;
+ text-indent: -99999px;
+ float: left;
+ cursor: help;
+}
+
.pub-disconnect-button {
-webkit-border-image: none;
border-bottom-color: #CCC;
@@ -148,25 +159,25 @@ table#option-profile td.details {
font-weight: bold; color: #333333
}
-table#option-fb-fanpage td {
+table#option-fb-fanpage td, table#option-fanpage td {
font-family: "Lucida Grande",Verdana,Arial,sans-serif;
vertical-align: middle;
}
-table#option-fb-fanpage td.thumbnail {
+table#option-fb-fanpage td.thumbnail, table#option-fanpage td.thumbnail {
padding: 5px 20px 5px 20px;
}
-table#option-fb-fanpage td.details {
+table#option-fb-fanpage td.details, table#option-fanpage td.details {
width: 130px;
padding-left: 10px;
}
-table#option-fb-fanpage td.details span.name {
+table#option-fb-fanpage td.details span.name, table#option-fanpage td.details span.name {
font-weight: bold; color: #333333;
}
-table#option-fb-fanpage td.details span.category {
+table#option-fb-fanpage td.details span.category, table#option-fanpage td.details span.category {
font-size: 10px; color: #888888;
}
diff --git a/plugins/jetpack/modules/publicize/assets/yahoo-logo.png b/plugins/jetpack/modules/publicize/assets/yahoo-logo.png
deleted file mode 100644
index b7ffe2bb..00000000
--- a/plugins/jetpack/modules/publicize/assets/yahoo-logo.png
+++ /dev/null
Binary files differ
diff --git a/plugins/jetpack/modules/publicize/publicize-jetpack.php b/plugins/jetpack/modules/publicize/publicize-jetpack.php
index 454f9999..6a985b09 100644
--- a/plugins/jetpack/modules/publicize/publicize-jetpack.php
+++ b/plugins/jetpack/modules/publicize/publicize-jetpack.php
@@ -11,11 +11,13 @@ class Publicize extends Publicize_Base {
add_action( 'wp_ajax_publicize_facebook_options_page', array( $this, 'options_page_facebook' ) );
add_action( 'wp_ajax_publicize_twitter_options_page', array( $this, 'options_page_twitter' ) );
add_action( 'wp_ajax_publicize_linkedin_options_page', array( $this, 'options_page_linkedin' ) );
+ add_action( 'wp_ajax_publicize_path_options_page', array( $this, 'options_page_path' ) );
add_action( 'wp_ajax_publicize_tumblr_options_save', array( $this, 'options_save_tumblr' ) );
add_action( 'wp_ajax_publicize_facebook_options_save', array( $this, 'options_save_facebook' ) );
add_action( 'wp_ajax_publicize_twitter_options_save', array( $this, 'options_save_twitter' ) );
add_action( 'wp_ajax_publicize_linkedin_options_save', array( $this, 'options_save_linkedin' ) );
+ add_action( 'wp_ajax_publicize_path_options_save', array( $this, 'options_save_path' ) );
add_action( 'load-settings_page_sharing', array( $this, 'force_user_connection' ) );
@@ -69,7 +71,7 @@ class Publicize extends Publicize_Base {
}
function get_connections( $service_name, $_blog_id = false, $_user_id = false ) {
- $connections = Jetpack::get_option( 'publicize_connections' );
+ $connections = Jetpack_Options::get_option( 'publicize_connections' );
$connections_to_return = array();
if ( !empty( $connections ) && is_array( $connections ) ) {
if ( !empty( $connections[$service_name] ) ) {
@@ -110,7 +112,7 @@ class Publicize extends Publicize_Base {
$verification = Jetpack::create_nonce( 'publicize' );
$stats_options = get_option( 'stats_options' );
- $wpcom_blog_id = Jetpack::get_option('id');
+ $wpcom_blog_id = Jetpack_Options::get_option('id');
$wpcom_blog_id = !empty( $wpcom_blog_id ) ? $wpcom_blog_id : $stats_options['blog_id'];
$user = wp_get_current_user();
@@ -137,7 +139,7 @@ class Publicize extends Publicize_Base {
if ( !$xml->isError() ) {
$response = $xml->getResponse();
- Jetpack::update_option( 'publicize_connections', $response );
+ Jetpack_Options::update_option( 'publicize_connections', $response );
}
break;
@@ -153,7 +155,7 @@ class Publicize extends Publicize_Base {
if ( !$xml->isError() ) {
$response = $xml->getResponse();
- Jetpack::update_option( 'publicize_connections', $response );
+ Jetpack_Options::update_option( 'publicize_connections', $response );
}
add_action( 'admin_notices', array( $this, 'display_disconnected' ) );
break;
@@ -228,7 +230,7 @@ class Publicize extends Publicize_Base {
if ( !$xml->isError() ) {
$response = $xml->getResponse();
- Jetpack::update_option( 'publicize_connections', $response );
+ Jetpack_Options::update_option( 'publicize_connections', $response );
}
}
}
@@ -291,6 +293,7 @@ class Publicize extends Publicize_Base {
'twitter' => array(),
'linkedin' => array(),
'tumblr' => array(),
+ 'path' => array(),
);
if ( 'all' == $filter ) {
@@ -330,9 +333,9 @@ class Publicize extends Publicize_Base {
*/
function options_page_facebook() {
- $connected_services = Jetpack::get_option( 'publicize_connections' );
+ $connected_services = Jetpack_Options::get_option( 'publicize_connections' );
$connection = $connected_services['facebook'][$_REQUEST['connection']];
- $options_to_show = $connection['connection_data']['meta']['options_responses'];
+ $options_to_show = ( ! empty( $connection['connection_data']['meta']['options_responses'] ) ? $connection['connection_data']['meta']['options_responses'] : false );
// Nonce check
check_admin_referer( 'options_page_facebook_' . $_REQUEST['connection'] );
@@ -466,7 +469,7 @@ class Publicize extends Publicize_Base {
if ( !$xml->isError() ) {
$response = $xml->getResponse();
- Jetpack::update_option( 'publicize_connections', $response );
+ Jetpack_Options::update_option( 'publicize_connections', $response );
}
$this->globalization();
@@ -476,7 +479,7 @@ class Publicize extends Publicize_Base {
// Nonce check
check_admin_referer( 'options_page_tumblr_' . $_REQUEST['connection'] );
- $connected_services = Jetpack::get_option( 'publicize_connections' );
+ $connected_services = Jetpack_Options::get_option( 'publicize_connections' );
$connection = $connected_services['tumblr'][$_POST['connection']];
$options_to_show = $connection['connection_data']['meta']['options_responses'];
$request = $options_to_show[0];
@@ -560,7 +563,7 @@ class Publicize extends Publicize_Base {
if ( !$xml->isError() ) {
$response = $xml->getResponse();
- Jetpack::update_option( 'publicize_connections', $response );
+ Jetpack_Options::update_option( 'publicize_connections', $response );
}
$this->globalization();
@@ -568,9 +571,11 @@ class Publicize extends Publicize_Base {
function options_page_twitter() { Publicize_UI::options_page_other( 'twitter' ); }
function options_page_linkedin() { Publicize_UI::options_page_other( 'linkedin' ); }
+ function options_page_path() { Publicize_UI::options_page_other( 'path' ); }
function options_save_twitter() { $this->options_save_other( 'twitter' ); }
function options_save_linkedin() { $this->options_save_other( 'linkedin' ); }
+ function options_save_path() { $this->options_save_other( 'path' ); }
function options_save_other( $service_name ) {
// Nonce check
diff --git a/plugins/jetpack/modules/publicize/ui.php b/plugins/jetpack/modules/publicize/ui.php
index 06d0d435..1011f7c0 100644
--- a/plugins/jetpack/modules/publicize/ui.php
+++ b/plugins/jetpack/modules/publicize/ui.php
@@ -77,6 +77,12 @@ class Publicize_UI {
</div><?php
}
+ public static function denied_notice() { ?>
+ <div class='updated'>
+ <p><?php _e ( "You have chosen not to connect your blog. Please click 'accept' when prompted if you wish to connect your accounts.", 'jetpack' ); ?></p>
+ </div><?php
+ }
+
/**
* Lists the current user's publicized accounts for the blog
* looks exactly like Publicize v1 for now, UI and functionality updates will come after the move to keyring
@@ -87,6 +93,12 @@ class Publicize_UI {
<form action="" id="publicize-form">
<h3 id="publicize"><?php _e( 'Publicize', 'jetpack' ) ?></h3>
+
+ <?php
+ if ( !empty( $_GET['action'] ) && 'deny' == $_GET['action'] )
+ $this->denied_notice();
+ ?>
+
<p>
<?php esc_html_e( 'Connect your blog to popular social networking sites and automatically share new posts with your friends.', 'jetpack' ) ?>
<?php esc_html_e( 'You can make a connection for just yourself or for all users on your blog. Shared connections are marked with the (Shared) text.', 'jetpack' ); ?>
@@ -99,7 +111,7 @@ class Publicize_UI {
$doc_link = "http://en.support.wordpress.com/publicize/";
?>
- <p>&rarr; <a href="<?php echo esc_url( $doc_link ); ?>"><?php esc_html_e( 'More information on using Publicize.', 'jetpack' ); ?></a></p>
+ <p>&rarr; <a href="<?php echo esc_url( $doc_link ); ?>" target="_blank"><?php esc_html_e( 'More information on using Publicize.', 'jetpack' ); ?></a></p>
<div id="publicize-services-block">
<?php
@@ -108,7 +120,7 @@ class Publicize_UI {
?>
<div class="publicize-service-entry">
<div id="<?php echo esc_attr( $name ); ?>" class="publicize-service-left">
- <a href="<?php echo esc_url( $connect_url ); ?>"><span class="pub-logos" id="<?php echo esc_attr( $name ); ?>">&nbsp;</span></a>
+ <a href="<?php echo esc_url( $connect_url ); ?>" target="_top"><span class="pub-logos" id="<?php echo esc_attr( $name ); ?>">&nbsp;</span></a>
</div>
<div class="publicize-service-right">
@@ -141,7 +153,7 @@ class Publicize_UI {
<li>
<?php
if ( !empty( $profile_link ) ) : ?>
- <a class="publicize-profile-link" href="<?php echo esc_url( $profile_link ); ?>">
+ <a class="publicize-profile-link" href="<?php echo esc_url( $profile_link ); ?>" target="_top">
<?php echo esc_html( $connection_display ); ?>
</a><?php
else :
@@ -153,11 +165,11 @@ class Publicize_UI {
<small>(<?php esc_html_e( 'Shared', 'jetpack' ); ?>)</small>
<?php if ( current_user_can( $this->publicize->GLOBAL_CAP ) ) : ?>
- <a class="pub-disconnect-button" title="<?php esc_html_e( 'Disconnect', 'jetpack' ); ?>" href="<?php echo esc_url( $disconnect_url ); ?>">×</a>
+ <a class="pub-disconnect-button" title="<?php esc_html_e( 'Disconnect', 'jetpack' ); ?>" href="<?php echo esc_url( $disconnect_url ); ?>" target="_top">×</a>
<?php endif; ?>
<?php else : ?>
- <a class="pub-disconnect-button" title="<?php esc_html_e( 'Disconnect', 'jetpack' ); ?>" href="<?php echo esc_url( $disconnect_url ); ?>">×</a>
+ <a class="pub-disconnect-button" title="<?php esc_html_e( 'Disconnect', 'jetpack' ); ?>" href="<?php echo esc_url( $disconnect_url ); ?>" target="_top">×</a>
<?php endif; ?>
</li>
@@ -166,7 +178,13 @@ class Publicize_UI {
?>
</ul>
<?php endif; ?>
- <a id="<?php echo esc_attr( $name ); ?>" class="publicize-add-connection" href="<?php echo esc_url( $connect_url); ?>"><?php echo esc_html( sprintf( __( 'Add new %s connection.', 'jetpack' ), $this->publicize->get_service_label( $name ) ) ); ?></a>
+ <a id="<?php echo esc_attr( $name ); ?>" class="publicize-add-connection" href="<?php echo esc_url( $connect_url); ?>" target="_top"><?php echo esc_html( sprintf( __( 'Add new %s connection.', 'jetpack' ), $this->publicize->get_service_label( $name ) ) ); ?></a>
+ <?php
+ $help = apply_filters( 'publicize_help_text_' . $name, false );
+ if ( $help ) {
+ echo ' <a href="javascript:void(0);" title="' . esc_attr( $help ) . '" class="publicize-info">?</a>';
+ }
+ ?>
</div>
</div>
<?php endforeach; ?>
@@ -464,6 +482,10 @@ jQuery( function($) {
$checked = $skip != 1 || $done;
$checked = apply_filters( 'publicize_checkbox_default', $checked, $post->ID, $name, $connection );
+ // Force the checkbox to be checked if the post was DONE, regardless of what the filter does
+ if ( $done )
+ $checked = true;
+
// This post has been handled, so disable everything
if ( $all_done )
$disabled = ' disabled="disabled"';
diff --git a/plugins/jetpack/modules/random-redirect.php b/plugins/jetpack/modules/random-redirect.php
new file mode 100644
index 00000000..2c568dbf
--- /dev/null
+++ b/plugins/jetpack/modules/random-redirect.php
@@ -0,0 +1,43 @@
+<?php
+/*
+Plugin Name: Random Redirect
+Plugin URI: http://wordpress.org/extend/plugins/random-redirect/
+Description: Allows you to create a link to yourblog.example.com/?random which will redirect someone to a random post on your blog, in a StumbleUpon-like fashion.
+Version: 1.2-wpcom
+Author: Matt Mullenweg
+Author URI: http://photomatt.net/
+*/
+
+function matt_random_redirect() {
+ // Acceptables URL formats: /[...]/?random=[post type], /?random, /&random, /&random=1
+ if ( ! isset( $_GET['random'] ) && ! in_array( strtolower( $_SERVER['REQUEST_URI'] ), array( '/&random', '/&random=1' ) ) )
+ return;
+
+ // Ignore requests that include more than just the random parameter.
+ if ( ! empty( $_POST ) || ( isset( $_GET['random'] ) && count( $_GET ) > 1 ) )
+ return;
+
+ // Persistent AppEngine abuse. ORDER BY RAND is expensive.
+ if ( strstr( $_SERVER['HTTP_USER_AGENT'], 'AppEngine-Google' ) )
+ wp_die( 'Please <a href="http://en.support.wordpress.com/contact/">contact support</a>' );
+
+ // Use the post type of the current page as the context for the random lookup.
+ $post_type = get_post_type();
+
+ // /?random should always show a random post, even if the home page is a static page.
+ if ( '/' == $_SERVER['DOCUMENT_URI'] )
+ $post_type = 'post';
+ else
+ $post_type = get_post_type();
+
+ if ( ! $post_type )
+ $post_type = 'post';
+
+ global $wpdb;
+ $random_id = $wpdb->get_var( $wpdb->prepare( "SELECT ID FROM $wpdb->posts WHERE post_type = %s AND post_password = '' AND post_status = 'publish' ORDER BY RAND() LIMIT 1", $post_type ) );
+ $permalink = get_permalink( $random_id );
+ wp_safe_redirect( $permalink );
+ exit;
+}
+
+add_action( 'template_redirect', 'matt_random_redirect' );
diff --git a/plugins/jetpack/modules/sharedaddy.php b/plugins/jetpack/modules/sharedaddy.php
index 924176fa..0cb830e2 100644
--- a/plugins/jetpack/modules/sharedaddy.php
+++ b/plugins/jetpack/modules/sharedaddy.php
@@ -6,6 +6,7 @@
* First Introduced: 1.1
* Major Changes In: 1.2
* Requires Connection: No
+ * Auto Activate: Yes
*/
if ( !function_exists( 'sharing_init' ) )
diff --git a/plugins/jetpack/modules/sharedaddy/admin-sharing.css b/plugins/jetpack/modules/sharedaddy/admin-sharing.css
index 51234530..620d6fc3 100644
--- a/plugins/jetpack/modules/sharedaddy/admin-sharing.css
+++ b/plugins/jetpack/modules/sharedaddy/admin-sharing.css
@@ -164,8 +164,8 @@
.preview-google-plus-1 .option-smart-on {
background: #FFF url(images/smart-googleplus1.png) no-repeat top left;
- background-size: 60px 20px;
- width: 60px;
+ background-size: 96px 20px;
+ width: 96px;
height: 20px;
}
diff --git a/plugins/jetpack/modules/sharedaddy/images/googleplus1.png b/plugins/jetpack/modules/sharedaddy/images/googleplus1.png
index ee687af1..a2963ece 100644
--- a/plugins/jetpack/modules/sharedaddy/images/googleplus1.png
+++ b/plugins/jetpack/modules/sharedaddy/images/googleplus1.png
Binary files differ
diff --git a/plugins/jetpack/modules/sharedaddy/images/googleplus1@2x.png b/plugins/jetpack/modules/sharedaddy/images/googleplus1@2x.png
index 60dd1c03..6f59661d 100644
--- a/plugins/jetpack/modules/sharedaddy/images/googleplus1@2x.png
+++ b/plugins/jetpack/modules/sharedaddy/images/googleplus1@2x.png
Binary files differ
diff --git a/plugins/jetpack/modules/sharedaddy/images/icon-facebook-2x.png b/plugins/jetpack/modules/sharedaddy/images/icon-facebook-2x.png
index 10b36803..faed26b0 100644
--- a/plugins/jetpack/modules/sharedaddy/images/icon-facebook-2x.png
+++ b/plugins/jetpack/modules/sharedaddy/images/icon-facebook-2x.png
Binary files differ
diff --git a/plugins/jetpack/modules/sharedaddy/images/icon-facebook.png b/plugins/jetpack/modules/sharedaddy/images/icon-facebook.png
index 91d3702f..8b5ce251 100644
--- a/plugins/jetpack/modules/sharedaddy/images/icon-facebook.png
+++ b/plugins/jetpack/modules/sharedaddy/images/icon-facebook.png
Binary files differ
diff --git a/plugins/jetpack/modules/sharedaddy/images/smart-googleplus1.png b/plugins/jetpack/modules/sharedaddy/images/smart-googleplus1.png
index ba593f5a..13e257fa 100644
--- a/plugins/jetpack/modules/sharedaddy/images/smart-googleplus1.png
+++ b/plugins/jetpack/modules/sharedaddy/images/smart-googleplus1.png
Binary files differ
diff --git a/plugins/jetpack/modules/sharedaddy/images/smart-googleplus1@2x.png b/plugins/jetpack/modules/sharedaddy/images/smart-googleplus1@2x.png
index 0bb999a4..5bbb092b 100644
--- a/plugins/jetpack/modules/sharedaddy/images/smart-googleplus1@2x.png
+++ b/plugins/jetpack/modules/sharedaddy/images/smart-googleplus1@2x.png
Binary files differ
diff --git a/plugins/jetpack/modules/sharedaddy/sharing-service.php b/plugins/jetpack/modules/sharedaddy/sharing-service.php
index 8b10b6e0..80f4d8de 100644
--- a/plugins/jetpack/modules/sharedaddy/sharing-service.php
+++ b/plugins/jetpack/modules/sharedaddy/sharing-service.php
@@ -460,6 +460,9 @@ add_action( 'template_redirect', 'sharing_process_requests', 9 );
function sharing_display( $text = '', $echo = false ) {
global $post, $wp_current_filter;
+ if ( empty( $post ) )
+ return $text;
+
if ( is_preview() ) {
return $text;
}
@@ -620,7 +623,7 @@ function get_base_recaptcha_lang_code() {
'tr' => 'tr'
);
- $blog_lang_code = function_exists( 'get_blog_lang_code' ) ? get_blog_lang_code() : get_bloginfo( 'language' );
+ $blog_lang_code = function_exists( 'get_blog_lang_code' ) ? get_blog_lang_code() : get_bloginfo( 'language' );
if( isset( $base_recaptcha_lang_code_mapping[ $blog_lang_code ] ) )
return $base_recaptcha_lang_code_mapping[ $blog_lang_code ];
diff --git a/plugins/jetpack/modules/sharedaddy/sharing-sources.php b/plugins/jetpack/modules/sharedaddy/sharing-sources.php
index 53164741..1599eba9 100644
--- a/plugins/jetpack/modules/sharedaddy/sharing-sources.php
+++ b/plugins/jetpack/modules/sharedaddy/sharing-sources.php
@@ -827,7 +827,7 @@ class Share_GooglePlus1 extends Sharing_Source {
}
public function get_name() {
- return __( 'Google +1', 'jetpack' );
+ return __( 'Google', 'jetpack' );
}
public function has_custom_button_style() {
@@ -838,11 +838,9 @@ class Share_GooglePlus1 extends Sharing_Source {
$share_url = $this->get_share_url( $post->ID );
if ( $this->smart ) {
- return '<div class="googleplus1_button"><div class="g-plusone" data-size="medium" data-callback="sharing_plusone" data-href="' . esc_url( $share_url ) . '"></div></div>';
+ return '<div class="googleplus1_button"><div class="g-plus" data-action="share" data-annotation="bubble" data-href="' . esc_url( $share_url ) . '"></div></div>';
} else {
- //if ( 'icon-text' == $this->button_style || 'text' == $this->button_style )
- //sharing_register_post_for_share_counts( $post->ID );
- return $this->get_link( get_permalink( $post->ID ), _x( 'Google +1', 'share to', 'jetpack' ), __( 'Click to share on Google+', 'jetpack' ), 'share=google-plus-1', 'sharing-google-' . $post->ID );
+ return $this->get_link( get_permalink( $post->ID ), _x( 'Google', 'share to', 'jetpack' ), __( 'Click to share on Google+', 'jetpack' ), 'share=google-plus-1', 'sharing-google-' . $post->ID );
}
}
@@ -869,20 +867,15 @@ class Share_GooglePlus1 extends Sharing_Source {
if ( $this->smart ) { ?>
<script type="text/javascript">
- function sharing_plusone( obj ) {
- jQuery.ajax( {
- url: '<?php echo get_permalink( $post->ID ) . '?share=google-plus-1'; ?>',
- type: 'POST',
- data: obj
- } );
- }
- jQuery( document.body ).on( 'post-load', function() {
- gapi.plusone.go();
- });
+ (function() {
+ var po = document.createElement('script'); po.type = 'text/javascript'; po.async = true;
+ po.src = 'https://apis.google.com/js/plusone.js';
+ var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(po, s);
+ })();
</script>
- <script type="text/javascript" src="//apis.google.com/js/plusone.js"></script> <?php
+ <?php
} else {
- $this->js_dialog( 'google-plus-1', array( 'width' => 600, 'height' => 600 ) );
+ $this->js_dialog( 'google-plus-1', array( 'width' => 480, 'height' => 550 ) );
}
}
diff --git a/plugins/jetpack/modules/sharedaddy/sharing.php b/plugins/jetpack/modules/sharedaddy/sharing.php
index 974fe741..24a1e91f 100644
--- a/plugins/jetpack/modules/sharedaddy/sharing.php
+++ b/plugins/jetpack/modules/sharedaddy/sharing.php
@@ -166,6 +166,7 @@ class Sharing_Admin {
<?php if ( current_user_can( 'manage_options' ) ) : ?>
+ <div class="share_manage_options">
<h3><?php _e( 'Sharing Buttons', 'jetpack' ) ?></h3>
<p><?php _e( 'Add sharing buttons to your blog and allow your visitors to share posts with their friends.', 'jetpack' ) ?></p>
@@ -402,7 +403,7 @@ class Sharing_Admin {
<input type="hidden" name="_wpnonce" value="<?php echo wp_create_nonce( 'sharing-new_service' );?>" />
</form>
</div>
-
+ </div>
<?php endif; ?>
diff --git a/plugins/jetpack/modules/shortcodes.php b/plugins/jetpack/modules/shortcodes.php
index ce54d632..e9362aff 100644
--- a/plugins/jetpack/modules/shortcodes.php
+++ b/plugins/jetpack/modules/shortcodes.php
@@ -7,6 +7,7 @@
* First Introduced: 1.1
* Major Changes In: 1.2
* Requires Connection: No
+ * Auto Activate: Yes
*/
/**
diff --git a/plugins/jetpack/modules/shortcodes/css/rtl/slideshow-shortcode-rtl.css b/plugins/jetpack/modules/shortcodes/css/rtl/slideshow-shortcode-rtl.css
index eea868af..fdfa6aea 100644
--- a/plugins/jetpack/modules/shortcodes/css/rtl/slideshow-shortcode-rtl.css
+++ b/plugins/jetpack/modules/shortcodes/css/rtl/slideshow-shortcode-rtl.css
@@ -1,4 +1,4 @@
-/* This file was automatically generated on Mar 25 2013 08:14:36 */
+/* This file was automatically generated on Aug 22 2013 17:45:44 */
.slideshow-window {
background-color: #222;
@@ -8,6 +8,7 @@
-webkit-border-radius: 11px;
-khtml-border-radius: 11px;
margin-bottom: 20px;
+ height: 410px;
}
.slideshow-window, .slideshow-window * {
diff --git a/plugins/jetpack/modules/shortcodes/css/slideshow-shortcode.css b/plugins/jetpack/modules/shortcodes/css/slideshow-shortcode.css
index 13cef497..fb3ec530 100644
--- a/plugins/jetpack/modules/shortcodes/css/slideshow-shortcode.css
+++ b/plugins/jetpack/modules/shortcodes/css/slideshow-shortcode.css
@@ -6,6 +6,7 @@
-webkit-border-radius: 11px;
-khtml-border-radius: 11px;
margin-bottom: 20px;
+ height: 410px;
}
.slideshow-window, .slideshow-window * {
diff --git a/plugins/jetpack/modules/shortcodes/css/style.css b/plugins/jetpack/modules/shortcodes/css/style.css
new file mode 100644
index 00000000..137663a3
--- /dev/null
+++ b/plugins/jetpack/modules/shortcodes/css/style.css
@@ -0,0 +1,187 @@
+/**
+* 1. Fullscreen styles
+*/
+html.presentation-wrapper-fullscreen-parent,
+body.presentation-wrapper-fullscreen-parent {
+ overflow: hidden !important;
+}
+
+.presentation-wrapper-fullscreen-parent #wpadminbar {
+ display: none;
+}
+
+.presentation-wrapper-fullscreen,
+.presentation-wrapper-fullscreen-parent {
+ min-width: 100% !important;
+ min-height: 100% !important;
+ position: absolute !important;
+ top: 0 !important;
+ right: 0 !important;
+ bottom: 0 !important;
+ left: 0 !important;
+ margin: 0 !important;
+ padding: 0 !important;
+ z-index: 10000 !important;
+}
+
+.presentation-wrapper-fullscreen {
+ background-color: #808080;
+ border: none !important;
+}
+
+.presentation-wrapper-fullscreen .nav-arrow-left,
+.presentation-wrapper-fullscreen .nav-arrow-right {
+ z-index: 20001;
+}
+
+.presentation-wrapper-fullscreen .nav-fullscreen-button {
+ z-index: 20002;
+}
+
+
+/**
+ * 2. General presentation styles
+ */
+.presentation-wrapper {
+ margin: 20px auto;
+ border: 1px solid #e5e5e5;
+ overflow: hidden;
+}
+
+.presentation {
+ position: relative;
+ margin: 0;
+ overflow: hidden;
+ outline: none;
+}
+
+/**
+ * jmpress requires that step sizes are explicitly defined
+ * as it inserts sizeless divs before the steps. These
+ * dimensions are set by the js code on initialization
+ */
+.presentation,
+.presentation .step {
+ background-repeat: no-repeat;
+ background-position: center;
+ background-size: 100% 100%;
+}
+
+/**
+ * Opacity transition durations are set by the js code
+ * so they match the presentation animation durations
+ */
+.presentation .step.fade:not(.active) {
+ opacity: 0;
+}
+
+.presentation .slide-content {
+ padding: 30px;
+}
+
+
+/**
+ * 3. Styles for the navigation arrows
+ */
+.presentation .nav-arrow-left,
+.presentation .nav-arrow-right,
+.presentation .nav-fullscreen-button {
+ position: absolute;
+ width: 34px;
+ background-repeat: no-repeat;
+ z-index: 2;
+ opacity: 0;
+
+ -webkit-transition : opacity .25s;
+ -moz-transition : opacity .25s;
+ -ms-transition : opacity .25s;
+ -o-transition : opacity .25s;
+ transition : opacity .25s;
+}
+
+.presentation .nav-arrow-left,
+.presentation .nav-arrow-right {
+ height: 100%;
+ background-image: url(../images/slide-nav.png);
+ background-size: 450% 61px;
+}
+
+.presentation .nav-arrow-left {
+ left: 0;
+ background-position: 4px 50%;
+}
+
+.presentation .nav-arrow-right {
+ right: 0;
+ background-position: -120px 50%;
+}
+
+.presentation .nav-fullscreen-button {
+ width: 32px;
+ height: 32px;
+ margin: 4px;
+ bottom: 0;
+ right: 0;
+ z-index: 3;
+ background-image: url(../images/expand.png);
+ background-size: 100% 100%;
+}
+
+.presentation:hover .nav-arrow-left,
+.presentation:hover .nav-arrow-right {
+ opacity: 1;
+}
+
+.presentation:hover .nav-fullscreen-button {
+ opacity: 0.8;
+}
+
+.presentation-wrapper-fullscreen .nav-fullscreen-button {
+ background-image: url(../images/collapse.png);
+}
+
+/**
+ * 4. Styles for the autoplay overlay
+ */
+.presentation .autoplay-overlay {
+ height: 15%;
+ width: 80%;
+ margin: 30% 10%;
+ position: relative;
+ z-index: 100;
+ display: table;
+ border-radius: 50px;
+ background-color: #e5e5e5;
+ background-color: rgba(0, 0, 0, 0.75);
+
+ -webkit-transition : opacity .5s;
+ -moz-transition : opacity .5s;
+ -ms-transition : opacity .5s;
+ -o-transition : opacity .5s;
+ transition : opacity .5s;
+}
+
+.presentation .autoplay-overlay .overlay-msg {
+ position: relative;
+ display: table-cell;
+ text-align: center;
+ vertical-align: middle;
+ color: #fff;
+}
+
+/**
+ * 5. Styles for fading steps
+ */
+.presentation .will-fade {
+ opacity: 0;
+}
+
+.presentation .do-fade {
+ opacity: 1;
+
+ -webkit-transition : opacity .5s;
+ -moz-transition : opacity .5s;
+ -ms-transition : opacity .5s;
+ -o-transition : opacity .5s;
+ transition : opacity .5s;
+} \ No newline at end of file
diff --git a/plugins/jetpack/modules/shortcodes/facebook.php b/plugins/jetpack/modules/shortcodes/facebook.php
new file mode 100644
index 00000000..39152a2c
--- /dev/null
+++ b/plugins/jetpack/modules/shortcodes/facebook.php
@@ -0,0 +1,44 @@
+<?php
+
+/**
+ * Facebook embeds
+ */
+
+define( 'JETPACK_FACEBOOK_EMBED_REGEX', '#^https?://(www.)?facebook\.com/([^/]+)/posts/([^/]+)?#' );
+define( 'JETPACK_FACEBOOK_PHOTO_EMBED_REGEX', '#^https?://(www.)?facebook\.com/photo.php\?([^\s]+)#' );
+
+// Example URL: https://www.facebook.com/VenusWilliams/posts/10151647007373076
+wp_embed_register_handler( 'facebook', JETPACK_FACEBOOK_EMBED_REGEX, 'jetpack_facebook_embed_handler' );
+// Photos are handled on a different endpoint; e.g. https://www.facebook.com/photo.php?fbid=10151609960150073&set=a.398410140072.163165.106666030072&type=1
+wp_embed_register_handler( 'facebook-photo', JETPACK_FACEBOOK_PHOTO_EMBED_REGEX, 'jetpack_facebook_embed_handler' );
+
+function jetpack_facebook_embed_handler( $matches, $attr, $url ) {
+ static $did_script;
+
+ if ( ! $did_script ) {
+ $did_script = true;
+ add_action( 'wp_footer', 'jetpack_facebook_add_script' );
+ }
+
+ return sprintf( '<fb:post href="%s"></fb:post>', esc_url( $url ) );
+}
+
+function jetpack_facebook_add_script() {
+ ?>
+ <div id="fb-root"></div> <script>(function(d, s, id) { var js, fjs = d.getElementsByTagName(s)[0]; if (d.getElementById(id)) return; js = d.createElement(s); js.id = id; js.src = "//connect.facebook.net/en_US/all.js#xfbml=1"; fjs.parentNode.insertBefore(js, fjs); }(document, "script", "facebook-jssdk"));</script>
+ <?php
+}
+
+add_shortcode( 'facebook', 'jetpack_facebook_shortcode_handler' );
+
+function jetpack_facebook_shortcode_handler( $atts ) {
+ global $wp_embed;
+
+ if ( empty( $atts['url'] ) )
+ return;
+
+ if ( ! preg_match( JETPACK_FACEBOOK_EMBED_REGEX, $atts['url'] ) && ! preg_match( JETPACK_FACEBOOK_PHOTO_EMBED_REGEX, $atts['url'] ) )
+ return;
+
+ return $wp_embed->shortcode( $atts, $atts['url'] );
+}
diff --git a/plugins/jetpack/modules/shortcodes/googleplus.php b/plugins/jetpack/modules/shortcodes/googleplus.php
new file mode 100644
index 00000000..95d8b813
--- /dev/null
+++ b/plugins/jetpack/modules/shortcodes/googleplus.php
@@ -0,0 +1,41 @@
+<?php
+
+/**
+ * Google+ embeds
+ */
+
+define( 'JETPACK_GOOGLEPLUS_EMBED_REGEX', '#^https?://plus\.(sandbox\.)?google\.com/([^/]+)/posts/([^/]+)$#' );
+
+// Example URL: https://plus.google.com/114986219448604314131/posts/LgHkesWCmJo
+wp_embed_register_handler( 'googleplus', JETPACK_GOOGLEPLUS_EMBED_REGEX, 'jetpack_googleplus_embed_handler' );
+
+function jetpack_googleplus_embed_handler( $matches, $attr, $url ) {
+ static $did_script;
+
+ if ( ! $did_script ) {
+ $did_script = true;
+ add_action( 'wp_footer', 'jetpack_googleplus_add_script' );
+ }
+
+ return sprintf( '<div class="g-post" data-href="%s"></div>', esc_url( $url ) );
+}
+
+function jetpack_googleplus_add_script() {
+ ?>
+ <script src="https://apis.google.com/js/plusone.js"></script>
+ <?php
+}
+
+add_shortcode( 'googleplus', 'jetpack_googleplus_shortcode_handler' );
+
+function jetpack_googleplus_shortcode_handler( $atts ) {
+ global $wp_embed;
+
+ if ( empty( $atts['url'] ) )
+ return;
+
+ if ( ! preg_match( JETPACK_GOOGLEPLUS_EMBED_REGEX, $atts['url'] ) )
+ return;
+
+ return $wp_embed->shortcode( $atts, $atts['url'] );
+}
diff --git a/plugins/jetpack/modules/shortcodes/images/collapse.png b/plugins/jetpack/modules/shortcodes/images/collapse.png
new file mode 100644
index 00000000..6cdf84fd
--- /dev/null
+++ b/plugins/jetpack/modules/shortcodes/images/collapse.png
Binary files differ
diff --git a/plugins/jetpack/modules/shortcodes/images/expand.png b/plugins/jetpack/modules/shortcodes/images/expand.png
new file mode 100644
index 00000000..ddf11ea2
--- /dev/null
+++ b/plugins/jetpack/modules/shortcodes/images/expand.png
Binary files differ
diff --git a/plugins/jetpack/modules/shortcodes/images/slide-nav.png b/plugins/jetpack/modules/shortcodes/images/slide-nav.png
new file mode 100644
index 00000000..da6b74aa
--- /dev/null
+++ b/plugins/jetpack/modules/shortcodes/images/slide-nav.png
Binary files differ
diff --git a/plugins/jetpack/modules/shortcodes/js/jmpress.js b/plugins/jetpack/modules/shortcodes/js/jmpress.js
new file mode 100644
index 00000000..294e1fd6
--- /dev/null
+++ b/plugins/jetpack/modules/shortcodes/js/jmpress.js
@@ -0,0 +1,2721 @@
+/*!
+ * jmpress.js v0.4.5
+ * http://jmpressjs.github.com/jmpress.js
+ *
+ * A jQuery plugin to build a website on the infinite canvas.
+ *
+ * Copyright 2013 Kyle Robinson Young @shama & Tobias Koppers @sokra
+ * Licensed MIT
+ * http://www.opensource.org/licenses/mit-license.php
+ *
+ * Based on the foundation laid by Bartek Szopka @bartaz
+ *//*!
+ * jmpress.js v0.4.5
+ * http://jmpressjs.github.com/jmpress.js
+ *
+ * A jQuery plugin to build a website on the infinite canvas.
+ *
+ * Copyright 2013 Kyle Robinson Young @shama & Tobias Koppers @sokra
+ * Licensed MIT
+ * http://www.opensource.org/licenses/mit-license.php
+ *
+ * Based on the foundation laid by Bartek Szopka @bartaz
+ *//*
+ * core.js
+ * The core of jmpress.js
+ */
+(function( $, document, window, undefined ) {
+
+ 'use strict';
+
+ /**
+ * Set supported prefixes
+ *
+ * @access protected
+ * @return Function to get prefixed property
+ */
+ var pfx = (function () {
+ var style = document.createElement('dummy').style,
+ prefixes = 'Webkit Moz O ms Khtml'.split(' '),
+ memory = {};
+ return function ( prop ) {
+ if ( typeof memory[ prop ] === "undefined" ) {
+ var ucProp = prop.charAt(0).toUpperCase() + prop.substr(1),
+ props = (prop + ' ' + prefixes.join(ucProp + ' ') + ucProp).split(' ');
+ memory[ prop ] = null;
+ for ( var i in props ) {
+ if ( style[ props[i] ] !== undefined ) {
+ memory[ prop ] = props[i];
+ break;
+ }
+ }
+ }
+ return memory[ prop ];
+ };
+ }());
+
+ /**
+ * map ex. "WebkitTransform" to "-webkit-transform"
+ */
+ function mapProperty( name ) {
+ if(!name) {
+ return;
+ }
+ var index = 1 + name.substr(1).search(/[A-Z]/);
+ var prefix = name.substr(0, index).toLowerCase();
+ var postfix = name.substr(index).toLowerCase();
+ return "-" + prefix + "-" + postfix;
+ }
+ function addComma( attribute ) {
+ if(!attribute) {
+ return "";
+ }
+ return attribute + ",";
+ }
+ /**
+ * Return an jquery object only if it's not empty
+ */
+ function ifNotEmpty(el) {
+ if(el.length > 0) {
+ return el;
+ }
+ return null;
+ }
+
+ /**
+ * Default Settings
+ */
+ var defaults = {
+ /* CLASSES */
+ stepSelector: '.step'
+ ,containerClass: ''
+ ,canvasClass: ''
+ ,areaClass: ''
+ ,notSupportedClass: 'not-supported'
+
+ /* CONFIG */
+ ,fullscreen: true
+
+ /* ANIMATION */
+ ,animation: {
+ transformOrigin: 'top left'
+ ,transitionProperty: addComma(mapProperty(pfx('transform'))) + addComma(mapProperty(pfx('perspective'))) + 'opacity'
+ ,transitionDuration: '1s'
+ ,transitionDelay: '500ms'
+ ,transitionTimingFunction: 'ease-in-out'
+ ,transformStyle: "preserve-3d"
+ }
+ ,transitionDuration: 1500
+ };
+ var callbacks = {
+ 'beforeChange': 1
+ ,'beforeInitStep': 1
+ ,'initStep': 1
+ ,'beforeInit': 1
+ ,'afterInit': 1
+ ,'beforeDeinit': 1
+ ,'afterDeinit': 1
+ ,'applyStep': 1
+ ,'unapplyStep': 1
+ ,'setInactive': 1
+ ,'beforeActive': 1
+ ,'setActive': 1
+ ,'selectInitialStep': 1
+ ,'selectPrev': 1
+ ,'selectNext': 1
+ ,'selectHome': 1
+ ,'selectEnd': 1
+ ,'idle': 1
+ ,'applyTarget': 1
+ };
+ for(var callbackName in callbacks) {
+ defaults[callbackName] = [];
+ }
+
+
+ /**
+ * Initialize jmpress
+ */
+ function init( args ) {
+ args = $.extend(true, {}, args || {});
+
+ // accept functions and arrays of functions as callbacks
+ var callbackArgs = {};
+ var callbackName = null;
+ for (callbackName in callbacks) {
+ callbackArgs[callbackName] = $.isFunction( args[callbackName] ) ?
+ [ args[callbackName] ] :
+ args[callbackName];
+ args[callbackName] = [];
+ }
+
+ // MERGE SETTINGS
+ var settings = $.extend(true, {}, defaults, args);
+
+ for (callbackName in callbacks) {
+ if (callbackArgs[callbackName]) {
+ Array.prototype.push.apply(settings[callbackName], callbackArgs[callbackName]);
+ }
+ }
+
+ /*** MEMBER VARS ***/
+
+ var jmpress = $( this )
+ ,container = null
+ ,area = null
+ ,oldStyle = {
+ container: ""
+ ,area: ""
+ }
+ ,canvas = null
+ ,current = null
+ ,active = false
+ ,activeSubstep = null
+ ,activeDelegated = false;
+
+
+ /*** MEMBER FUNCTIONS ***/
+ // functions have to be called with this
+
+ /**
+ * Init a single step
+ *
+ * @param element the element of the step
+ * @param idx number of step
+ */
+ function doStepInit( element, idx ) {
+ var data = dataset( element );
+ var step = {
+ oldStyle: $(element).attr("style") || ""
+ };
+
+ var callbackData = {
+ data: data
+ ,stepData: step
+ };
+ callCallback.call(this, 'beforeInitStep', $(element), callbackData);
+ step.delegate = data.delegate;
+ callCallback.call(this, 'initStep', $(element), callbackData);
+
+ $(element).data('stepData', step);
+
+ if ( !$(element).attr('id') ) {
+ $(element).attr('id', 'step-' + (idx + 1));
+ }
+
+ callCallback.call(this, 'applyStep', $(element), callbackData);
+ }
+ /**
+ * Deinit a single step
+ *
+ * @param element the element of the step
+ */
+ function doStepDeinit( element ) {
+ var stepData = $(element).data('stepData');
+
+ $(element).attr("style", stepData.oldStyle);
+
+ callCallback.call(this, 'unapplyStep', $(element), {
+ stepData: stepData
+ });
+ }
+ /**
+ * Reapplies stepData to the element
+ *
+ * @param element
+ */
+ function doStepReapply( element ) {
+ callCallback.call(this, 'unapplyStep', $(element), {
+ stepData: element.data("stepData")
+ });
+
+ callCallback.call(this, 'applyStep', $(element), {
+ stepData: element.data("stepData")
+ });
+ }
+ /**
+ * Completly deinit jmpress
+ *
+ */
+ function deinit() {
+ if ( active ) {
+ callCallback.call(this, 'setInactive', active, {
+ stepData: $(active).data('stepData')
+ ,reason: "deinit"
+ } );
+ }
+ if (current.jmpressClass) {
+ $(jmpress).removeClass(current.jmpressClass);
+ }
+
+ callCallback.call(this, 'beforeDeinit', $(this), {});
+
+ $(settings.stepSelector, jmpress).each(function( idx ) {
+ doStepDeinit.call(jmpress, this );
+ });
+
+ container.attr("style", oldStyle.container);
+ if(settings.fullscreen) {
+ $("html").attr("style", "");
+ }
+ area.attr("style", oldStyle.area);
+ $(canvas).children().each(function() {
+ jmpress.append( $( this ) );
+ });
+ if( settings.fullscreen ) {
+ canvas.remove();
+ } else {
+ canvas.remove();
+ area.remove();
+ }
+
+ callCallback.call(this, 'afterDeinit', $(this), {});
+
+ $(jmpress).data("jmpressmethods", false);
+ }
+ /**
+ * Call a callback
+ *
+ * @param callbackName String callback which should be called
+ * @param element some arguments to the callback
+ * @param eventData
+ */
+ function callCallback( callbackName, element, eventData ) {
+ eventData.settings = settings;
+ eventData.current = current;
+ eventData.container = container;
+ eventData.parents = element ? getStepParents(element) : null;
+ eventData.current = current;
+ eventData.jmpress = this;
+ var result = {};
+ $.each( settings[callbackName], function(idx, callback) {
+ result.value = callback.call( jmpress, element, eventData ) || result.value;
+ });
+ return result.value;
+ }
+ /**
+ *
+ */
+ function getStepParents( el ) {
+ return $(el).parentsUntil(jmpress).not(jmpress).filter(settings.stepSelector);
+ }
+ /**
+ * Reselect the active step
+ *
+ * @param String type reason of reselecting step
+ */
+ function reselect( type ) {
+ return select( { step: active, substep: activeSubstep }, type);
+ }
+ /**
+ * Select a given step
+ *
+ * @param el element to select
+ * @param type reason of changing step
+ * @return Object element selected
+ */
+ function select( el, type ) {
+ var substep;
+ if ( $.isPlainObject( el ) ) {
+ substep = el.substep;
+ el = el.step;
+ }
+ if ( typeof el === 'string') {
+ el = jmpress.find( el ).first();
+ }
+ if ( !el || !$(el).data('stepData') ) {
+ return false;
+ }
+
+ scrollFix.call(this);
+
+ var step = $(el).data('stepData');
+
+ var cancelSelect = false;
+ callCallback.call(this, "beforeChange", el, {
+ stepData: step
+ ,reason: type
+ ,cancel: function() {
+ cancelSelect = true;
+ }
+ });
+ if (cancelSelect) {
+ return undefined;
+ }
+
+ var target = {};
+
+ var delegated = el;
+ if($(el).data("stepData").delegate) {
+ delegated = ifNotEmpty($(el).parentsUntil(jmpress).filter(settings.stepSelector).filter(step.delegate)) ||
+ ifNotEmpty($(el).near(step.delegate)) ||
+ ifNotEmpty($(el).near(step.delegate, true)) ||
+ ifNotEmpty($(step.delegate, jmpress));
+ if(delegated) {
+ step = delegated.data("stepData");
+ } else {
+ // Do not delegate if expression not found
+ delegated = el;
+ }
+ }
+ if ( activeDelegated ) {
+ callCallback.call(this, 'setInactive', activeDelegated, {
+ stepData: $(activeDelegated).data('stepData')
+ ,delegatedFrom: active
+ ,reason: type
+ ,target: target
+ ,nextStep: delegated
+ ,nextSubstep: substep
+ ,nextStepData: step
+ } );
+ }
+ var callbackData = {
+ stepData: step
+ ,delegatedFrom: el
+ ,reason: type
+ ,target: target
+ ,substep: substep
+ ,prevStep: activeDelegated
+ ,prevSubstep: activeSubstep
+ ,prevStepData: activeDelegated && $(activeDelegated).data('stepData')
+ };
+ callCallback.call(this, 'beforeActive', delegated, callbackData);
+ callCallback.call(this, 'setActive', delegated, callbackData);
+
+ // Set on step class on root element
+ if (current.jmpressClass) {
+ $(jmpress).removeClass(current.jmpressClass);
+ }
+ $(jmpress).addClass(current.jmpressClass = 'step-' + $(delegated).attr('id') );
+ if (current.jmpressDelegatedClass) {
+ $(jmpress).removeClass(current.jmpressDelegatedClass);
+ }
+ $(jmpress).addClass(current.jmpressDelegatedClass = 'delegating-step-' + $(el).attr('id') );
+
+ callCallback.call(this, "applyTarget", delegated, $.extend({
+ canvas: canvas
+ ,area: area
+ ,beforeActive: activeDelegated
+ }, callbackData));
+
+ active = el;
+ activeSubstep = callbackData.substep;
+ activeDelegated = delegated;
+
+ if(current.idleTimeout) {
+ clearTimeout(current.idleTimeout);
+ }
+ current.idleTimeout = setTimeout(function() {
+ callCallback.call(this, 'idle', delegated, callbackData);
+ }, Math.max(1, settings.transitionDuration - 100));
+
+ return delegated;
+ }
+ /**
+ * This should fix ANY kind of buggy scrolling
+ */
+ function scrollFix() {
+ (function fix() {
+ if ($(container)[0].tagName === "BODY") {
+ try {
+ window.scrollTo(0, 0);
+ } catch(e) {}
+ }
+ $(container).scrollTop(0);
+ $(container).scrollLeft(0);
+ function check() {
+ if ($(container).scrollTop() !== 0 ||
+ $(container).scrollLeft() !== 0) {
+ fix();
+ }
+ }
+ setTimeout(check, 1);
+ setTimeout(check, 10);
+ setTimeout(check, 100);
+ setTimeout(check, 200);
+ setTimeout(check, 400);
+ }());
+ }
+ /**
+ * Alias for select
+ */
+ function goTo( el ) {
+ return select.call(this, el, "jump" );
+ }
+ /**
+ * Goto Next Slide
+ *
+ * @return Object newly active slide
+ */
+ function next() {
+ return select.call(this, callCallback.call(this, 'selectNext', active, {
+ stepData: $(active).data('stepData')
+ ,substep: activeSubstep
+ }), "next" );
+ }
+ /**
+ * Goto Previous Slide
+ *
+ * @return Object newly active slide
+ */
+ function prev() {
+ return select.call(this, callCallback.call(this, 'selectPrev', active, {
+ stepData: $(active).data('stepData')
+ ,substep: activeSubstep
+ }), "prev" );
+ }
+ /**
+ * Goto First Slide
+ *
+ * @return Object newly active slide
+ */
+ function home() {
+ return select.call(this, callCallback.call(this, 'selectHome', active, {
+ stepData: $(active).data('stepData')
+ }), "home" );
+ }
+ /**
+ * Goto Last Slide
+ *
+ * @return Object newly active slide
+ */
+ function end() {
+ return select.call(this, callCallback.call(this, 'selectEnd', active, {
+ stepData: $(active).data('stepData')
+ }), "end" );
+ }
+ /**
+ * Manipulate the canvas
+ *
+ * @param props
+ * @return Object
+ */
+ function canvasMod( props ) {
+ css(canvas, props || {});
+ return $(canvas);
+ }
+ /**
+ * Return current step
+ *
+ * @return Object
+ */
+ function getActive() {
+ return activeDelegated && $(activeDelegated);
+ }
+ /**
+ * fire a callback
+ *
+ * @param callbackName
+ * @param element
+ * @param eventData
+ * @return void
+ */
+ function fire( callbackName, element, eventData ) {
+ if( !callbacks[callbackName] ) {
+ $.error( "callback " + callbackName + " is not registered." );
+ } else {
+ return callCallback.call(this, callbackName, element, eventData);
+ }
+ }
+
+ /**
+ * PUBLIC METHODS LIST
+ */
+ jmpress.data("jmpressmethods", {
+ select: select
+ ,reselect: reselect
+ ,scrollFix: scrollFix
+ ,goTo: goTo
+ ,next: next
+ ,prev: prev
+ ,home: home
+ ,end: end
+ ,canvas: canvasMod
+ ,container: function() { return container; }
+ ,settings: function() { return settings; }
+ ,active: getActive
+ ,current: function() { return current; }
+ ,fire: fire
+ ,init: function(step) {
+ doStepInit.call(this, $(step), current.nextIdNumber++);
+ }
+ ,deinit: function(step) {
+ if(step) {
+ doStepDeinit.call(this, $(step));
+ } else {
+ deinit.call(this);
+ }
+ }
+ ,reapply: doStepReapply
+ });
+
+ /**
+ * Check for support
+ * This will be removed in near future, when support is coming
+ *
+ * @access protected
+ * @return void
+ */
+ function checkSupport() {
+ var ua = navigator.userAgent.toLowerCase();
+ return (ua.search(/(iphone)|(ipod)|(android)/) === -1) || (ua.search(/(chrome)/) !== -1);
+ }
+
+ // BEGIN INIT
+
+ // CHECK FOR SUPPORT
+ if (checkSupport() === false) {
+ if (settings.notSupportedClass) {
+ jmpress.addClass(settings.notSupportedClass);
+ }
+ return;
+ } else {
+ if (settings.notSupportedClass) {
+ jmpress.removeClass(settings.notSupportedClass);
+ }
+ }
+
+ // grabbing all steps
+ var steps = $(settings.stepSelector, jmpress);
+
+ // GERNERAL INIT OF FRAME
+ container = jmpress;
+ area = $('<div />');
+ canvas = $('<div />');
+ $(jmpress).children().filter(steps).each(function() {
+ canvas.append( $( this ) );
+ });
+ if(settings.fullscreen) {
+ container = $('body');
+ $("html").css({
+ overflow: 'hidden'
+ });
+ area = jmpress;
+ }
+ oldStyle.area = area.attr("style") || "";
+ oldStyle.container = container.attr("style") || "";
+ if(settings.fullscreen) {
+ container.css({
+ height: '100%'
+ });
+ jmpress.append( canvas );
+ } else {
+ container.css({
+ position: "relative"
+ });
+ area.append( canvas );
+ jmpress.append( area );
+ }
+
+ $(container).addClass(settings.containerClass);
+ $(area).addClass(settings.areaClass);
+ $(canvas).addClass(settings.canvasClass);
+
+ document.documentElement.style.height = "100%";
+ container.css({
+ overflow: 'hidden'
+ });
+
+ var props = {
+ position: "absolute"
+ ,transitionDuration: '0s'
+ };
+ props = $.extend({}, settings.animation, props);
+ css(area, props);
+ css(area, {
+ top: '50%'
+ ,left: '50%'
+ ,perspective: '1000px'
+ });
+ css(canvas, props);
+
+ current = {};
+
+ callCallback.call(this, 'beforeInit', null, {});
+
+ // INITIALIZE EACH STEP
+ steps.each(function( idx ) {
+ doStepInit.call(jmpress, this, idx );
+ });
+ current.nextIdNumber = steps.length;
+
+ callCallback.call(this, 'afterInit', null, {});
+
+ // START
+ select.call(this, callCallback.call(this, 'selectInitialStep', "init", {}) );
+
+ if (settings.initClass) {
+ $(steps).removeClass(settings.initClass);
+ }
+ }
+ /**
+ * Return default settings
+ *
+ * @return Object
+ */
+ function getDefaults() {
+ return defaults;
+ }
+ /**
+ * Register a callback or a jmpress function
+ *
+ * @access public
+ * @param name String the name of the callback or function
+ * @param func Function? the function to be added
+ */
+ function register(name, func) {
+ if( $.isFunction(func) ) {
+ if( methods[name] ) {
+ $.error( "function " + name + " is already registered." );
+ } else {
+ methods[name] = func;
+ }
+ } else {
+ if( callbacks[name] ) {
+ $.error( "callback " + name + " is already registered." );
+ } else {
+ callbacks[name] = 1;
+ defaults[name] = [];
+ }
+ }
+ }
+ /**
+ * Set CSS on element w/ prefixes
+ *
+ * @return Object element which properties were set
+ *
+ * TODO: Consider bypassing pfx and blindly set as jQuery
+ * already checks for support
+ */
+ function css( el, props ) {
+ var key, pkey, cssObj = {};
+ for ( key in props ) {
+ if ( props.hasOwnProperty(key) ) {
+ pkey = pfx(key);
+ if ( pkey !== null ) {
+ cssObj[pkey] = props[key];
+ }
+ }
+ }
+ $(el).css(cssObj);
+ return el;
+ }
+ /**
+ * Return dataset for element
+ *
+ * @param el element
+ * @return Object
+ */
+ function dataset( el ) {
+ if ( $(el)[0].dataset ) {
+ return $.extend({}, $(el)[0].dataset);
+ }
+ function toCamelcase( str ) {
+ str = str.split( '-' );
+ for( var i = 1; i < str.length; i++ ) {
+ str[i] = str[i].substr(0, 1).toUpperCase() + str[i].substr(1);
+ }
+ return str.join( '' );
+ }
+ var returnDataset = {};
+ var attrs = $(el)[0].attributes;
+ $.each(attrs, function ( idx, attr ) {
+ if ( attr.nodeName.substr(0, 5) === "data-" ) {
+ returnDataset[ toCamelcase(attr.nodeName.substr(5)) ] = attr.nodeValue;
+ }
+ });
+ return returnDataset;
+ }
+ /**
+ * Returns true, if jmpress is initialized
+ *
+ * @return bool
+ */
+ function initialized() {
+ return !!$(this).data("jmpressmethods");
+ }
+
+
+ /**
+ * PUBLIC STATIC METHODS LIST
+ */
+ var methods = {
+ init: init
+ ,initialized: initialized
+ ,deinit: function() {}
+ ,css: css
+ ,pfx: pfx
+ ,defaults: getDefaults
+ ,register: register
+ ,dataset: dataset
+ };
+
+ /**
+ * $.jmpress()
+ */
+ $.fn.jmpress = function( method ) {
+ function f() {
+ var jmpressmethods = $(this).data("jmpressmethods");
+ if ( jmpressmethods && jmpressmethods[method] ) {
+ return jmpressmethods[method].apply( this, Array.prototype.slice.call( arguments, 1 ));
+ } else if ( methods[method] ) {
+ return methods[method].apply( this, Array.prototype.slice.call( arguments, 1 ));
+ } else if ( callbacks[method] && jmpressmethods ) {
+ var settings = jmpressmethods.settings();
+ var func = Array.prototype.slice.call( arguments, 1 )[0];
+ if ($.isFunction( func )) {
+ settings[method] = settings[method] || [];
+ settings[method].push(func);
+ }
+ } else if ( typeof method === 'object' || ! method ) {
+ return init.apply( this, arguments );
+ } else {
+ $.error( 'Method ' + method + ' does not exist on jQuery.jmpress' );
+ }
+ // to allow chaining
+ return this;
+ }
+ var args = arguments;
+ var result;
+ $(this).each(function(idx, element) {
+ result = f.apply(element, args);
+ });
+ return result;
+ };
+ $.extend({
+ jmpress: function( method ) {
+ if ( methods[method] ) {
+ return methods[method].apply( this, Array.prototype.slice.call( arguments, 1 ));
+ } else if ( callbacks[method] ) {
+ // plugin interface
+ var func = Array.prototype.slice.call( arguments, 1 )[0];
+ if ($.isFunction( func )) {
+ defaults[method].push(func);
+ } else {
+ $.error( 'Second parameter should be a function: $.jmpress( callbackName, callbackFunction )' );
+ }
+ } else {
+ $.error( 'Method ' + method + ' does not exist on jQuery.jmpress' );
+ }
+ }
+ });
+
+}(jQuery, document, window));
+
+/*
+ * near.js
+ * Find steps near each other
+ */
+(function( $, document, window, undefined ) {
+
+ 'use strict';
+
+ // add near( selector, backwards = false) to jquery
+
+
+ function checkAndGo( elements, func, selector, backwards ) {
+ var next;
+ elements.each(function(idx, element) {
+ if(backwards) {
+ next = func(element, selector, backwards);
+ if (next) {
+ return false;
+ }
+ }
+ if( $(element).is(selector) ) {
+ next = element;
+ return false;
+ }
+ if(!backwards) {
+ next = func(element, selector, backwards);
+ if (next) {
+ return false;
+ }
+ }
+ });
+ return next;
+ }
+ function findNextInChildren(item, selector, backwards) {
+ var children = $(item).children();
+ if(backwards) {
+ children = $(children.get().reverse());
+ }
+ return checkAndGo( children, findNextInChildren, selector, backwards );
+ }
+ function findNextInSiblings(item, selector, backwards) {
+ return checkAndGo(
+ $(item)[backwards ? "prevAll" : "nextAll"](),
+ findNextInChildren, selector, backwards );
+ }
+ function findNextInParents(item, selector, backwards) {
+ var next;
+ var parents = $(item).parents();
+ parents = $(parents.get());
+ $.each(parents.get(), function(idx, element) {
+ if( backwards && $(element).is(selector) ) {
+ next = element;
+ return false;
+ }
+ next = findNextInSiblings(element, selector, backwards);
+ if(next) {
+ return false;
+ }
+ });
+ return next;
+ }
+
+ $.fn.near = function( selector, backwards ) {
+ var array = [];
+ $(this).each(function(idx, element) {
+ var near = (backwards ?
+ false :
+ findNextInChildren( element, selector, backwards )) ||
+ findNextInSiblings( element, selector, backwards ) ||
+ findNextInParents( element, selector, backwards );
+ if( near ) {
+ array.push(near);
+ }
+ });
+ return $(array);
+ };
+}(jQuery, document, window));
+/*
+ * transform.js
+ * The engine that powers the transforms or falls back to other methods
+ */
+(function( $, document, window, undefined ) {
+
+ 'use strict';
+
+ /* FUNCTIONS */
+ function toCssNumber(number) {
+ return (Math.round(10000*number)/10000)+"";
+ }
+
+ /**
+ * 3D and 2D engines
+ */
+ var engines = {
+ 3: {
+ transform: function( el, data ) {
+ var transform = 'translate(-50%,-50%)';
+ $.each(data, function(idx, item) {
+ var coord = ["X", "Y", "Z"];
+ var i;
+ if(item[0] === "translate") { // ["translate", x, y, z]
+ transform += " translate3d(" + toCssNumber(item[1] || 0) + "px," + toCssNumber(item[2] || 0) + "px," + toCssNumber(item[3] || 0) + "px)";
+ } else if(item[0] === "rotate") {
+ var order = item[4] ? [1, 2, 3] : [3, 2, 1];
+ for(i = 0; i < 3; i++) {
+ transform += " rotate" + coord[order[i]-1] + "(" + toCssNumber(item[order[i]] || 0) + "deg)";
+ }
+ } else if(item[0] === "scale") {
+ for(i = 0; i < 3; i++) {
+ transform += " scale" + coord[i] + "(" + toCssNumber(item[i+1] || 1) + ")";
+ }
+ }
+ });
+ $.jmpress("css", el, $.extend({}, { transform: transform }));
+ }
+ }
+ ,2: {
+ transform: function( el, data ) {
+ var transform = 'translate(-50%,-50%)';
+ $.each(data, function(idx, item) {
+ var coord = ["X", "Y"];
+ if(item[0] === "translate") { // ["translate", x, y, z]
+ transform += " translate(" + toCssNumber(item[1] || 0) + "px," + toCssNumber(item[2] || 0) + "px)";
+ } else if(item[0] === "rotate") {
+ transform += " rotate(" + toCssNumber(item[3] || 0) + "deg)";
+ } else if(item[0] === "scale") {
+ for(var i = 0; i < 2; i++) {
+ transform += " scale" + coord[i] + "(" + toCssNumber(item[i+1] || 1) + ")";
+ }
+ }
+ });
+ $.jmpress("css", el, $.extend({}, { transform: transform }));
+ }
+ }
+ ,1: {
+ // CHECK IF SUPPORT IS REALLY NEEDED?
+ // this not even work without scaling...
+ // it may better to display the normal view
+ transform: function( el, data ) {
+ var anitarget = { top: 0, left: 0 };
+ $.each(data, function(idx, item) {
+ var coord = ["X", "Y"];
+ if(item[0] === "translate") { // ["translate", x, y, z]
+ anitarget.left = Math.round(item[1] || 0) + "px";
+ anitarget.top = Math.round(item[2] || 0) + "px";
+ }
+ });
+ el.animate(anitarget, 1000); // TODO: Use animation duration
+ }
+ }
+ };
+
+ /**
+ * Engine to power cross-browser translate, scale and rotate.
+ */
+ var engine = (function() {
+ if ($.jmpress("pfx", "perspective")) {
+ return engines[3];
+ } else if ($.jmpress("pfx", "transform")) {
+ return engines[2];
+ } else {
+ // CHECK IF SUPPORT IS REALLY NEEDED?
+ return engines[1];
+ }
+ }());
+
+ $.jmpress("defaults").reasonableAnimation = {};
+ $.jmpress("initStep", function( step, eventData ) {
+ var data = eventData.data;
+ var stepData = eventData.stepData;
+ var pf = parseFloat;
+ $.extend(stepData, {
+ x: pf(data.x) || 0
+ ,y: pf(data.y) || 0
+ ,z: pf(data.z) || 0
+ ,r: pf(data.r) || 0
+ ,phi: pf(data.phi) || 0
+ ,rotate: pf(data.rotate) || 0
+ ,rotateX: pf(data.rotateX) || 0
+ ,rotateY: pf(data.rotateY) || 0
+ ,rotateZ: pf(data.rotateZ) || 0
+ ,revertRotate: false
+ ,scale: pf(data.scale) || 1
+ ,scaleX: pf(data.scaleX) || false
+ ,scaleY: pf(data.scaleY) || false
+ ,scaleZ: pf(data.scaleZ) || 1
+ });
+ });
+ $.jmpress("afterInit", function( nil, eventData ) {
+ var stepSelector = eventData.settings.stepSelector,
+ current = eventData.current;
+ current.perspectiveScale = 1;
+ current.maxNestedDepth = 0;
+ var nestedSteps = $(eventData.jmpress).find(stepSelector).children(stepSelector);
+ while(nestedSteps.length) {
+ current.maxNestedDepth++;
+ nestedSteps = nestedSteps.children(stepSelector);
+ }
+ });
+ $.jmpress("applyStep", function( step, eventData ) {
+ $.jmpress("css", $(step), {
+ position: "absolute"
+ ,transformStyle: "preserve-3d"
+ });
+ if ( eventData.parents.length > 0 ) {
+ $.jmpress("css", $(step), {
+ top: "50%"
+ ,left: "50%"
+ });
+ }
+ var sd = eventData.stepData;
+ var transform = [
+ ["translate",
+ sd.x || (sd.r * Math.sin(sd.phi*Math.PI/180)),
+ sd.y || (-sd.r * Math.cos(sd.phi*Math.PI/180)),
+ sd.z],
+ ["rotate",
+ sd.rotateX,
+ sd.rotateY,
+ sd.rotateZ || sd.rotate,
+ true],
+ ["scale",
+ sd.scaleX || sd.scale,
+ sd.scaleY || sd.scale,
+ sd.scaleZ || sd.scale]
+ ];
+ engine.transform( step, transform );
+ });
+ $.jmpress("setActive", function( element, eventData ) {
+ var target = eventData.target;
+ var step = eventData.stepData;
+ var tf = target.transform = [];
+ target.perspectiveScale = 1;
+
+ for(var i = eventData.current.maxNestedDepth; i > (eventData.parents.length || 0); i--) {
+ tf.push(["scale"], ["rotate"], ["translate"]);
+ }
+
+ tf.push(["scale",
+ 1 / (step.scaleX || step.scale),
+ 1 / (step.scaleY || step.scale),
+ 1 / (step.scaleZ)]);
+ tf.push(["rotate",
+ -step.rotateX,
+ -step.rotateY,
+ -(step.rotateZ || step.rotate)]);
+ tf.push(["translate",
+ -(step.x || (step.r * Math.sin(step.phi*Math.PI/180))),
+ -(step.y || (-step.r * Math.cos(step.phi*Math.PI/180))),
+ -step.z]);
+ target.perspectiveScale *= (step.scaleX || step.scale);
+
+ $.each(eventData.parents, function(idx, element) {
+ var step = $(element).data("stepData");
+ tf.push(["scale",
+ 1 / (step.scaleX || step.scale),
+ 1 / (step.scaleY || step.scale),
+ 1 / (step.scaleZ)]);
+ tf.push(["rotate",
+ -step.rotateX,
+ -step.rotateY,
+ -(step.rotateZ || step.rotate)]);
+ tf.push(["translate",
+ -(step.x || (step.r * Math.sin(step.phi*Math.PI/180))),
+ -(step.y || (-step.r * Math.cos(step.phi*Math.PI/180))),
+ -step.z]);
+ target.perspectiveScale *= (step.scaleX || step.scale);
+ });
+
+ $.each(tf, function(idx, item) {
+ if(item[0] !== "rotate") {
+ return;
+ }
+ function lowRotate(name) {
+ if(eventData.current["rotate"+name+"-"+idx] === undefined) {
+ eventData.current["rotate"+name+"-"+idx] = item[name] || 0;
+ }
+ var cur = eventData.current["rotate"+name+"-"+idx], tar = item[name] || 0,
+ curmod = cur % 360, tarmod = tar % 360;
+ if(curmod < 0) {
+ curmod += 360;
+ }
+ if(tarmod < 0) {
+ tarmod += 360;
+ }
+ var diff = tarmod - curmod;
+ if(diff < -180) {
+ diff += 360;
+ } else if(diff > 180) {
+ diff -= 360;
+ }
+ eventData.current["rotate"+name+"-"+idx] = item[name] = cur + diff;
+ }
+ lowRotate(1);
+ lowRotate(2);
+ lowRotate(3);
+ });
+ });
+ $.jmpress("applyTarget", function( active, eventData ) {
+
+ var target = eventData.target,
+ props, step = eventData.stepData,
+ settings = eventData.settings,
+ zoomin = target.perspectiveScale * 1.3 < eventData.current.perspectiveScale,
+ zoomout = target.perspectiveScale > eventData.current.perspectiveScale * 1.3;
+
+ // extract first scale from transform
+ var lastScale = -1;
+ $.each(target.transform, function(idx, item) {
+ if(item.length <= 1) {
+ return;
+ }
+ if(item[0] === "rotate" &&
+ item[1] % 360 === 0 &&
+ item[2] % 360 === 0 &&
+ item[3] % 360 === 0) {
+ return;
+ }
+ if(item[0] === "scale") {
+ lastScale = idx;
+ } else {
+ return false;
+ }
+ });
+
+ if(lastScale !== eventData.current.oldLastScale) {
+ zoomin = zoomout = false;
+ eventData.current.oldLastScale = lastScale;
+ }
+
+ var extracted = [];
+ if(lastScale !== -1) {
+ while(lastScale >= 0) {
+ if(target.transform[lastScale][0] === "scale") {
+ extracted.push(target.transform[lastScale]);
+ target.transform[lastScale] = ["scale"];
+ }
+ lastScale--;
+ }
+ }
+
+ var animation = settings.animation;
+ if(settings.reasonableAnimation[eventData.reason]) {
+ animation = $.extend({},
+ animation,
+ settings.reasonableAnimation[eventData.reason]);
+ }
+
+ props = {
+ // to keep the perspective look similar for different scales
+ // we need to 'scale' the perspective, too
+ perspective: Math.round(target.perspectiveScale * 1000) + "px"
+ };
+ props = $.extend({}, animation, props);
+ if (!zoomin) {
+ props.transitionDelay = '0s';
+ }
+ if (!eventData.beforeActive) {
+ props.transitionDuration = '0s';
+ props.transitionDelay = '0s';
+ }
+ $.jmpress("css", eventData.area, props);
+ engine.transform(eventData.area, extracted);
+
+ props = $.extend({}, animation);
+ if (!zoomout) {
+ props.transitionDelay = '0s';
+ }
+ if (!eventData.beforeActive) {
+ props.transitionDuration = '0s';
+ props.transitionDelay = '0s';
+ }
+
+ eventData.current.perspectiveScale = target.perspectiveScale;
+
+ $.jmpress("css", eventData.canvas, props);
+ engine.transform(eventData.canvas, target.transform);
+ });
+
+}(jQuery, document, window));
+/*
+ * active.js
+ * Set the active classes on steps
+ */
+(function( $, document, window, undefined ) {
+
+ 'use strict';
+ var $jmpress = $.jmpress;
+
+ /* DEFINES */
+ var activeClass = 'activeClass',
+ nestedActiveClass = 'nestedActiveClass';
+
+ /* DEFAULTS */
+ var defaults = $jmpress( 'defaults' );
+ defaults[nestedActiveClass] = "nested-active";
+ defaults[activeClass] = "active";
+
+ /* HOOKS */
+ $jmpress( 'setInactive', function( step, eventData ) {
+ var settings = eventData.settings,
+ activeClassSetting = settings[activeClass],
+ nestedActiveClassSettings = settings[nestedActiveClass];
+ if(activeClassSetting) {
+ $(step).removeClass( activeClassSetting );
+ }
+ if(nestedActiveClassSettings) {
+ $.each(eventData.parents, function(idx, element) {
+ $(element).removeClass(nestedActiveClassSettings);
+ });
+ }
+ });
+ $jmpress( 'setActive', function( step, eventData ) {
+ var settings = eventData.settings,
+ activeClassSetting = settings[activeClass],
+ nestedActiveClassSettings = settings[nestedActiveClass];
+ if(activeClassSetting) {
+ $(step).addClass( activeClassSetting );
+ }
+ if(nestedActiveClassSettings) {
+ $.each(eventData.parents, function(idx, element) {
+ $(element).addClass(nestedActiveClassSettings);
+ });
+ }
+ });
+
+}(jQuery, document, window));
+/*
+ * circular.js
+ * Repeat from start after end
+ */
+(function( $, document, window, undefined ) {
+
+ 'use strict';
+ var $jmpress = $.jmpress;
+
+ /* FUNCTIONS */
+ function firstSlide( step, eventData ) {
+ return $(this).find(eventData.settings.stepSelector).first();
+ }
+ function prevOrNext( jmpress, step, eventData, prev) {
+ if (!step) {
+ return false;
+ }
+ var stepSelector = eventData.settings.stepSelector;
+ step = $(step);
+ do {
+ var item = step.near( stepSelector, prev );
+ if (item.length === 0 || item.closest(jmpress).length === 0) {
+ item = $(jmpress).find(stepSelector)[prev?"last":"first"]();
+ }
+ if (!item.length) {
+ return false;
+ }
+ step = item;
+ } while( step.data("stepData").exclude );
+ return step;
+ }
+
+ /* HOOKS */
+ $jmpress( 'initStep', function( step, eventData ) {
+ eventData.stepData.exclude = eventData.data.exclude && ["false", "no"].indexOf(eventData.data.exclude) === -1;
+ });
+ $jmpress( 'selectInitialStep', firstSlide);
+ $jmpress( 'selectHome', firstSlide);
+ $jmpress( 'selectEnd', function( step, eventData ) {
+ return $(this).find(eventData.settings.stepSelector).last();
+ });
+ $jmpress( 'selectPrev', function( step, eventData ) {
+ return prevOrNext(this, step, eventData, true);
+ });
+ $jmpress( 'selectNext', function( step, eventData ) {
+ return prevOrNext(this, step, eventData);
+ });
+}(jQuery, document, window));
+/*
+ * start.js
+ * Set the first step to start on
+ */
+(function( $, document, window, undefined ) {
+
+ 'use strict';
+
+ /* HOOKS */
+ $.jmpress( 'selectInitialStep', function( nil, eventData ) {
+ return eventData.settings.start;
+ });
+
+}(jQuery, document, window));
+/*
+ * ways.js
+ * Control the flow of the steps
+ */
+(function( $, document, window, undefined ) {
+
+ 'use strict';
+ var $jmpress = $.jmpress;
+
+ /* FUNCTIONS */
+ function routeFunc( jmpress, route, type ) {
+ for(var i = 0; i < route.length - 1; i++) {
+ var from = route[i];
+ var to = route[i+1];
+ if($(jmpress).jmpress("initialized")) {
+ $(from, jmpress).data("stepData")[type] = to;
+ } else {
+ $(from, jmpress).attr('data-' + type, to);
+ }
+ }
+ }
+ function selectPrevOrNext( step, eventData, attr, prev ) {
+ var stepData = eventData.stepData;
+ if(stepData[attr]) {
+ var near = $(step).near(stepData[attr], prev);
+ if(near && near.length) {
+ return near;
+ }
+ near = $(stepData[attr], this)[prev?"last":"first"]();
+ if(near && near.length) {
+ return near;
+ }
+ }
+ }
+
+ /* EXPORTED FUNCTIONS */
+ $jmpress( 'register', 'route', function( route, unidirectional, reversedRoute ) {
+ if( typeof route === "string" ) {
+ route = [route, route];
+ }
+ routeFunc(this, route, reversedRoute ? "prev" : "next");
+ if (!unidirectional) {
+ routeFunc(this, route.reverse(), reversedRoute ? "next" : "prev");
+ }
+ });
+
+ /* HOOKS */
+ $jmpress( 'initStep', function( step, eventData ) {
+ for(var attr in {next:1,prev:1}) {
+ eventData.stepData[attr] = eventData.data[attr];
+ }
+ });
+ $jmpress( 'selectNext', function( step, eventData ) {
+ return selectPrevOrNext.call(this, step, eventData, "next");
+ });
+ $jmpress( 'selectPrev', function( step, eventData ) {
+ return selectPrevOrNext.call(this, step, eventData, "prev", true);
+ });
+
+}(jQuery, document, window));
+/*
+ * ajax.js
+ * Load steps via ajax
+ */
+(function( $, document, window, undefined ) {
+
+ 'use strict';
+ var $jmpress = $.jmpress;
+
+ /* DEFINES */
+ var afterStepLoaded = 'ajax:afterStepLoaded',
+ loadStep = 'ajax:loadStep';
+
+ /* REGISTER EVENTS */
+ $jmpress('register', loadStep);
+ $jmpress('register', afterStepLoaded);
+
+ /* DEFAULTS */
+ $jmpress('defaults').ajaxLoadedClass = "loaded";
+
+ /* HOOKS */
+ $jmpress('initStep', function( step, eventData ) {
+ eventData.stepData.src = $(step).attr('href') || eventData.data.src || false;
+ eventData.stepData.srcLoaded = false;
+ });
+ $jmpress(loadStep, function( step, eventData ) {
+ var stepData = eventData.stepData,
+ href = stepData && stepData.src,
+ settings = eventData.settings;
+ if ( href ) {
+ $(step).addClass( settings.ajaxLoadedClass );
+ stepData.srcLoaded = true;
+ $(step).load(href, function(response, status, xhr) {
+ $(eventData.jmpress).jmpress('fire', afterStepLoaded, step, $.extend({}, eventData, {
+ response: response
+ ,status: status
+ ,xhr: xhr
+ }));
+ });
+ }
+ });
+ $jmpress('idle', function( step, eventData ) {
+ if (!step) {
+ return;
+ }
+ var settings = eventData.settings,
+ jmpress = $(this),
+ stepData = eventData.stepData;
+ var siblings = $(step)
+ .add( $(step).near( settings.stepSelector ) )
+ .add( $(step).near( settings.stepSelector, true) )
+ .add( jmpress.jmpress('fire', 'selectPrev', step, {
+ stepData: $(step).data('stepData')
+ }))
+ .add( jmpress.jmpress('fire', 'selectNext', step, {
+ stepData: $(step).data('stepData')
+ }));
+ siblings.each(function() {
+ var step = this,
+ stepData = $(step).data("stepData");
+ if(!stepData.src || stepData.srcLoaded) {
+ return;
+ }
+ jmpress.jmpress('fire', loadStep, step, {
+ stepData: $(step).data('stepData')
+ });
+ });
+ });
+ $jmpress("setActive", function(step, eventData) {
+ var stepData = $(step).data("stepData");
+ if(!stepData.src || stepData.srcLoaded) {
+ return;
+ }
+ $(this).jmpress('fire', loadStep, step, {
+ stepData: $(step).data('stepData')
+ });
+ });
+
+}(jQuery, document, window));
+/*
+ * hash.js
+ * Detect and set the URL hash
+ */
+(function( $, document, window, undefined ) {
+
+ 'use strict';
+ var $jmpress = $.jmpress,
+ hashLink = "a[href^=#]";
+
+ /* FUNCTIONS */
+ function randomString() {
+ return "" + Math.round(Math.random() * 100000, 0);
+ }
+ /**
+ * getElementFromUrl
+ *
+ * @return String or undefined
+ */
+ function getElementFromUrl(settings) {
+ // get id from url # by removing `#` or `#/` from the beginning,
+ // so both "fallback" `#slide-id` and "enhanced" `#/slide-id` will work
+ // TODO SECURITY check user input to be valid!
+ try {
+ var el = $( '#' + window.location.hash.replace(/^#\/?/,"") );
+ return el.length > 0 && el.is(settings.stepSelector) ? el : undefined;
+ } catch(e) {}
+ }
+ function setHash(stepid) {
+ var shouldBeHash = "#/" + stepid;
+ if(window.history && window.history.pushState) {
+ // shouldBeHash = "#" + stepid;
+ // consider this for future versions
+ // it has currently issues, when startup with a link with hash (webkit)
+ if(window.location.hash !== shouldBeHash) {
+ window.history.pushState({}, '', shouldBeHash);
+ }
+ } else {
+ if(window.location.hash !== shouldBeHash) {
+ window.location.hash = shouldBeHash;
+ }
+ }
+ }
+
+ /* DEFAULTS */
+ $jmpress('defaults').hash = {
+ use: true
+ ,update: true
+ ,bindChange: true
+ // NOTICE: {use: true, update: false, bindChange: true}
+ // will cause a error after clicking on a link to the current step
+ };
+
+ /* HOOKS */
+ $jmpress('selectInitialStep', function( step, eventData ) {
+ var settings = eventData.settings,
+ hashSettings = settings.hash,
+ current = eventData.current,
+ jmpress = $(this);
+ eventData.current.hashNamespace = ".jmpress-"+randomString();
+ // HASH CHANGE EVENT
+ if ( hashSettings.use ) {
+ if ( hashSettings.bindChange ) {
+ $(window).bind('hashchange'+current.hashNamespace, function(event) {
+ var urlItem = getElementFromUrl(settings);
+ if ( jmpress.jmpress('initialized') ) {
+ jmpress.jmpress("scrollFix");
+ }
+ if(urlItem && urlItem.length) {
+ if(urlItem.attr("id") !== jmpress.jmpress("active").attr("id")) {
+ jmpress.jmpress('select', urlItem);
+ }
+ setHash(urlItem.attr("id"));
+ }
+ event.preventDefault();
+ });
+ $(hashLink).on("click"+current.hashNamespace, function(event) {
+ var href = $(this).attr("href");
+ try {
+ if($(href).is(settings.stepSelector)) {
+ jmpress.jmpress("select", href);
+ event.preventDefault();
+ event.stopPropagation();
+ }
+ } catch(e) {}
+ });
+ }
+ return getElementFromUrl(settings);
+ }
+ });
+ $jmpress('afterDeinit', function( nil, eventData ) {
+ $(hashLink).off(eventData.current.hashNamespace);
+ $(window).unbind(eventData.current.hashNamespace);
+ });
+ $jmpress('setActive', function( step, eventData ) {
+ var settings = eventData.settings,
+ current = eventData.current;
+ // `#/step-id` is used instead of `#step-id` to prevent default browser
+ // scrolling to element in hash
+ if ( settings.hash.use && settings.hash.update ) {
+ clearTimeout(current.hashtimeout);
+ current.hashtimeout = setTimeout(function() {
+ setHash($(eventData.delegatedFrom).attr('id'));
+ }, settings.transitionDuration + 200);
+ }
+ });
+
+}(jQuery, document, window));
+/*
+ * keyboard.js
+ * Keyboard event mapping and default keyboard actions
+ */
+(function( $, document, window, undefined ) {
+
+ 'use strict';
+ var $jmpress = $.jmpress,
+ jmpressNext = "next",
+ jmpressPrev = "prev";
+
+ /* FUNCTIONS */
+ function randomString() {
+ return "" + Math.round(Math.random() * 100000, 0);
+ }
+ function stopEvent(event) {
+ event.preventDefault();
+ event.stopPropagation();
+ }
+
+ /* DEFAULTS */
+ $jmpress('defaults').keyboard = {
+ use: true
+ ,keys: {
+ 33: jmpressPrev // pg up
+ ,37: jmpressPrev // left
+ ,38: jmpressPrev // up
+
+ ,9: jmpressNext+":"+jmpressPrev // tab
+ ,32: jmpressNext // space
+ ,34: jmpressNext // pg down
+ ,39: jmpressNext // right
+ ,40: jmpressNext // down
+
+ ,36: "home" // home
+
+ ,35: "end" // end
+ }
+ ,ignore: {
+ "INPUT": [
+ 32 // space
+ ,37 // left
+ ,38 // up
+ ,39 // right
+ ,40 // down
+ ]
+ ,"TEXTAREA": [
+ 32 // space
+ ,37 // left
+ ,38 // up
+ ,39 // right
+ ,40 // down
+ ]
+ ,"SELECT": [
+ 38 // up
+ ,40 // down
+ ]
+ }
+ ,tabSelector: "a[href]:visible, :input:visible"
+ };
+
+ /* HOOKS */
+ $jmpress('afterInit', function( nil, eventData ) {
+ var settings = eventData.settings,
+ keyboardSettings = settings.keyboard,
+ ignoreKeyboardSettings = keyboardSettings.ignore,
+ current = eventData.current,
+ jmpress = $(this);
+
+ // tabindex make it focusable so that it can recieve key events
+ if(!settings.fullscreen) {
+ jmpress.attr("tabindex", 0);
+ }
+
+ current.keyboardNamespace = ".jmpress-"+randomString();
+
+ // KEYPRESS EVENT: this fixes a Opera bug
+ $(settings.fullscreen ? document : jmpress)
+ .bind("keypress"+current.keyboardNamespace, function( event ) {
+
+ for( var nodeName in ignoreKeyboardSettings ) {
+ if ( event.target.nodeName === nodeName && ignoreKeyboardSettings[nodeName].indexOf(event.which) !== -1 ) {
+ return;
+ }
+ }
+ if(event.which >= 37 && event.which <= 40 || event.which === 32) {
+ stopEvent(event);
+ }
+ });
+ // KEYDOWN EVENT
+ $(settings.fullscreen ? document : jmpress)
+ .bind("keydown"+current.keyboardNamespace, function( event ) {
+ var eventTarget = $(event.target);
+
+ if ( !settings.fullscreen && !eventTarget.closest(jmpress).length || !keyboardSettings.use ) {
+ return;
+ }
+
+ for( var nodeName in ignoreKeyboardSettings ) {
+ if ( eventTarget[0].nodeName === nodeName && ignoreKeyboardSettings[nodeName].indexOf(event.which) !== -1 ) {
+ return;
+ }
+ }
+
+ var reverseSelect = false;
+ var nextFocus;
+ if (event.which === 9) {
+ // tab
+ if ( !eventTarget.closest( jmpress.jmpress('active') ).length ) {
+ if ( !event.shiftKey ) {
+ nextFocus = jmpress.jmpress('active').find("a[href], :input").filter(":visible").first();
+ } else {
+ reverseSelect = true;
+ }
+ } else {
+ nextFocus = eventTarget.near( keyboardSettings.tabSelector, event.shiftKey );
+ if( !$(nextFocus)
+ .closest( settings.stepSelector )
+ .is(jmpress.jmpress('active') ) ) {
+ nextFocus = undefined;
+ }
+ }
+ if( nextFocus && nextFocus.length > 0 ) {
+ nextFocus.focus();
+ jmpress.jmpress("scrollFix");
+ stopEvent(event);
+ return;
+ } else {
+ if(event.shiftKey) {
+ reverseSelect = true;
+ }
+ }
+ }
+
+ var action = keyboardSettings.keys[ event.which ];
+ if ( typeof action === "string" ) {
+ if (action.indexOf(":") !== -1) {
+ action = action.split(":");
+ action = event.shiftKey ? action[1] : action[0];
+ }
+ jmpress.jmpress( action );
+ stopEvent(event);
+ } else if ( $.isFunction(action) ) {
+ action.call(jmpress, event);
+ } else if ( action ) {
+ jmpress.jmpress.apply( jmpress, action );
+ stopEvent(event);
+ }
+
+ if (reverseSelect) {
+ // tab
+ nextFocus = jmpress.jmpress('active').find("a[href], :input").filter(":visible").last();
+ nextFocus.focus();
+ jmpress.jmpress("scrollFix");
+ }
+ });
+ });
+ $jmpress('afterDeinit', function( nil, eventData ) {
+ $(document).unbind(eventData.current.keyboardNamespace);
+ });
+
+
+}(jQuery, document, window));
+/*
+ * viewport.js
+ * Scale to fit a given viewport
+ */
+(function( $, document, window, undefined ) {
+
+ 'use strict';
+
+ function randomString() {
+ return "" + Math.round(Math.random() * 100000, 0);
+ }
+
+ var browser = (function() {
+ var ua = navigator.userAgent.toLowerCase();
+ var match = /(chrome)[ \/]([\w.]+)/.exec(ua) ||
+ /(webkit)[ \/]([\w.]+)/.exec(ua) ||
+ /(opera)(?:.*version|)[ \/]([\w.]+)/.exec(ua) ||
+ /(msie) ([\w.]+)/.exec(ua) ||
+ ua.indexOf("compatible") < 0 && /(mozilla)(?:.*? rv:([\w.]+)|)/.exec(ua) ||
+ [];
+ return match[1] || "";
+ }());
+
+ var defaults = $.jmpress("defaults");
+ defaults.viewPort = {
+ width: false
+ ,height: false
+ ,maxScale: 0
+ ,minScale: 0
+ ,zoomable: 0
+ ,zoomBindMove: true
+ ,zoomBindWheel: true
+ };
+ var keys = defaults.keyboard.keys;
+ keys[browser === 'mozilla' ? 107 : 187] = "zoomIn"; // +
+ keys[browser === 'mozilla' ? 109 : 189] = "zoomOut"; // -
+ defaults.reasonableAnimation.resize = {
+ transitionDuration: '0s'
+ ,transitionDelay: '0ms'
+ };
+ defaults.reasonableAnimation.zoom = {
+ transitionDuration: '0s'
+ ,transitionDelay: '0ms'
+ };
+ $.jmpress("initStep", function( step, eventData ) {
+ for(var variable in {"viewPortHeight":1, "viewPortWidth":1, "viewPortMinScale":1, "viewPortMaxScale":1, "viewPortZoomable":1}) {
+ eventData.stepData[variable] = eventData.data[variable] && parseFloat(eventData.data[variable]);
+ }
+ });
+ $.jmpress("afterInit", function( nil, eventData ) {
+ var jmpress = this;
+ eventData.current.viewPortNamespace = ".jmpress-"+randomString();
+ $(window).bind("resize"+eventData.current.viewPortNamespace, function (event) {
+ $(jmpress).jmpress("reselect", "resize");
+ });
+ eventData.current.userZoom = 0;
+ eventData.current.userTranslateX = 0;
+ eventData.current.userTranslateY = 0;
+ if(eventData.settings.viewPort.zoomBindWheel) {
+ $(eventData.settings.fullscreen ? document : this)
+ .bind("mousewheel"+eventData.current.viewPortNamespace+" DOMMouseScroll"+eventData.current.viewPortNamespace, function( event, delta ) {
+ delta = delta || event.originalEvent.wheelDelta || -event.originalEvent.detail /* mozilla */;
+ var direction = (delta / Math.abs(delta));
+ if(direction < 0) {
+ $(eventData.jmpress).jmpress("zoomOut", event.originalEvent.x, event.originalEvent.y);
+ } else if(direction > 0) {
+ $(eventData.jmpress).jmpress("zoomIn", event.originalEvent.x, event.originalEvent.y);
+ }
+ return false;
+ });
+ }
+ if(eventData.settings.viewPort.zoomBindMove) {
+ $(eventData.settings.fullscreen ? document : this).bind("mousedown"+eventData.current.viewPortNamespace, function (event) {
+ if(eventData.current.userZoom) {
+ eventData.current.userTranslating = { x: event.clientX, y: event.clientY };
+ event.preventDefault();
+ event.stopImmediatePropagation();
+ }
+ }).bind("mousemove"+eventData.current.viewPortNamespace, function (event) {
+ var userTranslating = eventData.current.userTranslating;
+ if(userTranslating) {
+ $(jmpress).jmpress("zoomTranslate", event.clientX - userTranslating.x, event.clientY - userTranslating.y);
+ userTranslating.x = event.clientX;
+ userTranslating.y = event.clientY;
+ event.preventDefault();
+ event.stopImmediatePropagation();
+ }
+ }).bind("mouseup"+eventData.current.viewPortNamespace, function (event) {
+ if(eventData.current.userTranslating) {
+ eventData.current.userTranslating = undefined;
+ event.preventDefault();
+ event.stopImmediatePropagation();
+ }
+ });
+ }
+ });
+ function maxAbs(value, range) {
+ return Math.max(Math.min(value, range), -range);
+ }
+ function zoom(x, y, direction) {
+ var current = $(this).jmpress("current"),
+ settings = $(this).jmpress("settings"),
+ stepData = $(this).jmpress("active").data("stepData"),
+ container = $(this).jmpress("container");
+ if(current.userZoom === 0 && direction < 0) {
+ return;
+ }
+ var zoomableSteps = stepData.viewPortZoomable || settings.viewPort.zoomable;
+ if(current.userZoom === zoomableSteps && direction > 0) {
+ return;
+ }
+ current.userZoom += direction;
+
+ var halfWidth = $(container).innerWidth()/2,
+ halfHeight = $(container).innerHeight()/2;
+
+ x = x ? x - halfWidth : x;
+ y = y ? y - halfHeight : y;
+
+ // TODO this is not perfect... too much math... :(
+ current.userTranslateX =
+ maxAbs(current.userTranslateX - direction * x / current.zoomOriginWindowScale / zoomableSteps,
+ halfWidth * current.userZoom * current.userZoom / zoomableSteps);
+ current.userTranslateY =
+ maxAbs(current.userTranslateY - direction * y / current.zoomOriginWindowScale / zoomableSteps,
+ halfHeight * current.userZoom * current.userZoom / zoomableSteps);
+
+ $(this).jmpress("reselect", "zoom");
+ }
+ $.jmpress("register", "zoomIn", function(x, y) {
+ zoom.call(this, x||0, y||0, 1);
+ });
+ $.jmpress("register", "zoomOut", function(x, y) {
+ zoom.call(this, x||0, y||0, -1);
+ });
+ $.jmpress("register", "zoomTranslate", function(x, y) {
+ var current = $(this).jmpress("current"),
+ settings = $(this).jmpress("settings"),
+ stepData = $(this).jmpress("active").data("stepData"),
+ container = $(this).jmpress("container");
+ var zoomableSteps = stepData.viewPortZoomable || settings.viewPort.zoomable;
+ var halfWidth = $(container).innerWidth(),
+ halfHeight = $(container).innerHeight();
+ current.userTranslateX =
+ maxAbs(current.userTranslateX + x / current.zoomOriginWindowScale,
+ halfWidth * current.userZoom * current.userZoom / zoomableSteps);
+ current.userTranslateY =
+ maxAbs(current.userTranslateY + y / current.zoomOriginWindowScale,
+ halfHeight * current.userZoom * current.userZoom / zoomableSteps);
+ $(this).jmpress("reselect", "zoom");
+ });
+ $.jmpress('afterDeinit', function( nil, eventData ) {
+ $(eventData.settings.fullscreen ? document : this).unbind(eventData.current.viewPortNamespace);
+ $(window).unbind(eventData.current.viewPortNamespace);
+ });
+ $.jmpress("setActive", function( step, eventData ) {
+ var viewPort = eventData.settings.viewPort;
+ var viewPortHeight = eventData.stepData.viewPortHeight || viewPort.height;
+ var viewPortWidth = eventData.stepData.viewPortWidth || viewPort.width;
+ var viewPortMaxScale = eventData.stepData.viewPortMaxScale || viewPort.maxScale;
+ var viewPortMinScale = eventData.stepData.viewPortMinScale || viewPort.minScale;
+ // Correct the scale based on the window's size
+ var windowScaleY = viewPortHeight && $(eventData.container).innerHeight()/viewPortHeight;
+ var windowScaleX = viewPortWidth && $(eventData.container).innerWidth()/viewPortWidth;
+ var windowScale = (windowScaleX || windowScaleY) && Math.min( windowScaleX || windowScaleY, windowScaleY || windowScaleX );
+
+ if(windowScale) {
+ windowScale = windowScale || 1;
+ if(viewPortMaxScale) {
+ windowScale = Math.min(windowScale, viewPortMaxScale);
+ }
+ if(viewPortMinScale) {
+ windowScale = Math.max(windowScale, viewPortMinScale);
+ }
+
+ var zoomableSteps = eventData.stepData.viewPortZoomable || eventData.settings.viewPort.zoomable;
+ if(zoomableSteps) {
+ var diff = (1/windowScale) - (1/viewPortMaxScale);
+ diff /= zoomableSteps;
+ windowScale = 1/((1/windowScale) - diff * eventData.current.userZoom);
+ }
+
+ eventData.target.transform.reverse();
+ if(eventData.current.userTranslateX && eventData.current.userTranslateY) {
+ eventData.target.transform.push(["translate", eventData.current.userTranslateX, eventData.current.userTranslateY, 0]);
+ } else {
+ eventData.target.transform.push(["translate"]);
+ }
+ eventData.target.transform.push(["scale",
+ windowScale,
+ windowScale,
+ 1]);
+ eventData.target.transform.reverse();
+ eventData.target.perspectiveScale /= windowScale;
+ }
+ eventData.current.zoomOriginWindowScale = windowScale;
+ });
+ $.jmpress("setInactive", function( step, eventData ) {
+ if(!eventData.nextStep || !step || $(eventData.nextStep).attr("id") !== $(step).attr("id")) {
+ eventData.current.userZoom = 0;
+ eventData.current.userTranslateX = 0;
+ eventData.current.userTranslateY = 0;
+ }
+ });
+
+}(jQuery, document, window));
+
+/*
+ * mouse.js
+ * Clicking to select a step
+ */
+(function( $, document, window, undefined ) {
+
+ 'use strict';
+ var $jmpress = $.jmpress;
+
+ /* FUNCTIONS */
+ function randomString() {
+ return "" + Math.round(Math.random() * 100000, 0);
+ }
+
+ /* DEFAULTS */
+ $jmpress("defaults").mouse = {
+ clickSelects: true
+ };
+
+ /* HOOKS */
+ $jmpress("afterInit", function( nil, eventData ) {
+ var settings = eventData.settings,
+ stepSelector = settings.stepSelector,
+ current = eventData.current,
+ jmpress = $(this);
+ current.clickableStepsNamespace = ".jmpress-"+randomString();
+ jmpress.bind("click"+current.clickableStepsNamespace, function(event) {
+ if (!settings.mouse.clickSelects || current.userZoom) {
+ return;
+ }
+
+ // get clicked step
+ var clickedStep = $(event.target).closest(stepSelector);
+
+ // clicks on the active step do default
+ if ( clickedStep.is( jmpress.jmpress("active") ) ) {
+ return;
+ }
+
+ if (clickedStep.length) {
+ // select the clicked step
+ jmpress.jmpress("select", clickedStep[0], "click");
+ event.preventDefault();
+ event.stopPropagation();
+ }
+ });
+ });
+ $jmpress('afterDeinit', function( nil, eventData ) {
+ $(this).unbind(eventData.current.clickableStepsNamespace);
+ });
+
+}(jQuery, document, window));
+/*
+ * mobile.js
+ * Adds support for swipe on touch supported browsers
+ */
+(function( $, document, window, undefined ) {
+
+ 'use strict';
+ var $jmpress = $.jmpress;
+
+ /* FUNCTIONS */
+ function randomString() {
+ return "" + Math.round(Math.random() * 100000, 0);
+ }
+
+ /* HOOKS */
+ $jmpress( 'afterInit', function( step, eventData ) {
+ var settings = eventData.settings,
+ current = eventData.current,
+ jmpress = eventData.jmpress;
+ current.mobileNamespace = ".jmpress-"+randomString();
+ var data, start = [0,0];
+ $(settings.fullscreen ? document : jmpress)
+ .bind("touchstart"+current.mobileNamespace, function( event ) {
+
+ data = event.originalEvent.touches[0];
+ start = [ data.pageX, data.pageY ];
+
+ }).bind("touchmove"+current.mobileNamespace, function( event ) {
+ data = event.originalEvent.touches[0];
+ event.preventDefault();
+ return false;
+ }).bind("touchend"+current.mobileNamespace, function( event ) {
+ var end = [ data.pageX, data.pageY ],
+ diff = [ end[0]-start[0], end[1]-start[1] ];
+
+ if(Math.max(Math.abs(diff[0]), Math.abs(diff[1])) > 50) {
+ diff = Math.abs(diff[0]) > Math.abs(diff[1]) ? diff[0] : diff[1];
+ $(jmpress).jmpress(diff > 0 ? "prev" : "next");
+ event.preventDefault();
+ return false;
+ }
+ });
+ });
+ $jmpress('afterDeinit', function( nil, eventData ) {
+ var settings = eventData.settings,
+ current = eventData.current,
+ jmpress = eventData.jmpress;
+ $(settings.fullscreen ? document : jmpress).unbind(current.mobileNamespace);
+ });
+
+}(jQuery, document, window));
+/*
+ * templates.js
+ * The amazing template engine
+ */
+(function( $, document, window, undefined ) {
+
+ 'use strict';
+ var $jmpress = $.jmpress,
+ templateFromParentIdent = "_template_",
+ templateFromApplyIdent = "_applied_template_";
+
+ /* STATIC VARS */
+ var templates = {};
+
+ /* FUNCTIONS */
+ function addUndefined( target, values, prefix ) {
+ for( var name in values ) {
+ var targetName = name;
+ if ( prefix ) {
+ targetName = prefix + targetName.substr(0, 1).toUpperCase() + targetName.substr(1);
+ }
+ if ( $.isPlainObject(values[name]) ) {
+ addUndefined( target, values[name], targetName );
+ } else if( target[targetName] === undefined ) {
+ target[targetName] = values[name];
+ }
+ }
+ }
+ function applyChildrenTemplates( children, templateChildren ) {
+ if ($.isArray(templateChildren)) {
+ if (templateChildren.length < children.length) {
+ $.error("more nested steps than children in template");
+ } else {
+ children.each(function(idx, child) {
+ child = $(child);
+ var tmpl = child.data(templateFromParentIdent) || {};
+ addUndefined(tmpl, templateChildren[idx]);
+ child.data(templateFromParentIdent, tmpl);
+ });
+ }
+ } else if($.isFunction(templateChildren)) {
+ children.each(function(idx, child) {
+ child = $(child);
+ var tmpl = child.data(templateFromParentIdent) || {};
+ addUndefined(tmpl, templateChildren(idx, child, children));
+ child.data(templateFromParentIdent, tmpl);
+ });
+ } // TODO: else if(object)
+ }
+ function applyTemplate( data, element, template, eventData ) {
+ if (template.children) {
+ var children = element.children( eventData.settings.stepSelector );
+ applyChildrenTemplates( children, template.children );
+ }
+ applyTemplateData( data, template );
+ }
+ function applyTemplateData( data, template ) {
+ addUndefined(data, template);
+ }
+
+ /* HOOKS */
+ $jmpress("beforeInitStep", function( step, eventData ) {
+ step = $(step);
+ var data = eventData.data,
+ templateFromAttr = data.template,
+ templateFromApply = step.data(templateFromApplyIdent),
+ templateFromParent = step.data(templateFromParentIdent);
+ if(templateFromAttr) {
+ $.each(templateFromAttr.split(" "), function(idx, tmpl) {
+ var template = templates[tmpl];
+ applyTemplate( data, step, template, eventData );
+ });
+ }
+ if (templateFromApply) {
+ applyTemplate( data, step, templateFromApply, eventData );
+ }
+ if (templateFromParent) {
+ applyTemplate( data, step, templateFromParent, eventData );
+ step.data(templateFromParentIdent, null);
+ if(templateFromParent.template) {
+ $.each(templateFromParent.template.split(" "), function(idx, tmpl) {
+ var template = templates[tmpl];
+ applyTemplate( data, step, template, eventData );
+ });
+ }
+ }
+ });
+ $jmpress("beforeInit", function( nil, eventData ) {
+ var data = $jmpress("dataset", this),
+ dataTemplate = data.template,
+ stepSelector = eventData.settings.stepSelector;
+ if (dataTemplate) {
+ var template = templates[dataTemplate];
+ applyChildrenTemplates( $(this).find(stepSelector).filter(function() {
+ return !$(this).parent().is(stepSelector);
+ }), template.children );
+ }
+ });
+
+ /* EXPORTED FUNCTIONS */
+ $jmpress("register", "template", function( name, tmpl ) {
+ if (templates[name]) {
+ templates[name] = $.extend(true, {}, templates[name], tmpl);
+ } else {
+ templates[name] = $.extend(true, {}, tmpl);
+ }
+ });
+ $jmpress("register", "apply", function( selector, tmpl ) {
+ if( !tmpl ) {
+ // TODO ERROR because settings not found
+ var stepSelector = $(this).jmpress("settings").stepSelector;
+ applyChildrenTemplates( $(this).find(stepSelector).filter(function() {
+ return !$(this).parent().is(stepSelector);
+ }), selector );
+ } else if($.isArray(tmpl)) {
+ applyChildrenTemplates( $(selector), tmpl );
+ } else {
+ var template;
+ if(typeof tmpl === "string") {
+ template = templates[tmpl];
+ } else {
+ template = $.extend(true, {}, tmpl);
+ }
+ $(selector).each(function(idx, element) {
+ element = $(element);
+ var tmpl = element.data(templateFromApplyIdent) || {};
+ addUndefined(tmpl, template);
+ element.data(templateFromApplyIdent, tmpl);
+ });
+ }
+ });
+
+}(jQuery, document, window));
+/*
+ * jqevents.js
+ * Fires jQuery events
+ */
+(function( $, document, window, undefined ) {
+
+ 'use strict';
+
+ /* HOOKS */
+ // the events should not bubble up the tree
+ // elsewise nested jmpress would cause buggy behavior
+ $.jmpress("setActive", function( step, eventData ) {
+ if(eventData.prevStep !== step) {
+ $(step).triggerHandler("enterStep");
+ }
+ });
+ $.jmpress("setInactive", function( step, eventData ) {
+ if(eventData.nextStep !== step) {
+ $(step).triggerHandler("leaveStep");
+ }
+ });
+
+}(jQuery, document, window));
+/*
+ * animation.js
+ * Apply custom animations to steps
+ */
+(function( $, document, window, undefined ) {
+
+ 'use strict';
+
+ function parseSubstepInfo(str) {
+ var arr = str.split(" ");
+ var className = arr[0];
+ var config = { willClass: "will-"+className, doClass: "do-"+className, hasClass: "has-"+className };
+ var state = "";
+ for(var i = 1; i < arr.length; i++) {
+ var s = arr[i];
+ switch(state) {
+ case "":
+ if(s === "after") {
+ state = "after";
+ } else {
+ $.warn("unknown keyword in '"+str+"'. '"+s+"' unknown.");
+ }
+ break;
+ case "after":
+ if(s.match(/^[1-9][0-9]*m?s?/)) {
+ var value = parseFloat(s);
+ if(s.indexOf("ms") !== -1) {
+ value *= 1;
+ } else if(s.indexOf("s") !== -1) {
+ value *= 1000;
+ } else if(s.indexOf("m") !== -1) {
+ value *= 60000;
+ }
+ config.delay = value;
+ } else {
+ config.after = Array.prototype.slice.call(arr, i).join(" ");
+ i = arr.length;
+ }
+ }
+ }
+ return config;
+ }
+ function find(array, selector, start, end) {
+ end = end || (array.length - 1);
+ start = start || 0;
+ for(var i = start; i < end + 1; i++) {
+ if($(array[i].element).is(selector)) {
+ return i;
+ }
+ }
+ }
+ function addOn(list, substep, delay) {
+ $.each(substep._on, function(idx, child) {
+ list.push({substep: child.substep, delay: child.delay + delay});
+ addOn(list, child.substep, child.delay + delay);
+ });
+ }
+ $.jmpress("defaults").customAnimationDataAttribute = "jmpress";
+ $.jmpress("afterInit", function( nil, eventData ) {
+ eventData.current.animationTimeouts = [];
+ eventData.current.animationCleanupWaiting = [];
+ });
+ $.jmpress("applyStep", function( step, eventData ) {
+ // read custom animation from elements
+ var substepsData = {};
+ var listOfSubsteps = [];
+ $(step).find("[data-"+eventData.settings.customAnimationDataAttribute+"]")
+ .each(function(idx, element) {
+ if($(element).closest(eventData.settings.stepSelector).is(step)) {
+ listOfSubsteps.push({element: element});
+ }
+ });
+ if(listOfSubsteps.length === 0) {
+ return;
+ }
+ $.each(listOfSubsteps, function(idx, substep) {
+ substep.info = parseSubstepInfo(
+ $(substep.element).data(eventData.settings.customAnimationDataAttribute));
+ $(substep.element).addClass(substep.info.willClass);
+ substep._on = [];
+ substep._after = null;
+ });
+ var current = {_after: undefined, _on: [], info: {}}; // virtual zero step
+ $.each(listOfSubsteps, function(idx, substep) {
+ var other = substep.info.after;
+ if(other) {
+ if(other === "step") {
+ other = current;
+ } else if(other === "prev") {
+ other = listOfSubsteps[idx-1];
+ } else {
+ var index = find(listOfSubsteps, other, 0, idx - 1);
+ if(index === undefined) {
+ index = find(listOfSubsteps, other);
+ }
+ other = (index === undefined || index === idx) ? listOfSubsteps[idx-1] : listOfSubsteps[index];
+ }
+ } else {
+ other = listOfSubsteps[idx-1];
+ }
+ if(other) {
+ if(!substep.info.delay) {
+ if(!other._after) {
+ other._after = substep;
+ return;
+ }
+ other = other._after;
+ }
+ other._on.push({substep: substep, delay: substep.info.delay || 0});
+ }
+ });
+ if(current._after === undefined && current._on.length === 0) {
+ var startStep = find(listOfSubsteps, eventData.stepData.startSubstep) || 0;
+ current._after = listOfSubsteps[startStep];
+ }
+ var substepsInOrder = [];
+ function findNextFunc(idx, item) {
+ if(item.substep._after) {
+ current = item.substep._after;
+ return false;
+ }
+ }
+ do {
+ var substepList = [{substep: current, delay: 0}];
+ addOn(substepList, current, 0);
+ substepsInOrder.push(substepList);
+ current = null;
+ $.each(substepList, findNextFunc);
+ } while(current);
+ substepsData.list = substepsInOrder;
+ $(step).data("substepsData", substepsData);
+ });
+ $.jmpress("unapplyStep", function( step, eventData ) {
+ var substepsData = $(step).data("substepsData");
+ if(substepsData) {
+ $.each(substepsData.list, function(idx, activeSubsteps) {
+ $.each(activeSubsteps, function(idx, substep) {
+ if(substep.substep.info.willClass) {
+ $(substep.substep.element).removeClass(substep.substep.info.willClass);
+ }
+ if(substep.substep.info.hasClass) {
+ $(substep.substep.element).removeClass(substep.substep.info.hasClass);
+ }
+ if(substep.substep.info.doClass) {
+ $(substep.substep.element).removeClass(substep.substep.info.doClass);
+ }
+ });
+ });
+ }
+ });
+ $.jmpress("setActive", function(step, eventData) {
+ var substepsData = $(step).data("substepsData");
+ if(!substepsData) {
+ return;
+ }
+ if(eventData.substep === undefined) {
+ eventData.substep =
+ (eventData.reason === "prev" ?
+ substepsData.list.length-1 :
+ 0
+ );
+ }
+ var substep = eventData.substep;
+ $.each(eventData.current.animationTimeouts, function(idx, timeout) {
+ clearTimeout(timeout);
+ });
+ eventData.current.animationTimeouts = [];
+ $.each(substepsData.list, function(idx, activeSubsteps) {
+ var applyHas = idx < substep;
+ var applyDo = idx <= substep;
+ $.each(activeSubsteps, function(idx, substep) {
+ if(substep.substep.info.hasClass) {
+ $(substep.substep.element)[(applyHas?"add":"remove")+"Class"](substep.substep.info.hasClass);
+ }
+ function applyIt() {
+ $(substep.substep.element).addClass(substep.substep.info.doClass);
+ }
+ if(applyDo && !applyHas && substep.delay && eventData.reason !== "prev") {
+ if(substep.substep.info.doClass) {
+ $(substep.substep.element).removeClass(substep.substep.info.doClass);
+ eventData.current.animationTimeouts.push(setTimeout(applyIt, substep.delay));
+ }
+ } else {
+ if(substep.substep.info.doClass) {
+ $(substep.substep.element)[(applyDo?"add":"remove")+"Class"](substep.substep.info.doClass);
+ }
+ }
+ });
+ });
+ });
+ $.jmpress("setInactive", function(step, eventData) {
+ if(eventData.nextStep === step) {
+ return;
+ }
+ function cleanupAnimation( substepsData ) {
+ $.each(substepsData.list, function(idx, activeSubsteps) {
+ $.each(activeSubsteps, function(idx, substep) {
+ if(substep.substep.info.hasClass) {
+ $(substep.substep.element).removeClass(substep.substep.info.hasClass);
+ }
+ if(substep.substep.info.doClass) {
+ $(substep.substep.element).removeClass(substep.substep.info.doClass);
+ }
+ });
+ });
+ }
+ $.each(eventData.current.animationCleanupWaiting, function(idx, item) {
+ cleanupAnimation(item);
+ });
+ eventData.current.animationCleanupWaiting = [];
+ var substepsData = $(step).data("substepsData");
+ if(substepsData) {
+ eventData.current.animationCleanupWaiting.push( substepsData );
+ }
+ });
+ $.jmpress("selectNext", function( step, eventData ) {
+ if(eventData.substep === undefined) {
+ return;
+ }
+ var substepsData = $(step).data("substepsData");
+ if(!substepsData) {
+ return;
+ }
+ if(eventData.substep < substepsData.list.length-1) {
+ return {step: step, substep: eventData.substep+1};
+ }
+ });
+ $.jmpress("selectPrev", function( step, eventData ) {
+ if(eventData.substep === undefined) {
+ return;
+ }
+ var substepsData = $(step).data("substepsData");
+ if(!substepsData) {
+ return;
+ }
+ if(eventData.substep > 0) {
+ return {step: step, substep: eventData.substep-1};
+ }
+ });
+
+}(jQuery, document, window));
+/*!
+ * plugin for jmpress.js v0.4.5
+ *
+ * Copyright 2013 Kyle Robinson Young @shama & Tobias Koppers @sokra
+ * Licensed MIT
+ * http://www.opensource.org/licenses/mit-license.php
+ *//*
+ * jmpress.toggle plugin
+ * For binding a key to toggle de/initialization of jmpress.js.
+ */
+(function( $, document, window, undefined ) {
+ 'use strict';
+ $.jmpress("register", "toggle", function( key, config, initial ) {
+ var jmpress = this;
+ $(document).bind("keydown", function( event ) {
+ if ( event.keyCode === key ) {
+ if ($(jmpress).jmpress("initialized")) {
+ $(jmpress).jmpress("deinit");
+ } else {
+ $(jmpress).jmpress(config);
+ }
+ }
+ });
+ if ( initial ) {
+ $(jmpress).jmpress(config);
+ }
+ });
+}(jQuery, document, window));
+
+/*
+ * jmpress.secondary plugin
+ * Apply a secondary animation when step is selected.
+ */
+(function( $, document, window, undefined ) {
+ 'use strict';
+ $.jmpress("initStep", function( step, eventData ) {
+ for(var name in eventData.data) {
+ if(name.indexOf("secondary") === 0) {
+ eventData.stepData[name] = eventData.data[name];
+ }
+ }
+ });
+ function exchangeIf(childStepData, condition, step) {
+ if(childStepData.secondary &&
+ childStepData.secondary.split(" ").indexOf(condition) !== -1) {
+ for(var name in childStepData) {
+ if(name.length > 9 && name.indexOf("secondary") === 0) {
+ var tmp = childStepData[name];
+ var normal = name.substr(9);
+ normal = normal.substr(0, 1).toLowerCase() + normal.substr(1);
+ childStepData[name] = childStepData[normal];
+ childStepData[normal] = tmp;
+ }
+ }
+ $(this).jmpress("reapply", $(step));
+ }
+ }
+ $.jmpress("beforeActive", function( step, eventData ) {
+ exchangeIf.call(eventData.jmpress, $(step).data("stepData"), "self", step);
+ var parent = $(step).parent();
+ $(parent)
+ .children(eventData.settings.stepSelector)
+ .each(function(idx, child) {
+ var childStepData = $(child).data("stepData");
+ exchangeIf.call(eventData.jmpress, childStepData, "siblings", child);
+ });
+ function grandchildrenFunc(idx, child) {
+ var childStepData = $(child).data("stepData");
+ exchangeIf.call(eventData.jmpress, childStepData, "grandchildren", child);
+ }
+ for(var i = 1; i < eventData.parents.length; i++) {
+ $(eventData.parents[i])
+ .children(eventData.settings.stepSelector)
+ .each();
+ }
+ });
+ $.jmpress("setInactive", function( step, eventData ) {
+ exchangeIf.call(eventData.jmpress, $(step).data("stepData"), "self", step);
+ var parent = $(step).parent();
+ $(parent)
+ .children(eventData.settings.stepSelector)
+ .each(function(idx, child) {
+ var childStepData = $(child).data("stepData");
+ exchangeIf.call(eventData.jmpress, childStepData, "siblings", child);
+ });
+ function grandchildrenFunc(idx, child) {
+ var childStepData = $(child).data("stepData");
+ exchangeIf.call(eventData.jmpress, childStepData, "grandchildren", child);
+ }
+ for(var i = 1; i < eventData.parents.length; i++) {
+ $(eventData.parents[i])
+ .children(eventData.settings.stepSelector)
+ .each(grandchildrenFunc);
+ }
+ });
+}(jQuery, document, window));
+
+/*
+ * jmpress.duration plugin
+ * For auto advancing steps after a given duration and optionally displaying a
+ * progress bar.
+ */
+(function( $, document, window, undefined ) {
+ 'use strict';
+
+ $.jmpress("defaults").duration = {
+ defaultValue: -1
+ ,defaultAction: "next"
+ ,barSelector: undefined
+ ,barProperty: "width"
+ ,barPropertyStart: "0"
+ ,barPropertyEnd: "100%"
+ };
+ $.jmpress("initStep", function( step, eventData ) {
+ eventData.stepData.duration = eventData.data.duration && parseInt(eventData.data.duration, 10);
+ eventData.stepData.durationAction = eventData.data.durationAction;
+ });
+ $.jmpress("setInactive", function( step, eventData ) {
+ var settings = eventData.settings,
+ durationSettings = settings.duration,
+ current = eventData.current;
+ var dur = eventData.stepData.duration || durationSettings.defaultValue;
+ if( current.durationTimeout ) {
+ if( durationSettings.barSelector ) {
+ var css = {
+ transitionProperty: durationSettings.barProperty
+ ,transitionDuration: '0'
+ ,transitionDelay: '0'
+ ,transitionTimingFunction: 'linear'
+ };
+ css[durationSettings.barProperty] = durationSettings.barPropertyStart;
+ var bars = $(durationSettings.barSelector);
+ $.jmpress("css", bars, css);
+ bars.each(function(idx, element) {
+ var next = $(element).next();
+ var parent = $(element).parent();
+ $(element).detach();
+ if(next.length) {
+ next.insertBefore(element);
+ } else {
+ parent.append(element);
+ }
+ });
+ }
+ clearTimeout(current.durationTimeout);
+ delete current.durationTimeout;
+ }
+ });
+ $.jmpress("setActive", function( step, eventData ) {
+ var settings = eventData.settings,
+ durationSettings = settings.duration,
+ current = eventData.current;
+ var dur = eventData.stepData.duration || durationSettings.defaultValue;
+ if( dur && dur > 0 ) {
+ if( durationSettings.barSelector ) {
+ var css = {
+ transitionProperty: durationSettings.barProperty
+ ,transitionDuration: (dur-settings.transitionDuration*2/3-100)+"ms"
+ ,transitionDelay: (settings.transitionDuration*2/3)+'ms'
+ ,transitionTimingFunction: 'linear'
+ };
+ css[durationSettings.barProperty] = durationSettings.barPropertyEnd;
+ $.jmpress("css", $(durationSettings.barSelector), css);
+ }
+ var jmpress = this;
+ if(current.durationTimeout) {
+ clearTimeout(current.durationTimeout);
+ current.durationTimeout = undefined;
+ }
+ current.durationTimeout = setTimeout(function() {
+ var action = eventData.stepData.durationAction || durationSettings.defaultAction;
+ $(jmpress).jmpress(action);
+ }, dur);
+ }
+ });
+}(jQuery, document, window));
+
+/*
+ * jmpress.presentation-mode plugin
+ * Display a window for the presenter with notes and a control and view of the
+ * presentation
+ */
+(function( $, document, window, undefined ) {
+
+ 'use strict';
+ var $jmpress = $.jmpress;
+
+ var PREFIX = "jmpress-presentation-";
+
+ /* FUNCTIONS */
+ function randomString() {
+ return "" + Math.round(Math.random() * 100000, 0);
+ }
+
+ /* DEFAULTS */
+ $jmpress("defaults").presentationMode = {
+ use: true,
+ url: "presentation-screen.html",
+ notesUrl: false,
+ transferredValues: ["userZoom", "userTranslateX", "userTranslateY"]
+ };
+ $jmpress("defaults").keyboard.keys[80] = "presentationPopup"; // p key
+
+ /* HOOKS */
+ $jmpress("afterInit", function( nil, eventData) {
+ var current = eventData.current;
+
+ current.selectMessageListeners = [];
+
+ if(eventData.settings.presentationMode.use) {
+
+ window.addEventListener("message", function(event) {
+ // We do not test orgin, because we want to accept messages
+ // from all orgins
+ try {
+ if(typeof event.data !== "string" || event.data.indexOf(PREFIX) !== 0) {
+ return;
+ }
+ var json = JSON.parse(event.data.slice(PREFIX.length));
+ switch(json.type) {
+ case "select":
+ $.each(eventData.settings.presentationMode.transferredValues, function(idx, name) {
+ eventData.current[name] = json[name];
+ });
+ if(/[a-z0-9\-]+/i.test(json.targetId) && typeof json.substep in {number:1,undefined:1}) {
+ $(eventData.jmpress).jmpress("select", {step: "#"+json.targetId, substep: json.substep}, json.reason);
+ } else {
+ $.error("For security reasons the targetId must match /[a-z0-9\\-]+/i and substep must be a number.");
+ }
+ break;
+ case "listen":
+ current.selectMessageListeners.push(event.source);
+ break;
+ case "ok":
+ clearTimeout(current.presentationPopupTimeout);
+ break;
+ case "read":
+ try {
+ event.source.postMessage(PREFIX + JSON.stringify({type: "url", url: window.location.href, notesUrl: eventData.settings.presentationMode.notesUrl}), "*");
+ } catch(e) {
+ $.error("Cannot post message to source: " + e);
+ }
+ break;
+ default:
+ throw "Unknown message type: " + json.type;
+ }
+ } catch(e) {
+ $.error("Received message is malformed: " + e);
+ }
+ });
+ try {
+ if(window.parent && window.parent !== window) {
+ window.parent.postMessage(PREFIX + JSON.stringify({
+ "type": "afterInit"
+ }), "*");
+ }
+ } catch(e) {
+ $.error("Cannot post message to parent: " + e);
+ }
+ }
+ });
+ $jmpress("afterDeinit", function( nil, eventData) {
+ if(eventData.settings.presentationMode.use) {
+ try {
+ if(window.parent && window.parent !== window) {
+ window.parent.postMessage(PREFIX + JSON.stringify({
+ "type": "afterDeinit"
+ }), "*");
+ }
+ } catch(e) {
+ $.error("Cannot post message to parent: " + e);
+ }
+ }
+ });
+ $jmpress("setActive", function( step, eventData) {
+ var stepId = $(eventData.delegatedFrom).attr("id"),
+ substep = eventData.substep,
+ reason = eventData.reason;
+ $.each(eventData.current.selectMessageListeners, function(idx, listener) {
+ try {
+ var msg = {
+ "type": "select",
+ "targetId": stepId,
+ "substep": substep,
+ "reason": reason
+ };
+ $.each(eventData.settings.presentationMode.transferredValues, function(idx, name) {
+ msg[name] = eventData.current[name];
+ });
+ listener.postMessage(PREFIX + JSON.stringify(msg), "*");
+ } catch(e) {
+ $.error("Cannot post message to listener: " + e);
+ }
+ });
+ });
+ $jmpress("register", "presentationPopup", function() {
+ function trySend() {
+ jmpress.jmpress("current").presentationPopupTimeout = setTimeout(trySend, 100);
+ try {
+ popup.postMessage(PREFIX + JSON.stringify({type: "url", url: window.location.href, notesUrl: jmpress.jmpress("settings").presentationMode.notesUrl}), "*");
+ } catch(e) {
+ }
+ }
+ var jmpress = $(this),
+ popup;
+ if(jmpress.jmpress("settings").presentationMode.use) {
+ popup = window.open($(this).jmpress("settings").presentationMode.url);
+ jmpress.jmpress("current").presentationPopupTimeout = setTimeout(trySend, 100);
+ }
+ });
+}(jQuery, document, window));
diff --git a/plugins/jetpack/modules/shortcodes/js/jmpress.min.js b/plugins/jetpack/modules/shortcodes/js/jmpress.min.js
new file mode 100644
index 00000000..c650c830
--- /dev/null
+++ b/plugins/jetpack/modules/shortcodes/js/jmpress.min.js
@@ -0,0 +1,13 @@
+/*!
+ * jmpress.js v0.4.5
+ * http://jmpressjs.github.com/jmpress.js
+ *
+ * A jQuery plugin to build a website on the infinite canvas.
+ *
+ * Copyright 2013 Kyle Robinson Young @shama & Tobias Koppers @sokra
+ * Licensed MIT
+ * http://www.opensource.org/licenses/mit-license.php
+ *
+ * Based on the foundation laid by Bartek Szopka @bartaz
+ */(function(e,t,s,a){"use strict";function r(e){if(e){var t=1+e.substr(1).search(/[A-Z]/),s=e.substr(0,t).toLowerCase(),a=e.substr(t).toLowerCase();return"-"+s+"-"+a}}function n(e){return e?e+",":""}function i(e){return e.length>0?e:null}function o(r){function n(t,s){var a=p(t),r={oldStyle:e(t).attr("style")||""},n={data:a,stepData:r};f.call(this,"beforeInitStep",e(t),n),r.delegate=a.delegate,f.call(this,"initStep",e(t),n),e(t).data("stepData",r),e(t).attr("id")||e(t).attr("id","step-"+(s+1)),f.call(this,"applyStep",e(t),n)}function o(t){var s=e(t).data("stepData");e(t).attr("style",s.oldStyle),f.call(this,"unapplyStep",e(t),{stepData:s})}function c(t){f.call(this,"unapplyStep",e(t),{stepData:t.data("stepData")}),f.call(this,"applyStep",e(t),{stepData:t.data("stepData")})}function l(){O&&f.call(this,"setInactive",O,{stepData:e(O).data("stepData"),reason:"deinit"}),Y.jmpressClass&&e(I).removeClass(Y.jmpressClass),f.call(this,"beforeDeinit",e(this),{}),e(A.stepSelector,I).each(function(){o.call(I,this)}),k.attr("style",Z.container),A.fullscreen&&e("html").attr("style",""),z.attr("style",Z.area),e(X).children().each(function(){I.append(e(this))}),A.fullscreen?X.remove():(X.remove(),z.remove()),f.call(this,"afterDeinit",e(this),{}),e(I).data("jmpressmethods",!1)}function f(t,s,a){a.settings=A,a.current=Y,a.container=k,a.parents=s?d(s):null,a.current=Y,a.jmpress=this;var r={};return e.each(A[t],function(e,t){r.value=t.call(I,s,a)||r.value}),r.value}function d(t){return e(t).parentsUntil(I).not(I).filter(A.stepSelector)}function v(e){return g({step:O,substep:F},e)}function g(t,s){var r;if(e.isPlainObject(t)&&(r=t.substep,t=t.step),"string"==typeof t&&(t=I.find(t).first()),!t||!e(t).data("stepData"))return!1;b.call(this);var n=e(t).data("stepData"),o=!1;if(f.call(this,"beforeChange",t,{stepData:n,reason:s,cancel:function(){o=!0}}),o)return a;var c={},l=t;e(t).data("stepData").delegate&&(l=i(e(t).parentsUntil(I).filter(A.stepSelector).filter(n.delegate))||i(e(t).near(n.delegate))||i(e(t).near(n.delegate,!0))||i(e(n.delegate,I)),l?n=l.data("stepData"):l=t),Q&&f.call(this,"setInactive",Q,{stepData:e(Q).data("stepData"),delegatedFrom:O,reason:s,target:c,nextStep:l,nextSubstep:r,nextStepData:n});var u={stepData:n,delegatedFrom:t,reason:s,target:c,substep:r,prevStep:Q,prevSubstep:F,prevStepData:Q&&e(Q).data("stepData")};return f.call(this,"beforeActive",l,u),f.call(this,"setActive",l,u),Y.jmpressClass&&e(I).removeClass(Y.jmpressClass),e(I).addClass(Y.jmpressClass="step-"+e(l).attr("id")),Y.jmpressDelegatedClass&&e(I).removeClass(Y.jmpressDelegatedClass),e(I).addClass(Y.jmpressDelegatedClass="delegating-step-"+e(t).attr("id")),f.call(this,"applyTarget",l,e.extend({canvas:X,area:z,beforeActive:Q},u)),O=t,F=u.substep,Q=l,Y.idleTimeout&&clearTimeout(Y.idleTimeout),Y.idleTimeout=setTimeout(function(){f.call(this,"idle",l,u)},Math.max(1,A.transitionDuration-100)),l}function b(){(function t(){function a(){(0!==e(k).scrollTop()||0!==e(k).scrollLeft())&&t()}if("BODY"===e(k)[0].tagName)try{s.scrollTo(0,0)}catch(r){}e(k).scrollTop(0),e(k).scrollLeft(0),setTimeout(a,1),setTimeout(a,10),setTimeout(a,100),setTimeout(a,200),setTimeout(a,400)})()}function y(e){return g.call(this,e,"jump")}function j(){return g.call(this,f.call(this,"selectNext",O,{stepData:e(O).data("stepData"),substep:F}),"next")}function D(){return g.call(this,f.call(this,"selectPrev",O,{stepData:e(O).data("stepData"),substep:F}),"prev")}function S(){return g.call(this,f.call(this,"selectHome",O,{stepData:e(O).data("stepData")}),"home")}function x(){return g.call(this,f.call(this,"selectEnd",O,{stepData:e(O).data("stepData")}),"end")}function w(t){return u(X,t||{}),e(X)}function C(){return Q&&e(Q)}function T(t,s,r){return h[t]?f.call(this,t,s,r):(e.error("callback "+t+" is not registered."),a)}function M(){var e=navigator.userAgent.toLowerCase();return-1===e.search(/(iphone)|(ipod)|(android)/)||-1!==e.search(/(chrome)/)}r=e.extend(!0,{},r||{});var P={},N=null;for(N in h)P[N]=e.isFunction(r[N])?[r[N]]:r[N],r[N]=[];var A=e.extend(!0,{},m,r);for(N in h)P[N]&&Array.prototype.push.apply(A[N],P[N]);var I=e(this),k=null,z=null,Z={container:"",area:""},X=null,Y=null,O=!1,F=null,Q=!1;if(I.data("jmpressmethods",{select:g,reselect:v,scrollFix:b,goTo:y,next:j,prev:D,home:S,end:x,canvas:w,container:function(){return k},settings:function(){return A},active:C,current:function(){return Y},fire:T,init:function(t){n.call(this,e(t),Y.nextIdNumber++)},deinit:function(t){t?o.call(this,e(t)):l.call(this)},reapply:c}),M()===!1)return A.notSupportedClass&&I.addClass(A.notSupportedClass),a;A.notSupportedClass&&I.removeClass(A.notSupportedClass);var E=e(A.stepSelector,I);k=I,z=e("<div />"),X=e("<div />"),e(I).children().filter(E).each(function(){X.append(e(this))}),A.fullscreen&&(k=e("body"),e("html").css({overflow:"hidden"}),z=I),Z.area=z.attr("style")||"",Z.container=k.attr("style")||"",A.fullscreen?(k.css({height:"100%"}),I.append(X)):(k.css({position:"relative"}),z.append(X),I.append(z)),e(k).addClass(A.containerClass),e(z).addClass(A.areaClass),e(X).addClass(A.canvasClass),t.documentElement.style.height="100%",k.css({overflow:"hidden"});var L={position:"absolute",transitionDuration:"0s"};L=e.extend({},A.animation,L),u(z,L),u(z,{top:"50%",left:"50%",perspective:"1000px"}),u(X,L),Y={},f.call(this,"beforeInit",null,{}),E.each(function(e){n.call(I,this,e)}),Y.nextIdNumber=E.length,f.call(this,"afterInit",null,{}),g.call(this,f.call(this,"selectInitialStep","init",{})),A.initClass&&e(E).removeClass(A.initClass)}function c(){return m}function l(t,s){e.isFunction(s)?g[t]?e.error("function "+t+" is already registered."):g[t]=s:h[t]?e.error("callback "+t+" is already registered."):(h[t]=1,m[t]=[])}function u(t,s){var a,r,n={};for(a in s)s.hasOwnProperty(a)&&(r=d(a),null!==r&&(n[r]=s[a]));return e(t).css(n),t}function p(t){function s(e){e=e.split("-");for(var t=1;e.length>t;t++)e[t]=e[t].substr(0,1).toUpperCase()+e[t].substr(1);return e.join("")}if(e(t)[0].dataset)return e.extend({},e(t)[0].dataset);var a={},r=e(t)[0].attributes;return e.each(r,function(e,t){"data-"===t.nodeName.substr(0,5)&&(a[s(t.nodeName.substr(5))]=t.nodeValue)}),a}function f(){return!!e(this).data("jmpressmethods")}var d=function(){var e=t.createElement("dummy").style,s="Webkit Moz O ms Khtml".split(" "),r={};return function(t){if(r[t]===a){var n=t.charAt(0).toUpperCase()+t.substr(1),i=(t+" "+s.join(n+" ")+n).split(" ");r[t]=null;for(var o in i)if(e[i[o]]!==a){r[t]=i[o];break}}return r[t]}}(),m={stepSelector:".step",containerClass:"",canvasClass:"",areaClass:"",notSupportedClass:"not-supported",fullscreen:!0,animation:{transformOrigin:"top left",transitionProperty:n(r(d("transform")))+n(r(d("perspective")))+"opacity",transitionDuration:"1s",transitionDelay:"500ms",transitionTimingFunction:"ease-in-out",transformStyle:"preserve-3d"},transitionDuration:1500},h={beforeChange:1,beforeInitStep:1,initStep:1,beforeInit:1,afterInit:1,beforeDeinit:1,afterDeinit:1,applyStep:1,unapplyStep:1,setInactive:1,beforeActive:1,setActive:1,selectInitialStep:1,selectPrev:1,selectNext:1,selectHome:1,selectEnd:1,idle:1,applyTarget:1};for(var v in h)m[v]=[];var g={init:o,initialized:f,deinit:function(){},css:u,pfx:d,defaults:c,register:l,dataset:p};e.fn.jmpress=function(t){function s(){var s=e(this).data("jmpressmethods");if(s&&s[t])return s[t].apply(this,Array.prototype.slice.call(arguments,1));if(g[t])return g[t].apply(this,Array.prototype.slice.call(arguments,1));if(h[t]&&s){var a=s.settings(),r=Array.prototype.slice.call(arguments,1)[0];e.isFunction(r)&&(a[t]=a[t]||[],a[t].push(r))}else{if("object"==typeof t||!t)return o.apply(this,arguments);e.error("Method "+t+" does not exist on jQuery.jmpress")}return this}var a,r=arguments;return e(this).each(function(e,t){a=s.apply(t,r)}),a},e.extend({jmpress:function(t){if(g[t])return g[t].apply(this,Array.prototype.slice.call(arguments,1));if(h[t]){var s=Array.prototype.slice.call(arguments,1)[0];e.isFunction(s)?m[t].push(s):e.error("Second parameter should be a function: $.jmpress( callbackName, callbackFunction )")}else e.error("Method "+t+" does not exist on jQuery.jmpress")}})})(jQuery,document,window),function(e){"use strict";function t(t,s,a,r){var n;return t.each(function(t,i){return r&&(n=s(i,a,r))?!1:e(i).is(a)?(n=i,!1):!r&&(n=s(i,a,r))?!1:undefined}),n}function s(a,r,n){var i=e(a).children();return n&&(i=e(i.get().reverse())),t(i,s,r,n)}function a(a,r,n){return t(e(a)[n?"prevAll":"nextAll"](),s,r,n)}function r(t,s,r){var n,i=e(t).parents();return i=e(i.get()),e.each(i.get(),function(t,i){return r&&e(i).is(s)?(n=i,!1):(n=a(i,s,r),n?!1:undefined)}),n}e.fn.near=function(t,n){var i=[];return e(this).each(function(e,o){var c=(n?!1:s(o,t,n))||a(o,t,n)||r(o,t,n);c&&i.push(c)}),e(i)}}(jQuery,document,window),function(e,t,s,a){"use strict";function r(e){return Math.round(1e4*e)/1e4+""}var n={3:{transform:function(t,s){var a="translate(-50%,-50%)";e.each(s,function(e,t){var s,n=["X","Y","Z"];if("translate"===t[0])a+=" translate3d("+r(t[1]||0)+"px,"+r(t[2]||0)+"px,"+r(t[3]||0)+"px)";else if("rotate"===t[0]){var i=t[4]?[1,2,3]:[3,2,1];for(s=0;3>s;s++)a+=" rotate"+n[i[s]-1]+"("+r(t[i[s]]||0)+"deg)"}else if("scale"===t[0])for(s=0;3>s;s++)a+=" scale"+n[s]+"("+r(t[s+1]||1)+")"}),e.jmpress("css",t,e.extend({},{transform:a}))}},2:{transform:function(t,s){var a="translate(-50%,-50%)";e.each(s,function(e,t){var s=["X","Y"];if("translate"===t[0])a+=" translate("+r(t[1]||0)+"px,"+r(t[2]||0)+"px)";else if("rotate"===t[0])a+=" rotate("+r(t[3]||0)+"deg)";else if("scale"===t[0])for(var n=0;2>n;n++)a+=" scale"+s[n]+"("+r(t[n+1]||1)+")"}),e.jmpress("css",t,e.extend({},{transform:a}))}},1:{transform:function(t,s){var a={top:0,left:0};e.each(s,function(e,t){"translate"===t[0]&&(a.left=Math.round(t[1]||0)+"px",a.top=Math.round(t[2]||0)+"px")}),t.animate(a,1e3)}}},i=function(){return e.jmpress("pfx","perspective")?n[3]:e.jmpress("pfx","transform")?n[2]:n[1]}();e.jmpress("defaults").reasonableAnimation={},e.jmpress("initStep",function(t,s){var a=s.data,r=s.stepData,n=parseFloat;e.extend(r,{x:n(a.x)||0,y:n(a.y)||0,z:n(a.z)||0,r:n(a.r)||0,phi:n(a.phi)||0,rotate:n(a.rotate)||0,rotateX:n(a.rotateX)||0,rotateY:n(a.rotateY)||0,rotateZ:n(a.rotateZ)||0,revertRotate:!1,scale:n(a.scale)||1,scaleX:n(a.scaleX)||!1,scaleY:n(a.scaleY)||!1,scaleZ:n(a.scaleZ)||1})}),e.jmpress("afterInit",function(t,s){var a=s.settings.stepSelector,r=s.current;r.perspectiveScale=1,r.maxNestedDepth=0;for(var n=e(s.jmpress).find(a).children(a);n.length;)r.maxNestedDepth++,n=n.children(a)}),e.jmpress("applyStep",function(t,s){e.jmpress("css",e(t),{position:"absolute",transformStyle:"preserve-3d"}),s.parents.length>0&&e.jmpress("css",e(t),{top:"50%",left:"50%"});var a=s.stepData,r=[["translate",a.x||a.r*Math.sin(a.phi*Math.PI/180),a.y||-a.r*Math.cos(a.phi*Math.PI/180),a.z],["rotate",a.rotateX,a.rotateY,a.rotateZ||a.rotate,!0],["scale",a.scaleX||a.scale,a.scaleY||a.scale,a.scaleZ||a.scale]];i.transform(t,r)}),e.jmpress("setActive",function(t,s){var r=s.target,n=s.stepData,i=r.transform=[];r.perspectiveScale=1;for(var o=s.current.maxNestedDepth;o>(s.parents.length||0);o--)i.push(["scale"],["rotate"],["translate"]);i.push(["scale",1/(n.scaleX||n.scale),1/(n.scaleY||n.scale),1/n.scaleZ]),i.push(["rotate",-n.rotateX,-n.rotateY,-(n.rotateZ||n.rotate)]),i.push(["translate",-(n.x||n.r*Math.sin(n.phi*Math.PI/180)),-(n.y||-n.r*Math.cos(n.phi*Math.PI/180)),-n.z]),r.perspectiveScale*=n.scaleX||n.scale,e.each(s.parents,function(t,s){var a=e(s).data("stepData");i.push(["scale",1/(a.scaleX||a.scale),1/(a.scaleY||a.scale),1/a.scaleZ]),i.push(["rotate",-a.rotateX,-a.rotateY,-(a.rotateZ||a.rotate)]),i.push(["translate",-(a.x||a.r*Math.sin(a.phi*Math.PI/180)),-(a.y||-a.r*Math.cos(a.phi*Math.PI/180)),-a.z]),r.perspectiveScale*=a.scaleX||a.scale}),e.each(i,function(e,t){function r(r){s.current["rotate"+r+"-"+e]===a&&(s.current["rotate"+r+"-"+e]=t[r]||0);var n=s.current["rotate"+r+"-"+e],i=t[r]||0,o=n%360,c=i%360;0>o&&(o+=360),0>c&&(c+=360);var l=c-o;-180>l?l+=360:l>180&&(l-=360),s.current["rotate"+r+"-"+e]=t[r]=n+l}"rotate"===t[0]&&(r(1),r(2),r(3))})}),e.jmpress("applyTarget",function(t,s){var r,n=s.target,o=(s.stepData,s.settings),c=1.3*n.perspectiveScale<s.current.perspectiveScale,l=n.perspectiveScale>1.3*s.current.perspectiveScale,u=-1;e.each(n.transform,function(e,t){return 1>=t.length||"rotate"===t[0]&&0===t[1]%360&&0===t[2]%360&&0===t[3]%360?a:"scale"!==t[0]?!1:(u=e,a)}),u!==s.current.oldLastScale&&(c=l=!1,s.current.oldLastScale=u);var p=[];if(-1!==u)for(;u>=0;)"scale"===n.transform[u][0]&&(p.push(n.transform[u]),n.transform[u]=["scale"]),u--;var f=o.animation;o.reasonableAnimation[s.reason]&&(f=e.extend({},f,o.reasonableAnimation[s.reason])),r={perspective:Math.round(1e3*n.perspectiveScale)+"px"},r=e.extend({},f,r),c||(r.transitionDelay="0s"),s.beforeActive||(r.transitionDuration="0s",r.transitionDelay="0s"),e.jmpress("css",s.area,r),i.transform(s.area,p),r=e.extend({},f),l||(r.transitionDelay="0s"),s.beforeActive||(r.transitionDuration="0s",r.transitionDelay="0s"),s.current.perspectiveScale=n.perspectiveScale,e.jmpress("css",s.canvas,r),i.transform(s.canvas,n.transform)})}(jQuery,document,window),function(e){"use strict";var t=e.jmpress,s="activeClass",a="nestedActiveClass",r=t("defaults");r[a]="nested-active",r[s]="active",t("setInactive",function(t,r){var n=r.settings,i=n[s],o=n[a];i&&e(t).removeClass(i),o&&e.each(r.parents,function(t,s){e(s).removeClass(o)})}),t("setActive",function(t,r){var n=r.settings,i=n[s],o=n[a];i&&e(t).addClass(i),o&&e.each(r.parents,function(t,s){e(s).addClass(o)})})}(jQuery,document,window),function(e){"use strict";function t(t,s){return e(this).find(s.settings.stepSelector).first()}function s(t,s,a,r){if(!s)return!1;var n=a.settings.stepSelector;s=e(s);do{var i=s.near(n,r);if((0===i.length||0===i.closest(t).length)&&(i=e(t).find(n)[r?"last":"first"]()),!i.length)return!1;s=i}while(s.data("stepData").exclude);return s}var a=e.jmpress;a("initStep",function(e,t){t.stepData.exclude=t.data.exclude&&-1===["false","no"].indexOf(t.data.exclude)}),a("selectInitialStep",t),a("selectHome",t),a("selectEnd",function(t,s){return e(this).find(s.settings.stepSelector).last()}),a("selectPrev",function(e,t){return s(this,e,t,!0)}),a("selectNext",function(e,t){return s(this,e,t)})}(jQuery,document,window),function(e){"use strict";e.jmpress("selectInitialStep",function(e,t){return t.settings.start})}(jQuery,document,window),function(e){"use strict";function t(t,s,a){for(var r=0;s.length-1>r;r++){var n=s[r],i=s[r+1];e(t).jmpress("initialized")?e(n,t).data("stepData")[a]=i:e(n,t).attr("data-"+a,i)}}function s(t,s,a,r){var n=s.stepData;if(n[a]){var i=e(t).near(n[a],r);if(i&&i.length)return i;if(i=e(n[a],this)[r?"last":"first"](),i&&i.length)return i}}var a=e.jmpress;a("register","route",function(e,s,a){"string"==typeof e&&(e=[e,e]),t(this,e,a?"prev":"next"),s||t(this,e.reverse(),a?"next":"prev")}),a("initStep",function(e,t){for(var s in{next:1,prev:1})t.stepData[s]=t.data[s]}),a("selectNext",function(e,t){return s.call(this,e,t,"next")}),a("selectPrev",function(e,t){return s.call(this,e,t,"prev",!0)})}(jQuery,document,window),function(e){"use strict";var t=e.jmpress,s="ajax:afterStepLoaded",a="ajax:loadStep";t("register",a),t("register",s),t("defaults").ajaxLoadedClass="loaded",t("initStep",function(t,s){s.stepData.src=e(t).attr("href")||s.data.src||!1,s.stepData.srcLoaded=!1}),t(a,function(t,a){var r=a.stepData,n=r&&r.src,i=a.settings;n&&(e(t).addClass(i.ajaxLoadedClass),r.srcLoaded=!0,e(t).load(n,function(r,n,i){e(a.jmpress).jmpress("fire",s,t,e.extend({},a,{response:r,status:n,xhr:i}))}))}),t("idle",function(t,s){if(t){var r=s.settings,n=e(this);s.stepData;var i=e(t).add(e(t).near(r.stepSelector)).add(e(t).near(r.stepSelector,!0)).add(n.jmpress("fire","selectPrev",t,{stepData:e(t).data("stepData")})).add(n.jmpress("fire","selectNext",t,{stepData:e(t).data("stepData")}));i.each(function(){var t=this,s=e(t).data("stepData");s.src&&!s.srcLoaded&&n.jmpress("fire",a,t,{stepData:e(t).data("stepData")})})}}),t("setActive",function(t){var s=e(t).data("stepData");s.src&&!s.srcLoaded&&e(this).jmpress("fire",a,t,{stepData:e(t).data("stepData")})})}(jQuery,document,window),function(e,t,s,a){"use strict";function r(){return""+Math.round(1e5*Math.random(),0)}function n(t){try{var r=e("#"+s.location.hash.replace(/^#\/?/,""));return r.length>0&&r.is(t.stepSelector)?r:a}catch(n){}}function i(e){var t="#/"+e;s.history&&s.history.pushState?s.location.hash!==t&&s.history.pushState({},"",t):s.location.hash!==t&&(s.location.hash=t)}var o=e.jmpress,c="a[href^=#]";o("defaults").hash={use:!0,update:!0,bindChange:!0},o("selectInitialStep",function(t,o){var l=o.settings,u=l.hash,p=o.current,f=e(this);return o.current.hashNamespace=".jmpress-"+r(),u.use?(u.bindChange&&(e(s).bind("hashchange"+p.hashNamespace,function(e){var t=n(l);f.jmpress("initialized")&&f.jmpress("scrollFix"),t&&t.length&&(t.attr("id")!==f.jmpress("active").attr("id")&&f.jmpress("select",t),i(t.attr("id"))),e.preventDefault()}),e(c).on("click"+p.hashNamespace,function(t){var s=e(this).attr("href");try{e(s).is(l.stepSelector)&&(f.jmpress("select",s),t.preventDefault(),t.stopPropagation())}catch(a){}})),n(l)):a}),o("afterDeinit",function(t,a){e(c).off(a.current.hashNamespace),e(s).unbind(a.current.hashNamespace)}),o("setActive",function(t,s){var a=s.settings,r=s.current;a.hash.use&&a.hash.update&&(clearTimeout(r.hashtimeout),r.hashtimeout=setTimeout(function(){i(e(s.delegatedFrom).attr("id"))},a.transitionDuration+200))})}(jQuery,document,window),function(e,t,s,a){"use strict";function r(){return""+Math.round(1e5*Math.random(),0)}function n(e){e.preventDefault(),e.stopPropagation()}var i=e.jmpress,o="next",c="prev";i("defaults").keyboard={use:!0,keys:{33:c,37:c,38:c,9:o+":"+c,32:o,34:o,39:o,40:o,36:"home",35:"end"},ignore:{INPUT:[32,37,38,39,40],TEXTAREA:[32,37,38,39,40],SELECT:[38,40]},tabSelector:"a[href]:visible, :input:visible"},i("afterInit",function(s,i){var o=i.settings,c=o.keyboard,l=c.ignore,u=i.current,p=e(this);o.fullscreen||p.attr("tabindex",0),u.keyboardNamespace=".jmpress-"+r(),e(o.fullscreen?t:p).bind("keypress"+u.keyboardNamespace,function(e){for(var t in l)if(e.target.nodeName===t&&-1!==l[t].indexOf(e.which))return;(e.which>=37&&40>=e.which||32===e.which)&&n(e)}),e(o.fullscreen?t:p).bind("keydown"+u.keyboardNamespace,function(t){var s=e(t.target);if((o.fullscreen||s.closest(p).length)&&c.use){for(var r in l)if(s[0].nodeName===r&&-1!==l[r].indexOf(t.which))return;var i,u=!1;if(9===t.which){if(s.closest(p.jmpress("active")).length?(i=s.near(c.tabSelector,t.shiftKey),e(i).closest(o.stepSelector).is(p.jmpress("active"))||(i=a)):t.shiftKey?u=!0:i=p.jmpress("active").find("a[href], :input").filter(":visible").first(),i&&i.length>0)return i.focus(),p.jmpress("scrollFix"),n(t),a;t.shiftKey&&(u=!0)}var f=c.keys[t.which];"string"==typeof f?(-1!==f.indexOf(":")&&(f=f.split(":"),f=t.shiftKey?f[1]:f[0]),p.jmpress(f),n(t)):e.isFunction(f)?f.call(p,t):f&&(p.jmpress.apply(p,f),n(t)),u&&(i=p.jmpress("active").find("a[href], :input").filter(":visible").last(),i.focus(),p.jmpress("scrollFix"))}})}),i("afterDeinit",function(s,a){e(t).unbind(a.current.keyboardNamespace)})}(jQuery,document,window),function(e,t,s,a){"use strict";function r(){return""+Math.round(1e5*Math.random(),0)}function n(e,t){return Math.max(Math.min(e,t),-t)}function i(t,s,a){var r=e(this).jmpress("current"),i=e(this).jmpress("settings"),o=e(this).jmpress("active").data("stepData"),c=e(this).jmpress("container");if(!(0===r.userZoom&&0>a)){var l=o.viewPortZoomable||i.viewPort.zoomable;if(!(r.userZoom===l&&a>0)){r.userZoom+=a;var u=e(c).innerWidth()/2,p=e(c).innerHeight()/2;t=t?t-u:t,s=s?s-p:s,r.userTranslateX=n(r.userTranslateX-a*t/r.zoomOriginWindowScale/l,u*r.userZoom*r.userZoom/l),r.userTranslateY=n(r.userTranslateY-a*s/r.zoomOriginWindowScale/l,p*r.userZoom*r.userZoom/l),e(this).jmpress("reselect","zoom")}}}var o=function(){var e=navigator.userAgent.toLowerCase(),t=/(chrome)[ \/]([\w.]+)/.exec(e)||/(webkit)[ \/]([\w.]+)/.exec(e)||/(opera)(?:.*version|)[ \/]([\w.]+)/.exec(e)||/(msie) ([\w.]+)/.exec(e)||0>e.indexOf("compatible")&&/(mozilla)(?:.*? rv:([\w.]+)|)/.exec(e)||[];return t[1]||""}(),c=e.jmpress("defaults");c.viewPort={width:!1,height:!1,maxScale:0,minScale:0,zoomable:0,zoomBindMove:!0,zoomBindWheel:!0};var l=c.keyboard.keys;l["mozilla"===o?107:187]="zoomIn",l["mozilla"===o?109:189]="zoomOut",c.reasonableAnimation.resize={transitionDuration:"0s",transitionDelay:"0ms"},c.reasonableAnimation.zoom={transitionDuration:"0s",transitionDelay:"0ms"},e.jmpress("initStep",function(e,t){for(var s in{viewPortHeight:1,viewPortWidth:1,viewPortMinScale:1,viewPortMaxScale:1,viewPortZoomable:1})t.stepData[s]=t.data[s]&&parseFloat(t.data[s])}),e.jmpress("afterInit",function(n,i){var o=this;i.current.viewPortNamespace=".jmpress-"+r(),e(s).bind("resize"+i.current.viewPortNamespace,function(){e(o).jmpress("reselect","resize")}),i.current.userZoom=0,i.current.userTranslateX=0,i.current.userTranslateY=0,i.settings.viewPort.zoomBindWheel&&e(i.settings.fullscreen?t:this).bind("mousewheel"+i.current.viewPortNamespace+" DOMMouseScroll"+i.current.viewPortNamespace,function(t,s){s=s||t.originalEvent.wheelDelta||-t.originalEvent.detail;var a=s/Math.abs(s);return 0>a?e(i.jmpress).jmpress("zoomOut",t.originalEvent.x,t.originalEvent.y):a>0&&e(i.jmpress).jmpress("zoomIn",t.originalEvent.x,t.originalEvent.y),!1}),i.settings.viewPort.zoomBindMove&&e(i.settings.fullscreen?t:this).bind("mousedown"+i.current.viewPortNamespace,function(e){i.current.userZoom&&(i.current.userTranslating={x:e.clientX,y:e.clientY},e.preventDefault(),e.stopImmediatePropagation())}).bind("mousemove"+i.current.viewPortNamespace,function(t){var s=i.current.userTranslating;s&&(e(o).jmpress("zoomTranslate",t.clientX-s.x,t.clientY-s.y),s.x=t.clientX,s.y=t.clientY,t.preventDefault(),t.stopImmediatePropagation())}).bind("mouseup"+i.current.viewPortNamespace,function(e){i.current.userTranslating&&(i.current.userTranslating=a,e.preventDefault(),e.stopImmediatePropagation())})}),e.jmpress("register","zoomIn",function(e,t){i.call(this,e||0,t||0,1)}),e.jmpress("register","zoomOut",function(e,t){i.call(this,e||0,t||0,-1)}),e.jmpress("register","zoomTranslate",function(t,s){var a=e(this).jmpress("current"),r=e(this).jmpress("settings"),i=e(this).jmpress("active").data("stepData"),o=e(this).jmpress("container"),c=i.viewPortZoomable||r.viewPort.zoomable,l=e(o).innerWidth(),u=e(o).innerHeight();a.userTranslateX=n(a.userTranslateX+t/a.zoomOriginWindowScale,l*a.userZoom*a.userZoom/c),a.userTranslateY=n(a.userTranslateY+s/a.zoomOriginWindowScale,u*a.userZoom*a.userZoom/c),e(this).jmpress("reselect","zoom")}),e.jmpress("afterDeinit",function(a,r){e(r.settings.fullscreen?t:this).unbind(r.current.viewPortNamespace),e(s).unbind(r.current.viewPortNamespace)}),e.jmpress("setActive",function(t,s){var a=s.settings.viewPort,r=s.stepData.viewPortHeight||a.height,n=s.stepData.viewPortWidth||a.width,i=s.stepData.viewPortMaxScale||a.maxScale,o=s.stepData.viewPortMinScale||a.minScale,c=r&&e(s.container).innerHeight()/r,l=n&&e(s.container).innerWidth()/n,u=(l||c)&&Math.min(l||c,c||l);if(u){u=u||1,i&&(u=Math.min(u,i)),o&&(u=Math.max(u,o));var p=s.stepData.viewPortZoomable||s.settings.viewPort.zoomable;if(p){var f=1/u-1/i;f/=p,u=1/(1/u-f*s.current.userZoom)}s.target.transform.reverse(),s.current.userTranslateX&&s.current.userTranslateY?s.target.transform.push(["translate",s.current.userTranslateX,s.current.userTranslateY,0]):s.target.transform.push(["translate"]),s.target.transform.push(["scale",u,u,1]),s.target.transform.reverse(),s.target.perspectiveScale/=u}s.current.zoomOriginWindowScale=u}),e.jmpress("setInactive",function(t,s){s.nextStep&&t&&e(s.nextStep).attr("id")===e(t).attr("id")||(s.current.userZoom=0,s.current.userTranslateX=0,s.current.userTranslateY=0)})}(jQuery,document,window),function(e){"use strict";function t(){return""+Math.round(1e5*Math.random(),0)}var s=e.jmpress;s("defaults").mouse={clickSelects:!0},s("afterInit",function(s,a){var r=a.settings,n=r.stepSelector,i=a.current,o=e(this);i.clickableStepsNamespace=".jmpress-"+t(),o.bind("click"+i.clickableStepsNamespace,function(t){if(r.mouse.clickSelects&&!i.userZoom){var s=e(t.target).closest(n);s.is(o.jmpress("active"))||s.length&&(o.jmpress("select",s[0],"click"),t.preventDefault(),t.stopPropagation())}})}),s("afterDeinit",function(t,s){e(this).unbind(s.current.clickableStepsNamespace)})}(jQuery,document,window),function(e,t){"use strict";function s(){return""+Math.round(1e5*Math.random(),0)}var a=e.jmpress;a("afterInit",function(a,r){var n=r.settings,i=r.current,o=r.jmpress;i.mobileNamespace=".jmpress-"+s();var c,l=[0,0];e(n.fullscreen?t:o).bind("touchstart"+i.mobileNamespace,function(e){c=e.originalEvent.touches[0],l=[c.pageX,c.pageY]}).bind("touchmove"+i.mobileNamespace,function(e){return c=e.originalEvent.touches[0],e.preventDefault(),!1}).bind("touchend"+i.mobileNamespace,function(t){var s=[c.pageX,c.pageY],a=[s[0]-l[0],s[1]-l[1]];return Math.max(Math.abs(a[0]),Math.abs(a[1]))>50?(a=Math.abs(a[0])>Math.abs(a[1])?a[0]:a[1],e(o).jmpress(a>0?"prev":"next"),t.preventDefault(),!1):undefined})}),a("afterDeinit",function(s,a){var r=a.settings,n=a.current,i=a.jmpress;e(r.fullscreen?t:i).unbind(n.mobileNamespace)})}(jQuery,document,window),function(e,t,s,a){"use strict";function r(t,s,n){for(var i in s){var o=i;n&&(o=n+o.substr(0,1).toUpperCase()+o.substr(1)),e.isPlainObject(s[i])?r(t,s[i],o):t[o]===a&&(t[o]=s[i])}}function n(t,s){e.isArray(s)?s.length<t.length?e.error("more nested steps than children in template"):t.each(function(t,a){a=e(a);var n=a.data(l)||{};r(n,s[t]),a.data(l,n)}):e.isFunction(s)&&t.each(function(a,n){n=e(n);var i=n.data(l)||{};r(i,s(a,n,t)),n.data(l,i)})}function i(e,t,s,a){if(s.children){var r=t.children(a.settings.stepSelector);n(r,s.children)}o(e,s)}function o(e,t){r(e,t)}var c=e.jmpress,l="_template_",u="_applied_template_",p={};c("beforeInitStep",function(t,s){t=e(t);var a=s.data,r=a.template,n=t.data(u),o=t.data(l);r&&e.each(r.split(" "),function(e,r){var n=p[r];i(a,t,n,s)}),n&&i(a,t,n,s),o&&(i(a,t,o,s),t.data(l,null),o.template&&e.each(o.template.split(" "),function(e,r){var n=p[r];i(a,t,n,s)}))}),c("beforeInit",function(t,s){var a=c("dataset",this),r=a.template,i=s.settings.stepSelector;if(r){var o=p[r];n(e(this).find(i).filter(function(){return!e(this).parent().is(i)}),o.children)}}),c("register","template",function(t,s){p[t]=p[t]?e.extend(!0,{},p[t],s):e.extend(!0,{},s)}),c("register","apply",function(t,s){if(s)if(e.isArray(s))n(e(t),s);else{var a;a="string"==typeof s?p[s]:e.extend(!0,{},s),e(t).each(function(t,s){s=e(s);var n=s.data(u)||{};r(n,a),s.data(u,n)})}else{var i=e(this).jmpress("settings").stepSelector;n(e(this).find(i).filter(function(){return!e(this).parent().is(i)}),t)}})}(jQuery,document,window),function(e){"use strict";e.jmpress("setActive",function(t,s){s.prevStep!==t&&e(t).triggerHandler("enterStep")}),e.jmpress("setInactive",function(t,s){s.nextStep!==t&&e(t).triggerHandler("leaveStep")})}(jQuery,document,window),function(e,t,s,a){"use strict";function r(t){for(var s=t.split(" "),a=s[0],r={willClass:"will-"+a,doClass:"do-"+a,hasClass:"has-"+a},n="",i=1;s.length>i;i++){var o=s[i];switch(n){case"":"after"===o?n="after":e.warn("unknown keyword in '"+t+"'. '"+o+"' unknown.");break;case"after":if(o.match(/^[1-9][0-9]*m?s?/)){var c=parseFloat(o);-1!==o.indexOf("ms")?c*=1:-1!==o.indexOf("s")?c*=1e3:-1!==o.indexOf("m")&&(c*=6e4),r.delay=c}else r.after=Array.prototype.slice.call(s,i).join(" "),i=s.length}}return r}function n(t,s,a,r){r=r||t.length-1,a=a||0;for(var n=a;r+1>n;n++)if(e(t[n].element).is(s))return n}function i(t,s,a){e.each(s._on,function(e,s){t.push({substep:s.substep,delay:s.delay+a}),i(t,s.substep,s.delay+a)})}e.jmpress("defaults").customAnimationDataAttribute="jmpress",e.jmpress("afterInit",function(e,t){t.current.animationTimeouts=[],t.current.animationCleanupWaiting=[]}),e.jmpress("applyStep",function(t,s){function o(e,t){return t.substep._after?(u=t.substep._after,!1):a}var c={},l=[];if(e(t).find("[data-"+s.settings.customAnimationDataAttribute+"]").each(function(a,r){e(r).closest(s.settings.stepSelector).is(t)&&l.push({element:r})}),0!==l.length){e.each(l,function(t,a){a.info=r(e(a.element).data(s.settings.customAnimationDataAttribute)),e(a.element).addClass(a.info.willClass),a._on=[],a._after=null});var u={_after:a,_on:[],info:{}};if(e.each(l,function(e,t){var s=t.info.after;if(s)if("step"===s)s=u;else if("prev"===s)s=l[e-1];else{var r=n(l,s,0,e-1);r===a&&(r=n(l,s)),s=r===a||r===e?l[e-1]:l[r]}else s=l[e-1];if(s){if(!t.info.delay){if(!s._after)return s._after=t,a;s=s._after}s._on.push({substep:t,delay:t.info.delay||0})}}),u._after===a&&0===u._on.length){var p=n(l,s.stepData.startSubstep)||0;u._after=l[p]}var f=[];do{var d=[{substep:u,delay:0}];i(d,u,0),f.push(d),u=null,e.each(d,o)}while(u);c.list=f,e(t).data("substepsData",c)}}),e.jmpress("unapplyStep",function(t){var s=e(t).data("substepsData");s&&e.each(s.list,function(t,s){e.each(s,function(t,s){s.substep.info.willClass&&e(s.substep.element).removeClass(s.substep.info.willClass),s.substep.info.hasClass&&e(s.substep.element).removeClass(s.substep.info.hasClass),s.substep.info.doClass&&e(s.substep.element).removeClass(s.substep.info.doClass)})})}),e.jmpress("setActive",function(t,s){var r=e(t).data("substepsData");if(r){s.substep===a&&(s.substep="prev"===s.reason?r.list.length-1:0);var n=s.substep;e.each(s.current.animationTimeouts,function(e,t){clearTimeout(t)}),s.current.animationTimeouts=[],e.each(r.list,function(t,a){var r=n>t,i=n>=t;e.each(a,function(t,a){function n(){e(a.substep.element).addClass(a.substep.info.doClass)}a.substep.info.hasClass&&e(a.substep.element)[(r?"add":"remove")+"Class"](a.substep.info.hasClass),i&&!r&&a.delay&&"prev"!==s.reason?a.substep.info.doClass&&(e(a.substep.element).removeClass(a.substep.info.doClass),s.current.animationTimeouts.push(setTimeout(n,a.delay))):a.substep.info.doClass&&e(a.substep.element)[(i?"add":"remove")+"Class"](a.substep.info.doClass)})})}}),e.jmpress("setInactive",function(t,s){function a(t){e.each(t.list,function(t,s){e.each(s,function(t,s){s.substep.info.hasClass&&e(s.substep.element).removeClass(s.substep.info.hasClass),s.substep.info.doClass&&e(s.substep.element).removeClass(s.substep.info.doClass)})})}if(s.nextStep!==t){e.each(s.current.animationCleanupWaiting,function(e,t){a(t)}),s.current.animationCleanupWaiting=[];var r=e(t).data("substepsData");r&&s.current.animationCleanupWaiting.push(r)}}),e.jmpress("selectNext",function(t,s){if(s.substep!==a){var r=e(t).data("substepsData");if(r)return s.substep<r.list.length-1?{step:t,substep:s.substep+1}:a}}),e.jmpress("selectPrev",function(t,s){if(s.substep!==a){var r=e(t).data("substepsData");if(r)return s.substep>0?{step:t,substep:s.substep-1}:a}})}(jQuery,document,window),function(e,t){"use strict";e.jmpress("register","toggle",function(s,a,r){var n=this;e(t).bind("keydown",function(t){t.keyCode===s&&(e(n).jmpress("initialized")?e(n).jmpress("deinit"):e(n).jmpress(a))}),r&&e(n).jmpress(a)})}(jQuery,document,window),function(e){"use strict";function t(t,s,a){if(t.secondary&&-1!==t.secondary.split(" ").indexOf(s)){for(var r in t)if(r.length>9&&0===r.indexOf("secondary")){var n=t[r],i=r.substr(9);i=i.substr(0,1).toLowerCase()+i.substr(1),t[r]=t[i],t[i]=n}e(this).jmpress("reapply",e(a))}}e.jmpress("initStep",function(e,t){for(var s in t.data)0===s.indexOf("secondary")&&(t.stepData[s]=t.data[s])}),e.jmpress("beforeActive",function(s,a){t.call(a.jmpress,e(s).data("stepData"),"self",s);var r=e(s).parent();e(r).children(a.settings.stepSelector).each(function(s,r){var n=e(r).data("stepData");t.call(a.jmpress,n,"siblings",r)});for(var n=1;a.parents.length>n;n++)e(a.parents[n]).children(a.settings.stepSelector).each()}),e.jmpress("setInactive",function(s,a){function r(s,r){var n=e(r).data("stepData");t.call(a.jmpress,n,"grandchildren",r)}t.call(a.jmpress,e(s).data("stepData"),"self",s);
+var n=e(s).parent();e(n).children(a.settings.stepSelector).each(function(s,r){var n=e(r).data("stepData");t.call(a.jmpress,n,"siblings",r)});for(var i=1;a.parents.length>i;i++)e(a.parents[i]).children(a.settings.stepSelector).each(r)})}(jQuery,document,window),function(e,t,s,a){"use strict";e.jmpress("defaults").duration={defaultValue:-1,defaultAction:"next",barSelector:a,barProperty:"width",barPropertyStart:"0",barPropertyEnd:"100%"},e.jmpress("initStep",function(e,t){t.stepData.duration=t.data.duration&&parseInt(t.data.duration,10),t.stepData.durationAction=t.data.durationAction}),e.jmpress("setInactive",function(t,s){var a=s.settings,r=a.duration,n=s.current;if(s.stepData.duration||r.defaultValue,n.durationTimeout){if(r.barSelector){var i={transitionProperty:r.barProperty,transitionDuration:"0",transitionDelay:"0",transitionTimingFunction:"linear"};i[r.barProperty]=r.barPropertyStart;var o=e(r.barSelector);e.jmpress("css",o,i),o.each(function(t,s){var a=e(s).next(),r=e(s).parent();e(s).detach(),a.length?a.insertBefore(s):r.append(s)})}clearTimeout(n.durationTimeout),delete n.durationTimeout}}),e.jmpress("setActive",function(t,s){var r=s.settings,n=r.duration,i=s.current,o=s.stepData.duration||n.defaultValue;if(o&&o>0){if(n.barSelector){var c={transitionProperty:n.barProperty,transitionDuration:o-2*r.transitionDuration/3-100+"ms",transitionDelay:2*r.transitionDuration/3+"ms",transitionTimingFunction:"linear"};c[n.barProperty]=n.barPropertyEnd,e.jmpress("css",e(n.barSelector),c)}var l=this;i.durationTimeout&&(clearTimeout(i.durationTimeout),i.durationTimeout=a),i.durationTimeout=setTimeout(function(){var t=s.stepData.durationAction||n.defaultAction;e(l).jmpress(t)},o)}})}(jQuery,document,window),function(e,t,s){"use strict";var a=e.jmpress,r="jmpress-presentation-";a("defaults").presentationMode={use:!0,url:"presentation-screen.html",notesUrl:!1,transferredValues:["userZoom","userTranslateX","userTranslateY"]},a("defaults").keyboard.keys[80]="presentationPopup",a("afterInit",function(t,a){var n=a.current;if(n.selectMessageListeners=[],a.settings.presentationMode.use){s.addEventListener("message",function(t){try{if("string"!=typeof t.data||0!==t.data.indexOf(r))return;var i=JSON.parse(t.data.slice(r.length));switch(i.type){case"select":e.each(a.settings.presentationMode.transferredValues,function(e,t){a.current[t]=i[t]}),/[a-z0-9\-]+/i.test(i.targetId)&&typeof i.substep in{number:1,undefined:1}?e(a.jmpress).jmpress("select",{step:"#"+i.targetId,substep:i.substep},i.reason):e.error("For security reasons the targetId must match /[a-z0-9\\-]+/i and substep must be a number.");break;case"listen":n.selectMessageListeners.push(t.source);break;case"ok":clearTimeout(n.presentationPopupTimeout);break;case"read":try{t.source.postMessage(r+JSON.stringify({type:"url",url:s.location.href,notesUrl:a.settings.presentationMode.notesUrl}),"*")}catch(o){e.error("Cannot post message to source: "+o)}break;default:throw"Unknown message type: "+i.type}}catch(o){e.error("Received message is malformed: "+o)}});try{s.parent&&s.parent!==s&&s.parent.postMessage(r+JSON.stringify({type:"afterInit"}),"*")}catch(i){e.error("Cannot post message to parent: "+i)}}}),a("afterDeinit",function(t,a){if(a.settings.presentationMode.use)try{s.parent&&s.parent!==s&&s.parent.postMessage(r+JSON.stringify({type:"afterDeinit"}),"*")}catch(n){e.error("Cannot post message to parent: "+n)}}),a("setActive",function(t,s){var a=e(s.delegatedFrom).attr("id"),n=s.substep,i=s.reason;e.each(s.current.selectMessageListeners,function(t,o){try{var c={type:"select",targetId:a,substep:n,reason:i};e.each(s.settings.presentationMode.transferredValues,function(e,t){c[t]=s.current[t]}),o.postMessage(r+JSON.stringify(c),"*")}catch(l){e.error("Cannot post message to listener: "+l)}})}),a("register","presentationPopup",function(){function t(){n.jmpress("current").presentationPopupTimeout=setTimeout(t,100);try{a.postMessage(r+JSON.stringify({type:"url",url:s.location.href,notesUrl:n.jmpress("settings").presentationMode.notesUrl}),"*")}catch(e){}}var a,n=e(this);n.jmpress("settings").presentationMode.use&&(a=s.open(e(this).jmpress("settings").presentationMode.url),n.jmpress("current").presentationPopupTimeout=setTimeout(t,100))})}(jQuery,document,window); \ No newline at end of file
diff --git a/plugins/jetpack/modules/shortcodes/js/main.js b/plugins/jetpack/modules/shortcodes/js/main.js
new file mode 100644
index 00000000..a6913f78
--- /dev/null
+++ b/plugins/jetpack/modules/shortcodes/js/main.js
@@ -0,0 +1,255 @@
+(function($){
+ var jmpressOpts = {
+ fullscreen : false,
+ hash : { use : false },
+ mouse : { clickSelects : false },
+ keyboard : { use : true },
+ animation : { transitionDuration : '1s' },
+ presentationMode : false,
+ stepSelector : '.step',
+ duration : {
+ defaultValue: 0
+ }
+ };
+
+ /**
+ * Presentation constructor
+ */
+ function Presentation (wrapper) {
+ var _self, size, duration, new_css, ie_regex, matches;
+
+ _self = this;
+
+ _self.wrapper = $(wrapper); // The wrapper for toggling fullscreen
+ _self.slideshow = $('.presentation', wrapper); // Holds the slides for jmpress
+ _self.navLeft = $('.nav-arrow-left', wrapper);
+ _self.navRight = $('.nav-arrow-right', wrapper);
+ _self.expandButton = $('.nav-fullscreen-button', wrapper);
+ _self.overlay = $('.autoplay-overlay', wrapper);
+ _self.fullscreen = false;
+ _self.autoPlaying = false;
+ _self.autoplayTime = parseFloat(_self.slideshow.attr('data-autoplay'), 10) || 0;
+
+ // The wrapper is scaled to the contents' size so that its border wraps tightly
+ _self.wrapper.css({
+ width: _self.slideshow.width(),
+ height: _self.slideshow.height()
+ });
+
+ duration = _self.slideshow.attr('duration') || '1s';
+ jmpressOpts.animation.transitionDuration = duration;
+
+ // Compensate for transition times
+ if( _self.autoplayTime ) {
+ _self.autoplayTime += parseFloat(duration, 10) * 1000;
+ }
+
+ // Set the opacity transition duration
+ // as it is delegated by css and not jmpress
+ duration = 'opacity ' + duration;
+ new_css = {
+ 'width' : _self.slideshow.width(),
+ 'height' : _self.slideshow.height(),
+ '-webkit-transition': duration,
+ '-moz-transition' : duration,
+ '-ms-transition' : duration,
+ '-o-transition' : duration,
+ 'transition' : duration
+ };
+
+ $('.step', _self.slideshow).each(function(i, step) {
+ $(step).css(new_css);
+ });
+
+ // Apply attribute to allow fading individual bullets here,
+ // otherwise wp_kses will strip the attribute out
+ $('.step.fadebullets li', _self.slideshow).each(function(i, step) {
+ $(step).attr('data-jmpress', 'fade');
+ });
+
+ // Register resizing to window when fullscreen
+ $(window).resize(function() {
+ if ( _self.fullscreen )
+ _self.resizePresentation();
+ });
+
+ // Register the nav bars to move the slides
+ _self.navLeft.on('click', function(){
+ _self.slideshow.jmpress('prev');
+ _self.overlay.css('opacity', 0);
+ return false;
+ });
+
+ _self.navRight.on('click', function(){
+ _self.slideshow.jmpress('next');
+ _self.overlay.css('opacity', 0);
+ return false;
+ });
+
+ _self.slideshow.on('click', function() {
+ _self.setAutoplay(true);
+ return false;
+ });
+
+ _self.slideshow.on('focusout', function() {
+ _self.setAutoplay(false);
+ });
+
+ // Register toggling fullscreen except for IE 9 or lower
+ ie_regex = /MSIE\s(\d+)\.\d+/;
+ matches = ie_regex.exec(navigator.userAgent);
+
+ if ( matches && parseInt(matches[1], 10) < 10 ) {
+ _self.expandButton.remove();
+ _self.expandButton = null;
+ } else {
+ _self.expandButton.on('click', function() {
+ _self.setFullscreen( !_self.fullscreen );
+ return false;
+ });
+ }
+
+ // Register ESC key to exit fullscreen
+ $(window).on('keydown', function( event ) {
+ if ( event.which == 27 )
+ _self.setFullscreen( false );
+ });
+
+ // Start the presentation
+ _self.slideshow.jmpress(jmpressOpts);
+
+ // Make content visible and remove error message on jmpress success
+ if ( _self.slideshow.jmpress('initialized') ) {
+ _self.slideshow.css('display', '');
+ _self.overlay.css('display', '');
+ $('.not-supported-msg', _self.wrapper).remove();
+ }
+
+ // A bug in Firefox causes issues with the nav arrows appearing
+ // on hover in presentation mode. Explicitly disabling fullscreen
+ // on init seems to fix the issue
+ _self.setFullscreen( false );
+ }
+
+ $.extend( Presentation.prototype, {
+ resizePresentation: function () {
+ var scale, duration, settings, new_css, widthScale, heightScale;
+
+ // Set the animation duration to 0 during resizing
+ // so that there isn't an animation delay when scaling
+ // up the slide contents
+ settings = this.slideshow.jmpress('settings');
+ duration = settings.animation.transitionDuration;
+
+ settings.animation.transitionDuration = '0s';
+ this.slideshow.jmpress('reselect');
+
+ scale = 1;
+ new_css = {
+ top : 0,
+ left : 0,
+ zoom : 1
+ };
+
+ // Expand the presentation to fill the lesser of the max width or height
+ // This avoids content moving past the window for certain window sizes
+ if ( this.fullscreen ) {
+ widthScale = $(window).width() / this.slideshow.width();
+ heightScale = $(window).height() / this.slideshow.height();
+
+ scale = Math.min(widthScale, heightScale);
+
+ new_css.top = ( $(window).height() - (scale * this.slideshow.height()) ) / 2;
+ new_css.left = ( $(window).width() - (scale * this.slideshow.width() ) ) / 2;
+ }
+
+ // Firefox does not support the zoom property; IE does, but it does not work
+ // well like in webkit, so we manually transform and position the slideshow
+ if ( this.slideshow.css('-moz-transform') || this.slideshow.css('-ms-transform') ) {
+ // Firefox keeps the center of the element in place and expands outward
+ // so we must shift everything to compensate
+ new_css.top += (scale - 1) * this.slideshow.height() / 2;
+ new_css.left += (scale - 1) * this.slideshow.width() / 2;
+
+ scale = 'scale(' + scale + ')';
+
+ $.extend(new_css, {
+ '-moz-transform' : scale,
+ '-ms-transform' : scale,
+ 'transform' : scale,
+ });
+ } else {
+ // webkit scales everything with zoom so we need to offset the right amount
+ // so that the content is vertically centered after scaling effects
+ new_css.top /= scale;
+ new_css.left /= scale;
+ new_css.zoom = scale;
+ }
+
+ this.slideshow.css(new_css);
+
+ settings.animation.transitionDuration = duration;
+ this.slideshow.jmpress('reselect');
+ },
+
+ setFullscreen: function ( on ) {
+ this.fullscreen = on;
+ this.setAutoplay(false);
+
+ // Save the scroll positions before going into fullscreen mode
+ if ( on ) {
+ this.scrollVert = $(window).scrollTop();
+ this.scrollHoriz = $(window).scrollLeft();
+
+ // Chrome Bug: Force scroll to be at top
+ // otherwise the presentation can end up offscreen
+ $(window).scrollTop(0);
+ $(window).scrollLeft(0);
+ }
+
+ $('html').toggleClass('presentation-global-fullscreen', on);
+ $('body').toggleClass('presentation-global-fullscreen', on);
+
+ this.wrapper.toggleClass('presentation-wrapper-fullscreen', on);
+
+ this.wrapper.parents().each(function(i, e){
+ $(e).toggleClass('presentation-wrapper-fullscreen-parent', on);
+ });
+
+ this.resizePresentation();
+
+ // Reset the scroll positions after exiting fullscreen mode
+ if ( !on ) {
+ $(window).scrollTop(this.scrollVert);
+ $(window).scrollLeft(this.scrollHoriz);
+ }
+ },
+
+ setAutoplay: function ( on ) {
+ var _self = this, newAutoplayTime;
+
+ if ( _self.autoPlaying == on )
+ return;
+
+ newAutoplayTime = (on && _self.autoplayTime > 0) ? _self.autoplayTime : 0;
+ _self.slideshow.jmpress('settings').duration.defaultValue = newAutoplayTime;
+
+ // Move to the next slide when activating autoplay
+ if( newAutoplayTime ) {
+ _self.slideshow.jmpress('next');
+ _self.overlay.css('opacity', 0);
+ } else {
+ _self.slideshow.jmpress('reselect');
+ }
+
+ _self.autoPlaying = on;
+ }
+ });
+
+ $( document ).ready( function(){
+ $('.presentation-wrapper').map(function() {
+ new Presentation(this);
+ });
+ });
+
+})(jQuery);
diff --git a/plugins/jetpack/modules/shortcodes/presentations.php b/plugins/jetpack/modules/shortcodes/presentations.php
new file mode 100644
index 00000000..6fb2b674
--- /dev/null
+++ b/plugins/jetpack/modules/shortcodes/presentations.php
@@ -0,0 +1,431 @@
+<?php
+/*
+Plugin Name: Presentations
+Plugin URI: http://automattic.com/wordpress-plugins/
+Description: Presentations plugin based on the work done by <a href="http://darylkoop.com/">Daryl Koopersmith</a>. Powered by jmpress.js
+Version: 0.2
+Author: Automattic
+Author URI: http://automattic.com/wordpress-plugins/
+
+ 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 St, Fifth Floor, Boston, MA 02110-1301 USA
+*/
+
+
+/**
+ * Known issues:
+ *
+ * - IE 7/8 are not supported by jmpress and presentations will not work
+ * - IE 9 will not animate transitions at all, though it's possible to at least
+ * switch between slides.
+ * - Infinite Scroll themes will not load presentations properly unless the post
+ * happens to be on the first loaded page. The permalink page will function
+ * properly, however.
+ * - Exiting fullscreen mode will not properly reset the scroll locations in Safari
+ */
+
+
+/*
+HOW TO: How the plugin settings are organized and which features are supported.
+
+The entire presentation should be wrapped with a [presentation] shortcode, and every
+individual slide should be wrapped with a [slide] shortcode. Any settings supported
+by [slide] can be set into [presentation], which will apply that setting for the entire
+presentation unless overridden by individual slides.
+
+- [presentation] only settings:
+ - duration: transition durations, default is one second.
+ - height: content height, default is 400px
+ - width: content width, default is 550px
+ - autoplay: delay between transitions in seconds, default 3s
+ when set the presentation will automatically transition between slides
+ as long as the presentation remains in focus
+
+- [slide] settings:
+ - transition: specifies where the next slide will be placed relative
+ to the last one before it. Supported values are "up", "down"
+ "left", "right", or "none". Default value is "down".
+
+ - scale: scales the content relative to other slides, default value is one
+
+ - rotate: rotates the content by the specified degrees, default is zero
+
+ - fade: slides will fade in and out during transition. Values of "on" or
+ "true" will enable fading, while values of "no" or "false" will
+ disable it. Default value is "on"
+
+ - bgcolor: specifies a background color for the slides. Any CSS valid value
+ is permitted. Default color is transparent.
+
+ - bgimg: specifies an image url which will fill the background. Image is
+ set to fill the background 100% width and height
+
+ - fadebullets: any html <li> tags will start out with an opacity of 0 and any
+ subsequent slide transitions will show the bullets one by one
+*/
+
+if ( ! class_exists( 'Presentations' ) ) :
+
+class Presentations {
+
+ private $presentation_settings;
+ private $presentation_initialized;
+ private $scripts_and_style_included;
+
+ /**
+ * Constructor
+ */
+ function __construct() {
+ // Bail without 3.0.
+ if ( ! function_exists( '__return_false' ) )
+ return;
+
+ $this->presentation_initialized = false;
+ $this->scripts_and_style_included = false;
+
+ // Registers shortcodes
+ add_action( 'wp_head', array( &$this, 'add_scripts' ), 1 );
+
+ add_shortcode( 'presentation', array( &$this, 'presentation_shortcode' ) );
+ add_shortcode( 'slide', array( &$this, 'slide_shortcode' ) );
+ }
+
+ function add_scripts() {
+ $this->scripts_and_style_included = false;
+
+ if ( empty( $GLOBALS['posts'] ) || !is_array( $GLOBALS['posts'] ) ) {
+ return;
+ }
+
+ foreach ( $GLOBALS['posts'] as $p ) {
+ if ( false !== strpos( $p->post_content, '[presentation' ) ) {
+ $this->scripts_and_style_included = true;
+ break;
+ }
+ }
+
+ if ( ! $this->scripts_and_style_included )
+ return;
+
+ $plugin = plugin_dir_url( __FILE__ );
+ // Add CSS
+ wp_enqueue_style('presentations', $plugin . 'css/style.css');
+ // Add JavaScript
+ wp_enqueue_script('jquery');
+ wp_enqueue_script('jmpress',
+ $plugin . 'js/jmpress.min.js',
+ array('jquery'),
+ '0.4.5',
+ true);
+ wp_enqueue_script('presentations',
+ $plugin . 'js/main.js',
+ array('jquery', 'jmpress'),
+ false,
+ true);
+ }
+
+ function presentation_shortcode( $atts, $content='' ) {
+ // Mark that we've found a valid [presentation] shortcode
+ $this->presentation_initialized = true;
+
+ $atts = shortcode_atts( array(
+ 'duration' => '',
+ 'height' => '',
+ 'width' => '',
+ 'bgcolor' => '',
+ 'bgimg' => '',
+ 'autoplay' => '',
+
+ // Settings
+ 'transition' => '',
+ 'scale' => '',
+ 'rotate' => '',
+ 'fade' => '',
+ 'fadebullets'=> '',
+ ), $atts );
+
+ $this->presentation_settings = array(
+ 'transition' => 'down',
+ 'scale' => 1,
+ 'rotate' => 0,
+ 'fade' => 'on',
+ 'last' => array(
+ 'x' => 0,
+ 'y' => 0,
+ 'scale' => 1,
+ 'rotate' => 0,
+ ),
+ );
+
+ // Set the presentation-wide settings
+ if ( '' != trim( $atts['transition'] ) )
+ $this->presentation_settings['transition'] = $atts['transition'];
+
+ if ( '' != trim( $atts['scale'] ) )
+ $this->presentation_settings['scale'] = floatval( $atts['scale'] );
+
+ if ( '' != trim( $atts['rotate'] ) )
+ $this->presentation_settings['rotate'] = floatval( $atts['rotate'] );
+
+ if ( '' != trim( $atts['fade'] ) )
+ $this->presentation_settings['fade'] = $atts['fade'];
+
+ if ( '' != trim( $atts['fadebullets'] ) )
+ $this->presentation_settings['fadebullets'] = $atts['fadebullets'];
+
+ // Set any settings the slides don't care about
+ if ( '' != trim( $atts['duration'] ) )
+ $duration = floatval( $atts['duration'] ) . 's';
+ else
+ $duration = '1s';
+
+ // Autoplay durations are set in milliseconds
+ if ( '' != trim( $atts['autoplay'] ) )
+ $autoplay = floatval( $atts['autoplay'] ) * 1000;
+ else
+ $autoplay = 0; // No autoplay
+
+ // Set the presentation size as specified or with some nicely sized dimensions
+ if ( '' != trim( $atts['width'] ) )
+ $this->presentation_settings['width'] = intval( $atts['width'] );
+ else
+ $this->presentation_settings['width'] = 480;
+
+ if ( '' != trim( $atts['height'] ) )
+ $this->presentation_settings['height'] = intval( $atts['height'] );
+ else
+ $this->presentation_settings['height'] = 370;
+
+ // Hide the content by default in case the scripts fail
+ $style = 'display: none; width: ' . $this->presentation_settings['width'] . 'px; height: ' . $this->presentation_settings['height'] . 'px;';
+
+ // Check for background color XOR background image
+ // Use a white background if nothing specified
+ if ( preg_match( '/https?\:\/\/[^\'"\s]*/', $atts['bgimg'], $matches ) ) {
+ $style .= ' background-image: url("' . esc_url( $matches[0] ) . '");';
+ } else if ( '' != trim( $atts['bgcolor'] ) ) {
+ $style .= ' background-color: ' . esc_attr( $atts['bgcolor'] ) . ';';
+ } else {
+ $style .= ' background-color: #fff;';
+ }
+
+ // Not supported message style is inlined incase the style sheet doesn't get included
+ $out = "<section class='presentation-wrapper'>";
+ $out.= "<p class='not-supported-msg' style='display: inherit; padding: 25%; text-align: center;'>";
+ $out.= __( 'This slideshow could not be started. Try refreshing the page or viewing it in another browser.' , 'jetpack' ) . '</p>';
+
+ // Bail out unless the scripts were added
+ if ( $this->scripts_and_style_included ) {
+ $out.= sprintf(
+ '<div class="presentation" duration="%s" data-autoplay="%s" style="%s">',
+ esc_attr( $duration ),
+ esc_attr( $autoplay ),
+ esc_attr( $style )
+ );
+ $out.= "<div class='nav-arrow-left'></div>";
+ $out.= "<div class='nav-arrow-right'></div>";
+ $out.= "<div class='nav-fullscreen-button'></div>";
+
+ if ( $autoplay ) {
+ $out.= "<div class='autoplay-overlay' style='display: none'><p class='overlay-msg'>";
+ $out.= __( 'Click to autoplay the presentation!' , 'jetpack' );
+ $out.= "</p></div>";
+ }
+
+ $out.= do_shortcode( $content );
+ $out.= "</section>";
+ }
+
+ $out.= "</section>";
+
+ $this->presentation_initialized = false;
+ return $out;
+ }
+
+ function slide_shortcode( $atts, $content = '' ) {
+ // Bail out unless wrapped by a [presentation] shortcode
+ if ( ! $this->presentation_initialized )
+ return $content;
+
+ $atts = shortcode_atts( array(
+ 'transition' => '',
+ 'scale' => '',
+ 'rotate' => '',
+ 'fade' => '',
+ 'fadebullets'=> '',
+ 'bgcolor' => '',
+ 'bgimg' => '',
+ ), $atts );
+
+ // Determine positioning based on transition
+ if ( '' == trim( $atts['transition'] ) )
+ $atts['transition'] = $this->presentation_settings['transition'];
+
+ // Setting the content scale
+ if ( '' == trim( $atts['scale'] ) )
+ $atts['scale'] = $this->presentation_settings['scale'];
+
+ if( '' == trim( $atts['scale'] ) )
+ $scale = 1;
+ else
+ $scale = floatval( $atts['scale'] );
+
+ if ( $scale < 0 )
+ $scale *= -1;
+
+ // Setting the content rotation
+ if ( '' == trim( $atts['rotate'] ) )
+ $atts['rotate'] = $this->presentation_settings['rotate'];
+
+ if( '' == trim( $atts['rotate'] ) )
+ $rotate = 0;
+ else
+ $rotate = floatval( $atts['rotate'] );
+
+ // Setting if the content should fade
+ if ( '' == trim( $atts['fade'] ) )
+ $atts['fade'] = $this->presentation_settings['fade'];
+
+ if ( 'on' == $atts['fade'] || 'true' == $atts['fade'] )
+ $fade = 'fade';
+ else
+ $fade = '';
+
+ // Setting if bullets should fade on step changes
+ if ( '' == trim( $atts['fadebullets'] ) )
+ $atts['fadebullets'] = $this->presentation_settings['fadebullets'];
+
+ if ( 'on' == $atts['fadebullets'] || 'true' == $atts['fadebullets'] )
+ $fadebullets = 'fadebullets';
+ else
+ $fadebullets = '';
+
+ $coords = $this->get_coords( array(
+ 'transition' => $atts['transition'],
+ 'scale' => $scale,
+ 'rotate' => $rotate,
+ ));
+
+ $x = $coords['x'];
+ $y = $coords['y'];
+
+ // Check for background color XOR background image
+ // Use a white background if nothing specified
+ if ( preg_match( '/https?\:\/\/[^\'"\s]*/', $atts['bgimg'], $matches ) ) {
+ $style = 'background-image: url("' . esc_url( $matches[0] ) . '");';
+ } else if ( '' != trim( $atts['bgcolor'] ) ) {
+ $style = 'background-color: ' . esc_attr( $atts['bgcolor'] ) . ';';
+ } else {
+ $style = '';
+ }
+
+ // Put everything together and let jmpress do the magic!
+ $out = sprintf(
+ '<div class="step %s %s" data-x="%s" data-y="%s" data-scale="%s" data-rotate="%s" style="%s">',
+ esc_attr( $fade ),
+ esc_attr( $fadebullets ),
+ esc_attr( $x ),
+ esc_attr( $y ),
+ esc_attr( $scale ),
+ esc_attr( $rotate ),
+ esc_attr( $style )
+ );
+
+ $out.= "<div class='slide-content'>";
+ $out.= do_shortcode( $content );
+ $out.= "</div></div>";
+ return $out;
+ }
+
+ /**
+ * Determines the position of the next slide based on the position and scaling of the previous slide.
+ *
+ * @param array $args: an array with the following key-value pairs
+ * string $transition: the transition name, "up", "down", "left", or "right"
+ * float $scale: the scale of the next slide (used to determine the position of the slide after that)
+ *
+ * @return array with the 'x' and 'y' coordinates of the slide
+ */
+ function get_coords( $args ) {
+ if ( 0 == $args['scale'] )
+ $args['scale'] = 1;
+
+ $width = $this->presentation_settings['width'];
+ $height = $this->presentation_settings['height'];
+ $last = $this->presentation_settings['last'];
+ $scale = $last['scale'];
+
+ $next = array(
+ 'x' => $last['x'],
+ 'y' => $last['y'],
+ 'scale' => $args['scale'],
+ 'rotate' => $args['rotate'],
+ );
+
+ // All angles are measured from the vertical axis, so everything is backwards!
+ $diagAngle = atan2( $width, $height );
+ $diagonal = sqrt( pow( $width, 2 ) + pow( $height, 2 ) );
+
+ // We offset the angles by the angle formed by the diagonal so that
+ // we can multiply the sines directly against the diagonal length
+ $theta = deg2rad( $last['rotate'] ) - $diagAngle;
+ $phi = deg2rad( $next['rotate'] ) - $diagAngle;
+
+ // We start by displacing by the slide dimensions
+ $totalHorizDisp = $width * $scale;
+ $totalVertDisp = $height * $scale;
+
+ // If the previous slide was rotated, we add the incremental offset from the rotation
+ // Namely the difference between the regular dimension (no rotation) and the component
+ // of the diagonal for that angle
+ $totalHorizDisp += ( ( ( abs( sin( $theta ) ) * $diagonal) - $width ) / 2) * $scale;
+ $totalVertDisp += ( ( ( abs( cos( $theta ) ) * $diagonal) - $height) / 2) * $scale;
+
+ // Similarly, we check if the current slide has been rotated and add whatever additional
+ // offset has been added. This is so that two rotated corners don't clash with each other.
+ // Note: we are checking the raw angle relative to the vertical axis, NOT the diagonal angle.
+ if ( $next['rotate'] % 180 != 0 ){
+ $totalHorizDisp += ( abs( ( sin( $phi ) * $diagonal ) - $width ) / 2) * $next['scale'];
+ $totalVertDisp += ( abs( ( cos( $phi ) * $diagonal ) - $height ) / 2) * $next['scale'];
+ }
+
+ switch ( trim( $args['transition'] ) ) {
+ case 'none':
+ break;
+
+ case 'left':
+ $next['x'] -= $totalHorizDisp;
+ break;
+
+ case 'right':
+ $next['x'] += $totalHorizDisp;
+ break;
+
+ case 'up':
+ $next['y'] -= $totalVertDisp;
+ break;
+
+ case 'down':
+ default:
+ $next['y'] += $totalVertDisp;
+ break;
+ }
+
+ $this->presentation_settings['last'] = $next;
+ return $next;
+ }
+}
+
+$GLOBALS['presentations'] = new Presentations();
+endif;
diff --git a/plugins/jetpack/modules/shortcodes/twitter-timeline.php b/plugins/jetpack/modules/shortcodes/twitter-timeline.php
new file mode 100644
index 00000000..8931592b
--- /dev/null
+++ b/plugins/jetpack/modules/shortcodes/twitter-timeline.php
@@ -0,0 +1,31 @@
+<?php
+add_shortcode( 'twitter-timeline', 'twitter_timeline_shortcode' );
+
+function twitter_timeline_shortcode( $attr ) {
+
+ $default_atts = array(
+ 'username' => '',
+ 'id' => '',
+ 'height' => 282,
+ 'width' => 450,
+
+ );
+
+ $attr = shortcode_atts( $default_atts, $attr );
+
+ if ( $attr['username'] != preg_replace( '/[^A-Za-z0-9_]+/', '', $attr['username'] ) )
+ return '<!--' . __( 'Invalid username', 'jetpack' ) . '-->';
+
+ if ( ! is_numeric( $attr['id'] ) )
+ return '<!--' . __( 'Invalid id', 'jetpack' ) . '-->';
+
+ $tweets_by = sprintf( __( 'Tweets by @%s', 'jetpack' ), $attr['username'] );
+ $output = '<a class="twitter-timeline" width="' . (int)$attr['width'] . '" height="' . (int)$attr['width'] . '" href="' . esc_url( 'https://twitter.com/'. $attr['username'] ) . '" data-widget-id="' . (int)$attr['id'] . '">' . esc_html( $tweets_by ) . '</a>';
+ add_action( 'wp_footer', 'twitter_timeline_js' );
+
+ return $output;
+}
+
+function twitter_timeline_js() {
+ echo '<script>!function(d,s,id){var js,fjs=d.getElementsByTagName(s)[0];if(!d.getElementById(id)){js=d.createElement(s);js.id=id;js.src="//platform.twitter.com/widgets.js";fjs.parentNode.insertBefore(js,fjs);}}(document,"script","twitter-wjs");</script>';
+}
diff --git a/plugins/jetpack/modules/shortcodes/videopress.php b/plugins/jetpack/modules/shortcodes/videopress.php
index 6a3a75b0..08f33c6f 100644
--- a/plugins/jetpack/modules/shortcodes/videopress.php
+++ b/plugins/jetpack/modules/shortcodes/videopress.php
@@ -1,1333 +1,3 @@
<?php
-/**
- * @package video
- * @category video
- * @author Automattic Inc
- * @link http://automattic.com/wordpress-plugins/#videopress VideoPress
- * @version 1.5.4
- * @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.4
-Stable tag: 1.5.4
-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.4';
-
- /**
- * 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.09' );
-
- $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,
- 'hd' => false
- ), $attr ) );
-
- $freedom = (bool) $freedom;
- /**
- * Test if embedded blog prefers videos only displayed in Freedom-loving formats
- */
- if ( $freedom === false && (bool) get_option( 'video_player_freedom', false ) )
- $freedom = true;
-
- $forcestatic = get_option( 'video_player_static', false );
-
- /**
- * Set the video to HD if the blog option has it enabled
- */
- if ( (bool) get_option( 'video_player_high_quality', false ) )
- $hd = true;
-
- $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,
- 'forcestatic' => $forcestatic,
- 'hd' => (bool) $hd
- );
- 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 {
- return parse_url( $url, PHP_URL_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(
- '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->options['force_flash'] ) && $this->options['force_flash'] === true ) {
- $content = $this->flash_object();
- } elseif ( isset( $this->video->restricted_embed ) && $this->video->restricted_embed === true ) {
- if( $this->options['forcestatic'] )
- $content = $this->flash_object();
- else
- $content = $this->html5_dynamic();
- } elseif ( isset( $this->options['freedom'] ) && $this->options['freedom'] === true ) {
- $content = $this->html5_static();
- } 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' ) ),
- 'hd' => (bool) $this->options['hd']
- );
- 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;
- $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
- $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;
-?>
+// Boom!
+require_once( JETPACK__PLUGIN_DIR . 'modules/videopress/shortcode.php' ); \ No newline at end of file
diff --git a/plugins/jetpack/modules/shortcodes/vine.php b/plugins/jetpack/modules/shortcodes/vine.php
new file mode 100644
index 00000000..184d005e
--- /dev/null
+++ b/plugins/jetpack/modules/shortcodes/vine.php
@@ -0,0 +1,65 @@
+<?php
+/**
+ * Vine shortcode
+ */
+
+/**
+ * Vine embed code:
+ * <iframe class="vine-embed" src="https://vine.co/v/bjHh0zHdgZT" width="600" height="600" frameborder="0"></iframe>
+ * <script async src="//platform.vine.co/static/scripts/embed.js" charset="utf-8"></script>
+ *
+ * URL example:
+ * https://vine.co/v/bjHh0zHdgZT/
+ *
+ * Embed shortcode examples:
+ * [embed]https://vine.co/v/bjHh0zHdgZT[/embed]
+ * [embed width="300"]https://vine.co/v/bjHh0zHdgZT[/embed]
+ * [embed type="postcard" width="300"]https://vine.co/v/bjHh0zHdgZT[/embed]
+ **/
+
+function vine_embed_video( $matches, $attr, $url, $rawattr ) {
+ static $vine_flag_embedded_script;
+
+ $max_height = 300;
+ $type = 'simple';
+
+ // Only allow 'postcard' or 'simple' types
+ if ( isset( $rawattr['type'] ) && $rawattr['type'] === 'postcard' )
+ $type = 'postcard';
+
+ $vine_size = Jetpack::get_content_width();
+
+ // If the user enters a value for width or height, we ignore the Jetpack::get_content_width()
+ if ( isset( $rawattr['width'] ) || isset( $rawattr['height'] ) ) {
+ // 300 is the minimum size that Vine provides for embeds. Lower than that, the postcard embeds looks weird.
+ $vine_size = max( $max_height, min( $attr['width'], $attr['height'] ) );
+ }
+
+ if ( empty( $vine_size ) ) {
+ $vine_size = $max_height;
+ }
+
+ $url = 'https://vine.co/v/' . $matches[1] . '/embed/' . $type;
+ $vine_html = sprintf( '<iframe class="vine-embed" src="%s" width="%s" height="%s" frameborder="0"></iframe>', esc_url( $url ), (int) $vine_size, (int) $vine_size );
+
+ if ( $vine_flag_embedded_script !== true ) {
+ $vine_html .= '<script async src="//platform.vine.co/static/scripts/embed.js" charset="utf-8"></script>';
+ $vine_flag_embedded_script = true;
+ }
+
+ return $vine_html;
+}
+wp_embed_register_handler( 'jetpack_vine', '#https?://vine.co/v/([a-z0-9]+).*#i', 'vine_embed_video' );
+
+function vine_shortcode( $atts ) {
+ global $wp_embed;
+
+ if ( empty( $atts['url'] ) )
+ return '';
+
+ if ( ! preg_match( '#https?://vine.co/v/([a-z0-9]+).*#i', $atts['url'] ) )
+ return '';
+
+ return $wp_embed->shortcode( $atts, $atts['url'] );
+}
+add_shortcode( 'vine', 'vine_shortcode' );
diff --git a/plugins/jetpack/modules/shortlinks.php b/plugins/jetpack/modules/shortlinks.php
index 731a5c3a..e530941f 100644
--- a/plugins/jetpack/modules/shortlinks.php
+++ b/plugins/jetpack/modules/shortlinks.php
@@ -5,6 +5,7 @@
* Sort Order: 10
* First Introduced: 1.1
* Requires Connection: Yes
+ * Auto Activate: Yes
*/
add_filter( 'get_shortlink', 'wpme_get_shortlink_handler', 1, 4 );
@@ -32,7 +33,7 @@ if ( !function_exists( 'wpme_dec2sixtwo' ) ) {
function wpme_get_shortlink( $id = 0, $context = 'post', $allow_slugs = true ) {
global $wp_query;
- $blog_id = Jetpack::get_option( 'id' );
+ $blog_id = Jetpack_Options::get_option( 'id' );
if ( 'query' == $context ) {
if ( is_singular() ) {
diff --git a/plugins/jetpack/modules/stats.php b/plugins/jetpack/modules/stats.php
index 3ca38ddb..90fd34ff 100644
--- a/plugins/jetpack/modules/stats.php
+++ b/plugins/jetpack/modules/stats.php
@@ -5,6 +5,7 @@
* Sort Order: 1
* First Introduced: 1.1
* Requires Connection: Yes
+ * Auto Activate: Yes
*/
if ( defined( 'STATS_VERSION' ) ) {
@@ -121,7 +122,7 @@ function stats_template_redirect() {
add_action( 'wp_footer', 'stats_footer', 101 );
add_action( 'wp_head', 'stats_add_shutdown_action' );
- $blog = Jetpack::get_option( 'id' );
+ $blog = Jetpack_Options::get_option( 'id' );
$tz = get_option( 'gmt_offset' );
$v = 'ext';
$j = sprintf( '%s:%s', JETPACK__API_VERSION, JETPACK__VERSION );
@@ -184,7 +185,7 @@ function stats_get_option( $option ) {
$options = stats_get_options();
if ( $option == 'blog_id' )
- return Jetpack::get_option( 'id' );
+ return Jetpack_Options::get_option( 'id' );
if ( isset( $options[$option] ) )
return $options[$option];
@@ -209,7 +210,7 @@ function stats_upgrade_options( $options ) {
'admin_bar' => true,
'roles' => array( 'administrator' ),
'count_roles' => array(),
- 'blog_id' => Jetpack::get_option( 'id' ),
+ 'blog_id' => Jetpack_Options::get_option( 'id' ),
'do_not_track' => true, // @todo
'hide_smile' => false,
);
@@ -880,7 +881,7 @@ function stats_dashboard_widget_content() {
$_height = $height - ( $GLOBALS['is_winIE'] ? 16 : 5 ); // hack!
$options = stats_dashboard_widget_options();
- $blog_id = Jetpack::get_option( 'id' );
+ $blog_id = Jetpack_Options::get_option( 'id' );
$q = array(
'noheader' => 'true',
@@ -1036,7 +1037,7 @@ function stats_get_csv( $table, $args = null ) {
$args = wp_parse_args( $args, $defaults );
$args['table'] = $table;
- $args['blog_id'] = Jetpack::get_option( 'id' );
+ $args['blog_id'] = Jetpack_Options::get_option( 'id' );
$stats_csv_url = add_query_arg( $args, 'http://stats.wordpress.com/csv.php' );
diff --git a/plugins/jetpack/modules/subscriptions.php b/plugins/jetpack/modules/subscriptions.php
index 22366d07..1becb481 100644
--- a/plugins/jetpack/modules/subscriptions.php
+++ b/plugins/jetpack/modules/subscriptions.php
@@ -5,11 +5,13 @@
* Sort Order: 3
* First Introduced: 1.2
* Requires Connection: Yes
+ * Auto Activate: Yes
*/
add_action( 'jetpack_modules_loaded', 'jetpack_subscriptions_load' );
-Jetpack_Sync::sync_options( __FILE__,
+Jetpack_Sync::sync_options(
+ __FILE__,
'home',
'blogname',
'siteurl',
@@ -39,6 +41,8 @@ function jetpack_subscriptions_configuration_load() {
class Jetpack_Subscriptions {
var $jetpack = false;
+ public static $hash;
+
/**
* Singleton
* @static
@@ -56,6 +60,10 @@ class Jetpack_Subscriptions {
function Jetpack_Subscriptions() {
$this->jetpack = Jetpack::init();
+ // Don't use COOKIEHASH as it could be shared across installs && is non-unique in multisite.
+ // @see: https://twitter.com/nacin/status/378246957451333632
+ self::$hash = md5( get_option( 'siteurl' ) );
+
add_filter( 'jetpack_xmlrpc_methods', array( $this, 'xmlrpc_methods' ) );
// @todo remove sync from subscriptions and move elsewhere...
@@ -82,7 +90,9 @@ class Jetpack_Subscriptions {
return false;
}
- return 'publish' === $post->post_status && strlen( (string) $post->post_password ) < 1;
+ if ( 'publish' === $post->post_status && strlen( (string) $post->post_password ) < 1 ) {
+ return apply_filters( 'jetpack_is_post_mailable', true );
+ }
}
/**
@@ -92,9 +102,12 @@ class Jetpack_Subscriptions {
* @param array $methods
*/
function xmlrpc_methods( $methods ) {
- return array_merge( $methods, array(
- 'jetpack.subscriptions.subscribe' => array( $this, 'subscribe' ),
- ) );
+ return array_merge(
+ $methods,
+ array(
+ 'jetpack.subscriptions.subscribe' => array( $this, 'subscribe' ),
+ )
+ );
}
/**
@@ -158,7 +171,7 @@ class Jetpack_Subscriptions {
add_settings_field(
'invitation',
- __( 'Blog follow email text' , 'jetpack' ),
+ __( 'Blog follow email text', 'jetpack' ),
array( $this, 'setting_invitation' ),
'reading',
'email_settings'
@@ -235,25 +248,25 @@ class Jetpack_Subscriptions {
}
public function reading_section() {
- _e( 'These settings change emails sent from your blog to followers.' , 'jetpack');
+ _e( 'These settings change emails sent from your blog to followers.', 'jetpack' );
}
public function setting_invitation() {
$settings = $this->get_settings();
echo '<textarea name="subscription_options[invitation]" class="large-text" cols="50" rows="5">'.$settings['invitation'].'</textarea>';
- echo '<p><span class="description">'.__( 'Introduction text sent when someone follows your blog. (Site and confirmation details will be automatically added for you.)' , 'jetpack').'</span></p>';
+ echo '<p><span class="description">'.__( 'Introduction text sent when someone follows your blog. (Site and confirmation details will be automatically added for you.)', 'jetpack' ).'</span></p>';
}
public function setting_comment_follow() {
$settings = $this->get_settings();
echo '<textarea name="subscription_options[comment_follow]" class="large-text" cols="50" rows="5">'.$settings['comment_follow'].'</textarea>';
- echo '<p><span class="description">'.__( 'Introduction text sent when someone follows a post on your blog. (Site and confirmation details will be automatically added for you.)' , 'jetpack').'</span></p>';
+ echo '<p><span class="description">'.__( 'Introduction text sent when someone follows a post on your blog. (Site and confirmation details will be automatically added for you.)', 'jetpack' ).'</span></p>';
}
function get_default_settings() {
return array(
- 'invitation' => __( "Howdy.\n\nYou recently followed this blog's posts. This means you will receive each new post by email.\n\nTo activate, click confirm below. If you believe this is an error, ignore this message and we'll never bother you again." , 'jetpack'),
- 'comment_follow' => __( "Howdy.\n\nYou recently followed one of my posts. This means you will receive an email when new comments are posted.\n\nTo activate, click confirm below. If you believe this is an error, ignore this message and we'll never bother you again." , 'jetpack')
+ 'invitation' => __( "Howdy.\n\nYou recently followed this blog's posts. This means you will receive each new post by email.\n\nTo activate, click confirm below. If you believe this is an error, ignore this message and we'll never bother you again.", 'jetpack' ),
+ 'comment_follow' => __( "Howdy.\n\nYou recently followed one of my posts. This means you will receive an email when new comments are posted.\n\nTo activate, click confirm below. If you believe this is an error, ignore this message and we'll never bother you again.", 'jetpack' )
);
}
@@ -280,7 +293,7 @@ class Jetpack_Subscriptions {
* 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 ) {
+ function subscribe( $email, $post_ids = 0, $async = true, $extra_data = array() ) {
if ( !is_email( $email ) ) {
return new Jetpack_Error( 'invalid_email' );
}
@@ -290,7 +303,7 @@ class Jetpack_Subscriptions {
$xml = new Jetpack_IXR_ClientMulticall();
}
- foreach( (array) $post_ids as $post_id ) {
+ foreach ( (array) $post_ids as $post_id ) {
$post_id = (int) $post_id;
if ( $post_id < 0 ) {
return new Jetpack_Error( 'invalid_post_id' );
@@ -299,9 +312,9 @@ class Jetpack_Subscriptions {
}
if ( $async ) {
- Jetpack::xmlrpc_async_call( 'jetpack.subscribeToSite', $email, $post_id );
+ Jetpack::xmlrpc_async_call( 'jetpack.subscribeToSite', $email, $post_id, serialize( $extra_data ) );
} else {
- $xml->addCall( 'jetpack.subscribeToSite', $email, $post_id );
+ $xml->addCall( 'jetpack.subscribeToSite', $email, $post_id, serialize( $extra_data ) );
}
}
@@ -319,7 +332,7 @@ class Jetpack_Subscriptions {
$responses = $xml->getResponse();
$r = array();
- foreach( (array) $responses as $response ) {
+ foreach ( (array) $responses as $response ) {
if ( isset( $response['faultCode'] ) || isset( $response['faultString'] ) ) {
$r[] = $xml->get_jetpack_error( $response['faultCode'], $response['faultString'] );
continue;
@@ -383,7 +396,17 @@ class Jetpack_Subscriptions {
$redirect_fragment = 'subscribe-blog';
}
- $subscribe = Jetpack_Subscriptions::subscribe( $_REQUEST['email'], 0, false );
+ $subscribe = Jetpack_Subscriptions::subscribe(
+ $_REQUEST['email'],
+ 0,
+ false,
+ array(
+ 'source' => 'widget',
+ 'widget-in-use' => is_active_widget( false, false, 'blog_subscription', true ) ? 'yes' : 'no',
+ 'comment_status' => '',
+ 'server_data' => $_SERVER,
+ )
+ );
if ( is_wp_error( $subscribe ) ) {
$error = $subscribe->get_error_code();
@@ -398,7 +421,7 @@ class Jetpack_Subscriptions {
}
if ( $error ) {
- switch( $error ) {
+ switch ( $error ) {
case 'invalid_email':
$redirect = add_query_arg( 'subscribe', 'invalid_email' );
break;
@@ -422,18 +445,18 @@ class Jetpack_Subscriptions {
*
* Set up and add the comment subscription checkbox to the comment form.
*/
- function comment_subscribe_init() {
- global $post;
+ function comment_subscribe_init() {
+ global $post;
- $comments_checked = '';
- $blog_checked = '';
+ $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"';
+ // Check for a comment / blog submission and set a cookie to retain the setting and check the boxes.
+ if ( isset( $_COOKIE[ 'jetpack_comments_subscribe_' . self::$hash ] ) && $_COOKIE[ 'jetpack_comments_subscribe_' . self::$hash ] == $post->ID )
+ $comments_checked = ' checked="checked"';
- if ( isset( $_COOKIE[ 'jetpack_blog_subscribe_' . COOKIEHASH ] ) )
- $blog_checked = ' checked="checked"';
+ if ( isset( $_COOKIE[ 'jetpack_blog_subscribe_' . self::$hash ] ) )
+ $blog_checked = ' checked="checked"';
// Some themes call this function, don't show the checkbox again
remove_action( 'comment_form', 'subscription_comment_form' );
@@ -457,14 +480,14 @@ class Jetpack_Subscriptions {
}
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 ) {
+ function comment_subscribe_submit( $comment_id, $approved ) {
if ( 'spam' === $approved ) {
return;
}
@@ -472,10 +495,10 @@ class Jetpack_Subscriptions {
// 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'] ) )
+ if ( !isset( $_REQUEST['subscribe_comments'] ) && !isset( $_REQUEST['subscribe_blog'] ) )
return;
- $comment = get_comment( $comment_id );
+ $comment = get_comment( $comment_id );
$post_ids = array();
if ( isset( $_REQUEST['subscribe_comments'] ) )
@@ -484,29 +507,41 @@ class Jetpack_Subscriptions {
if ( isset( $_REQUEST['subscribe_blog'] ) )
$post_ids[] = 0;
- Jetpack_Subscriptions::subscribe( $comment->comment_author_email, $post_ids );
- }
+ Jetpack_Subscriptions::subscribe(
+ $comment->comment_author_email,
+ $post_ids,
+ true,
+ array(
+ 'source' => 'comment-form',
+ 'widget-in-use' => is_active_widget( false, false, 'blog_subscription', true ) ? 'yes' : 'no',
+ 'comment_status' => $approved,
+ 'server_data' => $_SERVER,
+ )
+ );
+ }
/**
* 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;
+ function set_cookies( $comments = true, $posts = true ) {
+ global $post;
- $cookie_lifetime = apply_filters( 'comment_cookie_lifetime', 30000000 );
+ $cookie_lifetime = apply_filters( 'comment_cookie_lifetime', 30000000 );
+ $cookie_path = apply_filters( 'jetpack_comment_cookie_path', COOKIEPATH );
+ $cookie_domain = apply_filters( 'jetpack_comment_cookie_domain', COOKIE_DOMAIN );
if ( $comments )
- setcookie( 'jetpack_comments_subscribe_' . COOKIEHASH, $post->ID, time() + $cookie_lifetime, COOKIEPATH, COOKIE_DOMAIN );
+ setcookie( 'jetpack_comments_subscribe_' . self::$hash, $post->ID, time() + $cookie_lifetime, $cookie_path, $cookie_domain );
else
- setcookie( 'jetpack_comments_subscribe_' . COOKIEHASH, '', time() - 3600, COOKIEPATH, COOKIE_DOMAIN );
+ setcookie( 'jetpack_comments_subscribe_' . self::$hash, '', time() - 3600, $cookie_path, $cookie_domain );
if ( $posts )
- setcookie( 'jetpack_blog_subscribe_' . COOKIEHASH, 1, time() + $cookie_lifetime, COOKIEPATH, COOKIE_DOMAIN );
+ setcookie( 'jetpack_blog_subscribe_' . self::$hash, 1, time() + $cookie_lifetime, $cookie_path, $cookie_domain );
else
- setcookie( 'jetpack_blog_subscribe_' . COOKIEHASH, '', time() - 3600, COOKIEPATH, COOKIE_DOMAIN );
- }
+ setcookie( 'jetpack_blog_subscribe_' . self::$hash, '', time() - 3600, $cookie_path, $cookie_domain );
+ }
}
Jetpack_Subscriptions::init();
@@ -527,17 +562,13 @@ class Jetpack_Subscriptions_Widget extends WP_Widget {
function widget( $args, $instance ) {
global $current_user;
- $source = 'widget';
-
- extract( $args );
-
+ $source = 'widget';
$instance = wp_parse_args( (array) $instance, $this->defaults() );
- $title = isset( $instance['title'] ) ? stripslashes( $instance['title'] ) : '';
$subscribe_text = isset( $instance['subscribe_text'] ) ? stripslashes( $instance['subscribe_text'] ) : '';
$subscribe_button = isset( $instance['subscribe_button'] ) ? stripslashes( $instance['subscribe_button'] ) : '';
- $subscribe_logged_in = isset( $instance['subscribe_logged_in'] ) ? stripslashes( $instance['subscribe_logged_in'] ) : '';
$show_subscribers_total = (bool) $instance['show_subscribers_total'];
$subscribers_total = $this->fetch_subscriber_count();
+ $widget_id = esc_attr( !empty( $args['widget_id'] ) ? esc_attr( $args['widget_id'] ) : mt_rand( 450, 550 ) );
if ( ! is_array( $subscribers_total ) )
$show_subscribers_total = FALSE;
@@ -575,7 +606,7 @@ class Jetpack_Subscriptions_Widget extends WP_Widget {
endif;
// Display a subscribe form ?>
- <form action="" method="post" accept-charset="utf-8" id="subscribe-blog-<?php echo !empty( $args['widget_id'] ) ? esc_attr( $args['widget_id'] ) : mt_rand( 450, 550 ); ?>">
+ <form action="" method="post" accept-charset="utf-8" id="subscribe-blog-<?php echo $widget_id; ?>">
<?php
if ( ! isset ( $_GET['subscribe'] ) ) {
?><p><?php echo $subscribe_text ?></p><?php
@@ -592,7 +623,7 @@ class Jetpack_Subscriptions_Widget extends WP_Widget {
<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 ); ?>" />
+ <input type="hidden" name="redirect_fragment" value="<?php echo $widget_id; ?>" />
<?php
if ( is_user_logged_in() ) {
wp_nonce_field( 'blogsub_subscribe_'. get_current_blog_id(), '_wpnonce', false );
@@ -621,9 +652,7 @@ class Jetpack_Subscriptions_Widget extends WP_Widget {
if ( FALSE === $subs_count || 'failed' == $subs_count['status'] ) {
Jetpack:: load_xml_rpc_client();
- $xml = new Jetpack_IXR_Client( array(
- 'user_id' => JETPACK_MASTER_USER,
- ) );
+ $xml = new Jetpack_IXR_Client( array( 'user_id' => JETPACK_MASTER_USER, ) );
$xml->query( 'jetpack.fetchSubscriberCount' );
@@ -650,11 +679,11 @@ class Jetpack_Subscriptions_Widget extends WP_Widget {
function update( $new_instance, $old_instance ) {
$instance = $old_instance;
- $instance['title'] = wp_kses( stripslashes( $new_instance['title'] ), array() );
- $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'] = wp_kses( stripslashes( $new_instance['subscribe_button'] ), array() );
- $instance['show_subscribers_total'] = isset( $new_instance['show_subscribers_total'] ) && $new_instance['show_subscribers_total'];
+ $instance['title'] = wp_kses( stripslashes( $new_instance['title'] ), array() );
+ $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'] = wp_kses( stripslashes( $new_instance['subscribe_button'] ), array() );
+ $instance['show_subscribers_total'] = isset( $new_instance['show_subscribers_total'] ) && $new_instance['show_subscribers_total'];
return $instance;
}
@@ -681,7 +710,6 @@ class Jetpack_Subscriptions_Widget extends WP_Widget {
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'] );
diff --git a/plugins/jetpack/modules/theme-tools.php b/plugins/jetpack/modules/theme-tools.php
new file mode 100644
index 00000000..ab746cf9
--- /dev/null
+++ b/plugins/jetpack/modules/theme-tools.php
@@ -0,0 +1,64 @@
+<?php
+/*
+ * Load code specific to themes or theme tools
+ */
+
+/**
+ * INFINITE SCROLL
+ */
+
+/**
+ * Load theme's infinite scroll annotation file, if present in the IS plugin.
+ * The `setup_theme` action is used because the annotation files should be using `after_setup_theme` to register support for IS.
+ *
+ * As released in Jetpack 2.0, a child theme's parent wasn't checked for in the plugin's bundled support, hence the convoluted way the parent is checked for now.
+ *
+ * @uses is_admin, wp_get_theme, get_theme, get_current_theme, apply_filters
+ * @action setup_theme
+ * @return null
+ */
+function jetpack_load_infinite_scroll_annotation() {
+ if ( is_admin() && isset( $_GET['page'] ) && 'jetpack' == $_GET['page'] ) {
+ $theme = function_exists( 'wp_get_theme' ) ? wp_get_theme() : get_theme( get_current_theme() );
+
+ if ( ! is_a( $theme, 'WP_Theme' ) && ! is_array( $theme ) )
+ return;
+
+ $customization_file = apply_filters( 'infinite_scroll_customization_file', dirname( __FILE__ ) . "/infinite-scroll/themes/{$theme['Stylesheet']}.php", $theme['Stylesheet'] );
+
+ if ( is_readable( $customization_file ) ) {
+ require_once( $customization_file );
+ }
+ elseif ( ! empty( $theme['Template'] ) ) {
+ $customization_file = dirname( __FILE__ ) . "/infinite-scroll/themes/{$theme['Template']}.php";
+
+ if ( is_readable( $customization_file ) )
+ require_once( $customization_file );
+ }
+ }
+}
+add_action( 'setup_theme', 'jetpack_load_infinite_scroll_annotation' );
+
+/**
+ * Prevent IS from being activated if theme doesn't support it
+ *
+ * @param bool $can_activate
+ * @filter jetpack_can_activate_infinite-scroll
+ * @return bool
+ */
+function jetpack_can_activate_infinite_scroll( $can_activate ) {
+ return (bool) current_theme_supports( 'infinite-scroll' );
+}
+add_filter( 'jetpack_can_activate_infinite-scroll', 'jetpack_can_activate_infinite_scroll' );
+
+require_once( dirname( __FILE__ ) . '/featured-content/featured-content.php' );
+
+require_once( dirname( __FILE__ ) . '/social-links.php' );
+
+require_once( dirname( __FILE__ ) . '/tonesque.php' );
+
+// Custom Post Types - we don't want a module card for these (yet)
+require_once( dirname( __FILE__ ) . '/custom-post-types/comics.php' );
+require_once( dirname( __FILE__ ) . '/custom-post-types/testimonial.php' );
+
+require_once( dirname( __FILE__ ) . '/random-redirect.php' );
diff --git a/plugins/jetpack/modules/tiled-gallery.php b/plugins/jetpack/modules/tiled-gallery.php
index f88cc680..e0e1e435 100644
--- a/plugins/jetpack/modules/tiled-gallery.php
+++ b/plugins/jetpack/modules/tiled-gallery.php
@@ -5,6 +5,7 @@
* Module Description: Create elegant magazine-style mosaic layouts for your photos without using an external graphic editor.
* First Introduced: 2.1
* Requires Connection: Yes
+ * Auto Activate: No
*/
function jetpack_load_tiled_gallery() {
diff --git a/plugins/jetpack/modules/tiled-gallery/tiled-gallery.php b/plugins/jetpack/modules/tiled-gallery/tiled-gallery.php
index 7855da83..519293a6 100644
--- a/plugins/jetpack/modules/tiled-gallery/tiled-gallery.php
+++ b/plugins/jetpack/modules/tiled-gallery/tiled-gallery.php
@@ -23,7 +23,7 @@ class Jetpack_Tiled_Gallery {
$this->atts = shortcode_atts( array(
'order' => 'ASC',
'orderby' => 'menu_order ID',
- 'id' => $post->ID,
+ 'id' => isset( $post->ID ) ? $post->ID : 0,
'include' => '',
'exclude' => '',
'type' => '',
@@ -59,6 +59,11 @@ class Jetpack_Tiled_Gallery {
foreach ( $_attachments as $key => $val ) {
$attachments[$val->ID] = $_attachments[$key];
}
+ } elseif ( 0 == $id ) {
+ // Should NEVER Happen but infinite_scroll_load_other_plugins_scripts means it does
+ // Querying with post_parent == 0 can generate stupidly memcache sets on sites with 10000's of unattached attachments as get_children puts every post in the cache.
+ // TODO Fix this properly
+ $attachments = array();
} elseif ( !empty( $exclude ) ) {
$exclude = preg_replace( '/[^0-9,]+/', '', $exclude );
$attachments = get_children( array('post_parent' => $id, 'exclude' => $exclude, 'post_status' => 'inherit', 'post_type' => 'attachment', 'post_mime_type' => 'image', 'order' => $order, 'orderby' => $orderby) );
@@ -218,11 +223,10 @@ class Jetpack_Tiled_Gallery {
if ( defined( 'IS_WPCOM' ) && IS_WPCOM ) {
$likes_blog_id = $blog_id;
} else {
- $jetpack = Jetpack::init();
- $likes_blog_id = $jetpack->get_option( 'id' );
+ $likes_blog_id = Jetpack_Options::get_option( 'id' );
}
- $extra_data = array( 'data-carousel-extra' => array( 'blog_id' => $blog_id, 'permalink' => get_permalink( $post->ID ), 'likes_blog_id' => $likes_blog_id ) );
+ $extra_data = array( 'data-carousel-extra' => array( 'blog_id' => $blog_id, 'permalink' => get_permalink( isset( $post->ID ) ? $post->ID : 0 ), 'likes_blog_id' => $likes_blog_id ) );
foreach ( (array) $extra_data as $data_key => $data_values ) {
$html = str_replace( '<div ', '<div ' . esc_attr( $data_key ) . "='" . json_encode( $data_values ) . "' ", $html );
@@ -429,7 +433,7 @@ class Jetpack_Tiled_Gallery_One_Three extends Jetpack_Tiled_Gallery_Shape {
public $shape = array( 1, 3 );
public function is_possible() {
- return $this->is_not_as_previous() && $this->images_left >= 3 &&
+ return $this->is_not_as_previous() && $this->images_left > 3 &&
$this->images[0]->ratio < 0.8 && $this->images[1]->ratio >=0.9 && $this->images[2]->ratio >= 0.9 && $this->images[3]->ratio >= 0.9;
}
}
diff --git a/plugins/jetpack/modules/tiled-gallery/tiled-gallery/rtl/tiled-gallery-rtl.css b/plugins/jetpack/modules/tiled-gallery/tiled-gallery/rtl/tiled-gallery-rtl.css
index a6b7aef0..ef12ca43 100644
--- a/plugins/jetpack/modules/tiled-gallery/tiled-gallery/rtl/tiled-gallery-rtl.css
+++ b/plugins/jetpack/modules/tiled-gallery/tiled-gallery/rtl/tiled-gallery-rtl.css
@@ -1,11 +1,11 @@
-/* This file was automatically generated on Jul 03 2013 18:11:40 */
+/* This file was automatically generated on Jul 31 2013 21:21:12 */
/* =Tiled Gallery Default Styles
-------------------------------------------------------------- */
.tiled-gallery {
clear: both;
- margin: 0;
+ margin: 0 0 20px;
overflow: hidden;
}
.tiled-gallery img {
diff --git a/plugins/jetpack/modules/tiled-gallery/tiled-gallery/tiled-gallery.css b/plugins/jetpack/modules/tiled-gallery/tiled-gallery/tiled-gallery.css
index c80a5b0d..1d96d004 100644
--- a/plugins/jetpack/modules/tiled-gallery/tiled-gallery/tiled-gallery.css
+++ b/plugins/jetpack/modules/tiled-gallery/tiled-gallery/tiled-gallery.css
@@ -3,7 +3,7 @@
.tiled-gallery {
clear: both;
- margin: 0;
+ margin: 0 0 20px;
overflow: hidden;
}
.tiled-gallery img {
diff --git a/plugins/jetpack/modules/tonesque.php b/plugins/jetpack/modules/tonesque.php
new file mode 100644
index 00000000..6d6e6141
--- /dev/null
+++ b/plugins/jetpack/modules/tonesque.php
@@ -0,0 +1,10 @@
+<?php
+/**
+ * Themes must declare that they support this module by adding
+ * add_theme_support( 'tonesque' ); on 'after_setup_theme'.
+ */
+function jetpack_load_tonesque() {
+ if ( current_theme_supports( 'tonesque' ) )
+ jetpack_require_lib( 'tonesque' );
+}
+add_action( 'init', 'jetpack_load_tonesque' );
diff --git a/plugins/jetpack/modules/vaultpress.php b/plugins/jetpack/modules/vaultpress.php
index 7766f539..f4739761 100644
--- a/plugins/jetpack/modules/vaultpress.php
+++ b/plugins/jetpack/modules/vaultpress.php
@@ -7,6 +7,7 @@
* Deactivate: false
* Free: false
* Requires Connection: Yes
+ * Auto Activate: Yes
*/
add_action( 'jetpack_modules_loaded', 'vaultpress_jetpack_stub' );
diff --git a/plugins/jetpack/modules/videopress.php b/plugins/jetpack/modules/videopress.php
new file mode 100644
index 00000000..093dba28
--- /dev/null
+++ b/plugins/jetpack/modules/videopress.php
@@ -0,0 +1,14 @@
+<?php
+/**
+ * Module Name: VideoPress
+ * Module Description: Quite possibly the easiest way to upload beautiful videos to your blog.
+ * First Introduced: 2.5
+ * Free: false
+ * Requires Connection: Yes
+ * Sort Order: 100
+ */
+
+function jetpack_load_videopress() {
+ include dirname( __FILE__ ) . "/videopress/videopress.php";
+}
+jetpack_load_videopress();
diff --git a/plugins/jetpack/modules/videopress/class.videopress-player.php b/plugins/jetpack/modules/videopress/class.videopress-player.php
new file mode 100644
index 00000000..31502d37
--- /dev/null
+++ b/plugins/jetpack/modules/videopress/class.videopress-player.php
@@ -0,0 +1,682 @@
+<?php
+/**
+ * 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;
+
+ /**
+ * Array of video GUIDs shown and their counts,
+ * moved from the old VideoPress class.
+ */
+ public static $shown = array();
+
+ /**
+ * 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() ) {
+ if ( empty( self::$shown[ $guid ] ) )
+ self::$shown[ $guid ] = 0;
+
+ self::$shown[ $guid ]++;
+
+ $this->video_container_id = 'v-' . $guid . '-' . self::$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() )
+ $cache_key_pieces[] = get_current_blog_id();
+
+ $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 );
+ }
+
+ /**
+ * 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->options['force_flash'] ) && $this->options['force_flash'] === true ) {
+ $content = $this->flash_object();
+ } elseif ( isset( $this->video->restricted_embed ) && $this->video->restricted_embed === true ) {
+ if( $this->options['forcestatic'] )
+ $content = $this->flash_object();
+ else
+ $content = $this->html5_dynamic();
+ } elseif ( isset( $this->options['freedom'] ) && $this->options['freedom'] === true ) {
+ $content = $this->html5_static();
+ } 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() {
+ global $wp_locale;
+ $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 . '">';
+
+ for( $i=0; $i<12; $i++ ) {
+ $html .= '<option value="' . esc_attr( $i ) . '">' . esc_html( $wp_locale->get_month( $i + 1 ) ) . '</option>';
+ }
+ $html .= '</select>';
+
+ /**
+ * 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() {
+ $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' ) ),
+ 'hd' => (bool) $this->options['hd']
+ );
+ 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;
+ $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 ) . '][' . self::$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}," . self::$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}," . self::$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
+ $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;
+ }
+} \ No newline at end of file
diff --git a/plugins/jetpack/modules/videopress/class.videopress-video.php b/plugins/jetpack/modules/videopress/class.videopress-video.php
new file mode 100644
index 00000000..a0d01b44
--- /dev/null
+++ b/plugins/jetpack/modules/videopress/class.videopress-video.php
@@ -0,0 +1,324 @@
+<?php
+/**
+ * VideoPress video object retrieved from VideoPress servers and parsed.
+ * @since 1.3
+ */
+class VideoPress_Video {
+ public $version = 3;
+
+ /**
+ * 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 ) {
+ $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;
+ }
+
+ /**
+ * 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 ) {
+ return parse_url( esc_url_raw( $url ), PHP_URL_HOST );
+ }
+
+
+ /**
+ * 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( add_query_arg( $request_params, $url ), array(
+ 'redirection' => 1,
+ 'user-agent' => 'VideoPress plugin ' . $this->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 );
+ }
+ }
+} \ No newline at end of file
diff --git a/plugins/jetpack/modules/videopress/shortcode.php b/plugins/jetpack/modules/videopress/shortcode.php
new file mode 100644
index 00000000..3aa174cf
--- /dev/null
+++ b/plugins/jetpack/modules/videopress/shortcode.php
@@ -0,0 +1,105 @@
+<?php
+/**
+ * VideoPress Shortcode Handler
+ *
+ * This file may or may not be included from the Jetpack VideoPress module.
+ */
+class Jetpack_VideoPress_Shortcode {
+ public $min_width = 60;
+
+ /**
+ * Singleton
+ */
+ public static function init() {
+ static $instance = false;
+
+ if ( ! $instance )
+ $instance = new Jetpack_VideoPress_Shortcode;
+
+ return $instance;
+ }
+
+ function __construct() {
+ add_shortcode( 'videopress', array( $this, 'shortcode_callback' ) );
+ add_shortcode( 'wpvideo', array( $this, 'shortcode_callback' ) );
+ }
+
+ /**
+ * 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_callback( $attr, $content = '' ) {
+ global $content_width;
+
+ $guid = $attr[0];
+ if ( ! $this->is_valid_guid( $guid ) )
+ return '';
+
+ $attr = shortcode_atts( array(
+ 'w' => 0,
+ 'freedom' => false,
+ 'flashonly' => false,
+ 'autoplay' => false,
+ 'hd' => false
+ ), $attr );
+
+ $attr['forcestatic'] = false;
+
+ $attr['freedom'] = (bool) $attr['freedom'];
+ $attr['hd'] = (bool) $attr['hd'];
+ $attr['width'] = absint( $attr['w'] );
+
+ if ( $attr['width'] < $this->min_width )
+ $attr['width'] = 0;
+ elseif ( isset( $content_width ) && $content_width > $this->min_width && $attr['width'] > $content_width )
+ $attr['width'] = 0;
+
+ if ( $attr['width'] === 0 && isset( $content_width ) && $content_width > $this->min_width )
+ $attr['width'] = $content_width;
+
+ if ( ( $attr['width'] % 2 ) === 1 )
+ $attr['width']--;
+
+ $options = apply_filters( 'videopress_shortcode_options', array(
+ 'freedom' => $attr['freedom'],
+ 'force_flash' => (bool) $attr['flashonly'],
+ 'autoplay' => (bool) $attr['autoplay'],
+ 'forcestatic' => $attr['forcestatic'],
+ 'hd' => (bool) $attr['hd']
+ ) );
+
+ // Enqueue VideoPress scripts
+ $js_url = ( is_ssl() ) ? 'https://v0.wordpress.com/js/videopress.js' : 'http://s0.videopress.com/js/videopress.js';
+ wp_enqueue_script( 'videopress', $js_url, array( 'jquery', 'swfobject' ), '1.09' );
+
+ require_once( dirname( __FILE__ ) . '/class.videopress-video.php' );
+ require_once( dirname( __FILE__ ) . '/class.videopress-player.php' );
+
+ $player = new VideoPress_Player( $guid, $attr['width'], $options );
+
+ if ( is_feed() )
+ return $player->asXML();
+ else
+ return $player->asHTML();
+ }
+
+ /**
+ * Validate user-supplied guid values against expected inputs
+ *
+ * @since 1.1
+ * @param string $guid video identifier
+ * @return bool true if passes validation test
+ */
+ public function is_valid_guid( $guid ) {
+ if ( ! empty( $guid ) && strlen( $guid ) === 8 && ctype_alnum( $guid ) )
+ return true;
+ else
+ return false;
+ }
+}
+
+// Initialize the shortcode handler.
+Jetpack_VideoPress_Shortcode::init(); \ No newline at end of file
diff --git a/plugins/jetpack/modules/videopress/videopress-admin.css b/plugins/jetpack/modules/videopress/videopress-admin.css
new file mode 100644
index 00000000..c8f1b0af
--- /dev/null
+++ b/plugins/jetpack/modules/videopress/videopress-admin.css
@@ -0,0 +1,97 @@
+/**
+ * VideoPress admin media styles
+ */
+.videopress-modal-backdrop {
+ background: #000;
+ opacity: 0.7;
+ position: absolute;
+ top: 0;
+ width: 100%;
+ height: 100%;
+ overflow: hidden;
+ z-index: 100;
+}
+
+.videopress-modal {
+ padding: 10px 20px;
+ background: white;
+ position: absolute;
+ top: 0;
+ width: 440px;
+ overflow: hidden;
+ left: 50%;
+ margin-left: -220px;
+ z-index: 101;
+ box-shadow: 2px 2px 5px 2px rgba( 0, 0, 0, 0.5 );
+ -webkit-border-bottom-right-radius: 2px;
+ -webkit-border-bottom-left-radius: 2px;
+ border-bottom-right-radius: 2px;
+ border-bottom-left-radius: 2px;
+}
+
+.videopress-modal .submit {
+ text-align: right;
+ padding: 10px 0 5px;
+}
+
+.videopress-preview {
+ display: block;
+ float: right;
+ width: 65%;
+ margin-top: 18px;
+ background: black;
+ min-height: 97px;
+ text-decoration: none;
+}
+
+.vp-preview span.videopress-preview-unavailable {
+ width: 65%;
+ float: right;
+ text-align: left;
+ margin-right: 0;
+}
+
+.videopress-preview img {
+ float: left;
+ width: 100%;
+}
+
+.videopress-preview span {
+ display: block;
+ padding-top: 40px;
+ color: white !important;
+ text-align: center;
+}
+
+.vp-setting .help {
+ margin: 0 0 4px 35%;
+}
+
+.media-sidebar .vp-setting input[type="checkbox"] {
+ float: left;
+ margin-top: 10px;
+}
+
+.vp-setting label {
+ float: left;
+ margin: 8px 8px 0 5px;
+ max-width: 135px;
+}
+
+.vp-setting input[type='radio'] {
+ float: left;
+ margin-top: 9px;
+ width: auto;
+}
+
+.vp-preview span {
+ margin-top: 18px;
+}
+
+.uploader-videopress {
+ margin: 16px;
+}
+
+.uploader-videopress .videopress-errors div {
+ margin: 16px 0;
+} \ No newline at end of file
diff --git a/plugins/jetpack/modules/videopress/videopress-admin.js b/plugins/jetpack/modules/videopress/videopress-admin.js
new file mode 100644
index 00000000..c4a30ca2
--- /dev/null
+++ b/plugins/jetpack/modules/videopress/videopress-admin.js
@@ -0,0 +1,458 @@
+/**
+ * VideoPress Admin
+ *
+ * @todo i18n
+ */
+(function($) {
+ var media = wp.media;
+ var VideoPress = VideoPress || {};
+
+ VideoPress.caps = VideoPressAdminSettings.caps;
+ VideoPress.l10n = VideoPressAdminSettings.l10n;
+
+ /**
+ * Create a new controller that simply adds a videopress key
+ * to the library query
+ */
+ media.controller.VideoPress = media.controller.Library.extend({
+ defaults: _.defaults({
+ id: 'videopress',
+ router: 'videopress',
+ toolbar: 'videopress-toolbar',
+ title: 'VideoPress',
+ priority: 200,
+ searchable: true,
+ sortable: false
+ }, media.controller.Library.prototype.defaults ),
+
+ initialize: function() {
+ if ( ! this.get('library') )
+ this.set( 'library', media.query({ videopress: true }) );
+
+ media.controller.Library.prototype.initialize.apply( this, arguments );
+ },
+
+ /**
+ * The original function saves content for the browse router only,
+ * so we hi-jack it a little bit.
+ */
+ saveContentMode: function() {
+ if ( 'videopress' !== this.get('router') )
+ return;
+
+ var mode = this.frame.content.mode(),
+ view = this.frame.router.get();
+
+ if ( view && view.get( mode ) ) {
+
+ // Map the Upload a Video back to the regular Upload Files.
+ if ( 'upload_videopress' === mode )
+ mode = 'upload';
+
+ setUserSetting( 'libraryContent', mode );
+ }
+ }
+ });
+
+ /**
+ * VideoPress Uploader
+ */
+ media.view.VideoPressUploader = media.View.extend({
+ tagName: 'div',
+ className: 'uploader-videopress',
+ template: media.template('videopress-uploader'),
+
+ events: {
+ 'submit .videopress-upload-form': 'submitForm'
+ },
+
+ initialize: function() {
+ var that = this;
+
+ if ( ! window.addEventListener )
+ window.attachEvent( "onmessage", function() { return that.messageHandler.apply( that, arguments ); } );
+ else
+ window.addEventListener( "message", function() { return that.messageHandler.apply( that, arguments ); }, false );
+
+ return media.View.prototype.initialize.apply( this, arguments );
+ },
+
+ submitForm: function() {
+ var data = false;
+
+ this.clearErrors();
+
+ if ( this.$( 'input[name="videopress_file"]').val().length < 1 ) {
+ this.error( VideoPress.l10n.selectVideoFile );
+ return false;
+ }
+
+ // Prevent multiple submissions.
+ this.$( '.videopress-upload-form .button' ).prop( 'disabled', true );
+
+ // A non-async request for an upload token.
+ media.ajax( 'videopress-get-upload-token', { async: false } ).done( function( response ) {
+ data = response;
+ data.success = true;
+ }).fail( function( response ) {
+ data = response;
+ data.success = false;
+ });
+
+ if ( ! data.success ) {
+ // Re-enable form elements.
+ this.$( '.videopress-upload-form .button' ).prop( 'disabled', false );
+
+ // Display an error message and cancel form submission.
+ this.error( data.message );
+ return false;
+ }
+
+ this.error( VideoPress.l10n.videoUploading, 'updated' );
+
+ // Set the form token.
+ this.$( 'input[name="videopress_blog_id"]' ).val( data.videopress_blog_id );
+ this.$( 'input[name="videopress_token"]' ).val( data.videopress_token );
+ this.$( '.videopress-upload-form' ).attr( 'action', data.videopress_action_url );
+ return true;
+ },
+
+ error: function( message, type ) {
+ type = type || 'error';
+ var div = $( '<div />' ).html( $( '<p />' ).text( message ) ).addClass( type );
+ this.$( '.videopress-errors' ).html( div );
+ return this;
+ },
+
+ success: function( message ) {
+ return this.error( message, 'updated' );
+ },
+
+ clearErrors: function() {
+ this.$( '.videopress-errors' ).html('');
+ return this;
+ },
+
+ messageHandler: function( event ) {
+ if ( ! event.origin.match( /\.wordpress\.com$/ ) )
+ return;
+
+ if ( event.data.indexOf && event.data.indexOf( 'vpUploadResult::' ) === 0 ) {
+ var result = JSON.parse( event.data.substr( 16 ) );
+
+ if ( ! result || ! result.code ) {
+ this.error( VideoPress.l10n.unknownError );
+ this.$( '.videopress-upload-form .button' ).prop( 'disabled', false );
+ return;
+ }
+
+ if ( 'success' == result.code && result.data ) {
+ var that = this, controller = this.controller,
+ state = controller.states.get( 'videopress' );
+
+ // Our new video has been added, so we need to reset the library.
+ // Since the Media API caches all queries, we add a random attribute
+ // to avoid the cache, then call more() to actually fetch the data.
+
+ state.set( 'library', media.query({ videopress:true, vp_random:Math.random() }) );
+ state.get( 'library' ).more().done(function(){
+ var model = state.get( 'library' ).get( result.data.attachment_id );
+
+ // Clear errors and select the uploaded item.
+ that.clearErrors();
+ state.get( 'selection' ).reset([ model ]);
+ controller.content.mode( 'browse' );
+ });
+ } else {
+ this.error( result.code );
+
+ // Re-enable form elements.
+ this.$( '.videopress-upload-form .button' ).prop( 'disabled', false );
+ }
+ }
+ }
+ });
+
+ /**
+ * Add a custom sync function that would add a few extra
+ * options for models which are VideoPress videos.
+ */
+ var attachmentSync = media.model.Attachment.prototype.sync;
+ media.model.Attachment.prototype.sync = function( method, model, options ) {
+ if ( model.get( 'vp_isVideoPress' ) ) {
+ console.log( 'syncing ' + model.get( 'vp_guid' ) );
+ options.data = _.extend( options.data || {}, {
+ is_videopress: true,
+ vp_nonces: model.get( 'vp_nonces' )
+ } );
+ }
+
+ // Call the original sync routine.
+ return attachmentSync.apply( this, arguments );
+ };
+
+ /**
+ * Extend the default Attachment Details view. Check for vp_isVideoPress before
+ * adding anything to these methods.
+ */
+ var AttachmentDetails = media.view.Attachment.Details;
+ media.view.Attachment.Details = AttachmentDetails.extend({
+
+ initialize: function() {
+ if ( this.model.get( 'vp_isVideoPress' ) ) {
+ _.extend( this.events, {
+ 'click a.videopress-preview': 'vpPreview',
+ 'change .vp-radio': 'vpRadioChange',
+ 'change .vp-checkbox': 'vpCheckboxChange'
+ });
+ }
+ return AttachmentDetails.prototype.initialize.apply( this, arguments );
+ },
+
+ render: function() {
+ var r = AttachmentDetails.prototype.render.apply( this, arguments );
+ if ( this.model.get( 'vp_isVideoPress' ) ) {
+ var template = media.template( 'videopress-attachment' );
+ var options = this.model.toJSON();
+
+ options.can = {};
+ options.can.save = !! options.nonces.update;
+
+ this.$el.append( template( options ) );
+ }
+ return r;
+ },
+
+ // Handle radio buttons
+ vpRadioChange: function(e) {
+ $( e.target ).parents( '.vp-setting' ).find( '.vp-radio-text' ).val( e.target.value ).change();
+ },
+
+ // And checkboxes
+ vpCheckboxChange: function(e) {
+ $( e.target ).parents( '.vp-setting' ).find( '.vp-checkbox-text' ).val( Number( e.target.checked ) ).change();
+ },
+
+ vpPreview: function() {
+ VideoPressModal.render( this );
+ return this;
+ }
+ });
+
+ /**
+ * Don't display the uploader dropzone for the VideoPress router.
+ */
+ var UploaderWindow = media.view.UploaderWindow;
+ media.view.UploaderWindow = UploaderWindow.extend({
+ show: function() {
+ if ( 'videopress' != this.controller.state().get('id') )
+ UploaderWindow.prototype.show.apply( this, arguments );
+
+ return this;
+ }
+ });
+
+ /**
+ * Don't display the uploader in the attachments browser.
+ */
+ var AttachmentsBrowser = media.view.AttachmentsBrowser;
+ media.view.AttachmentsBrowser = AttachmentsBrowser.extend({
+ createUploader: function() {
+ if ( 'videopress' != this.controller.state().get('id') )
+ return AttachmentsBrowser.prototype.createUploader.apply( this, arguments );
+ }
+ });
+
+ /**
+ * Add VideoPress-specific methods for all frames.
+ */
+ _.extend( media.view.MediaFrame.prototype, { VideoPress: { // this.VideoPress.method()
+
+ // When the VideoPress router is activated.
+ activate: function() {
+ var view = _.first( this.views.get( '.media-frame-router' ) ),
+ viewSettings = {};
+
+ if ( VideoPress.caps.read_videos )
+ viewSettings.browse = { text: VideoPress.l10n.VideoPressLibraryRouter, priority: 40 };
+
+ if ( VideoPress.caps.upload_videos )
+ viewSettings.upload_videopress = { text: VideoPress.l10n.uploadVideoRouter, priority: 20 };
+
+ view.set( viewSettings );
+
+ // Intercept and clear all incoming uploads
+ wp.Uploader.queue.on( 'add', this.VideoPress.disableUpload, this );
+
+ // Map the Upload Files view to the Upload a Video one (upload_videopress vs. upload)
+ if ( 'upload' === this.content.mode() && VideoPress.caps.upload_videos )
+ this.content.mode( 'upload_videopress' );
+ else
+ this.content.mode( 'browse' );
+ },
+
+ // When navigated away from the VideoPress router.
+ deactivate: function( view ) {
+ wp.Uploader.queue.off( 'add', this.VideoPress.disableUpload );
+ },
+
+ // Disable dragdrop uploads in the VideoPress router.
+ disableUpload: function( attachment ) {
+ var uploader = this.uploader.uploader.uploader;
+ uploader.stop();
+ uploader.splice();
+ attachment.destroy();
+ },
+
+ // Runs on videopress:insert event fired by our custom toolbar
+ insert: function( selection ) {
+ var guid = selection.models[0].get( 'vp_guid' ).replace( /[^a-zA-Z0-9]+/, '' );
+ media.editor.insert( '[wpvideo ' + guid + ']' );
+ return this;
+ },
+
+ // Triggered by the upload_videopress router item.
+ uploadVideo: function() {
+ this.content.set( new media.view.VideoPressUploader({
+ controller: this
+ }) );
+ return this;
+ },
+
+ // Create a custom toolbar
+ createToolbar: function( toolbar ) {
+ // Alow an option to hide the toolbar.
+ if ( this.options.VideoPress && this.options.VideoPress.hideToolbar )
+ return this;
+
+ var controller = this;
+ this.toolbar.set( new media.view.Toolbar({
+ controller: this,
+ items: {
+ insert: {
+ style: 'primary',
+ text: VideoPress.l10n.insertVideoButton,
+ priority: 80,
+ requires: {
+ library: true,
+ selection: true
+ },
+
+ click: function() {
+ var state = controller.state(),
+ selection = state.get('selection');
+
+ controller.close();
+ state.trigger( 'videopress:insert', selection ).reset();
+ }
+ }
+ }
+ }) );
+ }
+ }});
+
+ var MediaFrame = {};
+
+ /**
+ * Extend the selection media frame
+ */
+ MediaFrame.Select = media.view.MediaFrame.Select;
+ media.view.MediaFrame.Select = MediaFrame.Select.extend({
+ bindHandlers: function() {
+ MediaFrame.Select.prototype.bindHandlers.apply( this, arguments );
+
+ this.on( 'router:create:videopress', this.createRouter, this );
+ this.on( 'router:activate:videopress', this.VideoPress.activate, this );
+ this.on( 'router:deactivate:videopress', this.VideoPress.deactivate, this );
+
+ this.on( 'content:render:upload_videopress', this.VideoPress.uploadVideo, this );
+ this.on( 'toolbar:create:videopress-toolbar', this.VideoPress.createToolbar, this );
+ this.on( 'videopress:insert', this.VideoPress.insert, this );
+ }
+ });
+
+ /**
+ * Extend the post editor media frame with our own
+ */
+ MediaFrame.Post = media.view.MediaFrame.Post;
+ media.view.MediaFrame.Post = MediaFrame.Post.extend({
+ createStates: function() {
+ MediaFrame.Post.prototype.createStates.apply( this, arguments );
+ this.states.add([ new media.controller.VideoPress() ]);
+ }
+ });
+
+ /**
+ * A VideoPress Modal view that we can use to preview videos.
+ * Expects a controller object on render.
+ */
+ var VideoPressModalView = Backbone.View.extend({
+ 'className': 'videopress-modal-container',
+ 'template': wp.media.template( 'videopress-media-modal' ),
+
+ // Render the VideoPress modal with a video object by guid.
+ render: function( controller ) {
+ this.delegateEvents( {
+ 'click .videopress-modal-close': 'closeModal',
+ 'click .videopress-modal-backdrop': 'closeModal'
+ } );
+
+ this.model = controller.model;
+ this.guid = this.model.get( 'vp_guid' );
+
+ if ( ! this.$frame )
+ this.$frame = $( '.media-frame-content' );
+
+ this.$el.html( this.template( { 'video' : this.model.get( 'vp_embed' ) } ) );
+ this.$modal = this.$( '.videopress-modal' );
+ this.$modal.hide();
+
+ this.$frame.append( this.$el );
+ this.$modal.slideDown( 'fast' );
+
+ return this;
+ },
+
+ closeModal: function() {
+ var view = this;
+ this.$modal.slideUp( 'fast', function() { view.remove(); } );
+ return this;
+ }
+ });
+
+ var VideoPressModal = new VideoPressModalView();
+
+ // Configuration screen behavior
+ $(document).on( 'ready', function() {
+ var $form = $( '#videopress-settings' );
+
+ // Not on a configuration screen
+ if ( ! $form.length )
+ return;
+
+ var $access = $form.find( 'input[name="videopress-access"]' ),
+ $upload = $form.find( 'input[name="videopress-upload"]' );
+
+ $access.on( 'change', function() {
+ var access = $access.filter( ':checked' ).val();
+ $upload.attr( 'disabled', ! access );
+
+ if ( ! access )
+ $upload.attr( 'checked', false );
+ });
+
+ $access.trigger( 'change' );
+ });
+
+ // Media -> VideoPress menu
+ $(document).on( 'click', '#videopress-browse', function() {
+
+ var frame = wp.media({
+ state: 'videopress',
+ states: [ new media.controller.VideoPress() ],
+ VideoPress: { hideToolbar: true }
+ }).open();
+
+ return false;
+ });
+})(jQuery); \ No newline at end of file
diff --git a/plugins/jetpack/modules/videopress/videopress.php b/plugins/jetpack/modules/videopress/videopress.php
new file mode 100644
index 00000000..84739be2
--- /dev/null
+++ b/plugins/jetpack/modules/videopress/videopress.php
@@ -0,0 +1,734 @@
+<?php
+/**
+ * VideoPress in Jetpack
+ *
+ */
+class Jetpack_VideoPress {
+ public $module = 'videopress';
+ public $option_name = 'videopress';
+ public $version = 4;
+
+ /**
+ * Singleton
+ */
+ public static function init() {
+ static $instance = false;
+
+ if ( ! $instance )
+ $instance = new Jetpack_VideoPress;
+
+ return $instance;
+ }
+
+ function __construct() {
+ $this->version = time(); // <s>ghost</s> cache busters!
+ add_action( 'jetpack_modules_loaded', array( $this, 'jetpack_modules_loaded' ) );
+ add_action( 'jetpack_activate_module_videopress', array( $this, 'jetpack_module_activated' ) );
+ add_action( 'jetpack_deactivate_module_videopress', array( $this, 'jetpack_module_deactivated' ) );
+
+ require_once( dirname( __FILE__ ) . '/shortcode.php' );
+ }
+
+ /**
+ * After all modules have been loaded.
+ */
+ function jetpack_modules_loaded() {
+ $options = $this->get_options();
+
+ // Only the connection owner can configure this module.
+ if ( $this->is_connection_owner() ) {
+ Jetpack::enable_module_configurable( $this->module );
+ Jetpack::module_configuration_load( $this->module, array( $this, 'jetpack_configuration_load' ) );
+ Jetpack::module_configuration_screen( $this->module, array( $this, 'jetpack_configuration_screen' ) );
+ }
+
+ // Only if the current user can manage the VideoPress library and one has been connected.
+ if ( $this->can( 'read_videos' ) && $options['blog_id'] ) {
+ add_action( 'wp_enqueue_media', array( $this, 'enqueue_admin_scripts' ) );
+ add_action( 'print_media_templates', array( $this, 'print_media_templates' ) );
+
+ // Load these at priority -1 so they're fired before Core's are.
+ add_action( 'wp_ajax_query-attachments', array( $this, 'wp_ajax_query_attachments' ), -1 );
+ add_action( 'wp_ajax_save-attachment', array( $this, 'wp_ajax_save_attachment' ), -1 );
+ add_action( 'wp_ajax_save-attachment-compat', array( $this, 'wp_ajax_save_attachment' ), -1 );
+ add_action( 'wp_ajax_delete-post', array( $this, 'wp_ajax_delete_post' ), -1 );
+
+ add_action( 'admin_menu', array( $this, 'admin_menu' ) );
+ }
+
+ if ( $this->can( 'upload_videos' ) && $options['blog_id'] ) {
+ add_action( 'wp_ajax_videopress-get-upload-token', array( $this, 'wp_ajax_videopress_get_upload_token' ) );
+ }
+
+ add_filter( 'videopress_shortcode_options', array( $this, 'videopress_shortcode_options' ) );
+ }
+
+ function wp_ajax_videopress_get_upload_token() {
+ if ( ! $this->can( 'upload_videos' ) )
+ return wp_send_json_error();
+
+ $result = $this->query( 'jetpack.vpGetUploadToken' );
+ if ( is_wp_error( $result ) )
+ return wp_send_json_error( array( 'message' => __( 'Could not obtain a VideoPress upload token. Please try again later.', 'jetpack' ) ) );
+
+ $response = $result;
+ if ( empty( $response['videopress_blog_id'] ) || empty( $response['videopress_token'] ) || empty( $response[ 'videopress_action_url' ] ) )
+ return wp_send_json_error( array( 'message' => __( 'Could not obtain a VideoPress upload token. Please try again later.', 'jetpack' ) ) );
+
+ return wp_send_json_success( $response );
+ }
+
+ /**
+ * Get VideoPress options
+ */
+ function get_options() {
+ $defaults = array(
+ 'blogs' => array(),
+ 'blog_id' => 0,
+ 'access' => '',
+ 'allow-upload' => false,
+ 'freedom' => false,
+ 'hd' => false,
+ 'meta' => array(
+ 'max_upload_size' => 0,
+ ),
+ );
+
+ $options = Jetpack_Options::get_option( $this->option_name, array() );
+
+ // If options have not been saved yet, check for older VideoPress plugin options.
+ if ( empty( $options ) ) {
+ $options['freedom'] = (bool) get_option( 'video_player_freedom', false );
+ $options['hd'] = (bool) get_option( 'video_player_high_quality', false );
+ }
+
+ $options = array_merge( $defaults, $options );
+ return $options;
+ }
+
+ /**
+ * Update VideoPress options
+ */
+ function update_options( $options ) {
+ Jetpack_Options::update_option( $this->option_name, $options );
+ }
+
+ /**
+ * Runs when the VideoPress module is activated.
+ */
+ function jetpack_module_activated() {
+ if ( ! $this->is_connection_owner() )
+ return;
+
+ $options = $this->get_options();
+
+ // Ask WordPress.com for a list of VideoPress blogs
+ $result = $this->query( 'jetpack.vpGetBlogs' );
+ if ( ! is_wp_error() )
+ $options['blogs'] = $result;
+
+ // If there's at least one available blog, let's use it.
+ if ( is_array( $options['blogs'] ) && count( $options['blogs'] ) > 0 )
+ $options['blog_id'] = $options['blogs'][0]['blog_id'];
+
+ $this->update_options( $options );
+ }
+
+ /**
+ * Runs when the VideoPress module is deactivated.
+ */
+ function jetpack_module_deactivated() {
+ Jetpack_Options::delete_option( $this->option_name );
+ }
+
+ /**
+ * Remote Query
+ *
+ * Performs a remote XML-RPC query using Jetpack's IXR Client. And also
+ * appends some useful stuff about this setup to the query.
+ *
+ * @return the Jetpack_IXR_Client object after querying.
+ */
+ function query( $method, $args = null ) {
+ $options = $this->get_options();
+ Jetpack::load_xml_rpc_client();
+ $xml = new Jetpack_IXR_Client( array(
+ 'user_id' => JETPACK_MASTER_USER, // All requests are on behalf of the connection owner.
+ ) );
+
+ $params = array(
+ 'args' => $args,
+ 'video_blog_id' => $options['blog_id'],
+ 'caps' => array(),
+ );
+
+ // Let Jetpack know about our local caps.
+ foreach ( array( 'read_videos', 'edit_videos', 'delete_videos', 'upload_videos' ) as $cap )
+ if ( $this->can( $cap ) )
+ $params['caps'][] = $cap;
+
+ $xml->query( $method, $params );
+
+ if ( $xml->isError() )
+ return new WP_Error( 'xml_rpc_error', 'An XML-RPC error has occurred.' );
+
+ $response = $xml->getResponse();
+
+ // If there's any metadata with the response, save it for future use.
+ if ( is_array( $response ) && isset( $response['meta'] ) ) {
+ $options = $this->get_options();
+ if ( $response['meta'] !== $options['meta'] ) {
+ $options['meta'] = array_merge( $options['meta'], $response['meta'] );
+ $this->update_options( $options );
+ }
+ }
+
+ if ( is_array( $response ) && isset( $response['result'] ) )
+ return $response['result'];
+
+ return $response;
+ }
+
+ /**
+ * Runs before the VideoPress Configuration screen loads, useful
+ * to update options and yield errors.
+ */
+ function jetpack_configuration_load() {
+ $this->enqueue_admin_scripts();
+
+ /**
+ * Save configuration
+ */
+ if ( ! empty( $_POST['action'] ) && $_POST['action'] == 'videopress-save' ) {
+ check_admin_referer( 'videopress-settings' );
+ $options = $this->get_options();
+
+ if ( isset( $_POST['blog_id'] ) && in_array( $_POST['blog_id'], wp_list_pluck( $options['blogs'], 'blog_id' ) ) )
+ $options['blog_id'] = $_POST['blog_id'];
+
+ // Allow the None setting too.
+ if ( isset( $_POST['blog_id'] ) && $_POST['blog_id'] == 0 )
+ $options['blog_id'] = 0;
+
+ /**
+ * @see $this->can()
+ */
+ if ( isset( $_POST['videopress-access'] ) && in_array( $_POST['videopress-access'], array( '', 'read', 'edit', 'delete' ) ) )
+ $options['access'] = $_POST['videopress-access'];
+
+ $options['freedom'] = isset( $_POST['videopress-freedom'] );
+ $options['hd'] = isset( $_POST['videopress-hd'] );
+
+ // Allow upload only if some level of access has been granted, and uploads were allowed.
+ $options['allow-upload'] = false;
+ if ( ! empty( $options['access'] ) && isset( $_POST['videopress-upload'] ) )
+ $options['allow-upload'] = true;
+
+ $this->update_options( $options );
+ Jetpack::state( 'message', 'module_configured' );
+ wp_safe_redirect( Jetpack::module_configuration_url( $this->module ) );
+ }
+
+ /**
+ * Refresh the list of available WordPress.com blogs
+ */
+ if ( ! empty( $_GET['videopress'] ) && $_GET['videopress'] == 'refresh-blogs' ) {
+ check_admin_referer( 'videopress-settings' );
+ $options = $this->get_options();
+
+ $result = $this->query( 'jetpack.vpGetBlogs' );
+ if ( ! is_wp_error( $result ) ) {
+ $options['blogs'] = $result;
+ $this->update_options( $options );
+ }
+
+ wp_safe_redirect( Jetpack::module_configuration_url( $this->module ) );
+ }
+ }
+
+ /**
+ * Renders the VideoPress Configuration screen in Jetpack.
+ */
+ function jetpack_configuration_screen() {
+ $options = $this->get_options();
+ $refresh_url = wp_nonce_url( add_query_arg( 'videopress', 'refresh-blogs' ), 'videopress-settings' );
+ ?>
+ <div class="narrow">
+ <form method="post" id="videopress-settings">
+ <input type="hidden" name="action" value="videopress-save" />
+ <?php wp_nonce_field( 'videopress-settings' ); ?>
+
+ <table id="menu" class="form-table">
+ <tr>
+ <th scope="row" colspan="2">
+ <p><?php _e( 'Please note that the VideoPress module requires a WordPress.com account with an active <a href="http://store.wordpress.com/premium-upgrades/videopress/" target="_blank">VideoPress subscription</a>.</p>', 'jetpack' ); ?></p>
+ </th>
+ </tr>
+ <tr>
+ <th scope="row">
+ <label><?php _e( 'Connected WordPress.com Blog', 'jetpack' ); ?></label>
+ </th>
+ <td>
+ <select name="blog_id">
+ <option value="0" <?php selected( $options['blog_id'], 0 ); ?>> <?php esc_html_e( 'None', 'jetpack' ); ?></option>
+ <?php foreach ( $options['blogs'] as $blog ) : ?>
+ <option value="<?php echo absint( $blog['blog_id'] ); ?>" <?php selected( $options['blog_id'], $blog['blog_id'] ); ?>><?php echo esc_html( $blog['name'] ); ?> (<?php echo esc_html( $blog['domain'] ); ?>)</option>
+ <?php endforeach; ?>
+ </select>
+ <p class="description"><?php _e( 'Only videos from the selected blog will be available in your media library.', 'jetpack' ); ?>
+ <?php printf( __( '<a href="%s">Click here</a> to refresh this list.', 'jetpack' ), esc_url( $refresh_url ) ); ?>
+ </p>
+ </td>
+ </tr>
+ <tr>
+ <th scope="row">
+ <label><?php _e( 'Video Library Access', 'jetpack' ); ?></label>
+ </th>
+ <td>
+ <label><input type="radio" name="videopress-access" value="" <?php checked( '', $options['access'] ); ?> />
+ <?php _e( 'Do not allow other users to access my VideoPress library', 'jetpack' ); ?></label><br/>
+ <label><input type="radio" name="videopress-access" value="read" <?php checked( 'read', $options['access'] ); ?> />
+ <?php _e( 'Allow users to access my videos', 'jetpack' ); ?></label><br/>
+ <label><input type="radio" name="videopress-access" value="edit" <?php checked( 'edit', $options['access'] ); ?> />
+ <?php _e( 'Allow users to access and edit my videos', 'jetpack' ); ?></label><br/>
+ <label><input type="radio" name="videopress-access" value="delete" <?php checked( 'delete', $options['access'] ); ?> />
+ <?php _e( 'Allow users to access, edit, and delete my videos', 'jetpack' ); ?></label><br/><br />
+
+ <label><input type="checkbox" name="videopress-upload" value="1" <?php checked( $options['allow-upload'] ); ?> />
+ <?php _e( 'Allow users to upload videos', 'jetpack' ); ?></label><br />
+ </td>
+ </tr>
+ <tr>
+ <th scope="row">
+ <label for="videopress-freedom"><?php _e( 'Free formats', 'jetpack' ); ?></label>
+ </th>
+ <td>
+ <label><input type="checkbox" name="videopress-freedom" id="videopress-freedom" <?php checked( $options['freedom'] ); ?> />
+ <?php _e( 'Only display videos in free software formats', 'jetpack' ); ?></label>
+ <p class="description"><?php _e( 'Ogg file container with Theora video and Vorbis audio. Note that some browsers are unable to play free software video formats, including Internet Explorer and Safari.', 'jetpack' ); ?></p>
+ </td>
+ </tr>
+ <tr>
+ <th scope="row">
+ <label for="videopress-hd"><?php _e( 'Default quality', 'jetpack' ); ?></label>
+ </th>
+ <td>
+ <label><input type="checkbox" name="videopress-hd" id="videopress-hd" <?php checked( $options['hd'] ); ?> />
+ <?php _e( 'Display higher quality video by default.', 'jetpack' ); ?></label>
+ <p class="description"><?php _e( 'This setting may be overridden for individual videos.', 'jetpack' ); ?></p>
+ </td>
+ </tr>
+ </table>
+
+ <?php submit_button(); ?>
+ </form>
+ </div>
+ <?php
+ }
+
+ function admin_menu() {
+ add_media_page( __( 'VideoPress Library', 'jetpack' ), __( 'VideoPress', 'jetpack' ), 'upload_files', 'videopress-library', array( $this, 'admin_menu_library' ) );
+ }
+
+ function admin_menu_library() {
+ wp_enqueue_media();
+ $this->enqueue_admin_scripts();
+ ?>
+ <div class="wrap" style="max-width: 600px;">
+ <?php screen_icon(); ?>
+ <h2><?php _e( 'VideoPress Library', 'jetpack' ); ?></h2>
+ <p><?php _e( 'Use the button below to browse your VideoPress Library. Note that you can also browse your VideoPress Library while editing a post or page by using the <strong>Add Media</strong> button in the post editor.', 'jetpack' ); ?></p>
+ <p class="hide-if-no-js"><a href="#" id="videopress-browse" class="button"><?php _e( 'Browse Your VideoPress Library', 'jetpack' ); ?></a></p>
+ <p class="hide-if-js description"><?php _e( 'Please enable JavaScript support in your browser to use VideoPress.', 'jetpack' ); ?></p>
+ </div>
+ <?php
+ }
+
+ /**
+ * A can of coke
+ *
+ * Similar to current_user_can, but internal to VideoPress. Returns
+ * true if the given VideoPress capability is allowed by the given user.
+ */
+ function can( $cap, $user_id = false ) {
+ if ( ! $user_id )
+ $user_id = get_current_user_id();
+
+ // Connection owners are allowed to do all the things.
+ if ( $this->is_connection_owner( $user_id ) )
+ return true;
+
+ /**
+ * The access setting can be set by the connection owner, to allow sets
+ * of operations to other site users. Each access value corresponds to
+ * an array of things they can do.
+ */
+
+ $options = $this->get_options();
+ $map = array(
+ 'read' => array( 'read_videos' ),
+ 'edit' => array( 'read_videos', 'edit_videos' ),
+ 'delete' => array( 'read_videos', 'edit_videos', 'delete_videos' ),
+ );
+
+ if ( ! array_key_exists( $options['access'], $map ) )
+ return false;
+
+ if ( ! in_array( $cap, $map[ $options['access'] ] ) && 'upload_videos' != $cap )
+ return false;
+
+ // Additional and intrenal caps checks
+
+ if ( ! user_can( $user_id, 'upload_files' ) )
+ return false;
+
+ if ( 'edit_videos' == $cap && ! user_can( $user_id, 'edit_others_posts' ) )
+ return false;
+
+ if ( 'delete_videos' == $cap && ! user_can( $user_id, 'delete_others_posts' ) )
+ return false;
+
+ if ( 'upload_videos' == $cap && ! $options['allow-upload'] )
+ return false;
+
+ return true;
+ }
+
+ /**
+ * Returns true if the provided user is the Jetpack connection owner.
+ */
+ function is_connection_owner( $user_id = false ) {
+ if ( ! $user_id )
+ $user_id = get_current_user_id();
+
+ $user_token = Jetpack_Data::get_access_token( JETPACK_MASTER_USER );
+ return $user_token && is_object( $user_token ) && isset( $user_token->external_user_id ) && $user_id === $user_token->external_user_id;
+ }
+
+ /**
+ * Our custom AJAX callback for the query-attachments action
+ * used in the media modal. By-passed if not for VideoPress.
+ */
+ function wp_ajax_query_attachments() {
+
+ // Watch for VideoPress calls
+ if ( ! isset( $_POST['query']['videopress'] ) )
+ return;
+
+ if ( ! $this->can( 'read_videos' ) )
+ return wp_send_json_error( 'permission denied' );
+
+ // Get and sanitize query arguments.
+ $query_args = $this->sanitize_wp_query_args( $_POST['query'] );
+
+ // Fire a remote WP_Query
+ $result = $this->query( 'jetpack.vpQuery', $query_args );
+
+ if ( is_wp_error( $result ) )
+ return wp_send_json_error( 'xml rpc request error' );
+
+ $items = $result;
+ $shortcode_handler = Jetpack_VideoPress_Shortcode::init();
+
+ foreach ( $items as $key => $item ) {
+
+ // Check local permissions
+ if ( ! $this->can( 'edit_videos' ) )
+ unset( $item['vp_nonces']['update'] );
+
+ if ( ! $this->can( 'delete_videos' ) )
+ unset( $item['vp_nonces']['delete'] );
+
+ // Add a second pair of nonces for the .org blog.
+ $item['nonces'] = array();
+ if ( ! empty( $item['vp_nonces']['update'] ) )
+ $item['nonces']['update'] = wp_create_nonce( 'update-videopress-post_' . $item['id'] );
+
+ if ( ! empty( $item['vp_nonces']['delete'] ) )
+ $item['nonces']['delete'] = wp_create_nonce( 'delete-videopress-post_' . $item['id'] );
+
+ $item['vp_embed'] = $shortcode_handler->shortcode_callback( array(
+ $item['vp_guid'],
+ 'autoplay' => true,
+ 'flashonly' => true,
+ 'w' => 440,
+ ) );
+
+ $items[ $key ] = $item;
+ }
+
+ wp_send_json_success( $items );
+ }
+
+ /**
+ * Sanitize user-provided WP_Query arguments
+ *
+ * These might be sent to the VideoPress server, for a remote WP_Query
+ * call so let's make sure they're sanitized and safe to send.
+ */
+ function sanitize_wp_query_args( $args ) {
+ $args = shortcode_atts( array(
+ 'posts_per_page' => 40,
+ 'orderby' => 'date',
+ 'order' => 'desc',
+ 'paged' => 1,
+ 's' => '',
+ ), (array) $args );
+
+ $args['posts_per_page'] = absint( $args['posts_per_page'] );
+
+ $args['orderby'] = strtolower( $args['orderby'] );
+ $args['orderby'] = ( in_array( $args['orderby'], array( 'date' ) ) ) ? $args['orderby'] : 'date';
+
+ $args['order'] = strtolower( $args['order'] );
+ $args['order'] = ( in_array( $args['order'], array( 'asc', 'desc' ) ) ) ? $args['order'] : 'desc';
+
+ $args['paged'] = absint( $args['paged'] );
+ $args['s'] = sanitize_text_field( $args['s'] );
+ return $args;
+ }
+
+ /**
+ * Custom AJAX callback for the save-attachment action. If the request was
+ * not for a VideoPress object, core's fallback action will kick in.
+ */
+ function wp_ajax_save_attachment() {
+ if ( ! isset( $_POST['is_videopress'] ) )
+ return;
+
+ if ( ! $this->can( 'edit_videos' ) )
+ return wp_send_json_error( 'permission denied' );
+
+ $post_id = 0;
+ if ( ! isset( $_POST['id'] ) || ! $post_id = absint( $_POST['id'] ) )
+ wp_send_json_error();
+
+ if ( ! isset( $_POST['vp_nonces']['update'] ) )
+ wp_send_json_error();
+
+ check_ajax_referer( 'update-videopress-post_' . $post_id, 'nonce' );
+
+ $changes = ( ! empty( $_POST['changes'] ) ) ? (array) $_POST['changes'] : array();
+ $changes = shortcode_atts( array(
+ 'title' => null,
+ 'caption' => null,
+ 'description' => null,
+
+ 'vp_share' => null,
+ 'vp_rating' => null,
+ ), $changes );
+
+ if ( ! is_null( $changes['vp_share'] ) )
+ $changes['vp_share'] = (bool) $changes['vp_share'];
+
+ if ( ! is_null( $changes['vp_rating'] ) )
+ $changes['vp_rating'] = ( array_key_exists( $changes['vp_rating'], $this->get_available_ratings() ) ) ? $changes['vp_rating'] : null;
+
+ // Remove null-values
+ foreach ( $changes as $key => $value )
+ if ( is_null( $value ) )
+ unset( $changes[ $key ] );
+
+ $result = $this->query( 'jetpack.vpSaveAttachment', array(
+ 'post_id' => $post_id,
+ 'changes' => $changes,
+ 'nonce' => $_POST['vp_nonces']['update'],
+ ) );
+
+ if ( is_wp_error( $result ) )
+ return wp_send_json_error( 'xml rpc request error' );
+
+ wp_send_json_success();
+ }
+
+ /**
+ * Custom AJAX callback for the delete-post action, only for VideoPress objects.
+ */
+ function wp_ajax_delete_post() {
+ if ( ! isset( $_POST['is_videopress'] ) )
+ return;
+
+ if ( ! $this->can( 'delete_videos' ) )
+ return wp_send_json_error( 'permission denied' );
+
+ $post_id = 0;
+ if ( ! isset( $_POST['id'] ) || ! $post_id = absint( $_POST['id'] ) )
+ wp_send_json_error();
+
+ if ( ! isset( $_POST['vp_nonces']['delete'] ) )
+ wp_send_json_error();
+
+ check_ajax_referer( 'delete-videopress-post_' . $post_id );
+
+ $result = $this->query( 'jetpack.vpDeleteAttachment', array(
+ 'post_id' => $post_id,
+ 'nonce' => $_POST['vp_nonces']['delete'],
+ ) );
+
+ if ( is_wp_error( $result ) )
+ return wp_send_json_error( 'xml rpc request error' );
+
+ wp_send_json_success();
+ }
+
+ /**
+ * Register VideoPress admin scripts.
+ */
+ function enqueue_admin_scripts() {
+ if ( did_action( 'videopress_enqueue_admin_scripts' ) )
+ return;
+
+ wp_enqueue_script( 'videopress-admin', plugins_url( 'videopress-admin.js', __FILE__ ), array( 'jquery', 'media-views', 'media-models' ), $this->version );
+ wp_enqueue_style( 'videopress-admin', plugins_url( 'videopress-admin.css', __FILE__ ), array(), $this->version );
+
+ $caps = array();
+ foreach( array( 'read_videos', 'edit_videos', 'delete_videos', 'upload_videos' ) as $cap )
+ $caps[ $cap ] = $this->can( $cap );
+
+ $l10n = array(
+ 'selectVideoFile' => __( 'Please select a video file to upload.', 'jetpack' ),
+ 'videoUploading' => __( 'Your video is uploading... Please do not close this window.', 'jetpack' ),
+ 'unknownError' => __( 'An unknown error has occurred. Please try again later.', 'jetpack' ),
+ 'videoUploaded' => __( 'Your video has successfully been uploaded. It will appear in your VideoPress Library shortly.', 'jetpack' ),
+ 'VideoPressLibraryRouter' => __( 'VideoPress Library', 'jetpack' ),
+ 'uploadVideoRouter' => __( 'Upload a Video', 'jetpack' ),
+ 'insertVideoButton' => __( 'Insert Video', 'jetpack' ),
+
+ );
+
+ wp_localize_script( 'videopress-admin', 'VideoPressAdminSettings', array(
+ 'caps' => $caps,
+ 'l10n' => $l10n,
+ ) );
+
+ do_action( 'videopress_enqueue_admin_scripts' );
+ }
+
+ /**
+ * Get an array of available ratings. Keys are options, values are labels.
+ */
+ function get_available_ratings() {
+ return array(
+ 'G' => 'G',
+ 'PG-13' => 'PG-13',
+ 'R-17' => 'R',
+ 'X-18' => 'X',
+ );
+ }
+
+ /**
+ * Additional VideoPress media templates.
+ */
+ function print_media_templates() {
+ $options = $this->get_options();
+ ?>
+ <script type="text/html" id="tmpl-videopress-attachment">
+ <# if ( data.vp_ogg_url ) { #>
+ <label class="setting vp-setting">
+ <span><?php _e( 'Ogg File URL', 'jetpack' ); ?></span>
+ <input type="text" value="{{ data.vp_ogg_url }}" onclick="this.focus();this.select();" readonly />
+ <p class="help"><?php _e( 'Location of the Ogg video file.', 'jetpack' ); ?></p>
+ </label>
+ <# } #>
+
+ <label class="setting vp-setting">
+ <span><?php _e( 'Share', 'jetpack' ); ?></span>
+ <input class="vp-checkbox" type="checkbox" <# if ( '1' === data.vp_share ) { #>checked<# } #> <# if ( ! data.can.save ) { #>disabled<# } #> />
+ <label>
+ <?php _e( 'Display share menu and allow viewers to embed or download this video', 'jetpack' ); ?>
+ </label>
+ <input class="vp-checkbox-text" type="text" value="{{ data.vp_share }}" data-setting="vp_share" style="display:none;" />
+ </label>
+
+ <label class="setting vp-setting">
+ <span><?php _e( 'Rating', 'jetpack' ); ?></span>
+
+ <?php foreach ( $this->get_available_ratings() as $value => $label ) : ?>
+ <input class="vp-radio" type="radio" name="vp-radio-group" id="vp-rating-<?php echo sanitize_html_class( $value ); ?>" value="<?php echo esc_attr( $value ); ?>"
+ <# if ( '<?php echo esc_attr( $value ); ?>' === data.vp_rating ) { #>checked<# } #>
+ <# if ( ! data.can.save ) { #>disabled<# } #> />
+ <label for="vp-rating-<?php echo sanitize_html_class( $value ); ?>"><?php echo esc_html( $label ); ?></label>
+ <?php endforeach; ?>
+
+ <input class="vp-radio-text" type="text" value="{{ data.vp_rating }}" data-setting="vp_rating" style="display:none;" />
+ </label>
+
+ <label class="setting vp-setting">
+ <span><?php _e( 'Shortcode', 'jetpack' ); ?></span>
+ <input type="text" value="[wpvideo {{ data.vp_guid }}]" onclick="this.focus();this.select();" readonly />
+ </label>
+
+ <label class="setting vp-setting vp-preview">
+ <span><?php _e( 'Preview', 'jetpack' ); ?></span>
+ <# if ( ! data.vp_thumbnail_url ) { #>
+ <span class="videopress-preview-unavailable"><?php esc_html_e( 'The preview is unavailable while this video is being processed.', 'jetpack' ); ?></span>
+ <# } else { #>
+ <a href="#" class="videopress-preview" id="videopress-thumbnail-{{ data.vp_guid }}" data-videopress-guid="{{ data.vp_guid }}"><img src="{{ data.vp_thumbnail_url }}" /></a>
+ <# } #>
+ </label>
+ </script>
+
+ <script type="text/html" id="tmpl-videopress-media-modal">
+ <div class="videopress-modal">
+ <p><?php _e( 'Video Preview:', 'jetpack' ); ?></p>
+ <div class="videopress-video-container">{{{ data.video }}}</div>
+ <p class="submit">
+ <a class="videopress-modal-close button" href="#"><?php _e( 'Close', 'jetpack' ); ?></a>
+ </p>
+ </div>
+ <div class="videopress-modal-backdrop"></div>
+ </script>
+
+ <script type="text/html" id="tmpl-videopress-uploader">
+ <div class="videopress-errors"></div>
+ <form class="videopress-upload-form" action="" method="post" target="videopress_upload_frame" enctype="multipart/form-data">
+ <input type="hidden" name="action" value="videopress_upload" />
+ <input type="hidden" name="videopress_blog_id" value="0" />
+ <input type="hidden" name="videopress_token" value="0" />
+ <?php $formats = 'ogv, mp4, m4v, mov, wmv, avi, mpg, 3gp, 3g2'; ?>
+ <?php
+ $max_upload_size = 0;
+ if ( ! empty( $options['meta']['max_upload_size'] ) )
+ $max_upload_size = absint( $options['meta']['max_upload_size'] );
+
+ $upload_size_unit = $max_upload_size;
+ $byte_sizes = array( 'KB', 'MB', 'GB' );
+
+ for ( $u = -1; $upload_size_unit > 1024 && $u < count( $byte_sizes ) - 1; $u++ )
+ $upload_size_unit /= 1024;
+
+ if ( $u < 0 ) {
+ $upload_size_unit = 0;
+ $u = 0;
+ } else {
+ $upload_size_unit = (int) $upload_size_unit;
+ }
+ ?>
+ <p><?php printf( __( 'Use the form below to upload a video to your VideoPress Library. The following video formats are supported: %s. Maximum upload file size is %d%s.', 'jetpack' ), esc_html( $formats ), esc_html( $upload_size_unit ), esc_html( $byte_sizes[ $u ] ) ); ?></p>
+
+ <input type="file" name="videopress_file" />
+ <?php submit_button( __( 'Upload Video', 'jetpack' ) ); ?>
+ </form>
+ <iframe width="0" height="0" name="videopress_upload_frame"></iframe>
+ </script>
+ <?php
+ }
+
+ /**
+ * Filters the VideoPress shortcode options, makes sure that
+ * the settings set in Jetpack's VideoPress module are applied.
+ */
+ function videopress_shortcode_options( $options ) {
+ $videopress_options = $this->get_options();
+
+ if ( false === $options['freedom'] )
+ $options['freedom'] = $videopress_options['freedom'];
+
+ $options['hd'] = $videopress_options['hd'];
+
+ return $options;
+ }
+}
+
+// Initialize the module.
+Jetpack_VideoPress::init();
diff --git a/plugins/jetpack/modules/widget-visibility.php b/plugins/jetpack/modules/widget-visibility.php
new file mode 100644
index 00000000..f96dc61c
--- /dev/null
+++ b/plugins/jetpack/modules/widget-visibility.php
@@ -0,0 +1,11 @@
+<?php
+
+/**
+ * Module Name: Widget Visibility
+ * Module Description: Control what pages your widgets appear on.
+ * First Introduced: 2.4
+ * Requires Connection: No
+ * Auto Activate: Yes
+ */
+
+include dirname( __FILE__ ) . "/widget-visibility/widget-conditions.php";
diff --git a/plugins/jetpack/modules/widget-visibility/widget-conditions.php b/plugins/jetpack/modules/widget-visibility/widget-conditions.php
new file mode 100644
index 00000000..580b708c
--- /dev/null
+++ b/plugins/jetpack/modules/widget-visibility/widget-conditions.php
@@ -0,0 +1,379 @@
+<?php
+
+
+/**
+ * Hide or show widgets conditionally.
+ */
+
+class Jetpack_Widget_Conditions {
+ public static function init() {
+ if ( is_admin() ) {
+ add_action( 'sidebar_admin_setup', array( __CLASS__, 'widget_admin_setup' ) );
+ add_filter( 'widget_update_callback', array( __CLASS__, 'widget_update' ), 10, 3 );
+ add_action( 'in_widget_form', array( __CLASS__, 'widget_conditions_admin' ), 10, 3 );
+ add_action( 'wp_ajax_widget_conditions_options', array( __CLASS__, 'widget_conditions_options' ) );
+ }
+ else {
+ add_action( 'widget_display_callback', array( __CLASS__, 'filter_widget' ) );
+ add_action( 'sidebars_widgets', array( __CLASS__, 'sidebars_widgets' ) );
+ }
+ }
+
+ public static function widget_admin_setup() {
+ wp_enqueue_style( 'widget-conditions', plugins_url( 'widget-conditions/widget-conditions.css', __FILE__ ) );
+ wp_enqueue_script( 'widget-conditions', plugins_url( 'widget-conditions/widget-conditions.js', __FILE__ ), array( 'jquery', 'jquery-ui-core' ), 20130129, true );
+ }
+
+ /**
+ * Provided a second level of granularity for widget conditions.
+ */
+ public static function widget_conditions_options_echo( $major = '', $minor = '' ) {
+ switch ( $major ) {
+ case 'category':
+ ?>
+ <option value=""><?php _e( 'All category pages', 'jetpack' ); ?></option>
+ <?php
+
+ $categories = get_categories( array( 'number' => 1000, 'orderby' => 'count', 'order' => 'DESC' ) );
+ usort( $categories, array( __CLASS__, 'strcasecmp_name' ) );
+
+ foreach ( $categories as $category ) {
+ ?>
+ <option value="<?php echo esc_attr( $category->term_id ); ?>" <?php selected( $category->term_id, $minor ); ?>><?php echo esc_html( $category->name ); ?></option>
+ <?php
+ }
+ break;
+ case 'author':
+ ?>
+ <option value=""><?php _e( 'All author pages', 'jetpack' ); ?></option>
+ <?php
+
+ foreach ( get_users( array( 'orderby' => 'name', 'exclude_admin' => true ) ) as $author ) {
+ ?>
+ <option value="<?php echo esc_attr( $author->ID ); ?>" <?php selected( $author->ID, $minor ); ?>><?php echo esc_html( $author->display_name ); ?></option>
+ <?php
+ }
+ break;
+ case 'tag':
+ ?>
+ <option value=""><?php _e( 'All tag pages', 'jetpack' ); ?></option>
+ <?php
+
+ $tags = get_tags( array( 'number' => 1000, 'orderby' => 'count', 'order' => 'DESC' ) );
+ usort( $tags, array( __CLASS__, 'strcasecmp_name' ) );
+
+ foreach ( $tags as $tag ) {
+ ?>
+ <option value="<?php echo esc_attr($tag->term_id ); ?>" <?php selected( $tag->term_id, $minor ); ?>><?php echo esc_html( $tag->name ); ?></option>
+ <?php
+ }
+ break;
+ case 'date':
+ ?>
+ <option value="" <?php selected( '', $minor ); ?>><?php _e( 'All date archives', 'jetpack' ); ?></option>
+ <option value="day"<?php selected( 'day', $minor ); ?>><?php _e( 'Daily archives', 'jetpack' ); ?></option>
+ <option value="month"<?php selected( 'month', $minor ); ?>><?php _e( 'Monthly archives', 'jetpack' ); ?></option>
+ <option value="year"<?php selected( 'year', $minor ); ?>><?php _e( 'Yearly archives', 'jetpack' ); ?></option>
+ <?php
+ break;
+ case 'page':
+ // Previously hardcoded post type options.
+ if ( ! $minor )
+ $minor = 'post_type-page';
+ else if ( 'post' == $minor )
+ $minor = 'post_type-post';
+
+ ?>
+ <option value="front" <?php selected( 'front', $minor ); ?>><?php _e( 'Front page', 'jetpack' ); ?></option>
+ <option value="posts" <?php selected( 'posts', $minor ); ?>><?php _e( 'Posts page', 'jetpack' ); ?></option>
+ <option value="404" <?php selected( '404', $minor ); ?>><?php _e( '404 error page', 'jetpack' ); ?></option>
+ <option value="search" <?php selected( 'search', $minor ); ?>><?php _e( 'Search results', 'jetpack' ); ?></option>
+ <optgroup label="<?php esc_attr_e( 'Post type:', 'jetpack' ); ?>">
+ <?php
+
+ $post_types = get_post_types( array( 'public' => true ), 'objects' );
+
+ foreach ( $post_types as $post_type ) {
+ ?>
+ <option value="<?php echo esc_attr( 'post_type-' . $post_type->name ); ?>" <?php selected( 'post_type-' . $post_type->name, $minor ); ?>><?php echo esc_html( $post_type->labels->singular_name ); ?></option>
+ <?php
+ }
+
+ ?>
+ </optgroup>
+ <optgroup label="<?php esc_attr_e( 'Static page:', 'jetpack' ); ?>">
+ <?php
+
+ echo str_replace( ' value="' . esc_attr( $minor ) . '"', ' value="' . esc_attr( $minor ) . '" selected="selected"', preg_replace( '/<\/?select[^>]*?>/i', '', wp_dropdown_pages( array( 'echo' => false ) ) ) );
+
+ ?>
+ </optgroup>
+ <?php
+ break;
+ }
+ }
+
+ /**
+ * This is the AJAX endpoint for the second level of conditions.
+ */
+ public static function widget_conditions_options() {
+ self::widget_conditions_options_echo( $_REQUEST['major'], isset( $_REQUEST['minor'] ) ? $_REQUEST['minor'] : '' );
+ die;
+ }
+
+ /**
+ * Add the widget conditions to each widget in the admin.
+ *
+ * @param $widget unused.
+ * @param $return unused.
+ * @param array $instance The widget settings.
+ */
+ public static function widget_conditions_admin( $widget, $return, $instance ) {
+ $conditions = array();
+
+ if ( isset( $instance['conditions'] ) )
+ $conditions = $instance['conditions'];
+
+ if ( ! isset( $conditions['action'] ) )
+ $conditions['action'] = 'show';
+
+ if ( empty( $conditions['rules'] ) )
+ $conditions['rules'][] = array( 'major' => '', 'minor' => '' );
+
+ ?>
+ <div class="widget-conditional <?php if ( empty( $_POST['widget-conditions-visible'] ) || $_POST['widget-conditions-visible'] == '0' ) { ?>widget-conditional-hide<?php } ?>">
+ <input type="hidden" name="widget-conditions-visible" value="<?php if ( isset( $_POST['widget-conditions-visible'] ) ) { echo esc_attr( $_POST['widget-conditions-visible'] ); } else { ?>0<?php } ?>" />
+ <?php if ( ! isset( $_POST['widget-conditions-visible'] ) ) { ?><a href="#" class="button display-options"><?php _e( 'Visibility', 'jetpack' ); ?></a><?php } ?>
+ <div class="widget-conditional-inner">
+ <div class="condition-top">
+ <?php printf( _x( '%s if:', 'placeholder: dropdown menu to select widget visibility; hide if or show if', 'jetpack' ), '<select name="conditions[action]"><option value="show" ' . selected( $conditions['action'], 'show', false ) . '>' . esc_html_x( 'Show', 'Used in the "%s if:" translation for the widget visibility dropdown', 'jetpack' ) . '</option><option value="hide" ' . selected( $conditions['action'], 'hide', false ) . '>' . esc_html_x( 'Hide', 'Used in the "%s if:" translation for the widget visibility dropdown', 'jetpack' ) . '</option></select>' ); ?>
+ </div><!-- .condition-top -->
+
+ <div class="conditions">
+ <?php
+
+ foreach ( $conditions['rules'] as $rule ) {
+ ?>
+ <div class="condition">
+ <div class="alignleft">
+ <select class="conditions-rule-major" name="conditions[rules_major][]">
+ <option value="" <?php selected( "", $rule['major'] ); ?>><?php echo esc_html_x( '-- Select --', 'Used as the default option in a dropdown list', 'jetpack' ); ?></option>
+ <option value="category" <?php selected( "category", $rule['major'] ); ?>><?php esc_html_e( 'Category', 'jetpack' ); ?></option>
+ <option value="author" <?php selected( "author", $rule['major'] ); ?>><?php echo esc_html_x( 'Author', 'Noun, as in: "The author of this post is..."', 'jetpack' ); ?></option>
+ <option value="tag" <?php selected( "tag", $rule['major'] ); ?>><?php echo esc_html_x( 'Tag', 'Noun, as in: "This post has one tag."', 'jetpack' ); ?></option>
+ <option value="date" <?php selected( "date", $rule['major'] ); ?>><?php echo esc_html_x( 'Date', 'Noun, as in: "This page is a date archive."', 'jetpack' ); ?></option>
+ <option value="page" <?php selected( "page", $rule['major'] ); ?>><?php echo esc_html_x( 'Page', 'Example: The user is looking at a page, not a post.', 'jetpack' ); ?></option>
+ </select>
+ <?php _ex( 'is', 'Widget Visibility: {Rule Major [Page]} is {Rule Minor [Search results]}', 'jetpack' ); ?>
+ <select class="conditions-rule-minor" name="conditions[rules_minor][]" <?php if ( ! $rule['major'] ) { ?> disabled="disabled"<?php } ?> data-loading-text="<?php esc_attr_e( 'Loading...', 'jetpack' ); ?>">
+ <?php self::widget_conditions_options_echo( $rule['major'], $rule['minor'] ); ?>
+ </select>
+ <span class="condition-conjunction"><?php echo esc_html_x( 'or', 'Shown between widget visibility conditions.', 'jetpack' ); ?></span>
+ </div>
+ <div class="condition-control alignright">
+ <a href="#" class="delete-condition"><?php esc_html_e( 'Delete', 'jetpack' ); ?></a> | <a href="#" class="add-condition"><?php esc_html_e( 'Add', 'jetpack' ); ?></a>
+ </div>
+ <br class="clear" />
+ </div><!-- .condition -->
+ <?php
+ }
+
+ ?>
+ </div><!-- .conditions -->
+ </div><!-- .widget-conditional-inner -->
+ </div><!-- .widget-conditional -->
+ <?php
+ }
+
+ /**
+ * On an AJAX update of the widget settings, process the display conditions.
+ *
+ * @param array $new_instance New settings for this instance as input by the user.
+ * @param array $old_instance Old settings for this instance.
+ * @return array Modified settings.
+ */
+ public static function widget_update( $instance, $new_instance, $old_instance ) {
+ $conditions = array();
+ $conditions['action'] = $_POST['conditions']['action'];
+ $conditions['rules'] = array();
+
+ foreach ( $_POST['conditions']['rules_major'] as $index => $major_rule ) {
+ if ( ! $major_rule )
+ continue;
+
+ $conditions['rules'][] = array(
+ 'major' => $major_rule,
+ 'minor' => isset( $_POST['conditions']['rules_minor'][$index] ) ? $_POST['conditions']['rules_minor'][$index] : ''
+ );
+ }
+
+ if ( ! empty( $conditions['rules'] ) )
+ $instance['conditions'] = $conditions;
+ else
+ unset( $instance['conditions'] );
+
+ if (
+ ( isset( $instance['conditions'] ) && ! isset( $old_instance['conditions'] ) )
+ ||
+ (
+ isset( $instance['conditions'], $old_instance['conditions'] )
+ &&
+ serialize( $instance['conditions'] ) != serialize( $old_instance['conditions'] )
+ )
+ ) {
+ do_action( 'widget_conditions_save' );
+ }
+ else if ( ! isset( $instance['conditions'] ) && isset( $old_instance['conditions'] ) ) {
+ do_action( 'widget_conditions_delete' );
+ }
+
+ return $instance;
+ }
+
+ /**
+ * Filter the list of widgets for a sidebar so that active sidebars work as expected.
+ *
+ * @param array $widget_areas An array of widget areas and their widgets.
+ * @return array The modified $widget_area array.
+ */
+ public static function sidebars_widgets( $widget_areas ) {
+ $settings = array();
+
+ foreach ( $widget_areas as $widget_area => $widgets ) {
+ if ( empty( $widgets ) )
+ continue;
+
+ if ( 'wp_inactive_widgets' == $widget_area )
+ continue;
+
+ foreach ( $widgets as $position => $widget_id ) {
+ // Find the conditions for this widget.
+ list( $basename, $suffix ) = explode( "-", $widget_id, 2 );
+
+ if ( ! isset( $settings[$basename] ) )
+ $settings[$basename] = get_option( 'widget_' . $basename );
+
+ if ( isset( $settings[$basename][$suffix] ) ) {
+ if ( false === self::filter_widget( $settings[$basename][$suffix] ) )
+ unset( $widget_areas[$widget_area][$position] );
+ }
+ }
+ }
+
+ return $widget_areas;
+ }
+
+ /**
+ * Determine whether the widget should be displayed based on conditions set by the user.
+ *
+ * @param array $instance The widget settings.
+ * @return array Settings to display or bool false to hide.
+ */
+ public static function filter_widget( $instance ) {
+ global $post, $wp_query;
+
+ if ( empty( $instance['conditions'] ) || empty( $instance['conditions']['rules'] ) )
+ return $instance;
+
+ $condition_result = false;
+
+ foreach ( $instance['conditions']['rules'] as $rule ) {
+ switch ( $rule['major'] ) {
+ case 'date':
+ switch ( $rule['minor'] ) {
+ case '':
+ $condition_result = is_date();
+ break;
+ case 'month':
+ $condition_result = is_month();
+ break;
+ case 'day':
+ $condition_result = is_day();
+ break;
+ case 'year':
+ $condition_result = is_year();
+ break;
+ }
+ break;
+ case 'page':
+ // Previously hardcoded post type options.
+ if ( 'post' == $rule['minor'] )
+ $rule['minor'] = 'post_type-post';
+ else if ( ! $rule['minor'] )
+ $rule['minor'] = 'post_type-page';
+
+ switch ( $rule['minor'] ) {
+ case '404':
+ $condition_result = is_404();
+ break;
+ case 'search':
+ $condition_result = is_search();
+ break;
+ case 'archive':
+ $condition_result = is_archive();
+ break;
+ case 'posts':
+ $condition_result = $wp_query->is_posts_page;
+ break;
+ case 'home':
+ $condition_result = is_home();
+ break;
+ case 'front':
+ $condition_result = is_front_page();
+ break;
+ default:
+ if ( substr( $rule['minor'], 0, 10 ) == 'post_type-' )
+ $condition_result = is_singular( substr( $rule['minor'], 10 ) );
+ else {
+ // $rule['minor'] is a page ID
+ $condition_result = is_page( $rule['minor'] );
+ }
+ break;
+ }
+ break;
+ case 'tag':
+ if ( ! $rule['minor'] && is_tag() )
+ $condition_result = true;
+ else if ( is_singular() && $rule['minor'] && has_tag( $rule['minor'] ) )
+ $condition_result = true;
+ else {
+ $tag = get_tag( $rule['minor'] );
+
+ if ( $tag && is_tag( $tag->slug ) )
+ $condition_result = true;
+ }
+ break;
+ case 'category':
+ if ( ! $rule['minor'] && is_category() )
+ $condition_result = true;
+ else if ( is_category( $rule['minor'] ) )
+ $condition_result = true;
+ else if ( is_singular() && $rule['minor'] && has_category( $rule['minor'] ) )
+ $condition_result = true;
+ break;
+ case 'author':
+ if ( ! $rule['minor'] && is_author() )
+ $condition_result = true;
+ else if ( $rule['minor'] && is_author( $rule['minor'] ) )
+ $condition_result = true;
+ else if ( is_singular() && $rule['minor'] && $rule['minor'] == $post->post_author )
+ $condition_result = true;
+ break;
+ }
+
+ if ( $condition_result )
+ break;
+ }
+
+ if ( ( 'show' == $instance['conditions']['action'] && ! $condition_result ) || ( 'hide' == $instance['conditions']['action'] && $condition_result ) )
+ return false;
+
+ return $instance;
+ }
+
+ public static function strcasecmp_name( $a, $b ) {
+ return strcasecmp( $a->name, $b->name );
+ }
+}
+
+add_action( 'init', array( 'Jetpack_Widget_Conditions', 'init' ) );
diff --git a/plugins/jetpack/modules/widget-visibility/widget-conditions/rtl/widget-conditions-rtl.css b/plugins/jetpack/modules/widget-visibility/widget-conditions/rtl/widget-conditions-rtl.css
new file mode 100644
index 00000000..58b9e149
--- /dev/null
+++ b/plugins/jetpack/modules/widget-visibility/widget-conditions/rtl/widget-conditions-rtl.css
@@ -0,0 +1,41 @@
+/* This file was automatically generated on Mar 14 2013 17:20:54 */
+
+.widget-liquid-right .widget.expanded {
+ overflow: visible;
+}
+.widget-conditional-hide {
+ display: none;
+}
+.widget-conditional .widget-conditional-inner {
+ background: #F9F9F9;
+ border: 1px solid #DFDFDF;
+ padding: 12px 12px 0;
+}
+.widget-conditional .condition,
+.widget-conditional .condition-top {
+ margin-bottom: 12px;
+}
+.widget-conditional .condition select {
+ width: 120px;
+}
+.widget-conditional .condition-top select {
+ width: auto;
+}
+.widget-conditional .condition .alignright {
+ padding-top: 4px;
+}
+.widget-conditional .condition-control a {
+ text-decoration: none;
+}
+.widget-conditional .condition:last-child .condition-conjunction {
+ display: none;
+}
+.widget-control-actions .alignright {
+ text-align: left;
+}
+.wp-core-ui .button.display-options {
+ margin-left: 5px;
+}
+.wp-core-ui .button.display-options:hover {
+ text-decoration: none;
+} \ No newline at end of file
diff --git a/plugins/jetpack/modules/widget-visibility/widget-conditions/widget-conditions.css b/plugins/jetpack/modules/widget-visibility/widget-conditions/widget-conditions.css
new file mode 100644
index 00000000..bdd41f66
--- /dev/null
+++ b/plugins/jetpack/modules/widget-visibility/widget-conditions/widget-conditions.css
@@ -0,0 +1,39 @@
+.widget-liquid-right .widget.expanded {
+ overflow: visible;
+}
+.widget-conditional-hide {
+ display: none;
+}
+.widget-conditional .widget-conditional-inner {
+ background: #F9F9F9;
+ border: 1px solid #DFDFDF;
+ padding: 12px 12px 0;
+}
+.widget-conditional .condition,
+.widget-conditional .condition-top {
+ margin-bottom: 12px;
+}
+.widget-conditional .condition select {
+ width: 120px;
+}
+.widget-conditional .condition-top select {
+ width: auto;
+}
+.widget-conditional .condition .alignright {
+ padding-top: 4px;
+}
+.widget-conditional .condition-control a {
+ text-decoration: none;
+}
+.widget-conditional .condition:last-child .condition-conjunction {
+ display: none;
+}
+.widget-control-actions .alignright {
+ text-align: right;
+}
+.wp-core-ui .button.display-options {
+ margin-right: 5px;
+}
+.wp-core-ui .button.display-options:hover {
+ text-decoration: none;
+} \ No newline at end of file
diff --git a/plugins/jetpack/modules/widget-visibility/widget-conditions/widget-conditions.js b/plugins/jetpack/modules/widget-visibility/widget-conditions/widget-conditions.js
new file mode 100644
index 00000000..0458d663
--- /dev/null
+++ b/plugins/jetpack/modules/widget-visibility/widget-conditions/widget-conditions.js
@@ -0,0 +1,112 @@
+jQuery( function( $ ) {
+ function setWidgetMargin( $widget ) {
+ if ( $widget.hasClass( 'expanded' ) ) {
+ // The expanded widget must be at least 400px wide in order to
+ // contain the visibility settings. IE wasn't handling the
+ // margin-left value properly.
+
+ if ( $widget.attr( 'style' ) )
+ $widget.data( 'original-style', $widget.attr( 'style' ) );
+
+ var currentWidth = $widget.width();
+
+ if ( currentWidth < 400 ) {
+ var extra = 400 - currentWidth;
+ $widget.css( 'position', 'relative' ).css( 'left', '-' + extra + 'px' ).css( 'width', '400px' );
+ }
+ }
+ else if ( $widget.data( 'original-style' ) ) {
+ // Restore any original inline styles when visibility is toggled off.
+ $widget.attr( 'style', $widget.data( 'original-style' ) ).data( 'original-style', null );
+ }
+ else {
+ $widget.removeAttr( 'style' );
+ }
+ }
+
+ $( "a.display-options" ).each( function() {
+ var $displayOptionsButton = $( this ),
+ $widget = $displayOptionsButton.closest( "div.widget" );
+ $displayOptionsButton.insertBefore( $widget.find( "input.widget-control-save" ) );
+
+ // Widgets with no configurable options don't show the Save button's container.
+ $displayOptionsButton
+ .parent()
+ .removeClass( 'widget-control-noform' )
+ .find( '.spinner' )
+ .remove()
+ .css( 'float', 'left' )
+ .prependTo( $displayOptionsButton.parent() );
+
+ } );
+
+ $( "div#widgets-right" ).on( "click", "a.add-condition", function( e ) {
+ e.preventDefault();
+ var $condition = $( this ).closest( "div.condition" ),
+ $conditionClone = $condition.clone().insertAfter( $condition );
+ $conditionClone.find( "select.conditions-rule-major" ).val( "" );
+ $conditionClone.find( "select.conditions-rule-minor" ).html( "" ).attr( "disabled" );
+ } ).on( "click", "a.display-options", function ( e ) {
+ e.preventDefault();
+
+ var $displayOptionsButton = $( this ),
+ $widget = $displayOptionsButton.closest( "div.widget" );
+
+ $widget.find( "div.widget-conditional" ).toggleClass( "widget-conditional-hide" );
+ $( this ).toggleClass( "active" );
+ $widget.toggleClass( "expanded" );
+ setWidgetMargin( $widget );
+
+ if ( $( this ).hasClass( 'active' ) )
+ $widget.find( 'input[name=widget-conditions-visible]' ).val( '1' );
+ else
+ $widget.find( 'input[name=widget-conditions-visible]' ).val( '0' );
+
+ } );
+
+ $( "div#widgets-right" ).on( "click", "a.delete-condition", function( e ) {
+ e.preventDefault();
+
+ var $condition = $( this ).closest( "div.condition" );
+
+ if ( $condition.is( ":first-child" ) && $condition.is( ":last-child" ) ) {
+ $( this ).closest( "div.widget" ).find( "a.display-options" ).click();
+ $condition.find( "select.conditions-rule-major" ).val( "" ).change();
+ } else {
+ $condition.detach();
+ }
+ } ).on( "click", "div.widget-top", function() {
+ var $widget = $( this ).closest( "div.widget" ),
+ $displayOptionsButton = $widget.find( "a.display-options" );
+
+ if ( $displayOptionsButton.hasClass( "active" ) ) {
+ $displayOptionsButton.attr( "opened", "true" );
+ }
+
+ if ( $displayOptionsButton.attr( "opened" ) ) {
+ $displayOptionsButton.removeAttr( "opened" );
+ $widget.toggleClass( "expanded" );
+ setWidgetMargin( $widget );
+ }
+ } );
+
+ $( document ).on( "change", "select.conditions-rule-major", function() {
+ var $conditionsRuleMajor = $ ( this );
+ var $conditionsRuleMinor = $conditionsRuleMajor.siblings( "select.conditions-rule-minor:first" );
+
+ if ( $conditionsRuleMajor.val() ) {
+ $conditionsRuleMinor.html( '' ).append( $( '<option/>' ).text( $conditionsRuleMinor.data( 'loading-text' ) ) );
+
+ var data = {
+ action: 'widget_conditions_options',
+ major: $conditionsRuleMajor.val()
+ };
+
+ jQuery.post( ajaxurl, data, function( html ) {
+ $conditionsRuleMinor.html( html ).removeAttr( "disabled" );
+ } );
+ } else {
+ $conditionsRuleMajor.siblings( "select.conditions-rule-minor" ).attr( "disabled", "disabled" ).html( "" );
+ }
+ } );
+} );
diff --git a/plugins/jetpack/modules/widgets.php b/plugins/jetpack/modules/widgets.php
index 4d0f509d..c2100ccd 100644
--- a/plugins/jetpack/modules/widgets.php
+++ b/plugins/jetpack/modules/widgets.php
@@ -5,6 +5,7 @@
* Sort Order: 13
* First Introduced: 1.2
* Requires Connection: No
+ * Auto Activate: Yes
*/
function jetpack_load_widgets() {
diff --git a/plugins/jetpack/modules/widgets/gallery.php b/plugins/jetpack/modules/widgets/gallery.php
new file mode 100644
index 00000000..0d33f5c5
--- /dev/null
+++ b/plugins/jetpack/modules/widgets/gallery.php
@@ -0,0 +1,392 @@
+<?php
+
+/*
+Plugin Name: Gallery
+Description: Gallery widget
+Author: Automattic, Inc.
+Version: 1.0
+Author URI: http://automattic.com
+*/
+
+class Jetpack_Gallery_Widget extends WP_Widget {
+ const THUMB_SIZE = 45;
+ const DEFAULT_WIDTH = 265;
+
+ protected $_instance_width ;
+
+ public function __construct() {
+ $widget_ops = array(
+ 'classname' => 'widget-gallery',
+ 'description' => __( 'Display a photo gallery or slideshow', 'jetpack' )
+ );
+ $control_ops = array( 'width' => 250 );
+
+ add_action( 'admin_init', array( $this, 'admin_init' ) );
+
+ $this->WP_Widget( 'gallery', apply_filters( 'jetpack_widget_name', __( 'Gallery', 'jetpack' ) ), $widget_ops, $control_ops );
+ }
+
+ /**
+ * @param array $args Display arguments including before_title, after_title, before_widget, and after_widget.
+ * @param array $instance The settings for the particular instance of the widget
+ */
+ public function widget( $args, $instance ) {
+ $this->enqueue_frontend_scripts();
+
+ extract( $args );
+
+ $instance['attachments'] = $this->get_attachments( $instance );
+
+ $classes = array();
+
+ $classes[] = 'widget-gallery-' . $instance['type'];
+
+ // Due to a bug in the carousel plugin, carousels will be triggered for all tiled galleries that exist on a page
+ // with other tiled galleries, regardless of whether or not the widget was set to Carousel mode. The onClick selector
+ // is simply too broad, since it was not written with widgets in mind. This special class prevents that behavior, via
+ // an override handler in gallery.js
+ if( 'carousel' != $instance['link'] && 'slideshow' != $instance['type'] )
+ $classes[] = 'no-carousel';
+ else
+ $classes[] = 'carousel';
+
+ $classes = implode( ' ', $classes );
+
+ if ( 'carousel' == $instance['link'] ) {
+ require_once plugin_dir_path( realpath( dirname( __FILE__ ) . '/../carousel/jetpack-carousel.php' ) ) . 'jetpack-carousel.php';
+
+ if ( class_exists( 'Jetpack_Carousel' ) ) {
+ // Create new carousel so we can use the enqueue_assets() method. Not ideal, but there is a decent amount
+ // of logic in that method that shouldn't be duplicated.
+ $carousel = new Jetpack_Carousel();
+
+ // First parameter is $output, which comes from filters, and causes bypass of the asset enqueuing. Passing null is correct.
+ $carousel->enqueue_assets( null );
+ }
+ }
+
+ echo $before_widget . "\n";
+
+ $title = apply_filters( 'widget_title', $instance['title'] );
+
+ if ( $title )
+ echo $before_title . esc_html( $title ) . $after_title . "\n";
+
+ echo '<div class="' . esc_attr( $classes ) . '">' . "\n";
+
+ $method = $instance['type'] . '_widget';
+
+ // Allow the width of a gallery to be altered by themes or other code
+ $this->_instance_width = apply_filters( 'gallery_widget_content_width', self::DEFAULT_WIDTH, $args, $instance );
+
+ // Register a filter to modify the tiled_gallery_content_width, so Jetpack_Tiled_Gallery
+ // can appropriately size the tiles.
+ add_filter( 'tiled_gallery_content_width', array( $this, 'tiled_gallery_content_width' ) );
+
+ if ( method_exists( $this, $method ) )
+ echo $this->$method( $args, $instance );
+
+ // Remove the stored $_instance_width, as it is no longer needed
+ $this->_instance_width = null;
+
+ // Remove the filter, so any Jetpack_Tiled_Gallery in a post is not affected
+ remove_filter( 'tiled_gallery_content_width', array( $this, 'tiled_gallery_content_width' ) );
+
+ echo "\n" . '</div>'; // .widget-gallery-$type
+
+ echo "\n" . $after_widget;
+ }
+
+ /**
+ * Fetch the images attached to the gallery Widget
+ *
+ * @param array $instance The Widget instance for which you'd like attachments
+ * @return array Array of attachment objects for the Widget in $instance
+ */
+ public function get_attachments( $instance ){
+ $ids = explode( ',', $instance['ids'] );
+
+ $order = ( isset( $instance['random'] ) && $instance['random'] ) ? 'rand' : 'post__in';
+
+ $attachments_query = new WP_Query( array(
+ 'post__in' => $ids,
+ 'post_status' => 'inherit',
+ 'post_type' => 'attachment',
+ 'post_mime_type' => 'image',
+ 'posts_per_page' => -1,
+ 'orderby' => $order
+ ) );
+
+ $attachments = $attachments_query->get_posts();
+
+ wp_reset_postdata();
+
+ return $attachments;
+ }
+
+ /**
+ * Generate HTML for a rectangular, tiled Widget
+ *
+ * @param array $args Display arguments including before_title, after_title, before_widget, and after_widget.
+ * @param array $instance The Widget instance to generate HTML for
+ * @return string String of HTML representing a rectangular gallery
+ */
+ public function rectangular_widget( $args, $instance ) {
+ if ( ! class_exists( 'Jetpack_Tiled_Gallery' ) )
+ return;
+
+ $widget_tiled_gallery = new Jetpack_Tiled_Gallery();
+
+ $widget_tiled_gallery->set_atts( array(
+ 'link' => $instance['link'],
+ ) );
+
+ $widget_tiled_gallery->default_scripts_and_styles();
+
+ $html = $widget_tiled_gallery->rectangular_talavera( $instance['attachments'] );
+
+ return $html;
+ }
+
+ /**
+ * Generate HTML for a square (grid style) Widget
+ *
+ * @param array $args Display arguments including before_title, after_title, before_widget, and after_widget.
+ * @param array $instance The Widget instance to generate HTML for
+ * @return string String of HTML representing a square gallery
+ */
+ public function square_widget( $args, $instance ) {
+ if ( ! class_exists( 'Jetpack_Tiled_Gallery' ) )
+ return;
+
+ $widget_tiled_gallery = new Jetpack_Tiled_Gallery();
+
+ $widget_tiled_gallery->set_atts( array(
+ 'link' => $instance['link'],
+ //'columns' => $instance['columns']
+ ) );
+
+ $widget_tiled_gallery->default_scripts_and_styles();
+
+ $html = $widget_tiled_gallery->circle_talavera( $instance['attachments'] );
+
+ return $html;
+ }
+
+ /**
+ * Generate HTML for a circular (grid style) Widget
+ *
+ * @param array $args Display arguments including before_title, after_title, before_widget, and after_widget.
+ * @param array $instance The Widget instance to generate HTML for
+ * @return string String of HTML representing a circular gallery
+ */
+ public function circle_widget( $args, $instance ) {
+ if ( ! class_exists( 'Jetpack_Tiled_Gallery' ) )
+ return;
+
+ $widget_tiled_gallery = new Jetpack_Tiled_Gallery();
+
+ // Tell the Tiled_Gallery what we want the images to link to
+ $widget_tiled_gallery->set_atts( array(
+ 'link' => $instance['link'],
+ //'columns' => $instance['columns'],
+ 'type' => 'circle'
+ ) );
+
+ $widget_tiled_gallery->default_scripts_and_styles();
+
+ $html = $widget_tiled_gallery->circle_talavera( $instance['attachments'] );
+
+ return $html;
+ }
+
+ /**
+ * Generate HTML for a slideshow Widget
+ *
+ * @param array $args Display arguments including before_title, after_title, before_widget, and after_widget.
+ * @param array $instance The Widget instance to generate HTML for
+ * @return string String of HTML representing a slideshow gallery
+ */
+ public function slideshow_widget( $args, $instance ) {
+ global $content_width;
+
+ require_once plugin_dir_path( realpath( dirname( __FILE__ ) . '/../shortcodes/slideshow.php' ) ) . 'slideshow.php';
+
+ if ( ! class_exists( 'Jetpack_Slideshow_Shortcode' ) )
+ return;
+
+ $slideshow = new Jetpack_Slideshow_Shortcode();
+
+ $slideshow->enqueue_scripts();
+
+ $gallery_instance = "widget-" . $args['widget_id'];
+
+ $gallery = array();
+
+ foreach ( $instance['attachments'] as $attachment ) {
+ $attachment_image_src = wp_get_attachment_image_src( $attachment->ID, 'full' );
+ $attachment_image_src = $attachment_image_src[0]; // [url, width, height]
+
+ $caption = wptexturize( strip_tags( $attachment->post_excerpt ) );
+
+ $gallery[] = (object) array(
+ 'src' => (string) esc_url_raw( $attachment_image_src ),
+ 'id' => (string) $attachment->ID,
+ 'caption' => (string) $caption,
+ );
+ }
+
+ $max_width = intval( get_option( 'large_size_w' ) );
+ $max_height = 175;
+
+ if ( intval( $content_width ) > 0 )
+ $max_width = min( intval( $content_width ), $max_width );
+
+ $js_attr = array(
+ 'gallery' => $gallery,
+ 'selector' => $gallery_instance,
+ 'width' => $max_width,
+ 'height' => $max_height,
+ 'trans' => 'fade',
+ );
+
+ $html = $slideshow->slideshow_js( $js_attr );
+
+ return $html;
+ }
+
+ /**
+ * tiled_gallery_content_width filter
+ *
+ * Used to adjust the content width of Jetpack_Tiled_Gallery's in sidebars
+ *
+ * $this->_instance_width is filtered in widget() and this filter is added then removed in widget()
+ *
+ * @param int $width int The original width value
+ * @return int The filtered width
+ */
+ public function tiled_gallery_content_width( $width ) {
+ return $this->_instance_width;
+ }
+
+ public function form( $instance ) {
+ $defaults = $this->defaults();
+ $allowed_values = $this->allowed_values();
+
+ $instance = wp_parse_args( (array) $instance, $defaults );
+
+ include dirname( __FILE__ ) . '/gallery/templates/form.php';
+ }
+
+ public function update( $new_instance, $old_instance ) {
+ $instance = $this->sanitize( $new_instance );
+
+ return $instance;
+ }
+
+ /**
+ * Sanitize the $instance's values to the set of allowed values. If a value is not acceptable,
+ * it is set to its default.
+ *
+ * Helps keep things nice and secure by whitelisting only allowed values
+ *
+ * @param array $instance The Widget instance to sanitize values for
+ * @return array $instance The Widget instance with values sanitized
+ */
+ public function sanitize( $instance ) {
+ $allowed_values = $this->allowed_values();
+ $defaults = $this->defaults();
+
+ foreach ( $instance as $key => $value ) {
+ $value = trim( $value );
+
+ if ( isset( $allowed_values[ $key ] ) && $allowed_values[ $key ] && ! array_key_exists( $value, $allowed_values[ $key ] ) )
+ $instance[ $key ] = $defaults[ $key ];
+ }
+
+ return $instance;
+ }
+
+ /**
+ * Return a multi-dimensional array of allowed values (and their labels) for all widget form
+ * elements
+ *
+ * To allow all values on an input, omit it from the returned array
+ *
+ * @return array Array of allowed values for each option
+ */
+ public function allowed_values() {
+ $max_columns = 5;
+
+ // Create an associative array of allowed column values. This just automates the generation of
+ // column <option>s, from 1 to $max_columns
+ $allowed_columns = array_combine( range( 1, $max_columns ), range( 1, $max_columns ) );
+
+ return array(
+ 'type' => array(
+ 'rectangular' => __( 'Tiles', 'jetpack' ),
+ 'square' => __( 'Square Tiles', 'jetpack' ),
+ 'circle' => __( 'Circles', 'jetpack' ),
+ 'slideshow' => __( 'Slideshow', 'jetpack' ),
+ ),
+ 'columns' => $allowed_columns,
+ 'link' => array(
+ 'carousel' => __( 'Carousel', 'jetpack' ),
+ 'post' => __( 'Attachment Page', 'jetpack' ),
+ 'file' => __( 'Media File', 'jetpack' ),
+ )
+ );
+ }
+
+ /**
+ * Return an associative array of default values
+ *
+ * These values are used in new widgets as well as when sanitizing input. If a given value is not allowed,
+ * as defined in allowed_values(), that input is set to the default value defined here.
+ *
+ * @return array Array of default values for the Widget's options
+ */
+ public function defaults() {
+ return array(
+ 'title' => '',
+ 'type' => 'rectangular',
+ 'ids' => '',
+ 'columns' => 3,
+ 'link' => 'carousel'
+ );
+ }
+
+ public function enqueue_frontend_scripts() {
+ wp_register_script( 'gallery-widget', plugins_url( '/gallery/js/gallery.js', __FILE__ ) );
+
+ wp_enqueue_script( 'gallery-widget' );
+ }
+
+ public function admin_init() {
+ global $pagenow;
+
+ if ( 'widgets.php' == $pagenow ) {
+ wp_enqueue_media();
+
+ wp_enqueue_script( 'gallery-widget-admin', plugins_url( '/gallery/js/admin.js', __FILE__ ), array(
+ 'media-models',
+ 'media-views'
+ ) );
+
+ $js_settings = array(
+ 'thumbSize' => self::THUMB_SIZE
+ );
+
+ wp_localize_script( 'gallery-widget-admin', '_wpGalleryWidgetAdminSettings', $js_settings );
+
+ wp_enqueue_style( 'gallery-widget-admin', plugins_url( '/gallery/css/admin.css', __FILE__ ) );
+ }
+ }
+}
+
+add_action( 'widgets_init', 'jetpack_gallery_widget_init' );
+
+function jetpack_gallery_widget_init() {
+ if ( ! method_exists( 'Jetpack', 'is_module_active' ) || Jetpack::is_module_active( 'tiled-gallery' ) )
+ register_widget( 'Jetpack_Gallery_Widget' );
+}
diff --git a/plugins/jetpack/modules/widgets/gallery/css/admin.css b/plugins/jetpack/modules/widgets/gallery/css/admin.css
new file mode 100644
index 00000000..c3d96d80
--- /dev/null
+++ b/plugins/jetpack/modules/widgets/gallery/css/admin.css
@@ -0,0 +1,11 @@
+.gallery-widget-thumbs-wrapper {
+ margin: -5px 0 0.3em 0;
+}
+
+.gallery-widget-thumbs img {
+ border: 1px solid #ccc;
+ padding: 2px;
+ background-color: #fff;
+ margin: 0 5px 5px 0;
+ float: left;
+} \ No newline at end of file
diff --git a/plugins/jetpack/modules/widgets/gallery/css/rtl/admin-rtl.css b/plugins/jetpack/modules/widgets/gallery/css/rtl/admin-rtl.css
new file mode 100644
index 00000000..e0e4b615
--- /dev/null
+++ b/plugins/jetpack/modules/widgets/gallery/css/rtl/admin-rtl.css
@@ -0,0 +1,13 @@
+/* This file was automatically generated on Mar 22 2013 21:33:14 */
+
+.gallery-widget-thumbs-wrapper {
+ margin: -5px 0 0.3em 0;
+}
+
+.gallery-widget-thumbs img {
+ border: 1px solid #ccc;
+ padding: 2px;
+ background-color: #fff;
+ margin: 0 0 5px 5px;
+ float: right;
+} \ No newline at end of file
diff --git a/plugins/jetpack/modules/widgets/gallery/js/admin.js b/plugins/jetpack/modules/widgets/gallery/js/admin.js
new file mode 100644
index 00000000..58afb347
--- /dev/null
+++ b/plugins/jetpack/modules/widgets/gallery/js/admin.js
@@ -0,0 +1,207 @@
+(function($){
+ var $ids;
+ var $thumbs;
+
+ $(function(){
+ $( '.widgets-holder-wrap, .editwidget' ).on( 'click', '.gallery-widget-choose-images', function( event ) {
+ event.preventDefault();
+
+ var widget_form = $( this ).closest( 'form' );
+
+ $ids = widget_form.find( '.gallery-widget-ids' );
+ $thumbs = widget_form.find( '.gallery-widget-thumbs' );
+
+ var idsString = $ids.val();
+
+ var attachments = getAttachments( idsString );
+
+ var selection = null;
+ var editing = false;
+
+ if ( attachments ) {
+ var selection = getSelection( attachments );
+
+ editing = true;
+ }
+
+ var options = {
+ state: 'gallery-edit',
+ title: wp.media.view.l10n.addMedia,
+ multiple: true,
+ editing: editing,
+ selection: selection
+ };
+
+ var workflow = getWorkflow( options );
+
+ workflow.open();
+ });
+
+ // Setup an onchange handler to toggle various options when changing style. The different style options
+ // require different form inputs to be presented in the widget; this event will keep the UI in sync
+ // with the selected style
+ $( ".widget-inside" ).on( 'change', '.gallery-widget-style', setupStyleOptions);
+
+ // Setup the Link To options for all forms currently on the page. Does the same as the onChange handler, but
+ // is called once to display the correct form inputs for each widget on the page
+ setupStyleOptions();
+ });
+
+ var media = wp.media,
+ Attachment = media.model.Attachment,
+ Attachments = media.model.Attachments,
+ Query = media.model.Query,
+ l10n;
+
+ // Link any localized strings.
+ l10n = media.view.l10n = typeof _wpMediaViewsL10n === 'undefined' ? {} : _wpMediaViewsL10n;
+
+ /**
+ * wp.media.view.MediaFrame.GalleryWidget
+ *
+ * This behavior can be very nearly had by setting the workflow's state to 'gallery-edit', but
+ * we cannot use the custom WidgetGalleryEdit controller with it (must overide createStates(),
+ * which is necessary to disable the sidebar gallery settings in the media browser)
+ */
+ media.view.MediaFrame.GalleryWidget = media.view.MediaFrame.Post.extend({
+ createStates: function() {
+ var options = this.options;
+
+ this.states.add([
+ new media.controller.WidgetGalleryEdit({
+ library: options.selection,
+ editing: options.editing,
+ menu: 'gallery'
+ }),
+ new media.controller.GalleryAdd({
+
+ })
+ ]);
+ }
+ });
+
+ /**
+ * wp.media.controller.WidgetGalleryEdit
+ *
+ * Removes the gallery settings sidebar when editing widgets...settings are instead handled
+ * via the standard widget interface form
+ *
+ */
+ media.controller.WidgetGalleryEdit = media.controller.GalleryEdit.extend({
+ gallerySettings: function( browser ) {
+ return;
+ }
+ });
+
+ function setupStyleOptions(){
+ $( '.widget-inside .gallery-widget-style' ).each( function( i ){
+ var style = $( this ).val();
+
+ var form = $( this ).parents( 'form' );
+
+ switch ( style ) {
+ case 'slideshow':
+ form.find( '.gallery-widget-link-wrapper' ).hide();
+ form.find( '.gallery-widget-columns-wrapper' ).hide();
+
+ break;
+
+ default:
+ form.find( '.gallery-widget-link-wrapper' ).show();
+ form.find( '.gallery-widget-columns-wrapper' ).show();
+ }
+ });
+ }
+
+ /**
+ * Take a given Selection of attachments and a thumbs wrapper div (jQuery object)
+ * and fill it with thumbnails
+ */
+ function setupThumbs( selection, wrapper ){
+ wrapper.empty();
+
+ var imageSize = _wpGalleryWidgetAdminSettings.thumbSize;
+
+ selection.each( function( model ){
+ var sizedUrl = model.get('url') + '?w=' + imageSize + '&h=' + imageSize + '&crop=true';
+
+ var thumb = '<img src="' + sizedUrl + '" alt="' + model.get('title') + '" \
+ title="' + model.get('title') + '" width="' + imageSize + '" height="' + imageSize + '" class="thumb" />';
+
+ wrapper.append( thumb );
+ });
+ }
+
+ /**
+ * Take a csv string of ids (as stored in db) and fetch a full Attachments collection
+ */
+ function getAttachments( idsString ) {
+ if( ! idsString )
+ return null;
+
+ // Found in /wp-includes/js/media-editor.js
+ var shortcode = wp.shortcode.next( 'gallery', '[gallery ids="' + idsString + '"]' );
+
+ // Ignore the rest of the match object, to give attachments() below what it expects
+ shortcode = shortcode.shortcode;
+
+ var attachments = wp.media.gallery.attachments( shortcode );
+
+ return attachments;
+ }
+
+ /**
+ * Take an Attachments collection and return a corresponding Selection model that can be
+ * passed to a MediaFrame to prepopulate the gallery picker
+ */
+ function getSelection( attachments ) {
+ var selection = new wp.media.model.Selection( attachments.models, {
+ props: attachments.props.toJSON(),
+ multiple: true
+ });
+
+ selection.gallery = attachments.gallery;
+
+ // Fetch the query's attachments, and then break ties from the
+ // query to allow for sorting.
+ selection.more().done( function() {
+ // Break ties with the query.
+ selection.props.set( { query: false } );
+ selection.unmirror();
+ selection.props.unset( 'orderby' );
+ });
+
+ return selection;
+ }
+
+ /**
+ * Create a media 'workflow' (MediaFrame). This is the main entry point for the media picker
+ */
+ function getWorkflow( options ) {
+ var workflow = new wp.media.view.MediaFrame.GalleryWidget( options );
+
+ workflow.on( 'update', function( selection ) {
+ var state = workflow.state();
+
+ selection = selection || state.get( 'selection' );
+
+ if ( ! selection )
+ return;
+
+ // Map the Models down into a simple array of ids that can be easily imploded to a csv string
+ var ids = selection.map( function( model ){
+ return model.get( 'id' );
+ } );
+
+ var id_string = ids.join( ',' );
+
+ $ids.val( id_string );
+
+ setupThumbs( selection, $thumbs );
+ }, this );
+
+ workflow.setState( workflow.options.state );
+
+ return workflow;
+ }
+})(jQuery);
diff --git a/plugins/jetpack/modules/widgets/gallery/js/gallery.js b/plugins/jetpack/modules/widgets/gallery/js/gallery.js
new file mode 100644
index 00000000..b5764f87
--- /dev/null
+++ b/plugins/jetpack/modules/widgets/gallery/js/gallery.js
@@ -0,0 +1,12 @@
+(function($){
+ $(function(){
+ // Fixes a bug with carousels being triggered even when a widget's Link To option is not set to carousel.
+ // Happens when another gallery is loaded on the page, either in a post or separate widget
+ $( '.widget-gallery .no-carousel .tiled-gallery-item a' ).on( 'click', function( event ){
+ // Have to trigger default, instead of carousel
+ event.stopPropagation();
+
+ return true;
+ });
+ });
+})(jQuery); \ No newline at end of file
diff --git a/plugins/jetpack/modules/widgets/gallery/templates/form.php b/plugins/jetpack/modules/widgets/gallery/templates/form.php
new file mode 100644
index 00000000..08833657
--- /dev/null
+++ b/plugins/jetpack/modules/widgets/gallery/templates/form.php
@@ -0,0 +1,89 @@
+<p>
+ <label for="<?php echo $this->get_field_id( 'title' ); ?>"><?php esc_html_e( '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( $instance['title'] ); ?>" />
+ </label>
+</p>
+
+<p>
+ <label>
+ <?php esc_html_e( 'Images:' , 'jetpack' ); ?>
+ </label>
+</p>
+
+<div class="gallery-widget-thumbs-wrapper">
+ <div class="gallery-widget-thumbs">
+ <?php
+
+ // Add the thumbnails to the widget box
+ $attachments = $this->get_attachments( $instance );
+
+ foreach( $attachments as $attachment ){
+ $url = add_query_arg( array(
+ 'w' => self::THUMB_SIZE,
+ 'h' => self::THUMB_SIZE,
+ 'crop' => 'true'
+ ), wp_get_attachment_url( $attachment->ID ) );
+
+ ?>
+
+ <img src="<?php echo esc_url( $url ); ?>" title="<?php echo esc_attr( $attachment->post_title ); ?>" alt="<?php echo esc_attr( $attachment->post_title ); ?>"
+ width="<?php echo self::THUMB_SIZE; ?>" height="<?php echo self::THUMB_SIZE; ?>" class="thumb" />
+ <?php } ?>
+ </div>
+
+ <div style="clear: both;"></div>
+</div>
+
+<p>
+ <a class="button gallery-widget-choose-images" title="Choose Images"><span class="wp-media-buttons-icon"></span> Choose Images</a>
+</p>
+
+<p class="gallery-widget-link-wrapper">
+ <label for="<?php echo $this->get_field_id( 'link' ); ?>"><?php esc_html_e( 'Link To:' , 'jetpack' ); ?></label>
+ <select name="<?php echo $this->get_field_name( 'link' ); ?>" id="<?php echo $this->get_field_id( 'link' ); ?>" class="widefat">
+ <?php foreach ( $allowed_values['link'] as $key => $label ) {
+ $selected = '';
+
+ if ( $instance['link'] == $key ) {
+ $selected = "selected='selected' ";
+ } ?>
+
+ <option value="<?php echo $key; ?>" <?php echo $selected; ?>><?php esc_html_e( $label , 'jetpack' ); ?></option>
+ <?php } ?>
+ </select>
+</p>
+
+<p>
+ <label for="<?php echo $this->get_field_id( 'random' ); ?>"><?php esc_html_e( 'Random Order:' , 'jetpack' ); ?></label>
+ <?php $checked = '';
+
+ if ( isset( $instance['random'] ) && $instance['random'] )
+ $checked = 'checked="checked"';
+
+ ?>
+ <input name="<?php echo $this->get_field_name( 'random' ); ?>" id="<?php echo $this->get_field_id( 'random' ); ?>" type="checkbox" <?php echo $checked; ?>>
+</p>
+
+<p class="gallery-widget-style-wrapper">
+ <label for="<?php echo $this->get_field_id( 'type' ); ?>"><?php esc_html_e( 'Style:' , 'jetpack' ); ?></label>
+ <select name="<?php echo $this->get_field_name( 'type' ); ?>" id="<?php echo $this->get_field_id( 'type' ); ?>" class="widefat gallery-widget-style">
+ <?php foreach ( $allowed_values['type'] as $key => $label ) {
+ $selected = '';
+
+ if ( $instance['type'] == $key ) {
+ $selected = "selected='selected' ";
+ } ?>
+
+ <option value="<?php echo $key; ?>" <?php echo $selected; ?>><?php esc_html_e( $label, 'jetpack' ); ?></option>
+ <?php } ?>
+ </select>
+</p>
+
+<?php
+
+
+?>
+
+<?php // Hidden input to hold the selected image ids as a csv list ?>
+<input type="hidden" class="gallery-widget-ids" name="<?php echo $this->get_field_name( 'ids' ); ?>" id="<?php echo $this->get_field_id( 'ids' ); ?>" value="<?php echo esc_attr( $instance['ids'] ); ?>" />
diff --git a/plugins/jetpack/modules/widgets/top-posts.php b/plugins/jetpack/modules/widgets/top-posts.php
index ec55fe74..252903aa 100644
--- a/plugins/jetpack/modules/widgets/top-posts.php
+++ b/plugins/jetpack/modules/widgets/top-posts.php
@@ -222,7 +222,7 @@ class Jetpack_Top_Posts_Widget extends WP_Widget {
function get_by_views( $count ) {
global $wpdb;
- $post_view_posts = stats_get_csv( 'postviews', array( 'days' => 2, 'limit' => 10 ) );
+ $post_view_posts = stats_get_csv( 'postviews', array( 'days' => 2, 'limit' => 11 ) );
if ( !$post_view_posts ) {
return array();
}
diff --git a/plugins/jetpack/modules/widgets/twitter-timeline.php b/plugins/jetpack/modules/widgets/twitter-timeline.php
index 4ab6fcb4..75235294 100644
--- a/plugins/jetpack/modules/widgets/twitter-timeline.php
+++ b/plugins/jetpack/modules/widgets/twitter-timeline.php
@@ -49,7 +49,7 @@ class Jetpack_Twitter_Timeline_Widget extends WP_Widget {
if ( $instance['title'] )
echo $args['before_title'] . apply_filters( 'widget_title', $instance['title'] ) . $args['after_title'];
- $data_attribs = array( 'widget-id', 'theme', 'link-color', 'border-color', 'chrome' );
+ $data_attribs = array( 'widget-id', 'theme', 'link-color', 'border-color', 'chrome', 'tweet-limit' );
$attribs = array( 'width', 'height', 'lang' );
// Start tag output
@@ -57,6 +57,9 @@ class Jetpack_Twitter_Timeline_Widget extends WP_Widget {
foreach ( $data_attribs as $att ) {
if ( !empty( $instance[$att] ) ) {
+ if ( 'tweet-limit' == $att && 0 === $instance[$att] )
+ continue;
+
if ( is_array( $instance[$att] ) )
echo ' data-' . esc_attr( $att ) . '="' . esc_attr( join( ' ', $instance['chrome'] ) ) . '"';
else
@@ -94,8 +97,9 @@ class Jetpack_Twitter_Timeline_Widget extends WP_Widget {
$instance['title'] = sanitize_text_field( $new_instance['title'] );
$instance['width'] = (int) $new_instance['width'];
$instance['height'] = (int) $new_instance['height'];
- $instance['width'] = ( 0 !== (int) $instance['width'] ) ? (int) $instance['width'] : 225;
- $instance['height'] = ( 0 !== (int) $instance['height'] ) ? (int) $instance['height'] : 400;
+ $instance['width'] = ( 0 !== (int) $new_instance['width'] ) ? (int) $new_instance['width'] : 225;
+ $instance['height'] = ( 0 !== (int) $new_instance['height'] ) ? (int) $new_instance['height'] : 400;
+ $instance['tweet-limit'] = (int) $new_instance['tweet-limit'];
// If they entered something that might be a full URL, try to parse it out
if ( is_string( $new_instance['widget-id'] ) ) {
@@ -167,6 +171,11 @@ class Jetpack_Twitter_Timeline_Widget extends WP_Widget {
<input class="widefat" id="<?php echo $this->get_field_id( 'height' ); ?>" name="<?php echo $this->get_field_name( 'height' ); ?>" type="text" value="<?php echo esc_attr( $instance['height'] ); ?>" />
</p>
+ <p>
+ <label for="<?php echo $this->get_field_id( 'tweet-limit' ); ?>"><?php esc_html_e( '# of Tweets Shown:', 'jetpack' ); ?></label>
+ <input class="widefat" id="<?php echo $this->get_field_id( 'tweet-limit' ); ?>" name="<?php echo $this->get_field_name( 'tweet-limit' ); ?>" type="text" value="<?php echo esc_attr( $instance['tweet-limit'] ); ?>" />
+ </p>
+
<p><small>
<?php
echo wp_kses_post(
diff --git a/plugins/jetpack/modules/widgets/twitter-widget.php b/plugins/jetpack/modules/widgets/twitter-widget.php
deleted file mode 100644
index a7f359cd..00000000
--- a/plugins/jetpack/modules/widgets/twitter-widget.php
+++ /dev/null
@@ -1,273 +0,0 @@
-<?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/widgets/twitter.php b/plugins/jetpack/modules/widgets/twitter.php
deleted file mode 100644
index ff49abf7..00000000
--- a/plugins/jetpack/modules/widgets/twitter.php
+++ /dev/null
@@ -1,407 +0,0 @@
-<?php
-
-/**
- * Twitter widget class
- * Display the latest N tweets from a Twitter screenname as a widget
- * Customize screenname, maximum number of tweets displayed, show or hide @replies, and text displayed between tweet text and a timestamp
- *
- */
-
-/**
- * Register the widget for use in Appearance -> Widgets
- */
-add_action( 'widgets_init', 'jetpack_twitter_widget_init' );
-
-function jetpack_twitter_widget_init() {
- register_widget( 'Jetpack_Widget_Twitter' );
-}
-
-class Jetpack_Widget_Twitter extends WP_Widget {
-
- function __construct() {
- parent::__construct(
- 'twitter',
- apply_filters( 'jetpack_widget_name', __( 'Twitter', 'jetpack' ) ),
- array(
- 'classname' => 'widget_twitter',
- 'description' => __( 'Display your Tweets from Twitter', 'jetpack' )
- )
- );
-
- if ( is_active_widget( false, false, $this->id_base ) || is_active_widget( false, false, 'monster' ) ) {
- add_action( 'wp_head', array( $this, 'style' ) );
- }
- }
-
- function style() {
-?>
-<style type="text/css">
-.widget_twitter li {
- word-wrap: break-word;
-}
-</style>
-<?php
- }
-
- function widget( $args, $instance ) {
- $account = trim( urlencode( $instance['account'] ) );
-
- if ( empty( $account ) ) {
- if ( current_user_can('edit_theme_options') ) {
- echo $args['before_widget'];
- echo '<p>' . sprintf( __( 'Please configure your Twitter username for the <a href="%s">Twitter Widget</a>.', 'jetpack' ), admin_url( 'widgets.php' ) ) . '</p>';
- echo $args['after_widget'];
- }
-
- 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'];
- $hidepublicized = (bool) $instance['hidepublicized'];
- $include_retweets = (bool) $instance['includeretweets'];
- $follow_button = (bool) $instance['followbutton'];
-
- echo "{$args['before_widget']}{$args['before_title']}<a href='" . esc_url( "http://twitter.com/{$account}" ) . "'>" . esc_html( $title ) . "</a>{$args['after_title']}";
-
- $tweets = $this->fetch_twitter_user_stream( $account, $hidereplies, $show, $include_retweets );
-
- if ( isset( $tweets['error'] ) && ( isset( $tweets['data'] ) && ! empty( $tweets['data'] ) ) )
- $tweets['error'] = '';
-
- if ( empty( $tweets['error'] ) ) {
- $before_tweet = isset( $instance['beforetweet'] ) ? stripslashes( wp_filter_post_kses( $instance['beforetweet'] ) ) : '';
- $before_timesince = ( isset( $instance['beforetimesince'] ) && ! empty( $instance['beforetimesince'] ) ) ? esc_html( $instance['beforetimesince'] ) : ' ';
-
- $this->display_tweets( $show, $tweets['data'], $hidepublicized, $before_tweet, $before_timesince, $account );
-
- if ( $follow_button )
- $this->display_follow_button( $account );
-
- add_action( 'wp_footer', array( $this, 'twitter_widget_script' ) );
- } else {
- echo $tweets['error'];
- }
-
- echo $args['after_widget'];
- do_action( 'jetpack_bump_stats_extras', 'widget', 'twitter' );
- }
-
- function display_tweets( $show, $tweets, $hidepublicized, $before_tweet, $before_timesince, $account ) {
- $tweets_out = 0;
- ?><ul class='tweets'><?php
-
- foreach( (array) $tweets as $tweet ) {
- if ( $tweets_out >= $show )
- break;
-
- if ( empty( $tweet['text'] ) )
- continue;
-
- if( $hidepublicized && false !== strstr( $tweet['source'], 'http://publicize.wp.com/' ) )
- continue;
-
- $tweet['text'] = esc_html( $tweet['text'] ); // escape here so that Twitter handles in Tweets don't get mangled
- $tweet = $this->expand_tco_links( $tweet );
- $tweet['text'] = make_clickable( $tweet['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
- */
- $tweet['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, '_jetpack_widget_twitter_hashtag' ), $tweet['text'] );
- $tweet['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, '_jetpack_widget_twitter_username' ), $tweet['text'] );
-
- if ( isset( $tweet['id_str'] ) )
- $tweet_id = urlencode( $tweet['id_str'] );
- else
- $tweet_id = urlencode( $tweet['id'] );
-
- ?>
-
- <li>
- <?php echo esc_attr( $before_tweet ) . $tweet['text'] . esc_attr( $before_timesince ) ?>
- <a href="<?php echo esc_url( "http://twitter.com/{$account}/statuses/{$tweet_id}" ); ?>" class="timesince"><?php echo esc_html( str_replace( ' ', '&nbsp;', $this->time_since( strtotime( $tweet['created_at'] ) ) ) ); ?>&nbsp;ago</a>
- </li>
-
- <?php
-
- unset( $tweet_it );
- $tweets_out++;
- }
-
- ?></ul><?php
- }
-
- function display_follow_button( $account ) {
- global $themecolors;
-
- $follow_colors = isset( $themecolors['link'] ) ? " data-link-color='#{$themecolors['link']}'" : '';
- $follow_colors .= isset( $themecolors['text'] ) ? " data-text-color='#{$themecolors['text']}'" : '';
- $follow_button_attrs = " class='twitter-follow-button' data-show-count='false'{$follow_colors}";
-
- ?><a href="http://twitter.com/<?php echo esc_attr( $account ); ?>" <?php echo $follow_button_attrs; ?>>Follow @<?php echo esc_attr( $account ); ?></a><?php
- }
-
- function expand_tco_links( $tweet ) {
- if ( ! empty( $tweet['entities']['urls'] ) && is_array( $tweet['entities']['urls'] ) ) {
- foreach ( $tweet['entities']['urls'] as $entity_url ) {
- if ( ! empty( $entity_url['expanded_url'] ) ) {
- $tweet['text'] = str_replace(
- $entity_url['url'],
- '<a href="' . esc_url( $entity_url['expanded_url'] ) . '"> ' . esc_html( $entity_url['display_url'] ) . '</a>',
- $tweet['text']
- );
- }
- }
- }
-
- return $tweet;
- }
-
- function fetch_twitter_user_stream( $account, $hidereplies, $show, $include_retweets ) {
- $tweets = get_transient( 'widget-twitter-' . $this->number );
- $the_error = get_transient( 'widget-twitter-error-' . $this->number );
-
- if ( ! $tweets ) {
- $params = array(
- 'screen_name' => $account, // Twitter account name
- 'trim_user' => true, // only basic user data (slims the result)
- 'include_entities' => true
- );
-
- // If combined with $count, $exclude_replies only filters that number of tweets (not all tweets up to the requested 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 );
-
- switch( $response_code ) {
- case 200 : // process tweets and display
- $tweets = json_decode( wp_remote_retrieve_body( $response ), true );
-
- if ( ! is_array( $tweets ) || isset( $tweets['error'] ) ) {
- do_action( 'jetpack_bump_stats_extras', 'twitter_widget', "request-fail-{$response_code}-bad-data" );
- $the_error = '<p>' . esc_html__( 'Error: Twitter did not respond. Please wait a few minutes and refresh this page.', 'jetpack' ) . '</p>';
- $tweet_cache_expire = 300;
- break;
- } else {
- set_transient( 'widget-twitter-backup-' . $this->number, $tweets, 86400 ); // A one day backup in case there is trouble talking to Twitter
- }
-
- do_action( 'jetpack_bump_stats_extras', 'twitter_widget', 'request-success' );
- $tweet_cache_expire = 900;
- break;
- case 401 : // display private stream notice
- do_action( 'jetpack_bump_stats_extras', 'twitter_widget', "request-fail-{$response_code}" );
-
- $tweets = array();
- $the_error = '<p>' . sprintf( esc_html__( 'Error: Please make sure the Twitter account is %1$spublic%2$s.', 'jetpack' ), '<a href="http://support.twitter.com/forums/10711/entries/14016">', '</a>' ) . '</p>';
- $tweet_cache_expire = 300;
- break;
- default : // display an error message
- do_action( 'jetpack_bump_stats_extras', 'twitter_widget', "request-fail-{$response_code}" );
-
- $tweets = get_transient( 'widget-twitter-backup-' . $this->number );
- $the_error = '<p>' . esc_html__( 'Error: Twitter did not respond. Please wait a few minutes and refresh this page.', 'jetpack' ) . '</p>';
- $tweet_cache_expire = 300;
- break;
- }
-
- set_transient( 'widget-twitter-' . $this->number, $tweets, $tweet_cache_expire );
- set_transient( 'widget-twitter-error-' . $this->number, $the_error, $tweet_cache_expire );
- }
-
- return array( 'data' => $tweets, 'error' => $the_error );
- }
-
- function update( $new_instance, $old_instance ) {
- $instance = array();
-
- $instance['account'] = trim( wp_kses( $new_instance['account'], array() ) );
- $instance['account'] = str_replace( array( 'http://twitter.com/', '/', '@', '#!', ), array( '', '', '', '', ), $instance['account'] );
-
- $instance['title'] = wp_kses( $new_instance['title'], array() );
- $instance['show'] = absint( $new_instance['show'] );
- $instance['hidereplies'] = isset( $new_instance['hidereplies'] );
- $instance['hidepublicized'] = isset( $new_instance['hidepublicized'] );
- $instance['includeretweets'] = isset( $new_instance['includeretweets'] );
-
- if ( $instance['followbutton'] != $new_instance['followbutton'] ) {
- if ( $new_instance['followbutton'] )
- do_action( 'jetpack_bump_stats_extras', 'twitter_widget', 'follow_button_enabled' );
- else
- do_action( 'jetpack_bump_stats_extras', 'twitter_widget', 'follow_button_disabled' );
- }
-
- $instance['followbutton'] = ! isset( $new_instance['followbutton'] ) ? 0 : 1;
- $instance['beforetimesince'] = $new_instance['beforetimesince'];
-
- delete_transient( 'widget-twitter-' . $this->number );
- delete_transient( 'widget-twitter-error-' . $this->number );
-
- return $instance;
- }
-
- function form( $instance ) {
- //Defaults
- $account = isset( $instance['account'] ) ? wp_kses( $instance['account'], array() ) : '';
- $title = isset( $instance['title'] ) ? $instance['title'] : '';
- $show = isset( $instance['show'] ) ? absint( $instance['show'] ) : 5;
- $show = ( $show < 1 || 20 < $show ) ? 5 : $show;
- $hidereplies = isset( $instance['hidereplies'] ) && ! empty( $instance['hidereplies'] ) ? (bool) $instance['hidereplies'] : false;
- $hidepublicized = isset( $instance['hidepublicized'] ) && ! empty( $instance['hidepublicized'] ) ? (bool) $instance['hidepublicized'] : false;
- $include_retweets = isset( $instance['includeretweets'] ) && ! empty( $instance['includeretweets'] ) ? (bool) $instance['includeretweets'] : false;
- $follow_button = isset( $instance['followbutton'] ) && ! empty( $instance['followbutton'] ) ? 1 : 0;
- $before_timesince = isset( $instance['beforetimesince'] ) && ! empty( $instance['beforetimesince'] ) ? esc_attr( $instance['beforetimesince'] ) : '';
- ?>
-
- <p>
- <label for="<?php echo $this->get_field_id( 'title' ); ?>">
- <?php esc_html_e( '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( 'account' ); ?>">
- <?php esc_html_e( 'Twitter username:', 'jetpack' ); ?> <a href="http://support.wordpress.com/widgets/twitter-widget/#twitter-username" target="_blank">( ? )</a>
- <input class="widefat" id="<?php echo $this->get_field_id( 'account' ); ?>" name="<?php echo $this->get_field_name( 'account' ); ?>" type="text" value="<?php echo esc_attr( $account ); ?>" />
- </label>
- </p>
-
- <p>
- <label for="<?php echo $this->get_field_id( 'show' ); ?>">
- <?php esc_html_e( 'Maximum number of Tweets to show:', 'jetpack' ); ?>
- <select id="<?php echo $this->get_field_id( 'show' ); ?>" name="<?php echo $this->get_field_name( 'show' ); ?>">
- <?php
- for ( $i = 1; $i <= 20; ++$i ) :
- ?><option value="<?php echo esc_attr( $i ); ?>" <?php selected( $show, $i ); ?>><?php echo esc_attr( $i ); ?></option><?php
- endfor;
- ?>
- </select>
- </label>
- </p>
-
- <p>
- <label for="<?php echo $this->get_field_id( 'hidereplies' ); ?>">
- <input id="<?php echo $this->get_field_id( 'hidereplies' );?>" class="checkbox" type="checkbox" name="<?php echo $this->get_field_name( 'hidereplies' ); ?>" <?php checked( $hidereplies, true ); ?> />
- <?php esc_html_e( 'Hide replies', 'jetpack' ); ?>
- </label>
- </p>
-
- <p>
- <label for="<?php echo $this->get_field_id( 'hidepublicized' ); ?>">
- <input id="<?php echo $this->get_field_id( 'hidepublicized' ); ?>" class="checkbox" type="checkbox" name="<?php echo $this->get_field_name( 'hidepublicized' ); ?>" <?php checked( $hidepublicized, true ); ?> />
- <?php esc_html_e( 'Hide Tweets pushed by Publicize', 'jetpack' ); ?>
- </label>
- </p>
-
- <p>
- <label for="<?php echo $this->get_field_id( 'includeretweets' ); ?>">
- <input id="<?php echo $this->get_field_id( 'includeretweets' ); ?>" class="checkbox" type="checkbox" name="<?php echo $this->get_field_name( 'includeretweets' ); ?>" <?php checked( $include_retweets, true ); ?> />
- <?php esc_html_e( 'Include retweets', 'jetpack' ); ?>
- </label>
- </p>
-
- <p>
- <label for="<?php echo $this->get_field_id( 'followbutton' ); ?>">
- <input id="<?php echo $this->get_field_id( 'followbutton' ); ?>" class="checkbox" type="checkbox" name="<?php echo $this->get_field_name( 'followbutton' ); ?>" <?php checked( $follow_button, 1 ); ?> />
- <?php esc_html_e( 'Display Follow Button', 'jetpack' ); ?>
- </label>
- </p>
-
- <p>
- <label for="<?php echo $this->get_field_id( 'beforetimesince' ); ?>">
- <?php esc_html_e( 'Text to display between Tweet and timestamp:', 'jetpack' ); ?>
- <input class="widefat" id="<?php echo $this->get_field_id( 'beforetimesince' ); ?>" name="<?php echo $this->get_field_name( 'beforetimesince' ); ?>" type="text" value="<?php echo esc_attr( $before_timesince ); ?>" />
- </label>
- </p>
-
- <?php
- }
-
- function 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;
- }
-
- /**
- * 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 _jetpack_widget_twitter_username( array $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 _jetpack_widget_twitter_hashtag( array $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>";
- }
-
- function twitter_widget_script() {
- if ( ! wp_script_is( 'twitter-widgets', 'registered' ) ) {
- if ( is_ssl() )
- $twitter_widget_js = 'https://platform.twitter.com/widgets.js';
- else
- $twitter_widget_js = 'http://platform.twitter.com/widgets.js';
- wp_register_script( 'twitter-widgets', $twitter_widget_js, array(), '20111117', true );
- wp_print_scripts( 'twitter-widgets' );
- }
- }
-}
diff --git a/plugins/jetpack/modules/wpcc.php b/plugins/jetpack/modules/wpcc.php
new file mode 100644
index 00000000..df16b530
--- /dev/null
+++ b/plugins/jetpack/modules/wpcc.php
@@ -0,0 +1,24 @@
+<?php
+
+/**
+ * Module Name: WordPress.com Connect
+ * Module Description: Let users login with their WordPress.com Credentials, through <a href="http://jetpack.me/support/wpcc/">WordPress.com Connect</a>
+ * Sort Order: 50
+ * First Introduced: 2.4
+ * Requires Connection: No
+ * Auto Activate: No
+ */
+
+if ( ! class_exists( 'WPCC_Sign_On' ) )
+ require_once( dirname( __FILE__ ) . '/wpcc/wpcc-sign-on.php' );
+
+add_action( 'jetpack_modules_loaded', 'jetpack_wpcc_loaded' );
+function jetpack_wpcc_loaded() {
+ Jetpack::enable_module_configurable( __FILE__ );
+ Jetpack::module_configuration_load( __FILE__, 'jetpack_wpcc_configuration_load' );
+}
+
+function jetpack_wpcc_configuration_load() {
+ wp_safe_redirect( admin_url( 'options-general.php#wpcc-sign-on-section' ) );
+ exit;
+}
diff --git a/plugins/jetpack/modules/wpcc/wpcc-sign-on.css b/plugins/jetpack/modules/wpcc/wpcc-sign-on.css
new file mode 100644
index 00000000..478b3b9a
--- /dev/null
+++ b/plugins/jetpack/modules/wpcc/wpcc-sign-on.css
@@ -0,0 +1,11 @@
+
+#loginform {
+ overflow: hidden;
+ padding-bottom: 26px;
+}
+
+#wpcc-sign-on {
+ display: block;
+ float: right;
+ margin:1em 0 0;
+}
diff --git a/plugins/jetpack/modules/wpcc/wpcc-sign-on.js b/plugins/jetpack/modules/wpcc/wpcc-sign-on.js
new file mode 100644
index 00000000..318e6921
--- /dev/null
+++ b/plugins/jetpack/modules/wpcc/wpcc-sign-on.js
@@ -0,0 +1,4 @@
+
+jQuery(document).ready(function($){
+ $( '#loginform' ).append( $( '#wpcc-sign-on' ) );
+});
diff --git a/plugins/jetpack/modules/wpcc/wpcc-sign-on.php b/plugins/jetpack/modules/wpcc/wpcc-sign-on.php
new file mode 100644
index 00000000..47638b8a
--- /dev/null
+++ b/plugins/jetpack/modules/wpcc/wpcc-sign-on.php
@@ -0,0 +1,437 @@
+<?php
+/**
+ * Plugin Name: WPCC Sign On
+ * Plugin URI: https://github.com/Automattic/wpcc-sign-on
+ * Description: A single-sign-on via WordPress.com for your WordPress.org site!
+ * Author: Automatticians
+ * Version: 1.0
+ * Author URI: http://automattic.com/
+ */
+
+class WPCC_Sign_On {
+ static $instance = null;
+
+ var $request_token_url, // Fixed URL.
+ $authenticate_url, // Fixed URL.
+ $user_data_url, // Fixed URL.
+ $new_app_url_base, // Fixed URL.
+ $options_prefix, // Where the options are in the DB.
+ $options, // Options Array.
+ $client_id, // Option.
+ $client_secret, // Option.
+ $new_user_override, // Option.
+ $match_by_email, // Option.
+ $wpcc_state,
+ $secret,
+ $user_data;
+
+ function __construct() {
+ if ( self::$instance ) {
+ return self::$instance;
+ }
+
+ self::$instance = $this;
+
+ $this->request_token_url = 'https://public-api.wordpress.com/oauth2/token';
+ $this->authenticate_url = 'https://public-api.wordpress.com/oauth2/authenticate';
+ $this->user_data_url = 'https://public-api.wordpress.com/rest/v1/me/';
+ $this->new_app_url_base = 'https://developer.wordpress.com/apps/new/';
+ $this->options_prefix = $this->in_jetpack() ? 'jetpack_' : '';
+ $this->options = $this->fetch_options();
+ $this->client_id = $this->options['client_id'];
+ $this->client_secret = $this->options['client_secret'];
+ $this->new_user_override = $this->options['new_user_override'];
+ $this->match_by_email = $this->options['match_by_email'];
+
+ add_action( 'admin_init', array( $this, 'admin_init' ) );
+
+ if ( empty( $this->client_id ) ) {
+ return;
+ }
+
+ add_action( 'login_init', array( $this, 'login_init' ) );
+ add_action( 'login_enqueue_scripts', array( $this, 'login_enqueue_scripts' ) );
+ add_action( 'login_form', array( $this, 'login_form' ) );
+ }
+
+ function in_jetpack() {
+ return '/modules/wpcc' == substr( dirname( __FILE__ ), ( 0 - strlen( '/modules/wpcc' ) ) );
+ }
+
+ function fetch_options() {
+ $options = $this->in_jetpack() ? Jetpack_Options::get_option( 'wpcc_options' ) : get_option( 'wpcc_options' );
+ $args = wp_parse_args( $options, $this->default_options() );
+
+ $args['new_user_override'] = defined( 'WPCC_NEW_USER_OVERRIDE' ) ? WPCC_NEW_USER_OVERRIDE : false;
+ $args['match_by_email'] = defined( 'WPCC_MATCH_BY_EMAIL' ) ? WPCC_MATCH_BY_EMAIL : true;
+
+ return $args;
+ }
+
+ function default_options() {
+ return array(
+ 'client_id' => '',
+ 'client_secret' => '',
+ 'new_user_override' => false,
+ 'match_by_email' => true,
+ );
+ }
+
+ function admin_init() {
+ // Create the section
+ add_settings_section(
+ 'wpcc',
+ esc_html__( 'WordPress.com Connect', 'jetpack' ),
+ array( $this, 'wpcc_settings_section' ),
+ 'general'
+ );
+
+ add_settings_field(
+ 'wpcc_sign_on_client_id',
+ sprintf( '<label for="wpcc_sign_on_client_id">%1$s</label>', __( 'WPCC Client ID', 'jetpack' ) ),
+ array( $this, 'wpcc_sign_on_client_id_cb' ),
+ 'general',
+ 'wpcc'
+ );
+ add_settings_field(
+ 'wpcc_sign_on_client_secret',
+ sprintf( '<label for="wpcc_sign_on_client_secret">%1$s</label>', __( 'WPCC Client Secret', 'jetpack' ) ),
+ array( $this, 'wpcc_sign_on_client_secret_cb' ),
+ 'general',
+ 'wpcc'
+ );
+
+ register_setting( 'general', "{$this->options_prefix}wpcc_options", array( $this, 'sanitize_options' ) );
+
+ if ( ! empty( $this->client_id ) && ! empty( $this->client_secret ) ) {
+ add_action( 'show_user_profile', array( $this, 'edit_profile_fields' ) ); // For their own profile
+ add_action( 'edit_user_profile', array( $this, 'edit_profile_fields' ) ); // For folks editing others profiles
+ } else {
+ add_action( 'admin_notices', array( $this, 'no_credentials_admin_notice' ) );
+ }
+
+ if ( $user_ID = get_current_user_id() ) {
+ $this->wpcc_state = "localuser{$user_ID}";
+ }
+ }
+
+ function no_credentials_admin_notice() {
+ $disable_url = wp_nonce_url( Jetpack::admin_url( 'action=deactivate&module=wpcc' ), 'jetpack_deactivate-wpcc' );
+ ?>
+ <div id="wpcc-needs-config" class="updated">
+ <p class="alignright"><a href="<?php echo esc_url( $disable_url ); ?>"><?php _e( 'Close', 'jetpack' ); ?></a></p>
+ <p><?php printf( __( '<strong>Almost done.</strong> Before WordPress.com Connect can finish activating, you\'ll need to <a href="%s">register your website as an application on WordPress.com</a>', 'jetpack' ), esc_url( admin_url( 'options-general.php#wpcc-sign-on-section' ) ) ); ?></p>
+ </div>
+ <?php
+ }
+
+ function sanitize_options( $options ) {
+ if ( ! empty( $options['client_id'] ) ) {
+ $options['client_id'] = intval( $options['client_id'] );
+ }
+
+ if ( ! empty( $options['client_secret'] ) ) {
+ $options['client_secret'] = sanitize_text_field( $options['client_secret'] );
+ }
+
+ return $options;
+ }
+
+ function wpcc_settings_section() {
+ ?>
+
+ <p id="wpcc-sign-on-section">
+ <?php
+ if ( empty( $this->client_id ) || empty( $this->client_secret ) ) {
+ printf( __( 'Visit WordPress.com to <a href="%s">register a new WPCC client id and secret key</a>.', 'jetpack' ), $this->get_new_app_url() );
+ } else {
+ printf( __( 'Visit WordPress.com to <a href="%s">manage your WPCC client settings</a>.', 'jetpack' ), $this->get_edit_app_url() );
+ }
+ ?>
+ </p>
+ <script>
+ jQuery('#wpcc-sign-on-section a').attr( 'target', '_blank' );
+ </script>
+
+ <?php
+ }
+
+ function wpcc_sign_on_client_id_cb() {
+ echo '<input class="regular-text code" autocomplete="off" type="text" id="wpcc_sign_on_client_id" name="' . $this->options_prefix . 'wpcc_options[client_id]" value="' . esc_attr( $this->client_id ) . '" />';
+ }
+
+ function wpcc_sign_on_client_secret_cb() {
+ echo '<input class="regular-text code" autocomplete="off" type="text" id="wpcc_sign_on_client_secret" name="' . $this->options_prefix . 'wpcc_options[client_secret]" value="' . esc_attr( $this->client_secret ) . '" />';
+ }
+
+ function edit_profile_fields( $user ) {
+ if ( isset( $_GET['wpcc'] ) && 'purge' == $_GET['wpcc'] ) {
+ delete_user_meta( $user->ID, 'wpcom_user_id' );
+ delete_user_meta( $user->ID, 'wpcom_user_data' );
+ }
+ ?>
+
+ <h3><?php _e( 'WordPress.com Connect', 'jetpack' ); ?></h3>
+ <p><?php _e( 'Connecting with WordPress.com Connect enables you to log on via your WordPress.com account.', 'jetpack' ); ?></p>
+
+ <?php if ( ( $user_data = get_user_meta( $user->ID, 'wpcom_user_data', true ) ) && ! empty( $user_data->ID ) ) : /* If the user is currently connected... */ ?>
+
+ <table class="form-table wpcc-form-table">
+ <tbody>
+ <tr>
+ <td>
+ <div class="profile-card">
+ <img src="<?php echo esc_url( $user_data->avatar_URL ); ?>" height="48" width="48" />
+ <p class="connected"><strong><?php _e( 'Connected', 'jetpack' ); ?></strong></p>
+ <p><?php echo esc_html( $user_data->username ); ?></p>
+ </div>
+ <p><a class="button button-secondary" href="<?php echo esc_url( add_query_arg( 'wpcc', 'purge' ) ); ?>"><?php _e( 'Unlink This Account', 'jetpack' ); ?></a></p>
+ </td>
+ </tr>
+ </tbody>
+ </table>
+
+ <style>
+ .wpcc-form-table td {
+ padding-left: 0;
+ }
+
+ .wpcc-form-table .profile-card {
+ padding: 10px;
+ background: #fff;
+ overflow: hidden;
+ max-width: 400px;
+ box-shadow: 0 2px 5px rgba( 0, 0, 0, 0.4 );
+ margin-bottom: 1em;
+ }
+
+ .wpcc-form-table .profile-card img {
+ float: left;
+ margin-right: 1em;
+ }
+
+ .wpcc-form-table .profile-card .connected {
+ float: right;
+ margin-right: 0.5em;
+ color: #0a0;
+ }
+
+ .wpcc-form-table .profile-card p {
+ margin-top: 0.7em;
+ font-size: 1.2em;
+ }
+ </style>
+
+ <?php elseif ( get_current_user_id() == $user->ID ) : ?>
+
+ <?php echo $this->button( array( 'redirect_uri' => add_query_arg( 'for', 'profile', wp_login_url() ) ) ); ?>
+
+ <?php else : ?>
+
+ <p><?php _e( 'This profile is not currently linked to a WordPress.com Profile.', 'jetpack' ); ?></p>
+
+ <?php endif;
+ }
+
+ function verify_connection( $get_args = array() ) {
+ if ( empty( $_GET['state'] ) ) {
+ wp_die( __( 'Warning! State variable missing after authentication.', 'jetpack' ) );
+ }
+
+ if ( $_GET['state'] != $this->wpcc_state ) {
+ wp_die( __( 'Warning! State mismatch. Authentication attempt may have been compromised.', 'jetpack' ) );
+ }
+
+ $redirect_uri = wp_login_url();
+
+ if ( $get_args ) {
+ $redirect_uri = add_query_arg( $get_args, $redirect_uri );
+ }
+
+ $args = array(
+ 'client_id' => $this->client_id,
+ 'redirect_uri' => $redirect_uri,
+ 'client_secret' => $this->client_secret,
+ 'code' => sanitize_text_field( $_GET['code'] ), // The code from the previous request
+ 'grant_type' => 'authorization_code',
+ );
+
+ $response = wp_remote_post( $this->request_token_url, array( 'body' => $args ) );
+
+ if ( is_wp_error( $response ) ) {
+ wp_die( __( 'Warning! Could not confirm request token url!', 'jetpack' ) );
+ }
+
+ $this->secret = json_decode( wp_remote_retrieve_body( $response ) );
+
+ $args = array(
+ 'headers' => array(
+ 'Authorization' => sprintf( 'Bearer %s', $this->secret->access_token ),
+ ),
+ );
+
+ $response = wp_remote_get( $this->user_data_url, $args );
+
+ if ( is_wp_error( $response ) ) {
+ wp_die( __( 'Warning! Could not fetch user data!', 'jetpack' ) );
+ }
+
+ $this->user_data = json_decode( wp_remote_retrieve_body( $response ) );
+
+ return $this->user_data;
+ }
+
+ function login_init() {
+ // Set the wpcc_state
+ $this->wpcc_state = md5( mt_rand() );
+ if ( isset( $_COOKIE['wpcc_state'] ) ) {
+ $this->wpcc_state = $_COOKIE['wpcc_state'];
+ } else {
+ setcookie( 'wpcc_state', $this->wpcc_state );
+ }
+
+ if ( isset( $_GET['for'] ) && ( 'profile' == $_GET['for'] ) ) {
+ $user_ID = get_current_user_id();
+
+ $this->wpcc_state = "localuser{$user_ID}";
+ $user_data = $this->verify_connection( array( 'for' => 'profile' ) );
+
+ update_user_meta( get_current_user_id(), 'wpcom_user_id', $user_data->ID );
+ update_user_meta( get_current_user_id(), 'wpcom_user_data', $user_data );
+
+ wp_safe_redirect( admin_url( 'profile.php' ) );
+ exit;
+ }
+
+ // If they just got forwarded back ...
+ if ( isset( $_GET['code'] ) ) {
+
+ $user_data = $this->verify_connection();
+
+ $this->auth_user( $user_data );
+ }
+ }
+
+ function login_enqueue_scripts() {
+ wp_enqueue_style( 'wpcc-sign-on', plugins_url( 'wpcc-sign-on.css', __FILE__ ), 0, filemtime( dirname( __FILE__ ) . '/wpcc-sign-on.css' ) );
+ wp_enqueue_script( 'wpcc-sign-on', plugins_url( 'wpcc-sign-on.js', __FILE__ ), array( 'jquery' ), filemtime( dirname( __FILE__ ) . '/wpcc-sign-on.js' ) );
+ }
+
+ function login_form() {
+ if( ! did_action( 'login_init' ) )
+ return;
+
+ echo $this->button();
+ }
+
+ function button( $args = array() ) {
+ $defaults = array(
+ 'response_type' => 'code',
+ 'client_id' => $this->client_id,
+ 'state' => $this->wpcc_state,
+ 'redirect_uri' => wp_login_url(),
+ );
+
+ $args = wp_parse_args( $args, $defaults );
+
+ if ( ! empty( $_REQUEST['redirect_to'] ) ) {
+ $args['redirect_uri'] = add_query_arg( 'redirect_to', $_REQUEST['redirect_to'], $args['redirect_uri'] );
+ }
+
+ $url = add_query_arg( $args, $this->authenticate_url );
+
+ return sprintf( '<a id="wpcc-sign-on" href="%1$s"><img src="//s0.wp.com/i/wpcc-button.png" width="231" /></a>', esc_url( $url ) );
+ }
+
+ function auth_user( $user_data ) {
+
+ if ( ! $user_data->verified ) {
+ return false;
+ }
+
+ $user = $this->get_user_by_wpcom_id( $user_data->ID );
+
+ // If we don't have one by wpcom_user_id, try by the email?
+ if ( empty( $user ) && $this->match_by_email ) {
+ $user = get_user_by( 'email', $user_data->email );
+ if ( $user ) {
+ update_user_meta( $user->ID, 'wpcom_user_id', $user_data->ID );
+ }
+ }
+
+ // If we've still got nothing, create the user.
+ if ( empty( $user ) && ( get_option( 'users_can_register' ) || $this->new_user_override ) ) {
+ $username = $user_data->username;
+
+ if ( username_exists( $username ) ) {
+ $username .= '_' . $user_data->ID;
+ }
+
+ if ( username_exists( $username ) )
+ $username .= '_' . mt_rand();
+
+ $password = wp_generate_password( 20 );
+ $user_id = wp_create_user( $username, $password, $user_data->email );
+ $user = get_userdata( $user_id );
+
+ $user->display_name = $user_data->display_name;
+ wp_update_user( $user );
+
+ update_user_meta( $user->ID, 'wpcom_user_id', $user_data->ID );
+ }
+
+ if ( $user ) {
+ // Cache the user's details, so we can present it back to them on their user screen.
+ update_user_meta( $user->ID, 'wpcom_user_data', $user_data );
+ wp_set_auth_cookie( $user->ID );
+
+ $_request_redirect_to = isset( $_REQUEST['redirect_to'] ) ? $_REQUEST['redirect_to'] : '';
+ $redirect_to = user_can( $user, 'edit_posts' ) ? admin_url() : admin_url( 'profile.php' );
+ wp_safe_redirect( apply_filters( 'login_redirect', $redirect_to, $_request_redirect_to, $user ) );
+ exit;
+ }
+
+ add_action( 'login_message', array( $this, 'cant_find_user' ) );
+ }
+
+ function get_user_by_wpcom_id( $wpcom_user_id ) {
+ $user_query = new WP_User_Query( array(
+ 'meta_key' => 'wpcom_user_id',
+ 'meta_value' => intval( $wpcom_user_id ),
+ ) );
+
+ $users = $user_query->get_results();
+
+ return ( is_array( $users ) && ! empty( $users ) ) ? array_shift( $users ) : $users;
+ }
+
+ function cant_find_user( $message ) {
+ $err_format = __( 'We couldn\'t find an account with the email <strong><code>%1$s</code></strong> to log you in with. If you already have an account on <strong>%2$s</strong>, please make sure that <strong><code>%1$s</code></strong> is configured as the email address, or that you have connected to WordPress.com on your profile page.', 'jetpack' );
+ if ( ! $this->match_by_email ) {
+ $err_format = __( 'We couldn\'t find any account on <strong>%2$s</strong> that is linked to your WordPress.com account to log you in with. If you already have an account on <strong>%2$s</strong>, please make sure that you have connected to WordPress.com on your profile page.', 'jetpack' );
+ }
+ $err = sprintf( $err_format, $this->user_data->email, get_bloginfo( 'name' ) );
+ $message .= sprintf( '<p class="message" id="login_error">%s</p>', $err );
+ return $message;
+ }
+
+ function get_new_app_url() {
+ $args = array(
+ 'for' => 'jetpack',
+ 'title' => urlencode( get_bloginfo( 'name' ) ),
+ 'description' => urlencode( get_bloginfo( 'description' ) ),
+ 'url' => urlencode( site_url() ),
+ 'redirect_uri' => urlencode( wp_login_url() ),
+ );
+ return add_query_arg( $args, $this->new_app_url_base );
+ }
+
+ function get_edit_app_url( $client_id = null ) {
+ if ( empty( $client_id ) ) {
+ $client_id = $this->client_id;
+ }
+ return sprintf( 'https://developer.wordpress.com/apps/%d/', $client_id );
+ }
+}
+
+new WPCC_Sign_On;