summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTheo Chatzimichos <tampakrap@gentoo.org>2013-03-10 12:10:26 +0100
committerTheo Chatzimichos <tampakrap@gentoo.org>2013-03-10 12:10:26 +0100
commit7aea9fc04bd42e2ac02a1925d3a02a76d391c3e7 (patch)
tree68c852c654cef340592f1001b6310e33827b130c /plugins/jetpack/modules
parentMake the script more silent (diff)
downloadblogs-gentoo-7aea9fc04bd42e2ac02a1925d3a02a76d391c3e7.tar.gz
blogs-gentoo-7aea9fc04bd42e2ac02a1925d3a02a76d391c3e7.tar.bz2
blogs-gentoo-7aea9fc04bd42e2ac02a1925d3a02a76d391c3e7.zip
update plugins
Diffstat (limited to 'plugins/jetpack/modules')
-rw-r--r--plugins/jetpack/modules/after-the-deadline.php2
-rw-r--r--plugins/jetpack/modules/after-the-deadline/config-options.php4
-rw-r--r--plugins/jetpack/modules/carousel/jetpack-carousel-ie8fix.css8
-rw-r--r--plugins/jetpack/modules/carousel/jetpack-carousel.css89
-rw-r--r--plugins/jetpack/modules/carousel/jetpack-carousel.js344
-rw-r--r--plugins/jetpack/modules/carousel/jetpack-carousel.php85
-rw-r--r--plugins/jetpack/modules/carousel/rtl/jetpack-carousel-rtl.css1106
-rw-r--r--plugins/jetpack/modules/comments.php9
-rw-r--r--plugins/jetpack/modules/comments/base.php47
-rw-r--r--plugins/jetpack/modules/comments/comments.php134
-rw-r--r--plugins/jetpack/modules/contact-form/admin.php245
-rw-r--r--plugins/jetpack/modules/contact-form/css/grunion.css7
-rw-r--r--plugins/jetpack/modules/contact-form/grunion-contact-form.php1866
-rw-r--r--plugins/jetpack/modules/contact-form/images/grunion-menu-2x.pngbin541 -> 53439 bytes
-rw-r--r--plugins/jetpack/modules/contact-form/images/grunion-menu-hover-2x.pngbin697 -> 51427 bytes
-rw-r--r--plugins/jetpack/modules/contact-form/images/grunion-remove-field-2x.pngbin201 -> 47832 bytes
-rw-r--r--plugins/jetpack/modules/contact-form/images/grunion-remove-field-hover-2x.pngbin207 -> 47745 bytes
-rw-r--r--plugins/jetpack/modules/contact-form/js/grunion.js60
-rw-r--r--plugins/jetpack/modules/custom-css.php26
-rw-r--r--plugins/jetpack/modules/custom-css/csstidy/class.csstidy.php1241
-rw-r--r--plugins/jetpack/modules/custom-css/csstidy/class.csstidy_ctype.php46
-rw-r--r--plugins/jetpack/modules/custom-css/csstidy/class.csstidy_optimise.php936
-rw-r--r--plugins/jetpack/modules/custom-css/csstidy/class.csstidy_print.php408
-rw-r--r--plugins/jetpack/modules/custom-css/csstidy/cssparse.css118
-rw-r--r--plugins/jetpack/modules/custom-css/csstidy/cssparsed.css29
-rw-r--r--plugins/jetpack/modules/custom-css/csstidy/data-wp.inc.php75
-rw-r--r--plugins/jetpack/modules/custom-css/csstidy/data.inc.php661
-rw-r--r--plugins/jetpack/modules/custom-css/csstidy/lang.inc.php311
-rw-r--r--plugins/jetpack/modules/custom-css/csstidy/wordpress-standard.tpl10
-rw-r--r--plugins/jetpack/modules/custom-css/custom-css.php1468
-rw-r--r--plugins/jetpack/modules/custom-css/custom-css/blank.css1
-rw-r--r--plugins/jetpack/modules/custom-css/custom-css/js/ace/ace.js11
-rw-r--r--plugins/jetpack/modules/custom-css/custom-css/js/ace/mode-css.js1
-rw-r--r--plugins/jetpack/modules/custom-css/custom-css/js/ace/readme.txt1
-rw-r--r--plugins/jetpack/modules/custom-css/custom-css/js/ace/theme-textmate.js1
-rw-r--r--plugins/jetpack/modules/custom-css/custom-css/js/ace/worker-css.js1
-rw-r--r--plugins/jetpack/modules/custom-css/custom-css/js/safecss-ace.js69
-rw-r--r--plugins/jetpack/modules/custom-css/custom-css/preprocessors.php57
-rw-r--r--plugins/jetpack/modules/custom-css/custom-css/preprocessors/lessc.inc.php3359
-rw-r--r--plugins/jetpack/modules/custom-css/custom-css/preprocessors/scss.inc.php3759
-rw-r--r--plugins/jetpack/modules/enhanced-distribution.php23
-rw-r--r--plugins/jetpack/modules/featured-content/featured-content.php454
-rw-r--r--plugins/jetpack/modules/gravatar-hovercards.php17
-rw-r--r--plugins/jetpack/modules/holiday-snow.php72
-rw-r--r--plugins/jetpack/modules/holiday-snow/snowstorm.js539
-rw-r--r--plugins/jetpack/modules/infinite-scroll.php196
-rw-r--r--plugins/jetpack/modules/infinite-scroll/infinity.css137
-rw-r--r--plugins/jetpack/modules/infinite-scroll/infinity.js490
-rw-r--r--plugins/jetpack/modules/infinite-scroll/infinity.php945
-rw-r--r--plugins/jetpack/modules/infinite-scroll/themes/twentyeleven.css45
-rw-r--r--plugins/jetpack/modules/infinite-scroll/themes/twentyeleven.php27
-rw-r--r--plugins/jetpack/modules/infinite-scroll/themes/twentyten.css25
-rw-r--r--plugins/jetpack/modules/infinite-scroll/themes/twentyten.php48
-rw-r--r--plugins/jetpack/modules/infinite-scroll/themes/twentytwelve.css33
-rw-r--r--plugins/jetpack/modules/infinite-scroll/themes/twentytwelve.php46
-rw-r--r--plugins/jetpack/modules/json-api.php19
-rw-r--r--plugins/jetpack/modules/latex.php6
-rw-r--r--plugins/jetpack/modules/likes.php970
-rw-r--r--plugins/jetpack/modules/likes/style.css189
-rw-r--r--plugins/jetpack/modules/minileven.php108
-rw-r--r--plugins/jetpack/modules/minileven/images/wp-app-devices.pngbin0 -> 1865 bytes
-rw-r--r--plugins/jetpack/modules/minileven/minileven.php313
-rw-r--r--plugins/jetpack/modules/minileven/theme/pub/minileven/comments.php52
-rw-r--r--plugins/jetpack/modules/minileven/theme/pub/minileven/content-gallery.php78
-rw-r--r--plugins/jetpack/modules/minileven/theme/pub/minileven/content.php60
-rw-r--r--plugins/jetpack/modules/minileven/theme/pub/minileven/footer.php36
-rw-r--r--plugins/jetpack/modules/minileven/theme/pub/minileven/functions.php152
-rw-r--r--plugins/jetpack/modules/minileven/theme/pub/minileven/header.php49
-rw-r--r--plugins/jetpack/modules/minileven/theme/pub/minileven/image.php98
-rw-r--r--plugins/jetpack/modules/minileven/theme/pub/minileven/inc/custom-header.php101
-rw-r--r--plugins/jetpack/modules/minileven/theme/pub/minileven/inc/template-tags.php99
-rw-r--r--plugins/jetpack/modules/minileven/theme/pub/minileven/inc/tweaks.php94
-rw-r--r--plugins/jetpack/modules/minileven/theme/pub/minileven/index.php73
-rw-r--r--plugins/jetpack/modules/minileven/theme/pub/minileven/js/small-menu.js41
-rw-r--r--plugins/jetpack/modules/minileven/theme/pub/minileven/page.php42
-rw-r--r--plugins/jetpack/modules/minileven/theme/pub/minileven/rtl.css582
-rw-r--r--plugins/jetpack/modules/minileven/theme/pub/minileven/screenshot.pngbin0 -> 59059 bytes
-rw-r--r--plugins/jetpack/modules/minileven/theme/pub/minileven/searchform.php12
-rw-r--r--plugins/jetpack/modules/minileven/theme/pub/minileven/sidebar.php12
-rw-r--r--plugins/jetpack/modules/minileven/theme/pub/minileven/style.css1437
-rw-r--r--plugins/jetpack/modules/mobile-push.php11
-rw-r--r--plugins/jetpack/modules/module-extras.php58
-rw-r--r--plugins/jetpack/modules/module-info.php347
-rw-r--r--plugins/jetpack/modules/notes.php195
-rw-r--r--plugins/jetpack/modules/photon.php9
-rw-r--r--plugins/jetpack/modules/photon/photon.js42
-rw-r--r--plugins/jetpack/modules/post-by-email.php240
-rw-r--r--plugins/jetpack/modules/post-by-email/post-by-email.css6
-rw-r--r--plugins/jetpack/modules/post-by-email/post-by-email.js129
-rw-r--r--plugins/jetpack/modules/publicize.php283
-rw-r--r--plugins/jetpack/modules/publicize/assets/connected.gifbin0 -> 1681 bytes
-rw-r--r--plugins/jetpack/modules/publicize/assets/facebook-logo.pngbin0 -> 37624 bytes
-rw-r--r--plugins/jetpack/modules/publicize/assets/linkedin-logo.pngbin0 -> 6882 bytes
-rw-r--r--plugins/jetpack/modules/publicize/assets/publicize.css175
-rw-r--r--plugins/jetpack/modules/publicize/assets/publicize.js108
-rw-r--r--plugins/jetpack/modules/publicize/assets/rtl/publicize-rtl.css177
-rw-r--r--plugins/jetpack/modules/publicize/assets/spinner.gifbin0 -> 457 bytes
-rw-r--r--plugins/jetpack/modules/publicize/assets/tumblr-logo.pngbin0 -> 9001 bytes
-rw-r--r--plugins/jetpack/modules/publicize/assets/twitter-logo.pngbin0 -> 4623 bytes
-rw-r--r--plugins/jetpack/modules/publicize/assets/yahoo-logo.pngbin0 -> 9675 bytes
-rw-r--r--plugins/jetpack/modules/publicize/publicize-jetpack.php578
-rw-r--r--plugins/jetpack/modules/publicize/publicize.php328
-rw-r--r--plugins/jetpack/modules/publicize/ui.php545
-rw-r--r--plugins/jetpack/modules/sharedaddy.php2
-rw-r--r--plugins/jetpack/modules/sharedaddy/admin-sharing.css140
-rw-r--r--plugins/jetpack/modules/sharedaddy/images/icon-facebook-2x.pngbin0 -> 671 bytes
-rw-r--r--plugins/jetpack/modules/sharedaddy/images/icon-facebook.pngbin0 -> 887 bytes
-rw-r--r--plugins/jetpack/modules/sharedaddy/images/icon-twitter-2x.pngbin0 -> 1451 bytes
-rw-r--r--plugins/jetpack/modules/sharedaddy/images/icon-twitter.pngbin0 -> 523 bytes
-rw-r--r--plugins/jetpack/modules/sharedaddy/images/icon-wordpress-2x.pngbin0 -> 657 bytes
-rw-r--r--plugins/jetpack/modules/sharedaddy/images/icon-wordpress.pngbin0 -> 775 bytes
-rw-r--r--plugins/jetpack/modules/sharedaddy/sharedaddy.php57
-rw-r--r--plugins/jetpack/modules/sharedaddy/sharing-service.php217
-rw-r--r--plugins/jetpack/modules/sharedaddy/sharing-sources.php566
-rw-r--r--plugins/jetpack/modules/sharedaddy/sharing.css142
-rw-r--r--plugins/jetpack/modules/sharedaddy/sharing.js123
-rw-r--r--plugins/jetpack/modules/sharedaddy/sharing.php128
-rw-r--r--plugins/jetpack/modules/shortcodes/audio.php45
-rw-r--r--plugins/jetpack/modules/shortcodes/css/rtl/slideshow-shortcode-rtl.css131
-rw-r--r--plugins/jetpack/modules/shortcodes/css/slideshow-shortcode.css129
-rw-r--r--plugins/jetpack/modules/shortcodes/googlemaps.php2
-rw-r--r--plugins/jetpack/modules/shortcodes/img/slideshow-controls-2x.pngbin0 -> 1944 bytes
-rw-r--r--plugins/jetpack/modules/shortcodes/img/slideshow-controls.pngbin0 -> 1009 bytes
-rw-r--r--plugins/jetpack/modules/shortcodes/img/slideshow-loader.gifbin0 -> 13545 bytes
-rw-r--r--plugins/jetpack/modules/shortcodes/js/audio-shortcode.js154
-rw-r--r--plugins/jetpack/modules/shortcodes/js/jquery.cycle.js1551
-rw-r--r--plugins/jetpack/modules/shortcodes/js/slideshow-shortcode.js187
-rw-r--r--plugins/jetpack/modules/shortcodes/polldaddy.php6
-rw-r--r--plugins/jetpack/modules/shortcodes/slideshow.php208
-rw-r--r--plugins/jetpack/modules/shortcodes/soundcloud.php246
-rw-r--r--plugins/jetpack/modules/shortcodes/ted.php68
-rw-r--r--plugins/jetpack/modules/shortcodes/videopress.php51
-rw-r--r--plugins/jetpack/modules/shortcodes/vimeo.php20
-rw-r--r--plugins/jetpack/modules/shortcodes/youtube.php10
-rw-r--r--plugins/jetpack/modules/stats.php172
-rw-r--r--plugins/jetpack/modules/subscriptions.php289
-rw-r--r--plugins/jetpack/modules/tiled-gallery.php25
-rw-r--r--plugins/jetpack/modules/tiled-gallery/math/class-constrained-array-rounding.php75
-rw-r--r--plugins/jetpack/modules/tiled-gallery/tiled-gallery.php588
-rw-r--r--plugins/jetpack/modules/tiled-gallery/tiled-gallery/rtl/tiled-gallery-rtl.css88
-rw-r--r--plugins/jetpack/modules/tiled-gallery/tiled-gallery/tiled-gallery.css85
-rw-r--r--plugins/jetpack/modules/tiled-gallery/tiled-gallery/tiled-gallery.js150
-rw-r--r--plugins/jetpack/modules/widgets.php21
-rw-r--r--plugins/jetpack/modules/widgets/facebook-likebox.php57
-rw-r--r--plugins/jetpack/modules/widgets/gravatar-profile.css15
-rw-r--r--plugins/jetpack/modules/widgets/gravatar-profile.php128
-rw-r--r--plugins/jetpack/modules/widgets/image-widget.php2
-rw-r--r--plugins/jetpack/modules/widgets/readmill.php138
-rw-r--r--plugins/jetpack/modules/widgets/top-posts.php295
-rw-r--r--plugins/jetpack/modules/widgets/twitter.php407
-rw-r--r--plugins/jetpack/modules/widgets/widget-grid-and-list.css110
-rw-r--r--plugins/jetpack/modules/widgets/widgets.css13
152 files changed, 32405 insertions, 1982 deletions
diff --git a/plugins/jetpack/modules/after-the-deadline.php b/plugins/jetpack/modules/after-the-deadline.php
index b187de35..606f6488 100644
--- a/plugins/jetpack/modules/after-the-deadline.php
+++ b/plugins/jetpack/modules/after-the-deadline.php
@@ -14,7 +14,7 @@ function AtD_load() {
}
function AtD_configuration_load() {
- wp_safe_redirect( admin_url( 'profile.php#atd' ) );
+ wp_safe_redirect( get_edit_profile_url( get_current_user_id() ) . '#atd' );
exit;
}
diff --git a/plugins/jetpack/modules/after-the-deadline/config-options.php b/plugins/jetpack/modules/after-the-deadline/config-options.php
index 31cc4da3..097b062d 100644
--- a/plugins/jetpack/modules/after-the-deadline/config-options.php
+++ b/plugins/jetpack/modules/after-the-deadline/config-options.php
@@ -45,7 +45,7 @@ function AtD_display_options_form() {
?>
<table class="form-table">
<tr valign="top">
- <th scope="row"> <a name="atd"></a> <?php _e( 'Proofreading', 'jetpack' ); ?></th>
+ <th scope="row"> <a id="atd"></a> <?php _e( 'Proofreading', 'jetpack' ); ?></th>
<td>
<p><?php _e( 'Automatically proofread content when:', 'jetpack' ); ?>
@@ -86,7 +86,7 @@ function AtD_display_options_form() {
<p style="font-weight: bold"><?php _e( 'Language', 'jetpack' ); ?></font>
<p><?php printf(
- _x( 'The proofreader supports English, French, German, Portuguese, and Spanish. Your <a href="%1$s">%2%s</a> value is the default proofreading language.', '%1$s = http://codex.wordpress.org/Installing_WordPress_in_Your_Language, %2$s = WPLANG', 'jetpack' ),
+ _x( 'The proofreader supports English, French, German, Portuguese, and Spanish. Your <a href="%1$s">%2$s</a> value is the default proofreading language.', '%1$s = http://codex.wordpress.org/Installing_WordPress_in_Your_Language, %2$s = WPLANG', 'jetpack' ),
'http://codex.wordpress.org/Installing_WordPress_in_Your_Language',
'WPLANG'
); ?></p>
diff --git a/plugins/jetpack/modules/carousel/jetpack-carousel-ie8fix.css b/plugins/jetpack/modules/carousel/jetpack-carousel-ie8fix.css
new file mode 100644
index 00000000..9f2f6cff
--- /dev/null
+++ b/plugins/jetpack/modules/carousel/jetpack-carousel-ie8fix.css
@@ -0,0 +1,8 @@
+.jp-carousel .jp-carousel-slide {
+ display: none !important;
+}
+
+.jp-carousel .selected {
+ margin: 0 auto;
+ display: block !important;
+}
diff --git a/plugins/jetpack/modules/carousel/jetpack-carousel.css b/plugins/jetpack/modules/carousel/jetpack-carousel.css
index aa2b1d0f..49bdad8e 100644
--- a/plugins/jetpack/modules/carousel/jetpack-carousel.css
+++ b/plugins/jetpack/modules/carousel/jetpack-carousel.css
@@ -11,7 +11,7 @@ div.jp-carousel-fadeaway {
background: -webkit-gradient(linear, left bottom, left top, from(rgba(0,0,0,0.5)), to(rgba(0,0,0,0)));
position: fixed;
bottom: 0;
- z-index: 99999;
+ z-index: 2147483647;
width: 100%;
height: 15px;
}
@@ -199,13 +199,11 @@ div.jp-carousel-buttons a:hover {
.jp-carousel-close-hint {
color: #999;
cursor: default;
- font: 16px/1 "Helvetica Neue", sans-serif !important;
- font-weight: 600 !important;
letter-spacing: 0 !important;
- padding:0.55em 0 0;
- text-align: left;
- width: 100%;
+ padding:0.35em 0 0;
position: absolute;
+ text-align: left;
+ width: 90%;
-webkit-transition: color 200ms linear;
-moz-transition: color 200ms linear;
-o-transition: color 200ms linear;
@@ -213,16 +211,17 @@ div.jp-carousel-buttons a:hover {
}
.jp-carousel-close-hint span {
- cursor:pointer;
+ cursor: pointer;
background-color: black;
background-color: rgba(0,0,0,0.8);
- height: 26px;
- width: 26px;
display: block;
- text-align: center;
- vertical-align: middle;
+ height: 22px;
+ font: 400 24px/1 "Helvetica Neue", sans-serif !important;
line-height: 22px;
margin: 0 0 0 0.4em;
+ text-align: center;
+ vertical-align: middle;
+ width: 22px;
-moz-border-radius: 4px;
-webkit-border-radius: 4px;
border-radius: 4px;
@@ -482,7 +481,10 @@ div#carousel-reblog-box {
display: none;
}
-h1:before, h1:after {
+.jp-carousel-photo-info h1:before,
+.jp-carousel-photo-info h1:after,
+.jp-carousel-left-column-wrapper h1:before,
+.jp-carousel-left-column-wrapper h1:after {
content:none !important;
}
/** Title and Desc End **/
@@ -631,6 +633,8 @@ a.jp-carousel-image-download:hover {
width:auto;
display: inline;
float:none;
+ border:none;
+ margin:0;
}
.jp-carousel-comment .comment-author a {
@@ -644,6 +648,7 @@ a.jp-carousel-image-download:hover {
.jp-carousel-comment .comment-content {
border:none;
margin-left:85px;
+ padding: 0;
}
.jp-carousel-comment .avatar {
@@ -1037,3 +1042,63 @@ textarea#jp-carousel-comment-form-comment-field:focus::-webkit-input-placeholder
background: -moz-linear-gradient(bottom, rgba(255,255,255,0.75), rgba(255,255,255,0));
background: -webkit-gradient(linear, left bottom, left top, from(rgba(255,255,255,0.75)), to(rgba(255,255,255,0)));
}
+
+/* Small screens */
+@media only screen and (max-width: 760px) {
+
+ .jp-carousel-info {
+ margin: 0 10px !important;
+ }
+
+ .jp-carousel-next-button, .jp-carousel-previous-button {
+ display: none !important;
+ }
+
+ .jp-carousel-buttons {
+ display: none !important;
+ }
+
+ .jp-carousel-image-meta {
+ float: none !important;
+ width: 100% !important;
+ -moz-box-sizing:border-box;
+ -webkit-box-sizing:border-box;
+ box-sizing: border-box;
+ }
+
+ .jp-carousel-close-hint {
+ font-weight: 800 !important;
+ font-size: 26px !important;
+ position: fixed !important;
+ top: -10px;
+ }
+
+ .jp-carousel-slide img {
+ filter: alpha(opacity=100);
+ opacity: 1;
+ }
+
+ .jp-carousel-wrap {
+ background-color: #000;
+ }
+
+ .jp-carousel-fadeaway {
+ display: none;
+ }
+
+ #jp-carousel-comment-form-container {
+ display: none !important;
+ }
+
+ .jp-carousel-titleanddesc {
+ padding-top: 0 !important;
+ border: none !important;
+ }
+ .jp-carousel-titleanddesc-title {
+ font-size: 1em !important;
+ }
+
+ .jp-carousel-left-column-wrapper {
+ padding: 0;
+ }
+}
diff --git a/plugins/jetpack/modules/carousel/jetpack-carousel.js b/plugins/jetpack/modules/carousel/jetpack-carousel.js
index 5760811e..43f1d1f7 100644
--- a/plugins/jetpack/modules/carousel/jetpack-carousel.js
+++ b/plugins/jetpack/modules/carousel/jetpack-carousel.js
@@ -4,7 +4,12 @@ jQuery(document).ready(function($) {
// gallery faded layer and container elements
var overlay, comments, gallery, container, nextButton, previousButton, info, title,
caption, resizeTimeout, mouseTimeout, photo_info, close_hint, commentInterval, buttons,
- screenPadding = 110, originalOverflow = $('body').css('overflow'), proportion = 85;
+ screenPadding = 110, originalOverflow = $('body').css('overflow'), originalHOverflow = $('html').css('overflow'), proportion = 85, isMobile;
+
+ isMobile = /Android|iPhone|iPod/i.test(navigator.userAgent);
+
+ if (isMobile)
+ screenPadding = 0;
var keyListener = function(e){
switch(e.which){
@@ -64,7 +69,7 @@ jQuery(document).ready(function($) {
buttons = '<a class="jp-carousel-commentlink" href="#">' + jetpackCarouselStrings.comment + '</a>';
buttons = $('<div class="jp-carousel-buttons">' + buttons + '</div>');
-
+
caption = $('<h2></h2>');
photo_info = $('<div class="jp-carousel-photo-info"></div>').append(caption);
@@ -127,7 +132,7 @@ jQuery(document).ready(function($) {
'bottom' : '10px',
'margin-top' : '20px'
});
-
+
leftWidth = ( $(window).width() - ( screenPadding * 2 ) ) - (imageMeta.width() + 40);
if ( $.browser.mozilla )
leftWidth -= 55;
@@ -135,6 +140,9 @@ jQuery(document).ready(function($) {
leftWidth -= 20;
leftWidth += 'px';
+ if (isMobile)
+ leftWidth = '100%';
+
leftColWrapper = $('<div></div>')
.addClass('jp-carousel-left-column-wrapper')
.css({
@@ -147,7 +155,7 @@ jQuery(document).ready(function($) {
fadeaway = $('<div></div>')
.addClass('jp-carousel-fadeaway');
-
+
info = $('<div></div>')
.addClass('jp-carousel-info')
.css({
@@ -159,6 +167,11 @@ jQuery(document).ready(function($) {
.append(imageMeta)
.append(leftColWrapper);
+ if (isMobile)
+ info.prepend(leftColWrapper);
+ else
+ info.append(leftColWrapper);
+
targetBottomPos = ( $(window).height() - parseInt( info.css('top'), 10 ) ) + 'px';
nextButton = $("<div><span></span></div>")
@@ -166,7 +179,7 @@ jQuery(document).ready(function($) {
.css({
'position' : 'fixed',
'top' : 0,
- 'right' : 0,
+ 'right' : '15px',
'bottom' : 0,
'width' : screenPadding
});
@@ -175,7 +188,7 @@ jQuery(document).ready(function($) {
'top' : '40px',
'bottom' : targetBottomPos
});
-
+
previousButton = $("<div><span></span></div>")
.addClass('jp-carousel-previous-button')
.css({
@@ -190,7 +203,7 @@ jQuery(document).ready(function($) {
'top' : '40px',
'bottom' : targetBottomPos
});
-
+
gallery = $('<div></div>')
.addClass('jp-carousel')
.css({
@@ -205,20 +218,20 @@ jQuery(document).ready(function($) {
.css({
position : 'fixed'
});
-
+
container = $("<div></div>")
.addClass('jp-carousel-wrap');
-
+
if ( 'white' == jetpackCarouselStrings.background_color )
container.addClass('jp-carousel-light');
-
+
container.css({
'position' : 'fixed',
'top' : 0,
'right' : 0,
'bottom' : 0,
'left' : 0,
- 'z-index' : 999999,
+ 'z-index' : 2147483647,
'overflow-x' : 'hidden',
'overflow-y' : 'auto',
'direction' : 'ltr'
@@ -315,7 +328,7 @@ jQuery(document).ready(function($) {
return;
}
}
-
+
$.ajax({
type: 'POST',
url: jetpackCarouselStrings.ajaxurl,
@@ -349,6 +362,7 @@ jQuery(document).ready(function($) {
.bind('jp_carousel.afterOpen', function(){
$(window).bind('keydown', keyListener);
$(window).bind('resize', resizeListener);
+ gallery.opened = true;
})
.bind('jp_carousel.beforeClose', function(){
var scroll = $(window).scrollTop();
@@ -357,6 +371,15 @@ jQuery(document).ready(function($) {
$(window).unbind('resize', resizeListener);
document.location.hash = '';
$(window).scrollTop(scroll);
+ gallery.opened = false;
+ });
+
+ $('.jp-carousel').touchwipe({
+ wipeLeft: function() { gallery.jp_carousel('next'); },
+ wipeRight: function() { gallery.jp_carousel('previous'); },
+ min_move_x: 20,
+ min_move_y: 20,
+ preventDefaultEvents: true
});
nextButton.add(previousButton).click(function(e){
@@ -372,9 +395,22 @@ jQuery(document).ready(function($) {
};
var methods = {
+ testForData: function(gallery) {
+ gallery = $( gallery ); // make sure we have it as a jQuery object.
+ if ( ! gallery.length || undefined == gallery.data( 'carousel-extra' ) )
+ return false;
+ return true;
+ },
+
+ testIfOpened: function() {
+ if ( 'undefined' != typeof(gallery) && 'undefined' != typeof(gallery.opened) && true == gallery.opened )
+ return true;
+ return false;
+ },
+
open: function(options) {
var settings = {
- 'items_selector' : ".gallery-item [data-attachment-id]",
+ 'items_selector' : ".gallery-item [data-attachment-id], .tiled-gallery-item [data-attachment-id]",
'start_index': 0
},
data = $(this).data('carousel-extra');
@@ -382,12 +418,19 @@ jQuery(document).ready(function($) {
if ( !data )
return; // don't run if the default gallery functions weren't used
+ prepareGallery();
+
+ if ( gallery.jp_carousel( 'testIfOpened' ) )
+ return; // don't open if already opened
+
// make sure to stop the page from scrolling behind the carousel overlay, so we don't trigger
// infiniscroll for it when enabled (Reader, theme infiniscroll, etc).
originalOverflow = $('body').css('overflow');
$('body').css('overflow', 'hidden');
-
- prepareGallery();
+ // prevent html from overflowing on some of the new themes.
+ originalHOverflow = $('html').css('overflow');
+ $('html').css('overflow', 'hidden');
+
container.data('carousel-extra', data);
return this.each(function() {
@@ -415,7 +458,7 @@ jQuery(document).ready(function($) {
if ( 0 === selected.length )
selected = slides.eq(0);
-
+
gallery.jp_carousel('selectSlide', selected, false);
return this;
},
@@ -423,6 +466,7 @@ jQuery(document).ready(function($) {
close : function(){
// make sure to let the page scroll again
$('body').css('overflow', originalOverflow);
+ $('html').css('overflow', originalHOverflow);
return container
.trigger('jp_carousel.beforeClose')
.fadeOut('fast', function(){
@@ -473,29 +517,21 @@ jQuery(document).ready(function($) {
gallery.jp_carousel('selectedSlide').removeClass('selected').css({'position': 'fixed'});
if (reverse !== true ) {
last = slides.last();
- slides.first().nextAll().not(last).css({'left':gallery.width()+slides.first().width()}).hide();
- last.css({
- 'left' : -last.width()
- });
- last.prev().css({
- 'left' : -last.width() - last.prev().width()
- });
- slides.first().css({'left':gallery.width()});
+ slides.first().nextAll().not(last).jp_carousel('setSlidePosition', gallery.width()+slides.first().width()).hide();
+ last.jp_carousel('setSlidePosition', -last.width());
+ last.prev().jp_carousel('setSlidePosition', -last.width() - last.prev().width());
+ slides.first().jp_carousel('setSlidePosition', gallery.width());
setTimeout(function(){
gallery.jp_carousel('selectSlide', slides.show().first());
}, 400);
} else {
first = slides.first();
- first.css({
- 'left':gallery.width()
- });
- first.next().css({
- 'left':gallery.width() + first.width()
- });
- first.next().nextAll().hide().css({'left':-slides.last().width()});
- slides.last().css({'left':-slides.last().width()});
- slides.last().prevAll().not(first, first.next()).hide().css({'left':-slides.last().width()-slides.last().prev().width()});
+ first.jp_carousel('setSlidePosition', gallery.width());
+ first.next().jp_carousel('setSlidePosition', gallery.width() + first.width());
+ first.next().nextAll().hide().jp_carousel('setSlidePosition', -slides.last().width());
+ slides.last().jp_carousel('setSlidePosition', -slides.last().width());
+ slides.last().prevAll().not(first, first.next()).hide().jp_carousel('setSlidePosition', -slides.last().width()-slides.last().prev().width());
setTimeout(function(){
gallery.jp_carousel('selectSlide', slides.show().last());
}, 400);
@@ -507,6 +543,16 @@ jQuery(document).ready(function($) {
return this.find('.selected');
},
+ setSlidePosition : function(x) {
+ return this.css({
+ '-webkit-transform':'translate3d(' + x + 'px,0,0)',
+ '-moz-transform':'translate3d(' + x + 'px,0,0)',
+ '-ms-transform':'translate(' + x + 'px,0)',
+ '-o-transform':'translate(' + x + 'px,0)',
+ 'transform':'translate3d(' + x + 'px,0,0)'
+ });
+ },
+
selectSlide : function(slide, animate){
var last = this.find('.selected').removeClass('selected'),
slides = gallery.jp_carousel('slides').css({'position': 'fixed'}),
@@ -521,7 +567,7 @@ jQuery(document).ready(function($) {
animated,
info_min;
// center the main image
-
+
caption.hide();
method = 'css';
@@ -534,7 +580,7 @@ jQuery(document).ready(function($) {
// slide the whole view to the x we want
slides.not(animated).hide();
- current[method]({left:left}).show();
+ current.jp_carousel('setSlidePosition', left).show();
// minimum width
gallery.jp_carousel('fitInfo', animate);
@@ -542,24 +588,24 @@ jQuery(document).ready(function($) {
// prep the slides
var direction = last.is(current.prevAll()) ? 1 : -1;
if ( 1 == direction ) {
- next_next.css({'left':gallery.width() + next.width()}).show();
- next.hide().css({'left':gallery.width() + current.width()}).show();
- previous_previous.css({'left':-previous_previous.width() - current.width()});
+ next_next.jp_carousel('setSlidePosition', gallery.width() + next.width()).show();
+ next.hide().jp_carousel('setSlidePosition', gallery.width() + current.width()).show();
+ previous_previous.jp_carousel('setSlidePosition', -previous_previous.width() - current.width()).show();
} else {
- previous.css({'left':-previous.width() - current.width()});
- next_next.css({'left':gallery.width() + current.width()});
+ previous.jp_carousel('setSlidePosition', -previous.width() - current.width()).show();
+ next_next.jp_carousel('setSlidePosition', gallery.width() + current.width()).show();
}
-
- // if advancing prepare the slide that will enter the screen
- previous[method]({left:-previous.width() + (screenPadding * 0.75) }).show();
- next[method]({left:gallery.width() - (screenPadding * 0.75) }).show();
+ // if advancing prepare the slide that will enter the screen
+ previous.jp_carousel('setSlidePosition', -previous.width() + (screenPadding * 0.75)).show();
+ next.jp_carousel('setSlidePosition', gallery.width() - (screenPadding * 0.75)).show();
+ next.css({'position': ''});
document.location.href = document.location.href.replace(/#.*/, '') + '#jp-carousel-' + current.data('attachment-id');
gallery.jp_carousel('resetButtons', current);
container.trigger('jp_carousel.selectSlide', [current]);
$( 'div.jp-carousel-image-meta', 'div.jp-carousel-wrap' ).html('');
-
+
gallery.jp_carousel('getTitleDesc', { title: current.data('title'), desc: current.data('desc') } );
gallery.jp_carousel('getMeta', current.data('image-meta'));
gallery.jp_carousel('getFullSizeLink', current);
@@ -568,7 +614,7 @@ jQuery(document).ready(function($) {
gallery.jp_carousel('getComments', {'attachment_id': current.data('attachment-id'), 'offset': 0, 'clear': true});
$('#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)
if ( current.data('caption') ) {
if ( $('<div />').html(current.data('caption')).text() == $('<div />').html(current.data('title')).text() )
@@ -593,7 +639,7 @@ jQuery(document).ready(function($) {
};
},
- loadSlide : function(){
+ loadSlide : function() {
return this.each(function(){
var slide = $(this);
slide.find('img')
@@ -642,13 +688,19 @@ jQuery(document).ready(function($) {
'left' : (info.width() - size.width) * 0.5,
'width' : size.width
});
+
+ if (isMobile){
+ photo_info.css('left', '0px');
+ photo_info.css('top', '-20px');
+ }
+
return this;
},
fitMeta : function(animated){
var newInfoTop = { top: ( $(window).height() / 100 * proportion + 5 ) + 'px' };
var newLeftWidth = { width: ( info.width() - (imageMeta.width() + 80) ) + 'px' };
-
+
if (animated) {
info.animate(newInfoTop);
leftColWrapper.animate(newLeftWidth);
@@ -666,27 +718,14 @@ jQuery(document).ready(function($) {
method = 'css',
max = gallery.jp_carousel('slideDimensions');
- if ( 0 === selected.length ) {
- dimensions.left = $(window).width();
- } else if ($this.is(selected)) {
- dimensions.left = ($(window).width() - dimensions.width) * 0.5;
- } else if ($this.is(selected.next())) {
- dimensions.left = gallery.width() - ( screenPadding * 0.75 );
- } else if ($this.is(selected.prev())) {
- dimensions.left = -dimensions.width + screenPadding * 0.75;
- } else {
- if ($this.is(selected.nextAll())) {
- dimensions.left = $(window).width();
- } else {
- dimensions.left = -dimensions.width;
- }
- }
+ dimensions.left = 0;
dimensions.top = ( (max.height - dimensions.height) * 0.5 ) + 40;
$this[method](dimensions);
});
},
texturize : function(text) {
+ text = new String(text); // make sure we get a string. Title "1" came in as int 1, for example, which did not support .replace().
text = text.replace(/'/g, '&#8217;').replace(/&#039;/g, '&#8217;').replace(/[\u2019]/g, '&#8217;');
text = text.replace(/"/g, '&#8221;').replace(/&#034;/g, '&#8221;').replace(/&quot;/g, '&#8221;').replace(/[\u201D]/g, '&#8221;');
text = text.replace(/([\w]+)=&#[\d]+;(.+?)&#[\d]+;/g, '$1="$2"'); // untexturize allowed HTML tags params double-quotes
@@ -700,7 +739,7 @@ jQuery(document).ready(function($) {
// Calculate the new src.
items.each(function(i){
var src_item = $(this),
- orig_size = src_item.data('orig-size') || 0,
+ orig_size = src_item.data('orig-size') || '',
max = gallery.jp_carousel('slideDimensions'),
parts = orig_size.split(',');
orig_size = {width: parseInt(parts[0], 10), height: parseInt(parts[1], 10)},
@@ -708,7 +747,7 @@ jQuery(document).ready(function($) {
large_file = src_item.data('large-file') || '';
src = src_item.data('orig-file');
-
+
src = gallery.jp_carousel('selectBestImageSize', {
orig_file : src,
orig_width : orig_size.width,
@@ -718,7 +757,7 @@ jQuery(document).ready(function($) {
medium_file : medium_file,
large_file : large_file
});
-
+
// Set the final src
$(this).data( 'gallery-src', src );
});
@@ -733,45 +772,49 @@ jQuery(document).ready(function($) {
attachment_id = src_item.data('attachment-id') || 0,
comments_opened = src_item.data('comments-opened') || 0,
image_meta = src_item.data('image-meta') || {},
- orig_size = src_item.data('orig-size') || 0,
- title = src_item.attr('title') || '',
+ orig_size = src_item.data('orig-size') || '',
+ title = src_item.data('image-title') || '',
description = src_item.data('image-description') || '',
caption = src_item.parents('dl').find('dd.gallery-caption').html() || '',
- src = src_item.data('gallery-src') || '',
+ src = src_item.data('gallery-src') || '',
medium_file = src_item.data('medium-file') || '',
- large_file = src_item.data('large-file') || '';
-
- if ( !attachment_id || !orig_size )
- return false; // break the loop if we are missing the data-* attributes
-
- title = gallery.jp_carousel('texturize', title);
- description = gallery.jp_carousel('texturize', description);
- caption = gallery.jp_carousel('texturize', caption);
-
- var slide = $('<div class="jp-carousel-slide"></div>')
- .hide()
- .css({
- 'position' : 'fixed',
- 'left' : i < start_index ? -1000 : gallery.width()
- })
- .append($('<img>'))
- .appendTo(gallery)
- .data('src', src )
- .data('title', title)
- .data('desc', description)
- .data('caption', caption)
- .data('attachment-id', attachment_id)
- .data('permalink', src_item.parents('a').attr('href'))
- .data('orig-size', orig_size)
- .data('comments-opened', comments_opened)
- .data('image-meta', image_meta)
- .data('medium-file', medium_file)
- .data('large-file', large_file)
- .jp_carousel('fitSlide', false);
-
-
- // Preloading all images
- slide.find('img').first().attr('src', src );
+ large_file = src_item.data('large-file') || '',
+ orig_file = src_item.data('orig-file') || '';
+
+ var tiledCaption = src_item.parents('div.tiled-gallery-item').find('div.tiled-gallery-caption').html();
+ if ( tiledCaption )
+ caption = tiledCaption;
+
+ if ( attachment_id && orig_size.length ) {
+ title = gallery.jp_carousel('texturize', title);
+ description = gallery.jp_carousel('texturize', description);
+ caption = gallery.jp_carousel('texturize', caption);
+
+ var slide = $('<div class="jp-carousel-slide"></div>')
+ .hide()
+ .css({
+ //'position' : 'fixed',
+ 'left' : i < start_index ? -1000 : gallery.width()
+ })
+ .append($('<img>'))
+ .appendTo(gallery)
+ .data('src', src )
+ .data('title', title)
+ .data('desc', description)
+ .data('caption', caption)
+ .data('attachment-id', attachment_id)
+ .data('permalink', src_item.parents('a').attr('href'))
+ .data('orig-size', orig_size)
+ .data('comments-opened', comments_opened)
+ .data('image-meta', image_meta)
+ .data('medium-file', medium_file)
+ .data('large-file', large_file)
+ .data('orig-file', orig_file)
+ .jp_carousel('fitSlide', false);
+
+ // Preloading all images
+ slide.find('img').first().attr('src', src );
+ }
});
return this;
},
@@ -779,13 +822,13 @@ jQuery(document).ready(function($) {
selectBestImageSize: function(args) {
if ( 'object' != typeof args )
args = {};
-
+
if ( 'undefined' == typeof args.orig_file )
return '';
-
+
if ( 'undefined' == typeof args.orig_width || 'undefined' == typeof args.max_width )
return args.orig_file;
-
+
if ( 'undefined' == typeof args.medium_file || 'undefined' == typeof args.large_file )
return args.orig_file;
@@ -797,19 +840,19 @@ jQuery(document).ready(function($) {
large_size_parts = (large_size != args.large_file) ? large_size.split('x') : [args.orig_width, 0],
large_width = parseInt( large_size_parts[0], 10 ),
large_height = parseInt( large_size_parts[1], 10 );
-
+
// Give devices with a higher devicePixelRatio higher-res images (Retina display = 2, Android phones = 1.5, etc)
if ('undefined' != typeof window.devicePixelRatio && window.devicePixelRatio > 1) {
args.max_width = args.max_width * window.devicePixelRatio;
args.max_height = args.max_height * window.devicePixelRatio;
}
- if ( medium_width >= args.max_width || medium_height >= args.max_height )
- return args.medium_file;
-
if ( large_width >= args.max_width || large_height >= args.max_height )
return args.large_file;
+ if ( medium_width >= args.max_width || medium_height >= args.max_height )
+ return args.medium_file;
+
return args.orig_file;
},
@@ -826,14 +869,14 @@ jQuery(document).ready(function($) {
return;
if ( ! args.replacements || 'undefined' == typeof args.replacements )
return args.text;
- return args.text.replace(/{(\d+)}/g, function(match, number) {
+ return args.text.replace(/{(\d+)}/g, function(match, number) {
return typeof args.replacements[number] != 'undefined' ? args.replacements[number] : match;
});
},
shutterSpeed: function(d) {
if (d >= 1)
- Math.round(d) + 's';
+ return Math.round(d) + 's';
var df = 1, top = 1, bot = 1;
var limit = 1e5; //Increase for greater precision.
while (df != d && limit-- > 0) {
@@ -887,16 +930,16 @@ jQuery(document).ready(function($) {
});
return value;
},
-
+
getTitleDesc: function( data ) {
var title ='', desc = '', markup = '', target, commentWrappere;
-
+
target = $( 'div.jp-carousel-titleanddesc', 'div.jp-carousel-wrap' );
target.hide();
-
+
title = gallery.jp_carousel('parseTitleDesc', data.title) || '';
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() )
@@ -911,16 +954,16 @@ jQuery(document).ready(function($) {
$( 'div#jp-carousel-comment-form-container' ).css('margin-top', '20px');
$( 'div#jp-carousel-comments-loading' ).css('margin-top', '20px');
},
-
+
getMeta: function( meta ) {
if ( !meta || 1 != jetpackCarouselStrings.display_exif )
return false;
-
+
var $ul = $( '<ul></ul>' );
$.each( meta, function( key, val ) {
if ( 0 === parseFloat(val) || !val.length || -1 === $.inArray( key, [ 'camera', 'aperture', 'shutter_speed', 'focal_length' ] ) )
return;
-
+
switch( key ) {
case 'focal_length':
val = val + 'mm';
@@ -935,7 +978,7 @@ jQuery(document).ready(function($) {
// making jslint happy
break;
}
-
+
$ul.append( '<li><h5>' + jetpackCarouselStrings[key] + '</h5>' + val + '</li>' );
});
@@ -949,23 +992,23 @@ jQuery(document).ready(function($) {
getFullSizeLink: function(current) {
if(!current || !current.data)
return false;
- var original = current.data('src').replace(/\?.+$/, ''),
+ var original = current.data('orig-file').replace(/\?.+$/, ''),
origSize = current.data('orig-size').split(','),
permalink = $( '<a>'+gallery.jp_carousel('format', {'text': jetpackCarouselStrings.download_original, 'replacements': origSize})+'</a>' )
.addClass( 'jp-carousel-image-download' )
.attr( 'href', original )
.attr( 'target', '_blank' );
-
+
$( 'div.jp-carousel-image-meta', 'div.jp-carousel-wrap' )
.append( permalink );
},
-
+
getMap: function( meta ) {
if ( !meta.latitude || !meta.longitude || 1 != jetpackCarouselStrings.display_geo )
return;
-
- var latitude = meta.latitude,
- longitude = meta.longitude,
+
+ var latitude = meta.latitude,
+ longitude = meta.longitude,
$metabox = $( 'div.jp-carousel-image-meta', 'div.jp-carousel-wrap' ),
$mapbox = $( '<div></div>' ),
style = '&scale=2&style=feature:all|element:all|invert_lightness:true|hue:0x0077FF|saturation:-50|lightness:-5|gamma:0.91';
@@ -1003,23 +1046,23 @@ jQuery(document).ready(function($) {
getComments: function( args ) {
if ( 'object' != typeof args )
args = {};
-
+
if ( ! args.attachment_id || 'undefined' == typeof args.attachment_id )
return;
-
+
if ( ! args.offset || 'undefined' == typeof args.offset || args.offset < 1 )
args.offset = 0;
-
+
var comments = $('.jp-carousel-comments'),
commentsLoading = $('#jp-carousel-comments-loading');
-
+
commentsLoading.show();
-
+
if ( args.clear ) {
comments.hide();
comments.empty();
}
-
+
$.ajax({
type: 'GET',
url: jetpackCarouselStrings.ajaxurl,
@@ -1056,7 +1099,7 @@ jQuery(document).ready(function($) {
+ '</div>'
);
comments.append(comment);
-
+
// Set the interval to check for a new page of comments.
clearInterval( commentInterval );
commentInterval = setInterval( function() {
@@ -1066,7 +1109,7 @@ jQuery(document).ready(function($) {
}
}, 150 );
});
-
+
// Verify (late) that the user didn't repeatldy click the arrows really fast, in which case the requested
// attachment id might no longer match the current attachment id by the time we get the data back or a now
// registered infiniscroll event kicks in, so we don't ever display comments for the wrong image by mistake.
@@ -1079,7 +1122,7 @@ jQuery(document).ready(function($) {
// Increase the height of the background, semi-transparent overlay to match the new length of the comments list.
$('.jp-carousel-overlay').height( $(window).height() + titleAndDescription.height() + commentForm.height() + ( (comments.height() > 0) ? comments.height() : imageMeta.height() ) + 200 );
-
+
comments.show();
commentsLoading.hide();
},
@@ -1130,18 +1173,28 @@ jQuery(document).ready(function($) {
};
- // register the event listener for staring the gallery
- $( document.body ).on( 'click', 'div.gallery', function(e) {
+ // register the event listener for starting the gallery
+ $( document.body ).on( 'click', 'div.gallery,div.tiled-gallery', function(e) {
+ if ( ! $(this).jp_carousel( 'testForData', e.currentTarget ) )
+ return;
if ( $(e.target).parent().hasClass('gallery-caption') )
return;
e.preventDefault();
- $(this).jp_carousel('open', {start_index: $(this).find('.gallery-item').index($(e.target).parents('.gallery-item'))});
+ $(this).jp_carousel('open', {start_index: $(this).find('.gallery-item, .tiled-gallery-item').index($(e.target).parents('.gallery-item, .tiled-gallery-item'))});
});
- // start on page load if hash exists
- if ( document.location.hash && document.location.hash.match(/jp-carousel-(\d+)/) ) {
- $(document).ready(function(){
- var gallery = $('div.gallery'), index = -1, n = document.location.hash.match(/jp-carousel-(\d+)/);
+ // Set an interval on page load to load the carousel if hash exists and not already opened.
+ // Makes carousel work on page load and when back button leads to same URL with carousel hash (ie: no actual document.ready trigger)
+ $(document).ready(function(){
+ var jp_carousel_open_interval = window.setInterval(function(){
+ // We should have a URL hash by now.
+ if ( ! document.location.hash || ! document.location.hash.match(/jp-carousel-(\d+)/) )
+ return;
+
+ var gallery = $('div.gallery, div.tiled-gallery'), index = -1, n = document.location.hash.match(/jp-carousel-(\d+)/);
+
+ if ( ! $(this).jp_carousel( 'testForData', gallery ) )
+ return;
n = parseInt(n[1], 10);
@@ -1153,7 +1206,10 @@ jQuery(document).ready(function($) {
});
if ( index != -1 )
- gallery.jp_carousel('open', {start_index: index});
- });
- }
+ gallery.jp_carousel('open', {start_index: index}); // open method checks if already opened
+ }, 1000);
+ });
});
+
+// Swipe gesture detection
+(function($){$.fn.touchwipe=function(settings){var config={min_move_x:20,min_move_y:20,wipeLeft:function(){},wipeRight:function(){},wipeUp:function(){},wipeDown:function(){},preventDefaultEvents:true};if(settings)$.extend(config,settings);this.each(function(){var startX;var startY;var isMoving=false;function cancelTouch(){this.removeEventListener('touchmove',onTouchMove);startX=null;isMoving=false}function onTouchMove(e){if(config.preventDefaultEvents){e.preventDefault()}if(isMoving){var x=e.touches[0].pageX;var y=e.touches[0].pageY;var dx=startX-x;var dy=startY-y;if(Math.abs(dx)>=config.min_move_x){cancelTouch();if(dx>0){config.wipeLeft()}else{config.wipeRight()}}else if(Math.abs(dy)>=config.min_move_y){cancelTouch();if(dy>0){config.wipeDown()}else{config.wipeUp()}}}}function onTouchStart(e){if(e.touches.length==1){startX=e.touches[0].pageX;startY=e.touches[0].pageY;isMoving=true;this.addEventListener('touchmove',onTouchMove,false)}}if('ontouchstart'in document.documentElement){this.addEventListener('touchstart',onTouchStart,false)}});return this}})(jQuery);
diff --git a/plugins/jetpack/modules/carousel/jetpack-carousel.php b/plugins/jetpack/modules/carousel/jetpack-carousel.php
index 3d48681b..314dd7b2 100644
--- a/plugins/jetpack/modules/carousel/jetpack-carousel.php
+++ b/plugins/jetpack/modules/carousel/jetpack-carousel.php
@@ -76,7 +76,7 @@ class Jetpack_Carousel {
}
function enqueue_assets( $output ) {
- if ( ! empty( $output ) ) {
+ if ( ! empty( $output ) && ! apply_filters( 'jp_carousel_force_enable', false ) ) {
// Bail because someone is overriding the [gallery] shortcode.
remove_filter( 'gallery_style', array( $this, 'add_data_to_container' ) );
remove_filter( 'wp_get_attachment_link', array( $this, 'add_data_to_images' ) );
@@ -86,12 +86,7 @@ class Jetpack_Carousel {
do_action( 'jp_carousel_thumbnails_shown' );
if ( $this->first_run ) {
- if ( ! has_action( 'wp_enqueue_scripts', 'register_spin_scripts' ) ) {
- wp_enqueue_script( 'spin', plugins_url( 'spin.js', __FILE__ ), false, '1.2.4' );
- wp_enqueue_script( 'jquery.spin', plugins_url( 'jquery.spin.js', __FILE__ ) , array( 'jquery', 'spin' ) );
- }
-
- wp_enqueue_script( 'jetpack-carousel', plugins_url( 'jetpack-carousel.js', __FILE__ ), array( 'jquery' ), $this->asset_version( '20120629' ), true );
+ wp_enqueue_script( 'jetpack-carousel', plugins_url( 'jetpack-carousel.js', __FILE__ ), array( 'jquery.spin' ), $this->asset_version( '20130109' ), true );
// Note: using home_url() instead of admin_url() for ajaxurl to be sure to get same domain on wpcom when using mapped domains (also works on self-hosted)
// Also: not hardcoding path since there is no guarantee site is running on site root in self-hosted context.
@@ -140,7 +135,14 @@ class Jetpack_Carousel {
$localize_strings = apply_filters( 'jp_carousel_localize_strings', $localize_strings );
wp_localize_script( 'jetpack-carousel', 'jetpackCarouselStrings', $localize_strings );
wp_enqueue_style( 'jetpack-carousel', plugins_url( 'jetpack-carousel.css', __FILE__ ), array(), $this->asset_version( '20120629' ) );
-
+ global $is_IE;
+ if( $is_IE )
+ {
+ $msie = strpos( $_SERVER['HTTP_USER_AGENT'], 'MSIE' ) + 4;
+ $version = (float) substr( $_SERVER['HTTP_USER_AGENT'], $msie, strpos( $_SERVER['HTTP_USER_AGENT'], ';', $msie ) - $msie );
+ if( $version < 9 )
+ wp_enqueue_style( 'jetpack-carousel-ie8fix', plugins_url( 'jetpack-carousel-ie8fix.css', __FILE__ ), array(), $this->asset_version( '20121024' ) );
+ }
do_action( 'jp_carousel_enqueue_assets', $this->first_run, $localize_strings );
$this->first_run = false;
@@ -154,10 +156,11 @@ class Jetpack_Carousel {
return $html;
$attachment_id = intval( $attachment_id );
- $orig_file = wp_get_attachment_url( $attachment_id );
+ $orig_file = wp_get_attachment_image_src( $attachment_id, 'full' );
+ $orig_file = isset( $orig_file[0] ) ? $orig_file[0] : wp_get_attachment_url( $attachment_id );
$meta = wp_get_attachment_metadata( $attachment_id );
$size = isset( $meta['width'] ) ? intval( $meta['width'] ) . ',' . intval( $meta['height'] ) : '';
- $img_meta = $meta['image_meta'];
+ $img_meta = ( ! empty( $meta['image_meta'] ) ) ? (array) $meta['image_meta'] : array();
$comments_opened = intval( comments_open( $attachment_id ) );
/*
@@ -167,10 +170,10 @@ class Jetpack_Carousel {
* $content_width has no filter we could temporarily de-register, run wp_get_attachment_image_src(), then
* re-register. So using returned file URL instead, which we can define the sizes from through filename
* parsing in the JS, as this is a failsafe file reference.
- *
+ *
* EG with Twenty Eleven activated:
* array(4) { [0]=> string(82) "http://vanillawpinstall.blah/wp-content/uploads/2012/06/IMG_3534-1024x764.jpg" [1]=> int(584) [2]=> int(435) [3]=> bool(true) }
- *
+ *
* EG with Twenty Ten activated:
* array(4) { [0]=> string(82) "http://vanillawpinstall.blah/wp-content/uploads/2012/06/IMG_3534-1024x764.jpg" [1]=> int(640) [2]=> int(477) [3]=> bool(true) }
*/
@@ -178,11 +181,12 @@ class Jetpack_Carousel {
$medium_file_info = wp_get_attachment_image_src( $attachment_id, 'medium' );
$medium_file = isset( $medium_file_info[0] ) ? $medium_file_info[0] : '';
- $large_file_info = wp_get_attachment_image_src( $attachment_id, 'large' );
- $large_file = isset( $large_file_info[0] ) ? $large_file_info[0] : '';
+ $large_file_info = wp_get_attachment_image_src( $attachment_id, 'large' );
+ $large_file = isset( $large_file_info[0] ) ? $large_file_info[0] : '';
- $attachment = get_post( $attachment_id );
- $attachment_desc = wpautop( wptexturize( $attachment->post_content ) );
+ $attachment = get_post( $attachment_id );
+ $attachment_title = wptexturize( $attachment->post_title );
+ $attachment_desc = wpautop( wptexturize( $attachment->post_content ) );
// Not yet providing geo-data, need to "fuzzify" for privacy
if ( ! empty( $img_meta ) ) {
@@ -192,17 +196,18 @@ class Jetpack_Carousel {
}
}
- $img_meta = json_encode( $img_meta );
+ $img_meta = json_encode( array_map( 'strval', $img_meta ) );
$html = str_replace(
'<img ',
sprintf(
- '<img data-attachment-id="%1$d" data-orig-file="%2$s" data-orig-size="%3$s" data-comments-opened="%4$s" data-image-meta="%5$s" data-image-description="%6$s" data-medium-file="%7$s" data-large-file="%8$s" ',
+ '<img data-attachment-id="%1$d" data-orig-file="%2$s" data-orig-size="%3$s" data-comments-opened="%4$s" data-image-meta="%5$s" data-image-title="%6$s" data-image-description="%7$s" data-medium-file="%8$s" data-large-file="%9$s" ',
$attachment_id,
esc_attr( $orig_file ),
$size,
$comments_opened,
esc_attr( $img_meta ),
+ esc_attr( $attachment_title ),
esc_attr( $attachment_desc ),
esc_attr( $medium_file ),
esc_attr( $large_file )
@@ -230,24 +235,24 @@ class Jetpack_Carousel {
return $html;
}
-
+
function get_attachment_comments() {
if ( ! headers_sent() )
header('Content-type: text/javascript');
-
+
do_action('jp_carousel_check_blog_user_privileges');
-
+
$attachment_id = ( isset( $_REQUEST['id'] ) ) ? (int) $_REQUEST['id'] : 0;
$offset = ( isset( $_REQUEST['offset'] ) ) ? (int) $_REQUEST['offset'] : 0;
-
+
if ( ! $attachment_id ) {
echo json_encode( __( 'Missing attachment ID.', 'jetpack' ) );
die();
}
-
+
if ( $offset < 1 )
$offset = 0;
-
+
$comments = get_comments( array(
'status' => 'approve',
'order' => ( 'asc' == get_option('comment_order') ) ? 'ASC' : 'DESC',
@@ -255,9 +260,9 @@ class Jetpack_Carousel {
'offset' => $offset,
'post_id' => $attachment_id,
) );
-
+
$out = array();
-
+
// Can't just send the results, they contain the commenter's email address.
foreach ( $comments as $comment ) {
$author_markup = '<a href="' . esc_url( $comment->comment_author_url ) . '">' . esc_html( $comment->comment_author ) . '</a>';
@@ -270,42 +275,42 @@ class Jetpack_Carousel {
'content' => wpautop($comment->comment_content),
);
}
-
+
die( json_encode( $out ) );
}
function post_attachment_comment() {
if ( ! headers_sent() )
header('Content-type: text/javascript');
-
+
if ( empty( $_POST['nonce'] ) || ! wp_verify_nonce($_POST['nonce'], 'carousel_nonce') )
die( json_encode( array( 'error' => __( 'Nonce verification failed.', 'jetpack' ) ) ) );
-
+
$_blog_id = (int) $_POST['blog_id'];
$_post_id = (int) $_POST['id'];
$comment = $_POST['comment'];
-
+
if ( empty( $_blog_id ) )
die( json_encode( array( 'error' => __( 'Missing target blog ID.', 'jetpack' ) ) ) );
-
+
if ( empty( $_post_id ) )
die( json_encode( array( 'error' => __( 'Missing target post ID.', 'jetpack' ) ) ) );
-
+
if ( empty( $comment ) )
die( json_encode( array( 'error' => __( 'No comment text was submitted.', 'jetpack' ) ) ) );
// Used in context like NewDash
$switched = false;
- if ( $_blog_id != get_current_blog_id() ) {
+ if ( is_multisite() && $_blog_id != get_current_blog_id() ) {
switch_to_blog( $_blog_id );
$switched = true;
}
-
+
do_action('jp_carousel_check_blog_user_privileges');
if ( ! comments_open( $_post_id ) )
die( json_encode( array( 'error' => __( 'Comments on this post are closed.', 'jetpack' ) ) ) );
-
+
if ( is_user_logged_in() ) {
$user = wp_get_current_user();
$user_id = $user->ID;
@@ -353,10 +358,10 @@ class Jetpack_Carousel {
die( json_encode( array( 'comment_id' => $comment_id, 'comment_status' => $comment_status ) ) );
}
-
+
function register_settings() {
add_settings_section('carousel_section', __( 'Image Gallery Carousel', 'jetpack' ), array( $this, 'carousel_section_callback' ), 'media');
-
+
if ( ! $this->in_jetpack ) {
add_settings_field('carousel_enable_it', __( 'Enable carousel', 'jetpack' ), array( $this, 'carousel_enable_it_callback' ), 'media', 'carousel_section' );
register_setting( 'media', 'carousel_enable_it', array( $this, 'carousel_enable_it_sanitize' ) );
@@ -364,7 +369,7 @@ class Jetpack_Carousel {
add_settings_field('carousel_background_color', __( 'Background color', 'jetpack' ), array( $this, 'carousel_background_color_callback' ), 'media', 'carousel_section' );
register_setting( 'media', 'carousel_background_color', array( $this, 'carousel_background_color_sanitize' ) );
-
+
add_settings_field('carousel_display_exif', __( 'Metadata', 'jetpack'), array( $this, 'carousel_display_exif_callback' ), 'media', 'carousel_section' );
register_setting( 'media', 'carousel_display_exif', array( $this, 'carousel_display_exif_sanitize' ) );
@@ -386,7 +391,7 @@ class Jetpack_Carousel {
}
return ( 1 == $value ) ? 1 : 0;
}
-
+
function sanitize_1or0_option( $value ) {
return ( 1 == $value ) ? 1 : 0;
}
@@ -435,7 +440,7 @@ class Jetpack_Carousel {
function carousel_display_geo_sanitize( $value ) {
return $this->sanitize_1or0_option( $value );
- }
+ }
function carousel_background_color_callback() {
$this->settings_select( 'carousel_background_color', array( 'black' => __( 'Black', 'jetpack' ), 'white' => __( 'White', 'jetpack', 'jetpack' ) ) );
diff --git a/plugins/jetpack/modules/carousel/rtl/jetpack-carousel-rtl.css b/plugins/jetpack/modules/carousel/rtl/jetpack-carousel-rtl.css
new file mode 100644
index 00000000..0d010eb5
--- /dev/null
+++ b/plugins/jetpack/modules/carousel/rtl/jetpack-carousel-rtl.css
@@ -0,0 +1,1106 @@
+/* This file was automatically generated on Jan 29 2013 22:51:19 */
+
+* {
+ line-height:inherit; /* prevent declarations of line-height in the universal selector */
+}
+
+.jp-carousel-overlay {
+ background: #000;
+}
+
+div.jp-carousel-fadeaway {
+ background: -moz-linear-gradient(bottom, rgba(0,0,0,0.5), rgba(0,0,0,0));
+ background: -webkit-gradient(linear, right bottom, right top, from(rgba(0,0,0,0.5)), to(rgba(0,0,0,0)));
+ position: fixed;
+ bottom: 0;
+ z-index: 2147483647;
+ width: 100%;
+ height: 15px;
+}
+
+.jp-carousel-next-button span,
+.jp-carousel-previous-button span {
+ background: url(.././images/arrows.png) no-repeat center center;
+ background-size: 200px 126px;
+}
+
+@media
+only screen and (-webkit-min-device-pixel-ratio: 1.5),
+only screen and (-o-min-device-pixel-ratio: 3/2),
+only screen and (min--moz-device-pixel-ratio: 1.5),
+only screen and (min-device-pixel-ratio: 1.5) {
+ .jp-carousel-next-button span,
+ .jp-carousel-previous-button span {
+ background-image: url(.././images/arrows-2x.png);
+ }
+}
+
+.jp-carousel-wrap {
+ font-family: "Helvetica Neue", sans-serif !important;
+}
+
+.jp-carousel-info {
+ position: absolute;
+ bottom: 0;
+ text-align: right !important;
+ -webkit-font-smoothing: subpixel-antialiased !important;
+}
+
+.jp-carousel-info ::selection {
+ background: #68c9e8; /* Safari */
+ color: #fff;
+ }
+
+.jp-carousel-info ::-moz-selection {
+ background: #68c9e8; /* Firefox */
+ color: #fff;
+}
+
+.jp-carousel-photo-info {
+ position: relative;
+ -webkit-transition: 400ms ease-out;
+ -moz-transition: 400ms ease-out;
+ -o-transition: 400ms ease-out;
+ transition: 400ms ease-out;
+ right: 25%;
+ width: 50%;
+}
+
+.jp-carousel-info h2 {
+ background: none !important;
+ border: none !important;
+ color: #999;
+ display: block !important;
+ font: normal 13px/1.25em "Helvetica Neue", sans-serif !important;
+ letter-spacing: 0 !important;
+ margin: 7px 0 0 0 !important;
+ padding: 10px 0 0 !important;
+ overflow: hidden;
+ text-align: right;
+ text-shadow: none !important;
+ text-transform: none !important;
+ -webkit-font-smoothing: subpixel-antialiased;
+}
+
+.jp-carousel-next-button,
+.jp-carousel-previous-button {
+ text-indent: -9999px;
+ overflow: hidden;
+ cursor: pointer;
+}
+
+.jp-carousel-next-button span,
+.jp-carousel-previous-button span {
+ position: absolute;
+ top: 0;
+ bottom: 0;
+ width: 82px;
+ zoom: 1;
+ filter: alpha(opacity=20);
+ opacity: 0.2;
+ -webkit-transition: 500ms opacity ease-out;
+ -moz-transition: 500ms opacity ease-out;
+ -o-transition: 500ms opacity ease-out;
+ transition: 500ms opacity ease-out;
+}
+
+.jp-carousel-next-button:hover span,
+.jp-carousel-previous-button:hover span {
+ filter: alpha(opacity=60);
+ opacity: 0.6;
+}
+.jp-carousel-next-button span {
+ background-position: -110px center;
+ left: 0;
+}
+
+.jp-carousel-previous-button span {
+ background-position: -10px center;
+ right:0;
+}
+
+.jp-carousel-buttons {
+ margin:-18px -20px 15px;
+ padding:8px 10px;
+ border-bottom:1px solid #222;
+ background: #222;
+ text-align: center;
+}
+
+div.jp-carousel-buttons a {
+ border: none !important;
+ color: #999;
+ font: normal 11px/1.2em "Helvetica Neue", sans-serif !important;
+ letter-spacing: 0 !important;
+ padding: 5px 0 5px 2px;
+ text-decoration: none !important;
+ text-shadow: none !important;
+ vertical-align: baseline !important;
+ -webkit-font-smoothing: subpixel-antialiased;
+}
+
+div.jp-carousel-buttons a:hover {
+ color: #68c9e8;
+ border: none !important;
+ -webkit-transition: none !important;
+ -moz-transition: none !important;
+ -o-transition: none !important;
+ transition: none !important;
+}
+
+.jp-carousel-slide, .jp-carousel-slide img, .jp-carousel-next-button,
+.jp-carousel-previous-button {
+ -webkit-transform:translate3d(0, 0, 0);
+ -moz-transform:translate3d(0, 0, 0);
+ -o-transform:translate3d(0, 0, 0);
+ -ms-transform:translate3d(0, 0, 0);
+}
+
+.jp-carousel-slide {
+ position:absolute;
+ width:0;
+ bottom:0;
+ background-color:#000;
+ border-radius:2px;
+ -webkit-border-radius:2px;
+ -moz-border-radius:2px;
+ -ms-border-radius:2px;
+ -o-border-radius:2px;
+ -webkit-transition: 400ms ease-out;
+ -moz-transition: 400ms ease-out;
+ -o-transition: 400ms ease-out;
+ transition: 400ms ease-out;
+}
+
+.jp-carousel-slide img {
+ display: block;
+ width: 100% !important;
+ height: 100% !important;
+ max-width: 100% !important;
+ max-height: 100% !important;
+ background: none !important;
+ border: none !important;
+ padding: 0 !important;
+ -webkit-box-shadow: 0 2px 8px rgba(0,0,0,0.1);
+ -moz-box-shadow: 0 2px 8px rgba(0,0,0,0.1);
+ box-shadow: 0 2px 8px rgba(0,0,0,0.1);
+ zoom: 1;
+ filter: alpha(opacity=25);
+ opacity: 0.25;
+ -webkit-transition: opacity 400ms linear;
+ -moz-transition: opacity 400ms linear;
+ -o-transition: opacity 400ms linear;
+ transition: opacity 400ms linear;
+}
+
+.jp-carousel-slide.selected img {
+ filter: alpha(opacity=100);
+ opacity: 1;
+}
+
+.jp-carousel-close-hint {
+ color: #999;
+ cursor: default;
+ letter-spacing: 0 !important;
+ padding:0.35em 0 0;
+ position: absolute;
+ text-align: right;
+ width: 90%;
+ -webkit-transition: color 200ms linear;
+ -moz-transition: color 200ms linear;
+ -o-transition: color 200ms linear;
+ transition: color 200ms linear;
+}
+
+.jp-carousel-close-hint span {
+ cursor: pointer;
+ background-color: black;
+ background-color: rgba(0,0,0,0.8);
+ display: block;
+ height: 22px;
+ font: 400 24px/1 "Helvetica Neue", sans-serif !important;
+ line-height: 22px;
+ margin: 0 0.4em 0 0;
+ text-align: center;
+ vertical-align: middle;
+ width: 22px;
+ -moz-border-radius: 4px;
+ -webkit-border-radius: 4px;
+ border-radius: 4px;
+ -webkit-transition: border-color 200ms linear;
+ -moz-transition: border-color 200ms linear;
+ -o-transition: border-color 200ms linear;
+ transition: border-color 200ms linear;
+}
+
+.jp-carousel-close-hint:hover {
+ cursor: default;
+ color: #fff;
+}
+
+.jp-carousel-close-hint:hover span {
+ border-color: #fff;
+}
+
+div.jp-carousel-buttons a.jp-carousel-like,
+div.jp-carousel-buttons a.jp-carousel-reblog,
+div.jp-carousel-buttons a.jp-carousel-commentlink,
+a.jp-carousel-image-download {
+ background: url(.././images/carousel-sprite.png?4) no-repeat;
+ background-size: 16px 160px;
+}
+
+div.jp-carousel-buttons a.jp-carousel-reblog,
+div.jp-carousel-buttons a.jp-carousel-commentlink {
+ margin:0 0 0 14px !important;
+}
+
+div.jp-carousel-buttons a.jp-carousel-reblog.reblogged,
+div.jp-carousel-buttons a.jp-carousel-like.liked {
+ background-color: #303030;
+ padding-left: 8px !important;
+ border-radius: 2px;
+ border-radius:2px;
+ -webkit-border-radius:2px;
+ -moz-border-radius:2px;
+ -ms-border-radius:2px;
+ -o-border-radius:2px;
+}
+
+div.jp-carousel-buttons a.jp-carousel-reblog.reblogged {
+ margin:0 -12px 0 2px !important;
+}
+
+
+div.jp-carousel-buttons a.jp-carousel-reblog,
+div.jp-carousel-buttons a.jp-carousel-reblog.reblogged:hover {
+ background-position: 6px -36px;
+ padding-right: 26px !important;
+ color: #999;
+}
+
+div.jp-carousel-buttons a.jp-carousel-commentlink {
+ background-position: 0px -116px;
+ padding-right: 19px !important;
+}
+
+div.jp-carousel-buttons a.jp-carousel-reblog.reblogged:hover {
+ cursor: default;
+}
+
+div.jp-carousel-buttons a.jp-carousel-reblog:hover {
+ background-position: 6px -56px;
+ color: #68c9e8;
+}
+
+div.jp-carousel-buttons a.jp-carousel-like {
+ background-position: 5px 5px;
+ padding-right: 24px !important;
+}
+
+div.jp-carousel-buttons a.jp-carousel-like:hover {
+ background-position: 5px -15px;
+}
+
+@media
+only screen and (-webkit-min-device-pixel-ratio: 1.5),
+only screen and (-o-min-device-pixel-ratio: 3/2),
+only screen and (min--moz-device-pixel-ratio: 1.5),
+only screen and (min-device-pixel-ratio: 1.5) {
+ div.jp-carousel-buttons a.jp-carousel-like,
+ div.jp-carousel-buttons a.jp-carousel-reblog,
+ div.jp-carousel-buttons a.jp-carousel-commentlink,
+ a.jp-carousel-image-download {
+ background-image: url(.././images/carousel-sprite-2x.png?4);
+ }
+}
+
+/* reblog */
+div#carousel-reblog-box {
+ background: #222;
+ background: -moz-linear-gradient(bottom, #222, #333);
+ background: -webkit-gradient(linear, right bottom, right top, from(#222), to(#333));
+ padding: 3px 0 0;
+ display: none;
+ margin: 5px auto 0;
+ -moz-border-radius: 2px;
+ -webkit-border-radius: 2px;
+ border-radius: 2px;
+ -webkit-box-shadow: 0 0 20px rgba(0,0,0,0.9);
+ -moz-box-shadow: 0 0 20px rgba(0,0,0,0.9);
+ box-shadow: 0 0 20px rgba(0,0,0,0.9);
+ height: 74px;
+ width: 565px;
+}
+
+#carousel-reblog-box textarea {
+ background: #999;
+ font: 13px/1.4 "Helvetica Neue", sans-serif !important;
+ color: #444;
+ padding: 3px 6px;
+ width: 370px;
+ height: 48px;
+ float: right;
+ margin: 6px 9px 0 9px;
+ border: 1px solid #666;
+ -webkit-box-shadow: inset 2px 2px 2px rgba(0,0,0,0.2);
+ box-shadow: inset 2px 2px 2px rgba(0,0,0,0.2);
+ -moz-border-radius: 2px;
+ -webkit-border-radius: 2px;
+ border-radius: 2px;
+}
+
+#carousel-reblog-box textarea:focus {
+ background: #ccc;
+ color: #222;
+}
+
+#carousel-reblog-box label {
+ color: #aaa;
+ font-size: 11px;
+ padding-left: 2px;
+ padding-right: 2px;
+ display: inline;
+ font-weight: normal;
+}
+
+#carousel-reblog-box select {
+ width: 110px;
+ padding: 0;
+ font-size: 12px;
+ font-family: "Helvetica Neue", sans-serif !important;
+ background: #333;
+ color: #eee;
+ border: 1px solid #444;
+ margin-top:5px;
+}
+
+#carousel-reblog-box .submit,
+#wrapper #carousel-reblog-box p.response {
+ float: right;
+ width: 154px;
+ padding-top: 0;
+ padding-right: 1px;
+ overflow: hidden;
+ height: 34px;
+ margin:3px 2px 0 0 !important;
+}
+
+#wrapper #carousel-reblog-box p.response {
+ font-size: 13px;
+ clear: none;
+ padding-right: 2px;
+ height: 34px;
+ color: #aaa;
+}
+
+#carousel-reblog-box input#carousel-reblog-submit, #jp-carousel-comment-form-button-submit {
+ font: 13px/24px "Helvetica Neue", sans-serif !important;
+ margin-top: 8px;
+ padding: 0 10px !important;
+ border-radius: 1em;
+ height: 24px;
+ color: #333;
+ cursor:pointer;
+ font-weight: normal;
+ background: #aaa;
+ background: -moz-linear-gradient(bottom, #aaa, #ccc);
+ background: -webkit-gradient(linear, right bottom, right top, from(#aaa), to(#ccc));
+ border: 1px solid #444;
+}
+
+#carousel-reblog-box input#carousel-reblog-submit:hover, #jp-carousel-comment-form-button-submit:hover {
+ background: #ccc;
+ background: -moz-linear-gradient(bottom, #ccc, #eee);
+ background: -webkit-gradient(linear, right bottom, right top, from(#ccc), to(#eee));
+}
+
+#carousel-reblog-box .canceltext {
+ color: #aaa;
+ font-size: 11px;
+ line-height: 24px;
+}
+
+#carousel-reblog-box .canceltext a {
+ color: #fff;
+}
+/* reblog end */
+
+
+/** Title and Desc Start **/
+.jp-carousel-titleanddesc {
+ border-top: 1px solid #222;
+ color: #999;
+ font-size: 15px;
+ padding-top: 24px;
+ margin-bottom: 20px;
+ font-weight:400;
+}
+.jp-carousel-titleanddesc-title {
+ font: 300 1.5em/1.1 "Helvetica Neue", sans-serif !important;
+ text-transform: none !important; /* prevents uppercase from leaking through */
+ color: #fff;
+ margin: 0 0 15px;
+ padding:0;
+}
+
+.jp-carousel-titleanddesc-desc p {
+ color: #999;
+ line-height:1.4;
+ margin-bottom: 0.75em;
+}
+
+.jp-carousel-titleanddesc p a,
+.jp-carousel-comments p a,
+.jp-carousel-info h2 a {
+ color: #fff !important;
+ border: none !important;
+ text-decoration: underline !important;
+ font-weight: normal !important;
+ font-style: normal !important;
+}
+
+.jp-carousel-titleanddesc p strong,
+.jp-carousel-titleanddesc p b {
+ font-weight: bold;
+ color: #999;
+}
+
+.jp-carousel-titleanddesc p em,
+.jp-carousel-titleanddesc p i {
+ font-style: italic;
+ color: #999;
+}
+
+
+.jp-carousel-titleanddesc p a:hover,
+.jp-carousel-comments p a:hover,
+.jp-carousel-info h2 a:hover {
+ color: #68c9e8 !important;
+}
+
+.jp-carousel-titleanddesc p:empty {
+ display: none;
+}
+
+.jp-carousel-photo-info h1:before,
+.jp-carousel-photo-info h1:after,
+.jp-carousel-left-column-wrapper h1:before,
+.jp-carousel-left-column-wrapper h1:after {
+ content:none !important;
+}
+/** Title and Desc End **/
+
+/** Meta Box Start **/
+.jp-carousel-image-meta {
+ background: #111;
+ border: 1px solid #222;
+ color: #fff;
+ font-size: 13px;
+ font: 12px/1.4 "Helvetica Neue", sans-serif !important;
+ overflow: hidden;
+ padding: 18px 20px;
+ width: 209px !important;
+}
+
+.jp-carousel-image-meta li,
+.jp-carousel-image-meta h5 {
+ font-family: "Helvetica Neue", sans-serif !important;
+ position: inherit !important;
+ top: auto !important;
+ left: auto !important;
+ right: auto !important;
+ bottom: auto !important;
+ background: none !important;
+ border: none !important;
+ font-weight: 400 !important;
+ line-height: 1.3em !important;
+}
+
+.jp-carousel-image-meta ul {
+ margin: 0 !important;
+ padding: 0 !important;
+ list-style: none !important;
+}
+
+.jp-carousel-image-meta li {
+ width: 48% !important;
+ float: right !important;
+ margin: 0 0 15px 2% !important;
+ color: #fff !important;
+ font-size:13px !important;
+}
+
+.jp-carousel-image-meta h5 {
+ color: #999 !important;
+ text-transform: uppercase !important;
+ font-size:10px !important;
+ margin:0 0 2px !important;
+ letter-spacing: 0.1em !important;
+}
+
+a.jp-carousel-image-download {
+ padding-right: 23px;
+ display: inline-block;
+ clear: both;
+ color: #999;
+ line-height: 1;
+ font-weight: 400;
+ font-size: 13px;
+ text-decoration: none;
+ background-position: 0 -82px;
+}
+
+a.jp-carousel-image-download span.photo-size {
+ font-size: 11px;
+ border-radius: 1em;
+ margin-right: 2px;
+ display: inline-block;
+}
+
+a.jp-carousel-image-download span.photo-size-times {
+ padding: 0 2px 0 1px;
+}
+
+a.jp-carousel-image-download:hover {
+ background-position: 0 -102px;
+ color: #68c9e8;
+ border: none !important;
+}
+
+/** Meta Box End **/
+
+/** GPS Map Start **/
+.jp-carousel-image-map {
+ position: relative;
+ margin: -20px -20px 20px;
+ border-bottom: 1px solid rgba( 255, 255, 255, 0.17 );
+ height: 154px;
+}
+
+.jp-carousel-image-map img.gmap-main {
+ -moz-border-radius-topleft: 6px;
+ border-top-right-radius: 6px;
+ border-left: 1px solid rgba( 255, 255, 255, 0.17 );
+}
+.jp-carousel-image-map div.gmap-topright {
+ width: 94px;
+ height: 154px;
+ position: absolute;
+ top: 0;
+ left: 0;
+}
+.jp-carousel-image-map div.imgclip {
+ overflow: hidden;
+ -moz-border-radius-topright: 6px;
+ border-top-left-radius: 6px;
+}
+.jp-carousel-image-map div.gmap-topright img {
+ margin-right: -40px;
+}
+.jp-carousel-image-map img.gmap-bottomright {
+ position: absolute;
+ top: 96px;
+ left: 0;
+}
+
+/** Comments Start **/
+.jp-carousel-comments {
+ font: 15px/1.7 "Helvetica Neue", sans-serif !important;
+ font-weight: 400;
+ background:none transparent;
+}
+
+.jp-carousel-comments p a:hover, .jp-carousel-comments p a:focus, .jp-carousel-comments p a:active {
+ color: #68c9e8 !important;
+}
+
+.jp-carousel-comment {
+ background:none transparent;
+ color: #999;
+ margin-bottom: 20px;
+ clear:right;
+ overflow: auto;
+ width: 100%
+}
+
+.jp-carousel-comment p {
+ color: #999 !important;
+}
+
+.jp-carousel-comment .comment-author {
+ font-size: 13px;
+ font-weight:400;
+ padding:0;
+ width:auto;
+ display: inline;
+ float:none;
+ border:none;
+ margin:0;
+}
+
+.jp-carousel-comment .comment-author a {
+ color: #fff;
+}
+
+.jp-carousel-comment .comment-gravatar {
+ float:right;
+}
+
+.jp-carousel-comment .comment-content {
+ border:none;
+ margin-right:85px;
+ padding: 0;
+}
+
+.jp-carousel-comment .avatar {
+ margin:0 0 0 20px;
+ -moz-border-radius: 4px;
+ -webkit-border-radius: 4px;
+ border-radius: 4px;
+ border: none !important;
+ padding: 0 !important;
+ background-color: transparent !important;
+}
+
+.jp-carousel-comment .comment-date {
+ color:#999;
+ margin-top: 4px;
+ font-size:11px;
+ display: inline;
+ float: left;
+ /*clear: right;*/
+}
+
+#jp-carousel-comment-form {
+ margin:0 0 10px !important;
+ float: right;
+ width: 100%;
+}
+
+textarea#jp-carousel-comment-form-comment-field {
+ background: rgba(34,34,34,0.9);
+ border: 1px solid #3a3a3a;
+ color: #aaa;
+ font: 15px/1.4 "Helvetica Neue", sans-serif !important;
+ width: 100%;
+ padding: 10px 10px 5px;
+ margin: 0;
+ float: none;
+ height: 147px;
+ -webkit-box-shadow: inset 2px 2px 2px rgba(0,0,0,0.2);
+ box-shadow: inset 2px 2px 2px rgba(0,0,0,0.2);
+ -moz-border-radius: 3px;
+ -webkit-border-radius: 3px;
+ border-radius: 3px;
+ overflow: hidden;
+ -webkit-box-sizing: border-box;
+ -moz-box-sizing: border-box;
+ box-sizing: border-box;
+}
+
+textarea#jp-carousel-comment-form-comment-field::-webkit-input-placeholder {
+ color: #555;
+}
+
+textarea#jp-carousel-comment-form-comment-field:focus {
+ background: #ccc;
+ color: #222;
+}
+
+textarea#jp-carousel-comment-form-comment-field:focus::-webkit-input-placeholder {
+ color: #aaa;
+}
+
+#jp-carousel-comment-form-spinner {
+ color: #fff;
+ margin:22px 10px 0 0;
+ display: block;
+ width: 20px;
+ height: 20px;
+ float: right;
+}
+
+#jp-carousel-comment-form-submit-and-info-wrapper {
+ display: none;
+ /*margin-bottom:15px;*/
+ overflow: hidden;
+ width: 100%
+}
+
+#jp-carousel-comment-form-commenting-as {
+}
+
+#jp-carousel-comment-form-commenting-as input {
+ background: rgba(34,34,34,0.9);
+ border: 1px solid #3a3a3a;
+ color: #aaa;
+ font: 13px/1.4 "Helvetica Neue", sans-serif !important;
+ padding: 3px 6px;
+ float: right;
+ -webkit-box-shadow: inset 2px 2px 2px rgba(0,0,0,0.2);
+ box-shadow: inset 2px 2px 2px rgba(0,0,0,0.2);
+ -moz-border-radius: 2px;
+ -webkit-border-radius: 2px;
+ border-radius: 2px;
+ width:285px;
+}
+
+#jp-carousel-comment-form-commenting-as input:focus {
+ background: #ccc;
+ color: #222;
+}
+
+#jp-carousel-comment-form-commenting-as p {
+ font: 400 13px/1.7 "Helvetica Neue", sans-serif !important;
+ margin:22px 0 0;
+ float: right;
+}
+
+#jp-carousel-comment-form-commenting-as fieldset {
+ float:right;
+ border:none;
+ margin:20px 0 0 0;
+ padding:0;
+}
+
+#jp-carousel-comment-form-commenting-as fieldset {
+ clear: both;
+}
+
+#jp-carousel-comment-form-commenting-as label {
+ font: 400 13px/1.7 "Helvetica Neue", sans-serif !important;
+ margin:0 0 3px 20px;
+ float:right;
+ width:100px;
+}
+
+#jp-carousel-comment-form-button-submit {
+ margin-top: 20px;
+ float:left;
+}
+
+#js-carousel-comment-form-container {
+ margin-bottom:15px;
+ overflow: auto;
+ width: 100%;
+}
+
+#jp-carousel-comment-form-container {
+ margin-bottom:15px;
+ overflow: auto;
+ width: 100%;
+}
+
+#jp-carousel-comment-post-results {
+ display: none;
+ overflow:auto;
+ width:100%;
+}
+
+#jp-carousel-comment-post-results span {
+ display:block;
+ text-align: center;
+ margin-top:20px;
+ width: 100%;
+ overflow: auto;
+ padding: 1em 0;
+ box-sizing: border-box;
+ background: rgba( 0, 0, 0, 0.7 );
+ border-radius: 2px;
+ font: 13px/1.4 "Helvetica Neue", sans-serif !important;
+ border: 1px solid rgba( 255, 255, 255, 0.17 );
+ -webkit-box-shadow: inset 0px 5px 5px 0px rgba(0, 0, 0, 1);
+ box-shadow: inset 0px 5px 5px 0px rgba(0, 0, 0, 1);
+}
+
+.jp-carousel-comment-post-error {
+ color:#DF4926;
+}
+
+.jp-carousel-comment-post-success {
+ /*color:#21759B;*/
+}
+
+#jp-carousel-comments-closed {
+ display: none;
+ color: #999;
+}
+
+#jp-carousel-comments-loading {
+ font: 444 15px/1.7 "Helvetica Neue", sans-serif !important;
+ display: none;
+ color: #999;
+ text-align: right;
+ margin-bottom: 20px;
+}
+
+
+/* ----- Light variant ----- */
+
+.jp-carousel-light .jp-carousel-overlay {
+ background: #fff;
+}
+
+.jp-carousel-light .jp-carousel-next-button:hover span,
+.jp-carousel-light .jp-carousel-previous-button:hover span {
+ opacity: 0.8;
+}
+
+.jp-carousel-light .jp-carousel-close-hint:hover,
+.jp-carousel-light .jp-carousel-titleanddesc div {
+ color: #000 !important;
+}
+
+.jp-carousel-light .jp-carousel-comments p a,
+.jp-carousel-light .jp-carousel-comment .comment-author a,
+.jp-carousel-light .jp-carousel-titleanddesc p a,
+.jp-carousel-light .jp-carousel-titleanddesc p a,
+.jp-carousel-light .jp-carousel-comments p a,
+.jp-carousel-light .jp-carousel-info h2 a {
+ color: #1e8cbe !important;
+}
+
+.jp-carousel-light .jp-carousel-comments p a:hover,
+.jp-carousel-light .jp-carousel-comment .comment-author a:hover,
+.jp-carousel-light .jp-carousel-titleanddesc p a:hover,
+.jp-carousel-light .jp-carousel-titleanddesc p a:hover,
+.jp-carousel-light .jp-carousel-comments p a:hover,
+.jp-carousel-light .jp-carousel-info h2 a:hover {
+ color: #f1831e !important;
+}
+
+.jp-carousel-light .jp-carousel-info h2,
+.jp-carousel-light .jp-carousel-titleanddesc,
+.jp-carousel-light .jp-carousel-titleanddesc p,
+.jp-carousel-light .jp-carousel-comment,
+.jp-carousel-light .jp-carousel-comment p,
+.jp-carousel-light div.jp-carousel-buttons a,
+.jp-carousel-light .jp-carousel-titleanddesc p strong,
+.jp-carousel-light .jp-carousel-titleanddesc p b,
+.jp-carousel-light .jp-carousel-titleanddesc p em,
+.jp-carousel-light .jp-carousel-titleanddesc p i {
+ color: #666;
+}
+
+.jp-carousel-light .jp-carousel-buttons {
+ border-bottom-color: #f0f0f0;
+ background: #f5f5f5;
+}
+
+.jp-carousel-light div.jp-carousel-buttons a:hover {
+ text-decoration: none;
+ color: #f1831e;
+}
+
+.jp-carousel-light div.jp-carousel-buttons a.jp-carousel-reblog,
+.jp-carousel-light div.jp-carousel-buttons a.jp-carousel-reblog:hover {
+ background-position: 4px -56px;
+ padding-right: 24px !important;
+}
+
+.jp-carousel-light div.jp-carousel-buttons a.jp-carousel-reblog.reblogged,
+.jp-carousel-light div.jp-carousel-buttons a.jp-carousel-like.liked {
+ background-color: #2ea2cc;
+ color: #fff;
+}
+
+.jp-carousel-light div.jp-carousel-buttons a.jp-carousel-commentlink {
+ background-position: 0px -136px;
+}
+
+.jp-carousel-light div.jp-carousel-buttons a.jp-carousel-like,
+.jp-carousel-light div.jp-carousel-buttons a.jp-carousel-like:hover {
+ background-position: 5px -15px;
+ padding-right: 23px !important;
+}
+
+.jp-carousel-light div.jp-carousel-buttons a.jp-carousel-reblog.reblogged {
+ background-position: 5px -36px;
+}
+
+.jp-carousel-light div.jp-carousel-buttons a.jp-carousel-like.liked {
+ background-position: 5px 5px;
+}
+
+.jp-carousel-light div#carousel-reblog-box {
+ background: #eee;
+ background: -moz-linear-gradient(bottom, #ececec, #f7f7f7);
+ background: -webkit-gradient(linear, right bottom, right top, from(#ececec), to(#f7f7f7));
+ -webkit-box-shadow: 0 2px 6px rgba(0,0,0,0.1);
+ -moz-box-shadow: 0 2px 10px rgba(0,0,0,0.1);
+ box-shadow: 0 2px 10px rgba(0,0,0,0.1);
+ border:1px solid #ddd;
+}
+
+.jp-carousel-light #carousel-reblog-box textarea {
+ border: 1px inset #ccc;
+ color: #666;
+ border: 1px solid #cfcfcf;
+ background: #fff;
+}
+
+.jp-carousel-light #carousel-reblog-box .canceltext {
+ color: #888;
+}
+
+.jp-carousel-light #carousel-reblog-box .canceltext a {
+ color: #666;
+}
+
+.jp-carousel-light #carousel-reblog-box select {
+ background: #eee;
+ color: #333;
+ border: 1px solid #aaa;
+}
+
+.jp-carousel-light #carousel-reblog-box input#carousel-reblog-submit, #jp-carousel-comment-form-button-submit {
+ color: #333;
+ background: #fff;
+ background: -moz-linear-gradient(bottom, #ddd, #fff);
+ background: -webkit-gradient(linear, right bottom, right top, from(#ddd), to(#fff));
+ border: 1px solid #aaa;
+}
+
+.jp-carousel-light .jp-carousel-image-meta {
+ background: #fafafa;
+ border: 1px solid #eee;
+ border-top-color: #f5f5f5;
+ border-right-color: #f5f5f5;
+ color: #333;
+}
+
+.jp-carousel-light .jp-carousel-image-meta li {
+ color: #000 !important;
+}
+
+.jp-carousel-light .jp-carousel-close-hint {
+ color: #ccc;
+}
+
+.jp-carousel-light .jp-carousel-close-hint span {
+ background-color: white;
+ border-color: #ccc;
+}
+
+.jp-carousel-light #jp-carousel-comment-form-comment-field::-webkit-input-placeholder {
+ color: #aaa;
+}
+
+.jp-carousel-light #jp-carousel-comment-form-comment-field:focus {
+ color: #333;
+}
+
+.jp-carousel-light #jp-carousel-comment-form-comment-field:focus::-webkit-input-placeholder {
+ color: #ddd;
+}
+
+.jp-carousel-light a.jp-carousel-image-download {
+ background-position: 0 -102px;
+}
+
+.jp-carousel-light a.jp-carousel-image-download:hover {
+ background-position: 0 -102px;
+ color: #f1831e;
+}
+
+.jp-carousel-light textarea#jp-carousel-comment-form-comment-field {
+ background: #fbfbfb;
+ color: #333;
+ border: 1px solid #dfdfdf;
+ -webkit-box-shadow: inset 2px 2px 2px rgba(0,0,0,0.1);
+ box-shadow: inset 2px 2px 2px rgba(0,0,0,0.1);
+}
+
+.jp-carousel-light #jp-carousel-comment-form-commenting-as input {
+ background: #fbfbfb;
+ border: 1px solid #dfdfdf;
+ color: #333;
+ -webkit-box-shadow: inset 2px 2px 2px rgba(0,0,0,0.1);
+ box-shadow: inset 2px 2px 2px rgba(0,0,0,0.1);
+}
+
+.jp-carousel-light #jp-carousel-comment-form-commenting-as input:focus {
+ background: #fbfbfb;
+ color: #333;
+}
+
+.jp-carousel-light #jp-carousel-comment-post-results span {
+ background: #f7f7f7;
+ border:1px solid #dfdfdf;
+ -webkit-box-shadow: inset 0px 0px 5px rgba(0, 0, 0, 0.05);
+ box-shadow: inset 0px 0px 5px rgba(0, 0, 0, 0.05);
+}
+
+.jp-carousel-light .jp-carousel-slide {
+ background-color:#fff;
+}
+
+.jp-carousel-light .jp-carousel-titleanddesc {
+ border-top: 1px solid #eee;
+}
+
+.jp-carousel-light .jp-carousel-fadeaway {
+ background: -moz-linear-gradient(bottom, rgba(255,255,255,0.75), rgba(255,255,255,0));
+ background: -webkit-gradient(linear, right bottom, right top, from(rgba(255,255,255,0.75)), to(rgba(255,255,255,0)));
+}
+
+/* Small screens */
+@media only screen and (max-width: 760px) {
+
+ .jp-carousel-info {
+ margin: 0 10px !important;
+ }
+
+ .jp-carousel-next-button, .jp-carousel-previous-button {
+ display: none !important;
+ }
+
+ .jp-carousel-buttons {
+ display: none !important;
+ }
+
+ .jp-carousel-image-meta {
+ float: none !important;
+ width: 100% !important;
+ -moz-box-sizing:border-box;
+ -webkit-box-sizing:border-box;
+ box-sizing: border-box;
+ }
+
+ .jp-carousel-close-hint {
+ font-weight: 800 !important;
+ font-size: 26px !important;
+ position: fixed !important;
+ top: -10px;
+ }
+
+ .jp-carousel-slide img {
+ filter: alpha(opacity=100);
+ opacity: 1;
+ }
+
+ .jp-carousel-wrap {
+ background-color: #000;
+ }
+
+ .jp-carousel-fadeaway {
+ display: none;
+ }
+
+ #jp-carousel-comment-form-container {
+ display: none !important;
+ }
+
+ .jp-carousel-titleanddesc {
+ padding-top: 0 !important;
+ border: none !important;
+ }
+ .jp-carousel-titleanddesc-title {
+ font-size: 1em !important;
+ }
+
+ .jp-carousel-left-column-wrapper {
+ padding: 0;
+ }
+}
diff --git a/plugins/jetpack/modules/comments.php b/plugins/jetpack/modules/comments.php
index b30ccd73..d5d4da4d 100644
--- a/plugins/jetpack/modules/comments.php
+++ b/plugins/jetpack/modules/comments.php
@@ -13,6 +13,15 @@ if ( is_admin() ) {
require dirname( __FILE__ ) . '/comments/admin.php';
}
+Jetpack_Sync::sync_options( __FILE__,
+ 'comment_registration',
+ 'require_name_email',
+ 'show_avatars',
+ 'avatar_default',
+ 'highlander_comment_form_prompt',
+ 'jetpack_comment_form_color_scheme'
+);
+
function jetpack_comments_load() {
Jetpack::enable_module_configurable( __FILE__ );
Jetpack::module_configuration_load( __FILE__, 'jetpack_comments_configuration_load' );
diff --git a/plugins/jetpack/modules/comments/base.php b/plugins/jetpack/modules/comments/base.php
index 2835313f..f1b5440e 100644
--- a/plugins/jetpack/modules/comments/base.php
+++ b/plugins/jetpack/modules/comments/base.php
@@ -35,7 +35,6 @@ class Highlander_Comments_Base {
protected function setup_filters() {
add_filter( 'comments_array', array( $this, 'comments_array' ) );
add_filter( 'preprocess_comment', array( $this, 'allow_logged_in_user_to_comment_as_guest' ), 0 );
- add_filter( 'get_avatar', array( $this, 'get_avatar' ), 10, 4 );
}
/**
@@ -273,56 +272,16 @@ class Highlander_Comments_Base {
}
/**
- * Get the comment avatar from Gravatar, Twitter, or Facebook
- *
- * @since JetpackComments (1.4)
- * @param string $avatar Current avatar URL
- * @param string $comment Comment for the avatar
- * @param int $size Size of the avatar
- * @param string $default Not used
- * @return string New avatar
- */
- public function get_avatar( $avatar, $comment, $size, $default ) {
- if ( ! isset( $comment->comment_post_ID ) || ! isset( $comment->comment_ID ) ) {
- // it's not a comment - bail
- return $avatar;
- }
-
- if ( false === strpos( $comment->comment_author_url, '/www.facebook.com/' ) && false === strpos( $comment->comment_author_url, '/twitter.com/' ) ) {
- // It's neither FB nor Twitter - bail
- return $avatar;
- }
-
- // It's a FB or Twitter avatar
- $foreign_avatar = get_comment_meta( $comment->comment_ID, 'hc_avatar', true );
- if ( empty( $foreign_avatar ) ) {
- // Can't find the avatar details - bail
- return $avatar;
- }
-
- // Return the FB or Twitter avatar
- return preg_replace( '#src=([\'"])[^\'"]+\\1#', 'src=\\1' . esc_url( $this->imgpress_avatar( $foreign_avatar, $size ) ) . '\\1', $avatar );
- }
-
- /**
- * Get an avatar from Imgpress
+ * Get an avatar from Photon
*
* @since JetpackComments (1.4)
* @param string $url
* @param int $size
* @return string
*/
- protected function imgpress_avatar( $url, $size ) {
+ protected function photon_avatar( $url, $size ) {
$size = (int) $size;
- $args = urlencode_deep( array(
- 'url' => $url,
- 'resize' => "$size,$size",
- ) );
-
- $url = apply_filters( 'jetpack_static_url', ( is_ssl() ? 'https://s-ssl.wordpress.com' : 'http://s.wordpress.com' ) . '/imgpress' );
- $url = add_query_arg( $args, $url );
-
- return $url;
+ return jetpack_photon_url( $url, array( 'resize' => "$size,$size" ) );
}
}
diff --git a/plugins/jetpack/modules/comments/comments.php b/plugins/jetpack/modules/comments/comments.php
index d33ad240..8dae39b7 100644
--- a/plugins/jetpack/modules/comments/comments.php
+++ b/plugins/jetpack/modules/comments/comments.php
@@ -115,6 +115,49 @@ class Jetpack_Comments extends Highlander_Comments_Base {
add_action( 'comment_post', array( $this, 'add_comment_meta' ) );
}
+ /**
+ * Setup filters for methods in this class
+ * @since 1.6.2
+ */
+ protected function setup_filters() {
+ parent::setup_filters();
+
+ add_filter( 'comment_post_redirect', array( $this, 'capture_comment_post_redirect_to_reload_parent_frame' ), 100 );
+ add_filter( 'get_avatar', array( $this, 'get_avatar' ), 10, 4 );
+ }
+
+ /**
+ * Get the comment avatar from Gravatar, Twitter, or Facebook
+ *
+ * @since JetpackComments (1.4)
+ * @param string $avatar Current avatar URL
+ * @param string $comment Comment for the avatar
+ * @param int $size Size of the avatar
+ * @param string $default Not used
+ * @return string New avatar
+ */
+ public function get_avatar( $avatar, $comment, $size, $default ) {
+ if ( ! isset( $comment->comment_post_ID ) || ! isset( $comment->comment_ID ) ) {
+ // it's not a comment - bail
+ return $avatar;
+ }
+
+ if ( false === strpos( $comment->comment_author_url, '/www.facebook.com/' ) && false === strpos( $comment->comment_author_url, '/twitter.com/' ) ) {
+ // It's neither FB nor Twitter - bail
+ return $avatar;
+ }
+
+ // It's a FB or Twitter avatar
+ $foreign_avatar = get_comment_meta( $comment->comment_ID, 'hc_avatar', true );
+ if ( empty( $foreign_avatar ) ) {
+ // Can't find the avatar details - bail
+ return $avatar;
+ }
+
+ // Return the FB or Twitter avatar
+ return preg_replace( '#src=([\'"])[^\'"]+\\1#', 'src=\\1' . esc_url( $this->photon_avatar( $foreign_avatar, $size ) ) . '\\1', $avatar );
+ }
+
/** Output Methods ********************************************************/
/**
@@ -168,6 +211,7 @@ class Jetpack_Comments extends Highlander_Comments_Base {
'greeting' => get_option( 'highlander_comment_form_prompt', __( 'Leave a Reply', 'jetpack' ) ),
'color_scheme' => get_option( 'jetpack_comment_form_color_scheme', $this->default_color_scheme ),
'lang' => get_bloginfo( 'language' ),
+ 'jetpack_version' => JETPACK__VERSION,
);
// Extra parameters for logged in user
@@ -223,6 +267,14 @@ class Jetpack_Comments extends Highlander_Comments_Base {
$url_origin = ( is_ssl() ? 'https' : 'http' ) . '://jetpack.wordpress.com';
?>
+ <!--[if IE]>
+ <script type="text/javascript">
+ if ( 0 === window.location.hash.indexOf( '#comment-' ) ) {
+ // window.location.reload() doesn't respect the Hash in IE
+ window.location.hash = window.location.hash;
+ }
+ </script>
+ <![endif]-->
<script type="text/javascript">
var comm_par_el = document.getElementById( 'comment_parent' ),
comm_par = (comm_par_el && comm_par_el.value) ? comm_par_el.value : '',
@@ -361,6 +413,7 @@ class Jetpack_Comments extends Highlander_Comments_Base {
$comment_meta['hc_post_as'] = 'wordpress';
$comment_meta['hc_avatar'] = stripslashes( $_POST['hc_avatar'] );
$comment_meta['hc_foreign_user_id'] = stripslashes( $_POST['hc_userid'] );
+ $comment_meta['hc_wpcom_id_sig'] = stripslashes( $_POST['hc_wpcom_id_sig'] ); //since 1.9
break;
case 'jetpack' :
@@ -379,6 +432,87 @@ class Jetpack_Comments extends Highlander_Comments_Base {
foreach ( $comment_meta as $key => $value )
add_comment_meta( $comment_id, $key, $value, true );
}
+ function capture_comment_post_redirect_to_reload_parent_frame( $url ) {
+ if ( !isset( $_GET['for'] ) || 'jetpack' != $_GET['for'] ) {
+ return $url;
+ }
+?>
+<!DOCTYPE html>
+<html <?php language_attributes(); ?>>
+<!--<![endif]-->
+<head>
+<meta charset="<?php bloginfo( 'charset' ); ?>" />
+<title><?php printf( __( 'Submitting Comment%s', 'jetpack' ), '&hellip;' ); ?></title>
+<style type="text/css">
+body {
+ display: table;
+ width: 100%;
+ height: 60%;
+ position: absolute;
+ top: 0;
+ left: 0;
+ overflow: hidden;
+ color: #333;
+}
+
+h1 {
+ text-align: center;
+ margin: 0;
+ padding: 0;
+ display: table-cell;
+ vertical-align: middle;
+ font-family: "HelveticaNeue-Light", "Helvetica Neue Light", "Helvetica Neue", sans-serif;
+ font-weight: normal;
+}
+
+.hidden {
+ opacity: 0;
+}
+
+h1 span {
+ -moz-transition-property: opacity;
+ -moz-transition-duration: 1s;
+ -moz-transition-timing-function: ease-in-out;
+
+ -webkit-transition-property: opacity;
+ -webkit-transition-duration: 1s;
+ -webbit-transition-timing-function: ease-in-out;
+
+ -o-transition-property: opacity;
+ -o-transition-duration: 1s;
+ -o-transition-timing-function: ease-in-out;
+
+ -ms-transition-property: opacity;
+ -ms-transition-duration: 1s;
+ -ms-transition-timing-function: ease-in-out;
+
+ transition-property: opacity;
+ transition-duration: 1s;
+ transition-timing-function: ease-in-out;
+}
+</style>
+</head>
+<body>
+ <h1><?php printf( __( 'Submitting Comment%s', 'jetpack' ), '<span id="ellipsis" class="hidden">&hellip;</span>' ); ?></h1>
+<script type="text/javascript">
+try {
+ window.parent.location = <?php echo json_encode( $url ); ?>;
+ window.parent.location.reload( true );
+} catch ( e ) {
+ window.location = <?php echo json_encode( $url ); ?>;
+ window.location.reload( true );
+}
+ellipsis = document.getElementById( 'ellipsis' );
+function toggleEllipsis() {
+ ellipsis.className = ellipsis.className ? '' : 'hidden';
+}
+setInterval( toggleEllipsis, 1200 );
+</script>
+</body>
+</html>
+<?php
+ exit;
+ }
}
Jetpack_Comments::init();
diff --git a/plugins/jetpack/modules/contact-form/admin.php b/plugins/jetpack/modules/contact-form/admin.php
index b5fcadd9..d3efefa0 100644
--- a/plugins/jetpack/modules/contact-form/admin.php
+++ b/plugins/jetpack/modules/contact-form/admin.php
@@ -1,5 +1,43 @@
<?php
+function menu_alter() {
+ echo '
+ <style>
+ #menu-posts-feedback .wp-menu-image img { display: none; }
+ #adminmenu .menu-icon-feedback:hover div.wp-menu-image, #adminmenu .menu-icon-feedback.wp-has-current-submenu div.wp-menu-image, #adminmenu .menu-icon-feedback.current div.wp-menu-image { background: url("' .GRUNION_PLUGIN_URL . 'images/grunion-menu-hover.png") no-repeat 7px 7px !important; }
+ #adminmenu .menu-icon-feedback div.wp-menu-image, #adminmenu .menu-icon-feedback div.wp-menu-image, #adminmenu .menu-icon-feedback div.wp-menu-image { background: url("' . GRUNION_PLUGIN_URL . 'images/grunion-menu.png") no-repeat 7px 7px !important; }
+ .grunion-menu-button { background: url("' . GRUNION_PLUGIN_URL . 'images/grunion-form.png") no-repeat; width: 13px; height: 13px; display: inline-block; vertical-align: middle; ) }
+ @media only screen and (-moz-min-device-pixel-ratio: 1.5), only screen and (-o-min-device-pixel-ratio: 3/2), only screen and (-webkit-min-device-pixel-ratio: 1.5), only screen and (min-device-pixel-ratio: 1.5) {
+ #adminmenu .menu-icon-feedback:hover div.wp-menu-image, #adminmenu .menu-icon-feedback.wp-has-current-submenu div.wp-menu-image, #adminmenu .menu-icon-feedback.current div.wp-menu-image { background: url("' .GRUNION_PLUGIN_URL . 'images/grunion-menu-hover-2x.png") no-repeat 7px 7px !important; background-size: 15px 16px !important; }
+ #adminmenu .menu-icon-feedback div.wp-menu-image, #adminmenu .menu-icon-feedback div.wp-menu-image, #adminmenu .menu-icon-feedback div.wp-menu-image { background: url("' . GRUNION_PLUGIN_URL . 'images/grunion-menu-2x.png") no-repeat 7px 7px !important; background-size: 15px 16px !important; }
+ .grunion-menu-button { background-image: url("' . GRUNION_PLUGIN_URL . 'images/grunion-form-2x.png"); background-size: 13px 12px !important; vertical-align: bottom; }
+ }
+ </style>';
+}
+
+add_action('admin_head', 'menu_alter');
+
+/**
+ * Add a contact form button to the post composition screen
+ */
+add_action( 'media_buttons', 'grunion_media_button', 999 );
+function grunion_media_button( ) {
+ global $post_ID, $temp_ID;
+ $iframe_post_id = (int) (0 == $post_ID ? $temp_ID : $post_ID);
+ $title = esc_attr( __( 'Add a custom form', 'jetpack' ) );
+ $plugin_url = esc_url( GRUNION_PLUGIN_URL );
+ $site_url = esc_url( admin_url( "/admin-ajax.php?post_id={$iframe_post_id}&action=grunion_form_builder&TB_iframe=true&width=768" ) );
+
+ echo '<a href="' . $site_url . '&id=add_form" class="thickbox" title="' . $title . '"><div class="grunion-menu-button" alt="' . $title . '"></div></a>';
+}
+
+add_action( 'wp_ajax_grunion_form_builder', 'display_form_view' );
+
+function display_form_view() {
+ require_once GRUNION_PLUGIN_DIR . 'grunion-form-view.php';
+ exit;
+}
+
// feedback specific css items
add_action( 'admin_print_styles', 'grunion_admin_css' );
function grunion_admin_css() {
@@ -40,17 +78,91 @@ function grunion_admin_css() {
color: #D98500;
}
-#icon-edit { background-position: -432px -5px; }
-
-#icon-edit, #icon-post { background: url("<?php echo GRUNION_PLUGIN_URL; ?>images/grunion-menu-big.png") no-repeat !important; }
+#icon-edit.icon32-posts-feedback, #icon-post.icon32-posts-feedback { background: url("<?php echo GRUNION_PLUGIN_URL; ?>images/grunion-menu-big.png") no-repeat !important; }
@media only screen and (-moz-min-device-pixel-ratio: 1.5), only screen and (-o-min-device-pixel-ratio: 3/2), only screen and (-webkit-min-device-pixel-ratio: 1.5), only screen and (min-device-pixel-ratio: 1.5) {
- #icon-edit, #icon-post { background: url("<?php echo GRUNION_PLUGIN_URL; ?>images/grunion-menu-big-2x.png") no-repeat !important; background-size: 30px 31px !important; }
+ #icon-edit.icon32-posts-feedback, #icon-post.icon32-posts-feedback { background: url("<?php echo GRUNION_PLUGIN_URL; ?>images/grunion-menu-big-2x.png") no-repeat !important; background-size: 30px 31px !important; }
}
+
+#icon-edit.icon32-posts-feedback { background-position: 2px 2px !important; }
+
</style>
<?php
}
+/**
+ * Hack a 'Bulk Spam' option for bulk edit
+ * There isn't a better way to do this until
+ * http://core.trac.wordpress.org/changeset/17297 is resolved
+ */
+add_action( 'admin_head', 'grunion_add_bulk_edit_option' );
+function grunion_add_bulk_edit_option() {
+
+ $screen = get_current_screen();
+
+ if ( 'edit-feedback' != $screen->id
+ || ( ! empty( $_GET['post_status'] ) && 'spam' == $_GET['post_status'] ) )
+ return;
+
+ $spam_text = __( 'Mark Spam', 'jetpack' );
+ ?>
+ <script type="text/javascript">
+ jQuery(document).ready(function($) {
+ $('#posts-filter .actions select[name=action] option:first-child').after('<option value="spam"><?php echo esc_attr( $spam_text ); ?></option>' );
+ })
+ </script>
+ <?php
+}
+
+/**
+ * Handle a bulk spam report
+ */
+add_action( 'admin_init', 'grunion_handle_bulk_spam' );
+function grunion_handle_bulk_spam() {
+ global $pagenow;
+
+ if ( 'edit.php' != $pagenow
+ || ( empty( $_REQUEST['post_type'] ) || 'feedback' != $_REQUEST['post_type'] ) )
+ return;
+
+ // Slip in a success message
+ if ( ! empty( $_REQUEST['message'] ) && 'marked-spam' == $_REQUEST['message'] )
+ add_action( 'admin_notices', 'grunion_message_bulk_spam' );
+
+ if ( empty( $_REQUEST['action'] ) || 'spam' != $_REQUEST['action'] )
+ return;
+
+ check_admin_referer('bulk-posts');
+
+ if ( empty( $_REQUEST['post'] ) ) {
+ wp_safe_redirect( wp_get_referer() );
+ exit;
+ }
+
+ $post_ids = array_map( 'intval', $_REQUEST['post'] );
+
+ foreach( $post_ids as $post_id ) {
+ if ( ! current_user_can( "edit_page", $post_id ) )
+ wp_die( __( 'You are not allowed to manage this item.', 'jetpack' ) );
+
+ $post = array(
+ 'ID' => $post_id,
+ 'post_status' => 'spam',
+ );
+ $akismet_values = get_post_meta( $post_id, '_feedback_akismet_values', true );
+ wp_update_post( $post );
+ do_action( 'contact_form_akismet', 'spam', $akismet_values );
+ }
+
+ $redirect_url = add_query_arg( 'message', 'marked-spam', wp_get_referer() );
+ wp_safe_redirect( $redirect_url );
+ exit;
+}
+
+function grunion_message_bulk_spam() {
+ echo '<div class="updated"><p>' . __( 'Feedback(s) marked as spam', 'jetpack' ) . '</p></div>';
+}
+
// remove admin UI parts that we don't support in feedback management
add_action( 'admin_menu', 'grunion_admin_menu' );
function grunion_admin_menu() {
@@ -320,48 +432,41 @@ function grunion_sort_objects( $a, $b ) {
// returns both the shortcode form, and HTML markup representing a preview of the form
function grunion_ajax_shortcode() {
check_ajax_referer( 'grunion_shortcode' );
-
- $atts = '';
- if ( trim( $_POST['subject'] ) )
- $atts .= ' subject="'.grunion_esc_attr($_POST['subject']).'"';
- if ( trim( $_POST['to'] ) )
- $atts .= ' to="'.grunion_esc_attr($_POST['to']).'"';
-
- $shortcode = '[contact-form'.$atts.']';
- $shortcode .= "\n";
+
+ $attributes = array();
+
+ foreach ( array( 'subject', 'to' ) as $attribute ) {
+ if ( isset( $_POST[$attribute] ) && strlen( $_POST[$attribute] ) ) {
+ $attributes[$attribute] = stripslashes( $_POST[$attribute] );
+ }
+ }
+
if ( is_array( $_POST['fields'] ) ) {
- usort( $_POST['fields'], 'grunion_sort_objects' );
- foreach ( $_POST['fields'] as $field ) {
- $req = $opts = '';
- if ( $field['required'] == 'true' )
- $req = ' required="true"';
- if ( isset( $field['options'] ) && $field['options'] ) {
- $opts = ' options="';
- foreach ( $field['options'] as $option ) {
- $option = wp_kses( $option, array() );
- $option = grunion_esc_attr( $option );
-
- # we need to be very specific about how we
- # encode these values
- $option = str_replace( ',', '&#x002c;', $option );
- $option = str_replace( '"', '&#x0022;', $option );
- $option = str_replace( "'", '&#x0027;', $option );
- $option = str_replace( '&', '&#x0026;', $option );
-
- $opts .= $option . ',';
- }
- $opts = rtrim( $opts, ',' ) . '"';
+ $fields = stripslashes_deep( $_POST['fields'] );
+ usort( $fields, 'grunion_sort_objects' );
+
+ $field_shortcodes = array();
+
+ foreach ( $fields as $field ) {
+ $field_attributes = array();
+
+ if ( isset( $field['required'] ) && 'true' === $field['required'] ) {
+ $field_attributes['required'] = 'true';
}
- $field['label'] = wp_kses( $field['label'], array() );
- $field['label'] = str_replace( '"', '&#x0022;', $field['label'] );
+ foreach ( array( 'options', 'label', 'type' ) as $attribute ) {
+ if ( isset( $field[$attribute] ) ) {
+ $field_attributes[$attribute] = $field[$attribute];
+ }
+ }
- $shortcode .= '[contact-field label="'. $field['label'] .'" type="'.grunion_esc_attr($field['type']).'"' . $req . $opts .' /]'."\n";
+ $field_shortcodes[] = new Grunion_Contact_Form_Field( $field_attributes );
}
}
- $shortcode .= '[/contact-form]';
-
- die( "\n$shortcode\n" );
+
+ $grunion = new Grunion_Contact_Form( $attributes, $field_shortcodes );
+
+ die( "\n$grunion\n" );
}
// takes a post_id, extracts the contact-form shortcode from that post (if there is one), parses it,
@@ -370,33 +475,45 @@ function grunion_ajax_shortcode_to_json() {
global $post, $grunion_form;
check_ajax_referer( 'grunion_shortcode_to_json' );
- if ( isset( $_POST['content'] ) && is_numeric( $_POST['post_id'] ) ) {
- $content = stripslashes( $_POST['content'] );
- $post = get_post( $_POST['post_id'] );
- // does it look like a post with a [contact-form] already?
- if ( strpos( $content, '[contact-form' ) !== false ) {
- $out = do_shortcode($content);
- global $contact_form_fields;
- if ( is_array($contact_form_fields) && !empty($contact_form_fields) ) {
- foreach ( $contact_form_fields as $field_id => $field ) {
- # need to dig deeper on select field options
- if ( preg_match( "|^(.*)\-select$|", $field_id ) ) {
- foreach ( (array) $field['options'] as $opt_i => $opt ) {
- $contact_form_fields[$field_id]['options'][$opt_i] = html_entity_decode( $opt );
- }
- }
- $contact_form_fields[$field_id]['label'] = html_entity_decode( $contact_form_fields[$field_id]['label'] );
- $contact_form_fields[$field_id]['label'] = wp_kses( $contact_form_fields[$field_id]['label'], array() );
- }
- $out = array( 'fields' => $contact_form_fields, 'to' => $grunion_form->to, 'subject' => $grunion_form->subject );
- die( json_encode( $out ) );
- }
- }
+ if ( !isset( $_POST['content'] ) || !is_numeric( $_POST['post_id'] ) ) {
+ die( '-1' );
+ }
+
+ $content = stripslashes( $_POST['content'] );
+
+ // doesn't look like a post with a [contact-form] already.
+ if ( false === strpos( $content, '[contact-form' ) ) {
die( '' );
}
-
- die( -1 );
+
+ $post = get_post( $_POST['post_id'] );
+
+ do_shortcode( $content );
+
+ $grunion = Grunion_Contact_Form::$last;
+
+ $out = array(
+ 'to' => '',
+ 'subject' => '',
+ 'fields' => array(),
+ );
+
+ foreach ( $grunion->fields as $field ) {
+ $out['fields'][$field->get_attribute( 'id' )] = $field->attributes;
+ }
+
+ $to = $grunion->get_attribute( 'to' );
+ $subject = $grunion->get_attribute( 'subject' );
+ foreach ( array( 'to', 'subject' ) as $attribute ) {
+ $value = $grunion->get_attribute( $attribute );
+ if ( isset( $grunion->defaults[$attribute] ) && $value == $grunion->defaults[$attribute] ) {
+ $value = '';
+ }
+ $out[$attribute] = $value;
+ }
+
+ die( json_encode( $out ) );
}
diff --git a/plugins/jetpack/modules/contact-form/css/grunion.css b/plugins/jetpack/modules/contact-form/css/grunion.css
index a8f1651d..00035180 100644
--- a/plugins/jetpack/modules/contact-form/css/grunion.css
+++ b/plugins/jetpack/modules/contact-form/css/grunion.css
@@ -1,9 +1,10 @@
-.textwidget input[type='text'], .textwidget textarea { width: 100% !important; }
.contact-form .clear-form { clear: both; }
-.contact-form input[type='text'] { width: 300px; margin-bottom: 13px; }
+.contact-form input[type='text'], .contact-form input[type='email'] { width: 300px; margin-bottom: 13px; }
.contact-form select { margin-bottom: 13px; }
.contact-form textarea { height: 200px; width: 80%; float: none; margin-bottom: 13px; }
.contact-form input[type='radio'], .contact-form input[type='checkbox'] { float: none; margin-bottom: 13px; }
.contact-form label { margin-bottom: 3px; float: none; font-weight: bold; display: block; }
.contact-form label.checkbox, .contact-form label.radio { margin-bottom: 3px; float: none; font-weight: bold; display: inline-block; }
-.contact-form label span { color: #AAA; margin-left: 4px; font-weight: normal; } \ No newline at end of file
+.contact-form label span { color: #AAA; margin-left: 4px; font-weight: normal; }
+.form-errors .form-error-message { color: red; }
+.textwidget input[type='text'], .textwidget input[type='email'], .textwidget textarea { width: 250px; }
diff --git a/plugins/jetpack/modules/contact-form/grunion-contact-form.php b/plugins/jetpack/modules/contact-form/grunion-contact-form.php
index bbf2ba54..5600febb 100644
--- a/plugins/jetpack/modules/contact-form/grunion-contact-form.php
+++ b/plugins/jetpack/modules/contact-form/grunion-contact-form.php
@@ -6,7 +6,7 @@ Description: Add a contact form to any post, page or text widget. Emails will b
Plugin URI: http://automattic.com/#
AUthor: Automattic, Inc.
Author URI: http://automattic.com/
-Version: 2.3
+Version: 2.4
License: GPLv2 or later
*/
@@ -16,782 +16,1338 @@ define( 'GRUNION_PLUGIN_URL', plugin_dir_url( __FILE__ ) );
if ( is_admin() )
require_once GRUNION_PLUGIN_DIR . '/admin.php';
-// take the content of a contact-form shortcode and parse it into a list of field types
-function contact_form_parse( $content ) {
- // first parse all the contact-field shortcodes into an array
- global $contact_form_fields, $grunion_form;
- $contact_form_fields = array();
+/**
+ * Sets up various actions, filters, post types, post statuses, shortcodes.
+ */
+class Grunion_Contact_Form_Plugin {
+ /**
+ * @var string The Widget ID of the widget currently being processed. Used to build the unique contact-form ID for forms embedded in widgets.
+ */
+ var $current_widget_id;
- if ( empty( $_REQUEST['action'] ) || $_REQUEST['action'] != 'grunion_shortcode_to_json' ) {
- wp_print_styles( 'grunion.css' );
- }
-
- $out = do_shortcode( $content );
-
- if ( empty($contact_form_fields) || !is_array($contact_form_fields) ) {
- // default form: same as the original Grunion form
- $default_form = '
- [contact-field label="'.__( 'Name', 'jetpack' ).'" type="name" required="true" /]
- [contact-field label="'.__( 'Email', 'jetpack' ).'" type="email" required="true" /]
- [contact-field label="'.__( 'Website', 'jetpack' ).'" type="url" /]';
- if ( 'yes' == strtolower($grunion_form->show_subject) ) {
- $default_form .= '
- [contact-field label="'.__( 'Subject', 'jetpack' ).'" type="subject" /]';
+ static function init() {
+ static $instance = false;
+
+ if ( !$instance ) {
+ $instance = new Grunion_Contact_Form_Plugin;
}
- $default_form .= '
- [contact-field label="'.__( 'Message', 'jetpack' ).'" type="textarea" /]';
- $out = do_shortcode( $default_form );
+ return $instance;
}
- return $out;
-}
-
-function contact_form_render_field( $field ) {
- global $contact_form_last_id, $contact_form_errors, $contact_form_fields, $current_user, $user_identity;
-
- $r = '';
-
- $field_id = $field['id'];
- if ( isset($_POST[ $field_id ]) ) {
- $field_value = stripslashes( $_POST[ $field_id ] );
- } elseif ( is_user_logged_in() ) {
- // Special defaults for logged-in users
- if ( $field['type'] == 'email' )
- $field_value = $current_user->data->user_email;
- elseif ( $field['type'] == 'name' )
- $field_value = $user_identity;
- elseif ( $field['type'] == 'url' )
- $field_value = $current_user->data->user_url;
- else
- $field_value = $field['default'];
- } else {
- $field_value = $field['default'];
+ /**
+ * Strips HTML tags from input. Output is NOT HTML safe.
+ *
+ * @param string $string
+ * @return string
+ */
+ static function strip_tags( $string ) {
+ $string = wp_kses( $string, array() );
+ return str_replace( '&amp;', '&', $string ); // undo damage done by wp_kses_normalize_entities()
}
-
- $field_value = wp_kses($field_value, array());
-
- $field['label'] = html_entity_decode( $field['label'] );
- $field['label'] = wp_kses( $field['label'], array() );
-
- if ( $field['type'] == 'email' ) {
- $r .= "\n<div>\n";
- $r .= "\t\t<label for='".esc_attr($field_id)."' class='grunion-field-label ".esc_attr($field['type']) . ( contact_form_is_error($field_id) ? ' form-error' : '' ) . "'>" . htmlspecialchars( $field['label'] ) . ( $field['required'] ? '<span>'. __( "(required)", 'jetpack' ) . '</span>' : '' ) . "</label>\n";
- $r .= "\t\t<input type='text' name='".esc_attr($field_id)."' id='".esc_attr($field_id)."' value='".esc_attr($field_value)."' class='".esc_attr($field['type'])."'/>\n";
- $r .= "\t</div>\n";
- } elseif ( $field['type'] == 'textarea' ) {
- $r .= "\n<div>\n";
- $r .= "\t\t<label class='".esc_attr($field['type']) . ( contact_form_is_error($field_id) ? ' form-error' : '' ) . "' for='contact-form-comment-" . esc_attr( $field_id ) . "'>" . htmlspecialchars( $field['label'] ) . ( $field['required'] ? '<span>'. __( "(required)", 'jetpack' ) . '</span>' : '' ) . "</label>\n";
- $r .= "\t\t<textarea name='".esc_attr($field_id)."' id='contact-form-comment-".esc_attr($field_id)."' rows='20'>".htmlspecialchars($field_value)."</textarea>\n";
- $r .= "\t</div>\n";
- } elseif ( $field['type'] == 'radio' ) {
- $r .= "\t<div><label class='". ( contact_form_is_error($field_id) ? ' form-error' : '' ) . "'>" . htmlspecialchars( $field['label'] ) . ( $field['required'] ? '<span>'. __( "(required)", 'jetpack' ) . '</span>' : '' ) . "</label>\n";
- foreach ( $field['options'] as $option ) {
- $r .= "\t\t<label class='" . esc_attr( $field['type'] ) . ( contact_form_is_error( $field_id ) ? ' form-error' : '' ) . "'>";
- $r .= "<input type='radio' name='".esc_attr($field_id)."' value='".esc_attr($option)."' class='".esc_attr($field['type'])."' ".( $option == $field_value ? "checked='checked' " : "")." /> ";
- $r .= htmlspecialchars( $option ) . "</label>\n";
- $r .= "\t\t<div class='clear-form'></div>\n";
+
+ function __construct() {
+ $this->add_shortcode();
+
+ // While generating the output of a text widget with a contact-form shortcode, we need to know its widget ID.
+ add_action( 'dynamic_sidebar', array( $this, 'track_current_widget' ) );
+
+ // Add a "widget" shortcode attribute to all contact-form shortcodes embedded in widgets
+ add_filter( 'widget_text', array( $this, 'widget_atts' ), 0 );
+
+ // If Text Widgets don't get shortcode processed, hack ours into place.
+ if ( !has_filter( 'widget_text', 'do_shortcode' ) )
+ add_filter( 'widget_text', array( $this, 'widget_shortcode_hack' ), 5 );
+
+ // Akismet to the rescue
+ if ( function_exists( 'akismet_http_post' ) ) {
+ add_filter( 'contact_form_is_spam', array( $this, 'is_spam_akismet' ), 10 );
+ add_action( 'contact_form_akismet', array( $this, 'akismet_submit' ), 10, 2 );
}
- $r .= "\t\t</div>\n";
- } elseif ( $field['type'] == 'checkbox' ) {
- $r .= "\t<div>\n";
- $r .= "\t\t<label class='".esc_attr($field['type']) . ( contact_form_is_error($field_id) ? ' form-error' : '' ) . "'>\n";
- $r .= "\t\t<input type='checkbox' name='".esc_attr($field_id)."' value='".__( 'Yes', 'jetpack' )."' class='".esc_attr($field['type'])."' ".( $field_value ? "checked='checked' " : "")." /> \n";
- $r .= "\t\t". htmlspecialchars( $field['label'] ) . ( $field['required'] ? '<span>'. __( "(required)", 'jetpack' ) . '</span>' : '' ) . "</label>\n";
- $r .= "\t\t<div class='clear-form'></div>\n";
- $r .= "\t</div>\n";
- } elseif ( $field['type'] == 'select' ) {
- $r .= "\n<div>\n";
- $r .= "\t\t<label for='".esc_attr($field_id)."' class='".esc_attr($field['type']) . ( contact_form_is_error($field_id) ? ' form-error' : '' ) . "'>" . htmlspecialchars( $field['label'] ) . ( $field['required'] ? '<span>'. __( "(required)", 'jetpack' ) . '</span>' : '' ) . "</label>\n";
- $r .= "\t<select name='".esc_attr($field_id)."' id='".esc_attr($field_id)."' value='".esc_attr($field_value)."' class='".esc_attr($field['type'])."'/>\n";
- foreach ( $field['options'] as $option ) {
- $option = html_entity_decode( $option );
- $option = wp_kses( $option, array() );
- $r .= "\t\t<option".( $option == $field_value ? " selected='selected'" : "").">". esc_html( $option ) ."</option>\n";
- }
- $r .= "\t</select>\n";
- $r .= "\t</div>\n";
- } else {
- // default: text field
- // note that any unknown types will produce a text input, so we can use arbitrary type names to handle
- // input fields like name, email, url that require special validation or handling at POST
- $r .= "\n<div>\n";
- $r .= "\t\t<label for='".esc_attr($field_id)."' class='".esc_attr($field['type']) . ( contact_form_is_error($field_id) ? ' form-error' : '' ) . "'>" . htmlspecialchars( $field['label'] ) . ( $field['required'] ? '<span>'. __( "(required)", 'jetpack' ) . '</span>' : '' ) . "</label>\n";
- $r .= "\t\t<input type='text' name='".esc_attr($field_id)."' id='".esc_attr($field_id)."' value='".esc_attr($field_value)."' class='".esc_attr($field['type'])."'/>\n";
- $r .= "\t</div>\n";
+
+ add_action( 'loop_start', array( 'Grunion_Contact_Form', '_style_on' ) );
+
+ // custom post type we'll use to keep copies of the feedback items
+ register_post_type( 'feedback', array(
+ 'labels' => array(
+ 'name' => __( 'Feedbacks', 'jetpack' ),
+ 'singular_name' => __( 'Feedback', 'jetpack' ),
+ 'search_items' => __( 'Search Feedback', 'jetpack' ),
+ 'not_found' => __( 'No feedback found', 'jetpack' ),
+ 'not_found_in_trash' => __( 'No feedback found', 'jetpack' )
+ ),
+ 'menu_icon' => GRUNION_PLUGIN_URL . '/images/grunion-menu.png',
+ 'show_ui' => TRUE,
+ 'show_in_admin_bar' => FALSE,
+ 'public' => FALSE,
+ 'rewrite' => FALSE,
+ 'query_var' => FALSE,
+ 'capability_type' => 'page'
+ ) );
+
+ // Add "spam" as a post status
+ register_post_status( 'spam', array(
+ 'label' => 'Spam',
+ 'public' => FALSE,
+ 'exclude_from_search' => TRUE,
+ 'show_in_admin_all_list' => FALSE,
+ 'label_count' => _n_noop( 'Spam <span class="count">(%s)</span>', 'Spam <span class="count">(%s)</span>', 'jetpack' ),
+ 'protected' => TRUE,
+ '_builtin' => FALSE
+ ) );
+
+ // POST handler
+ if (
+ 'POST' == strtoupper( $_SERVER['REQUEST_METHOD'] )
+ &&
+ isset( $_POST['action'] ) && 'grunion-contact-form' == $_POST['action']
+ &&
+ isset( $_POST['contact-form-id'] )
+ ) {
+ add_action( 'template_redirect', array( $this, 'process_form_submission' ) );
+ }
+
+ /* Can be dequeued by placing the following in wp-content/themes/yourtheme/functions.php
+ *
+ * function remove_grunion_style() {
+ * wp_deregister_style('grunion.css');
+ * }
+ * add_action('wp_print_styles', 'remove_grunion_style');
+ */
+
+ wp_register_style( 'grunion.css', GRUNION_PLUGIN_URL . 'css/grunion.css', array(), JETPACK__VERSION );
}
-
- return $r;
-}
-function contact_form_validate_field( $field ) {
- global $contact_form_last_id, $contact_form_errors, $contact_form_values;
+ /**
+ * Handles all contact-form POST submissions
+ *
+ * Conditionally attached to `template_redirect`
+ */
+ function process_form_submission() {
+ $id = stripslashes( $_POST['contact-form-id'] );
+
+ check_admin_referer( "contact-form_{$id}" );
+
+ $is_widget = 0 === strpos( $id, 'widget-' );
+
+ $form = false;
- $field_id = $field['id'];
- $field_value = isset($_POST[ $field_id ]) ? stripslashes($_POST[ $field_id ]) : '';
+ if ( $is_widget ) {
+ // It's a form embedded in a text widget
- # pay special attention to required email fields
- if ( $field['required'] && $field['type'] == 'email' ) {
- if ( !is_email( $field_value ) ) {
- if ( !is_wp_error( $contact_form_errors ) ) {
- $contact_form_errors = new WP_Error();
+ $this->current_widget_id = substr( $id, 7 ); // remove "widget-"
+
+ // Is the widget active?
+ $sidebar = is_active_widget( false, $this->current_widget_id, 'text' );
+
+ // This is lame - no core API for getting a widget by ID
+ $widget = isset( $GLOBALS['wp_registered_widgets'][$this->current_widget_id] ) ? $GLOBALS['wp_registered_widgets'][$this->current_widget_id] : false;
+
+ if ( $sidebar && $widget && isset( $widget['callback'] ) ) {
+ // This is lamer - no API for outputting a given widget by ID
+ ob_start();
+ // Process the widget to populate Grunion_Contact_Form::$last
+ call_user_func( $widget['callback'], array(), $widget['params'][0] );
+ ob_end_clean();
}
+ } else {
+ // It's a form embedded in a post
+
+ $post = get_post( $id );
- $contact_form_errors->add( $field_id, sprintf( __( '%s requires a valid email address', 'jetpack' ), $field['label'] ) );
+ // Process the content to populate Grunion_Contact_Form::$last
+ apply_filters( 'the_content', $post->post_content );
}
- } elseif ( $field['required'] && !trim($field_value) ) {
- if ( !is_wp_error($contact_form_errors) ) {
- $contact_form_errors = new WP_Error();
+
+ $form = Grunion_Contact_Form::$last;
+
+ if ( !$form || ( is_wp_error( $form->errors ) && $form->errors->get_error_codes() ) ) {
+ return;
}
- $contact_form_errors->add( $field_id, sprintf( __( '%s is required', 'jetpack' ), $field['label'] ) );
+ // Process the form
+ $form->process_submission();
}
-
- $contact_form_values[ $field_id ] = $field_value;
-}
-function contact_form_is_error( $field_id ) {
- global $contact_form_errors;
-
- return ( is_wp_error( $contact_form_errors ) && $contact_form_errors->get_error_message( $field_id ) );
-}
+ /**
+ * Ensure the post author is always zero for contact-form feedbacks
+ * Attached to `wp_insert_post_data`
+ *
+ * @see Grunion_Contact_Form::process_submission()
+ *
+ * @param array $data the data to insert
+ * @param array $postarr the data sent to wp_insert_post()
+ * @return array The filtered $data to insert
+ */
+ function insert_feedback_filter( $data, $postarr ) {
+ if ( $data['post_type'] == 'feedback' && $postarr['post_type'] == 'feedback' ) {
+ $data['post_author'] = 0;
+ }
-// generic shortcode that handles all of the major input types
-// this parses the field attributes into an array that is used by other functions for rendering, validation etc
-function contact_form_field( $atts, $content, $tag ) {
- global $contact_form_fields, $contact_form_last_id, $grunion_form;
-
- $field = shortcode_atts( array(
- 'label' => null,
- 'type' => 'text',
- 'required' => false,
- 'options' => array(),
- 'id' => null,
- 'default' => null,
- ), $atts);
-
- // special default for subject field
- if ( $field['type'] == 'subject' && is_null($field['default']) )
- $field['default'] = $grunion_form->subject;
-
- // allow required=1 or required=true
- if ( $field['required'] == '1' || strtolower($field['required']) == 'true' )
- $field['required'] = true;
- else
- $field['required'] = false;
-
- // parse out comma-separated options list
- if ( !empty($field['options']) && is_string($field['options']) )
- $field['options'] = array_map('trim', explode(',', $field['options']));
-
- // make a unique field ID based on the label, with an incrementing number if needed to avoid clashes
- $id = $field['id'];
- if ( empty($id) ) {
- $id = sanitize_title_with_dashes( $contact_form_last_id . '-' . $field['label'] );
- $i = 0;
- $max_tries = 12;
- while ( isset( $contact_form_fields[ $id ] ) ) {
- $i++;
- $id = sanitize_title_with_dashes( $contact_form_last_id . '-' . $field['label'] . '-' . $i );
-
- if ( $i > $max_tries ) {
- break;
- }
+ return $data;
+ }
+
+ /*
+ * Adds our contact-form shortcode
+ * The "child" contact-field shortcode is added as needed by the contact-form shortcode handler
+ */
+ function add_shortcode() {
+ add_shortcode( 'contact-form', array( 'Grunion_Contact_Form', 'parse' ) );
+ }
+
+ /**
+ * Tracks the widget currently being processed.
+ * Attached to `dynamic_sidebar`
+ *
+ * @see $current_widget_id
+ *
+ * @param array $widget The widget data
+ */
+ function track_current_widget( $widget ) {
+ $this->current_widget_id = $widget['id'];
+ }
+
+ /**
+ * Adds a "widget" attribute to every contact-form embedded in a text widget.
+ * Used to tell the difference between post-embedded contact-forms and widget-embedded contact-forms
+ * Attached to `widget_text`
+ *
+ * @param string $text The widget text
+ * @return string The filtered widget text
+ */
+ function widget_atts( $text ) {
+ Grunion_Contact_Form::style( true );
+
+ return preg_replace( '/\[contact-form([^a-zA-Z_-])/', '[contact-form widget="' . $this->current_widget_id . '"\\1', $text );
+ }
+
+ /**
+ * For sites where text widgets are not processed for shortcodes, we add this hack to process just our shortcode
+ * Attached to `widget_text`
+ *
+ * @param string $text The widget text
+ * @return string The contact-form filtered widget text
+ */
+ function widget_shortcode_hack( $text ) {
+ if ( !preg_match( '/\[contact-form([^a-zA-Z_-])/', $text ) ) {
+ return $text;
}
- $field['id'] = $id;
+
+ $old = $GLOBALS['shortcode_tags'];
+ remove_all_shortcodes();
+ $this->add_shortcode();
+
+ $text = do_shortcode( $text );
+
+ $GLOBALS['shortcode_tags'] = $old;
+
+ return $text;
}
+
+ /**
+ * Populate an array with all values necessary to submit a NEW contact-form feedback to Akismet.
+ * Note that this includes the current user_ip etc, so this should only be called when accepting a new item via $_POST
+ *
+ * @param array $form Contact form feedback array
+ * @return array feedback array with additional data ready for submission to Akismet
+ */
+ function prepare_for_akismet( $form ) {
+ $form['comment_type'] = 'contact_form';
+ $form['user_ip'] = preg_replace( '/[^0-9., ]/', '', $_SERVER['REMOTE_ADDR'] );
+ $form['user_agent'] = $_SERVER['HTTP_USER_AGENT'];
+ $form['referrer'] = $_SERVER['HTTP_REFERER'];
+ $form['blog'] = get_option( 'home' );
+
+ $ignore = array( 'HTTP_COOKIE' );
+
+ foreach ( $_SERVER as $k => $value )
+ if ( !in_array( $k, $ignore ) && is_string( $value ) )
+ $form["$k"] = $value;
+
+ return $form;
+ }
+
+ /**
+ * Submit contact-form data to Akismet to check for spam.
+ * If you're accepting a new item via $_POST, run it Grunion_Contact_Form_Plugin::prepare_for_akismet() first
+ * Attached to `contact_form_is_spam`
+ *
+ * @param array $form
+ * @return bool|WP_Error TRUE => spam, FALSE => not spam, WP_Error => stop processing entirely
+ */
+ function is_spam_akismet( $form ) {
+ global $akismet_api_host, $akismet_api_port;
- $contact_form_fields[ $id ] = $field;
-
- if ( isset( $_POST['contact-form-id'] ) && $_POST['contact-form-id'] == $contact_form_last_id )
- contact_form_validate_field( $field );
-
- return contact_form_render_field( $field );
+ if ( !function_exists( 'akismet_http_post' ) )
+ return false;
+
+ $query_string = http_build_query( $form );
+
+ $response = akismet_http_post( $query_string, $akismet_api_host, '/1.1/comment-check', $akismet_api_port );
+ $result = false;
+ if ( 'true' == trim( $response[1] ) ) // 'true' is spam
+ $result = true;
+ return apply_filters( 'contact_form_is_spam_akismet', $result, $form );
+ }
+
+ /**
+ * Submit a feedback as either spam or ham
+ *
+ * @param string $as Either 'spam' or 'ham'.
+ * @param array $form the contact-form data
+ */
+ function akismet_submit( $as, $form ) {
+ global $akismet_api_host, $akismet_api_port;
+
+ if ( !in_array( $as, array( 'ham', 'spam' ) ) )
+ return false;
+
+ $query_string = http_build_query( $form );
+
+ $response = akismet_http_post( $query_string, $akismet_api_host, "/1.1/submit-{$as}", $akismet_api_port );
+ return trim( $response[1] );
+ }
}
-add_shortcode('contact-field', 'contact_form_field');
+/**
+ * Generic shortcode class.
+ * Does nothing other than store structured data and output the shortcode as a string
+ *
+ * Not very general - specific to Grunion.
+ */
+class Crunion_Contact_Form_Shortcode {
+ /**
+ * @var string the name of the shortcode: [$shortcode_name /]
+ */
+ var $shortcode_name;
+
+ /**
+ * @var array key => value pairs for the shortcode's attributes: [$shortcode_name key="value" ... /]
+ */
+ var $attributes;
+
+ /**
+ * @var array key => value pair for attribute defaults
+ */
+ var $defaults = array();
+
+ /**
+ * @var null|string Null for selfclosing shortcodes. Hhe inner content of otherwise: [$shortcode_name]$content[/$shortcode_name]
+ */
+ var $content;
+
+ /**
+ * @var array Associative array of inner "child" shortcodes equivalent to the $content: [$shortcode_name][child 1/][child 2/][/$shortcode_name]
+ */
+ var $fields;
+
+ /**
+ * @var null|string The HTML of the parsed inner "child" shortcodes". Null for selfclosing shortcodes.
+ */
+ var $body;
+
+ /**
+ * @param array $attributes An associative array of shortcode attributes. @see shortcode_atts()
+ * @param null|string $content Null for selfclosing shortcodes. The inner content otherwise.
+ */
+ function __construct( $attributes, $content = null ) {
+ $this->attributes = $this->unesc_attr( $attributes );
+ if ( is_array( $content ) ) {
+ $string_content = '';
+ foreach ( $content as $field ) {
+ $string_content .= (string) $field;
+ }
+
+ $this->content = $string_content;
+ } else {
+ $this->content = $content;
+ }
+ $this->parse_content( $content );
+ }
-function contact_form_shortcode( $atts, $content ) {
- global $post;
+ /**
+ * Processes the shortcode's inner content for "child" shortcodes
+ *
+ * @param string $content The shortcode's inner content: [shortcode]$content[/shortcode]
+ */
+ function parse_content( $content ) {
+ if ( is_null( $content ) ) {
+ $this->body = null;
+ }
- $default_to = get_option( 'admin_email' );
- $default_subject = "[" . get_option( 'blogname' ) . "]";
+ $this->body = do_shortcode( $content );
+ }
- if ( !empty( $atts['widget'] ) && $atts['widget'] ) {
- $default_subject .= " Sidebar";
- } elseif ( $post->ID ) {
- $default_subject .= " ". wp_kses( $post->post_title, array() );
- $post_author = get_userdata( $post->post_author );
- $default_to = $post_author->user_email;
+ /**
+ * Returns the value of the requested attribute.
+ *
+ * @param string $key The attribute to retrieve
+ * @return mixed
+ */
+ function get_attribute( $key ) {
+ return isset( $this->attributes[$key] ) ? $this->attributes[$key] : null;
}
- extract( shortcode_atts( array(
- 'to' => $default_to,
- 'subject' => $default_subject,
- 'show_subject' => 'no', // only used in back-compat mode
- 'widget' => 0 //This is not exposed to the user. Works with contact_form_widget_atts
- ), $atts ) );
+ function esc_attr( $value ) {
+ if ( is_array( $value ) ) {
+ return array_map( array( $this, 'esc_attr' ), $value );
+ }
- $widget = esc_attr( $widget );
+ $value = Grunion_Contact_Form_Plugin::strip_tags( $value );
+ $value = _wp_specialchars( $value, ENT_QUOTES, false, true );
- if ( ( function_exists( 'faux_faux' ) && faux_faux() ) || is_feed() )
- return '[contact-form]';
+ // Shortcode attributes can't contain "]"
+ $value = str_replace( ']', '', $value );
+ $value = str_replace( ',', '&#x002c;', $value ); // store commas encoded
+ $value = strtr( $value, array( '%' => '%25', '&' => '%26' ) );
- global $wp_query, $grunion_form, $contact_form_errors, $contact_form_values, $user_identity, $contact_form_last_id, $contact_form_message;
-
- // used to store attributes, configuration etc for access by contact-field shortcodes
- $grunion_form = new stdClass();
- $grunion_form->to = $to;
- $grunion_form->subject = $subject;
- $grunion_form->show_subject = $show_subject;
-
- if ( $widget )
- $id = 'widget-' . $widget;
- elseif ( is_singular() )
- $id = $wp_query->get_queried_object_id();
- else
- $id = $GLOBALS['post']->ID;
- if ( !$id ) // something terrible has happened
- return '[contact-form]';
-
- if ( $id == $contact_form_last_id )
- return;
- else
- $contact_form_last_id = $id;
-
- ob_start();
- wp_nonce_field( 'contact-form_' . $id );
- $nonce = ob_get_contents();
- ob_end_clean();
-
-
- $body = contact_form_parse( $content );
-
- $r = "<div id='contact-form-$id'>\n";
-
- $errors = array();
- if ( is_wp_error( $contact_form_errors ) && $errors = (array) $contact_form_errors->get_error_codes() ) {
- $r .= "<div class='form-error'>\n<h3>" . __( 'Error!', 'jetpack' ) . "</h3>\n<ul class='form-errors'>\n";
- foreach ( $contact_form_errors->get_error_messages() as $message )
- $r .= "\t<li class='form-error-message' style='color: red;'>$message</li>\n";
- $r .= "</ul>\n</div>\n\n";
+ // shortcode_parse_atts() does stripcslashes()
+ $value = addslashes( $value );
+ return $value;
}
-
- $action = apply_filters( 'grunion_contact_form_form_action', get_permalink( $post->ID ) . "#contact-form-$id", $post, $id );
- $r .= "<form action='" . esc_url( $action ) . "' method='post' class='contact-form commentsblock'>\n";
- $r .= $body;
- $r .= "\t<p class='contact-submit'>\n";
- $r .= "\t\t<input type='submit' value='" . __( "Submit &#187;", 'jetpack' ) . "' class='pushbutton-wide'/>\n";
- $r .= "\t\t$nonce\n";
- $r .= "\t\t<input type='hidden' name='contact-form-id' value='$id' />\n";
- $r .= "\t</p>\n";
- $r .= "</form>\n</div>";
-
- if ( !isset( $_POST['contact-form-id'] ) || $_POST['contact-form-id'] != $contact_form_last_id )
- return $r;
+ function unesc_attr( $value ) {
+ if ( is_array( $value ) ) {
+ return array_map( array( $this, 'unesc_attr' ), $value );
+ }
- if ( is_wp_error($contact_form_errors) )
- return $r;
+ // For back-compat with old Grunion encoding
+ // Also, unencode commas
+ $value = strtr( $value, array( '%26' => '&', '%25' => '%' ) );
+ $value = preg_replace( array( '/&#x0*22;/i', '/&#x0*27;/i', '/&#x0*26;/i', '/&#x0*2c;/i' ), array( '"', "'", '&', ',' ), $value );
+ $value = htmlspecialchars_decode( $value, ENT_QUOTES );
+ $value = Grunion_Contact_Form_Plugin::strip_tags( $value );
-
- $emails = str_replace( ' ', '', $to );
- $emails = explode( ',', $emails );
- foreach ( (array) $emails as $email ) {
- if ( is_email( $email ) && ( !function_exists( 'is_email_address_unsafe' ) || !is_email_address_unsafe( $email ) ) )
- $valid_emails[] = $email;
+ return $value;
}
- $to = ( $valid_emails ) ? $valid_emails : $default_to;
+ /**
+ * Generates the shortcode
+ */
+ function __toString() {
+ $r = "[{$this->shortcode_name} ";
- $message_sent = contact_form_send_message( $to, $subject, $widget );
+ foreach ( $this->attributes as $key => $value ) {
+ if ( !$value ) {
+ continue;
+ }
- if ( is_array( $contact_form_values ) )
- extract( $contact_form_values );
+ if ( isset( $this->defaults[$key] ) && $this->defaults[$key] == $value ) {
+ continue;
+ }
- if ( !isset( $comment_content ) )
- $comment_content = '';
- else
- $comment_content = wp_kses( $comment_content, array() );
+ if ( 'id' == $key ) {
+ continue;
+ }
+ $value = $this->esc_attr( $value );
- $r = "<div id='contact-form-$id'>\n";
+ if ( is_array( $value ) ) {
+ $value = join( ',', $value );
+ }
- $errors = array();
- if ( is_wp_error( $contact_form_errors ) && $errors = (array) $contact_form_errors->get_error_codes() ) :
- $r .= "<div class='form-error'>\n<h3>" . __( 'Error!', 'jetpack' ) . "</h3>\n<p>\n";
- foreach ( $contact_form_errors->get_error_messages() as $message )
- $r .= "\t$message<br />\n";
- $r .= "</p>\n</div>\n\n";
- else :
- $r_success_message = "<h3>" . __( 'Message Sent', 'jetpack' ) . "</h3>\n\n";
- $r_success_message .= wp_kses($contact_form_message, array('br' => array(), 'blockquote' => array()));
+ if ( false === strpos( $value, "'" ) ) {
+ $value = "'$value'";
+ } elseif ( false === strpos( $value, '"' ) ) {
+ $value = '"' . $value . '"';
+ } else {
+ // Shortcodes can't contain both '"' and "'". Strip one.
+ $value = str_replace( "'", '', $value );
+ $value = "'$value'";
+ }
- $r .= apply_filters( 'grunion_contact_form_success_message', $r_success_message );
+ $r .= "{$key}={$value} ";
+ }
- $r .= "</div>";
-
- // Reset for multiple contact forms. Hacky
- $contact_form_values['comment_content'] = '';
+ $r = rtrim( $r );
- return $r;
- endif;
+ if ( $this->fields ) {
+ $r .= ']';
- return $r;
-}
-add_shortcode( 'contact-form', 'contact_form_shortcode' );
+ foreach ( $this->fields as $field ) {
+ $r .= (string) $field;
+ }
-function contact_form_send_message( $to, $subject, $widget ) {
- global $post;
-
- if ( !isset( $_POST['contact-form-id'] ) )
- return;
-
- if ( ( $widget && 'widget-' . $widget != $_POST['contact-form-id'] ) || ( !$widget && $post->ID != $_POST['contact-form-id'] ) )
- return;
-
- if ( $widget )
- check_admin_referer( 'contact-form_widget-' . $widget );
- else
- check_admin_referer( 'contact-form_' . $post->ID );
-
- global $contact_form_values, $contact_form_errors, $current_user, $user_identity;
- global $contact_form_fields, $contact_form_message;
-
- // compact the fields and values into an array of Label => Value pairs
- // also find values for comment_author_email and other significant fields
- $all_values = $extra_values = array();
-
- foreach ( $contact_form_fields as $id => $field ) {
- if ( $field['type'] == 'email' && !isset( $comment_author_email ) ) {
- $comment_author_email = $contact_form_values[ $id ];
- $comment_author_email_label = $field['label'];
- } elseif ( $field['type'] == 'name' && !isset( $comment_author ) ) {
- $comment_author = $contact_form_values[ $id ];
- $comment_author_label = $field['label'];
- } elseif ( $field['type'] == 'url' && !isset( $comment_author_url ) ) {
- $comment_author_url = $contact_form_values[ $id ];
- $comment_author_url_label = $field['label'];
- } elseif ( $field['type'] == 'subject' && !isset( $contact_form_subject ) ) {
- $contact_form_subject = $contact_form_values[$id];
- $contact_form_subject_label = $field['label'];
- } elseif ( $field['type'] == 'textarea' && !isset( $comment_content ) ) {
- $comment_content = $contact_form_values[ $id ];
- $comment_content_label = $field['label'];
+ $r .= "[/{$this->shortcode_name}]";
} else {
- $extra_values[ $field['label'] ] = $contact_form_values[ $id ];
+ $r .= '/]';
}
-
- $all_values[ $field['label'] ] = $contact_form_values[ $id ];
+
+ return $r;
}
+}
-/*
- $contact_form_values = array();
- $contact_form_errors = new WP_Error();
+/**
+ * Class for the contact-form shortcode.
+ * Parses shortcode to output the contact form as HTML
+ * Sends email and stores the contact form response (a.k.a. "feedback")
+ */
+class Grunion_Contact_Form extends Crunion_Contact_Form_Shortcode {
+ var $shortcode_name = 'contact-form';
- list($comment_author, $comment_author_email, $comment_author_url) = is_user_logged_in() ?
- add_magic_quotes( array( $user_identity, $current_user->data->user_email, $current_user->data->user_url ) ) :
- array( $_POST['comment_author'], $_POST['comment_author_email'], $_POST['comment_author_url'] );
-*/
+ /**
+ * @var WP_Error stores form submission errors
+ */
+ var $errors;
- $comment_author = stripslashes( apply_filters( 'pre_comment_author_name', $comment_author ) );
+ /**
+ * @var Grunion_Contact_Form The most recent (inclusive) contact-form shortcode processed
+ */
+ static $last;
- if ( !empty( $comment_author_email ) ) {
- $comment_author_email = stripslashes( apply_filters( 'pre_comment_author_email', $comment_author_email ) );
- } else {
- $comment_author_email = '';
- $comment_author_email_label = '';
- }
+ /**
+ * @var bool Whether to print the grunion.css style when processing the contact-form shortcode
+ */
+ static $style = false;
+
+ function __construct( $attributes, $content = null ) {
+ global $post;
+
+ // Set up the default subject and recipient for this form
+ $default_to = get_option( 'admin_email' );
+ $default_subject = "[" . get_option( 'blogname' ) . "]";
- if ( !empty( $comment_author_url ) ) {
- $comment_author_url = stripslashes( apply_filters( 'pre_comment_author_url', $comment_author_url ) );
- if ( 'http://' == $comment_author_url ) {
- $comment_author_url = '';
+ if ( !empty( $attributes['widget'] ) && $attributes['widget'] ) {
+ $attributes['id'] = 'widget-' . $attributes['widget'];
+
+ $default_subject = sprintf( _x( '%1$s Sidebar', '%1$s = blog name', 'jetpack' ), $default_subject );
+ } else if ( $post ) {
+ $attributes['id'] = $post->ID;
+ $default_subject = sprintf( _x( '%1$s %2$s', '%1$s = blog name, %2$s = post title', 'jetpack' ), $default_subject, Grunion_Contact_Form_Plugin::strip_tags( $post->post_title ) );
+ $post_author = get_userdata( $post->post_author );
+ $default_to = $post_author->user_email;
}
- } else {
- $comment_author_url = '';
- $comment_author_url_label = '';
- }
- $comment_content = stripslashes( $comment_content );
- $comment_content = trim( wp_kses( $comment_content, array() ) );
+ $this->defaults = array(
+ 'to' => $default_to,
+ 'subject' => $default_subject,
+ 'show_subject' => 'no', // only used in back-compat mode
+ 'widget' => 0, // Not exposed to the user. Works with Grunion_Contact_Form_Plugin::widget_atts()
+ 'id' => null, // Not exposed to the user. Set above.
+ );
+
+ $attributes = shortcode_atts( $this->defaults, $attributes );
- if ( empty( $contact_form_subject ) )
- $contact_form_subject = trim( wp_kses( $subject, array() ) );
- else
- $contact_form_subject = trim( wp_kses( $contact_form_subject, array() ) );
-
- $comment_author_IP = $_SERVER['REMOTE_ADDR'];
+ // We only add the contact-field shortcode temporarily while processing the contact-form shortcode
+ add_shortcode( 'contact-field', array( $this, 'parse_contact_field' ) );
- $vars = array( 'comment_author', 'comment_author_email', 'comment_author_url', 'contact_form_subject', 'comment_author_IP' );
- foreach ( $vars as $var )
- $$var = str_replace( array("\n", "\r" ), '', $$var ); // I don't know if it's possible to inject this
- $vars[] = 'comment_content';
+ parent::__construct( $attributes, $content );
- $contact_form_values = compact( $vars );
+ // There were no fields in the contact form. The form was probably just [contact-form /]. Build a default form.
+ if ( empty( $this->fields ) ) {
+ // same as the original Grunion v1 form
+ $default_form = '
+ [contact-field label="' . __( 'Name', 'jetpack' ) . '" type="name" required="true" /]
+ [contact-field label="' . __( 'Email', 'jetpack' ) . '" type="email" required="true" /]
+ [contact-field label="' . __( 'Website', 'jetpack' ) . '" type="url" /]';
- $spam = '';
- $akismet_values = contact_form_prepare_for_akismet( $contact_form_values );
- $is_spam = apply_filters( 'contact_form_is_spam', $akismet_values );
- if ( is_wp_error( $is_spam ) )
- return; // abort
- else if ( $is_spam === TRUE )
- $spam = '***SPAM*** ';
+ if ( 'yes' == strtolower( $this->get_attribute( 'show_subject' ) ) ) {
+ $default_form .= '
+ [contact-field label="' . __( 'Subject', 'jetpack' ) . '" type="subject" /]';
+ }
- if ( !$comment_author )
- $comment_author = $comment_author_email;
+ $default_form .= '
+ [contact-field label="' . __( 'Message', 'jetpack' ) . '" type="textarea" /]';
- $to = apply_filters( 'contact_form_to', $to );
- foreach ( (array) $to as $to_key => $to_value ) {
- $to[$to_key] = wp_kses( $to_value, array() );
+ $this->parse_content( $default_form );
+ }
+
+ // $this->body and $this->fields have been setup. We no longer need the contact-field shortcode.
+ remove_shortcode( 'contact-field' );
+ }
+
+ /**
+ * Toggle for printing the grunion.css stylesheet
+ *
+ * @param bool $style
+ */
+ static function style( $style ) {
+ $previous_style = self::$style;
+ self::$style = (bool) $style;
+ return $previous_style;
}
- $from_email_addr = $to[0];
- if ( !empty( $comment_author_email ) ) {
- $from_email_addr = $comment_author_email;
+ /**
+ * Turn on printing of grunion.css stylesheet
+ * @see ::style()
+ * @internal
+ * @param bool $style
+ */
+ static function _style_on() {
+ return self::style( true );
}
- $headers = 'From: ' . wp_kses( $comment_author, array() ) .
- ' <' . wp_kses( $from_email_addr, array() ) . ">\r\n" .
- 'Reply-To: ' . wp_kses( $from_email_addr, array() ) . "\r\n" .
- "Content-Type: text/plain; charset=\"" . get_option('blog_charset') . "\"";
- $subject = apply_filters( 'contact_form_subject', $contact_form_subject );
- $subject = wp_kses( $subject, array() );
+ /**
+ * The contact-form shortcode processor
+ *
+ * @param array $attributes Key => Value pairs as parsed by shortcode_parse_atts()
+ * @param string|null $content The shortcode's inner content: [contact-form]$content[/contact-form]
+ * @return string HTML for the concat form.
+ */
+ static function parse( $attributes, $content ) {
+ // Create a new Grunion_Contact_Form object (this class)
+ $form = new Grunion_Contact_Form( $attributes, $content );
+
+ $id = $form->get_attribute( 'id' );
- $time = date_i18n( __( 'l F j, Y \a\t g:i a', 'jetpack' ), current_time( 'timestamp' ) );
-
- $extra_content = '';
- $extra_content_br = '';
+ if ( !$id ) { // something terrible has happened
+ return '[contact-form]';
+ }
+
+ if ( apply_filters( 'jetpack_bail_on_shortcode', false, 'contact-form' ) || is_feed() ) {
+ return '[contact-form]';
+ }
+
+ // Only allow one contact form per post/widget
+ if ( self::$last && $id == self::$last->get_attribute( 'id' ) ) {
+ // We're processing the same post
+
+ if ( self::$last->attributes != $form->attributes || self::$last->content != $form->content ) {
+ // And we're processing a different shortcode;
+ return '';
+ } // else, we're processing the same shortcode - probably a separate run of do_shortcode() - let it through
+
+ } else {
+ self::$last = $form;
+ }
+
+ // Enqueue the grunion.css stylesheet if self::$style allows it
+ if ( self::$style && ( empty( $_REQUEST['action'] ) || $_REQUEST['action'] != 'grunion_shortcode_to_json' ) ) {
+ // Enqueue the style here instead of printing it, because if some other plugin has run the_post()+rewind_posts(),
+ // (like VideoPress does), the style tag gets "printed" the first time and discarded, leaving the contact form unstyled.
+ // when WordPress does the real loop.
+ wp_enqueue_style( 'grunion.css' );
+ }
+
+ $r = '';
+ $r .= "<div id='contact-form-$id'>\n";
- foreach ( $extra_values as $label => $value ) {
- $extra_content .= $label . ': ' . trim($value) . "\n";
- $extra_content_br .= wp_kses( $label, array() ) . ': ' . wp_kses( trim($value), array() ) . "<br />";
- }
-
- $message = "$comment_author_label: $comment_author\n";
- if ( !empty( $comment_author_email ) ) {
- $message .= "$comment_author_email_label: $comment_author_email\n";
- }
- if ( !empty( $comment_author_url ) ) {
- $message .= "$comment_author_url_label: $comment_author_url\n";
- }
- $message .= "$comment_content_label: $comment_content\n";
- $message .= $extra_content . "\n";
-
- $message .= __( "Time:", 'jetpack' ) . " " . $time . "\n";
- $message .= __( "IP Address:", 'jetpack' ) . " " . $comment_author_IP . "\n";
- $message .= __( "Contact Form URL:", 'jetpack' ) . " " . get_permalink( $post->ID ) . "\n";
-
-
- // Construct message that is returned to user
- $contact_form_message = "<blockquote>";
- if (isset($comment_author_label))
- $contact_form_message .= wp_kses( $comment_author_label, array() ) . ": " . wp_kses( $comment_author, array() ) . "<br />";
- if ( !empty( $comment_author_email ) )
- $contact_form_message .= wp_kses( $comment_author_email_label, array() ) . ": " . wp_kses( $comment_author_email, array() ) . "<br />";
- if ( !empty( $comment_author_url ) )
- $contact_form_message .= wp_kses( $comment_author_url_label, array() ) . ": " . wp_kses( $comment_author_url, array() ) . "<br />";
- if ( !empty( $contact_form_subject_label ) ) {
- $contact_form_message .= wp_kses( $contact_form_subject_label, array() ) . ": " . wp_kses( $contact_form_subject, array() ) . "<br />";
- }
- if (isset($comment_content_label))
- $contact_form_message .= wp_kses( $comment_content_label, array() ) . ": " . wp_kses( $comment_content, array() ) . "<br />";
- if (isset($extra_content_br))
- $contact_form_message .= $extra_content_br;
- $contact_form_message .= "</blockquote><br /><br />";
-
- if ( is_user_logged_in() ) {
- $message .= "\n";
- $message .= sprintf(
- __( 'Sent by a verified %s user.', 'jetpack' ),
- isset( $GLOBALS['current_site']->site_name ) && $GLOBALS['current_site']->site_name ? $GLOBALS['current_site']->site_name : '"' . get_option( 'blogname' ) . '"'
- );
- } else {
- $message .= __( "Sent by an unverified visitor to your site.", 'jetpack' );
- }
+ if ( is_wp_error( $form->errors ) && $form->errors->get_error_codes() ) {
+ // There are errors. Display them
+ $r .= "<div class='form-error'>\n<h3>" . __( 'Error!', 'jetpack' ) . "</h3>\n<ul class='form-errors'>\n";
+ foreach ( $form->errors->get_error_messages() as $message )
+ $r .= "\t<li class='form-error-message'>" . esc_html( $message ) . "</li>\n";
+ $r .= "</ul>\n</div>\n\n";
+ }
+
+ if ( isset( $_GET['contact-form-id'] ) && $_GET['contact-form-id'] == self::$last->get_attribute( 'id' ) && isset( $_GET['contact-form-sent'] ) ) {
+ // The contact form was submitted. Show the success message/results
+
+ $feedback_id = (int) $_GET['contact-form-sent'];
+
+ $back_url = remove_query_arg( array( 'contact-form-id', 'contact-form-sent', '_wpnonce' ) );
+
+ $r_success_message =
+ "<h3>" . __( 'Message Sent', 'jetpack' ) .
+ ' (<a href="' . esc_url( $back_url ) . '">' . esc_html__( 'go back', 'jetpack' ) . '</a>)' .
+ "</h3>\n\n";
+
+ // Don't show the feedback details unless the nonce matches
+ if ( $feedback_id && wp_verify_nonce( stripslashes( $_GET['_wpnonce'] ), "contact-form-sent-{$feedback_id}" ) ) {
+ $feedback = get_post( $feedback_id );
+
+ $field_ids = $form->get_field_ids();
+
+ // Maps field_ids to post_meta keys
+ $field_value_map = array(
+ 'name' => 'author',
+ 'email' => 'author_email',
+ 'url' => 'author_url',
+ 'subject' => 'subject',
+ 'textarea' => false, // not a post_meta key. This is stored in post_content
+ );
+
+ $contact_form_message = "<blockquote>\n";
+
+ // "Standard" field whitelist
+ foreach ( $field_value_map as $type => $meta_key ) {
+ if ( isset( $field_ids[$type] ) ) {
+ $field = $form->fields[$field_ids[$type]];
+
+ if ( $meta_key ) {
+ $value = get_post_meta( $feedback_id, "_feedback_{$meta_key}", true );
+ } else {
+ // The feedback content is stored as the first "half" of post_content
+ $value = $feedback->post_content;
+ list( $value ) = explode( '<!--more-->', $value );
+ $value = trim( $value );
+ }
+
+ $contact_form_message .= sprintf(
+ _x( '%1$s: %2$s', '%1$s = form field label, %2$s = form field value', 'jetpack' ),
+ wp_kses( $field->get_attribute( 'label' ), array() ),
+ wp_kses( $value, array() )
+ ) . '<br />';
+ }
+ }
+
+ // "Non-standard" fields
+ if ( $field_ids['extra'] ) {
+ // array indexed by field label (not field id)
+ $extra_fields = get_post_meta( $feedback_id, '_feedback_extra_fields', true );
+
+ foreach ( $field_ids['extra'] as $field_id ) {
+ $field = $form->fields[$field_id];
+ $label = $field->get_attribute( 'label' );
+ $contact_form_message .= sprintf(
+ _x( '%1$s: %2$s', '%1$s = form field label, %2$s = form field value', 'jetpack' ),
+ wp_kses( $label, array() ),
+ wp_kses( $extra_fields[$label], array() )
+ ) . '<br />';
+ }
+ }
+
+ $contact_form_message .= "</blockquote><br /><br />";
+
+ $r_success_message .= wp_kses( $contact_form_message, array( 'br' => array(), 'blockquote' => array() ) );
+ }
- $message = apply_filters( 'contact_form_message', $message );
- $message = wp_kses( $message, array() );
+ $r .= apply_filters( 'grunion_contact_form_success_message', $r_success_message );
+ } else {
+ // Nothing special - show the normal contact form
+
+ if ( $form->get_attribute( 'widget' ) ) {
+ // Submit form to the current URL
+ $url = remove_query_arg( array( 'contact-form-id', 'contact-form-sent', 'action', '_wpnonce' ) );
+ } else {
+ // Submit form to the post permalink
+ $url = get_permalink();
+ }
+
+ // May eventually want to send this to admin-post.php...
+ $url = apply_filters( 'grunion_contact_form_form_action', "{$url}#contact-form-{$id}", $GLOBALS['post'], $id );
+
+ $r .= "<form action='" . esc_url( $url ) . "' method='post' class='contact-form commentsblock'>\n";
+ $r .= $form->body;
+ $r .= "\t<p class='contact-submit'>\n";
+ $r .= "\t\t<input type='submit' value='" . esc_attr__( 'Submit &#187;', 'jetpack' ) . "' class='pushbutton-wide'/>\n";
+ $r .= "\t\t" . wp_nonce_field( 'contact-form_' . $id, '_wpnonce', true, false ) . "\n"; // nonce and referer
+ $r .= "\t\t<input type='hidden' name='contact-form-id' value='$id' />\n";
+ $r .= "\t\t<input type='hidden' name='action' value='grunion-contact-form' />\n";
+ $r .= "\t</p>\n";
+ $r .= "</form>\n";
+ }
- // keep a copy of the feedback as a custom post type
- $feedback_mysql_time = current_time( 'mysql' );
- $feedback_title = "{$comment_author} - {$feedback_mysql_time}";
- $feedback_status = 'publish';
- if ( $is_spam === TRUE )
- $feedback_status = 'spam';
+ $r .= "</div>";
- foreach ( (array) $akismet_values as $av_key => $av_value ) {
- $akismet_values[$av_key] = wp_kses( $av_value, array() );
+ return $r;
}
- foreach ( (array) $all_values as $all_key => $all_value ) {
- $all_values[$all_key] = wp_kses( $all_value, array() );
+ /**
+ * The contact-field shortcode processor
+ * We use an object method here instead of a static Grunion_Contact_Form_Field class method to parse contact-field shortcodes so that we can tie them to the contact-form object.
+ *
+ * @param array $attributes Key => Value pairs as parsed by shortcode_parse_atts()
+ * @param string|null $content The shortcode's inner content: [contact-field]$content[/contact-field]
+ * @return HTML for the contact form field
+ */
+ function parse_contact_field( $attributes, $content ) {
+ $field = new Grunion_Contact_Form_Field( $attributes, $content, $this );
+
+ $field_id = $field->get_attribute( 'id' );
+ if ( $field_id ) {
+ $this->fields[$field_id] = $field;
+ } else {
+ $this->fields[] = $field;
+ }
+
+ if (
+ isset( $_POST['action'] ) && 'grunion-contact-form' === $_POST['action']
+ &&
+ isset( $_POST['contact-form-id'] ) && $this->get_attribute( 'id' ) == $_POST['contact-form-id']
+ ) {
+ // If we're processing a POST submission for this contact form, validate the field value so we can show errors as necessary.
+ $field->validate();
+ }
+
+ // Output HTML
+ return $field->render();
}
- foreach ( (array) $extra_values as $ev_key => $ev_value ) {
- $ev_values[$ev_key] = wp_kses( $ev_value, array() );
+ /**
+ * Loops through $this->fields to generate a (structured) list of field IDs
+ * @return array
+ */
+ function get_field_ids() {
+ $field_ids = array(
+ 'all' => array(), // array of all field_ids
+ 'extra' => array(), // array of all non-whitelisted field IDs
+
+ // Whitelisted "standard" field IDs:
+ // 'email' => field_id,
+ // 'name' => field_id,
+ // 'url' => field_id,
+ // 'subject' => field_id,
+ // 'textarea' => field_id,
+ );
+
+ foreach ( $this->fields as $id => $field ) {
+ $field_ids['all'][] = $id;
+
+ $type = $field->get_attribute( 'type' );
+ if ( isset( $field_ids[$type] ) ) {
+ // This type of field is already present in our whitelist of "standard" fields for this form
+ // Put it in extra
+ $field_ids['extra'][] = $id;
+ continue;
+ }
+
+ switch ( $type ) {
+ case 'email' :
+ case 'name' :
+ case 'url' :
+ case 'subject' :
+ case 'textarea' :
+ $field_ids[$type] = $id;
+ break;
+ default :
+ // Put everything else in extra
+ $field_ids['extra'][] = $id;
+ }
+ }
+
+ return $field_ids;
}
- # We need to make sure that the post author is always zero for contact
- # form submissions. This prevents export/import from trying to create
- # new users based on form submissions from people who were logged in
- # at the time.
- #
- # Unfortunately wp_insert_post() tries very hard to make sure the post
- # author gets the currently logged in user id. That is how we ended up
- # with this work around.
- global $do_grunion_insert;
- $do_grunion_insert = TRUE;
- add_filter( 'wp_insert_post_data', 'grunion_insert_filter', 10, 2 );
+ /**
+ * Process the contact form's POST submission
+ * Stores feedback. Sends email.
+ */
+ function process_submission() {
+ global $post;
- $post_id = wp_insert_post( array(
- 'post_date' => $feedback_mysql_time,
- 'post_type' => 'feedback',
- 'post_status' => $feedback_status,
- 'post_parent' => $post->ID,
- 'post_title' => wp_kses( $feedback_title, array() ),
- 'post_content' => wp_kses($comment_content . "\n<!--more-->\n" . "AUTHOR: {$comment_author}\nAUTHOR EMAIL: {$comment_author_email}\nAUTHOR URL: {$comment_author_url}\nSUBJECT: {$contact_form_subject}\nIP: {$comment_author_IP}\n" . print_r( $all_values, TRUE ), array()), // so that search will pick up this data
- 'post_name' => md5( $feedback_title )
- ) );
+ $plugin = Grunion_Contact_Form_Plugin::init();
- # once insert has finished we don't need this filter any more
- remove_filter( 'wp_insert_post_data', 'grunion_insert_filter' );
- $do_grunion_insert = FALSE;
+ $id = $this->get_attribute( 'id' );
+ $to = $this->get_attribute( 'to' );
+ $widget = $this->get_attribute( 'widget' );
- update_post_meta( $post_id, '_feedback_author', wp_kses( $comment_author, array() ) );
- update_post_meta( $post_id, '_feedback_author_email', wp_kses( $comment_author_email, array() ) );
- update_post_meta( $post_id, '_feedback_author_url', wp_kses( $comment_author_url, array() ) );
- update_post_meta( $post_id, '_feedback_subject', wp_kses( $contact_form_subject, array() ) );
- update_post_meta( $post_id, '_feedback_ip', wp_kses( $comment_author_IP, array() ) );
- update_post_meta( $post_id, '_feedback_contact_form_url', wp_kses( get_permalink( $post->ID ), array() ) );
- update_post_meta( $post_id, '_feedback_all_fields', $all_values );
- update_post_meta( $post_id, '_feedback_extra_fields', $extra_values );
- update_post_meta( $post_id, '_feedback_akismet_values', $akismet_values );
- update_post_meta( $post_id, '_feedback_email', array( 'to' => $to, 'subject' => $subject, 'message' => $message, 'headers' => $headers ) );
+ $contact_form_subject = $this->get_attribute( 'subject' );
- do_action( 'grunion_pre_message_sent', $post_id, $all_values, $extra_values );
+ $to = str_replace( ' ', '', $to );
+ $emails = explode( ',', $to );
- # schedule deletes of old spam feedbacks
- if ( !wp_next_scheduled( 'grunion_scheduled_delete' ) ) {
- wp_schedule_event( time() + 250, 'daily', 'grunion_scheduled_delete' );
- }
+ $valid_emails = array();
- if ( $is_spam !== TRUE )
- return wp_mail( $to, "{$spam}{$subject}", $message, $headers );
- elseif ( apply_filters( 'grunion_still_email_spam', FALSE ) == TRUE )
- return wp_mail( $to, "{$spam}{$subject}", $message, $headers );
+ foreach ( (array) $emails as $email ) {
+ if ( !is_email( $email ) ) {
+ continue;
+ }
-}
+ if ( function_exists( 'is_email_address_unsafe' ) && is_email_address_unsafe( $email ) ) {
+ continue;
+ }
-// populate an array with all values necessary to submit a NEW comment to Akismet
-// note that this includes the current user_ip etc, so this should only be called when accepting a new item via $_POST
-function contact_form_prepare_for_akismet( $form ) {
+ $valid_emails[] = $email;
+ }
- $form['comment_type'] = 'contact_form';
- $form['user_ip'] = preg_replace( '/[^0-9., ]/', '', $_SERVER['REMOTE_ADDR'] );
- $form['user_agent'] = $_SERVER['HTTP_USER_AGENT'];
- $form['referrer'] = $_SERVER['HTTP_REFERER'];
- $form['blog'] = get_option( 'home' );
+ // No one to send it to :(
+ if ( !$valid_emails ) {
+ return;
+ }
- $ignore = array( 'HTTP_COOKIE' );
+ $to = $valid_emails;
- foreach ( $_SERVER as $k => $value )
- if ( !in_array( $k, $ignore ) && is_string( $value ) )
- $form["$k"] = $value;
-
- return $form;
-}
+ // Make sure we're processing the form we think we're processing... probably a redundant check.
+ if ( $widget ) {
+ if ( 'widget-' . $widget != $_POST['contact-form-id'] ) {
+ return;
+ }
+ } else {
+ if ( $post->ID != $_POST['contact-form-id'] ) {
+ return;
+ }
+ }
-// submit an array to Akismet. If you're accepting a new item via $_POST, run it through contact_form_prepare_for_akismet() first
-function contact_form_is_spam_akismet( $form ) {
- if ( !function_exists( 'akismet_http_post' ) )
- return false;
-
- global $akismet_api_host, $akismet_api_port;
-
- $query_string = '';
- foreach ( array_keys( $form ) as $k )
- $query_string .= $k . '=' . urlencode( $form[$k] ) . '&';
-
- $response = akismet_http_post( $query_string, $akismet_api_host, '/1.1/comment-check', $akismet_api_port );
- $result = false;
- if ( 'true' == trim( $response[1] ) ) // 'true' is spam
- $result = true;
- return apply_filters( 'contact_form_is_spam_akismet', $result, $form );
-}
+ $field_ids = $this->get_field_ids();
-// submit a comment as either spam or ham
-// $as should be a string (either 'spam' or 'ham'), $form should be the comment array
-function contact_form_akismet_submit( $as, $form ) {
- global $akismet_api_host, $akismet_api_port;
-
- if ( !in_array( $as, array( 'ham', 'spam' ) ) )
- return false;
+ // Initialize all these "standard" fields to null
+ $comment_author_email = $comment_author_email_label = // v
+ $comment_author = $comment_author_label = // v
+ $comment_author_url = $comment_author_url_label = // v
+ $comment_content = $comment_content_label = null;
- $query_string = '';
- foreach ( array_keys( $form ) as $k )
- $query_string .= $k . '=' . urlencode( $form[$k] ) . '&';
+ // For each of the "standard" fields, grab their field label and value.
- $response = akismet_http_post( $query_string, $akismet_api_host, '/1.1/submit-'.$as, $akismet_api_port );
- return trim( $response[1] );
-}
+ if ( isset( $field_ids['name'] ) ) {
+ $field = $this->fields[$field_ids['name']];
+ $comment_author = Grunion_Contact_Form_Plugin::strip_tags( stripslashes( apply_filters( 'pre_comment_author_name', addslashes( $field->value ) ) ) );
+ $comment_author_label = Grunion_Contact_Form_Plugin::strip_tags( $field->get_attribute( 'label' ) );
+ }
-function contact_form_widget_atts( $text ) {
- static $widget = 0;
-
- $widget++;
+ if ( isset( $field_ids['email'] ) ) {
+ $field = $this->fields[$field_ids['email']];
+ $comment_author_email = Grunion_Contact_Form_Plugin::strip_tags( stripslashes( apply_filters( 'pre_comment_author_email', addslashes( $field->value ) ) ) );
+ $comment_author_email_label = Grunion_Contact_Form_Plugin::strip_tags( $field->get_attribute( 'label' ) );
+ }
- return preg_replace( '/\[contact-form([^a-zA-Z_-])/', '[contact-form widget="' . $widget . '"\\1', $text );
-}
-add_filter( 'widget_text', 'contact_form_widget_atts', 0 );
+ if ( isset( $field_ids['url'] ) ) {
+ $field = $this->fields[$field_ids['url']];
+ $comment_author_url = Grunion_Contact_Form_Plugin::strip_tags( stripslashes( apply_filters( 'pre_comment_author_url', addslashes( $field->value ) ) ) );
+ if ( 'http://' == $comment_author_url ) {
+ $comment_author_url = '';
+ }
+ $comment_author_url_label = Grunion_Contact_Form_Plugin::strip_tags( $field->get_attribute( 'label' ) );
+ }
-function contact_form_widget_shortcode_hack( $text ) {
- if ( !preg_match( '/\[contact-form([^a-zA-Z_-])/', $text ) ) {
- return $text;
- }
+ if ( isset( $field_ids['textarea'] ) ) {
+ $field = $this->fields[$field_ids['textarea']];
+ $comment_content = trim( Grunion_Contact_Form_Plugin::strip_tags( $field->value ) );
+ $comment_content_label = Grunion_Contact_Form_Plugin::strip_tags( $field->get_attribute( 'label' ) );
+ }
- $old = $GLOBALS['shortcode_tags'];
- remove_all_shortcodes();
- add_shortcode( 'contact-form', 'contact_form_shortcode' );
- add_shortcode( 'contact-field', 'contact_form_field' );
- $text = do_shortcode( $text );
- $GLOBALS['shortcode_tags'] = $old;
- return $text;
-}
+ if ( isset( $field_ids['subject'] ) ) {
+ $field = $this->fields[$field_ids['subject']];
+ if ( $field->value ) {
+ $contact_form_subject = Grunion_Contact_Form_Plugin::strip_tags( $field->value );
+ }
+ }
+
+ $all_values = $extra_values = array();
+
+ // For all fields, grab label and value
+ foreach ( $field_ids['all'] as $field_id ) {
+ $field = $this->fields[$field_id];
+ $label = $field->get_attribute( 'label' );
+ $value = $field->value;
+ $all_values[$label] = $value;
+ }
+
+ // For the "non-standard" fields, grab label and value
+ foreach ( $field_ids['extra'] as $field_id ) {
+ $field = $this->fields[$field_id];
+ $label = $field->get_attribute( 'label' );
+ $value = $field->value;
+ $extra_values[$label] = $value;
+ }
+
+ $contact_form_subject = trim( $contact_form_subject );
+
+ $comment_author_IP = Grunion_Contact_Form_Plugin::strip_tags( $_SERVER['REMOTE_ADDR'] );
+
+ $vars = array( 'comment_author', 'comment_author_email', 'comment_author_url', 'contact_form_subject', 'comment_author_IP' );
+ foreach ( $vars as $var )
+ $$var = str_replace( array( "\n", "\r" ), '', $$var );
+ $vars[] = 'comment_content';
+
+ $spam = '';
+ $akismet_values = $plugin->prepare_for_akismet( compact( $vars ) );
+
+ // Is it spam?
+ $is_spam = apply_filters( 'contact_form_is_spam', $akismet_values );
+ if ( is_wp_error( $is_spam ) ) // WP_Error to abort
+ return; // abort
+ else if ( $is_spam === TRUE ) // TRUE to flag a spam
+ $spam = '***SPAM*** ';
+
+ if ( !$comment_author )
+ $comment_author = $comment_author_email;
+
+ $to = (array) apply_filters( 'contact_form_to', $to );
+ foreach ( $to as $to_key => $to_value ) {
+ $to[$to_key] = Grunion_Contact_Form_Plugin::strip_tags( $to_value );
+ }
-function contact_form_init() {
- if ( function_exists( 'akismet_http_post' ) ) {
- add_filter( 'contact_form_is_spam', 'contact_form_is_spam_akismet', 10 );
- add_action( 'contact_form_akismet', 'contact_form_akismet_submit', 10, 2 );
- }
- if ( !has_filter( 'widget_text', 'do_shortcode' ) )
- add_filter( 'widget_text', 'contact_form_widget_shortcode_hack', 5 );
-
- // custom post type we'll use to keep copies of the feedback items
- register_post_type( 'feedback', array(
- 'labels' => array(
- 'name' => __( 'Feedbacks', 'jetpack' ),
- 'singular_name' => __( 'Feedback', 'jetpack' ),
- 'search_items' => __( 'Search Feedback', 'jetpack' ),
- 'not_found' => __( 'No feedback found', 'jetpack' ),
- 'not_found_in_trash' => __( 'No feedback found', 'jetpack' )
- ),
- 'menu_icon' => GRUNION_PLUGIN_URL . 'images/grunion-menu.png',
- 'show_ui' => TRUE,
- 'show_in_admin_bar' => FALSE,
- 'public' => FALSE,
- 'rewrite' => FALSE,
- 'query_var' => FALSE,
- 'capability_type' => 'page'
- ) );
-
- register_post_status( 'spam', array(
- 'label' => 'Spam',
- 'public' => FALSE,
- 'exclude_from_search' => TRUE,
- 'show_in_admin_all_list' => FALSE,
- 'label_count' => _n_noop( 'Spam <span class="count">(%s)</span>', 'Spam <span class="count">(%s)</span>', 'jetpack' ),
- 'protected' => TRUE,
- '_builtin' => FALSE
- ) );
+ $blog_url = parse_url( site_url() );
+ $from_email_addr = 'wordpress@' . $blog_url['host'];
+
+ $reply_to_addr = $to[0];
+ if ( ! empty( $comment_author_email ) ) {
+ $reply_to_addr = $comment_author_email;
+ }
+
+ $headers = 'From: ' . $comment_author .' <' . $from_email_addr . ">\r\n" .
+ 'Reply-To: ' . $comment_author . ' <' . $reply_to_addr . ">\r\n" .
+ "Content-Type: text/plain; charset=\"" . get_option('blog_charset') . "\"";
+
+ $subject = apply_filters( 'contact_form_subject', $contact_form_subject );
+
+ $time = date_i18n( __( 'l F j, Y \a\t g:i a', 'jetpack' ), current_time( 'timestamp' ) );
- /* Can be dequeued by placing the following in wp-content/themes/yourtheme/functions.php
- *
- * function remove_grunion_style() {
- * wp_deregister_style('grunion.css');
- * }
- * add_action('wp_print_styles', 'remove_grunion_style');
- */
+ $extra_content = '';
- wp_register_style('grunion.css', GRUNION_PLUGIN_URL . 'css/grunion.css');
+ foreach ( $extra_values as $label => $value ) {
+ $extra_content .= $label . ': ' . trim( $value ) . "\n";
+ }
+
+ $message = "$comment_author_label: $comment_author\n";
+ if ( !empty( $comment_author_email ) ) {
+ $message .= "$comment_author_email_label: $comment_author_email\n";
+ }
+ if ( !empty( $comment_author_url ) ) {
+ $message .= "$comment_author_url_label: $comment_author_url\n";
+ }
+ if ( !empty( $comment_content_label ) ) {
+ $message .= "$comment_content_label: $comment_content\n";
+ }
+ $message .= $extra_content . "\n";
+
+ $message .= __( 'Time:', 'jetpack' ) . ' ' . $time . "\n";
+ $message .= __( 'IP Address:', 'jetpack' ) . ' ' . $comment_author_IP . "\n";
+
+ if ( $widget ) {
+ $url = home_url( '/' );
+ } else {
+ $url = get_permalink( $post->ID );
+ }
+
+ $message .= __( 'Contact Form URL:', 'jetpack' ) . " $url\n";
+
+ if ( is_user_logged_in() ) {
+ $message .= "\n";
+ $message .= sprintf(
+ __( 'Sent by a verified %s user.', 'jetpack' ),
+ isset( $GLOBALS['current_site']->site_name ) && $GLOBALS['current_site']->site_name ? $GLOBALS['current_site']->site_name : '"' . get_option( 'blogname' ) . '"'
+ );
+ } else {
+ $message .= __( 'Sent by an unverified visitor to your site.', 'jetpack' );
+ }
+
+ $message = apply_filters( 'contact_form_message', $message );
+ $message = Grunion_Contact_Form_Plugin::strip_tags( $message );
+
+ // keep a copy of the feedback as a custom post type
+ $feedback_mysql_time = current_time( 'mysql' );
+ $feedback_title = "{$comment_author} - {$feedback_mysql_time}";
+ $feedback_status = 'publish';
+ if ( $is_spam === TRUE )
+ $feedback_status = 'spam';
+
+ foreach ( (array) $akismet_values as $av_key => $av_value ) {
+ $akismet_values[$av_key] = Grunion_Contact_Form_Plugin::strip_tags( $av_value );
+ }
+
+ foreach ( (array) $all_values as $all_key => $all_value ) {
+ $all_values[$all_key] = Grunion_Contact_Form_Plugin::strip_tags( $all_value );
+ }
+
+ foreach ( (array) $extra_values as $ev_key => $ev_value ) {
+ $extra_values[$ev_key] = Grunion_Contact_Form_Plugin::strip_tags( $ev_value );
+ }
+
+ /* We need to make sure that the post author is always zero for contact
+ * form submissions. This prevents export/import from trying to create
+ * new users based on form submissions from people who were logged in
+ * at the time.
+ *
+ * Unfortunately wp_insert_post() tries very hard to make sure the post
+ * author gets the currently logged in user id. That is how we ended up
+ * with this work around. */
+ add_filter( 'wp_insert_post_data', array( $plugin, 'insert_feedback_filter' ), 10, 2 );
+
+ $post_id = wp_insert_post( array(
+ 'post_date' => addslashes( $feedback_mysql_time ),
+ 'post_type' => 'feedback',
+ 'post_status' => addslashes( $feedback_status ),
+ 'post_parent' => (int) $post->ID,
+ 'post_title' => addslashes( wp_kses( $feedback_title, array() ) ),
+ 'post_content' => addslashes( wp_kses( $comment_content . "\n<!--more-->\n" . "AUTHOR: {$comment_author}\nAUTHOR EMAIL: {$comment_author_email}\nAUTHOR URL: {$comment_author_url}\nSUBJECT: {$contact_form_subject}\nIP: {$comment_author_IP}\n" . print_r( $all_values, TRUE ), array() ) ), // so that search will pick up this data
+ 'post_name' => md5( $feedback_title ),
+ ) );
+
+ // once insert has finished we don't need this filter any more
+ remove_filter( 'wp_insert_post_data', array( $plugin, 'insert_feedback_filter' ), 10, 2 );
+
+ update_post_meta( $post_id, '_feedback_author', addslashes( $comment_author ) );
+ update_post_meta( $post_id, '_feedback_author_email', addslashes( $comment_author_email ) );
+ update_post_meta( $post_id, '_feedback_author_url', addslashes( $comment_author_url ) );
+ update_post_meta( $post_id, '_feedback_subject', addslashes( $contact_form_subject ) );
+ update_post_meta( $post_id, '_feedback_ip', addslashes( $comment_author_IP ) );
+ update_post_meta( $post_id, '_feedback_contact_form_url', addslashes( get_permalink( $post->ID ) ) );
+ update_post_meta( $post_id, '_feedback_all_fields', $this->addslashes_deep( $all_values ) );
+ update_post_meta( $post_id, '_feedback_extra_fields', $this->addslashes_deep( $extra_values ) );
+ update_post_meta( $post_id, '_feedback_akismet_values', $this->addslashes_deep( $akismet_values ) );
+ update_post_meta( $post_id, '_feedback_email', $this->addslashes_deep( array( 'to' => $to, 'subject' => $subject, 'message' => $message, 'headers' => $headers ) ) );
+
+ do_action( 'grunion_pre_message_sent', $post_id, $all_values, $extra_values );
+
+ // schedule deletes of old spam feedbacks
+ if ( !wp_next_scheduled( 'grunion_scheduled_delete' ) ) {
+ wp_schedule_event( time() + 250, 'daily', 'grunion_scheduled_delete' );
+ }
+
+ if ( $is_spam !== TRUE )
+ wp_mail( $to, "{$spam}{$subject}", $message, $headers );
+ elseif ( apply_filters( 'grunion_still_email_spam', FALSE ) == TRUE ) // don't send spam by default. Filterable.
+ wp_mail( $to, "{$spam}{$subject}", $message, $headers );
+
+ $redirect = wp_get_referer();
+ if ( !$redirect ) { // wp_get_referer() returns false if the referer is the same as the current page
+ $redirect = $_SERVER['REQUEST_URI'];
+ }
+
+ $redirect = add_query_arg( urlencode_deep( array(
+ 'contact-form-id' => $id,
+ 'contact-form-sent' => $post_id,
+ '_wpnonce' => wp_create_nonce( "contact-form-sent-{$post_id}" ), // wp_nonce_url HTMLencodes :(
+ ) ), $redirect );
+
+ $redirect = apply_filters( 'grunion_contact_form_redirect_url', $redirect, $id, $post_id );
+
+ wp_safe_redirect( $redirect );
+ exit;
+ }
+
+ function addslashes_deep( $value ) {
+ if ( is_array( $value ) ) {
+ return array_map( array( $this, 'addslashes_deep' ), $value );
+ } elseif ( is_object( $value ) ) {
+ $vars = get_object_vars( $value );
+ foreach ( $vars as $key => $data ) {
+ $value->{$key} = $this->addslashes_deep( $data );
+ }
+ return $value;
+ }
+
+ return addslashes( $value );
+ }
}
-add_action( 'init', 'contact_form_init' );
/**
- * Add a contact form button to the post composition screen
+ * Class for the contact-field shortcode.
+ * Parses shortcode to output the contact form field as HTML.
+ * Validates input.
*/
-add_action( 'media_buttons', 'grunion_media_button', 999 );
-function grunion_media_button( ) {
- global $post_ID, $temp_ID;
- $iframe_post_id = (int) (0 == $post_ID ? $temp_ID : $post_ID);
- $title = esc_attr( __( 'Add a custom form', 'jetpack' ) );
- $plugin_url = esc_url( GRUNION_PLUGIN_URL );
- $site_url = admin_url( "/admin-ajax.php?post_id=$iframe_post_id&amp;grunion=form-builder&amp;action=grunion_form_builder&amp;TB_iframe=true&amp;width=768" );
-
- echo '<a href="' . $site_url . '&id=add_form" class="thickbox" title="' . $title . '"><div class="grunion-menu-button" alt="' . $title . '"></div></a>';
-}
+class Grunion_Contact_Form_Field extends Crunion_Contact_Form_Shortcode {
+ var $shortcode_name = 'contact-field';
+ /**
+ * @var Grunion_Contact_Form parent form
+ */
+ var $form;
-if ( !empty( $_GET['grunion'] ) && $_GET['grunion'] == 'form-builder' ) {
- add_action( 'parse_request', 'parse_wp_request' );
- add_action( 'wp_ajax_grunion_form_builder', 'parse_wp_request' );
-}
+ /**
+ * @var string default or POSTed value
+ */
+ var $value;
-function parse_wp_request( $wp ) {
- display_form_view( );
- exit;
-}
+ /**
+ * @var bool Is the input invalid?
+ */
+ var $error = false;
-function display_form_view( ) {
- require_once GRUNION_PLUGIN_DIR . 'grunion-form-view.php';
-}
+ /**
+ * @param array $attributes An associative array of shortcode attributes. @see shortcode_atts()
+ * @param null|string $content Null for selfclosing shortcodes. The inner content otherwise.
+ * @param Grunion_Contact_Form $form The parent form
+ */
+ function __construct( $attributes, $content = null, $form = null ) {
+ $attributes = shortcode_atts( array(
+ 'label' => null,
+ 'type' => 'text',
+ 'required' => false,
+ 'options' => array(),
+ 'id' => null,
+ 'default' => null,
+ ), $attributes );
+
+ // special default for subject field
+ if ( 'subject' == $attributes['type'] && is_null( $attributes['default'] ) && !is_null( $form ) ) {
+ $attributes['default'] = $form->get_attribute( 'subject' );
+ }
-function menu_alter() {
- echo '
- <style>
- #menu-posts-feedback .wp-menu-image img { display: none; }
- #adminmenu .menu-icon-feedback:hover div.wp-menu-image,
- #adminmenu .menu-icon-feedback.wp-has-current-submenu div.wp-menu-image,
- #adminmenu .menu-icon-feedback.current div.wp-menu-image {
- background: url("' .GRUNION_PLUGIN_URL . 'images/grunion-menu-hover.png") no-repeat 6px 7px;
- }
- #adminmenu .menu-icon-feedback div.wp-menu-image {
- background: url("' . GRUNION_PLUGIN_URL . 'images/grunion-menu.png") no-repeat 6px 7px;
- }
- .grunion-menu-button {
- background: url("' . GRUNION_PLUGIN_URL . 'images/grunion-form.png") no-repeat;
- width: 13px;
- height: 12px;
- display: inline-block;
- }
- @media only screen and (-moz-min-device-pixel-ratio: 1.5), only screen and (-o-min-device-pixel-ratio: 3/2), only screen and (-webkit-min-device-pixel-ratio: 1.5), only screen and (min-device-pixel-ratio: 1.5) {
- #adminmenu .menu-icon-feedback:hover div.wp-menu-image,
- #adminmenu .menu-icon-feedback.wp-has-current-submenu div.wp-menu-image,
- #adminmenu .menu-icon-feedback.current div.wp-menu-image {
- background: url("' .GRUNION_PLUGIN_URL . 'images/grunion-menu-hover-2x.png") no-repeat 6px 7px;
- background-size: 15px 16px;
- }
- #adminmenu .menu-icon-feedback div.wp-menu-image {
- background: url("' . GRUNION_PLUGIN_URL . 'images/grunion-menu-2x.png") no-repeat 6px 7px;
- background-size: 15px 16px;
- }
- .grunion-menu-button {
- background-image: url("' . GRUNION_PLUGIN_URL . 'images/grunion-form-2x.png");
- background-size: 13px 12px;
- vertical-align: bottom;
- }
- }
- </style>';
-}
+ // allow required=1 or required=true
+ if ( '1' == $attributes['required'] || 'true' == strtolower( $attributes['required'] ) )
+ $attributes['required'] = true;
+ else
+ $attributes['required'] = false;
-add_action('admin_head', 'menu_alter');
+ // parse out comma-separated options list (for selects and radios)
+ if ( !empty( $attributes['options'] ) && is_string( $attributes['options'] ) ) {
+ $attributes['options'] = array_map( 'trim', explode( ',', $attributes['options'] ) );
+ }
-function grunion_insert_filter( $data, $postarr ) {
- global $do_grunion_insert;
+ if ( $form ) {
+ // make a unique field ID based on the label, with an incrementing number if needed to avoid clashes
+ $form_id = $form->get_attribute( 'id' );
+ $id = isset( $attributes['id'] ) ? $attributes['id'] : false;
+
+ $unescaped_label = $this->unesc_attr( $attributes['label'] );
+ $unescaped_label = str_replace( '%', '-', $unescaped_label ); // jQuery doesn't like % in IDs?
+ $unescaped_label = preg_replace( '/[^a-zA-Z0-9.-_:]/', '', $unescaped_label );
+
+ if ( empty( $id ) ) {
+ $id = sanitize_title_with_dashes( 'g' + $form_id . '-' . $unescaped_label );
+ $i = 0;
+ $max_tries = 24;
+ while ( isset( $form->fields[$id] ) ) {
+ $i++;
+ $id = sanitize_title_with_dashes( 'g' + $form_id . '-' . $unescaped_label . '-' . $i );
+
+ if ( $i > $max_tries ) {
+ break;
+ }
+ }
+ }
- if ( $do_grunion_insert === TRUE ) {
- if ( $data['post_type'] == 'feedback' ) {
- if ( $postarr['post_type'] == 'feedback' ) {
- $data['post_author'] = 0;
+ $attributes['id'] = $id;
+ }
+
+ parent::__construct( $attributes, $content );
+
+ // Store parent form
+ $this->form = $form;
+ }
+
+ /**
+ * This field's input is invalid. Flag as invalid and add an error to the parent form
+ *
+ * @param string $message The error message to display on the form.
+ */
+ function add_error( $message ) {
+ $this->is_error = true;
+
+ if ( !is_wp_error( $this->form->errors ) ) {
+ $this->form->errors = new WP_Error;
+ }
+
+ $this->form->errors->add( $this->get_attribute( 'id' ), $message );
+ }
+
+ /**
+ * Is the field input invalid?
+ *
+ * @see $error
+ *
+ * @return bool
+ */
+ function is_error() {
+ return $this->error;
+ }
+
+ /**
+ * Validates the form input
+ */
+ function validate() {
+ // If it's not required, there's nothing to validate
+ if ( !$this->get_attribute( 'required' ) ) {
+ return;
+ }
+
+ $field_id = $this->get_attribute( 'id' );
+ $field_type = $this->get_attribute( 'type' );
+ $field_label = $this->get_attribute( 'label' );
+
+ $field_value = isset( $_POST[$field_id] ) ? stripslashes( $_POST[$field_id] ) : '';
+
+ switch ( $field_type ) {
+ case 'email' :
+ // Make sure the email address is valid
+ if ( !is_email( $field_value ) ) {
+ $this->add_error( sprintf( __( '%s requires a valid email address', 'jetpack' ), $field_label ) );
+ }
+ break;
+ default :
+ // Just check for presence of any text
+ if ( !strlen( trim( $field_value ) ) ) {
+ $this->add_error( sprintf( __( '%s is required', 'jetpack' ), $field_label ) );
}
}
}
- return $data;
+ /**
+ * Outputs the HTML for this form field
+ *
+ * @return string HTML
+ */
+ function render() {
+ global $current_user, $user_identity;
+
+ $r = '';
+
+ $field_id = $this->get_attribute( 'id' );
+ $field_type = $this->get_attribute( 'type' );
+ $field_label = $this->get_attribute( 'label' );
+ $field_required = $this->get_attribute( 'required' );
+
+ if ( isset( $_POST[$field_id] ) ) {
+ $this->value = stripslashes( (string) $_POST[$field_id] );
+ } elseif ( is_user_logged_in() ) {
+ // Special defaults for logged-in users
+ switch ( $this->get_attribute( 'type' ) ) {
+ case 'email';
+ $this->value = $current_user->data->user_email;
+ break;
+ case 'name' :
+ $this->value = $user_identity;
+ break;
+ case 'url' :
+ $this->value = $current_user->data->user_url;
+ break;
+ default :
+ $this->value = $this->get_attribute( 'default' );
+ }
+ } else {
+ $this->value = $this->get_attribute( 'default' );
+ }
+
+ $field_value = Grunion_Contact_Form_Plugin::strip_tags( $this->value );
+ $field_label = Grunion_Contact_Form_Plugin::strip_tags( $field_label );
+
+ switch ( $field_type ) {
+ case 'email' :
+ $r .= "\n<div>\n";
+ $r .= "\t\t<label for='" . esc_attr( $field_id ) . "' class='grunion-field-label email" . ( $this->is_error() ? ' form-error' : '' ) . "'>" . esc_html( $field_label ) . ( $field_required ? '<span>' . __( "(required)", 'jetpack' ) . '</span>' : '' ) . "</label>\n";
+ $r .= "\t\t<input type='email' name='" . esc_attr( $field_id ) . "' id='" . esc_attr( $field_id ) . "' value='" . esc_attr( $field_value ) . "' class='email' />\n";
+ $r .= "\t</div>\n";
+ break;
+ case 'textarea' :
+ $r .= "\n<div>\n";
+ $r .= "\t\t<label for='contact-form-comment-" . esc_attr( $field_id ) . "' class='grunion-field-label textarea" . ( $this->is_error() ? ' form-error' : '' ) . "'>" . esc_html( $field_label ) . ( $field_required ? '<span>' . __( "(required)", 'jetpack' ) . '</span>' : '' ) . "</label>\n";
+ $r .= "\t\t<textarea name='" . esc_attr( $field_id ) . "' id='contact-form-comment-" . esc_attr( $field_id ) . "' rows='20'>" . esc_textarea( $field_value ) . "</textarea>\n";
+ $r .= "\t</div>\n";
+ break;
+ case 'radio' :
+ $r .= "\t<div><label class='grunion-field-label" . ( $this->is_error() ? ' form-error' : '' ) . "'>" . esc_html( $field_label ) . ( $field_required ? '<span>' . __( "(required)", 'jetpack' ) . '</span>' : '' ) . "</label>\n";
+ foreach ( $this->get_attribute( 'options' ) as $option ) {
+ $option = Grunion_Contact_Form_Plugin::strip_tags( $option );
+ $r .= "\t\t<label class='grunion-radio-label radio" . ( $this->is_error() ? ' form-error' : '' ) . "'>";
+ $r .= "<input type='radio' name='" . esc_attr( $field_id ) . "' value='" . esc_attr( $option ) . "' class='radio' " . checked( $option, $field_value, false ) . " /> ";
+ $r .= esc_html( $option ) . "</label>\n";
+ $r .= "\t\t<div class='clear-form'></div>\n";
+ }
+ $r .= "\t\t</div>\n";
+ break;
+ case 'checkbox' :
+ $r .= "\t<div>\n";
+ $r .= "\t\t<label class='grunion-field-label checkbox" . ( $this->is_error() ? ' form-error' : '' ) . "'>\n";
+ $r .= "\t\t<input type='checkbox' name='" . esc_attr( $field_id ) . "' value='" . esc_attr__( 'Yes', 'jetpack' ) . "' class='checkbox' " . checked( (bool) $field_value, true, false ) . " /> \n";
+ $r .= "\t\t" . esc_html( $field_label ) . ( $field_required ? '<span>'. __( "(required)", 'jetpack' ) . '</span>' : '' ) . "</label>\n";
+ $r .= "\t\t<div class='clear-form'></div>\n";
+ $r .= "\t</div>\n";
+ break;
+ case 'select' :
+ $r .= "\n<div>\n";
+ $r .= "\t\t<label for='" . esc_attr( $field_id ) . "' class='grunion-field-label select" . ( $this->is_error() ? ' form-error' : '' ) . "'>" . esc_html( $field_label ) . ( $field_required ? '<span>'. __( "(required)", 'jetpack' ) . '</span>' : '' ) . "</label>\n";
+ $r .= "\t<select name='" . esc_attr( $field_id ) . "' id='" . esc_attr( $field_id ) . "' class='select' />\n";
+ foreach ( $this->get_attribute( 'options' ) as $option ) {
+ $option = Grunion_Contact_Form_Plugin::strip_tags( $option );
+ $r .= "\t\t<option" . selected( $option, $field_value, false ) . ">" . esc_html( $option ) . "</option>\n";
+ }
+ $r .= "\t</select>\n";
+ $r .= "\t</div>\n";
+ break;
+ default : // text field
+ // note that any unknown types will produce a text input, so we can use arbitrary type names to handle
+ // input fields like name, email, url that require special validation or handling at POST
+ $r .= "\n<div>\n";
+ $r .= "\t\t<label for='" . esc_attr( $field_id ) . "' class='grunion-field-label " . esc_attr( $field_type ) . ( $this->is_error() ? ' form-error' : '' ) . "'>" . esc_html( $field_label ) . ( $field_required ? '<span>' . __( "(required)", 'jetpack' ) . '</span>' : '' ) . "</label>\n";
+ $r .= "\t\t<input type='text' name='" . esc_attr( $field_id ) . "' id='" . esc_attr( $field_id ) . "' value='" . esc_attr( $field_value ) . "' class='" . esc_attr( $field_type ) . "'/>\n";
+ $r .= "\t</div>\n";
+ }
+
+ return $r;
+ }
}
+add_action( 'init', array( 'Grunion_Contact_Form_Plugin', 'init' ) );
+
add_action( 'grunion_scheduled_delete', 'grunion_delete_old_spam' );
+
+/**
+ * Deletes old spam feedbacks to keep the posts table size under control
+ */
function grunion_delete_old_spam() {
global $wpdb;
diff --git a/plugins/jetpack/modules/contact-form/images/grunion-menu-2x.png b/plugins/jetpack/modules/contact-form/images/grunion-menu-2x.png
index 04fd8be5..7c197441 100644
--- a/plugins/jetpack/modules/contact-form/images/grunion-menu-2x.png
+++ b/plugins/jetpack/modules/contact-form/images/grunion-menu-2x.png
Binary files differ
diff --git a/plugins/jetpack/modules/contact-form/images/grunion-menu-hover-2x.png b/plugins/jetpack/modules/contact-form/images/grunion-menu-hover-2x.png
index f4656a98..21840d17 100644
--- a/plugins/jetpack/modules/contact-form/images/grunion-menu-hover-2x.png
+++ b/plugins/jetpack/modules/contact-form/images/grunion-menu-hover-2x.png
Binary files differ
diff --git a/plugins/jetpack/modules/contact-form/images/grunion-remove-field-2x.png b/plugins/jetpack/modules/contact-form/images/grunion-remove-field-2x.png
index 49553746..244c102c 100644
--- a/plugins/jetpack/modules/contact-form/images/grunion-remove-field-2x.png
+++ b/plugins/jetpack/modules/contact-form/images/grunion-remove-field-2x.png
Binary files differ
diff --git a/plugins/jetpack/modules/contact-form/images/grunion-remove-field-hover-2x.png b/plugins/jetpack/modules/contact-form/images/grunion-remove-field-hover-2x.png
index 561382fa..11fc10ed 100644
--- a/plugins/jetpack/modules/contact-form/images/grunion-remove-field-hover-2x.png
+++ b/plugins/jetpack/modules/contact-form/images/grunion-remove-field-hover-2x.png
Binary files differ
diff --git a/plugins/jetpack/modules/contact-form/js/grunion.js b/plugins/jetpack/modules/contact-form/js/grunion.js
index 4fe391bf..835f7f80 100644
--- a/plugins/jetpack/modules/contact-form/js/grunion.js
+++ b/plugins/jetpack/modules/contact-form/js/grunion.js
@@ -22,6 +22,16 @@ GrunionFB_i18n = jQuery.extend( {
GrunionFB_i18n.moveInstructions = GrunionFB_i18n.moveInstructions.replace( "\n", '<br />' );
+FB.span = jQuery( '<span>' );
+FB.esc_html = function( string ) {
+ return FB.span.text( string ).html();
+};
+
+FB.esc_attr = function( string ) {
+ string = FB.esc_html( string );
+ return string.replace( '"', '&quot;' ).replace( "'", '&#039;' );
+};
+
FB.ContactForm = function() {
var fbForm = { // Main object that generated shortcode via AJAX call
'action' : 'grunion_shortcode',
@@ -31,28 +41,28 @@ FB.ContactForm = function() {
'fields' : {}
};
var defaultFields = {
- 'name': {
+ 'name': {
'label' : GrunionFB_i18n.nameLabel,
'type' : 'name',
'required' : true,
'options' : [],
'order' : '1'
- },
- 'email': {
+ },
+ 'email': {
'label' : GrunionFB_i18n.emailLabel,
'type' : 'email',
'required' : true,
'options' : [],
'order' : '2'
- },
- 'url': {
+ },
+ 'url': {
'label' : GrunionFB_i18n.urlLabel,
'type' : 'url',
'required' : false,
'options' : [],
'order' : '3'
- },
- 'comment': {
+ },
+ 'comment': {
'label' : GrunionFB_i18n.commentLabel,
'type' : 'textarea',
'required' : true,
@@ -66,7 +76,7 @@ FB.ContactForm = function() {
var optionsCache = {};
var optionsCount = 0; // increment for options
var shortcode;
-
+
function addField () {
try {
grunionNewCount++;
@@ -103,7 +113,7 @@ FB.ContactForm = function() {
}
}
function addOption () {
- try {
+ try {
optionsCount++;
var thisId = jQuery('#fb-field-id').val();
var thisType = jQuery('#fb-new-type').val();
@@ -157,9 +167,9 @@ FB.ContactForm = function() {
for (i=0; i<optionsCache[id].options.length; i++) {
if (optionsCache[id].options[i] !== undefined) {
if (thisType === "radio") {
- thisOptions = thisOptions + '<div id="fb-radio-' + id + '-' + i + '"><input type="radio" id="fb-field' + id + '" name="radio-' + id + '" /><span>' + optionsCache[id].options[i] + '</span><div class="clear"></div></div>';
+ thisOptions = thisOptions + '<div id="fb-radio-' + id + '-' + i + '"><input type="radio" id="fb-field' + id + '" name="radio-' + id + '" /><span>' + FB.esc_html( optionsCache[id].options[i] ) + '</span><div class="clear"></div></div>';
} else {
- thisOptions = thisOptions + '<option id="fb-' + id + '-' + i + '" value="' + id + '-' + i + '">' + optionsCache[id].options[i] + '</option>';
+ thisOptions = thisOptions + '<option id="fb-' + id + '-' + i + '" value="' + id + '-' + i + '">' + FB.esc_html( optionsCache[id].options[i] ) + '</option>';
}
}
}
@@ -277,9 +287,9 @@ FB.ContactForm = function() {
for (i=0; i<thisOptions.length; i++) {
if (thisOptions[i] !== undefined) {
if (thisType === "radio") {
- jQuery('#fb-new-options').append('<div id="fb-option-box-' + i + '" class="fb-new-fields"><span optionid="' + i + '" class="fb-remove-option"></span><label></label><input type="text" id="fb-option' + i + '" optionid="' + i + '" value="' + fbForm.fields[id].options[i] + '" class="fb-options" /><div>');
+ jQuery('#fb-new-options').append('<div id="fb-option-box-' + i + '" class="fb-new-fields"><span optionid="' + i + '" class="fb-remove-option"></span><label></label><input type="text" id="fb-option' + i + '" optionid="' + i + '" value="' + FB.esc_attr( fbForm.fields[id].options[i] ) + '" class="fb-options" /><div>');
} else {
- jQuery('#fb-new-options').append('<div id="fb-option-box-' + i + '" class="fb-new-fields"><span optionid="' + i + '" class="fb-remove-option"></span><label></label><input type="text" id="fb-option' + i + '" optionid="' + i + '" value="' + fbForm.fields[id].options[i] + '" class="fb-options" /><div>');
+ jQuery('#fb-new-options').append('<div id="fb-option-box-' + i + '" class="fb-new-fields"><span optionid="' + i + '" class="fb-remove-option"></span><label></label><input type="text" id="fb-option' + i + '" optionid="' + i + '" value="' + FB.esc_attr( fbForm.fields[id].options[i] ) + '" class="fb-options" /><div>');
}
}
}
@@ -301,6 +311,8 @@ FB.ContactForm = function() {
fbForm.fields = defaultFields;
} else {
jQuery.each(data.fields, function(index, value) {
+ if ( 1 == value.required )
+ value.required = 'true';
fbForm.fields[index] = value;
});
fbForm.to = data.to;
@@ -373,10 +385,10 @@ FB.ContactForm = function() {
}
}
var regexp = new RegExp("\\[contact-form\\b.*?\\/?\\](?:[\\s\\S]+?\\[\\/contact-form\\])?");
-
+
// Remove new lines that cause BR tags to show up
response = response.replace(/\n/g,' ');
-
+
// Add new shortcode
if (currentCode.match(regexp)) {
if (isVisual) {
@@ -526,11 +538,11 @@ FB.ContactForm = function() {
var isLoaded = thisType;
var thisId = jQuery('#fb-field-id').val();
if (!thisType) { var thisType = jQuery('#fb-new-type').val(); }
- if (!thisLabelText) { var thisLabelText = jQuery('#fb-new-field' + thisId + ' label').html(); }
+ if (!thisLabelText) { var thisLabelText = jQuery('#fb-new-field' + thisId + ' .label-text').html(); }
var isRequired = (thisRequired) ? '<span class="label-required">' + GrunionFB_i18n.requiredLabel + '</span>' : '';
- var thisLabel = '<label fieldid="' + thisId + '" for="fb-field' + thisId + '"><span class="label-text">' + thisLabelText + '</span>' + isRequired + '</label>';
+ var thisLabel = '<label fieldid="' + thisId + '" for="fb-field' + thisId + '"><span class="label-text">' + FB.esc_html( thisLabelText ) + '</span>' + isRequired + '</label>';
var thisRadio = '<input type="radio" name="radio-' + thisId + '" id="fb-field' + thisId + ' "disabled="disabled" />';
- var thisRadioLabel = '<label fieldid="' + thisId + '" for="fb-field' + thisId + '" class="fb-radio-label"><span class="label-text">' + thisLabelText + '</span>' + isRequired + '</label>';
+ var thisRadioLabel = '<label fieldid="' + thisId + '" for="fb-field' + thisId + '" class="fb-radio-label"><span class="label-text">' + FB.esc_html( thisLabelText ) + '</span>' + isRequired + '</label>';
var thisRadioRemove = '<div class="fb-remove fb-remove-small" id="' + thisId + '"></div>';
var thisRemove = '<div class="fb-remove" id="' + thisId + '"></div>';
var thisCheckbox = '<input type="checkbox" id="fb-field' + thisId + '" "disabled="disabled" />';
@@ -545,11 +557,11 @@ FB.ContactForm = function() {
break;
case "email":
removeOptions();
- jQuery('#fb-new-field' + thisId + ' .fb-fields').html(thisRemove + thisLabel + thisText);
+ jQuery('#fb-new-field' + thisId + ' .fb-fields').html(thisRemove + thisLabel + thisText);
break;
case "name":
removeOptions();
- jQuery('#fb-new-field' + thisId + ' .fb-fields').html(thisRemove + thisLabel + thisText);
+ jQuery('#fb-new-field' + thisId + ' .fb-fields').html(thisRemove + thisLabel + thisText);
break;
case "radio":
jQuery('#fb-new-field' + thisId + ' .fb-fields').html(thisLabel + thisRadioRemove + '<div fieldid="' + thisId + '" id="fb-custom-radio' + thisId + '"></div>');
@@ -578,7 +590,7 @@ FB.ContactForm = function() {
break;
case "text":
removeOptions();
- jQuery('#fb-new-field' + thisId + ' .fb-fields').html(thisRemove + thisLabel + thisText);
+ jQuery('#fb-new-field' + thisId + ' .fb-fields').html(thisRemove + thisLabel + thisText);
break;
case "textarea":
removeOptions();
@@ -586,7 +598,7 @@ FB.ContactForm = function() {
break;
case "url":
removeOptions();
- jQuery('#fb-new-field' + thisId + ' .fb-fields').html(thisRemove + thisLabel + thisText);
+ jQuery('#fb-new-field' + thisId + ' .fb-fields').html(thisRemove + thisLabel + thisText);
break;
}
// update object
@@ -604,7 +616,7 @@ FB.ContactForm = function() {
var totalWidth = jQuery('body', window.parent.document).width();
var totalHeight = jQuery('body', window.parent.document).height();
var isIE6 = typeof document.body.style.maxHeight === "undefined";
-
+
jQuery('#TB_window, #TB_iframeContent', window.parent.document).css('width', '768px');
jQuery('#TB_window', window.parent.document).css({ left: (totalWidth-768)/2 + 'px', top: '23px', position: 'absolute', marginLeft: '0' });
if ( ! isIE6 ) { // take away IE6
@@ -632,7 +644,7 @@ FB.ContactForm = function() {
post_id: postId,
content: contentSource
};
-
+
jQuery.post(ajaxurl, data, function(response) {
// Setup fbForm
parseShortcode(jQuery.parseJSON(response));
diff --git a/plugins/jetpack/modules/custom-css.php b/plugins/jetpack/modules/custom-css.php
new file mode 100644
index 00000000..2b5e6a34
--- /dev/null
+++ b/plugins/jetpack/modules/custom-css.php
@@ -0,0 +1,26 @@
+<?php
+
+/**
+ * Module Name: Custom CSS
+ * Module Description: Customize the appearance of your site using CSS but without modifying your theme.
+ * Sort Order: 11
+ * First Introduced: 1.7
+ */
+
+function jetpack_load_custom_css() {
+ include dirname( __FILE__ ) . "/custom-css/custom-css.php";
+}
+
+add_action( 'jetpack_modules_loaded', 'custom_css_loaded' );
+
+function custom_css_loaded() {
+ Jetpack::enable_module_configurable( __FILE__ );
+ Jetpack::module_configuration_load( __FILE__, 'custom_css_configuration_load' );
+}
+
+function custom_css_configuration_load() {
+ wp_safe_redirect( admin_url( 'themes.php?page=editcss#settingsdiv' ) );
+ exit;
+}
+
+jetpack_load_custom_css(); \ No newline at end of file
diff --git a/plugins/jetpack/modules/custom-css/csstidy/class.csstidy.php b/plugins/jetpack/modules/custom-css/csstidy/class.csstidy.php
new file mode 100644
index 00000000..f458a46e
--- /dev/null
+++ b/plugins/jetpack/modules/custom-css/csstidy/class.csstidy.php
@@ -0,0 +1,1241 @@
+<?php
+
+/**
+ * CSSTidy - CSS Parser and Optimiser
+ *
+ * CSS Parser class
+ *
+ * Copyright 2005, 2006, 2007 Florian Schmitz
+ *
+ * This file is part of CSSTidy.
+ *
+ * CSSTidy is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * CSSTidy 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * @license http://opensource.org/licenses/lgpl-license.php GNU Lesser General Public License
+ * @package csstidy
+ * @author Florian Schmitz (floele at gmail dot com) 2005-2007
+ * @author Brett Zamir (brettz9 at yahoo dot com) 2007
+ * @author Nikolay Matsievsky (speed at webo dot name) 2009-2010
+ * @author Cedric Morin (cedric at yterium dot com) 2010
+ */
+/**
+ * Defines ctype functions if required
+ *
+ * @version 1.0
+ */
+require_once('class.csstidy_ctype.php');
+
+/**
+ * Various CSS data needed for correct optimisations etc.
+ *
+ * @version 1.3
+ */
+require('data.inc.php');
+
+/**
+ * Contains a class for printing CSS code
+ *
+ * @version 1.0
+ */
+require('class.csstidy_print.php');
+
+/**
+ * Contains a class for optimising CSS code
+ *
+ * @version 1.0
+ */
+require('class.csstidy_optimise.php');
+
+/**
+ * CSS Parser class
+ *
+
+ * This class represents a CSS parser which reads CSS code and saves it in an array.
+ * In opposite to most other CSS parsers, it does not use regular expressions and
+ * thus has full CSS2 support and a higher reliability.
+ * Additional to that it applies some optimisations and fixes to the CSS code.
+ * An online version should be available here: http://cdburnerxp.se/cssparse/css_optimiser.php
+ * @package csstidy
+ * @author Florian Schmitz (floele at gmail dot com) 2005-2006
+ * @version 1.3.1
+ */
+class csstidy {
+
+ /**
+ * Saves the parsed CSS. This array is empty if preserve_css is on.
+ * @var array
+ * @access public
+ */
+ var $css = array();
+ /**
+ * Saves the parsed CSS (raw)
+ * @var array
+ * @access private
+ */
+ var $tokens = array();
+ /**
+ * Printer class
+ * @see csstidy_print
+ * @var object
+ * @access public
+ */
+ var $print;
+ /**
+ * Optimiser class
+ * @see csstidy_optimise
+ * @var object
+ * @access private
+ */
+ var $optimise;
+ /**
+ * Saves the CSS charset (@charset)
+ * @var string
+ * @access private
+ */
+ var $charset = '';
+ /**
+ * Saves all @import URLs
+ * @var array
+ * @access private
+ */
+ var $import = array();
+ /**
+ * Saves the namespace
+ * @var string
+ * @access private
+ */
+ var $namespace = '';
+ /**
+ * Contains the version of csstidy
+ * @var string
+ * @access private
+ */
+ var $version = '1.3';
+ /**
+ * Stores the settings
+ * @var array
+ * @access private
+ */
+ var $settings = array();
+ /**
+ * Saves the parser-status.
+ *
+ * Possible values:
+ * - is = in selector
+ * - ip = in property
+ * - iv = in value
+ * - instr = in string (started at " or ' or ( )
+ * - ic = in comment (ignore everything)
+ * - at = in @-block
+ *
+ * @var string
+ * @access private
+ */
+ var $status = 'is';
+ /**
+ * Saves the current at rule (@media)
+ * @var string
+ * @access private
+ */
+ var $at = '';
+ /**
+ * Saves the current selector
+ * @var string
+ * @access private
+ */
+ var $selector = '';
+ /**
+ * Saves the current property
+ * @var string
+ * @access private
+ */
+ var $property = '';
+ /**
+ * Saves the position of , in selectors
+ * @var array
+ * @access private
+ */
+ var $sel_separate = array();
+ /**
+ * Saves the current value
+ * @var string
+ * @access private
+ */
+ var $value = '';
+ /**
+ * Saves the current sub-value
+ *
+ * Example for a subvalue:
+ * background:url(foo.png) red no-repeat;
+ * "url(foo.png)", "red", and "no-repeat" are subvalues,
+ * seperated by whitespace
+ * @var string
+ * @access private
+ */
+ var $sub_value = '';
+ /**
+ * Array which saves all subvalues for a property.
+ * @var array
+ * @see sub_value
+ * @access private
+ */
+ var $sub_value_arr = array();
+ /**
+ * Saves the stack of characters that opened the current strings
+ * @var array
+ * @access private
+ */
+ var $str_char = array();
+ var $cur_string = array();
+ /**
+ * Status from which the parser switched to ic or instr
+ * @var array
+ * @access private
+ */
+ var $from = array();
+ /**
+ /**
+ * =true if in invalid at-rule
+ * @var bool
+ * @access private
+ */
+ var $invalid_at = false;
+ /**
+ * =true if something has been added to the current selector
+ * @var bool
+ * @access private
+ */
+ var $added = false;
+ /**
+ * Array which saves the message log
+ * @var array
+ * @access private
+ */
+ var $log = array();
+ /**
+ * Saves the line number
+ * @var integer
+ * @access private
+ */
+ var $line = 1;
+ /**
+ * Marks if we need to leave quotes for a string
+ * @var array
+ * @access private
+ */
+ var $quoted_string = array();
+
+ /**
+ * List of tokens
+ * @var string
+ */
+ var $tokens_list = "";
+ /**
+ * Loads standard template and sets default settings
+ * @access private
+ * @version 1.3
+ */
+ function csstidy() {
+ $this->settings['remove_bslash'] = true;
+ $this->settings['compress_colors'] = true;
+ $this->settings['compress_font-weight'] = true;
+ $this->settings['lowercase_s'] = false;
+ /*
+ 1 common shorthands optimization
+ 2 + font property optimization
+ 3 + background property optimization
+ */
+ $this->settings['optimise_shorthands'] = 1;
+ $this->settings['remove_last_;'] = true;
+ /* rewrite all properties with low case, better for later gzip OK, safe*/
+ $this->settings['case_properties'] = 1;
+ /* sort properties in alpabetic order, better for later gzip
+ * but can cause trouble in case of overiding same propertie or using hack
+ */
+ $this->settings['sort_properties'] = false;
+ /*
+ 1, 3, 5, etc -- enable sorting selectors inside @media: a{}b{}c{}
+ 2, 5, 8, etc -- enable sorting selectors inside one CSS declaration: a,b,c{}
+ preserve order by default cause it can break functionnality
+ */
+ $this->settings['sort_selectors'] = 0;
+ /* is dangeroues to be used: CSS is broken sometimes */
+ $this->settings['merge_selectors'] = 0;
+ /* preserve or not browser hacks */
+ $this->settings['discard_invalid_selectors'] = false;
+ $this->settings['discard_invalid_properties'] = false;
+ $this->settings['css_level'] = 'CSS2.1';
+ $this->settings['preserve_css'] = false;
+ $this->settings['timestamp'] = false;
+ $this->settings['template'] = ''; // say that propertie exist
+ $this->set_cfg('template','default'); // call load_template
+ $this->optimise = new csstidy_optimise($this);
+
+ $this->tokens_list = & $GLOBALS['csstidy']['tokens'];
+ }
+
+ /**
+ * Get the value of a setting.
+ * @param string $setting
+ * @access public
+ * @return mixed
+ * @version 1.0
+ */
+ function get_cfg($setting) {
+ if (isset($this->settings[$setting])) {
+ return $this->settings[$setting];
+ }
+ return false;
+ }
+
+ /**
+ * Load a template
+ * @param string $template used by set_cfg to load a template via a configuration setting
+ * @access private
+ * @version 1.4
+ */
+ function _load_template($template) {
+ switch ($template) {
+ case 'default':
+ $this->load_template('default');
+ break;
+
+ case 'highest':
+ $this->load_template('highest_compression');
+ break;
+
+ case 'high':
+ $this->load_template('high_compression');
+ break;
+
+ case 'low':
+ $this->load_template('low_compression');
+ break;
+
+ default:
+ $this->load_template($template);
+ break;
+ }
+ }
+
+ /**
+ * Set the value of a setting.
+ * @param string $setting
+ * @param mixed $value
+ * @access public
+ * @return bool
+ * @version 1.0
+ */
+ function set_cfg($setting, $value=null) {
+ if (is_array($setting) && $value === null) {
+ foreach ($setting as $setprop => $setval) {
+ $this->settings[$setprop] = $setval;
+ }
+ if (array_key_exists('template', $setting)) {
+ $this->_load_template($this->settings['template']);
+ }
+ return true;
+ } else if (isset($this->settings[$setting]) && $value !== '') {
+ $this->settings[$setting] = $value;
+ if ($setting === 'template') {
+ $this->_load_template($this->settings['template']);
+ }
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Adds a token to $this->tokens
+ * @param mixed $type
+ * @param string $data
+ * @param bool $do add a token even if preserve_css is off
+ * @access private
+ * @version 1.0
+ */
+ function _add_token($type, $data, $do = false) {
+ if ($this->get_cfg('preserve_css') || $do) {
+ $this->tokens[] = array($type, ($type == COMMENT) ? $data : trim($data));
+ }
+ }
+
+ /**
+ * Add a message to the message log
+ * @param string $message
+ * @param string $type
+ * @param integer $line
+ * @access private
+ * @version 1.0
+ */
+ function log($message, $type, $line = -1) {
+ if ($line === -1) {
+ $line = $this->line;
+ }
+ $line = intval($line);
+ $add = array('m' => $message, 't' => $type);
+ if (!isset($this->log[$line]) || !in_array($add, $this->log[$line])) {
+ $this->log[$line][] = $add;
+ }
+ }
+
+ /**
+ * Parse unicode notations and find a replacement character
+ * @param string $string
+ * @param integer $i
+ * @access private
+ * @return string
+ * @version 1.2
+ */
+ function _unicode(&$string, &$i) {
+ ++$i;
+ $add = '';
+ $replaced = false;
+
+ while ($i < strlen($string) && (ctype_xdigit($string{$i}) || ctype_space($string{$i})) && strlen($add) < 6) {
+ $add .= $string{$i};
+
+ if (ctype_space($string{$i})) {
+ break;
+ }
+ $i++;
+ }
+
+ if (hexdec($add) > 47 && hexdec($add) < 58 || hexdec($add) > 64 && hexdec($add) < 91 || hexdec($add) > 96 && hexdec($add) < 123) {
+ $this->log('Replaced unicode notation: Changed \\' . $add . ' to ' . chr(hexdec($add)), 'Information');
+ $add = chr(hexdec($add));
+ $replaced = true;
+ } else {
+ $add = trim('\\' . $add);
+ }
+
+ if (@ctype_xdigit($string{$i + 1}) && ctype_space($string{$i})
+ && !$replaced || !ctype_space($string{$i})) {
+ $i--;
+ }
+
+ if ($add !== '\\' || !$this->get_cfg('remove_bslash') || strpos($this->tokens_list, $string{$i + 1}) !== false) {
+ return $add;
+ }
+
+ if ($add === '\\') {
+ $this->log('Removed unnecessary backslash', 'Information');
+ }
+ return '';
+ }
+
+ /**
+ * Write formatted output to a file
+ * @param string $filename
+ * @param string $doctype when printing formatted, is a shorthand for the document type
+ * @param bool $externalcss when printing formatted, indicates whether styles to be attached internally or as an external stylesheet
+ * @param string $title when printing formatted, is the title to be added in the head of the document
+ * @param string $lang when printing formatted, gives a two-letter language code to be added to the output
+ * @access public
+ * @version 1.4
+ */
+ function write_page($filename, $doctype='xhtml1.1', $externalcss=true, $title='', $lang='en') {
+ $this->write($filename, true);
+ }
+
+ /**
+ * Write plain output to a file
+ * @param string $filename
+ * @param bool $formatted whether to print formatted or not
+ * @param string $doctype when printing formatted, is a shorthand for the document type
+ * @param bool $externalcss when printing formatted, indicates whether styles to be attached internally or as an external stylesheet
+ * @param string $title when printing formatted, is the title to be added in the head of the document
+ * @param string $lang when printing formatted, gives a two-letter language code to be added to the output
+ * @param bool $pre_code whether to add pre and code tags around the code (for light HTML formatted templates)
+ * @access public
+ * @version 1.4
+ */
+ function write($filename, $formatted=false, $doctype='xhtml1.1', $externalcss=true, $title='', $lang='en', $pre_code=true) {
+ $filename .= ( $formatted) ? '.xhtml' : '.css';
+
+ if (!is_dir('temp')) {
+ $madedir = mkdir('temp');
+ if (!$madedir) {
+ print 'Could not make directory "temp" in ' . dirname(__FILE__);
+ exit;
+ }
+ }
+ $handle = fopen('temp/' . $filename, 'w');
+ if ($handle) {
+ if (!$formatted) {
+ fwrite($handle, $this->print->plain());
+ } else {
+ fwrite($handle, $this->print->formatted_page($doctype, $externalcss, $title, $lang, $pre_code));
+ }
+ }
+ fclose($handle);
+ }
+
+ /**
+ * Loads a new template
+ * @param string $content either filename (if $from_file == true), content of a template file, "high_compression", "highest_compression", "low_compression", or "default"
+ * @param bool $from_file uses $content as filename if true
+ * @access public
+ * @version 1.1
+ * @see http://csstidy.sourceforge.net/templates.php
+ */
+ function load_template($content, $from_file=true) {
+ $predefined_templates = & $GLOBALS['csstidy']['predefined_templates'];
+ if ($content === 'high_compression' || $content === 'default' || $content === 'highest_compression' || $content === 'low_compression') {
+ $this->template = $predefined_templates[$content];
+ return;
+ }
+
+
+ if ($from_file) {
+ $content = strip_tags(file_get_contents($content), '<span>');
+ }
+ $content = str_replace("\r\n", "\n", $content); // Unify newlines (because the output also only uses \n)
+ $template = explode('|', $content);
+
+ for ($i = 0; $i < count($template); $i++) {
+ $this->template[$i] = $template[$i];
+ }
+ }
+
+ /**
+ * Starts parsing from URL
+ * @param string $url
+ * @access public
+ * @version 1.0
+ */
+ function parse_from_url($url) {
+ return $this->parse(@file_get_contents($url));
+ }
+
+ /**
+ * Checks if there is a token at the current position
+ * @param string $string
+ * @param integer $i
+ * @access public
+ * @version 1.11
+ */
+ function is_token(&$string, $i) {
+ return (strpos($this->tokens_list, $string{$i}) !== false && !csstidy::escaped($string, $i));
+ }
+
+ /**
+ * Parses CSS in $string. The code is saved as array in $this->css
+ * @param string $string the CSS code
+ * @access public
+ * @return bool
+ * @version 1.1
+ */
+ function parse($string) {
+ // Temporarily set locale to en_US in order to handle floats properly
+ $old = @setlocale(LC_ALL, 0);
+ @setlocale(LC_ALL, 'C');
+
+ // PHP bug? Settings need to be refreshed in PHP4
+ $this->print = new csstidy_print($this);
+ //$this->optimise = new csstidy_optimise($this);
+
+ $all_properties = & $GLOBALS['csstidy']['all_properties'];
+ $at_rules = & $GLOBALS['csstidy']['at_rules'];
+ $quoted_string_properties = & $GLOBALS['csstidy']['quoted_string_properties'];
+
+ $this->css = array();
+ $this->print->input_css = $string;
+ $string = str_replace("\r\n", "\n", $string) . ' ';
+ $cur_comment = '';
+
+ for ($i = 0, $size = strlen($string); $i < $size; $i++) {
+ if ($string{$i} === "\n" || $string{$i} === "\r") {
+ ++$this->line;
+ }
+
+ switch ($this->status) {
+ /* Case in at-block */
+ case 'at':
+ if (csstidy::is_token($string, $i)) {
+ if ($string{$i} === '/' && @$string{$i + 1} === '*') {
+ $this->status = 'ic';
+ ++$i;
+ $this->from[] = 'at';
+ } elseif ($string{$i} === '{') {
+ $this->status = 'is';
+ $this->at = $this->css_new_media_section($this->at);
+ $this->_add_token(AT_START, $this->at);
+ } elseif ($string{$i} === ',') {
+ $this->at = trim($this->at) . ',';
+ } elseif ($string{$i} === '\\') {
+ $this->at .= $this->_unicode($string, $i);
+ }
+ // fix for complicated media, i.e @media screen and (-webkit-min-device-pixel-ratio:1.5)
+ // '/' is included for ratios in Opera: (-o-min-device-pixel-ratio: 3/2)
+ elseif (in_array($string{$i}, array('(', ')', ':', '.', '/'))) {
+ $this->at .= $string{$i};
+ }
+ } else {
+ $lastpos = strlen($this->at) - 1;
+ if (!( (ctype_space($this->at{$lastpos}) || csstidy::is_token($this->at, $lastpos) && $this->at{$lastpos} === ',') && ctype_space($string{$i}))) {
+ $this->at .= $string{$i};
+ }
+ }
+ break;
+
+ /* Case in-selector */
+ case 'is':
+ if (csstidy::is_token($string, $i)) {
+ if ($string{$i} === '/' && @$string{$i + 1} === '*' && trim($this->selector) == '') {
+ $this->status = 'ic';
+ ++$i;
+ $this->from[] = 'is';
+ } elseif ($string{$i} === '@' && trim($this->selector) == '') {
+ // Check for at-rule
+ $this->invalid_at = true;
+ foreach ($at_rules as $name => $type) {
+ if (!strcasecmp(substr($string, $i + 1, strlen($name)), $name)) {
+ ($type === 'at') ? $this->at = '@' . $name : $this->selector = '@' . $name;
+ $this->status = $type;
+ $i += strlen($name);
+ $this->invalid_at = false;
+ }
+ }
+
+ if ($this->invalid_at) {
+ $this->selector = '@';
+ $invalid_at_name = '';
+ for ($j = $i + 1; $j < $size; ++$j) {
+ if (!ctype_alpha($string{$j})) {
+ break;
+ }
+ $invalid_at_name .= $string{$j};
+ }
+ $this->log('Invalid @-rule: ' . $invalid_at_name . ' (removed)', 'Warning');
+ }
+ } elseif (($string{$i} === '"' || $string{$i} === "'")) {
+ $this->cur_string[] = $string{$i};
+ $this->status = 'instr';
+ $this->str_char[] = $string{$i};
+ $this->from[] = 'is';
+ /* fixing CSS3 attribute selectors, i.e. a[href$=".mp3" */
+ $this->quoted_string[] = ($string{$i - 1} == '=' );
+ } elseif ($this->invalid_at && $string{$i} === ';') {
+ $this->invalid_at = false;
+ $this->status = 'is';
+ } elseif ($string{$i} === '{') {
+ $this->status = 'ip';
+ if($this->at == '') {
+ $this->at = $this->css_new_media_section(DEFAULT_AT);
+ }
+ $this->selector = $this->css_new_selector($this->at,$this->selector);
+ $this->_add_token(SEL_START, $this->selector);
+ $this->added = false;
+ } elseif ($string{$i} === '}') {
+ $this->_add_token(AT_END, $this->at);
+ $this->at = '';
+ $this->selector = '';
+ $this->sel_separate = array();
+ } elseif ($string{$i} === ',') {
+ $this->selector = trim($this->selector) . ',';
+ $this->sel_separate[] = strlen($this->selector);
+ } elseif ($string{$i} === '\\') {
+ $this->selector .= $this->_unicode($string, $i);
+ } elseif ($string{$i} === '*' && @in_array($string{$i + 1}, array('.', '#', '[', ':'))) {
+ // remove unnecessary universal selector, FS#147
+ } else {
+ $this->selector .= $string{$i};
+ }
+ } else {
+ $lastpos = strlen($this->selector) - 1;
+ if ($lastpos == -1 || !( (ctype_space($this->selector{$lastpos}) || csstidy::is_token($this->selector, $lastpos) && $this->selector{$lastpos} === ',') && ctype_space($string{$i}))) {
+ $this->selector .= $string{$i};
+ }
+ else if (ctype_space($string{$i}) && $this->get_cfg('preserve_css') && !$this->get_cfg('merge_selectors')) {
+ $this->selector .= $string{$i};
+ }
+ }
+ break;
+
+ /* Case in-property */
+ case 'ip':
+ if (csstidy::is_token($string, $i)) {
+ if (($string{$i} === ':' || $string{$i} === '=') && $this->property != '') {
+ $this->status = 'iv';
+ if (!$this->get_cfg('discard_invalid_properties') || csstidy::property_is_valid($this->property)) {
+ $this->property = $this->css_new_property($this->at,$this->selector,$this->property);
+ $this->_add_token(PROPERTY, $this->property);
+ }
+ } elseif ($string{$i} === '/' && @$string{$i + 1} === '*' && $this->property == '') {
+ $this->status = 'ic';
+ ++$i;
+ $this->from[] = 'ip';
+ } elseif ($string{$i} === '}') {
+ $this->explode_selectors();
+ $this->status = 'is';
+ $this->invalid_at = false;
+ $this->_add_token(SEL_END, $this->selector);
+ $this->selector = '';
+ $this->property = '';
+ } elseif ($string{$i} === ';') {
+ $this->property = '';
+ } elseif ($string{$i} === '\\') {
+ $this->property .= $this->_unicode($string, $i);
+ }
+ // else this is dumb IE a hack, keep it
+ elseif ($this->property=='' AND !ctype_space($string{$i})) {
+ $this->property .= $string{$i};
+ }
+ }
+ elseif (!ctype_space($string{$i})) {
+ $this->property .= $string{$i};
+ }
+ break;
+
+ /* Case in-value */
+ case 'iv':
+ $pn = (($string{$i} === "\n" || $string{$i} === "\r") && $this->property_is_next($string, $i + 1) || $i == strlen($string) - 1);
+ if ((csstidy::is_token($string, $i) || $pn) && (!($string{$i} == ',' && !ctype_space($string{$i+1})))) {
+ if ($string{$i} === '/' && @$string{$i + 1} === '*') {
+ $this->status = 'ic';
+ ++$i;
+ $this->from[] = 'iv';
+ } elseif (($string{$i} === '"' || $string{$i} === "'" || $string{$i} === '(')) {
+ $this->cur_string[] = $string{$i};
+ $this->str_char[] = ($string{$i} === '(') ? ')' : $string{$i};
+ $this->status = 'instr';
+ $this->from[] = 'iv';
+ $this->quoted_string[] = in_array(strtolower($this->property), $quoted_string_properties);
+ } elseif ($string{$i} === ',') {
+ $this->sub_value = trim($this->sub_value) . ',';
+ } elseif ($string{$i} === '\\') {
+ $this->sub_value .= $this->_unicode($string, $i);
+ } elseif ($string{$i} === ';' || $pn) {
+ if ($this->selector{0} === '@' && isset($at_rules[substr($this->selector, 1)]) && $at_rules[substr($this->selector, 1)] === 'iv') {
+ $this->status = 'is';
+
+ switch ($this->selector) {
+ case '@charset':
+ /* Add quotes to charset */
+ $this->sub_value_arr[] = '"' . trim($this->sub_value) . '"';
+ $this->charset = $this->sub_value_arr[0];
+ break;
+ case '@namespace':
+ /* Add quotes to namespace */
+ $this->sub_value_arr[] = '"' . trim($this->sub_value) . '"';
+ $this->namespace = implode(' ', $this->sub_value_arr);
+ break;
+ case '@import':
+ $this->sub_value = trim($this->sub_value);
+
+ if (empty($this->sub_value_arr)) {
+ // Quote URLs in imports only if they're not already inside url() and not already quoted.
+ if (substr($this->sub_value, 0, 4) != 'url(') {
+ if (!($this->sub_value{0} == substr($this->sub_value, -1) && in_array($this->sub_value{0}, array("'", '"')))) {
+ $this->sub_value = '"' . $this->sub_value . '"';
+ }
+ }
+ }
+
+ $this->sub_value_arr[] = $this->sub_value;
+ $this->import[] = implode(' ', $this->sub_value_arr);
+ break;
+ }
+
+ $this->sub_value_arr = array();
+ $this->sub_value = '';
+ $this->selector = '';
+ $this->sel_separate = array();
+ } else {
+ $this->status = 'ip';
+ }
+ } elseif ($string{$i} !== '}') {
+ $this->sub_value .= $string{$i};
+ }
+ if (($string{$i} === '}' || $string{$i} === ';' || $pn) && !empty($this->selector)) {
+ if ($this->at == '') {
+ $this->at = $this->css_new_media_section(DEFAULT_AT);
+ }
+
+ // case settings
+ if ($this->get_cfg('lowercase_s')) {
+ $this->selector = strtolower($this->selector);
+ }
+ $this->property = strtolower($this->property);
+
+ $this->optimise->subvalue();
+ if ($this->sub_value != '') {
+ if (substr($this->sub_value, 0, 6) == 'format') {
+ $format_strings = csstidy::parse_string_list(substr($this->sub_value, 7, -1));
+ if (!$format_strings) {
+ $this->sub_value = "";
+ }
+ else {
+ $this->sub_value = "format(";
+
+ foreach ($format_strings as $format_string) {
+ $this->sub_value .= '"' . str_replace('"', '\\"', $format_string) . '",';
+ }
+
+ $this->sub_value = substr($this->sub_value, 0, -1) . ")";
+ }
+ }
+ if ($this->sub_value != '') {
+ $this->sub_value_arr[] = $this->sub_value;
+ }
+ $this->sub_value = '';
+ }
+
+ $this->value = array_shift($this->sub_value_arr);
+ while(count($this->sub_value_arr)){
+ //$this->value .= (substr($this->value,-1,1)==','?'':' ').array_shift($this->sub_value_arr);
+ $this->value .= ' '.array_shift($this->sub_value_arr);
+ }
+
+ $this->optimise->value();
+
+ $valid = csstidy::property_is_valid($this->property);
+ if ((!$this->invalid_at || $this->get_cfg('preserve_css')) && (!$this->get_cfg('discard_invalid_properties') || $valid)) {
+ $this->css_add_property($this->at, $this->selector, $this->property, $this->value);
+ $this->_add_token(VALUE, $this->value);
+ $this->optimise->shorthands();
+ }
+ if (!$valid) {
+ if ($this->get_cfg('discard_invalid_properties')) {
+ $this->log('Removed invalid property: ' . $this->property, 'Warning');
+ } else {
+ $this->log('Invalid property in ' . strtoupper($this->get_cfg('css_level')) . ': ' . $this->property, 'Warning');
+ }
+ }
+
+ $this->property = '';
+ $this->sub_value_arr = array();
+ $this->value = '';
+ }
+ if ($string{$i} === '}') {
+ $this->explode_selectors();
+ $this->_add_token(SEL_END, $this->selector);
+ $this->status = 'is';
+ $this->invalid_at = false;
+ $this->selector = '';
+ }
+ } elseif (!$pn) {
+ $this->sub_value .= $string{$i};
+
+ if (ctype_space($string{$i}) || $string{$i} == ',') {
+ $this->optimise->subvalue();
+ if ($this->sub_value != '') {
+ $this->sub_value_arr[] = $this->sub_value;
+ $this->sub_value = '';
+ }
+ }
+ }
+ break;
+
+ /* Case in string */
+ case 'instr':
+ $_str_char = $this->str_char[count($this->str_char)-1];
+ $_cur_string = $this->cur_string[count($this->cur_string)-1];
+ $temp_add = $string{$i};
+
+ // Add another string to the stack. Strings can't be nested inside of quotes, only parentheses, but
+ // parentheticals can be nested more than once.
+ if ($_str_char === ")" && ($string{$i} === "(" || $string{$i} === '"' || $string{$i} === '\'') && !csstidy::escaped($string, $i)) {
+ $this->cur_string[] = $string{$i};
+ $this->str_char[] = $string{$i} == "(" ? ")" : $string{$i};
+ $this->from[] = 'instr';
+ $this->quoted_string[] = !($string{$i} === "(");
+ continue;
+ }
+
+ if ($_str_char !== ")" && ($string{$i} === "\n" || $string{$i} === "\r") && !($string{$i - 1} === '\\' && !csstidy::escaped($string, $i - 1))) {
+ $temp_add = "\\A";
+ $this->log('Fixed incorrect newline in string', 'Warning');
+ }
+
+ $_cur_string .= $temp_add;
+
+ if ($string{$i} === $_str_char && !csstidy::escaped($string, $i)) {
+ $_quoted_string = array_pop($this->quoted_string);
+
+ $this->status = array_pop($this->from);
+
+ if (!preg_match('|[' . implode('', $GLOBALS['csstidy']['whitespace']) . ']|uis', $_cur_string) && $this->property !== 'content') {
+ if (!$_quoted_string) {
+ if ($_str_char !== ')') {
+ // Convert properties like
+ // font-family: 'Arial';
+ // to
+ // font-family: Arial;
+ // or
+ // url("abc")
+ // to
+ // url(abc)
+ $_cur_string = substr($_cur_string, 1, -1);
+ }
+ } else {
+ $_quoted_string = false;
+ }
+ }
+
+ array_pop($this->cur_string);
+ array_pop($this->str_char);
+
+ if ($_str_char === ")") {
+ $_cur_string = "(" . trim(substr($_cur_string, 1, -1)) . ")";
+ }
+
+ if ($this->status === 'iv') {
+ if (!$_quoted_string){
+ if (strpos($_cur_string,',')!==false)
+ // we can on only remove space next to ','
+ $_cur_string = implode(',',array_map('trim',explode(',',$_cur_string)));
+ // and multiple spaces (too expensive)
+ if (strpos($_cur_string,' ')!==false)
+ $_cur_string = preg_replace(",\s+,"," ",$_cur_string);
+ }
+ $this->sub_value .= $_cur_string;
+ } elseif ($this->status === 'is') {
+ $this->selector .= $_cur_string;
+ } elseif ($this->status === 'instr') {
+ $this->cur_string[count($this->cur_string)-1] .= $_cur_string;
+ }
+ }
+ else {
+ $this->cur_string[count($this->cur_string)-1] = $_cur_string;
+ }
+ break;
+
+ /* Case in-comment */
+ case 'ic':
+ if ($string{$i} === '*' && $string{$i + 1} === '/') {
+ $this->status = array_pop($this->from);
+ $i++;
+ $this->_add_token(COMMENT, $cur_comment);
+ $cur_comment = '';
+ } else {
+ $cur_comment .= $string{$i};
+ }
+ break;
+ }
+ }
+
+ $this->optimise->postparse();
+
+ $this->print->_reset();
+
+ @setlocale(LC_ALL, $old); // Set locale back to original setting
+
+ return!(empty($this->css) && empty($this->import) && empty($this->charset) && empty($this->tokens) && empty($this->namespace));
+ }
+
+ /**
+ * Explodes selectors
+ * @access private
+ * @version 1.0
+ */
+ function explode_selectors() {
+ // Explode multiple selectors
+ if ($this->get_cfg('merge_selectors') === 1) {
+ $new_sels = array();
+ $lastpos = 0;
+ $this->sel_separate[] = strlen($this->selector);
+ foreach ($this->sel_separate as $num => $pos) {
+ if ($num == count($this->sel_separate) - 1) {
+ $pos += 1;
+ }
+
+ $new_sels[] = substr($this->selector, $lastpos, $pos - $lastpos - 1);
+ $lastpos = $pos;
+ }
+
+ if (count($new_sels) > 1) {
+ foreach ($new_sels as $selector) {
+ if (isset($this->css[$this->at][$this->selector])) {
+ $this->merge_css_blocks($this->at, $selector, $this->css[$this->at][$this->selector]);
+ }
+ }
+ unset($this->css[$this->at][$this->selector]);
+ }
+ }
+ $this->sel_separate = array();
+ }
+
+ /**
+ * Checks if a character is escaped (and returns true if it is)
+ * @param string $string
+ * @param integer $pos
+ * @access public
+ * @return bool
+ * @version 1.02
+ */
+ static function escaped(&$string, $pos) {
+ return!(@($string{$pos - 1} !== '\\') || csstidy::escaped($string, $pos - 1));
+ }
+
+ /**
+ * Adds a property with value to the existing CSS code
+ * @param string $media
+ * @param string $selector
+ * @param string $property
+ * @param string $new_val
+ * @access private
+ * @version 1.2
+ */
+ function css_add_property($media, $selector, $property, $new_val) {
+ if ($this->get_cfg('preserve_css') || trim($new_val) == '') {
+ return;
+ }
+
+ $this->added = true;
+ if (isset($this->css[$media][$selector][$property])) {
+ if ((csstidy::is_important($this->css[$media][$selector][$property]) && csstidy::is_important($new_val)) || !csstidy::is_important($this->css[$media][$selector][$property])) {
+ $this->css[$media][$selector][$property] = trim($new_val);
+ }
+ } else {
+ $this->css[$media][$selector][$property] = trim($new_val);
+ }
+ }
+
+ /**
+ * Start a new media section.
+ * Check if the media is not already known,
+ * else rename it with extra spaces
+ * to avoid merging
+ *
+ * @param string $media
+ * @return string
+ */
+ function css_new_media_section($media){
+ if($this->get_cfg('preserve_css')) {
+ return $media;
+ }
+
+ // if the last @media is the same as this
+ // keep it
+ if (!$this->css OR !is_array($this->css) OR empty($this->css)){
+ return $media;
+ }
+ end($this->css);
+ list($at,) = each($this->css);
+ if ($at == $media){
+ return $media;
+ }
+ while (isset($this->css[$media]))
+ if (is_numeric($media))
+ $media++;
+ else
+ $media .= " ";
+ return $media;
+ }
+
+ /**
+ * Start a new selector.
+ * If already referenced in this media section,
+ * rename it with extra space to avoid merging
+ * except if merging is required,
+ * or last selector is the same (merge siblings)
+ *
+ * never merge @font-face
+ *
+ * @param string $media
+ * @param string $selector
+ * @return string
+ */
+ function css_new_selector($media,$selector){
+ if($this->get_cfg('preserve_css')) {
+ return $selector;
+ }
+ $selector = trim($selector);
+ if (strncmp($selector,"@font-face",10)!=0){
+ if ($this->settings['merge_selectors'] != false)
+ return $selector;
+
+ if (!$this->css OR !isset($this->css[$media]) OR !$this->css[$media])
+ return $selector;
+
+ // if last is the same, keep it
+ end($this->css[$media]);
+ list($sel,) = each($this->css[$media]);
+ if ($sel == $selector){
+ return $selector;
+ }
+ }
+
+ while (isset($this->css[$media][$selector]))
+ $selector .= " ";
+ return $selector;
+ }
+
+ /**
+ * Start a new propertie.
+ * If already references in this selector,
+ * rename it with extra space to avoid override
+ *
+ * @param string $media
+ * @param string $selector
+ * @param string $property
+ * @return string
+ */
+ function css_new_property($media, $selector, $property){
+ if($this->get_cfg('preserve_css')) {
+ return $property;
+ }
+ if (!$this->css OR !isset($this->css[$media][$selector]) OR !$this->css[$media][$selector])
+ return $property;
+
+ while (isset($this->css[$media][$selector][$property]))
+ $property .= " ";
+
+ return $property;
+ }
+
+ /**
+ * Adds CSS to an existing media/selector
+ * @param string $media
+ * @param string $selector
+ * @param array $css_add
+ * @access private
+ * @version 1.1
+ */
+ function merge_css_blocks($media, $selector, $css_add) {
+ foreach ($css_add as $property => $value) {
+ $this->css_add_property($media, $selector, $property, $value, false);
+ }
+ }
+
+ /**
+ * Checks if $value is !important.
+ * @param string $value
+ * @return bool
+ * @access public
+ * @version 1.0
+ */
+ static function is_important(&$value) {
+ return (!strcasecmp(substr(str_replace($GLOBALS['csstidy']['whitespace'], '', $value), -10, 10), '!important'));
+ }
+
+ /**
+ * Returns a value without !important
+ * @param string $value
+ * @return string
+ * @access public
+ * @version 1.0
+ */
+ static function gvw_important($value) {
+ if (csstidy::is_important($value)) {
+ $value = trim($value);
+ $value = substr($value, 0, -9);
+ $value = trim($value);
+ $value = substr($value, 0, -1);
+ $value = trim($value);
+ return $value;
+ }
+ return $value;
+ }
+
+ /**
+ * Checks if the next word in a string from pos is a CSS property
+ * @param string $istring
+ * @param integer $pos
+ * @return bool
+ * @access private
+ * @version 1.2
+ */
+ function property_is_next($istring, $pos) {
+ $all_properties = & $GLOBALS['csstidy']['all_properties'];
+ $istring = substr($istring, $pos, strlen($istring) - $pos);
+ $pos = strpos($istring, ':');
+ if ($pos === false) {
+ return false;
+ }
+ $istring = strtolower(trim(substr($istring, 0, $pos)));
+ if (isset($all_properties[$istring])) {
+ $this->log('Added semicolon to the end of declaration', 'Warning');
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Checks if a property is valid
+ * @param string $property
+ * @return bool;
+ * @access public
+ * @version 1.0
+ */
+ function property_is_valid($property) {
+ if (in_array(trim($property), $GLOBALS['csstidy']['multiple_properties'])) $property = trim($property);
+ $all_properties = & $GLOBALS['csstidy']['all_properties'];
+ return (isset($all_properties[$property]) && strpos($all_properties[$property], strtoupper($this->get_cfg('css_level'))) !== false );
+ }
+
+ /**
+ * Accepts a list of strings (e.g., the argument to format() in a @font-face src property)
+ * and returns a list of the strings. Converts things like:
+ *
+ * format(abc) => format("abc")
+ * format(abc def) => format("abc","def")
+ * format(abc "def") => format("abc","def")
+ * format(abc, def, ghi) => format("abc","def","ghi")
+ * format("abc",'def') => format("abc","def")
+ * format("abc, def, ghi") => format("abc, def, ghi")
+ *
+ * @param string
+ * @return array
+ */
+
+ function parse_string_list($value) {
+ $value = trim($value);
+
+ // Case: empty
+ if (!$value) return array();
+
+ $strings = array();
+
+ $in_str = false;
+ $current_string = "";
+
+ for ($i = 0, $_len = strlen($value); $i < $_len; $i++) {
+ if (($value{$i} == "," || $value{$i} === " ") && $in_str === true) {
+ $in_str = false;
+ $strings[] = $current_string;
+ $current_string = "";
+ }
+ else if ($value{$i} == '"' || $value{$i} == "'"){
+ if ($in_str === $value{$i}) {
+ $strings[] = $current_string;
+ $in_str = false;
+ $current_string = "";
+ continue;
+ }
+ else if (!$in_str) {
+ $in_str = $value{$i};
+ }
+ }
+ else {
+ if ($in_str){
+ $current_string .= $value{$i};
+ }
+ else {
+ if (!preg_match("/[\s,]/", $value{$i})) {
+ $in_str = true;
+ $current_string = $value{$i};
+ }
+ }
+ }
+ }
+
+ if ($current_string) {
+ $strings[] = $current_string;
+ }
+
+ return $strings;
+ }
+}
diff --git a/plugins/jetpack/modules/custom-css/csstidy/class.csstidy_ctype.php b/plugins/jetpack/modules/custom-css/csstidy/class.csstidy_ctype.php
new file mode 100644
index 00000000..bc5accc5
--- /dev/null
+++ b/plugins/jetpack/modules/custom-css/csstidy/class.csstidy_ctype.php
@@ -0,0 +1,46 @@
+<?php
+
+/**
+ * CSSTidy - CSS Parser and Optimiser
+ *
+ * CSS ctype functions
+ * Defines some functions that can be not defined.
+ *
+ * This file is part of CSSTidy.
+ *
+ * CSSTidy 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.
+ *
+ * CSSTidy 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 CSSTidy; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * @license http://opensource.org/licenses/gpl-license.php GNU Public License
+ * @package csstidy
+ * @author Nikolay Matsievsky (speed at webo dot name) 2009-2010
+ * @version 1.0
+ */
+/* ctype_space Check for whitespace character(s) */
+if (!function_exists('ctype_space')) {
+
+ function ctype_space($text) {
+ return!preg_match("/[^\s\r\n\t\f]/", $text);
+ }
+
+}
+/* ctype_alpha Check for alphabetic character(s) */
+if (!function_exists('ctype_alpha')) {
+
+ function ctype_alpha($text) {
+ return preg_match("/[a-zA-Z]/", $text);
+ }
+
+}
+?> \ No newline at end of file
diff --git a/plugins/jetpack/modules/custom-css/csstidy/class.csstidy_optimise.php b/plugins/jetpack/modules/custom-css/csstidy/class.csstidy_optimise.php
new file mode 100644
index 00000000..364573a3
--- /dev/null
+++ b/plugins/jetpack/modules/custom-css/csstidy/class.csstidy_optimise.php
@@ -0,0 +1,936 @@
+<?php
+
+/**
+ * CSSTidy - CSS Parser and Optimiser
+ *
+ * CSS Optimising Class
+ * This class optimises CSS data generated by csstidy.
+ *
+ * Copyright 2005, 2006, 2007 Florian Schmitz
+ *
+ * This file is part of CSSTidy.
+ *
+ * CSSTidy is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * CSSTidy 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * @license http://opensource.org/licenses/lgpl-license.php GNU Lesser General Public License
+ * @package csstidy
+ * @author Florian Schmitz (floele at gmail dot com) 2005-2007
+ * @author Brett Zamir (brettz9 at yahoo dot com) 2007
+ * @author Nikolay Matsievsky (speed at webo dot name) 2009-2010
+ */
+
+/**
+ * CSS Optimising Class
+ *
+ * This class optimises CSS data generated by csstidy.
+ *
+ * @package csstidy
+ * @author Florian Schmitz (floele at gmail dot com) 2005-2006
+ * @version 1.0
+ */
+class csstidy_optimise {
+
+ /**
+ * Constructor
+ * @param array $css contains the class csstidy
+ * @access private
+ * @version 1.0
+ */
+ function csstidy_optimise(&$css) {
+ $this->parser = & $css;
+ $this->css = & $css->css;
+ $this->sub_value = & $css->sub_value;
+ $this->at = & $css->at;
+ $this->selector = & $css->selector;
+ $this->property = & $css->property;
+ $this->value = & $css->value;
+ }
+
+ /**
+ * Optimises $css after parsing
+ * @access public
+ * @version 1.0
+ */
+ function postparse() {
+ if ($this->parser->get_cfg('preserve_css')) {
+ return;
+ }
+
+ if ($this->parser->get_cfg('merge_selectors') === 2) {
+ foreach ($this->css as $medium => $value) {
+ $this->merge_selectors($this->css[$medium]);
+ }
+ }
+
+ if ($this->parser->get_cfg('discard_invalid_selectors')) {
+ foreach ($this->css as $medium => $value) {
+ $this->discard_invalid_selectors($this->css[$medium]);
+ }
+ }
+
+ if ($this->parser->get_cfg('optimise_shorthands') > 0) {
+ foreach ($this->css as $medium => $value) {
+ foreach ($value as $selector => $value1) {
+ $this->css[$medium][$selector] = csstidy_optimise::merge_4value_shorthands($this->css[$medium][$selector]);
+
+ if ($this->parser->get_cfg('optimise_shorthands') < 2) {
+ continue;
+ }
+
+ $this->css[$medium][$selector] = csstidy_optimise::merge_font($this->css[$medium][$selector]);
+
+ if ($this->parser->get_cfg('optimise_shorthands') < 3) {
+ continue;
+ }
+
+ $this->css[$medium][$selector] = csstidy_optimise::merge_bg($this->css[$medium][$selector]);
+ if (empty($this->css[$medium][$selector])) {
+ unset($this->css[$medium][$selector]);
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ * Optimises values
+ * @access public
+ * @version 1.0
+ */
+ function value() {
+ $shorthands = & $GLOBALS['csstidy']['shorthands'];
+
+ // optimise shorthand properties
+ if (isset($shorthands[$this->property])) {
+ $temp = csstidy_optimise::shorthand($this->value); // FIXME - move
+ if ($temp != $this->value) {
+ $this->parser->log('Optimised shorthand notation (' . $this->property . '): Changed "' . $this->value . '" to "' . $temp . '"', 'Information');
+ }
+ $this->value = $temp;
+ }
+
+ // Remove whitespace at ! important
+ if ($this->value != $this->compress_important($this->value)) {
+ $this->parser->log('Optimised !important', 'Information');
+ }
+ }
+
+ /**
+ * Optimises shorthands
+ * @access public
+ * @version 1.0
+ */
+ function shorthands() {
+ $shorthands = & $GLOBALS['csstidy']['shorthands'];
+
+ if (!$this->parser->get_cfg('optimise_shorthands') || $this->parser->get_cfg('preserve_css')) {
+ return;
+ }
+
+ if ($this->property === 'font' && $this->parser->get_cfg('optimise_shorthands') > 1) {
+ $this->css[$this->at][$this->selector]['font']='';
+ $this->parser->merge_css_blocks($this->at, $this->selector, csstidy_optimise::dissolve_short_font($this->value));
+ }
+ if ($this->property === 'background' && $this->parser->get_cfg('optimise_shorthands') > 2) {
+ $this->css[$this->at][$this->selector]['background']='';
+ $this->parser->merge_css_blocks($this->at, $this->selector, csstidy_optimise::dissolve_short_bg($this->value));
+ }
+ if (isset($shorthands[$this->property])) {
+ $this->parser->merge_css_blocks($this->at, $this->selector, csstidy_optimise::dissolve_4value_shorthands($this->property, $this->value));
+ if (is_array($shorthands[$this->property])) {
+ $this->css[$this->at][$this->selector][$this->property] = '';
+ }
+ }
+ }
+
+ /**
+ * Optimises a sub-value
+ * @access public
+ * @version 1.0
+ */
+ function subvalue() {
+ $replace_colors = & $GLOBALS['csstidy']['replace_colors'];
+
+ $this->sub_value = trim($this->sub_value);
+ if ($this->sub_value == '') { // caution : '0'
+ return;
+ }
+
+ $important = '';
+ if (csstidy::is_important($this->sub_value)) {
+ $important = '!important';
+ }
+ $this->sub_value = csstidy::gvw_important($this->sub_value);
+
+ // Compress font-weight
+ if ($this->property === 'font-weight' && $this->parser->get_cfg('compress_font-weight')) {
+ if ($this->sub_value === 'bold') {
+ $this->sub_value = '700';
+ $this->parser->log('Optimised font-weight: Changed "bold" to "700"', 'Information');
+ } else if ($this->sub_value === 'normal') {
+ $this->sub_value = '400';
+ $this->parser->log('Optimised font-weight: Changed "normal" to "400"', 'Information');
+ }
+ }
+
+ $temp = $this->compress_numbers($this->sub_value);
+ if (strcasecmp($temp, $this->sub_value) !== 0) {
+ if (strlen($temp) > strlen($this->sub_value)) {
+ $this->parser->log('Fixed invalid number: Changed "' . $this->sub_value . '" to "' . $temp . '"', 'Warning');
+ } else {
+ $this->parser->log('Optimised number: Changed "' . $this->sub_value . '" to "' . $temp . '"', 'Information');
+ }
+ $this->sub_value = $temp;
+ }
+ if ($this->parser->get_cfg('compress_colors')) {
+ $temp = $this->cut_color($this->sub_value);
+ if ($temp !== $this->sub_value) {
+ if (isset($replace_colors[$this->sub_value])) {
+ $this->parser->log('Fixed invalid color name: Changed "' . $this->sub_value . '" to "' . $temp . '"', 'Warning');
+ } else {
+ $this->parser->log('Optimised color: Changed "' . $this->sub_value . '" to "' . $temp . '"', 'Information');
+ }
+ $this->sub_value = $temp;
+ }
+ }
+ $this->sub_value .= $important;
+ }
+
+ /**
+ * Compresses shorthand values. Example: margin:1px 1px 1px 1px -> margin:1px
+ * @param string $value
+ * @access public
+ * @return string
+ * @version 1.0
+ */
+ function shorthand($value) {
+ $important = '';
+ if (csstidy::is_important($value)) {
+ $values = csstidy::gvw_important($value);
+ $important = '!important';
+ }
+ else
+ $values = $value;
+
+ $values = explode(' ', $values);
+ switch (count($values)) {
+ case 4:
+ if ($values[0] == $values[1] && $values[0] == $values[2] && $values[0] == $values[3]) {
+ return $values[0] . $important;
+ } elseif ($values[1] == $values[3] && $values[0] == $values[2]) {
+ return $values[0] . ' ' . $values[1] . $important;
+ } elseif ($values[1] == $values[3]) {
+ return $values[0] . ' ' . $values[1] . ' ' . $values[2] . $important;
+ }
+ break;
+
+ case 3:
+ if ($values[0] == $values[1] && $values[0] == $values[2]) {
+ return $values[0] . $important;
+ } elseif ($values[0] == $values[2]) {
+ return $values[0] . ' ' . $values[1] . $important;
+ }
+ break;
+
+ case 2:
+ if ($values[0] == $values[1]) {
+ return $values[0] . $important;
+ }
+ break;
+ }
+
+ return $value;
+ }
+
+ /**
+ * Removes unnecessary whitespace in ! important
+ * @param string $string
+ * @return string
+ * @access public
+ * @version 1.1
+ */
+ function compress_important(&$string) {
+ if (csstidy::is_important($string)) {
+ $string = csstidy::gvw_important($string) . '!important';
+ }
+ return $string;
+ }
+
+ /**
+ * Color compression function. Converts all rgb() values to #-values and uses the short-form if possible. Also replaces 4 color names by #-values.
+ * @param string $color
+ * @return string
+ * @version 1.1
+ */
+ function cut_color($color) {
+ $replace_colors = & $GLOBALS['csstidy']['replace_colors'];
+
+ // rgb(0,0,0) -> #000000 (or #000 in this case later)
+ if (strtolower(substr($color, 0, 4)) === 'rgb(') {
+ $color_tmp = substr($color, 4, strlen($color) - 5);
+ $color_tmp = explode(',', $color_tmp);
+ for ($i = 0; $i < count($color_tmp); $i++) {
+ $color_tmp[$i] = trim($color_tmp[$i]);
+ if (substr($color_tmp[$i], -1) === '%') {
+ $color_tmp[$i] = round((255 * $color_tmp[$i]) / 100);
+ }
+ if ($color_tmp[$i] > 255)
+ $color_tmp[$i] = 255;
+ }
+ $color = '#';
+ for ($i = 0; $i < 3; $i++) {
+ if ($color_tmp[$i] < 16) {
+ $color .= '0' . dechex($color_tmp[$i]);
+ } else {
+ $color .= dechex($color_tmp[$i]);
+ }
+ }
+ }
+
+ // Fix bad color names
+ if (isset($replace_colors[strtolower($color)])) {
+ $color = $replace_colors[strtolower($color)];
+ }
+
+ // #aabbcc -> #abc
+ if (strlen($color) == 7) {
+ $color_temp = strtolower($color);
+ if ($color_temp{0} === '#' && $color_temp{1} == $color_temp{2} && $color_temp{3} == $color_temp{4} && $color_temp{5} == $color_temp{6}) {
+ $color = '#' . $color{1} . $color{3} . $color{5};
+ }
+ }
+
+ switch (strtolower($color)) {
+ /* color name -> hex code */
+ case 'black': return '#000';
+ case 'fuchsia': return '#f0f';
+ case 'white': return '#fff';
+ case 'yellow': return '#ff0';
+
+ /* hex code -> color name */
+ case '#800000': return 'maroon';
+ case '#ffa500': return 'orange';
+ case '#808000': return 'olive';
+ case '#800080': return 'purple';
+ case '#008000': return 'green';
+ case '#000080': return 'navy';
+ case '#008080': return 'teal';
+ case '#c0c0c0': return 'silver';
+ case '#808080': return 'gray';
+ case '#f00': return 'red';
+ }
+
+ return $color;
+ }
+
+ /**
+ * Compresses numbers (ie. 1.0 becomes 1 or 1.100 becomes 1.1 )
+ * @param string $subvalue
+ * @return string
+ * @version 1.2
+ */
+ function compress_numbers($subvalue) {
+ $unit_values = & $GLOBALS['csstidy']['unit_values'];
+ $color_values = & $GLOBALS['csstidy']['color_values'];
+
+ // for font:1em/1em sans-serif...;
+ if ($this->property === 'font') {
+ $temp = explode('/', $subvalue);
+ } else {
+ $temp = array($subvalue);
+ }
+ for ($l = 0; $l < count($temp); $l++) {
+ // if we are not dealing with a number at this point, do not optimise anything
+ $number = $this->AnalyseCssNumber($temp[$l]);
+ if ($number === false) {
+ return $subvalue;
+ }
+
+ // Fix bad colors
+ if (in_array($this->property, $color_values)) {
+ if (strlen($temp[$l]) == 3 || strlen($temp[$l]) == 6) {
+ $temp[$l] = '#' . $temp[$l];
+ }
+ else {
+ $temp[$l] = "0";
+ }
+ continue;
+ }
+
+ if (abs($number[0]) > 0) {
+ if ($number[1] == '' && in_array($this->property, $unit_values, true)) {
+ $number[1] = 'px';
+ }
+ } else {
+ $number[1] = '';
+ }
+
+ $temp[$l] = $number[0] . $number[1];
+ }
+
+ return ((count($temp) > 1) ? $temp[0] . '/' . $temp[1] : $temp[0]);
+ }
+
+ /**
+ * Checks if a given string is a CSS valid number. If it is,
+ * an array containing the value and unit is returned
+ * @param string $string
+ * @return array ('unit' if unit is found or '' if no unit exists, number value) or false if no number
+ */
+ function AnalyseCssNumber($string) {
+ // most simple checks first
+ if (strlen($string) == 0 || ctype_alpha($string{0})) {
+ return false;
+ }
+
+ $units = & $GLOBALS['csstidy']['units'];
+ $return = array(0, '');
+
+ $return[0] = floatval($string);
+ if (abs($return[0]) > 0 && abs($return[0]) < 1) {
+ if ($return[0] < 0) {
+ $return[0] = '-' . ltrim(substr($return[0], 1), '0');
+ } else {
+ $return[0] = ltrim($return[0], '0');
+ }
+ }
+
+ // Look for unit and split from value if exists
+ foreach ($units as $unit) {
+ $expectUnitAt = strlen($string) - strlen($unit);
+ if (!($unitInString = stristr($string, $unit))) { // mb_strpos() fails with "false"
+ continue;
+ }
+ $actualPosition = strpos($string, $unitInString);
+ if ($expectUnitAt === $actualPosition) {
+ $return[1] = $unit;
+ $string = substr($string, 0, - strlen($unit));
+ break;
+ }
+ }
+ if (!is_numeric($string)) {
+ return false;
+ }
+ return $return;
+ }
+
+ /**
+ * Merges selectors with same properties. Example: a{color:red} b{color:red} -> a,b{color:red}
+ * Very basic and has at least one bug. Hopefully there is a replacement soon.
+ * @param array $array
+ * @return array
+ * @access public
+ * @version 1.2
+ */
+ function merge_selectors(&$array) {
+ $css = $array;
+ foreach ($css as $key => $value) {
+ if (!isset($css[$key])) {
+ continue;
+ }
+ $newsel = '';
+
+ // Check if properties also exist in another selector
+ $keys = array();
+ // PHP bug (?) without $css = $array; here
+ foreach ($css as $selector => $vali) {
+ if ($selector == $key) {
+ continue;
+ }
+
+ if ($css[$key] === $vali) {
+ $keys[] = $selector;
+ }
+ }
+
+ if (!empty($keys)) {
+ $newsel = $key;
+ unset($css[$key]);
+ foreach ($keys as $selector) {
+ unset($css[$selector]);
+ $newsel .= ',' . $selector;
+ }
+ $css[$newsel] = $value;
+ }
+ }
+ $array = $css;
+ }
+
+ /**
+ * Removes invalid selectors and their corresponding rule-sets as
+ * defined by 4.1.7 in REC-CSS2. This is a very rudimentary check
+ * and should be replaced by a full-blown parsing algorithm or
+ * regular expression
+ * @version 1.4
+ */
+ function discard_invalid_selectors(&$array) {
+ $invalid = array('+' => true, '~' => true, ',' => true, '>' => true);
+ foreach ($array as $selector => $decls) {
+ $ok = true;
+ $selectors = array_map('trim', explode(',', $selector));
+ foreach ($selectors as $s) {
+ $simple_selectors = preg_split('/\s*[+>~\s]\s*/', $s);
+ foreach ($simple_selectors as $ss) {
+ if ($ss === '')
+ $ok = false;
+ // could also check $ss for internal structure,
+ // but that probably would be too slow
+ }
+ }
+ if (!$ok)
+ unset($array[$selector]);
+ }
+ }
+
+ /**
+ * Dissolves properties like padding:10px 10px 10px to padding-top:10px;padding-bottom:10px;...
+ * @param string $property
+ * @param string $value
+ * @return array
+ * @version 1.0
+ * @see merge_4value_shorthands()
+ */
+ function dissolve_4value_shorthands($property, $value) {
+ $shorthands = & $GLOBALS['csstidy']['shorthands'];
+ if (!is_array($shorthands[$property])) {
+ $return[$property] = $value;
+ return $return;
+ }
+
+ $important = '';
+ if (csstidy::is_important($value)) {
+ $value = csstidy::gvw_important($value);
+ $important = '!important';
+ }
+ $values = explode(' ', $value);
+
+
+ $return = array();
+ if (count($values) == 4) {
+ for ($i = 0; $i < 4; $i++) {
+ $return[$shorthands[$property][$i]] = $values[$i] . $important;
+ }
+ } elseif (count($values) == 3) {
+ $return[$shorthands[$property][0]] = $values[0] . $important;
+ $return[$shorthands[$property][1]] = $values[1] . $important;
+ $return[$shorthands[$property][3]] = $values[1] . $important;
+ $return[$shorthands[$property][2]] = $values[2] . $important;
+ } elseif (count($values) == 2) {
+ for ($i = 0; $i < 4; $i++) {
+ $return[$shorthands[$property][$i]] = (($i % 2 != 0)) ? $values[1] . $important : $values[0] . $important;
+ }
+ } else {
+ for ($i = 0; $i < 4; $i++) {
+ $return[$shorthands[$property][$i]] = $values[0] . $important;
+ }
+ }
+
+ return $return;
+ }
+
+ /**
+ * Explodes a string as explode() does, however, not if $sep is escaped or within a string.
+ * @param string $sep seperator
+ * @param string $string
+ * @return array
+ * @version 1.0
+ */
+ function explode_ws($sep, $string) {
+ $status = 'st';
+ $to = '';
+
+ $output = array();
+ $num = 0;
+ for ($i = 0, $len = strlen($string); $i < $len; $i++) {
+ switch ($status) {
+ case 'st':
+ if ($string{$i} == $sep && !csstidy::escaped($string, $i)) {
+ ++$num;
+ } elseif ($string{$i} === '"' || $string{$i} === '\'' || $string{$i} === '(' && !csstidy::escaped($string, $i)) {
+ $status = 'str';
+ $to = ($string{$i} === '(') ? ')' : $string{$i};
+ (isset($output[$num])) ? $output[$num] .= $string{$i} : $output[$num] = $string{$i};
+ } else {
+ (isset($output[$num])) ? $output[$num] .= $string{$i} : $output[$num] = $string{$i};
+ }
+ break;
+
+ case 'str':
+ if ($string{$i} == $to && !csstidy::escaped($string, $i)) {
+ $status = 'st';
+ }
+ (isset($output[$num])) ? $output[$num] .= $string{$i} : $output[$num] = $string{$i};
+ break;
+ }
+ }
+
+ if (isset($output[0])) {
+ return $output;
+ } else {
+ return array($output);
+ }
+ }
+
+ /**
+ * Merges Shorthand properties again, the opposite of dissolve_4value_shorthands()
+ * @param array $array
+ * @return array
+ * @version 1.2
+ * @see dissolve_4value_shorthands()
+ */
+ function merge_4value_shorthands($array) {
+ $return = $array;
+ $shorthands = & $GLOBALS['csstidy']['shorthands'];
+
+ foreach ($shorthands as $key => $value) {
+ if (isset($array[$value[0]]) && isset($array[$value[1]])
+ && isset($array[$value[2]]) && isset($array[$value[3]]) && $value !== 0) {
+ $return[$key] = '';
+
+ $important = '';
+ for ($i = 0; $i < 4; $i++) {
+ $val = $array[$value[$i]];
+ if (csstidy::is_important($val)) {
+ $important = '!important';
+ $return[$key] .= csstidy::gvw_important($val) . ' ';
+ } else {
+ $return[$key] .= $val . ' ';
+ }
+ unset($return[$value[$i]]);
+ }
+ $return[$key] = csstidy_optimise::shorthand(trim($return[$key] . $important));
+ }
+ }
+ return $return;
+ }
+
+ /**
+ * Dissolve background property
+ * @param string $str_value
+ * @return array
+ * @version 1.0
+ * @see merge_bg()
+ * @todo full CSS 3 compliance
+ */
+ function dissolve_short_bg($str_value) {
+ // don't try to explose background gradient !
+ if (stripos($str_value, "gradient(")!==FALSE)
+ return array('background'=>$str_value);
+
+ $background_prop_default = & $GLOBALS['csstidy']['background_prop_default'];
+ $repeat = array('repeat', 'repeat-x', 'repeat-y', 'no-repeat', 'space');
+ $attachment = array('scroll', 'fixed', 'local');
+ $clip = array('border', 'padding');
+ $origin = array('border', 'padding', 'content');
+ $pos = array('top', 'center', 'bottom', 'left', 'right');
+ $important = '';
+ $return = array('background-image' => null, 'background-size' => null, 'background-repeat' => null, 'background-position' => null, 'background-attachment' => null, 'background-clip' => null, 'background-origin' => null, 'background-color' => null);
+
+ if (csstidy::is_important($str_value)) {
+ $important = ' !important';
+ $str_value = csstidy::gvw_important($str_value);
+ }
+
+ $str_value = csstidy_optimise::explode_ws(',', $str_value);
+ for ($i = 0; $i < count($str_value); $i++) {
+ $have['clip'] = false;
+ $have['pos'] = false;
+ $have['color'] = false;
+ $have['bg'] = false;
+
+ if (is_array($str_value[$i])) {
+ $str_value[$i] = $str_value[$i][0];
+ }
+ $str_value[$i] = csstidy_optimise::explode_ws(' ', trim($str_value[$i]));
+
+ for ($j = 0; $j < count($str_value[$i]); $j++) {
+ if ($have['bg'] === false && (substr($str_value[$i][$j], 0, 4) === 'url(' || $str_value[$i][$j] === 'none')) {
+ $return['background-image'] .= $str_value[$i][$j] . ',';
+ $have['bg'] = true;
+ } elseif (in_array($str_value[$i][$j], $repeat, true)) {
+ $return['background-repeat'] .= $str_value[$i][$j] . ',';
+ } elseif (in_array($str_value[$i][$j], $attachment, true)) {
+ $return['background-attachment'] .= $str_value[$i][$j] . ',';
+ } elseif (in_array($str_value[$i][$j], $clip, true) && !$have['clip']) {
+ $return['background-clip'] .= $str_value[$i][$j] . ',';
+ $have['clip'] = true;
+ } elseif (in_array($str_value[$i][$j], $origin, true)) {
+ $return['background-origin'] .= $str_value[$i][$j] . ',';
+ } elseif ($str_value[$i][$j]{0} === '(') {
+ $return['background-size'] .= substr($str_value[$i][$j], 1, -1) . ',';
+ } elseif (in_array($str_value[$i][$j], $pos, true) || is_numeric($str_value[$i][$j]{0}) || $str_value[$i][$j]{0} === null || $str_value[$i][$j]{0} === '-' || $str_value[$i][$j]{0} === '.') {
+ $return['background-position'] .= $str_value[$i][$j];
+ if (!$have['pos'])
+ $return['background-position'] .= ' '; else
+ $return['background-position'].= ',';
+ $have['pos'] = true;
+ }
+ elseif (!$have['color']) {
+ $return['background-color'] .= $str_value[$i][$j] . ',';
+ $have['color'] = true;
+ }
+ }
+ }
+
+ foreach ($background_prop_default as $bg_prop => $default_value) {
+ if ($return[$bg_prop] !== null) {
+ $return[$bg_prop] = substr($return[$bg_prop], 0, -1) . $important;
+ }
+ else
+ $return[$bg_prop] = $default_value . $important;
+ }
+ return $return;
+ }
+
+ /**
+ * Merges all background properties
+ * @param array $input_css
+ * @return array
+ * @version 1.0
+ * @see dissolve_short_bg()
+ * @todo full CSS 3 compliance
+ */
+ function merge_bg($input_css) {
+ $background_prop_default = & $GLOBALS['csstidy']['background_prop_default'];
+ // Max number of background images. CSS3 not yet fully implemented
+ $number_of_values = @max(count(csstidy_optimise::explode_ws(',', $input_css['background-image'])), count(csstidy_optimise::explode_ws(',', $input_css['background-color'])), 1);
+ // Array with background images to check if BG image exists
+ $bg_img_array = @csstidy_optimise::explode_ws(',', csstidy::gvw_important($input_css['background-image']));
+ $new_bg_value = '';
+ $important = '';
+
+ // if background properties is here and not empty, don't try anything
+ if (isset($input_css['background']) AND $input_css['background'])
+ return $input_css;
+
+ for ($i = 0; $i < $number_of_values; $i++) {
+ foreach ($background_prop_default as $bg_property => $default_value) {
+ // Skip if property does not exist
+ if (!isset($input_css[$bg_property])) {
+ continue;
+ }
+
+ $cur_value = $input_css[$bg_property];
+ // skip all optimisation if gradient() somewhere
+ if (stripos($cur_value, "gradient(")!==FALSE)
+ return $input_css;
+
+ // Skip some properties if there is no background image
+ if ((!isset($bg_img_array[$i]) || $bg_img_array[$i] === 'none')
+ && ($bg_property === 'background-size' || $bg_property === 'background-position'
+ || $bg_property === 'background-attachment' || $bg_property === 'background-repeat')) {
+ continue;
+ }
+
+ // Remove !important
+ if (csstidy::is_important($cur_value)) {
+ $important = ' !important';
+ $cur_value = csstidy::gvw_important($cur_value);
+ }
+
+ // Do not add default values
+ if ($cur_value === $default_value) {
+ continue;
+ }
+
+ $temp = csstidy_optimise::explode_ws(',', $cur_value);
+
+ if (isset($temp[$i])) {
+ if ($bg_property === 'background-size') {
+ $new_bg_value .= '(' . $temp[$i] . ') ';
+ } else {
+ $new_bg_value .= $temp[$i] . ' ';
+ }
+ }
+ }
+
+ $new_bg_value = trim($new_bg_value);
+ if ($i != $number_of_values - 1)
+ $new_bg_value .= ',';
+ }
+
+ // Delete all background-properties
+ foreach ($background_prop_default as $bg_property => $default_value) {
+ unset($input_css[$bg_property]);
+ }
+
+ // Add new background property
+ if ($new_bg_value !== '')
+ $input_css['background'] = $new_bg_value . $important;
+ elseif(isset ($input_css['background']))
+ $input_css['background'] = 'none';
+
+ return $input_css;
+ }
+
+ /**
+ * Dissolve font property
+ * @param string $str_value
+ * @return array
+ * @version 1.3
+ * @see merge_font()
+ */
+ function dissolve_short_font($str_value) {
+ $font_prop_default = & $GLOBALS['csstidy']['font_prop_default'];
+ $font_weight = array('normal', 'bold', 'bolder', 'lighter', 100, 200, 300, 400, 500, 600, 700, 800, 900);
+ $font_variant = array('normal', 'small-caps');
+ $font_style = array('normal', 'italic', 'oblique');
+ $important = '';
+ $return = array('font-style' => null, 'font-variant' => null, 'font-weight' => null, 'font-size' => null, 'line-height' => null, 'font-family' => null);
+
+ if (csstidy::is_important($str_value)) {
+ $important = '!important';
+ $str_value = csstidy::gvw_important($str_value);
+ }
+
+ $have['style'] = false;
+ $have['variant'] = false;
+ $have['weight'] = false;
+ $have['size'] = false;
+ // Detects if font-family consists of several words w/o quotes
+ $multiwords = false;
+
+ // Workaround with multiple font-family
+ $str_value = csstidy_optimise::explode_ws(',', trim($str_value));
+
+ $str_value[0] = csstidy_optimise::explode_ws(' ', trim($str_value[0]));
+
+ for ($j = 0; $j < count($str_value[0]); $j++) {
+ if ($have['weight'] === false && in_array($str_value[0][$j], $font_weight)) {
+ $return['font-weight'] = $str_value[0][$j];
+ $have['weight'] = true;
+ } elseif ($have['variant'] === false && in_array($str_value[0][$j], $font_variant)) {
+ $return['font-variant'] = $str_value[0][$j];
+ $have['variant'] = true;
+ } elseif ($have['style'] === false && in_array($str_value[0][$j], $font_style)) {
+ $return['font-style'] = $str_value[0][$j];
+ $have['style'] = true;
+ } elseif ($have['size'] === false && (is_numeric($str_value[0][$j]{0}) || $str_value[0][$j]{0} === null || $str_value[0][$j]{0} === '.')) {
+ $size = csstidy_optimise::explode_ws('/', trim($str_value[0][$j]));
+ $return['font-size'] = $size[0];
+ if (isset($size[1])) {
+ $return['line-height'] = $size[1];
+ } else {
+ $return['line-height'] = ''; // don't add 'normal' !
+ }
+ $have['size'] = true;
+ } else {
+ if (isset($return['font-family'])) {
+ $return['font-family'] .= ' ' . $str_value[0][$j];
+ $multiwords = true;
+ } else {
+ $return['font-family'] = $str_value[0][$j];
+ }
+ }
+ }
+ // add quotes if we have several qords in font-family
+ if ($multiwords !== false) {
+ $return['font-family'] = '"' . $return['font-family'] . '"';
+ }
+ $i = 1;
+ while (isset($str_value[$i])) {
+ $return['font-family'] .= ',' . trim($str_value[$i]);
+ $i++;
+ }
+
+ // Fix for 100 and more font-size
+ if ($have['size'] === false && isset($return['font-weight']) &&
+ is_numeric($return['font-weight']{0})) {
+ $return['font-size'] = $return['font-weight'];
+ unset($return['font-weight']);
+ }
+
+ foreach ($font_prop_default as $font_prop => $default_value) {
+ if ($return[$font_prop] !== null) {
+ $return[$font_prop] = $return[$font_prop] . $important;
+ }
+ else
+ $return[$font_prop] = $default_value . $important;
+ }
+ return $return;
+ }
+
+ /**
+ * Merges all fonts properties
+ * @param array $input_css
+ * @return array
+ * @version 1.3
+ * @see dissolve_short_font()
+ */
+ function merge_font($input_css) {
+ $font_prop_default = & $GLOBALS['csstidy']['font_prop_default'];
+ $new_font_value = '';
+ $important = '';
+ // Skip if not font-family and font-size set
+ if (isset($input_css['font-family']) && isset($input_css['font-size'])) {
+ // fix several words in font-family - add quotes
+ if (isset($input_css['font-family'])) {
+ $families = explode(",", $input_css['font-family']);
+ $result_families = array();
+ foreach ($families as $family) {
+ $family = trim($family);
+ $len = strlen($family);
+ if (strpos($family, " ") &&
+ !(($family{0} == '"' && $family{$len - 1} == '"') ||
+ ($family{0} == "'" && $family{$len - 1} == "'"))) {
+ $family = '"' . $family . '"';
+ }
+ $result_families[] = $family;
+ }
+ $input_css['font-family'] = implode(",", $result_families);
+ }
+ foreach ($font_prop_default as $font_property => $default_value) {
+
+ // Skip if property does not exist
+ if (!isset($input_css[$font_property])) {
+ continue;
+ }
+
+ $cur_value = $input_css[$font_property];
+
+ // Skip if default value is used
+ if ($cur_value === $default_value) {
+ continue;
+ }
+
+ // Remove !important
+ if (csstidy::is_important($cur_value)) {
+ $important = '!important';
+ $cur_value = csstidy::gvw_important($cur_value);
+ }
+
+ $new_font_value .= $cur_value;
+ // Add delimiter
+ $new_font_value .= ( $font_property === 'font-size' &&
+ isset($input_css['line-height'])) ? '/' : ' ';
+ }
+
+ $new_font_value = trim($new_font_value);
+
+ // Delete all font-properties
+ foreach ($font_prop_default as $font_property => $default_value) {
+ if ($font_property!=='font' OR !$new_font_value)
+ unset($input_css[$font_property]);
+ }
+
+ // Add new font property
+ if ($new_font_value !== '') {
+ $input_css['font'] = $new_font_value . $important;
+ }
+ }
+
+ return $input_css;
+ }
+
+}
diff --git a/plugins/jetpack/modules/custom-css/csstidy/class.csstidy_print.php b/plugins/jetpack/modules/custom-css/csstidy/class.csstidy_print.php
new file mode 100644
index 00000000..00a1956b
--- /dev/null
+++ b/plugins/jetpack/modules/custom-css/csstidy/class.csstidy_print.php
@@ -0,0 +1,408 @@
+<?php
+
+/**
+ * CSSTidy - CSS Parser and Optimiser
+ *
+ * CSS Printing class
+ * This class prints CSS data generated by csstidy.
+ *
+ * Copyright 2005, 2006, 2007 Florian Schmitz
+ *
+ * This file is part of CSSTidy.
+ *
+ * CSSTidy is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * CSSTidy 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * @license http://opensource.org/licenses/lgpl-license.php GNU Lesser General Public License
+ * @package csstidy
+ * @author Florian Schmitz (floele at gmail dot com) 2005-2007
+ * @author Brett Zamir (brettz9 at yahoo dot com) 2007
+ * @author Cedric Morin (cedric at yterium dot com) 2010
+ */
+
+/**
+ * CSS Printing class
+ *
+ * This class prints CSS data generated by csstidy.
+ *
+ * @package csstidy
+ * @author Florian Schmitz (floele at gmail dot com) 2005-2006
+ * @version 1.0.1
+ */
+class csstidy_print {
+
+ /**
+ * Saves the input CSS string
+ * @var string
+ * @access private
+ */
+ var $input_css = '';
+ /**
+ * Saves the formatted CSS string
+ * @var string
+ * @access public
+ */
+ var $output_css = '';
+ /**
+ * Saves the formatted CSS string (plain text)
+ * @var string
+ * @access public
+ */
+ var $output_css_plain = '';
+
+ /**
+ * Constructor
+ * @param array $css contains the class csstidy
+ * @access private
+ * @version 1.0
+ */
+ function csstidy_print(&$css) {
+ $this->parser = & $css;
+ $this->css = & $css->css;
+ $this->template = & $css->template;
+ $this->tokens = & $css->tokens;
+ $this->charset = & $css->charset;
+ $this->import = & $css->import;
+ $this->namespace = & $css->namespace;
+ }
+
+ /**
+ * Resets output_css and output_css_plain (new css code)
+ * @access private
+ * @version 1.0
+ */
+ function _reset() {
+ $this->output_css = '';
+ $this->output_css_plain = '';
+ }
+
+ /**
+ * Returns the CSS code as plain text
+ * @param string $default_media default @media to add to selectors without any @media
+ * @return string
+ * @access public
+ * @version 1.0
+ */
+ function plain($default_media='') {
+ $this->_print(true, $default_media);
+ return $this->output_css_plain;
+ }
+
+ /**
+ * Returns the formatted CSS code
+ * @param string $default_media default @media to add to selectors without any @media
+ * @return string
+ * @access public
+ * @version 1.0
+ */
+ function formatted($default_media='') {
+ $this->_print(false, $default_media);
+ return $this->output_css;
+ }
+
+ /**
+ * Returns the formatted CSS code to make a complete webpage
+ * @param string $doctype shorthand for the document type
+ * @param bool $externalcss indicates whether styles to be attached internally or as an external stylesheet
+ * @param string $title title to be added in the head of the document
+ * @param string $lang two-letter language code to be added to the output
+ * @return string
+ * @access public
+ * @version 1.4
+ */
+ function formatted_page($doctype='xhtml1.1', $externalcss=true, $title='', $lang='en') {
+ switch ($doctype) {
+ case 'xhtml1.0strict':
+ $doctype_output = '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">';
+ break;
+ case 'xhtml1.1':
+ default:
+ $doctype_output = '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
+ "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">';
+ break;
+ }
+
+ $output = $cssparsed = '';
+ $this->output_css_plain = & $output;
+
+ $output .= $doctype_output . "\n" . '<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="' . $lang . '"';
+ $output .= ( $doctype === 'xhtml1.1') ? '>' : ' lang="' . $lang . '">';
+ $output .= "\n<head>\n <title>$title</title>";
+
+ if ($externalcss) {
+ $output .= "\n <style type=\"text/css\">\n";
+ $cssparsed = file_get_contents('cssparsed.css');
+ $output .= $cssparsed; // Adds an invisible BOM or something, but not in css_optimised.php
+ $output .= "\n</style>";
+ } else {
+ $output .= "\n" . ' <link rel="stylesheet" type="text/css" href="cssparsed.css" />';
+// }
+ }
+ $output .= "\n</head>\n<body><code id=\"copytext\">";
+ $output .= $this->formatted();
+ $output .= '</code>' . "\n" . '</body></html>';
+ return $this->output_css_plain;
+ }
+
+ /**
+ * Returns the formatted CSS Code and saves it into $this->output_css and $this->output_css_plain
+ * @param bool $plain plain text or not
+ * @param string $default_media default @media to add to selectors without any @media
+ * @access private
+ * @version 2.0
+ */
+ function _print($plain = false, $default_media='') {
+ if ($this->output_css && $this->output_css_plain) {
+ return;
+ }
+
+ $output = '';
+ if (!$this->parser->get_cfg('preserve_css')) {
+ $this->_convert_raw_css($default_media);
+ }
+
+ $template = & $this->template;
+
+ if ($plain) {
+ $template = array_map('strip_tags', $template);
+ }
+
+ if ($this->parser->get_cfg('timestamp')) {
+ array_unshift($this->tokens, array(COMMENT, ' CSSTidy ' . $this->parser->version . ': ' . date('r') . ' '));
+ }
+
+ if (!empty($this->charset)) {
+ $output .= $template[0] . '@charset ' . $template[5] . $this->charset . $template[6];
+ }
+
+ if (!empty($this->import)) {
+ for ($i = 0, $size = count($this->import); $i < $size; $i++) {
+ $import_components = explode(' ', $this->import[$i]);
+ if (substr($import_components[0], 0, 4) === 'url(' && substr($import_components[0], -1, 1) === ')') {
+ $import_components[0] = '\'' . trim(substr($import_components[0], 4, -1), "'\"") . '\'';
+ $this->import[$i] = implode(' ', $import_components);
+ $this->parser->log('Optimised @import : Removed "url("', 'Information');
+ }
+ $output .= $template[0] . '@import ' . $template[5] . $this->import[$i] . $template[6];
+ }
+ }
+
+ if (!empty($this->namespace)) {
+ if (substr($this->namespace, 0, 4) === 'url(' && substr($this->namespace, -1, 1) === ')') {
+ $this->namespace = '\'' . substr($this->namespace, 4, -1) . '\'';
+ $this->parser->log('Optimised @namespace : Removed "url("', 'Information');
+ }
+ $output .= $template[0] . '@namespace ' . $template[5] . $this->namespace . $template[6];
+ }
+
+ $output .= $template[13];
+ $in_at_out = '';
+ $out = & $output;
+
+ foreach ($this->tokens as $key => $token) {
+ switch ($token[0]) {
+ case AT_START:
+ $out .= $template[0] . $this->_htmlsp($token[1], $plain) . $template[1];
+ $out = & $in_at_out;
+ break;
+
+ case SEL_START:
+ if ($this->parser->get_cfg('lowercase_s'))
+ $token[1] = strtolower($token[1]);
+ $out .= ( $token[1]{0} !== '@') ? $template[2] . $this->_htmlsp($token[1], $plain) : $template[0] . $this->_htmlsp($token[1], $plain);
+ $out .= $template[3];
+ break;
+
+ case PROPERTY:
+ if ($this->parser->get_cfg('case_properties') === 2) {
+ $token[1] = strtoupper($token[1]);
+ } elseif ($this->parser->get_cfg('case_properties') === 1) {
+ $token[1] = strtolower($token[1]);
+ }
+ $out .= $template[4] . $this->_htmlsp($token[1], $plain) . ':' . $template[5];
+ break;
+
+ case VALUE:
+ $out .= $this->_htmlsp($token[1], $plain);
+ if ($this->_seeknocomment($key, 1) == SEL_END && $this->parser->get_cfg('remove_last_;')) {
+ $out .= str_replace(';', '', $template[6]);
+ } else {
+ $out .= $template[6];
+ }
+ break;
+
+ case SEL_END:
+ $out .= $template[7];
+ if ($this->_seeknocomment($key, 1) != AT_END)
+ $out .= $template[8];
+ break;
+
+ case AT_END:
+ $out = & $output;
+ $out .= $template[10] . str_replace("\n", "\n" . $template[10], $in_at_out);
+ $in_at_out = '';
+ $out .= $template[9];
+ break;
+
+ case COMMENT:
+ $out .= $template[11] . '/*' . $this->_htmlsp($token[1], $plain) . '*/' . $template[12];
+ break;
+ }
+ }
+
+ $output = trim($output);
+
+ if (!$plain) {
+ $this->output_css = $output;
+ $this->_print(true);
+ } else {
+ // If using spaces in the template, don't want these to appear in the plain output
+ $this->output_css_plain = str_replace('&#160;', '', $output);
+ }
+ }
+
+ /**
+ * Gets the next token type which is $move away from $key, excluding comments
+ * @param integer $key current position
+ * @param integer $move move this far
+ * @return mixed a token type
+ * @access private
+ * @version 1.0
+ */
+ function _seeknocomment($key, $move) {
+ $go = ($move > 0) ? 1 : -1;
+ for ($i = $key + 1; abs($key - $i) - 1 < abs($move); $i += $go) {
+ if (!isset($this->tokens[$i])) {
+ return;
+ }
+ if ($this->tokens[$i][0] == COMMENT) {
+ $move += 1;
+ continue;
+ }
+ return $this->tokens[$i][0];
+ }
+ }
+
+ /**
+ * Converts $this->css array to a raw array ($this->tokens)
+ * @param string $default_media default @media to add to selectors without any @media
+ * @access private
+ * @version 1.0
+ */
+ function _convert_raw_css($default_media='') {
+ $this->tokens = array();
+
+ foreach ($this->css as $medium => $val) {
+ if ($this->parser->get_cfg('sort_selectors'))
+ ksort($val);
+ if (intval($medium) < DEFAULT_AT) {
+ $this->parser->_add_token(AT_START, $medium, true);
+ }
+ elseif ($default_media) {
+ $this->parser->_add_token(AT_START, $default_media, true);
+ }
+
+ foreach ($val as $selector => $vali) {
+ if ($this->parser->get_cfg('sort_properties'))
+ ksort($vali);
+ $this->parser->_add_token(SEL_START, $selector, true);
+
+ foreach ($vali as $property => $valj) {
+ $this->parser->_add_token(PROPERTY, $property, true);
+ $this->parser->_add_token(VALUE, $valj, true);
+ }
+
+ $this->parser->_add_token(SEL_END, $selector, true);
+ }
+
+ if (intval($medium) < DEFAULT_AT) {
+ $this->parser->_add_token(AT_END, $medium, true);
+ }
+ elseif ($default_media) {
+ $this->parser->_add_token(AT_END, $default_media, true);
+ }
+ }
+ }
+
+ /**
+ * Same as htmlspecialchars, only that chars are not replaced if $plain !== true. This makes print_code() cleaner.
+ * @param string $string
+ * @param bool $plain
+ * @return string
+ * @see csstidy_print::_print()
+ * @access private
+ * @version 1.0
+ */
+ function _htmlsp($string, $plain) {
+ if (!$plain) {
+ return htmlspecialchars($string, ENT_QUOTES, 'utf-8');
+ }
+ return $string;
+ }
+
+ /**
+ * Get compression ratio
+ * @access public
+ * @return float
+ * @version 1.2
+ */
+ function get_ratio() {
+ if (!$this->output_css_plain) {
+ $this->formatted();
+ }
+ return round((strlen($this->input_css) - strlen($this->output_css_plain)) / strlen($this->input_css), 3) * 100;
+ }
+
+ /**
+ * Get difference between the old and new code in bytes and prints the code if necessary.
+ * @access public
+ * @return string
+ * @version 1.1
+ */
+ function get_diff() {
+ if (!$this->output_css_plain) {
+ $this->formatted();
+ }
+
+ $diff = strlen($this->output_css_plain) - strlen($this->input_css);
+
+ if ($diff > 0) {
+ return '+' . $diff;
+ } elseif ($diff == 0) {
+ return '+-' . $diff;
+ }
+
+ return $diff;
+ }
+
+ /**
+ * Get the size of either input or output CSS in KB
+ * @param string $loc default is "output"
+ * @access public
+ * @return integer
+ * @version 1.0
+ */
+ function size($loc = 'output') {
+ if ($loc === 'output' && !$this->output_css) {
+ $this->formatted();
+ }
+
+ if ($loc === 'input') {
+ return (strlen($this->input_css) / 1000);
+ } else {
+ return (strlen($this->output_css_plain) / 1000);
+ }
+ }
+
+}
diff --git a/plugins/jetpack/modules/custom-css/csstidy/cssparse.css b/plugins/jetpack/modules/custom-css/csstidy/cssparse.css
new file mode 100644
index 00000000..38fc40b4
--- /dev/null
+++ b/plugins/jetpack/modules/custom-css/csstidy/cssparse.css
@@ -0,0 +1,118 @@
+@import url("cssparsed.css");
+
+html, body {
+font:0.8em Verdana,Helvetica,sans-serif;
+background:#F8F8F6;
+}
+
+code {
+font-size:1.2em;
+}
+
+div#rightcol {
+padding-left:32em;
+}
+
+fieldset {
+display:block;
+margin:0.5em 0;
+padding:1em;
+border:solid #7284AB 2px;
+}
+fieldset.code_output {
+display:inline;
+}
+
+h1 {
+font-size:2em;
+}
+
+small {
+font-size:0.7em;
+}
+
+fieldset#field_input {
+float:left;
+margin:0 0.5em 1em 0;
+}
+
+fieldset#options,fieldset#code_layout {
+width:31em;
+}
+
+input#submit {
+clear:both;
+display:block;
+margin:1em;
+}
+
+select {
+margin:2px 0 0;
+}
+
+label.block {
+display:block;
+}
+
+legend {
+background:#c4E1C3;
+padding:2px 4px;
+border:dashed 1px;
+}
+
+textarea#css_text {
+width:27em;
+height:370px;
+display:block;
+margin-right:1em;
+}
+
+.help {
+cursor:help;
+}
+
+p.important {
+border:solid 1px red;
+font-weight:bold;
+padding:1em;
+background:white;
+}
+
+p {
+margin:1em 0;
+}
+
+dl {
+padding-left:0.5em;
+}
+
+dt {
+font-weight:bold;
+margin:0;
+float:left;
+clear:both;
+height:1.5em;
+}
+
+dd {
+margin:0 0 0 4em;
+height:1.5em;
+}
+
+fieldset#messages {
+background:white;
+padding:0 0 0 1em;
+}
+
+fieldset#messages div {
+height:10em;
+overflow:auto;
+}
+
+dd.Warning {
+color:orange;
+}
+
+dd.Information {
+color:green;
+} \ No newline at end of file
diff --git a/plugins/jetpack/modules/custom-css/csstidy/cssparsed.css b/plugins/jetpack/modules/custom-css/csstidy/cssparsed.css
new file mode 100644
index 00000000..5aaf2bb1
--- /dev/null
+++ b/plugins/jetpack/modules/custom-css/csstidy/cssparsed.css
@@ -0,0 +1,29 @@
+code#copytext {
+ white-space: pre;
+ font-family: Verdana;
+}
+
+.at {
+color:darkblue;
+}
+
+.format {
+color:gray;
+}
+
+.property {
+color:green;
+}
+
+.selector {
+color:blue;
+}
+
+.value {
+color:red;
+left: 500px;
+}
+
+.comment {
+color:orange;
+} \ No newline at end of file
diff --git a/plugins/jetpack/modules/custom-css/csstidy/data-wp.inc.php b/plugins/jetpack/modules/custom-css/csstidy/data-wp.inc.php
new file mode 100644
index 00000000..f39ecbf8
--- /dev/null
+++ b/plugins/jetpack/modules/custom-css/csstidy/data-wp.inc.php
@@ -0,0 +1,75 @@
+<?php
+
+unset( $GLOBALS['csstidy']['all_properties']['binding'] );
+
+$GLOBALS['csstidy']['all_properties']['text-size-adjust'] = 'CSS3.0';
+
+// Support browser prefixes for properties only in the latest CSS draft
+foreach ( $GLOBALS['csstidy']['all_properties'] as $property => $levels ) {
+ if ( strpos( $levels, "," ) === false ) {
+ $GLOBALS['csstidy']['all_properties']['-moz-' . $property] = $levels;
+ $GLOBALS['csstidy']['all_properties']['-webkit-' . $property] = $levels;
+ $GLOBALS['csstidy']['all_properties']['-ms-' . $property] = $levels;
+ $GLOBALS['csstidy']['all_properties']['-o-' . $property] = $levels;
+ $GLOBALS['csstidy']['all_properties']['-khtml-' . $property] = $levels;
+
+ if ( in_array( $property, $GLOBALS['csstidy']['unit_values'] ) ) {
+ $GLOBALS['csstidy']['unit_values'][] = '-moz-' . $property;
+ $GLOBALS['csstidy']['unit_values'][] = '-webkit-' . $property;
+ $GLOBALS['csstidy']['unit_values'][] = '-ms-' . $property;
+ $GLOBALS['csstidy']['unit_values'][] = '-o-' . $property;
+ $GLOBALS['csstidy']['unit_values'][] = '-khtml-' . $property;
+ }
+
+ if ( in_array( $property, $GLOBALS['csstidy']['color_values'] ) ) {
+ $GLOBALS['csstidy']['color_values'][] = '-moz-' . $property;
+ $GLOBALS['csstidy']['color_values'][] = '-webkit-' . $property;
+ $GLOBALS['csstidy']['color_values'][] = '-ms-' . $property;
+ $GLOBALS['csstidy']['color_values'][] = '-o-' . $property;
+ $GLOBALS['csstidy']['color_values'][] = '-khtml-' . $property;
+ }
+ }
+}
+
+foreach ( $GLOBALS['csstidy']['multiple_properties'] as $property ) {
+ if ( '-' != $property[0] ) {
+ $GLOBALS['csstidy']['multiple_properties'][] = '-o-' . $property;
+ $GLOBALS['csstidy']['multiple_properties'][] = '-ms-' . $property;
+ $GLOBALS['csstidy']['multiple_properties'][] = '-webkit-' . $property;
+ $GLOBALS['csstidy']['multiple_properties'][] = '-moz-' . $property;
+ $GLOBALS['csstidy']['multiple_properties'][] = '-khtml-' . $property;
+ }
+}
+
+/**
+ * CSS Animation
+ *
+ * @see https://developer.mozilla.org/en/CSS/CSS_animations
+ */
+$GLOBALS['csstidy']['at_rules']['-webkit-keyframes'] = 'at';
+$GLOBALS['csstidy']['at_rules']['-moz-keyframes'] = 'at';
+$GLOBALS['csstidy']['at_rules']['-ms-keyframes'] = 'at';
+
+/**
+ * Non-standard CSS properties. They're not part of any spec, but we say
+ * they're in all of them so that we can support them.
+ */
+$GLOBALS['csstidy']['all_properties']['-webkit-filter'] = 'CSS2.0,CSS2.1,CSS3.0';
+$GLOBALS['csstidy']['all_properties']['-moz-filter'] = 'CSS2.0,CSS2.1,CSS3.0';
+$GLOBALS['csstidy']['all_properties']['-ms-filter'] = 'CSS2.0,CSS2.1,CSS3.0';
+$GLOBALS['csstidy']['all_properties']['filter'] = 'CSS2.0,CSS2.1,CSS3.0';
+$GLOBALS['csstidy']['all_properties']['scrollbar-face-color'] = 'CSS2.0,CSS2.1,CSS3.0';
+$GLOBALS['csstidy']['all_properties']['-ms-interpolation-mode'] = 'CSS2.0,CSS2.1,CSS3.0';
+$GLOBALS['csstidy']['all_properties']['text-rendering'] = 'CSS2.0,CSS2.1,CSS3.0';
+$GLOBALS['csstidy']['all_properties']['-webkit-transform-origin-x'] = 'CSS3.0';
+$GLOBALS['csstidy']['all_properties']['-webkit-transform-origin-y'] = 'CSS3.0';
+$GLOBALS['csstidy']['all_properties']['-webkit-transform-origin-z'] = 'CSS3.0';
+$GLOBALS['csstidy']['all_properties']['-webkit-font-smoothing'] = 'CSS3.0';
+$GLOBALS['csstidy']['all_properties']['-font-smooth'] = 'CSS3.0';
+$GLOBALS['csstidy']['all_properties']['-o-object-fit'] = 'CSS3.0';
+$GLOBALS['csstidy']['all_properties']['object-fit'] = 'CSS3.0';
+$GLOBALS['csstidy']['all_properties']['-o-object-position'] = 'CSS3.0';
+$GLOBALS['csstidy']['all_properties']['object-position'] = 'CSS3.0';
+$GLOBALS['csstidy']['all_properties']['text-overflow'] = 'CSS3.0';
+$GLOBALS['csstidy']['all_properties']['zoom'] = 'CSS3.0';
+
diff --git a/plugins/jetpack/modules/custom-css/csstidy/data.inc.php b/plugins/jetpack/modules/custom-css/csstidy/data.inc.php
new file mode 100644
index 00000000..c0855681
--- /dev/null
+++ b/plugins/jetpack/modules/custom-css/csstidy/data.inc.php
@@ -0,0 +1,661 @@
+<?php
+/**
+ * Various CSS Data for CSSTidy
+ *
+ * This file is part of CSSTidy.
+ *
+ * CSSTidy 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.
+ *
+ * CSSTidy 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 CSSTidy; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * @license http://opensource.org/licenses/gpl-license.php GNU Public License
+ * @package csstidy
+ * @author Florian Schmitz (floele at gmail dot com) 2005
+ * @author Nikolay Matsievsky (speed at webo dot name) 2010
+ */
+
+define('AT_START', 1);
+define('AT_END', 2);
+define('SEL_START', 3);
+define('SEL_END', 4);
+define('PROPERTY', 5);
+define('VALUE', 6);
+define('COMMENT', 7);
+define('DEFAULT_AT', 41);
+
+/**
+ * All whitespace allowed in CSS
+ *
+ * @global array $GLOBALS['csstidy']['whitespace']
+ * @version 1.0
+ */
+$GLOBALS['csstidy']['whitespace'] = array(' ',"\n","\t","\r","\x0B");
+
+/**
+ * All CSS tokens used by csstidy
+ *
+ * @global string $GLOBALS['csstidy']['tokens']
+ * @version 1.0
+ */
+$GLOBALS['csstidy']['tokens'] = '/@}{;:=\'"(,\\!$%&)*+.<>?[]^`|~';
+
+/**
+ * All CSS units (CSS 3 units included)
+ *
+ * @see compress_numbers()
+ * @global array $GLOBALS['csstidy']['units']
+ * @version 1.0
+ */
+$GLOBALS['csstidy']['units'] = array('in','cm','mm','pt','pc','px','rem','em','%','ex','gd','vw','vh','vm','deg','grad','rad','ms','s','khz','hz');
+
+/**
+ * Available at-rules
+ *
+ * @global array $GLOBALS['csstidy']['at_rules']
+ * @version 1.0
+ */
+$GLOBALS['csstidy']['at_rules'] = array('page' => 'is','font-face' => 'is','charset' => 'iv', 'import' => 'iv','namespace' => 'iv','media' => 'at','keyframes' => 'at');
+
+ /**
+ * Properties that need a value with unit
+ *
+ * @todo CSS3 properties
+ * @see compress_numbers();
+ * @global array $GLOBALS['csstidy']['unit_values']
+ * @version 1.2
+ */
+$GLOBALS['csstidy']['unit_values'] = array ('background', 'background-position', 'background-size', 'border', 'border-top', 'border-right', 'border-bottom', 'border-left', 'border-width',
+ 'border-top-width', 'border-right-width', 'border-left-width', 'border-bottom-width', 'bottom', 'border-spacing', 'column-gap', 'column-width',
+ 'font-size', 'height', 'left', 'margin', 'margin-top', 'margin-right', 'margin-bottom', 'margin-left', 'max-height',
+ 'max-width', 'min-height', 'min-width', 'outline', 'outline-width', 'padding', 'padding-top', 'padding-right',
+ 'padding-bottom', 'padding-left', 'perspective', 'right', 'top', 'text-indent', 'letter-spacing', 'word-spacing', 'width');
+
+/**
+ * Properties that allow <color> as value
+ *
+ * @todo CSS3 properties
+ * @see compress_numbers();
+ * @global array $GLOBALS['csstidy']['color_values']
+ * @version 1.0
+ */
+$GLOBALS['csstidy']['color_values'] = array();
+$GLOBALS['csstidy']['color_values'][] = 'background-color';
+$GLOBALS['csstidy']['color_values'][] = 'border-color';
+$GLOBALS['csstidy']['color_values'][] = 'border-top-color';
+$GLOBALS['csstidy']['color_values'][] = 'border-right-color';
+$GLOBALS['csstidy']['color_values'][] = 'border-bottom-color';
+$GLOBALS['csstidy']['color_values'][] = 'border-left-color';
+$GLOBALS['csstidy']['color_values'][] = 'color';
+$GLOBALS['csstidy']['color_values'][] = 'outline-color';
+$GLOBALS['csstidy']['color_values'][] = 'column-rule-color';
+
+/**
+ * Default values for the background properties
+ *
+ * @todo Possibly property names will change during CSS3 development
+ * @global array $GLOBALS['csstidy']['background_prop_default']
+ * @see dissolve_short_bg()
+ * @see merge_bg()
+ * @version 1.0
+ */
+$GLOBALS['csstidy']['background_prop_default'] = array();
+$GLOBALS['csstidy']['background_prop_default']['background-image'] = 'none';
+$GLOBALS['csstidy']['background_prop_default']['background-size'] = 'auto';
+$GLOBALS['csstidy']['background_prop_default']['background-repeat'] = 'repeat';
+$GLOBALS['csstidy']['background_prop_default']['background-position'] = '0 0';
+$GLOBALS['csstidy']['background_prop_default']['background-attachment'] = 'scroll';
+$GLOBALS['csstidy']['background_prop_default']['background-clip'] = 'border';
+$GLOBALS['csstidy']['background_prop_default']['background-origin'] = 'padding';
+$GLOBALS['csstidy']['background_prop_default']['background-color'] = 'transparent';
+
+/**
+ * Default values for the font properties
+ *
+ * @global array $GLOBALS['csstidy']['font_prop_default']
+ * @see merge_fonts()
+ * @version 1.3
+ */
+$GLOBALS['csstidy']['font_prop_default'] = array();
+$GLOBALS['csstidy']['font_prop_default']['font-style'] = 'normal';
+$GLOBALS['csstidy']['font_prop_default']['font-variant'] = 'normal';
+$GLOBALS['csstidy']['font_prop_default']['font-weight'] = 'normal';
+$GLOBALS['csstidy']['font_prop_default']['font-size'] = '';
+$GLOBALS['csstidy']['font_prop_default']['line-height'] = '';
+$GLOBALS['csstidy']['font_prop_default']['font-family'] = '';
+
+/**
+ * A list of non-W3C color names which get replaced by their hex-codes
+ *
+ * @global array $GLOBALS['csstidy']['replace_colors']
+ * @see cut_color()
+ * @version 1.0
+ */
+$GLOBALS['csstidy']['replace_colors'] = array();
+$GLOBALS['csstidy']['replace_colors']['aliceblue'] = '#f0f8ff';
+$GLOBALS['csstidy']['replace_colors']['antiquewhite'] = '#faebd7';
+$GLOBALS['csstidy']['replace_colors']['aquamarine'] = '#7fffd4';
+$GLOBALS['csstidy']['replace_colors']['azure'] = '#f0ffff';
+$GLOBALS['csstidy']['replace_colors']['beige'] = '#f5f5dc';
+$GLOBALS['csstidy']['replace_colors']['bisque'] = '#ffe4c4';
+$GLOBALS['csstidy']['replace_colors']['blanchedalmond'] = '#ffebcd';
+$GLOBALS['csstidy']['replace_colors']['blueviolet'] = '#8a2be2';
+$GLOBALS['csstidy']['replace_colors']['brown'] = '#a52a2a';
+$GLOBALS['csstidy']['replace_colors']['burlywood'] = '#deb887';
+$GLOBALS['csstidy']['replace_colors']['cadetblue'] = '#5f9ea0';
+$GLOBALS['csstidy']['replace_colors']['chartreuse'] = '#7fff00';
+$GLOBALS['csstidy']['replace_colors']['chocolate'] = '#d2691e';
+$GLOBALS['csstidy']['replace_colors']['coral'] = '#ff7f50';
+$GLOBALS['csstidy']['replace_colors']['cornflowerblue'] = '#6495ed';
+$GLOBALS['csstidy']['replace_colors']['cornsilk'] = '#fff8dc';
+$GLOBALS['csstidy']['replace_colors']['crimson'] = '#dc143c';
+$GLOBALS['csstidy']['replace_colors']['cyan'] = '#00ffff';
+$GLOBALS['csstidy']['replace_colors']['darkblue'] = '#00008b';
+$GLOBALS['csstidy']['replace_colors']['darkcyan'] = '#008b8b';
+$GLOBALS['csstidy']['replace_colors']['darkgoldenrod'] = '#b8860b';
+$GLOBALS['csstidy']['replace_colors']['darkgray'] = '#a9a9a9';
+$GLOBALS['csstidy']['replace_colors']['darkgreen'] = '#006400';
+$GLOBALS['csstidy']['replace_colors']['darkkhaki'] = '#bdb76b';
+$GLOBALS['csstidy']['replace_colors']['darkmagenta'] = '#8b008b';
+$GLOBALS['csstidy']['replace_colors']['darkolivegreen'] = '#556b2f';
+$GLOBALS['csstidy']['replace_colors']['darkorange'] = '#ff8c00';
+$GLOBALS['csstidy']['replace_colors']['darkorchid'] = '#9932cc';
+$GLOBALS['csstidy']['replace_colors']['darkred'] = '#8b0000';
+$GLOBALS['csstidy']['replace_colors']['darksalmon'] = '#e9967a';
+$GLOBALS['csstidy']['replace_colors']['darkseagreen'] = '#8fbc8f';
+$GLOBALS['csstidy']['replace_colors']['darkslateblue'] = '#483d8b';
+$GLOBALS['csstidy']['replace_colors']['darkslategray'] = '#2f4f4f';
+$GLOBALS['csstidy']['replace_colors']['darkturquoise'] = '#00ced1';
+$GLOBALS['csstidy']['replace_colors']['darkviolet'] = '#9400d3';
+$GLOBALS['csstidy']['replace_colors']['deeppink'] = '#ff1493';
+$GLOBALS['csstidy']['replace_colors']['deepskyblue'] = '#00bfff';
+$GLOBALS['csstidy']['replace_colors']['dimgray'] = '#696969';
+$GLOBALS['csstidy']['replace_colors']['dodgerblue'] = '#1e90ff';
+$GLOBALS['csstidy']['replace_colors']['feldspar'] = '#d19275';
+$GLOBALS['csstidy']['replace_colors']['firebrick'] = '#b22222';
+$GLOBALS['csstidy']['replace_colors']['floralwhite'] = '#fffaf0';
+$GLOBALS['csstidy']['replace_colors']['forestgreen'] = '#228b22';
+$GLOBALS['csstidy']['replace_colors']['gainsboro'] = '#dcdcdc';
+$GLOBALS['csstidy']['replace_colors']['ghostwhite'] = '#f8f8ff';
+$GLOBALS['csstidy']['replace_colors']['gold'] = '#ffd700';
+$GLOBALS['csstidy']['replace_colors']['goldenrod'] = '#daa520';
+$GLOBALS['csstidy']['replace_colors']['greenyellow'] = '#adff2f';
+$GLOBALS['csstidy']['replace_colors']['honeydew'] = '#f0fff0';
+$GLOBALS['csstidy']['replace_colors']['hotpink'] = '#ff69b4';
+$GLOBALS['csstidy']['replace_colors']['indianred'] = '#cd5c5c';
+$GLOBALS['csstidy']['replace_colors']['indigo'] = '#4b0082';
+$GLOBALS['csstidy']['replace_colors']['ivory'] = '#fffff0';
+$GLOBALS['csstidy']['replace_colors']['khaki'] = '#f0e68c';
+$GLOBALS['csstidy']['replace_colors']['lavender'] = '#e6e6fa';
+$GLOBALS['csstidy']['replace_colors']['lavenderblush'] = '#fff0f5';
+$GLOBALS['csstidy']['replace_colors']['lawngreen'] = '#7cfc00';
+$GLOBALS['csstidy']['replace_colors']['lemonchiffon'] = '#fffacd';
+$GLOBALS['csstidy']['replace_colors']['lightblue'] = '#add8e6';
+$GLOBALS['csstidy']['replace_colors']['lightcoral'] = '#f08080';
+$GLOBALS['csstidy']['replace_colors']['lightcyan'] = '#e0ffff';
+$GLOBALS['csstidy']['replace_colors']['lightgoldenrodyellow'] = '#fafad2';
+$GLOBALS['csstidy']['replace_colors']['lightgrey'] = '#d3d3d3';
+$GLOBALS['csstidy']['replace_colors']['lightgreen'] = '#90ee90';
+$GLOBALS['csstidy']['replace_colors']['lightpink'] = '#ffb6c1';
+$GLOBALS['csstidy']['replace_colors']['lightsalmon'] = '#ffa07a';
+$GLOBALS['csstidy']['replace_colors']['lightseagreen'] = '#20b2aa';
+$GLOBALS['csstidy']['replace_colors']['lightskyblue'] = '#87cefa';
+$GLOBALS['csstidy']['replace_colors']['lightslateblue'] = '#8470ff';
+$GLOBALS['csstidy']['replace_colors']['lightslategray'] = '#778899';
+$GLOBALS['csstidy']['replace_colors']['lightsteelblue'] = '#b0c4de';
+$GLOBALS['csstidy']['replace_colors']['lightyellow'] = '#ffffe0';
+$GLOBALS['csstidy']['replace_colors']['limegreen'] = '#32cd32';
+$GLOBALS['csstidy']['replace_colors']['linen'] = '#faf0e6';
+$GLOBALS['csstidy']['replace_colors']['magenta'] = '#ff00ff';
+$GLOBALS['csstidy']['replace_colors']['mediumaquamarine'] = '#66cdaa';
+$GLOBALS['csstidy']['replace_colors']['mediumblue'] = '#0000cd';
+$GLOBALS['csstidy']['replace_colors']['mediumorchid'] = '#ba55d3';
+$GLOBALS['csstidy']['replace_colors']['mediumpurple'] = '#9370d8';
+$GLOBALS['csstidy']['replace_colors']['mediumseagreen'] = '#3cb371';
+$GLOBALS['csstidy']['replace_colors']['mediumslateblue'] = '#7b68ee';
+$GLOBALS['csstidy']['replace_colors']['mediumspringgreen'] = '#00fa9a';
+$GLOBALS['csstidy']['replace_colors']['mediumturquoise'] = '#48d1cc';
+$GLOBALS['csstidy']['replace_colors']['mediumvioletred'] = '#c71585';
+$GLOBALS['csstidy']['replace_colors']['midnightblue'] = '#191970';
+$GLOBALS['csstidy']['replace_colors']['mintcream'] = '#f5fffa';
+$GLOBALS['csstidy']['replace_colors']['mistyrose'] = '#ffe4e1';
+$GLOBALS['csstidy']['replace_colors']['moccasin'] = '#ffe4b5';
+$GLOBALS['csstidy']['replace_colors']['navajowhite'] = '#ffdead';
+$GLOBALS['csstidy']['replace_colors']['oldlace'] = '#fdf5e6';
+$GLOBALS['csstidy']['replace_colors']['olivedrab'] = '#6b8e23';
+$GLOBALS['csstidy']['replace_colors']['orangered'] = '#ff4500';
+$GLOBALS['csstidy']['replace_colors']['orchid'] = '#da70d6';
+$GLOBALS['csstidy']['replace_colors']['palegoldenrod'] = '#eee8aa';
+$GLOBALS['csstidy']['replace_colors']['palegreen'] = '#98fb98';
+$GLOBALS['csstidy']['replace_colors']['paleturquoise'] = '#afeeee';
+$GLOBALS['csstidy']['replace_colors']['palevioletred'] = '#d87093';
+$GLOBALS['csstidy']['replace_colors']['papayawhip'] = '#ffefd5';
+$GLOBALS['csstidy']['replace_colors']['peachpuff'] = '#ffdab9';
+$GLOBALS['csstidy']['replace_colors']['peru'] = '#cd853f';
+$GLOBALS['csstidy']['replace_colors']['pink'] = '#ffc0cb';
+$GLOBALS['csstidy']['replace_colors']['plum'] = '#dda0dd';
+$GLOBALS['csstidy']['replace_colors']['powderblue'] = '#b0e0e6';
+$GLOBALS['csstidy']['replace_colors']['rosybrown'] = '#bc8f8f';
+$GLOBALS['csstidy']['replace_colors']['royalblue'] = '#4169e1';
+$GLOBALS['csstidy']['replace_colors']['saddlebrown'] = '#8b4513';
+$GLOBALS['csstidy']['replace_colors']['salmon'] = '#fa8072';
+$GLOBALS['csstidy']['replace_colors']['sandybrown'] = '#f4a460';
+$GLOBALS['csstidy']['replace_colors']['seagreen'] = '#2e8b57';
+$GLOBALS['csstidy']['replace_colors']['seashell'] = '#fff5ee';
+$GLOBALS['csstidy']['replace_colors']['sienna'] = '#a0522d';
+$GLOBALS['csstidy']['replace_colors']['skyblue'] = '#87ceeb';
+$GLOBALS['csstidy']['replace_colors']['slateblue'] = '#6a5acd';
+$GLOBALS['csstidy']['replace_colors']['slategray'] = '#708090';
+$GLOBALS['csstidy']['replace_colors']['snow'] = '#fffafa';
+$GLOBALS['csstidy']['replace_colors']['springgreen'] = '#00ff7f';
+$GLOBALS['csstidy']['replace_colors']['steelblue'] = '#4682b4';
+$GLOBALS['csstidy']['replace_colors']['tan'] = '#d2b48c';
+$GLOBALS['csstidy']['replace_colors']['thistle'] = '#d8bfd8';
+$GLOBALS['csstidy']['replace_colors']['tomato'] = '#ff6347';
+$GLOBALS['csstidy']['replace_colors']['turquoise'] = '#40e0d0';
+$GLOBALS['csstidy']['replace_colors']['violet'] = '#ee82ee';
+$GLOBALS['csstidy']['replace_colors']['violetred'] = '#d02090';
+$GLOBALS['csstidy']['replace_colors']['wheat'] = '#f5deb3';
+$GLOBALS['csstidy']['replace_colors']['whitesmoke'] = '#f5f5f5';
+$GLOBALS['csstidy']['replace_colors']['yellowgreen'] = '#9acd32';
+
+/**
+ * A list of all shorthand properties that are devided into four properties and/or have four subvalues
+ *
+ * @global array $GLOBALS['csstidy']['shorthands']
+ * @todo Are there new ones in CSS3?
+ * @see dissolve_4value_shorthands()
+ * @see merge_4value_shorthands()
+ * @version 1.0
+ */
+$GLOBALS['csstidy']['shorthands'] = array();
+$GLOBALS['csstidy']['shorthands']['border-color'] = array('border-top-color','border-right-color','border-bottom-color','border-left-color');
+$GLOBALS['csstidy']['shorthands']['border-style'] = array('border-top-style','border-right-style','border-bottom-style','border-left-style');
+$GLOBALS['csstidy']['shorthands']['border-width'] = array('border-top-width','border-right-width','border-bottom-width','border-left-width');
+$GLOBALS['csstidy']['shorthands']['margin'] = array('margin-top','margin-right','margin-bottom','margin-left');
+$GLOBALS['csstidy']['shorthands']['padding'] = array('padding-top','padding-right','padding-bottom','padding-left');
+$GLOBALS['csstidy']['shorthands']['-moz-border-radius'] = 0;
+
+/**
+ * All CSS Properties. Needed for csstidy::property_is_next()
+ *
+ * @global array $GLOBALS['csstidy']['all_properties']
+ * @todo Add CSS3 properties
+ * @version 1.0
+ * @see csstidy::property_is_next()
+ */
+$GLOBALS['csstidy']['all_properties']['alignment-adjust'] = 'CSS3.0';
+$GLOBALS['csstidy']['all_properties']['alignment-baseline'] = 'CSS3.0';
+$GLOBALS['csstidy']['all_properties']['animation'] = 'CSS3.0';
+$GLOBALS['csstidy']['all_properties']['animation-delay'] = 'CSS3.0';
+$GLOBALS['csstidy']['all_properties']['animation-direction'] = 'CSS3.0';
+$GLOBALS['csstidy']['all_properties']['animation-duration'] = 'CSS3.0';
+$GLOBALS['csstidy']['all_properties']['animation-iteration-count'] = 'CSS3.0';
+$GLOBALS['csstidy']['all_properties']['animation-name'] = 'CSS3.0';
+$GLOBALS['csstidy']['all_properties']['animation-play-state'] = 'CSS3.0';
+$GLOBALS['csstidy']['all_properties']['animation-timing-function'] = 'CSS3.0';
+$GLOBALS['csstidy']['all_properties']['appearance'] = 'CSS3.0';
+$GLOBALS['csstidy']['all_properties']['azimuth'] = 'CSS2.0,CSS2.1,CSS3.0';
+$GLOBALS['csstidy']['all_properties']['backface-visibility'] = 'CSS3.0';
+$GLOBALS['csstidy']['all_properties']['background'] = 'CSS1.0,CSS2.0,CSS2.1,CSS3.0';
+$GLOBALS['csstidy']['all_properties']['background-attachment'] = 'CSS1.0,CSS2.0,CSS2.1,CSS3.0';
+$GLOBALS['csstidy']['all_properties']['background-clip'] = 'CSS3.0';
+$GLOBALS['csstidy']['all_properties']['background-color'] = 'CSS1.0,CSS2.0,CSS2.1,CSS3.0';
+$GLOBALS['csstidy']['all_properties']['background-image'] = 'CSS1.0,CSS2.0,CSS2.1,CSS3.0';
+$GLOBALS['csstidy']['all_properties']['background-origin'] = 'CSS3.0';
+$GLOBALS['csstidy']['all_properties']['background-position'] = 'CSS1.0,CSS2.0,CSS2.1,CSS3.0';
+$GLOBALS['csstidy']['all_properties']['background-repeat'] = 'CSS1.0,CSS2.0,CSS2.1,CSS3.0';
+$GLOBALS['csstidy']['all_properties']['background-size'] = 'CSS3.0';
+$GLOBALS['csstidy']['all_properties']['baseline-shift'] = 'CSS3.0';
+$GLOBALS['csstidy']['all_properties']['binding'] = 'CSS3.0';
+$GLOBALS['csstidy']['all_properties']['bleed'] = 'CSS3.0';
+$GLOBALS['csstidy']['all_properties']['bookmark-label'] = 'CSS3.0';
+$GLOBALS['csstidy']['all_properties']['bookmark-level'] = 'CSS3.0';
+$GLOBALS['csstidy']['all_properties']['bookmark-state'] = 'CSS3.0';
+$GLOBALS['csstidy']['all_properties']['bookmark-target'] = 'CSS3.0';
+$GLOBALS['csstidy']['all_properties']['border'] = 'CSS1.0,CSS2.0,CSS2.1,CSS3.0';
+$GLOBALS['csstidy']['all_properties']['border-bottom'] = 'CSS1.0,CSS2.0,CSS2.1,CSS3.0';
+$GLOBALS['csstidy']['all_properties']['border-bottom-color'] = 'CSS2.0,CSS2.1,CSS3.0';
+$GLOBALS['csstidy']['all_properties']['border-bottom-left-radius'] = 'CSS3.0';
+$GLOBALS['csstidy']['all_properties']['border-bottom-right-radius'] = 'CSS3.0';
+$GLOBALS['csstidy']['all_properties']['border-bottom-style'] = 'CSS2.0,CSS2.1,CSS3.0';
+$GLOBALS['csstidy']['all_properties']['border-bottom-width'] = 'CSS1.0,CSS2.0,CSS2.1,CSS3.0';
+$GLOBALS['csstidy']['all_properties']['border-collapse'] = 'CSS2.0,CSS2.1,CSS3.0';
+$GLOBALS['csstidy']['all_properties']['border-color'] = 'CSS1.0,CSS2.0,CSS2.1,CSS3.0';
+$GLOBALS['csstidy']['all_properties']['border-image'] = 'CSS3.0';
+$GLOBALS['csstidy']['all_properties']['border-image-outset'] = 'CSS3.0';
+$GLOBALS['csstidy']['all_properties']['border-image-repeat'] = 'CSS3.0';
+$GLOBALS['csstidy']['all_properties']['border-image-slice'] = 'CSS3.0';
+$GLOBALS['csstidy']['all_properties']['border-image-source'] = 'CSS3.0';
+$GLOBALS['csstidy']['all_properties']['border-image-width'] = 'CSS3.0';
+$GLOBALS['csstidy']['all_properties']['border-left'] = 'CSS1.0,CSS2.0,CSS2.1,CSS3.0';
+$GLOBALS['csstidy']['all_properties']['border-left-color'] = 'CSS2.0,CSS2.1,CSS3.0';
+$GLOBALS['csstidy']['all_properties']['border-left-style'] = 'CSS2.0,CSS2.1,CSS3.0';
+$GLOBALS['csstidy']['all_properties']['border-left-width'] = 'CSS1.0,CSS2.0,CSS2.1,CSS3.0';
+$GLOBALS['csstidy']['all_properties']['border-radius'] = 'CSS3.0';
+$GLOBALS['csstidy']['all_properties']['border-right'] = 'CSS1.0,CSS2.0,CSS2.1,CSS3.0';
+$GLOBALS['csstidy']['all_properties']['border-right-color'] = 'CSS2.0,CSS2.1,CSS3.0';
+$GLOBALS['csstidy']['all_properties']['border-right-style'] = 'CSS2.0,CSS2.1,CSS3.0';
+$GLOBALS['csstidy']['all_properties']['border-right-width'] = 'CSS1.0,CSS2.0,CSS2.1,CSS3.0';
+$GLOBALS['csstidy']['all_properties']['border-spacing'] = 'CSS2.0,CSS2.1,CSS3.0';
+$GLOBALS['csstidy']['all_properties']['border-style'] = 'CSS1.0,CSS2.0,CSS2.1,CSS3.0';
+$GLOBALS['csstidy']['all_properties']['border-top'] = 'CSS1.0,CSS2.0,CSS2.1,CSS3.0';
+$GLOBALS['csstidy']['all_properties']['border-top-color'] = 'CSS2.0,CSS2.1,CSS3.0';
+$GLOBALS['csstidy']['all_properties']['border-top-left-radius'] = 'CSS3.0';
+$GLOBALS['csstidy']['all_properties']['border-top-right-radius'] = 'CSS3.0';
+$GLOBALS['csstidy']['all_properties']['border-top-style'] = 'CSS2.0,CSS2.1,CSS3.0';
+$GLOBALS['csstidy']['all_properties']['border-top-width'] = 'CSS1.0,CSS2.0,CSS2.1,CSS3.0';
+$GLOBALS['csstidy']['all_properties']['border-width'] = 'CSS1.0,CSS2.0,CSS2.1,CSS3.0';
+$GLOBALS['csstidy']['all_properties']['bottom'] = 'CSS2.0,CSS2.1,CSS3.0';
+$GLOBALS['csstidy']['all_properties']['box-decoration-break'] = 'CSS3.0';
+$GLOBALS['csstidy']['all_properties']['box-shadow'] = 'CSS3.0';
+$GLOBALS['csstidy']['all_properties']['box-sizing'] = 'CSS3.0';
+$GLOBALS['csstidy']['all_properties']['break-after'] = 'CSS3.0';
+$GLOBALS['csstidy']['all_properties']['break-before'] = 'CSS3.0';
+$GLOBALS['csstidy']['all_properties']['break-inside'] = 'CSS3.0';
+$GLOBALS['csstidy']['all_properties']['caption-side'] = 'CSS2.0,CSS2.1,CSS3.0';
+$GLOBALS['csstidy']['all_properties']['clear'] = 'CSS1.0,CSS2.0,CSS2.1,CSS3.0';
+$GLOBALS['csstidy']['all_properties']['clip'] = 'CSS2.0,CSS2.1,CSS3.0';
+$GLOBALS['csstidy']['all_properties']['color'] = 'CSS1.0,CSS2.0,CSS2.1,CSS3.0';
+$GLOBALS['csstidy']['all_properties']['color-profile'] = 'CSS3.0';
+$GLOBALS['csstidy']['all_properties']['column-count'] = 'CSS3.0';
+$GLOBALS['csstidy']['all_properties']['column-fill'] = 'CSS3.0';
+$GLOBALS['csstidy']['all_properties']['column-gap'] = 'CSS3.0';
+$GLOBALS['csstidy']['all_properties']['column-rule'] = 'CSS3.0';
+$GLOBALS['csstidy']['all_properties']['column-rule-color'] = 'CSS3.0';
+$GLOBALS['csstidy']['all_properties']['column-rule-style'] = 'CSS3.0';
+$GLOBALS['csstidy']['all_properties']['column-rule-width'] = 'CSS3.0';
+$GLOBALS['csstidy']['all_properties']['column-span'] = 'CSS3.0';
+$GLOBALS['csstidy']['all_properties']['column-width'] = 'CSS3.0';
+$GLOBALS['csstidy']['all_properties']['columns'] = 'CSS3.0';
+$GLOBALS['csstidy']['all_properties']['content'] = 'CSS2.0,CSS2.1,CSS3.0';
+$GLOBALS['csstidy']['all_properties']['counter-increment'] = 'CSS2.0,CSS2.1,CSS3.0';
+$GLOBALS['csstidy']['all_properties']['counter-reset'] = 'CSS2.0,CSS2.1,CSS3.0';
+$GLOBALS['csstidy']['all_properties']['crop'] = 'CSS3.0';
+$GLOBALS['csstidy']['all_properties']['cue'] = 'CSS2.0,CSS2.1,CSS3.0';
+$GLOBALS['csstidy']['all_properties']['cue-after'] = 'CSS2.0,CSS2.1,CSS3.0';
+$GLOBALS['csstidy']['all_properties']['cue-before'] = 'CSS2.0,CSS2.1,CSS3.0';
+$GLOBALS['csstidy']['all_properties']['cursor'] = 'CSS2.0,CSS2.1,CSS3.0';
+$GLOBALS['csstidy']['all_properties']['direction'] = 'CSS2.0,CSS2.1,CSS3.0';
+$GLOBALS['csstidy']['all_properties']['display'] = 'CSS1.0,CSS2.0,CSS2.1,CSS3.0';
+$GLOBALS['csstidy']['all_properties']['dominant-baseline'] = 'CSS3.0';
+$GLOBALS['csstidy']['all_properties']['drop-initial-after-adjust'] = 'CSS3.0';
+$GLOBALS['csstidy']['all_properties']['drop-initial-after-align'] = 'CSS3.0';
+$GLOBALS['csstidy']['all_properties']['drop-initial-before-adjust'] = 'CSS3.0';
+$GLOBALS['csstidy']['all_properties']['drop-initial-before-align'] = 'CSS3.0';
+$GLOBALS['csstidy']['all_properties']['drop-initial-size'] = 'CSS3.0';
+$GLOBALS['csstidy']['all_properties']['drop-initial-value'] = 'CSS3.0';
+$GLOBALS['csstidy']['all_properties']['elevation'] = 'CSS2.0,CSS2.1,CSS3.0';
+$GLOBALS['csstidy']['all_properties']['empty-cells'] = 'CSS2.0,CSS2.1,CSS3.0';
+$GLOBALS['csstidy']['all_properties']['fit'] = 'CSS3.0';
+$GLOBALS['csstidy']['all_properties']['fit-position'] = 'CSS3.0';
+$GLOBALS['csstidy']['all_properties']['flex-align'] = 'CSS3.0';
+$GLOBALS['csstidy']['all_properties']['flex-flow'] = 'CSS3.0';
+$GLOBALS['csstidy']['all_properties']['flex-line-pack'] = 'CSS3.0';
+$GLOBALS['csstidy']['all_properties']['flex-order'] = 'CSS3.0';
+$GLOBALS['csstidy']['all_properties']['flex-pack'] = 'CSS3.0';
+$GLOBALS['csstidy']['all_properties']['float'] = 'CSS1.0,CSS2.0,CSS2.1,CSS3.0';
+$GLOBALS['csstidy']['all_properties']['float-offset'] = 'CSS3.0';
+$GLOBALS['csstidy']['all_properties']['font'] = 'CSS1.0,CSS2.0,CSS2.1,CSS3.0';
+$GLOBALS['csstidy']['all_properties']['font-family'] = 'CSS1.0,CSS2.0,CSS2.1,CSS3.0';
+$GLOBALS['csstidy']['all_properties']['font-size'] = 'CSS1.0,CSS2.0,CSS2.1,CSS3.0';
+$GLOBALS['csstidy']['all_properties']['font-size-adjust'] = 'CSS2.0,CSS3.0';
+$GLOBALS['csstidy']['all_properties']['font-stretch'] = 'CSS2.0,CSS3.0';
+$GLOBALS['csstidy']['all_properties']['font-style'] = 'CSS1.0,CSS2.0,CSS2.1,CSS3.0';
+$GLOBALS['csstidy']['all_properties']['font-variant'] = 'CSS1.0,CSS2.0,CSS2.1,CSS3.0';
+$GLOBALS['csstidy']['all_properties']['font-weight'] = 'CSS1.0,CSS2.0,CSS2.1,CSS3.0';
+$GLOBALS['csstidy']['all_properties']['grid-columns'] = 'CSS3.0';
+$GLOBALS['csstidy']['all_properties']['grid-rows'] = 'CSS3.0';
+$GLOBALS['csstidy']['all_properties']['hanging-punctuation'] = 'CSS3.0';
+$GLOBALS['csstidy']['all_properties']['height'] = 'CSS1.0,CSS2.0,CSS2.1,CSS3.0';
+$GLOBALS['csstidy']['all_properties']['hyphenate-after'] = 'CSS3.0';
+$GLOBALS['csstidy']['all_properties']['hyphenate-before'] = 'CSS3.0';
+$GLOBALS['csstidy']['all_properties']['hyphenate-character'] = 'CSS3.0';
+$GLOBALS['csstidy']['all_properties']['hyphenate-lines'] = 'CSS3.0';
+$GLOBALS['csstidy']['all_properties']['hyphenate-resource'] = 'CSS3.0';
+$GLOBALS['csstidy']['all_properties']['hyphens'] = 'CSS3.0';
+$GLOBALS['csstidy']['all_properties']['icon'] = 'CSS3.0';
+$GLOBALS['csstidy']['all_properties']['image-orientation'] = 'CSS3.0';
+$GLOBALS['csstidy']['all_properties']['image-rendering'] = 'CSS3.0';
+$GLOBALS['csstidy']['all_properties']['image-resolution'] = 'CSS3.0';
+$GLOBALS['csstidy']['all_properties']['inline-box-align'] = 'CSS3.0';
+$GLOBALS['csstidy']['all_properties']['left'] = 'CSS2.0,CSS2.1,CSS3.0';
+$GLOBALS['csstidy']['all_properties']['letter-spacing'] = 'CSS1.0,CSS2.0,CSS2.1,CSS3.0';
+$GLOBALS['csstidy']['all_properties']['line-break'] = 'CSS3.0';
+$GLOBALS['csstidy']['all_properties']['line-height'] = 'CSS1.0,CSS2.0,CSS2.1,CSS3.0';
+$GLOBALS['csstidy']['all_properties']['line-stacking'] = 'CSS3.0';
+$GLOBALS['csstidy']['all_properties']['line-stacking-ruby'] = 'CSS3.0';
+$GLOBALS['csstidy']['all_properties']['line-stacking-shift'] = 'CSS3.0';
+$GLOBALS['csstidy']['all_properties']['line-stacking-strategy'] = 'CSS3.0';
+$GLOBALS['csstidy']['all_properties']['list-style'] = 'CSS1.0,CSS2.0,CSS2.1,CSS3.0';
+$GLOBALS['csstidy']['all_properties']['list-style-image'] = 'CSS1.0,CSS2.0,CSS2.1,CSS3.0';
+$GLOBALS['csstidy']['all_properties']['list-style-position'] = 'CSS1.0,CSS2.0,CSS2.1,CSS3.0';
+$GLOBALS['csstidy']['all_properties']['list-style-type'] = 'CSS1.0,CSS2.0,CSS2.1,CSS3.0';
+$GLOBALS['csstidy']['all_properties']['margin'] = 'CSS1.0,CSS2.0,CSS2.1,CSS3.0';
+$GLOBALS['csstidy']['all_properties']['margin-bottom'] = 'CSS1.0,CSS2.0,CSS2.1,CSS3.0';
+$GLOBALS['csstidy']['all_properties']['margin-left'] = 'CSS1.0,CSS2.0,CSS2.1,CSS3.0';
+$GLOBALS['csstidy']['all_properties']['margin-right'] = 'CSS1.0,CSS2.0,CSS2.1,CSS3.0';
+$GLOBALS['csstidy']['all_properties']['margin-top'] = 'CSS1.0,CSS2.0,CSS2.1,CSS3.0';
+$GLOBALS['csstidy']['all_properties']['marker-offset'] = 'CSS3.0';
+$GLOBALS['csstidy']['all_properties']['marks'] = 'CSS2.0,CSS3.0';
+$GLOBALS['csstidy']['all_properties']['marquee-direction'] = 'CSS3.0';
+$GLOBALS['csstidy']['all_properties']['marquee-loop'] = 'CSS3.0';
+$GLOBALS['csstidy']['all_properties']['marquee-play-count'] = 'CSS3.0';
+$GLOBALS['csstidy']['all_properties']['marquee-speed'] = 'CSS3.0';
+$GLOBALS['csstidy']['all_properties']['marquee-style'] = 'CSS3.0';
+$GLOBALS['csstidy']['all_properties']['max-height'] = 'CSS2.0,CSS2.1,CSS3.0';
+$GLOBALS['csstidy']['all_properties']['max-width'] = 'CSS2.0,CSS2.1,CSS3.0';
+$GLOBALS['csstidy']['all_properties']['min-height'] = 'CSS2.0,CSS2.1,CSS3.0';
+$GLOBALS['csstidy']['all_properties']['min-width'] = 'CSS2.0,CSS2.1,CSS3.0';
+$GLOBALS['csstidy']['all_properties']['move-to'] = 'CSS3.0';
+$GLOBALS['csstidy']['all_properties']['nav-down'] = 'CSS3.0';
+$GLOBALS['csstidy']['all_properties']['nav-index'] = 'CSS3.0';
+$GLOBALS['csstidy']['all_properties']['nav-left'] = 'CSS3.0';
+$GLOBALS['csstidy']['all_properties']['nav-right'] = 'CSS3.0';
+$GLOBALS['csstidy']['all_properties']['nav-up'] = 'CSS3.0';
+$GLOBALS['csstidy']['all_properties']['opacity'] = 'CSS3.0';
+$GLOBALS['csstidy']['all_properties']['orphans'] = 'CSS2.0,CSS2.1,CSS3.0';
+$GLOBALS['csstidy']['all_properties']['outline'] = 'CSS2.0,CSS2.1,CSS3.0';
+$GLOBALS['csstidy']['all_properties']['outline-color'] = 'CSS2.0,CSS2.1,CSS3.0';
+$GLOBALS['csstidy']['all_properties']['outline-offset'] = 'CSS3.0';
+$GLOBALS['csstidy']['all_properties']['outline-style'] = 'CSS2.0,CSS2.1,CSS3.0';
+$GLOBALS['csstidy']['all_properties']['outline-width'] = 'CSS2.0,CSS2.1,CSS3.0';
+$GLOBALS['csstidy']['all_properties']['overflow'] = 'CSS2.0,CSS2.1,CSS3.0';
+$GLOBALS['csstidy']['all_properties']['overflow-style'] = 'CSS3.0';
+$GLOBALS['csstidy']['all_properties']['overflow-wrap'] = 'CSS3.0';
+$GLOBALS['csstidy']['all_properties']['overflow-x'] = 'CSS3.0';
+$GLOBALS['csstidy']['all_properties']['overflow-y'] = 'CSS3.0';
+$GLOBALS['csstidy']['all_properties']['padding'] = 'CSS1.0,CSS2.0,CSS2.1,CSS3.0';
+$GLOBALS['csstidy']['all_properties']['padding-bottom'] = 'CSS1.0,CSS2.0,CSS2.1,CSS3.0';
+$GLOBALS['csstidy']['all_properties']['padding-left'] = 'CSS1.0,CSS2.0,CSS2.1,CSS3.0';
+$GLOBALS['csstidy']['all_properties']['padding-right'] = 'CSS1.0,CSS2.0,CSS2.1,CSS3.0';
+$GLOBALS['csstidy']['all_properties']['padding-top'] = 'CSS1.0,CSS2.0,CSS2.1,CSS3.0';
+$GLOBALS['csstidy']['all_properties']['page'] = 'CSS2.0,CSS3.0';
+$GLOBALS['csstidy']['all_properties']['page-break-after'] = 'CSS2.0,CSS2.1,CSS3.0';
+$GLOBALS['csstidy']['all_properties']['page-break-before'] = 'CSS2.0,CSS2.1,CSS3.0';
+$GLOBALS['csstidy']['all_properties']['page-break-inside'] = 'CSS2.0,CSS2.1,CSS3.0';
+$GLOBALS['csstidy']['all_properties']['page-policy'] = 'CSS3.0';
+$GLOBALS['csstidy']['all_properties']['pause'] = 'CSS2.0,CSS2.1,CSS3.0';
+$GLOBALS['csstidy']['all_properties']['pause-after'] = 'CSS2.0,CSS2.1,CSS3.0';
+$GLOBALS['csstidy']['all_properties']['pause-before'] = 'CSS2.0,CSS2.1,CSS3.0';
+$GLOBALS['csstidy']['all_properties']['perspective'] = 'CSS3.0';
+$GLOBALS['csstidy']['all_properties']['perspective-origin'] = 'CSS3.0';
+$GLOBALS['csstidy']['all_properties']['phonemes'] = 'CSS3.0';
+$GLOBALS['csstidy']['all_properties']['pitch'] = 'CSS2.0,CSS2.1,CSS3.0';
+$GLOBALS['csstidy']['all_properties']['pitch-range'] = 'CSS2.0,CSS2.1,CSS3.0';
+$GLOBALS['csstidy']['all_properties']['play-during'] = 'CSS2.0,CSS2.1,CSS3.0';
+$GLOBALS['csstidy']['all_properties']['position'] = 'CSS2.0,CSS2.1,CSS3.0';
+$GLOBALS['csstidy']['all_properties']['presentation-level'] = 'CSS3.0';
+$GLOBALS['csstidy']['all_properties']['punctuation-trim'] = 'CSS3.0';
+$GLOBALS['csstidy']['all_properties']['quotes'] = 'CSS2.0,CSS2.1,CSS3.0';
+$GLOBALS['csstidy']['all_properties']['rendering-intent'] = 'CSS3.0';
+$GLOBALS['csstidy']['all_properties']['resize'] = 'CSS3.0';
+$GLOBALS['csstidy']['all_properties']['rest'] = 'CSS3.0';
+$GLOBALS['csstidy']['all_properties']['rest-after'] = 'CSS3.0';
+$GLOBALS['csstidy']['all_properties']['rest-before'] = 'CSS3.0';
+$GLOBALS['csstidy']['all_properties']['richness'] = 'CSS2.0,CSS2.1,CSS3.0';
+$GLOBALS['csstidy']['all_properties']['right'] = 'CSS2.0,CSS2.1,CSS3.0';
+$GLOBALS['csstidy']['all_properties']['rotation'] = 'CSS3.0';
+$GLOBALS['csstidy']['all_properties']['rotation-point'] = 'CSS3.0';
+$GLOBALS['csstidy']['all_properties']['ruby-align'] = 'CSS3.0';
+$GLOBALS['csstidy']['all_properties']['ruby-overhang'] = 'CSS3.0';
+$GLOBALS['csstidy']['all_properties']['ruby-position'] = 'CSS3.0';
+$GLOBALS['csstidy']['all_properties']['ruby-span'] = 'CSS3.0';
+$GLOBALS['csstidy']['all_properties']['size'] = 'CSS2.0,CSS3.0';
+$GLOBALS['csstidy']['all_properties']['speak'] = 'CSS2.0,CSS2.1,CSS3.0';
+$GLOBALS['csstidy']['all_properties']['speak-header'] = 'CSS2.0,CSS2.1,CSS3.0';
+$GLOBALS['csstidy']['all_properties']['speak-numeral'] = 'CSS2.0,CSS2.1,CSS3.0';
+$GLOBALS['csstidy']['all_properties']['speak-punctuation'] = 'CSS2.0,CSS2.1,CSS3.0';
+$GLOBALS['csstidy']['all_properties']['speech-rate'] = 'CSS2.0,CSS2.1,CSS3.0';
+$GLOBALS['csstidy']['all_properties']['src'] = 'CSS3.0';
+$GLOBALS['csstidy']['all_properties']['stress'] = 'CSS2.0,CSS2.1,CSS3.0';
+$GLOBALS['csstidy']['all_properties']['string-set'] = 'CSS3.0';
+$GLOBALS['csstidy']['all_properties']['tab-size'] = 'CSS3.0';
+$GLOBALS['csstidy']['all_properties']['table-layout'] = 'CSS2.0,CSS2.1,CSS3.0';
+$GLOBALS['csstidy']['all_properties']['target'] = 'CSS3.0';
+$GLOBALS['csstidy']['all_properties']['target-name'] = 'CSS3.0';
+$GLOBALS['csstidy']['all_properties']['target-new'] = 'CSS3.0';
+$GLOBALS['csstidy']['all_properties']['target-position'] = 'CSS3.0';
+$GLOBALS['csstidy']['all_properties']['text-align'] = 'CSS1.0,CSS2.0,CSS2.1,CSS3.0';
+$GLOBALS['csstidy']['all_properties']['text-align-last'] = 'CSS3.0';
+$GLOBALS['csstidy']['all_properties']['text-decoration'] = 'CSS1.0,CSS2.0,CSS2.1,CSS3.0';
+$GLOBALS['csstidy']['all_properties']['text-decoration-color'] = 'CSS3.0';
+$GLOBALS['csstidy']['all_properties']['text-decoration-line'] = 'CSS3.0';
+$GLOBALS['csstidy']['all_properties']['text-decoration-skip'] = 'CSS3.0';
+$GLOBALS['csstidy']['all_properties']['text-decoration-style'] = 'CSS3.0';
+$GLOBALS['csstidy']['all_properties']['text-emphasis'] = 'CSS3.0';
+$GLOBALS['csstidy']['all_properties']['text-emphasis-color'] = 'CSS3.0';
+$GLOBALS['csstidy']['all_properties']['text-emphasis-position'] = 'CSS3.0';
+$GLOBALS['csstidy']['all_properties']['text-emphasis-style'] = 'CSS3.0';
+$GLOBALS['csstidy']['all_properties']['text-height'] = 'CSS3.0';
+$GLOBALS['csstidy']['all_properties']['text-indent'] = 'CSS1.0,CSS2.0,CSS2.1,CSS3.0';
+$GLOBALS['csstidy']['all_properties']['text-justify'] = 'CSS3.0';
+$GLOBALS['csstidy']['all_properties']['text-outline'] = 'CSS3.0';
+$GLOBALS['csstidy']['all_properties']['text-shadow'] = 'CSS2.0,CSS3.0';
+$GLOBALS['csstidy']['all_properties']['text-space-collapse'] = 'CSS3.0';
+$GLOBALS['csstidy']['all_properties']['text-transform'] = 'CSS1.0,CSS2.0,CSS2.1,CSS3.0';
+$GLOBALS['csstidy']['all_properties']['text-underline-position'] = 'CSS3.0';
+$GLOBALS['csstidy']['all_properties']['text-wrap'] = 'CSS3.0';
+$GLOBALS['csstidy']['all_properties']['top'] = 'CSS2.0,CSS2.1,CSS3.0';
+$GLOBALS['csstidy']['all_properties']['transform'] = 'CSS3.0';
+$GLOBALS['csstidy']['all_properties']['transform-origin'] = 'CSS3.0';
+$GLOBALS['csstidy']['all_properties']['transform-style'] = 'CSS3.0';
+$GLOBALS['csstidy']['all_properties']['transition'] = 'CSS3.0';
+$GLOBALS['csstidy']['all_properties']['transition-delay'] = 'CSS3.0';
+$GLOBALS['csstidy']['all_properties']['transition-duration'] = 'CSS3.0';
+$GLOBALS['csstidy']['all_properties']['transition-property'] = 'CSS3.0';
+$GLOBALS['csstidy']['all_properties']['transition-timing-function'] = 'CSS3.0';
+$GLOBALS['csstidy']['all_properties']['unicode-bidi'] = 'CSS2.0,CSS2.1,CSS3.0';
+$GLOBALS['csstidy']['all_properties']['vertical-align'] = 'CSS1.0,CSS2.0,CSS2.1,CSS3.0';
+$GLOBALS['csstidy']['all_properties']['visibility'] = 'CSS2.0,CSS2.1,CSS3.0';
+$GLOBALS['csstidy']['all_properties']['voice-balance'] = 'CSS3.0';
+$GLOBALS['csstidy']['all_properties']['voice-duration'] = 'CSS3.0';
+$GLOBALS['csstidy']['all_properties']['voice-family'] = 'CSS2.0,CSS2.1,CSS3.0';
+$GLOBALS['csstidy']['all_properties']['voice-pitch'] = 'CSS3.0';
+$GLOBALS['csstidy']['all_properties']['voice-pitch-range'] = 'CSS3.0';
+$GLOBALS['csstidy']['all_properties']['voice-rate'] = 'CSS3.0';
+$GLOBALS['csstidy']['all_properties']['voice-stress'] = 'CSS3.0';
+$GLOBALS['csstidy']['all_properties']['voice-volume'] = 'CSS3.0';
+$GLOBALS['csstidy']['all_properties']['volume'] = 'CSS2.0,CSS2.1,CSS3.0';
+$GLOBALS['csstidy']['all_properties']['white-space'] = 'CSS1.0,CSS2.0,CSS2.1,CSS3.0';
+$GLOBALS['csstidy']['all_properties']['widows'] = 'CSS2.0,CSS2.1,CSS3.0';
+$GLOBALS['csstidy']['all_properties']['width'] = 'CSS1.0,CSS2.0,CSS2.1,CSS3.0';
+$GLOBALS['csstidy']['all_properties']['word-break'] = 'CSS3.0';
+$GLOBALS['csstidy']['all_properties']['word-spacing'] = 'CSS1.0,CSS2.0,CSS2.1,CSS3.0';
+$GLOBALS['csstidy']['all_properties']['word-wrap'] = 'CSS3.0';
+$GLOBALS['csstidy']['all_properties']['z-index'] = 'CSS2.0,CSS2.1,CSS3.0';
+
+/**
+ * An array containing all properties that can accept a quoted string as a value.
+ *
+ * @global array $GLOBALS['csstidy']['quoted_string_properties']
+ */
+$GLOBALS['csstidy']['quoted_string_properties'] = array('content', 'font', 'font-family', 'quotes');
+
+/**
+ * An array containing all properties that can be defined multiple times without being overwritten.
+ * All unit values are included so that units like rem can be supported with fallbacks to px or em.
+ *
+ * @global array $GLOBALS['csstidy']['quoted_string_properties']
+ */
+$GLOBALS['csstidy']['multiple_properties'] = array_merge( $GLOBALS['csstidy']['unit_values'], array('background', 'background-image', 'transition') );
+
+/**
+ * An array containing all predefined templates.
+ *
+ * @global array $GLOBALS['csstidy']['predefined_templates']
+ * @version 1.0
+ * @see csstidy::load_template()
+ */
+$GLOBALS['csstidy']['predefined_templates']['default'][] = '<span class="at">'; //string before @rule
+$GLOBALS['csstidy']['predefined_templates']['default'][] = '</span> <span class="format">{</span>'."\n"; //bracket after @-rule
+$GLOBALS['csstidy']['predefined_templates']['default'][] = '<span class="selector">'; //string before selector
+$GLOBALS['csstidy']['predefined_templates']['default'][] = '</span> <span class="format">{</span>'."\n"; //bracket after selector
+$GLOBALS['csstidy']['predefined_templates']['default'][] = '<span class="property">'; //string before property
+$GLOBALS['csstidy']['predefined_templates']['default'][] = '</span><span class="value">'; //string after property+before value
+$GLOBALS['csstidy']['predefined_templates']['default'][] = '</span><span class="format">;</span>'."\n"; //string after value
+$GLOBALS['csstidy']['predefined_templates']['default'][] = '<span class="format">}</span>'; //closing bracket - selector
+$GLOBALS['csstidy']['predefined_templates']['default'][] = "\n\n"; //space between blocks {...}
+$GLOBALS['csstidy']['predefined_templates']['default'][] = "\n".'<span class="format">}</span>'. "\n\n"; //closing bracket @-rule
+$GLOBALS['csstidy']['predefined_templates']['default'][] = ''; //indent in @-rule
+$GLOBALS['csstidy']['predefined_templates']['default'][] = '<span class="comment">'; // before comment
+$GLOBALS['csstidy']['predefined_templates']['default'][] = '</span>'."\n"; // after comment
+$GLOBALS['csstidy']['predefined_templates']['default'][] = "\n"; // after last line @-rule
+
+$GLOBALS['csstidy']['predefined_templates']['high_compression'][] = '<span class="at">';
+$GLOBALS['csstidy']['predefined_templates']['high_compression'][] = '</span> <span class="format">{</span>'."\n";
+$GLOBALS['csstidy']['predefined_templates']['high_compression'][] = '<span class="selector">';
+$GLOBALS['csstidy']['predefined_templates']['high_compression'][] = '</span><span class="format">{</span>';
+$GLOBALS['csstidy']['predefined_templates']['high_compression'][] = '<span class="property">';
+$GLOBALS['csstidy']['predefined_templates']['high_compression'][] = '</span><span class="value">';
+$GLOBALS['csstidy']['predefined_templates']['high_compression'][] = '</span><span class="format">;</span>';
+$GLOBALS['csstidy']['predefined_templates']['high_compression'][] = '<span class="format">}</span>';
+$GLOBALS['csstidy']['predefined_templates']['high_compression'][] = "\n";
+$GLOBALS['csstidy']['predefined_templates']['high_compression'][] = "\n". '<span class="format">}'."\n".'</span>';
+$GLOBALS['csstidy']['predefined_templates']['high_compression'][] = '';
+$GLOBALS['csstidy']['predefined_templates']['high_compression'][] = '<span class="comment">'; // before comment
+$GLOBALS['csstidy']['predefined_templates']['high_compression'][] = '</span>'; // after comment
+$GLOBALS['csstidy']['predefined_templates']['high_compression'][] = "\n";
+
+$GLOBALS['csstidy']['predefined_templates']['highest_compression'][] = '<span class="at">';
+$GLOBALS['csstidy']['predefined_templates']['highest_compression'][] = '</span><span class="format">{</span>';
+$GLOBALS['csstidy']['predefined_templates']['highest_compression'][] = '<span class="selector">';
+$GLOBALS['csstidy']['predefined_templates']['highest_compression'][] = '</span><span class="format">{</span>';
+$GLOBALS['csstidy']['predefined_templates']['highest_compression'][] = '<span class="property">';
+$GLOBALS['csstidy']['predefined_templates']['highest_compression'][] = '</span><span class="value">';
+$GLOBALS['csstidy']['predefined_templates']['highest_compression'][] = '</span><span class="format">;</span>';
+$GLOBALS['csstidy']['predefined_templates']['highest_compression'][] = '<span class="format">}</span>';
+$GLOBALS['csstidy']['predefined_templates']['highest_compression'][] = '';
+$GLOBALS['csstidy']['predefined_templates']['highest_compression'][] = '<span class="format">}</span>';
+$GLOBALS['csstidy']['predefined_templates']['highest_compression'][] = '';
+$GLOBALS['csstidy']['predefined_templates']['highest_compression'][] = '<span class="comment">'; // before comment
+$GLOBALS['csstidy']['predefined_templates']['highest_compression'][] = '</span>'; // after comment
+$GLOBALS['csstidy']['predefined_templates']['highest_compression'][] = '';
+
+$GLOBALS['csstidy']['predefined_templates']['low_compression'][] = '<span class="at">';
+$GLOBALS['csstidy']['predefined_templates']['low_compression'][] = '</span> <span class="format">{</span>'."\n";
+$GLOBALS['csstidy']['predefined_templates']['low_compression'][] = '<span class="selector">';
+$GLOBALS['csstidy']['predefined_templates']['low_compression'][] = '</span>'."\n".'<span class="format">{</span>'."\n";
+$GLOBALS['csstidy']['predefined_templates']['low_compression'][] = ' <span class="property">';
+$GLOBALS['csstidy']['predefined_templates']['low_compression'][] = '</span><span class="value">';
+$GLOBALS['csstidy']['predefined_templates']['low_compression'][] = '</span><span class="format">;</span>'."\n";
+$GLOBALS['csstidy']['predefined_templates']['low_compression'][] = '<span class="format">}</span>';
+$GLOBALS['csstidy']['predefined_templates']['low_compression'][] = "\n\n";
+$GLOBALS['csstidy']['predefined_templates']['low_compression'][] = "\n".'<span class="format">}</span>'."\n\n";
+$GLOBALS['csstidy']['predefined_templates']['low_compression'][] = ' ';
+$GLOBALS['csstidy']['predefined_templates']['low_compression'][] = '<span class="comment">'; // before comment
+$GLOBALS['csstidy']['predefined_templates']['low_compression'][] = '</span>'."\n"; // after comment
+$GLOBALS['csstidy']['predefined_templates']['low_compression'][] = "\n";
+
+require dirname( __FILE__ ) . '/data-wp.inc.php';
+
+?>
diff --git a/plugins/jetpack/modules/custom-css/csstidy/lang.inc.php b/plugins/jetpack/modules/custom-css/csstidy/lang.inc.php
new file mode 100644
index 00000000..9e6b24b0
--- /dev/null
+++ b/plugins/jetpack/modules/custom-css/csstidy/lang.inc.php
@@ -0,0 +1,311 @@
+<?php
+
+/**
+ * Localization of CSS Optimiser Interface of CSSTidy
+ *
+ * Copyright 2005, 2006, 2007 Florian Schmitz
+ *
+ * This file is part of CSSTidy.
+ *
+ * CSSTidy is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * CSSTidy 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * @license http://opensource.org/licenses/lgpl-license.php GNU Lesser General Public License
+ * @package csstidy
+ * @author Florian Schmitz (floele at gmail dot com) 2005-2007
+ * @author Brett Zamir (brettz9 at yahoo dot com) 2007
+ */
+
+
+if(isset($_GET['lang'])) {
+ $l = $_GET['lang'];
+}
+else if(isset($_SERVER['HTTP_ACCEPT_LANGUAGE'])) {
+ $l = $_SERVER['HTTP_ACCEPT_LANGUAGE'];
+ $l = strtolower(substr($l, 0, 2));
+}
+else {
+ $l = '';
+}
+
+$l = (in_array($l, array('de', 'fr', 'zh'))) ? $l : 'en';
+
+// note 5 in all but French, and 40 in all are orphaned
+
+$lang = array();
+$lang['en'][0] = 'CSS Formatter and Optimiser/Optimizer (based on CSSTidy ';
+$lang['en'][1] = 'CSS Formatter and Optimiser';
+$lang['en'][2] = '(based on';
+$lang['en'][3] = '(plaintext)';
+$lang['en'][4] = 'Important Note:';
+$lang['en'][6] = 'Your code should be well-formed. This is <strong>not a validator</strong> which points out errors in your CSS code. To make sure that your code is valid, use the <a href="http://jigsaw.w3.org/css-validator/">W3C Validator</a>.';
+$lang['en'][7] = 'all comments are removed';
+$lang['en'][8] = 'CSS Input:';
+$lang['en'][9] = 'CSS-Code:';
+$lang['en'][10] = 'CSS from URL:';
+$lang['en'][11] = 'Code Layout:';
+$lang['en'][12] = 'Compression&#160;(code&#160;layout):';
+$lang['en'][13] = 'Highest (no readability, smallest size)';
+$lang['en'][14] = 'High (moderate readability, smaller size)';
+$lang['en'][15] = 'Standard (balance between readability and size)';
+$lang['en'][16] = 'Low (higher readability)';
+$lang['en'][17] = 'Custom (enter below)';
+$lang['en'][18] = 'Custom <a href="http://csstidy.sourceforge.net/templates.php">template</a>';
+$lang['en'][19] = 'Options';
+$lang['en'][20] = 'Sort Selectors (caution)';
+$lang['en'][21] = 'Sort Properties';
+$lang['en'][22] = 'Regroup selectors';
+$lang['en'][23] = 'Optimise shorthands';
+$lang['en'][24] = 'Compress colors';
+$lang['en'][25] = 'Lowercase selectors';
+$lang['en'][26] = 'Case for properties:';
+$lang['en'][27] = 'Lowercase';
+$lang['en'][28] = 'No or invalid CSS input or wrong URL!';
+$lang['en'][29] = 'Uppercase';
+$lang['en'][30] = 'lowercase elementnames needed for XHTML';
+$lang['en'][31] = 'Remove unnecessary backslashes';
+$lang['en'][32] = 'convert !important-hack';
+$lang['en'][33] = 'Output as file';
+$lang['en'][34] = 'Bigger compression because of smaller newlines (copy &#38; paste doesn\'t work)';
+$lang['en'][35] = 'Process CSS';
+$lang['en'][36] = 'Compression Ratio';
+$lang['en'][37] = 'Input';
+$lang['en'][38] = 'Output';
+$lang['en'][39] = 'Language';
+$lang['en'][41] = 'Attention: This may change the behaviour of your CSS Code!';
+$lang['en'][42] = 'Remove last ;';
+$lang['en'][43] = 'Discard invalid properties';
+$lang['en'][44] = 'Only safe optimisations';
+$lang['en'][45] = 'Compress font-weight';
+$lang['en'][46] = 'Save comments';
+$lang['en'][47] = 'Do not change anything';
+$lang['en'][48] = 'Only seperate selectors (split at ,)';
+$lang['en'][49] = 'Merge selectors with the same properties (fast)';
+$lang['en'][50] = 'Merge selectors intelligently (slow)';
+$lang['en'][51] = 'Preserve CSS';
+$lang['en'][52] = 'Save comments, hacks, etc. Most optimisations can *not* be applied if this is enabled.';
+$lang['en'][53] = 'None';
+$lang['en'][54] = 'Don\'t optimise';
+$lang['en'][55] = 'Safe optimisations';
+$lang['en'][56] = 'All optimisations';
+$lang['en'][57] = 'Add timestamp';
+$lang['en'][58] = 'Copy to clipboard';
+$lang['en'][59] = 'Back to top';
+$lang['en'][60] = 'Your browser doesn\'t support copy to clipboard.';
+$lang['en'][61] = 'For bugs and suggestions feel free to';
+$lang['en'][62] = 'contact me';
+$lang['en'][63] = 'Output CSS code as complete HTML document';
+$lang['en'][64] = 'Code';
+$lang['en'][65] = 'CSS to style CSS output';
+$lang['en'][66] = 'You need to go to about:config in your URL bar, select \'signed.applets.codebase_principal_support\' in the filter field, and set its value to true in order to use this feature; however, be aware that doing so increases security risks.';
+
+
+$lang['de'][0] = 'CSS Formatierer und Optimierer (basierend auf CSSTidy ';
+$lang['de'][1] = 'CSS Formatierer und Optimierer';
+$lang['de'][2] = '(basierend auf';
+$lang['de'][3] = '(Textversion)';
+$lang['de'][4] = 'Wichtiger Hinweis:';
+$lang['de'][6] = 'Der CSS Code sollte wohlgeformt sein. Der CSS Code wird <strong>nicht auf Gültigkeit überprüft</strong>. Um sicherzugehen dass dein Code valide ist, benutze den <a href="http://jigsaw.w3.org/css-validator/">W3C Validierungsservice</a>.';
+$lang['de'][7] = 'alle Kommentare werden entfernt';
+$lang['de'][8] = 'CSS Eingabe:';
+$lang['de'][9] = 'CSS-Code:';
+$lang['de'][10] = 'CSS von URL:';
+$lang['de'][11] = 'Code Layout:';
+$lang['de'][12] = 'Komprimierung&#160;(Code&#160;Layout):';
+$lang['de'][13] = 'Höchste (keine Lesbarkeit, niedrigste Größe)';
+$lang['de'][14] = 'Hoch (mittelmäßige Lesbarkeit, geringe Größe)';
+$lang['de'][15] = 'Standard (Kompromiss zwischen Lesbarkeit und Größe)';
+$lang['de'][16] = 'Niedrig (höhere Lesbarkeit)';
+$lang['de'][17] = 'Benutzerdefiniert (unten eingeben)';
+$lang['de'][18] = 'Benutzerdefinierte <a href="http://csstidy.sourceforge.net/templates.php">Vorlage</a>';
+$lang['de'][19] = 'Optionen';
+$lang['de'][20] = 'Selektoren sortieren (Vorsicht)';
+$lang['de'][21] = 'Eigenschaften sortieren';
+$lang['de'][22] = 'Selektoren umgruppieren';
+$lang['de'][23] = 'Shorthands optimieren';
+$lang['de'][24] = 'Farben komprimieren';
+$lang['de'][25] = 'Selektoren in Kleinbuchstaben';
+$lang['de'][26] = 'Groß-/Kleinschreibung für Eigenschaften';
+$lang['de'][27] = 'Kleinbuchstaben';
+$lang['de'][28] = 'Keine oder ungültige CSS Eingabe oder falsche URL!';
+$lang['de'][29] = 'Großbuchstaben';
+$lang['de'][30] = 'kleingeschriebene Elementnamen benötigt für XHTML';
+$lang['de'][31] = 'Unnötige Backslashes entfernen';
+$lang['de'][32] = '!important-Hack konvertieren';
+$lang['de'][33] = 'Als Datei ausgeben';
+$lang['de'][34] = 'Größere Komprimierung augrund von kleineren Neuezeile-Zeichen';
+$lang['de'][35] = 'CSS verarbeiten';
+$lang['de'][36] = 'Komprimierungsrate';
+$lang['de'][37] = 'Eingabe';
+$lang['de'][38] = 'Ausgabe';
+$lang['de'][39] = 'Sprache';
+$lang['de'][41] = 'Achtung: Dies könnte das Verhalten ihres CSS-Codes verändern!';
+$lang['de'][42] = 'Letztes ; entfernen';
+$lang['de'][43] = 'Ungültige Eigenschaften entfernen';
+$lang['de'][44] = 'Nur sichere Optimierungen';
+$lang['de'][45] = 'font-weight komprimieren';
+$lang['de'][46] = 'Kommentare beibehalten';
+$lang['de'][47] = 'Nichts ändern';
+$lang['de'][48] = 'Selektoren nur trennen (am Komma)';
+$lang['de'][49] = 'Selektoren mit gleichen Eigenschaften zusammenfassen (schnell)';
+$lang['de'][50] = 'Selektoren intelligent zusammenfassen (langsam!)';
+$lang['de'][51] = 'CSS erhalten';
+$lang['de'][52] = 'Kommentare, Hacks, etc. speichern. Viele Optimierungen sind dann aber nicht mehr möglich.';
+$lang['de'][53] = 'Keine';
+$lang['de'][54] = 'Nicht optimieren';
+$lang['de'][55] = 'Sichere Optimierungen';
+$lang['de'][56] = 'Alle Optimierungen';
+$lang['de'][57] = 'Zeitstempel hinzufügen';
+$lang['de'][58] = 'Copy to clipboard';
+$lang['de'][59] = 'Back to top';
+$lang['de'][60] = 'Your browser doesn\'t support copy to clipboard.';
+$lang['de'][61] = 'For bugs and suggestions feel free to';
+$lang['de'][62] = 'contact me';
+$lang['de'][63] = 'Output CSS code as complete HTML document';
+$lang['de'][64] = 'Code';
+$lang['de'][65] = 'CSS to style CSS output';
+$lang['de'][66] = 'You need to go to about:config in your URL bar, select \'signed.applets.codebase_principal_support\' in the filter field, and set its value to true in order to use this feature; however, be aware that doing so increases security risks.';
+
+
+$lang['fr'][0] = 'CSS Formatteur et Optimiseur (basé sur CSSTidy ';
+$lang['fr'][1] = 'CSS Formatteur et Optimiseur';
+$lang['fr'][2] = '(basé sur ';
+$lang['fr'][3] = '(Version texte)';
+$lang['fr'][4] = 'Note Importante&#160;:';
+$lang['fr'][6] = 'Votre code doit être valide. Ce n’est <strong>pas un validateur</strong> qui signale les erreurs dans votre code CSS. Pour être sûr que votre code est correct, utilisez le validateur&#160;: <a href="http://jigsaw.w3.org/css-validator/">W3C Validator</a>.';
+$lang['fr'][7] = 'tous les commentaires sont enlevés';
+$lang['fr'][8] = 'Champ CSS&#160;:';
+$lang['fr'][9] = 'Code CSS&#160;:';
+$lang['fr'][10] = 'CSS en provenance d’une URL&#160;:<br />';
+$lang['fr'][11] = 'Mise en page du code&#160;:';
+$lang['fr'][12] = 'Compression (mise en page du code)&#160;:';
+$lang['fr'][13] = 'La plus élevée (aucune lisibilité, taille minimale)';
+$lang['fr'][14] = 'Élevée (lisibilité modérée, petite taille)';
+$lang['fr'][15] = 'Normale (équilibre entre lisibilité et taille)';
+$lang['fr'][16] = 'Faible (lisibilité élevée)';
+$lang['fr'][17] = 'Sur mesure (entrer ci-dessous)';
+$lang['fr'][18] = '<a href="http://csstidy.sourceforge.net/templates.php">Gabarit</a> sur mesure';
+$lang['fr'][19] = 'Options';
+$lang['fr'][20] = 'Trier les sélecteurs (attention)';
+$lang['fr'][21] = 'Trier les propriétés';
+$lang['fr'][22] = 'Regrouper les sélecteurs';
+$lang['fr'][23] = 'Propriétés raccourcies';
+$lang['fr'][24] = 'Compresser les couleurs';
+$lang['fr'][25] = 'Sélecteurs en minuscules';
+$lang['fr'][26] = 'Case pour les propriétés&#160;:';
+$lang['fr'][27] = 'Minuscule';
+$lang['fr'][28] = 'CSS non valide ou URL incorrecte&#160;!';
+$lang['fr'][29] = 'Majuscule';
+$lang['fr'][30] = 'les noms des éléments en minuscules (indispensables pour XHTML)';
+$lang['fr'][31] = 'enlever les antislashs inutiles';
+$lang['fr'][32] = 'convertir !important-hack';
+$lang['fr'][33] = 'Sauver en tant que fichier';
+$lang['fr'][34] = 'Meilleure compression grâce aux caractères de saut de ligne plus petits (copier &#38; coller ne marche pas)';
+$lang['fr'][35] = 'Compresser la CSS';
+$lang['fr'][36] = 'Facteur de Compression';
+$lang['fr'][37] = 'Entrée';
+$lang['fr'][38] = 'Sortie';
+$lang['fr'][39] = 'Langue';
+$lang['fr'][41] = 'Attention&#160;: ceci peut changer le comportement de votre code CSS&#160;!';
+$lang['fr'][42] = 'Enlever le dernier ;';
+$lang['fr'][43] = 'Supprimer les propriétés non valide';
+$lang['fr'][44] = 'Seulement les optimisations sûres';
+$lang['fr'][45] = 'Compresser font-weight';
+$lang['fr'][46] = 'Sauvegarder les commentaires ';
+$lang['fr'][47] = 'Ne rien changer';
+$lang['fr'][48] = 'Sépare les sélecteurs (sépare au niveau de ,)';
+$lang['fr'][49] = 'Fusionne les sélecteurs avec les mêmes propriétés (rapide)';
+$lang['fr'][50] = 'Fusionne les sélecteurs intelligemment (lent)';
+$lang['fr'][51] = 'Préserver la CSS';
+$lang['fr'][52] = 'Sauvegarder les commentaires, hacks, etc. La plupart des optimisations ne peuvent *pas* être appliquées si cela est activé.';
+$lang['fr'][53] = 'Aucun';
+$lang['fr'][54] = 'Ne pas optimiser';
+$lang['fr'][55] = 'Optimisations sûres';
+$lang['fr'][56] = 'Toutes les optimisations';
+$lang['fr'][57] = 'Ajouter un timestamp';
+$lang['fr'][58] = 'Copier dans le presse-papiers';
+$lang['fr'][59] = 'Retour en haut';
+$lang['fr'][60] = 'Votre navigateur ne suporte pas la copie vers le presse-papiers.';
+$lang['fr'][61] = 'Pour signaler des bugs ou pour des suggestions,';
+$lang['fr'][62] = 'contactez-moi';
+$lang['fr'][63] = 'Sauver le code CSS comme document complet HTML';
+$lang['fr'][64] = 'Code';
+$lang['fr'][65] = 'CSS pour colorier la sortie CSS';
+$lang['fr'][66] = 'Vous devez aller dans about:config dans votre barre d’adresse, selectionner \'signed.applets.codebase_principal_support\' dans le champ Filtre et attribuez-lui la valeur \'true\' pour utiliser cette fonctionnalité; toutefois, soyez conscient que cela augmente les risques de sécurité.';
+
+
+$lang['zh'][0] = 'CSS整形與最佳化工具(使用 CSSTidy ';
+$lang['zh'][1] = 'CSS整形與最佳化工具';
+$lang['zh'][2] = '(使用';
+$lang['zh'][3] = '(純文字)';
+$lang['zh'][4] = '重要事項:';
+$lang['zh'][6] = '你的原始碼必須是良構的(well-formed). 這個工具<strong>沒有內建驗證器(validator)</strong>. 驗證器能夠指出你CSS原始碼裡的錯誤. 請使用 <a href="http://jigsaw.w3.org/css-validator/">W3C 驗證器</a>, 確保你的原始碼合乎規範.';
+$lang['zh'][7] = '所有註解都移除了';
+$lang['zh'][8] = 'CSS 輸入:';
+$lang['zh'][9] = 'CSS 原始碼:';
+$lang['zh'][10] = 'CSS 檔案網址(URL):';
+$lang['zh'][11] = '原始碼規劃:';
+$lang['zh'][12] = '壓縮程度(原始碼規劃):';
+$lang['zh'][13] = '最高 (沒有辦法讀, 檔案最小)';
+$lang['zh'][14] = '高 (適度的可讀性, 檔案小)';
+$lang['zh'][15] = '標準 (兼顧可讀性與檔案大小)';
+$lang['zh'][16] = '低 (注重可讀性)';
+$lang['zh'][17] = '自訂 (在下方設定)';
+$lang['zh'][18] = '自訂<a href="http://csstidy.sourceforge.net/templates.php">樣板</a>';
+$lang['zh'][19] = '選項';
+$lang['zh'][20] = '整理選擇符(請謹慎使用)';
+$lang['zh'][21] = '整理屬性';
+$lang['zh'][22] = '重組選擇符';
+$lang['zh'][23] = '速記法(shorthand)最佳化';
+$lang['zh'][24] = '壓縮色彩語法';
+$lang['zh'][25] = '改用小寫選擇符';
+$lang['zh'][26] = '屬性的字形:';
+$lang['zh'][27] = '小寫';
+$lang['zh'][28] = '沒有輸入CSS, 語法不符合規定, 或是網址錯誤!';
+$lang['zh'][29] = '大寫';
+$lang['zh'][30] = 'XHTML必須使用小寫的元素名稱';
+$lang['zh'][31] = '移除不必要的反斜線';
+$lang['zh'][32] = '轉換 !important-hack';
+$lang['zh'][33] = '輸出成檔案形式';
+$lang['zh'][34] = '由於比較少換行字元, 會有更大的壓縮比率(複製&#38;貼上沒有用)';
+$lang['zh'][35] = '執行';
+$lang['zh'][36] = '壓縮比率';
+$lang['zh'][37] = '輸入';
+$lang['zh'][38] = '輸出';
+$lang['zh'][39] = '語言';
+$lang['zh'][41] = '注意: 這或許會變更你CSS原始碼的行為!';
+$lang['zh'][42] = '除去最後一個分號';
+$lang['zh'][43] = '拋棄不符合規定的屬性';
+$lang['zh'][44] = '只安全地最佳化';
+$lang['zh'][45] = '壓縮 font-weight';
+$lang['zh'][46] = '保留註解';
+$lang['zh'][47] = '什麼都不要改';
+$lang['zh'][48] = '只分開原本用逗號分隔的選擇符';
+$lang['zh'][49] = '合併有相同屬性的選擇符(快速)';
+$lang['zh'][50] = '聰明地合併選擇符(慢速)';
+$lang['zh'][51] = '保護CSS';
+$lang['zh'][52] = '保留註解與 hack 等等. 如果啟用這個選項, 大多數的最佳化程序都不會執行.';
+$lang['zh'][53] = '不改變';
+$lang['zh'][54] = '不做最佳化';
+$lang['zh'][55] = '安全地最佳化';
+$lang['zh'][56] = '全部最佳化';
+$lang['zh'][57] = '加上時間戳記';
+$lang['zh'][58] = '复制到剪贴板';
+$lang['zh'][59] = '回到页面上方';
+$lang['zh'][60] = '你的浏览器不支持复制到剪贴板。';
+$lang['zh'][61] = '如果程序有错误或你有建议,欢迎';
+$lang['zh'][62] = '和我联系';
+$lang['zh'][63] = 'Output CSS code as complete HTML document';
+$lang['zh'][64] = '代码';
+$lang['zh'][65] = 'CSS to style CSS output';
+$lang['zh'][66] = 'You need to go to about:config in your URL bar, select \'signed.applets.codebase_principal_support\' in the filter field, and set its value to true in order to use this feature; however, be aware that doing so increases security risks.';
diff --git a/plugins/jetpack/modules/custom-css/csstidy/wordpress-standard.tpl b/plugins/jetpack/modules/custom-css/csstidy/wordpress-standard.tpl
new file mode 100644
index 00000000..9499e839
--- /dev/null
+++ b/plugins/jetpack/modules/custom-css/csstidy/wordpress-standard.tpl
@@ -0,0 +1,10 @@
+| {
+|| {
+| | |;
+|}|
+
+|
+}
+
+| ||
+|
diff --git a/plugins/jetpack/modules/custom-css/custom-css.php b/plugins/jetpack/modules/custom-css/custom-css.php
new file mode 100644
index 00000000..9bc0c211
--- /dev/null
+++ b/plugins/jetpack/modules/custom-css/custom-css.php
@@ -0,0 +1,1468 @@
+<?php
+
+class Jetpack_Custom_CSS {
+ static function init() {
+ add_action( 'switch_theme', array( __CLASS__, 'reset' ) );
+ add_action( 'wp_restore_post_revision', array( __CLASS__, 'restore_revision' ), 10, 2 );
+
+ // Save revisions for posts of type safecss.
+ add_filter( 'revision_redirect', array( __CLASS__, 'revision_redirect' ) );
+
+ // Override the edit link, the default link causes a redirect loop
+ add_filter( 'get_edit_post_link', array( __CLASS__, 'revision_post_link' ), 10, 3 );
+
+ if ( ! is_admin() )
+ add_filter( 'stylesheet_uri', array( __CLASS__, 'style_filter' ) );
+
+ define( 'SAFECSS_USE_ACE', ! jetpack_is_mobile() && ! Jetpack_User_Agent_Info::is_ipad() && apply_filters( 'safecss_use_ace', true ) );
+
+ // Register safecss as a custom post_type
+ // Explicit capability definitions are largely unnecessary because the posts are manipulated in code via an options page, managing CSS revisions does check the capabilities, so let's ensure that the proper caps are checked.
+ register_post_type( 'safecss', array(
+ // These are the defaults
+ // 'exclude_from_search' => true,
+ // 'public' => false,
+ // 'publicly_queryable' => false,
+ // 'show_ui' => false,
+ 'supports' => array( 'revisions' ),
+ 'label' => 'Custom CSS',
+ 'can_export' => false,
+ 'rewrite' => false,
+ 'capabilities' => array(
+ 'edit_post' => 'edit_theme_options',
+ 'read_post' => 'read',
+ 'delete_post' => 'edit_theme_options',
+ 'edit_posts' => 'edit_theme_options',
+ 'edit_others_posts' => 'edit_theme_options',
+ 'publish_posts' => 'edit_theme_options',
+ 'read_private_posts' => 'read'
+ )
+ ) );
+
+ // Short-circuit WP if this is a CSS stylesheet request
+ if ( isset( $_GET['custom-css'] ) ) {
+ header( 'Content-Type: text/css', true, 200 );
+ header( 'Expires: ' . gmdate( 'D, d M Y H:i:s', time() + 31536000) . ' GMT' ); // 1 year
+ Jetpack_Custom_CSS::print_css();
+ exit;
+ }
+
+ if ( isset( $_GET['page'] ) && 'editcss' == $_GET['page'] && is_admin() ) {
+ // Do migration routine if necessary
+ Jetpack_Custom_CSS::upgrade();
+
+ do_action( 'safecss_migrate_post' );
+ }
+
+ add_action( 'wp_head', array( 'Jetpack_Custom_CSS', 'link_tag' ), 101 );
+
+ if ( !current_user_can( 'switch_themes' ) && !is_super_admin() )
+ return;
+
+ add_action( 'admin_menu', array( 'Jetpack_Custom_CSS', 'menu' ) );
+
+ if ( isset( $_POST['safecss'] ) && false == strstr( $_SERVER[ 'REQUEST_URI' ], 'options.php' ) ) {
+ check_admin_referer( 'safecss' );
+
+ $save_result = self::save( array(
+ 'css' => $_POST['safecss'],
+ 'is_preview' => isset( $_POST['action'] ) && $_POST['action'] == 'preview',
+ 'preprocessor' => isset( $_POST['custom_css_preprocessor'] ) ? $_POST['custom_css_preprocessor'] : '',
+ 'add_to_existing' => isset( $_POST['add_to_existing'] ) ? $_POST['add_to_existing'] == 'true' : true,
+ 'content_width' => isset( $_POST['custom_content_width'] ) ? $_POST['custom_content_width'] : false,
+ ) );
+
+ if ( $_POST['action'] == 'preview' ) {
+ wp_safe_redirect( add_query_arg( 'csspreview', 'true', get_option( 'home' ) ) );
+ exit;
+ }
+
+ if ( $save_result )
+ add_action( 'admin_notices', array( 'Jetpack_Custom_CSS', 'saved_message' ) );
+ }
+
+ // Modify all internal links so that preview state persists
+ if ( Jetpack_Custom_CSS::is_preview() )
+ ob_start( array( 'Jetpack_Custom_CSS', 'buffer' ) );
+ }
+
+ /**
+ * Save new custom CSS. This should be the entry point for any third-party code using Jetpack_Custom_CSS
+ * to save CSS.
+ *
+ * @param array $args Array of arguments:
+ * string $css The CSS (or LESS or Sass)
+ * bool $is_preview Whether this CSS is preview or published
+ * bool $add_to_existing Whether this CSS replaces the theme's CSS or supplements it.
+ * int $content_width A custom $content_width to go along with this CSS.
+ * @return int The post ID of the saved Custom CSS post.
+ */
+ public static function save( $args = array() ) {
+ $defaults = array(
+ 'css' => '',
+ 'is_preview' => false,
+ 'preprocessor' => '',
+ 'add_to_existing' => true,
+ 'content_width' => false,
+ );
+
+ $args = wp_parse_args( $args, $defaults );
+
+ // Remove wp_filter_post_kses, this causes CSS escaping issues
+ remove_filter( 'content_save_pre', 'wp_filter_post_kses' );
+ remove_filter( 'content_filtered_save_pre', 'wp_filter_post_kses' );
+ remove_all_filters( 'content_save_pre' );
+
+ do_action( 'safecss_save_pre', $args );
+
+ $warnings = array();
+
+ safecss_class();
+ $csstidy = new csstidy();
+ $csstidy->optimise = new safecss( $csstidy );
+
+ $csstidy->set_cfg( 'remove_bslash', false );
+ $csstidy->set_cfg( 'compress_colors', false );
+ $csstidy->set_cfg( 'compress_font-weight', false );
+ $csstidy->set_cfg( 'optimise_shorthands', 0 );
+ $csstidy->set_cfg( 'remove_last_;', false );
+ $csstidy->set_cfg( 'case_properties', false );
+ $csstidy->set_cfg( 'discard_invalid_properties', true );
+ $csstidy->set_cfg( 'css_level', 'CSS3.0' );
+ $csstidy->set_cfg( 'preserve_css', true );
+ $csstidy->set_cfg( 'template', dirname( __FILE__ ) . '/csstidy/wordpress-standard.tpl' );
+
+ $css = $orig = stripslashes( $args['css'] );
+
+ $css = preg_replace( '/\\\\([0-9a-fA-F]{4})/', '\\\\\\\\$1', $prev = $css );
+
+ if ( $css != $prev )
+ $warnings[] = 'preg_replace found stuff';
+
+ // Some people put weird stuff in their CSS, KSES tends to be greedy
+ $css = str_replace( '<=', '&lt;=', $css );
+ // Why KSES instead of strip_tags? Who knows?
+ $css = wp_kses_split( $prev = $css, array(), array() );
+ $css = str_replace( '&gt;', '>', $css ); // kses replaces lone '>' with &gt;
+ // Why both KSES and strip_tags? Because we just added some '>'.
+ $css = strip_tags( $css );
+
+ if ( $css != $prev )
+ $warnings[] = 'kses found stuff';
+
+ // if we're not using a preprocessor
+ if ( ! $args['preprocessor'] ) {
+ do_action( 'safecss_parse_pre', $csstidy, $css, $args );
+
+ $csstidy->parse( $css );
+
+ do_action( 'safecss_parse_post', $csstidy, $warnings, $args );
+
+ $css = $csstidy->print->plain();
+ }
+
+ if ( $args['content_width'] && intval( $args['content_width']) > 0 && ( ! isset( $GLOBALS['content_width'] ) || $args['content_width'] != $GLOBALS['content_width'] ) )
+ $custom_content_width = intval( $args['content_width'] );
+ else
+ $custom_content_width = false;
+
+ if ( $args['add_to_existing'] )
+ $add_to_existing = 'yes';
+ else
+ $add_to_existing = 'no';
+
+ if ( $args['is_preview'] || Jetpack_Custom_CSS::is_freetrial() ) {
+ // Save the CSS
+ $safecss_revision_id = Jetpack_Custom_CSS::save_revision( $css, true, $args['preprocessor'] );
+
+ // Cache Buster
+ update_option( 'safecss_preview_rev', intval( get_option( 'safecss_preview_rev' ) ) + 1);
+
+ update_metadata( 'post', $safecss_revision_id, 'custom_css_add', $add_to_existing );
+ update_metadata( 'post', $safecss_revision_id, 'content_width', $custom_content_width );
+ update_metadata( 'post', $safecss_revision_id, 'custom_css_preprocessor', $args['preprocessor'] );
+
+ if ( $args['is_preview'] ) {
+ return $safecss_revision_id;
+ }
+
+ // Freetrial only.
+ do_action( 'safecss_save_preview_post' );
+ }
+
+ // Save the CSS
+ $safecss_post_id = Jetpack_Custom_CSS::save_revision( $css, false, $args['preprocessor'] );
+
+ $safecss_post_revision = Jetpack_Custom_CSS::get_current_revision();
+
+ update_option( 'safecss_rev', intval( get_option( 'safecss_rev' ) ) + 1 );
+
+ update_post_meta( $safecss_post_id, 'custom_css_add', $add_to_existing );
+ update_post_meta( $safecss_post_id, 'content_width', $custom_content_width );
+ update_post_meta( $safecss_post_id, 'custom_css_preprocessor', $args['preprocessor'] );
+ update_metadata( 'post', $safecss_post_revision['ID'], 'custom_css_add', $add_to_existing );
+ update_metadata( 'post', $safecss_post_revision['ID'], 'content_width', $custom_content_width );
+ update_metadata( 'post', $safecss_post_revision['ID'], 'custom_css_preprocessor', $args['preprocessor'] );
+
+ return $safecss_post_id;
+ }
+
+ /**
+ * Get the published custom CSS post.
+ *
+ * @return array
+ */
+ static function get_post() {
+ $custom_css_post_id = Jetpack_Custom_CSS::post_id();
+
+ if ( $custom_css_post_id )
+ return get_post( $custom_css_post_id, ARRAY_A );
+
+ return array();
+ }
+
+ /**
+ * Get the post ID of the published custom CSS post.
+ *
+ * @return int|bool The post ID if it exists; false otherwise.
+ */
+ static function post_id() {
+ $custom_css_post_id = wp_cache_get( 'custom_css_post_id' );
+
+ if ( false === $custom_css_post_id ) {
+ $custom_css_post = array_shift( get_posts( array(
+ 'posts_per_page' => 1,
+ 'post_type' => 'safecss',
+ 'post_status' => 'publish',
+ 'orderby' => 'date',
+ 'order' => 'DESC'
+ ) ) );
+
+ if ( $custom_css_post )
+ $custom_css_post_id = $custom_css_post->ID;
+ else
+ $custom_css_post_id = 0;
+
+ // Save post_id=0 to note that no safecss post exists.
+ wp_cache_set( 'custom_css_post_id', $custom_css_post_id );
+ }
+
+ if ( ! $custom_css_post_id )
+ return false;
+
+ return $custom_css_post_id;
+ }
+
+ /**
+ * Get the current revision of the original safecss record
+ *
+ * @return object
+ */
+ static function get_current_revision() {
+ $safecss_post = Jetpack_Custom_CSS::get_post();
+
+ if ( empty( $safecss_post ) ) {
+ return false;
+ }
+
+ $revisions = wp_get_post_revisions( $safecss_post['ID'], array( 'posts_per_page' => 1, 'orderby' => 'date', 'order' => 'DESC' ) );
+
+ // Empty array if no revisions exist
+ if ( empty( $revisions ) ) {
+ // Return original post
+ return $safecss_post;
+ } else {
+ // Return the first entry in $revisions, this will be the current revision
+ $current_revision = get_object_vars( array_shift( $revisions ) );
+ return $current_revision;
+ }
+ }
+
+ /**
+ * Save new revision of CSS
+ * Checks to see if content was modified before really saving
+ *
+ * @param string $css
+ * @param bool $is_preview
+ * @return bool|int If nothing was saved, returns false. If a post
+ * or revision was saved, returns the post ID.
+ */
+ static function save_revision( $css, $is_preview = false, $preprocessor = '' ) {
+ $safecss_post = Jetpack_Custom_CSS::get_post();
+
+ $compressed_css = Jetpack_Custom_CSS::minify( $css, $preprocessor );
+
+ // If null, there was no original safecss record, so create one
+ if ( null == $safecss_post ) {
+ if ( ! $css )
+ return false;
+
+ $post = array();
+ $post['post_content'] = $css;
+ $post['post_title'] = 'safecss';
+ $post['post_status'] = 'publish';
+ $post['post_type'] = 'safecss';
+ $post['post_content_filtered'] = $compressed_css;
+
+ // Set excerpt to current theme, for display in revisions list
+ if ( function_exists( 'wp_get_theme' ) ) {
+ $current_theme = wp_get_theme();
+ $post['post_excerpt'] = $current_theme->Name;
+ }
+ else {
+ $post['post_excerpt'] = get_current_theme();
+ }
+
+ // Insert the CSS into wp_posts
+ $post_id = wp_insert_post( $post );
+ wp_cache_set( 'custom_css_post_id', $post_id );
+ return $post_id;
+ }
+
+ // Update CSS in post array with new value passed to this function
+ $safecss_post['post_content'] = $css;
+ $safecss_post['post_content_filtered'] = $compressed_css;
+
+ // Set excerpt to current theme, for display in revisions list
+ if ( function_exists( 'wp_get_theme' ) ) {
+ $current_theme = wp_get_theme();
+ $safecss_post['post_excerpt'] = $current_theme->Name;
+ }
+ else {
+ $safecss_post['post_excerpt'] = get_current_theme();
+ }
+
+ // Don't carry over last revision's timestamps, otherwise revisions all have matching timestamps
+ unset( $safecss_post['post_date'] );
+ unset( $safecss_post['post_date_gmt'] );
+ unset( $safecss_post['post_modified'] );
+ unset( $safecss_post['post_modified_gmt'] );
+
+ // Do not update post if we are only saving a preview
+ if ( false === $is_preview ) {
+ $post_id = wp_update_post( $safecss_post );
+ wp_cache_set( 'custom_css_post_id', $post_id );
+ return $post_id;
+ }
+ else if ( ! defined( 'DOING_MIGRATE' ) ) {
+ return _wp_put_post_revision( $safecss_post );
+ }
+ }
+
+ static function skip_stylesheet() {
+ if ( Jetpack_Custom_CSS::is_customizer_preview() ) {
+ return false;
+ }
+ else {
+ if ( Jetpack_Custom_CSS::is_preview() ) {
+ $safecss_post = Jetpack_Custom_CSS::get_current_revision();
+
+ return (bool) ( get_option( 'safecss_preview_add' ) == 'no' || get_post_meta( $safecss_post['ID'], 'custom_css_add', true ) == 'no' );
+ }
+ else {
+ $custom_css_post_id = Jetpack_Custom_CSS::post_id();
+
+ return (bool) ( get_option( 'safecss_add' ) == 'no' || ( $custom_css_post_id && get_post_meta( $custom_css_post_id, 'custom_css_add', true ) == 'no' ) );
+ }
+ }
+ }
+
+ static function is_preview() {
+ return isset( $_GET['csspreview'] ) && $_GET['csspreview'] === 'true';
+ }
+
+ /*
+ * False when the site has the Custom Design upgrade.
+ * Used only on WordPress.com.
+ */
+ static function is_freetrial() {
+ return apply_filters( 'safecss_is_freetrial', false );
+ }
+
+ static function get_css( $compressed = false ) {
+ $default_css = apply_filters( 'safecss_get_css_error', false );
+
+ if ( $default_css !== false )
+ return $default_css;
+
+ $option = ( Jetpack_Custom_CSS::is_preview() || Jetpack_Custom_CSS::is_freetrial() ) ? 'safecss_preview' : 'safecss';
+
+ if ( 'safecss' == $option ) {
+ if ( get_option( 'safecss_revision_migrated' ) ) {
+ $safecss_post = Jetpack_Custom_CSS::get_post();
+ $css = ( $compressed && $safecss_post['post_content_filtered'] ) ? $safecss_post['post_content_filtered'] : $safecss_post['post_content'];
+ } else {
+ $current_revision = Jetpack_Custom_CSS::get_current_revision();
+ if ( false === $current_revision ) {
+ $css = '';
+ } else {
+ $css = ( $compressed && $current_revision['post_content_filtered'] ) ? $current_revision['post_content_filtered'] : $current_revision['post_content'];
+ }
+ }
+
+ // Fix for un-migrated Custom CSS
+ if ( empty( $safecss_post ) ) {
+ $_css = get_option( 'safecss' );
+ if ( !empty( $_css ) ) {
+ $css = $_css;
+ }
+ }
+ }
+ else if ( 'safecss_preview' == $option ) {
+ $safecss_post = Jetpack_Custom_CSS::get_current_revision();
+ $css = $safecss_post['post_content'];
+ $css = stripslashes( $css );
+ $css = Jetpack_Custom_CSS::minify( $css, get_post_meta( $safecss_post['ID'], 'custom_css_preprocessor', true ) );
+ }
+
+ $css = str_replace( array( '\\\00BB \\\0020', '\0BB \020', '0BB 020' ), '\00BB \0020', $css );
+
+ if ( empty( $css ) ) {
+ $css = "/*\n"
+ . wordwrap(
+ apply_filters(
+ 'safecss_default_css',
+ __(
+ "Welcome to Custom CSS!\n\nCSS (Cascading Style Sheets) is a kind of code that tells the browser how to render a web page. You may delete these comments and get started with your customizations.\n\nBy default, your stylesheet will be loaded after the theme stylesheets, which means that your rules can take precedence and override the theme CSS rules. Just write here what you want to change, you don't need to copy all your theme's stylesheet content.",
+ 'jetpack'
+ )
+ )
+ )
+ . "\n*/";
+ }
+
+ $css = apply_filters( 'safecss_css', $css );
+
+ return $css;
+ }
+
+ static function print_css() {
+ do_action( 'safecss_print_pre' );
+
+ echo Jetpack_Custom_CSS::get_css( true );
+ }
+
+ static function link_tag() {
+ global $blog_id, $current_blog;
+
+ if ( apply_filters( 'safecss_style_error', false ) )
+ return;
+
+ if ( ! is_super_admin() && isset( $current_blog ) && ( 1 == $current_blog->spam || 1 == $current_blog->deleted ) )
+ return;
+
+ if ( Jetpack_Custom_CSS::is_customizer_preview() )
+ return;
+
+ $css = '';
+ $option = Jetpack_Custom_CSS::is_preview() ? 'safecss_preview' : 'safecss';
+
+ if ( 'safecss' == $option ) {
+ if ( get_option( 'safecss_revision_migrated' ) ) {
+ $safecss_post = Jetpack_Custom_CSS::get_post();
+
+ if ( ! empty( $safecss_post['post_content'] ) ) {
+ $css = $safecss_post['post_content'];
+ }
+ } else {
+ $current_revision = Jetpack_Custom_CSS::get_current_revision();
+
+ if ( ! empty( $current_revision['post_content'] ) ) {
+ $css = $current_revision['post_content'];
+ }
+ }
+
+ // Fix for un-migrated Custom CSS
+ if ( empty( $safecss_post ) ) {
+ $_css = get_option( 'safecss' );
+ if ( !empty( $_css ) ) {
+ $css = $_css;
+ }
+ }
+ }
+
+ if ( 'safecss_preview' == $option ) {
+ $safecss_post = Jetpack_Custom_CSS::get_current_revision();
+
+ if ( !empty( $safecss_post['post_content'] ) ) {
+ $css = $safecss_post['post_content'];
+ }
+ }
+
+ $css = str_replace( array( '\\\00BB \\\0020', '\0BB \020', '0BB 020' ), '\00BB \0020', $css );
+
+ if ( $css == '' )
+ return;
+
+ $href = trailingslashit( site_url() );
+ $href = add_query_arg( 'custom-css', 1, $href );
+ $href = add_query_arg( 'csblog', $blog_id, $href );
+ $href = add_query_arg( 'cscache', 6, $href );
+ $href = add_query_arg( 'csrev', (int) get_option( $option . '_rev' ), $href );
+
+ $href = apply_filters( 'safecss_href', $href, $blog_id );
+
+ if ( Jetpack_Custom_CSS::is_preview() )
+ $href = add_query_arg( 'csspreview', 'true', $href );
+
+ ?>
+ <link rel="stylesheet" id="custom-css-css" type="text/css" href="<?php echo esc_url( $href ); ?>" />
+ <?php
+ }
+
+ static function style_filter( $current ) {
+ if ( Jetpack_Custom_CSS::is_freetrial() && ( ! Jetpack_Custom_CSS::is_preview() || ! current_user_can( 'switch_themes' ) ) )
+ return $current;
+ else if ( Jetpack_Custom_CSS::skip_stylesheet() )
+ return apply_filters( 'safecss_style_filter_url', plugins_url( 'custom-css/blank.css', __FILE__ ) );
+
+ return $current;
+ }
+
+ static function buffer( $html ) {
+ $html = str_replace( '</body>', Jetpack_Custom_CSS::preview_flag(), $html );
+ return preg_replace_callback( '!href=([\'"])(.*?)\\1!', array( 'Jetpack_Custom_CSS', 'preview_links' ), $html );
+ }
+
+ static function preview_links( $matches ) {
+ if ( 0 !== strpos( $matches[2], get_option( 'home' ) ) )
+ return $matches[0];
+
+ $link = wp_specialchars_decode( $matches[2] );
+ $link = add_query_arg( 'csspreview', 'true', $link );
+ $link = esc_url( $link );
+ return "href={$matches[1]}$link{$matches[1]}";
+ }
+
+ /**
+ * Places a black bar above every preview page
+ */
+ static function preview_flag() {
+ if ( is_admin() )
+ return;
+
+ $message = esc_html__( 'Preview: changes must be saved or they will be lost', 'jetpack' );
+ $message = apply_filters( 'safecss_preview_message', $message );
+
+ $preview_flag_js = "var flag = document.createElement('div');
+ flag.innerHTML = " . json_encode( $message ) . ";
+ flag.style.background = 'black';
+ flag.style.color = 'white';
+ flag.style.textAlign = 'center';
+ flag.style.fontSize = '15px';
+ flag.style.padding = '1px';
+ document.body.style.paddingTop = '32px';
+ document.body.insertBefore(flag, document.body.childNodes[0]);
+ ";
+
+ $preview_flag_js = apply_filters( 'safecss_preview_flag_js', $preview_flag_js );
+ if ( $preview_flag_js ) {
+ $preview_flag_js = '<script type="text/javascript">
+ // <![CDATA[
+ ' . $preview_flag_js . '
+ // ]]>
+ </script>';
+ }
+
+ return $preview_flag_js;
+ }
+
+ static function menu() {
+ $parent = 'themes.php';
+ $title = __( 'Edit CSS', 'jetpack' );
+ $hook = add_theme_page( $title, $title, 'edit_theme_options', 'editcss', array( 'Jetpack_Custom_CSS', 'admin' ) );
+ add_action( "admin_print_scripts-$hook", array( 'Jetpack_Custom_CSS', 'enqueue_scripts' ) );
+ add_action( "admin_head-$hook", array( 'Jetpack_Custom_CSS', 'admin_head' ) );
+ add_action( "load-revision.php", array( 'Jetpack_Custom_CSS', 'prettify_post_revisions' ) );
+ add_action( "load-$hook", array( 'Jetpack_Custom_CSS', 'update_title' ) );
+ }
+
+ /**
+ * Adds a menu item in the appearance section for this plugin's administration
+ * page. Also adds hooks to enqueue the CSS and JS for the admin page.
+ */
+ static function update_title() {
+ global $title;
+ $title = __( 'CSS', 'jetpack' );
+ }
+
+ static function prettify_post_revisions() {
+ add_filter( 'the_title', array( 'Jetpack_Custom_CSS', 'post_title' ), 10, 2 );
+ add_action( 'admin_head', array( 'Jetpack_Custom_CSS', 'remove_title_excerpt_from_revisions' ) );
+ }
+
+ static function remove_title_excerpt_from_revisions() {
+ global $post;
+
+ if ( !$post ) {
+ return;
+ }
+
+ if ( 'safecss' != $post->post_type ) {
+ return;
+ }
+ ?>
+ <style type="text/css">
+ #revision-field-post_title, #revision-field-post_excerpt {
+ display: none;
+ }
+ </style>
+ <?php
+ }
+
+ static function post_title( $title, $post_id ) {
+ if ( !$post_id = (int) $post_id ) {
+ return $title;
+ }
+
+ if ( !$post = get_post( $post_id ) ) {
+ return $title;
+ }
+
+ if ( 'safecss' != $post->post_type ) {
+ return $title;
+ }
+
+ return __( 'Custom CSS Stylesheet', 'jetpack' );
+ }
+
+ static function enqueue_scripts() {
+ wp_enqueue_script( 'postbox' );
+
+ if ( defined( 'SAFECSS_USE_ACE' ) && SAFECSS_USE_ACE ) {
+ $url = plugins_url( 'custom-css/js/', __FILE__ );
+ wp_enqueue_script( 'jquery.spin' );
+ wp_enqueue_script( 'safecss-ace', $url . 'ace/ace.js', array(), '20130213', true );
+ wp_enqueue_script( 'safecss-ace-css', $url . 'ace/mode-css.js', array( 'safecss-ace' ), '20130213', true );
+ wp_enqueue_script( 'safecss-ace-use', $url . 'safecss-ace.js', array( 'jquery', 'safecss-ace-css' ), '20130213', true );
+ }
+ }
+
+ static function admin_head() {
+ ?>
+ <style type="text/css">
+ #safecssform {
+ position: relative;
+ }
+
+ #poststuff {
+ padding-top: 0;
+ }
+
+ #safecss {
+ min-height: 250px;
+ width: 100%;
+ }
+
+ .misc-pub-section > span {
+ font-weight: bold;
+ }
+
+ .misc-pub-section > div {
+ margin-top: 3px;
+ }
+
+ <?php
+
+ if ( defined( 'SAFECSS_USE_ACE' ) && SAFECSS_USE_ACE ) {
+
+ ?>
+ #safecss-container {
+ position: relative;
+ width: 99.5%;
+ height: 400px;
+ border: 1px solid #dfdfdf;
+ border-radius: 3px;
+ }
+
+ #safecss-container .ace_editor {
+ font-family: Consolas, Monaco, Courier, monospace;
+ }
+
+ #safecss-ace {
+ width: 100%;
+ height: 100%;
+ display: none; /* Hide on load otherwise it looks weird */
+ }
+
+ #safecss-ace.ace_editor {
+ display: block;
+ }
+
+ #safecss-container .ace-tm .ace_gutter {
+ background-color: #ededed;
+ }
+ <?php
+ }
+
+ ?>
+ </style>
+ <script type="text/javascript">
+ /*<![CDATA[*/
+ var safecssResize, safecssInit;
+
+ <?php
+
+ if ( defined( 'SAFECSS_USE_ACE' ) && SAFECSS_USE_ACE ) {
+ ?>var safecssAceSrcPath = <?php echo json_encode( parse_url( plugins_url( 'custom-css/js/ace/', __FILE__ ), PHP_URL_PATH ) ); ?>;<?php
+ }
+
+ ?>
+
+ ( function ( $ ) {
+ var safe, win;
+
+ safecssResize = function () {
+ safe.height( win.height() - safe.offset().top - 250 );
+ };
+
+ safecssInit = function() {
+ safe = $('#safecss');
+ win = $(window);
+
+ postboxes.add_postbox_toggles('editcss');
+ safecssResize();
+ var button = document.getElementById( 'preview' );
+ button.onclick = function ( event ) {
+ <?php
+
+ // hack for now for previewing.
+ // TODO: move all of this JS into its own file.
+ if ( defined( 'SAFECSS_USE_ACE' ) && SAFECSS_USE_ACE ) { echo "\t\taceSyncCSS();\n"; }
+
+ ?>
+ document.forms["safecssform"].target = "csspreview";
+ document.forms["safecssform"].action.value = 'preview';
+ document.forms["safecssform"].submit();
+ document.forms["safecssform"].target = "";
+ document.forms["safecssform"].action.value = 'save';
+
+ event = event || window.event;
+
+ if ( event.preventDefault )
+ event.preventDefault();
+
+ return false;
+ }
+ };
+
+ window.onresize = safecssResize;
+ addLoadEvent( safecssInit );
+ } )( jQuery );
+
+ jQuery( function ( $ ) {
+ $( '.edit-preprocessor' ).bind( 'click', function ( e ) {
+ e.preventDefault();
+
+ $( '#preprocessor-select' ).slideDown();
+ $( this ).hide();
+ } );
+
+ $( '.cancel-preprocessor' ).bind( 'click', function ( e ) {
+ e.preventDefault();
+
+ $( '#preprocessor-select' ).slideUp( function () {
+ $( '.edit-preprocessor' ).show();
+ $( '#preprocessor_choices' ).val( $( '#custom_css_preprocessor' ).val() );
+ } );
+ } );
+
+ $( '.save-preprocessor' ).bind( 'click', function ( e ) {
+ e.preventDefault();
+
+ $( '#preprocessor-select' ).slideUp();
+ $( '#preprocessor-display' ).text( $( '#preprocessor_choices option:selected' ).text() );
+ $( '#custom_css_preprocessor' ).val( $( '#preprocessor_choices' ).val() );
+ $( '.edit-preprocessor' ).show();
+ } );
+ } );
+ /*]]>*/
+ </script>
+ <?php
+ }
+
+ static function saved_message() {
+ echo '<div id="message" class="updated fade"><p><strong>' . __( 'Stylesheet saved.', 'jetpack' ) . '</strong></p></div>';
+ }
+
+ static function admin() {
+ add_meta_box( 'submitdiv', __( 'Publish', 'jetpack' ), array( __CLASS__, 'publish_box' ), 'editcss', 'side' );
+
+ $safecss_post = Jetpack_Custom_CSS::get_post();
+
+ if ( ! empty( $safecss_post ) && 0 < $safecss_post['ID'] && wp_get_post_revisions( $safecss_post['ID'] ) )
+ add_meta_box( 'revisionsdiv', __( 'CSS Revisions', 'jetpack' ), array( __CLASS__, 'revisions_meta_box' ), 'editcss', 'side' );
+
+ ?>
+ <div class="wrap columns-2">
+ <?php do_action( 'custom_design_header' ); ?>
+ <h2><?php _e( 'CSS Stylesheet Editor', 'jetpack' ); ?></h2>
+ <form id="safecssform" action="" method="post">
+ <?php wp_nonce_field( 'safecss' ) ?>
+ <?php wp_nonce_field( 'meta-box-order', 'meta-box-order-nonce', false ); ?>
+ <?php wp_nonce_field( 'closedpostboxes', 'closedpostboxesnonce', false ); ?>
+ <input type="hidden" name="action" value="save" />
+ <div id="poststuff" class="metabox-holder has-right-sidebar">
+ <p class="css-support"><?php echo apply_filters( 'safecss_intro_text', __( 'New to CSS? Start with a <a href="http://www.htmldog.com/guides/cssbeginner/">beginner tutorial</a>. Questions?
+ Ask in the <a href="http://wordpress.org/support/forum/themes-and-templates">Themes and Templates forum</a>.', 'jetpack' ) ); ?></p>
+ <div id="postbox-container-1" class="inner-sidebar">
+ <?php do_meta_boxes( 'editcss', 'side', $safecss_post ); ?>
+ </div>
+ <div id="post-body">
+ <div id="post-body-content">
+ <div class="postarea">
+ <?php if ( defined( 'SAFECSS_USE_ACE' ) && SAFECSS_USE_ACE ) { ?>
+ <div id="safecss-container">
+ <div id="safecss-ace"></div>
+ </div>
+ <script type="text/javascript">
+ jQuery.fn.spin && jQuery("#safecss-container").spin( 'large' );
+ </script>
+ <textarea id="safecss" name="safecss" class="hide-if-js"><?php echo esc_textarea( Jetpack_Custom_CSS::get_css() ); ?></textarea>
+ <div class="clear"></div>
+ <?php } else { ?>
+ <p><textarea id="safecss" name="safecss"><?php echo str_replace('</textarea>', '&lt;/textarea&gt', Jetpack_Custom_CSS::get_css()); ?></textarea></p>
+ <?php } ?>
+ </div>
+ </div>
+ </div>
+ <br class="clear" />
+ </div>
+ </form>
+ </div>
+ <?php
+ }
+
+ static function publish_box() {
+ ?>
+ <div id="minor-publishing">
+ <div id="misc-publishing-actions">
+ <?php
+
+ $preprocessors = apply_filters( 'jetpack_custom_css_preprocessors', array() );
+
+ if ( ! empty( $preprocessors ) ) {
+ $safecss_post = Jetpack_Custom_CSS::get_current_revision();
+ $selected_preprocessor_key = get_post_meta( $safecss_post['ID'], 'custom_css_preprocessor', true );
+ $selected_preprocessor = isset( $preprocessors[$selected_preprocessor_key] ) ? $preprocessors[$selected_preprocessor_key] : null;
+
+ ?>
+ <div class="misc-pub-section">
+ <label><?php esc_html_e( 'Preprocessor:', 'jetpack' ); ?></label>
+ <span id="preprocessor-display"><?php echo esc_html( $selected_preprocessor ? $selected_preprocessor['name'] : __( 'None', 'jetpack' ) ); ?></span>
+ <a class="edit-preprocessor hide-if-no-js" href="#preprocessor"><?php echo esc_html_e( 'Edit', 'jetpack' ); ?></a>
+ <div id="preprocessor-select" class="hide-if-js">
+ <input type="hidden" name="custom_css_preprocessor" id="custom_css_preprocessor" value="<?php echo esc_attr( $selected_preprocessor_key ); ?>" />
+ <select id="preprocessor_choices">
+ <option value=""><?php esc_html_e( 'None', 'jetpack' ); ?></option>
+ <?php
+
+ foreach ( $preprocessors as $preprocessor_key => $preprocessor ) {
+ ?>
+ <option value="<?php echo esc_attr( $preprocessor_key ); ?>" <?php selected( $selected_preprocessor_key, $preprocessor_key ); ?>><?php echo esc_html( $preprocessor['name'] ); ?></option>
+ <?php
+ }
+
+ ?>
+ </select>
+ <a class="save-preprocessor hide-if-no-js button" href="#preprocessor"><?php esc_html_e( 'OK', 'jetpack' ); ?></a>
+ <a class="cancel-preprocessor hide-if-no-js" href="#preprocessor"><?php esc_html_e( 'Cancel', 'jetpack' ); ?></a>
+ </div>
+ </div>
+ <?php
+ }
+
+ $safecss_post = Jetpack_Custom_CSS::get_current_revision();
+
+ $add_css = ( get_post_meta( $safecss_post['ID'], 'custom_css_add', true ) != 'no' );
+
+ ?>
+ <div class="misc-pub-section">
+ <label><?php esc_html_e( 'Mode:', 'jetpack' ); ?></label>
+ <span id="css-mode-display"><?php echo esc_html( $add_css ? __( 'Add-on', 'jetpack' ) : __( 'Replacement', 'jetpack' ) ); ?></span>
+ <a class="edit-css-mode hide-if-no-js" href="#css-mode"><?php echo esc_html_e( 'Edit', 'jetpack' ); ?></a>
+ <div id="css-mode-select" class="hide-if-js">
+ <input type="hidden" name="add_to_existing" id="add_to_existing" value="<?php echo $add_css ? 'true' : 'false'; ?>" />
+ <p>
+ <label>
+ <input type="radio" name="add_to_existing_display" value="true" <?php checked( $add_css ); ?>/>
+ <?php _e( 'Add-on CSS <b>(Recommended)</b>', 'jetpack' ); ?>
+ </label>
+ <br />
+ <label>
+ <input type="radio" name="add_to_existing_display" value="false" <?php checked( ! $add_css ); ?>/>
+ <?php printf( __( 'Replace <a href="%s">theme\'s CSS</a> <b>(Advanced)</b>', 'jetpack' ), apply_filters( 'safecss_theme_stylesheet_url', get_stylesheet_uri() ) ); ?>
+ </label>
+ </p>
+ <a class="save-css-mode hide-if-no-js button" href="#css-mode"><?php esc_html_e( 'OK', 'jetpack' ); ?></a>
+ <a class="cancel-css-mode hide-if-no-js" href="#css-mode"><?php esc_html_e( 'Cancel', 'jetpack' ); ?></a>
+ </div>
+ <script type="text/javascript">
+ jQuery( function ( $ ) {
+ $( '.edit-css-mode' ).bind( 'click', function ( e ) {
+ e.preventDefault();
+
+ $( '#css-mode-select' ).slideDown();
+ $( this ).hide();
+ } );
+
+ $( '.cancel-css-mode' ).bind( 'click', function ( e ) {
+ e.preventDefault();
+
+ $( '#css-mode-select' ).slideUp( function () {
+ $( '.edit-css-mode' ).show();
+ $( 'input[name=add_to_existing_display][value=' + $( '#add_to_existing' ).val() + ']' ).attr( 'checked', true );
+ } );
+ } );
+
+ $( '.save-css-mode' ).bind( 'click', function ( e ) {
+ e.preventDefault();
+
+ $( '#css-mode-select' ).slideUp();
+ $( '#css-mode-display' ).text( $( 'input[name=add_to_existing_display]:checked' ).val() == 'true' ? 'Add-on' : 'Replacement' );
+ $( '#add_to_existing' ).val( $( 'input[name=add_to_existing_display]:checked' ).val() );
+ $( '.edit-css-mode' ).show();
+ } );
+ } );
+ </script>
+ </div>
+ <?php do_action( 'custom_css_submitbox_misc_actions' ); ?>
+ </div>
+ </div>
+ <div id="major-publishing-actions">
+ <input type="button" class="button" id="preview" name="preview" value="<?php esc_attr_e( 'Preview', 'jetpack' ) ?>" />
+ <div id="publishing-action">
+ <input type="submit" class="button-primary" id="save" name="save" value="<?php ( Jetpack_Custom_CSS::is_freetrial() ) ? esc_attr_e( 'Save &amp; Buy Upgrade', 'jetpack' ) : esc_attr_e( 'Save Stylesheet', 'jetpack' ); ?>" />
+ </div>
+ </div>
+ <?php
+ }
+
+ /**
+ * Render metabox listing CSS revisions and the themes that correspond to the revisions.
+ * Called by afecss_admin *
+ * @param array $safecss_post
+ * @global $post
+ * @uses WP_Query, wp_post_revision_title, esc_html, add_query_arg, menu_page_url, 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;
+ $posts_per_page = isset( $_GET['show_all_rev'] ) ? $max_revisions : 6;
+
+ $revisions = new WP_Query( array(
+ 'posts_per_page' => $posts_per_page,
+ 'post_type' => 'revision',
+ 'post_status' => 'inherit',
+ 'post_parent' => $safecss_post['ID'],
+ 'orderby' => 'date',
+ 'order' => 'DESC'
+ ) );
+
+ if ( $revisions->have_posts() ) { ?>
+ <ul class="post-revisions"><?php
+
+ global $post;
+
+ while ( $revisions->have_posts() ) :
+ $revisions->the_post();
+
+ ?><li>
+ <?php
+ echo wp_post_revision_title( $post );
+
+ if ( ! empty( $post->post_excerpt ) )
+ echo ' (' . esc_html( $post->post_excerpt ) . ')';
+ ?>
+ </li><?php
+
+ endwhile;
+
+ ?></ul><?php
+
+ if ( $revisions->found_posts > 6 ) {
+ ?>
+ <br>
+ <a href="<?php echo add_query_arg( 'show_all_rev', 'true', menu_page_url( 'editcss', false ) ); ?>"><?php esc_html_e( 'Show more', 'jetpack' ); ?></a>
+ <?php
+ }
+ }
+
+ wp_reset_query();
+ }
+
+ /**
+ * Hook in init at priority 11 to disable custom CSS.
+ */
+ static function disable() {
+ remove_action( 'wp_head', array( 'Jetpack_Custom_CSS', 'link_tag' ), 101 );
+ remove_filter( 'stylesheet_uri', array( 'Jetpack_Custom_CSS', 'style_filter' ) );
+ }
+
+ /**
+ * Reset all aspects of Custom CSS on a theme switch so that changing
+ * themes is a sure-fire way to get a clean start.
+ */
+ static function reset() {
+ $safecss_post_id = Jetpack_Custom_CSS::save_revision( '' );
+ $safecss_revision = Jetpack_Custom_CSS::get_current_revision();
+
+ update_option( 'safecss_rev', intval( get_option( 'safecss_rev' ) ) + 1 );
+
+ update_post_meta( $safecss_post_id, 'custom_css_add', 'yes' );
+ update_post_meta( $safecss_post_id, 'content_width', false );
+ update_post_meta( $safecss_post_id, 'custom_css_preprocessor', '' );
+ update_metadata( 'post', $safecss_revision['ID'], 'custom_css_add', 'yes' );
+ update_metadata( 'post', $safecss_revision['ID'], 'content_width', false );
+ update_metadata( 'post', $safecss_revision['ID'], 'custom_css_preprocessor', '' );
+ }
+
+ static function is_customizer_preview() {
+ if ( isset ( $GLOBALS['wp_customize'] ) )
+ return ! $GLOBALS['wp_customize']->is_theme_active();
+
+ return false;
+ }
+
+ static function minify( $css, $preprocessor = '' ) {
+ if ( ! $css )
+ return '';
+
+ if ( $preprocessor ) {
+ $preprocessors = apply_filters( 'jetpack_custom_css_preprocessors', array() );
+
+ if ( isset( $preprocessors[$preprocessor] ) ) {
+ $css = call_user_func( $preprocessors[$preprocessor]['callback'], $css );
+ }
+ }
+
+ safecss_class();
+ $csstidy = new csstidy();
+ $csstidy->optimise = new safecss( $csstidy );
+
+ $csstidy->set_cfg( 'remove_bslash', false );
+ $csstidy->set_cfg( 'compress_colors', true );
+ $csstidy->set_cfg( 'compress_font-weight', true );
+ $csstidy->set_cfg( 'remove_last_;', true );
+ $csstidy->set_cfg( 'case_properties', true );
+ $csstidy->set_cfg( 'discard_invalid_properties', true );
+ $csstidy->set_cfg( 'css_level', 'CSS3.0' );
+ $csstidy->set_cfg( 'template', 'highest');
+ $csstidy->parse( $css );
+
+ return $csstidy->print->plain();
+ }
+
+ /**
+ * When restoring a SafeCSS post revision, also copy over the
+ * content_width and custom_css_add post metadata.
+ */
+ static function restore_revision( $_post_id, $_revision_id ) {
+ $_post = get_post( $_post_id );
+
+ if ( 'safecss' != $_post->post_type )
+ return;
+
+ $safecss_revision = Jetpack_Custom_CSS::get_current_revision();
+
+ $content_width = get_post_meta( $_revision_id, 'content_width', true );
+ $custom_css_add = get_post_meta( $_revision_id, 'custom_css_add', true );
+ $preprocessor = get_post_meta( $_revision_id, 'custom_css_preprocessor', true );
+
+ update_metadata( 'post', $safecss_revision['ID'], 'content_width', $content_width );
+ update_metadata( 'post', $safecss_revision['ID'], 'custom_css_add', $custom_css_add );
+ update_metadata( 'post', $safecss_revision['ID'], 'custom_css_preprocessor', $preprocessor );
+ update_post_meta( $_post->ID, 'content_width', $content_width );
+ update_post_meta( $_post->ID, 'custom_css_add', $custom_css_add );
+ update_post_meta( $_post->ID, 'custom_css_preprocessor', $preprocessor );
+ }
+
+ /**
+ * Migration routine for moving safecss from wp_options to wp_posts to support revisions
+ *
+ * @return void
+ */
+ static function upgrade() {
+ $css = get_option( 'safecss' );
+
+ // Check if CSS is stored in wp_options
+ if ( $css ) {
+ // Remove the async actions from publish_post
+ remove_action( 'publish_post', 'queue_publish_post' );
+
+ $post = array();
+ $post['post_content'] = $css;
+ $post['post_title'] = 'safecss';
+ $post['post_status'] = 'publish';
+ $post['post_type'] = 'safecss';
+
+ // Insert the CSS into wp_posts
+ $post_id = wp_insert_post( $post );
+ // Check for errors
+ if ( !$post_id or is_wp_error( $post_id ) )
+ die( $post_id->get_error_message() );
+
+ // Delete safecss option
+ delete_option( 'safecss' );
+ }
+
+ unset( $css );
+
+ // Check if we have already done this
+ if ( !get_option( 'safecss_revision_migrated' ) ) {
+ define( 'DOING_MIGRATE', true );
+
+ // Get hashes of safecss post and current revision
+ $safecss_post = Jetpack_Custom_CSS::get_post();
+
+ if ( empty( $safecss_post ) )
+ return;
+
+ $safecss_post_hash = md5( $safecss_post['post_content'] );
+ $current_revision = Jetpack_Custom_CSS::get_current_revision();
+
+ if ( null == $current_revision )
+ return;
+
+ $current_revision_hash = md5( $current_revision['post_content'] );
+
+ // If hashes are not equal, set safecss post with content from current revision
+ if ( $safecss_post_hash !== $current_revision_hash ) {
+ Jetpack_Custom_CSS::save_revision( $current_revision['post_content'] );
+ // Reset post_content to display the migrated revsion
+ $safecss_post['post_content'] = $current_revision['post_content'];
+ }
+
+ // Set option so that we dont keep doing this
+ update_option( 'safecss_revision_migrated', time() );
+ }
+
+ $newest_safecss_post = Jetpack_Custom_CSS::get_current_revision();
+
+ if ( $newest_safecss_post ) {
+ if ( get_option( 'safecss_content_width' ) ) {
+ // Add the meta to the post and the latest revision.
+ update_post_meta( $newest_safecss_post['ID'], 'content_width', get_option( 'safecss_content_width' ) );
+ update_metadata( 'post', $newest_safecss_post['ID'], 'content_width', get_option( 'safecss_content_width' ) );
+
+ delete_option( 'safecss_content_width' );
+ }
+
+ if ( get_option( 'safecss_add' ) ) {
+ update_post_meta( $newest_safecss_post['ID'], 'custom_css_add', get_option( 'safecss_add' ) );
+ update_metadata( 'post', $newest_safecss_post['ID'], 'custom_css_add', get_option( 'safecss_add' ) );
+
+ delete_option( 'safecss_add' );
+ }
+ }
+ }
+
+ static function revision_redirect( $redirect ) {
+ global $post;
+
+ if ( 'safecss' == $post->post_type ) {
+ if ( strstr( $redirect, 'action=edit' ) ) {
+ return 'themes.php?page=editcss';
+ }
+
+ if ( 'edit.php' == $redirect ) {
+ return '';
+ }
+ }
+
+ return $redirect;
+ }
+
+ static function revision_post_link( $post_link, $post_id, $context ) {
+ if ( !$post_id = (int) $post_id ) {
+ return $post_link;
+ }
+
+ if ( !$post = get_post( $post_id ) ) {
+ return $post_link;
+ }
+
+ if ( 'safecss' != $post->post_type ) {
+ return $post_link;
+ }
+
+ $post_link = admin_url( 'themes.php?page=editcss' );
+
+ if ( 'display' == $context ) {
+ return esc_url( $post_link );
+ }
+
+ return esc_url_raw( $post_link );
+ }
+}
+
+class Jetpack_Safe_CSS {
+ static function filter_attr( $css, $element = 'div' ) {
+ safecss_class();
+
+ $css = $element . ' {' . $css . '}';
+
+ $csstidy = new csstidy();
+ $csstidy->optimise = new safecss( $csstidy );
+ $csstidy->set_cfg( 'remove_bslash', false );
+ $csstidy->set_cfg( 'compress_colors', false );
+ $csstidy->set_cfg( 'compress_font-weight', false );
+ $csstidy->set_cfg( 'discard_invalid_properties', true );
+ $csstidy->set_cfg( 'merge_selectors', false );
+ $csstidy->set_cfg( 'remove_last_;', false );
+ $csstidy->set_cfg( 'css_level', 'CSS3.0' );
+
+ $css = preg_replace( '/\\\\([0-9a-fA-F]{4})/', '\\\\\\\\$1', $css );
+ $css = wp_kses_split( $css, array(), array() );
+ $csstidy->parse( $css );
+
+ $css = $csstidy->print->plain();
+
+ $css = str_replace( array( "\n","\r","\t" ), '', $css );
+
+ preg_match( "/^{$element}\s*{(.*)}\s*$/", $css, $matches );
+
+ if ( empty( $matches[1] ) )
+ return '';
+
+ return $matches[1];
+ }
+}
+
+function migrate() {
+ _deprecated_function( __FUNCTION__, '2.1', 'Jetpack_Custom_CSS::upgrade()' );
+
+ return Jetpack_Custom_CSS::upgrade();
+}
+
+function safecss_revision_redirect( $redirect ) {
+ _deprecated_function( __FUNCTION__, '2.1', 'Jetpack_Custom_CSS::revision_redirect()' );
+
+ return Jetpack_Custom_CSS::revision_redirect( $redirect );
+}
+
+function safecss_revision_post_link( $post_link, $post_id, $context ) {
+ _deprecated_function( __FUNCTION__, '2.1', 'Jetpack_Custom_CSS::revision_post_link()' );
+
+ return Jetpack_Custom_CSS::revision_post_link( $post_link, $post_id, $context );
+}
+
+function get_safecss_post() {
+ _deprecated_function( __FUNCTION__, '2.1', 'Jetpack_Custom_CSS::get_post()' );
+
+ return Jetpack_Custom_CSS::get_post();
+}
+
+function custom_css_post_id() {
+ _deprecated_function( __FUNCTION__, '2.1', 'Jetpack_Custom_CSS::post_id()' );
+
+ return Jetpack_Custom_CSS::post_id();
+}
+
+function get_current_revision() {
+ _deprecated_function( __FUNCTION__, '2.1', 'Jetpack_Custom_CSS::get_current_revision()' );
+
+ return Jetpack_Custom_CSS::get_current_revision();
+}
+
+function save_revision( $css, $is_preview = false, $preprocessor = '' ) {
+ _deprecated_function( __FUNCTION__, '2.1', 'Jetpack_Custom_CSS::save_revision()' );
+
+ return Jetpack_Custom_CSS::save_revision( $css, $is_preview, $preprocessor );
+}
+
+function safecss_skip_stylesheet() {
+ _deprecated_function( __FUNCTION__, '2.1', 'Jetpack_Custom_CSS::skip_stylesheet()' );
+
+ return Jetpack_Custom_CSS::skip_stylesheet();
+}
+
+function safecss_init() {
+ _deprecated_function( __FUNCTION__, '2.1', 'Jetpack_Custom_CSS::init()' );
+
+ return Jetpack_Custom_CSS::init();
+}
+
+function safecss_is_preview() {
+ _deprecated_function( __FUNCTION__, '2.1', 'Jetpack_Custom_CSS::is_preview()' );
+
+ return Jetpack_Custom_CSS::is_preview();
+}
+
+function safecss_is_freetrial() {
+ _deprecated_function( __FUNCTION__, '2.1', 'Jetpack_Custom_CSS::is_freetrial()' );
+
+ return Jetpack_Custom_CSS::is_freetrial();
+}
+
+function safecss( $compressed = false ) {
+ _deprecated_function( __FUNCTION__, '2.1', 'Jetpack_Custom_CSS::get_css()' );
+
+ return Jetpack_Custom_CSS::get_css( $compressed );
+}
+
+function safecss_print() {
+ _deprecated_function( __FUNCTION__, '2.1', 'Jetpack_Custom_CSS::print_css()' );
+
+ return Jetpack_Custom_CSS::print_css();
+}
+
+function safecss_style() {
+ _deprecated_function( __FUNCTION__, '2.1', 'Jetpack_Custom_CSS::link_tag()' );
+
+ return Jetpack_Custom_CSS::link_tag();
+}
+
+function safecss_style_filter( $current ) {
+ _deprecated_function( __FUNCTION__, '2.1', 'Jetpack_Custom_CSS::style_filter()' );
+
+ return Jetpack_Custom_CSS::style_filter( $current );
+}
+
+function safecss_buffer( $html ) {
+ _deprecated_function( __FUNCTION__, '2.1', 'Jetpack_Custom_CSS::buffer()' );
+
+ return Jetpack_Custom_CSS::buffer( $html );
+}
+
+function safecss_preview_links( $matches ) {
+ _deprecated_function( __FUNCTION__, '2.1', 'Jetpack_Custom_CSS::preview_links()' );
+
+ return Jetpack_Custom_CSS::preview_links( $matches );
+}
+
+function safecss_preview_flag() {
+ _deprecated_function( __FUNCTION__, '2.1', 'Jetpack_Custom_CSS::preview_flag()' );
+
+ return Jetpack_Custom_CSS::preview_flag();
+}
+
+function safecss_menu() {
+ _deprecated_function( __FUNCTION__, '2.1', 'Jetpack_Custom_CSS::menu()' );
+
+ return Jetpack_Custom_CSS::menu();
+}
+
+function update_title() {
+ _deprecated_function( __FUNCTION__, '2.1', 'Jetpack_Custom_CSS::update_title()' );
+
+ return Jetpack_Custom_CSS::update_title();
+}
+
+function safecss_prettify_post_revisions() {
+ _deprecated_function( __FUNCTION__, '2.1', 'Jetpack_Custom_CSS::prettify_post_revisions()' );
+
+ return Jetpack_Custom_CSS::prettify_post_revisions();
+}
+
+function safecss_remove_title_excerpt_from_revisions() {
+ _deprecated_function( __FUNCTION__, '2.1', 'Jetpack_Custom_CSS::remove_title_excerpt_from_revisions()' );
+
+ return Jetpack_Custom_CSS::remove_title_excerpt_from_revisions();
+}
+
+function safecss_post_title( $title, $post_id ) {
+ _deprecated_function( __FUNCTION__, '2.1', 'Jetpack_Custom_CSS::post_title()' );
+
+ return Jetpack_Custom_CSS::post_title( $title, $post_id );
+}
+
+function safe_css_enqueue_scripts() {
+ _deprecated_function( __FUNCTION__, '2.1', 'Jetpack_Custom_CSS::enqueue_scripts()' );
+
+ return Jetpack_Custom_CSS::enqueue_scripts();
+}
+
+function safecss_admin_head() {
+ _deprecated_function( __FUNCTION__, '2.1', 'Jetpack_Custom_CSS::admin_head()' );
+
+ return Jetpack_Custom_CSS::admin_head();
+}
+
+function safecss_saved() {
+ _deprecated_function( __FUNCTION__, '2.1', 'Jetpack_Custom_CSS::saved_message()' );
+
+ return Jetpack_Custom_CSS::saved_message();
+}
+
+function safecss_admin() {
+ _deprecated_function( __FUNCTION__, '2.1', 'Jetpack_Custom_CSS::admin()' );
+
+ return Jetpack_Custom_CSS::admin();
+}
+
+function custom_css_meta_box() {
+ _deprecated_function( __FUNCTION__, '2.1', 'add_meta_box( $id, $title, $callback, \'editcss\', \'side\' )' );
+}
+
+function custom_css_post_revisions_meta_box( $safecss_post ) {
+ _deprecated_function( __FUNCTION__, '2.1', 'Jetpack_Custom_CSS::revisions_meta_box()' );
+
+ return Jetpack_Custom_CSS::revisions_meta_box( $safecss_post );
+}
+
+function disable_safecss_style() {
+ _deprecated_function( __FUNCTION__, '2.1', 'Jetpack_Custom_CSS::disable()' );
+
+ return Jetpack_Custom_CSS::disable();
+}
+
+function custom_css_reset() {
+ _deprecated_function( __FUNCTION__, '2.1', 'Jetpack_Custom_CSS::reset()' );
+
+ return Jetpack_Custom_CSS::reset();
+}
+
+function custom_css_is_customizer_preview() {
+ _deprecated_function( __FUNCTION__, '2.1', 'Jetpack_Custom_CSS::is_customizer_preview()' );
+
+ return Jetpack_Custom_CSS::is_customizer_preview();
+}
+
+function custom_css_minify( $css, $preprocessor = '' ) {
+ _deprecated_function( __FUNCTION__, '2.1', 'Jetpack_Custom_CSS::minify()' );
+
+ return Jetpack_Custom_CSS::minify( $css, $preprocessor );
+}
+
+function custom_css_restore_revision( $_post_id, $_revision_id ) {
+ _deprecated_function( __FUNCTION__, '2.1', 'Jetpack_Custom_CSS::restore_revision()' );
+
+ return Jetpack_Custom_CSS::restore_revision( $_post_id, $_revision_id );;
+}
+
+function safecss_class() {
+ // Wrapped so we don't need the parent class just to load the plugin
+ if ( class_exists('safecss') )
+ return;
+
+ require_once( dirname( __FILE__ ) . '/csstidy/class.csstidy.php' );
+
+ class safecss extends csstidy_optimise {
+ function safecss( &$css ) {
+ return $this->csstidy_optimise( $css );
+ }
+
+ function postparse() {
+ do_action( 'csstidy_optimize_postparse', $this );
+
+ return parent::postparse();
+ }
+
+ function subvalue() {
+ do_action( 'csstidy_optimize_subvalue', $this );
+
+ return parent::subvalue();
+ }
+ }
+}
+
+if ( ! function_exists( 'safecss_filter_attr' ) ) {
+ function safecss_filter_attr( $css, $element = 'div' ) {
+ return Jetpack_Safe_CSS::filter_attr( $css, $element );
+ }
+}
+
+add_action( 'init', array( 'Jetpack_Custom_CSS', 'init' ) );
+
+include dirname( __FILE__ ) . '/custom-css/preprocessors.php'; \ No newline at end of file
diff --git a/plugins/jetpack/modules/custom-css/custom-css/blank.css b/plugins/jetpack/modules/custom-css/custom-css/blank.css
new file mode 100644
index 00000000..79a9626b
--- /dev/null
+++ b/plugins/jetpack/modules/custom-css/custom-css/blank.css
@@ -0,0 +1 @@
+/* */
diff --git a/plugins/jetpack/modules/custom-css/custom-css/js/ace/ace.js b/plugins/jetpack/modules/custom-css/custom-css/js/ace/ace.js
new file mode 100644
index 00000000..520b8fca
--- /dev/null
+++ b/plugins/jetpack/modules/custom-css/custom-css/js/ace/ace.js
@@ -0,0 +1,11 @@
+(function(){function o(e){var i=function(e,t){return r("",e,t)},s=t;e&&(t[e]||(t[e]={}),s=t[e]);if(!s.define||!s.define.packaged)n.original=s.define,s.define=n,s.define.packaged=!0;if(!s.require||!s.require.packaged)r.original=s.require,s.require=i,s.require.packaged=!0}var e="ace",t=function(){return this}();if(!e&&typeof requirejs!="undefined")return;var n=function(e,t,r){if(typeof e!="string"){n.original?n.original.apply(window,arguments):(console.error("dropping module because define wasn't a string."),console.trace());return}arguments.length==2&&(r=t),n.modules||(n.modules={}),n.modules[e]=r},r=function(e,t,n){if(Object.prototype.toString.call(t)==="[object Array]"){var i=[];for(var o=0,u=t.length;o<u;++o){var a=s(e,t[o]);if(!a&&r.original)return r.original.apply(window,arguments);i.push(a)}n&&n.apply(null,i)}else{if(typeof t=="string"){var f=s(e,t);return!f&&r.original?r.original.apply(window,arguments):(n&&n(),f)}if(r.original)return r.original.apply(window,arguments)}},i=function(e,t){if(t.indexOf("!")!==-1){var n=t.split("!");return i(e,n[0])+"!"+i(e,n[1])}if(t.charAt(0)=="."){var r=e.split("/").slice(0,-1).join("/");t=r+"/"+t;while(t.indexOf(".")!==-1&&s!=t){var s=t;t=t.replace(/\/\.\//,"/").replace(/[^\/]+\/\.\.\//,"")}}return t},s=function(e,t){t=i(e,t);var s=n.modules[t];if(!s)return null;if(typeof s=="function"){var o={},u={id:t,uri:"",exports:o,packaged:!0},a=function(e,n){return r(t,e,n)},f=s(a,o,u);return o=f||u.exports,n.modules[t]=o,o}return s};o(e)})(),ace.define("ace/ace",["require","exports","module","ace/lib/fixoldbrowsers","ace/lib/dom","ace/lib/event","ace/editor","ace/edit_session","ace/undomanager","ace/virtual_renderer","ace/multi_select","ace/worker/worker_client","ace/keyboard/hash_handler","ace/placeholder","ace/mode/folding/fold_mode","ace/config"],function(e,t,n){e("./lib/fixoldbrowsers");var r=e("./lib/dom"),i=e("./lib/event"),s=e("./editor").Editor,o=e("./edit_session").EditSession,u=e("./undomanager").UndoManager,a=e("./virtual_renderer").VirtualRenderer,f=e("./multi_select").MultiSelect;e("./worker/worker_client"),e("./keyboard/hash_handler"),e("./placeholder"),e("./mode/folding/fold_mode"),t.config=e("./config"),t.require=e,t.edit=function(e){if(typeof e=="string"){var n=e,e=document.getElementById(n);if(!e)throw"ace.edit can't find div #"+n}if(e.env&&e.env.editor instanceof s)return e.env.editor;var o=t.createEditSession(r.getInnerText(e));e.innerHTML="";var u=new s(new a(e));new f(u),u.setSession(o);var l={document:o,editor:u,onResize:u.resize.bind(u)};return i.addListener(window,"resize",l.onResize),e.env=u.env=l,u},t.createEditSession=function(e,t){var n=new o(e,n);return n.setUndoManager(new u),n},t.EditSession=o,t.UndoManager=u}),ace.define("ace/lib/fixoldbrowsers",["require","exports","module","ace/lib/regexp","ace/lib/es5-shim"],function(e,t,n){e("./regexp"),e("./es5-shim")}),ace.define("ace/lib/regexp",["require","exports","module"],function(e,t,n){function o(e){return(e.global?"g":"")+(e.ignoreCase?"i":"")+(e.multiline?"m":"")+(e.extended?"x":"")+(e.sticky?"y":"")}function u(e,t,n){if(Array.prototype.indexOf)return e.indexOf(t,n);for(var r=n||0;r<e.length;r++)if(e[r]===t)return r;return-1}var r={exec:RegExp.prototype.exec,test:RegExp.prototype.test,match:String.prototype.match,replace:String.prototype.replace,split:String.prototype.split},i=r.exec.call(/()??/,"")[1]===undefined,s=function(){var e=/^/g;return r.test.call(e,""),!e.lastIndex}();if(s&&i)return;RegExp.prototype.exec=function(e){var t=r.exec.apply(this,arguments),n,a;if(typeof e=="string"&&t){!i&&t.length>1&&u(t,"")>-1&&(a=RegExp(this.source,r.replace.call(o(this),"g","")),r.replace.call(e.slice(t.index),a,function(){for(var e=1;e<arguments.length-2;e++)arguments[e]===undefined&&(t[e]=undefined)}));if(this._xregexp&&this._xregexp.captureNames)for(var f=1;f<t.length;f++)n=this._xregexp.captureNames[f-1],n&&(t[n]=t[f]);!s&&this.global&&!t[0].length&&this.lastIndex>t.index&&this.lastIndex--}return t},s||(RegExp.prototype.test=function(e){var t=r.exec.call(this,e);return t&&this.global&&!t[0].length&&this.lastIndex>t.index&&this.lastIndex--,!!t})}),ace.define("ace/lib/es5-shim",["require","exports","module"],function(e,t,n){function m(e){try{return Object.defineProperty(e,"sentinel",{}),"sentinel"in e}catch(t){}}Function.prototype.bind||(Function.prototype.bind=function(t){var n=this;if(typeof n!="function")throw new TypeError;var r=o.call(arguments,1),i=function(){if(this instanceof i){var e=function(){};e.prototype=n.prototype;var s=new e,u=n.apply(s,r.concat(o.call(arguments)));return u!==null&&Object(u)===u?u:s}return n.apply(t,r.concat(o.call(arguments)))};return i});var r=Function.prototype.call,i=Array.prototype,s=Object.prototype,o=i.slice,u=r.bind(s.toString),a=r.bind(s.hasOwnProperty),f,l,c,h,p;if(p=a(s,"__defineGetter__"))f=r.bind(s.__defineGetter__),l=r.bind(s.__defineSetter__),c=r.bind(s.__lookupGetter__),h=r.bind(s.__lookupSetter__);Array.isArray||(Array.isArray=function(t){return u(t)=="[object Array]"}),Array.prototype.forEach||(Array.prototype.forEach=function(t){var n=D(this),r=arguments[1],i=0,s=n.length>>>0;if(u(t)!="[object Function]")throw new TypeError;while(i<s)i in n&&t.call(r,n[i],i,n),i++}),Array.prototype.map||(Array.prototype.map=function(t){var n=D(this),r=n.length>>>0,i=Array(r),s=arguments[1];if(u(t)!="[object Function]")throw new TypeError;for(var o=0;o<r;o++)o in n&&(i[o]=t.call(s,n[o],o,n));return i}),Array.prototype.filter||(Array.prototype.filter=function(t){var n=D(this),r=n.length>>>0,i=[],s=arguments[1];if(u(t)!="[object Function]")throw new TypeError;for(var o=0;o<r;o++)o in n&&t.call(s,n[o],o,n)&&i.push(n[o]);return i}),Array.prototype.every||(Array.prototype.every=function(t){var n=D(this),r=n.length>>>0,i=arguments[1];if(u(t)!="[object Function]")throw new TypeError;for(var s=0;s<r;s++)if(s in n&&!t.call(i,n[s],s,n))return!1;return!0}),Array.prototype.some||(Array.prototype.some=function(t){var n=D(this),r=n.length>>>0,i=arguments[1];if(u(t)!="[object Function]")throw new TypeError;for(var s=0;s<r;s++)if(s in n&&t.call(i,n[s],s,n))return!0;return!1}),Array.prototype.reduce||(Array.prototype.reduce=function(t){var n=D(this),r=n.length>>>0;if(u(t)!="[object Function]")throw new TypeError;if(!r&&arguments.length==1)throw new TypeError;var i=0,s;if(arguments.length>=2)s=arguments[1];else do{if(i in n){s=n[i++];break}if(++i>=r)throw new TypeError}while(!0);for(;i<r;i++)i in n&&(s=t.call(void 0,s,n[i],i,n));return s}),Array.prototype.reduceRight||(Array.prototype.reduceRight=function(t){var n=D(this),r=n.length>>>0;if(u(t)!="[object Function]")throw new TypeError;if(!r&&arguments.length==1)throw new TypeError;var i,s=r-1;if(arguments.length>=2)i=arguments[1];else do{if(s in n){i=n[s--];break}if(--s<0)throw new TypeError}while(!0);do s in this&&(i=t.call(void 0,i,n[s],s,n));while(s--);return i}),Array.prototype.indexOf||(Array.prototype.indexOf=function(t){var n=D(this),r=n.length>>>0;if(!r)return-1;var i=0;arguments.length>1&&(i=M(arguments[1])),i=i>=0?i:Math.max(0,r+i);for(;i<r;i++)if(i in n&&n[i]===t)return i;return-1}),Array.prototype.lastIndexOf||(Array.prototype.lastIndexOf=function(t){var n=D(this),r=n.length>>>0;if(!r)return-1;var i=r-1;arguments.length>1&&(i=Math.min(i,M(arguments[1]))),i=i>=0?i:r-Math.abs(i);for(;i>=0;i--)if(i in n&&t===n[i])return i;return-1}),Object.getPrototypeOf||(Object.getPrototypeOf=function(t){return t.__proto__||(t.constructor?t.constructor.prototype:s)});if(!Object.getOwnPropertyDescriptor){var d="Object.getOwnPropertyDescriptor called on a non-object: ";Object.getOwnPropertyDescriptor=function(t,n){if(typeof t!="object"&&typeof t!="function"||t===null)throw new TypeError(d+t);if(!a(t,n))return;var r,i,o;r={enumerable:!0,configurable:!0};if(p){var u=t.__proto__;t.__proto__=s;var i=c(t,n),o=h(t,n);t.__proto__=u;if(i||o)return i&&(r.get=i),o&&(r.set=o),r}return r.value=t[n],r}}Object.getOwnPropertyNames||(Object.getOwnPropertyNames=function(t){return Object.keys(t)});if(!Object.create){var v;Object.prototype.__proto__===null?v=function(){return{__proto__:null}}:v=function(){var e={};for(var t in e)e[t]=null;return e.constructor=e.hasOwnProperty=e.propertyIsEnumerable=e.isPrototypeOf=e.toLocaleString=e.toString=e.valueOf=e.__proto__=null,e},Object.create=function(t,n){var r;if(t===null)r=v();else{if(typeof t!="object")throw new TypeError("typeof prototype["+typeof t+"] != 'object'");var i=function(){};i.prototype=t,r=new i,r.__proto__=t}return n!==void 0&&Object.defineProperties(r,n),r}}if(Object.defineProperty){var g=m({}),y=typeof document=="undefined"||m(document.createElement("div"));if(!g||!y)var b=Object.defineProperty}if(!Object.defineProperty||b){var w="Property description must be an object: ",E="Object.defineProperty called on non-object: ",S="getters & setters can not be defined on this javascript engine";Object.defineProperty=function(t,n,r){if(typeof t!="object"&&typeof t!="function"||t===null)throw new TypeError(E+t);if(typeof r!="object"&&typeof r!="function"||r===null)throw new TypeError(w+r);if(b)try{return b.call(Object,t,n,r)}catch(i){}if(a(r,"value"))if(p&&(c(t,n)||h(t,n))){var o=t.__proto__;t.__proto__=s,delete t[n],t[n]=r.value,t.__proto__=o}else t[n]=r.value;else{if(!p)throw new TypeError(S);a(r,"get")&&f(t,n,r.get),a(r,"set")&&l(t,n,r.set)}return t}}Object.defineProperties||(Object.defineProperties=function(t,n){for(var r in n)a(n,r)&&Object.defineProperty(t,r,n[r]);return t}),Object.seal||(Object.seal=function(t){return t}),Object.freeze||(Object.freeze=function(t){return t});try{Object.freeze(function(){})}catch(x){Object.freeze=function(t){return function(n){return typeof n=="function"?n:t(n)}}(Object.freeze)}Object.preventExtensions||(Object.preventExtensions=function(t){return t}),Object.isSealed||(Object.isSealed=function(t){return!1}),Object.isFrozen||(Object.isFrozen=function(t){return!1}),Object.isExtensible||(Object.isExtensible=function(t){if(Object(t)===t)throw new TypeError;var n="";while(a(t,n))n+="?";t[n]=!0;var r=a(t,n);return delete t[n],r});if(!Object.keys){var T=!0,N=["toString","toLocaleString","valueOf","hasOwnProperty","isPrototypeOf","propertyIsEnumerable","constructor"],C=N.length;for(var k in{toString:null})T=!1;Object.keys=function P(e){if(typeof e!="object"&&typeof e!="function"||e===null)throw new TypeError("Object.keys called on a non-object");var P=[];for(var t in e)a(e,t)&&P.push(t);if(T)for(var n=0,r=C;n<r;n++){var i=N[n];a(e,i)&&P.push(i)}return P}}if(!Date.prototype.toISOString||(new Date(-621987552e5)).toISOString().indexOf("-000001")===-1)Date.prototype.toISOString=function(){var t,n,r,i;if(!isFinite(this))throw new RangeError;t=[this.getUTCMonth()+1,this.getUTCDate(),this.getUTCHours(),this.getUTCMinutes(),this.getUTCSeconds()],i=this.getUTCFullYear(),i=(i<0?"-":i>9999?"+":"")+("00000"+Math.abs(i)).slice(0<=i&&i<=9999?-4:-6),n=t.length;while(n--)r=t[n],r<10&&(t[n]="0"+r);return i+"-"+t.slice(0,2).join("-")+"T"+t.slice(2).join(":")+"."+("000"+this.getUTCMilliseconds()).slice(-3)+"Z"};Date.now||(Date.now=function(){return(new Date).getTime()}),Date.prototype.toJSON||(Date.prototype.toJSON=function(t){if(typeof this.toISOString!="function")throw new TypeError;return this.toISOString()}),Date.parse("+275760-09-13T00:00:00.000Z")!==864e13&&(Date=function(e){var t=function i(t,n,r,s,o,u,a){var f=arguments.length;if(this instanceof e){var l=f==1&&String(t)===t?new e(i.parse(t)):f>=7?new e(t,n,r,s,o,u,a):f>=6?new e(t,n,r,s,o,u):f>=5?new e(t,n,r,s,o):f>=4?new e(t,n,r,s):f>=3?new e(t,n,r):f>=2?new e(t,n):f>=1?new e(t):new e;return l.constructor=i,l}return e.apply(this,arguments)},n=new RegExp("^(\\d{4}|[+-]\\d{6})(?:-(\\d{2})(?:-(\\d{2})(?:T(\\d{2}):(\\d{2})(?::(\\d{2})(?:\\.(\\d{3}))?)?(?:Z|(?:([-+])(\\d{2}):(\\d{2})))?)?)?)?$");for(var r in e)t[r]=e[r];return t.now=e.now,t.UTC=e.UTC,t.prototype=e.prototype,t.prototype.constructor=t,t.parse=function(r){var i=n.exec(r);if(i){i.shift();for(var s=1;s<7;s++)i[s]=+(i[s]||(s<3?1:0)),s==1&&i[s]--;var o=+i.pop(),u=+i.pop(),a=i.pop(),f=0;if(a){if(u>23||o>59)return NaN;f=(u*60+o)*6e4*(a=="+"?-1:1)}var l=+i[0];return 0<=l&&l<=99?(i[0]=l+400,e.UTC.apply(this,i)+f-126227808e5):e.UTC.apply(this,i)+f}return e.parse.apply(this,arguments)},t}(Date));var L=" \n \f\r   ᠎              \u2028\u2029";if(!String.prototype.trim||L.trim()){L="["+L+"]";var A=new RegExp("^"+L+L+"*"),O=new RegExp(L+L+"*$");String.prototype.trim=function(){return String(this).replace(A,"").replace(O,"")}}var M=function(e){return e=+e,e!==e?e=0:e!==0&&e!==1/0&&e!==-1/0&&(e=(e>0||-1)*Math.floor(Math.abs(e))),e},_="a"[0]!="a",D=function(e){if(e==null)throw new TypeError;return _&&typeof e=="string"&&e?e.split(""):Object(e)}}),ace.define("ace/lib/dom",["require","exports","module"],function(e,t,n){var r="http://www.w3.org/1999/xhtml";t.createElement=function(e,t){return document.createElementNS?document.createElementNS(t||r,e):document.createElement(e)},t.setText=function(e,t){e.innerText!==undefined&&(e.innerText=t),e.textContent!==undefined&&(e.textContent=t)},t.hasCssClass=function(e,t){var n=e.className.split(/\s+/g);return n.indexOf(t)!==-1},t.addCssClass=function(e,n){t.hasCssClass(e,n)||(e.className+=" "+n)},t.removeCssClass=function(e,t){var n=e.className.split(/\s+/g);for(;;){var r=n.indexOf(t);if(r==-1)break;n.splice(r,1)}e.className=n.join(" ")},t.toggleCssClass=function(e,t){var n=e.className.split(/\s+/g),r=!0;for(;;){var i=n.indexOf(t);if(i==-1)break;r=!1,n.splice(i,1)}return r&&n.push(t),e.className=n.join(" "),r},t.setCssClass=function(e,n,r){r?t.addCssClass(e,n):t.removeCssClass(e,n)},t.hasCssString=function(e,t){var n=0,r;t=t||document;if(t.createStyleSheet&&(r=t.styleSheets)){while(n<r.length)if(r[n++].owningElement.id===e)return!0}else if(r=t.getElementsByTagName("style"))while(n<r.length)if(r[n++].id===e)return!0;return!1},t.importCssString=function(n,i,s){s=s||document;if(i&&t.hasCssString(i,s))return null;var o;if(s.createStyleSheet)o=s.createStyleSheet(),o.cssText=n,i&&(o.owningElement.id=i);else{o=s.createElementNS?s.createElementNS(r,"style"):s.createElement("style"),o.appendChild(s.createTextNode(n)),i&&(o.id=i);var u=s.getElementsByTagName("head")[0]||s.documentElement;u.appendChild(o)}},t.importCssStylsheet=function(e,n){if(n.createStyleSheet)n.createStyleSheet(e);else{var r=t.createElement("link");r.rel="stylesheet",r.href=e;var i=n.getElementsByTagName("head")[0]||n.documentElement;i.appendChild(r)}},t.getInnerWidth=function(e){return parseInt(t.computedStyle(e,"paddingLeft"),10)+parseInt(t.computedStyle(e,"paddingRight"),10)+e.clientWidth},t.getInnerHeight=function(e){return parseInt(t.computedStyle(e,"paddingTop"),10)+parseInt(t.computedStyle(e,"paddingBottom"),10)+e.clientHeight},window.pageYOffset!==undefined?(t.getPageScrollTop=function(){return window.pageYOffset},t.getPageScrollLeft=function(){return window.pageXOffset}):(t.getPageScrollTop=function(){return document.body.scrollTop},t.getPageScrollLeft=function(){return document.body.scrollLeft}),window.getComputedStyle?t.computedStyle=function(e,t){return t?(window.getComputedStyle(e,"")||{})[t]||"":window.getComputedStyle(e,"")||{}}:t.computedStyle=function(e,t){return t?e.currentStyle[t]:e.currentStyle},t.scrollbarWidth=function(e){var n=t.createElement("p");n.style.width="100%",n.style.minWidth="0px",n.style.height="200px";var r=t.createElement("div"),i=r.style;i.position="absolute",i.left="-10000px",i.overflow="hidden",i.width="200px",i.minWidth="0px",i.height="150px",r.appendChild(n);var s=e.body||e.documentElement;s.appendChild(r);var o=n.offsetWidth;i.overflow="scroll";var u=n.offsetWidth;return o==u&&(u=r.clientWidth),s.removeChild(r),o-u},t.setInnerHtml=function(e,t){var n=e.cloneNode(!1);return n.innerHTML=t,e.parentNode.replaceChild(n,e),n},t.setInnerText=function(e,t){var n=e.ownerDocument;n.body&&"textContent"in n.body?e.textContent=t:e.innerText=t},t.getInnerText=function(e){var t=e.ownerDocument;return t.body&&"textContent"in t.body?e.textContent:e.innerText||e.textContent||""},t.getParentWindow=function(e){return e.defaultView||e.parentWindow}}),ace.define("ace/lib/event",["require","exports","module","ace/lib/keys","ace/lib/useragent","ace/lib/dom"],function(e,t,n){function o(e,t,n){var s=0;!i.isOpera||"KeyboardEvent"in window||!i.isMac?s=0|(t.ctrlKey?1:0)|(t.altKey?2:0)|(t.shiftKey?4:0)|(t.metaKey?8:0):s=0|(t.metaKey?1:0)|(t.altKey?2:0)|(t.shiftKey?4:0)|(t.ctrlKey?8:0);if(n in r.MODIFIER_KEYS){switch(r.MODIFIER_KEYS[n]){case"Alt":s=2;break;case"Shift":s=4;break;case"Ctrl":s=1;break;default:s=8}n=0}return s&8&&(n==91||n==93)&&(n=0),!!s||n in r.FUNCTION_KEYS||n in r.PRINTABLE_KEYS?e(t,s,n):!1}var r=e("./keys"),i=e("./useragent"),s=e("./dom");t.addListener=function(e,t,n){if(e.addEventListener)return e.addEventListener(t,n,!1);if(e.attachEvent){var r=function(){n(window.event)};n._wrapper=r,e.attachEvent("on"+t,r)}},t.removeListener=function(e,t,n){if(e.removeEventListener)return e.removeEventListener(t,n,!1);e.detachEvent&&e.detachEvent("on"+t,n._wrapper||n)},t.stopEvent=function(e){return t.stopPropagation(e),t.preventDefault(e),!1},t.stopPropagation=function(e){e.stopPropagation?e.stopPropagation():e.cancelBubble=!0},t.preventDefault=function(e){e.preventDefault?e.preventDefault():e.returnValue=!1},t.getButton=function(e){return e.type=="dblclick"?0:e.type=="contextmenu"||e.ctrlKey&&i.isMac?2:e.preventDefault?e.button:{1:0,2:2,4:1}[e.button]},document.documentElement.setCapture?t.capture=function(e,n,r){function s(o){n(o),i||(i=!0,r(o)),t.removeListener(e,"mousemove",n),t.removeListener(e,"mouseup",s),t.removeListener(e,"losecapture",s),e.releaseCapture()}var i=!1;t.addListener(e,"mousemove",n),t.addListener(e,"mouseup",s),t.addListener(e,"losecapture",s),e.setCapture()}:t.capture=function(e,t,n){function r(e){t&&t(e),n&&n(e),document.removeEventListener("mousemove",t,!0),document.removeEventListener("mouseup",r,!0),e.stopPropagation()}document.addEventListener("mousemove",t,!0),document.addEventListener("mouseup",r,!0)},t.addMouseWheelListener=function(e,n){var r=8,i=function(e){e.wheelDelta!==undefined?e.wheelDeltaX!==undefined?(e.wheelX=-e.wheelDeltaX/r,e.wheelY=-e.wheelDeltaY/r):(e.wheelX=0,e.wheelY=-e.wheelDelta/r):e.axis&&e.axis==e.HORIZONTAL_AXIS?(e.wheelX=(e.detail||0)*5,e.wheelY=0):(e.wheelX=0,e.wheelY=(e.detail||0)*5),n(e)};t.addListener(e,"DOMMouseScroll",i),t.addListener(e,"mousewheel",i)},t.addMultiMouseDownListener=function(e,n,r,s){var o=0,u,a,f,l={2:"dblclick",3:"tripleclick",4:"quadclick"};t.addListener(e,"mousedown",function(e){if(t.getButton(e)!=0)o=0;else{var i=Math.abs(e.clientX-u)>5||Math.abs(e.clientY-a)>5;if(!f||i)o=0;o+=1,f&&clearTimeout(f),f=setTimeout(function(){f=null},n[o-1]||600)}o==1&&(u=e.clientX,a=e.clientY),r[s]("mousedown",e);if(o>4)o=0;else if(o>1)return r[s](l[o],e)}),i.isOldIE&&t.addListener(e,"dblclick",function(e){o=2,f&&clearTimeout(f),f=setTimeout(function(){f=null},n[o-1]||600),r[s]("mousedown",e),r[s](l[o],e)})},t.addCommandKeyListener=function(e,n){var r=t.addListener;if(i.isOldGecko||i.isOpera&&!("KeyboardEvent"in window)){var s=null;r(e,"keydown",function(e){s=e.keyCode}),r(e,"keypress",function(e){return o(n,e,s)})}else{var u=null;r(e,"keydown",function(e){return u=e.keyIdentifier||e.keyCode,o(n,e,e.keyCode)})}};if(window.postMessage&&!i.isOldIE){var u=1;t.nextTick=function(e,n){n=n||window;var r="zero-timeout-message-"+u;t.addListener(n,"message",function i(s){s.data==r&&(t.stopPropagation(s),t.removeListener(n,"message",i),e())}),n.postMessage(r,"*")}}t.nextFrame=window.requestAnimationFrame||window.oRequestAnimationFrame||window.msRequestAnimationFrame||window.mozRequestAnimationFrame||window.webkitRequestAnimationFrame,t.nextFrame?t.nextFrame=t.nextFrame.bind(window):t.nextFrame=function(e){setTimeout(e,17)}}),ace.define("ace/lib/keys",["require","exports","module","ace/lib/oop"],function(e,t,n){var r=e("./oop"),i=function(){var e={MODIFIER_KEYS:{16:"Shift",17:"Ctrl",18:"Alt",224:"Meta"},KEY_MODS:{ctrl:1,alt:2,option:2,shift:4,meta:8,command:8},FUNCTION_KEYS:{8:"Backspace",9:"Tab",13:"Return",19:"Pause",27:"Esc",32:"Space",33:"PageUp",34:"PageDown",35:"End",36:"Home",37:"Left",38:"Up",39:"Right",40:"Down",44:"Print",45:"Insert",46:"Delete",96:"Numpad0",97:"Numpad1",98:"Numpad2",99:"Numpad3",100:"Numpad4",101:"Numpad5",102:"Numpad6",103:"Numpad7",104:"Numpad8",105:"Numpad9",112:"F1",113:"F2",114:"F3",115:"F4",116:"F5",117:"F6",118:"F7",119:"F8",120:"F9",121:"F10",122:"F11",123:"F12",144:"Numlock",145:"Scrolllock"},PRINTABLE_KEYS:{32:" ",48:"0",49:"1",50:"2",51:"3",52:"4",53:"5",54:"6",55:"7",56:"8",57:"9",59:";",61:"=",65:"a",66:"b",67:"c",68:"d",69:"e",70:"f",71:"g",72:"h",73:"i",74:"j",75:"k",76:"l",77:"m",78:"n",79:"o",80:"p",81:"q",82:"r",83:"s",84:"t",85:"u",86:"v",87:"w",88:"x",89:"y",90:"z",107:"+",109:"-",110:".",188:",",190:".",191:"/",192:"`",219:"[",220:"\\",221:"]",222:"'"}};for(var t in e.FUNCTION_KEYS){var n=e.FUNCTION_KEYS[t].toLowerCase();e[n]=parseInt(t,10)}return r.mixin(e,e.MODIFIER_KEYS),r.mixin(e,e.PRINTABLE_KEYS),r.mixin(e,e.FUNCTION_KEYS),e.enter=e["return"],e.escape=e.esc,e.del=e["delete"],e[173]="-",e}();r.mixin(t,i),t.keyCodeToString=function(e){return(i[e]||String.fromCharCode(e)).toLowerCase()}}),ace.define("ace/lib/oop",["require","exports","module"],function(e,t,n){t.inherits=function(){var e=function(){};return function(t,n){e.prototype=n.prototype,t.super_=n.prototype,t.prototype=new e,t.prototype.constructor=t}}(),t.mixin=function(e,t){for(var n in t)e[n]=t[n]},t.implement=function(e,n){t.mixin(e,n)}}),ace.define("ace/lib/useragent",["require","exports","module"],function(e,t,n){t.OS={LINUX:"LINUX",MAC:"MAC",WINDOWS:"WINDOWS"},t.getOS=function(){return t.isMac?t.OS.MAC:t.isLinux?t.OS.LINUX:t.OS.WINDOWS};if(typeof navigator!="object")return;var r=(navigator.platform.match(/mac|win|linux/i)||["other"])[0].toLowerCase(),i=navigator.userAgent;t.isWin=r=="win",t.isMac=r=="mac",t.isLinux=r=="linux",t.isIE=navigator.appName=="Microsoft Internet Explorer"&&parseFloat(navigator.userAgent.match(/MSIE ([0-9]+[\.0-9]+)/)[1]),t.isOldIE=t.isIE&&t.isIE<9,t.isGecko=t.isMozilla=window.controllers&&window.navigator.product==="Gecko",t.isOldGecko=t.isGecko&&parseInt((navigator.userAgent.match(/rv\:(\d+)/)||[])[1],10)<4,t.isOpera=window.opera&&Object.prototype.toString.call(window.opera)=="[object Opera]",t.isWebKit=parseFloat(i.split("WebKit/")[1])||undefined,t.isChrome=parseFloat(i.split(" Chrome/")[1])||undefined,t.isAIR=i.indexOf("AdobeAIR")>=0,t.isIPad=i.indexOf("iPad")>=0,t.isTouchPad=i.indexOf("TouchPad")>=0}),ace.define("ace/editor",["require","exports","module","ace/lib/fixoldbrowsers","ace/lib/oop","ace/lib/lang","ace/lib/useragent","ace/keyboard/textinput","ace/mouse/mouse_handler","ace/mouse/fold_handler","ace/keyboard/keybinding","ace/edit_session","ace/search","ace/range","ace/lib/event_emitter","ace/commands/command_manager","ace/commands/default_commands"],function(e,t,n){e("./lib/fixoldbrowsers");var r=e("./lib/oop"),i=e("./lib/lang"),s=e("./lib/useragent"),o=e("./keyboard/textinput").TextInput,u=e("./mouse/mouse_handler").MouseHandler,a=e("./mouse/fold_handler").FoldHandler,f=e("./keyboard/keybinding").KeyBinding,l=e("./edit_session").EditSession,c=e("./search").Search,h=e("./range").Range,p=e("./lib/event_emitter").EventEmitter,d=e("./commands/command_manager").CommandManager,v=e("./commands/default_commands").commands,m=function(e,t){var n=e.getContainerElement();this.container=n,this.renderer=e,this.commands=new d(s.isMac?"mac":"win",v),this.textInput=new o(e.getTextAreaContainer(),this),this.renderer.textarea=this.textInput.getElement(),this.keyBinding=new f(this),this.$mouseHandler=new u(this),new a(this),this.$blockScrolling=0,this.$search=(new c).set({wrap:!0}),this.setSession(t||new l(""))};(function(){r.implement(this,p),this.setKeyboardHandler=function(e){this.keyBinding.setKeyboardHandler(e)},this.getKeyboardHandler=function(){return this.keyBinding.getKeyboardHandler()},this.setSession=function(e){if(this.session==e)return;if(this.session){var t=this.session;this.session.removeEventListener("change",this.$onDocumentChange),this.session.removeEventListener("changeMode",this.$onChangeMode),this.session.removeEventListener("tokenizerUpdate",this.$onTokenizerUpdate),this.session.removeEventListener("changeTabSize",this.$onChangeTabSize),this.session.removeEventListener("changeWrapLimit",this.$onChangeWrapLimit),this.session.removeEventListener("changeWrapMode",this.$onChangeWrapMode),this.session.removeEventListener("onChangeFold",this.$onChangeFold),this.session.removeEventListener("changeFrontMarker",this.$onChangeFrontMarker),this.session.removeEventListener("changeBackMarker",this.$onChangeBackMarker),this.session.removeEventListener("changeBreakpoint",this.$onChangeBreakpoint),this.session.removeEventListener("changeAnnotation",this.$onChangeAnnotation),this.session.removeEventListener("changeOverwrite",this.$onCursorChange),this.session.removeEventListener("changeScrollTop",this.$onScrollTopChange),this.session.removeEventListener("changeLeftTop",this.$onScrollLeftChange);var n=this.session.getSelection();n.removeEventListener("changeCursor",this.$onCursorChange),n.removeEventListener("changeSelection",this.$onSelectionChange)}this.session=e,this.$onDocumentChange=this.onDocumentChange.bind(this),e.addEventListener("change",this.$onDocumentChange),this.renderer.setSession(e),this.$onChangeMode=this.onChangeMode.bind(this),e.addEventListener("changeMode",this.$onChangeMode),this.$onTokenizerUpdate=this.onTokenizerUpdate.bind(this),e.addEventListener("tokenizerUpdate",this.$onTokenizerUpdate),this.$onChangeTabSize=this.renderer.onChangeTabSize.bind(this.renderer),e.addEventListener("changeTabSize",this.$onChangeTabSize),this.$onChangeWrapLimit=this.onChangeWrapLimit.bind(this),e.addEventListener("changeWrapLimit",this.$onChangeWrapLimit),this.$onChangeWrapMode=this.onChangeWrapMode.bind(this),e.addEventListener("changeWrapMode",this.$onChangeWrapMode),this.$onChangeFold=this.onChangeFold.bind(this),e.addEventListener("changeFold",this.$onChangeFold),this.$onChangeFrontMarker=this.onChangeFrontMarker.bind(this),this.session.addEventListener("changeFrontMarker",this.$onChangeFrontMarker),this.$onChangeBackMarker=this.onChangeBackMarker.bind(this),this.session.addEventListener("changeBackMarker",this.$onChangeBackMarker),this.$onChangeBreakpoint=this.onChangeBreakpoint.bind(this),this.session.addEventListener("changeBreakpoint",this.$onChangeBreakpoint),this.$onChangeAnnotation=this.onChangeAnnotation.bind(this),this.session.addEventListener("changeAnnotation",this.$onChangeAnnotation),this.$onCursorChange=this.onCursorChange.bind(this),this.session.addEventListener("changeOverwrite",this.$onCursorChange),this.$onScrollTopChange=this.onScrollTopChange.bind(this),this.session.addEventListener("changeScrollTop",this.$onScrollTopChange),this.$onScrollLeftChange=this.onScrollLeftChange.bind(this),this.session.addEventListener("changeScrollLeft",this.$onScrollLeftChange),this.selection=e.getSelection(),this.selection.addEventListener("changeCursor",this.$onCursorChange),this.$onSelectionChange=this.onSelectionChange.bind(this),this.selection.addEventListener("changeSelection",this.$onSelectionChange),this.onChangeMode(),this.$blockScrolling+=1,this.onCursorChange(),this.$blockScrolling-=1,this.onScrollTopChange(),this.onScrollLeftChange(),this.onSelectionChange(),this.onChangeFrontMarker(),this.onChangeBackMarker(),this.onChangeBreakpoint(),this.onChangeAnnotation(),this.session.getUseWrapMode()&&this.renderer.adjustWrapLimit(),this.renderer.updateFull(),this._emit("changeSession",{session:e,oldSession:t})},this.getSession=function(){return this.session},this.setValue=function(e,t){return this.session.doc.setValue(e),t?t==1?this.navigateFileEnd():t==-1&&this.navigateFileStart():this.selectAll(),e},this.getValue=function(){return this.session.getValue()},this.getSelection=function(){return this.selection},this.resize=function(e){this.renderer.onResize(e)},this.setTheme=function(e){this.renderer.setTheme(e)},this.getTheme=function(){return this.renderer.getTheme()},this.setStyle=function(e){this.renderer.setStyle(e)},this.unsetStyle=function(e){this.renderer.unsetStyle(e)},this.setFontSize=function(e){this.container.style.fontSize=e,this.renderer.updateFontSize()},this.$highlightBrackets=function(){this.session.$bracketHighlight&&(this.session.removeMarker(this.session.$bracketHighlight),this.session.$bracketHighlight=null);if(this.$highlightPending)return;var e=this;this.$highlightPending=!0,setTimeout(function(){e.$highlightPending=!1;var t=e.session.findMatchingBracket(e.getCursorPosition());if(t){var n=new h(t.row,t.column,t.row,t.column+1);e.session.$bracketHighlight=e.session.addMarker(n,"ace_bracket","text")}},50)},this.focus=function(){var e=this;setTimeout(function(){e.textInput.focus()}),this.textInput.focus()},this.isFocused=function(){return this.textInput.isFocused()},this.blur=function(){this.textInput.blur()},this.onFocus=function(){if(this.$isFocused)return;this.$isFocused=!0,this.renderer.showCursor(),this.renderer.visualizeFocus(),this._emit("focus")},this.onBlur=function(){if(!this.$isFocused)return;this.$isFocused=!1,this.renderer.hideCursor(),this.renderer.visualizeBlur(),this._emit("blur")},this.$cursorChange=function(){this.renderer.updateCursor()},this.onDocumentChange=function(e){var t=e.data,n=t.range,r;n.start.row==n.end.row&&t.action!="insertLines"&&t.action!="removeLines"?r=n.end.row:r=Infinity,this.renderer.updateLines(n.start.row,r),this._emit("change",e),this.$cursorChange()},this.onTokenizerUpdate=function(e){var t=e.data;this.renderer.updateLines(t.first,t.last)},this.onScrollTopChange=function(){this.renderer.scrollToY(this.session.getScrollTop())},this.onScrollLeftChange=function(){this.renderer.scrollToX(this.session.getScrollLeft())},this.onCursorChange=function(){this.$cursorChange(),this.$blockScrolling||this.renderer.scrollCursorIntoView(),this.$highlightBrackets(),this.$updateHighlightActiveLine(),this._emit("changeSelection")},this.$updateHighlightActiveLine=function(){var e=this.getSession(),t;this.$highlightActiveLine&&(this.$selectionStyle!="line"||!this.selection.isMultiLine())&&(t=this.getCursorPosition()),e.$highlightLineMarker&&!t?(e.removeMarker(e.$highlightLineMarker.id),e.$highlightLineMarker=null):!e.$highlightLineMarker&&t?e.$highlightLineMarker=e.highlightLines(t.row,t.row,"ace_active-line"):t&&(e.$highlightLineMarker.start.row=t.row,e.$highlightLineMarker.end.row=t.row,e._emit("changeBackMarker"))},this.onSelectionChange=function(e){var t=this.session;t.$selectionMarker&&t.removeMarker(t.$selectionMarker),t.$selectionMarker=null;if(!this.selection.isEmpty()){var n=this.selection.getRange(),r=this.getSelectionStyle();t.$selectionMarker=t.addMarker(n,"ace_selection",r)}else this.$updateHighlightActiveLine();var i=this.$highlightSelectedWord&&this.$getSelectionHighLightRegexp();this.session.highlight(i),this._emit("changeSelection")},this.$getSelectionHighLightRegexp=function(){var e=this.session,t=this.getSelectionRange();if(t.isEmpty()||t.isMultiLine())return;var n=t.start.column-1,r=t.end.column+1,i=e.getLine(t.start.row),s=i.length,o=i.substring(Math.max(n,0),Math.min(r,s));if(n>=0&&/^[\w\d]/.test(o)||r<=s&&/[\w\d]$/.test(o))return;o=i.substring(t.start.column,t.end.column);if(!/^[\w\d]+$/.test(o))return;var u=this.$search.$assembleRegExp({wholeWord:!0,caseSensitive:!0,needle:o});return u},this.onChangeFrontMarker=function(){this.renderer.updateFrontMarkers()},this.onChangeBackMarker=function(){this.renderer.updateBackMarkers()},this.onChangeBreakpoint=function(){this.renderer.updateBreakpoints()},this.onChangeAnnotation=function(){this.renderer.setAnnotations(this.session.getAnnotations())},this.onChangeMode=function(){this.renderer.updateText()},this.onChangeWrapLimit=function(){this.renderer.updateFull()},this.onChangeWrapMode=function(){this.renderer.onResize(!0)},this.onChangeFold=function(){this.$updateHighlightActiveLine(),this.renderer.updateFull()},this.getCopyText=function(){var e="";return this.selection.isEmpty()||(e=this.session.getTextRange(this.getSelectionRange())),this._emit("copy",e),e},this.onCopy=function(){this.commands.exec("copy",this)},this.onCut=function(){this.commands.exec("cut",this)},this.onPaste=function(e){if(this.$readOnly)return;this._emit("paste",e),this.insert(e)},this.execCommand=function(e,t){this.commands.exec(e,this,t)},this.insert=function(e){var t=this.session,n=t.getMode(),r=this.getCursorPosition();if(this.getBehavioursEnabled()){var i=n.transformAction(t.getState(r.row),"insertion",this,t,e);i&&(e=i.text)}e=e.replace(" ",this.session.getTabString());if(!this.selection.isEmpty())r=this.session.remove(this.getSelectionRange()),this.clearSelection();else if(this.session.getOverwrite()){var s=new h.fromPoints(r,r);s.end.column+=e.length,this.session.remove(s)}this.clearSelection();var o=r.column,u=t.getState(r.row),a=t.getLine(r.row),f=n.checkOutdent(u,a,e),l=t.insert(r,e);i&&i.selection&&(i.selection.length==2?this.selection.setSelectionRange(new h(r.row,o+i.selection[0],r.row,o+i.selection[1])):this.selection.setSelectionRange(new h(r.row+i.selection[0],i.selection[1],r.row+i.selection[2],i.selection[3])));if(t.getDocument().isNewLine(e)){var c=n.getNextLineIndent(u,a.slice(0,r.column),t.getTabString());this.moveCursorTo(r.row+1,0);var p=t.getTabSize(),d=Number.MAX_VALUE;for(var v=r.row+1;v<=l.row;++v){var m=0;a=t.getLine(v);for(var g=0;g<a.length;++g)if(a.charAt(g)==" ")m+=p;else{if(a.charAt(g)!=" ")break;m+=1}/[^\s]/.test(a)&&(d=Math.min(m,d))}for(var v=r.row+1;v<=l.row;++v){var y=d;a=t.getLine(v);for(var g=0;g<a.length&&y>0;++g)a.charAt(g)==" "?y-=p:a.charAt(g)==" "&&(y-=1);t.remove(new h(v,0,v,g))}t.indentRows(r.row+1,l.row,c)}f&&n.autoOutdent(u,t,r.row)},this.onTextInput=function(e){this.keyBinding.onTextInput(e)},this.onCommandKey=function(e,t,n){this.keyBinding.onCommandKey(e,t,n)},this.setOverwrite=function(e){this.session.setOverwrite(e)},this.getOverwrite=function(){return this.session.getOverwrite()},this.toggleOverwrite=function(){this.session.toggleOverwrite()},this.setScrollSpeed=function(e){this.$mouseHandler.setScrollSpeed(e)},this.getScrollSpeed=function(){return this.$mouseHandler.getScrollSpeed()},this.setDragDelay=function(e){this.$mouseHandler.setDragDelay(e)},this.getDragDelay=function(){return this.$mouseHandler.getDragDelay()},this.$selectionStyle="line",this.setSelectionStyle=function(e){if(this.$selectionStyle==e)return;this.$selectionStyle=e,this.onSelectionChange(),this._emit("changeSelectionStyle",{data:e})},this.getSelectionStyle=function(){return this.$selectionStyle},this.$highlightActiveLine=!0,this.setHighlightActiveLine=function(e){if(this.$highlightActiveLine==e)return;this.$highlightActiveLine=e,this.$updateHighlightActiveLine()},this.getHighlightActiveLine=function(){return this.$highlightActiveLine},this.$highlightGutterLine=!0,this.setHighlightGutterLine=function(e){if(this.$highlightGutterLine==e)return;this.renderer.setHighlightGutterLine(e),this.$highlightGutterLine=e},this.getHighlightGutterLine=function(){return this.$highlightGutterLine},this.$highlightSelectedWord=!0,this.setHighlightSelectedWord=function(e){if(this.$highlightSelectedWord==e)return;this.$highlightSelectedWord=e,this.$onSelectionChange()},this.getHighlightSelectedWord=function(){return this.$highlightSelectedWord},this.setAnimatedScroll=function(e){this.renderer.setAnimatedScroll(e)},this.getAnimatedScroll=function(){return this.renderer.getAnimatedScroll()},this.setShowInvisibles=function(e){this.renderer.setShowInvisibles(e)},this.getShowInvisibles=function(){return this.renderer.getShowInvisibles()},this.setDisplayIndentGuides=function(e){this.renderer.setDisplayIndentGuides(e)},this.getDisplayIndentGuides=function(){return this.renderer.getDisplayIndentGuides()},this.setShowPrintMargin=function(e){this.renderer.setShowPrintMargin(e)},this.getShowPrintMargin=function(){return this.renderer.getShowPrintMargin()},this.setPrintMarginColumn=function(e){this.renderer.setPrintMarginColumn(e)},this.getPrintMarginColumn=function(){return this.renderer.getPrintMarginColumn()},this.$readOnly=!1,this.setReadOnly=function(e){this.$readOnly=e},this.getReadOnly=function(){return this.$readOnly},this.$modeBehaviours=!0,this.setBehavioursEnabled=function(e){this.$modeBehaviours=e},this.getBehavioursEnabled=function(){return this.$modeBehaviours},this.$modeWrapBehaviours=!0,this.setWrapBehavioursEnabled=function(e){this.$modeWrapBehaviours=e},this.getWrapBehavioursEnabled=function(){return this.$modeWrapBehaviours},this.setShowFoldWidgets=function(e){var t=this.renderer.$gutterLayer;if(t.getShowFoldWidgets()==e)return;this.renderer.$gutterLayer.setShowFoldWidgets(e),this.$showFoldWidgets=e,this.renderer.updateFull()},this.getShowFoldWidgets=function(){return this.renderer.$gutterLayer.getShowFoldWidgets()},this.setFadeFoldWidgets=function(e){this.renderer.setFadeFoldWidgets(e)},this.getFadeFoldWidgets=function(){return this.renderer.getFadeFoldWidgets()},this.remove=function(e){this.selection.isEmpty()&&(e=="left"?this.selection.selectLeft():this.selection.selectRight());var t=this.getSelectionRange();if(this.getBehavioursEnabled()){var n=this.session,r=n.getState(t.start.row),i=n.getMode().transformAction(r,"deletion",this,n,t);i&&(t=i)}this.session.remove(t),this.clearSelection()},this.removeWordRight=function(){this.selection.isEmpty()&&this.selection.selectWordRight(),this.session.remove(this.getSelectionRange()),this.clearSelection()},this.removeWordLeft=function(){this.selection.isEmpty()&&this.selection.selectWordLeft(),this.session.remove(this.getSelectionRange()),this.clearSelection()},this.removeToLineStart=function(){this.selection.isEmpty()&&this.selection.selectLineStart(),this.session.remove(this.getSelectionRange()),this.clearSelection()},this.removeToLineEnd=function(){this.selection.isEmpty()&&this.selection.selectLineEnd();var e=this.getSelectionRange();e.start.column==e.end.column&&e.start.row==e.end.row&&(e.end.column=0,e.end.row++),this.session.remove(e),this.clearSelection()},this.splitLine=function(){this.selection.isEmpty()||(this.session.remove(this.getSelectionRange()),this.clearSelection());var e=this.getCursorPosition();this.insert("\n"),this.moveCursorToPosition(e)},this.transposeLetters=function(){if(!this.selection.isEmpty())return;var e=this.getCursorPosition(),t=e.column;if(t===0)return;var n=this.session.getLine(e.row),r,i;t<n.length?(r=n.charAt(t)+n.charAt(t-1),i=new h(e.row,t-1,e.row,t+1)):(r=n.charAt(t-1)+n.charAt(t-2),i=new h(e.row,t-2,e.row,t)),this.session.replace(i,r)},this.toLowerCase=function(){var e=this.getSelectionRange();this.selection.isEmpty()&&this.selection.selectWord();var t=this.getSelectionRange(),n=this.session.getTextRange(t);this.session.replace(t,n.toLowerCase()),this.selection.setSelectionRange(e)},this.toUpperCase=function(){var e=this.getSelectionRange();this.selection.isEmpty()&&this.selection.selectWord();var t=this.getSelectionRange(),n=this.session.getTextRange(t);this.session.replace(t,n.toUpperCase()),this.selection.setSelectionRange(e)},this.indent=function(){var e=this.session,t=this.getSelectionRange();if(!(t.start.row<t.end.row||t.start.column<t.end.column)){var r;if(this.session.getUseSoftTabs()){var s=e.getTabSize(),o=this.getCursorPosition(),u=e.documentToScreenColumn(o.row,o.column),a=s-u%s;r=i.stringRepeat(" ",a)}else r=" ";return this.insert(r)}var n=this.$getSelectedRows();e.indentRows(n.first,n.last," ")},this.blockOutdent=function(){var e=this.session.getSelection();this.session.outdentRows(e.getRange())},this.sortLines=function(){var e=this.$getSelectedRows(),t=this.session,n=[];for(i=e.first;i<=e.last;i++)n.push(t.getLine(i));n.sort(function(e,t){return e.toLowerCase()<t.toLowerCase()?-1:e.toLowerCase()>t.toLowerCase()?1:0});var r=new h(0,0,0,0);for(var i=e.first;i<=e.last;i++){var s=t.getLine(i);r.start.row=i,r.end.row=i,r.end.column=s.length,t.replace(r,n[i-e.first])}},this.toggleCommentLines=function(){var e=this.session.getState(this.getCursorPosition().row),t=this.$getSelectedRows();this.session.getMode().toggleCommentLines(e,this.session,t.first,t.last)},this.getNumberAt=function(e,t){var n=/[\-]?[0-9]+(?:\.[0-9]+)?/g;n.lastIndex=0;var r=this.session.getLine(e);while(n.lastIndex<t-1){var i=n.exec(r);if(i.index<=t&&i.index+i[0].length>=t){var s={value:i[0],start:i.index,end:i.index+i[0].length};return s}}return null},this.modifyNumber=function(e){var t=this.selection.getCursor().row,n=this.selection.getCursor().column,r=new h(t,n-1,t,n),i=this.session.getTextRange(r);if(!isNaN(parseFloat(i))&&isFinite(i)){var s=this.getNumberAt(t,n);if(s){var o=s.value.indexOf(".")>=0?s.start+s.value.indexOf(".")+1:s.end,u=s.start+s.value.length-o,a=parseFloat(s.value);a*=Math.pow(10,u),o!==s.end&&n<o?e*=Math.pow(10,s.end-n-1):e*=Math.pow(10,s.end-n),a+=e,a/=Math.pow(10,u);var f=a.toFixed(u),l=new h(t,s.start,t,s.end);this.session.replace(l,f),this.moveCursorTo(t,Math.max(s.start+1,n+f.length-s.value.length))}}},this.removeLines=function(){var e=this.$getSelectedRows(),t;e.first===0||e.last+1<this.session.getLength()?t=new h(e.first,0,e.last+1,0):t=new h(e.first-1,this.session.getLine(e.first-1).length,e.last,this.session.getLine(e.last).length),this.session.remove(t),this.clearSelection()},this.duplicateSelection=function(){var e=this.selection,t=this.session,n=e.getRange();if(n.isEmpty()){var r=n.start.row;t.duplicateLines(r,r)}else{var i=e.isBackwards(),s=e.isBackwards()?n.start:n.end,o=t.insert(s,t.getTextRange(n),!1);n.start=s,n.end=o,e.setSelectionRange(n,i)}},this.moveLinesDown=function(){this.$moveLines(function(e,t){return this.session.moveLinesDown(e,t)})},this.moveLinesUp=function(){this.$moveLines(function(e,t){return this.session.moveLinesUp(e,t)})},this.moveText=function(e,t){return this.$readOnly?null:this.session.moveText(e,t)},this.copyLinesUp=function(){this.$moveLines(function(e,t){return this.session.duplicateLines(e,t),0})},this.copyLinesDown=function(){this.$moveLines(function(e,t){return this.session.duplicateLines(e,t)})},this.$moveLines=function(e){var t=this.$getSelectedRows(),n=this.selection;if(!n.isMultiLine())var r=n.getRange(),i=n.isBackwards();var s=e.call(this,t.first,t.last);r?(r.start.row+=s,r.end.row+=s,n.setSelectionRange(r,i)):(n.setSelectionAnchor(t.last+s+1,0),n.$moveSelection(function(){n.moveCursorTo(t.first+s,0)}))},this.$getSelectedRows=function(){var e=this.getSelectionRange().collapseRows();return{first:e.start.row,last:e.end.row}},this.onCompositionStart=function(e){this.renderer.showComposition(this.getCursorPosition())},this.onCompositionUpdate=function(e){this.renderer.setCompositionText(e)},this.onCompositionEnd=function(){this.renderer.hideComposition()},this.getFirstVisibleRow=function(){return this.renderer.getFirstVisibleRow()},this.getLastVisibleRow=function(){return this.renderer.getLastVisibleRow()},this.isRowVisible=function(e){return e>=this.getFirstVisibleRow()&&e<=this.getLastVisibleRow()},this.isRowFullyVisible=function(e){return e>=this.renderer.getFirstFullyVisibleRow()&&e<=this.renderer.getLastFullyVisibleRow()},this.$getVisibleRowCount=function(){return this.renderer.getScrollBottomRow()-this.renderer.getScrollTopRow()+1},this.$moveByPage=function(e,t){var n=this.renderer,r=this.renderer.layerConfig,i=e*Math.floor(r.height/r.lineHeight);this.$blockScrolling++,t==1?this.selection.$moveSelection(function(){this.moveCursorBy(i,0)}):t==0&&(this.selection.moveCursorBy(i,0),this.selection.clearSelection()),this.$blockScrolling--;var s=n.scrollTop;n.scrollBy(0,i*r.lineHeight),t!=null&&n.scrollCursorIntoView(null,.5),n.animateScrolling(s)},this.selectPageDown=function(){this.$moveByPage(1,!0)},this.selectPageUp=function(){this.$moveByPage(-1,!0)},this.gotoPageDown=function(){this.$moveByPage(1,!1)},this.gotoPageUp=function(){this.$moveByPage(-1,!1)},this.scrollPageDown=function(){this.$moveByPage(1)},this.scrollPageUp=function(){this.$moveByPage(-1)},this.scrollToRow=function(e){this.renderer.scrollToRow(e)},this.scrollToLine=function(e,t,n,r){this.renderer.scrollToLine(e,t,n,r)},this.centerSelection=function(){var e=this.getSelectionRange(),t={row:Math.floor(e.start.row+(e.end.row-e.start.row)/2),column:Math.floor(e.start.column+(e.end.column-e.start.column)/2)};this.renderer.alignCursor(t,.5)},this.getCursorPosition=function(){return this.selection.getCursor()},this.getCursorPositionScreen=function(){return this.session.documentToScreenPosition(this.getCursorPosition())},this.getSelectionRange=function(){return this.selection.getRange()},this.selectAll=function(){this.$blockScrolling+=1,this.selection.selectAll(),this.$blockScrolling-=1},this.clearSelection=function(){this.selection.clearSelection()},this.moveCursorTo=function(e,t){this.selection.moveCursorTo(e,t)},this.moveCursorToPosition=function(e){this.selection.moveCursorToPosition(e)},this.jumpToMatching=function(e){var t=this.getCursorPosition(),n=this.session.getBracketRange(t);if(!n){n=this.find({needle:/[{}()\[\]]/g,preventScroll:!0,start:{row:t.row,column:t.column-1}});if(!n)return;var r=n.start;r.row==t.row&&Math.abs(r.column-t.column)<2&&(n=this.session.getBracketRange(r))}r=n&&n.cursor||r,r&&(e?n&&n.isEqual(this.getSelectionRange())?this.clearSelection():this.selection.selectTo(r.row,r.column):(this.clearSelection(),this.moveCursorTo(r.row,r.column)))},this.gotoLine=function(e,t,n){this.selection.clearSelection(),this.session.unfold({row:e-1,column:t||0}),this.$blockScrolling+=1,this.moveCursorTo(e-1,t||0),this.$blockScrolling-=1,this.isRowFullyVisible(e-1)||this.scrollToLine(e-1,!0,n)},this.navigateTo=function(e,t){this.clearSelection(),this.moveCursorTo(e,t)},this.navigateUp=function(e){this.selection.clearSelection(),e=e||1,this.selection.moveCursorBy(-e,0)},this.navigateDown=function(e){this.selection.clearSelection(),e=e||1,this.selection.moveCursorBy(e,0)},this.navigateLeft=function(e){if(!this.selection.isEmpty()){var t=this.getSelectionRange().start;this.moveCursorToPosition(t)}else{e=e||1;while(e--)this.selection.moveCursorLeft()}this.clearSelection()},this.navigateRight=function(e){if(!this.selection.isEmpty()){var t=this.getSelectionRange().end;this.moveCursorToPosition(t)}else{e=e||1;while(e--)this.selection.moveCursorRight()}this.clearSelection()},this.navigateLineStart=function(){this.selection.moveCursorLineStart(),this.clearSelection()},this.navigateLineEnd=function(){this.selection.moveCursorLineEnd(),this.clearSelection()},this.navigateFileEnd=function(){var e=this.renderer.scrollTop;this.selection.moveCursorFileEnd(),this.clearSelection(),this.renderer.animateScrolling(e)},this.navigateFileStart=function(){var e=this.renderer.scrollTop;this.selection.moveCursorFileStart(),this.clearSelection(),this.renderer.animateScrolling(e)},this.navigateWordRight=function(){this.selection.moveCursorWordRight(),this.clearSelection()},this.navigateWordLeft=function(){this.selection.moveCursorWordLeft(),this.clearSelection()},this.replace=function(e,t){t&&this.$search.set(t);var n=this.$search.find(this.session),r=0;return n?(this.$tryReplace(n,e)&&(r=1),n!==null&&(this.selection.setSelectionRange(n),this.renderer.scrollSelectionIntoView(n.start,n.end)),r):r},this.replaceAll=function(e,t){t&&this.$search.set(t);var n=this.$search.findAll(this.session),r=0;if(!n.length)return r;this.$blockScrolling+=1;var i=this.getSelectionRange();this.clearSelection(),this.selection.moveCursorTo(0,0);for(var s=n.length-1;s>=0;--s)this.$tryReplace(n[s],e)&&r++;return this.selection.setSelectionRange(i),this.$blockScrolling-=1,r},this.$tryReplace=function(e,t){var n=this.session.getTextRange(e);return t=this.$search.replace(n,t),t!==null?(e.end=this.session.replace(e,t),e):null},this.getLastSearchOptions=function(){return this.$search.getOptions()},this.find=function(e,t,n){t||(t={}),typeof e=="string"||e instanceof RegExp?t.needle=e:typeof e=="object"&&r.mixin(t,e);var i=this.selection.getRange();t.needle==null&&(e=this.session.getTextRange(i)||this.$search.$options.needle,e||(i=this.session.getWordRange(i.start.row,i.start.column),e=this.session.getTextRange(i)),this.$search.set({needle:e})),this.$search.set(t),t.start||this.$search.set({start:i});var s=this.$search.find(this.session);if(t.preventScroll)return s;if(s)return this.revealRange(s,n),s;t.backwards?i.start=i.end:i.end=i.start,this.selection.setRange(i)},this.findNext=function(e,t){this.find({skipCurrent:!0,backwards:!1},e,t)},this.findPrevious=function(e,t){this.find(e,{skipCurrent:!0,backwards:!0},t)},this.revealRange=function(e,t){this.$blockScrolling+=1,this.session.unfold(e),this.selection.setSelectionRange(e),this.$blockScrolling-=1;var n=this.renderer.scrollTop;this.renderer.scrollSelectionIntoView(e.start,e.end,.5),t!=0&&this.renderer.animateScrolling(n)},this.undo=function(){this.$blockScrolling++,this.session.getUndoManager().undo(),this.$blockScrolling--,this.renderer.scrollCursorIntoView(null,.5)},this.redo=function(){this.$blockScrolling++,this.session.getUndoManager().redo(),this.$blockScrolling--,this.renderer.scrollCursorIntoView(null,.5)},this.destroy=function(){this.renderer.destroy()}}).call(m.prototype),t.Editor=m}),ace.define("ace/lib/lang",["require","exports","module"],function(e,t,n){t.stringReverse=function(e){return e.split("").reverse().join("")},t.stringRepeat=function(e,t){return(new Array(t+1)).join(e)};var r=/^\s\s*/,i=/\s\s*$/;t.stringTrimLeft=function(e){return e.replace(r,"")},t.stringTrimRight=function(e){return e.replace(i,"")},t.copyObject=function(e){var t={};for(var n in e)t[n]=e[n];return t},t.copyArray=function(e){var t=[];for(var n=0,r=e.length;n<r;n++)e[n]&&typeof e[n]=="object"?t[n]=this.copyObject(e[n]):t[n]=e[n];return t},t.deepCopy=function(e){if(typeof e!="object")return e;var t=e.constructor();for(var n in e)typeof e[n]=="object"?t[n]=this.deepCopy(e[n]):t[n]=e[n];return t},t.arrayToMap=function(e){var t={};for(var n=0;n<e.length;n++)t[e[n]]=1;return t},t.createMap=function(e){var t=Object.create(null);for(var n in e)t[n]=e[n];return t},t.arrayRemove=function(e,t){for(var n=0;n<=e.length;n++)t===e[n]&&e.splice(n,1)},t.escapeRegExp=function(e){return e.replace(/([.*+?^${}()|[\]\/\\])/g,"\\$1")},t.escapeHTML=function(e){return e.replace(/&/g,"&#38;").replace(/"/g,"&#34;").replace(/'/g,"&#39;").replace(/</g,"&#60;")},t.getMatchOffsets=function(e,t){var n=[];return e.replace(t,function(e){n.push({offset:arguments[arguments.length-2],length:e.length})}),n},t.deferredCall=function(e){var t=null,n=function(){t=null,e()},r=function(e){return r.cancel(),t=setTimeout(n,e||0),r};return r.schedule=r,r.call=function(){return this.cancel(),e(),r},r.cancel=function(){return clearTimeout(t),t=null,r},r},t.delayedCall=function(e,t){var n=null,r=function(){n=null,e()},i=function(e){n&&clearTimeout(n),n=setTimeout(r,e||t)};return i.delay=i,i.schedule=function(e){n==null&&(n=setTimeout(r,e||0))},i.call=function(){this.cancel(),e()},i.cancel=function(){n&&clearTimeout(n),n=null},i.isPending=function(){return n},i}}),ace.define("ace/keyboard/textinput",["require","exports","module","ace/lib/event","ace/lib/useragent","ace/lib/dom","ace/lib/lang"],function(e,t,n){var r=e("../lib/event"),i=e("../lib/useragent"),s=e("../lib/dom"),o=e("../lib/lang"),u=function(e,t){function g(e){if(c)return;var t=e?2:1,r=2;try{n.setSelectionRange(t,r)}catch(i){}}function y(){if(c)return;n.value=u,i.isWebKit&&m.schedule()}function D(){setTimeout(function(){h&&(n.style.cssText=h,h=""),t.renderer.$keepTextAreaAtCursor==null&&(t.renderer.$keepTextAreaAtCursor=!0,t.renderer.$moveTextAreaToCursor())},0)}var n=s.createElement("textarea");n.className="ace_text-input",i.isTouchPad&&n.setAttribute("x-palm-disable-auto-cap",!0),n.wrap="off",n.autocorrect="off",n.autocapitalize="off",n.spellcheck=!1,n.style.top="-2em",e.insertBefore(n,e.firstChild);var u="",a=!1,f=!1,l=!1,c=!1,h="",p=!0,d=document.activeElement===n;r.addListener(n,"blur",function(){t.onBlur(),d=!1}),r.addListener(n,"focus",function(){d=!0,t.onFocus(),g()}),this.focus=function(){n.focus()},this.blur=function(){n.blur()},this.isFocused=function(){return d};var v=o.delayedCall(function(){d&&g(p)}),m=o.delayedCall(function(){c||(n.value=u,d&&g())});i.isWebKit||t.addEventListener("changeSelection",function(){t.selection.isEmpty()!=p&&(p=!p,v.schedule())}),y(),d&&t.onFocus();var b=function(e){return e.selectionStart===0&&e.selectionEnd===e.value.length};!n.setSelectionRange&&n.createTextRange&&(n.setSelectionRange=function(e,t){var n=this.createTextRange();n.collapse(!0),n.moveStart("character",e),n.moveEnd("character",t),n.select()},b=function(e){try{var t=e.ownerDocument.selection.createRange()}catch(n){}return!t||t.parentElement()!=e?!1:t.text==e.value});if(i.isOldIE){var w=!1,E=function(e){if(w)return;var t=n.value;if(c||!t||t==u)return;if(e&&t==u[0])return S.schedule();N(t),w=!0,y(),w=!1},S=o.delayedCall(E);r.addListener(n,"propertychange",E);var x={13:1,27:1};r.addListener(n,"keyup",function(e){c&&(!n.value||x[e.keyCode])&&setTimeout(_,0);if((n.value.charCodeAt(0)||0)<129)return;c?M():O()})}var T=function(e){if(a){a=!1;return}if(f){f=!1;return}b(n)&&(t.selectAll(),g())},N=function(e){l?(g(),e&&t.onPaste(e),l=!1):e==u[0]?t.execCommand("del",{source:"ace"}):(e.substring(0,2)==u?e=e.substr(2):e[0]==u[0]?e=e.substr(1):e[e.length-1]==u[0]&&(e=e.slice(0,-1)),e[e.length-1]==u[0]&&(e=e.slice(0,-1)),e&&t.onTextInput(e))},C=function(e){if(c)return;var t=n.value;y(),N(t)},k=function(e){var i=t.getCopyText();if(!i){r.preventDefault(e);return}var s=e.clipboardData||window.clipboardData;if(s){var o=s.setData("Text",i);o&&(t.onCut(),r.preventDefault(e))}o||(a=!0,n.value=i,n.select(),setTimeout(function(){a=!1,y(),g(),t.onCut()}))},L=function(e){var i=t.getCopyText();if(!i){r.preventDefault(e);return}var s=e.clipboardData||window.clipboardData;if(s){var o=s.setData("Text",i);o&&(t.onCopy(),r.preventDefault(e))}o||(f=!0,n.value=i,n.select(),setTimeout(function(){f=!1,y(),g(),t.onCopy()}))},A=function(e){var s=e.clipboardData||window.clipboardData;if(s){var o=s.getData("Text");o&&t.onPaste(o),i.isIE&&setTimeout(g),r.preventDefault(e)}else n.value="",l=!0};r.addCommandKeyListener(n,t.onCommandKey.bind(t)),r.addListener(n,"select",T),r.addListener(n,"input",C),r.addListener(n,"cut",k),r.addListener(n,"copy",L),r.addListener(n,"paste",A),(!("oncut"in n)||!("oncopy"in n)||!("onpaste"in n))&&r.addListener(e,"keydown",function(e){if(i.isMac&&!e.metaKey||!e.ctrlKey)return;switch(e.keyCode){case 67:L(e);break;case 86:A(e);break;case 88:k(e)}});var O=function(e){c=!0,t.onCompositionStart(),setTimeout(M,0)},M=function(){if(!c)return;t.onCompositionUpdate(n.value)},_=function(e){c=!1,t.onCompositionEnd()};r.addListener(n,"compositionstart",O),i.isGecko?r.addListener(n,"text",M):r.addListener(n,"keyup",M),r.addListener(n,"compositionend",_),this.getElement=function(){return n},this.onContextMenu=function(e){h||(h=n.style.cssText),n.style.cssText="z-index:100000;"+(i.isIE?"opacity:0.1;":""),g(t.selection.isEmpty()),t._emit("nativecontextmenu",{target:t});var s=t.container.getBoundingClientRect(),o=function(e){n.style.left=e.clientX-s.left-2+"px",n.style.top=e.clientY-s.top-2+"px"};o(e);if(e.type!="mousedown")return;t.renderer.$keepTextAreaAtCursor&&(t.renderer.$keepTextAreaAtCursor=null),i.isWin&&r.capture(t.container,o,D)},this.onContextMenuClose=D,i.isGecko||r.addListener(n,"contextmenu",function(e){t.textInput.onContextMenu(e),D()})};t.TextInput=u}),ace.define("ace/mouse/mouse_handler",["require","exports","module","ace/lib/event","ace/lib/useragent","ace/mouse/default_handlers","ace/mouse/default_gutter_handler","ace/mouse/mouse_event","ace/mouse/dragdrop"],function(e,t,n){var r=e("../lib/event"),i=e("../lib/useragent"),s=e("./default_handlers").DefaultHandlers,o=e("./default_gutter_handler").GutterHandler,u=e("./mouse_event").MouseEvent,a=e("./dragdrop").DragdropHandler,f=function(e){this.editor=e,new s(this),new o(this),new a(this),r.addListener(e.container,"mousedown",function(t){return e.focus(),r.preventDefault(t)});var t=e.renderer.getMouseEventTarget();r.addListener(t,"click",this.onMouseEvent.bind(this,"click")),r.addListener(t,"mousemove",this.onMouseMove.bind(this,"mousemove")),r.addMultiMouseDownListener(t,[300,300,250],this,"onMouseEvent"),r.addMouseWheelListener(e.container,this.onMouseWheel.bind(this,"mousewheel"));var n=e.renderer.$gutter;r.addListener(n,"mousedown",this.onMouseEvent.bind(this,"guttermousedown")),r.addListener(n,"click",this.onMouseEvent.bind(this,"gutterclick")),r.addListener(n,"dblclick",this.onMouseEvent.bind(this,"gutterdblclick")),r.addListener(n,"mousemove",this.onMouseEvent.bind(this,"guttermousemove"))};(function(){this.$scrollSpeed=1,this.setScrollSpeed=function(e){this.$scrollSpeed=e},this.getScrollSpeed=function(){return this.$scrollSpeed},this.onMouseEvent=function(e,t){this.editor._emit(e,new u(t,this.editor))},this.$dragDelay=250,this.setDragDelay=function(e){this.$dragDelay=e},this.getDragDelay=function(){return this.$dragDelay},this.onMouseMove=function(e,t){var n=this.editor._eventRegistry&&this.editor._eventRegistry.mousemove;if(!n||!n.length)return;this.editor._emit(e,new u(t,this.editor))},this.onMouseWheel=function(e,t){var n=new u(t,this.editor);n.speed=this.$scrollSpeed*2,n.wheelX=t.wheelX,n.wheelY=t.wheelY,this.editor._emit(e,n)},this.setState=function(e){this.state=e},this.captureMouse=function(e,t){t&&this.setState(t),this.x=e.x,this.y=e.y,this.isMousePressed=!0;var n=this.editor.renderer;n.$keepTextAreaAtCursor&&(n.$keepTextAreaAtCursor=null);var s=this,o=function(e){s.x=e.clientX,s.y=e.clientY},u=function(e){clearInterval(f),s[s.state+"End"]&&s[s.state+"End"](e),s.$clickSelection=null,n.$keepTextAreaAtCursor==null&&(n.$keepTextAreaAtCursor=!0,n.$moveTextAreaToCursor()),s.isMousePressed=!1},a=function(){s[s.state]&&s[s.state]()};if(i.isOldIE&&e.domEvent.type=="dblclick"){setTimeout(function(){a(),u(e.domEvent)});return}r.capture(this.editor.container,o,u);var f=setInterval(a,20)}}).call(f.prototype),t.MouseHandler=f}),ace.define("ace/mouse/default_handlers",["require","exports","module","ace/lib/dom","ace/lib/useragent"],function(e,t,n){function o(e){e.$clickSelection=null;var t=e.editor;t.setDefaultHandler("mousedown",this.onMouseDown.bind(e)),t.setDefaultHandler("dblclick",this.onDoubleClick.bind(e)),t.setDefaultHandler("tripleclick",this.onTripleClick.bind(e)),t.setDefaultHandler("quadclick",this.onQuadClick.bind(e)),t.setDefaultHandler("mousewheel",this.onMouseWheel.bind(e));var n=["select","startSelect","drag","dragEnd","dragWait","dragWaitEnd","startDrag","focusWait"];n.forEach(function(t){e[t]=this[t]},this),e.selectByLines=this.extendSelectionBy.bind(e,"getLineRange"),e.selectByWords=this.extendSelectionBy.bind(e,"getWordRange"),e.$focusWaitTimout=250}function u(e,t,n,r){return Math.sqrt(Math.pow(n-e,2)+Math.pow(r-t,2))}function a(e,t){if(e.start.row==e.end.row)var n=2*t.column-e.start.column-e.end.column;else var n=2*t.row-e.start.row-e.end.row;return n<0?{cursor:e.start,anchor:e.end}:{cursor:e.end,anchor:e.start}}var r=e("../lib/dom"),i=e("../lib/useragent"),s=5;(function(){this.onMouseDown=function(e){var t=e.inSelection(),n=e.getDocumentPosition();this.mousedownEvent=e;var r=this.editor,i=e.getButton();if(i!==0){var s=r.getSelectionRange(),o=s.isEmpty();o&&(r.moveCursorToPosition(n),r.selection.clearSelection()),r.textInput.onContextMenu(e.domEvent);return}if(t&&!r.isFocused()){r.focus();if(this.$focusWaitTimout&&!this.$clickSelection)return this.setState("focusWait"),this.captureMouse(e),e.preventDefault()}return!t||this.$clickSelection||e.getShiftKey()?this.startSelect(n):t&&(this.mousedownEvent.time=(new Date).getTime(),this.setState("dragWait")),this.captureMouse(e),e.preventDefault()},this.startSelect=function(e){e=e||this.editor.renderer.screenToTextCoordinates(this.x,this.y),this.mousedownEvent.getShiftKey()?this.editor.selection.selectToPosition(e):this.$clickSelection||(this.editor.moveCursorToPosition(e),this.editor.selection.clearSelection()),this.setState("select")},this.select=function(){var e,t=this.editor,n=t.renderer.screenToTextCoordinates(this.x,this.y);if(this.$clickSelection){var r=this.$clickSelection.comparePoint(n);if(r==-1)e=this.$clickSelection.end;else if(r==1)e=this.$clickSelection.start;else{var i=a(this.$clickSelection,n);n=i.cursor,e=i.anchor}t.selection.setSelectionAnchor(e.row,e.column)}t.selection.selectToPosition(n),t.renderer.scrollCursorIntoView()},this.extendSelectionBy=function(e){var t,n=this.editor,r=n.renderer.screenToTextCoordinates(this.x,this.y),i=n.selection[e](r.row,r.column);if(this.$clickSelection){var s=this.$clickSelection.comparePoint(i.start),o=this.$clickSelection.comparePoint(i.end);if(s==-1&&o<=0){t=this.$clickSelection.end;if(i.end.row!=r.row||i.end.column!=r.column)r=i.start}else if(o==1&&s>=0){t=this.$clickSelection.start;if(i.start.row!=r.row||i.start.column!=r.column)r=i.end}else if(s==-1&&o==1)r=i.end,t=i.start;else{var u=a(this.$clickSelection,r);r=u.cursor,t=u.anchor}n.selection.setSelectionAnchor(t.row,t.column)}n.selection.selectToPosition(r),n.renderer.scrollCursorIntoView()},this.startDrag=function(){var e=this.editor;this.setState("drag"),this.dragRange=e.getSelectionRange();var t=e.getSelectionStyle();this.dragSelectionMarker=e.session.addMarker(this.dragRange,"ace_selection",t),e.clearSelection(),r.addCssClass(e.container,"ace_dragging"),this.$dragKeybinding||(this.$dragKeybinding={handleKeyboard:function(e,t,n,r){if(n=="esc")return{command:this.command}},command:{exec:function(e){var t=e.$mouseHandler;t.dragCursor=null,t.dragEnd(),t.startSelect()}}}),e.keyBinding.addKeyboardHandler(this.$dragKeybinding)},this.focusWait=function(){var e=u(this.mousedownEvent.x,this.mousedownEvent.y,this.x,this.y),t=(new Date).getTime();(e>s||t-this.mousedownEvent.time>this.$focusWaitTimout)&&this.startSelect()},this.dragWait=function(e){var t=u(this.mousedownEvent.x,this.mousedownEvent.y,this.x,this.y),n=(new Date).getTime(),r=this.editor;t>s?this.startSelect(this.mousedownEvent.getDocumentPosition()):n-this.mousedownEvent.time>r.getDragDelay()&&this.startDrag()},this.dragWaitEnd=function(e){this.mousedownEvent.domEvent=e,this.startSelect()},this.drag=function(){var e=this.editor;this.dragCursor=e.renderer.screenToTextCoordinates(this.x,this.y),e.moveCursorToPosition(this.dragCursor),e.renderer.scrollCursorIntoView()},this.dragEnd=function(e){var t=this.editor,n=this.dragCursor,i=this.dragRange;r.removeCssClass(t.container,"ace_dragging"),t.session.removeMarker(this.dragSelectionMarker),t.keyBinding.removeKeyboardHandler(this.$dragKeybinding);if(!n)return;t.clearSelection();if(e&&(e.ctrlKey||e.altKey)){var s=t.session,o=i;o.end=s.insert(n,s.getTextRange(i)),o.start=n}else{if(i.contains(n.row,n.column))return;var o=t.moveText(i,n)}if(!o)return;t.selection.setSelectionRange(o)},this.onDoubleClick=function(e){var t=e.getDocumentPosition(),n=this.editor,r=n.session,i=r.getBracketRange(t);if(i){i.isEmpty()&&(i.start.column--,i.end.column++),this.$clickSelection=i,this.setState("select");return}this.$clickSelection=n.selection.getWordRange(t.row,t.column),this.setState("selectByWords")},this.onTripleClick=function(e){var t=e.getDocumentPosition(),n=this.editor;this.setState("selectByLines"),this.$clickSelection=n.selection.getLineRange(t.row)},this.onQuadClick=function(e){var t=this.editor;t.selectAll(),this.$clickSelection=t.getSelectionRange(),this.setState("null")},this.onMouseWheel=function(e){if(e.getShiftKey()||e.getAccelKey())return;var t=this.editor,n=t.renderer.isScrollableBy(e.wheelX*e.speed,e.wheelY*e.speed);if(n)this.$passScrollEvent=!1;else{if(this.$passScrollEvent)return;if(!this.$scrollStopTimeout){var r=this;this.$scrollStopTimeout=setTimeout(function(){r.$passScrollEvent=!0,r.$scrollStopTimeout=null},200)}}return t.renderer.scrollBy(e.wheelX*e.speed,e.wheelY*e.speed),e.preventDefault()}}).call(o.prototype),t.DefaultHandlers=o}),ace.define("ace/mouse/default_gutter_handler",["require","exports","module","ace/lib/dom","ace/lib/event"],function(e,t,n){function s(e){function f(){u=r.createElement("div"),u.className="ace_gutter-tooltip",u.style.maxWidth="500px",u.style.display="none",t.container.appendChild(u)}function l(){u||f();var e=o.getDocumentPosition().row,r=n.$annotations[e];if(!r)return c();var i=t.session.getLength();if(e==i){var s=t.renderer.pixelToScreenCoordinates(0,o.y).row,l=o.$pos;if(s>t.session.documentToScreenRow(l.row,l.column))return c()}if(a==r)return;a=r.text.join("<br/>"),u.style.display="block",u.innerHTML=a,t.on("mousewheel",c),h(o)}function c(){s&&(s=clearTimeout(s)),a&&(u.style.display="none",a=null,t.removeEventListener("mousewheel",c))}function h(e){var n=t.renderer.$gutter.getBoundingClientRect();u.style.left=e.x-n.left+15+"px",e.y+3*t.renderer.lineHeight+15<n.bottom?(u.style.bottom="",u.style.top=e.y-n.top+15+"px"):(u.style.top="",u.style.bottom=n.bottom-e.y+5+"px")}var t=e.editor,n=t.renderer.$gutterLayer;e.editor.setDefaultHandler("guttermousedown",function(r){if(!t.isFocused())return;var i=n.getRegion(r);if(i)return;var s=r.getDocumentPosition().row,o=t.session.selection;if(r.getShiftKey())o.selectTo(s,0);else{if(r.domEvent.detail==2)return t.selectAll(),r.preventDefault();e.$clickSelection=t.selection.getLineRange(s)}return e.captureMouse(r,"selectByLines"),r.preventDefault()});var s,o,u,a;e.editor.setDefaultHandler("guttermousemove",function(t){var n=t.domEvent.target||t.domEvent.srcElement;if(r.hasCssClass(n,"ace_fold-widget"))return c();a&&h(t),o=t;if(s)return;s=setTimeout(function(){s=null,o&&!e.isMousePressed?l():c()},50)}),i.addListener(t.renderer.$gutter,"mouseout",function(e){o=null;if(!a||s)return;s=setTimeout(function(){s=null,c()},50)})}var r=e("../lib/dom"),i=e("../lib/event");t.GutterHandler=s}),ace.define("ace/mouse/mouse_event",["require","exports","module","ace/lib/event","ace/lib/useragent"],function(e,t,n){var r=e("../lib/event"),i=e("../lib/useragent"),s=t.MouseEvent=function(e,t){this.domEvent=e,this.editor=t,this.x=this.clientX=e.clientX,this.y=this.clientY=e.clientY,this.$pos=null,this.$inSelection=null,this.propagationStopped=!1,this.defaultPrevented=!1};(function(){this.stopPropagation=function(){r.stopPropagation(this.domEvent),this.propagationStopped=!0},this.preventDefault=function(){r.preventDefault(this.domEvent),this.defaultPrevented=!0},this.stop=function(){this.stopPropagation(),this.preventDefault()},this.getDocumentPosition=function(){return this.$pos?this.$pos:(this.$pos=this.editor.renderer.screenToTextCoordinates(this.clientX,this.clientY),this.$pos)},this.inSelection=function(){if(this.$inSelection!==null)return this.$inSelection;var e=this.editor;if(e.getReadOnly())this.$inSelection=!1;else{var t=e.getSelectionRange();if(t.isEmpty())this.$inSelection=!1;else{var n=this.getDocumentPosition();this.$inSelection=t.contains(n.row,n.column)}}return this.$inSelection},this.getButton=function(){return r.getButton(this.domEvent)},this.getShiftKey=function(){return this.domEvent.shiftKey},this.getAccelKey=i.isMac?function(){return this.domEvent.metaKey}:function(){return this.domEvent.ctrlKey}}).call(s.prototype)}),ace.define("ace/mouse/dragdrop",["require","exports","module","ace/lib/event"],function(e,t,n){var r=e("../lib/event"),i=function(e){var t=e.editor,n,i,s,o,u,a,f,l=0,c=t.container;r.addListener(c,"dragenter",function(e){l++;if(!n){u=t.getSelectionRange(),a=t.selection.isBackwards();var i=t.getSelectionStyle();n=t.session.addMarker(u,"ace_selection",i),t.clearSelection(),clearInterval(o),o=setInterval(h,20)}return r.preventDefault(e)}),r.addListener(c,"dragover",function(e){return i=e.clientX,s=e.clientY,r.preventDefault(e)});var h=function(){f=t.renderer.screenToTextCoordinates(i,s),t.moveCursorToPosition(f),t.renderer.scrollCursorIntoView()};r.addListener(c,"dragleave",function(e){l--;if(l>0)return;return console.log(e.type,l,e.target),clearInterval(o),t.session.removeMarker(n),n=null,t.selection.setSelectionRange(u,a),r.preventDefault(e)}),r.addListener(c,"drop",function(e){return console.log(e.type,l,e.target),l=0,clearInterval(o),t.session.removeMarker(n),n=null,u.end=t.session.insert(f,e.dataTransfer.getData("Text")),u.start=f,t.focus(),t.selection.setSelectionRange(u),r.preventDefault(e)})};t.DragdropHandler=i}),ace.define("ace/mouse/fold_handler",["require","exports","module"],function(e,t,n){function r(e){e.on("click",function(t){var n=t.getDocumentPosition(),r=e.session,i=r.getFoldAt(n.row,n.column,1);i&&(t.getAccelKey()?r.removeFold(i):r.expandFold(i),t.stop())}),e.on("guttermousedown",function(t){var n=e.renderer.$gutterLayer.getRegion(t);if(n=="foldWidgets"){var r=t.getDocumentPosition().row,i=e.session;i.foldWidgets&&i.foldWidgets[r]&&e.session.onFoldWidgetClick(r,t),t.stop()}}),e.on("gutterdblclick",function(t){var n=e.renderer.$gutterLayer.getRegion(t);if(n=="foldWidgets"){var r=t.getDocumentPosition().row,i=e.session,s=i.foldWidgets;if(!s||s[r])return;var o=r-1,u;while(o>=0){var a=s[o];a==null&&(a=s[o]=i.getFoldWidget());if(a=="start"){var f=i.getFoldWidgetRange(o);u||(u=f);if(f&&f.end.row>=r)break}o--}o==-1&&(f=u);if(f){var r=f.start.row,l=i.getFoldAt(r,i.getLine(r).length,1);l?i.removeFold(l):(i.addFold("...",f),e.renderer.scrollCursorIntoView({row:f.start.row,column:0}))}t.stop()}})}t.FoldHandler=r}),ace.define("ace/keyboard/keybinding",["require","exports","module","ace/lib/keys","ace/lib/event"],function(e,t,n){var r=e("../lib/keys"),i=e("../lib/event"),s=function(e){this.$editor=e,this.$data={},this.$handlers=[],this.setDefaultHandler(e.commands)};(function(){this.setDefaultHandler=function(e){this.removeKeyboardHandler(this.$defaultHandler),this.$defaultHandler=e,this.addKeyboardHandler(e,0),this.$data={editor:this.$editor}},this.setKeyboardHandler=function(e){if(this.$handlers[this.$handlers.length-1]==e)return;while(this.$handlers[1])this.removeKeyboardHandler(this.$handlers[1]);this.addKeyboardHandler(e,1)},this.addKeyboardHandler=function(e,t){if(!e)return;var n=this.$handlers.indexOf(e);n!=-1&&this.$handlers.splice(n,1),t==undefined?this.$handlers.push(e):this.$handlers.splice(t,0,e),n==-1&&e.attach&&e.attach(this.$editor)},this.removeKeyboardHandler=function(e){var t=this.$handlers.indexOf(e);return t==-1?!1:(this.$handlers.splice(t,1),e.detach&&e.detach(this.$editor),!0)},this.getKeyboardHandler=function(){return this.$handlers[this.$handlers.length-1]},this.$callKeyboardHandlers=function(e,t,n,r){var s;for(var o=this.$handlers.length;o--;){s=this.$handlers[o].handleKeyboard(this.$data,e,t,n,r);if(s&&s.command)break}if(!s||!s.command)return!1;var u=!1,a=this.$editor.commands;return s.command!="null"?u=a.exec(s.command,this.$editor,s.args,r):u=s.passEvent!=1,u&&r&&e!=-1&&i.stopEvent(r),u},this.onCommandKey=function(e,t,n){var i=r.keyCodeToString(n);this.$callKeyboardHandlers(t,i,n,e)},this.onTextInput=function(e){var t=this.$callKeyboardHandlers(-1,e);t||this.$editor.commands.exec("insertstring",this.$editor,e)}}).call(s.prototype),t.KeyBinding=s}),ace.define("ace/edit_session",["require","exports","module","ace/config","ace/lib/oop","ace/lib/lang","ace/lib/net","ace/lib/event_emitter","ace/selection","ace/mode/text","ace/range","ace/document","ace/background_tokenizer","ace/search_highlight","ace/edit_session/folding","ace/edit_session/bracket_match"],function(e,t,n){var r=e("./config"),i=e("./lib/oop"),s=e("./lib/lang"),o=e("./lib/net"),u=e("./lib/event_emitter").EventEmitter,a=e("./selection").Selection,f=e("./mode/text").Mode,l=e("./range").Range,c=e("./document").Document,h=e("./background_tokenizer").BackgroundTokenizer,p=e("./search_highlight").SearchHighlight,d=function(e,t){this.$breakpoints=[],this.$decorations=[],this.$frontMarkers={},this.$backMarkers={},this.$markerId=1,this.$undoSelect=!0,this.$foldData=[],this.$foldData.toString=function(){var e="";return this.forEach(function(t){e+="\n"+t.toString()}),e},this.on("changeFold",this.onChangeFold.bind(this)),this.$onChange=this.onChange.bind(this);if(typeof e!="object"||!e.getLine)e=new c(e);this.setDocument(e),this.selection=new a(this),this.setMode(t)};(function(){function y(e){return e<4352?!1:e>=4352&&e<=4447||e>=4515&&e<=4519||e>=4602&&e<=4607||e>=9001&&e<=9002||e>=11904&&e<=11929||e>=11931&&e<=12019||e>=12032&&e<=12245||e>=12272&&e<=12283||e>=12288&&e<=12350||e>=12353&&e<=12438||e>=12441&&e<=12543||e>=12549&&e<=12589||e>=12593&&e<=12686||e>=12688&&e<=12730||e>=12736&&e<=12771||e>=12784&&e<=12830||e>=12832&&e<=12871||e>=12880&&e<=13054||e>=13056&&e<=19903||e>=19968&&e<=42124||e>=42128&&e<=42182||e>=43360&&e<=43388||e>=44032&&e<=55203||e>=55216&&e<=55238||e>=55243&&e<=55291||e>=63744&&e<=64255||e>=65040&&e<=65049||e>=65072&&e<=65106||e>=65108&&e<=65126||e>=65128&&e<=65131||e>=65281&&e<=65376||e>=65504&&e<=65510}i.implement(this,u),this.setDocument=function(e){this.doc&&this.doc.removeListener("change",this.$onChange),this.doc=e,e.on("change",this.$onChange),this.bgTokenizer&&this.bgTokenizer.setDocument(this.getDocument()),this.resetCaches()},this.getDocument=function(){return this.doc},this.$resetRowCache=function(e){if(!e){this.$docRowCache=[],this.$screenRowCache=[];return}var t=this.$getRowCacheIndex(this.$docRowCache,e)+1,n=this.$docRowCache.length;this.$docRowCache.splice(t,n),this.$screenRowCache.splice(t,n)},this.$getRowCacheIndex=function(e,t){var n=0,r=e.length-1;while(n<=r){var i=n+r>>1,s=e[i];if(t>s)n=i+1;else{if(!(t<s))return i;r=i-1}}return n-1},this.resetCaches=function(){this.$modified=!0,this.$wrapData=[],this.$rowLengthCache=[],this.$resetRowCache(0),this.bgTokenizer&&this.bgTokenizer.start(0)},this.onChangeFold=function(e){var t=e.data;this.$resetRowCache(t.start.row)},this.onChange=function(e){var t=e.data;this.$modified=!0,this.$resetRowCache(t.range.start.row);var n=this.$updateInternalDataOnChange(e);!this.$fromUndo&&this.$undoManager&&!t.ignore&&(this.$deltasDoc.push(t),n&&n.length!=0&&this.$deltasFold.push({action:"removeFolds",folds:n}),this.$informUndoManager.schedule()),this.bgTokenizer.$updateOnChange(t),this._emit("change",e)},this.setValue=function(e){this.doc.setValue(e),this.selection.moveCursorTo(0,0),this.selection.clearSelection(),this.$resetRowCache(0),this.$deltas=[],this.$deltasDoc=[],this.$deltasFold=[],this.getUndoManager().reset()},this.getValue=this.toString=function(){return this.doc.getValue()},this.getSelection=function(){return this.selection},this.getState=function(e){return this.bgTokenizer.getState(e)},this.getTokens=function(e){return this.bgTokenizer.getTokens(e)},this.getTokenAt=function(e,t){var n=this.bgTokenizer.getTokens(e),r,i=0;if(t==null)s=n.length-1,i=this.getLine(e).length;else for(var s=0;s<n.length;s++){i+=n[s].value.length;if(i>=t)break}return r=n[s],r?(r.index=s,r.start=i-r.value.length,r):null},this.setUndoManager=function(e){this.$undoManager=e,this.$deltas=[],this.$deltasDoc=[],this.$deltasFold=[],this.$informUndoManager&&this.$informUndoManager.cancel();if(e){var t=this;this.$syncInformUndoManager=function(){t.$informUndoManager.cancel(),t.$deltasFold.length&&(t.$deltas.push({group:"fold",deltas:t.$deltasFold}),t.$deltasFold=[]),t.$deltasDoc.length&&(t.$deltas.push({group:"doc",deltas:t.$deltasDoc}),t.$deltasDoc=[]),t.$deltas.length>0&&e.execute({action:"aceupdate",args:[t.$deltas,t]}),t.$deltas=[]},this.$informUndoManager=s.deferredCall(this.$syncInformUndoManager)}},this.$defaultUndoManager={undo:function(){},redo:function(){},reset:function(){}},this.getUndoManager=function(){return this.$undoManager||this.$defaultUndoManager},this.getTabString=function(){return this.getUseSoftTabs()?s.stringRepeat(" ",this.getTabSize()):" "},this.$useSoftTabs=!0,this.setUseSoftTabs=function(e){if(this.$useSoftTabs===e)return;this.$useSoftTabs=e},this.getUseSoftTabs=function(){return this.$useSoftTabs},this.$tabSize=4,this.setTabSize=function(e){if(isNaN(e)||this.$tabSize===e)return;this.$modified=!0,this.$rowLengthCache=[],this.$tabSize=e,this._emit("changeTabSize")},this.getTabSize=function(){return this.$tabSize},this.isTabStop=function(e){return this.$useSoftTabs&&e.column%this.$tabSize==0},this.$overwrite=!1,this.setOverwrite=function(e){if(this.$overwrite==e)return;this.$overwrite=e,this._emit("changeOverwrite")},this.getOverwrite=function(){return this.$overwrite},this.toggleOverwrite=function(){this.setOverwrite(!this.$overwrite)},this.addGutterDecoration=function(e,t){this.$decorations[e]||(this.$decorations[e]=""),this.$decorations[e]+=" "+t,this._emit("changeBreakpoint",{})},this.removeGutterDecoration=function(e,t){this.$decorations[e]=(this.$decorations[e]||"").replace(" "+t,""),this._emit("changeBreakpoint",{})},this.getBreakpoints=function(){return this.$breakpoints},this.setBreakpoints=function(e){this.$breakpoints=[];for(var t=0;t<e.length;t++)this.$breakpoints[e[t]]="ace_breakpoint";this._emit("changeBreakpoint",{})},this.clearBreakpoints=function(){this.$breakpoints=[],this._emit("changeBreakpoint",{})},this.setBreakpoint=function(e,t){t===undefined&&(t="ace_breakpoint"),t?this.$breakpoints[e]=t:delete this.$breakpoints[e],this._emit("changeBreakpoint",{})},this.clearBreakpoint=function(e){delete this.$breakpoints[e],this._emit("changeBreakpoint",{})},this.addMarker=function(e,t,n,r){var i=this.$markerId++,s={range:e,type:n||"line",renderer:typeof n=="function"?n:null,clazz:t,inFront:!!r,id:i};return r?(this.$frontMarkers[i]=s,this._emit("changeFrontMarker")):(this.$backMarkers[i]=s,this._emit("changeBackMarker")),i},this.addDynamicMarker=function(e,t){if(!e.update)return;var n=this.$markerId++;return e.id=n,e.inFront=!!t,t?(this.$frontMarkers[n]=e,this._emit("changeFrontMarker")):(this.$backMarkers[n]=e,this._emit("changeBackMarker")),e},this.removeMarker=function(e){var t=this.$frontMarkers[e]||this.$backMarkers[e];if(!t)return;var n=t.inFront?this.$frontMarkers:this.$backMarkers;t&&(delete n[e],this._emit(t.inFront?"changeFrontMarker":"changeBackMarker"))},this.getMarkers=function(e){return e?this.$frontMarkers:this.$backMarkers},this.highlight=function(e){if(!this.$searchHighlight){var t=new p(null,"ace_selected-word","text");this.$searchHighlight=this.addDynamicMarker(t)}this.$searchHighlight.setRegexp(e)},this.highlightLines=function(e,t,n,r){typeof t!="number"&&(n=t,t=e),n||(n="ace_step");var i=new l(e,0,t,Infinity),s=this.addMarker(i,n,"fullLine",r);return i.id=s,i},this.setAnnotations=function(e){this.$annotations=e,this._emit("changeAnnotation",{})},this.getAnnotations=function(){return this.$annotations||[]},this.clearAnnotations=function(){this.$annotations={},this._emit("changeAnnotation",{})},this.$detectNewLine=function(e){var t=e.match(/^.*?(\r?\n)/m);t?this.$autoNewLine=t[1]:this.$autoNewLine="\n"},this.getWordRange=function(e,t){var n=this.getLine(e),r=!1;t>0&&(r=!!n.charAt(t-1).match(this.tokenRe)),r||(r=!!n.charAt(t).match(this.tokenRe));if(r)var i=this.tokenRe;else if(/^\s+$/.test(n.slice(t-1,t+1)))var i=/\s/;else var i=this.nonTokenRe;var s=t;if(s>0){do s--;while(s>=0&&n.charAt(s).match(i));s++}var o=t;while(o<n.length&&n.charAt(o).match(i))o++;return new l(e,s,e,o)},this.getAWordRange=function(e,t){var n=this.getWordRange(e,t),r=this.getLine(n.end.row);while(r.charAt(n.end.column).match(/[ \t]/))n.end.column+=1;return n},this.setNewLineMode=function(e){this.doc.setNewLineMode(e)},this.getNewLineMode=function(){return this.doc.getNewLineMode()},this.$useWorker=!0,this.setUseWorker=function(e){if(this.$useWorker==e)return;this.$useWorker=e,this.$stopWorker(),e&&this.$startWorker()},this.getUseWorker=function(){return this.$useWorker},this.onReloadTokenizer=function(e){var t=e.data;this.bgTokenizer.start(t.first),this._emit("tokenizerUpdate",e)},this.$modes={},this._loadMode=function(t,n){function a(e){if(i.$modes[t])return n(i.$modes[t]);i.$modes[t]=new e.Mode,i.$modes[t].$id=t,i._emit("loadmode",{name:t,mode:i.$modes[t]}),n(i.$modes[t])}function l(e,t){if(!r.get("packaged"))return t();o.loadScript(r.moduleUrl(e,"mode"),t)}this.$modes["null"]||(this.$modes["null"]=this.$modes["ace/mode/text"]=new f);if(this.$modes[t])return n(this.$modes[t]);var i=this,s;try{s=e(t)}catch(u){}if(s&&s.Mode)return a(s);this.$mode||this.$setModePlaceholder(),l(t,function(){e([t],a)})},this.$setModePlaceholder=function(){this.$mode=this.$modes["null"];var e=this.$mode.getTokenizer();if(!this.bgTokenizer){this.bgTokenizer=new h(e);var t=this;this.bgTokenizer.addEventListener("update",function(e){t._emit("tokenizerUpdate",e)})}else this.bgTokenizer.setTokenizer(e);this.bgTokenizer.setDocument(this.getDocument()),this.tokenRe=this.$mode.tokenRe,this.nonTokenRe=this.$mode.nonTokenRe},this.$mode=null,this.$modeId=null,this.setMode=function(e){e=e||"null";if(typeof e=="string"){if(this.$modeId==e)return;this.$modeId=e;var t=this;this._loadMode(e,function(n){if(t.$modeId!==e)return;t.setMode(n)});return}if(this.$mode===e)return;this.$mode=e,this.$modeId=e.$id,this.$stopWorker(),this.$useWorker&&this.$startWorker();var n=e.getTokenizer();if(n.addEventListener!==undefined){var r=this.onReloadTokenizer.bind(this);n.addEventListener("update",r)}if(!this.bgTokenizer){this.bgTokenizer=new h(n);var t=this;this.bgTokenizer.addEventListener("update",function(e){t._emit("tokenizerUpdate",e)})}else this.bgTokenizer.setTokenizer(n);this.bgTokenizer.setDocument(this.getDocument()),this.bgTokenizer.start(0),this.tokenRe=e.tokenRe,this.nonTokenRe=e.nonTokenRe,this.$setFolding(e.foldingRules),this._emit("changeMode")},this.$stopWorker=function(){this.$worker&&this.$worker.terminate(),this.$worker=null},this.$startWorker=function(){if(typeof Worker!="undefined"&&!e.noWorker)try{this.$worker=this.$mode.createWorker(this)}catch(t){console.log("Could not load worker"),console.log(t),this.$worker=null}else this.$worker=null},this.getMode=function(){return this.$mode},this.$scrollTop=0,this.setScrollTop=function(e){e=Math.round(Math.max(0,e));if(this.$scrollTop===e)return;this.$scrollTop=e,this._emit("changeScrollTop",e)},this.getScrollTop=function(){return this.$scrollTop},this.$scrollLeft=0,this.setScrollLeft=function(e){e=Math.round(Math.max(0,e));if(this.$scrollLeft===e)return;this.$scrollLeft=e,this._emit("changeScrollLeft",e)},this.getScrollLeft=function(){return this.$scrollLeft},this.getScreenWidth=function(){return this.$computeWidth(),this.screenWidth},this.$computeWidth=function(e){if(this.$modified||e){this.$modified=!1;if(this.$useWrapMode)return this.screenWidth=this.$wrapLimit;var t=this.doc.getAllLines(),n=this.$rowLengthCache,r=0,i=0,s=this.$foldData[i],o=s?s.start.row:Infinity,u=t.length;for(var a=0;a<u;a++){if(a>o){a=s.end.row+1;if(a>=u)break;s=this.$foldData[i++],o=s?s.start.row:Infinity}n[a]==null&&(n[a]=this.$getStringScreenWidth(t[a])[0]),n[a]>r&&(r=n[a])}this.screenWidth=r}},this.getLine=function(e){return this.doc.getLine(e)},this.getLines=function(e,t){return this.doc.getLines(e,t)},this.getLength=function(){return this.doc.getLength()},this.getTextRange=function(e){return this.doc.getTextRange(e||this.selection.getRange())},this.insert=function(e,t){return this.doc.insert(e,t)},this.remove=function(e){return this.doc.remove(e)},this.undoChanges=function(e,t){if(!e.length)return;this.$fromUndo=!0;var n=null;for(var r=e.length-1;r!=-1;r--){var i=e[r];i.group=="doc"?(this.doc.revertDeltas(i.deltas),n=this.$getUndoSelection(i.deltas,!0,n)):i.deltas.forEach(function(e){this.addFolds(e.folds)},this)}return this.$fromUndo=!1,n&&this.$undoSelect&&!t&&this.selection.setSelectionRange(n),n},this.redoChanges=function(e,t){if(!e.length)return;this.$fromUndo=!0;var n=null;for(var r=0;r<e.length;r++){var i=e[r];i.group=="doc"&&(this.doc.applyDeltas(i.deltas),n=this.$getUndoSelection(i.deltas,!1,n))}return this.$fromUndo=!1,n&&this.$undoSelect&&!t&&this.selection.setSelectionRange(n),n},this.setUndoSelect=function(e){this.$undoSelect=e},this.$getUndoSelection=function(e,t,n){function r(e){var n=e.action=="insertText"||e.action=="insertLines";return t?!n:n}var i=e[0],s,o,u=!1;r(i)?(s=i.range.clone(),u=!0):(s=l.fromPoints(i.range.start,i.range.start),u=!1);for(var a=1;a<e.length;a++)i=e[a],r(i)?(o=i.range.start,s.compare(o.row,o.column)==-1&&s.setStart(i.range.start),o=i.range.end,s.compare(o.row,o.column)==1&&s.setEnd(i.range.end),u=!0):(o=i.range.start,s.compare(o.row,o.column)==-1&&(s=l.fromPoints(i.range.start,i.range.start)),u=!1);if(n!=null){var f=n.compareRange(s);f==1?s.setStart(n.start):f==-1&&s.setEnd(n.end)}return s},this.replace=function(e,t){return this.doc.replace(e,t)},this.moveText=function(e,t){var n=this.getTextRange(e);this.remove(e);var r=t.row,i=t.column;!e.isMultiLine()&&e.start.row==r&&e.end.column<i&&(i-=n.length);if(e.isMultiLine()&&e.end.row<r){var s=this.doc.$split(n);r-=s.length-1}var o=r+e.end.row-e.start.row,u=e.isMultiLine()?e.end.column:i+e.end.column-e.start.column,a=new l(r,i,o,u);return this.insert(a.start,n),a},this.indentRows=function(e,t,n){n=n.replace(/\t/g,this.getTabString());for(var r=e;r<=t;r++)this.insert({row:r,column:0},n)},this.outdentRows=function(e){var t=e.collapseRows(),n=new l(0,0,0,0),r=this.getTabSize();for(var i=t.start.row;i<=t.end.row;++i){var s=this.getLine(i);n.start.row=i,n.end.row=i;for(var o=0;o<r;++o)if(s.charAt(o)!=" ")break;o<r&&s.charAt(o)==" "?(n.start.column=o,n.end.column=o+1):(n.start.column=0,n.end.column=o),this.remove(n)}},this.moveLinesUp=function(e,t){if(e<=0)return 0;var n=this.doc.removeLines(e,t);return this.doc.insertLines(e-1,n),-1},this.moveLinesDown=function(e,t){if(t>=this.doc.getLength()-1)return 0;var n=this.doc.removeLines(e,t);return this.doc.insertLines(e+1,n),1},this.duplicateLines=function(e,t){var e=this.$clipRowToDocument(e),t=this.$clipRowToDocument(t),n=this.getLines(e,t);this.doc.insertLines(e,n);var r=t-e+1;return r},this.$clipRowToDocument=function(e){return Math.max(0,Math.min(e,this.doc.getLength()-1))},this.$clipColumnToRow=function(e,t){return t<0?0:Math.min(this.doc.getLine(e).length,t)},this.$clipPositionToDocument=function(e,t){t=Math.max(0,t);if(e<0)e=0,t=0;else{var n=this.doc.getLength();e>=n?(e=n-1,t=this.doc.getLine(n-1).length):t=Math.min(this.doc.getLine(e).length,t)}return{row:e,column:t}},this.$clipRangeToDocument=function(e){e.start.row<0?(e.start.row=0,e.start.column=0):e.start.column=this.$clipColumnToRow(e.start.row,e.start.column);var t=this.doc.getLength()-1;return e.end.row>t?(e.end.row=t,e.end.column=this.doc.getLine(t).length):e.end.column=this.$clipColumnToRow(e.end.row,e.end.column),e},this.$wrapLimit=80,this.$useWrapMode=!1,this.$wrapLimitRange={min:null,max:null},this.setUseWrapMode=function(e){if(e!=this.$useWrapMode){this.$useWrapMode=e,this.$modified=!0,this.$resetRowCache(0);if(e){var t=this.getLength();this.$wrapData=[];for(var n=0;n<t;n++)this.$wrapData.push([]);this.$updateWrapData(0,t-1)}this._emit("changeWrapMode")}},this.getUseWrapMode=function(){return this.$useWrapMode},this.setWrapLimitRange=function(e,t){if(this.$wrapLimitRange.min!==e||this.$wrapLimitRange.max!==t)this.$wrapLimitRange.min=e,this.$wrapLimitRange.max=t,this.$modified=!0,this._emit("changeWrapMode")},this.adjustWrapLimit=function(e){var t=this.$constrainWrapLimit(e);return t!=this.$wrapLimit&&t>0?(this.$wrapLimit=t,this.$modified=!0,this.$useWrapMode&&(this.$updateWrapData(0,this.getLength()-1),this.$resetRowCache(0),this._emit("changeWrapLimit")),!0):!1},this.$constrainWrapLimit=function(e){var t=this.$wrapLimitRange.min;t&&(e=Math.max(t,e));var n=this.$wrapLimitRange.max;return n&&(e=Math.min(n,e)),Math.max(1,e)},this.getWrapLimit=function(){return this.$wrapLimit},this.getWrapLimitRange=function(){return{min:this.$wrapLimitRange.min,max:this.$wrapLimitRange.max}},this.$updateInternalDataOnChange=function(e){var t=this.$useWrapMode,n,r=e.data.action,i=e.data.range.start.row,s=e.data.range.end.row,o=e.data.range.start,u=e.data.range.end,a=null;r.indexOf("Lines")!=-1?(r=="insertLines"?s=i+e.data.lines.length:s=i,n=e.data.lines?e.data.lines.length:s-i):n=s-i;if(n!=0)if(r.indexOf("remove")!=-1){this[t?"$wrapData":"$rowLengthCache"].splice(i,n);var f=this.$foldData;a=this.getFoldsInRange(e.data.range),this.removeFolds(a);var l=this.getFoldLine(u.row),c=0;if(l){l.addRemoveChars(u.row,u.column,o.column-u.column),l.shiftRow(-n);var h=this.getFoldLine(i);h&&h!==l&&(h.merge(l),l=h),c=f.indexOf(l)+1}for(c;c<f.length;c++){var l=f[c];l.start.row>=u.row&&l.shiftRow(-n)}s=i}else{var p;if(t){p=[i,0];for(var d=0;d<n;d++)p.push([]);this.$wrapData.splice.apply(this.$wrapData,p)}else p=Array(n),p.unshift(i,0),this.$rowLengthCache.splice.apply(this.$rowLengthCache,p);var f=this.$foldData,l=this.getFoldLine(i),c=0;if(l){var v=l.range.compareInside(o.row,o.column);v==0?(l=l.split(o.row,o.column),l.shiftRow(n),l.addRemoveChars(s,0,u.column-o.column)):v==-1&&(l.addRemoveChars(i,0,u.column-o.column),l.shiftRow(n)),c=f.indexOf(l)+1}for(c;c<f.length;c++){var l=f[c];l.start.row>=i&&l.shiftRow(n)}}else{n=Math.abs(e.data.range.start.column-e.data.range.end.column),r.indexOf("remove")!=-1&&(a=this.getFoldsInRange(e.data.range),this.removeFolds(a),n=-n);var l=this.getFoldLine(i);l&&l.addRemoveChars(i,o.column,n)}return t&&this.$wrapData.length!=this.doc.getLength()&&console.error("doc.getLength() and $wrapData.length have to be the same!"),t?this.$updateWrapData(i,s):this.$updateRowLengthCache(i,s),a},this.$updateRowLengthCache=function(e,t,n){this.$rowLengthCache[e]=null,this.$rowLengthCache[t]=null},this.$updateWrapData=function(e,t){var n=this.doc.getAllLines(),r=this.getTabSize(),i=this.$wrapData,o=this.$wrapLimit,u,f,l=e;t=Math.min(t,n.length-1);while(l<=t){f=this.getFoldLine(l,f);if(!f)u=this.$getDisplayTokens(s.stringTrimRight(n[l])),i[l]=this.$computeWrapSplits(u,o,r),l++;else{u=[],f.walk(function(e,t,r,i){var s;if(e!=null){s=this.$getDisplayTokens(e,u.length),s[0]=a;for(var o=1;o<s.length;o++)s[o]=c}else s=this.$getDisplayTokens(n[t].substring(i,r),u.length);u=u.concat(s)}.bind(this),f.end.row,n[f.end.row].length+1);while(u.length!=0&&u[u.length-1]>=v)u.pop();i[f.start.row]=this.$computeWrapSplits(u,o,r),l=f.end.row+1}}};var t=1,n=2,a=3,c=4,d=9,v=10,m=11,g=12;this.$computeWrapSplits=function(e,t){function o(t){var r=e.slice(i,t),o=r.length;r.join("").replace(/12/g,function(){o-=1}).replace(/2/g,function(){o-=1}),s+=o,n.push(s),i=t}if(e.length==0)return[];var n=[],r=e.length,i=0,s=0;while(r-i>t){var u=i+t;if(e[u]>=v){while(e[u]>=v)u++;o(u);continue}if(e[u]==a||e[u]==c){for(u;u!=i-1;u--)if(e[u]==a)break;if(u>i){o(u);continue}u=i+t;for(u;u<e.length;u++)if(e[u]!=c)break;if(u==e.length)break;o(u);continue}var f=Math.max(u-10,i-1);while(u>f&&e[u]<a)u--;while(u>f&&e[u]==d)u--;if(u>f){o(++u);continue}u=i+t,o(u)}return n},this.$getDisplayTokens=function(e,r){var i=[],s;r=r||0;for(var o=0;o<e.length;o++){var u=e.charCodeAt(o);if(u==9){s=this.getScreenTabSize(i.length+r),i.push(m);for(var a=1;a<s;a++)i.push(g)}else u==32?i.push(v):u>39&&u<48||u>57&&u<64?i.push(d):u>=4352&&y(u)?i.push(t,n):i.push(t)}return i},this.$getStringScreenWidth=function(e,t,n){if(t==0)return[0,0];t==null&&(t=Infinity),n=n||0;var r,i;for(i=0;i<e.length;i++){r=e.charCodeAt(i),r==9?n+=this.getScreenTabSize(n):r>=4352&&y(r)?n+=2:n+=1;if(n>t)break}return[n,i]},this.getRowLength=function(e){return!this.$useWrapMode||!this.$wrapData[e]?1:this.$wrapData[e].length+1},this.getScreenLastRowColumn=function(e){var t=this.screenToDocumentPosition(e,Number.MAX_VALUE);return this.documentToScreenColumn(t.row,t.column)},this.getDocumentLastRowColumn=function(e,t){var n=this.documentToScreenRow(e,t);return this.getScreenLastRowColumn(n)},this.getDocumentLastRowColumnPosition=function(e,t){var n=this.documentToScreenRow(e,t);return this.screenToDocumentPosition(n,Number.MAX_VALUE/10)},this.getRowSplitData=function(e){return this.$useWrapMode?this.$wrapData[e]:undefined},this.getScreenTabSize=function(e){return this.$tabSize-e%this.$tabSize},this.screenToDocumentRow=function(e,t){return this.screenToDocumentPosition(e,t).row},this.screenToDocumentColumn=function(e,t){return this.screenToDocumentPosition(e,t).column},this.screenToDocumentPosition=function(e,t){if(e<0)return{row:0,column:0};var n,r=0,i=0,s,o=0,u=0,a=this.$screenRowCache,f=this.$getRowCacheIndex(a,e),l=a.length;if(l&&f>=0)var o=a[f],r=this.$docRowCache[f],c=e>a[l-1];else var c=!l;var h=this.getLength()-1,p=this.getNextFoldLine(r),d=p?p.start.row:Infinity;while(o<=e){u=this.getRowLength(r);if(o+u-1>=e||r>=h)break;o+=u,r++,r>d&&(r=p.end.row+1,p=this.getNextFoldLine(r,p),d=p?p.start.row:Infinity),c&&(this.$docRowCache.push(r),this.$screenRowCache.push(o))}if(p&&p.start.row<=r)n=this.getFoldDisplayLine(p),r=p.start.row;else{if(o+u<=e||r>h)return{row:h,column:this.getLine(h).length};n=this.getLine(r),p=null}if(this.$useWrapMode){var v=this.$wrapData[r];v&&(s=v[e-o],e>o&&v.length&&(i=v[e-o-1]||v[v.length-1],n=n.substring(i)))}return i+=this.$getStringScreenWidth(n,t)[1],this.$useWrapMode&&i>=s&&(i=s-1),p?p.idxToPosition(i):{row:r,column:i}},this.documentToScreenPosition=function(e,t){if(typeof t=="undefined")var n=this.$clipPositionToDocument(e.row,e.column);else n=this.$clipPositionToDocument(e,t);e=n.row,t=n.column;var r=0,i=null,s=null;s=this.getFoldAt(e,t,1),s&&(e=s.start.row,t=s.start.column);var o,u=0,a=this.$docRowCache,f=this.$getRowCacheIndex(a,e),l=a.length;if(l&&f>=0)var u=a[f],r=this.$screenRowCache[f],c=e>a[l-1];else var c=!l;var h=this.getNextFoldLine(u),p=h?h.start.row:Infinity;while(u<e){if(u>=p){o=h.end.row+1;if(o>e)break;h=this.getNextFoldLine(o,h),p=h?h.start.row:Infinity}else o=u+1;r+=this.getRowLength(u),u=o,c&&(this.$docRowCache.push(u),this.$screenRowCache.push(r))}var d="";h&&u>=p?(d=this.getFoldDisplayLine(h,e,t),i=h.start.row):(d=this.getLine(e).substring(0,t),i=e);if(this.$useWrapMode){var v=this.$wrapData[i],m=0;while(d.length>=v[m])r++,m++;d=d.substring(v[m-1]||0,d.length)}return{row:r,column:this.$getStringScreenWidth(d)[0]}},this.documentToScreenColumn=function(e,t){return this.documentToScreenPosition(e,t).column},this.documentToScreenRow=function(e,t){return this.documentToScreenPosition(e,t).row},this.getScreenLength=function(){var e=0,t=null;if(!this.$useWrapMode){e=this.getLength();var n=this.$foldData;for(var r=0;r<n.length;r++)t=n[r],e-=t.end.row-t.start.row}else{var i=this.$wrapData.length,s=0,r=0,t=this.$foldData[r++],o=t?t.start.row:Infinity;while(s<i)e+=this.$wrapData[s].length+1,s++,s>o&&(s=t.end.row+1,t=this.$foldData[r++],o=t?t.start.row:Infinity)}return e}}).call(d.prototype),e("./edit_session/folding").Folding.call(d.prototype),e("./edit_session/bracket_match").BracketMatch.call(d.prototype),t.EditSession=d}),ace.define("ace/config",["require","exports","module","ace/lib/lang"],function(e,t,n){"no use strict";function o(e){return e.replace(/-(.)/g,function(e,t){return t.toUpperCase()})}var r=e("./lib/lang"),i=function(){return this}(),s={packaged:!1,workerPath:null,modePath:null,themePath:null,basePath:"",suffix:".js",$moduleUrls:{}};t.get=function(e){if(!s.hasOwnProperty(e))throw new Error("Unknown config key: "+e);return s[e]},t.set=function(e,t){if(!s.hasOwnProperty(e))throw new Error("Unknown config key: "+e);s[e]=t},t.all=function(){return r.copyObject(s)},t.moduleUrl=function(e,t){if(s.$moduleUrls[e])return s.$moduleUrls[e];var n=e.split("/");t=t||n[n.length-2]||"";var r=n[n.length-1].replace(t,"").replace(/(^[\-_])|([\-_]$)/,"");!r&&n.length>1&&(r=n[n.length-2]);var i=s[t+"Path"];return i==null&&(i=s.basePath),i&&i.slice(-1)!="/"&&(i+="/"),i+t+"-"+r+this.get("suffix")},t.setModuleUrl=function(e,t){return s.$moduleUrls[e]=t},t.init=function(){s.packaged=e.packaged||n.packaged||i.define&&define.packaged;if(!i.document)return"";var r={},u="",a=document.getElementsByTagName("script");for(var f=0;f<a.length;f++){var l=a[f],c=l.src||l.getAttribute("src");if(!c)continue;var h=l.attributes;for(var p=0,d=h.length;p<d;p++){var v=h[p];v.name.indexOf("data-ace-")===0&&(r[o(v.name.replace(/^data-ace-/,""))]=v.value)}var m=c.match(/^(.*)\/ace(\-\w+)?\.js(\?|$)/);m&&(u=m[1])}u&&(r.base=r.base||u,r.packaged=!0),r.workerPath=r.workerPath||r.base,r.modePath=r.modePath||r.base,r.themePath=r.themePath||r.base,delete r.base;for(var g in r)typeof r[g]!="undefined"&&t.set(g,r[g])}}),ace.define("ace/lib/net",["require","exports","module","ace/lib/useragent"],function(e,t,n){var r=e("./useragent");t.get=function(e,n){var r=t.createXhr();r.open("GET",e,!0),r.onreadystatechange=function(e){r.readyState===4&&n(r.responseText)},r.send(null)};var i=["Msxml2.XMLHTTP","Microsoft.XMLHTTP","Msxml2.XMLHTTP.4.0"];t.createXhr=function(){var e,t,n;if(typeof XMLHttpRequest!="undefined")return new XMLHttpRequest;for(t=0;t<3;t++){n=i[t];try{e=new ActiveXObject(n)}catch(r){}if(e){i=[n];break}}if(!e)throw new Error("createXhr(): XMLHttpRequest not available");return e},t.loadScript=function(e,t){var n=document.getElementsByTagName("head")[0],i=document.createElement("script");i.src=e,n.appendChild(i),r.isOldIE?i.onreadystatechange=function(){this.readyState=="loaded"&&t()}:i.onload=t}}),ace.define("ace/lib/event_emitter",["require","exports","module"],function(e,t,n){var r={};r._emit=r._dispatchEvent=function(e,t){this._eventRegistry=this._eventRegistry||{},this._defaultHandlers=this._defaultHandlers||{};var n=this._eventRegistry[e]||[],r=this._defaultHandlers[e];if(!n.length&&!r)return;if(typeof t!="object"||!t)t={};t.type||(t.type=e),t.stopPropagation||(t.stopPropagation=function(){this.propagationStopped=!0}),t.preventDefault||(t.preventDefault=function(){this.defaultPrevented=!0});for(var i=0;i<n.length;i++){n[i](t);if(t.propagationStopped)break}if(r&&!t.defaultPrevented)return r(t)},r.setDefaultHandler=function(e,t){this._defaultHandlers=this._defaultHandlers||{};if(this._defaultHandlers[e])throw new Error("The default handler for '"+e+"' is already set");this._defaultHandlers[e]=t},r.on=r.addEventListener=function(e,t){this._eventRegistry=this._eventRegistry||{};var n=this._eventRegistry[e];n||(n=this._eventRegistry[e]=[]),n.indexOf(t)==-1&&n.push(t)},r.removeListener=r.removeEventListener=function(e,t){this._eventRegistry=this._eventRegistry||{};var n=this._eventRegistry[e];if(!n)return;var r=n.indexOf(t);r!==-1&&n.splice(r,1)},r.removeAllListeners=function(e){this._eventRegistry&&(this._eventRegistry[e]=[])},t.EventEmitter=r}),ace.define("ace/selection",["require","exports","module","ace/lib/oop","ace/lib/lang","ace/lib/event_emitter","ace/range"],function(e,t,n){var r=e("./lib/oop"),i=e("./lib/lang"),s=e("./lib/event_emitter").EventEmitter,o=e("./range").Range,u=function(e){this.session=e,this.doc=e.getDocument(),this.clearSelection(),this.lead=this.selectionLead=this.doc.createAnchor(0,0),this.anchor=this.selectionAnchor=this.doc.createAnchor(0,0);var t=this;this.lead.on("change",function(e){t._emit("changeCursor"),t.$isEmpty||t._emit("changeSelection"),!t.$keepDesiredColumnOnChange&&e.old.column!=e.value.column&&(t.$desiredColumn=null)}),this.selectionAnchor.on("change",function(){t.$isEmpty||t._emit("changeSelection")})};(function(){r.implement(this,s),this.isEmpty=function(){return this.$isEmpty||this.anchor.row==this.lead.row&&this.anchor.column==this.lead.column},this.isMultiLine=function(){return this.isEmpty()?!1:this.getRange().isMultiLine()},this.getCursor=function(){return this.lead.getPosition()},this.setSelectionAnchor=function(e,t){this.anchor.setPosition(e,t),this.$isEmpty&&(this.$isEmpty=!1,this._emit("changeSelection"))},this.getSelectionAnchor=function(){return this.$isEmpty?this.getSelectionLead():this.anchor.getPosition()},this.getSelectionLead=function(){return this.lead.getPosition()},this.shiftSelection=function(e){if(this.$isEmpty){this.moveCursorTo(this.lead.row,this.lead.column+e);return}var t=this.getSelectionAnchor(),n=this.getSelectionLead(),r=this.isBackwards();(!r||t.column!==0)&&this.setSelectionAnchor(t.row,t.column+e),(r||n.column!==0)&&this.$moveSelection(function(){this.moveCursorTo(n.row,n.column+e)})},this.isBackwards=function(){var e=this.anchor,t=this.lead;return e.row>t.row||e.row==t.row&&e.column>t.column},this.getRange=function(){var e=this.anchor,t=this.lead;return this.isEmpty()?o.fromPoints(t,t):this.isBackwards()?o.fromPoints(t,e):o.fromPoints(e,t)},this.clearSelection=function(){this.$isEmpty||(this.$isEmpty=!0,this._emit("changeSelection"))},this.selectAll=function(){var e=this.doc.getLength()-1;this.setSelectionAnchor(0,0),this.moveCursorTo(e,this.doc.getLine(e).length)},this.setRange=this.setSelectionRange=function(e,t){t?(this.setSelectionAnchor(e.end.row,e.end.column),this.selectTo(e.start.row,e.start.column)):(this.setSelectionAnchor(e.start.row,e.start.column),this.selectTo(e.end.row,e.end.column)),this.$desiredColumn=null},this.$moveSelection=function(e){var t=this.lead;this.$isEmpty&&this.setSelectionAnchor(t.row,t.column),e.call(this)},this.selectTo=function(e,t){this.$moveSelection(function(){this.moveCursorTo(e,t)})},this.selectToPosition=function(e){this.$moveSelection(function(){this.moveCursorToPosition(e)})},this.selectUp=function(){this.$moveSelection(this.moveCursorUp)},this.selectDown=function(){this.$moveSelection(this.moveCursorDown)},this.selectRight=function(){this.$moveSelection(this.moveCursorRight)},this.selectLeft=function(){this.$moveSelection(this.moveCursorLeft)},this.selectLineStart=function(){this.$moveSelection(this.moveCursorLineStart)},this.selectLineEnd=function(){this.$moveSelection(this.moveCursorLineEnd)},this.selectFileEnd=function(){this.$moveSelection(this.moveCursorFileEnd)},this.selectFileStart=function(){this.$moveSelection(this.moveCursorFileStart)},this.selectWordRight=function(){this.$moveSelection(this.moveCursorWordRight)},this.selectWordLeft=function(){this.$moveSelection(this.moveCursorWordLeft)},this.getWordRange=function(e,t){if(typeof t=="undefined"){var n=e||this.lead;e=n.row,t=n.column}return this.session.getWordRange(e,t)},this.selectWord=function(){this.setSelectionRange(this.getWordRange())},this.selectAWord=function(){var e=this.getCursor(),t=this.session.getAWordRange(e.row,e.column);this.setSelectionRange(t)},this.getLineRange=function(e,t){var n=typeof e=="number"?e:this.lead.row,r,i=this.session.getFoldLine(n);return i?(n=i.start.row,r=i.end.row):r=n,t?new o(n,0,r,this.session.getLine(r).length):new o(n,0,r+1,0)},this.selectLine=function(){this.setSelectionRange(this.getLineRange())},this.moveCursorUp=function(){this.moveCursorBy(-1,0)},this.moveCursorDown=function(){this.moveCursorBy(1,0)},this.moveCursorLeft=function(){var e=this.lead.getPosition(),t;if(t=this.session.getFoldAt(e.row,e.column,-1))this.moveCursorTo(t.start.row,t.start.column);else if(e.column==0)e.row>0&&this.moveCursorTo(e.row-1,this.doc.getLine(e.row-1).length);else{var n=this.session.getTabSize();this.session.isTabStop(e)&&this.doc.getLine(e.row).slice(e.column-n,e.column).split(" ").length-1==n?this.moveCursorBy(0,-n):this.moveCursorBy(0,-1)}},this.moveCursorRight=function(){var e=this.lead.getPosition(),t;if(t=this.session.getFoldAt(e.row,e.column,1))this.moveCursorTo(t.end.row,t.end.column);else if(this.lead.column==this.doc.getLine(this.lead.row).length)this.lead.row<this.doc.getLength()-1&&this.moveCursorTo(this.lead.row+1,0);else{var n=this.session.getTabSize(),e=this.lead;this.session.isTabStop(e)&&this.doc.getLine(e.row).slice(e.column,e.column+n).split(" ").length-1==n?this.moveCursorBy(0,n):this.moveCursorBy(0,1)}},this.moveCursorLineStart=function(){var e=this.lead.row,t=this.lead.column,n=this.session.documentToScreenRow(e,t),r=this.session.screenToDocumentPosition(n,0),i=this.session.getDisplayLine(e,null,r.row,r.column),s=i.match(/^\s*/);s[0].length==t?this.moveCursorTo(r.row,r.column):this.moveCursorTo(r.row,r.column+s[0].length)},this.moveCursorLineEnd=function(){var e=this.lead,t=this.session.getDocumentLastRowColumnPosition(e.row,e.column);if(this.lead.column==t.column){var n=this.session.getLine(t.row);if(t.column==n.length){var r=n.search(/\s+$/);r>0&&(t.column=r)}}this.moveCursorTo(t.row,t.column)},this.moveCursorFileEnd=function(){var e=this.doc.getLength()-1,t=this.doc.getLine(e).length;this.moveCursorTo(e,t)},this.moveCursorFileStart=function(){this.moveCursorTo(0,0)},this.moveCursorLongWordRight=function(){var e=this.lead.row,t=this.lead.column,n=this.doc.getLine(e),r=n.substring(t),i;this.session.nonTokenRe.lastIndex=0,this.session.tokenRe.lastIndex=0;var s=this.session.getFoldAt(e,t,1);if(s){this.moveCursorTo(s.end.row,s.end.column);return}if(i=this.session.nonTokenRe.exec(r))t+=this.session.nonTokenRe.lastIndex,this.session.nonTokenRe.lastIndex=0,r=n.substring(t);if(t>=n.length){this.moveCursorTo(e,n.length),this.moveCursorRight(),e<this.doc.getLength()-1&&this.moveCursorWordRight();return}if(i=this.session.tokenRe.exec(r))t+=this.session.tokenRe.lastIndex,this.session.tokenRe.lastIndex=0;this.moveCursorTo(e,t)},this.moveCursorLongWordLeft=function(){var e=this.lead.row,t=this.lead.column,n;if(n=this.session.getFoldAt(e,t,-1)){this.moveCursorTo(n.start.row,n.start.column);return}var r=this.session.getFoldStringAt(e,t,-1);r==null&&(r=this.doc.getLine(e).substring(0,t));var s=i.stringReverse(r),o;this.session.nonTokenRe.lastIndex=0,this.session.tokenRe.lastIndex=0;if(o=this.session.nonTokenRe.exec(s))t-=this.session.nonTokenRe.lastIndex,s=s.slice(this.session.nonTokenRe.lastIndex),this.session.nonTokenRe.lastIndex=0;if(t<=0){this.moveCursorTo(e,0),this.moveCursorLeft(),e>0&&this.moveCursorWordLeft();return}if(o=this.session.tokenRe.exec(s))t-=this.session.tokenRe.lastIndex,this.session.tokenRe.lastIndex=0;this.moveCursorTo(e,t)},this.$shortWordEndIndex=function(e){var t,n=0,r,i=/\s/,s=this.session.tokenRe;s.lastIndex=0;if(t=this.session.tokenRe.exec(e))n=this.session.tokenRe.lastIndex;else{while((r=e[n])&&i.test(r))n++;if(n<=1){s.lastIndex=0;while((r=e[n])&&!s.test(r)){s.lastIndex=0,n++;if(i.test(r)){if(n>2){n--;break}while((r=e[n])&&i.test(r))n++;if(n>2)break}}}}return s.lastIndex=0,n},this.moveCursorShortWordRight=function(){var e=this.lead.row,t=this.lead.column,n=this.doc.getLine(e),r=n.substring(t),i=this.session.getFoldAt(e,t,1);if(i)return this.moveCursorTo(i.end.row,i.end.column);if(t==n.length){var s=this.doc.getLength();do e++,r=this.doc.getLine(e);while(e<s&&/^\s*$/.test(r));/^\s+/.test(r)||(r=""),t=0}var o=this.$shortWordEndIndex(r);this.moveCursorTo(e,t+o)},this.moveCursorShortWordLeft=function(){var e=this.lead.row,t=this.lead.column,n;if(n=this.session.getFoldAt(e,t,-1))return this.moveCursorTo(n.start.row,n.start.column);var r=this.session.getLine(e).substring(0,t);if(t==0){do e--,r=this.doc.getLine(e);while(e>0&&/^\s*$/.test(r));t=r.length,/\s+$/.test(r)||(r="")}var s=i.stringReverse(r),o=this.$shortWordEndIndex(s);return this.moveCursorTo(e,t-o)},this.moveCursorWordRight=function(){this.session.$selectLongWords?this.moveCursorLongWordRight():this.moveCursorShortWordRight()},this.moveCursorWordLeft=function(){this.session.$selectLongWords?this.moveCursorLongWordLeft():this.moveCursorShortWordLeft()},this.moveCursorBy=function(e,t){var n=this.session.documentToScreenPosition(this.lead.row,this.lead.column);t===0&&(this.$desiredColumn?n.column=this.$desiredColumn:this.$desiredColumn=n.column);var r=this.session.screenToDocumentPosition(n.row+e,n.column);this.moveCursorTo(r.row,r.column+t,t===0)},this.moveCursorToPosition=function(e){this.moveCursorTo(e.row,e.column)},this.moveCursorTo=function(e,t,n){var r=this.session.getFoldAt(e,t,1);r&&(e=r.start.row,t=r.start.column),this.$keepDesiredColumnOnChange=!0,this.lead.setPosition(e,t),this.$keepDesiredColumnOnChange=!1,n||(this.$desiredColumn=null)},this.moveCursorToScreen=function(e,t,n){var r=this.session.screenToDocumentPosition(e,t);this.moveCursorTo(r.row,r.column,n)},this.detach=function(){this.lead.detach(),this.anchor.detach(),this.session=this.doc=null},this.fromOrientedRange=function(e){this.setSelectionRange(e,e.cursor==e.start),this.$desiredColumn=e.desiredColumn||this.$desiredColumn},this.toOrientedRange=function(e){var t=this.getRange();return e?(e.start.column=t.start.column,e.start.row=t.start.row,e.end.column=t.end.column,e.end.row=t.end.row):e=t,e.cursor=this.isBackwards()?e.start:e.end,e.desiredColumn=this.$desiredColumn,e}}).call(u.prototype),t.Selection=u}),ace.define("ace/range",["require","exports","module"],function(e,t,n){var r=function(e,t,n,r){this.start={row:e,column:t},this.end={row:n,column:r}};(function(){this.isEqual=function(e){return this.start.row==e.start.row&&this.end.row==e.end.row&&this.start.column==e.start.column&&this.end.column==e.end.column},this.toString=function(){return"Range: ["+this.start.row+"/"+this.start.column+"] -> ["+this.end.row+"/"+this.end.column+"]"},this.contains=function(e,t){return this.compare(e,t)==0},this.compareRange=function(e){var t,n=e.end,r=e.start;return t=this.compare(n.row,n.column),t==1?(t=this.compare(r.row,r.column),t==1?2:t==0?1:0):t==-1?-2:(t=this.compare(r.row,r.column),t==-1?-1:t==1?42:0)},this.comparePoint=function(e){return this.compare(e.row,e.column)},this.containsRange=function(e){return this.comparePoint(e.start)==0&&this.comparePoint(e.end)==0},this.intersects=function(e){var t=this.compareRange(e);return t==-1||t==0||t==1},this.isEnd=function(e,t){return this.end.row==e&&this.end.column==t},this.isStart=function(e,t){return this.start.row==e&&this.start.column==t},this.setStart=function(e,t){typeof e=="object"?(this.start.column=e.column,this.start.row=e.row):(this.start.row=e,this.start.column=t)},this.setEnd=function(e,t){typeof e=="object"?(this.end.column=e.column,this.end.row=e.row):(this.end.row=e,this.end.column=t)},this.inside=function(e,t){return this.compare(e,t)==0?this.isEnd(e,t)||this.isStart(e,t)?!1:!0:!1},this.insideStart=function(e,t){return this.compare(e,t)==0?this.isEnd(e,t)?!1:!0:!1},this.insideEnd=function(e,t){return this.compare(e,t)==0?this.isStart(e,t)?!1:!0:!1},this.compare=function(e,t){return!this.isMultiLine()&&e===this.start.row?t<this.start.column?-1:t>this.end.column?1:0:e<this.start.row?-1:e>this.end.row?1:this.start.row===e?t>=this.start.column?0:-1:this.end.row===e?t<=this.end.column?0:1:0},this.compareStart=function(e,t){return this.start.row==e&&this.start.column==t?-1:this.compare(e,t)},this.compareEnd=function(e,t){return this.end.row==e&&this.end.column==t?1:this.compare(e,t)},this.compareInside=function(e,t){return this.end.row==e&&this.end.column==t?1:this.start.row==e&&this.start.column==t?-1:this.compare(e,t)},this.clipRows=function(e,t){if(this.end.row>t)var n={row:t+1,column:0};if(this.start.row>t)var i={row:t+1,column:0};if(this.start.row<e)var i={row:e,column:0};if(this.end.row<e)var n={row:e,column:0};return r.fromPoints(i||this.start,n||this.end)},this.extend=function(e,t){var n=this.compare(e,t);if(n==0)return this;if(n==-1)var i={row:e,column:t};else var s={row:e,column:t};return r.fromPoints(i||this.start,s||this.end)},this.isEmpty=function(){return this.start.row==this.end.row&&this.start.column==this.end.column},this.isMultiLine=function(){return this.start.row!==this.end.row},this.clone=function(){return r.fromPoints(this.start,this.end)},this.collapseRows=function(){return this.end.column==0?new r(this.start.row,0,Math.max(this.start.row,this.end.row-1),0):new r(this.start.row,0,this.end.row,0)},this.toScreenRange=function(e){var t=e.documentToScreenPosition(this.start),n=e.documentToScreenPosition(this.end);return new r(t.row,t.column,n.row,n.column)}}).call(r.prototype),r.fromPoints=function(e,t){return new r(e.row,e.column,t.row,t.column)},t.Range=r}),ace.define("ace/mode/text",["require","exports","module","ace/tokenizer","ace/mode/text_highlight_rules","ace/mode/behaviour","ace/unicode"],function(e,t,n){var r=e("../tokenizer").Tokenizer,i=e("./text_highlight_rules").TextHighlightRules,s=e("./behaviour").Behaviour,o=e("../unicode"),u=function(){this.$tokenizer=new r((new i).getRules()),this.$behaviour=new s};(function(){this.tokenRe=new RegExp("^["+o.packages.L+o.packages.Mn+o.packages.Mc+o.packages.Nd+o.packages.Pc+"\\$_]+","g"),this.nonTokenRe=new RegExp("^(?:[^"+o.packages.L+o.packages.Mn+o.packages.Mc+o.packages.Nd+o.packages.Pc+"\\$_]|s])+","g"),this.getTokenizer=function(){return this.$tokenizer},this.toggleCommentLines=function(e,t,n,r){},this.getNextLineIndent=function(e,t,n){return""},this.checkOutdent=function(e,t,n){return!1},this.autoOutdent=function(e,t,n){},this.$getIndent=function(e){var t=e.match(/^(\s+)/);return t?t[1]:""},this.createWorker=function(e){return null},this.createModeDelegates=function(e){if(!this.$embeds)return;this.$modes={};for(var t=0;t<this.$embeds.length;t++)e[this.$embeds[t]]&&(this.$modes[this.$embeds[t]]=new e[this.$embeds[t]]);var n=["toggleCommentLines","getNextLineIndent","checkOutdent","autoOutdent","transformAction"];for(var t=0;t<n.length;t++)(function(e){var r=n[t],i=e[r];e[n[t]]=function(){return this.$delegator(r,arguments,i)}})(this)},this.$delegator=function(e,t,n){var r=t[0];for(var i=0;i<this.$embeds.length;i++){if(!this.$modes[this.$embeds[i]])continue;var s=r.split(this.$embeds[i]);if(!s[0]&&s[1]){t[0]=s[1];var o=this.$modes[this.$embeds[i]];return o[e].apply(o,t)}}var u=n.apply(this,t);return n?u:undefined},this.transformAction=function(e,t,n,r,i){if(this.$behaviour){var s=this.$behaviour.getBehaviours();for(var o in s)if(s[o][t]){var u=s[o][t].apply(this,arguments);if(u)return u}}}}).call(u.prototype),t.Mode=u}),ace.define("ace/tokenizer",["require","exports","module"],function(e,t,n){var r=function(e,t){t=t?"g"+t:"g",this.rules=e,this.regExps={},this.matchMappings={};for(var n in this.rules){var r=this.rules[n],i=r,s=[],o=0,u=this.matchMappings[n]={};for(var a=0;a<i.length;a++){i[a].regex instanceof RegExp&&(i[a].regex=i[a].regex.toString().slice(1,-1));var f=(new RegExp("(?:("+i[a].regex+")|(.))")).exec("a").length-2,l=i[a].regex.replace(/\\([0-9]+)/g,function(e,t){return"\\"+(parseInt(t,10)+o+1)});if(f>1&&i[a].token.length!==f-1)throw new Error("For "+i[a].regex+" the matching groups ("+(f-1)+") and length of the token array ("+i[a].token.length+") don't match (rule #"+a+" of state "+n+")");u[o]={rule:a,len:f},o+=f,s.push(l)}this.regExps[n]=new RegExp("(?:("+s.join(")|(")+")|(.))",t)}};(function(){this.getLineTokens=function(e,t){var n=t||"start",r=this.rules[n],i=this.matchMappings[n],s=this.regExps[n];s.lastIndex=0;var o,u=[],a=0,f={type:null,value:""};while(o=s.exec(e)){var l="text",c=null,h=[o[0]];for(var p=0;p<o.length-2;p++){if(o[p+1]===undefined)continue;c=r[i[p].rule],i[p].len>1&&(h=o.slice(p+2,p+1+i[p].len)),typeof c.token=="function"?l=c.token.apply(this,h):l=c.token;if(c.next){n=c.next,r=this.rules[n],i=this.matchMappings[n],a=s.lastIndex,s=this.regExps[n];if(s===undefined)throw new Error("You indicated a state of "+c.next+" to go to, but it doesn't exist!");s.lastIndex=a}break}if(h[0]){typeof l=="string"&&(h=[h.join("")],l=[l]);for(var p=0;p<h.length;p++){if(!h[p])continue;(!c||c.merge||l[p]==="text")&&f.type===l[p]?f.value+=h[p]:(f.type&&u.push(f),f={type:l[p],value:h[p]})}}if(a==e.length)break;a=s.lastIndex}return f.type&&u.push(f),{tokens:u,state:n}}}).call(r.prototype),t.Tokenizer=r}),ace.define("ace/mode/text_highlight_rules",["require","exports","module","ace/lib/lang"],function(e,t,n){var r=e("../lib/lang"),i=function(){this.$rules={start:[{token:"empty_line",regex:"^$"},{token:"text",regex:".+"}]}};(function(){this.addRules=function(e,t){for(var n in e){var r=e[n];for(var i=0;i<r.length;i++){var s=r[i];s.next&&(s.next=t+s.next)}this.$rules[t+n]=r}},this.getRules=function(){return this.$rules},this.embedRules=function(e,t,n,i,s){var o=(new e).getRules();if(i)for(var u=0;u<i.length;u++)i[u]=t+i[u];else{i=[];for(var a in o)i.push(t+a)}this.addRules(o,t);var f=Array.prototype[s?"push":"unshift"];for(var u=0;u<i.length;u++)f.apply(this.$rules[i[u]],r.deepCopy(n));this.$embeds||(this.$embeds=[]),this.$embeds.push(t)},this.getEmbeds=function(){return this.$embeds},this.createKeywordMapper=function(e,t,n,r){var i=Object.create(null);return Object.keys(e).forEach(function(t){var s=e[t];n&&(s=s.toLowerCase());var o=s.split(r||"|");for(var u=o.length;u--;)i[o[u]]=t}),e=null,n?function(e){return i[e.toLowerCase()]||t}:function(e){return i[e]||t}},this.getKeywords=function(){return this.$keywords}}).call(i.prototype),t.TextHighlightRules=i}),ace.define("ace/mode/behaviour",["require","exports","module"],function(e,t,n){var r=function(){this.$behaviours={}};(function(){this.add=function(e,t,n){switch(undefined){case this.$behaviours:this.$behaviours={};case this.$behaviours[e]:this.$behaviours[e]={}}this.$behaviours[e][t]=n},this.addBehaviours=function(e){for(var t in e)for(var n in e[t])this.add(t,n,e[t][n])},this.remove=function(e){this.$behaviours&&this.$behaviours[e]&&delete this.$behaviours[e]},this.inherit=function(e,t){if(typeof e=="function")var n=(new e).getBehaviours(t);else var n=e.getBehaviours(t);this.addBehaviours(n)},this.getBehaviours=function(e){if(!e)return this.$behaviours;var t={};for(var n=0;n<e.length;n++)this.$behaviours[e[n]]&&(t[e[n]]=this.$behaviours[e[n]]);return t}}).call(r.prototype),t.Behaviour=r}),ace.define("ace/unicode",["require","exports","module"],function(e,t,n){function r(e){var n=/\w{4}/g;for(var r in e)t.packages[r]=e[r].replace(n,"\\u$&")}t.packages={},r({L:"0041-005A0061-007A00AA00B500BA00C0-00D600D8-00F600F8-02C102C6-02D102E0-02E402EC02EE0370-037403760377037A-037D03860388-038A038C038E-03A103A3-03F503F7-0481048A-05250531-055605590561-058705D0-05EA05F0-05F20621-064A066E066F0671-06D306D506E506E606EE06EF06FA-06FC06FF07100712-072F074D-07A507B107CA-07EA07F407F507FA0800-0815081A082408280904-0939093D09500958-0961097109720979-097F0985-098C098F09900993-09A809AA-09B009B209B6-09B909BD09CE09DC09DD09DF-09E109F009F10A05-0A0A0A0F0A100A13-0A280A2A-0A300A320A330A350A360A380A390A59-0A5C0A5E0A72-0A740A85-0A8D0A8F-0A910A93-0AA80AAA-0AB00AB20AB30AB5-0AB90ABD0AD00AE00AE10B05-0B0C0B0F0B100B13-0B280B2A-0B300B320B330B35-0B390B3D0B5C0B5D0B5F-0B610B710B830B85-0B8A0B8E-0B900B92-0B950B990B9A0B9C0B9E0B9F0BA30BA40BA8-0BAA0BAE-0BB90BD00C05-0C0C0C0E-0C100C12-0C280C2A-0C330C35-0C390C3D0C580C590C600C610C85-0C8C0C8E-0C900C92-0CA80CAA-0CB30CB5-0CB90CBD0CDE0CE00CE10D05-0D0C0D0E-0D100D12-0D280D2A-0D390D3D0D600D610D7A-0D7F0D85-0D960D9A-0DB10DB3-0DBB0DBD0DC0-0DC60E01-0E300E320E330E40-0E460E810E820E840E870E880E8A0E8D0E94-0E970E99-0E9F0EA1-0EA30EA50EA70EAA0EAB0EAD-0EB00EB20EB30EBD0EC0-0EC40EC60EDC0EDD0F000F40-0F470F49-0F6C0F88-0F8B1000-102A103F1050-1055105A-105D106110651066106E-10701075-1081108E10A0-10C510D0-10FA10FC1100-1248124A-124D1250-12561258125A-125D1260-1288128A-128D1290-12B012B2-12B512B8-12BE12C012C2-12C512C8-12D612D8-13101312-13151318-135A1380-138F13A0-13F41401-166C166F-167F1681-169A16A0-16EA1700-170C170E-17111720-17311740-17511760-176C176E-17701780-17B317D717DC1820-18771880-18A818AA18B0-18F51900-191C1950-196D1970-19741980-19AB19C1-19C71A00-1A161A20-1A541AA71B05-1B331B45-1B4B1B83-1BA01BAE1BAF1C00-1C231C4D-1C4F1C5A-1C7D1CE9-1CEC1CEE-1CF11D00-1DBF1E00-1F151F18-1F1D1F20-1F451F48-1F4D1F50-1F571F591F5B1F5D1F5F-1F7D1F80-1FB41FB6-1FBC1FBE1FC2-1FC41FC6-1FCC1FD0-1FD31FD6-1FDB1FE0-1FEC1FF2-1FF41FF6-1FFC2071207F2090-209421022107210A-211321152119-211D212421262128212A-212D212F-2139213C-213F2145-2149214E218321842C00-2C2E2C30-2C5E2C60-2CE42CEB-2CEE2D00-2D252D30-2D652D6F2D80-2D962DA0-2DA62DA8-2DAE2DB0-2DB62DB8-2DBE2DC0-2DC62DC8-2DCE2DD0-2DD62DD8-2DDE2E2F300530063031-3035303B303C3041-3096309D-309F30A1-30FA30FC-30FF3105-312D3131-318E31A0-31B731F0-31FF3400-4DB54E00-9FCBA000-A48CA4D0-A4FDA500-A60CA610-A61FA62AA62BA640-A65FA662-A66EA67F-A697A6A0-A6E5A717-A71FA722-A788A78BA78CA7FB-A801A803-A805A807-A80AA80C-A822A840-A873A882-A8B3A8F2-A8F7A8FBA90A-A925A930-A946A960-A97CA984-A9B2A9CFAA00-AA28AA40-AA42AA44-AA4BAA60-AA76AA7AAA80-AAAFAAB1AAB5AAB6AAB9-AABDAAC0AAC2AADB-AADDABC0-ABE2AC00-D7A3D7B0-D7C6D7CB-D7FBF900-FA2DFA30-FA6DFA70-FAD9FB00-FB06FB13-FB17FB1DFB1F-FB28FB2A-FB36FB38-FB3CFB3EFB40FB41FB43FB44FB46-FBB1FBD3-FD3DFD50-FD8FFD92-FDC7FDF0-FDFBFE70-FE74FE76-FEFCFF21-FF3AFF41-FF5AFF66-FFBEFFC2-FFC7FFCA-FFCFFFD2-FFD7FFDA-FFDC",Ll:"0061-007A00AA00B500BA00DF-00F600F8-00FF01010103010501070109010B010D010F01110113011501170119011B011D011F01210123012501270129012B012D012F01310133013501370138013A013C013E014001420144014601480149014B014D014F01510153015501570159015B015D015F01610163016501670169016B016D016F0171017301750177017A017C017E-0180018301850188018C018D019201950199-019B019E01A101A301A501A801AA01AB01AD01B001B401B601B901BA01BD-01BF01C601C901CC01CE01D001D201D401D601D801DA01DC01DD01DF01E101E301E501E701E901EB01ED01EF01F001F301F501F901FB01FD01FF02010203020502070209020B020D020F02110213021502170219021B021D021F02210223022502270229022B022D022F02310233-0239023C023F0240024202470249024B024D024F-02930295-02AF037103730377037B-037D039003AC-03CE03D003D103D5-03D703D903DB03DD03DF03E103E303E503E703E903EB03ED03EF-03F303F503F803FB03FC0430-045F04610463046504670469046B046D046F04710473047504770479047B047D047F0481048B048D048F04910493049504970499049B049D049F04A104A304A504A704A904AB04AD04AF04B104B304B504B704B904BB04BD04BF04C204C404C604C804CA04CC04CE04CF04D104D304D504D704D904DB04DD04DF04E104E304E504E704E904EB04ED04EF04F104F304F504F704F904FB04FD04FF05010503050505070509050B050D050F05110513051505170519051B051D051F0521052305250561-05871D00-1D2B1D62-1D771D79-1D9A1E011E031E051E071E091E0B1E0D1E0F1E111E131E151E171E191E1B1E1D1E1F1E211E231E251E271E291E2B1E2D1E2F1E311E331E351E371E391E3B1E3D1E3F1E411E431E451E471E491E4B1E4D1E4F1E511E531E551E571E591E5B1E5D1E5F1E611E631E651E671E691E6B1E6D1E6F1E711E731E751E771E791E7B1E7D1E7F1E811E831E851E871E891E8B1E8D1E8F1E911E931E95-1E9D1E9F1EA11EA31EA51EA71EA91EAB1EAD1EAF1EB11EB31EB51EB71EB91EBB1EBD1EBF1EC11EC31EC51EC71EC91ECB1ECD1ECF1ED11ED31ED51ED71ED91EDB1EDD1EDF1EE11EE31EE51EE71EE91EEB1EED1EEF1EF11EF31EF51EF71EF91EFB1EFD1EFF-1F071F10-1F151F20-1F271F30-1F371F40-1F451F50-1F571F60-1F671F70-1F7D1F80-1F871F90-1F971FA0-1FA71FB0-1FB41FB61FB71FBE1FC2-1FC41FC61FC71FD0-1FD31FD61FD71FE0-1FE71FF2-1FF41FF61FF7210A210E210F2113212F21342139213C213D2146-2149214E21842C30-2C5E2C612C652C662C682C6A2C6C2C712C732C742C76-2C7C2C812C832C852C872C892C8B2C8D2C8F2C912C932C952C972C992C9B2C9D2C9F2CA12CA32CA52CA72CA92CAB2CAD2CAF2CB12CB32CB52CB72CB92CBB2CBD2CBF2CC12CC32CC52CC72CC92CCB2CCD2CCF2CD12CD32CD52CD72CD92CDB2CDD2CDF2CE12CE32CE42CEC2CEE2D00-2D25A641A643A645A647A649A64BA64DA64FA651A653A655A657A659A65BA65DA65FA663A665A667A669A66BA66DA681A683A685A687A689A68BA68DA68FA691A693A695A697A723A725A727A729A72BA72DA72F-A731A733A735A737A739A73BA73DA73FA741A743A745A747A749A74BA74DA74FA751A753A755A757A759A75BA75DA75FA761A763A765A767A769A76BA76DA76FA771-A778A77AA77CA77FA781A783A785A787A78CFB00-FB06FB13-FB17FF41-FF5A",Lu:"0041-005A00C0-00D600D8-00DE01000102010401060108010A010C010E01100112011401160118011A011C011E01200122012401260128012A012C012E01300132013401360139013B013D013F0141014301450147014A014C014E01500152015401560158015A015C015E01600162016401660168016A016C016E017001720174017601780179017B017D018101820184018601870189-018B018E-0191019301940196-0198019C019D019F01A001A201A401A601A701A901AC01AE01AF01B1-01B301B501B701B801BC01C401C701CA01CD01CF01D101D301D501D701D901DB01DE01E001E201E401E601E801EA01EC01EE01F101F401F6-01F801FA01FC01FE02000202020402060208020A020C020E02100212021402160218021A021C021E02200222022402260228022A022C022E02300232023A023B023D023E02410243-02460248024A024C024E03700372037603860388-038A038C038E038F0391-03A103A3-03AB03CF03D2-03D403D803DA03DC03DE03E003E203E403E603E803EA03EC03EE03F403F703F903FA03FD-042F04600462046404660468046A046C046E04700472047404760478047A047C047E0480048A048C048E04900492049404960498049A049C049E04A004A204A404A604A804AA04AC04AE04B004B204B404B604B804BA04BC04BE04C004C104C304C504C704C904CB04CD04D004D204D404D604D804DA04DC04DE04E004E204E404E604E804EA04EC04EE04F004F204F404F604F804FA04FC04FE05000502050405060508050A050C050E05100512051405160518051A051C051E0520052205240531-055610A0-10C51E001E021E041E061E081E0A1E0C1E0E1E101E121E141E161E181E1A1E1C1E1E1E201E221E241E261E281E2A1E2C1E2E1E301E321E341E361E381E3A1E3C1E3E1E401E421E441E461E481E4A1E4C1E4E1E501E521E541E561E581E5A1E5C1E5E1E601E621E641E661E681E6A1E6C1E6E1E701E721E741E761E781E7A1E7C1E7E1E801E821E841E861E881E8A1E8C1E8E1E901E921E941E9E1EA01EA21EA41EA61EA81EAA1EAC1EAE1EB01EB21EB41EB61EB81EBA1EBC1EBE1EC01EC21EC41EC61EC81ECA1ECC1ECE1ED01ED21ED41ED61ED81EDA1EDC1EDE1EE01EE21EE41EE61EE81EEA1EEC1EEE1EF01EF21EF41EF61EF81EFA1EFC1EFE1F08-1F0F1F18-1F1D1F28-1F2F1F38-1F3F1F48-1F4D1F591F5B1F5D1F5F1F68-1F6F1FB8-1FBB1FC8-1FCB1FD8-1FDB1FE8-1FEC1FF8-1FFB21022107210B-210D2110-211221152119-211D212421262128212A-212D2130-2133213E213F214521832C00-2C2E2C602C62-2C642C672C692C6B2C6D-2C702C722C752C7E-2C802C822C842C862C882C8A2C8C2C8E2C902C922C942C962C982C9A2C9C2C9E2CA02CA22CA42CA62CA82CAA2CAC2CAE2CB02CB22CB42CB62CB82CBA2CBC2CBE2CC02CC22CC42CC62CC82CCA2CCC2CCE2CD02CD22CD42CD62CD82CDA2CDC2CDE2CE02CE22CEB2CEDA640A642A644A646A648A64AA64CA64EA650A652A654A656A658A65AA65CA65EA662A664A666A668A66AA66CA680A682A684A686A688A68AA68CA68EA690A692A694A696A722A724A726A728A72AA72CA72EA732A734A736A738A73AA73CA73EA740A742A744A746A748A74AA74CA74EA750A752A754A756A758A75AA75CA75EA760A762A764A766A768A76AA76CA76EA779A77BA77DA77EA780A782A784A786A78BFF21-FF3A",Lt:"01C501C801CB01F21F88-1F8F1F98-1F9F1FA8-1FAF1FBC1FCC1FFC",Lm:"02B0-02C102C6-02D102E0-02E402EC02EE0374037A0559064006E506E607F407F507FA081A0824082809710E460EC610FC17D718431AA71C78-1C7D1D2C-1D611D781D9B-1DBF2071207F2090-20942C7D2D6F2E2F30053031-3035303B309D309E30FC-30FEA015A4F8-A4FDA60CA67FA717-A71FA770A788A9CFAA70AADDFF70FF9EFF9F",Lo:"01BB01C0-01C3029405D0-05EA05F0-05F20621-063F0641-064A066E066F0671-06D306D506EE06EF06FA-06FC06FF07100712-072F074D-07A507B107CA-07EA0800-08150904-0939093D09500958-096109720979-097F0985-098C098F09900993-09A809AA-09B009B209B6-09B909BD09CE09DC09DD09DF-09E109F009F10A05-0A0A0A0F0A100A13-0A280A2A-0A300A320A330A350A360A380A390A59-0A5C0A5E0A72-0A740A85-0A8D0A8F-0A910A93-0AA80AAA-0AB00AB20AB30AB5-0AB90ABD0AD00AE00AE10B05-0B0C0B0F0B100B13-0B280B2A-0B300B320B330B35-0B390B3D0B5C0B5D0B5F-0B610B710B830B85-0B8A0B8E-0B900B92-0B950B990B9A0B9C0B9E0B9F0BA30BA40BA8-0BAA0BAE-0BB90BD00C05-0C0C0C0E-0C100C12-0C280C2A-0C330C35-0C390C3D0C580C590C600C610C85-0C8C0C8E-0C900C92-0CA80CAA-0CB30CB5-0CB90CBD0CDE0CE00CE10D05-0D0C0D0E-0D100D12-0D280D2A-0D390D3D0D600D610D7A-0D7F0D85-0D960D9A-0DB10DB3-0DBB0DBD0DC0-0DC60E01-0E300E320E330E40-0E450E810E820E840E870E880E8A0E8D0E94-0E970E99-0E9F0EA1-0EA30EA50EA70EAA0EAB0EAD-0EB00EB20EB30EBD0EC0-0EC40EDC0EDD0F000F40-0F470F49-0F6C0F88-0F8B1000-102A103F1050-1055105A-105D106110651066106E-10701075-1081108E10D0-10FA1100-1248124A-124D1250-12561258125A-125D1260-1288128A-128D1290-12B012B2-12B512B8-12BE12C012C2-12C512C8-12D612D8-13101312-13151318-135A1380-138F13A0-13F41401-166C166F-167F1681-169A16A0-16EA1700-170C170E-17111720-17311740-17511760-176C176E-17701780-17B317DC1820-18421844-18771880-18A818AA18B0-18F51900-191C1950-196D1970-19741980-19AB19C1-19C71A00-1A161A20-1A541B05-1B331B45-1B4B1B83-1BA01BAE1BAF1C00-1C231C4D-1C4F1C5A-1C771CE9-1CEC1CEE-1CF12135-21382D30-2D652D80-2D962DA0-2DA62DA8-2DAE2DB0-2DB62DB8-2DBE2DC0-2DC62DC8-2DCE2DD0-2DD62DD8-2DDE3006303C3041-3096309F30A1-30FA30FF3105-312D3131-318E31A0-31B731F0-31FF3400-4DB54E00-9FCBA000-A014A016-A48CA4D0-A4F7A500-A60BA610-A61FA62AA62BA66EA6A0-A6E5A7FB-A801A803-A805A807-A80AA80C-A822A840-A873A882-A8B3A8F2-A8F7A8FBA90A-A925A930-A946A960-A97CA984-A9B2AA00-AA28AA40-AA42AA44-AA4BAA60-AA6FAA71-AA76AA7AAA80-AAAFAAB1AAB5AAB6AAB9-AABDAAC0AAC2AADBAADCABC0-ABE2AC00-D7A3D7B0-D7C6D7CB-D7FBF900-FA2DFA30-FA6DFA70-FAD9FB1DFB1F-FB28FB2A-FB36FB38-FB3CFB3EFB40FB41FB43FB44FB46-FBB1FBD3-FD3DFD50-FD8FFD92-FDC7FDF0-FDFBFE70-FE74FE76-FEFCFF66-FF6FFF71-FF9DFFA0-FFBEFFC2-FFC7FFCA-FFCFFFD2-FFD7FFDA-FFDC",M:"0300-036F0483-04890591-05BD05BF05C105C205C405C505C70610-061A064B-065E067006D6-06DC06DE-06E406E706E806EA-06ED07110730-074A07A6-07B007EB-07F30816-0819081B-08230825-08270829-082D0900-0903093C093E-094E0951-0955096209630981-098309BC09BE-09C409C709C809CB-09CD09D709E209E30A01-0A030A3C0A3E-0A420A470A480A4B-0A4D0A510A700A710A750A81-0A830ABC0ABE-0AC50AC7-0AC90ACB-0ACD0AE20AE30B01-0B030B3C0B3E-0B440B470B480B4B-0B4D0B560B570B620B630B820BBE-0BC20BC6-0BC80BCA-0BCD0BD70C01-0C030C3E-0C440C46-0C480C4A-0C4D0C550C560C620C630C820C830CBC0CBE-0CC40CC6-0CC80CCA-0CCD0CD50CD60CE20CE30D020D030D3E-0D440D46-0D480D4A-0D4D0D570D620D630D820D830DCA0DCF-0DD40DD60DD8-0DDF0DF20DF30E310E34-0E3A0E47-0E4E0EB10EB4-0EB90EBB0EBC0EC8-0ECD0F180F190F350F370F390F3E0F3F0F71-0F840F860F870F90-0F970F99-0FBC0FC6102B-103E1056-1059105E-10601062-10641067-106D1071-10741082-108D108F109A-109D135F1712-17141732-1734175217531772177317B6-17D317DD180B-180D18A91920-192B1930-193B19B0-19C019C819C91A17-1A1B1A55-1A5E1A60-1A7C1A7F1B00-1B041B34-1B441B6B-1B731B80-1B821BA1-1BAA1C24-1C371CD0-1CD21CD4-1CE81CED1CF21DC0-1DE61DFD-1DFF20D0-20F02CEF-2CF12DE0-2DFF302A-302F3099309AA66F-A672A67CA67DA6F0A6F1A802A806A80BA823-A827A880A881A8B4-A8C4A8E0-A8F1A926-A92DA947-A953A980-A983A9B3-A9C0AA29-AA36AA43AA4CAA4DAA7BAAB0AAB2-AAB4AAB7AAB8AABEAABFAAC1ABE3-ABEAABECABEDFB1EFE00-FE0FFE20-FE26",Mn:"0300-036F0483-04870591-05BD05BF05C105C205C405C505C70610-061A064B-065E067006D6-06DC06DF-06E406E706E806EA-06ED07110730-074A07A6-07B007EB-07F30816-0819081B-08230825-08270829-082D0900-0902093C0941-0948094D0951-095509620963098109BC09C1-09C409CD09E209E30A010A020A3C0A410A420A470A480A4B-0A4D0A510A700A710A750A810A820ABC0AC1-0AC50AC70AC80ACD0AE20AE30B010B3C0B3F0B41-0B440B4D0B560B620B630B820BC00BCD0C3E-0C400C46-0C480C4A-0C4D0C550C560C620C630CBC0CBF0CC60CCC0CCD0CE20CE30D41-0D440D4D0D620D630DCA0DD2-0DD40DD60E310E34-0E3A0E47-0E4E0EB10EB4-0EB90EBB0EBC0EC8-0ECD0F180F190F350F370F390F71-0F7E0F80-0F840F860F870F90-0F970F99-0FBC0FC6102D-10301032-10371039103A103D103E10581059105E-10601071-1074108210851086108D109D135F1712-17141732-1734175217531772177317B7-17BD17C617C9-17D317DD180B-180D18A91920-19221927192819321939-193B1A171A181A561A58-1A5E1A601A621A65-1A6C1A73-1A7C1A7F1B00-1B031B341B36-1B3A1B3C1B421B6B-1B731B801B811BA2-1BA51BA81BA91C2C-1C331C361C371CD0-1CD21CD4-1CE01CE2-1CE81CED1DC0-1DE61DFD-1DFF20D0-20DC20E120E5-20F02CEF-2CF12DE0-2DFF302A-302F3099309AA66FA67CA67DA6F0A6F1A802A806A80BA825A826A8C4A8E0-A8F1A926-A92DA947-A951A980-A982A9B3A9B6-A9B9A9BCAA29-AA2EAA31AA32AA35AA36AA43AA4CAAB0AAB2-AAB4AAB7AAB8AABEAABFAAC1ABE5ABE8ABEDFB1EFE00-FE0FFE20-FE26",Mc:"0903093E-09400949-094C094E0982098309BE-09C009C709C809CB09CC09D70A030A3E-0A400A830ABE-0AC00AC90ACB0ACC0B020B030B3E0B400B470B480B4B0B4C0B570BBE0BBF0BC10BC20BC6-0BC80BCA-0BCC0BD70C01-0C030C41-0C440C820C830CBE0CC0-0CC40CC70CC80CCA0CCB0CD50CD60D020D030D3E-0D400D46-0D480D4A-0D4C0D570D820D830DCF-0DD10DD8-0DDF0DF20DF30F3E0F3F0F7F102B102C10311038103B103C105610571062-10641067-106D108310841087-108C108F109A-109C17B617BE-17C517C717C81923-19261929-192B193019311933-193819B0-19C019C819C91A19-1A1B1A551A571A611A631A641A6D-1A721B041B351B3B1B3D-1B411B431B441B821BA11BA61BA71BAA1C24-1C2B1C341C351CE11CF2A823A824A827A880A881A8B4-A8C3A952A953A983A9B4A9B5A9BAA9BBA9BD-A9C0AA2FAA30AA33AA34AA4DAA7BABE3ABE4ABE6ABE7ABE9ABEAABEC",Me:"0488048906DE20DD-20E020E2-20E4A670-A672",N:"0030-003900B200B300B900BC-00BE0660-066906F0-06F907C0-07C90966-096F09E6-09EF09F4-09F90A66-0A6F0AE6-0AEF0B66-0B6F0BE6-0BF20C66-0C6F0C78-0C7E0CE6-0CEF0D66-0D750E50-0E590ED0-0ED90F20-0F331040-10491090-10991369-137C16EE-16F017E0-17E917F0-17F91810-18191946-194F19D0-19DA1A80-1A891A90-1A991B50-1B591BB0-1BB91C40-1C491C50-1C5920702074-20792080-20892150-21822185-21892460-249B24EA-24FF2776-27932CFD30073021-30293038-303A3192-31953220-32293251-325F3280-328932B1-32BFA620-A629A6E6-A6EFA830-A835A8D0-A8D9A900-A909A9D0-A9D9AA50-AA59ABF0-ABF9FF10-FF19",Nd:"0030-00390660-066906F0-06F907C0-07C90966-096F09E6-09EF0A66-0A6F0AE6-0AEF0B66-0B6F0BE6-0BEF0C66-0C6F0CE6-0CEF0D66-0D6F0E50-0E590ED0-0ED90F20-0F291040-10491090-109917E0-17E91810-18191946-194F19D0-19DA1A80-1A891A90-1A991B50-1B591BB0-1BB91C40-1C491C50-1C59A620-A629A8D0-A8D9A900-A909A9D0-A9D9AA50-AA59ABF0-ABF9FF10-FF19",Nl:"16EE-16F02160-21822185-218830073021-30293038-303AA6E6-A6EF",No:"00B200B300B900BC-00BE09F4-09F90BF0-0BF20C78-0C7E0D70-0D750F2A-0F331369-137C17F0-17F920702074-20792080-20892150-215F21892460-249B24EA-24FF2776-27932CFD3192-31953220-32293251-325F3280-328932B1-32BFA830-A835",P:"0021-00230025-002A002C-002F003A003B003F0040005B-005D005F007B007D00A100AB00B700BB00BF037E0387055A-055F0589058A05BE05C005C305C605F305F40609060A060C060D061B061E061F066A-066D06D40700-070D07F7-07F90830-083E0964096509700DF40E4F0E5A0E5B0F04-0F120F3A-0F3D0F850FD0-0FD4104A-104F10FB1361-13681400166D166E169B169C16EB-16ED1735173617D4-17D617D8-17DA1800-180A1944194519DE19DF1A1E1A1F1AA0-1AA61AA8-1AAD1B5A-1B601C3B-1C3F1C7E1C7F1CD32010-20272030-20432045-20512053-205E207D207E208D208E2329232A2768-277527C527C627E6-27EF2983-299829D8-29DB29FC29FD2CF9-2CFC2CFE2CFF2E00-2E2E2E302E313001-30033008-30113014-301F3030303D30A030FBA4FEA4FFA60D-A60FA673A67EA6F2-A6F7A874-A877A8CEA8CFA8F8-A8FAA92EA92FA95FA9C1-A9CDA9DEA9DFAA5C-AA5FAADEAADFABEBFD3EFD3FFE10-FE19FE30-FE52FE54-FE61FE63FE68FE6AFE6BFF01-FF03FF05-FF0AFF0C-FF0FFF1AFF1BFF1FFF20FF3B-FF3DFF3FFF5BFF5DFF5F-FF65",Pd:"002D058A05BE140018062010-20152E172E1A301C303030A0FE31FE32FE58FE63FF0D",Ps:"0028005B007B0F3A0F3C169B201A201E2045207D208D23292768276A276C276E27702772277427C527E627E827EA27EC27EE2983298529872989298B298D298F299129932995299729D829DA29FC2E222E242E262E283008300A300C300E3010301430163018301A301DFD3EFE17FE35FE37FE39FE3BFE3DFE3FFE41FE43FE47FE59FE5BFE5DFF08FF3BFF5BFF5FFF62",Pe:"0029005D007D0F3B0F3D169C2046207E208E232A2769276B276D276F27712773277527C627E727E927EB27ED27EF298429862988298A298C298E2990299229942996299829D929DB29FD2E232E252E272E293009300B300D300F3011301530173019301B301E301FFD3FFE18FE36FE38FE3AFE3CFE3EFE40FE42FE44FE48FE5AFE5CFE5EFF09FF3DFF5DFF60FF63",Pi:"00AB2018201B201C201F20392E022E042E092E0C2E1C2E20",Pf:"00BB2019201D203A2E032E052E0A2E0D2E1D2E21",Pc:"005F203F20402054FE33FE34FE4D-FE4FFF3F",Po:"0021-00230025-0027002A002C002E002F003A003B003F0040005C00A100B700BF037E0387055A-055F058905C005C305C605F305F40609060A060C060D061B061E061F066A-066D06D40700-070D07F7-07F90830-083E0964096509700DF40E4F0E5A0E5B0F04-0F120F850FD0-0FD4104A-104F10FB1361-1368166D166E16EB-16ED1735173617D4-17D617D8-17DA1800-18051807-180A1944194519DE19DF1A1E1A1F1AA0-1AA61AA8-1AAD1B5A-1B601C3B-1C3F1C7E1C7F1CD3201620172020-20272030-2038203B-203E2041-20432047-205120532055-205E2CF9-2CFC2CFE2CFF2E002E012E06-2E082E0B2E0E-2E162E182E192E1B2E1E2E1F2E2A-2E2E2E302E313001-3003303D30FBA4FEA4FFA60D-A60FA673A67EA6F2-A6F7A874-A877A8CEA8CFA8F8-A8FAA92EA92FA95FA9C1-A9CDA9DEA9DFAA5C-AA5FAADEAADFABEBFE10-FE16FE19FE30FE45FE46FE49-FE4CFE50-FE52FE54-FE57FE5F-FE61FE68FE6AFE6BFF01-FF03FF05-FF07FF0AFF0CFF0EFF0FFF1AFF1BFF1FFF20FF3CFF61FF64FF65",S:"0024002B003C-003E005E0060007C007E00A2-00A900AC00AE-00B100B400B600B800D700F702C2-02C502D2-02DF02E5-02EB02ED02EF-02FF03750384038503F604820606-0608060B060E060F06E906FD06FE07F609F209F309FA09FB0AF10B700BF3-0BFA0C7F0CF10CF20D790E3F0F01-0F030F13-0F170F1A-0F1F0F340F360F380FBE-0FC50FC7-0FCC0FCE0FCF0FD5-0FD8109E109F13601390-139917DB194019E0-19FF1B61-1B6A1B74-1B7C1FBD1FBF-1FC11FCD-1FCF1FDD-1FDF1FED-1FEF1FFD1FFE20442052207A-207C208A-208C20A0-20B8210021012103-21062108210921142116-2118211E-2123212521272129212E213A213B2140-2144214A-214D214F2190-2328232B-23E82400-24262440-244A249C-24E92500-26CD26CF-26E126E326E8-26FF2701-27042706-2709270C-27272729-274B274D274F-27522756-275E2761-276727942798-27AF27B1-27BE27C0-27C427C7-27CA27CC27D0-27E527F0-29822999-29D729DC-29FB29FE-2B4C2B50-2B592CE5-2CEA2E80-2E992E9B-2EF32F00-2FD52FF0-2FFB300430123013302030363037303E303F309B309C319031913196-319F31C0-31E33200-321E322A-32503260-327F328A-32B032C0-32FE3300-33FF4DC0-4DFFA490-A4C6A700-A716A720A721A789A78AA828-A82BA836-A839AA77-AA79FB29FDFCFDFDFE62FE64-FE66FE69FF04FF0BFF1C-FF1EFF3EFF40FF5CFF5EFFE0-FFE6FFE8-FFEEFFFCFFFD",Sm:"002B003C-003E007C007E00AC00B100D700F703F60606-060820442052207A-207C208A-208C2140-2144214B2190-2194219A219B21A021A321A621AE21CE21CF21D221D421F4-22FF2308-230B23202321237C239B-23B323DC-23E125B725C125F8-25FF266F27C0-27C427C7-27CA27CC27D0-27E527F0-27FF2900-29822999-29D729DC-29FB29FE-2AFF2B30-2B442B47-2B4CFB29FE62FE64-FE66FF0BFF1C-FF1EFF5CFF5EFFE2FFE9-FFEC",Sc:"002400A2-00A5060B09F209F309FB0AF10BF90E3F17DB20A0-20B8A838FDFCFE69FF04FFE0FFE1FFE5FFE6",Sk:"005E006000A800AF00B400B802C2-02C502D2-02DF02E5-02EB02ED02EF-02FF0375038403851FBD1FBF-1FC11FCD-1FCF1FDD-1FDF1FED-1FEF1FFD1FFE309B309CA700-A716A720A721A789A78AFF3EFF40FFE3",So:"00A600A700A900AE00B000B60482060E060F06E906FD06FE07F609FA0B700BF3-0BF80BFA0C7F0CF10CF20D790F01-0F030F13-0F170F1A-0F1F0F340F360F380FBE-0FC50FC7-0FCC0FCE0FCF0FD5-0FD8109E109F13601390-1399194019E0-19FF1B61-1B6A1B74-1B7C210021012103-21062108210921142116-2118211E-2123212521272129212E213A213B214A214C214D214F2195-2199219C-219F21A121A221A421A521A7-21AD21AF-21CD21D021D121D321D5-21F32300-2307230C-231F2322-2328232B-237B237D-239A23B4-23DB23E2-23E82400-24262440-244A249C-24E92500-25B625B8-25C025C2-25F72600-266E2670-26CD26CF-26E126E326E8-26FF2701-27042706-2709270C-27272729-274B274D274F-27522756-275E2761-276727942798-27AF27B1-27BE2800-28FF2B00-2B2F2B452B462B50-2B592CE5-2CEA2E80-2E992E9B-2EF32F00-2FD52FF0-2FFB300430123013302030363037303E303F319031913196-319F31C0-31E33200-321E322A-32503260-327F328A-32B032C0-32FE3300-33FF4DC0-4DFFA490-A4C6A828-A82BA836A837A839AA77-AA79FDFDFFE4FFE8FFEDFFEEFFFCFFFD",Z:"002000A01680180E2000-200A20282029202F205F3000",Zs:"002000A01680180E2000-200A202F205F3000",Zl:"2028",Zp:"2029",C:"0000-001F007F-009F00AD03780379037F-0383038B038D03A20526-05300557055805600588058B-059005C8-05CF05EB-05EF05F5-0605061C061D0620065F06DD070E070F074B074C07B2-07BF07FB-07FF082E082F083F-08FF093A093B094F095609570973-097809800984098D098E0991099209A909B109B3-09B509BA09BB09C509C609C909CA09CF-09D609D8-09DB09DE09E409E509FC-0A000A040A0B-0A0E0A110A120A290A310A340A370A3A0A3B0A3D0A43-0A460A490A4A0A4E-0A500A52-0A580A5D0A5F-0A650A76-0A800A840A8E0A920AA90AB10AB40ABA0ABB0AC60ACA0ACE0ACF0AD1-0ADF0AE40AE50AF00AF2-0B000B040B0D0B0E0B110B120B290B310B340B3A0B3B0B450B460B490B4A0B4E-0B550B58-0B5B0B5E0B640B650B72-0B810B840B8B-0B8D0B910B96-0B980B9B0B9D0BA0-0BA20BA5-0BA70BAB-0BAD0BBA-0BBD0BC3-0BC50BC90BCE0BCF0BD1-0BD60BD8-0BE50BFB-0C000C040C0D0C110C290C340C3A-0C3C0C450C490C4E-0C540C570C5A-0C5F0C640C650C70-0C770C800C810C840C8D0C910CA90CB40CBA0CBB0CC50CC90CCE-0CD40CD7-0CDD0CDF0CE40CE50CF00CF3-0D010D040D0D0D110D290D3A-0D3C0D450D490D4E-0D560D58-0D5F0D640D650D76-0D780D800D810D840D97-0D990DB20DBC0DBE0DBF0DC7-0DC90DCB-0DCE0DD50DD70DE0-0DF10DF5-0E000E3B-0E3E0E5C-0E800E830E850E860E890E8B0E8C0E8E-0E930E980EA00EA40EA60EA80EA90EAC0EBA0EBE0EBF0EC50EC70ECE0ECF0EDA0EDB0EDE-0EFF0F480F6D-0F700F8C-0F8F0F980FBD0FCD0FD9-0FFF10C6-10CF10FD-10FF1249124E124F12571259125E125F1289128E128F12B112B612B712BF12C112C612C712D7131113161317135B-135E137D-137F139A-139F13F5-13FF169D-169F16F1-16FF170D1715-171F1737-173F1754-175F176D17711774-177F17B417B517DE17DF17EA-17EF17FA-17FF180F181A-181F1878-187F18AB-18AF18F6-18FF191D-191F192C-192F193C-193F1941-1943196E196F1975-197F19AC-19AF19CA-19CF19DB-19DD1A1C1A1D1A5F1A7D1A7E1A8A-1A8F1A9A-1A9F1AAE-1AFF1B4C-1B4F1B7D-1B7F1BAB-1BAD1BBA-1BFF1C38-1C3A1C4A-1C4C1C80-1CCF1CF3-1CFF1DE7-1DFC1F161F171F1E1F1F1F461F471F4E1F4F1F581F5A1F5C1F5E1F7E1F7F1FB51FC51FD41FD51FDC1FF01FF11FF51FFF200B-200F202A-202E2060-206F20722073208F2095-209F20B9-20CF20F1-20FF218A-218F23E9-23FF2427-243F244B-245F26CE26E226E4-26E727002705270A270B2728274C274E2753-2755275F27602795-279727B027BF27CB27CD-27CF2B4D-2B4F2B5A-2BFF2C2F2C5F2CF2-2CF82D26-2D2F2D66-2D6E2D70-2D7F2D97-2D9F2DA72DAF2DB72DBF2DC72DCF2DD72DDF2E32-2E7F2E9A2EF4-2EFF2FD6-2FEF2FFC-2FFF3040309730983100-3104312E-3130318F31B8-31BF31E4-31EF321F32FF4DB6-4DBF9FCC-9FFFA48D-A48FA4C7-A4CFA62C-A63FA660A661A674-A67BA698-A69FA6F8-A6FFA78D-A7FAA82C-A82FA83A-A83FA878-A87FA8C5-A8CDA8DA-A8DFA8FC-A8FFA954-A95EA97D-A97FA9CEA9DA-A9DDA9E0-A9FFAA37-AA3FAA4EAA4FAA5AAA5BAA7C-AA7FAAC3-AADAAAE0-ABBFABEEABEFABFA-ABFFD7A4-D7AFD7C7-D7CAD7FC-F8FFFA2EFA2FFA6EFA6FFADA-FAFFFB07-FB12FB18-FB1CFB37FB3DFB3FFB42FB45FBB2-FBD2FD40-FD4FFD90FD91FDC8-FDEFFDFEFDFFFE1A-FE1FFE27-FE2FFE53FE67FE6C-FE6FFE75FEFD-FF00FFBF-FFC1FFC8FFC9FFD0FFD1FFD8FFD9FFDD-FFDFFFE7FFEF-FFFBFFFEFFFF",Cc:"0000-001F007F-009F",Cf:"00AD0600-060306DD070F17B417B5200B-200F202A-202E2060-2064206A-206FFEFFFFF9-FFFB",Co:"E000-F8FF",Cs:"D800-DFFF",Cn:"03780379037F-0383038B038D03A20526-05300557055805600588058B-059005C8-05CF05EB-05EF05F5-05FF06040605061C061D0620065F070E074B074C07B2-07BF07FB-07FF082E082F083F-08FF093A093B094F095609570973-097809800984098D098E0991099209A909B109B3-09B509BA09BB09C509C609C909CA09CF-09D609D8-09DB09DE09E409E509FC-0A000A040A0B-0A0E0A110A120A290A310A340A370A3A0A3B0A3D0A43-0A460A490A4A0A4E-0A500A52-0A580A5D0A5F-0A650A76-0A800A840A8E0A920AA90AB10AB40ABA0ABB0AC60ACA0ACE0ACF0AD1-0ADF0AE40AE50AF00AF2-0B000B040B0D0B0E0B110B120B290B310B340B3A0B3B0B450B460B490B4A0B4E-0B550B58-0B5B0B5E0B640B650B72-0B810B840B8B-0B8D0B910B96-0B980B9B0B9D0BA0-0BA20BA5-0BA70BAB-0BAD0BBA-0BBD0BC3-0BC50BC90BCE0BCF0BD1-0BD60BD8-0BE50BFB-0C000C040C0D0C110C290C340C3A-0C3C0C450C490C4E-0C540C570C5A-0C5F0C640C650C70-0C770C800C810C840C8D0C910CA90CB40CBA0CBB0CC50CC90CCE-0CD40CD7-0CDD0CDF0CE40CE50CF00CF3-0D010D040D0D0D110D290D3A-0D3C0D450D490D4E-0D560D58-0D5F0D640D650D76-0D780D800D810D840D97-0D990DB20DBC0DBE0DBF0DC7-0DC90DCB-0DCE0DD50DD70DE0-0DF10DF5-0E000E3B-0E3E0E5C-0E800E830E850E860E890E8B0E8C0E8E-0E930E980EA00EA40EA60EA80EA90EAC0EBA0EBE0EBF0EC50EC70ECE0ECF0EDA0EDB0EDE-0EFF0F480F6D-0F700F8C-0F8F0F980FBD0FCD0FD9-0FFF10C6-10CF10FD-10FF1249124E124F12571259125E125F1289128E128F12B112B612B712BF12C112C612C712D7131113161317135B-135E137D-137F139A-139F13F5-13FF169D-169F16F1-16FF170D1715-171F1737-173F1754-175F176D17711774-177F17DE17DF17EA-17EF17FA-17FF180F181A-181F1878-187F18AB-18AF18F6-18FF191D-191F192C-192F193C-193F1941-1943196E196F1975-197F19AC-19AF19CA-19CF19DB-19DD1A1C1A1D1A5F1A7D1A7E1A8A-1A8F1A9A-1A9F1AAE-1AFF1B4C-1B4F1B7D-1B7F1BAB-1BAD1BBA-1BFF1C38-1C3A1C4A-1C4C1C80-1CCF1CF3-1CFF1DE7-1DFC1F161F171F1E1F1F1F461F471F4E1F4F1F581F5A1F5C1F5E1F7E1F7F1FB51FC51FD41FD51FDC1FF01FF11FF51FFF2065-206920722073208F2095-209F20B9-20CF20F1-20FF218A-218F23E9-23FF2427-243F244B-245F26CE26E226E4-26E727002705270A270B2728274C274E2753-2755275F27602795-279727B027BF27CB27CD-27CF2B4D-2B4F2B5A-2BFF2C2F2C5F2CF2-2CF82D26-2D2F2D66-2D6E2D70-2D7F2D97-2D9F2DA72DAF2DB72DBF2DC72DCF2DD72DDF2E32-2E7F2E9A2EF4-2EFF2FD6-2FEF2FFC-2FFF3040309730983100-3104312E-3130318F31B8-31BF31E4-31EF321F32FF4DB6-4DBF9FCC-9FFFA48D-A48FA4C7-A4CFA62C-A63FA660A661A674-A67BA698-A69FA6F8-A6FFA78D-A7FAA82C-A82FA83A-A83FA878-A87FA8C5-A8CDA8DA-A8DFA8FC-A8FFA954-A95EA97D-A97FA9CEA9DA-A9DDA9E0-A9FFAA37-AA3FAA4EAA4FAA5AAA5BAA7C-AA7FAAC3-AADAAAE0-ABBFABEEABEFABFA-ABFFD7A4-D7AFD7C7-D7CAD7FC-D7FFFA2EFA2FFA6EFA6FFADA-FAFFFB07-FB12FB18-FB1CFB37FB3DFB3FFB42FB45FBB2-FBD2FD40-FD4FFD90FD91FDC8-FDEFFDFEFDFFFE1A-FE1FFE27-FE2FFE53FE67FE6C-FE6FFE75FEFDFEFEFF00FFBF-FFC1FFC8FFC9FFD0FFD1FFD8FFD9FFDD-FFDFFFE7FFEF-FFF8FFFEFFFF"})}),ace.define("ace/document",["require","exports","module","ace/lib/oop","ace/lib/event_emitter","ace/range","ace/anchor"],function(e,t,n){var r=e("./lib/oop"),i=e("./lib/event_emitter").EventEmitter,s=e("./range").Range,o=e("./anchor").Anchor,u=function(e){this.$lines=[],e.length==0?this.$lines=[""]:Array.isArray(e)?this.insertLines(0,e):this.insert({row:0,column:0},e)};(function(){r.implement(this,i),this.setValue=function(e){var t=this.getLength();this.remove(new s(0,0,t,this.getLine(t-1).length)),this.insert({row:0,column:0},e)},this.getValue=function(){return this.getAllLines().join(this.getNewLineCharacter())},this.createAnchor=function(e,t){return new o(this,e,t)},"aaa".split(/a/).length==0?this.$split=function(e){return e.replace(/\r\n|\r/g,"\n").split("\n")}:this.$split=function(e){return e.split(/\r\n|\r|\n/)},this.$detectNewLine=function(e){var t=e.match(/^.*?(\r\n|\r|\n)/m);t?this.$autoNewLine=t[1]:this.$autoNewLine="\n"},this.getNewLineCharacter=function(){switch(this.$newLineMode){case"windows":return"\r\n";case"unix":return"\n";case"auto":return this.$autoNewLine}},this.$autoNewLine="\n",this.$newLineMode="auto",this.setNewLineMode=function(e){if(this.$newLineMode===e)return;this.$newLineMode=e},this.getNewLineMode=function(){return this.$newLineMode},this.isNewLine=function(e){return e=="\r\n"||e=="\r"||e=="\n"},this.getLine=function(e){return this.$lines[e]||""},this.getLines=function(e,t){return this.$lines.slice(e,t+1)},this.getAllLines=function(){return this.getLines(0,this.getLength())},this.getLength=function(){return this.$lines.length},this.getTextRange=function(e){if(e.start.row==e.end.row)return this.$lines[e.start.row].substring(e.start.column,e.end.column);var t=this.getLines(e.start.row+1,e.end.row-1);return t.unshift((this.$lines[e.start.row]||"").substring(e.start.column)),t.push((this.$lines[e.end.row]||"").substring(0,e.end.column)),t.join(this.getNewLineCharacter())},this.$clipPosition=function(e){var t=this.getLength();return e.row>=t&&(e.row=Math.max(0,t-1),e.column=this.getLine(t-1).length),e},this.insert=function(e,t){if(!t||t.length===0)return e;e=this.$clipPosition(e),this.getLength()<=1&&this.$detectNewLine(t);var n=this.$split(t),r=n.splice(0,1)[0],i=n.length==0?null:n.splice(n.length-1,1)[0];return e=this.insertInLine(e,r),i!==null&&(e=this.insertNewLine(e),e=this.insertLines(e.row,n),e=this.insertInLine(e,i||"")),e},this.insertLines=function(e,t){if(t.length==0)return{row:e,column:0};if(t.length>65535){var n=this.insertLines(e,t.slice(65535));t=t.slice(0,65535)}var r=[e,0];r.push.apply(r,t),this.$lines.splice.apply(this.$lines,r);var i=new s(e,0,e+t.length,0),o={action:"insertLines",range:i,lines:t};return this._emit("change",{data:o}),n||i.end},this.insertNewLine=function(e){e=this.$clipPosition(e);var t=this.$lines[e.row]||"";this.$lines[e.row]=t.substring(0,e.column),this.$lines.splice(e.row+1,0,t.substring(e.column,t.length));var n={row:e.row+1,column:0},r={action:"insertText",range:s.fromPoints(e,n),text:this.getNewLineCharacter()};return this._emit("change",{data:r}),n},this.insertInLine=function(e,t){if(t.length==0)return e;var n=this.$lines[e.row]||"";this.$lines[e.row]=n.substring(0,e.column)+t+n.substring(e.column);var r={row:e.row,column:e.column+t.length},i={action:"insertText",range:s.fromPoints(e,r),text:t};return this._emit("change",{data:i}),r},this.remove=function(e){e.start=this.$clipPosition(e.start),e.end=this.$clipPosition(e.end);if(e.isEmpty())return e.start;var t=e.start.row,n=e.end.row;if(e.isMultiLine()){var r=e.start.column==0?t:t+1,i=n-1;e.end.column>0&&this.removeInLine(n,0,e.end.column),i>=r&&this.removeLines(r,i),r!=t&&(this.removeInLine(t,e.start.column,this.getLine(t).length),this.removeNewLine(e.start.row))}else this.removeInLine(t,e.start.column,e.end.column);return e.start},this.removeInLine=function(e,t,n){if(t==n)return;var r=new s(e,t,e,n),i=this.getLine(e),o=i.substring(t,n),u=i.substring(0,t)+i.substring(n,i.length);this.$lines.splice(e,1,u);var a={action:"removeText",range:r,text:o};return this._emit("change",{data:a}),r.start},this.removeLines=function(e,t){var n=new s(e,0,t+1,0),r=this.$lines.splice(e,t-e+1),i={action:"removeLines",range:n,nl:this.getNewLineCharacter(),lines:r};return this._emit("change",{data:i}),r},this.removeNewLine=function(e){var t=this.getLine(e),n=this.getLine(e+1),r=new s(e,t.length,e+1,0),i=t+n;this.$lines.splice(e,2,i);var o={action:"removeText",range:r,text:this.getNewLineCharacter()};this._emit("change",{data:o})},this.replace=function(e,t){if(t.length==0&&e.isEmpty())return e.start;if(t==this.getTextRange(e))return e.end;this.remove(e);if(t)var n=this.insert(e.start,t);else n=e.start;return n},this.applyDeltas=function(e){for(var t=0;t<e.length;t++){var n=e[t],r=s.fromPoints(n.range.start,n.range.end);n.action=="insertLines"?this.insertLines(r.start.row,n.lines):n.action=="insertText"?this.insert(r.start,n.text):n.action=="removeLines"?this.removeLines(r.start.row,r.end.row-1):n.action=="removeText"&&this.remove(r)}},this.revertDeltas=function(e){for(var t=e.length-1;t>=0;t--){var n=e[t],r=s.fromPoints(n.range.start,n.range.end);n.action=="insertLines"?this.removeLines(r.start.row,r.end.row-1):n.action=="insertText"?this.remove(r):n.action=="removeLines"?this.insertLines(r.start.row,n.lines):n.action=="removeText"&&this.insert(r.start,n.text)}}}).call(u.prototype),t.Document=u}),ace.define("ace/anchor",["require","exports","module","ace/lib/oop","ace/lib/event_emitter"],function(e,t,n){var r=e("./lib/oop"),i=e("./lib/event_emitter").EventEmitter,s=t.Anchor=function(e,t,n){this.document=e,typeof n=="undefined"?this.setPosition(t.row,t.column):this.setPosition(t,n),this.$onChange=this.onChange.bind(this),e.on("change",this.$onChange)};(function(){r.implement(this,i),this.getPosition=function(){return this.$clipPositionToDocument(this.row,this.column)},this.getDocument=function(){return this.document},this.onChange=function(e){var t=e.data,n=t.range;if(n.start.row==n.end.row&&n.start.row!=this.row)return;if(n.start.row>this.row)return;if(n.start.row==this.row&&n.start.column>this.column)return;var r=this.row,i=this.column;t.action==="insertText"?n.start.row===r&&n.start.column<=i?n.start.row===n.end.row?i+=n.end.column-n.start.column:(i-=n.start.column,r+=n.end.row-n.start.row):n.start.row!==n.end.row&&n.start.row<r&&(r+=n.end.row-n.start.row):t.action==="insertLines"?n.start.row<=r&&(r+=n.end.row-n.start.row):t.action=="removeText"?n.start.row==r&&n.start.column<i?n.end.column>=i?i=n.start.column:i=Math.max(0,i-(n.end.column-n.start.column)):n.start.row!==n.end.row&&n.start.row<r?(n.end.row==r&&(i=Math.max(0,i-n.end.column)+n.start.column),r-=n.end.row-n.start.row):n.end.row==r&&(r-=n.end.row-n.start.row,i=Math.max(0,i-n.end.column)+n.start.column):t.action=="removeLines"&&n.start.row<=r&&(n.end.row<=r?r-=n.end.row-n.start.row:(r=n.start.row,i=0)),this.setPosition(r,i,!0)},this.setPosition=function(e,t,n){var r;n?r={row:e,column:t}:r=this.$clipPositionToDocument(e,t);if(this.row==r.row&&this.column==r.column)return;var i={row:this.row,column:this.column};this.row=r.row,this.column=r.column,this._emit("change",{old:i,value:r})},this.detach=function(){this.document.removeEventListener("change",this.$onChange)},this.$clipPositionToDocument=function(e,t){var n={};return e>=this.document.getLength()?(n.row=Math.max(0,this.document.getLength()-1),n.column=this.document.getLine(n.row).length):e<0?(n.row=0,n.column=0):(n.row=e,n.column=Math.min(this.document.getLine(n.row).length,Math.max(0,t))),t<0&&(n.column=0),n}}).call(s.prototype)}),ace.define("ace/background_tokenizer",["require","exports","module","ace/lib/oop","ace/lib/event_emitter"],function(e,t,n){var r=e("./lib/oop"),i=e("./lib/event_emitter").EventEmitter,s=5e3,o=function(e,t){this.running=!1,this.lines=[],this.states=[],this.currentLine=0,this.tokenizer=e;var n=this;this.$worker=function(){if(!n.running)return;var e=new Date,t=n.currentLine,r=n.doc,i=0,s=r.getLength();while(n.currentLine<s){n.$tokenizeRow(n.currentLine);while(n.lines[n.currentLine])n.currentLine++;i++;if(i%5==0&&new Date-e>20){n.fireUpdateEvent(t,n.currentLine-1),n.running=setTimeout(n.$worker,20);return}}n.running=!1,n.fireUpdateEvent(t,s-1)}};(function(){r.implement(this,i),this.setTokenizer=function(e){this.tokenizer=e,this.lines=[],this.states=[],this.start(0)},this.setDocument=function(e){this.doc=e,this.lines=[],this.states=[],this.stop()},this.fireUpdateEvent=function(e,t){var n={first:e,last:t};this._emit("update",{data:n})},this.start=function(e){this.currentLine=Math.min(e||0,this.currentLine,this.doc.getLength()),this.lines.splice(this.currentLine,this.lines.length),this.states.splice(this.currentLine,this.states.length),this.stop(),this.running=setTimeout(this.$worker,700)},this.$updateOnChange=function(e){var t=e.range,n=t.start.row,r=t.end.row-n;if(r===0)this.lines[n]=null;else if(e.action=="removeText"||e.action=="removeLines")this.lines.splice(n,r+1,null),this.states.splice(n,r+1,null);else{var i=Array(r+1);i.unshift(n,1),this.lines.splice.apply(this.lines,i),this.states.splice.apply(this.states,i)}this.currentLine=Math.min(n,this.currentLine,this.doc.getLength()),this.stop(),this.running=setTimeout(this.$worker,700)},this.stop=function(){this.running&&clearTimeout(this.running),this.running=!1},this.getTokens=function(e){return this.lines[e]||this.$tokenizeRow(e)},this.getState=function(e){return this.currentLine==e&&this.$tokenizeRow(e),this.states[e]||"start"},this.$tokenizeRow=function(e){var t=this.doc.getLine(e),n=this.states[e-1];if(t.length>s){var r={value:t.substr(s),type:"text"};t=t.slice(0,s)}var i=this.tokenizer.getLineTokens(t,n);return r&&(i.tokens.push(r),i.state="start"),this.states[e]!==i.state?(this.states[e]=i.state,this.lines[e+1]=null,this.currentLine>e+1&&(this.currentLine=e+1)):this.currentLine==e&&(this.currentLine=e+1),this.lines[e]=i.tokens}}).call(o.prototype),t.BackgroundTokenizer=o}),ace.define("ace/search_highlight",["require","exports","module","ace/lib/lang","ace/lib/oop","ace/range"],function(e,t,n){var r=e("./lib/lang"),i=e("./lib/oop"),s=e("./range").Range,o=function(e,t,n){this.setRegexp(e),this.clazz=t,this.type=n||"text"};(function(){this.MAX_RANGES=500,this.setRegexp=function(e){if(this.regExp+""==e+"")return;this.regExp=e,this.cache=[]},this.update=function(e,t,n,i){if(!this.regExp)return;var o=i.firstRow,u=i.lastRow;for(var a=o;a<=u;a++){var f=this.cache[a];f==null&&(f=r.getMatchOffsets(n.getLine(a),this.regExp),f.length>this.MAX_RANGES&&(f=f.slice(0,this.MAX_RANGES)),f=f.map(function(e){return new s(a,e.offset,a,e.offset+e.length)}),this.cache[a]=f.length?f:"");for(var l=f.length;l--;)t.drawSingleLineMarker(e,f[l].toScreenRange(n),this.clazz,i,null,this.type)}}}).call(o.prototype),t.SearchHighlight=o}),ace.define("ace/edit_session/folding",["require","exports","module","ace/range","ace/edit_session/fold_line","ace/edit_session/fold","ace/token_iterator"],function(e,t,n){function u(){this.getFoldAt=function(e,t,n){var r=this.getFoldLine(e);if(!r)return null;var i=r.folds;for(var s=0;s<i.length;s++){var o=i[s];if(o.range.contains(e,t)){if(n==1&&o.range.isEnd(e,t))continue;if(n==-1&&o.range.isStart(e,t))continue;return o}}},this.getFoldsInRange=function(e){e=e.clone();var t=e.start,n=e.end,r=this.$foldData,i=[];t.column+=1,n.column-=1;for(var s=0;s<r.length;s++){var o=r[s].range.compareRange(e);if(o==2)continue;if(o==-2)break;var u=r[s].folds;for(var a=0;a<u.length;a++){var f=u[a];o=f.range.compareRange(e);if(o==-2)break;if(o==2)continue;if(o==42)break;i.push(f)}}return i},this.getAllFolds=function(){function n(t){e.push(t);if(!t.subFolds)return;for(var r=0;r<t.subFolds.length;r++)n(t.subFolds[r])}var e=[],t=this.$foldData;for(var r=0;r<t.length;r++)for(var i=0;i<t[r].folds.length;i++)n(t[r].folds[i]);return e},this.getFoldStringAt=function(e,t,n,r){r=r||this.getFoldLine(e);if(!r)return null;var i={end:{column:0}},s,o;for(var u=0;u<r.folds.length;u++){o=r.folds[u];var a=o.range.compareEnd(e,t);if(a==-1){s=this.getLine(o.start.row).substring(i.end.column,o.start.column);break}if(a===0)return null;i=o}return s||(s=this.getLine(o.start.row).substring(i.end.column)),n==-1?s.substring(0,t-i.end.column):n==1?s.substring(t-i.end.column):s},this.getFoldLine=function(e,t){var n=this.$foldData,r=0;t&&(r=n.indexOf(t)),r==-1&&(r=0);for(r;r<n.length;r++){var i=n[r];if(i.start.row<=e&&i.end.row>=e)return i;if(i.end.row>e)return null}return null},this.getNextFoldLine=function(e,t){var n=this.$foldData,r=0;t&&(r=n.indexOf(t)),r==-1&&(r=0);for(r;r<n.length;r++){var i=n[r];if(i.end.row>=e)return i}return null},this.getFoldedRowCount=function(e,t){var n=this.$foldData,r=t-e+1;for(var i=0;i<n.length;i++){var s=n[i],o=s.end.row,u=s.start.row;if(o>=t){u<t&&(u>=e?r-=t-u:r=0);break}o>=e&&(u>=e?r-=o-u:r-=o-e+1)}return r},this.$addFoldLine=function(e){return this.$foldData.push(e),this.$foldData.sort(function(e,t){return e.start.row-t.start.row}),e},this.addFold=function(e,t){var n=this.$foldData,r=!1,o;e instanceof s?o=e:o=new s(t,e),this.$clipRangeToDocument(o.range);var u=o.start.row,a=o.start.column,f=o.end.row,l=o.end.column;if(u==f&&l-a<2)throw"The range has to be at least 2 characters width";var c=this.getFoldAt(u,a,1),h=this.getFoldAt(f,l,-1);if(c&&h==c)return c.addSubFold(o);if(c&&!c.range.isStart(u,a)||h&&!h.range.isEnd(f,l))throw"A fold can't intersect already existing fold"+o.range+c.range;var p=this.getFoldsInRange(o.range);p.length>0&&(this.removeFolds(p),o.subFolds=p);for(var d=0;d<n.length;d++){var v=n[d];if(f==v.start.row){v.addFold(o),r=!0;break}if(u==v.end.row){v.addFold(o),r=!0;if(!o.sameRow){var m=n[d+1];if(m&&m.start.row==f){v.merge(m);break}}break}if(f<=v.start.row)break}return r||(v=this.$addFoldLine(new i(this.$foldData,o))),this.$useWrapMode?this.$updateWrapData(v.start.row,v.start.row):this.$updateRowLengthCache(v.start.row,v.start.row),this.$modified=!0,this._emit("changeFold",{data:o}),o},this.addFolds=function(e){e.forEach(function(e){this.addFold(e)},this)},this.removeFold=function(e){var t=e.foldLine,n=t.start.row,r=t.end.row,i=this.$foldData,s=t.folds;if(s.length==1)i.splice(i.indexOf(t),1);else if(t.range.isEnd(e.end.row,e.end.column))s.pop(),t.end.row=s[s.length-1].end.row,t.end.column=s[s.length-1].end.column;else if(t.range.isStart(e.start.row,e.start.column))s.shift(),t.start.row=s[0].start.row,t.start.column=s[0].start.column;else if(e.sameRow)s.splice(s.indexOf(e),1);else{var o=t.split(e.start.row,e.start.column);s=o.folds,s.shift(),o.start.row=s[0].start.row,o.start.column=s[0].start.column}this.$useWrapMode?this.$updateWrapData(n,r):this.$updateRowLengthCache(n,r),this.$modified=!0,this._emit("changeFold",{data:e})},this.removeFolds=function(e){var t=[];for(var n=0;n<e.length;n++)t.push(e[n]);t.forEach(function(e){this.removeFold(e)},this),this.$modified=!0},this.expandFold=function(e){this.removeFold(e),e.subFolds.forEach(function(e){this.addFold(e)},this),e.subFolds=[]},this.expandFolds=function(e){e.forEach(function(e){this.expandFold(e)},this)},this.unfold=function(e,t){var n,i;e==null?n=new r(0,0,this.getLength(),0):typeof e=="number"?n=new r(e,0,e,this.getLine(e).length):"row"in e?n=r.fromPoints(e,e):n=e,i=this.getFoldsInRange(n);if(t)this.removeFolds(i);else while(i.length)this.expandFolds(i),i=this.getFoldsInRange(n)},this.isRowFolded=function(e,t){return!!this.getFoldLine(e,t)},this.getRowFoldEnd=function(e,t){var n=this.getFoldLine(e,t);return n?n.end.row:e},this.getFoldDisplayLine=function(e,t,n,r,i){r==null&&(r=e.start.row,i=0),t==null&&(t=e.end.row,n=this.getLine(t).length);var s=this.doc,o="";return e.walk(function(e,t,n,u){if(t<r)return;if(t==r){if(n<i)return;u=Math.max(i,u)}e!=null?o+=e:o+=s.getLine(t).substring(u,n)}.bind(this),t,n),o},this.getDisplayLine=function(e,t,n,r){var i=this.getFoldLine(e);if(!i){var s;return s=this.doc.getLine(e),s.substring(r||0,t||s.length)}return this.getFoldDisplayLine(i,e,t,n,r)},this.$cloneFoldData=function(){var e=[];return e=this.$foldData.map(function(t){var n=t.folds.map(function(e){return e.clone()});return new i(e,n)}),e},this.toggleFold=function(e){var t=this.selection,n=t.getRange(),r,i;if(n.isEmpty()){var s=n.start;r=this.getFoldAt(s.row,s.column);if(r){this.expandFold(r);return}(i=this.findMatchingBracket(s))?n.comparePoint(i)==1?n.end=i:(n.start=i,n.start.column++,n.end.column--):(i=this.findMatchingBracket({row:s.row,column:s.column+1}))?(n.comparePoint(i)==1?n.end=i:n.start=i,n.start.column++):n=this.getCommentFoldRange(s.row,s.column)||n}else{var o=this.getFoldsInRange(n);if(e&&o.length){this.expandFolds(o);return}o.length==1&&(r=o[0])}r||(r=this.getFoldAt(n.start.row,n.start.column));if(r&&r.range.toString()==n.toString()){this.expandFold(r);return}var u="...";if(!n.isMultiLine()){u=this.getTextRange(n);if(u.length<4)return;u=u.trim().substring(0,2)+".."}this.addFold(u,n)},this.getCommentFoldRange=function(e,t,n){var i=new o(this,e,t),s=i.getCurrentToken();if(s&&/^comment|string/.test(s.type)){var u=new r,a=new RegExp(s.type.replace(/\..*/,"\\."));if(n!=1){do s=i.stepBackward();while(s&&a.test(s.type));i.stepForward()}u.start.row=i.getCurrentTokenRow(),u.start.column=i.getCurrentTokenColumn()+2,i=new o(this,e,t);if(n!=-1){do s=i.stepForward();while(s&&a.test(s.type));s=i.stepBackward()}else s=i.getCurrentToken();return u.end.row=i.getCurrentTokenRow(),u.end.column=i.getCurrentTokenColumn()+s.value.length-2,u}},this.foldAll=function(e,t){var n=this.foldWidgets;t=t||this.getLength();for(var r=e||0;r<t;r++){n[r]==null&&(n[r]=this.getFoldWidget(r));if(n[r]!="start")continue;var i=this.getFoldWidgetRange(r);if(i&&i.end.row<=t)try{this.addFold("...",i)}catch(s){}}},this.$foldStyles={manual:1,markbegin:1,markbeginend:1},this.$foldStyle="markbegin",this.setFoldStyle=function(e){if(!this.$foldStyles[e])throw new Error("invalid fold style: "+e+"["+Object.keys(this.$foldStyles).join(", ")+"]");if(this.$foldStyle==e)return;this.$foldStyle=e,e=="manual"&&this.unfold();var t=this.$foldMode;this.$setFolding(null),this.$setFolding(t)},this.$setFolding=function(e){if(this.$foldMode==e)return;this.$foldMode=e,this.removeListener("change",this.$updateFoldWidgets),this._emit("changeAnnotation");if(!e||this.$foldStyle=="manual"){this.foldWidgets=null;return}this.foldWidgets=[],this.getFoldWidget=e.getFoldWidget.bind(e,this,this.$foldStyle),this.getFoldWidgetRange=e.getFoldWidgetRange.bind(e,this,this.$foldStyle),this.$updateFoldWidgets=this.updateFoldWidgets.bind(this),this.on("change",this.$updateFoldWidgets)},this.onFoldWidgetClick=function(e,t){t=t.domEvent;var n=this.getFoldWidget(e),r=this.getLine(e),i=t.shiftKey,s=i||t.ctrlKey||t.altKey||t.metaKey,o;n=="end"?o=this.getFoldAt(e,0,-1):o=this.getFoldAt(e,r.length,1);if(o){s?this.removeFold(o):this.expandFold(o);return}var u=this.getFoldWidgetRange(e);if(u){if(!u.isMultiLine()){o=this.getFoldAt(u.start.row,u.start.column,1);if(o&&u.isEqual(o.range)){this.removeFold(o);return}}i||this.addFold("...",u),s&&this.foldAll(u.start.row+1,u.end.row)}else s&&this.foldAll(e+1,this.getLength()),(t.target||t.srcElement).className+=" ace_invalid"},this.updateFoldWidgets=function(e){var t=e.data,n=t.range,r=n.start.row,i=n.end.row-r;if(i===0)this.foldWidgets[r]=null;else if(t.action=="removeText"||t.action=="removeLines")this.foldWidgets.splice(r,i+1,null);else{var s=Array(i+1);s.unshift(r,1),this.foldWidgets.splice.apply(this.foldWidgets,s)}}}var r=e("../range").Range,i=e("./fold_line").FoldLine,s=e("./fold").Fold,o=e("../token_iterator").TokenIterator;t.Folding=u}),ace.define("ace/edit_session/fold_line",["require","exports","module","ace/range"],function(e,t,n){function i(e,t){this.foldData=e,Array.isArray(t)?this.folds=t:t=this.folds=[t];var n=t[t.length-1];this.range=new r(t[0].start.row,t[0].start.column,n.end.row,n.end.column),this.start=this.range.start,this.end=this.range.end,this.folds.forEach(function(e){e.setFoldLine(this)},this)}var r=e("../range").Range;(function(){this.shiftRow=function(e){this.start.row+=e,this.end.row+=e,this.folds.forEach(function(t){t.start.row+=e,t.end.row+=e})},this.addFold=function(e){if(e.sameRow){if(e.start.row<this.startRow||e.endRow>this.endRow)throw"Can't add a fold to this FoldLine as it has no connection";this.folds.push(e),this.folds.sort(function(e,t){return-e.range.compareEnd(t.start.row,t.start.column)}),this.range.compareEnd(e.start.row,e.start.column)>0?(this.end.row=e.end.row,this.end.column=e.end.column):this.range.compareStart(e.end.row,e.end.column)<0&&(this.start.row=e.start.row,this.start.column=e.start.column)}else if(e.start.row==this.end.row)this.folds.push(e),this.end.row=e.end.row,this.end.column=e.end.column;else{if(e.end.row!=this.start.row)throw"Trying to add fold to FoldRow that doesn't have a matching row";this.folds.unshift(e),this.start.row=e.start.row,this.start.column=e.start.column}e.foldLine=this},this.containsRow=function(e){return e>=this.start.row&&e<=this.end.row},this.walk=function(e,t,n){var r=0,i=this.folds,s,o,u,a=!0;t==null&&(t=this.end.row,n=this.end.column);for(var f=0;f<i.length;f++){s=i[f],o=s.range.compareStart(t,n);if(o==-1){e(null,t,n,r,a);return}u=e(null,s.start.row,s.start.column,r,a),u=!u&&e(s.placeholder,s.start.row,s.start.column,r);if(u||o==0)return;a=!s.sameRow,r=s.end.column}e(null,t,n,r,a)},this.getNextFoldTo=function(e,t){var n,r;for(var i=0;i<this.folds.length;i++){n=this.folds[i],r=n.range.compareEnd(e,t);if(r==-1)return{fold:n,kind:"after"};if(r==0)return{fold:n,kind:"inside"}}return null},this.addRemoveChars=function(e,t,n){var r=this.getNextFoldTo(e,t),i,s;if(r){i=r.fold;if(r.kind=="inside"&&i.start.column!=t&&i.start.row!=e)window.console&&window.console.log(e,t,i);else if(i.start.row==e){s=this.folds;var o=s.indexOf(i);o==0&&(this.start.column+=n);for(o;o<s.length;o++){i=s[o],i.start.column+=n;if(!i.sameRow)return;i.end.column+=n}this.end.column+=n}}},this.split=function(e,t){var n=this.getNextFoldTo(e,t).fold,r=this.folds,s=this.foldData;if(!n)return null;var o=r.indexOf(n),u=r[o-1];this.end.row=u.end.row,this.end.column=u.end.column,r=r.splice(o,r.length-o);var a=new i(s,r);return s.splice(s.indexOf(this)+1,0,a),a},this.merge=function(e){var t=e.folds;for(var n=0;n<t.length;n++)this.addFold(t[n]);var r=this.foldData;r.splice(r.indexOf(e),1)},this.toString=function(){var e=[this.range.toString()+": ["];return this.folds.forEach(function(t){e.push(" "+t.toString())}),e.push("]"),e.join("\n")},this.idxToPosition=function(e){var t=0,n;for(var r=0;r<this.folds.length;r++){var n=this.folds[r];e-=n.start.column-t;if(e<0)return{row:n.start.row,column:n.start.column+e};e-=n.placeholder.length;if(e<0)return n.start;t=n.end.column}return{row:this.end.row,column:this.end.column+e}}}).call(i.prototype),t.FoldLine=i}),ace.define("ace/edit_session/fold",["require","exports","module"],function(e,t,n){var r=t.Fold=function(e,t){this.foldLine=null,this.placeholder=t,this.range=e,this.start=e.start,this.end=e.end,this.sameRow=e.start.row==e.end.row,this.subFolds=[]};(function(){this.toString=function(){return'"'+this.placeholder+'" '+this.range.toString()},this.setFoldLine=function(e){this.foldLine=e,this.subFolds.forEach(function(t){t.setFoldLine(e)})},this.clone=function(){var e=this.range.clone(),t=new r(e,this.placeholder);return this.subFolds.forEach(function(e){t.subFolds.push(e.clone())}),t},this.addSubFold=function(e){if(this.range.isEqual(e))return this;if(!this.range.containsRange(e))throw"A fold can't intersect already existing fold"+e.range+this.range;var t=e.range.start.row,n=e.range.start.column;for(var r=0,i=-1;r<this.subFolds.length;r++){i=this.subFolds[r].range.compare(t,n);if(i!=1)break}var s=this.subFolds[r];if(i==0)return s.addSubFold(e);var t=e.range.end.row,n=e.range.end.column;for(var o=r,i=-1;o<this.subFolds.length;o++){i=this.subFolds[o].range.compare(t,n);if(i!=1)break}var u=this.subFolds[o];if(i==0)throw"A fold can't intersect already existing fold"+e.range+this.range;var a=this.subFolds.splice(r,o-r,e);return e.setFoldLine(this.foldLine),e}}).call(r.prototype)}),ace.define("ace/token_iterator",["require","exports","module"],function(e,t,n){var r=function(e,t,n){this.$session=e,this.$row=t,this.$rowTokens=e.getTokens(t);var r=e.getTokenAt(t,n);this.$tokenIndex=r?r.index:-1};(function(){this.stepBackward=function(){this.$tokenIndex-=1;while(this.$tokenIndex<0){this.$row-=1;if(this.$row<0)return this.$row=0,null;this.$rowTokens=this.$session.getTokens(this.$row),this.$tokenIndex=this.$rowTokens.length-1}return this.$rowTokens[this.$tokenIndex]},this.stepForward=function(){var e=this.$session.getLength();this.$tokenIndex+=1;while(this.$tokenIndex>=this.$rowTokens.length){this.$row+=1;if(this.$row>=e)return this.$row=e-1,null;this.$rowTokens=this.$session.getTokens(this.$row),this.$tokenIndex=0}return this.$rowTokens[this.$tokenIndex]},this.getCurrentToken=function(){return this.$rowTokens[this.$tokenIndex]},this.getCurrentTokenRow=function(){return this.$row},this.getCurrentTokenColumn=function(){var e=this.$rowTokens,t=this.$tokenIndex,n=e[t].start;if(n!==undefined)return n;n=0;while(t>0)t-=1,n+=e[t].value.length;return n}}).call(r.prototype),t.TokenIterator=r}),ace.define("ace/edit_session/bracket_match",["require","exports","module","ace/token_iterator","ace/range"],function(e,t,n){function s(){this.findMatchingBracket=function(e,t){if(e.column==0)return null;var n=t||this.getLine(e.row).charAt(e.column-1);if(n=="")return null;var r=n.match(/([\(\[\{])|([\)\]\}])/);return r?r[1]?this.$findClosingBracket(r[1],e):this.$findOpeningBracket(r[2],e):null},this.getBracketRange=function(e){var t=this.getLine(e.row),n=!0,r,s=t.charAt(e.column-1),o=s&&s.match(/([\(\[\{])|([\)\]\}])/);o||(s=t.charAt(e.column),e={row:e.row,column:e.column+1},o=s&&s.match(/([\(\[\{])|([\)\]\}])/),n=!1);if(!o)return null;if(o[1]){var u=this.$findClosingBracket(o[1],e);if(!u)return null;r=i.fromPoints(e,u),n||(r.end.column++,r.start.column--),r.cursor=r.end}else{var u=this.$findOpeningBracket(o[2],e);if(!u)return null;r=i.fromPoints(u,e),n||(r.start.column++,r.end.column--),r.cursor=r.start}return r},this.$brackets={")":"(","(":")","]":"[","[":"]","{":"}","}":"{"},this.$findOpeningBracket=function(e,t,n){var i=this.$brackets[e],s=1,o=new r(this,t.row,t.column),u=o.getCurrentToken();u||(u=o.stepForward());if(!u)return;n||(n=new RegExp("(\\.?"+u.type.replace(".","\\.").replace("rparen",".paren")+")+"));var a=t.column-o.getCurrentTokenColumn()-2,f=u.value;for(;;){while(a>=0){var l=f.charAt(a);if(l==i){s-=1;if(s==0)return{row:o.getCurrentTokenRow(),column:a+o.getCurrentTokenColumn()}}else l==e&&(s+=1);a-=1}do u=o.stepBackward();while(u&&!n.test(u.type));if(u==null)break;f=u.value,a=f.length-1}return null},this.$findClosingBracket=function(e,t,n){var i=this.$brackets[e],s=1,o=new r(this,t.row,t.column),u=o.getCurrentToken();u||(u=o.stepForward());if(!u)return;n||(n=new RegExp("(\\.?"+u.type.replace(".","\\.").replace("lparen",".paren")+")+"));var a=t.column-o.getCurrentTokenColumn();for(;;){var f=u.value,l=f.length;while(a<l){var c=f.charAt(a);if(c==i){s-=1;if(s==0)return{row:o.getCurrentTokenRow(),column:a+o.getCurrentTokenColumn()}}else c==e&&(s+=1);a+=1}do u=o.stepForward();while(u&&!n.test(u.type));if(u==null)break;a=0}return null}}var r=e("../token_iterator").TokenIterator,i=e("../range").Range;t.BracketMatch=s}),ace.define("ace/search",["require","exports","module","ace/lib/lang","ace/lib/oop","ace/range"],function(e,t,n){var r=e("./lib/lang"),i=e("./lib/oop"),s=e("./range").Range,o=function(){this.$options={}};(function(){this.set=function(e){return i.mixin(this.$options,e),this},this.getOptions=function(){return r.copyObject(this.$options)},this.setOptions=function(e){this.$options=e},this.find=function(e){var t=this.$matchIterator(e,this.$options);if(!t)return!1;var n=null;return t.forEach(function(e,t,r){if(!e.start){var i=e.offset+(r||0);n=new s(t,i,t,i+e.length)}else n=e;return!0}),n},this.findAll=function(e){var t=this.$options;if(!t.needle)return[];this.$assembleRegExp(t);var n=t.range,i=n?e.getLines(n.start.row,n.end.row):e.doc.getAllLines(),o=[],u=t.re;if(t.$isMultiLine){var a=u.length,f=i.length-a;for(var l=u.offset||0;l<=f;l++){for(var c=0;c<a;c++)if(i[l+c].search(u[c])==-1)break;var h=i[l],p=i[l+a-1],d=h.match(u[0])[0].length,v=p.match(u[a-1])[0].length;o.push(new s(l,h.length-d,l+a-1,v))}}else for(var m=0;m<i.length;m++){var g=r.getMatchOffsets(i[m],u);for(var c=0;c<g.length;c++){var y=g[c];o.push(new s(m,y.offset,m,y.offset+y.length))}}if(n){var b=n.start.column,w=n.start.column,m=0,c=o.length-1;while(m<c&&o[m].start.column<b&&o[m].start.row==n.start.row)m++;while(m<c&&o[c].end.column>w&&o[c].end.row==n.end.row)c--;return o.slice(m,c+1)}return o},this.replace=function(e,t){var n=this.$options,r=this.$assembleRegExp(n);if(n.$isMultiLine)return t;if(!r)return;var i=r.exec(e);if(!i||i[0].length!=e.length)return null;t=e.replace(r,t);if(n.preserveCase){t=t.split("");for(var s=Math.min(e.length,e.length);s--;){var o=e[s];o&&o.toLowerCase()!=o?t[s]=t[s].toUpperCase():t[s]=t[s].toLowerCase()}t=t.join("")}return t},this.$matchIterator=function(e,t){var n=this.$assembleRegExp(t);if(!n)return!1;var i=this,o,u=t.backwards;if(t.$isMultiLine)var a=n.length,f=function(t,r,i){var u=t.search(n[0]);if(u==-1)return;for(var f=1;f<a;f++){t=e.getLine(r+f);if(t.search(n[f])==-1)return}var l=t.match(n[a-1])[0].length,c=new s(r,u,r+a-1,l);n.offset==1?(c.start.row--,c.start.column=Number.MAX_VALUE):i&&(c.start.column+=i);if(o(c))return!0};else if(u)var f=function(e,t,i){var s=r.getMatchOffsets(e,n);for(var u=s.length-1;u>=0;u--)if(o(s[u],t,i))return!0};else var f=function(e,t,i){var s=r.getMatchOffsets(e,n);for(var u=0;u<s.length;u++)if(o(s[u],t,i))return!0};return{forEach:function(n){o=n,i.$lineIterator(e,t).forEach(f)}}},this.$assembleRegExp=function(e){if(e.needle instanceof RegExp)return e.re=e.needle;var t=e.needle;if(!e.needle)return e.re=!1;e.regExp||(t=r.escapeRegExp(t)),e.wholeWord&&(t="\\b"+t+"\\b");var n=e.caseSensitive?"g":"gi";e.$isMultiLine=/[\n\r]/.test(t);if(e.$isMultiLine)return e.re=this.$assembleMultilineRegExp(t,n);try{var i=new RegExp(t,n)}catch(s){i=!1}return e.re=i},this.$assembleMultilineRegExp=function(e,t){var n=e.replace(/\r\n|\r|\n/g,"$\n^").split("\n"),r=[];for(var i=0;i<n.length;i++)try{r.push(new RegExp(n[i],t))}catch(s){return!1}return n[0]==""?(r.shift(),r.offset=1):r.offset=0,r},this.$lineIterator=function(e,t){var n=t.backwards==1,r=t.skipCurrent!=0,i=t.range,s=t.start;s||(s=i?i[n?"end":"start"]:e.selection.getRange()),s.start&&(s=s[r!=n?"end":"start"]);var o=i?i.start.row:0,u=i?i.end.row:e.getLength()-1,a=n?function(n){var r=s.row,i=e.getLine(r).substring(0,s.column);if(n(i,r))return;for(r--;r>=o;r--)if(n(e.getLine(r),r))return;if(t.wrap==0)return;for(r=u,o=s.row;r>=o;r--)if(n(e.getLine(r),r))return}:function(n){var r=s.row,i=e.getLine(r).substr(s.column);if(n(i,r,s.column))return;for(r+=1;r<=u;r++)if(n(e.getLine(r),r))return;if(t.wrap==0)return;for(r=o,u=s.row;r<=u;r++)if(n(e.getLine(r),r))return};return{forEach:a}}}).call(o.prototype),t.Search=o}),ace.define("ace/commands/command_manager",["require","exports","module","ace/lib/oop","ace/keyboard/hash_handler","ace/lib/event_emitter"],function(e,t,n){var r=e("../lib/oop"),i=e("../keyboard/hash_handler").HashHandler,s=e("../lib/event_emitter").EventEmitter,o=function(e,t){this.platform=e,this.commands=this.byName={},this.commmandKeyBinding={},this.addCommands(t),this.setDefaultHandler("exec",function(e){return e.command.exec(e.editor,e.args||{})})};r.inherits(o,i),function(){r.implement(this,s),this.exec=function(e,t,n){typeof e=="string"&&(e=this.commands[e]);if(!e)return!1;if(t&&t.$readOnly&&!e.readOnly)return!1;var r=this._emit("exec",{editor:t,command:e,args:n});return r===!1?!1:!0},this.toggleRecording=function(e){if(this.$inReplay)return;return e&&e._emit("changeStatus"),this.recording?(this.macro.pop(),this.removeEventListener("exec",this.$addCommandToMacro),this.macro.length||(this.macro=this.oldMacro),this.recording=!1):(this.$addCommandToMacro||(this.$addCommandToMacro=function(e){this.macro.push([e.command,e.args])}.bind(this)),this.oldMacro=this.macro,this.macro=[],this.on("exec",this.$addCommandToMacro),this.recording=!0)},this.replay=function(e){if(this.$inReplay||!this.macro)return;if(this.recording)return this.toggleRecording(e);try{this.$inReplay=!0,this.macro.forEach(function(t){typeof t=="string"?this.exec(t,e):this.exec(t[0],e,t[1])},this)}finally{this.$inReplay=!1}},this.trimMacro=function(e){return e.map(function(e){return typeof e[0]!="string"&&(e[0]=e[0].name),e[1]||(e=e[0]),e})}}.call(o.prototype),t.CommandManager=o}),ace.define("ace/keyboard/hash_handler",["require","exports","module","ace/lib/keys"],function(e,t,n){function i(e,t){this.platform=t,this.commands={},this.commmandKeyBinding={},this.addCommands(e)}var r=e("../lib/keys");(function(){this.addCommand=function(e){this.commands[e.name]&&this.removeCommand(e),this.commands[e.name]=e,e.bindKey&&this._buildKeyHash(e)},this.removeCommand=function(e){var t=typeof e=="string"?e:e.name;e=this.commands[t],delete this.commands[t];var n=this.commmandKeyBinding;for(var r in n)for(var i in n[r])n[r][i]==e&&delete n[r][i]},this.bindKey=function(e,t){if(!e)return;if(typeof t=="function"){this.addCommand({exec:t,bindKey:e,name:e});return}var n=this.commmandKeyBinding;e.split("|").forEach(function(e){var r=this.parseKeys(e,t),i=r.hashId;(n[i]||(n[i]={}))[r.key]=t},this)},this.addCommands=function(e){e&&Object.keys(e).forEach(function(t){var n=e[t];if(typeof n=="string")return this.bindKey(n,t);typeof n=="function"&&(n={exec:n}),n.name||(n.name=t),this.addCommand(n)},this)},this.removeCommands=function(e){Object.keys(e).forEach(function(t){this.removeCommand(e[t])},this)},this.bindKeys=function(e){Object.keys(e).forEach(function(t){this.bindKey(t,e[t])},this)},this._buildKeyHash=function(e){var t=e.bindKey;if(!t)return;var n=typeof t=="string"?t:t[this.platform];this.bindKey(n,e)},this.parseKeys=function(e){var t=e.toLowerCase().split(/[\-\+]([\-\+])?/).filter(function(e){return e}),n=t.pop(),i=r[n];if(r.FUNCTION_KEYS[i])n=r.FUNCTION_KEYS[i].toLowerCase();else{if(!t.length)return{key:n,hashId:-1};if(t.length==1&&t[0]=="shift")return{key:n.toUpperCase(),hashId:-1}}var s=0;for(var o=t.length;o--;){var u=r.KEY_MODS[t[o]];if(u==null)throw"invalid modifier "+t[o]+" in "+e;s|=u}return{key:n,hashId:s}},this.findKeyCommand=function(t,n){var r=this.commmandKeyBinding;return r[t]&&r[t][n]},this.handleKeyboard=function(e,t,n,r){return{command:this.findKeyCommand(t,n)}}}).call(i.prototype),t.HashHandler=i}),ace.define("ace/commands/default_commands",["require","exports","module","ace/lib/lang"],function(e,t,n){function i(e,t){return{win:e,mac:t}}var r=e("../lib/lang");t.commands=[{name:"selectall",bindKey:i("Ctrl-A","Command-A"),exec:function(e){e.selectAll()},readOnly:!0},{name:"centerselection",bindKey:i(null,"Ctrl-L"),exec:function(e){e.centerSelection()},readOnly:!0},{name:"gotoline",bindKey:i("Ctrl-L","Command-L"),exec:function(e){var t=parseInt(prompt("Enter line number:"),10);isNaN(t)||e.gotoLine(t)},readOnly:!0},{name:"fold",bindKey:i("Alt-L|Ctrl-F1","Command-Alt-L|Command-F1"),exec:function(e){e.session.toggleFold(!1)},readOnly:!0},{name:"unfold",bindKey:i("Alt-Shift-L|Ctrl-Shift-F1","Command-Alt-Shift-L|Command-Shift-F1"),exec:function(e){e.session.toggleFold(!0)},readOnly:!0},{name:"foldall",bindKey:i("Alt-0","Command-Option-0"),exec:function(e){e.session.foldAll()},readOnly:!0},{name:"unfoldall",bindKey:i("Alt-Shift-0","Command-Option-Shift-0"),exec:function(e){e.session.unfold()},readOnly:!0},{name:"findnext",bindKey:i("Ctrl-K","Command-G"),exec:function(e){e.findNext()},readOnly:!0},{name:"findprevious",bindKey:i("Ctrl-Shift-K","Command-Shift-G"),exec:function(e){e.findPrevious()},readOnly:!0},{name:"find",bindKey:i("Ctrl-F","Command-F"),exec:function(e){var t=prompt("Find:",e.getCopyText());e.find(t)},readOnly:!0},{name:"overwrite",bindKey:"Insert",exec:function(e){e.toggleOverwrite()},readOnly:!0},{name:"selecttostart",bindKey:i("Ctrl-Shift-Home","Command-Shift-Up"),exec:function(e){e.getSelection().selectFileStart()},multiSelectAction:"forEach",readOnly:!0},{name:"gotostart",bindKey:i("Ctrl-Home","Command-Home|Command-Up"),exec:function(e){e.navigateFileStart()},multiSelectAction:"forEach",readOnly:!0},{name:"selectup",bindKey:i("Shift-Up","Shift-Up"),exec:function(e){e.getSelection().selectUp()},multiSelectAction:"forEach",readOnly:!0},{name:"golineup",bindKey:i("Up","Up|Ctrl-P"),exec:function(e,t){e.navigateUp(t.times)},multiSelectAction:"forEach",readOnly:!0},{name:"selecttoend",bindKey:i("Ctrl-Shift-End","Command-Shift-Down"),exec:function(e){e.getSelection().selectFileEnd()},multiSelectAction:"forEach",readOnly:!0},{name:"gotoend",bindKey:i("Ctrl-End","Command-End|Command-Down"),exec:function(e){e.navigateFileEnd()},multiSelectAction:"forEach",readOnly:!0},{name:"selectdown",bindKey:i("Shift-Down","Shift-Down"),exec:function(e){e.getSelection().selectDown()},multiSelectAction:"forEach",readOnly:!0},{name:"golinedown",bindKey:i("Down","Down|Ctrl-N"),exec:function(e,t){e.navigateDown(t.times)},multiSelectAction:"forEach",readOnly:!0},{name:"selectwordleft",bindKey:i("Ctrl-Shift-Left","Option-Shift-Left"),exec:function(e){e.getSelection().selectWordLeft()},multiSelectAction:"forEach",readOnly:!0},{name:"gotowordleft",bindKey:i("Ctrl-Left","Option-Left"),exec:function(e){e.navigateWordLeft()},multiSelectAction:"forEach",readOnly:!0},{name:"selecttolinestart",bindKey:i("Alt-Shift-Left","Command-Shift-Left"),exec:function(e){e.getSelection().selectLineStart()},multiSelectAction:"forEach",readOnly:!0},{name:"gotolinestart",bindKey:i("Alt-Left|Home","Command-Left|Home|Ctrl-A"),exec:function(e){e.navigateLineStart()},multiSelectAction:"forEach",readOnly:!0},{name:"selectleft",bindKey:i("Shift-Left","Shift-Left"),exec:function(e){e.getSelection().selectLeft()},multiSelectAction:"forEach",readOnly:!0},{name:"gotoleft",bindKey:i("Left","Left|Ctrl-B"),exec:function(e,t){e.navigateLeft(t.times)},multiSelectAction:"forEach",readOnly:!0},{name:"selectwordright",bindKey:i("Ctrl-Shift-Right","Option-Shift-Right"),exec:function(e){e.getSelection().selectWordRight()},multiSelectAction:"forEach",readOnly:!0},{name:"gotowordright",bindKey:i("Ctrl-Right","Option-Right"),exec:function(e){e.navigateWordRight()},multiSelectAction:"forEach",readOnly:!0},{name:"selecttolineend",bindKey:i("Alt-Shift-Right","Command-Shift-Right"),exec:function(e){e.getSelection().selectLineEnd()},multiSelectAction:"forEach",readOnly:!0},{name:"gotolineend",bindKey:i("Alt-Right|End","Command-Right|End|Ctrl-E"),exec:function(e){e.navigateLineEnd()},multiSelectAction:"forEach",readOnly:!0},{name:"selectright",bindKey:i("Shift-Right","Shift-Right"),exec:function(e){e.getSelection().selectRight()},multiSelectAction:"forEach",readOnly:!0},{name:"gotoright",bindKey:i("Right","Right|Ctrl-F"),exec:function(e,t){e.navigateRight(t.times)},multiSelectAction:"forEach",readOnly:!0},{name:"selectpagedown",bindKey:"Shift-PageDown",exec:function(e){e.selectPageDown()},readOnly:!0},{name:"pagedown",bindKey:i(null,"Option-PageDown"),exec:function(e){e.scrollPageDown()},readOnly:!0},{name:"gotopagedown",bindKey:i("PageDown","PageDown|Ctrl-V"),exec:function(e){e.gotoPageDown()},readOnly:!0},{name:"selectpageup",bindKey:"Shift-PageUp",exec:function(e){e.selectPageUp()},readOnly:!0},{name:"pageup",bindKey:i(null,"Option-PageUp"),exec:function(e){e.scrollPageUp()},readOnly:!0},{name:"gotopageup",bindKey:"PageUp",exec:function(e){e.gotoPageUp()},readOnly:!0},{name:"scrollup",bindKey:i("Ctrl-Up",null),exec:function(e){e.renderer.scrollBy(0,-2*e.renderer.layerConfig.lineHeight)},readOnly:!0},{name:"scrolldown",bindKey:i("Ctrl-Down",null),exec:function(e){e.renderer.scrollBy(0,2*e.renderer.layerConfig.lineHeight)},readOnly:!0},{name:"selectlinestart",bindKey:"Shift-Home",exec:function(e){e.getSelection().selectLineStart()},multiSelectAction:"forEach",readOnly:!0},{name:"selectlineend",bindKey:"Shift-End",exec:function(e){e.getSelection().selectLineEnd()},multiSelectAction:"forEach",readOnly:!0},{name:"togglerecording",bindKey:i("Ctrl-Alt-E","Command-Option-E"),exec:function(e){e.commands.toggleRecording(e)},readOnly:!0},{name:"replaymacro",bindKey:i("Ctrl-Shift-E","Command-Shift-E"),exec:function(e){e.commands.replay(e)},readOnly:!0},{name:"jumptomatching",bindKey:i("Ctrl-P","Ctrl-Shift-P"),exec:function(e){e.jumpToMatching()},multiSelectAction:"forEach",readOnly:!0},{name:"selecttomatching",bindKey:i("Ctrl-Shift-P",null),exec:function(e){e.jumpToMatching(!0)},readOnly:!0},{name:"cut",exec:function(e){var t=e.getSelectionRange();e._emit("cut",t),e.selection.isEmpty()||(e.session.remove(t),e.clearSelection())},multiSelectAction:"forEach"},{name:"removeline",bindKey:i("Ctrl-D","Command-D"),exec:function(e){e.removeLines()},multiSelectAction:"forEach"},{name:"duplicateSelection",bindKey:i("Ctrl-Shift-D","Command-Shift-D"),exec:function(e){e.duplicateSelection()},multiSelectAction:"forEach"},{name:"sortlines",bindKey:i("Ctrl-Alt-S","Command-Alt-S"),exec:function(e){e.sortLines()},multiSelectAction:"forEach"},{name:"togglecomment",bindKey:i("Ctrl-/","Command-/"),exec:function(e){e.toggleCommentLines()},multiSelectAction:"forEach"},{name:"modifyNumberUp",bindKey:i("Ctrl-Shift-Up","Alt-Shift-Up"),exec:function(e){e.modifyNumber(1)},multiSelectAction:"forEach"},{name:"modifyNumberDown",bindKey:i("Ctrl-Shift-Down","Alt-Shift-Down"),exec:function(e){e.modifyNumber(-1)},multiSelectAction:"forEach"},{name:"replace",bindKey:i("Ctrl-R","Command-Option-F"),exec:function(e){var t=prompt("Find:",e.getCopyText());if(!t)return;var n=prompt("Replacement:");if(!n)return;e.replace(n,{needle:t})}},{name:"replaceall",bindKey:i("Ctrl-Shift-R","Command-Shift-Option-F"),exec:function(e){var t=prompt("Find:");if(!t)return;var n=prompt("Replacement:");if(!n)return;e.replaceAll(n,{needle:t})}},{name:"undo",bindKey:i("Ctrl-Z","Command-Z"),exec:function(e){e.undo()}},{name:"redo",bindKey:i("Ctrl-Shift-Z|Ctrl-Y","Command-Shift-Z|Command-Y"),exec:function(e){e.redo()}},{name:"copylinesup",bindKey:i("Alt-Shift-Up","Command-Option-Up"),exec:function(e){e.copyLinesUp()}},{name:"movelinesup",bindKey:i("Alt-Up","Option-Up"),exec:function(e){e.moveLinesUp()}},{name:"copylinesdown",bindKey:i("Alt-Shift-Down","Command-Option-Down"),exec:function(e){e.copyLinesDown()}},{name:"movelinesdown",bindKey:i("Alt-Down","Option-Down"),exec:function(e){e.moveLinesDown()}},{name:"del",bindKey:i("Delete","Delete|Ctrl-D"),exec:function(e){e.remove("right")},multiSelectAction:"forEach"},{name:"backspace",bindKey:i("Command-Backspace|Option-Backspace|Shift-Backspace|Backspace","Ctrl-Backspace|Command-Backspace|Shift-Backspace|Backspace|Ctrl-H"),exec:function(e){e.remove("left")},multiSelectAction:"forEach"},{name:"removetolinestart",bindKey:i("Alt-Backspace","Command-Backspace"),exec:function(e){e.removeToLineStart()},multiSelectAction:"forEach"},{name:"removetolineend",bindKey:i("Alt-Delete","Ctrl-K"),exec:function(e){e.removeToLineEnd()},multiSelectAction:"forEach"},{name:"removewordleft",bindKey:i("Ctrl-Backspace","Alt-Backspace|Ctrl-Alt-Backspace"),exec:function(e){e.removeWordLeft()},multiSelectAction:"forEach"},{name:"removewordright",bindKey:i("Ctrl-Delete","Alt-Delete"),exec:function(e){e.removeWordRight()},multiSelectAction:"forEach"},{name:"outdent",bindKey:i("Shift-Tab","Shift-Tab"),exec:function(e){e.blockOutdent()},multiSelectAction:"forEach"},{name:"indent",bindKey:i("Tab","Tab"),exec:function(e){e.indent()},multiSelectAction:"forEach"},{name:"insertstring",exec:function(e,t){e.insert(t)},multiSelectAction:"forEach"},{name:"inserttext",exec:function(e,t){e.insert(r.stringRepeat(t.text||"",t.times||1))},multiSelectAction:"forEach"},{name:"splitline",bindKey:i(null,"Ctrl-O"),exec:function(e){e.splitLine()},multiSelectAction:"forEach"},{name:"transposeletters",bindKey:i("Ctrl-T","Ctrl-T"),exec:function(e){e.transposeLetters()},multiSelectAction:function(e){e.transposeSelections(1)}},{name:"touppercase",bindKey:i("Ctrl-U","Ctrl-U"),exec:function(e){e.toUpperCase()},multiSelectAction:"forEach"},{name:"tolowercase",bindKey:i("Ctrl-Shift-U","Ctrl-Shift-U"),exec:function(e){e.toLowerCase()},multiSelectAction:"forEach"}]}),ace.define("ace/undomanager",["require","exports","module"],function(e,t,n){var r=function(){this.reset()};(function(){this.execute=function(e){var t=e.args[0];this.$doc=e.args[1],this.$undoStack.push(t),this.$redoStack=[]},this.undo=function(e){var t=this.$undoStack.pop(),n=null;return t&&(n=this.$doc.undoChanges(t,e),this.$redoStack.push(t)),n},this.redo=function(e){var t=this.$redoStack.pop(),n=null;return t&&(n=this.$doc.redoChanges(t,e),this.$undoStack.push(t)),n},this.reset=function(){this.$undoStack=[],this.$redoStack=[]},this.hasUndo=function(){return this.$undoStack.length>0},this.hasRedo=function(){return this.$redoStack.length>0}}).call(r.prototype),t.UndoManager=r}),ace.define("ace/virtual_renderer",["require","exports","module","ace/lib/oop","ace/lib/dom","ace/lib/event","ace/lib/useragent","ace/config","ace/lib/net","ace/layer/gutter","ace/layer/marker","ace/layer/text","ace/layer/cursor","ace/scrollbar","ace/renderloop","ace/lib/event_emitter"],function(e,t,n){var r=e("./lib/oop"),i=e("./lib/dom"),s=e("./lib/event"),o=e("./lib/useragent"),u=e("./config"),a=e("./lib/net"),f=e("./layer/gutter").Gutter,l=e("./layer/marker").Marker,c=e("./layer/text").Text,h=e("./layer/cursor").Cursor,p=e("./scrollbar").ScrollBar,d=e("./renderloop").RenderLoop,v=e("./lib/event_emitter").EventEmitter,m=".ace_editor {position: absolute;overflow: hidden;font-family: 'Monaco', 'Menlo', 'Ubuntu Mono', 'Consolas', 'source-code-pro', monospace;font-size: 12px;}.ace_scroller {position: absolute;overflow: hidden;}.ace_content {position: absolute;-moz-box-sizing: border-box;-webkit-box-sizing: border-box;box-sizing: border-box;cursor: text;}.ace_gutter {position: absolute;overflow : hidden;height: 100%;width: auto;cursor: default;z-index: 4;}.ace_gutter-active-line {position: absolute;left: 0;right: 0;}.ace_scroller.ace_scroll-left {box-shadow: 17px 0 16px -16px rgba(0, 0, 0, 0.4) inset;}.ace_gutter-cell {padding-left: 19px;padding-right: 6px;background-repeat: no-repeat;}.ace_gutter-cell.ace_error {background-image: url(\"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyJpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMC1jMDYwIDYxLjEzNDc3NywgMjAxMC8wMi8xMi0xNzozMjowMCAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENTNSBNYWNpbnRvc2giIHhtcE1NOkluc3RhbmNlSUQ9InhtcC5paWQ6QUM2OEZDQTQ4RTU0MTFFMUEzM0VFRTM2RUY1M0RBMjYiIHhtcE1NOkRvY3VtZW50SUQ9InhtcC5kaWQ6QUM2OEZDQTU4RTU0MTFFMUEzM0VFRTM2RUY1M0RBMjYiPiA8eG1wTU06RGVyaXZlZEZyb20gc3RSZWY6aW5zdGFuY2VJRD0ieG1wLmlpZDpBQzY4RkNBMjhFNTQxMUUxQTMzRUVFMzZFRjUzREEyNiIgc3RSZWY6ZG9jdW1lbnRJRD0ieG1wLmRpZDpBQzY4RkNBMzhFNTQxMUUxQTMzRUVFMzZFRjUzREEyNiIvPiA8L3JkZjpEZXNjcmlwdGlvbj4gPC9yZGY6UkRGPiA8L3g6eG1wbWV0YT4gPD94cGFja2V0IGVuZD0iciI/PkgXxbAAAAJbSURBVHjapFNNaBNBFH4zs5vdZLP5sQmNpT82QY209heh1ioWisaDRcSKF0WKJ0GQnrzrxasHsR6EnlrwD0TagxJabaVEpFYxLWlLSS822tr87m66ccfd2GKyVhA6MMybgfe97/vmPUQphd0sZjto9XIn9OOsvlu2nkqRzVU+6vvlzPf8W6bk8dxQ0NPbxAALgCgg2JkaQuhzQau/El0zbmUA7U0Es8v2CiYmKQJHGO1QICCLoqilMhkmurDAyapKgqItezi/USRdJqEYY4D5jCy03ht2yMkkvL91jTTX10qzyyu2hruPRN7jgbH+EOsXcMLgYiThEgAMhABW85oqy1DXdRIdvP1AHJ2acQXvDIrVHcdQNrEKNYSVMSZGMjEzIIAwDXIo+6G/FxcGnzkC3T2oMhLjre49sBB+RRcHLqdafK6sYdE/GGBwU1VpFNj0aN8pJbe+BkZyevUrvLl6Xmm0W9IuTc0DxrDNAJd5oEvI/KRsNC3bQyNjPO9yQ1YHcfj2QvfQc/5TUhJTBc2iM0U7AWDQtc1nJHvD/cfO2s7jaGkiTEfa/Ep8coLu7zmNmh8+dc5lZDuUeFAGUNA/OY6JVaypQ0vjr7XYjUvJM37vt+j1vuTK5DgVfVUoTjVe+y3/LxMxY2GgU+CSLy4cpfsYorRXuXIOi0Vt40h67uZFTdIo6nLaZcwUJWAzwNS0tBnqqKzQDnjdG/iPyZxo46HaKUpbvYkj8qYRTZsBhge+JHhZyh0x9b95JqjVJkT084kZIPwu/mPWqPgfQ5jXh2+92Ay7HedfAgwA6KDWafb4w3cAAAAASUVORK5CYII=\");background-repeat: no-repeat;background-position: 2px center;}.ace_gutter-cell.ace_warning {background-image: url(\"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyJpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMC1jMDYwIDYxLjEzNDc3NywgMjAxMC8wMi8xMi0xNzozMjowMCAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENTNSBNYWNpbnRvc2giIHhtcE1NOkluc3RhbmNlSUQ9InhtcC5paWQ6QUM2OEZDQTg4RTU0MTFFMUEzM0VFRTM2RUY1M0RBMjYiIHhtcE1NOkRvY3VtZW50SUQ9InhtcC5kaWQ6QUM2OEZDQTk4RTU0MTFFMUEzM0VFRTM2RUY1M0RBMjYiPiA8eG1wTU06RGVyaXZlZEZyb20gc3RSZWY6aW5zdGFuY2VJRD0ieG1wLmlpZDpBQzY4RkNBNjhFNTQxMUUxQTMzRUVFMzZFRjUzREEyNiIgc3RSZWY6ZG9jdW1lbnRJRD0ieG1wLmRpZDpBQzY4RkNBNzhFNTQxMUUxQTMzRUVFMzZFRjUzREEyNiIvPiA8L3JkZjpEZXNjcmlwdGlvbj4gPC9yZGY6UkRGPiA8L3g6eG1wbWV0YT4gPD94cGFja2V0IGVuZD0iciI/Pgd7PfIAAAGmSURBVHjaYvr//z8DJZiJgUIANoCRkREb9gLiSVAaQx4OQM7AAkwd7XU2/v++/rOttdYGEB9dASEvOMydGKfH8Gv/p4XTkvRBfLxeQAP+1cUhXopyvzhP7P/IoSj7g7Mw09cNKO6J1QQ0L4gICPIv/veg/8W+JdFvQNLHVsW9/nmn9zk7B+cCkDwhL7gt6knSZnx9/LuCEOcvkIAMP+cvto9nfqyZmmUAksfnBUtbM60gX/3/kgyv3/xSFOL5DZT+L8vP+Yfh5cvfPvp/xUHyQHXGyAYwgpwBjZYFT3Y1OEl/OfCH4ffv3wzc4iwMvNIsDJ+f/mH4+vIPAxsb631WW0Yln6ZpQLXdMK/DXGDflh+sIv37EivD5x//Gb7+YWT4y86sl7BCCkSD+Z++/1dkvsFRl+HnD1Rvje4F8whjMXmGj58YGf5zsDMwcnAwfPvKcml62DsQDeaDxN+/Y0qwlpEHqrdB94IRNIDUgfgfKJChGK4OikEW3gTiXUB950ASLFAF54AC94A0G9QAfOnmF9DCDzABFqS08IHYDIScdijOjQABBgC+/9awBH96jwAAAABJRU5ErkJggg==\");background-position: 2px center;}.ace_gutter-cell.ace_info {background-image: url(\"data:image/gif;base64,R0lGODlhEAAQAMQAAAAAAEFBQVJSUl5eXmRkZGtra39/f4WFhYmJiZGRkaampry8vMPDw8zMzNXV1dzc3OTk5Orq6vDw8P///wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACH5BAkAABQALAAAAAAQABAAAAUuICWOZGmeaBml5XGwFCQSBGyXRSAwtqQIiRuiwIM5BoYVbEFIyGCQoeJGrVptIQA7\");background-position: 2px center;}.ace_dark .ace_gutter-cell.ace_info {background-image: url(\"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyRpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMy1jMDExIDY2LjE0NTY2MSwgMjAxMi8wMi8wNi0xNDo1NjoyNyAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENTNiAoTWFjaW50b3NoKSIgeG1wTU06SW5zdGFuY2VJRD0ieG1wLmlpZDpGRTk5MTVGREIxNDkxMUUxOTc5Q0FFREQyMTNGMjBFQyIgeG1wTU06RG9jdW1lbnRJRD0ieG1wLmRpZDpGRTk5MTVGRUIxNDkxMUUxOTc5Q0FFREQyMTNGMjBFQyI+IDx4bXBNTTpEZXJpdmVkRnJvbSBzdFJlZjppbnN0YW5jZUlEPSJ4bXAuaWlkOkZFOTkxNUZCQjE0OTExRTE5NzlDQUVERDIxM0YyMEVDIiBzdFJlZjpkb2N1bWVudElEPSJ4bXAuZGlkOkZFOTkxNUZDQjE0OTExRTE5NzlDQUVERDIxM0YyMEVDIi8+IDwvcmRmOkRlc2NyaXB0aW9uPiA8L3JkZjpSREY+IDwveDp4bXBtZXRhPiA8P3hwYWNrZXQgZW5kPSJyIj8+SIDkjAAAAJ1JREFUeNpi/P//PwMlgImBQkB7A6qrq/+DMC55FkIGKCoq4pVnpFkgTp069f/+/fv/r1u37r+tre1/kg0A+ptn9uzZYLaRkRHpLvjw4cNXWVlZhufPnzOcO3eOdAO0tbVPAjHDmzdvGA4fPsxIsgGSkpJmv379Ynj37h2DjIyMCMkG3LhxQ/T27dsMampqDHZ2dq/pH41DxwCAAAMAFdc68dUsFZgAAAAASUVORK5CYII=\");}.ace_scrollbar {position: absolute;overflow-x: hidden;overflow-y: scroll;right: 0;}.ace_scrollbar-inner {position: absolute;width: 1px;left: 0;}.ace_print-margin {position: absolute;height: 100%;}.ace_text-input {position: absolute;z-index: 0;width: 0.5em;height: 1em;opacity: 0;background: transparent;-moz-appearance: none;appearance: none;border: none;resize: none;outline: none;overflow: hidden;}.ace_text-input.ace_composition {background: #fff;color: #000;z-index: 1000;opacity: 1;border: solid lightgray 1px;margin: -1px}.ace_layer {z-index: 1;position: absolute;overflow: hidden;white-space: nowrap;height: 100%;width: 100%;-moz-box-sizing: border-box;-webkit-box-sizing: border-box;box-sizing: border-box;/* setting pointer-events: auto; on node under the mouse, which changesduring scroll, will break mouse wheel scrolling in Safari */pointer-events: none;}.ace_gutter-layer {position: relative;width: auto;text-align: right;pointer-events: auto;}.ace_text-layer {color: black;font: inherit !important;}.ace_cjk {display: inline-block;text-align: center;}.ace_cursor-layer {z-index: 4;}.ace_cursor {z-index: 4;position: absolute;-moz-box-sizing: border-box;-webkit-box-sizing: border-box;box-sizing: border-box;}.ace_hidden-cursors .ace_cursor {opacity: 0.2;}.ace_smooth-blinking .ace_cursor {-moz-transition: opacity 0.18s;-webkit-transition: opacity 0.18s;-o-transition: opacity 0.18s;-ms-transition: opacity 0.18s;transition: opacity 0.18s;}.ace_cursor[style*=\"opacity: 0\"]{-ms-filter: \"progid:DXImageTransform.Microsoft.Alpha(Opacity=0)\";}.ace_editor.ace_multiselect .ace_cursor {border-left-width: 1px;}.ace_line {white-space: nowrap;}.ace_marker-layer .ace_step {position: absolute;z-index: 3;}.ace_marker-layer .ace_selection {position: absolute;z-index: 5;}.ace_marker-layer .ace_bracket {position: absolute;z-index: 6;}.ace_marker-layer .ace_active-line {position: absolute;z-index: 2;}.ace_marker-layer .ace_selected-word {position: absolute;z-index: 4;-moz-box-sizing: border-box;-webkit-box-sizing: border-box;box-sizing: border-box;}.ace_line .ace_fold {-moz-box-sizing: border-box;-webkit-box-sizing: border-box;box-sizing: border-box;display: inline-block;height: 11px;margin-top: -2px;vertical-align: middle;background-image:url(\"data:image/png,%89PNG%0D%0A%1A%0A%00%00%00%0DIHDR%00%00%00%11%00%00%00%09%08%06%00%00%00%D4%E8%C7%0C%00%00%03%1EiCCPICC%20Profile%00%00x%01%85T%DFk%D3P%14%FE%DAe%9D%B0%E1%8B%3Ag%11%09%3Eh%91ndStC%9C%B6kW%BA%CDZ%EA6%B7!H%9B%A6m%5C%9A%C6%24%ED~%B0%07%D9%8Bo%3A%C5w%F1%07%3E%F9%07%0C%D9%83o%7B%92%0D%C6%14a%F8%AC%88%22L%F6%22%B3%9E%9B4M'S%03%B9%F7%BB%DF%F9%EE9'%E7%E4%5E%A0%F9qZ%D3%14%2F%0F%14USO%C5%C2%FC%C4%E4%14%DF%F2%01%5E%1CC%2B%FChM%8B%86%16J%26G%40%0F%D3%B2y%EF%B3%F3%0E%1E%C6lt%EEo%DF%AB%FEc%D5%9A%95%0C%11%F0%1C%20%BE%945%C4%22%E1Y%A0i%5C%D4t%13%E0%D6%89%EF%9D15%C2%CDLsX%A7%04%09%1Fg8oc%81%E1%8C%8D%23%96f45%40%9A%09%C2%07%C5B%3AK%B8%408%98i%E0%F3%0D%D8%CE%81%14%E4'%26%A9%92.%8B%3C%ABER%2F%E5dE%B2%0C%F6%F0%1Fs%83%F2_%B0%A8%94%E9%9B%AD%E7%10%8Dm%9A%19N%D1%7C%8A%DE%1F9%7Dp%8C%E6%00%D5%C1%3F_%18%BDA%B8%9DpX6%E3%A35~B%CD%24%AE%11%26%BD%E7%EEti%98%EDe%9A%97Y)%12%25%1C%24%BCbT%AE3li%E6%0B%03%89%9A%E6%D3%ED%F4P%92%B0%9F4%BF43Y%F3%E3%EDP%95%04%EB1%C5%F5%F6KF%F4%BA%BD%D7%DB%91%93%07%E35%3E%A7)%D6%7F%40%FE%BD%F7%F5r%8A%E5y%92%F0%EB%B4%1E%8D%D5%F4%5B%92%3AV%DB%DB%E4%CD%A6%23%C3%C4wQ%3F%03HB%82%8E%1Cd(%E0%91B%0Ca%9Ac%C4%AA%F8L%16%19%22J%A4%D2itTy%B28%D6%3B(%93%96%ED%1CGx%C9_%0E%B8%5E%16%F5%5B%B2%B8%F6%E0%FB%9E%DD%25%D7%8E%BC%15%85%C5%B7%A3%D8Q%ED%B5%81%E9%BA%B2%13%9A%1B%7Fua%A5%A3n%E17%B9%E5%9B%1Bm%AB%0B%08Q%FE%8A%E5%B1H%5Ee%CAO%82Q%D7u6%E6%90S%97%FCu%0B%CF2%94%EE%25v%12X%0C%BA%AC%F0%5E%F8*l%0AO%85%17%C2%97%BF%D4%C8%CE%DE%AD%11%CB%80q%2C%3E%AB%9ES%CD%C6%EC%25%D2L%D2%EBd%B8%BF%8A%F5B%C6%18%F9%901CZ%9D%BE%24M%9C%8A9%F2%DAP%0B'%06w%82%EB%E6%E2%5C%2F%D7%07%9E%BB%CC%5D%E1%FA%B9%08%AD.r%23%8E%C2%17%F5E%7C!%F0%BE3%BE%3E_%B7o%88a%A7%DB%BE%D3d%EB%A31Z%EB%BB%D3%91%BA%A2%B1z%94%8F%DB'%F6%3D%8E%AA%13%19%B2%B1%BE%B1~V%08%2B%B4%A2cjJ%B3tO%00%03%25mN%97%F3%05%93%EF%11%84%0B%7C%88%AE-%89%8F%ABbW%90O%2B%0Ao%99%0C%5E%97%0CI%AFH%D9.%B0%3B%8F%ED%03%B6S%D6%5D%E6i_s9%F3*p%E9%1B%FD%C3%EB.7U%06%5E%19%C0%D1s.%17%A03u%E4%09%B0%7C%5E%2C%EB%15%DB%1F%3C%9E%B7%80%91%3B%DBc%AD%3Dma%BA%8B%3EV%AB%DBt.%5B%1E%01%BB%0F%AB%D5%9F%CF%AA%D5%DD%E7%E4%7F%0Bx%A3%FC%06%A9%23%0A%D6%C2%A1_2%00%00%00%09pHYs%00%00%0B%13%00%00%0B%13%01%00%9A%9C%18%00%00%00%B5IDAT(%15%A5%91%3D%0E%02!%10%85ac%E1%05%D6%CE%D6%C6%CE%D2%E8%ED%CD%DE%C0%C6%D6N.%E0V%F8%3D%9Ca%891XH%C2%BE%D9y%3F%90!%E6%9C%C3%BFk%E5%011%C6-%F5%C8N%04%DF%BD%FF%89%DFt%83DN%60%3E%F3%AB%A0%DE%1A%5Dg%BE%10Q%97%1B%40%9C%A8o%10%8F%5E%828%B4%1B%60%87%F6%02%26%85%1Ch%1E%C1%2B%5Bk%FF%86%EE%B7j%09%9A%DA%9B%ACe%A3%F9%EC%DA!9%B4%D5%A6%81%86%86%98%CC%3C%5B%40%FA%81%B3%E9%CB%23%94%C16Azo%05%D4%E1%C1%95a%3B%8A'%A0%E8%CC%17%22%85%1D%BA%00%A2%FA%DC%0A%94%D1%D1%8D%8B%3A%84%17B%C7%60%1A%25Z%FC%8D%00%00%00%00IEND%AEB%60%82\"),url(\"data:image/png,%89PNG%0D%0A%1A%0A%00%00%00%0DIHDR%00%00%00%05%00%00%007%08%06%00%00%00%C4%DD%80C%00%00%03%1EiCCPICC%20Profile%00%00x%01%85T%DFk%D3P%14%FE%DAe%9D%B0%E1%8B%3Ag%11%09%3Eh%91ndStC%9C%B6kW%BA%CDZ%EA6%B7!H%9B%A6m%5C%9A%C6%24%ED~%B0%07%D9%8Bo%3A%C5w%F1%07%3E%F9%07%0C%D9%83o%7B%92%0D%C6%14a%F8%AC%88%22L%F6%22%B3%9E%9B4M'S%03%B9%F7%BB%DF%F9%EE9'%E7%E4%5E%A0%F9qZ%D3%14%2F%0F%14USO%C5%C2%FC%C4%E4%14%DF%F2%01%5E%1CC%2B%FChM%8B%86%16J%26G%40%0F%D3%B2y%EF%B3%F3%0E%1E%C6lt%EEo%DF%AB%FEc%D5%9A%95%0C%11%F0%1C%20%BE%945%C4%22%E1Y%A0i%5C%D4t%13%E0%D6%89%EF%9D15%C2%CDLsX%A7%04%09%1Fg8oc%81%E1%8C%8D%23%96f45%40%9A%09%C2%07%C5B%3AK%B8%408%98i%E0%F3%0D%D8%CE%81%14%E4'%26%A9%92.%8B%3C%ABER%2F%E5dE%B2%0C%F6%F0%1Fs%83%F2_%B0%A8%94%E9%9B%AD%E7%10%8Dm%9A%19N%D1%7C%8A%DE%1F9%7Dp%8C%E6%00%D5%C1%3F_%18%BDA%B8%9DpX6%E3%A35~B%CD%24%AE%11%26%BD%E7%EEti%98%EDe%9A%97Y)%12%25%1C%24%BCbT%AE3li%E6%0B%03%89%9A%E6%D3%ED%F4P%92%B0%9F4%BF43Y%F3%E3%EDP%95%04%EB1%C5%F5%F6KF%F4%BA%BD%D7%DB%91%93%07%E35%3E%A7)%D6%7F%40%FE%BD%F7%F5r%8A%E5y%92%F0%EB%B4%1E%8D%D5%F4%5B%92%3AV%DB%DB%E4%CD%A6%23%C3%C4wQ%3F%03HB%82%8E%1Cd(%E0%91B%0Ca%9Ac%C4%AA%F8L%16%19%22J%A4%D2itTy%B28%D6%3B(%93%96%ED%1CGx%C9_%0E%B8%5E%16%F5%5B%B2%B8%F6%E0%FB%9E%DD%25%D7%8E%BC%15%85%C5%B7%A3%D8Q%ED%B5%81%E9%BA%B2%13%9A%1B%7Fua%A5%A3n%E17%B9%E5%9B%1Bm%AB%0B%08Q%FE%8A%E5%B1H%5Ee%CAO%82Q%D7u6%E6%90S%97%FCu%0B%CF2%94%EE%25v%12X%0C%BA%AC%F0%5E%F8*l%0AO%85%17%C2%97%BF%D4%C8%CE%DE%AD%11%CB%80q%2C%3E%AB%9ES%CD%C6%EC%25%D2L%D2%EBd%B8%BF%8A%F5B%C6%18%F9%901CZ%9D%BE%24M%9C%8A9%F2%DAP%0B'%06w%82%EB%E6%E2%5C%2F%D7%07%9E%BB%CC%5D%E1%FA%B9%08%AD.r%23%8E%C2%17%F5E%7C!%F0%BE3%BE%3E_%B7o%88a%A7%DB%BE%D3d%EB%A31Z%EB%BB%D3%91%BA%A2%B1z%94%8F%DB'%F6%3D%8E%AA%13%19%B2%B1%BE%B1~V%08%2B%B4%A2cjJ%B3tO%00%03%25mN%97%F3%05%93%EF%11%84%0B%7C%88%AE-%89%8F%ABbW%90O%2B%0Ao%99%0C%5E%97%0CI%AFH%D9.%B0%3B%8F%ED%03%B6S%D6%5D%E6i_s9%F3*p%E9%1B%FD%C3%EB.7U%06%5E%19%C0%D1s.%17%A03u%E4%09%B0%7C%5E%2C%EB%15%DB%1F%3C%9E%B7%80%91%3B%DBc%AD%3Dma%BA%8B%3EV%AB%DBt.%5B%1E%01%BB%0F%AB%D5%9F%CF%AA%D5%DD%E7%E4%7F%0Bx%A3%FC%06%A9%23%0A%D6%C2%A1_2%00%00%00%09pHYs%00%00%0B%13%00%00%0B%13%01%00%9A%9C%18%00%00%00%3AIDAT8%11c%FC%FF%FF%7F%18%03%1A%60%01%F2%3F%A0%891%80%04%FF%11-%F8%17%9BJ%E2%05%B1ZD%81v%26t%E7%80%F8%A3%82h%A12%1A%20%A3%01%02%0F%01%BA%25%06%00%19%C0%0D%AEF%D5%3ES%00%00%00%00IEND%AEB%60%82\");background-repeat: no-repeat, repeat-x;background-position: center center, top left;color: transparent;border: 1px solid black;-moz-border-radius: 2px;-webkit-border-radius: 2px;border-radius: 2px;cursor: pointer;pointer-events: auto;}.ace_dark .ace_fold {}.ace_fold:hover{background-image:url(\"data:image/png,%89PNG%0D%0A%1A%0A%00%00%00%0DIHDR%00%00%00%11%00%00%00%09%08%06%00%00%00%D4%E8%C7%0C%00%00%03%1EiCCPICC%20Profile%00%00x%01%85T%DFk%D3P%14%FE%DAe%9D%B0%E1%8B%3Ag%11%09%3Eh%91ndStC%9C%B6kW%BA%CDZ%EA6%B7!H%9B%A6m%5C%9A%C6%24%ED~%B0%07%D9%8Bo%3A%C5w%F1%07%3E%F9%07%0C%D9%83o%7B%92%0D%C6%14a%F8%AC%88%22L%F6%22%B3%9E%9B4M'S%03%B9%F7%BB%DF%F9%EE9'%E7%E4%5E%A0%F9qZ%D3%14%2F%0F%14USO%C5%C2%FC%C4%E4%14%DF%F2%01%5E%1CC%2B%FChM%8B%86%16J%26G%40%0F%D3%B2y%EF%B3%F3%0E%1E%C6lt%EEo%DF%AB%FEc%D5%9A%95%0C%11%F0%1C%20%BE%945%C4%22%E1Y%A0i%5C%D4t%13%E0%D6%89%EF%9D15%C2%CDLsX%A7%04%09%1Fg8oc%81%E1%8C%8D%23%96f45%40%9A%09%C2%07%C5B%3AK%B8%408%98i%E0%F3%0D%D8%CE%81%14%E4'%26%A9%92.%8B%3C%ABER%2F%E5dE%B2%0C%F6%F0%1Fs%83%F2_%B0%A8%94%E9%9B%AD%E7%10%8Dm%9A%19N%D1%7C%8A%DE%1F9%7Dp%8C%E6%00%D5%C1%3F_%18%BDA%B8%9DpX6%E3%A35~B%CD%24%AE%11%26%BD%E7%EEti%98%EDe%9A%97Y)%12%25%1C%24%BCbT%AE3li%E6%0B%03%89%9A%E6%D3%ED%F4P%92%B0%9F4%BF43Y%F3%E3%EDP%95%04%EB1%C5%F5%F6KF%F4%BA%BD%D7%DB%91%93%07%E35%3E%A7)%D6%7F%40%FE%BD%F7%F5r%8A%E5y%92%F0%EB%B4%1E%8D%D5%F4%5B%92%3AV%DB%DB%E4%CD%A6%23%C3%C4wQ%3F%03HB%82%8E%1Cd(%E0%91B%0Ca%9Ac%C4%AA%F8L%16%19%22J%A4%D2itTy%B28%D6%3B(%93%96%ED%1CGx%C9_%0E%B8%5E%16%F5%5B%B2%B8%F6%E0%FB%9E%DD%25%D7%8E%BC%15%85%C5%B7%A3%D8Q%ED%B5%81%E9%BA%B2%13%9A%1B%7Fua%A5%A3n%E17%B9%E5%9B%1Bm%AB%0B%08Q%FE%8A%E5%B1H%5Ee%CAO%82Q%D7u6%E6%90S%97%FCu%0B%CF2%94%EE%25v%12X%0C%BA%AC%F0%5E%F8*l%0AO%85%17%C2%97%BF%D4%C8%CE%DE%AD%11%CB%80q%2C%3E%AB%9ES%CD%C6%EC%25%D2L%D2%EBd%B8%BF%8A%F5B%C6%18%F9%901CZ%9D%BE%24M%9C%8A9%F2%DAP%0B'%06w%82%EB%E6%E2%5C%2F%D7%07%9E%BB%CC%5D%E1%FA%B9%08%AD.r%23%8E%C2%17%F5E%7C!%F0%BE3%BE%3E_%B7o%88a%A7%DB%BE%D3d%EB%A31Z%EB%BB%D3%91%BA%A2%B1z%94%8F%DB'%F6%3D%8E%AA%13%19%B2%B1%BE%B1~V%08%2B%B4%A2cjJ%B3tO%00%03%25mN%97%F3%05%93%EF%11%84%0B%7C%88%AE-%89%8F%ABbW%90O%2B%0Ao%99%0C%5E%97%0CI%AFH%D9.%B0%3B%8F%ED%03%B6S%D6%5D%E6i_s9%F3*p%E9%1B%FD%C3%EB.7U%06%5E%19%C0%D1s.%17%A03u%E4%09%B0%7C%5E%2C%EB%15%DB%1F%3C%9E%B7%80%91%3B%DBc%AD%3Dma%BA%8B%3EV%AB%DBt.%5B%1E%01%BB%0F%AB%D5%9F%CF%AA%D5%DD%E7%E4%7F%0Bx%A3%FC%06%A9%23%0A%D6%C2%A1_2%00%00%00%09pHYs%00%00%0B%13%00%00%0B%13%01%00%9A%9C%18%00%00%00%B5IDAT(%15%A5%91%3D%0E%02!%10%85ac%E1%05%D6%CE%D6%C6%CE%D2%E8%ED%CD%DE%C0%C6%D6N.%E0V%F8%3D%9Ca%891XH%C2%BE%D9y%3F%90!%E6%9C%C3%BFk%E5%011%C6-%F5%C8N%04%DF%BD%FF%89%DFt%83DN%60%3E%F3%AB%A0%DE%1A%5Dg%BE%10Q%97%1B%40%9C%A8o%10%8F%5E%828%B4%1B%60%87%F6%02%26%85%1Ch%1E%C1%2B%5Bk%FF%86%EE%B7j%09%9A%DA%9B%ACe%A3%F9%EC%DA!9%B4%D5%A6%81%86%86%98%CC%3C%5B%40%FA%81%B3%E9%CB%23%94%C16Azo%05%D4%E1%C1%95a%3B%8A'%A0%E8%CC%17%22%85%1D%BA%00%A2%FA%DC%0A%94%D1%D1%8D%8B%3A%84%17B%C7%60%1A%25Z%FC%8D%00%00%00%00IEND%AEB%60%82\"),url(\"data:image/png,%89PNG%0D%0A%1A%0A%00%00%00%0DIHDR%00%00%00%05%00%00%007%08%06%00%00%00%C4%DD%80C%00%00%03%1EiCCPICC%20Profile%00%00x%01%85T%DFk%D3P%14%FE%DAe%9D%B0%E1%8B%3Ag%11%09%3Eh%91ndStC%9C%B6kW%BA%CDZ%EA6%B7!H%9B%A6m%5C%9A%C6%24%ED~%B0%07%D9%8Bo%3A%C5w%F1%07%3E%F9%07%0C%D9%83o%7B%92%0D%C6%14a%F8%AC%88%22L%F6%22%B3%9E%9B4M'S%03%B9%F7%BB%DF%F9%EE9'%E7%E4%5E%A0%F9qZ%D3%14%2F%0F%14USO%C5%C2%FC%C4%E4%14%DF%F2%01%5E%1CC%2B%FChM%8B%86%16J%26G%40%0F%D3%B2y%EF%B3%F3%0E%1E%C6lt%EEo%DF%AB%FEc%D5%9A%95%0C%11%F0%1C%20%BE%945%C4%22%E1Y%A0i%5C%D4t%13%E0%D6%89%EF%9D15%C2%CDLsX%A7%04%09%1Fg8oc%81%E1%8C%8D%23%96f45%40%9A%09%C2%07%C5B%3AK%B8%408%98i%E0%F3%0D%D8%CE%81%14%E4'%26%A9%92.%8B%3C%ABER%2F%E5dE%B2%0C%F6%F0%1Fs%83%F2_%B0%A8%94%E9%9B%AD%E7%10%8Dm%9A%19N%D1%7C%8A%DE%1F9%7Dp%8C%E6%00%D5%C1%3F_%18%BDA%B8%9DpX6%E3%A35~B%CD%24%AE%11%26%BD%E7%EEti%98%EDe%9A%97Y)%12%25%1C%24%BCbT%AE3li%E6%0B%03%89%9A%E6%D3%ED%F4P%92%B0%9F4%BF43Y%F3%E3%EDP%95%04%EB1%C5%F5%F6KF%F4%BA%BD%D7%DB%91%93%07%E35%3E%A7)%D6%7F%40%FE%BD%F7%F5r%8A%E5y%92%F0%EB%B4%1E%8D%D5%F4%5B%92%3AV%DB%DB%E4%CD%A6%23%C3%C4wQ%3F%03HB%82%8E%1Cd(%E0%91B%0Ca%9Ac%C4%AA%F8L%16%19%22J%A4%D2itTy%B28%D6%3B(%93%96%ED%1CGx%C9_%0E%B8%5E%16%F5%5B%B2%B8%F6%E0%FB%9E%DD%25%D7%8E%BC%15%85%C5%B7%A3%D8Q%ED%B5%81%E9%BA%B2%13%9A%1B%7Fua%A5%A3n%E17%B9%E5%9B%1Bm%AB%0B%08Q%FE%8A%E5%B1H%5Ee%CAO%82Q%D7u6%E6%90S%97%FCu%0B%CF2%94%EE%25v%12X%0C%BA%AC%F0%5E%F8*l%0AO%85%17%C2%97%BF%D4%C8%CE%DE%AD%11%CB%80q%2C%3E%AB%9ES%CD%C6%EC%25%D2L%D2%EBd%B8%BF%8A%F5B%C6%18%F9%901CZ%9D%BE%24M%9C%8A9%F2%DAP%0B'%06w%82%EB%E6%E2%5C%2F%D7%07%9E%BB%CC%5D%E1%FA%B9%08%AD.r%23%8E%C2%17%F5E%7C!%F0%BE3%BE%3E_%B7o%88a%A7%DB%BE%D3d%EB%A31Z%EB%BB%D3%91%BA%A2%B1z%94%8F%DB'%F6%3D%8E%AA%13%19%B2%B1%BE%B1~V%08%2B%B4%A2cjJ%B3tO%00%03%25mN%97%F3%05%93%EF%11%84%0B%7C%88%AE-%89%8F%ABbW%90O%2B%0Ao%99%0C%5E%97%0CI%AFH%D9.%B0%3B%8F%ED%03%B6S%D6%5D%E6i_s9%F3*p%E9%1B%FD%C3%EB.7U%06%5E%19%C0%D1s.%17%A03u%E4%09%B0%7C%5E%2C%EB%15%DB%1F%3C%9E%B7%80%91%3B%DBc%AD%3Dma%BA%8B%3EV%AB%DBt.%5B%1E%01%BB%0F%AB%D5%9F%CF%AA%D5%DD%E7%E4%7F%0Bx%A3%FC%06%A9%23%0A%D6%C2%A1_2%00%00%00%09pHYs%00%00%0B%13%00%00%0B%13%01%00%9A%9C%18%00%00%003IDAT8%11c%FC%FF%FF%7F%3E%03%1A%60%01%F2%3F%A3%891%80%04%FFQ%26%F8w%C0%B43%A1%DB%0C%E2%8F%0A%A2%85%CAh%80%8C%06%08%3C%04%E8%96%18%00%A3S%0D%CD%CF%D8%C1%9D%00%00%00%00IEND%AEB%60%82\");background-repeat: no-repeat, repeat-x;background-position: center center, top left;}.ace_editor.ace_dragging .ace_content {cursor: move;}.ace_gutter-tooltip {background-color: #FFFFD5;border: 1px solid gray;box-shadow: 0 1px 1px rgba(0, 0, 0, 0.4);color: black;display: inline-block;padding: 4px;position: absolute;z-index: 300;-moz-box-sizing: border-box;-webkit-box-sizing: border-box;box-sizing: border-box;cursor: default;white-space: pre-line;word-wrap: break-word;}.ace_folding-enabled > .ace_gutter-cell {padding-right: 13px;}.ace_fold-widget {-moz-box-sizing: border-box;-webkit-box-sizing: border-box;box-sizing: border-box;margin: 0 -12px 0 1px;display: inline-block;width: 11px;vertical-align: top;background-image: url(\"data:image/png,%89PNG%0D%0A%1A%0A%00%00%00%0DIHDR%00%00%00%05%00%00%00%05%08%06%00%00%00%8Do%26%E5%00%00%004IDATx%DAe%8A%B1%0D%000%0C%C2%F2%2CK%96%BC%D0%8F9%81%88H%E9%D0%0E%96%C0%10%92%3E%02%80%5E%82%E4%A9*-%EEsw%C8%CC%11%EE%96w%D8%DC%E9*Eh%0C%151(%00%00%00%00IEND%AEB%60%82\");background-repeat: no-repeat;background-position: center;border-radius: 3px;border: 1px solid transparent;}.ace_fold-widget.ace_end {background-image: url(\"data:image/png,%89PNG%0D%0A%1A%0A%00%00%00%0DIHDR%00%00%00%05%00%00%00%05%08%06%00%00%00%8Do%26%E5%00%00%004IDATx%DAm%C7%C1%09%000%08C%D1%8C%ECE%C8E(%8E%EC%02)%1EZJ%F1%C1'%04%07I%E1%E5%EE%CAL%F5%A2%99%99%22%E2%D6%1FU%B5%FE0%D9x%A7%26Wz5%0E%D5%00%00%00%00IEND%AEB%60%82\");}.ace_fold-widget.ace_closed {background-image: url(\"data:image/png,%89PNG%0D%0A%1A%0A%00%00%00%0DIHDR%00%00%00%03%00%00%00%06%08%06%00%00%00%06%E5%24%0C%00%00%009IDATx%DA5%CA%C1%09%000%08%03%C0%AC*(%3E%04%C1%0D%BA%B1%23%A4Uh%E0%20%81%C0%CC%F8%82%81%AA%A2%AArGfr%88%08%11%11%1C%DD%7D%E0%EE%5B%F6%F6%CB%B8%05Q%2F%E9tai%D9%00%00%00%00IEND%AEB%60%82\");}.ace_fold-widget:hover {border: 1px solid rgba(0, 0, 0, 0.3);background-color: rgba(255, 255, 255, 0.2);-moz-box-shadow: 0 1px 1px rgba(255, 255, 255, 0.7);-webkit-box-shadow: 0 1px 1px rgba(255, 255, 255, 0.7);box-shadow: 0 1px 1px rgba(255, 255, 255, 0.7);}.ace_fold-widget:active {border: 1px solid rgba(0, 0, 0, 0.4);background-color: rgba(0, 0, 0, 0.05);-moz-box-shadow: 0 1px 1px rgba(255, 255, 255, 0.8);-webkit-box-shadow: 0 1px 1px rgba(255, 255, 255, 0.8);box-shadow: 0 1px 1px rgba(255, 255, 255, 0.8);}/*** Dark version for fold widgets*/.ace_dark .ace_fold-widget {background-image: url(\"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHklEQVQIW2P4//8/AzoGEQ7oGCaLLAhWiSwB146BAQCSTPYocqT0AAAAAElFTkSuQmCC\");}.ace_dark .ace_fold-widget.ace_end {background-image: url(\"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAH0lEQVQIW2P4//8/AxQ7wNjIAjDMgC4AxjCVKBirIAAF0kz2rlhxpAAAAABJRU5ErkJggg==\");}.ace_dark .ace_fold-widget.ace_closed {background-image: url(\"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAMAAAAFCAYAAACAcVaiAAAAHElEQVQIW2P4//+/AxAzgDADlOOAznHAKgPWAwARji8UIDTfQQAAAABJRU5ErkJggg==\");}.ace_dark .ace_fold-widget:hover {box-shadow: 0 1px 1px rgba(255, 255, 255, 0.2);background-color: rgba(255, 255, 255, 0.1);}.ace_dark .ace_fold-widget:active {-moz-box-shadow: 0 1px 1px rgba(255, 255, 255, 0.2);-webkit-box-shadow: 0 1px 1px rgba(255, 255, 255, 0.2);box-shadow: 0 1px 1px rgba(255, 255, 255, 0.2);}.ace_fold-widget.ace_invalid {background-color: #FFB4B4;border-color: #DE5555;}.ace_fade-fold-widgets .ace_fold-widget {-moz-transition: opacity 0.4s ease 0.05s;-webkit-transition: opacity 0.4s ease 0.05s;-o-transition: opacity 0.4s ease 0.05s;-ms-transition: opacity 0.4s ease 0.05s;transition: opacity 0.4s ease 0.05s;opacity: 0;}.ace_fade-fold-widgets:hover .ace_fold-widget {-moz-transition: opacity 0.05s ease 0.05s;-webkit-transition: opacity 0.05s ease 0.05s;-o-transition: opacity 0.05s ease 0.05s;-ms-transition: opacity 0.05s ease 0.05s;transition: opacity 0.05s ease 0.05s;opacity:1;}.ace_underline {text-decoration: underline;}.ace_bold {font-weight: bold;}.ace_nobold .ace_bold {font-weight: normal;}.ace_italic {font-style: italic;}";i.importCssString(m,"ace_editor");var g=function(e,t){var n=this;this.container=e,this.$keepTextAreaAtCursor=!o.isIE,i.addCssClass(e,"ace_editor"),this.setTheme(t),this.$gutter=i.createElement("div"),this.$gutter.className="ace_gutter",this.container.appendChild(this.$gutter),this.scroller=i.createElement("div"),this.scroller.className="ace_scroller",this.container.appendChild(this.scroller),this.content=i.createElement("div"),this.content.className="ace_content",this.scroller.appendChild(this.content),this.setHighlightGutterLine(!0),this.$gutterLayer=new f(this.$gutter),this.$gutterLayer.on("changeGutterWidth",this.onGutterResize.bind(this)),this.$markerBack=new l(this.content);var r=this.$textLayer=new c(this.content);this.canvas=r.element,this.$markerFront=new l(this.content),this.$cursorLayer=new h(this.content),this.$horizScroll=!1,this.$horizScrollAlwaysVisible=!1,this.$animatedScroll=!1,this.scrollBar=new p(e),this.scrollBar.addEventListener("scroll",function(e){n.$inScrollAnimation||n.session.setScrollTop(e.data)}),this.scrollTop=0,this.scrollLeft=0,s.addListener(this.scroller,"scroll",function(){var e=n.scroller.scrollLeft;n.scrollLeft=e,n.session.setScrollLeft(e)}),this.cursorPos={row:0,column:0},this.$textLayer.addEventListener("changeCharacterSize",function(){n.updateCharacterSize(),n.onResize(!0)}),this.$size={width:0,height:0,scrollerHeight:0,scrollerWidth:0},this.layerConfig={width:1,padding:0,firstRow:0,firstRowScreen:0,lastRow:0,lineHeight:1,characterWidth:1,minHeight:1,maxHeight:1,offset:0,height:1},this.$loop=new d(this.$renderChanges.bind(this),this.container.ownerDocument.defaultView),this.$loop.schedule(this.CHANGE_FULL),this.updateCharacterSize(),this.setPadding(4)};(function(){this.showGutter=!0,this.CHANGE_CURSOR=1,this.CHANGE_MARKER=2,this.CHANGE_GUTTER=4,this.CHANGE_SCROLL=8,this.CHANGE_LINES=16,this.CHANGE_TEXT=32,this.CHANGE_SIZE=64,this.CHANGE_MARKER_BACK=128,this.CHANGE_MARKER_FRONT=256,this.CHANGE_FULL=512,this.CHANGE_H_SCROLL=1024,r.implement(this,v),this.updateCharacterSize=function(){this.$textLayer.allowBoldFonts!=this.$allowBoldFonts&&(this.$allowBoldFonts=this.$textLayer.allowBoldFonts,this.setStyle("ace_nobold",!this.$allowBoldFonts)),this.characterWidth=this.$textLayer.getCharacterWidth(),this.lineHeight=this.$textLayer.getLineHeight(),this.$updatePrintMargin()},this.setSession=function(e){this.session=e,this.scroller.className="ace_scroller",this.$cursorLayer.setSession(e),this.$markerBack.setSession(e),this.$markerFront.setSession(e),this.$gutterLayer.setSession(e),this.$textLayer.setSession(e),this.$loop.schedule(this.CHANGE_FULL)},this.updateLines=function(e,t){t===undefined&&(t=Infinity),this.$changedLines?(this.$changedLines.firstRow>e&&(this.$changedLines.firstRow=e),this.$changedLines.lastRow<t&&(this.$changedLines.lastRow=t)):this.$changedLines={firstRow:e,lastRow:t};if(this.$changedLines.firstRow>this.layerConfig.lastRow||this.$changedLines.lastRow<this.layerConfig.firstRow)return;this.$loop.schedule(this.CHANGE_LINES)},this.onChangeTabSize=function(){this.$loop.schedule(this.CHANGE_TEXT|this.CHANGE_MARKER),this.$textLayer.onChangeTabSize()},this.updateText=function(){this.$loop.schedule(this.CHANGE_TEXT)},this.updateFull=function(e){e?this.$renderChanges(this.CHANGE_FULL,!0):this.$loop.schedule(this.CHANGE_FULL)},this.updateFontSize=function(){this.$textLayer.checkForSizeChanges()},this.onResize=function(e,t,n,r){var s=this.CHANGE_SIZE,o=this.$size;if(this.resizing>2)return;this.resizing>1?this.resizing++:this.resizing=e?1:0,r||(r=i.getInnerHeight(this.container));if(e||o.height!=r)o.height=r,this.scroller.style.height=r+"px",o.scrollerHeight=this.scroller.clientHeight,this.scrollBar.setHeight(o.scrollerHeight),this.session&&(this.session.setScrollTop(this.getScrollTop()),s|=this.CHANGE_FULL);n||(n=i.getInnerWidth(this.container));if(e||this.resizing>1||o.width!=n){o.width=n;var t=this.showGutter?this.$gutter.offsetWidth:0;this.scroller.style.left=t+"px",o.scrollerWidth=Math.max(0,n-t-this.scrollBar.getWidth()),this.scroller.style.right=this.scrollBar.getWidth()+"px";if(this.session.getUseWrapMode()&&this.adjustWrapLimit()||e)s|=this.CHANGE_FULL}e?this.$renderChanges(s,!0):this.$loop.schedule(s),e&&delete this.resizing},this.onGutterResize=function(){var e=this.$size.width,t=this.showGutter?this.$gutter.offsetWidth:0;this.scroller.style.left=t+"px",this.$size.scrollerWidth=Math.max(0,e-t-this.scrollBar.getWidth()),this.session.getUseWrapMode()&&this.adjustWrapLimit()&&this.$loop.schedule(this.CHANGE_FULL)},this.adjustWrapLimit=function(){var e=this.$size.scrollerWidth-this.$padding*2,t=Math.floor(e/this.characterWidth);return this.session.adjustWrapLimit(t)},this.setAnimatedScroll=function(e){this.$animatedScroll=e},this.getAnimatedScroll=function(){return this.$animatedScroll},this.setShowInvisibles=function(e){this.$textLayer.setShowInvisibles(e)&&this.$loop.schedule(this.CHANGE_TEXT)},this.getShowInvisibles=function(){return this.$textLayer.showInvisibles},this.getDisplayIndentGuides=function(){return this.$textLayer.displayIndentGuides},this.setDisplayIndentGuides=function(e){this.$textLayer.setDisplayIndentGuides(e)&&this.$loop.schedule(this.CHANGE_TEXT)},this.$showPrintMargin=!0,this.setShowPrintMargin=function(e){this.$showPrintMargin=e,this.$updatePrintMargin()},this.getShowPrintMargin=function(){return this.$showPrintMargin},this.$printMarginColumn=80,this.setPrintMarginColumn=function(e){this.$printMarginColumn=e,this.$updatePrintMargin()},this.getPrintMarginColumn=function(){return this.$printMarginColumn},this.getShowGutter=function(){return this.showGutter},this.setShowGutter=function(e){if(this.showGutter===e)return;this.$gutter.style.display=e?"block":"none",this.showGutter=e,this.onResize(!0)},this.getFadeFoldWidgets=function(){return i.hasCssClass(this.$gutter,"ace_fade-fold-widgets")},this.setFadeFoldWidgets=function(e){e?i.addCssClass(this.$gutter,"ace_fade-fold-widgets"):i.removeCssClass(this.$gutter,"ace_fade-fold-widgets")},this.$highlightGutterLine=!1,this.setHighlightGutterLine=function(e){if(this.$highlightGutterLine==e)return;this.$highlightGutterLine=e;if(!this.$gutterLineHighlight){this.$gutterLineHighlight=i.createElement("div"),this.$gutterLineHighlight.className="ace_gutter-active-line",this.$gutter.appendChild(this.$gutterLineHighlight);return}this.$gutterLineHighlight.style.display=e?"":"none",this.$cursorLayer.$pixelPos&&this.$updateGutterLineHighlight()},this.getHighlightGutterLine=function(){return this.$highlightGutterLine},this.$updateGutterLineHighlight=function(){this.$gutterLineHighlight.style.top=this.$cursorLayer.$pixelPos.top-this.layerConfig.offset+"px",this.$gutterLineHighlight.style.height=this.layerConfig.lineHeight+"px"},this.$updatePrintMargin=function(){if(!this.$showPrintMargin&&!this.$printMarginEl)return;if(!this.$printMarginEl){var e=i.createElement("div");e.className="ace_layer ace_print-margin-layer",this.$printMarginEl=i.createElement("div"),this.$printMarginEl.className="ace_print-margin",e.appendChild(this.$printMarginEl),this.content.insertBefore(e,this.content.firstChild)}var t=this.$printMarginEl.style;t.left=this.characterWidth*this.$printMarginColumn+this.$padding+"px",t.visibility=this.$showPrintMargin?"visible":"hidden"},this.getContainerElement=function(){return this.container},this.getMouseEventTarget=function(){return this.content},this.getTextAreaContainer=function(){return this.container},this.$moveTextAreaToCursor=function(){if(!this.$keepTextAreaAtCursor)return;var e=this.$cursorLayer.$pixelPos.top,t=this.$cursorLayer.$pixelPos.left;e-=this.layerConfig.offset;if(e<0||e>this.layerConfig.height-this.lineHeight)return;var n=this.characterWidth;this.$composition&&(n+=this.textarea.scrollWidth),t-=this.scrollLeft,t>this.$size.scrollerWidth-n&&(t=this.$size.scrollerWidth-n),this.showGutter&&(t+=this.$gutterLayer.gutterWidth),this.textarea.style.height=this.lineHeight+"px",this.textarea.style.width=n+"px",this.textarea.style.left=t+"px",this.textarea.style.top=e-1+"px"},this.getFirstVisibleRow=function(){return this.layerConfig.firstRow},this.getFirstFullyVisibleRow=function(){return this.layerConfig.firstRow+(this.layerConfig.offset===0?0:1)},this.getLastFullyVisibleRow=function(){var e=Math.floor((this.layerConfig.height+this.layerConfig.offset)/this.layerConfig.lineHeight);return this.layerConfig.firstRow-1+e},this.getLastVisibleRow=function(){return this.layerConfig.lastRow},this.$padding=null,this.setPadding=function(e){this.$padding=e,this.$textLayer.setPadding(e),this.$cursorLayer.setPadding(e),this.$markerFront.setPadding(e),this.$markerBack.setPadding(e),this.$loop.schedule(this.CHANGE_FULL),this.$updatePrintMargin()},this.getHScrollBarAlwaysVisible=function(){return this.$horizScrollAlwaysVisible},this.setHScrollBarAlwaysVisible=function(e){this.$horizScrollAlwaysVisible!=e&&(this.$horizScrollAlwaysVisible=e,(!this.$horizScrollAlwaysVisible||!this.$horizScroll)&&this.$loop.schedule(this.CHANGE_SCROLL))},this.$updateScrollBar=function(){this.scrollBar.setInnerHeight(this.layerConfig.maxHeight),this.scrollBar.setScrollTop(this.scrollTop)},this.$renderChanges=function(e,t){if(!t&&(!e||!this.session||!this.container.offsetWidth))return;(e&this.CHANGE_FULL||e&this.CHANGE_SIZE||e&this.CHANGE_TEXT||e&this.CHANGE_LINES||e&this.CHANGE_SCROLL)&&this.$computeLayerConfig();if(e&this.CHANGE_H_SCROLL){this.scroller.scrollLeft=this.scrollLeft;var n=this.scroller.scrollLeft;this.scrollLeft=n,this.session.setScrollLeft(n),this.scroller.className=this.scrollLeft==0?"ace_scroller":"ace_scroller ace_scroll-left"}if(e&this.CHANGE_FULL){this.$textLayer.checkForSizeChanges(),this.$updateScrollBar(),this.$textLayer.update(this.layerConfig),this.showGutter&&this.$gutterLayer.update(this.layerConfig),this.$markerBack.update(this.layerConfig),this.$markerFront.update(this.layerConfig),this.$cursorLayer.update(this.layerConfig),this.$moveTextAreaToCursor(),this.$highlightGutterLine&&this.$updateGutterLineHighlight();return}if(e&this.CHANGE_SCROLL){this.$updateScrollBar(),e&this.CHANGE_TEXT||e&this.CHANGE_LINES?this.$textLayer.update(this.layerConfig):this.$textLayer.scrollLines(this.layerConfig),this.showGutter&&this.$gutterLayer.update(this.layerConfig),this.$markerBack.update(this.layerConfig),this.$markerFront.update(this.layerConfig),this.$cursorLayer.update(this.layerConfig),this.$moveTextAreaToCursor(),this.$highlightGutterLine&&this.$updateGutterLineHighlight();return}e&this.CHANGE_TEXT?(this.$textLayer.update(this.layerConfig),this.showGutter&&this.$gutterLayer.update(this.layerConfig)):e&this.CHANGE_LINES?(this.$updateLines()||e&this.CHANGE_GUTTER&&this.showGutter)&&this.$gutterLayer.update(this.layerConfig):(e&this.CHANGE_TEXT||e&this.CHANGE_GUTTER)&&this.showGutter&&this.$gutterLayer.update(this.layerConfig),e&this.CHANGE_CURSOR&&(this.$cursorLayer.update(this.layerConfig),this.$moveTextAreaToCursor(),this.$highlightGutterLine&&this.$updateGutterLineHighlight()),e&(this.CHANGE_MARKER|this.CHANGE_MARKER_FRONT)&&this.$markerFront.update(this.layerConfig),e&(this.CHANGE_MARKER|this.CHANGE_MARKER_BACK)&&this.$markerBack.update(this.layerConfig),e&this.CHANGE_SIZE&&this.$updateScrollBar()},this.$computeLayerConfig=function(){var e=this.session,t=this.scrollTop%this.lineHeight,n=this.$size.scrollerHeight+this.lineHeight,r=this.$getLongestLine(),i=this.$horizScrollAlwaysVisible||this.$size.scrollerWidth-r<0,s=this.$horizScroll!==i;this.$horizScroll=i,s&&(this.scroller.style.overflowX=i?"scroll":"hidden",i||this.session.setScrollLeft(0));var o=this.session.getScreenLength()*this.lineHeight;this.session.setScrollTop(Math.max(0,Math.min(this.scrollTop,o-this.$size.scrollerHeight)));var u=Math.ceil(n/this.lineHeight)-1,a=Math.max(0,Math.round((this.scrollTop-t)/this.lineHeight)),f=a+u,l,c,h=this.lineHeight;a=e.screenToDocumentRow(a,0);var p=e.getFoldLine(a);p&&(a=p.start.row),l=e.documentToScreenRow(a,0),c=e.getRowLength(a)*h,f=Math.min(e.screenToDocumentRow(f,0),e.getLength()-1),n=this.$size.scrollerHeight+e.getRowLength(f)*h+c,t=this.scrollTop-l*h,this.layerConfig={width:r,padding:this.$padding,firstRow:a,firstRowScreen:l,lastRow:f,lineHeight:h,characterWidth:this.characterWidth,minHeight:n,maxHeight:o,offset:t,height:this.$size.scrollerHeight},this.$gutterLayer.element.style.marginTop=-t+"px",this.content.style.marginTop=-t+"px",this.content.style.width=r+2*this.$padding+"px",this.content.style.height=n+"px",s&&this.onResize(!0)},this.$updateLines=function(){var e=this.$changedLines.firstRow,t=this.$changedLines.lastRow;this.$changedLines=null;var n=this.layerConfig;if(e>n.lastRow+1)return;if(t<n.firstRow)return;if(t===Infinity){this.showGutter&&this.$gutterLayer.update(n),this.$textLayer.update(n);return}return this.$textLayer.updateLines(n,e,t),!0},this.$getLongestLine=function(){var e=this.session.getScreenWidth();return this.$textLayer.showInvisibles&&(e+=1),Math.max(this.$size.scrollerWidth-2*this.$padding,Math.round(e*this.characterWidth))},this.updateFrontMarkers=function(){this.$markerFront.setMarkers(this.session.getMarkers(!0)),this.$loop.schedule(this.CHANGE_MARKER_FRONT)},this.updateBackMarkers=function(){this.$markerBack.setMarkers(this.session.getMarkers()),this.$loop.schedule(this.CHANGE_MARKER_BACK)},this.addGutterDecoration=function(e,t){this.$gutterLayer.addGutterDecoration(e,t)},this.removeGutterDecoration=function(e,t){this.$gutterLayer.removeGutterDecoration(e,t)},this.updateBreakpoints=function(e){this.$loop.schedule(this.CHANGE_GUTTER)},this.setAnnotations=function(e){this.$gutterLayer.setAnnotations(e),this.$loop.schedule(this.CHANGE_GUTTER)},this.updateCursor=function(){this.$loop.schedule(this.CHANGE_CURSOR)},this.hideCursor=function(){this.$cursorLayer.hideCursor()},this.showCursor=function(){this.$cursorLayer.showCursor()},this.scrollSelectionIntoView=function(e,t,n){this.scrollCursorIntoView(e,n),this.scrollCursorIntoView(t,n)},this.scrollCursorIntoView=function(e,t){if(this.$size.scrollerHeight===0)return;var n=this.$cursorLayer.getPixelPosition(e),r=n.left,i=n.top;this.scrollTop>i?(t&&(i-=t*this.$size.scrollerHeight),this.session.setScrollTop(i)):this.scrollTop+this.$size.scrollerHeight<i+this.lineHeight&&(t&&(i+=t*this.$size.scrollerHeight),this.session.setScrollTop(i+this.lineHeight-this.$size.scrollerHeight));var s=this.scrollLeft;s>r?(r<this.$padding+2*this.layerConfig.characterWidth&&(r=0),this.session.setScrollLeft(r)):s+this.$size.scrollerWidth<r+this.characterWidth&&this.session.setScrollLeft(Math.round(r+this.characterWidth-this.$size.scrollerWidth))},this.getScrollTop=function(){return this.session.getScrollTop()},this.getScrollLeft=function(){return this.session.getScrollLeft()},this.getScrollTopRow=function(){return this.scrollTop/this.lineHeight},this.getScrollBottomRow=function(){return Math.max(0,Math.floor((this.scrollTop+this.$size.scrollerHeight)/this.lineHeight)-1)},this.scrollToRow=function(e){this.session.setScrollTop(e*this.lineHeight)},this.alignCursor=function(e,t){typeof e=="number"&&(e={row:e,column:0});var n=this.$cursorLayer.getPixelPosition(e),r=this.$size.scrollerHeight-this.lineHeight,i=n.top-r*(t||0);return this.session.setScrollTop(i),i},this.STEPS=8,this.$calcSteps=function(e,t){var n=0,r=this.STEPS,i=[],s=function(e,t,n){return n*(Math.pow(e-1,3)+1)+t};for(n=0;n<r;++n)i.push(s(n/this.STEPS,e,t-e));return i},this.scrollToLine=function(e,t,n,r){var i=this.$cursorLayer.getPixelPosition({row:e,column:0}),s=i.top;t&&(s-=this.$size.scrollerHeight/2);var o=this.scrollTop;this.session.setScrollTop(s),n!==!1&&this.animateScrolling(o,r)},this.animateScrolling=function(e,t){var n=this.scrollTop;if(this.$animatedScroll&&Math.abs(e-n)<1e5){var r=this,i=r.$calcSteps(e,n);this.$inScrollAnimation=!0,clearInterval(this.$timer),r.session.setScrollTop(i.shift()),this.$timer=setInterval(function(){i.length?(r.session.setScrollTop(i.shift()),r.session.$scrollTop=n):n!=null?(r.session.$scrollTop=-1,r.session.setScrollTop(n),n=null):(r.$timer=clearInterval(r.$timer),r.$inScrollAnimation=!1,t&&t())},10)}},this.scrollToY=function(e){this.scrollTop!==e&&(this.$loop.schedule(this.CHANGE_SCROLL),this.scrollTop=e)},this.scrollToX=function(e){e<0&&(e=0),this.scrollLeft!==e&&(this.scrollLeft=e),this.$loop.schedule(this.CHANGE_H_SCROLL)},this.scrollBy=function(e,t){t&&this.session.setScrollTop(this.session.getScrollTop()+t),e&&this.session.setScrollLeft(this.session.getScrollLeft()+e)},this.isScrollableBy=function(e,t){if(t<0&&this.session.getScrollTop()>0)return!0;if(t>0&&this.session.getScrollTop()+this.$size.scrollerHeight<this.layerConfig.maxHeight)return!0},this.pixelToScreenCoordinates=function(e,t){var n=this.scroller.getBoundingClientRect(),r=(e+this.scrollLeft-n.left-this.$padding)/this.characterWidth,i=Math.floor((t+this.scrollTop-n.top)/this.lineHeight),s=Math.round(r);return{row:i,column:s,side:r-s>0?1:-1}},this.screenToTextCoordinates=function(e,t){var n=this.scroller.getBoundingClientRect(),r=Math.round((e+this.scrollLeft-n.left-this.$padding)/this.characterWidth),i=Math.floor((t+this.scrollTop-n.top)/this.lineHeight);return this.session.screenToDocumentPosition(i,Math.max(r,0))},this.textToScreenCoordinates=function(e,t){var n=this.scroller.getBoundingClientRect(),r=this.session.documentToScreenPosition(e,t),i=this.$padding+Math.round(r.column*this.characterWidth),s=r.row*this.lineHeight;return{pageX:n.left+i-this.scrollLeft,pageY:n.top+s-this.scrollTop}},this.visualizeFocus=function(){i.addCssClass(this.container,"ace_focus")},this.visualizeBlur=function(){i.removeCssClass(this.container,"ace_focus")},this.showComposition=function(e){this.$composition||(this.$composition={keepTextAreaAtCursor:this.$keepTextAreaAtCursor,cssText:this.textarea.style.cssText}),this.$keepTextAreaAtCursor=!0,i.addCssClass(this.textarea,"ace_composition"),this.textarea.style.cssText="",this.$moveTextAreaToCursor()},this.setCompositionText=function(e){this.$moveTextAreaToCursor()},this.hideComposition=function(){if(!this.$composition)return;i.removeCssClass(this.textarea,"ace_composition"),this.$keepTextAreaAtCursor=this.$composition.keepTextAreaAtCursor,this.textarea.style.cssText=this.$composition.cssText,this.$composition=null},this._loadTheme=function(e,t){if(!u.get("packaged"))return t();a.loadScript(u.moduleUrl(e,"theme"),t)},this.setTheme=function(t){function u(e){i.importCssString(e.cssText,e.cssClass,n.container.ownerDocument),n.theme&&i.removeCssClass(n.container,n.theme.cssClass),n.$theme=e.cssClass,n.theme=e,i.addCssClass(n.container,e.cssClass),i.setCssClass(n.container,"ace_dark",e.isDark);var t=e.padding||4;n.$padding&&t!=n.$padding&&n.setPadding(t),n.$size&&(n.$size.width=0,n.onResize()),n._dispatchEvent("themeLoaded",{theme:e})}var n=this;this.$themeValue=t,n._dispatchEvent("themeChange",{theme:t});if(!t||typeof t=="string"){var r=t||"ace/theme/textmate",s;try{s=e(r)}catch(o){}if(s)return u(s);n._loadTheme(r,function(){e([r],function(e){if(n.$themeValue!==t)return;u(e)})})}else u(t)},this.getTheme=function(){return this.$themeValue},this.setStyle=function(t,n){i.setCssClass(this.container,t,n!=0)},this.unsetStyle=function(t){i.removeCssClass(this.container,t)},this.destroy=function(){this.$textLayer.destroy(),this.$cursorLayer.destroy()}}).call(g.prototype),t.VirtualRenderer=g}),ace.define("ace/layer/gutter",["require","exports","module","ace/lib/dom","ace/lib/oop","ace/lib/lang","ace/lib/event_emitter"],function(e,t,n){var r=e("../lib/dom"),i=e("../lib/oop"),s=e("../lib/lang"),o=e("../lib/event_emitter").EventEmitter,u=function(e){this.element=r.createElement("div"),this.element.className="ace_layer ace_gutter-layer",e.appendChild(this.element),this.setShowFoldWidgets(this.$showFoldWidgets),this.gutterWidth=0,this.$annotations=[],this.$updateAnnotations=this.$updateAnnotations.bind(this)};(function(){i.implement(this,o),this.setSession=function(e){this.session&&this.session.removeEventListener("change",this.$updateAnnotations),this.session=e,e.on("change",this.$updateAnnotations)},this.addGutterDecoration=function(e,t){window.console&&console.warn&&console.warn("deprecated use session.addGutterDecoration"),this.session.addGutterDecoration(e,t)},this.removeGutterDecoration=function(e,t){window.console&&console.warn&&console.warn("deprecated use session.removeGutterDecoration"),this.session.removeGutterDecoration(e,t)},this.setAnnotations=function(e){this.$annotations=[];var t,n;for(var r=0;r<e.length;r++){var i=e[r],n=i.row,t=this.$annotations[n];t||(t=this.$annotations[n]={text:[]});var o=i.text;o=o?s.escapeHTML(o):i.html||"",t.text.indexOf(o)===-1&&t.text.push(o);var u=i.type;u=="error"?t.className=" ace_error":u=="warning"&&t.className!=" ace_error"?t.className=" ace_warning":u=="info"&&!t.className&&(t.className=" ace_info")}},this.$updateAnnotations=function(e){if(!this.$annotations.length)return;var t=e.data,n=t.range,r=n.start.row,i=n.end.row-r;if(i!==0)if(t.action=="removeText"||t.action=="removeLines")this.$annotations.splice(r,i+1,null);else{var s=Array(i+1);s.unshift(r,1),this.$annotations.splice.apply(this.$annotations,s)}},this.update=function(e){var t={className:""},n=[],i=e.firstRow,s=e.lastRow,o=this.session.getNextFoldLine(i),u=o?o.start.row:Infinity,a=this.$showFoldWidgets&&this.session.foldWidgets,f=this.session.$breakpoints,l=this.session.$decorations,c=0;for(;;){i>u&&(i=o.end.row+1,o=this.session.getNextFoldLine(i,o),u=o?o.start.row:Infinity);if(i>s)break;var h=this.$annotations[i]||t;n.push("<div class='ace_gutter-cell ",f[i]||"",l[i]||"",h.className,"' style='height:",this.session.getRowLength(i)*e.lineHeight,"px;'>",c=i+1);if(a){var p=a[i];p==null&&(p=a[i]=this.session.getFoldWidget(i)),p&&n.push("<span class='ace_fold-widget ace_",p,p=="start"&&i==u&&i<o.end.row?" ace_closed":" ace_open","' style='height:",e.lineHeight,"px","'></span>")}n.push("</div>"),i++}this.element=r.setInnerHtml(this.element,n.join("")),this.element.style.height=e.minHeight+"px",this.session.$useWrapMode&&(c=this.session.getLength());var d=(""+c).length*e.characterWidth,v=this.$padding||this.$computePadding();d+=v.left+v.right,d!==this.gutterWidth&&(this.gutterWidth=d,this.element.style.width=Math.ceil(this.gutterWidth)+"px",this._emit("changeGutterWidth",d))},this.$showFoldWidgets=!0,this.setShowFoldWidgets=function(e){e?r.addCssClass(this.element,"ace_folding-enabled"):r.removeCssClass(this.element,"ace_folding-enabled"),this.$showFoldWidgets=e,this.$padding=null},this.getShowFoldWidgets=function(){return this.$showFoldWidgets},this.$computePadding=function(){if(!this.element.firstChild)return{left:0,right:0};var e=r.computedStyle(this.element.firstChild);return this.$padding={},this.$padding.left=parseInt(e.paddingLeft)+1,this.$padding.right=parseInt(e.paddingRight),this.$padding},this.getRegion=function(e){var t=this.$padding||this.$computePadding(),n=this.element.getBoundingClientRect();if(e.x<t.left+n.left)return"markers";if(this.$showFoldWidgets&&e.x>n.right-t.right)return"foldWidgets"}}).call(u.prototype),t.Gutter=u}),ace.define("ace/layer/marker",["require","exports","module","ace/range","ace/lib/dom"],function(e,t,n){var r=e("../range").Range,i=e("../lib/dom"),s=function(e){this.element=i.createElement("div"),this.element.className="ace_layer ace_marker-layer",e.appendChild(this.element)};(function(){this.$padding=0,this.setPadding=function(e){this.$padding=e},this.setSession=function(e){this.session=e},this.setMarkers=function(e){this.markers=e},this.update=function(e){var e=e||this.config;if(!e)return;this.config=e;var t=[];for(var n in this.markers){var r=this.markers[n];if(!r.range){r.update(t,this,this.session,e);continue}var s=r.range.clipRows(e.firstRow,e.lastRow);if(s.isEmpty())continue;s=s.toScreenRange(this.session);if(r.renderer){var o=this.$getTop(s.start.row,e),u=this.$padding+s.start.column*e.characterWidth;r.renderer(t,s,u,o,e)}else r.type=="fullLine"?this.drawFullLineMarker(t,s,r.clazz,e):s.isMultiLine()?r.type=="text"?this.drawTextMarker(t,s,r.clazz,e):this.drawMultiLineMarker(t,s,r.clazz,e):this.drawSingleLineMarker(t,s,r.clazz+" ace_start",e)}this.element=i.setInnerHtml(this.element,t.join(""))},this.$getTop=function(e,t){return(e-t.firstRowScreen)*t.lineHeight},this.drawTextMarker=function(e,t,n,i){var s=t.start.row,o=new r(s,t.start.column,s,this.session.getScreenLastRowColumn(s));this.drawSingleLineMarker(e,o,n+" ace_start",i,1,"text"),s=t.end.row,o=new r(s,0,s,t.end.column),this.drawSingleLineMarker(e,o,n,i,0,"text");for(s=t.start.row+1;s<t.end.row;s++)o.start.row=s,o.end.row=s,o.end.column=this.session.getScreenLastRowColumn(s),this.drawSingleLineMarker(e,o,n,i,1,"text")},this.drawMultiLineMarker=function(e,t,n,r,i){var s=this.$padding,o=r.lineHeight,u=this.$getTop(t.start.row,r),a=s+t.start.column*r.characterWidth;e.push("<div class='",n," ace_start' style='","height:",o,"px;","right:0;","top:",u,"px;","left:",a,"px;'></div>"),u=this.$getTop(t.end.row,r);var f=t.end.column*r.characterWidth;e.push("<div class='",n,"' style='","height:",o,"px;","width:",f,"px;","top:",u,"px;","left:",s,"px;'></div>"),o=(t.end.row-t.start.row-1)*r.lineHeight;if(o<0)return;u=this.$getTop(t.start.row+1,r),e.push("<div class='",n,"' style='","height:",o,"px;","right:0;","top:",u,"px;","left:",s,"px;'></div>")},this.drawSingleLineMarker=function(e,t,n,r,i){var s=r.lineHeight,o=(t.end.column+(i||0)-t.start.column)*r.characterWidth,u=this.$getTop(t.start.row,r),a=this.$padding+t.start.column*r.characterWidth;e.push("<div class='",n,"' style='","height:",s,"px;","width:",o,"px;","top:",u,"px;","left:",a,"px;'></div>")},this.drawFullLineMarker=function(e,t,n,r){var i=this.$getTop(t.start.row,r),s=r.lineHeight;t.start.row!=t.end.row&&(s+=this.$getTop(t.end.row,r)-i),e.push("<div class='",n,"' style='","height:",s,"px;","top:",i,"px;","left:0;right:0;'></div>")}}).call(s.prototype),t.Marker=s}),ace.define("ace/layer/text",["require","exports","module","ace/lib/oop","ace/lib/dom","ace/lib/lang","ace/lib/useragent","ace/lib/event_emitter"],function(e,t,n){var r=e("../lib/oop"),i=e("../lib/dom"),s=e("../lib/lang"),o=e("../lib/useragent"),u=e("../lib/event_emitter").EventEmitter,a=function(e){this.element=i.createElement("div"),this.element.className="ace_layer ace_text-layer",e.appendChild(this.element),this.$characterSize={width:0,height:0},this.checkForSizeChanges(),this.$pollSizeChanges()};(function(){r.implement(this,u),this.EOF_CHAR="¶",this.EOL_CHAR="¬",this.TAB_CHAR="→",this.SPACE_CHAR="·",this.$padding=0,this.setPadding=function(e){this.$padding=e,this.element.style.padding="0 "+e+"px"},this.getLineHeight=function(){return this.$characterSize.height||1},this.getCharacterWidth=function(){return this.$characterSize.width||1},this.checkForSizeChanges=function(){var e=this.$measureSizes();if(e&&(this.$characterSize.width!==e.width||this.$characterSize.height!==e.height)){this.$measureNode.style.fontWeight="bold";var t=this.$measureSizes();this.$measureNode.style.fontWeight="",this.$characterSize=e,this.allowBoldFonts=t&&t.width===e.width&&t.height===e.height,this._emit("changeCharacterSize",{data:e})}},this.$pollSizeChanges=function(){var e=this;this.$pollSizeChangesTimer=setInterval(function(){e.checkForSizeChanges()},500)},this.$fontStyles={fontFamily:1,fontSize:1,fontWeight:1,fontStyle:1,lineHeight:1},this.$measureSizes=o.isIE||o.isOldGecko?function(){var e=1e3;if(!this.$measureNode){var t=this.$measureNode=i.createElement("div"),n=t.style;n.width=n.height="auto",n.left=n.top=-e*40+"px",n.visibility="hidden",n.position="fixed",n.overflow="visible",n.whiteSpace="nowrap",t.innerHTML=s.stringRepeat("Xy",e);if(this.element.ownerDocument.body)this.element.ownerDocument.body.appendChild(t);else{var r=this.element.parentNode;while(!i.hasCssClass(r,"ace_editor"))r=r.parentNode;r.appendChild(t)}}if(!this.element.offsetWidth)return null;var n=this.$measureNode.style,o=i.computedStyle(this.element);for(var u in this.$fontStyles)n[u]=o[u];var a={height:this.$measureNode.offsetHeight,width:this.$measureNode.offsetWidth/(e*2)};return a.width==0||a.height==0?null:a}:function(){if(!this.$measureNode){var e=this.$measureNode=i.createElement("div"),t=e.style;t.width=t.height="auto",t.left=t.top="-100px",t.visibility="hidden",t.position="fixed",t.overflow="visible",t.whiteSpace="nowrap",e.innerHTML="X";var n=this.element.parentNode;while(n&&!i.hasCssClass(n,"ace_editor"))n=n.parentNode;if(!n)return this.$measureNode=null;n.appendChild(e)}var r=this.$measureNode.getBoundingClientRect(),s={height:r.height,width:r.width};return s.width==0||s.height==0?null:s},this.setSession=function(e){this.session=e,this.$computeTabString()},this.showInvisibles=!1,this.setShowInvisibles=function(e){return this.showInvisibles==e?!1:(this.showInvisibles=e,this.$computeTabString(),!0)},this.displayIndentGuides=!0,this.setDisplayIndentGuides=function(e){return this.displayIndentGuides==e?!1:(this.displayIndentGuides=e,this.$computeTabString(),!0)},this.$tabStrings=[],this.onChangeTabSize=this.$computeTabString=function(){var e=this.session.getTabSize();this.tabSize=e;var t=this.$tabStrings=[0];for(var n=1;n<e+1;n++)this.showInvisibles?t.push("<span class='ace_invisible'>"+this.TAB_CHAR+Array(n).join("&#160;")+"</span>"):t.push((new Array(n+1)).join("&#160;"));if(this.displayIndentGuides){this.$indentGuideRe=/\s\S| \t|\t |\s$/;var r="ace_indent-guide",i=Array(this.tabSize+1).join("&#160;"),s=i;this.showInvisibles&&(r+=" ace_invisible",s=this.TAB_CHAR+i.substr(6)),this.$tabStrings[" "]="<span class='"+r+"'>"+i+"</span>",this.$tabStrings[" "]="<span class='"+r+"'>"+s+"</span>"}},this.updateLines=function(e,t,n){(this.config.lastRow!=e.lastRow||this.config.firstRow!=e.firstRow)&&this.scrollLines(e),this.config=e;var r=Math.max(t,e.firstRow),s=Math.min(n,e.lastRow),o=this.element.childNodes,u=0;for(var a=e.firstRow;a<r;a++){var f=this.session.getFoldLine(a);if(f){if(f.containsRow(r)){r=f.start.row;break}a=f.end.row}u++}var a=r,f=this.session.getNextFoldLine(a),l=f?f.start.row:Infinity;for(;;){a>l&&(a=f.end.row+1,f=this.session.getNextFoldLine(a,f),l=f?f.start.row:Infinity);if(a>s)break;var c=o[u++];if(c){var h=[];this.$renderLine(h,a,!this.$useLineGroups(),a==l?f:!1),i.setInnerHtml(c,h.join(""))}a++}},this.scrollLines=function(e){var t=this.config;this.config=e;if(!t||t.lastRow<e.firstRow)return this.update(e);if(e.lastRow<t.firstRow)return this.update(e);var n=this.element;if(t.firstRow<e.firstRow)for(var r=this.session.getFoldedRowCount(t.firstRow,e.firstRow-1);r>0;r--)n.removeChild(n.firstChild);if(t.lastRow>e.lastRow)for(var r=this.session.getFoldedRowCount(e.lastRow+1,t.lastRow);r>0;r--)n.removeChild(n.lastChild);if(e.firstRow<t.firstRow){var i=this.$renderLinesFragment(e,e.firstRow,t.firstRow-1);n.firstChild?n.insertBefore(i,n.firstChild):n.appendChild(i)}if(e.lastRow>t.lastRow){var i=this.$renderLinesFragment(e,t.lastRow+1,e.lastRow);n.appendChild(i)}},this.$renderLinesFragment=function(e,t,n){var r=this.element.ownerDocument.createDocumentFragment(),s=t,o=this.session.getNextFoldLine(s),u=o?o.start.row:Infinity;for(;;){s>u&&(s=o.end.row+1,o=this.session.getNextFoldLine(s,o),u=o?o.start.row:Infinity);if(s>n)break;var a=i.createElement("div"),f=[];this.$renderLine(f,s,!1,s==u?o:!1),a.innerHTML=f.join("");if(this.$useLineGroups())a.className="ace_line_group",r.appendChild(a);else{var l=a.childNodes;while(l.length)r.appendChild(l[0])}s++}return r},this.update=function(e){this.config=e;var t=[],n=e.firstRow,r=e.lastRow,s=n,o=this.session.getNextFoldLine(s),u=o?o.start.row:Infinity;for(;;){s>u&&(s=o.end.row+1,o=this.session.getNextFoldLine(s,o),u=o?o.start.row:Infinity);if(s>r)break;this.$useLineGroups()&&t.push("<div class='ace_line_group'>"),this.$renderLine(t,s,!1,s==u?o:!1),this.$useLineGroups()&&t.push("</div>"),s++}this.element=i.setInnerHtml(this.element,t.join(""))},this.$textToken={text:!0,rparen:!0,lparen:!0},this.$renderToken=function(e,t,n,r){var i=this,s=/\t|&|<|( +)|([\x00-\x1f\x80-\xa0\u1680\u180E\u2000-\u200f\u2028\u2029\u202F\u205F\u3000\uFEFF])|[\u1100-\u115F\u11A3-\u11A7\u11FA-\u11FF\u2329-\u232A\u2E80-\u2E99\u2E9B-\u2EF3\u2F00-\u2FD5\u2FF0-\u2FFB\u3000-\u303E\u3041-\u3096\u3099-\u30FF\u3105-\u312D\u3131-\u318E\u3190-\u31BA\u31C0-\u31E3\u31F0-\u321E\u3220-\u3247\u3250-\u32FE\u3300-\u4DBF\u4E00-\uA48C\uA490-\uA4C6\uA960-\uA97C\uAC00-\uD7A3\uD7B0-\uD7C6\uD7CB-\uD7FB\uF900-\uFAFF\uFE10-\uFE19\uFE30-\uFE52\uFE54-\uFE66\uFE68-\uFE6B\uFF01-\uFF60\uFFE0-\uFFE6]/g,o=function(e,n,r,s,o){if(n)return(new Array(e.length+1)).join("&#160;");if(e=="&")return"&#38;";if(e=="<")return"&#60;";if(e==" "){var u=i.session.getScreenTabSize(t+s);return t+=u-1,i.$tabStrings[u]}if(e==" "){var a=i.showInvisibles?"ace_cjk ace_invisible":"ace_cjk",f=i.showInvisibles?i.SPACE_CHAR:"";return t+=1,"<span class='"+a+"' style='width:"+i.config.characterWidth*2+"px'>"+f+"</span>"}return r?"<span class='ace_invisible ace_invalid'>"+i.SPACE_CHAR+"</span>":(t+=1,"<span class='ace_cjk' style='width:"+i.config.characterWidth*2+"px'>"+e+"</span>")},u=r.replace(s,o);if(!this.$textToken[n.type]){var a="ace_"+n.type.replace(/\./g," ace_"),f="";n.type=="fold"&&(f=" style='width:"+n.value.length*this.config.characterWidth+"px;' "),e.push("<span class='",a,"'",f,">",u,"</span>")}else e.push(u);return t+r.length},this.renderIndentGuide=function(e,t){var n=t.search(this.$indentGuideRe);return n<=0?t:t[0]==" "?(n-=n%this.tabSize,e.push(Array(n/this.tabSize+1).join(this.$tabStrings[" "])),t.substr(n)):t[0]==" "?(e.push(Array(n+1).join(this.$tabStrings[" "])),t.substr(n)):t},this.$renderWrappedLine=function(e,t,n,r){var i=0,s=0,o=n[0],u=0;for(var a=0;a<t.length;a++){var f=t[a],l=f.value;if(a==0&&this.displayIndentGuides){i=l.length,l=this.renderIndentGuide(e,l);if(!l)continue;i-=l.length}if(i+l.length<o)u=this.$renderToken(e,u,f,l),i+=l.length;else{while(i+l.length>=o)u=this.$renderToken(e,u,f,l.substring(0,o-i)),l=l.substring(o-i),i=o,r||e.push("</div>","<div class='ace_line' style='height:",this.config.lineHeight,"px'>"),s++,u=0,o=n[s]||Number.MAX_VALUE;l.length!=0&&(i+=l.length,u=this.$renderToken(e,u,f,l))}}},this.$renderSimpleLine=function(e,t){var n=0,r=t[0],i=r.value;this.displayIndentGuides&&(i=this.renderIndentGuide(e,i)),i&&(n=this.$renderToken(e,n,r,i));for(var s=1;s<t.length;s++)r=t[s],i=r.value,n=this.$renderToken(e,n,r,i)},this.$renderLine=function(e,t,n,r){!r&&r!=0&&(r=this.session.getFoldLine(t));if(r)var i=this.$getFoldLineTokens(t,r);else var i=this.session.getTokens(t);n||e.push("<div class='ace_line' style='height:",this.config.lineHeight,"px'>");if(i.length){var s=this.session.getRowSplitData(t);s&&s.length?this.$renderWrappedLine(e,i,s,n):this.$renderSimpleLine(e,i)}this.showInvisibles&&(r&&(t=r.end.row),e.push("<span class='ace_invisible'>",t==this.session.getLength()-1?this.EOF_CHAR:this.EOL_CHAR,"</span>")),n||e.push("</div>")},this.$getFoldLineTokens=function(e,t){function i(e,t,n){var i=0,s=0;while(s+e[i].value.length<t){s+=e[i].value.length,i++;if(i==e.length)return}if(s!=t){var o=e[i].value.substring(t-s);o.length>n-t&&(o=o.substring(0,n-t)),r.push({type:e[i].type,value:o}),s=t+o.length,i+=1}while(s<n&&i<e.length){var o=e[i].value;o.length+s>n?r.push({type:e[i].type,value:o.substring(0,n-s)}):r.push(e[i]),s+=o.length,i+=1}}var n=this.session,r=[],s=n.getTokens(e);return t.walk(function(e,t,o,u,a){e!=null?r.push({type:"fold",value:e}):(a&&(s=n.getTokens(t)),s.length&&i(s,u,o))},t.end.row,this.session.getLine(t.end.row).length),r},this.$useLineGroups=function(){return this.session.getUseWrapMode()},this.destroy=function(){clearInterval(this.$pollSizeChangesTimer),this.$measureNode&&this.$measureNode.parentNode.removeChild(this.$measureNode),delete this.$measureNode}}).call(a.prototype),t.Text=a}),ace.define("ace/layer/cursor",["require","exports","module","ace/lib/dom"],function(e,t,n){var r=e("../lib/dom"),i=function(e){this.element=r.createElement("div"),this.element.className="ace_layer ace_cursor-layer",e.appendChild(this.element),this.isVisible=!1,this.isBlinking=!0,this.blinkInterval=1e3,this.smoothBlinking=!1,this.cursors=[],this.cursor=this.addCursor(),r.addCssClass(this.element,"ace_hidden-cursors")};(function(){this.$padding=0,this.setPadding=function(e){this.$padding=e},this.setSession=function(e){this.session=e},this.setBlinking=function(e){e!=this.isBlinking&&(this.isBlinking=e,this.restartTimer())},this.setBlinkInterval=function(e){e!=this.blinkInterval&&(this.blinkInterval=e,this.restartTimer())},this.setSmoothBlinking=function(e){e!=this.smoothBlinking&&(this.smoothBlinking=e,e?r.addCssClass(this.element,"ace_smooth-blinking"):r.removeCssClass(this.element,"ace_smooth-blinking"),this.restartTimer())},this.addCursor=function(){var e=r.createElement("div");return e.className="ace_cursor",this.element.appendChild(e),this.cursors.push(e),e},this.removeCursor=function(){if(this.cursors.length>1){var e=this.cursors.pop();return e.parentNode.removeChild(e),e}},this.hideCursor=function(){this.isVisible=!1,r.addCssClass(this.element,"ace_hidden-cursors"),this.restartTimer()},this.showCursor=function(){this.isVisible=!0,r.removeCssClass(this.element,"ace_hidden-cursors"),this.restartTimer()},this.restartTimer=function(){clearInterval(this.intervalId),clearTimeout(this.timeoutId),this.smoothBlinking&&r.removeCssClass(this.element,"ace_smooth-blinking");for(var e=this.cursors.length;e--;)this.cursors[e].style.opacity="";if(!this.isBlinking||!this.blinkInterval||!this.isVisible)return;this.smoothBlinking&&setTimeout(function(){r.addCssClass(this.element,"ace_smooth-blinking")}.bind(this));var t=function(){this.timeoutId=setTimeout(function(){for(var e=this.cursors.length;e--;)this.cursors[e].style.opacity=0}.bind(this),.6*this.blinkInterval)}.bind(this);this.intervalId=setInterval(function(){for(var e=this.cursors.length;e--;)this.cursors[e].style.opacity="";t()}.bind(this),this.blinkInterval),t()},this.getPixelPosition=function(e,t){if(!this.config||!this.session)return{left:0,top:0};e||(e=this.session.selection.getCursor());var n=this.session.documentToScreenPosition(e),r=this.$padding+n.column*this.config.characterWidth,i=(n.row-(t?this.config.firstRowScreen:0))*this.config.lineHeight;return{left:r,top:i}},this.update=function(e){this.config=e;var t=this.session.$selectionMarkers,n=0,r=0;if(t===undefined||t.length===0)t=[{cursor:null}];for(var n=t.length;n--;){var i=this.getPixelPosition(t[n].cursor,!0);if((i.top>e.height+e.offset||i.top<-e.offset)&&n>1)continue;var s=(this.cursors[r++]||this.addCursor()).style;s.left=i.left+"px",s.top=i.top+"px",s.width=e.characterWidth+"px",s.height=e.lineHeight+"px"}while(this.cursors.length>r)this.removeCursor();var o=this.session.getOverwrite();this.$setOverwrite(o),this.$pixelPos=i,this.restartTimer()},this.$setOverwrite=function(e){e!=this.overwrite&&(this.overwrite=e,e?r.addCssClass(this.element,"ace_overwrite-cursors"):r.removeCssClass(this.element,"ace_overwrite-cursors"))},this.destroy=function(){clearInterval(this.intervalId),clearTimeout(this.timeoutId)}}).call(i.prototype),t.Cursor=i}),ace.define("ace/scrollbar",["require","exports","module","ace/lib/oop","ace/lib/dom","ace/lib/event","ace/lib/event_emitter"],function(e,t,n){var r=e("./lib/oop"),i=e("./lib/dom"),s=e("./lib/event"),o=e("./lib/event_emitter").EventEmitter,u=function(e){this.element=i.createElement("div"),this.element.className="ace_scrollbar",this.inner=i.createElement("div"),this.inner.className="ace_scrollbar-inner",this.element.appendChild(this.inner),e.appendChild(this.element),this.width=i.scrollbarWidth(e.ownerDocument),this.element.style.width=(this.width||15)+5+"px",s.addListener(this.element,"scroll",this.onScroll.bind(this))};(function(){r.implement(this,o),this.onScroll=function(){this.skipEvent||(this.scrollTop=this.element.scrollTop,this._emit("scroll",{data:this.scrollTop})),this.skipEvent=!1},this.getWidth=function(){return this.width},this.setHeight=function(e){this.element.style.height=e+"px"},this.setInnerHeight=function(e){this.inner.style.height=e+"px"},this.setScrollTop=function(e){this.scrollTop!=e&&(this.skipEvent=!0,this.scrollTop=this.element.scrollTop=e)}}).call(u.prototype),t.ScrollBar=u}),ace.define("ace/renderloop",["require","exports","module","ace/lib/event"],function(e,t,n){var r=e("./lib/event"),i=function(e,t){this.onRender=e,this.pending=!1,this.changes=0,this.window=t||window};(function(){this.schedule=function(e){this.changes=this.changes|e;if(!this.pending){this.pending=!0;var t=this;r.nextFrame(function(){t.pending=!1;var e;while(e=t.changes)t.changes=0,t.onRender(e)},this.window)}}}).call(i.prototype),t.RenderLoop=i}),ace.define("ace/multi_select",["require","exports","module","ace/range_list","ace/range","ace/selection","ace/mouse/multi_select_handler","ace/lib/event","ace/lib/lang","ace/commands/multi_select_commands","ace/search","ace/edit_session","ace/editor"],function(e,t,n){function h(e,t,n){return c.$options.wrap=!0,c.$options.needle=t,c.$options.backwards=n==-1,c.find(e)}function v(e,t){return e.row==t.row&&e.column==t.column}function m(e){e.$onAddRange=e.$onAddRange.bind(e),e.$onRemoveRange=e.$onRemoveRange.bind(e),e.$onMultiSelect=e.$onMultiSelect.bind(e),e.$onSingleSelect=e.$onSingleSelect.bind(e),t.onSessionChange.call(e,e),e.on("changeSession",t.onSessionChange.bind(e)),e.on("mousedown",o),e.commands.addCommands(f.defaultCommands),g(e)}function g(e){function i(){n&&(r.style.cursor="",n=!1)}var t=e.textInput.getElement(),n=!1,r=e.renderer.content;u.addListener(t,"keydown",function(e){e.keyCode==18&&!(e.ctrlKey||e.shiftKey||e.metaKey)?n||(r.style.cursor="crosshair",n=!0):n&&(r.style.cursor="")}),u.addListener(t,"keyup",i),u.addListener(t,"blur",i)}var r=e("./range_list").RangeList,i=e("./range").Range,s=e("./selection").Selection,o=e("./mouse/multi_select_handler").onMouseDown,u=e("./lib/event"),a=e("./lib/lang"),f=e("./commands/multi_select_commands");t.commands=f.defaultCommands.concat(f.multiSelectCommands);var l=e("./search").Search,c=new l,p=e("./edit_session").EditSession;(function(){this.getSelectionMarkers=function(){return this.$selectionMarkers}}).call(p.prototype),function(){this.ranges=null,this.rangeList=null,this.addRange=function(e,t){if(!e)return;if(!this.inMultiSelectMode&&this.rangeCount==0){var n=this.toOrientedRange();if(e.intersects(n))return t||this.fromOrientedRange(e);this.rangeList.add(n),this.$onAddRange(n)}e.cursor||(e.cursor=e.end);var r=this.rangeList.add(e);return this.$onAddRange(e),r.length&&this.$onRemoveRange(r),this.rangeCount>1&&!this.inMultiSelectMode&&(this._emit("multiSelect"),this.inMultiSelectMode=!0,this.session.$undoSelect=!1,this.rangeList.attach(this.session)),t||this.fromOrientedRange(e)},this.toSingleRange=function(e){e=e||this.ranges[0];var t=this.rangeList.removeAll();t.length&&this.$onRemoveRange(t),e&&this.fromOrientedRange(e)},this.substractPoint=function(e){var t=this.rangeList.substractPoint(e);if(t)return this.$onRemoveRange(t),t[0]},this.mergeOverlappingRanges=function(){var e=this.rangeList.merge();e.length?this.$onRemoveRange(e):this.ranges[0]&&this.fromOrientedRange(this.ranges[0])},this.$onAddRange=function(e){this.rangeCount=this.rangeList.ranges.length,this.ranges.unshift(e),this._emit("addRange",{range:e})},this.$onRemoveRange=function(e){this.rangeCount=this.rangeList.ranges.length;if(this.rangeCount==1&&this.inMultiSelectMode){var t=this.rangeList.ranges.pop();e.push(t),this.rangeCount=0}for(var n=e.length;n--;){var r=this.ranges.indexOf(e[n]);this.ranges.splice(r,1)}this._emit("removeRange",{ranges:e}),this.rangeCount==0&&this.inMultiSelectMode&&(this.inMultiSelectMode=!1,this._emit("singleSelect"),this.session.$undoSelect=!0,this.rangeList.detach(this.session)),t=t||this.ranges[0],t&&!t.isEqual(this.getRange())&&this.fromOrientedRange(t)},this.$initRangeList=function(){if(this.rangeList)return;this.rangeList=new r,this.ranges=[],this.rangeCount=0},this.getAllRanges=function(){return this.rangeList.ranges.concat()},this.splitIntoLines=function(){if(this.rangeCount>1){var e=this.rangeList.ranges,t=e[e.length-1],n=i.fromPoints(e[0].start,t.end);this.toSingleRange(),this.setSelectionRange(n,t.cursor==t.start)}else{var n=this.getRange(),r=this.isBackwards(),s=n.start.row,o=n.end.row;if(s==o){if(r)var u=n.end,a=n.start;else var u=n.start,a=n.end;this.addRange(i.fromPoints(a,a)),this.addRange(i.fromPoints(u,u));return}var f=[],l=this.getLineRange(s,!0);l.start.column=n.start.column,f.push(l);for(var c=s+1;c<o;c++)f.push(this.getLineRange(c,!0));l=this.getLineRange(o,!0),l.end.column=n.end.column,f.push(l),f.forEach(this.addRange,this)}},this.toggleBlockSelection=function(){if(this.rangeCount>1){var e=this.rangeList.ranges,t=e[e.length-1],n=i.fromPoints(e[0].start,t.end);this.toSingleRange(),this.setSelectionRange(n,t.cursor==t.start)}else{var r=this.session.documentToScreenPosition(this.selectionLead),s=this.session.documentToScreenPosition(this.selectionAnchor),o=this.rectangularRangeBlock(r,s);o.forEach(this.addRange,this)}},this.rectangularRangeBlock=function(e,t,n){var r=[],s=e.column<t.column;if(s)var o=e.column,u=t.column;else var o=t.column,u=e.column;var a=e.row<t.row;if(a)var f=e.row,l=t.row;else var f=t.row,l=e.row;o<0&&(o=0),f<0&&(f=0),f==l&&(n=!0);for(var c=f;c<=l;c++){var h=i.fromPoints(this.session.screenToDocumentPosition(c,o),this.session.screenToDocumentPosition(c,u));if(h.isEmpty()){if(p&&v(h.end,p))break;var p=h.end}h.cursor=s?h.start:h.end,r.push(h)}a&&r.reverse();if(!n){var d=r.length-1;while(r[d].isEmpty()&&d>0)d--;if(d>0){var m=0;while(r[m].isEmpty())m++}for(var g=d;g>=m;g--)r[g].isEmpty()&&r.splice(g,1)}return r}}.call(s.prototype);var d=e("./editor").Editor;(function(){this.updateSelectionMarkers=function(){this.renderer.updateCursor(),this.renderer.updateBackMarkers()},this.addSelectionMarker=function(e){e.cursor||(e.cursor=e.end);var t=this.getSelectionStyle();return e.marker=this.session.addMarker(e,"ace_selection",t),this.session.$selectionMarkers.push(e),this.session.selectionMarkerCount=this.session.$selectionMarkers.length,e},this.removeSelectionMarker=function(e){if(!e.marker)return;this.session.removeMarker(e.marker);var t=this.session.$selectionMarkers.indexOf(e);t!=-1&&this.session.$selectionMarkers.splice(t,1),this.session.selectionMarkerCount=this.session.$selectionMarkers.length},this.removeSelectionMarkers=function(e){var t=this.session.$selectionMarkers;for(var n=e.length;n--;){var r=e[n];if(!r.marker)continue;this.session.removeMarker(r.marker);var i=t.indexOf(r);i!=-1&&t.splice(i,1)}this.session.selectionMarkerCount=t.length},this.$onAddRange=function(e){this.addSelectionMarker(e.range),this.renderer.updateCursor(),this.renderer.updateBackMarkers()},this.$onRemoveRange=function(e){this.removeSelectionMarkers(e.ranges),this.renderer.updateCursor(),this.renderer.updateBackMarkers()},this.$onMultiSelect=function(e){if(this.inMultiSelectMode)return;this.inMultiSelectMode=!0,this.setStyle("ace_multiselect"),this.keyBinding.addKeyboardHandler(f.keyboardHandler),this.commands.on("exec",this.$onMultiSelectExec),this.renderer.updateCursor(),this.renderer.updateBackMarkers()},this.$onSingleSelect=function(e){if(this.session.multiSelect.inVirtualMode)return;this.inMultiSelectMode=!1,this.unsetStyle("ace_multiselect"),this.keyBinding.removeKeyboardHandler(f.keyboardHandler),this.commands.removeEventListener("exec",this.$onMultiSelectExec),this.renderer.updateCursor(),this.renderer.updateBackMarkers()},this.$onMultiSelectExec=function(e){var t=e.command,n=e.editor;if(!n.multiSelect)return;t.multiSelectAction?t.multiSelectAction=="forEach"?n.forEachSelection(t,e.args):t.multiSelectAction=="single"?(n.exitMultiSelectMode(),t.exec(n,e.args||{})):t.multiSelectAction(n,e.args||{}):(t.exec(n,e.args||{}),n.multiSelect.addRange(n.multiSelect.toOrientedRange()),n.multiSelect.mergeOverlappingRanges()),e.preventDefault()},this.forEachSelection=function(e,t){if(this.inVirtualSelectionMode)return;var n=this.session,r=this.selection,i=r.rangeList,o=r._eventRegistry;r._eventRegistry={};var u=new s(n);this.inVirtualSelectionMode=!0;for(var a=i.ranges.length;a--;)u.fromOrientedRange(i.ranges[a]),this.selection=n.selection=u,e.exec(this,t||{}),u.toOrientedRange(i.ranges[a]);u.detach(),this.selection=n.selection=r,this.inVirtualSelectionMode=!1,r._eventRegistry=o,r.mergeOverlappingRanges(),this.onCursorChange(),this.onSelectionChange()},this.exitMultiSelectMode=function(){if(this.inVirtualSelectionMode)return;this.multiSelect.toSingleRange()},this.getCopyText=function(){var e="";if(this.inMultiSelectMode){var t=this.multiSelect.rangeList.ranges;e=[];for(var n=0;n<t.length;n++)e.push(this.session.getTextRange(t[n]));e=e.join(this.session.getDocument().getNewLineCharacter())}else this.selection.isEmpty()||(e=this.session.getTextRange(this.getSelectionRange()));return e},this.onPaste=function(e){if(this.$readOnly)return;this._emit("paste",e);if(!this.inMultiSelectMode)return this.insert(e);var t=e.split(/\r\n|\r|\n/),n=this.selection.rangeList.ranges;if(t.length>n.length||t.length<=2||!t[1])return this.commands.exec("insertstring",this,e);for(var r=n.length;r--;){var i=n[r];i.isEmpty()||this.session.remove(i),this.session.insert(i.start,t[r])}},this.findAll=function(e,t,n){t=t||{},t.needle=e||t.needle,this.$search.set(t);var r=this.$search.findAll(this.session);if(!r.length)return 0;this.$blockScrolling+=1;var i=this.multiSelect;n||i.toSingleRange(r[0]);for(var s=r.length;s--;)i.addRange(r[s],!0);return this.$blockScrolling-=1,r.length},this.selectMoreLines=function(e,t){var n=this.selection.toOrientedRange(),r=n.cursor==n.end,s=this.session.documentToScreenPosition(n.cursor);this.selection.$desiredColumn&&(s.column=this.selection.$desiredColumn);var o=this.session.screenToDocumentPosition(s.row+e,s.column);if(!n.isEmpty())var u=this.session.documentToScreenPosition(r?n.end:n.start),a=this.session.screenToDocumentPosition(u.row+e,u.column);else var a=o;if(r){var f=i.fromPoints(o,a);f.cursor=f.start}else{var f=i.fromPoints(a,o);f.cursor=f.end}f.desiredColumn=s.column;if(!this.selection.inMultiSelectMode)this.selection.addRange(n);else if(t)var l=n.cursor;this.selection.addRange(f),l&&this.selection.substractPoint(l)},this.transposeSelections=function(e){var t=this.session,n=t.multiSelect,r=n.ranges;for(var i=r.length;i--;){var s=r[i];if(s.isEmpty()){var o=t.getWordRange(s.start.row,s.start.column);s.start.row=o.start.row,s.start.column=o.start.column,s.end.row=o.end.row,s.end.column=o.end.column}}n.mergeOverlappingRanges();var u=[];for(var i=r.length;i--;){var s=r[i];u.unshift(t.getTextRange(s))}e<0?u.unshift(u.pop()):u.push(u.shift());for(var i=r.length;i--;){var s=r[i],o=s.clone();t.replace(s,u[i]),s.start.row=o.start.row,s.start.column=o.start.column}},this.selectMore=function(e,t){var n=this.session,r=n.multiSelect,i=r.toOrientedRange();if(i.isEmpty()){var i=n.getWordRange(i.start.row,i.start.column);i.cursor=i.end,this.multiSelect.addRange(i)}var s=n.getTextRange(i),o=h(n,s,e);o&&(o.cursor=e==-1?o.start:o.end,this.multiSelect.addRange(o)),t&&this.multiSelect.substractPoint(i.cursor)},this.alignCursors=function(){var e=this.session,t=e.multiSelect,n=t.ranges;if(!n.length){var r=this.selection.getRange(),s=r.start.row,o=r.end.row,u=this.session.doc.removeLines(s,o);u=this.$reAlignText(u),this.session.doc.insertLines(s,u),r.start.column=0,r.end.column=u[u.length-1].length,this.selection.setRange(r)}else{var f=-1,l=n.filter(function(e){if(e.cursor.row==f)return!0;f=e.cursor.row});t.$onRemoveRange(l);var c=0,h=Infinity,p=n.map(function(t){var n=t.cursor,r=e.getLine(n.row),i=r.substr(n.column).search(/\S/g);return i==-1&&(i=0),n.column>c&&(c=n.column),i<h&&(h=i),i});n.forEach(function(t,n){var r=t.cursor,s=c-r.column,o=p[n]-h;s>o?e.insert(r,a.stringRepeat(" ",s-o)):e.remove(new i(r.row,r.column,r.row,r.column-s+o)),t.start.column=t.end.column=c,t.start.row=t.end.row=r.row,t.cursor=t.end}),t.fromOrientedRange(n[0]),this.renderer.updateCursor(),this.renderer.updateBackMarkers()}},this.$reAlignText=function(e){function o(e,t){return Array(e+1).join(t)}function u(e){return e[2]?o(r," ")+e[2]+o(i-e[2].length+s," ")+e[4].replace(/^([=:])\s+/,"$1 "):e[0]}function a(e){return e[2]?o(r+i-e[2].length," ")+e[2]+o(s," ")+e[4].replace(/^([=:])\s+/,"$1 "):e[0]}function f(e){return e[2]?o(r," ")+e[2]+o(s," ")+e[4].replace(/^([=:])\s+/,"$1 "):e[0]}var t=!0,n=!0,r,i,s;return e.map(function(e){var o=e.match(/(\s*)(.*?)(\s*)([=:].*)/);return o?r==null?(r=o[1].length,i=o[2].length,s=o[3].length,o):(r+i+s!=o[1].length+o[2].length+o[3].length&&(n=!1),r!=o[1].length&&(t=!1),r>o[1].length&&(r=o[1].length),i<o[2].length&&(i=o[2].length),s>o[3].length&&(s=o[3].length),o):[e]}).map(t?n?a:u:f)}}).call(d.prototype),t.onSessionChange=function(e){var t=e.session;t.multiSelect||(t.$selectionMarkers=[],t.selection.$initRangeList(),t.multiSelect=t.selection),this.multiSelect=t.multiSelect;var n=e.oldSession;n&&(n.multiSelect&&n.multiSelect.editor==this&&(n.multiSelect.editor=null),t.multiSelect.removeEventListener("addRange",this.$onAddRange),t.multiSelect.removeEventListener("removeRange",this.$onRemoveRange),t.multiSelect.removeEventListener("multiSelect",this.$onMultiSelect),t.multiSelect.removeEventListener("singleSelect",this.$onSingleSelect)),t.multiSelect.on("addRange",this.$onAddRange),t.multiSelect.on("removeRange",this.$onRemoveRange),t.multiSelect.on("multiSelect",this.$onMultiSelect),t.multiSelect.on("singleSelect",this.$onSingleSelect),this.inMultiSelectMode!=t.selection.inMultiSelectMode&&(t.selection.inMultiSelectMode?this.$onMultiSelect():this.$onSingleSelect())},t.MultiSelect=m}),ace.define("ace/range_list",["require","exports","module"],function(e,t,n){var r=function(){this.ranges=[]};(function(){this.comparePoints=function(e,t){return e.row-t.row||e.column-t.column},this.pointIndex=function(e,t){var n=this.ranges;for(var r=t||0;r<n.length;r++){var i=n[r],s=this.comparePoints(e,i.end);if(s>0)continue;return s==0?r:(s=this.comparePoints(e,i.start),s>=0?r:-r-1)}return-r-1},this.add=function(e){var t=this.pointIndex(e.start);t<0&&(t=-t-1);var n=this.pointIndex(e.end,t);return n<0?n=-n-1:n++,this.ranges.splice(t,n-t,e)},this.addList=function(e){var t=[];for(var n=e.length;n--;)t.push.call(t,this.add(e[n]));return t},this.substractPoint=function(e){var t=this.pointIndex(e);if(t>=0)return this.ranges.splice(t,1)},this.merge=function(){var e=[],t=this.ranges,n=t[0],r;for(var i=1;i<t.length;i++){r=n,n=t[i];var s=this.comparePoints(r.end,n.start);if(s<0)continue;if(s==0&&!r.isEmpty()&&!n.isEmpty())continue;this.comparePoints(r.end,n.end)<0&&(r.end.row=n.end.row,r.end.column=n.end.column),t.splice(i,1),e.push(n),n=r,i--}return e},this.contains=function(e,t){return this.pointIndex({row:e,column:t})>=0},this.containsPoint=function(e){return this.pointIndex(e)>=0},this.rangeAtPoint=function(e){var t=this.pointIndex(e);if(t>=0)return this.ranges[t]},this.clipRows=function(e,t){var n=this.ranges;if(n[0].start.row>t||n[n.length-1].start.row<e)return[];var r=this.pointIndex({row:e,column:0});r<0&&(r=-r-1);var i=this.pointIndex({row:t,column:0},r);i<0&&(i=-i-1);var s=[];for(var o=r;o<i;o++)s.push(n[o]);return s},this.removeAll=function(){return this.ranges.splice(0,this.ranges.length)},this.attach=function(e){this.session&&this.detach(),this.session=e,this.onChange=this.$onChange.bind(this),this.session.on("change",this.onChange)},this.detach=function(){if(!this.session)return;this.session.removeListener("change",this.onChange),this.session=null},this.$onChange=function(e){var t=e.data.range;if(e.data.action[0]=="i")var n=t.start,r=t.end;else var r=t.start,n=t.end;var i=n.row,s=r.row,o=s-i,u=-n.column+r.column,a=this.ranges;for(var f=0,l=a.length;f<l;f++){var c=a[f];if(c.end.row<i)continue;if(c.start.row>i)break;c.start.row==i&&c.start.column>=n.column&&(c.start.column+=u,c.start.row+=o),c.end.row==i&&c.end.column>=n.column&&(c.end.column+=u,c.end.row+=o)}if(o!=0&&f<l)for(;f<l;f++){var c=a[f];c.start.row+=o,c.end.row+=o}}}).call(r.prototype),t.RangeList=r}),ace.define("ace/mouse/multi_select_handler",["require","exports","module","ace/lib/event"],function(e,t,n){function i(e,t){return e.row==t.row&&e.column==t.column}function s(e){var t=e.domEvent,n=t.altKey,s=t.shiftKey,o=e.getAccelKey(),u=e.getButton();if(e.editor.inMultiSelectMode&&u==2){e.editor.textInput.onContextMenu(e.domEvent);return}if(!o&&!n){u==0&&e.editor.inMultiSelectMode&&e.editor.exitMultiSelectMode();return}var a=e.editor,f=a.selection,l=a.inMultiSelectMode,c=e.getDocumentPosition(),h=f.getCursor(),p=e.inSelection()||f.isEmpty()&&i(c,h),d=e.x,v=e.y,m=function(e){d=e.clientX,v=e.clientY},g=function(){var e=a.renderer.pixelToScreenCoordinates(d,v),t=y.screenToDocumentPosition(e.row,e.column);if(i(w,e)&&i(t,f.selectionLead))return;w=e,a.selection.moveCursorToPosition(t),a.selection.clearSelection(),a.renderer.scrollCursorIntoView(),a.removeSelectionMarkers(x),x=f.rectangularRangeBlock(w,b),x.forEach(a.addSelectionMarker,a),a.updateSelectionMarkers()},y=a.session,b=a.renderer.pixelToScreenCoordinates(d,v),w=b;if(o&&!s&&!n&&u==0){if(!l&&p)return;if(!l){var E=f.toOrientedRange();a.addSelectionMarker(E)}var S=f.rangeList.rangeAtPoint(c);r.capture(a.container,function(){},function(){var e=f.toOrientedRange();S&&e.isEmpty()&&i(S.cursor,e.cursor)?f.substractPoint(e.cursor):(E&&(a.removeSelectionMarker(E),f.addRange(E)),f.addRange(e))})}else if(!s&&n&&u==0){e.stop(),l&&!o?f.toSingleRange():!l&&o&&f.addRange(),f.moveCursorToPosition(c),f.clearSelection();var x=[],T=function(e){clearInterval(C),a.removeSelectionMarkers(x);for(var t=0;t<x.length;t++)f.addRange(x[t])},N=g;r.capture(a.container,m,T);var C=setInterval(function(){N()},20);return e.preventDefault()}}var r=e("../lib/event");t.onMouseDown=s}),ace.define("ace/commands/multi_select_commands",["require","exports","module","ace/keyboard/hash_handler"],function(e,t,n){t.defaultCommands=[{name:"addCursorAbove",exec:function(e){e.selectMoreLines(-1)},bindKey:{win:"Ctrl-Alt-Up",mac:"Ctrl-Alt-Up"},readonly:!0},{name:"addCursorBelow",exec:function(e){e.selectMoreLines(1)},bindKey:{win:"Ctrl-Alt-Down",mac:"Ctrl-Alt-Down"},readonly:!0},{name:"addCursorAboveSkipCurrent",exec:function(e){e.selectMoreLines(-1,!0)},bindKey:{win:"Ctrl-Alt-Shift-Up",mac:"Ctrl-Alt-Shift-Up"},readonly:!0},{name:"addCursorBelowSkipCurrent",exec:function(e){e.selectMoreLines(1,!0)},bindKey:{win:"Ctrl-Alt-Shift-Down",mac:"Ctrl-Alt-Shift-Down"},readonly:!0},{name:"selectMoreBefore",exec:function(e){e.selectMore(-1)},bindKey:{win:"Ctrl-Alt-Left",mac:"Ctrl-Alt-Left"},readonly:!0},{name:"selectMoreAfter",exec:function(e){e.selectMore(1)},bindKey:{win:"Ctrl-Alt-Right",mac:"Ctrl-Alt-Right"},readonly:!0},{name:"selectNextBefore",exec:function(e){e.selectMore(-1,!0)},bindKey:{win:"Ctrl-Alt-Shift-Left",mac:"Ctrl-Alt-Shift-Left"},readonly:!0},{name:"selectNextAfter",exec:function(e){e.selectMore(1,!0)},bindKey:{win:"Ctrl-Alt-Shift-Right",mac:"Ctrl-Alt-Shift-Right"},readonly:!0},{name:"splitIntoLines",exec:function(e){e.multiSelect.splitIntoLines()},bindKey:{win:"Ctrl-Alt-L",mac:"Ctrl-Alt-L"},readonly:!0},{name:"alignCursors",exec:function(e){e.alignCursors()},bindKey:{win:"Ctrl-Alt-A",mac:"Ctrl-Alt-A"}}],t.multiSelectCommands=[{name:"singleSelection",bindKey:"esc",exec:function(e){e.exitMultiSelectMode()},readonly:!0,isAvailable:function(e){return e&&e.inMultiSelectMode}}];var r=e("../keyboard/hash_handler").HashHandler;t.keyboardHandler=new r(t.multiSelectCommands)}),ace.define("ace/worker/worker_client",["require","exports","module","ace/lib/oop","ace/lib/event_emitter","ace/config"],function(e,t,n){var r=e("../lib/oop"),i=e("../lib/event_emitter").EventEmitter,s=e("../config"),o=function(t,n,r){this.changeListener=this.changeListener.bind(this),this.onMessage=this.onMessage.bind(this),this.onError=this.onError.bind(this);var i;if(s.get("packaged"))i=s.moduleUrl(n,"worker");else{var o=this.$normalizePath;typeof e.supports!="undefined"&&e.supports.indexOf("ucjs2-pinf-0")>=0?i=e.nameToUrl("ace/worker/worker_sourcemint"):(e.nameToUrl&&!e.toUrl&&(e.toUrl=e.nameToUrl),i=o(e.toUrl("ace/worker/worker",null,"_")));var u={};t.forEach(function(t){u[t]=o(e.toUrl(t,null,"_").replace(/.js(\?.*)?$/,""))})}this.$worker=new Worker(i),this.$worker.postMessage({init:!0,tlns:u,module:n,classname:r}),this.callbackId=1,this.callbacks={},this.$worker.onerror=this.onError,this.$worker.onmessage=this.onMessage};(function(){r.implement(this,i),this.onError=function(e){throw window.console&&console.log&&console.log(e),e},this.onMessage=function(e){var t=e.data;switch(t.type){case"log":window.console&&console.log&&console.log.apply(console,t.data);break;case"event":this._emit(t.name,{data:t.data});break;case"call":var n=this.callbacks[t.id];n&&(n(t.data),delete this.callbacks[t.id])}},this.$normalizePath=function(e){return location.host?(e=e.replace(/^[a-z]+:\/\/[^\/]+/,""),e=location.protocol+"//"+location.host+(e.charAt(0)=="/"?"":location.pathname.replace(/\/[^\/]*$/,""))+"/"+e.replace(/^[\/]+/,""),e):e},this.terminate=function(){this._emit("terminate",{}),this.$worker.terminate(),this.$worker=null,this.$doc.removeEventListener("change",this.changeListener),this.$doc=null},this.send=function(e,t){this.$worker.postMessage({command:e,args:t})},this.call=function(e,t,n){if(n){var r=this.callbackId++;this.callbacks[r]=n,t.push(r)}this.send(e,t)},this.emit=function(e,t){try{this.$worker.postMessage({event:e,data:{data:t.data}})}catch(n){}},this.attachToDocument=function(e){this.$doc&&this.terminate(),this.$doc=e,this.call("setValue",[e.getValue()]),e.on("change",this.changeListener)},this.changeListener=function(e){e.range={start:e.data.range.start,end:e.data.range.end},this.emit("change",e)}}).call(o.prototype);var u=function(t,n,r){this.changeListener=this.changeListener.bind(this),this.callbackId=1,this.callbacks={},this.messageBuffer=[];var s=null,o=Object.create(i),u=this;this.$worker={},this.$worker.postMessage=function(e){u.messageBuffer.push(e),s&&setTimeout(a)};var a=function(){var e=u.messageBuffer.shift();e.command?s[e.command].apply(s,e.args):e.event&&o._emit(e.event,e.data)};o.postMessage=function(e){u.onMessage({data:e})},o.callback=function(e,t){this.postMessage({type:"call",id:t,data:e})},o.emit=function(e,t){this.postMessage({type:"event",name:e,data:t})},e([n],function(e){s=new e[r](o);while(u.messageBuffer.length)a()})};u.prototype=o.prototype,t.UIWorkerClient=u,t.WorkerClient=o}),ace.define("ace/placeholder",["require","exports","module","ace/range","ace/lib/event_emitter","ace/lib/oop"],function(e,t,n){var r=e("./range").Range,i=e("./lib/event_emitter").EventEmitter,s=e("./lib/oop"),o=function(e,t,n,r,i,s){var o=this;this.length=t,this.session=e,this.doc=e.getDocument(),this.mainClass=i,this.othersClass=s,this.$onUpdate=this.onUpdate.bind(this),this.doc.on("change",this.$onUpdate),this.$others=r,this.$onCursorChange=function(){setTimeout(function(){o.onCursorChange()})},this.$pos=n;var u=e.getUndoManager().$undoStack||e.getUndoManager().$undostack||{length:-1};this.$undoStackDepth=u.length,this.setup(),e.selection.on("changeCursor",this.$onCursorChange)};(function(){s.implement(this,i),this.setup=function(){var e=this,t=this.doc,n=this.session,i=this.$pos;this.pos=t.createAnchor(i.row,i.column),this.markerId=n.addMarker(new r(i.row,i.column,i.row,i.column+this.length),this.mainClass,null,!1),this.pos.on("change",function(t){n.removeMarker(e.markerId),e.markerId=n.addMarker(new r(t.value.row,t.value.column,t.value.row,t.value.column+e.length),e.mainClass,null,!1)}),this.others=[],this.$others.forEach(function(n){var r=t.createAnchor(n.row,n.column);e.others.push(r)}),n.setUndoSelect(!1)},this.showOtherMarkers=function(){if(this.othersActive)return;var e=this.session,t=this;this.othersActive=!0,this.others.forEach(function(n){n.markerId=e.addMarker(new r(n.row,n.column,n.row,n.column+t.length),t.othersClass,null,!1),n.on("change",function(i){e.removeMarker(n.markerId),n.markerId=e.addMarker(new r(i.value.row,i.value.column,i.value.row,i.value.column+t.length),t.othersClass,null,!1)})})},this.hideOtherMarkers=function(){if(!this.othersActive)return;this.othersActive=!1;for(var e=0;e<this.others.length;e++)this.session.removeMarker(this.others[e].markerId)},this.onUpdate=function(e){var t=e.data,n=t.range;if(n.start.row!==n.end.row)return;if(n.start.row!==this.pos.row)return;if(this.$updating)return;this.$updating=!0;var i=t.action==="insertText"?n.end.column-n.start.column:n.start.column-n.end.column;if(n.start.column>=this.pos.column&&n.start.column<=this.pos.column+this.length+1){var s=n.start.column-this.pos.column;this.length+=i;if(!this.session.$fromUndo){if(t.action==="insertText")for(var o=this.others.length-1;o>=0;o--){var u=this.others[o],a={row:u.row,column:u.column+s};u.row===n.start.row&&n.start.column<u.column&&(a.column+=i),this.doc.insert(a,t.text)}else if(t.action==="removeText")for(var o=this.others.length-1;o>=0;o--){var u=this.others[o],a={row:u.row,column:u.column+s};u.row===n.start.row&&n.start.column<u.column&&(a.column+=i),this.doc.remove(new r(a.row,a.column,a.row,a.column-i))}n.start.column===this.pos.column&&t.action==="insertText"?setTimeout(function(){this.pos.setPosition(this.pos.row,this.pos.column-i);for(var e=0;e<this.others.length;e++){var t=this.others[e],r={row:t.row,column:t.column-i};t.row===n.start.row&&n.start.column<t.column&&(r.column+=i),t.setPosition(r.row,r.column)}}.bind(this),0):n.start.column===this.pos.column&&t.action==="removeText"&&setTimeout(function(){for(var e=0;e<this.others.length;e++){var t=this.others[e];t.row===n.start.row&&n.start.column<t.column&&t.setPosition(t.row,t.column-i)}}.bind(this),0)}this.pos._emit("change",{value:this.pos});for(var o=0;o<this.others.length;o++)this.others[o]._emit("change",{value:this.others[o]})}this.$updating=!1},this.onCursorChange=function(e){if(this.$updating)return;var t=this.session.selection.getCursor();t.row===this.pos.row&&t.column>=this.pos.column&&t.column<=this.pos.column+this.length?(this.showOtherMarkers(),this._emit("cursorEnter",e)):(this.hideOtherMarkers(),this._emit("cursorLeave",e))},this.detach=function(){this.session.removeMarker(this.markerId),this.hideOtherMarkers(),this.doc.removeEventListener("change",this.$onUpdate),this.session.selection.removeEventListener("changeCursor",this.$onCursorChange),this.pos.detach();for(var e=0;e<this.others.length;e++)this.others[e].detach();this.session.setUndoSelect(!0)},this.cancel=function(){if(this.$undoStackDepth===-1)throw Error("Canceling placeholders only supported with undo manager attached to session.");var e=this.session.getUndoManager(),t=(e.$undoStack||e.$undostack).length-this.$undoStackDepth;for(var n=0;n<t;n++)e.undo(!0)}}).call(o.prototype),t.PlaceHolder=o}),ace.define("ace/mode/folding/fold_mode",["require","exports","module","ace/range"],function(e,t,n){var r=e("../../range").Range,i=t.FoldMode=function(){};(function(){this.foldingStartMarker=null,this.foldingStopMarker=null,this.getFoldWidget=function(e,t,n){var r=e.getLine(n);return this.foldingStartMarker.test(r)?"start":t=="markbeginend"&&this.foldingStopMarker&&this.foldingStopMarker.test(r)?"end":""},this.getFoldWidgetRange=function(e,t,n){return null},this.indentationBlock=function(e,t,n){var i=/\S/,s=e.getLine(t),o=s.search(i);if(o==-1)return;var u=n||s.length,a=e.getLength(),f=t,l=t;while(++t<a){var c=e.getLine(t).search(i);if(c==-1)continue;if(c<=o)break;l=t}if(l>f){var h=e.getLine(l).length;return new r(f,u,l,h)}},this.openingBracketBlock=function(e,t,n,i,s){var o={row:n,column:i+1},u=e.$findClosingBracket(t,o,s);if(!u)return;var a=e.foldWidgets[u.row];return a==null&&(a=this.getFoldWidget(e,u.row)),a=="start"&&u.row>o.row&&(u.row--,u.column=e.getLine(u.row).length),r.fromPoints(o,u)},this.closingBracketBlock=function(e,t,n,i,s){var o={row:n,column:i},u=e.$findOpeningBracket(t,o);if(!u)return;return u.column++,o.column--,r.fromPoints(u,o)}}).call(i.prototype)});
+ (function() {
+ ace.require(["ace/ace"], function(a) {
+ a && a.config.init();
+ if (!window.ace)
+ window.ace = {};
+ for (var key in a) if (a.hasOwnProperty(key))
+ ace[key] = a[key];
+ });
+ })();
+ \ No newline at end of file
diff --git a/plugins/jetpack/modules/custom-css/custom-css/js/ace/mode-css.js b/plugins/jetpack/modules/custom-css/custom-css/js/ace/mode-css.js
new file mode 100644
index 00000000..20155551
--- /dev/null
+++ b/plugins/jetpack/modules/custom-css/custom-css/js/ace/mode-css.js
@@ -0,0 +1 @@
+ace.define("ace/mode/css",["require","exports","module","ace/lib/oop","ace/mode/text","ace/tokenizer","ace/mode/css_highlight_rules","ace/mode/matching_brace_outdent","ace/worker/worker_client","ace/mode/behaviour/cstyle","ace/mode/folding/cstyle"],function(e,t,n){var r=e("../lib/oop"),i=e("./text").Mode,s=e("../tokenizer").Tokenizer,o=e("./css_highlight_rules").CssHighlightRules,u=e("./matching_brace_outdent").MatchingBraceOutdent,a=e("../worker/worker_client").WorkerClient,f=e("./behaviour/cstyle").CstyleBehaviour,l=e("./folding/cstyle").FoldMode,c=function(){this.$tokenizer=new s((new o).getRules(),"i"),this.$outdent=new u,this.$behaviour=new f,this.foldingRules=new l};r.inherits(c,i),function(){this.foldingRules="cStyle",this.getNextLineIndent=function(e,t,n){var r=this.$getIndent(t),i=this.$tokenizer.getLineTokens(t,e).tokens;if(i.length&&i[i.length-1].type=="comment")return r;var s=t.match(/^.*\{\s*$/);return s&&(r+=n),r},this.checkOutdent=function(e,t,n){return this.$outdent.checkOutdent(t,n)},this.autoOutdent=function(e,t,n){this.$outdent.autoOutdent(t,n)},this.createWorker=function(e){var t=new a(["ace"],"ace/mode/css_worker","Worker");return t.attachToDocument(e.getDocument()),t.on("csslint",function(t){e.setAnnotations(t.data)}),t.on("terminate",function(){e.clearAnnotations()}),t}}.call(c.prototype),t.Mode=c}),ace.define("ace/mode/css_highlight_rules",["require","exports","module","ace/lib/oop","ace/lib/lang","ace/mode/text_highlight_rules"],function(e,t,n){var r=e("../lib/oop"),i=e("../lib/lang"),s=e("./text_highlight_rules").TextHighlightRules,o=t.supportType="animation-fill-mode|alignment-adjust|alignment-baseline|animation-delay|animation-direction|animation-duration|animation-iteration-count|animation-name|animation-play-state|animation-timing-function|animation|appearance|azimuth|backface-visibility|background-attachment|background-break|background-clip|background-color|background-image|background-origin|background-position|background-repeat|background-size|background|baseline-shift|binding|bleed|bookmark-label|bookmark-level|bookmark-state|bookmark-target|border-bottom|border-bottom-color|border-bottom-left-radius|border-bottom-right-radius|border-bottom-style|border-bottom-width|border-collapse|border-color|border-image|border-image-outset|border-image-repeat|border-image-slice|border-image-source|border-image-width|border-left|border-left-color|border-left-style|border-left-width|border-radius|border-right|border-right-color|border-right-style|border-right-width|border-spacing|border-style|border-top|border-top-color|border-top-left-radius|border-top-right-radius|border-top-style|border-top-width|border-width|border|bottom|box-align|box-decoration-break|box-direction|box-flex-group|box-flex|box-lines|box-ordinal-group|box-orient|box-pack|box-shadow|box-sizing|break-after|break-before|break-inside|caption-side|clear|clip|color-profile|color|column-count|column-fill|column-gap|column-rule|column-rule-color|column-rule-style|column-rule-width|column-span|column-width|columns|content|counter-increment|counter-reset|crop|cue-after|cue-before|cue|cursor|direction|display|dominant-baseline|drop-initial-after-adjust|drop-initial-after-align|drop-initial-before-adjust|drop-initial-before-align|drop-initial-size|drop-initial-value|elevation|empty-cells|fit|fit-position|float-offset|float|font-family|font-size|font-size-adjust|font-stretch|font-style|font-variant|font-weight|font|grid-columns|grid-rows|hanging-punctuation|height|hyphenate-after|hyphenate-before|hyphenate-character|hyphenate-lines|hyphenate-resource|hyphens|icon|image-orientation|image-rendering|image-resolution|inline-box-align|left|letter-spacing|line-height|line-stacking-ruby|line-stacking-shift|line-stacking-strategy|line-stacking|list-style-image|list-style-position|list-style-type|list-style|margin-bottom|margin-left|margin-right|margin-top|margin|mark-after|mark-before|mark|marks|marquee-direction|marquee-play-count|marquee-speed|marquee-style|max-height|max-width|min-height|min-width|move-to|nav-down|nav-index|nav-left|nav-right|nav-up|opacity|orphans|outline-color|outline-offset|outline-style|outline-width|outline|overflow-style|overflow-x|overflow-y|overflow|padding-bottom|padding-left|padding-right|padding-top|padding|page-break-after|page-break-before|page-break-inside|page-policy|page|pause-after|pause-before|pause|perspective-origin|perspective|phonemes|pitch-range|pitch|play-during|position|presentation-level|punctuation-trim|quotes|rendering-intent|resize|rest-after|rest-before|rest|richness|right|rotation-point|rotation|ruby-align|ruby-overhang|ruby-position|ruby-span|size|speak-header|speak-numeral|speak-punctuation|speak|speech-rate|stress|string-set|table-layout|target-name|target-new|target-position|target|text-align-last|text-align|text-decoration|text-emphasis|text-height|text-indent|text-justify|text-outline|text-shadow|text-transform|text-wrap|top|transform-origin|transform-style|transform|transition-delay|transition-duration|transition-property|transition-timing-function|transition|unicode-bidi|vertical-align|visibility|voice-balance|voice-duration|voice-family|voice-pitch-range|voice-pitch|voice-rate|voice-stress|voice-volume|volume|white-space-collapse|white-space|widows|width|word-break|word-spacing|word-wrap|z-index",u=t.supportFunction="rgb|rgba|url|attr|counter|counters",a=t.supportConstant="absolute|after-edge|after|all-scroll|all|alphabetic|always|antialiased|armenian|auto|avoid-column|avoid-page|avoid|balance|baseline|before-edge|before|below|bidi-override|block-line-height|block|bold|bolder|border-box|both|bottom|box|break-all|break-word|capitalize|caps-height|caption|center|central|char|circle|cjk-ideographic|clone|close-quote|col-resize|collapse|column|consider-shifts|contain|content-box|cover|crosshair|cubic-bezier|dashed|decimal-leading-zero|decimal|default|disabled|disc|disregard-shifts|distribute-all-lines|distribute-letter|distribute-space|distribute|dotted|double|e-resize|ease-in|ease-in-out|ease-out|ease|ellipsis|end|exclude-ruby|fill|fixed|georgian|glyphs|grid-height|groove|hand|hanging|hebrew|help|hidden|hiragana-iroha|hiragana|horizontal|icon|ideograph-alpha|ideograph-numeric|ideograph-parenthesis|ideograph-space|ideographic|inactive|include-ruby|inherit|initial|inline-block|inline-box|inline-line-height|inline-table|inline|inset|inside|inter-ideograph|inter-word|invert|italic|justify|katakana-iroha|katakana|keep-all|last|left|lighter|line-edge|line-through|line|linear|list-item|local|loose|lower-alpha|lower-greek|lower-latin|lower-roman|lowercase|lr-tb|ltr|mathematical|max-height|max-size|medium|menu|message-box|middle|move|n-resize|ne-resize|newspaper|no-change|no-close-quote|no-drop|no-open-quote|no-repeat|none|normal|not-allowed|nowrap|nw-resize|oblique|open-quote|outset|outside|overline|padding-box|page|pointer|pre-line|pre-wrap|pre|preserve-3d|progress|relative|repeat-x|repeat-y|repeat|replaced|reset-size|ridge|right|round|row-resize|rtl|s-resize|scroll|se-resize|separate|slice|small-caps|small-caption|solid|space|square|start|static|status-bar|step-end|step-start|steps|stretch|strict|sub|super|sw-resize|table-caption|table-cell|table-column-group|table-column|table-footer-group|table-header-group|table-row-group|table-row|table|tb-rl|text-after-edge|text-before-edge|text-bottom|text-size|text-top|text|thick|thin|transparent|underline|upper-alpha|upper-latin|upper-roman|uppercase|use-script|vertical-ideographic|vertical-text|visible|w-resize|wait|whitespace|z-index|zero",f=t.supportConstantColor="aqua|black|blue|fuchsia|gray|green|lime|maroon|navy|olive|orange|purple|red|silver|teal|white|yellow",l=t.supportConstantFonts="arial|century|comic|courier|garamond|georgia|helvetica|impact|lucida|symbol|system|tahoma|times|trebuchet|utopia|verdana|webdings|sans-serif|serif|monospace",c=t.numRe="\\-?(?:(?:[0-9]+)|(?:[0-9]*\\.[0-9]+))",h=t.pseudoElements="(\\:+)\\b(after|before|first-letter|first-line|moz-selection|selection)\\b",p=t.pseudoClasses="(:)\\b(active|checked|disabled|empty|enabled|first-child|first-of-type|focus|hover|indeterminate|invalid|last-child|last-of-type|link|not|nth-child|nth-last-child|nth-last-of-type|nth-of-type|only-child|only-of-type|required|root|target|valid|visited)\\b",d=function(){var e=this.createKeywordMapper({"support.function":u,"support.constant":a,"support.type":o,"support.constant.color":f,"support.constant.fonts":l},"text",!0),t=[{token:"comment",merge:!0,regex:"\\/\\*",next:"ruleset_comment"},{token:"string",regex:'["](?:(?:\\\\.)|(?:[^"\\\\]))*?["]'},{token:"string",regex:"['](?:(?:\\\\.)|(?:[^'\\\\]))*?[']"},{token:["constant.numeric","keyword"],regex:"("+c+")(ch|cm|deg|em|ex|fr|gd|grad|Hz|in|kHz|mm|ms|pc|pt|px|rad|rem|s|turn|vh|vm|vw|%)"},{token:"constant.numeric",regex:c},{token:"constant.numeric",regex:"#[a-f0-9]{6}"},{token:"constant.numeric",regex:"#[a-f0-9]{3}"},{token:["punctuation","entity.other.attribute-name.pseudo-element.css"],regex:h},{token:["punctuation","entity.other.attribute-name.pseudo-class.css"],regex:p},{token:e,regex:"\\-?[a-zA-Z_][a-zA-Z0-9_\\-]*"}],n=i.copyArray(t);n.unshift({token:"paren.rparen",regex:"\\}",next:"start"});var r=i.copyArray(t);r.unshift({token:"paren.rparen",regex:"\\}",next:"media"});var s=[{token:"comment",merge:!0,regex:".+"}],d=i.copyArray(s);d.unshift({token:"comment",regex:".*?\\*\\/",next:"start"});var v=i.copyArray(s);v.unshift({token:"comment",regex:".*?\\*\\/",next:"media"});var m=i.copyArray(s);m.unshift({token:"comment",regex:".*?\\*\\/",next:"ruleset"}),this.$rules={start:[{token:"comment",merge:!0,regex:"\\/\\*",next:"comment"},{token:"paren.lparen",regex:"\\{",next:"ruleset"},{token:"string",regex:"@.*?{",next:"media"},{token:"keyword",regex:"#[a-z0-9-_]+"},{token:"variable",regex:"\\.[a-z0-9-_]+"},{token:"string",regex:":[a-z0-9-_]+"},{token:"constant",regex:"[a-z0-9-_]+"}],media:[{token:"comment",merge:!0,regex:"\\/\\*",next:"media_comment"},{token:"paren.lparen",regex:"\\{",next:"media_ruleset"},{token:"string",regex:"\\}",next:"start"},{token:"keyword",regex:"#[a-z0-9-_]+"},{token:"variable",regex:"\\.[a-z0-9-_]+"},{token:"string",regex:":[a-z0-9-_]+"},{token:"constant",regex:"[a-z0-9-_]+"}],comment:d,ruleset:n,ruleset_comment:m,media_ruleset:r,media_comment:v}};r.inherits(d,s),t.CssHighlightRules=d}),ace.define("ace/mode/matching_brace_outdent",["require","exports","module","ace/range"],function(e,t,n){var r=e("../range").Range,i=function(){};(function(){this.checkOutdent=function(e,t){return/^\s+$/.test(e)?/^\s*\}/.test(t):!1},this.autoOutdent=function(e,t){var n=e.getLine(t),i=n.match(/^(\s*\})/);if(!i)return 0;var s=i[1].length,o=e.findMatchingBracket({row:t,column:s});if(!o||o.row==t)return 0;var u=this.$getIndent(e.getLine(o.row));e.replace(new r(t,0,t,s-1),u)},this.$getIndent=function(e){var t=e.match(/^(\s+)/);return t?t[1]:""}}).call(i.prototype),t.MatchingBraceOutdent=i}),ace.define("ace/mode/behaviour/cstyle",["require","exports","module","ace/lib/oop","ace/mode/behaviour","ace/token_iterator","ace/lib/lang"],function(e,t,n){var r=e("../../lib/oop"),i=e("../behaviour").Behaviour,s=e("../../token_iterator").TokenIterator,o=e("../../lib/lang"),u=["text","paren.rparen","punctuation.operator"],a=["text","paren.rparen","punctuation.operator","comment"],f=0,l=-1,c="",h=0,p=-1,d="",v="",m=function(){m.isSaneInsertion=function(e,t){var n=e.getCursorPosition(),r=new s(t,n.row,n.column);if(!this.$matchTokenType(r.getCurrentToken()||"text",u)){var i=new s(t,n.row,n.column+1);if(!this.$matchTokenType(i.getCurrentToken()||"text",u))return!1}return r.stepForward(),r.getCurrentTokenRow()!==n.row||this.$matchTokenType(r.getCurrentToken()||"text",a)},m.$matchTokenType=function(e,t){return t.indexOf(e.type||e)>-1},m.recordAutoInsert=function(e,t,n){var r=e.getCursorPosition(),i=t.doc.getLine(r.row);this.isAutoInsertedClosing(r,i,c[0])||(f=0),l=r.row,c=n+i.substr(r.column),f++},m.recordMaybeInsert=function(e,t,n){var r=e.getCursorPosition(),i=t.doc.getLine(r.row);this.isMaybeInsertedClosing(r,i)||(h=0),p=r.row,d=i.substr(0,r.column)+n,v=i.substr(r.column),h++},m.isAutoInsertedClosing=function(e,t,n){return f>0&&e.row===l&&n===c[0]&&t.substr(e.column)===c},m.isMaybeInsertedClosing=function(e,t){return h>0&&e.row===p&&t.substr(e.column)===v&&t.substr(0,e.column)==d},m.popAutoInsertedClosing=function(){c=c.substr(1),f--},m.clearMaybeInsertedClosing=function(){h=0,p=-1},this.add("braces","insertion",function(e,t,n,r,i){var s=n.getCursorPosition(),u=r.doc.getLine(s.row);if(i=="{"){var a=n.getSelectionRange(),f=r.doc.getTextRange(a);if(f!==""&&f!=="{"&&n.getWrapBehavioursEnabled())return{text:"{"+f+"}",selection:!1};if(m.isSaneInsertion(n,r))return/[\]\}\)]/.test(u[s.column])?(m.recordAutoInsert(n,r,"}"),{text:"{}",selection:[1,1]}):(m.recordMaybeInsert(n,r,"{"),{text:"{",selection:[1,1]})}else if(i=="}"){var l=u.substring(s.column,s.column+1);if(l=="}"){var c=r.$findOpeningBracket("}",{column:s.column+1,row:s.row});if(c!==null&&m.isAutoInsertedClosing(s,u,i))return m.popAutoInsertedClosing(),{text:"",selection:[1,1]}}}else if(i=="\n"||i=="\r\n"){var p="";m.isMaybeInsertedClosing(s,u)&&(p=o.stringRepeat("}",h),m.clearMaybeInsertedClosing());var l=u.substring(s.column,s.column+1);if(l=="}"||p!==""){var d=r.findMatchingBracket({row:s.row,column:s.column},"}");if(!d)return null;var v=this.getNextLineIndent(e,u.substring(0,s.column),r.getTabString()),g=this.$getIndent(u);return{text:"\n"+v+"\n"+g+p,selection:[1,v.length,1,v.length]}}}}),this.add("braces","deletion",function(e,t,n,r,i){var s=r.doc.getTextRange(i);if(!i.isMultiLine()&&s=="{"){var o=r.doc.getLine(i.start.row),u=o.substring(i.end.column,i.end.column+1);if(u=="}")return i.end.column++,i;h--}}),this.add("parens","insertion",function(e,t,n,r,i){if(i=="("){var s=n.getSelectionRange(),o=r.doc.getTextRange(s);if(o!==""&&n.getWrapBehavioursEnabled())return{text:"("+o+")",selection:!1};if(m.isSaneInsertion(n,r))return m.recordAutoInsert(n,r,")"),{text:"()",selection:[1,1]}}else if(i==")"){var u=n.getCursorPosition(),a=r.doc.getLine(u.row),f=a.substring(u.column,u.column+1);if(f==")"){var l=r.$findOpeningBracket(")",{column:u.column+1,row:u.row});if(l!==null&&m.isAutoInsertedClosing(u,a,i))return m.popAutoInsertedClosing(),{text:"",selection:[1,1]}}}}),this.add("parens","deletion",function(e,t,n,r,i){var s=r.doc.getTextRange(i);if(!i.isMultiLine()&&s=="("){var o=r.doc.getLine(i.start.row),u=o.substring(i.start.column+1,i.start.column+2);if(u==")")return i.end.column++,i}}),this.add("brackets","insertion",function(e,t,n,r,i){if(i=="["){var s=n.getSelectionRange(),o=r.doc.getTextRange(s);if(o!==""&&n.getWrapBehavioursEnabled())return{text:"["+o+"]",selection:!1};if(m.isSaneInsertion(n,r))return m.recordAutoInsert(n,r,"]"),{text:"[]",selection:[1,1]}}else if(i=="]"){var u=n.getCursorPosition(),a=r.doc.getLine(u.row),f=a.substring(u.column,u.column+1);if(f=="]"){var l=r.$findOpeningBracket("]",{column:u.column+1,row:u.row});if(l!==null&&m.isAutoInsertedClosing(u,a,i))return m.popAutoInsertedClosing(),{text:"",selection:[1,1]}}}}),this.add("brackets","deletion",function(e,t,n,r,i){var s=r.doc.getTextRange(i);if(!i.isMultiLine()&&s=="["){var o=r.doc.getLine(i.start.row),u=o.substring(i.start.column+1,i.start.column+2);if(u=="]")return i.end.column++,i}}),this.add("string_dquotes","insertion",function(e,t,n,r,i){if(i=='"'||i=="'"){var s=i,o=n.getSelectionRange(),u=r.doc.getTextRange(o);if(u!==""&&u!=="'"&&u!='"'&&n.getWrapBehavioursEnabled())return{text:s+u+s,selection:!1};var a=n.getCursorPosition(),f=r.doc.getLine(a.row),l=f.substring(a.column-1,a.column);if(l=="\\")return null;var c=r.getTokens(o.start.row),h=0,p,d=-1;for(var v=0;v<c.length;v++){p=c[v],p.type=="string"?d=-1:d<0&&(d=p.value.indexOf(s));if(p.value.length+h>o.start.column)break;h+=c[v].value.length}if(!p||d<0&&p.type!=="comment"&&(p.type!=="string"||o.start.column!==p.value.length+h-1&&p.value.lastIndexOf(s)===p.value.length-1)){if(!m.isSaneInsertion(n,r))return;return{text:s+s,selection:[1,1]}}if(p&&p.type==="string"){var g=f.substring(a.column,a.column+1);if(g==s)return{text:"",selection:[1,1]}}}}),this.add("string_dquotes","deletion",function(e,t,n,r,i){var s=r.doc.getTextRange(i);if(!i.isMultiLine()&&(s=='"'||s=="'")){var o=r.doc.getLine(i.start.row),u=o.substring(i.start.column+1,i.start.column+2);if(u=='"')return i.end.column++,i}})};r.inherits(m,i),t.CstyleBehaviour=m}),ace.define("ace/mode/folding/cstyle",["require","exports","module","ace/lib/oop","ace/range","ace/mode/folding/fold_mode"],function(e,t,n){var r=e("../../lib/oop"),i=e("../../range").Range,s=e("./fold_mode").FoldMode,o=t.FoldMode=function(){};r.inherits(o,s),function(){this.foldingStartMarker=/(\{|\[)[^\}\]]*$|^\s*(\/\*)/,this.foldingStopMarker=/^[^\[\{]*(\}|\])|^[\s\*]*(\*\/)/,this.getFoldWidgetRange=function(e,t,n){var r=e.getLine(n),i=r.match(this.foldingStartMarker);if(i){var s=i.index;return i[1]?this.openingBracketBlock(e,i[1],n,s):e.getCommentFoldRange(n,s+i[0].length,1)}if(t!=="markbeginend")return;var i=r.match(this.foldingStopMarker);if(i){var s=i.index+i[0].length;return i[1]?this.closingBracketBlock(e,i[1],n,s):e.getCommentFoldRange(n,s,-1)}}}.call(o.prototype)}) \ No newline at end of file
diff --git a/plugins/jetpack/modules/custom-css/custom-css/js/ace/readme.txt b/plugins/jetpack/modules/custom-css/custom-css/js/ace/readme.txt
new file mode 100644
index 00000000..6ef94f82
--- /dev/null
+++ b/plugins/jetpack/modules/custom-css/custom-css/js/ace/readme.txt
@@ -0,0 +1 @@
+The ACE editor files in this directory were pulled from https://github.com/ajaxorg/ace-builds/tree/master/src-min-noconflict, package 12.17.2012. \ No newline at end of file
diff --git a/plugins/jetpack/modules/custom-css/custom-css/js/ace/theme-textmate.js b/plugins/jetpack/modules/custom-css/custom-css/js/ace/theme-textmate.js
new file mode 100644
index 00000000..d8ef4710
--- /dev/null
+++ b/plugins/jetpack/modules/custom-css/custom-css/js/ace/theme-textmate.js
@@ -0,0 +1 @@
+ace.define("ace/theme/textmate",["require","exports","module","ace/lib/dom"],function(e,t,n){t.isDark=!1,t.cssClass="ace-tm",t.cssText='.ace-tm .ace_gutter {background: #f0f0f0;color: #333;}.ace-tm .ace_print-margin {width: 1px;background: #e8e8e8;}.ace-tm .ace_fold {background-color: #6B72E6;}.ace-tm .ace_scroller {background-color: #FFFFFF;}.ace-tm .ace_cursor {border-left: 2px solid black;}.ace-tm .ace_overwrite-cursors .ace_cursor {border-left: 0px;border-bottom: 1px solid black;}.ace-tm .ace_invisible {color: rgb(191, 191, 191);}.ace-tm .ace_storage,.ace-tm .ace_keyword {color: blue;}.ace-tm .ace_constant {color: rgb(197, 6, 11);}.ace-tm .ace_constant.ace_buildin {color: rgb(88, 72, 246);}.ace-tm .ace_constant.ace_language {color: rgb(88, 92, 246);}.ace-tm .ace_constant.ace_library {color: rgb(6, 150, 14);}.ace-tm .ace_invalid {background-color: rgba(255, 0, 0, 0.1);color: red;}.ace-tm .ace_support.ace_function {color: rgb(60, 76, 114);}.ace-tm .ace_support.ace_constant {color: rgb(6, 150, 14);}.ace-tm .ace_support.ace_type,.ace-tm .ace_support.ace_class {color: rgb(109, 121, 222);}.ace-tm .ace_keyword.ace_operator {color: rgb(104, 118, 135);}.ace-tm .ace_string {color: rgb(3, 106, 7);}.ace-tm .ace_comment {color: rgb(76, 136, 107);}.ace-tm .ace_comment.ace_doc {color: rgb(0, 102, 255);}.ace-tm .ace_comment.ace_doc.ace_tag {color: rgb(128, 159, 191);}.ace-tm .ace_constant.ace_numeric {color: rgb(0, 0, 205);}.ace-tm .ace_variable {color: rgb(49, 132, 149);}.ace-tm .ace_xml-pe {color: rgb(104, 104, 91);}.ace-tm .ace_entity.ace_name.ace_function {color: #0000A2;}.ace-tm .ace_markup.ace_heading {color: rgb(12, 7, 255);}.ace-tm .ace_markup.ace_list {color:rgb(185, 6, 144);}.ace-tm .ace_meta.ace_tag {color:rgb(0, 22, 142);}.ace-tm .ace_string.ace_regex {color: rgb(255, 0, 0)}.ace-tm .ace_marker-layer .ace_selection {background: rgb(181, 213, 255);}.ace-tm.ace_multiselect .ace_selection.ace_start {box-shadow: 0 0 3px 0px white;border-radius: 2px;}.ace-tm .ace_marker-layer .ace_step {background: rgb(252, 255, 0);}.ace-tm .ace_marker-layer .ace_stack {background: rgb(164, 229, 101);}.ace-tm .ace_marker-layer .ace_bracket {margin: -1px 0 0 -1px;border: 1px solid rgb(192, 192, 192);}.ace-tm .ace_marker-layer .ace_active-line {background: rgba(0, 0, 0, 0.07);}.ace-tm .ace_gutter-active-line {background-color : #dcdcdc;}.ace-tm .ace_marker-layer .ace_selected-word {background: rgb(250, 250, 255);border: 1px solid rgb(200, 200, 250);}.ace-tm .ace_indent-guide {background: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAACCAYAAACZgbYnAAAAE0lEQVQImWP4////f4bLly//BwAmVgd1/w11/gAAAABJRU5ErkJggg==") right repeat-y;}';var r=e("../lib/dom");r.importCssString(t.cssText,t.cssClass)}) \ No newline at end of file
diff --git a/plugins/jetpack/modules/custom-css/custom-css/js/ace/worker-css.js b/plugins/jetpack/modules/custom-css/custom-css/js/ace/worker-css.js
new file mode 100644
index 00000000..59ceeaf5
--- /dev/null
+++ b/plugins/jetpack/modules/custom-css/custom-css/js/ace/worker-css.js
@@ -0,0 +1 @@
+"no use strict";function initBaseUrls(e){require.tlns=e}function initSender(){var e=require(null,"ace/lib/event_emitter").EventEmitter,t=require(null,"ace/lib/oop"),n=function(){};return function(){t.implement(this,e),this.callback=function(e,t){postMessage({type:"call",id:t,data:e})},this.emit=function(e,t){postMessage({type:"event",name:e,data:t})}}.call(n.prototype),new n}if(typeof window!="undefined"&&window.document)throw"atempt to load ace worker into main window instead of webWorker";var console={log:function(){var e=Array.prototype.slice.call(arguments,0);postMessage({type:"log",data:e})},error:function(){var e=Array.prototype.slice.call(arguments,0);postMessage({type:"log",data:e})}},window={console:console},normalizeModule=function(e,t){if(t.indexOf("!")!==-1){var n=t.split("!");return normalizeModule(e,n[0])+"!"+normalizeModule(e,n[1])}if(t.charAt(0)=="."){var r=e.split("/").slice(0,-1).join("/"),t=r+"/"+t;while(t.indexOf(".")!==-1&&i!=t)var i=t,t=t.replace(/\/\.\//,"/").replace(/[^\/]+\/\.\.\//,"")}return t},require=function(e,t){if(!t.charAt)throw new Error("worker.js require() accepts only (parentId, id) as arguments");var t=normalizeModule(e,t),n=require.modules[t];if(n)return n.initialized||(n.initialized=!0,n.exports=n.factory().exports),n.exports;var r=t.split("/");r[0]=require.tlns[r[0]]||r[0];var i=r.join("/")+".js";return require.id=t,importScripts(i),require(e,t)};require.modules={},require.tlns={};var define=function(e,t,n){arguments.length==2?(n=t,typeof e!="string"&&(t=e,e=require.id)):arguments.length==1&&(n=e,e=require.id);if(e.indexOf("text!")===0)return;var r=function(t,n){return require(e,t,n)};require.modules[e]={factory:function(){var e={exports:{}},t=n(r,e.exports,e);return t&&(e.exports=t),e}}},main,sender;onmessage=function(e){var t=e.data;if(t.command){if(!main[t.command])throw new Error("Unknown command:"+t.command);main[t.command].apply(main,t.args)}else if(t.init){initBaseUrls(t.tlns),require(null,"ace/lib/fixoldbrowsers"),sender=initSender();var n=require(null,t.module)[t.classname];main=new n(sender)}else t.event&&sender&&sender._emit(t.event,t.data)},define("ace/lib/fixoldbrowsers",["require","exports","module","ace/lib/regexp","ace/lib/es5-shim"],function(e,t,n){e("./regexp"),e("./es5-shim")}),define("ace/lib/regexp",["require","exports","module"],function(e,t,n){function o(e){return(e.global?"g":"")+(e.ignoreCase?"i":"")+(e.multiline?"m":"")+(e.extended?"x":"")+(e.sticky?"y":"")}function u(e,t,n){if(Array.prototype.indexOf)return e.indexOf(t,n);for(var r=n||0;r<e.length;r++)if(e[r]===t)return r;return-1}var r={exec:RegExp.prototype.exec,test:RegExp.prototype.test,match:String.prototype.match,replace:String.prototype.replace,split:String.prototype.split},i=r.exec.call(/()??/,"")[1]===undefined,s=function(){var e=/^/g;return r.test.call(e,""),!e.lastIndex}();if(s&&i)return;RegExp.prototype.exec=function(e){var t=r.exec.apply(this,arguments),n,a;if(typeof e=="string"&&t){!i&&t.length>1&&u(t,"")>-1&&(a=RegExp(this.source,r.replace.call(o(this),"g","")),r.replace.call(e.slice(t.index),a,function(){for(var e=1;e<arguments.length-2;e++)arguments[e]===undefined&&(t[e]=undefined)}));if(this._xregexp&&this._xregexp.captureNames)for(var f=1;f<t.length;f++)n=this._xregexp.captureNames[f-1],n&&(t[n]=t[f]);!s&&this.global&&!t[0].length&&this.lastIndex>t.index&&this.lastIndex--}return t},s||(RegExp.prototype.test=function(e){var t=r.exec.call(this,e);return t&&this.global&&!t[0].length&&this.lastIndex>t.index&&this.lastIndex--,!!t})}),define("ace/lib/es5-shim",["require","exports","module"],function(e,t,n){function m(e){try{return Object.defineProperty(e,"sentinel",{}),"sentinel"in e}catch(t){}}Function.prototype.bind||(Function.prototype.bind=function(t){var n=this;if(typeof n!="function")throw new TypeError;var r=o.call(arguments,1),i=function(){if(this instanceof i){var e=function(){};e.prototype=n.prototype;var s=new e,u=n.apply(s,r.concat(o.call(arguments)));return u!==null&&Object(u)===u?u:s}return n.apply(t,r.concat(o.call(arguments)))};return i});var r=Function.prototype.call,i=Array.prototype,s=Object.prototype,o=i.slice,u=r.bind(s.toString),a=r.bind(s.hasOwnProperty),f,l,c,h,p;if(p=a(s,"__defineGetter__"))f=r.bind(s.__defineGetter__),l=r.bind(s.__defineSetter__),c=r.bind(s.__lookupGetter__),h=r.bind(s.__lookupSetter__);Array.isArray||(Array.isArray=function(t){return u(t)=="[object Array]"}),Array.prototype.forEach||(Array.prototype.forEach=function(t){var n=D(this),r=arguments[1],i=0,s=n.length>>>0;if(u(t)!="[object Function]")throw new TypeError;while(i<s)i in n&&t.call(r,n[i],i,n),i++}),Array.prototype.map||(Array.prototype.map=function(t){var n=D(this),r=n.length>>>0,i=Array(r),s=arguments[1];if(u(t)!="[object Function]")throw new TypeError;for(var o=0;o<r;o++)o in n&&(i[o]=t.call(s,n[o],o,n));return i}),Array.prototype.filter||(Array.prototype.filter=function(t){var n=D(this),r=n.length>>>0,i=[],s=arguments[1];if(u(t)!="[object Function]")throw new TypeError;for(var o=0;o<r;o++)o in n&&t.call(s,n[o],o,n)&&i.push(n[o]);return i}),Array.prototype.every||(Array.prototype.every=function(t){var n=D(this),r=n.length>>>0,i=arguments[1];if(u(t)!="[object Function]")throw new TypeError;for(var s=0;s<r;s++)if(s in n&&!t.call(i,n[s],s,n))return!1;return!0}),Array.prototype.some||(Array.prototype.some=function(t){var n=D(this),r=n.length>>>0,i=arguments[1];if(u(t)!="[object Function]")throw new TypeError;for(var s=0;s<r;s++)if(s in n&&t.call(i,n[s],s,n))return!0;return!1}),Array.prototype.reduce||(Array.prototype.reduce=function(t){var n=D(this),r=n.length>>>0;if(u(t)!="[object Function]")throw new TypeError;if(!r&&arguments.length==1)throw new TypeError;var i=0,s;if(arguments.length>=2)s=arguments[1];else do{if(i in n){s=n[i++];break}if(++i>=r)throw new TypeError}while(!0);for(;i<r;i++)i in n&&(s=t.call(void 0,s,n[i],i,n));return s}),Array.prototype.reduceRight||(Array.prototype.reduceRight=function(t){var n=D(this),r=n.length>>>0;if(u(t)!="[object Function]")throw new TypeError;if(!r&&arguments.length==1)throw new TypeError;var i,s=r-1;if(arguments.length>=2)i=arguments[1];else do{if(s in n){i=n[s--];break}if(--s<0)throw new TypeError}while(!0);do s in this&&(i=t.call(void 0,i,n[s],s,n));while(s--);return i}),Array.prototype.indexOf||(Array.prototype.indexOf=function(t){var n=D(this),r=n.length>>>0;if(!r)return-1;var i=0;arguments.length>1&&(i=M(arguments[1])),i=i>=0?i:Math.max(0,r+i);for(;i<r;i++)if(i in n&&n[i]===t)return i;return-1}),Array.prototype.lastIndexOf||(Array.prototype.lastIndexOf=function(t){var n=D(this),r=n.length>>>0;if(!r)return-1;var i=r-1;arguments.length>1&&(i=Math.min(i,M(arguments[1]))),i=i>=0?i:r-Math.abs(i);for(;i>=0;i--)if(i in n&&t===n[i])return i;return-1}),Object.getPrototypeOf||(Object.getPrototypeOf=function(t){return t.__proto__||(t.constructor?t.constructor.prototype:s)});if(!Object.getOwnPropertyDescriptor){var d="Object.getOwnPropertyDescriptor called on a non-object: ";Object.getOwnPropertyDescriptor=function(t,n){if(typeof t!="object"&&typeof t!="function"||t===null)throw new TypeError(d+t);if(!a(t,n))return;var r,i,o;r={enumerable:!0,configurable:!0};if(p){var u=t.__proto__;t.__proto__=s;var i=c(t,n),o=h(t,n);t.__proto__=u;if(i||o)return i&&(r.get=i),o&&(r.set=o),r}return r.value=t[n],r}}Object.getOwnPropertyNames||(Object.getOwnPropertyNames=function(t){return Object.keys(t)});if(!Object.create){var v;Object.prototype.__proto__===null?v=function(){return{__proto__:null}}:v=function(){var e={};for(var t in e)e[t]=null;return e.constructor=e.hasOwnProperty=e.propertyIsEnumerable=e.isPrototypeOf=e.toLocaleString=e.toString=e.valueOf=e.__proto__=null,e},Object.create=function(t,n){var r;if(t===null)r=v();else{if(typeof t!="object")throw new TypeError("typeof prototype["+typeof t+"] != 'object'");var i=function(){};i.prototype=t,r=new i,r.__proto__=t}return n!==void 0&&Object.defineProperties(r,n),r}}if(Object.defineProperty){var g=m({}),y=typeof document=="undefined"||m(document.createElement("div"));if(!g||!y)var b=Object.defineProperty}if(!Object.defineProperty||b){var w="Property description must be an object: ",E="Object.defineProperty called on non-object: ",S="getters & setters can not be defined on this javascript engine";Object.defineProperty=function(t,n,r){if(typeof t!="object"&&typeof t!="function"||t===null)throw new TypeError(E+t);if(typeof r!="object"&&typeof r!="function"||r===null)throw new TypeError(w+r);if(b)try{return b.call(Object,t,n,r)}catch(i){}if(a(r,"value"))if(p&&(c(t,n)||h(t,n))){var o=t.__proto__;t.__proto__=s,delete t[n],t[n]=r.value,t.__proto__=o}else t[n]=r.value;else{if(!p)throw new TypeError(S);a(r,"get")&&f(t,n,r.get),a(r,"set")&&l(t,n,r.set)}return t}}Object.defineProperties||(Object.defineProperties=function(t,n){for(var r in n)a(n,r)&&Object.defineProperty(t,r,n[r]);return t}),Object.seal||(Object.seal=function(t){return t}),Object.freeze||(Object.freeze=function(t){return t});try{Object.freeze(function(){})}catch(x){Object.freeze=function(t){return function(n){return typeof n=="function"?n:t(n)}}(Object.freeze)}Object.preventExtensions||(Object.preventExtensions=function(t){return t}),Object.isSealed||(Object.isSealed=function(t){return!1}),Object.isFrozen||(Object.isFrozen=function(t){return!1}),Object.isExtensible||(Object.isExtensible=function(t){if(Object(t)===t)throw new TypeError;var n="";while(a(t,n))n+="?";t[n]=!0;var r=a(t,n);return delete t[n],r});if(!Object.keys){var T=!0,N=["toString","toLocaleString","valueOf","hasOwnProperty","isPrototypeOf","propertyIsEnumerable","constructor"],C=N.length;for(var k in{toString:null})T=!1;Object.keys=function P(e){if(typeof e!="object"&&typeof e!="function"||e===null)throw new TypeError("Object.keys called on a non-object");var P=[];for(var t in e)a(e,t)&&P.push(t);if(T)for(var n=0,r=C;n<r;n++){var i=N[n];a(e,i)&&P.push(i)}return P}}if(!Date.prototype.toISOString||(new Date(-621987552e5)).toISOString().indexOf("-000001")===-1)Date.prototype.toISOString=function(){var t,n,r,i;if(!isFinite(this))throw new RangeError;t=[this.getUTCMonth()+1,this.getUTCDate(),this.getUTCHours(),this.getUTCMinutes(),this.getUTCSeconds()],i=this.getUTCFullYear(),i=(i<0?"-":i>9999?"+":"")+("00000"+Math.abs(i)).slice(0<=i&&i<=9999?-4:-6),n=t.length;while(n--)r=t[n],r<10&&(t[n]="0"+r);return i+"-"+t.slice(0,2).join("-")+"T"+t.slice(2).join(":")+"."+("000"+this.getUTCMilliseconds()).slice(-3)+"Z"};Date.now||(Date.now=function(){return(new Date).getTime()}),Date.prototype.toJSON||(Date.prototype.toJSON=function(t){if(typeof this.toISOString!="function")throw new TypeError;return this.toISOString()}),Date.parse("+275760-09-13T00:00:00.000Z")!==864e13&&(Date=function(e){var t=function i(t,n,r,s,o,u,a){var f=arguments.length;if(this instanceof e){var l=f==1&&String(t)===t?new e(i.parse(t)):f>=7?new e(t,n,r,s,o,u,a):f>=6?new e(t,n,r,s,o,u):f>=5?new e(t,n,r,s,o):f>=4?new e(t,n,r,s):f>=3?new e(t,n,r):f>=2?new e(t,n):f>=1?new e(t):new e;return l.constructor=i,l}return e.apply(this,arguments)},n=new RegExp("^(\\d{4}|[+-]\\d{6})(?:-(\\d{2})(?:-(\\d{2})(?:T(\\d{2}):(\\d{2})(?::(\\d{2})(?:\\.(\\d{3}))?)?(?:Z|(?:([-+])(\\d{2}):(\\d{2})))?)?)?)?$");for(var r in e)t[r]=e[r];return t.now=e.now,t.UTC=e.UTC,t.prototype=e.prototype,t.prototype.constructor=t,t.parse=function(r){var i=n.exec(r);if(i){i.shift();for(var s=1;s<7;s++)i[s]=+(i[s]||(s<3?1:0)),s==1&&i[s]--;var o=+i.pop(),u=+i.pop(),a=i.pop(),f=0;if(a){if(u>23||o>59)return NaN;f=(u*60+o)*6e4*(a=="+"?-1:1)}var l=+i[0];return 0<=l&&l<=99?(i[0]=l+400,e.UTC.apply(this,i)+f-126227808e5):e.UTC.apply(this,i)+f}return e.parse.apply(this,arguments)},t}(Date));var L=" \n \f\r   ᠎              \u2028\u2029";if(!String.prototype.trim||L.trim()){L="["+L+"]";var A=new RegExp("^"+L+L+"*"),O=new RegExp(L+L+"*$");String.prototype.trim=function(){return String(this).replace(A,"").replace(O,"")}}var M=function(e){return e=+e,e!==e?e=0:e!==0&&e!==1/0&&e!==-1/0&&(e=(e>0||-1)*Math.floor(Math.abs(e))),e},_="a"[0]!="a",D=function(e){if(e==null)throw new TypeError;return _&&typeof e=="string"&&e?e.split(""):Object(e)}}),define("ace/lib/event_emitter",["require","exports","module"],function(e,t,n){var r={};r._emit=r._dispatchEvent=function(e,t){this._eventRegistry=this._eventRegistry||{},this._defaultHandlers=this._defaultHandlers||{};var n=this._eventRegistry[e]||[],r=this._defaultHandlers[e];if(!n.length&&!r)return;if(typeof t!="object"||!t)t={};t.type||(t.type=e),t.stopPropagation||(t.stopPropagation=function(){this.propagationStopped=!0}),t.preventDefault||(t.preventDefault=function(){this.defaultPrevented=!0});for(var i=0;i<n.length;i++){n[i](t);if(t.propagationStopped)break}if(r&&!t.defaultPrevented)return r(t)},r.setDefaultHandler=function(e,t){this._defaultHandlers=this._defaultHandlers||{};if(this._defaultHandlers[e])throw new Error("The default handler for '"+e+"' is already set");this._defaultHandlers[e]=t},r.on=r.addEventListener=function(e,t){this._eventRegistry=this._eventRegistry||{};var n=this._eventRegistry[e];n||(n=this._eventRegistry[e]=[]),n.indexOf(t)==-1&&n.push(t)},r.removeListener=r.removeEventListener=function(e,t){this._eventRegistry=this._eventRegistry||{};var n=this._eventRegistry[e];if(!n)return;var r=n.indexOf(t);r!==-1&&n.splice(r,1)},r.removeAllListeners=function(e){this._eventRegistry&&(this._eventRegistry[e]=[])},t.EventEmitter=r}),define("ace/lib/oop",["require","exports","module"],function(e,t,n){t.inherits=function(){var e=function(){};return function(t,n){e.prototype=n.prototype,t.super_=n.prototype,t.prototype=new e,t.prototype.constructor=t}}(),t.mixin=function(e,t){for(var n in t)e[n]=t[n]},t.implement=function(e,n){t.mixin(e,n)}}),define("ace/mode/css_worker",["require","exports","module","ace/lib/oop","ace/lib/lang","ace/worker/mirror","ace/mode/css/csslint"],function(e,t,n){var r=e("../lib/oop"),i=e("../lib/lang"),s=e("../worker/mirror").Mirror,o=e("./css/csslint").CSSLint,u=t.Worker=function(e){s.call(this,e),this.setTimeout(400),this.ruleset=null,this.setDisabledRules("ids"),this.setInfoRules("adjoining-classes|qualified-headings|zero-units|gradients|import|outline-none")};r.inherits(u,s),function(){this.setInfoRules=function(e){typeof e=="string"&&(e=e.split("|")),this.infoRules=i.arrayToMap(e),this.doc.getValue()&&this.deferredUpdate.schedule(100)},this.setDisabledRules=function(e){if(!e)this.ruleset=null;else{typeof e=="string"&&(e=e.split("|"));var t={};o.getRules().forEach(function(e){t[e.id]=!0}),e.forEach(function(e){delete t[e]}),console.log(t),this.ruleset=t}this.doc.getValue()&&this.deferredUpdate.schedule(100)},this.onUpdate=function(){var e=this.doc.getValue(),t=this.infoRules,n=o.verify(e,this.ruleset);this.sender.emit("csslint",n.messages.map(function(e){return{row:e.line-1,column:e.col-1,text:e.message,type:t[e.rule.id]?"info":e.type}}))}}.call(u.prototype)}),define("ace/lib/lang",["require","exports","module"],function(e,t,n){t.stringReverse=function(e){return e.split("").reverse().join("")},t.stringRepeat=function(e,t){return(new Array(t+1)).join(e)};var r=/^\s\s*/,i=/\s\s*$/;t.stringTrimLeft=function(e){return e.replace(r,"")},t.stringTrimRight=function(e){return e.replace(i,"")},t.copyObject=function(e){var t={};for(var n in e)t[n]=e[n];return t},t.copyArray=function(e){var t=[];for(var n=0,r=e.length;n<r;n++)e[n]&&typeof e[n]=="object"?t[n]=this.copyObject(e[n]):t[n]=e[n];return t},t.deepCopy=function(e){if(typeof e!="object")return e;var t=e.constructor();for(var n in e)typeof e[n]=="object"?t[n]=this.deepCopy(e[n]):t[n]=e[n];return t},t.arrayToMap=function(e){var t={};for(var n=0;n<e.length;n++)t[e[n]]=1;return t},t.createMap=function(e){var t=Object.create(null);for(var n in e)t[n]=e[n];return t},t.arrayRemove=function(e,t){for(var n=0;n<=e.length;n++)t===e[n]&&e.splice(n,1)},t.escapeRegExp=function(e){return e.replace(/([.*+?^${}()|[\]\/\\])/g,"\\$1")},t.escapeHTML=function(e){return e.replace(/&/g,"&#38;").replace(/"/g,"&#34;").replace(/'/g,"&#39;").replace(/</g,"&#60;")},t.getMatchOffsets=function(e,t){var n=[];return e.replace(t,function(e){n.push({offset:arguments[arguments.length-2],length:e.length})}),n},t.deferredCall=function(e){var t=null,n=function(){t=null,e()},r=function(e){return r.cancel(),t=setTimeout(n,e||0),r};return r.schedule=r,r.call=function(){return this.cancel(),e(),r},r.cancel=function(){return clearTimeout(t),t=null,r},r},t.delayedCall=function(e,t){var n=null,r=function(){n=null,e()},i=function(e){n&&clearTimeout(n),n=setTimeout(r,e||t)};return i.delay=i,i.schedule=function(e){n==null&&(n=setTimeout(r,e||0))},i.call=function(){this.cancel(),e()},i.cancel=function(){n&&clearTimeout(n),n=null},i.isPending=function(){return n},i}}),define("ace/worker/mirror",["require","exports","module","ace/document","ace/lib/lang"],function(e,t,n){var r=e("../document").Document,i=e("../lib/lang"),s=t.Mirror=function(e){this.sender=e;var t=this.doc=new r(""),n=this.deferredUpdate=i.deferredCall(this.onUpdate.bind(this)),s=this;e.on("change",function(e){t.applyDeltas([e.data]),n.schedule(s.$timeout)})};(function(){this.$timeout=500,this.setTimeout=function(e){this.$timeout=e},this.setValue=function(e){this.doc.setValue(e),this.deferredUpdate.schedule(this.$timeout)},this.getValue=function(e){this.sender.callback(this.doc.getValue(),e)},this.onUpdate=function(){}}).call(s.prototype)}),define("ace/document",["require","exports","module","ace/lib/oop","ace/lib/event_emitter","ace/range","ace/anchor"],function(e,t,n){var r=e("./lib/oop"),i=e("./lib/event_emitter").EventEmitter,s=e("./range").Range,o=e("./anchor").Anchor,u=function(e){this.$lines=[],e.length==0?this.$lines=[""]:Array.isArray(e)?this.insertLines(0,e):this.insert({row:0,column:0},e)};(function(){r.implement(this,i),this.setValue=function(e){var t=this.getLength();this.remove(new s(0,0,t,this.getLine(t-1).length)),this.insert({row:0,column:0},e)},this.getValue=function(){return this.getAllLines().join(this.getNewLineCharacter())},this.createAnchor=function(e,t){return new o(this,e,t)},"aaa".split(/a/).length==0?this.$split=function(e){return e.replace(/\r\n|\r/g,"\n").split("\n")}:this.$split=function(e){return e.split(/\r\n|\r|\n/)},this.$detectNewLine=function(e){var t=e.match(/^.*?(\r\n|\r|\n)/m);t?this.$autoNewLine=t[1]:this.$autoNewLine="\n"},this.getNewLineCharacter=function(){switch(this.$newLineMode){case"windows":return"\r\n";case"unix":return"\n";case"auto":return this.$autoNewLine}},this.$autoNewLine="\n",this.$newLineMode="auto",this.setNewLineMode=function(e){if(this.$newLineMode===e)return;this.$newLineMode=e},this.getNewLineMode=function(){return this.$newLineMode},this.isNewLine=function(e){return e=="\r\n"||e=="\r"||e=="\n"},this.getLine=function(e){return this.$lines[e]||""},this.getLines=function(e,t){return this.$lines.slice(e,t+1)},this.getAllLines=function(){return this.getLines(0,this.getLength())},this.getLength=function(){return this.$lines.length},this.getTextRange=function(e){if(e.start.row==e.end.row)return this.$lines[e.start.row].substring(e.start.column,e.end.column);var t=this.getLines(e.start.row+1,e.end.row-1);return t.unshift((this.$lines[e.start.row]||"").substring(e.start.column)),t.push((this.$lines[e.end.row]||"").substring(0,e.end.column)),t.join(this.getNewLineCharacter())},this.$clipPosition=function(e){var t=this.getLength();return e.row>=t&&(e.row=Math.max(0,t-1),e.column=this.getLine(t-1).length),e},this.insert=function(e,t){if(!t||t.length===0)return e;e=this.$clipPosition(e),this.getLength()<=1&&this.$detectNewLine(t);var n=this.$split(t),r=n.splice(0,1)[0],i=n.length==0?null:n.splice(n.length-1,1)[0];return e=this.insertInLine(e,r),i!==null&&(e=this.insertNewLine(e),e=this.insertLines(e.row,n),e=this.insertInLine(e,i||"")),e},this.insertLines=function(e,t){if(t.length==0)return{row:e,column:0};if(t.length>65535){var n=this.insertLines(e,t.slice(65535));t=t.slice(0,65535)}var r=[e,0];r.push.apply(r,t),this.$lines.splice.apply(this.$lines,r);var i=new s(e,0,e+t.length,0),o={action:"insertLines",range:i,lines:t};return this._emit("change",{data:o}),n||i.end},this.insertNewLine=function(e){e=this.$clipPosition(e);var t=this.$lines[e.row]||"";this.$lines[e.row]=t.substring(0,e.column),this.$lines.splice(e.row+1,0,t.substring(e.column,t.length));var n={row:e.row+1,column:0},r={action:"insertText",range:s.fromPoints(e,n),text:this.getNewLineCharacter()};return this._emit("change",{data:r}),n},this.insertInLine=function(e,t){if(t.length==0)return e;var n=this.$lines[e.row]||"";this.$lines[e.row]=n.substring(0,e.column)+t+n.substring(e.column);var r={row:e.row,column:e.column+t.length},i={action:"insertText",range:s.fromPoints(e,r),text:t};return this._emit("change",{data:i}),r},this.remove=function(e){e.start=this.$clipPosition(e.start),e.end=this.$clipPosition(e.end);if(e.isEmpty())return e.start;var t=e.start.row,n=e.end.row;if(e.isMultiLine()){var r=e.start.column==0?t:t+1,i=n-1;e.end.column>0&&this.removeInLine(n,0,e.end.column),i>=r&&this.removeLines(r,i),r!=t&&(this.removeInLine(t,e.start.column,this.getLine(t).length),this.removeNewLine(e.start.row))}else this.removeInLine(t,e.start.column,e.end.column);return e.start},this.removeInLine=function(e,t,n){if(t==n)return;var r=new s(e,t,e,n),i=this.getLine(e),o=i.substring(t,n),u=i.substring(0,t)+i.substring(n,i.length);this.$lines.splice(e,1,u);var a={action:"removeText",range:r,text:o};return this._emit("change",{data:a}),r.start},this.removeLines=function(e,t){var n=new s(e,0,t+1,0),r=this.$lines.splice(e,t-e+1),i={action:"removeLines",range:n,nl:this.getNewLineCharacter(),lines:r};return this._emit("change",{data:i}),r},this.removeNewLine=function(e){var t=this.getLine(e),n=this.getLine(e+1),r=new s(e,t.length,e+1,0),i=t+n;this.$lines.splice(e,2,i);var o={action:"removeText",range:r,text:this.getNewLineCharacter()};this._emit("change",{data:o})},this.replace=function(e,t){if(t.length==0&&e.isEmpty())return e.start;if(t==this.getTextRange(e))return e.end;this.remove(e);if(t)var n=this.insert(e.start,t);else n=e.start;return n},this.applyDeltas=function(e){for(var t=0;t<e.length;t++){var n=e[t],r=s.fromPoints(n.range.start,n.range.end);n.action=="insertLines"?this.insertLines(r.start.row,n.lines):n.action=="insertText"?this.insert(r.start,n.text):n.action=="removeLines"?this.removeLines(r.start.row,r.end.row-1):n.action=="removeText"&&this.remove(r)}},this.revertDeltas=function(e){for(var t=e.length-1;t>=0;t--){var n=e[t],r=s.fromPoints(n.range.start,n.range.end);n.action=="insertLines"?this.removeLines(r.start.row,r.end.row-1):n.action=="insertText"?this.remove(r):n.action=="removeLines"?this.insertLines(r.start.row,n.lines):n.action=="removeText"&&this.insert(r.start,n.text)}}}).call(u.prototype),t.Document=u}),define("ace/range",["require","exports","module"],function(e,t,n){var r=function(e,t,n,r){this.start={row:e,column:t},this.end={row:n,column:r}};(function(){this.isEqual=function(e){return this.start.row==e.start.row&&this.end.row==e.end.row&&this.start.column==e.start.column&&this.end.column==e.end.column},this.toString=function(){return"Range: ["+this.start.row+"/"+this.start.column+"] -> ["+this.end.row+"/"+this.end.column+"]"},this.contains=function(e,t){return this.compare(e,t)==0},this.compareRange=function(e){var t,n=e.end,r=e.start;return t=this.compare(n.row,n.column),t==1?(t=this.compare(r.row,r.column),t==1?2:t==0?1:0):t==-1?-2:(t=this.compare(r.row,r.column),t==-1?-1:t==1?42:0)},this.comparePoint=function(e){return this.compare(e.row,e.column)},this.containsRange=function(e){return this.comparePoint(e.start)==0&&this.comparePoint(e.end)==0},this.intersects=function(e){var t=this.compareRange(e);return t==-1||t==0||t==1},this.isEnd=function(e,t){return this.end.row==e&&this.end.column==t},this.isStart=function(e,t){return this.start.row==e&&this.start.column==t},this.setStart=function(e,t){typeof e=="object"?(this.start.column=e.column,this.start.row=e.row):(this.start.row=e,this.start.column=t)},this.setEnd=function(e,t){typeof e=="object"?(this.end.column=e.column,this.end.row=e.row):(this.end.row=e,this.end.column=t)},this.inside=function(e,t){return this.compare(e,t)==0?this.isEnd(e,t)||this.isStart(e,t)?!1:!0:!1},this.insideStart=function(e,t){return this.compare(e,t)==0?this.isEnd(e,t)?!1:!0:!1},this.insideEnd=function(e,t){return this.compare(e,t)==0?this.isStart(e,t)?!1:!0:!1},this.compare=function(e,t){return!this.isMultiLine()&&e===this.start.row?t<this.start.column?-1:t>this.end.column?1:0:e<this.start.row?-1:e>this.end.row?1:this.start.row===e?t>=this.start.column?0:-1:this.end.row===e?t<=this.end.column?0:1:0},this.compareStart=function(e,t){return this.start.row==e&&this.start.column==t?-1:this.compare(e,t)},this.compareEnd=function(e,t){return this.end.row==e&&this.end.column==t?1:this.compare(e,t)},this.compareInside=function(e,t){return this.end.row==e&&this.end.column==t?1:this.start.row==e&&this.start.column==t?-1:this.compare(e,t)},this.clipRows=function(e,t){if(this.end.row>t)var n={row:t+1,column:0};if(this.start.row>t)var i={row:t+1,column:0};if(this.start.row<e)var i={row:e,column:0};if(this.end.row<e)var n={row:e,column:0};return r.fromPoints(i||this.start,n||this.end)},this.extend=function(e,t){var n=this.compare(e,t);if(n==0)return this;if(n==-1)var i={row:e,column:t};else var s={row:e,column:t};return r.fromPoints(i||this.start,s||this.end)},this.isEmpty=function(){return this.start.row==this.end.row&&this.start.column==this.end.column},this.isMultiLine=function(){return this.start.row!==this.end.row},this.clone=function(){return r.fromPoints(this.start,this.end)},this.collapseRows=function(){return this.end.column==0?new r(this.start.row,0,Math.max(this.start.row,this.end.row-1),0):new r(this.start.row,0,this.end.row,0)},this.toScreenRange=function(e){var t=e.documentToScreenPosition(this.start),n=e.documentToScreenPosition(this.end);return new r(t.row,t.column,n.row,n.column)}}).call(r.prototype),r.fromPoints=function(e,t){return new r(e.row,e.column,t.row,t.column)},t.Range=r}),define("ace/anchor",["require","exports","module","ace/lib/oop","ace/lib/event_emitter"],function(e,t,n){var r=e("./lib/oop"),i=e("./lib/event_emitter").EventEmitter,s=t.Anchor=function(e,t,n){this.document=e,typeof n=="undefined"?this.setPosition(t.row,t.column):this.setPosition(t,n),this.$onChange=this.onChange.bind(this),e.on("change",this.$onChange)};(function(){r.implement(this,i),this.getPosition=function(){return this.$clipPositionToDocument(this.row,this.column)},this.getDocument=function(){return this.document},this.onChange=function(e){var t=e.data,n=t.range;if(n.start.row==n.end.row&&n.start.row!=this.row)return;if(n.start.row>this.row)return;if(n.start.row==this.row&&n.start.column>this.column)return;var r=this.row,i=this.column;t.action==="insertText"?n.start.row===r&&n.start.column<=i?n.start.row===n.end.row?i+=n.end.column-n.start.column:(i-=n.start.column,r+=n.end.row-n.start.row):n.start.row!==n.end.row&&n.start.row<r&&(r+=n.end.row-n.start.row):t.action==="insertLines"?n.start.row<=r&&(r+=n.end.row-n.start.row):t.action=="removeText"?n.start.row==r&&n.start.column<i?n.end.column>=i?i=n.start.column:i=Math.max(0,i-(n.end.column-n.start.column)):n.start.row!==n.end.row&&n.start.row<r?(n.end.row==r&&(i=Math.max(0,i-n.end.column)+n.start.column),r-=n.end.row-n.start.row):n.end.row==r&&(r-=n.end.row-n.start.row,i=Math.max(0,i-n.end.column)+n.start.column):t.action=="removeLines"&&n.start.row<=r&&(n.end.row<=r?r-=n.end.row-n.start.row:(r=n.start.row,i=0)),this.setPosition(r,i,!0)},this.setPosition=function(e,t,n){var r;n?r={row:e,column:t}:r=this.$clipPositionToDocument(e,t);if(this.row==r.row&&this.column==r.column)return;var i={row:this.row,column:this.column};this.row=r.row,this.column=r.column,this._emit("change",{old:i,value:r})},this.detach=function(){this.document.removeEventListener("change",this.$onChange)},this.$clipPositionToDocument=function(e,t){var n={};return e>=this.document.getLength()?(n.row=Math.max(0,this.document.getLength()-1),n.column=this.document.getLine(n.row).length):e<0?(n.row=0,n.column=0):(n.row=e,n.column=Math.min(this.document.getLine(n.row).length,Math.max(0,t))),t<0&&(n.column=0),n}}).call(s.prototype)}),define("ace/mode/css/csslint",["require","exports","module"],function(require,exports,module){function Reporter(e,t){this.messages=[],this.stats=[],this.lines=e,this.ruleset=t}var parserlib={};(function(){function e(){this._listeners={}}function t(e){this._input=e.replace(/\n\r?/g,"\n"),this._line=1,this._col=1,this._cursor=0}function n(e,t,n){this.col=n,this.line=t,this.message=e}function r(e,t,n,r){this.col=n,this.line=t,this.text=e,this.type=r}function i(e,n){this._reader=e?new t(e.toString()):null,this._token=null,this._tokenData=n,this._lt=[],this._ltIndex=0,this._ltIndexCache=[]}e.prototype={constructor:e,addListener:function(e,t){this._listeners[e]||(this._listeners[e]=[]),this._listeners[e].push(t)},fire:function(e){typeof e=="string"&&(e={type:e}),typeof e.target!="undefined"&&(e.target=this);if(typeof e.type=="undefined")throw new Error("Event object missing 'type' property.");if(this._listeners[e.type]){var t=this._listeners[e.type].concat();for(var n=0,r=t.length;n<r;n++)t[n].call(this,e)}},removeListener:function(e,t){if(this._listeners[e]){var n=this._listeners[e];for(var r=0,i=n.length;r<i;r++)if(n[r]===t){n.splice(r,1);break}}}},t.prototype={constructor:t,getCol:function(){return this._col},getLine:function(){return this._line},eof:function(){return this._cursor==this._input.length},peek:function(e){var t=null;return e=typeof e=="undefined"?1:e,this._cursor<this._input.length&&(t=this._input.charAt(this._cursor+e-1)),t},read:function(){var e=null;return this._cursor<this._input.length&&(this._input.charAt(this._cursor)=="\n"?(this._line++,this._col=1):this._col++,e=this._input.charAt(this._cursor++)),e},mark:function(){this._bookmark={cursor:this._cursor,line:this._line,col:this._col}},reset:function(){this._bookmark&&(this._cursor=this._bookmark.cursor,this._line=this._bookmark.line,this._col=this._bookmark.col,delete this._bookmark)},readTo:function(e){var t="",n;while(t.length<e.length||t.lastIndexOf(e)!=t.length-e.length){n=this.read();if(!n)throw new Error('Expected "'+e+'" at line '+this._line+", col "+this._col+".");t+=n}return t},readWhile:function(e){var t="",n=this.read();while(n!==null&&e(n))t+=n,n=this.read();return t},readMatch:function(e){var t=this._input.substring(this._cursor),n=null;return typeof e=="string"?t.indexOf(e)===0&&(n=this.readCount(e.length)):e instanceof RegExp&&e.test(t)&&(n=this.readCount(RegExp.lastMatch.length)),n},readCount:function(e){var t="";while(e--)t+=this.read();return t}},n.prototype=new Error,r.fromToken=function(e){return new r(e.value,e.startLine,e.startCol)},r.prototype={constructor:r,valueOf:function(){return this.toString()},toString:function(){return this.text}},i.createTokenData=function(e){var t=[],n={},r=e.concat([]),i=0,s=r.length+1;r.UNKNOWN=-1,r.unshift({name:"EOF"});for(;i<s;i++)t.push(r[i].name),r[r[i].name]=i,r[i].text&&(n[r[i].text]=i);return r.name=function(e){return t[e]},r.type=function(e){return n[e]},r},i.prototype={constructor:i,match:function(e,t){e instanceof Array||(e=[e]);var n=this.get(t),r=0,i=e.length;while(r<i)if(n==e[r++])return!0;return this.unget(),!1},mustMatch:function(e,t){var r;e instanceof Array||(e=[e]);if(!this.match.apply(this,arguments))throw r=this.LT(1),new n("Expected "+this._tokenData[e[0]].name+" at line "+r.startLine+", col "+r.startCol+".",r.startLine,r.startCol)},advance:function(e,t){while(this.LA(0)!==0&&!this.match(e,t))this.get();return this.LA(0)},get:function(e){var t=this._tokenData,n=this._reader,r,i=0,s=t.length,o=!1,u,a;if(this._lt.length&&this._ltIndex>=0&&this._ltIndex<this._lt.length){i++,this._token=this._lt[this._ltIndex++],a=t[this._token.type];while(a.channel!==undefined&&e!==a.channel&&this._ltIndex<this._lt.length)this._token=this._lt[this._ltIndex++],a=t[this._token.type],i++;if((a.channel===undefined||e===a.channel)&&this._ltIndex<=this._lt.length)return this._ltIndexCache.push(i),this._token.type}return u=this._getToken(),u.type>-1&&!t[u.type].hide&&(u.channel=t[u.type].channel,this._token=u,this._lt.push(u),this._ltIndexCache.push(this._lt.length-this._ltIndex+i),this._lt.length>5&&this._lt.shift(),this._ltIndexCache.length>5&&this._ltIndexCache.shift(),this._ltIndex=this._lt.length),a=t[u.type],a&&(a.hide||a.channel!==undefined&&e!==a.channel)?this.get(e):u.type},LA:function(e){var t=e,n;if(e>0){if(e>5)throw new Error("Too much lookahead.");while(t)n=this.get(),t--;while(t<e)this.unget(),t++}else if(e<0){if(!this._lt[this._ltIndex+e])throw new Error("Too much lookbehind.");n=this._lt[this._ltIndex+e].type}else n=this._token.type;return n},LT:function(e){return this.LA(e),this._lt[this._ltIndex+e-1]},peek:function(){return this.LA(1)},token:function(){return this._token},tokenName:function(e){return e<0||e>this._tokenData.length?"UNKNOWN_TOKEN":this._tokenData[e].name},tokenType:function(e){return this._tokenData[e]||-1},unget:function(){if(!this._ltIndexCache.length)throw new Error("Too much lookahead.");this._ltIndex-=this._ltIndexCache.pop(),this._token=this._lt[this._ltIndex-1]}},parserlib.util={StringReader:t,SyntaxError:n,SyntaxUnit:r,EventTarget:e,TokenStreamBase:i}})(),function(){function Combinator(e,t,n){SyntaxUnit.call(this,e,t,n,Parser.COMBINATOR_TYPE),this.type="unknown",/^\s+$/.test(e)?this.type="descendant":e==">"?this.type="child":e=="+"?this.type="adjacent-sibling":e=="~"&&(this.type="sibling")}function MediaFeature(e,t){SyntaxUnit.call(this,"("+e+(t!==null?":"+t:"")+")",e.startLine,e.startCol,Parser.MEDIA_FEATURE_TYPE),this.name=e,this.value=t}function MediaQuery(e,t,n,r,i){SyntaxUnit.call(this,(e?e+" ":"")+(t?t+" ":"")+n.join(" and "),r,i,Parser.MEDIA_QUERY_TYPE),this.modifier=e,this.mediaType=t,this.features=n}function Parser(e){EventTarget.call(this),this.options=e||{},this._tokenStream=null}function PropertyName(e,t,n,r){SyntaxUnit.call(this,e,n,r,Parser.PROPERTY_NAME_TYPE),this.hack=t}function PropertyValue(e,t,n){SyntaxUnit.call(this,e.join(" "),t,n,Parser.PROPERTY_VALUE_TYPE),this.parts=e}function PropertyValueIterator(e){this._i=0,this._parts=e.parts,this._marks=[],this.value=e}function PropertyValuePart(text,line,col){SyntaxUnit.call(this,text,line,col,Parser.PROPERTY_VALUE_PART_TYPE),this.type="unknown";var temp;if(/^([+\-]?[\d\.]+)([a-z]+)$/i.test(text)){this.type="dimension",this.value=+RegExp.$1,this.units=RegExp.$2;switch(this.units.toLowerCase()){case"em":case"rem":case"ex":case"px":case"cm":case"mm":case"in":case"pt":case"pc":case"ch":this.type="length";break;case"deg":case"rad":case"grad":this.type="angle";break;case"ms":case"s":this.type="time";break;case"hz":case"khz":this.type="frequency";break;case"dpi":case"dpcm":this.type="resolution"}}else/^([+\-]?[\d\.]+)%$/i.test(text)?(this.type="percentage",this.value=+RegExp.$1):/^([+\-]?[\d\.]+)%$/i.test(text)?(this.type="percentage",this.value=+RegExp.$1):/^([+\-]?\d+)$/i.test(text)?(this.type="integer",this.value=+RegExp.$1):/^([+\-]?[\d\.]+)$/i.test(text)?(this.type="number",this.value=+RegExp.$1):/^#([a-f0-9]{3,6})/i.test(text)?(this.type="color",temp=RegExp.$1,temp.length==3?(this.red=parseInt(temp.charAt(0)+temp.charAt(0),16),this.green=parseInt(temp.charAt(1)+temp.charAt(1),16),this.blue=parseInt(temp.charAt(2)+temp.charAt(2),16)):(this.red=parseInt(temp.substring(0,2),16),this.green=parseInt(temp.substring(2,4),16),this.blue=parseInt(temp.substring(4,6),16))):/^rgb\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*\)/i.test(text)?(this.type="color",this.red=+RegExp.$1,this.green=+RegExp.$2,this.blue=+RegExp.$3):/^rgb\(\s*(\d+)%\s*,\s*(\d+)%\s*,\s*(\d+)%\s*\)/i.test(text)?(this.type="color",this.red=+RegExp.$1*255/100,this.green=+RegExp.$2*255/100,this.blue=+RegExp.$3*255/100):/^rgba\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*,\s*([\d\.]+)\s*\)/i.test(text)?(this.type="color",this.red=+RegExp.$1,this.green=+RegExp.$2,this.blue=+RegExp.$3,this.alpha=+RegExp.$4):/^rgba\(\s*(\d+)%\s*,\s*(\d+)%\s*,\s*(\d+)%\s*,\s*([\d\.]+)\s*\)/i.test(text)?(this.type="color",this.red=+RegExp.$1*255/100,this.green=+RegExp.$2*255/100,this.blue=+RegExp.$3*255/100,this.alpha=+RegExp.$4):/^hsl\(\s*(\d+)\s*,\s*(\d+)%\s*,\s*(\d+)%\s*\)/i.test(text)?(this.type="color",this.hue=+RegExp.$1,this.saturation=+RegExp.$2/100,this.lightness=+RegExp.$3/100):/^hsla\(\s*(\d+)\s*,\s*(\d+)%\s*,\s*(\d+)%\s*,\s*([\d\.]+)\s*\)/i.test(text)?(this.type="color",this.hue=+RegExp.$1,this.saturation=+RegExp.$2/100,this.lightness=+RegExp.$3/100,this.alpha=+RegExp.$4):/^url\(["']?([^\)"']+)["']?\)/i.test(text)?(this.type="uri",this.uri=RegExp.$1):/^([^\(]+)\(/i.test(text)?(this.type="function",this.name=RegExp.$1,this.value=text):/^["'][^"']*["']/.test(text)?(this.type="string",this.value=eval(text)):Colors[text.toLowerCase()]?(this.type="color",temp=Colors[text.toLowerCase()].substring(1),this.red=parseInt(temp.substring(0,2),16),this.green=parseInt(temp.substring(2,4),16),this.blue=parseInt(temp.substring(4,6),16)):/^[\,\/]$/.test(text)?(this.type="operator",this.value=text):/^[a-z\-\u0080-\uFFFF][a-z0-9\-\u0080-\uFFFF]*$/i.test(text)&&(this.type="identifier",this.value=text)}function Selector(e,t,n){SyntaxUnit.call(this,e.join(" "),t,n,Parser.SELECTOR_TYPE),this.parts=e,this.specificity=Specificity.calculate(this)}function SelectorPart(e,t,n,r,i){SyntaxUnit.call(this,n,r,i,Parser.SELECTOR_PART_TYPE),this.elementName=e,this.modifiers=t}function SelectorSubPart(e,t,n,r){SyntaxUnit.call(this,e,n,r,Parser.SELECTOR_SUB_PART_TYPE),this.type=t,this.args=[]}function Specificity(e,t,n,r){this.a=e,this.b=t,this.c=n,this.d=r}function isHexDigit(e){return e!==null&&h.test(e)}function isDigit(e){return e!==null&&/\d/.test(e)}function isWhitespace(e){return e!==null&&/\s/.test(e)}function isNewLine(e){return e!==null&&nl.test(e)}function isNameStart(e){return e!==null&&/[a-z_\u0080-\uFFFF\\]/i.test(e)}function isNameChar(e){return e!==null&&(isNameStart(e)||/[0-9\-\\]/.test(e))}function isIdentStart(e){return e!==null&&(isNameStart(e)||/\-\\/.test(e))}function mix(e,t){for(var n in t)t.hasOwnProperty(n)&&(e[n]=t[n]);return e}function TokenStream(e){TokenStreamBase.call(this,e,Tokens)}function ValidationError(e,t,n){this.col=n,this.line=t,this.message=e}var EventTarget=parserlib.util.EventTarget,TokenStreamBase=parserlib.util.TokenStreamBase,StringReader=parserlib.util.StringReader,SyntaxError=parserlib.util.SyntaxError,SyntaxUnit=parserlib.util.SyntaxUnit,Colors={aliceblue:"#f0f8ff",antiquewhite:"#faebd7",aqua:"#00ffff",aquamarine:"#7fffd4",azure:"#f0ffff",beige:"#f5f5dc",bisque:"#ffe4c4",black:"#000000",blanchedalmond:"#ffebcd",blue:"#0000ff",blueviolet:"#8a2be2",brown:"#a52a2a",burlywood:"#deb887",cadetblue:"#5f9ea0",chartreuse:"#7fff00",chocolate:"#d2691e",coral:"#ff7f50",cornflowerblue:"#6495ed",cornsilk:"#fff8dc",crimson:"#dc143c",cyan:"#00ffff",darkblue:"#00008b",darkcyan:"#008b8b",darkgoldenrod:"#b8860b",darkgray:"#a9a9a9",darkgreen:"#006400",darkkhaki:"#bdb76b",darkmagenta:"#8b008b",darkolivegreen:"#556b2f",darkorange:"#ff8c00",darkorchid:"#9932cc",darkred:"#8b0000",darksalmon:"#e9967a",darkseagreen:"#8fbc8f",darkslateblue:"#483d8b",darkslategray:"#2f4f4f",darkturquoise:"#00ced1",darkviolet:"#9400d3",deeppink:"#ff1493",deepskyblue:"#00bfff",dimgray:"#696969",dodgerblue:"#1e90ff",firebrick:"#b22222",floralwhite:"#fffaf0",forestgreen:"#228b22",fuchsia:"#ff00ff",gainsboro:"#dcdcdc",ghostwhite:"#f8f8ff",gold:"#ffd700",goldenrod:"#daa520",gray:"#808080",green:"#008000",greenyellow:"#adff2f",honeydew:"#f0fff0",hotpink:"#ff69b4",indianred:"#cd5c5c",indigo:"#4b0082",ivory:"#fffff0",khaki:"#f0e68c",lavender:"#e6e6fa",lavenderblush:"#fff0f5",lawngreen:"#7cfc00",lemonchiffon:"#fffacd",lightblue:"#add8e6",lightcoral:"#f08080",lightcyan:"#e0ffff",lightgoldenrodyellow:"#fafad2",lightgray:"#d3d3d3",lightgreen:"#90ee90",lightpink:"#ffb6c1",lightsalmon:"#ffa07a",lightseagreen:"#20b2aa",lightskyblue:"#87cefa",lightslategray:"#778899",lightsteelblue:"#b0c4de",lightyellow:"#ffffe0",lime:"#00ff00",limegreen:"#32cd32",linen:"#faf0e6",magenta:"#ff00ff",maroon:"#800000",mediumaquamarine:"#66cdaa",mediumblue:"#0000cd",mediumorchid:"#ba55d3",mediumpurple:"#9370d8",mediumseagreen:"#3cb371",mediumslateblue:"#7b68ee",mediumspringgreen:"#00fa9a",mediumturquoise:"#48d1cc",mediumvioletred:"#c71585",midnightblue:"#191970",mintcream:"#f5fffa",mistyrose:"#ffe4e1",moccasin:"#ffe4b5",navajowhite:"#ffdead",navy:"#000080",oldlace:"#fdf5e6",olive:"#808000",olivedrab:"#6b8e23",orange:"#ffa500",orangered:"#ff4500",orchid:"#da70d6",palegoldenrod:"#eee8aa",palegreen:"#98fb98",paleturquoise:"#afeeee",palevioletred:"#d87093",papayawhip:"#ffefd5",peachpuff:"#ffdab9",peru:"#cd853f",pink:"#ffc0cb",plum:"#dda0dd",powderblue:"#b0e0e6",purple:"#800080",red:"#ff0000",rosybrown:"#bc8f8f",royalblue:"#4169e1",saddlebrown:"#8b4513",salmon:"#fa8072",sandybrown:"#f4a460",seagreen:"#2e8b57",seashell:"#fff5ee",sienna:"#a0522d",silver:"#c0c0c0",skyblue:"#87ceeb",slateblue:"#6a5acd",slategray:"#708090",snow:"#fffafa",springgreen:"#00ff7f",steelblue:"#4682b4",tan:"#d2b48c",teal:"#008080",thistle:"#d8bfd8",tomato:"#ff6347",turquoise:"#40e0d0",violet:"#ee82ee",wheat:"#f5deb3",white:"#ffffff",whitesmoke:"#f5f5f5",yellow:"#ffff00",yellowgreen:"#9acd32"};Combinator.prototype=new SyntaxUnit,Combinator.prototype.constructor=Combinator,MediaFeature.prototype=new SyntaxUnit,MediaFeature.prototype.constructor=MediaFeature,MediaQuery.prototype=new SyntaxUnit,MediaQuery.prototype.constructor=MediaQuery,Parser.DEFAULT_TYPE=0,Parser.COMBINATOR_TYPE=1,Parser.MEDIA_FEATURE_TYPE=2,Parser.MEDIA_QUERY_TYPE=3,Parser.PROPERTY_NAME_TYPE=4,Parser.PROPERTY_VALUE_TYPE=5,Parser.PROPERTY_VALUE_PART_TYPE=6,Parser.SELECTOR_TYPE=7,Parser.SELECTOR_PART_TYPE=8,Parser.SELECTOR_SUB_PART_TYPE=9,Parser.prototype=function(){var e=new EventTarget,t,n={constructor:Parser,DEFAULT_TYPE:0,COMBINATOR_TYPE:1,MEDIA_FEATURE_TYPE:2,MEDIA_QUERY_TYPE:3,PROPERTY_NAME_TYPE:4,PROPERTY_VALUE_TYPE:5,PROPERTY_VALUE_PART_TYPE:6,SELECTOR_TYPE:7,SELECTOR_PART_TYPE:8,SELECTOR_SUB_PART_TYPE:9,_stylesheet:function(){var e=this._tokenStream,t=null,n,r,i;this.fire("startstylesheet"),this._charset(),this._skipCruft();while(e.peek()==Tokens.IMPORT_SYM)this._import(),this._skipCruft();while(e.peek()==Tokens.NAMESPACE_SYM)this._namespace(),this._skipCruft();i=e.peek();while(i>Tokens.EOF){try{switch(i){case Tokens.MEDIA_SYM:this._media(),this._skipCruft();break;case Tokens.PAGE_SYM:this._page(),this._skipCruft();break;case Tokens.FONT_FACE_SYM:this._font_face(),this._skipCruft();break;case Tokens.KEYFRAMES_SYM:this._keyframes(),this._skipCruft();break;case Tokens.UNKNOWN_SYM:e.get();if(!!this.options.strict)throw new SyntaxError("Unknown @ rule.",e.LT(0).startLine,e.LT(0).startCol);this.fire({type:"error",error:null,message:"Unknown @ rule: "+e.LT(0).value+".",line:e.LT(0).startLine,col:e.LT(0).startCol}),n=0;while(e.advance([Tokens.LBRACE,Tokens.RBRACE])==Tokens.LBRACE)n++;while(n)e.advance([Tokens.RBRACE]),n--;break;case Tokens.S:this._readWhitespace();break;default:if(!this._ruleset())switch(i){case Tokens.CHARSET_SYM:throw r=e.LT(1),this._charset(!1),new SyntaxError("@charset not allowed here.",r.startLine,r.startCol);case Tokens.IMPORT_SYM:throw r=e.LT(1),this._import(!1),new SyntaxError("@import not allowed here.",r.startLine,r.startCol);case Tokens.NAMESPACE_SYM:throw r=e.LT(1),this._namespace(!1),new SyntaxError("@namespace not allowed here.",r.startLine,r.startCol);default:e.get(),this._unexpectedToken(e.token())}}}catch(s){if(!(s instanceof SyntaxError&&!this.options.strict))throw s;this.fire({type:"error",error:s,message:s.message,line:s.line,col:s.col})}i=e.peek()}i!=Tokens.EOF&&this._unexpectedToken(e.token()),this.fire("endstylesheet")},_charset:function(e){var t=this._tokenStream,n,r,i,s;t.match(Tokens.CHARSET_SYM)&&(i=t.token().startLine,s=t.token().startCol,this._readWhitespace(),t.mustMatch(Tokens.STRING),r=t.token(),n=r.value,this._readWhitespace(),t.mustMatch(Tokens.SEMICOLON),e!==!1&&this.fire({type:"charset",charset:n,line:i,col:s}))},_import:function(e){var t=this._tokenStream,n,r,i,s=[];t.mustMatch(Tokens.IMPORT_SYM),i=t.token(),this._readWhitespace(),t.mustMatch([Tokens.STRING,Tokens.URI]),r=t.token().value.replace(/(?:url\()?["']([^"']+)["']\)?/,"$1"),this._readWhitespace(),s=this._media_query_list(),t.mustMatch(Tokens.SEMICOLON),this._readWhitespace(),e!==!1&&this.fire({type:"import",uri:r,media:s,line:i.startLine,col:i.startCol})},_namespace:function(e){var t=this._tokenStream,n,r,i,s;t.mustMatch(Tokens.NAMESPACE_SYM),n=t.token().startLine,r=t.token().startCol,this._readWhitespace(),t.match(Tokens.IDENT)&&(i=t.token().value,this._readWhitespace()),t.mustMatch([Tokens.STRING,Tokens.URI]),s=t.token().value.replace(/(?:url\()?["']([^"']+)["']\)?/,"$1"),this._readWhitespace(),t.mustMatch(Tokens.SEMICOLON),this._readWhitespace(),e!==!1&&this.fire({type:"namespace",prefix:i,uri:s,line:n,col:r})},_media:function(){var e=this._tokenStream,t,n,r;e.mustMatch(Tokens.MEDIA_SYM),t=e.token().startLine,n=e.token().startCol,this._readWhitespace(),r=this._media_query_list(),e.mustMatch(Tokens.LBRACE),this._readWhitespace(),this.fire({type:"startmedia",media:r,line:t,col:n});for(;;)if(e.peek()==Tokens.PAGE_SYM)this._page();else if(!this._ruleset())break;e.mustMatch(Tokens.RBRACE),this._readWhitespace(),this.fire({type:"endmedia",media:r,line:t,col:n})},_media_query_list:function(){var e=this._tokenStream,t=[];this._readWhitespace(),(e.peek()==Tokens.IDENT||e.peek()==Tokens.LPAREN)&&t.push(this._media_query());while(e.match(Tokens.COMMA))this._readWhitespace(),t.push(this._media_query());return t},_media_query:function(){var e=this._tokenStream,t=null,n=null,r=null,i=[];e.match(Tokens.IDENT)&&(n=e.token().value.toLowerCase(),n!="only"&&n!="not"?(e.unget(),n=null):r=e.token()),this._readWhitespace(),e.peek()==Tokens.IDENT?(t=this._media_type(),r===null&&(r=e.token())):e.peek()==Tokens.LPAREN&&(r===null&&(r=e.LT(1)),i.push(this._media_expression()));if(t===null&&i.length===0)return null;this._readWhitespace();while(e.match(Tokens.IDENT))e.token().value.toLowerCase()!="and"&&this._unexpectedToken(e.token()),this._readWhitespace(),i.push(this._media_expression());return new MediaQuery(n,t,i,r.startLine,r.startCol)},_media_type:function(){return this._media_feature()},_media_expression:function(){var e=this._tokenStream,t=null,n,r=null;return e.mustMatch(Tokens.LPAREN),t=this._media_feature(),this._readWhitespace(),e.match(Tokens.COLON)&&(this._readWhitespace(),n=e.LT(1),r=this._expression()),e.mustMatch(Tokens.RPAREN),this._readWhitespace(),new MediaFeature(t,r?new SyntaxUnit(r,n.startLine,n.startCol):null)},_media_feature:function(){var e=this._tokenStream;return e.mustMatch(Tokens.IDENT),SyntaxUnit.fromToken(e.token())},_page:function(){var e=this._tokenStream,t,n,r=null,i=null;e.mustMatch(Tokens.PAGE_SYM),t=e.token().startLine,n=e.token().startCol,this._readWhitespace(),e.match(Tokens.IDENT)&&(r=e.token().value,r.toLowerCase()==="auto"&&this._unexpectedToken(e.token())),e.peek()==Tokens.COLON&&(i=this._pseudo_page()),this._readWhitespace(),this.fire({type:"startpage",id:r,pseudo:i,line:t,col:n}),this._readDeclarations(!0,!0),this.fire({type:"endpage",id:r,pseudo:i,line:t,col:n})},_margin:function(){var e=this._tokenStream,t,n,r=this._margin_sym();return r?(t=e.token().startLine,n=e.token().startCol,this.fire({type:"startpagemargin",margin:r,line:t,col:n}),this._readDeclarations(!0),this.fire({type:"endpagemargin",margin:r,line:t,col:n}),!0):!1},_margin_sym:function(){var e=this._tokenStream;return e.match([Tokens.TOPLEFTCORNER_SYM,Tokens.TOPLEFT_SYM,Tokens.TOPCENTER_SYM,Tokens.TOPRIGHT_SYM,Tokens.TOPRIGHTCORNER_SYM,Tokens.BOTTOMLEFTCORNER_SYM,Tokens.BOTTOMLEFT_SYM,Tokens.BOTTOMCENTER_SYM,Tokens.BOTTOMRIGHT_SYM,Tokens.BOTTOMRIGHTCORNER_SYM,Tokens.LEFTTOP_SYM,Tokens.LEFTMIDDLE_SYM,Tokens.LEFTBOTTOM_SYM,Tokens.RIGHTTOP_SYM,Tokens.RIGHTMIDDLE_SYM,Tokens.RIGHTBOTTOM_SYM])?SyntaxUnit.fromToken(e.token()):null},_pseudo_page:function(){var e=this._tokenStream;return e.mustMatch(Tokens.COLON),e.mustMatch(Tokens.IDENT),e.token().value},_font_face:function(){var e=this._tokenStream,t,n;e.mustMatch(Tokens.FONT_FACE_SYM),t=e.token().startLine,n=e.token().startCol,this._readWhitespace(),this.fire({type:"startfontface",line:t,col:n}),this._readDeclarations(!0),this.fire({type:"endfontface",line:t,col:n})},_operator:function(){var e=this._tokenStream,t=null;return e.match([Tokens.SLASH,Tokens.COMMA])&&(t=e.token(),this._readWhitespace()),t?PropertyValuePart.fromToken(t):null},_combinator:function(){var e=this._tokenStream,t=null,n;return e.match([Tokens.PLUS,Tokens.GREATER,Tokens.TILDE])&&(n=e.token(),t=new Combinator(n.value,n.startLine,n.startCol),this._readWhitespace()),t},_unary_operator:function(){var e=this._tokenStream;return e.match([Tokens.MINUS,Tokens.PLUS])?e.token().value:null},_property:function(){var e=this._tokenStream,t=null,n=null,r,i,s,o;return e.peek()==Tokens.STAR&&this.options.starHack&&(e.get(),i=e.token(),n=i.value,s=i.startLine,o=i.startCol),e.match(Tokens.IDENT)&&(i=e.token(),r=i.value,r.charAt(0)=="_"&&this.options.underscoreHack&&(n="_",r=r.substring(1)),t=new PropertyName(r,n,s||i.startLine,o||i.startCol),this._readWhitespace()),t},_ruleset:function(){var e=this._tokenStream,t,n;try{n=this._selectors_group()}catch(r){if(r instanceof SyntaxError&&!this.options.strict){this.fire({type:"error",error:r,message:r.message,line:r.line,col:r.col}),t=e.advance([Tokens.RBRACE]);if(t!=Tokens.RBRACE)throw r;return!0}throw r}return n&&(this.fire({type:"startrule",selectors:n,line:n[0].line,col:n[0].col}),this._readDeclarations(!0),this.fire({type:"endrule",selectors:n,line:n[0].line,col:n[0].col})),n},_selectors_group:function(){var e=this._tokenStream,t=[],n;n=this._selector();if(n!==null){t.push(n);while(e.match(Tokens.COMMA))this._readWhitespace(),n=this._selector(),n!==null?t.push(n):this._unexpectedToken(e.LT(1))}return t.length?t:null},_selector:function(){var e=this._tokenStream,t=[],n=null,r=null,i=null;n=this._simple_selector_sequence();if(n===null)return null;t.push(n);do{r=this._combinator();if(r!==null)t.push(r),n=this._simple_selector_sequence(),n===null?this._unexpectedToken(e.LT(1)):t.push(n);else{if(!this._readWhitespace())break;i=new Combinator(e.token().value,e.token().startLine,e.token().startCol),r=this._combinator(),n=this._simple_selector_sequence(),n===null?r!==null&&this._unexpectedToken(e.LT(1)):(r!==null?t.push(r):t.push(i),t.push(n))}}while(!0);return new Selector(t,t[0].line,t[0].col)},_simple_selector_sequence:function(){var e=this._tokenStream,t=null,n=[],r="",i=[function(){return e.match(Tokens.HASH)?new SelectorSubPart(e.token().value,"id",e.token().startLine,e.token().startCol):null},this._class,this._attrib,this._pseudo,this._negation],s=0,o=i.length,u=null,a=!1,f,l;f=e.LT(1).startLine,l=e.LT(1).startCol,t=this._type_selector(),t||(t=this._universal()),t!==null&&(r+=t);for(;;){if(e.peek()===Tokens.S)break;while(s<o&&u===null)u=i[s++].call(this);if(u===null){if(r==="")return null;break}s=0,n.push(u),r+=u.toString(),u=null}return r!==""?new SelectorPart(t,n,r,f,l):null},_type_selector:function(){var e=this._tokenStream,t=this._namespace_prefix(),n=this._element_name();return n?(t&&(n.text=t+n.text,n.col-=t.length),n):(t&&(e.unget(),t.length>1&&e.unget()),null)},_class:function(){var e=this._tokenStream,t;return e.match(Tokens.DOT)?(e.mustMatch(Tokens.IDENT),t=e.token(),new SelectorSubPart("."+t.value,"class",t.startLine,t.startCol-1)):null},_element_name:function(){var e=this._tokenStream,t;return e.match(Tokens.IDENT)?(t=e.token(),new SelectorSubPart(t.value,"elementName",t.startLine,t.startCol)):null},_namespace_prefix:function(){var e=this._tokenStream,t="";if(e.LA(1)===Tokens.PIPE||e.LA(2)===Tokens.PIPE)e.match([Tokens.IDENT,Tokens.STAR])&&(t+=e.token().value),e.mustMatch(Tokens.PIPE),t+="|";return t.length?t:null},_universal:function(){var e=this._tokenStream,t="",n;return n=this._namespace_prefix(),n&&(t+=n),e.match(Tokens.STAR)&&(t+="*"),t.length?t:null},_attrib:function(){var e=this._tokenStream,t=null,n,r;return e.match(Tokens.LBRACKET)?(r=e.token(),t=r.value,t+=this._readWhitespace(),n=this._namespace_prefix(),n&&(t+=n),e.mustMatch(Tokens.IDENT),t+=e.token().value,t+=this._readWhitespace(),e.match([Tokens.PREFIXMATCH,Tokens.SUFFIXMATCH,Tokens.SUBSTRINGMATCH,Tokens.EQUALS,Tokens.INCLUDES,Tokens.DASHMATCH])&&(t+=e.token().value,t+=this._readWhitespace(),e.mustMatch([Tokens.IDENT,Tokens.STRING]),t+=e.token().value,t+=this._readWhitespace()),e.mustMatch(Tokens.RBRACKET),new SelectorSubPart(t+"]","attribute",r.startLine,r.startCol)):null},_pseudo:function(){var e=this._tokenStream,t=null,n=":",r,i;return e.match(Tokens.COLON)&&(e.match(Tokens.COLON)&&(n+=":"),e.match(Tokens.IDENT)?(t=e.token().value,r=e.token().startLine,i=e.token().startCol-n.length):e.peek()==Tokens.FUNCTION&&(r=e.LT(1).startLine,i=e.LT(1).startCol-n.length,t=this._functional_pseudo()),t&&(t=new SelectorSubPart(n+t,"pseudo",r,i))),t},_functional_pseudo:function(){var e=this._tokenStream,t=null;return e.match(Tokens.FUNCTION)&&(t=e.token().value,t+=this._readWhitespace(),t+=this._expression(),e.mustMatch(Tokens.RPAREN),t+=")"),t},_expression:function(){var e=this._tokenStream,t="";while(e.match([Tokens.PLUS,Tokens.MINUS,Tokens.DIMENSION,Tokens.NUMBER,Tokens.STRING,Tokens.IDENT,Tokens.LENGTH,Tokens.FREQ,Tokens.ANGLE,Tokens.TIME,Tokens.RESOLUTION]))t+=e.token().value,t+=this._readWhitespace();return t.length?t:null},_negation:function(){var e=this._tokenStream,t,n,r="",i,s=null;return e.match(Tokens.NOT)&&(r=e.token().value,t=e.token().startLine,n=e.token().startCol,r+=this._readWhitespace(),i=this._negation_arg(),r+=i,r+=this._readWhitespace(),e.match(Tokens.RPAREN),r+=e.token().value,s=new SelectorSubPart(r,"not",t,n),s.args.push(i)),s},_negation_arg:function(){var e=this._tokenStream,t=[this._type_selector,this._universal,function(){return e.match(Tokens.HASH)?new SelectorSubPart(e.token().value,"id",e.token().startLine,e.token().startCol):null},this._class,this._attrib,this._pseudo],n=null,r=0,i=t.length,s,o,u,a;o=e.LT(1).startLine,u=e.LT(1).startCol;while(r<i&&n===null)n=t[r].call(this),r++;return n===null&&this._unexpectedToken(e.LT(1)),n.type=="elementName"?a=new SelectorPart(n,[],n.toString(),o,u):a=new SelectorPart(null,[n],n.toString(),o,u),a},_declaration:function(){var e=this._tokenStream,t=null,n=null,r=null,i=null,s=null,o="";t=this._property();if(t!==null){e.mustMatch(Tokens.COLON),this._readWhitespace(),n=this._expr(),(!n||n.length===0)&&this._unexpectedToken(e.LT(1)),r=this._prio(),o=t.toString();if(this.options.starHack&&t.hack=="*"||this.options.underscoreHack&&t.hack=="_")o=t.text;try{this._validateProperty(o,n)}catch(u){s=u}return this.fire({type:"property",property:t,value:n,important:r,line:t.line,col:t.col,invalid:s}),!0}return!1},_prio:function(){var e=this._tokenStream,t=e.match(Tokens.IMPORTANT_SYM);return this._readWhitespace(),t},_expr:function(){var e=this._tokenStream,t=[],n=null,r=null;n=this._term();if(n!==null){t.push(n);do{r=this._operator(),r&&t.push(r),n=this._term();if(n===null)break;t.push(n)}while(!0)}return t.length>0?new PropertyValue(t,t[0].line,t[0].col):null},_term:function(){var e=this._tokenStream,t=null,n=null,r,i,s;return t=this._unary_operator(),t!==null&&(i=e.token().startLine,s=e.token().startCol),e.peek()==Tokens.IE_FUNCTION&&this.options.ieFilters?(n=this._ie_function(),t===null&&(i=e.token().startLine,s=e.token().startCol)):e.match([Tokens.NUMBER,Tokens.PERCENTAGE,Tokens.LENGTH,Tokens.ANGLE,Tokens.TIME,Tokens.FREQ,Tokens.STRING,Tokens.IDENT,Tokens.URI,Tokens.UNICODE_RANGE])?(n=e.token().value,t===null&&(i=e.token().startLine,s=e.token().startCol),this._readWhitespace()):(r=this._hexcolor(),r===null?(t===null&&(i=e.LT(1).startLine,s=e.LT(1).startCol),n===null&&(e.LA(3)==Tokens.EQUALS&&this.options.ieFilters?n=this._ie_function():n=this._function())):(n=r.value,t===null&&(i=r.startLine,s=r.startCol))),n!==null?new PropertyValuePart(t!==null?t+n:n,i,s):null},_function:function(){var e=this._tokenStream,t=null,n=null,r;if(e.match(Tokens.FUNCTION)){t=e.token().value,this._readWhitespace(),n=this._expr(),t+=n;if(this.options.ieFilters&&e.peek()==Tokens.EQUALS)do{this._readWhitespace()&&(t+=e.token().value),e.LA(0)==Tokens.COMMA&&(t+=e.token().value),e.match(Tokens.IDENT),t+=e.token().value,e.match(Tokens.EQUALS),t+=e.token().value,r=e.peek();while(r!=Tokens.COMMA&&r!=Tokens.S&&r!=Tokens.RPAREN)e.get(),t+=e.token().value,r=e.peek()}while(e.match([Tokens.COMMA,Tokens.S]));e.match(Tokens.RPAREN),t+=")",this._readWhitespace()}return t},_ie_function:function(){var e=this._tokenStream,t=null,n=null,r;if(e.match([Tokens.IE_FUNCTION,Tokens.FUNCTION])){t=e.token().value;do{this._readWhitespace()&&(t+=e.token().value),e.LA(0)==Tokens.COMMA&&(t+=e.token().value),e.match(Tokens.IDENT),t+=e.token().value,e.match(Tokens.EQUALS),t+=e.token().value,r=e.peek();while(r!=Tokens.COMMA&&r!=Tokens.S&&r!=Tokens.RPAREN)e.get(),t+=e.token().value,r=e.peek()}while(e.match([Tokens.COMMA,Tokens.S]));e.match(Tokens.RPAREN),t+=")",this._readWhitespace()}return t},_hexcolor:function(){var e=this._tokenStream,t=null,n;if(e.match(Tokens.HASH)){t=e.token(),n=t.value;if(!/#[a-f0-9]{3,6}/i.test(n))throw new SyntaxError("Expected a hex color but found '"+n+"' at line "+t.startLine+", col "+t.startCol+".",t.startLine,t.startCol);this._readWhitespace()}return t},_keyframes:function(){var e=this._tokenStream,t,n,r,i="";e.mustMatch(Tokens.KEYFRAMES_SYM),t=e.token(),/^@\-([^\-]+)\-/.test(t.value)&&(i=RegExp.$1),this._readWhitespace(),r=this._keyframe_name(),this._readWhitespace(),e.mustMatch(Tokens.LBRACE),this.fire({type:"startkeyframes",name:r,prefix:i,line:t.startLine,col:t.startCol}),this._readWhitespace(),n=e.peek();while(n==Tokens.IDENT||n==Tokens.PERCENTAGE)this._keyframe_rule(),this._readWhitespace(),n=e.peek();this.fire({type:"endkeyframes",name:r,prefix:i,line:t.startLine,col:t.startCol}),this._readWhitespace(),e.mustMatch(Tokens.RBRACE)},_keyframe_name:function(){var e=this._tokenStream,t;return e.mustMatch([Tokens.IDENT,Tokens.STRING]),SyntaxUnit.fromToken(e.token())},_keyframe_rule:function(){var e=this._tokenStream,t,n=this._key_list();this.fire({type:"startkeyframerule",keys:n,line:n[0].line,col:n[0].col}),this._readDeclarations(!0),this.fire({type:"endkeyframerule",keys:n,line:n[0].line,col:n[0].col})},_key_list:function(){var e=this._tokenStream,t,n,r=[];r.push(this._key()),this._readWhitespace();while(e.match(Tokens.COMMA))this._readWhitespace(),r.push(this._key()),this._readWhitespace();return r},_key:function(){var e=this._tokenStream,t;if(e.match(Tokens.PERCENTAGE))return SyntaxUnit.fromToken(e.token());if(e.match(Tokens.IDENT)){t=e.token();if(/from|to/i.test(t.value))return SyntaxUnit.fromToken(t);e.unget()}this._unexpectedToken(e.LT(1))},_skipCruft:function(){while(this._tokenStream.match([Tokens.S,Tokens.CDO,Tokens.CDC]));},_readDeclarations:function(e,t){var n=this._tokenStream,r;this._readWhitespace(),e&&n.mustMatch(Tokens.LBRACE),this._readWhitespace();try{for(;;){if(!(n.match(Tokens.SEMICOLON)||t&&this._margin())){if(!this._declaration())break;if(!n.match(Tokens.SEMICOLON))break}this._readWhitespace()}n.mustMatch(Tokens.RBRACE),this._readWhitespace()}catch(i){if(!(i instanceof SyntaxError&&!this.options.strict))throw i;this.fire({type:"error",error:i,message:i.message,line:i.line,col:i.col}),r=n.advance([Tokens.SEMICOLON,Tokens.RBRACE]);if(r==Tokens.SEMICOLON)this._readDeclarations(!1,t);else if(r!=Tokens.RBRACE)throw i}},_readWhitespace:function(){var e=this._tokenStream,t="";while(e.match(Tokens.S))t+=e.token().value;return t},_unexpectedToken:function(e){throw new SyntaxError("Unexpected token '"+e.value+"' at line "+e.startLine+", col "+e.startCol+".",e.startLine,e.startCol)},_verifyEnd:function(){this._tokenStream.LA(1)!=Tokens.EOF&&this._unexpectedToken(this._tokenStream.LT(1))},_validateProperty:function(e,t){Validation.validate(e,t)},parse:function(e){this._tokenStream=new TokenStream(e,Tokens),this._stylesheet()},parseStyleSheet:function(e){return this.parse(e)},parseMediaQuery:function(e){this._tokenStream=new TokenStream(e,Tokens);var t=this._media_query();return this._verifyEnd(),t},parsePropertyValue:function(e){this._tokenStream=new TokenStream(e,Tokens),this._readWhitespace();var t=this._expr();return this._readWhitespace(),this._verifyEnd(),t},parseRule:function(e){this._tokenStream=new TokenStream(e,Tokens),this._readWhitespace();var t=this._ruleset();return this._readWhitespace(),this._verifyEnd(),t},parseSelector:function(e){this._tokenStream=new TokenStream(e,Tokens),this._readWhitespace();var t=this._selector();return this._readWhitespace(),this._verifyEnd(),t},parseStyleAttribute:function(e){e+="}",this._tokenStream=new TokenStream(e,Tokens),this._readDeclarations()}};for(t in n)n.hasOwnProperty(t)&&(e[t]=n[t]);return e}();var Properties={"alignment-adjust":"auto | baseline | before-edge | text-before-edge | middle | central | after-edge | text-after-edge | ideographic | alphabetic | hanging | mathematical | <percentage> | <length>","alignment-baseline":"baseline | use-script | before-edge | text-before-edge | after-edge | text-after-edge | central | middle | ideographic | alphabetic | hanging | mathematical",animation:1,"animation-delay":{multi:"<time>",comma:!0},"animation-direction":{multi:"normal | alternate",comma:!0},"animation-duration":{multi:"<time>",comma:!0},"animation-iteration-count":{multi:"<number> | infinite",comma:!0},"animation-name":{multi:"none | <ident>",comma:!0},"animation-play-state":{multi:"running | paused",comma:!0},"animation-timing-function":1,"-moz-animation-delay":{multi:"<time>",comma:!0},"-moz-animation-direction":{multi:"normal | alternate",comma:!0},"-moz-animation-duration":{multi:"<time>",comma:!0},"-moz-animation-iteration-count":{multi:"<number> | infinite",comma:!0},"-moz-animation-name":{multi:"none | <ident>",comma:!0},"-moz-animation-play-state":{multi:"running | paused",comma:!0},"-ms-animation-delay":{multi:"<time>",comma:!0},"-ms-animation-direction":{multi:"normal | alternate",comma:!0},"-ms-animation-duration":{multi:"<time>",comma:!0},"-ms-animation-iteration-count":{multi:"<number> | infinite",comma:!0},"-ms-animation-name":{multi:"none | <ident>",comma:!0},"-ms-animation-play-state":{multi:"running | paused",comma:!0},"-webkit-animation-delay":{multi:"<time>",comma:!0},"-webkit-animation-direction":{multi:"normal | alternate",comma:!0},"-webkit-animation-duration":{multi:"<time>",comma:!0},"-webkit-animation-iteration-count":{multi:"<number> | infinite",comma:!0},"-webkit-animation-name":{multi:"none | <ident>",comma:!0},"-webkit-animation-play-state":{multi:"running | paused",comma:!0},"-o-animation-delay":{multi:"<time>",comma:!0},"-o-animation-direction":{multi:"normal | alternate",comma:!0},"-o-animation-duration":{multi:"<time>",comma:!0},"-o-animation-iteration-count":{multi:"<number> | infinite",comma:!0},"-o-animation-name":{multi:"none | <ident>",comma:!0},"-o-animation-play-state":{multi:"running | paused",comma:!0},appearance:"icon | window | desktop | workspace | document | tooltip | dialog | button | push-button | hyperlink | radio-button | checkbox | menu-item | tab | menu | menubar | pull-down-menu | pop-up-menu | list-menu | radio-group | checkbox-group | outline-tree | range | field | combo-box | signature | password | normal | inherit",azimuth:function(e){var t="<angle> | leftwards | rightwards | inherit",n="left-side | far-left | left | center-left | center | center-right | right | far-right | right-side",r=!1,i=!1,s;ValidationTypes.isAny(e,t)||(ValidationTypes.isAny(e,"behind")&&(r=!0,i=!0),ValidationTypes.isAny(e,n)&&(i=!0,r||ValidationTypes.isAny(e,"behind")));if(e.hasNext())throw s=e.next(),i?new ValidationError("Expected end of value but found '"+s+"'.",s.line,s.col):new ValidationError("Expected (<'azimuth'>) but found '"+s+"'.",s.line,s.col)},"backface-visibility":"visible | hidden",background:1,"background-attachment":{multi:"<attachment>",comma:!0},"background-clip":{multi:"<box>",comma:!0},"background-color":"<color> | inherit","background-image":{multi:"<bg-image>",comma:!0},"background-origin":{multi:"<box>",comma:!0},"background-position":{multi:"<bg-position>",comma:!0},"background-repeat":{multi:"<repeat-style>"},"background-size":{multi:"<bg-size>",comma:!0},"baseline-shift":"baseline | sub | super | <percentage> | <length>",behavior:1,binding:1,bleed:"<length>","bookmark-label":"<content> | <attr> | <string>","bookmark-level":"none | <integer>","bookmark-state":"open | closed","bookmark-target":"none | <uri> | <attr>",border:"<border-width> || <border-style> || <color>","border-bottom":"<border-width> || <border-style> || <color>","border-bottom-color":"<color>","border-bottom-left-radius":"<x-one-radius>","border-bottom-right-radius":"<x-one-radius>","border-bottom-style":"<border-style>","border-bottom-width":"<border-width>","border-collapse":"collapse | separate | inherit","border-color":{multi:"<color> | inherit",max:4},"border-image":1,"border-image-outset":{multi:"<length> | <number>",max:4},"border-image-repeat":{multi:"stretch | repeat | round",max:2},"border-image-slice":function(e){var t=!1,n="<number> | <percentage>",r=!1,i=0,s=4,o;ValidationTypes.isAny(e,"fill")&&(r=!0,t=!0);while(e.hasNext()&&i<s){t=ValidationTypes.isAny(e,n);if(!t)break;i++}r?t=!0:ValidationTypes.isAny(e,"fill");if(e.hasNext())throw o=e.next(),t?new ValidationError("Expected end of value but found '"+o+"'.",o.line,o.col):new ValidationError("Expected ([<number> | <percentage>]{1,4} && fill?) but found '"+o+"'.",o.line,o.col)},"border-image-source":"<image> | none","border-image-width":{multi:"<length> | <percentage> | <number> | auto",max:4},"border-left":"<border-width> || <border-style> || <color>","border-left-color":"<color> | inherit","border-left-style":"<border-style>","border-left-width":"<border-width>","border-radius":function(e){var t=!1,n="<length> | <percentage>",r=!1,i=!1,s=0,o=8,u;while(e.hasNext()&&s<o){t=ValidationTypes.isAny(e,n);if(!t){if(!(e.peek()=="/"&&s>1&&!r))break;r=!0,o=s+5,e.next()}s++}if(e.hasNext())throw u=e.next(),t?new ValidationError("Expected end of value but found '"+u+"'.",u.line,u.col):new ValidationError("Expected (<'border-radius'>) but found '"+u+"'.",u.line,u.col)},"border-right":"<border-width> || <border-style> || <color>","border-right-color":"<color> | inherit","border-right-style":"<border-style>","border-right-width":"<border-width>","border-spacing":{multi:"<length> | inherit",max:2},"border-style":{multi:"<border-style>",max:4},"border-top":"<border-width> || <border-style> || <color>","border-top-color":"<color> | inherit","border-top-left-radius":"<x-one-radius>","border-top-right-radius":"<x-one-radius>","border-top-style":"<border-style>","border-top-width":"<border-width>","border-width":{multi:"<border-width>",max:4},bottom:"<margin-width> | inherit","box-align":"start | end | center | baseline | stretch","box-decoration-break":"slice |clone","box-direction":"normal | reverse | inherit","box-flex":"<number>","box-flex-group":"<integer>","box-lines":"single | multiple","box-ordinal-group":"<integer>","box-orient":"horizontal | vertical | inline-axis | block-axis | inherit","box-pack":"start | end | center | justify","box-shadow":function(e){var t=!1,n;if(!ValidationTypes.isAny(e,"none"))Validation.multiProperty("<shadow>",e,!0,Infinity);else if(e.hasNext())throw n=e.next(),new ValidationError("Expected end of value but found '"+n+"'.",n.line,n.col)},"box-sizing":"content-box | border-box | inherit","break-after":"auto | always | avoid | left | right | page | column | avoid-page | avoid-column","break-before":"auto | always | avoid | left | right | page | column | avoid-page | avoid-column","break-inside":"auto | avoid | avoid-page | avoid-column","caption-side":"top | bottom | inherit",clear:"none | right | left | both | inherit",clip:1,color:"<color> | inherit","color-profile":1,"column-count":"<integer> | auto","column-fill":"auto | balance","column-gap":"<length> | normal","column-rule":"<border-width> || <border-style> || <color>","column-rule-color":"<color>","column-rule-style":"<border-style>","column-rule-width":"<border-width>","column-span":"none | all","column-width":"<length> | auto",columns:1,content:1,"counter-increment":1,"counter-reset":1,crop:"<shape> | auto",cue:"cue-after | cue-before | inherit","cue-after":1,"cue-before":1,cursor:1,direction:"ltr | rtl | inherit",display:"inline | block | list-item | inline-block | table | inline-table | table-row-group | table-header-group | table-footer-group | table-row | table-column-group | table-column | table-cell | table-caption | box | inline-box | grid | inline-grid | none | inherit","dominant-baseline":1,"drop-initial-after-adjust":"central | middle | after-edge | text-after-edge | ideographic | alphabetic | mathematical | <percentage> | <length>","drop-initial-after-align":"baseline | use-script | before-edge | text-before-edge | after-edge | text-after-edge | central | middle | ideographic | alphabetic | hanging | mathematical","drop-initial-before-adjust":"before-edge | text-before-edge | central | middle | hanging | mathematical | <percentage> | <length>","drop-initial-before-align":"caps-height | baseline | use-script | before-edge | text-before-edge | after-edge | text-after-edge | central | middle | ideographic | alphabetic | hanging | mathematical","drop-initial-size":"auto | line | <length> | <percentage>","drop-initial-value":"initial | <integer>",elevation:"<angle> | below | level | above | higher | lower | inherit","empty-cells":"show | hide | inherit",filter:1,fit:"fill | hidden | meet | slice","fit-position":1,"float":"left | right | none | inherit","float-offset":1,font:1,"font-family":1,"font-size":"<absolute-size> | <relative-size> | <length> | <percentage> | inherit","font-size-adjust":"<number> | none | inherit","font-stretch":"normal | ultra-condensed | extra-condensed | condensed | semi-condensed | semi-expanded | expanded | extra-expanded | ultra-expanded | inherit","font-style":"normal | italic | oblique | inherit","font-variant":"normal | small-caps | inherit","font-weight":"normal | bold | bolder | lighter | 100 | 200 | 300 | 400 | 500 | 600 | 700 | 800 | 900 | inherit","grid-cell-stacking":"columns | rows | layer","grid-column":1,"grid-columns":1,"grid-column-align":"start | end | center | stretch","grid-column-sizing":1,"grid-column-span":"<integer>","grid-flow":"none | rows | columns","grid-layer":"<integer>","grid-row":1,"grid-rows":1,"grid-row-align":"start | end | center | stretch","grid-row-span":"<integer>","grid-row-sizing":1,"hanging-punctuation":1,height:"<margin-width> | inherit","hyphenate-after":"<integer> | auto","hyphenate-before":"<integer> | auto","hyphenate-character":"<string> | auto","hyphenate-lines":"no-limit | <integer>","hyphenate-resource":1,hyphens:"none | manual | auto",icon:1,"image-orientation":"angle | auto","image-rendering":1,"image-resolution":1,"inline-box-align":"initial | last | <integer>",left:"<margin-width> | inherit","letter-spacing":"<length> | normal | inherit","line-height":"<number> | <length> | <percentage> | normal | inherit","line-break":"auto | loose | normal | strict","line-stacking":1,"line-stacking-ruby":"exclude-ruby | include-ruby","line-stacking-shift":"consider-shifts | disregard-shifts","line-stacking-strategy":"inline-line-height | block-line-height | max-height | grid-height","list-style":1,"list-style-image":"<uri> | none | inherit","list-style-position":"inside | outside | inherit","list-style-type":"disc | circle | square | decimal | decimal-leading-zero | lower-roman | upper-roman | lower-greek | lower-latin | upper-latin | armenian | georgian | lower-alpha | upper-alpha | none | inherit",margin:{multi:"<margin-width> | inherit",max:4},"margin-bottom":"<margin-width> | inherit","margin-left":"<margin-width> | inherit","margin-right":"<margin-width> | inherit","margin-top":"<margin-width> | inherit",mark:1,"mark-after":1,"mark-before":1,marks:1,"marquee-direction":1,"marquee-play-count":1,"marquee-speed":1,"marquee-style":1,"max-height":"<length> | <percentage> | none | inherit","max-width":"<length> | <percentage> | none | inherit","min-height":"<length> | <percentage> | inherit","min-width":"<length> | <percentage> | inherit","move-to":1,"nav-down":1,"nav-index":1,"nav-left":1,"nav-right":1,"nav-up":1,opacity:"<number> | inherit",orphans:"<integer> | inherit",outline:1,"outline-color":"<color> | invert | inherit","outline-offset":1,"outline-style":"<border-style> | inherit","outline-width":"<border-width> | inherit",overflow:"visible | hidden | scroll | auto | inherit","overflow-style":1,"overflow-x":1,"overflow-y":1,padding:{multi:"<padding-width> | inherit",max:4},"padding-bottom":"<padding-width> | inherit","padding-left":"<padding-width> | inherit","padding-right":"<padding-width> | inherit","padding-top":"<padding-width> | inherit",page:1,"page-break-after":"auto | always | avoid | left | right | inherit","page-break-before":"auto | always | avoid | left | right | inherit","page-break-inside":"auto | avoid | inherit","page-policy":1,pause:1,"pause-after":1,"pause-before":1,perspective:1,"perspective-origin":1,phonemes:1,pitch:1,"pitch-range":1,"play-during":1,"pointer-events":"auto | none | visiblePainted | visibleFill | visibleStroke | visible | painted | fill | stroke | all | inherit",position:"static | relative | absolute | fixed | inherit","presentation-level":1,"punctuation-trim":1,quotes:1,"rendering-intent":1,resize:1,rest:1,"rest-after":1,"rest-before":1,richness:1,right:"<margin-width> | inherit",rotation:1,"rotation-point":1,"ruby-align":1,"ruby-overhang":1,"ruby-position":1,"ruby-span":1,size:1,speak:"normal | none | spell-out | inherit","speak-header":"once | always | inherit","speak-numeral":"digits | continuous | inherit","speak-punctuation":"code | none | inherit","speech-rate":1,src:1,stress:1,"string-set":1,"table-layout":"auto | fixed | inherit","tab-size":"<integer> | <length>",target:1,"target-name":1,"target-new":1,"target-position":1,"text-align":"left | right | center | justify | inherit","text-align-last":1,"text-decoration":1,"text-emphasis":1,"text-height":1,"text-indent":"<length> | <percentage> | inherit","text-justify":"auto | none | inter-word | inter-ideograph | inter-cluster | distribute | kashida","text-outline":1,"text-overflow":1,"text-rendering":"auto | optimizeSpeed | optimizeLegibility | geometricPrecision | inherit","text-shadow":1,"text-transform":"capitalize | uppercase | lowercase | none | inherit","text-wrap":"normal | none | avoid",top:"<margin-width> | inherit",transform:1,"transform-origin":1,"transform-style":1,transition:1,"transition-delay":1,"transition-duration":1,"transition-property":1,"transition-timing-function":1,"unicode-bidi":"normal | embed | bidi-override | inherit","user-modify":"read-only | read-write | write-only | inherit","user-select":"none | text | toggle | element | elements | all | inherit","vertical-align":"<percentage> | <length> | baseline | sub | super | top | text-top | middle | bottom | text-bottom | inherit",visibility:"visible | hidden | collapse | inherit","voice-balance":1,"voice-duration":1,"voice-family":1,"voice-pitch":1,"voice-pitch-range":1,"voice-rate":1,"voice-stress":1,"voice-volume":1,volume:1,"white-space":"normal | pre | nowrap | pre-wrap | pre-line | inherit","white-space-collapse":1,widows:"<integer> | inherit",width:"<length> | <percentage> | auto | inherit","word-break":"normal | keep-all | break-all","word-spacing":"<length> | normal | inherit","word-wrap":1,"z-index":"<integer> | auto | inherit",zoom:"<number> | <percentage> | normal"};PropertyName.prototype=new SyntaxUnit,PropertyName.prototype.constructor=PropertyName,PropertyName.prototype.toString=function(){return(this.hack?this.hack:"")+this.text},PropertyValue.prototype=new SyntaxUnit,PropertyValue.prototype.constructor=PropertyValue,PropertyValueIterator.prototype.count=function(){return this._parts.length},PropertyValueIterator.prototype.isFirst=function(){return this._i===0},PropertyValueIterator.prototype.hasNext=function(){return this._i<this._parts.length},PropertyValueIterator.prototype.mark=function(){this._marks.push(this._i)},PropertyValueIterator.prototype.peek=function(e){return this.hasNext()?this._parts[this._i+(e||0)]:null},PropertyValueIterator.prototype.next=function(){return this.hasNext()?this._parts[this._i++]:null},PropertyValueIterator.prototype.previous=function(){return this._i>0?this._parts[--this._i]:null},PropertyValueIterator.prototype.restore=function(){this._marks.length&&(this._i=this._marks.pop())},PropertyValuePart.prototype=new SyntaxUnit,PropertyValuePart.prototype.constructor=PropertyValuePart,PropertyValuePart.fromToken=function(e){return new PropertyValuePart(e.value,e.startLine,e.startCol)};var Pseudos={":first-letter":1,":first-line":1,":before":1,":after":1};Pseudos.ELEMENT=1,Pseudos.CLASS=2,Pseudos.isElement=function(e){return e.indexOf("::")===0||Pseudos[e.toLowerCase()]==Pseudos.ELEMENT},Selector.prototype=new SyntaxUnit,Selector.prototype.constructor=Selector,SelectorPart.prototype=new SyntaxUnit,SelectorPart.prototype.constructor=SelectorPart,SelectorSubPart.prototype=new SyntaxUnit,SelectorSubPart.prototype.constructor=SelectorSubPart,Specificity.prototype={constructor:Specificity,compare:function(e){var t=["a","b","c","d"],n,r;for(n=0,r=t.length;n<r;n++){if(this[t[n]]<e[t[n]])return-1;if(this[t[n]]>e[t[n]])return 1}return 0},valueOf:function(){return this.a*1e3+this.b*100+this.c*10+this.d},toString:function(){return this.a+","+this.b+","+this.c+","+this.d}},Specificity.calculate=function(e){function u(e){var t,n,r,a,f=e.elementName?e.elementName.text:"",l;f&&f.charAt(f.length-1)!="*"&&o++;for(t=0,r=e.modifiers.length;t<r;t++){l=e.modifiers[t];switch(l.type){case"class":case"attribute":s++;break;case"id":i++;break;case"pseudo":Pseudos.isElement(l.text)?o++:s++;break;case"not":for(n=0,a=l.args.length;n<a;n++)u(l.args[n])}}}var t,n,r,i=0,s=0,o=0;for(t=0,n=e.parts.length;t<n;t++)r=e.parts[t],r instanceof SelectorPart&&u(r);return new Specificity(0,i,s,o)};var h=/^[0-9a-fA-F]$/,nonascii=/^[\u0080-\uFFFF]$/,nl=/\n|\r\n|\r|\f/;TokenStream.prototype=mix(new TokenStreamBase,{_getToken:function(e){var t,n=this._reader,r=null,i=n.getLine(),s=n.getCol();t=n.read();while(t){switch(t){case"/":n.peek()=="*"?r=this.commentToken(t,i,s):r=this.charToken(t,i,s);break;case"|":case"~":case"^":case"$":case"*":n.peek()=="="?r=this.comparisonToken(t,i,s):r=this.charToken(t,i,s);break;case'"':case"'":r=this.stringToken(t,i,s);break;case"#":isNameChar(n.peek())?r=this.hashToken(t,i,s):r=this.charToken(t,i,s);break;case".":isDigit(n.peek())?r=this.numberToken(t,i,s):r=this.charToken(t,i,s);break;case"-":n.peek()=="-"?r=this.htmlCommentEndToken(t,i,s):isNameStart(n.peek())?r=this.identOrFunctionToken(t,i,s):r=this.charToken(t,i,s);break;case"!":r=this.importantToken(t,i,s);break;case"@":r=this.atRuleToken(t,i,s);break;case":":r=this.notToken(t,i,s);break;case"<":r=this.htmlCommentStartToken(t,i,s);break;case"U":case"u":if(n.peek()=="+"){r=this.unicodeRangeToken(t,i,s);break};default:isDigit(t)?r=this.numberToken(t,i,s):isWhitespace(t)?r=this.whitespaceToken(t,i,s):isIdentStart(t)?r=this.identOrFunctionToken(t,i,s):r=this.charToken(t,i,s)}break}return!r&&t===null&&(r=this.createToken(Tokens.EOF,null,i,s)),r},createToken:function(e,t,n,r,i){var s=this._reader;return i=i||{},{value:t,type:e,channel:i.channel,hide:i.hide||!1,startLine:n,startCol:r,endLine:s.getLine(),endCol:s.getCol()}},atRuleToken:function(e,t,n){var r=e,i=this._reader,s=Tokens.CHAR,o=!1,u,a;i.mark(),u=this.readName(),r=e+u,s=Tokens.type(r.toLowerCase());if(s==Tokens.CHAR||s==Tokens.UNKNOWN)r.length>1?s=Tokens.UNKNOWN_SYM:(s=Tokens.CHAR,r=e,i.reset());return this.createToken(s,r,t,n)},charToken:function(e,t,n){var r=Tokens.type(e);return r==-1&&(r=Tokens.CHAR),this.createToken(r,e,t,n)},commentToken:function(e,t,n){var r=this._reader,i=this.readComment(e);return this.createToken(Tokens.COMMENT,i,t,n)},comparisonToken:function(e,t,n){var r=this._reader,i=e+r.read(),s=Tokens.type(i)||Tokens.CHAR;return this.createToken(s,i,t,n)},hashToken:function(e,t,n){var r=this._reader,i=this.readName(e);return this.createToken(Tokens.HASH,i,t,n)},htmlCommentStartToken:function(e,t,n){var r=this._reader,i=e;return r.mark(),i+=r.readCount(3),i=="<!--"?this.createToken(Tokens.CDO,i,t,n):(r.reset(),this.charToken(e,t,n))},htmlCommentEndToken:function(e,t,n){var r=this._reader,i=e;return r.mark(),i+=r.readCount(2),i=="-->"?this.createToken(Tokens.CDC,i,t,n):(r.reset(),this.charToken(e,t,n))},identOrFunctionToken:function(e,t,n){var r=this._reader,i=this.readName(e),s=Tokens.IDENT;return r.peek()=="("?(i+=r.read(),i.toLowerCase()=="url("?(s=Tokens.URI,i=this.readURI(i),i.toLowerCase()=="url("&&(s=Tokens.FUNCTION)):s=Tokens.FUNCTION):r.peek()==":"&&i.toLowerCase()=="progid"&&(i+=r.readTo("("),s=Tokens.IE_FUNCTION),this.createToken(s,i,t,n)},importantToken:function(e,t,n){var r=this._reader,i=e,s=Tokens.CHAR,o,u;r.mark(),u=r.read();while(u){if(u=="/"){if(r.peek()!="*")break;o=this.readComment(u);if(o==="")break}else{if(!isWhitespace(u)){if(/i/i.test(u)){o=r.readCount(8),/mportant/i.test(o)&&(i+=u+o,s=Tokens.IMPORTANT_SYM);break}break}i+=u+this.readWhitespace()}u=r.read()}return s==Tokens.CHAR?(r.reset(),this.charToken(e,t,n)):this.createToken(s,i,t,n)},notToken:function(e,t,n){var r=this._reader,i=e;return r.mark(),i+=r.readCount(4),i.toLowerCase()==":not("?this.createToken(Tokens.NOT,i,t,n):(r.reset(),this.charToken(e,t,n))},numberToken:function(e,t,n){var r=this._reader,i=this.readNumber(e),s,o=Tokens.NUMBER,u=r.peek();return isIdentStart(u)?(s=this.readName(r.read()),i+=s,/^em$|^ex$|^px$|^gd$|^rem$|^vw$|^vh$|^vm$|^ch$|^cm$|^mm$|^in$|^pt$|^pc$/i.test(s)?o=Tokens.LENGTH:/^deg|^rad$|^grad$/i.test(s)?o=Tokens.ANGLE:/^ms$|^s$/i.test(s)?o=Tokens.TIME:/^hz$|^khz$/i.test(s)?o=Tokens.FREQ:/^dpi$|^dpcm$/i.test(s)?o=Tokens.RESOLUTION:o=Tokens.DIMENSION):u=="%"&&(i+=r.read(),o=Tokens.PERCENTAGE),this.createToken(o,i,t,n)},stringToken:function(e,t,n){var r=e,i=e,s=this._reader,o=e,u=Tokens.STRING,a=s.read();while(a){i+=a;if(a==r&&o!="\\")break;if(isNewLine(s.peek())&&a!="\\"){u=Tokens.INVALID;break}o=a,a=s.read()}return a===null&&(u=Tokens.INVALID),this.createToken(u,i,t,n)},unicodeRangeToken:function(e,t,n){var r=this._reader,i=e,s,o=Tokens.CHAR;return r.peek()=="+"&&(r.mark(),i+=r.read(),i+=this.readUnicodeRangePart(!0),i.length==2?r.reset():(o=Tokens.UNICODE_RANGE,i.indexOf("?")==-1&&r.peek()=="-"&&(r.mark(),s=r.read(),s+=this.readUnicodeRangePart(!1),s.length==1?r.reset():i+=s))),this.createToken(o,i,t,n)},whitespaceToken:function(e,t,n){var r=this._reader,i=e+this.readWhitespace();return this.createToken(Tokens.S,i,t,n)},readUnicodeRangePart:function(e){var t=this._reader,n="",r=t.peek();while(isHexDigit(r)&&n.length<6)t.read(),n+=r,r=t.peek();if(e)while(r=="?"&&n.length<6)t.read(),n+=r,r=t.peek();return n},readWhitespace:function(){var e=this._reader,t="",n=e.peek();while(isWhitespace(n))e.read(),t+=n,n=e.peek();return t},readNumber:function(e){var t=this._reader,n=e,r=e==".",i=t.peek();while(i){if(isDigit(i))n+=t.read();else{if(i!=".")break;if(r)break;r=!0,n+=t.read()}i=t.peek()}return n},readString:function(){var e=this._reader,t=e.read(),n=t,r=t,i=e.peek();while(i){i=e.read(),n+=i;if(i==t&&r!="\\")break;if(isNewLine(e.peek())&&i!="\\"){n="";break}r=i,i=e.peek()}return i===null&&(n=""),n},readURI:function(e){var t=this._reader,n=e,r="",i=t.peek();t.mark();while(i&&isWhitespace(i))t.read(),i=t.peek();i=="'"||i=='"'?r=this.readString():r=this.readURL(),i=t.peek();while(i&&isWhitespace(i))t.read(),i=t.peek();return r===""||i!=")"?(n=e,t.reset()):n+=r+t.read(),n},readURL:function(){var e=this._reader,t="",n=e.peek();while(/^[!#$%&\\*-~]$/.test(n))t+=e.read(),n=e.peek();return t},readName:function(e){var t=this._reader,n=e||"",r=t.peek();for(;;)if(r=="\\")n+=this.readEscape(t.read()),r=t.peek();else{if(!r||!isNameChar(r))break;n+=t.read(),r=t.peek()}return n},readEscape:function(e){var t=this._reader,n=e||"",r=0,i=t.peek();if(isHexDigit(i))do n+=t.read(),i=t.peek();while(i&&isHexDigit(i)&&++r<6);return n.length==3&&/\s/.test(i)||n.length==7||n.length==1?t.read():i="",n+i},readComment:function(e){var t=this._reader,n=e||"",r=t.read();if(r=="*"){while(r){n+=r;if(n.length>2&&r=="*"&&t.peek()=="/"){n+=t.read();break}r=t.read()}return n}return""}});var Tokens=[{name:"CDO"},{name:"CDC"},{name:"S",whitespace:!0},{name:"COMMENT",comment:!0,hide:!0,channel:"comment"},{name:"INCLUDES",text:"~="},{name:"DASHMATCH",text:"|="},{name:"PREFIXMATCH",text:"^="},{name:"SUFFIXMATCH",text:"$="},{name:"SUBSTRINGMATCH",text:"*="},{name:"STRING"},{name:"IDENT"},{name:"HASH"},{name:"IMPORT_SYM",text:"@import"},{name:"PAGE_SYM",text:"@page"},{name:"MEDIA_SYM",text:"@media"},{name:"FONT_FACE_SYM",text:"@font-face"},{name:"CHARSET_SYM",text:"@charset"},{name:"NAMESPACE_SYM",text:"@namespace"},{name:"UNKNOWN_SYM"},{name:"KEYFRAMES_SYM",text:["@keyframes","@-webkit-keyframes","@-moz-keyframes","@-o-keyframes"]},{name:"IMPORTANT_SYM"},{name:"LENGTH"},{name:"ANGLE"},{name:"TIME"},{name:"FREQ"},{name:"DIMENSION"},{name:"PERCENTAGE"},{name:"NUMBER"},{name:"URI"},{name:"FUNCTION"},{name:"UNICODE_RANGE"},{name:"INVALID"},{name:"PLUS",text:"+"},{name:"GREATER",text:">"},{name:"COMMA",text:","},{name:"TILDE",text:"~"},{name:"NOT"},{name:"TOPLEFTCORNER_SYM",text:"@top-left-corner"},{name:"TOPLEFT_SYM",text:"@top-left"},{name:"TOPCENTER_SYM",text:"@top-center"},{name:"TOPRIGHT_SYM",text:"@top-right"},{name:"TOPRIGHTCORNER_SYM",text:"@top-right-corner"},{name:"BOTTOMLEFTCORNER_SYM",text:"@bottom-left-corner"},{name:"BOTTOMLEFT_SYM",text:"@bottom-left"},{name:"BOTTOMCENTER_SYM",text:"@bottom-center"},{name:"BOTTOMRIGHT_SYM",text:"@bottom-right"},{name:"BOTTOMRIGHTCORNER_SYM",text:"@bottom-right-corner"},{name:"LEFTTOP_SYM",text:"@left-top"},{name:"LEFTMIDDLE_SYM",text:"@left-middle"},{name:"LEFTBOTTOM_SYM",text:"@left-bottom"},{name:"RIGHTTOP_SYM",text:"@right-top"},{name:"RIGHTMIDDLE_SYM",text:"@right-middle"},{name:"RIGHTBOTTOM_SYM",text:"@right-bottom"},{name:"RESOLUTION",state:"media"},{name:"IE_FUNCTION"},{name:"CHAR"},{name:"PIPE",text:"|"},{name:"SLASH",text:"/"},{name:"MINUS",text:"-"},{name:"STAR",text:"*"},{name:"LBRACE",text:"{"},{name:"RBRACE",text:"}"},{name:"LBRACKET",text:"["},{name:"RBRACKET",text:"]"},{name:"EQUALS",text:"="},{name:"COLON",text:":"},{name:"SEMICOLON",text:";"},{name:"LPAREN",text:"("},{name:"RPAREN",text:")"},{name:"DOT",text:"."}];(function(){var e=[],t={};Tokens.UNKNOWN=-1,Tokens.unshift({name:"EOF"});for(var n=0,r=Tokens.length;n<r;n++){e.push(Tokens[n].name),Tokens[Tokens[n].name]=n;if(Tokens[n].text)if(Tokens[n].text instanceof Array)for(var i=0;i<Tokens[n].text.length;i++)t[Tokens[n].text[i]]=n;else t[Tokens[n].text]=n}Tokens.name=function(t){return e[t]},Tokens.type=function(e){return t[e]||-1}})();var Validation={validate:function(e,t){var n=e.toString().toLowerCase(),r=t.parts,i=new PropertyValueIterator(t),s=Properties[n],o,u,a,f,l,c,h,p,d,v,m;if(!s){if(n.indexOf("-")!==0)throw new ValidationError("Unknown property '"+e+"'.",e.line,e.col)}else typeof s!="number"&&(typeof s=="string"?s.indexOf("||")>-1?this.groupProperty(s,i):this.singleProperty(s,i,1):s.multi?this.multiProperty(s.multi,i,s.comma,s.max||Infinity):typeof s=="function"&&s(i))},singleProperty:function(e,t,n,r){var i=!1,s=t.value,o=0,u;while(t.hasNext()&&o<n){i=ValidationTypes.isAny(t,e);if(!i)break;o++}if(!i)throw t.hasNext()&&!t.isFirst()?(u=t.peek(),new ValidationError("Expected end of value but found '"+u+"'.",u.line,u.col)):new ValidationError("Expected ("+e+") but found '"+s+"'.",s.line,s.col);if(t.hasNext())throw u=t.next(),new ValidationError("Expected end of value but found '"+u+"'.",u.line,u.col)},multiProperty:function(e,t,n,r){var i=!1,s=t.value,o=0,u=!1,a;while(t.hasNext()&&!i&&o<r){if(!ValidationTypes.isAny(t,e))break;o++;if(!t.hasNext())i=!0;else if(n){if(t.peek()!=",")break;a=t.next()}}if(!i)throw t.hasNext()&&!t.isFirst()?(a=t.peek(),new ValidationError("Expected end of value but found '"+a+"'.",a.line,a.col)):(a=t.previous(),n&&a==","?new ValidationError("Expected end of value but found '"+a+"'.",a.line,a.col):new ValidationError("Expected ("+e+") but found '"+s+"'.",s.line,s.col));if(t.hasNext())throw a=t.next(),new ValidationError("Expected end of value but found '"+a+"'.",a.line,a.col)},groupProperty:function(e,t,n){var r=!1,i=t.value,s=e.split("||").length,o={count:0},u=!1,a,f;while(t.hasNext()&&!r){a=ValidationTypes.isAnyOfGroup(t,e);if(!a)break;if(o[a])break;o[a]=1,o.count++,u=!0;if(o.count==s||!t.hasNext())r=!0}if(!r)throw u&&t.hasNext()?(f=t.peek(),new ValidationError("Expected end of value but found '"+f+"'.",f.line,f.col)):new ValidationError("Expected ("+e+") but found '"+i+"'.",i.line,i.col);if(t.hasNext())throw f=t.next(),new ValidationError("Expected end of value but found '"+f+"'.",f.line,f.col)}};ValidationError.prototype=new Error;var ValidationTypes={isLiteral:function(e,t){var n=e.text.toString().toLowerCase(),r=t.split(" | "),i,s,o=!1;for(i=0,s=r.length;i<s&&!o;i++)n==r[i].toLowerCase()&&(o=!0);return o},isSimple:function(e){return!!this.simple[e]},isComplex:function(e){return!!this.complex[e]},isAny:function(e,t){var n=t.split(" | "),r,i,s=!1;for(r=0,i=n.length;r<i&&!s&&e.hasNext();r++)s=this.isType(e,n[r]);return s},isAnyOfGroup:function(e,t){var n=t.split(" || "),r,i,s=!1;for(r=0,i=n.length;r<i&&!s;r++)s=this.isType(e,n[r]);return s?n[r-1]:!1},isType:function(e,t){var n=e.peek(),r=!1;return t.charAt(0)!="<"?(r=this.isLiteral(n,t),r&&e.next()):this.simple[t]?(r=this.simple[t](n),r&&e.next()):r=this.complex[t](e),r},simple:{"<absolute-size>":function(e){return ValidationTypes.isLiteral(e,"xx-small | x-small | small | medium | large | x-large | xx-large")},"<attachment>":function(e){return ValidationTypes.isLiteral(e,"scroll | fixed | local")},"<attr>":function(e){return e.type=="function"&&e.name=="attr"},"<bg-image>":function(e){return this["<image>"](e)||this["<gradient>"](e)||e=="none"},"<gradient>":function(e){return e.type=="function"&&/^(?:\-(?:ms|moz|o|webkit)\-)?(?:repeating\-)?(?:radial\-|linear\-)?gradient/i.test(e)},"<box>":function(e){return ValidationTypes.isLiteral(e,"padding-box | border-box | content-box")},"<content>":function(e){return e.type=="function"&&e.name=="content"},"<relative-size>":function(e){return ValidationTypes.isLiteral(e,"smaller | larger")},"<ident>":function(e){return e.type=="identifier"},"<length>":function(e){return e.type=="length"||e.type=="number"||e.type=="integer"||e=="0"},"<color>":function(e){return e.type=="color"||e=="transparent"},"<number>":function(e){return e.type=="number"||this["<integer>"](e)},"<integer>":function(e){return e.type=="integer"},"<line>":function(e){return e.type=="integer"},"<angle>":function(e){return e.type=="angle"},"<uri>":function(e){return e.type=="uri"},"<image>":function(e){return this["<uri>"](e)},"<percentage>":function(e){return e.type=="percentage"||e=="0"},"<border-width>":function(e){return this["<length>"](e)||ValidationTypes.isLiteral(e,"thin | medium | thick")},"<border-style>":function(e){return ValidationTypes.isLiteral(e,"none | hidden | dotted | dashed | solid | double | groove | ridge | inset | outset")},"<margin-width>":function(e){return this["<length>"](e)||this["<percentage>"](e)||ValidationTypes.isLiteral(e,"auto")},"<padding-width>":function(e){return this["<length>"](e)||this["<percentage>"](e)},"<shape>":function(e){return e.type=="function"&&(e.name=="rect"||e.name=="inset-rect")},"<time>":function(e){return e.type=="time"}},complex:{"<bg-position>":function(e){var t=this,n=!1,r="<percentage> | <length>",i="left | center | right",s="top | center | bottom",o,u,a;return ValidationTypes.isAny(e,"top | bottom")?n=!0:ValidationTypes.isAny(e,r)?e.hasNext()&&(n=ValidationTypes.isAny(e,r+" | "+s)):ValidationTypes.isAny(e,i)&&e.hasNext()&&(ValidationTypes.isAny(e,s)?(n=!0,ValidationTypes.isAny(e,r)):ValidationTypes.isAny(e,r)&&(ValidationTypes.isAny(e,s)&&ValidationTypes.isAny(e,r),n=!0)),n},"<bg-size>":function(e){var t=this,n=!1,r="<percentage> | <length> | auto",i,s,o;return ValidationTypes.isAny(e,"cover | contain")?n=!0:ValidationTypes.isAny(e,r)&&(n=!0,ValidationTypes.isAny(e,r)),n},"<repeat-style>":function(e){var t=!1,n="repeat | space | round | no-repeat",r;return e.hasNext()&&(r=e.next(),ValidationTypes.isLiteral(r,"repeat-x | repeat-y")?t=!0:ValidationTypes.isLiteral(r,n)&&(t=!0,e.hasNext()&&ValidationTypes.isLiteral(e.peek(),n)&&e.next())),t},"<shadow>":function(e){var t=!1,n=0,r=!1,i=!1,s;if(e.hasNext()){ValidationTypes.isAny(e,"inset")&&(r=!0),ValidationTypes.isAny(e,"<color>")&&(i=!0);while(ValidationTypes.isAny(e,"<length>")&&n<4)n++;e.hasNext()&&(i||ValidationTypes.isAny(e,"<color>"),r||ValidationTypes.isAny(e,"inset")),t=n>=2&&n<=4}return t},"<x-one-radius>":function(e){var t=!1,n=0,r="<length> | <percentage>",i;return ValidationTypes.isAny(e,r)&&(t=!0,ValidationTypes.isAny(e,r)),t}}};parserlib.css={Colors:Colors,Combinator:Combinator,Parser:Parser,PropertyName:PropertyName,PropertyValue:PropertyValue,PropertyValuePart:PropertyValuePart,MediaFeature:MediaFeature,MediaQuery:MediaQuery,Selector:Selector,SelectorPart:SelectorPart,SelectorSubPart:SelectorSubPart,Specificity:Specificity,TokenStream:TokenStream,Tokens:Tokens,ValidationError:ValidationError}}();var CSSLint=function(){var e=[],t=[],n=new parserlib.util.EventTarget;return n.version="0.9.9",n.addRule=function(t){e.push(t),e[t.id]=t},n.clearRules=function(){e=[]},n.getRules=function(){return[].concat(e).sort(function(e,t){return e.id>t.id?1:0})},n.getRuleset=function(){var t={},n=0,r=e.length;while(n<r)t[e[n++].id]=1;return t},n.addFormatter=function(e){t[e.id]=e},n.getFormatter=function(e){return t[e]},n.format=function(e,t,n,r){var i=this.getFormatter(n),s=null;return i&&(s=i.startFormat(),s+=i.formatResults(e,t,r||{}),s+=i.endFormat()),s},n.hasFormat=function(e){return t.hasOwnProperty(e)},n.verify=function(t,n){var r=0,i=e.length,s,o,u,a=new parserlib.css.Parser({starHack:!0,ieFilters:!0,underscoreHack:!0,strict:!1});o=t.replace(/\n\r?/g,"$split$").split("$split$"),n||(n=this.getRuleset()),s=new Reporter(o,n),n.errors=2;for(r in n)n.hasOwnProperty(r)&&e[r]&&e[r].init(a,s);try{a.parse(t)}catch(f){s.error("Fatal error, cannot continue: "+f.message,f.line,f.col,{})}return u={messages:s.messages,stats:s.stats},u.messages.sort(function(e,t){return e.rollup&&!t.rollup?1:!e.rollup&&t.rollup?-1:e.line-t.line}),u},n}();Reporter.prototype={constructor:Reporter,error:function(e,t,n,r){this.messages.push({type:"error",line:t,col:n,message:e,evidence:this.lines[t-1],rule:r||{}})},warn:function(e,t,n,r){this.report(e,t,n,r)},report:function(e,t,n,r){this.messages.push({type:this.ruleset[r.id]==2?"error":"warning",line:t,col:n,message:e,evidence:this.lines[t-1],rule:r})},info:function(e,t,n,r){this.messages.push({type:"info",line:t,col:n,message:e,evidence:this.lines[t-1],rule:r})},rollupError:function(e,t){this.messages.push({type:"error",rollup:!0,message:e,rule:t})},rollupWarn:function(e,t){this.messages.push({type:"warning",rollup:!0,message:e,rule:t})},stat:function(e,t){this.stats[e]=t}},CSSLint._Reporter=Reporter,CSSLint.Util={mix:function(e,t){var n;for(n in t)t.hasOwnProperty(n)&&(e[n]=t[n]);return n},indexOf:function(e,t){if(e.indexOf)return e.indexOf(t);for(var n=0,r=e.length;n<r;n++)if(e[n]===t)return n;return-1},forEach:function(e,t){if(e.forEach)return e.forEach(t);for(var n=0,r=e.length;n<r;n++)t(e[n],n,e)}},CSSLint.addRule({id:"adjoining-classes",name:"Disallow adjoining classes",desc:"Don't use adjoining classes.",browsers:"IE6",init:function(e,t){var n=this;e.addListener("startrule",function(r){var i=r.selectors,s,o,u,a,f,l,c;for(f=0;f<i.length;f++){s=i[f];for(l=0;l<s.parts.length;l++){o=s.parts[l];if(o.type==e.SELECTOR_PART_TYPE){a=0;for(c=0;c<o.modifiers.length;c++)u=o.modifiers[c],u.type=="class"&&a++,a>1&&t.report("Don't use adjoining classes.",o.line,o.col,n)}}}})}}),CSSLint.addRule({id:"box-model",name:"Beware of broken box size",desc:"Don't use width or height when using padding or border.",browsers:"All",init:function(e,t){function u(){s={},o=!1}function a(){var e,u;if(!o){if(s.height)for(e in i)i.hasOwnProperty(e)&&s[e]&&(u=s[e].value,(e!="padding"||u.parts.length!==2||u.parts[0].value!==0)&&t.report("Using height with "+e+" can sometimes make elements larger than you expect.",s[e].line,s[e].col,n));if(s.width)for(e in r)r.hasOwnProperty(e)&&s[e]&&(u=s[e].value,(e!="padding"||u.parts.length!==2||u.parts[1].value!==0)&&t.report("Using width with "+e+" can sometimes make elements larger than you expect.",s[e].line,s[e].col,n))}}var n=this,r={border:1,"border-left":1,"border-right":1,padding:1,"padding-left":1,"padding-right":1},i={border:1,"border-bottom":1,"border-top":1,padding:1,"padding-bottom":1,"padding-top":1},s,o=!1;e.addListener("startrule",u),e.addListener("startfontface",u),e.addListener("startpage",u),e.addListener("startpagemargin",u),e.addListener("startkeyframerule",u),e.addListener("property",function(e){var t=e.property.text.toLowerCase();i[t]||r[t]?!/^0\S*$/.test(e.value)&&(t!="border"||e.value!="none")&&(s[t]={line:e.property.line,col:e.property.col,value:e.value}):/^(width|height)/i.test(t)&&/^(length|percentage)/.test(e.value.parts[0].type)?s[t]=1:t=="box-sizing"&&(o=!0)}),e.addListener("endrule",a),e.addListener("endfontface",a),e.addListener("endpage",a),e.addListener("endpagemargin",a),e.addListener("endkeyframerule",a)}}),CSSLint.addRule({id:"box-sizing",name:"Disallow use of box-sizing",desc:"The box-sizing properties isn't supported in IE6 and IE7.",browsers:"IE6, IE7",tags:["Compatibility"],init:function(e,t){var n=this;e.addListener("property",function(e){var r=e.property.text.toLowerCase();r=="box-sizing"&&t.report("The box-sizing property isn't supported in IE6 and IE7.",e.line,e.col,n)})}}),CSSLint.addRule({id:"compatible-vendor-prefixes",name:"Require compatible vendor prefixes",desc:"Include all compatible vendor prefixes to reach a wider range of users.",browsers:"All",init:function(e,t){var n=this,r,i,s,o,u,a,f,l=!1,c=Array.prototype.push,h=[];r={animation:"webkit moz","animation-delay":"webkit moz","animation-direction":"webkit moz","animation-duration":"webkit moz","animation-fill-mode":"webkit moz","animation-iteration-count":"webkit moz","animation-name":"webkit moz","animation-play-state":"webkit moz","animation-timing-function":"webkit moz",appearance:"webkit moz","border-end":"webkit moz","border-end-color":"webkit moz","border-end-style":"webkit moz","border-end-width":"webkit moz","border-image":"webkit moz o","border-radius":"webkit moz","border-start":"webkit moz","border-start-color":"webkit moz","border-start-style":"webkit moz","border-start-width":"webkit moz","box-align":"webkit moz ms","box-direction":"webkit moz ms","box-flex":"webkit moz ms","box-lines":"webkit ms","box-ordinal-group":"webkit moz ms","box-orient":"webkit moz ms","box-pack":"webkit moz ms","box-sizing":"webkit moz","box-shadow":"webkit moz","column-count":"webkit moz ms","column-gap":"webkit moz ms","column-rule":"webkit moz ms","column-rule-color":"webkit moz ms","column-rule-style":"webkit moz ms","column-rule-width":"webkit moz ms","column-width":"webkit moz ms",hyphens:"epub moz","line-break":"webkit ms","margin-end":"webkit moz","margin-start":"webkit moz","marquee-speed":"webkit wap","marquee-style":"webkit wap","padding-end":"webkit moz","padding-start":"webkit moz","tab-size":"moz o","text-size-adjust":"webkit ms",transform:"webkit moz ms o","transform-origin":"webkit moz ms o",transition:"webkit moz o","transition-delay":"webkit moz o","transition-duration":"webkit moz o","transition-property":"webkit moz o","transition-timing-function":"webkit moz o","user-modify":"webkit moz","user-select":"webkit moz ms","word-break":"epub ms","writing-mode":"epub ms"};for(s in r)if(r.hasOwnProperty(s)){o=[],u=r[s].split(" ");for(a=0,f=u.length;a<f;a++)o.push("-"+u[a]+"-"+s);r[s]=o,c.apply(h,o)}e.addListener("startrule",function(){i=[]}),e.addListener("startkeyframes",function(e){l=e.prefix||!0}),e.addListener("endkeyframes",function(e){l=!1}),e.addListener("property",function(e){var t=e.property;CSSLint.Util.indexOf(h,t.text)>-1&&(!l||typeof l!="string"||t.text.indexOf("-"+l+"-")!==0)&&i.push(t)}),e.addListener("endrule",function(e){if(!i.length)return;var s={},o,u,a,f,l,c,h,p,d,v;for(o=0,u=i.length;o<u;o++){a=i[o];for(f in r)r.hasOwnProperty(f)&&(l=r[f],CSSLint.Util.indexOf(l,a.text)>-1&&(s[f]||(s[f]={full:l.slice(0),actual:[],actualNodes:[]}),CSSLint.Util.indexOf(s[f].actual,a.text)===-1&&(s[f].actual.push(a.text),s[f].actualNodes.push(a))))}for(f in s)if(s.hasOwnProperty(f)){c=s[f],h=c.full,p=c.actual;if(h.length>p.length)for(o=0,u=h.length;o<u;o++)d=h[o],CSSLint.Util.indexOf(p,d)===-1&&(v=p.length===1?p[0]:p.length==2?p.join(" and "):p.join(", "),t.report("The property "+d+" is compatible with "+v+" and should be included as well.",c.actualNodes[0].line,c.actualNodes[0].col,n))}})}}),CSSLint.addRule({id:"display-property-grouping",name:"Require properties appropriate for display",desc:"Certain properties shouldn't be used with certain display property values.",browsers:"All",init:function(e,t){function s(e,s,o){i[e]&&(typeof r[e]!="string"||i[e].value.toLowerCase()!=r[e])&&t.report(o||e+" can't be used with display: "+s+".",i[e].line,i[e].col,n)}function o(){i={}}function u(){var e=i.display?i.display.value:null;if(e)switch(e){case"inline":s("height",e),s("width",e),s("margin",e),s("margin-top",e),s("margin-bottom",e),s("float",e,"display:inline has no effect on floated elements (but may be used to fix the IE6 double-margin bug).");break;case"block":s("vertical-align",e);break;case"inline-block":s("float",e);break;default:e.indexOf("table-")===0&&(s("margin",e),s("margin-left",e),s("margin-right",e),s("margin-top",e),s("margin-bottom",e),s("float",e))}}var n=this,r={display:1,"float":"none",height:1,width:1,margin:1,"margin-left":1,"margin-right":1,"margin-bottom":1,"margin-top":1,padding:1,"padding-left":1,"padding-right":1,"padding-bottom":1,"padding-top":1,"vertical-align":1},i;e.addListener("startrule",o),e.addListener("startfontface",o),e.addListener("startkeyframerule",o),e.addListener("startpagemargin",o),e.addListener("startpage",o),e.addListener("property",function(e){var t=e.property.text.toLowerCase();r[t]&&(i[t]={value:e.value.text,line:e.property.line,col:e.property.col})}),e.addListener("endrule",u),e.addListener("endfontface",u),e.addListener("endkeyframerule",u),e.addListener("endpagemargin",u),e.addListener("endpage",u)}}),CSSLint.addRule({id:"duplicate-background-images",name:"Disallow duplicate background images",desc:"Every background-image should be unique. Use a common class for e.g. sprites.",browsers:"All",init:function(e,t){var n=this,r={};e.addListener("property",function(e){var i=e.property.text,s=e.value,o,u;if(i.match(/background/i))for(o=0,u=s.parts.length;o<u;o++)s.parts[o].type=="uri"&&(typeof r[s.parts[o].uri]=="undefined"?r[s.parts[o].uri]=e:t.report("Background image '"+s.parts[o].uri+"' was used multiple times, first declared at line "+r[s.parts[o].uri].line+", col "+r[s.parts[o].uri].col+".",e.line,e.col,n))})}}),CSSLint.addRule({id:"duplicate-properties",name:"Disallow duplicate properties",desc:"Duplicate properties must appear one after the other.",browsers:"All",init:function(e,t){function s(e){r={}}var n=this,r,i;e.addListener("startrule",s),e.addListener("startfontface",s),e.addListener("startpage",s),e.addListener("startpagemargin",s),e.addListener("startkeyframerule",s),e.addListener("property",function(e){var s=e.property,o=s.text.toLowerCase();r[o]&&(i!=o||r[o]==e.value.text)&&t.report("Duplicate property '"+e.property+"' found.",e.line,e.col,n),r[o]=e.value.text,i=o})}}),CSSLint.addRule({id:"empty-rules",name:"Disallow empty rules",desc:"Rules without any properties specified should be removed.",browsers:"All",init:function(e,t){var n=this,r=0;e.addListener("startrule",function(){r=0}),e.addListener("property",function(){r++}),e.addListener("endrule",function(e){var i=e.selectors;r===0&&t.report("Rule is empty.",i[0].line,i[0].col,n)})}}),CSSLint.addRule({id:"errors",name:"Parsing Errors",desc:"This rule looks for recoverable syntax errors.",browsers:"All",init:function(e,t){var n=this;e.addListener("error",function(e){t.error(e.message,e.line,e.col,n)})}}),CSSLint.addRule({id:"fallback-colors",name:"Require fallback colors",desc:"For older browsers that don't support RGBA, HSL, or HSLA, provide a fallback color.",browsers:"IE6,IE7,IE8",init:function(e,t){function o(e){s={},r=null}var n=this,r,i={color:1,background:1,"background-color":1},s;e.addListener("startrule",o),e.addListener("startfontface",o),e.addListener("startpage",o),e.addListener("startpagemargin",o),e.addListener("startkeyframerule",o),e.addListener("property",function(e){var s=e.property,o=s.text.toLowerCase(),u=e.value.parts,a=0,f="",l=u.length;if(i[o])while(a<l)u[a].type=="color"&&("alpha"in u[a]||"hue"in u[a]?(/([^\)]+)\(/.test(u[a])&&(f=RegExp.$1.toUpperCase()),(!r||r.property.text.toLowerCase()!=o||r.colorType!="compat")&&t.report("Fallback "+o+" (hex or RGB) should precede "+f+" "+o+".",e.line,e.col,n)):e.colorType="compat"),a++;r=e})}}),CSSLint.addRule({id:"floats",name:"Disallow too many floats",desc:"This rule tests if the float property is used too many times",browsers:"All",init:function(e,t){var n=this,r=0;e.addListener("property",function(e){e.property.text.toLowerCase()=="float"&&e.value.text.toLowerCase()!="none"&&r++}),e.addListener("endstylesheet",function(){t.stat("floats",r),r>=10&&t.rollupWarn("Too many floats ("+r+"), you're probably using them for layout. Consider using a grid system instead.",n)})}}),CSSLint.addRule({id:"font-faces",name:"Don't use too many web fonts",desc:"Too many different web fonts in the same stylesheet.",browsers:"All",init:function(e,t){var n=this,r=0;e.addListener("startfontface",function(){r++}),e.addListener("endstylesheet",function(){r>5&&t.rollupWarn("Too many @font-face declarations ("+r+").",n)})}}),CSSLint.addRule({id:"font-sizes",name:"Disallow too many font sizes",desc:"Checks the number of font-size declarations.",browsers:"All",init:function(e,t){var n=this,r=0;e.addListener("property",function(e){e.property=="font-size"&&r++}),e.addListener("endstylesheet",function(){t.stat("font-sizes",r),r>=10&&t.rollupWarn("Too many font-size declarations ("+r+"), abstraction needed.",n)})}}),CSSLint.addRule({id:"gradients",name:"Require all gradient definitions",desc:"When using a vendor-prefixed gradient, make sure to use them all.",browsers:"All",init:function(e,t){var n=this,r;e.addListener("startrule",function(){r={moz:0,webkit:0,oldWebkit:0,ms:0,o:0}}),e.addListener("property",function(e){/\-(moz|ms|o|webkit)(?:\-(?:linear|radial))\-gradient/i.test(e.value)?r[RegExp.$1]=1:/\-webkit\-gradient/i.test(e.value)&&(r.oldWebkit=1)}),e.addListener("endrule",function(e){var i=[];r.moz||i.push("Firefox 3.6+"),r.webkit||i.push("Webkit (Safari 5+, Chrome)"),r.oldWebkit||i.push("Old Webkit (Safari 4+, Chrome)"),r.ms||i.push("Internet Explorer 10+"),r.o||i.push("Opera 11.1+"),i.length&&i.length<5&&t.report("Missing vendor-prefixed CSS gradients for "+i.join(", ")+".",e.selectors[0].line,e.selectors[0].col,n)})}}),CSSLint.addRule({id:"ids",name:"Disallow IDs in selectors",desc:"Selectors should not contain IDs.",browsers:"All",init:function(e,t){var n=this;e.addListener("startrule",function(r){var i=r.selectors,s,o,u,a,f,l,c;for(f=0;f<i.length;f++){s=i[f],a=0;for(l=0;l<s.parts.length;l++){o=s.parts[l];if(o.type==e.SELECTOR_PART_TYPE)for(c=0;c<o.modifiers.length;c++)u=o.modifiers[c],u.type=="id"&&a++}a==1?t.report("Don't use IDs in selectors.",s.line,s.col,n):a>1&&t.report(a+" IDs in the selector, really?",s.line,s.col,n)}})}}),CSSLint.addRule({id:"import",name:"Disallow @import",desc:"Don't use @import, use <link> instead.",browsers:"All",init:function(e,t){var n=this;e.addListener("import",function(e){t.report("@import prevents parallel downloads, use <link> instead.",e.line,e.col,n)})}}),CSSLint.addRule({id:"important",name:"Disallow !important",desc:"Be careful when using !important declaration",browsers:"All",init:function(e,t){var n=this,r=0;e.addListener("property",function(e){e.important===!0&&(r++,t.report("Use of !important",e.line,e.col,n))}),e.addListener("endstylesheet",function(){t.stat("important",r),r>=10&&t.rollupWarn("Too many !important declarations ("+r+"), try to use less than 10 to avoid specificity issues.",n)})}}),CSSLint.addRule({id:"known-properties",name:"Require use of known properties",desc:"Properties should be known (listed in CSS3 specification) or be a vendor-prefixed property.",browsers:"All",init:function(e,t){var n=this;e.addListener("property",function(e){var r=e.property.text.toLowerCase();e.invalid&&t.report(e.invalid.message,e.line,e.col,n)})}}),CSSLint.addRule({id:"outline-none",name:"Disallow outline: none",desc:"Use of outline: none or outline: 0 should be limited to :focus rules.",browsers:"All",tags:["Accessibility"],init:function(e,t){function i(e){e.selectors?r={line:e.line,col:e.col,selectors:e.selectors,propCount:0,outline:!1}:r=null}function s(e){r&&r.outline&&(r.selectors.toString().toLowerCase().indexOf(":focus")==-1?t.report("Outlines should only be modified using :focus.",r.line,r.col,n):r.propCount==1&&t.report("Outlines shouldn't be hidden unless other visual changes are made.",r.line,r.col,n))}var n=this,r;e.addListener("startrule",i),e.addListener("startfontface",i),e.addListener("startpage",i),e.addListener("startpagemargin",i),e.addListener("startkeyframerule",i),e.addListener("property",function(e){var t=e.property.text.toLowerCase(),n=e.value;r&&(r.propCount++,t=="outline"&&(n=="none"||n=="0")&&(r.outline=!0))}),e.addListener("endrule",s),e.addListener("endfontface",s),e.addListener("endpage",s),e.addListener("endpagemargin",s),e.addListener("endkeyframerule",s)}}),CSSLint.addRule({id:"overqualified-elements",name:"Disallow overqualified elements",desc:"Don't use classes or IDs with elements (a.foo or a#foo).",browsers:"All",init:function(e,t){var n=this,r={};e.addListener("startrule",function(i){var s=i.selectors,o,u,a,f,l,c;for(f=0;f<s.length;f++){o=s[f];for(l=0;l<o.parts.length;l++){u=o.parts[l];if(u.type==e.SELECTOR_PART_TYPE)for(c=0;c<u.modifiers.length;c++)a=u.modifiers[c],u.elementName&&a.type=="id"?t.report("Element ("+u+") is overqualified, just use "+a+" without element name.",u.line,u.col,n):a.type=="class"&&(r[a]||(r[a]=[]),r[a].push({modifier:a,part:u}))}}}),e.addListener("endstylesheet",function(){var e;for(e in r)r.hasOwnProperty(e)&&r[e].length==1&&r[e][0].part.elementName&&t.report("Element ("+r[e][0].part+") is overqualified, just use "+r[e][0].modifier+" without element name.",r[e][0].part.line,r[e][0].part.col,n)})}}),CSSLint.addRule({id:"qualified-headings",name:"Disallow qualified headings",desc:"Headings should not be qualified (namespaced).",browsers:"All",init:function(e,t){var n=this;e.addListener("startrule",function(r){var i=r.selectors,s,o,u,a;for(u=0;u<i.length;u++){s=i[u];for(a=0;a<s.parts.length;a++)o=s.parts[a],o.type==e.SELECTOR_PART_TYPE&&o.elementName&&/h[1-6]/.test(o.elementName.toString())&&a>0&&t.report("Heading ("+o.elementName+") should not be qualified.",o.line,o.col,n)}})}}),CSSLint.addRule({id:"regex-selectors",name:"Disallow selectors that look like regexs",desc:"Selectors that look like regular expressions are slow and should be avoided.",browsers:"All",init:function(e,t){var n=this;e.addListener("startrule",function(r){var i=r.selectors,s,o,u,a,f,l;for(a=0;a<i.length;a++){s=i[a];for(f=0;f<s.parts.length;f++){o=s.parts[f];if(o.type==e.SELECTOR_PART_TYPE)for(l=0;l<o.modifiers.length;l++)u=o.modifiers[l],u.type=="attribute"&&/([\~\|\^\$\*]=)/.test(u)&&t.report("Attribute selectors with "+RegExp.$1+" are slow!",u.line,u.col,n)}}})}}),CSSLint.addRule({id:"rules-count",name:"Rules Count",desc:"Track how many rules there are.",browsers:"All",init:function(e,t){var n=this,r=0;e.addListener("startrule",function(){r++}),e.addListener("endstylesheet",function(){t.stat("rule-count",r)})}}),CSSLint.addRule({id:"shorthand",name:"Require shorthand properties",desc:"Use shorthand properties where possible.",browsers:"All",init:function(e,t){function f(e){u={}}function l(e){var r,i,s,o;for(r in a)if(a.hasOwnProperty(r)){o=0;for(i=0,s=a[r].length;i<s;i++)o+=u[a[r][i]]?1:0;o==a[r].length&&t.report("The properties "+a[r].join(", ")+" can be replaced by "+r+".",e.line,e.col,n)}}var n=this,r,i,s,o={},u,a={margin:["margin-top","margin-bottom","margin-left","margin-right"],padding:["padding-top","padding-bottom","padding-left","padding-right"]};for(r in a)if(a.hasOwnProperty(r))for(i=0,s=a[r].length;i<s;i++)o[a[r][i]]=r;e.addListener("startrule",f),e.addListener("startfontface",f),e.addListener("property",function(e){var t=e.property.toString().toLowerCase(),n=e.value.parts[0].value;o[t]&&(u[t]=1)}),e.addListener("endrule",l),e.addListener("endfontface",l)}}),CSSLint.addRule({id:"star-property-hack",name:"Disallow properties with a star prefix",desc:"Checks for the star property hack (targets IE6/7)",browsers:"All",init:function(e,t){var n=this;e.addListener("property",function(e){var r=e.property;r.hack=="*"&&t.report("Property with star prefix found.",e.property.line,e.property.col,n)})}}),CSSLint.addRule({id:"text-indent",name:"Disallow negative text-indent",desc:"Checks for text indent less than -99px",browsers:"All",init:function(e,t){function s(e){r=!1,i="inherit"}function o(e){r&&i!="ltr"&&t.report("Negative text-indent doesn't work well with RTL. If you use text-indent for image replacement explicitly set direction for that item to ltr.",r.line,r.col,n)}var n=this,r,i;e.addListener("startrule",s),e.addListener("startfontface",s),e.addListener("property",function(e){var t=e.property.toString().toLowerCase(),n=e.value;t=="text-indent"&&n.parts[0].value<-99?r=e.property:t=="direction"&&n=="ltr"&&(i="ltr")}),e.addListener("endrule",o),e.addListener("endfontface",o)}}),CSSLint.addRule({id:"underscore-property-hack",name:"Disallow properties with an underscore prefix",desc:"Checks for the underscore property hack (targets IE6)",browsers:"All",init:function(e,t){var n=this;e.addListener("property",function(e){var r=e.property;r.hack=="_"&&t.report("Property with underscore prefix found.",e.property.line,e.property.col,n)})}}),CSSLint.addRule({id:"unique-headings",name:"Headings should only be defined once",desc:"Headings should be defined only once.",browsers:"All",init:function(e,t){var n=this,r={h1:0,h2:0,h3:0,h4:0,h5:0,h6:0};e.addListener("startrule",function(e){var i=e.selectors,s,o,u,a,f;for(a=0;a<i.length;a++){s=i[a],o=s.parts[s.parts.length-1];if(o.elementName&&/(h[1-6])/i.test(o.elementName.toString())){for(f=0;f<o.modifiers.length;f++)if(o.modifiers[f].type=="pseudo"){u=!0;break}u||(r[RegExp.$1]++,r[RegExp.$1]>1&&t.report("Heading ("+o.elementName+") has already been defined.",o.line,o.col,n))}}}),e.addListener("endstylesheet",function(e){var i,s=[];for(i in r)r.hasOwnProperty(i)&&r[i]>1&&s.push(r[i]+" "+i+"s");s.length&&t.rollupWarn("You have "+s.join(", ")+" defined in this stylesheet.",n)})}}),CSSLint.addRule({id:"universal-selector",name:"Disallow universal selector",desc:"The universal selector (*) is known to be slow.",browsers:"All",init:function(e,t){var n=this;e.addListener("startrule",function(e){var r=e.selectors,i,s,o,u,a,f;for(u=0;u<r.length;u++)i=r[u],s=i.parts[i.parts.length-1],s.elementName=="*"&&t.report(n.desc,s.line,s.col,n)})}}),CSSLint.addRule({id:"unqualified-attributes",name:"Disallow unqualified attribute selectors",desc:"Unqualified attribute selectors are known to be slow.",browsers:"All",init:function(e,t){var n=this;e.addListener("startrule",function(r){var i=r.selectors,s,o,u,a,f,l;for(a=0;a<i.length;a++){s=i[a],o=s.parts[s.parts.length-1];if(o.type==e.SELECTOR_PART_TYPE)for(l=0;l<o.modifiers.length;l++)u=o.modifiers[l],u.type=="attribute"&&(!o.elementName||o.elementName=="*")&&t.report(n.desc,o.line,o.col,n)}})}}),CSSLint.addRule({id:"vendor-prefix",name:"Require standard property with vendor prefix",desc:"When using a vendor-prefixed property, make sure to include the standard one.",browsers:"All",init:function(e,t){function o(){r={},i=1}function u(e){var i,o,u,a,f,l,c=[];for(i in r)s[i]&&c.push({actual:i,needed:s[i]});for(o=0,u=c.length;o<u;o++)f=c[o].needed,l=c[o].actual,r[f]?r[f][0].pos<r[l][0].pos&&t.report("Standard property '"+f+"' should come after vendor-prefixed property '"+l+"'.",r[l][0].name.line,r[l][0].name.col,n):t.report("Missing standard property '"+f+"' to go along with '"+l+"'.",r[l][0].name.line,r[l][0].name.col,n)}var n=this,r,i,s={"-webkit-border-radius":"border-radius","-webkit-border-top-left-radius":"border-top-left-radius","-webkit-border-top-right-radius":"border-top-right-radius","-webkit-border-bottom-left-radius":"border-bottom-left-radius","-webkit-border-bottom-right-radius":"border-bottom-right-radius","-o-border-radius":"border-radius","-o-border-top-left-radius":"border-top-left-radius","-o-border-top-right-radius":"border-top-right-radius","-o-border-bottom-left-radius":"border-bottom-left-radius","-o-border-bottom-right-radius":"border-bottom-right-radius","-moz-border-radius":"border-radius","-moz-border-radius-topleft":"border-top-left-radius","-moz-border-radius-topright":"border-top-right-radius","-moz-border-radius-bottomleft":"border-bottom-left-radius","-moz-border-radius-bottomright":"border-bottom-right-radius","-moz-column-count":"column-count","-webkit-column-count":"column-count","-moz-column-gap":"column-gap","-webkit-column-gap":"column-gap","-moz-column-rule":"column-rule","-webkit-column-rule":"column-rule","-moz-column-rule-style":"column-rule-style","-webkit-column-rule-style":"column-rule-style","-moz-column-rule-color":"column-rule-color","-webkit-column-rule-color":"column-rule-color","-moz-column-rule-width":"column-rule-width","-webkit-column-rule-width":"column-rule-width","-moz-column-width":"column-width","-webkit-column-width":"column-width","-webkit-column-span":"column-span","-webkit-columns":"columns","-moz-box-shadow":"box-shadow","-webkit-box-shadow":"box-shadow","-moz-transform":"transform","-webkit-transform":"transform","-o-transform":"transform","-ms-transform":"transform","-moz-transform-origin":"transform-origin","-webkit-transform-origin":"transform-origin","-o-transform-origin":"transform-origin","-ms-transform-origin":"transform-origin","-moz-box-sizing":"box-sizing","-webkit-box-sizing":"box-sizing","-moz-user-select":"user-select","-khtml-user-select":"user-select","-webkit-user-select":"user-select"};e.addListener("startrule",o),e.addListener("startfontface",o),e.addListener("startpage",o),e.addListener("startpagemargin",o),e.addListener("startkeyframerule",o),e.addListener("property",function(e){var t=e.property.text.toLowerCase();r[t]||(r[t]=[]),r[t].push({name:e.property,value:e.value,pos:i++})}),e.addListener("endrule",u),e.addListener("endfontface",u),e.addListener("endpage",u),e.addListener("endpagemargin",u),e.addListener("endkeyframerule",u)}}),CSSLint.addRule({id:"zero-units",name:"Disallow units for 0 values",desc:"You don't need to specify units when a value is 0.",browsers:"All",init:function(e,t){var n=this;e.addListener("property",function(e){var r=e.value.parts,i=0,s=r.length;while(i<s)(r[i].units||r[i].type=="percentage")&&r[i].value===0&&r[i].type!="time"&&t.report("Values of 0 shouldn't have units specified.",r[i].line,r[i].col,n),i++})}}),exports.CSSLint=CSSLint}) \ No newline at end of file
diff --git a/plugins/jetpack/modules/custom-css/custom-css/js/safecss-ace.js b/plugins/jetpack/modules/custom-css/custom-css/js/safecss-ace.js
new file mode 100644
index 00000000..65b01d6e
--- /dev/null
+++ b/plugins/jetpack/modules/custom-css/custom-css/js/safecss-ace.js
@@ -0,0 +1,69 @@
+(function(global, $){
+ // shared scope insied IIFE in case it's needed.
+ var editor;
+ var syncCSS = function() {
+ $("#safecss").val( editor.getSession().getValue() );
+ };
+ var loadAce = function() {
+ // Set up ACE editor
+ ace.config.set( 'modePath', safecssAceSrcPath );
+ ace.config.set( 'workerPath', safecssAceSrcPath );
+ ace.config.set( 'themePath', safecssAceSrcPath );
+
+ editor = ace.edit( 'safecss-ace' );
+ // Globalize it so we can access it other places
+ global.safecss_editor = editor;
+ // Word-wrap, othewise the initial comments are borked.
+ editor.getSession().setUseWrapMode(true);
+ // This adds an annoying vertical line to the editor; get rid of it.
+ editor.setShowPrintMargin( false );
+ // Grab straight from the textarea
+ editor.getSession().setValue( $("#safecss").val() );
+ // We're editing CSS content
+ var CSSMode = ace.require( 'ace/mode/css' ).Mode;
+ editor.getSession().setMode( new CSSMode() );
+ // ace.js comes with the textmate coloring scheme already.
+ // kill the spinner
+ jQuery.fn.spin && $("#safecss-container").spin( false );
+ /*
+ // TODO: Add shortcuts for save and preview
+ editor.commands.addCommand({
+ name: 'cssPreview',
+ bindKey: {
+ win: 'Ctrl-P',
+ mac: 'Command-P',
+ sender: 'editor'
+ },
+ exec: function( env, args, request ) {
+ safecss_update_content();
+ jQuery( '#preview' ).click(); // this doesn't work :(
+ }
+ } );
+ */
+
+ // When submitting, make sure to include the updated CSS
+ // The Ace editor unfortunately doesn't handle this for us
+ $( '#safecssform' ).submit(syncCSS);
+ }
+
+ // exit if we're on IE <= 7
+ if ( ( $.browser.msie && parseInt( $.browser.version, 10 ) <= 7 ) || navigator.userAgent.match(/iPad/i) != null ) {
+ $("#safecss-container").hide();
+ $("#safecss").removeClass('hide-if-js');
+ return false;
+ }
+ // syntaxy goodness.
+ else {
+ $( '#safecss-ace, #safecss-container' ).css( 'height',
+ Math.max( 250, $( window ).height() - $( '#safecss-container' ).offset().top - $( '#wpadminbar' ).height() )
+ );
+
+ $(global).load(loadAce);
+ }
+
+ // for now, expose the syncCSS function.
+ global.aceSyncCSS = syncCSS;
+
+})(this, jQuery);
+
+
diff --git a/plugins/jetpack/modules/custom-css/custom-css/preprocessors.php b/plugins/jetpack/modules/custom-css/custom-css/preprocessors.php
new file mode 100644
index 00000000..eef6ab43
--- /dev/null
+++ b/plugins/jetpack/modules/custom-css/custom-css/preprocessors.php
@@ -0,0 +1,57 @@
+<?php
+
+/**
+ * CSS preprocessor registration.
+ *
+ * To add a new preprocessor (or replace an existing one), hook into the
+ * jetpack_custom_css_preprocessors filter and add an entry to the array
+ * that is passed in.
+ *
+ * Format is:
+ * $preprocessors[ UNIQUE_KEY ] => array( 'name' => 'Processor name', 'callback' => [processing function] );
+ *
+ * The callback function accepts a single string argument (non-CSS markup) and returns a string (CSS).
+ *
+ * @param array $preprocessors The list of preprocessors added thus far.
+ * @return array
+ */
+
+function jetpack_register_css_preprocessors( $preprocessors ) {
+ $preprocessors['less'] = array(
+ 'name' => 'LESS',
+ 'callback' => 'jetpack_less_css_preprocess'
+ );
+
+ $preprocessors['sass'] = array(
+ 'name' => 'Sass (SCSS Syntax)',
+ 'callback' => 'jetpack_sass_css_preprocess'
+ );
+
+ return $preprocessors;
+}
+
+add_filter( 'jetpack_custom_css_preprocessors', 'jetpack_register_css_preprocessors' );
+
+function jetpack_less_css_preprocess( $less ) {
+ require( dirname( __FILE__ ) . '/preprocessors/lessc.inc.php' );
+
+ $compiler = new lessc();
+
+ try {
+ return $compiler->compile( $less );
+ } catch ( Exception $e ) {
+ return $less;
+ }
+}
+
+function jetpack_sass_css_preprocess( $sass ) {
+ require_once( dirname( __FILE__ ) . '/preprocessors/scss.inc.php' );
+
+ $compiler = new scssc();
+
+ try {
+ return $compiler->compile( $sass );
+ } catch ( Exception $e ) {
+ return $sass;
+ }
+} \ 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
new file mode 100644
index 00000000..c42147c5
--- /dev/null
+++ b/plugins/jetpack/modules/custom-css/custom-css/preprocessors/lessc.inc.php
@@ -0,0 +1,3359 @@
+<?php
+
+/**
+ * lessphp v0.3.8
+ * http://leafo.net/lessphp
+ *
+ * LESS css compiler, adapted from http://lesscss.org
+ *
+ * Copyright 2012, Leaf Corcoran <leafot@gmail.com>
+ * Licensed under MIT or GPLv3, see LICENSE
+ */
+
+
+/**
+ * The less compiler and parser.
+ *
+ * Converting LESS to CSS is a three stage process. The incoming file is parsed
+ * by `lessc_parser` into a syntax tree, then it is compiled into another tree
+ * representing the CSS structure by `lessc`. The CSS tree is fed into a
+ * formatter, like `lessc_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 `lessc` is the entry point.
+ *
+ * In summary:
+ *
+ * The `lessc` class creates an intstance of the parser, feeds it LESS 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 `lessc_parser` class is only concerned with parsing its input.
+ *
+ * The `lessc_formatter` takes a CSS tree, and dumps it to a formatted string,
+ * handling things like indentation.
+ */
+class lessc {
+ static public $VERSION = "v0.3.8";
+ static protected $TRUE = array("keyword", "true");
+ static protected $FALSE = array("keyword", "false");
+
+ protected $libFunctions = array();
+ protected $registeredVars = array();
+ protected $preserveComments = false;
+
+ public $vPrefix = '@'; // prefix of abstract properties
+ public $mPrefix = '$'; // prefix of abstract blocks
+ public $parentSelector = '&';
+
+ public $importDisabled = false;
+ public $importDir = '';
+
+ protected $numberPrecision = null;
+
+ // set to the parser that generated the current line when compiling
+ // so we know how to create error messages
+ protected $sourceParser = null;
+ protected $sourceLoc = null;
+
+ static public $defaultValue = array("keyword", "");
+
+ static protected $nextImportId = 0; // uniquely identify imports
+
+ // attempts to find the path of an import url, returns null for css files
+ protected function findImport($url) {
+ foreach ((array)$this->importDir as $dir) {
+ $full = $dir.(substr($dir, -1) != '/' ? '/' : '').$url;
+ if ($this->fileExists($file = $full.'.less') || $this->fileExists($file = $full)) {
+ return $file;
+ }
+ }
+
+ return null;
+ }
+
+ protected function fileExists($name) {
+ return is_file($name);
+ }
+
+ static public function compressList($items, $delim) {
+ if (!isset($items[1]) && isset($items[0])) return $items[0];
+ else return array('list', $delim, $items);
+ }
+
+ static public function preg_quote($what) {
+ return preg_quote($what, '/');
+ }
+
+ protected function tryImport($importPath, $parentBlock, $out) {
+ if ($importPath[0] == "function" && $importPath[1] == "url") {
+ $importPath = $this->flattenList($importPath[2]);
+ }
+
+ $str = $this->coerceString($importPath);
+ if ($str === null) return false;
+
+ $url = $this->compileValue($this->lib_e($str));
+
+ // don't import if it ends in css
+ 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 */");
+ }
+
+ $this->addParsedFile($realPath);
+ $parser = $this->makeParser($realPath);
+ $root = $parser->parse(file_get_contents($realPath));
+
+ // set the parents of all the block props
+ foreach ($root->props as $prop) {
+ if ($prop[0] == "block") {
+ $prop[1]->parent = $parentBlock;
+ }
+ }
+
+ // copy mixins into scope, set their parents
+ // bring blocks from import into current block
+ // TODO: need to mark the source parser these came from this file
+ foreach ($root->children as $childName => $child) {
+ if (isset($parentBlock->children[$childName])) {
+ $parentBlock->children[$childName] = array_merge(
+ $parentBlock->children[$childName],
+ $child);
+ } else {
+ $parentBlock->children[$childName] = $child;
+ }
+ }
+
+ $pi = pathinfo($realPath);
+ $dir = $pi["dirname"];
+
+ list($top, $bottom) = $this->sortProps($root->props, true);
+ $this->compileImportedProps($top, $parentBlock, $out, $parser, $dir);
+
+ return array(true, $bottom, $parser, $dir);
+ }
+
+ protected function compileImportedProps($props, $block, $out, $sourceParser, $importDir) {
+ $oldSourceParser = $this->sourceParser;
+
+ $oldImport = $this->importDir;
+
+ // TODO: this is because the importDir api is stupid
+ $this->importDir = (array)$this->importDir;
+ array_unshift($this->importDir, $importDir);
+
+ foreach ($props as $prop) {
+ $this->compileProp($prop, $block, $out);
+ }
+
+ $this->importDir = $oldImport;
+ $this->sourceParser = $oldSourceParser;
+ }
+
+ /**
+ * Recursively compiles a block.
+ *
+ * A block is analogous to a CSS block in most cases. A single LESS document
+ * is encapsulated in a block when parsed, but it does not have parent tags
+ * so all of it's children appear on the root level when compiled.
+ *
+ * Blocks are made up of props and children.
+ *
+ * Props are property instructions, array tuples which describe an action
+ * to be taken, eg. write a property, set a variable, mixin a block.
+ *
+ * The children of a block are just all the blocks that are defined within.
+ * This is used to look up mixins when performing a mixin.
+ *
+ * Compiling the block involves pushing a fresh environment on the stack,
+ * and iterating through the props, compiling each one.
+ *
+ * See lessc::compileProp()
+ *
+ */
+ protected function compileBlock($block) {
+ switch ($block->type) {
+ case "root":
+ $this->compileRoot($block);
+ break;
+ case null:
+ $this->compileCSSBlock($block);
+ break;
+ case "media":
+ $this->compileMedia($block);
+ break;
+ case "directive":
+ $name = "@" . $block->name;
+ if (!empty($block->value)) {
+ $name .= " " . $this->compileValue($this->reduce($block->value));
+ }
+
+ $this->compileNestedBlock($block, array($name));
+ break;
+ default:
+ $this->throwError("unknown block type: $block->type\n");
+ }
+ }
+
+ protected function compileCSSBlock($block) {
+ $env = $this->pushEnv();
+
+ $selectors = $this->compileSelectors($block->tags);
+ $env->selectors = $this->multiplySelectors($selectors);
+ $out = $this->makeOutputBlock(null, $env->selectors);
+
+ $this->scope->children[] = $out;
+ $this->compileProps($block, $out);
+
+ $block->scope = $env; // mixins carry scope with them!
+ $this->popEnv();
+ }
+
+ protected function compileMedia($media) {
+ $env = $this->pushEnv($media);
+ $parentScope = $this->mediaParent($this->scope);
+
+ $query = $this->compileMediaQuery($this->multiplyMedia($env));
+
+ $this->scope = $this->makeOutputBlock($media->type, array($query));
+ $parentScope->children[] = $this->scope;
+
+ $this->compileProps($media, $this->scope);
+
+ if (count($this->scope->lines) > 0) {
+ $orphanSelelectors = $this->findClosestSelectors();
+ if (!is_null($orphanSelelectors)) {
+ $orphan = $this->makeOutputBlock(null, $orphanSelelectors);
+ $orphan->lines = $this->scope->lines;
+ array_unshift($this->scope->children, $orphan);
+ $this->scope->lines = array();
+ }
+ }
+
+ $this->scope = $this->scope->parent;
+ $this->popEnv();
+ }
+
+ protected function mediaParent($scope) {
+ while (!empty($scope->parent)) {
+ if (!empty($scope->type) && $scope->type != "media") {
+ break;
+ }
+ $scope = $scope->parent;
+ }
+
+ return $scope;
+ }
+
+ protected function compileNestedBlock($block, $selectors) {
+ $this->pushEnv($block);
+ $this->scope = $this->makeOutputBlock($block->type, $selectors);
+ $this->scope->parent->children[] = $this->scope;
+
+ $this->compileProps($block, $this->scope);
+
+ $this->scope = $this->scope->parent;
+ $this->popEnv();
+ }
+
+ protected function compileRoot($root) {
+ $this->pushEnv();
+ $this->scope = $this->makeOutputBlock($root->type);
+ $this->compileProps($root, $this->scope);
+ $this->popEnv();
+ }
+
+ protected function compileProps($block, $out) {
+ foreach ($this->sortProps($block->props) as $prop) {
+ $this->compileProp($prop, $block, $out);
+ }
+ }
+
+ protected function sortProps($props, $split = false) {
+ $vars = array();
+ $imports = array();
+ $other = array();
+
+ foreach ($props as $prop) {
+ switch ($prop[0]) {
+ case "assign":
+ if (isset($prop[1][0]) && $prop[1][0] == $this->vPrefix) {
+ $vars[] = $prop;
+ } else {
+ $other[] = $prop;
+ }
+ break;
+ case "import":
+ $id = self::$nextImportId++;
+ $prop[] = $id;
+ $imports[] = $prop;
+ $other[] = array("import_mixin", $id);
+ break;
+ default:
+ $other[] = $prop;
+ }
+ }
+
+ if ($split) {
+ return array(array_merge($vars, $imports), $other);
+ } else {
+ return array_merge($vars, $imports, $other);
+ }
+ }
+
+ protected function compileMediaQuery($queries) {
+ $compiledQueries = array();
+ foreach ($queries as $query) {
+ $parts = array();
+ foreach ($query as $q) {
+ switch ($q[0]) {
+ case "mediaType":
+ $parts[] = implode(" ", array_slice($q, 1));
+ break;
+ case "mediaExp":
+ if (isset($q[2])) {
+ $parts[] = "($q[1]: " .
+ $this->compileValue($this->reduce($q[2])) . ")";
+ } else {
+ $parts[] = "($q[1])";
+ }
+ break;
+ }
+ }
+
+ if (count($parts) > 0) {
+ $compiledQueries[] = implode(" and ", $parts);
+ }
+ }
+
+ $out = "@media";
+ if (!empty($parts)) {
+ $out .= " " .
+ implode($this->formatter->selectorSeparator, $compiledQueries);
+ }
+ return $out;
+ }
+
+ protected function multiplyMedia($env, $childQueries = null) {
+ if (is_null($env) ||
+ !empty($env->block->type) && $env->block->type != "media")
+ {
+ return $childQueries;
+ }
+
+ // plain old block, skip
+ if (empty($env->block->type)) {
+ return $this->multiplyMedia($env->parent, $childQueries);
+ }
+
+ $out = array();
+ $queries = $env->block->queries;
+ if (is_null($childQueries)) {
+ $out = $queries;
+ } else {
+ foreach ($queries as $parent) {
+ foreach ($childQueries as $child) {
+ $out[] = array_merge($parent, $child);
+ }
+ }
+ }
+
+ return $this->multiplyMedia($env->parent, $out);
+ }
+
+ protected function expandParentSelectors(&$tag, $replace) {
+ $parts = explode("$&$", $tag);
+ $count = 0;
+ foreach ($parts as &$part) {
+ $part = str_replace($this->parentSelector, $replace, $part, $c);
+ $count += $c;
+ }
+ $tag = implode($this->parentSelector, $parts);
+ return $count;
+ }
+
+ protected function findClosestSelectors() {
+ $env = $this->env;
+ $selectors = null;
+ while ($env !== null) {
+ if (isset($env->selectors)) {
+ $selectors = $env->selectors;
+ break;
+ }
+ $env = $env->parent;
+ }
+
+ return $selectors;
+ }
+
+
+ // multiply $selectors against the nearest selectors in env
+ protected function multiplySelectors($selectors) {
+ // find parent selectors
+
+ $parentSelectors = $this->findClosestSelectors();
+ if (is_null($parentSelectors)) {
+ // kill parent reference in top level selector
+ foreach ($selectors as &$s) {
+ $this->expandParentSelectors($s, "");
+ }
+
+ return $selectors;
+ }
+
+ $out = array();
+ foreach ($parentSelectors as $parent) {
+ foreach ($selectors as $child) {
+ $count = $this->expandParentSelectors($child, $parent);
+
+ // don't prepend the parent tag if & was used
+ if ($count > 0) {
+ $out[] = trim($child);
+ } else {
+ $out[] = trim($parent . ' ' . $child);
+ }
+ }
+ }
+
+ return $out;
+ }
+
+ // reduces selector expressions
+ protected function compileSelectors($selectors) {
+ $out = array();
+
+ foreach ($selectors as $s) {
+ if (is_array($s)) {
+ list(, $value) = $s;
+ $out[] = $this->compileValue($this->reduce($value));
+ } else {
+ $out[] = $s;
+ }
+ }
+
+ return $out;
+ }
+
+ protected function eq($left, $right) {
+ return $left == $right;
+ }
+
+ protected function patternMatch($block, $callingArgs) {
+ // 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)) {
+ $groupPassed = false;
+ foreach ($block->guards as $guardGroup) {
+ foreach ($guardGroup as $guard) {
+ $this->pushEnv();
+ $this->zipSetArgs($block->args, $callingArgs);
+
+ $negate = false;
+ if ($guard[0] == "negate") {
+ $guard = $guard[1];
+ $negate = true;
+ }
+
+ $passed = $this->reduce($guard) == self::$TRUE;
+ if ($negate) $passed = !$passed;
+
+ $this->popEnv();
+
+ if ($passed) {
+ $groupPassed = true;
+ } else {
+ $groupPassed = false;
+ break;
+ }
+ }
+
+ if ($groupPassed) break;
+ }
+
+ if (!$groupPassed) {
+ return false;
+ }
+ }
+
+ $numCalling = count($callingArgs);
+
+ if (empty($block->args)) {
+ return $block->isVararg || $numCalling == 0;
+ }
+
+ $i = -1; // no args
+ // try to match by arity or by argument literal
+ foreach ($block->args as $i => $arg) {
+ switch ($arg[0]) {
+ case "lit":
+ if (empty($callingArgs[$i]) || !$this->eq($arg[1], $callingArgs[$i])) {
+ return false;
+ }
+ break;
+ case "arg":
+ // no arg and no default value
+ if (!isset($callingArgs[$i]) && !isset($arg[2])) {
+ return false;
+ }
+ break;
+ case "rest":
+ $i--; // rest can be empty
+ break 2;
+ }
+ }
+
+ if ($block->isVararg) {
+ return true; // not having enough is handled above
+ } else {
+ $numMatched = $i + 1;
+ // greater than becuase default values always match
+ return $numMatched >= $numCalling;
+ }
+ }
+
+ protected function patternMatchAll($blocks, $callingArgs) {
+ $matches = null;
+ foreach ($blocks as $block) {
+ if ($this->patternMatch($block, $callingArgs)) {
+ $matches[] = $block;
+ }
+ }
+
+ return $matches;
+ }
+
+ // attempt to find blocks matched by path and args
+ protected function findBlocks($searchIn, $path, $args, $seen=array()) {
+ if ($searchIn == null) return null;
+ if (isset($seen[$searchIn->id])) return null;
+ $seen[$searchIn->id] = true;
+
+ $name = $path[0];
+
+ if (isset($searchIn->children[$name])) {
+ $blocks = $searchIn->children[$name];
+ if (count($path) == 1) {
+ $matches = $this->patternMatchAll($blocks, $args);
+ if (!empty($matches)) {
+ // This will return all blocks that match in the closest
+ // scope that has any matching block, like lessjs
+ return $matches;
+ }
+ } else {
+ $matches = array();
+ foreach ($blocks as $subBlock) {
+ $subMatches = $this->findBlocks($subBlock,
+ array_slice($path, 1), $args, $seen);
+
+ if (!is_null($subMatches)) {
+ foreach ($subMatches as $sm) {
+ $matches[] = $sm;
+ }
+ }
+ }
+
+ return count($matches) > 0 ? $matches : null;
+ }
+ }
+
+ if ($searchIn->parent === $searchIn) return null;
+ return $this->findBlocks($searchIn->parent, $path, $args, $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;
+ $assignedValues = array();
+ foreach ($args as $a) {
+ if ($a[0] == "arg") {
+ if ($i < count($values) && !is_null($values[$i])) {
+ $value = $values[$i];
+ } elseif (isset($a[2])) {
+ $value = $a[2];
+ } else $value = null;
+
+ $value = $this->reduce($value);
+ $this->set($a[1], $value);
+ $assignedValues[] = $value;
+ }
+ $i++;
+ }
+
+ // check for a rest
+ $last = end($args);
+ if ($last[0] == "rest") {
+ $rest = array_slice($values, count($args) - 1);
+ $this->set($last[1], $this->reduce(array("list", " ", $rest)));
+ }
+
+ $this->env->arguments = $assignedValues;
+ }
+
+ // compile a prop and update $lines or $blocks appropriately
+ protected function compileProp($prop, $block, $out) {
+ // set error position context
+ $this->sourceLoc = isset($prop[-1]) ? $prop[-1] : -1;
+
+ switch ($prop[0]) {
+ case 'assign':
+ list(, $name, $value) = $prop;
+ if ($name[0] == $this->vPrefix) {
+ $this->set($name, $value);
+ } else {
+ $out->lines[] = $this->formatter->property($name,
+ $this->compileValue($this->reduce($value)));
+ }
+ break;
+ case 'block':
+ list(, $child) = $prop;
+ $this->compileBlock($child);
+ break;
+ case 'mixin':
+ list(, $path, $args, $suffix) = $prop;
+
+ $args = array_map(array($this, "reduce"), (array)$args);
+ $mixins = $this->findBlocks($block, $path, $args);
+
+ if ($mixins === null) {
+ // fwrite(STDERR,"failed to find block: ".implode(" > ", $path)."\n");
+ break; // throw error here??
+ }
+
+ foreach ($mixins as $mixin) {
+ $haveScope = false;
+ if (isset($mixin->parent->scope)) {
+ $haveScope = true;
+ $mixinParentEnv = $this->pushEnv();
+ $mixinParentEnv->storeParent = $mixin->parent->scope;
+ }
+
+ $haveArgs = false;
+ if (isset($mixin->args)) {
+ $haveArgs = true;
+ $this->pushEnv();
+ $this->zipSetArgs($mixin->args, $args);
+ }
+
+ $oldParent = $mixin->parent;
+ if ($mixin != $block) $mixin->parent = $block;
+
+ foreach ($this->sortProps($mixin->props) as $subProp) {
+ if ($suffix !== null &&
+ $subProp[0] == "assign" &&
+ is_string($subProp[1]) &&
+ $subProp[1]{0} != $this->vPrefix)
+ {
+ $subProp[2] = array(
+ 'list', ' ',
+ array($subProp[2], array('keyword', $suffix))
+ );
+ }
+
+ $this->compileProp($subProp, $mixin, $out);
+ }
+
+ $mixin->parent = $oldParent;
+
+ if ($haveArgs) $this->popEnv();
+ if ($haveScope) $this->popEnv();
+ }
+
+ break;
+ case 'raw':
+ $out->lines[] = $prop[1];
+ break;
+ case "directive":
+ list(, $name, $value) = $prop;
+ $out->lines[] = "@$name " . $this->compileValue($this->reduce($value)).';';
+ break;
+ case "comment":
+ $out->lines[] = $prop[1];
+ break;
+ case "import";
+ list(, $importPath, $importId) = $prop;
+ $importPath = $this->reduce($importPath);
+
+ if (!isset($this->env->imports)) {
+ $this->env->imports = array();
+ }
+
+ $result = $this->tryImport($importPath, $block, $out);
+
+ $this->env->imports[$importId] = $result === false ?
+ array(false, "@import " . $this->compileValue($importPath).";") :
+ $result;
+
+ break;
+ case "import_mixin":
+ list(,$importId) = $prop;
+ $import = $this->env->imports[$importId];
+ if ($import[0] === false) {
+ $out->lines[] = $import[1];
+ } else {
+ list(, $bottom, $parser, $importDir) = $import;
+ $this->compileImportedProps($bottom, $block, $out, $parser, $importDir);
+ }
+
+ break;
+ default:
+ $this->throwError("unknown op: {$prop[0]}\n");
+ }
+ }
+
+
+ /**
+ * Compiles a primitive value into a CSS property value.
+ *
+ * Values in lessphp 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.
+ */
+ protected function compileValue($value) {
+ switch ($value[0]) {
+ case 'list':
+ // [1] - delimiter
+ // [2] - array of values
+ return implode($value[1], array_map(array($this, 'compileValue'), $value[2]));
+ case 'raw_color':
+ if (!empty($this->formatter->compressColors)) {
+ return $this->compileValue($this->coerceColor($value));
+ }
+ return $value[1];
+ case 'keyword':
+ // [1] - the keyword
+ return $value[1];
+ case 'number':
+ list(, $num, $unit) = $value;
+ // [1] - the number
+ // [2] - the unit
+ if ($this->numberPrecision !== null) {
+ $num = round($num, $this->numberPrecision);
+ }
+ return $num . $unit;
+ case 'string':
+ // [1] - contents of string (includes quotes)
+ list(, $delim, $content) = $value;
+ foreach ($content as &$part) {
+ if (is_array($part)) {
+ $part = $this->compileValue($part);
+ }
+ }
+ return $delim . implode($content) . $delim;
+ case 'color':
+ // [1] - red component (either number or a %)
+ // [2] - green component
+ // [3] - blue component
+ // [4] - optional alpha component
+ list(, $r, $g, $b) = $value;
+ $r = round($r);
+ $g = round($g);
+ $b = round($b);
+
+ if (count($value) == 5 && $value[4] != 1) { // rgba
+ return 'rgba('.$r.','.$g.','.$b.','.$value[4].')';
+ }
+
+ $h = sprintf("#%02x%02x%02x", $r, $g, $b);
+
+ if (!empty($this->formatter->compressColors)) {
+ // Converting hex color to short notation (e.g. #003399 to #039)
+ if ($h[1] === $h[2] && $h[3] === $h[4] && $h[5] === $h[6]) {
+ $h = '#' . $h[1] . $h[3] . $h[5];
+ }
+ }
+
+ return $h;
+
+ case 'function':
+ list(, $name, $args) = $value;
+ return $name.'('.$this->compileValue($args).')';
+ default: // assumed to be unit
+ $this->throwError("unknown value type: $value[0]");
+ }
+ }
+
+ protected function lib_isnumber($value) {
+ return $this->toBool($value[0] == "number");
+ }
+
+ protected function lib_isstring($value) {
+ return $this->toBool($value[0] == "string");
+ }
+
+ protected function lib_iscolor($value) {
+ return $this->toBool($this->coerceColor($value));
+ }
+
+ protected function lib_iskeyword($value) {
+ return $this->toBool($value[0] == "keyword");
+ }
+
+ protected function lib_ispixel($value) {
+ return $this->toBool($value[0] == "number" && $value[2] == "px");
+ }
+
+ protected function lib_ispercentage($value) {
+ return $this->toBool($value[0] == "number" && $value[2] == "%");
+ }
+
+ protected function lib_isem($value) {
+ return $this->toBool($value[0] == "number" && $value[2] == "em");
+ }
+
+ protected function lib_rgbahex($color) {
+ $color = $this->coerceColor($color);
+ if (is_null($color))
+ $this->throwError("color expected for rgbahex");
+
+ return sprintf("#%02x%02x%02x%02x",
+ isset($color[4]) ? $color[4]*255 : 255,
+ $color[1],$color[2], $color[3]);
+ }
+
+ protected function lib_argb($color){
+ return $this->lib_rgbahex($color);
+ }
+
+ // utility func to unquote a string
+ protected function lib_e($arg) {
+ switch ($arg[0]) {
+ case "list":
+ $items = $arg[2];
+ if (isset($items[0])) {
+ return $this->lib_e($items[0]);
+ }
+ return self::$defaultValue;
+ case "string":
+ $arg[1] = "";
+ return $arg;
+ case "keyword":
+ return $arg;
+ default:
+ return array("keyword", $this->compileValue($arg));
+ }
+ }
+
+ protected function lib__sprintf($args) {
+ if ($args[0] != "list") return $args;
+ $values = $args[2];
+ $string = array_shift($values);
+ $template = $this->compileValue($this->lib_e($string));
+
+ $i = 0;
+ if (preg_match_all('/%[dsa]/', $template, $m)) {
+ foreach ($m[0] as $match) {
+ $val = isset($values[$i]) ?
+ $this->reduce($values[$i]) : array('keyword', '');
+
+ // lessjs compat, renders fully expanded color, not raw color
+ if ($color = $this->coerceColor($val)) {
+ $val = $color;
+ }
+
+ $i++;
+ $rep = $this->compileValue($this->lib_e($val));
+ $template = preg_replace('/'.self::preg_quote($match).'/',
+ $rep, $template, 1);
+ }
+ }
+
+ $d = $string[0] == "string" ? $string[1] : '"';
+ return array("string", $d, array($template));
+ }
+
+ protected function lib_floor($arg) {
+ $value = $this->assertNumber($arg);
+ return array("number", floor($value), $arg[2]);
+ }
+
+ protected function lib_ceil($arg) {
+ $value = $this->assertNumber($arg);
+ return array("number", ceil($value), $arg[2]);
+ }
+
+ protected function lib_round($arg) {
+ $value = $this->assertNumber($arg);
+ return array("number", round($value), $arg[2]);
+ }
+
+ /**
+ * Helper function to get arguments for color manipulation functions.
+ * takes a list that contains a color like thing and a percentage
+ */
+ protected function colorArgs($args) {
+ if ($args[0] != 'list' || count($args[2]) < 2) {
+ return array(array('color', 0, 0, 0), 0);
+ }
+ list($color, $delta) = $args[2];
+ $color = $this->assertColor($color);
+ $delta = floatval($delta[1]);
+
+ return array($color, $delta);
+ }
+
+ protected function lib_darken($args) {
+ list($color, $delta) = $this->colorArgs($args);
+
+ $hsl = $this->toHSL($color);
+ $hsl[3] = $this->clamp($hsl[3] - $delta, 100);
+ return $this->toRGB($hsl);
+ }
+
+ protected function lib_lighten($args) {
+ list($color, $delta) = $this->colorArgs($args);
+
+ $hsl = $this->toHSL($color);
+ $hsl[3] = $this->clamp($hsl[3] + $delta, 100);
+ return $this->toRGB($hsl);
+ }
+
+ protected function lib_saturate($args) {
+ list($color, $delta) = $this->colorArgs($args);
+
+ $hsl = $this->toHSL($color);
+ $hsl[2] = $this->clamp($hsl[2] + $delta, 100);
+ return $this->toRGB($hsl);
+ }
+
+ protected function lib_desaturate($args) {
+ list($color, $delta) = $this->colorArgs($args);
+
+ $hsl = $this->toHSL($color);
+ $hsl[2] = $this->clamp($hsl[2] - $delta, 100);
+ return $this->toRGB($hsl);
+ }
+
+ protected function lib_spin($args) {
+ list($color, $delta) = $this->colorArgs($args);
+
+ $hsl = $this->toHSL($color);
+
+ $hsl[1] = $hsl[1] + $delta % 360;
+ if ($hsl[1] < 0) $hsl[1] += 360;
+
+ return $this->toRGB($hsl);
+ }
+
+ protected function lib_fadeout($args) {
+ list($color, $delta) = $this->colorArgs($args);
+ $color[4] = $this->clamp((isset($color[4]) ? $color[4] : 1) - $delta/100);
+ return $color;
+ }
+
+ protected function lib_fadein($args) {
+ list($color, $delta) = $this->colorArgs($args);
+ $color[4] = $this->clamp((isset($color[4]) ? $color[4] : 1) + $delta/100);
+ return $color;
+ }
+
+ protected function lib_hue($color) {
+ $hsl = $this->toHSL($this->assertColor($color));
+ return round($hsl[1]);
+ }
+
+ protected function lib_saturation($color) {
+ $hsl = $this->toHSL($this->assertColor($color));
+ return round($hsl[2]);
+ }
+
+ protected function lib_lightness($color) {
+ $hsl = $this->toHSL($this->assertColor($color));
+ return round($hsl[3]);
+ }
+
+ // get the alpha of a color
+ // defaults to 1 for non-colors or colors without an alpha
+ protected function lib_alpha($value) {
+ if (!is_null($color = $this->coerceColor($value))) {
+ return isset($color[4]) ? $color[4] : 1;
+ }
+ }
+
+ // set the alpha of the color
+ protected function lib_fade($args) {
+ list($color, $alpha) = $this->colorArgs($args);
+ $color[4] = $this->clamp($alpha / 100.0);
+ return $color;
+ }
+
+ protected function lib_percentage($arg) {
+ $num = $this->assertNumber($arg);
+ return array("number", $num*100, "%");
+ }
+
+ // mixes two colors by weight
+ // mix(@color1, @color2, @weight);
+ // 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)
+ $this->throwError("mix expects (color1, color2, weight)");
+
+ list($first, $second, $weight) = $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;
+
+ $w = $weight * 2 - 1;
+ $a = $first_a - $second_a;
+
+ $w1 = (($w * $a == -1 ? $w : ($w + $a)/(1 + $w * $a)) + 1) / 2.0;
+ $w2 = 1.0 - $w1;
+
+ $new = array('color',
+ $w1 * $first[1] + $w2 * $second[1],
+ $w1 * $first[2] + $w2 * $second[2],
+ $w1 * $first[3] + $w2 * $second[3],
+ );
+
+ if ($first_a != 1.0 || $second_a != 1.0) {
+ $new[] = $first_a * $weight + $second_a * ($weight - 1);
+ }
+
+ return $this->fixColor($new);
+ }
+
+ protected function assertColor($value, $error = "expected color value") {
+ $color = $this->coerceColor($value);
+ if (is_null($color)) $this->throwError($error);
+ return $color;
+ }
+
+ protected function assertNumber($value, $error = "expecting number") {
+ if ($value[0] == "number") return $value[1];
+ $this->throwError($error);
+ }
+
+ protected function toHSL($color) {
+ if ($color[0] == 'hsl') return $color;
+
+ $r = $color[1] / 255;
+ $g = $color[2] / 255;
+ $b = $color[3] / 255;
+
+ $min = min($r, $g, $b);
+ $max = max($r, $g, $b);
+
+ $L = ($min + $max) / 2;
+ if ($min == $max) {
+ $S = $H = 0;
+ } else {
+ if ($L < 0.5)
+ $S = ($max - $min)/($max + $min);
+ 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);
+
+ }
+
+ $out = array('hsl',
+ ($H < 0 ? $H + 6 : $H)*60,
+ $S*100,
+ $L*100,
+ );
+
+ if (count($color) > 4) $out[] = $color[4]; // copy alpha
+ return $out;
+ }
+
+ protected function toRGB_helper($comp, $temp1, $temp2) {
+ if ($comp < 0) $comp += 1.0;
+ elseif ($comp > 1) $comp -= 1.0;
+
+ 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;
+
+ return $temp1;
+ }
+
+ /**
+ * Converts a hsl array into a color value in rgb.
+ * 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;
+
+ $H = $color[1] / 360;
+ $S = $color[2] / 100;
+ $L = $color[3] / 100;
+
+ if ($S == 0) {
+ $r = $g = $b = $L;
+ } else {
+ $temp2 = $L < 0.5 ?
+ $L*(1.0 + $S) :
+ $L + $S - $L * $S;
+
+ $temp1 = 2.0 * $L - $temp2;
+
+ $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);
+ }
+
+ // $out = array('color', round($r*255), round($g*255), round($b*255));
+ $out = array('color', $r*255, $g*255, $b*255);
+ if (count($color) > 4) $out[] = $color[4]; // copy alpha
+ return $out;
+ }
+
+ protected function clamp($v, $max = 1, $min = 0) {
+ return min($max, max($min, $v));
+ }
+
+ /**
+ * Convert the rgb, rgba, hsl color literals of function type
+ * as returned by the parser into values of color type.
+ */
+ protected function funcToColor($func) {
+ $fname = $func[1];
+ if ($func[2][0] != 'list') return false; // need a list of arguments
+ $rawComponents = $func[2][2];
+
+ if ($fname == 'hsl' || $fname == 'hsla') {
+ $hsl = array('hsl');
+ $i = 0;
+ foreach ($rawComponents as $c) {
+ $val = $this->reduce($c);
+ $val = isset($val[1]) ? floatval($val[1]) : 0;
+
+ if ($i == 0) $clamp = 360;
+ elseif ($i < 3) $clamp = 100;
+ else $clamp = 1;
+
+ $hsl[] = $this->clamp($val, $clamp);
+ $i++;
+ }
+
+ while (count($hsl) < 4) $hsl[] = 0;
+ return $this->toRGB($hsl);
+
+ } elseif ($fname == 'rgb' || $fname == 'rgba') {
+ $components = array();
+ $i = 1;
+ foreach ($rawComponents as $c) {
+ $c = $this->reduce($c);
+ if ($i < 4) {
+ if ($c[0] == "number" && $c[2] == "%") {
+ $components[] = 255 * ($c[1] / 100);
+ } else {
+ $components[] = floatval($c[1]);
+ }
+ } elseif ($i == 4) {
+ if ($c[0] == "number" && $c[2] == "%") {
+ $components[] = 1.0 * ($c[1] / 100);
+ } else {
+ $components[] = floatval($c[1]);
+ }
+ } else break;
+
+ $i++;
+ }
+ while (count($components) < 3) $components[] = 0;
+ array_unshift($components, 'color');
+ return $this->fixColor($components);
+ }
+
+ return false;
+ }
+
+ protected function reduce($value, $forExpression = false) {
+ switch ($value[0]) {
+ case "variable":
+ $key = $value[1];
+ if (is_array($key)) {
+ $key = $this->reduce($key);
+ $key = $this->vPrefix . $this->compileValue($this->lib_e($key));
+ }
+
+ $seen =& $this->env->seenNames;
+
+ if (!empty($seen[$key])) {
+ $this->throwError("infinite loop detected: $key");
+ }
+
+ $seen[$key] = true;
+ $out = $this->reduce($this->get($key, self::$defaultValue));
+ $seen[$key] = false;
+ return $out;
+ case "list":
+ foreach ($value[2] as &$item) {
+ $item = $this->reduce($item, $forExpression);
+ }
+ return $value;
+ case "expression":
+ return $this->evaluate($value);
+ case "string":
+ foreach ($value[2] as &$part) {
+ if (is_array($part)) {
+ $strip = $part[0] == "variable";
+ $part = $this->reduce($part);
+ if ($strip) $part = $this->lib_e($part);
+ }
+ }
+ return $value;
+ case "escape":
+ list(,$inner) = $value;
+ return $this->lib_e($this->reduce($inner));
+ case "function":
+ $color = $this->funcToColor($value);
+ if ($color) return $color;
+
+ list(, $name, $args) = $value;
+ if ($name == "%") $name = "_sprintf";
+ $f = isset($this->libFunctions[$name]) ?
+ $this->libFunctions[$name] : array($this, 'lib_'.$name);
+
+ if (is_callable($f)) {
+ if ($args[0] == 'list')
+ $args = self::compressList($args[2], $args[1]);
+
+ $ret = call_user_func($f, $this->reduce($args, true), $this);
+
+ if (is_null($ret)) {
+ return array("string", "", array(
+ $name, "(", $args, ")"
+ ));
+ }
+
+ // convert to a typed value if the result is a php primitive
+ if (is_numeric($ret)) $ret = array('number', $ret, "");
+ elseif (!is_array($ret)) $ret = array('keyword', $ret);
+
+ return $ret;
+ }
+
+ // plain function, reduce args
+ $value[2] = $this->reduce($value[2]);
+ return $value;
+ case "unary":
+ list(, $op, $exp) = $value;
+ $exp = $this->reduce($exp);
+
+ if ($exp[0] == "number") {
+ switch ($op) {
+ case "+":
+ return $exp;
+ case "-":
+ $exp[1] *= -1;
+ return $exp;
+ }
+ }
+ return array("string", "", array($op, $exp));
+ }
+
+ if ($forExpression) {
+ switch ($value[0]) {
+ case "keyword":
+ if ($color = $this->coerceColor($value)) {
+ return $color;
+ }
+ break;
+ case "raw_color":
+ return $this->coerceColor($value);
+ }
+ }
+
+ return $value;
+ }
+
+
+ // coerce a value for use in color operation
+ protected function coerceColor($value) {
+ switch($value[0]) {
+ case 'color': return $value;
+ case 'raw_color':
+ $c = array("color", 0, 0, 0);
+ $colorStr = substr($value[1], 1);
+ $num = hexdec($colorStr);
+ $width = strlen($colorStr) == 3 ? 16 : 256;
+
+ for ($i = 3; $i > 0; $i--) { // 3 2 1
+ $t = $num % $width;
+ $num /= $width;
+
+ $c[$i] = $t * (256/$width) + $t * floor(16/$width);
+ }
+
+ return $c;
+ case 'keyword':
+ $name = $value[1];
+ if (isset(self::$cssColors[$name])) {
+ list($r, $g, $b) = explode(',', self::$cssColors[$name]);
+ return array('color', $r, $g, $b);
+ }
+ return null;
+ }
+ }
+
+ // make something string like into a string
+ protected function coerceString($value) {
+ switch ($value[0]) {
+ case "string":
+ return $value;
+ case "keyword":
+ return array("string", "", array($value[1]));
+ }
+ return null;
+ }
+
+ // turn list of length 1 into value type
+ protected function flattenList($value) {
+ if ($value[0] == "list" && count($value[2]) == 1) {
+ return $this->flattenList($value[2][0]);
+ }
+ return $value;
+ }
+
+ protected function toBool($a) {
+ if ($a) return self::$TRUE;
+ else return self::$FALSE;
+ }
+
+ // evaluate an expression
+ protected function evaluate($exp) {
+ list(, $op, $left, $right, $whiteBefore, $whiteAfter) = $exp;
+
+ $left = $this->reduce($left, true);
+ $right = $this->reduce($right, true);
+
+ if ($leftColor = $this->coerceColor($left)) {
+ $left = $leftColor;
+ }
+
+ if ($rightColor = $this->coerceColor($right)) {
+ $right = $rightColor;
+ }
+
+ $ltype = $left[0];
+ $rtype = $right[0];
+
+ // operators that work on all types
+ if ($op == "and") {
+ return $this->toBool($left == self::$TRUE && $right == self::$TRUE);
+ }
+
+ if ($op == "=") {
+ return $this->toBool($this->eq($left, $right) );
+ }
+
+ if ($op == "+" && !is_null($str = $this->stringConcatenate($left, $right))) {
+ return $str;
+ }
+
+ // type based operators
+ $fname = "op_${ltype}_${rtype}";
+ if (is_callable(array($this, $fname))) {
+ $out = $this->$fname($op, $left, $right);
+ if (!is_null($out)) return $out;
+ }
+
+ // make the expression look it did before being parsed
+ $paddedOp = $op;
+ if ($whiteBefore) $paddedOp = " " . $paddedOp;
+ if ($whiteAfter) $paddedOp .= " ";
+
+ return array("string", "", array($left, $paddedOp, $right));
+ }
+
+ protected function stringConcatenate($left, $right) {
+ if ($strLeft = $this->coerceString($left)) {
+ if ($right[0] == "string") {
+ $right[1] = "";
+ }
+ $strLeft[2][] = $right;
+ return $strLeft;
+ }
+
+ if ($strRight = $this->coerceString($right)) {
+ array_unshift($strRight[2], $left);
+ return $strRight;
+ }
+ }
+
+
+ // make sure a color's components don't go out of bounds
+ protected function fixColor($c) {
+ foreach (range(1, 3) as $i) {
+ if ($c[$i] < 0) $c[$i] = 0;
+ if ($c[$i] > 255) $c[$i] = 255;
+ }
+
+ return $c;
+ }
+
+ protected function op_number_color($op, $lft, $rgt) {
+ if ($op == '+' || $op == '*') {
+ return $this->op_color_number($op, $rgt, $lft);
+ }
+ }
+
+ protected function op_color_number($op, $lft, $rgt) {
+ if ($rgt[0] == '%') $rgt[1] /= 100;
+
+ return $this->op_color_color($op, $lft,
+ array_fill(1, count($lft) - 1, $rgt[1]));
+ }
+
+ protected function op_color_color($op, $left, $right) {
+ $out = array('color');
+ $max = count($left) > count($right) ? count($left) : count($right);
+ foreach (range(1, $max - 1) as $i) {
+ $lval = isset($left[$i]) ? $left[$i] : 0;
+ $rval = isset($right[$i]) ? $right[$i] : 0;
+ switch ($op) {
+ case '+':
+ $out[] = $lval + $rval;
+ break;
+ case '-':
+ $out[] = $lval - $rval;
+ break;
+ case '*':
+ $out[] = $lval * $rval;
+ break;
+ case '%':
+ $out[] = $lval % $rval;
+ break;
+ case '/':
+ if ($rval == 0) $this->throwError("evaluate error: can't divide by zero");
+ $out[] = $lval / $rval;
+ break;
+ default:
+ $this->throwError('evaluate error: color op number failed on op '.$op);
+ }
+ }
+ return $this->fixColor($out);
+ }
+
+ // operator on two numbers
+ protected function op_number_number($op, $left, $right) {
+ $unit = empty($left[2]) ? $right[2] : $left[2];
+
+ $value = 0;
+ switch ($op) {
+ case '+':
+ $value = $left[1] + $right[1];
+ break;
+ case '*':
+ $value = $left[1] * $right[1];
+ break;
+ case '-':
+ $value = $left[1] - $right[1];
+ break;
+ case '%':
+ $value = $left[1] % $right[1];
+ break;
+ case '/':
+ if ($right[1] == 0) $this->throwError('parse error: divide by zero');
+ $value = $left[1] / $right[1];
+ break;
+ case '<':
+ return $this->toBool($left[1] < $right[1]);
+ case '>':
+ return $this->toBool($left[1] > $right[1]);
+ case '>=':
+ return $this->toBool($left[1] >= $right[1]);
+ case '=<':
+ return $this->toBool($left[1] <= $right[1]);
+ default:
+ $this->throwError('parse error: unknown number operator: '.$op);
+ }
+
+ return array("number", $value, $unit);
+ }
+
+
+ /* environment functions */
+
+ protected function makeOutputBlock($type, $selectors = null) {
+ $b = new stdclass;
+ $b->lines = array();
+ $b->children = array();
+ $b->selectors = $selectors;
+ $b->type = $type;
+ $b->parent = $this->scope;
+ return $b;
+ }
+
+ // the state of execution
+ protected function pushEnv($block = null) {
+ $e = new stdclass;
+ $e->parent = $this->env;
+ $e->store = array();
+ $e->block = $block;
+
+ $this->env = $e;
+ return $e;
+ }
+
+ // pop something off the stack
+ protected function popEnv() {
+ $old = $this->env;
+ $this->env = $this->env->parent;
+ return $old;
+ }
+
+ // set something in the current env
+ protected function set($name, $value) {
+ $this->env->store[$name] = $value;
+ }
+
+
+ // get the highest occurrence entry for a name
+ protected function get($name, $default=null) {
+ $current = $this->env;
+
+ $isArguments = $name == $this->vPrefix . 'arguments';
+ while ($current) {
+ if ($isArguments && isset($current->arguments)) {
+ return array('list', ' ', $current->arguments);
+ }
+
+ if (isset($current->store[$name]))
+ return $current->store[$name];
+ else {
+ $current = isset($current->storeParent) ?
+ $current->storeParent : $current->parent;
+ }
+ }
+
+ return $default;
+ }
+
+ // inject array of unparsed strings into environment as variables
+ protected function injectVariables($args) {
+ $this->pushEnv();
+ $parser = new lessc_parser($this, __METHOD__);
+ foreach ($args as $name => $strValue) {
+ if ($name{0} != '@') $name = '@'.$name;
+ $parser->count = 0;
+ $parser->buffer = (string)$strValue;
+ if (!$parser->propertyValue($value)) {
+ throw new Exception("failed to parse passed in variable $name: $strValue");
+ }
+
+ $this->set($name, $value);
+ }
+ }
+
+ /**
+ * Initialize any static state, can initialize parser for a file
+ * $opts isn't used yet
+ */
+ public function __construct($fname = null) {
+ if ($fname !== null) {
+ // used for deprecated parse method
+ $this->_parseFile = $fname;
+ }
+ }
+
+ public function compile($string, $name = null) {
+ $locale = setlocale(LC_NUMERIC, 0);
+ setlocale(LC_NUMERIC, "C");
+
+ $this->parser = $this->makeParser($name);
+ $root = $this->parser->parse($string);
+
+ $this->env = null;
+ $this->scope = null;
+
+ $this->formatter = $this->newFormatter();
+
+ if (!empty($this->registeredVars)) {
+ $this->injectVariables($this->registeredVars);
+ }
+
+ $this->sourceParser = $this->parser; // used for error messages
+ $this->compileBlock($root);
+
+ ob_start();
+ $this->formatter->block($this->scope);
+ $out = ob_get_clean();
+ setlocale(LC_NUMERIC, $locale);
+ return $out;
+ }
+
+ public function compileFile($fname, $outFname = null) {
+ if (!is_readable($fname)) {
+ throw new Exception('load error: failed to find '.$fname);
+ }
+
+ $pi = pathinfo($fname);
+
+ $oldImport = $this->importDir;
+
+ $this->importDir = (array)$this->importDir;
+ $this->importDir[] = $pi['dirname'].'/';
+
+ $this->allParsedFiles = array();
+ $this->addParsedFile($fname);
+
+ $out = $this->compile(file_get_contents($fname), $fname);
+
+ $this->importDir = $oldImport;
+
+ if ($outFname !== null) {
+ return file_put_contents($outFname, $out);
+ }
+
+ return $out;
+ }
+
+ // compile only if changed input has changed or output doesn't exist
+ public function checkedCompile($in, $out) {
+ if (!is_file($out) || filemtime($in) > filemtime($out)) {
+ $this->compileFile($in, $out);
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Execute lessphp on a .less file or a lessphp cache structure
+ *
+ * The lessphp cache structure contains information about a specific
+ * less file having been parsed. It can be used as a hint for future
+ * calls to determine whether or not a rebuild is required.
+ *
+ * The cache structure contains two important keys that may be used
+ * externally:
+ *
+ * compiled: The final compiled CSS
+ * updated: The time (in seconds) the CSS was last compiled
+ *
+ * The cache structure is a plain-ol' PHP associative array and can
+ * be serialized and unserialized without a hitch.
+ *
+ * @param mixed $in Input
+ * @param bool $force Force rebuild?
+ * @return array lessphp cache structure
+ */
+ public function cachedCompile($in, $force = false) {
+ // assume no root
+ $root = null;
+
+ if (is_string($in)) {
+ $root = $in;
+ } elseif (is_array($in) and isset($in['root'])) {
+ if ($force or ! isset($in['files'])) {
+ // If we are forcing a recompile or if for some reason the
+ // structure does not contain any file information we should
+ // specify the root to trigger a rebuild.
+ $root = $in['root'];
+ } elseif (isset($in['files']) and is_array($in['files'])) {
+ foreach ($in['files'] as $fname => $ftime ) {
+ if (!file_exists($fname) or filemtime($fname) > $ftime) {
+ // One of the files we knew about previously has changed
+ // so we should look at our incoming root again.
+ $root = $in['root'];
+ break;
+ }
+ }
+ }
+ } else {
+ // TODO: Throw an exception? We got neither a string nor something
+ // that looks like a compatible lessphp cache structure.
+ return null;
+ }
+
+ if ($root !== null) {
+ // If we have a root value which means we should rebuild.
+ $out = array();
+ $out['root'] = $root;
+ $out['compiled'] = $this->compileFile($root);
+ $out['files'] = $this->allParsedFiles();
+ $out['updated'] = time();
+ return $out;
+ } else {
+ // No changes, pass back the structure
+ // we were given initially.
+ return $in;
+ }
+
+ }
+
+ // parse and compile buffer
+ // This is deprecated
+ public function parse($str = null, $initialVariables = null) {
+ if (is_array($str)) {
+ $initialVariables = $str;
+ $str = null;
+ }
+
+ $oldVars = $this->registeredVars;
+ if ($initialVariables !== null) {
+ $this->setVariables($initialVariables);
+ }
+
+ if ($str == null) {
+ if (empty($this->_parseFile)) {
+ throw new exception("nothing to parse");
+ }
+
+ $out = $this->compileFile($this->_parseFile);
+ } else {
+ $out = $this->compile($str);
+ }
+
+ $this->registeredVars = $oldVars;
+ return $out;
+ }
+
+ protected function makeParser($name) {
+ $parser = new lessc_parser($this, $name);
+ $parser->writeComments = $this->preserveComments;
+
+ return $parser;
+ }
+
+ public function setFormatter($name) {
+ $this->formatterName = $name;
+ }
+
+ protected function newFormatter() {
+ $className = "lessc_formatter_lessjs";
+ if (!empty($this->formatterName)) {
+ if (!is_string($this->formatterName))
+ return $this->formatterName;
+ $className = "lessc_formatter_$this->formatterName";
+ }
+
+ return new $className;
+ }
+
+ public function setPreserveComments($preserve) {
+ $this->preserveComments = $preserve;
+ }
+
+ public function registerFunction($name, $func) {
+ $this->libFunctions[$name] = $func;
+ }
+
+ public function unregisterFunction($name) {
+ unset($this->libFunctions[$name]);
+ }
+
+ public function setVariables($variables) {
+ $this->registeredVars = array_merge($this->registeredVars, $variables);
+ }
+
+ public function unsetVariable($name) {
+ unset($this->registeredVars[$name]);
+ }
+
+ public function setImportDir($dirs) {
+ $this->importDir = (array)$dirs;
+ }
+
+ public function addImportDir($dir) {
+ $this->importDir = (array)$this->importDir;
+ $this->importDir[] = $dir;
+ }
+
+ public function allParsedFiles() {
+ return $this->allParsedFiles;
+ }
+
+ protected function addParsedFile($file) {
+ $this->allParsedFiles[realpath($file)] = filemtime($file);
+ }
+
+ /**
+ * Uses the current value of $this->count to show line and line number
+ */
+ protected function throwError($msg = null) {
+ if ($this->sourceLoc >= 0) {
+ $this->sourceParser->throwError($msg, $this->sourceLoc);
+ }
+ throw new exception($msg);
+ }
+
+ // compile file $in to file $out if $in is newer than $out
+ // returns true when it compiles, false otherwise
+ public static function ccompile($in, $out, $less = null) {
+ if ($less === null) {
+ $less = new self;
+ }
+ return $less->checkedCompile($in, $out);
+ }
+
+ public static function cexecute($in, $force = false, $less = null) {
+ if ($less === null) {
+ $less = new self;
+ }
+ return $less->cachedCompile($in, $force);
+ }
+
+ static protected $cssColors = array(
+ 'aliceblue' => '240,248,255',
+ 'antiquewhite' => '250,235,215',
+ 'aqua' => '0,255,255',
+ 'aquamarine' => '127,255,212',
+ 'azure' => '240,255,255',
+ 'beige' => '245,245,220',
+ 'bisque' => '255,228,196',
+ 'black' => '0,0,0',
+ 'blanchedalmond' => '255,235,205',
+ 'blue' => '0,0,255',
+ 'blueviolet' => '138,43,226',
+ 'brown' => '165,42,42',
+ 'burlywood' => '222,184,135',
+ 'cadetblue' => '95,158,160',
+ 'chartreuse' => '127,255,0',
+ 'chocolate' => '210,105,30',
+ 'coral' => '255,127,80',
+ 'cornflowerblue' => '100,149,237',
+ 'cornsilk' => '255,248,220',
+ 'crimson' => '220,20,60',
+ 'cyan' => '0,255,255',
+ 'darkblue' => '0,0,139',
+ 'darkcyan' => '0,139,139',
+ 'darkgoldenrod' => '184,134,11',
+ 'darkgray' => '169,169,169',
+ 'darkgreen' => '0,100,0',
+ 'darkgrey' => '169,169,169',
+ 'darkkhaki' => '189,183,107',
+ 'darkmagenta' => '139,0,139',
+ 'darkolivegreen' => '85,107,47',
+ 'darkorange' => '255,140,0',
+ 'darkorchid' => '153,50,204',
+ 'darkred' => '139,0,0',
+ 'darksalmon' => '233,150,122',
+ 'darkseagreen' => '143,188,143',
+ 'darkslateblue' => '72,61,139',
+ 'darkslategray' => '47,79,79',
+ 'darkslategrey' => '47,79,79',
+ 'darkturquoise' => '0,206,209',
+ 'darkviolet' => '148,0,211',
+ 'deeppink' => '255,20,147',
+ 'deepskyblue' => '0,191,255',
+ 'dimgray' => '105,105,105',
+ 'dimgrey' => '105,105,105',
+ 'dodgerblue' => '30,144,255',
+ 'firebrick' => '178,34,34',
+ 'floralwhite' => '255,250,240',
+ 'forestgreen' => '34,139,34',
+ 'fuchsia' => '255,0,255',
+ 'gainsboro' => '220,220,220',
+ 'ghostwhite' => '248,248,255',
+ 'gold' => '255,215,0',
+ 'goldenrod' => '218,165,32',
+ 'gray' => '128,128,128',
+ 'green' => '0,128,0',
+ 'greenyellow' => '173,255,47',
+ 'grey' => '128,128,128',
+ 'honeydew' => '240,255,240',
+ 'hotpink' => '255,105,180',
+ 'indianred' => '205,92,92',
+ 'indigo' => '75,0,130',
+ 'ivory' => '255,255,240',
+ 'khaki' => '240,230,140',
+ 'lavender' => '230,230,250',
+ 'lavenderblush' => '255,240,245',
+ 'lawngreen' => '124,252,0',
+ 'lemonchiffon' => '255,250,205',
+ 'lightblue' => '173,216,230',
+ 'lightcoral' => '240,128,128',
+ 'lightcyan' => '224,255,255',
+ 'lightgoldenrodyellow' => '250,250,210',
+ 'lightgray' => '211,211,211',
+ 'lightgreen' => '144,238,144',
+ 'lightgrey' => '211,211,211',
+ 'lightpink' => '255,182,193',
+ 'lightsalmon' => '255,160,122',
+ 'lightseagreen' => '32,178,170',
+ 'lightskyblue' => '135,206,250',
+ 'lightslategray' => '119,136,153',
+ 'lightslategrey' => '119,136,153',
+ 'lightsteelblue' => '176,196,222',
+ 'lightyellow' => '255,255,224',
+ 'lime' => '0,255,0',
+ 'limegreen' => '50,205,50',
+ 'linen' => '250,240,230',
+ 'magenta' => '255,0,255',
+ 'maroon' => '128,0,0',
+ 'mediumaquamarine' => '102,205,170',
+ 'mediumblue' => '0,0,205',
+ 'mediumorchid' => '186,85,211',
+ 'mediumpurple' => '147,112,219',
+ 'mediumseagreen' => '60,179,113',
+ 'mediumslateblue' => '123,104,238',
+ 'mediumspringgreen' => '0,250,154',
+ 'mediumturquoise' => '72,209,204',
+ 'mediumvioletred' => '199,21,133',
+ 'midnightblue' => '25,25,112',
+ 'mintcream' => '245,255,250',
+ 'mistyrose' => '255,228,225',
+ 'moccasin' => '255,228,181',
+ 'navajowhite' => '255,222,173',
+ 'navy' => '0,0,128',
+ 'oldlace' => '253,245,230',
+ 'olive' => '128,128,0',
+ 'olivedrab' => '107,142,35',
+ 'orange' => '255,165,0',
+ 'orangered' => '255,69,0',
+ 'orchid' => '218,112,214',
+ 'palegoldenrod' => '238,232,170',
+ 'palegreen' => '152,251,152',
+ 'paleturquoise' => '175,238,238',
+ 'palevioletred' => '219,112,147',
+ 'papayawhip' => '255,239,213',
+ 'peachpuff' => '255,218,185',
+ 'peru' => '205,133,63',
+ 'pink' => '255,192,203',
+ 'plum' => '221,160,221',
+ 'powderblue' => '176,224,230',
+ 'purple' => '128,0,128',
+ 'red' => '255,0,0',
+ 'rosybrown' => '188,143,143',
+ 'royalblue' => '65,105,225',
+ 'saddlebrown' => '139,69,19',
+ 'salmon' => '250,128,114',
+ 'sandybrown' => '244,164,96',
+ 'seagreen' => '46,139,87',
+ 'seashell' => '255,245,238',
+ 'sienna' => '160,82,45',
+ 'silver' => '192,192,192',
+ 'skyblue' => '135,206,235',
+ 'slateblue' => '106,90,205',
+ 'slategray' => '112,128,144',
+ 'slategrey' => '112,128,144',
+ 'snow' => '255,250,250',
+ 'springgreen' => '0,255,127',
+ 'steelblue' => '70,130,180',
+ 'tan' => '210,180,140',
+ 'teal' => '0,128,128',
+ 'thistle' => '216,191,216',
+ 'tomato' => '255,99,71',
+ 'turquoise' => '64,224,208',
+ 'violet' => '238,130,238',
+ 'wheat' => '245,222,179',
+ 'white' => '255,255,255',
+ 'whitesmoke' => '245,245,245',
+ 'yellow' => '255,255,0',
+ 'yellowgreen' => '154,205,50'
+ );
+}
+
+// responsible for taking a string of LESS code and converting it into a
+// syntax tree
+class lessc_parser {
+ static protected $nextBlockId = 0; // used to uniquely identify blocks
+
+ static protected $precedence = array(
+ '=<' => 0,
+ '>=' => 0,
+ '=' => 0,
+ '<' => 0,
+ '>' => 0,
+
+ '+' => 1,
+ '-' => 1,
+ '*' => 2,
+ '/' => 2,
+ '%' => 2,
+ );
+
+ static protected $whitePattern;
+ static protected $commentMulti;
+
+ static protected $commentSingle = "//";
+ static protected $commentMultiLeft = "/*";
+ static protected $commentMultiRight = "*/";
+
+ // regex string to match any of the operators
+ static protected $operatorString;
+
+ // these properties will supress division unless it's inside parenthases
+ static protected $supressDivisionProps =
+ array('/border-radius$/i', '/^font$/i');
+
+ protected $blockDirectives = array("font-face", "keyframes", "page", "-moz-document");
+ protected $lineDirectives = array("charset");
+
+ /**
+ * if we are in parens we can be more liberal with whitespace around
+ * operators because it must evaluate to a single value and thus is less
+ * ambiguous.
+ *
+ * Consider:
+ * property1: 10 -5; // is two numbers, 10 and -5
+ * property2: (10 -5); // should evaluate to 5
+ */
+ protected $inParens = false;
+
+ // caches preg escaped literals
+ static protected $literalCache = array();
+
+ public function __construct($lessc, $sourceName = null) {
+ $this->eatWhiteDefault = true;
+ // reference to less needed for vPrefix, mPrefix, and parentSelector
+ $this->lessc = $lessc;
+
+ $this->sourceName = $sourceName; // name used for error messages
+
+ $this->writeComments = false;
+
+ if (!self::$operatorString) {
+ self::$operatorString =
+ '('.implode('|', array_map(array('lessc', 'preg_quote'),
+ array_keys(self::$precedence))).')';
+
+ $commentSingle = lessc::preg_quote(self::$commentSingle);
+ $commentMultiLeft = lessc::preg_quote(self::$commentMultiLeft);
+ $commentMultiRight = lessc::preg_quote(self::$commentMultiRight);
+
+ self::$commentMulti = $commentMultiLeft.'.*?'.$commentMultiRight;
+ self::$whitePattern = '/'.$commentSingle.'[^\n]*\s*|('.self::$commentMulti.')\s*|\s+/Ais';
+ }
+ }
+
+ public function parse($buffer) {
+ $this->count = 0;
+ $this->line = 1;
+
+ $this->env = null; // block stack
+ $this->buffer = $this->writeComments ? $buffer : $this->removeComments($buffer);
+ $this->pushSpecialBlock("root");
+ $this->eatWhiteDefault = true;
+ $this->seenComments = array();
+
+ // trim whitespace on head
+ // if (preg_match('/^\s+/', $this->buffer, $m)) {
+ // $this->line += substr_count($m[0], "\n");
+ // $this->buffer = ltrim($this->buffer);
+ // }
+ $this->whitespace();
+
+ // parse the entire file
+ $lastCount = $this->count;
+ while (false !== $this->parseChunk());
+
+ if ($this->count != strlen($this->buffer))
+ $this->throwError();
+
+ // TODO report where the block was opened
+ if (!is_null($this->env->parent))
+ throw new exception('parse error: unclosed block');
+
+ 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 lessc::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 lessc::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, lessc::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.
+ */
+ protected function parseChunk() {
+ if (empty($this->buffer)) return false;
+ $s = $this->seek();
+
+ // setting a property
+ if ($this->keyword($key) && $this->assign() &&
+ $this->propertyValue($value, $key) && $this->end())
+ {
+ $this->append(array('assign', $key, $value), $s);
+ return true;
+ } else {
+ $this->seek($s);
+ }
+
+
+ // look for special css blocks
+ if ($this->literal('@', false)) {
+ $this->count--;
+
+ // media
+ if ($this->literal('@media')) {
+ if (($this->mediaQueryList($mediaQueries) || true)
+ && $this->literal('{'))
+ {
+ $media = $this->pushSpecialBlock("media");
+ $media->queries = is_null($mediaQueries) ? array() : $mediaQueries;
+ return true;
+ } else {
+ $this->seek($s);
+ return false;
+ }
+ }
+
+ if ($this->literal("@", false) && $this->keyword($dirName)) {
+ if ($this->isDirective($dirName, $this->blockDirectives)) {
+ if (($this->openString("{", $dirValue, null, array(";")) || true) &&
+ $this->literal("{"))
+ {
+ $dir = $this->pushSpecialBlock("directive");
+ $dir->name = $dirName;
+ if (isset($dirValue)) $dir->value = $dirValue;
+ return true;
+ }
+ } elseif ($this->isDirective($dirName, $this->lineDirectives)) {
+ if ($this->propertyValue($dirValue) && $this->end()) {
+ $this->append(array("directive", $dirName, $dirValue));
+ return true;
+ }
+ }
+ }
+
+ $this->seek($s);
+ }
+
+ // setting a variable
+ if ($this->variable($var) && $this->assign() &&
+ $this->propertyValue($value) && $this->end())
+ {
+ $this->append(array('assign', $var, $value), $s);
+ return true;
+ } else {
+ $this->seek($s);
+ }
+
+ if ($this->import($importValue)) {
+ $this->append($importValue, $s);
+ return true;
+ }
+
+ // opening parametric mixin
+ if ($this->tag($tag, true) && $this->argumentDef($args, $isVararg) &&
+ ($this->guards($guards) || true) &&
+ $this->literal('{'))
+ {
+ $block = $this->pushBlock($this->fixTags(array($tag)));
+ $block->args = $args;
+ $block->isVararg = $isVararg;
+ if (!empty($guards)) $block->guards = $guards;
+ return true;
+ } else {
+ $this->seek($s);
+ }
+
+ // opening a simple block
+ if ($this->tags($tags) && $this->literal('{')) {
+ $tags = $this->fixTags($tags);
+ $this->pushBlock($tags);
+ return true;
+ } else {
+ $this->seek($s);
+ }
+
+ // closing a block
+ if ($this->literal('}', false)) {
+ try {
+ $block = $this->pop();
+ } catch (exception $e) {
+ $this->seek($s);
+ $this->throwError($e->getMessage());
+ }
+
+ $hidden = false;
+ if (is_null($block->type)) {
+ $hidden = true;
+ if (!isset($block->args)) {
+ foreach ($block->tags as $tag) {
+ if (!is_string($tag) || $tag{0} != $this->lessc->mPrefix) {
+ $hidden = false;
+ break;
+ }
+ }
+ }
+
+ foreach ($block->tags as $tag) {
+ if (is_string($tag)) {
+ $this->env->children[$tag][] = $block;
+ }
+ }
+ }
+
+ if (!$hidden) {
+ $this->append(array('block', $block), $s);
+ }
+
+ // this is done here so comments aren't bundled into he block that
+ // was just closed
+ $this->whitespace();
+ return true;
+ }
+
+ // mixin
+ if ($this->mixinTags($tags) &&
+ ($this->argumentValues($argv) || true) &&
+ ($this->keyword($suffix) || true) && $this->end())
+ {
+ $tags = $this->fixTags($tags);
+ $this->append(array('mixin', $tags, $argv, $suffix), $s);
+ return true;
+ } else {
+ $this->seek($s);
+ }
+
+ // spare ;
+ if ($this->literal(';')) return true;
+
+ return false; // got nothing, throw error
+ }
+
+ protected function isDirective($dirname, $directives) {
+ // TODO: cache pattern in parser
+ $pattern = implode("|",
+ array_map(array("lessc", "preg_quote"), $directives));
+ $pattern = '/^(-[a-z-]+-)?(' . $pattern . ')$/i';
+
+ return preg_match($pattern, $dirname);
+ }
+
+ protected function fixTags($tags) {
+ // move @ tags out of variable namespace
+ foreach ($tags as &$tag) {
+ if ($tag{0} == $this->lessc->vPrefix)
+ $tag[0] = $this->lessc->mPrefix;
+ }
+ return $tags;
+ }
+
+ // a list of expressions
+ protected function expressionList(&$exps) {
+ $values = array();
+
+ while ($this->expression($exp)) {
+ $values[] = $exp;
+ }
+
+ if (count($values) == 0) return false;
+
+ $exps = lessc::compressList($values, ' ');
+ return true;
+ }
+
+ /**
+ * Attempt to consume an expression.
+ * @link http://en.wikipedia.org/wiki/Operator-precedence_parser#Pseudo-code
+ */
+ protected function expression(&$out) {
+ if ($this->value($lhs)) {
+ $out = $this->expHelper($lhs, 0);
+
+ // look for / shorthand
+ if (!empty($this->env->supressedDivision)) {
+ unset($this->env->supressedDivision);
+ $s = $this->seek();
+ if ($this->literal("/") && $this->value($rhs)) {
+ $out = array("list", "",
+ array($out, array("keyword", "/"), $rhs));
+ } else {
+ $this->seek($s);
+ }
+ }
+
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * recursively parse infix equation with $lhs at precedence $minP
+ */
+ protected function expHelper($lhs, $minP) {
+ $this->inExp = true;
+ $ss = $this->seek();
+
+ while (true) {
+ $whiteBefore = isset($this->buffer[$this->count - 1]) &&
+ ctype_space($this->buffer[$this->count - 1]);
+
+ // If there is whitespace before the operator, then we require
+ // whitespace after the operator for it to be an expression
+ $needWhite = $whiteBefore && !$this->inParens;
+
+ if ($this->match(self::$operatorString.($needWhite ? '\s' : ''), $m) && self::$precedence[$m[1]] >= $minP) {
+ if (!$this->inParens && isset($this->env->currentProperty) && $m[1] == "/" && empty($this->env->supressedDivision)) {
+ foreach (self::$supressDivisionProps as $pattern) {
+ if (preg_match($pattern, $this->env->currentProperty)) {
+ $this->env->supressedDivision = true;
+ break 2;
+ }
+ }
+ }
+
+
+ $whiteAfter = isset($this->buffer[$this->count - 1]) &&
+ ctype_space($this->buffer[$this->count - 1]);
+
+ if (!$this->value($rhs)) break;
+
+ // peek for next operator to see what to do with rhs
+ if ($this->peek(self::$operatorString, $next) && self::$precedence[$next[1]] > self::$precedence[$m[1]]) {
+ $rhs = $this->expHelper($rhs, self::$precedence[$next[1]]);
+ }
+
+ $lhs = array('expression', $m[1], $lhs, $rhs, $whiteBefore, $whiteAfter);
+ $ss = $this->seek();
+
+ continue;
+ }
+
+ break;
+ }
+
+ $this->seek($ss);
+
+ return $lhs;
+ }
+
+ // consume a list of values for a property
+ public function propertyValue(&$value, $keyName = null) {
+ $values = array();
+
+ if ($keyName !== null) $this->env->currentProperty = $keyName;
+
+ $s = null;
+ while ($this->expressionList($v)) {
+ $values[] = $v;
+ $s = $this->seek();
+ if (!$this->literal(',')) break;
+ }
+
+ if ($s) $this->seek($s);
+
+ if ($keyName !== null) unset($this->env->currentProperty);
+
+ if (count($values) == 0) return false;
+
+ $value = lessc::compressList($values, ', ');
+ return true;
+ }
+
+ protected function parenValue(&$out) {
+ $s = $this->seek();
+
+ // speed shortcut
+ if (isset($this->buffer[$this->count]) && $this->buffer[$this->count] != "(") {
+ return false;
+ }
+
+ $inParens = $this->inParens;
+ if ($this->literal("(") &&
+ ($this->inParens = true) && $this->expression($exp) &&
+ $this->literal(")"))
+ {
+ $out = $exp;
+ $this->inParens = $inParens;
+ return true;
+ } else {
+ $this->inParens = $inParens;
+ $this->seek($s);
+ }
+
+ return false;
+ }
+
+ // a single value
+ protected function value(&$value) {
+ $s = $this->seek();
+
+ // speed shortcut
+ if (isset($this->buffer[$this->count]) && $this->buffer[$this->count] == "-") {
+ // negation
+ if ($this->literal("-", false) &&
+ (($this->variable($inner) && $inner = array("variable", $inner)) ||
+ $this->unit($inner) ||
+ $this->parenValue($inner)))
+ {
+ $value = array("unary", "-", $inner);
+ return true;
+ } else {
+ $this->seek($s);
+ }
+ }
+
+ if ($this->parenValue($value)) return true;
+ if ($this->unit($value)) return true;
+ if ($this->color($value)) return true;
+ if ($this->func($value)) return true;
+ if ($this->string($value)) return true;
+
+ if ($this->keyword($word)) {
+ $value = array('keyword', $word);
+ return true;
+ }
+
+ // try a variable
+ if ($this->variable($var)) {
+ $value = array('variable', $var);
+ return true;
+ }
+
+ // unquote string (should this work on any type?
+ if ($this->literal("~") && $this->string($str)) {
+ $value = array("escape", $str);
+ return true;
+ } else {
+ $this->seek($s);
+ }
+
+ // css hack: \0
+ if ($this->literal('\\') && $this->match('([0-9]+)', $m)) {
+ $value = array('keyword', '\\'.$m[1]);
+ return true;
+ } else {
+ $this->seek($s);
+ }
+
+ return false;
+ }
+
+ // an import statement
+ protected function import(&$out) {
+ $s = $this->seek();
+ if (!$this->literal('@import')) return false;
+
+ // @import "something.css" media;
+ // @import url("something.css") media;
+ // @import url(something.css) media;
+
+ if ($this->propertyValue($value)) {
+ $out = array("import", $value);
+ return true;
+ }
+ }
+
+ protected function mediaQueryList(&$out) {
+ if ($this->genericList($list, "mediaQuery", ",", false)) {
+ $out = $list[2];
+ return true;
+ }
+ return false;
+ }
+
+ protected function mediaQuery(&$out) {
+ $s = $this->seek();
+
+ $expressions = null;
+ $parts = array();
+
+ if (($this->literal("only") && ($only = true) || $this->literal("not") && ($not = true) || true) && $this->keyword($mediaType)) {
+ $prop = array("mediaType");
+ if (isset($only)) $prop[] = "only";
+ if (isset($not)) $prop[] = "not";
+ $prop[] = $mediaType;
+ $parts[] = $prop;
+ } else {
+ $this->seek($s);
+ }
+
+
+ if (!empty($mediaType) && !$this->literal("and")) {
+ // ~
+ } else {
+ $this->genericList($expressions, "mediaExpression", "and", false);
+ if (is_array($expressions)) $parts = array_merge($parts, $expressions[2]);
+ }
+
+ if (count($parts) == 0) {
+ $this->seek($s);
+ return false;
+ }
+
+ $out = $parts;
+ return true;
+ }
+
+ protected function mediaExpression(&$out) {
+ $s = $this->seek();
+ $value = null;
+ if ($this->literal("(") &&
+ $this->keyword($feature) &&
+ ($this->literal(":") && $this->expression($value) || true) &&
+ $this->literal(")"))
+ {
+ $out = array("mediaExp", $feature);
+ if ($value) $out[] = $value;
+ return true;
+ }
+
+ $this->seek($s);
+ return false;
+ }
+
+ // an unbounded string stopped by $end
+ protected function openString($end, &$out, $nestingOpen=null, $rejectStrs = null) {
+ $oldWhite = $this->eatWhiteDefault;
+ $this->eatWhiteDefault = false;
+
+ $stop = array("'", '"', "@{", $end);
+ $stop = array_map(array("lessc", "preg_quote"), $stop);
+ // $stop[] = self::$commentMulti;
+
+ if (!is_null($rejectStrs)) {
+ $stop = array_merge($stop, $rejectStrs);
+ }
+
+ $patt = '(.*?)('.implode("|", $stop).')';
+
+ $nestingLevel = 0;
+
+ $content = array();
+ while ($this->match($patt, $m, false)) {
+ if (!empty($m[1])) {
+ $content[] = $m[1];
+ if ($nestingOpen) {
+ $nestingLevel += substr_count($m[1], $nestingOpen);
+ }
+ }
+
+ $tok = $m[2];
+
+ $this->count-= strlen($tok);
+ if ($tok == $end) {
+ if ($nestingLevel == 0) {
+ break;
+ } else {
+ $nestingLevel--;
+ }
+ }
+
+ if (($tok == "'" || $tok == '"') && $this->string($str)) {
+ $content[] = $str;
+ continue;
+ }
+
+ if ($tok == "@{" && $this->interpolation($inter)) {
+ $content[] = $inter;
+ continue;
+ }
+
+ if (in_array($tok, $rejectStrs)) {
+ $count = null;
+ break;
+ }
+
+
+ $content[] = $tok;
+ $this->count+= strlen($tok);
+ }
+
+ $this->eatWhiteDefault = $oldWhite;
+
+ if (count($content) == 0) return false;
+
+ // trim the end
+ if (is_string(end($content))) {
+ $content[count($content) - 1] = rtrim(end($content));
+ }
+
+ $out = array("string", "", $content);
+ return true;
+ }
+
+ protected function string(&$out) {
+ $s = $this->seek();
+ if ($this->literal('"', false)) {
+ $delim = '"';
+ } elseif ($this->literal("'", false)) {
+ $delim = "'";
+ } else {
+ return false;
+ }
+
+ $content = array();
+
+ // look for either ending delim , escape, or string interpolation
+ $patt = '([^\n]*?)(@\{|\\\\|' .
+ lessc::preg_quote($delim).')';
+
+ $oldWhite = $this->eatWhiteDefault;
+ $this->eatWhiteDefault = false;
+
+ while ($this->match($patt, $m, false)) {
+ $content[] = $m[1];
+ if ($m[2] == "@{") {
+ $this->count -= strlen($m[2]);
+ if ($this->interpolation($inter, false)) {
+ $content[] = $inter;
+ } else {
+ $this->count += strlen($m[2]);
+ $content[] = "@{"; // ignore it
+ }
+ } elseif ($m[2] == '\\') {
+ $content[] = $m[2];
+ if ($this->literal($delim, false)) {
+ $content[] = $delim;
+ }
+ } else {
+ $this->count -= strlen($delim);
+ break; // delim
+ }
+ }
+
+ $this->eatWhiteDefault = $oldWhite;
+
+ if ($this->literal($delim)) {
+ $out = array("string", $delim, $content);
+ return true;
+ }
+
+ $this->seek($s);
+ return false;
+ }
+
+ protected function interpolation(&$out) {
+ $oldWhite = $this->eatWhiteDefault;
+ $this->eatWhiteDefault = true;
+
+ $s = $this->seek();
+ if ($this->literal("@{") &&
+ $this->keyword($var) &&
+ $this->literal("}", false))
+ {
+ $out = array("variable", $this->lessc->vPrefix . $var);
+ $this->eatWhiteDefault = $oldWhite;
+ if ($this->eatWhiteDefault) $this->whitespace();
+ return true;
+ }
+
+ $this->eatWhiteDefault = $oldWhite;
+ $this->seek($s);
+ return false;
+ }
+
+ protected function unit(&$unit) {
+ // speed shortcut
+ if (isset($this->buffer[$this->count])) {
+ $char = $this->buffer[$this->count];
+ if (!ctype_digit($char) && $char != ".") return false;
+ }
+
+ if ($this->match('([0-9]+(?:\.[0-9]*)?|\.[0-9]+)([%a-zA-Z]+)?', $m)) {
+ $unit = array("number", $m[1], empty($m[2]) ? "" : $m[2]);
+ return true;
+ }
+ return false;
+ }
+
+ // a # color
+ protected function color(&$out) {
+ if ($this->match('(#(?:[0-9a-f]{8}|[0-9a-f]{6}|[0-9a-f]{3}))', $m)) {
+ if (strlen($m[1]) > 7) {
+ $out = array("string", "", array($m[1]));
+ } else {
+ $out = array("raw_color", $m[1]);
+ }
+ return true;
+ }
+
+ 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 = ',') {
+ $s = $this->seek();
+ if (!$this->literal('(')) return false;
+
+ $values = array();
+
+ $isVararg = false;
+ while (true) {
+ if ($this->literal("...")) {
+ $isVararg = true;
+ 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;
+ }
+ }
+ $values[] = $arg;
+ if ($isVararg) break;
+ continue;
+ }
+
+ if ($this->value($literal)) {
+ $values[] = array("lit", $literal);
+ }
+
+ if (!$this->literal($delim)) break;
+ }
+
+ if (!$this->literal(')')) {
+ $this->seek($s);
+ return false;
+ }
+
+ $args = $values;
+
+ return true;
+ }
+
+ // consume a list of tags
+ // this accepts a hanging delimiter
+ protected function tags(&$tags, $simple = false, $delim = ',') {
+ $tags = array();
+ while ($this->tag($tt, $simple)) {
+ $tags[] = $tt;
+ if (!$this->literal($delim)) break;
+ }
+ if (count($tags) == 0) return false;
+
+ return true;
+ }
+
+ // list of tags of specifying mixin path
+ // optionally separated by > (lazy, accepts extra >)
+ protected function mixinTags(&$tags) {
+ $s = $this->seek();
+ $tags = array();
+ while ($this->tag($tt, true)) {
+ $tags[] = $tt;
+ $this->literal(">");
+ }
+
+ if (count($tags) == 0) return false;
+
+ return true;
+ }
+
+ // a bracketed value (contained within in a tag definition)
+ protected function tagBracket(&$value) {
+ // 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;
+ }
+
+ $this->seek($s);
+ return false;
+ }
+
+ protected function tagExpression(&$value) {
+ $s = $this->seek();
+ if ($this->literal("(") && $this->expression($exp) && $this->literal(")")) {
+ $value = array('exp', $exp);
+ return true;
+ }
+
+ $this->seek($s);
+ return false;
+ }
+
+ // a single tag
+ protected function tag(&$tag, $simple = false) {
+ if ($simple)
+ $chars = '^,:;{}\][>\(\) "\'';
+ else
+ $chars = '^,;{}["\'';
+
+ if (!$simple && $this->tagExpression($tag)) {
+ return true;
+ }
+
+ $tag = '';
+ while ($this->tagBracket($first)) $tag .= $first;
+
+ while (true) {
+ if ($this->match('(['.$chars.'0-9]['.$chars.']*)', $m)) {
+ $tag .= $m[1];
+ if ($simple) break;
+
+ while ($this->tagBracket($brack)) $tag .= $brack;
+ continue;
+ } elseif ($this->unit($unit)) { // for keyframes
+ $tag .= $unit[1] . $unit[2];
+ continue;
+ }
+ break;
+ }
+
+
+ $tag = trim($tag);
+ if ($tag == '') return false;
+
+ return true;
+ }
+
+ // a css function
+ protected function func(&$func) {
+ $s = $this->seek();
+
+ if ($this->match('(%|[\w\-_][\w\-_:\.]+|[\w_])', $m) && $this->literal('(')) {
+ $fname = $m[1];
+
+ $sPreArgs = $this->seek();
+
+ $args = array();
+ while (true) {
+ $ss = $this->seek();
+ // this ugly nonsense is for ie filter properties
+ if ($this->keyword($name) && $this->literal('=') && $this->expressionList($value)) {
+ $args[] = array("string", "", array($name, "=", $value));
+ } else {
+ $this->seek($ss);
+ if ($this->expressionList($value)) {
+ $args[] = $value;
+ }
+ }
+
+ if (!$this->literal(',')) break;
+ }
+ $args = array('list', ',', $args);
+
+ if ($this->literal(')')) {
+ $func = array('function', $fname, $args);
+ return true;
+ } elseif ($fname == 'url') {
+ // couldn't parse and in url? treat as string
+ $this->seek($sPreArgs);
+ if ($this->openString(")", $string) && $this->literal(")")) {
+ $func = array('function', $fname, $string);
+ return true;
+ }
+ }
+ }
+
+ $this->seek($s);
+ return false;
+ }
+
+ // consume a less variable
+ protected function variable(&$name) {
+ $s = $this->seek();
+ if ($this->literal($this->lessc->vPrefix, false) &&
+ ($this->variable($sub) || $this->keyword($name)))
+ {
+ if (!empty($sub)) {
+ $name = array('variable', $sub);
+ } else {
+ $name = $this->lessc->vPrefix.$name;
+ }
+ return true;
+ }
+
+ $name = null;
+ $this->seek($s);
+ return false;
+ }
+
+ /**
+ * Consume an assignment operator
+ * Can optionally take a name that will be set to the current property name
+ */
+ protected function assign($name = null) {
+ if ($name) $this->currentProperty = $name;
+ return $this->literal(':') || $this->literal('=');
+ }
+
+ // consume a keyword
+ protected function keyword(&$word) {
+ if ($this->match('([\w_\-\*!"][\w\-_"]*)', $m)) {
+ $word = $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} == '}') {
+ // if there is end of file or a closing block next then we don't need a ;
+ return true;
+ }
+ return false;
+ }
+
+ protected function guards(&$guards) {
+ $s = $this->seek();
+
+ if (!$this->literal("when")) {
+ $this->seek($s);
+ return false;
+ }
+
+ $guards = array();
+
+ while ($this->guardGroup($g)) {
+ $guards[] = $g;
+ if (!$this->literal(",")) break;
+ }
+
+ if (count($guards) == 0) {
+ $guards = null;
+ $this->seek($s);
+ return false;
+ }
+
+ return true;
+ }
+
+ // a bunch of guards that are and'd together
+ // TODO rename to guardGroup
+ protected function guardGroup(&$guardGroup) {
+ $s = $this->seek();
+ $guardGroup = array();
+ while ($this->guard($guard)) {
+ $guardGroup[] = $guard;
+ if (!$this->literal("and")) break;
+ }
+
+ if (count($guardGroup) == 0) {
+ $guardGroup = null;
+ $this->seek($s);
+ return false;
+ }
+
+ return true;
+ }
+
+ protected function guard(&$guard) {
+ $s = $this->seek();
+ $negate = $this->literal("not");
+
+ if ($this->literal("(") && $this->expression($exp) && $this->literal(")")) {
+ $guard = $exp;
+ if ($negate) $guard = array("negate", $guard);
+ return true;
+ }
+
+ $this->seek($s);
+ return false;
+ }
+
+ /* raw parsing functions */
+
+ protected function literal($what, $eatWhitespace = null) {
+ if ($eatWhitespace === null) $eatWhitespace = $this->eatWhiteDefault;
+
+ // shortcut on single letter
+ 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;
+ }
+ }
+
+ if (!isset(self::$literalCache[$what])) {
+ self::$literalCache[$what] = lessc::preg_quote($what);
+ }
+
+ return $this->match(self::$literalCache[$what], $m, $eatWhitespace);
+ }
+
+ protected function genericList(&$out, $parseItem, $delim="", $flatten=true) {
+ $s = $this->seek();
+ $items = array();
+ while ($this->$parseItem($value)) {
+ $items[] = $value;
+ if ($delim) {
+ if (!$this->literal($delim)) break;
+ }
+ }
+
+ if (count($items) == 0) {
+ $this->seek($s);
+ return false;
+ }
+
+ if ($flatten && count($items) == 1) {
+ $out = $items[0];
+ } else {
+ $out = array("list", $delim, $items);
+ }
+
+ return true;
+ }
+
+
+ // advance counter to next occurrence of $what
+ // $until - don't include $what in advance
+ // $allowNewline, if string, will be used as valid char set
+ protected function to($what, &$out, $until = false, $allowNewline = false) {
+ if (is_string($allowNewline)) {
+ $validChars = $allowNewline;
+ } else {
+ $validChars = $allowNewline ? "." : "[^\n]";
+ }
+ if (!$this->match('('.$validChars.'*?)'.lessc::preg_quote($what), $m, !$until)) return false;
+ if ($until) $this->count -= strlen($what); // give back $what
+ $out = $m[1];
+ return true;
+ }
+
+ // try to match something on head of buffer
+ protected function match($regex, &$out, $eatWhitespace = null) {
+ if ($eatWhitespace === null) $eatWhitespace = $this->eatWhiteDefault;
+
+ $r = '/'.$regex.($eatWhitespace && !$this->writeComments ? '\s*' : '').'/Ais';
+ if (preg_match($r, $this->buffer, $out, null, $this->count)) {
+ $this->count += strlen($out[0]);
+ if ($eatWhitespace && $this->writeComments) $this->whitespace();
+ return true;
+ }
+ return false;
+ }
+
+ // match some whitespace
+ protected function whitespace() {
+ if ($this->writeComments) {
+ $gotWhite = false;
+ while (preg_match(self::$whitePattern, $this->buffer, $m, null, $this->count)) {
+ if (isset($m[1]) && empty($this->commentsSeen[$this->count])) {
+ $this->append(array("comment", $m[1]));
+ $this->commentsSeen[$this->count] = true;
+ }
+ $this->count += strlen($m[0]);
+ $gotWhite = true;
+ }
+ return $gotWhite;
+ } else {
+ $this->match("", $m);
+ return strlen($m[0]) > 0;
+ }
+ }
+
+ // match something without consuming it
+ protected function peek($regex, &$out = null, $from=null) {
+ if (is_null($from)) $from = $this->count;
+ $r = '/'.$regex.'/Ais';
+ $result = preg_match($r, $this->buffer, $out, null, $from);
+
+ return $result;
+ }
+
+ // seek to a spot in the buffer or return where we are on no argument
+ protected function seek($where = null) {
+ if ($where === null) return $this->count;
+ else $this->count = $where;
+ return true;
+ }
+
+ /* misc functions */
+
+ public function throwError($msg = "parse error", $count = null) {
+ $count = is_null($count) ? $this->count : $count;
+
+ $line = $this->line +
+ substr_count(substr($this->buffer, 0, $count), "\n");
+
+ if (!empty($this->sourceName)) {
+ $loc = "$this->sourceName on line $line";
+ } else {
+ $loc = "line: $line";
+ }
+
+ // TODO this depends on $this->count
+ if ($this->peek("(.*?)(\n|$)", $m, $count)) {
+ throw new exception("$msg: failed at `$m[1]` $loc");
+ } else {
+ throw new exception("$msg: $loc");
+ }
+ }
+
+ protected function pushBlock($selectors=null, $type=null) {
+ $b = new stdclass;
+ $b->parent = $this->env;
+
+ $b->type = $type;
+ $b->id = self::$nextBlockId++;
+
+ $b->isVararg = false; // TODO: kill me from here
+ $b->tags = $selectors;
+
+ $b->props = array();
+ $b->children = array();
+
+ $this->env = $b;
+ return $b;
+ }
+
+ // push a block that doesn't multiply tags
+ protected function pushSpecialBlock($type) {
+ return $this->pushBlock(null, $type);
+ }
+
+ // append a property to the current block
+ protected function append($prop, $pos = null) {
+ if ($pos !== null) $prop[-1] = $pos;
+ $this->env->props[] = $prop;
+ }
+
+ // pop something off the stack
+ protected function pop() {
+ $old = $this->env;
+ $this->env = $this->env->parent;
+ return $old;
+ }
+
+ // remove comments from $text
+ // todo: make it work for all functions, not just url
+ protected function removeComments($text) {
+ $look = array(
+ 'url(', '//', '/*', '"', "'"
+ );
+
+ $out = '';
+ $min = null;
+ while (true) {
+ // find the next item
+ foreach ($look as $token) {
+ $pos = strpos($text, $token);
+ if ($pos !== false) {
+ if (!isset($min) || $pos < $min[1]) $min = array($token, $pos);
+ }
+ }
+
+ if (is_null($min)) break;
+
+ $count = $min[1];
+ $skip = 0;
+ $newlines = 0;
+ switch ($min[0]) {
+ case 'url(':
+ if (preg_match('/url\(.*?\)/', $text, $m, 0, $count))
+ $count += strlen($m[0]) - strlen($min[0]);
+ break;
+ case '"':
+ case "'":
+ if (preg_match('/'.$min[0].'.*?'.$min[0].'/', $text, $m, 0, $count))
+ $count += strlen($m[0]) - 1;
+ break;
+ case '//':
+ $skip = strpos($text, "\n", $count);
+ if ($skip === false) $skip = strlen($text) - $count;
+ else $skip -= $count;
+ break;
+ case '/*':
+ if (preg_match('/\/\*.*?\*\//s', $text, $m, 0, $count)) {
+ $skip = strlen($m[0]);
+ $newlines = substr_count($m[0], "\n");
+ }
+ break;
+ }
+
+ if ($skip == 0) $count += strlen($min[0]);
+
+ $out .= substr($text, 0, $count).str_repeat("\n", $newlines);
+ $text = substr($text, $count + $skip);
+
+ $min = null;
+ }
+
+ return $out.$text;
+ }
+
+}
+
+class lessc_formatter_classic {
+ public $indentChar = " ";
+
+ public $break = "\n";
+ public $open = " {";
+ public $close = "}";
+ public $selectorSeparator = ", ";
+ public $assignSeparator = ":";
+
+ public $openSingle = " { ";
+ public $closeSingle = " }";
+
+ public $disableSingle = false;
+ public $breakSelectors = false;
+
+ public $compressColors = false;
+
+ public function __construct() {
+ $this->indentLevel = 0;
+ }
+
+ public function indentStr($n = 0) {
+ return str_repeat($this->indentChar, max($this->indentLevel + $n, 0));
+ }
+
+ public function property($name, $value) {
+ return $name . $this->assignSeparator . $value . ";";
+ }
+
+ protected function isEmpty($block) {
+ if (empty($block->lines)) {
+ foreach ($block->children as $child) {
+ if (!$this->isEmpty($child)) return false;
+ }
+
+ return true;
+ }
+ return false;
+ }
+
+ public function block($block) {
+ if ($this->isEmpty($block)) return;
+
+ $inner = $pre = $this->indentStr();
+
+ $isSingle = !$this->disableSingle &&
+ is_null($block->type) && count($block->lines) == 1;
+
+ if (!empty($block->selectors)) {
+ $this->indentLevel++;
+
+ if ($this->breakSelectors) {
+ $selectorSeparator = $this->selectorSeparator . $this->break . $pre;
+ } else {
+ $selectorSeparator = $this->selectorSeparator;
+ }
+
+ echo $pre .
+ implode($selectorSeparator, $block->selectors);
+ if ($isSingle) {
+ echo $this->openSingle;
+ $inner = "";
+ } else {
+ echo $this->open . $this->break;
+ $inner = $this->indentStr();
+ }
+
+ }
+
+ if (!empty($block->lines)) {
+ $glue = $this->break.$inner;
+ echo $inner . implode($glue, $block->lines);
+ if (!$isSingle && !empty($block->children)) {
+ echo $this->break;
+ }
+ }
+
+ foreach ($block->children as $child) {
+ $this->block($child);
+ }
+
+ if (!empty($block->selectors)) {
+ if (!$isSingle && empty($block->children)) echo $this->break;
+
+ if ($isSingle) {
+ echo $this->closeSingle . $this->break;
+ } else {
+ echo $pre . $this->close . $this->break;
+ }
+
+ $this->indentLevel--;
+ }
+ }
+}
+
+class lessc_formatter_compressed extends lessc_formatter_classic {
+ public $disableSingle = true;
+ public $open = "{";
+ public $selectorSeparator = ",";
+ public $assignSeparator = ":";
+ public $break = "";
+ public $compressColors = true;
+
+ public function indentStr($n = 0) {
+ return "";
+ }
+}
+
+class lessc_formatter_lessjs extends lessc_formatter_classic {
+ public $disableSingle = true;
+ public $breakSelectors = true;
+ public $assignSeparator = ": ";
+ 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
new file mode 100644
index 00000000..5ad188cc
--- /dev/null
+++ b/plugins/jetpack/modules/custom-css/custom-css/preprocessors/scss.inc.php
@@ -0,0 +1,3759 @@
+<?php
+
+class scssc {
+ static public $VERSION = "v0.0.4";
+
+ static protected $operatorNames = array(
+ '+' => "add",
+ '-' => "sub",
+ '*' => "mul",
+ '/' => "div",
+ '%' => "mod",
+
+ '==' => "eq",
+ '!=' => "neq",
+ '<' => "lt",
+ '>' => "gt",
+
+ '<=' => "lte",
+ '>=' => "gte",
+ );
+
+ static protected $namespaces = array(
+ "special" => "%",
+ "mixin" => "@",
+ "function" => "^",
+ );
+
+ static protected $numberPrecision = 3;
+ static protected $unitTable = array(
+ "in" => array(
+ "in" => 1,
+ "pt" => 72,
+ "pc" => 6,
+ "cm" => 2.54,
+ "mm" => 25.4,
+ "px" => 96,
+ )
+ );
+
+ static public $true = array("keyword", "true");
+ static public $false = array("keyword", "false");
+
+ static public $defaultValue = array("keyword", "");
+ static public $selfSelector = array("self");
+
+ protected $importPaths = array("");
+ protected $importCache = array();
+
+ protected $userFunctions = array();
+
+ protected $formatter = "scss_formatter_nested";
+
+ function compile($code, $name=null) {
+ $this->indentLevel = -1;
+ $this->commentsSeen = array();
+ $this->extends = array();
+ $this->extendsMap = array();
+
+ $locale = setlocale(LC_NUMERIC, 0);
+ setlocale(LC_NUMERIC, "C");
+
+ $this->parsedFiles = array();
+ $this->parser = new scss_parser($name);
+ $tree = $this->parser->parse($code);
+
+ $this->formatter = new $this->formatter();
+
+ $this->env = null;
+ $this->scope = null;
+
+ $this->compileRoot($tree);
+ $this->flattenSelectors($this->scope);
+
+ ob_start();
+ $this->formatter->block($this->scope);
+ $out = ob_get_clean();
+
+ setlocale(LC_NUMERIC, $locale);
+ return $out;
+ }
+
+ protected function pushExtends($target, $origin) {
+ $i = count($this->extends);
+ $this->extends[] = array($target, $origin);
+
+ foreach ($target as $part) {
+ if (isset($this->extendsMap[$part])) {
+ $this->extendsMap[$part][] = $i;
+ } else {
+ $this->extendsMap[$part] = array($i);
+ }
+ }
+ }
+
+ protected function makeOutputBlock($type, $selectors = null) {
+ $out = new stdclass;
+ $out->type = $type;
+ $out->lines = array();
+ $out->children = array();
+ $out->parent = $this->scope;
+ $out->selectors = $selectors;
+ $out->depth = $this->env->depth;
+
+ return $out;
+ }
+
+ protected function matchExtendsSingle($single, &$out_origin, &$out_rem) {
+ $counts = array();
+ foreach ($single as $part) {
+ if (!is_string($part)) return false; // hmm
+
+ if (isset($this->extendsMap[$part])) {
+ foreach ($this->extendsMap[$part] as $idx) {
+ $counts[$idx] =
+ isset($counts[$idx]) ? $counts[$idx] + 1 : 1;
+ }
+ }
+ }
+
+ 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);
+
+ return true;
+ }
+
+ return false;
+ }
+
+ protected function combineSelectorSingle($base, $other) {
+ $tag = null;
+ $out = array();
+
+ foreach (array($base, $other) as $single) {
+ foreach ($single as $part) {
+ if (preg_match('/^[^.#:]/', $part)) {
+ $tag = $part;
+ } else {
+ $out[] = $part;
+ }
+ }
+ }
+
+ if ($tag) {
+ array_unshift($out, $tag);
+ }
+
+ return $out;
+ }
+
+ protected function matchExtends($selector, &$out, $from = 0, $initial=true) {
+ foreach ($selector as $i => $part) {
+ if ($i < $from) continue;
+
+ if ($this->matchExtendsSingle($part, $origin, $rem)) {
+ $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) {
+ if (!isset($new[$k]) || $val != $new[$k]) {
+ break;
+ }
+ }
+ }
+
+ $result = array_merge(
+ $before,
+ $k > 0 ? array_slice($new, $k) : $new,
+ $after);
+
+
+ if ($result == $selector) continue;
+ $out[] = $result;
+
+ // recursively check for more matches
+ $this->matchExtends($result, $out, $i, false);
+
+ // selector sequence merging
+ if (!empty($before) && count($new) > 1) {
+ $result2 = array_merge(
+ array_slice($new, 0, -1),
+ $k > 0 ? array_slice($before, $k) : $before,
+ array_slice($new, -1),
+ $after);
+
+ $out[] = $result2;
+ }
+ }
+ }
+ }
+ }
+
+ protected function flattenSelectors($block) {
+ if ($block->selectors) {
+ $selectors = array();
+ foreach ($block->selectors as $s) {
+ $selectors[] = $s;
+ if (!is_array($s)) continue;
+ // check extends
+ if (!empty($this->extendsMap)) {
+ $this->matchExtends($s, $selectors);
+ }
+ }
+
+ $selectors = array_map(array($this, "compileSelector"), $selectors);
+ $block->selectors = $selectors;
+ }
+
+ foreach ($block->children as $child) {
+ $this->flattenSelectors($child);
+ }
+ }
+
+ protected function compileRoot($rootBlock) {
+ $this->pushEnv($rootBlock);
+ $this->scope = $this->makeOutputBlock("root");
+ $this->compileChildren($rootBlock->children, $this->scope);
+ $this->popEnv();
+ }
+
+ protected function compileMedia($media) {
+ $this->pushEnv($media);
+ $parentScope = $this->mediaParent($this->scope);
+
+ $this->scope = $this->makeOutputBlock("media", array(
+ $this->compileMediaQuery($this->multiplyMedia($this->env)))
+ );
+
+ $parentScope->children[] = $this->scope;
+
+ $this->compileChildren($media->children, $this->scope);
+
+ $this->scope = $this->scope->parent;
+ $this->popEnv();
+ }
+
+ protected function mediaParent($scope) {
+ while (!empty($scope->parent)) {
+ if (!empty($scope->type) && $scope->type != "media") {
+ break;
+ }
+ $scope = $scope->parent;
+ }
+
+ return $scope;
+ }
+
+ // TODO refactor compileNestedBlock and compileMedia into same thing
+ protected function compileNestedBlock($block, $selectors) {
+ $this->pushEnv($block);
+
+ $this->scope = $this->makeOutputBlock($block->type, $selectors);
+ $this->scope->parent->children[] = $this->scope;
+ $this->compileChildren($block->children, $this->scope);
+
+ $this->scope = $this->scope->parent;
+ $this->popEnv();
+ }
+
+ protected function compileBlock($block) {
+ $env = $this->pushEnv($block);
+
+ $env->selectors =
+ array_map(array($this, "evalSelector"), $block->selectors);
+
+ $out = $this->makeOutputBlock(null, $this->multiplySelectors($env));
+ $this->scope->children[] = $out;
+ $this->compileChildren($block->children, $out);
+
+ $this->popEnv();
+ }
+
+ // joins together .classes and #ids
+ protected function flattenSelectorSingle($single) {
+ $joined = array();
+ foreach ($single as $part) {
+ if (empty($joined) ||
+ !is_string($part) ||
+ preg_match('/[.:#]/', $part))
+ {
+ $joined[] = $part;
+ continue;
+ }
+
+ if (is_array(end($joined))) {
+ $joined[] = $part;
+ } else {
+ $joined[count($joined) - 1] .= $part;
+ }
+ }
+
+ return $joined;
+ }
+
+ // replaces all the interpolates
+ protected function evalSelector($selector) {
+ return array_map(array($this, "evalSelectorPart"), $selector);
+ }
+
+ protected function evalSelectorPart($piece) {
+ foreach ($piece as &$p) {
+ if (!is_array($p)) continue;
+
+ switch ($p[0]) {
+ case "interpolate":
+ $p = $this->compileValue($p);
+ break;
+ }
+ }
+
+ return $this->flattenSelectorSingle($piece);
+ }
+
+ // compiles to string
+ // self(&) should have been replaced by now
+ protected function compileSelector($selector) {
+ if (!is_array($selector)) return $selector; // media and the like
+
+ return implode(" ", array_map(
+ array($this, "compileSelectorPart"), $selector));
+ }
+
+ protected function compileSelectorPart($piece) {
+ foreach ($piece as &$p) {
+ if (!is_array($p)) continue;
+
+ switch ($p[0]) {
+ case "self":
+ $p = "&";
+ break;
+ default:
+ $p = $this->compileValue($p);
+ break;
+ }
+ }
+
+ return implode($piece);
+ }
+
+ protected function compileChildren($stms, $out) {
+ foreach ($stms as $stm) {
+ $ret = $this->compileChild($stm, $out);
+ if (!is_null($ret)) return $ret;
+ }
+ }
+
+ protected function compileMediaQuery($queryList) {
+ $out = "@media";
+ $first = true;
+ foreach ($queryList as $query){
+ $parts = array();
+ foreach ($query as $q) {
+ switch ($q[0]) {
+ case "mediaType":
+ $parts[] = implode(" ", array_slice($q, 1));
+ break;
+ case "mediaExp":
+ if (isset($q[2])) {
+ $parts[] = "($q[1]" . $this->formatter->assignSeparator . $this->compileValue($q[2]) . ")";
+ } else {
+ $parts[] = "($q[1])";
+ }
+ break;
+ }
+ }
+ if (!empty($parts)) {
+ if ($first) {
+ $first = false;
+ $out .= " ";
+ } else {
+ $out .= $this->formatter->tagSeparator;
+ }
+ $out .= implode(" and ", $parts);
+ }
+ }
+ return $out;
+ }
+
+ // returns true if the value was something that could be imported
+ protected function compileImport($rawPath, $out) {
+ if ($rawPath[0] == "string") {
+ $path = $this->compileStringContent($rawPath);
+ if ($path = $this->findImport($path)) {
+ $this->importFile($path, $out);
+ return true;
+ }
+ return false;
+ } if ($rawPath[0] == "list") {
+ // handle a list of strings
+ if (count($rawPath[2]) == 0) return false;
+ foreach ($rawPath[2] as $path) {
+ if ($path[0] != "string") return false;
+ }
+
+ foreach ($rawPath[2] as $path) {
+ $this->compileImport($path, $out);
+ }
+
+ return true;
+ }
+
+ return false;
+ }
+
+ // return a value to halt execution
+ protected function compileChild($child, $out) {
+ switch ($child[0]) {
+ case "import":
+ list(,$rawPath) = $child;
+ $rawPath = $this->reduce($rawPath);
+ if (!$this->compileImport($rawPath, $out)) {
+ $out->lines[] = "@import " . $this->compileValue($rawPath) . ";";
+ }
+ break;
+ case "directive":
+ list(, $directive) = $child;
+ $s = "@" . $directive->name;
+ if (!empty($directive->value)) {
+ $s .= " " . $this->compileValue($directive->value);
+ }
+ $this->compileNestedBlock($directive, array($s));
+ break;
+ case "media":
+ $this->compileMedia($child[1]);
+ break;
+ case "block":
+ $this->compileBlock($child[1]);
+ break;
+ case "charset":
+ $out->lines[] = "@charset ".$this->compileValue($child[1]).";";
+ break;
+ case "assign":
+ list(,$name, $value) = $child;
+ if ($name[0] == "var") {
+ $isDefault = !empty($child[3]);
+ if (!$isDefault || $this->get($name[1], true) === true) {
+ $this->set($name[1], $this->reduce($value));
+ }
+ break;
+ }
+
+ $out->lines[] = $this->formatter->property(
+ $this->compileValue($child[1]),
+ $this->compileValue($child[2]));
+ break;
+ case "comment":
+ $out->lines[] = $child[1];
+ break;
+ case "mixin":
+ case "function":
+ list(,$block) = $child;
+ $this->set(self::$namespaces[$block->type] . $block->name, $block);
+ break;
+ case "extend":
+ list(, $selectors) = $child;
+ foreach ($selectors as $sel) {
+ // only use the first one
+ $sel = current($this->evalSelector($sel));
+ $this->pushExtends($sel, $out->selectors);
+ }
+ break;
+ case "if":
+ list(, $if) = $child;
+ if ($this->reduce($if->cond, true) != self::$false) {
+ 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))
+ {
+ return $this->compileChildren($case->children, $out);
+ }
+ }
+ }
+ break;
+ case "return":
+ return $this->reduce($child[1], true);
+ case "each":
+ list(,$each) = $child;
+ $list = $this->reduce($this->coerceList($each->list));
+ foreach ($list[2] as $item) {
+ $this->pushEnv();
+ $this->set($each->var, $item);
+ // TODO: allow return from here
+ $this->compileChildren($each->children, $out);
+ $this->popEnv();
+ }
+ break;
+ case "while":
+ list(,$while) = $child;
+ while ($this->reduce($while->cond, true) != self::$false) {
+ $ret = $this->compileChildren($while->children, $out);
+ if ($ret) return $ret;
+ }
+ break;
+ case "for":
+ list(,$for) = $child;
+ $start = $this->reduce($for->start, true);
+ $start = $start[1];
+ $end = $this->reduce($for->end, true);
+ $end = $end[1];
+ $d = $start < $end ? 1 : -1;
+
+ while (true) {
+ if ((!$for->until && $start - $d == $end) ||
+ ($for->until && $start == $end))
+ {
+ break;
+ }
+
+ $this->set($for->var, array("number", $start, ""));
+ $start += $d;
+
+ $ret = $this->compileChildren($for->children, $out);
+ if ($ret) return $ret;
+ }
+
+ break;
+ case "nestedprop":
+ list(,$prop) = $child;
+ $prefixed = array();
+ $prefix = $this->compileValue($prop->prefix) . "-";
+ foreach ($prop->children as $child) {
+ if ($child[0] == "assign") {
+ array_unshift($child[1][2], $prefix);
+ }
+ if ($child[0] == "nestedprop") {
+ array_unshift($child[1]->prefix[2], $prefix);
+ }
+ $prefixed[] = $child;
+ }
+ $this->compileChildren($prefixed, $out);
+ break;
+ case "include": // including a mixin
+ list(,$name, $argValues, $content) = $child;
+ $mixin = $this->get(self::$namespaces["mixin"] . $name, false);
+ if (!$mixin) break; // throw error?
+
+ $callingScope = $this->env;
+
+ // push scope, apply args
+ $this->pushEnv();
+
+ if (!is_null($content)) {
+ $content->scope = $callingScope;
+ $this->setRaw(self::$namespaces["special"] . "content", $content);
+ }
+
+ if (!is_null($mixin->args)) {
+ $this->applyArguments($mixin->args, $argValues);
+ }
+
+ foreach ($mixin->children as $child) {
+ $this->compileChild($child, $out);
+ }
+
+ $this->popEnv();
+
+ break;
+ case "mixin_content":
+ $content = $this->get(self::$namespaces["special"] . "content");
+ if (is_null($content)) {
+ throw new \Exception("Unexpected @content inside of mixin");
+ }
+
+ $this->storeEnv = $content->scope;
+
+ foreach ($content->children as $child) {
+ $this->compileChild($child, $out);
+ }
+
+ unset($this->storeEnv);
+ break;
+ case "debug":
+ list(,$value, $pos) = $child;
+ $line = $this->parser->getLineNo($pos);
+ $value = $this->compileValue($this->reduce($value, true));
+ fwrite(STDERR, "Line $line DEBUG: $value\n");
+ break;
+ default:
+ throw new exception("unknown child type: $child[0]");
+ }
+ }
+
+ protected function expToString($exp) {
+ list(, $op, $left, $right, $inParens, $whiteLeft, $whiteRight) = $exp;
+ $content = array($left);
+ if ($whiteLeft) $content[] = " ";
+ $content[] = $op;
+ if ($whiteRight) $content[] = " ";
+ $content[] = $right;
+ return array("string", "", $content);
+ }
+
+ // should $value cause its operand to eval
+ protected function shouldEval($value) {
+ switch ($value[0]) {
+ case "exp":
+ if ($value[1] == "/") {
+ return $this->shouldEval($value[2], $value[3]);
+ }
+ case "var":
+ case "fncall":
+ return true;
+ }
+ return false;
+ }
+
+ protected function reduce($value, $inExp = false) {
+ list($type) = $value;
+ switch ($type) {
+ case "exp":
+ list(, $op, $left, $right, $inParens) = $value;
+ $opName = isset(self::$operatorNames[$op]) ? self::$operatorNames[$op] : $op;
+
+ $inExp = $inExp || $this->shouldEval($left) || $this->shouldEval($right);
+
+ $left = $this->reduce($left, true);
+ $right = $this->reduce($right, true);
+
+ // only do division in special cases
+ if ($opName == "div" && !$inParens && !$inExp) {
+ if ($left[0] != "color" && $right[0] != "color") {
+ return $this->expToString($value);
+ }
+ }
+
+ $left = $this->coerceForExpression($left);
+ $right = $this->coerceForExpression($right);
+
+ $ltype = $left[0];
+ $rtype = $right[0];
+
+ // this tries:
+ // 1. op_[op name]_[left type]_[right type]
+ // 2. op_[left type]_[right type] (passing the op as first arg
+ // 3. op_[op name]
+ $fn = "op_${opName}_${ltype}_${rtype}";
+ if (is_callable(array($this, $fn)) ||
+ (($fn = "op_${ltype}_${rtype}") &&
+ is_callable(array($this, $fn)) &&
+ $passOp = true) ||
+ (($fn = "op_${opName}") &&
+ is_callable(array($this, $fn)) &&
+ $genOp = true))
+ {
+ $unitChange = false;
+ if (!isset($genOp) &&
+ $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]));
+ }
+
+ $unitChange = true;
+ $emptyUnit = $left[2] == "" || $right[2] == "";
+ $targetUnit = "" != $left[2] ? $left[2] : $right[2];
+
+ if ($opName != "mul") {
+ $left[2] = "" != $left[2] ? $left[2] : $targetUnit;
+ $right[2] = "" != $right[2] ? $right[2] : $targetUnit;
+ }
+
+ if ($opName != "mod") {
+ $left = $this->normalizeNumber($left);
+ $right = $this->normalizeNumber($right);
+ }
+
+ if ($opName == "div" && !$emptyUnit && $left[2] == $right[2]) {
+ $targetUnit = "";
+ }
+
+ if ($opName == "mul") {
+ $left[2] = "" != $left[2] ? $left[2] : $right[2];
+ $right[2] = "" != $right[2] ? $right[2] : $left[2];
+ } elseif ($opName == "div" && $left[2] == $right[2]) {
+ $left[2] = "";
+ $right[2] = "";
+ }
+ }
+
+ $shouldEval = $inParens || $inExp;
+ if (isset($passOp)) {
+ $out = $this->$fn($op, $left, $right, $shouldEval);
+ } else {
+ $out = $this->$fn($left, $right, $shouldEval);
+ }
+
+ if (!is_null($out)) {
+ if ($unitChange && $out[0] == "number") {
+ $out = $this->coerceUnit($out, $targetUnit);
+ }
+ return $out;
+ }
+ }
+
+ return $this->expToString($value);
+ case "unary":
+ list(, $op, $exp, $inParens) = $value;
+ $inExp = $inExp || $this->shouldEval($exp);
+
+ $exp = $this->reduce($exp);
+ if ($exp[0] == "number") {
+ switch ($op) {
+ case "+":
+ return $exp;
+ case "-":
+ $exp[1] *= -1;
+ return $exp;
+ }
+ }
+
+ if ($op == "not") {
+ if ($inExp || $inParens) {
+ if ($exp == self::$false) {
+ return self::$true;
+ } else {
+ return self::$false;
+ }
+ } else {
+ $op = $op . " ";
+ }
+ }
+
+ return array("string", "", array($op, $exp));
+ case "var":
+ list(, $name) = $value;
+ return $this->reduce($this->get($name));
+ case "list":
+ foreach ($value[2] as &$item) {
+ $item = $this->reduce($item);
+ }
+ return $value;
+ case "string":
+ foreach ($value[2] as &$item) {
+ if (is_array($item)) {
+ $item = $this->reduce($item);
+ }
+ }
+ return $value;
+ case "interpolate":
+ $value[1] = $this->reduce($value[1]);
+ return $value;
+ case "fncall":
+ list(,$name, $argValues) = $value;
+
+ // user defined function?
+ $func = $this->get(self::$namespaces["function"] . $name, false);
+ if ($func) {
+ $this->pushEnv();
+
+ // set the args
+ if (isset($func->args)) {
+ $this->applyArguments($func->args, $argValues);
+ }
+
+ // throw away lines and children
+ $tmp = (object)array(
+ "lines" => array(),
+ "children" => array()
+ );
+ $ret = $this->compileChildren($func->children, $tmp);
+ $this->popEnv();
+
+ return is_null($ret) ? self::$defaultValue : $ret;
+ }
+
+ // built in function
+ if ($this->callBuiltin($name, $argValues, $returnValue)) {
+ return $returnValue;
+ }
+
+ // need to flatten the arguments into a list
+ $listArgs = array();
+ foreach ((array)$argValues as $arg) {
+ if (empty($arg[0])) {
+ $listArgs[] = $this->reduce($arg[1]);
+ }
+ }
+ return array("function", $name, array("list", ",", $listArgs));
+ default:
+ return $value;
+ }
+ }
+
+ // just does physical lengths for now
+ protected function normalizeNumber($number) {
+ list(, $value, $unit) = $number;
+ if (isset(self::$unitTable["in"][$unit])) {
+ $conv = self::$unitTable["in"][$unit];
+ return array("number", $value / $conv, "in");
+ }
+ return $number;
+ }
+
+ // $number should be normalized
+ protected function coerceUnit($number, $unit) {
+ list(, $value, $baseUnit) = $number;
+ if (isset(self::$unitTable[$baseUnit][$unit])) {
+ $value = $value * self::$unitTable[$baseUnit][$unit];
+ }
+
+ return array("number", $value, $unit);
+ }
+
+ protected function op_add_number_number($left, $right) {
+ return array("number", $left[1] + $right[1], $left[2]);
+ }
+
+ protected function op_mul_number_number($left, $right) {
+ return array("number", $left[1] * $right[1], $left[2]);
+ }
+
+ protected function op_sub_number_number($left, $right) {
+ return array("number", $left[1] - $right[1], $left[2]);
+ }
+
+ protected function op_div_number_number($left, $right) {
+ return array("number", $left[1] / $right[1], $left[2]);
+ }
+
+ protected function op_mod_number_number($left, $right) {
+ return array("number", $left[1] % $right[1], $left[2]);
+ }
+
+ // adding strings
+ protected function op_add($left, $right) {
+ if ($strLeft = $this->coerceString($left)) {
+ if ($right[0] == "string") {
+ $right[1] = "";
+ }
+ $strLeft[2][] = $right;
+ return $strLeft;
+ }
+
+ if ($strRight = $this->coerceString($right)) {
+ if ($left[0] == "string") {
+ $left[1] = "";
+ }
+ array_unshift($strRight[2], $left);
+ return $strRight;
+ }
+ }
+
+ protected function op_and($left, $right, $shouldEval) {
+ if (!$shouldEval) return;
+ if ($left != self::$false) return $right;
+ return $left;
+ }
+
+ protected function op_or($left, $right, $shouldEval) {
+ if (!$shouldEval) return;
+ if ($left != self::$false) return $left;
+ return $right;
+ }
+
+ protected function op_color_color($op, $left, $right) {
+ $out = array('color');
+ foreach (range(1, 3) as $i) {
+ $lval = isset($left[$i]) ? $left[$i] : 0;
+ $rval = isset($right[$i]) ? $right[$i] : 0;
+ switch ($op) {
+ case '+':
+ $out[] = $lval + $rval;
+ break;
+ case '-':
+ $out[] = $lval - $rval;
+ break;
+ case '*':
+ $out[] = $lval * $rval;
+ break;
+ case '%':
+ $out[] = $lval % $rval;
+ break;
+ case '/':
+ if ($rval == 0) {
+ throw new exception("color: Can't divide by zero");
+ }
+ $out[] = $lval / $rval;
+ break;
+ default:
+ throw new exception("color: unknow op $op");
+ }
+ }
+
+ if (isset($left[4])) $out[4] = $left[4];
+ elseif (isset($right[4])) $out[4] = $right[4];
+
+ return $this->fixColor($out);
+ }
+
+ protected function op_color_number($op, $left, $right) {
+ $value = $right[1];
+ return $this->op_color_color($op, $left,
+ array("color", $value, $value, $value));
+ }
+
+ protected function op_number_color($op, $left, $right) {
+ $value = $left[1];
+ return $this->op_color_color($op,
+ array("color", $value, $value, $value), $right);
+ }
+
+ protected function op_eq($left, $right) {
+ if (($lStr = $this->coerceString($left)) && ($rStr = $this->coerceString($right))) {
+ $lStr[1] = "";
+ $rStr[1] = "";
+ return $this->toBool($this->compileValue($lStr) == $this->compileValue($rStr));
+ }
+
+ return $this->toBool($left == $right);
+ }
+
+ protected function op_neq($left, $right) {
+ return $this->toBool($left != $right);
+ }
+
+ protected function op_gte_number_number($left, $right) {
+ return $this->toBool($left[1] >= $right[1]);
+ }
+
+ protected function op_gt_number_number($left, $right) {
+ return $this->toBool($left[1] > $right[1]);
+ }
+
+ protected function op_lte_number_number($left, $right) {
+ return $this->toBool($left[1] <= $right[1]);
+ }
+
+ protected function op_lt_number_number($left, $right) {
+ return $this->toBool($left[1] < $right[1]);
+ }
+
+ protected function toBool($thing) {
+ return $thing ? self::$true : self::$false;
+ }
+
+ protected function compileValue($value) {
+ $value = $this->reduce($value);
+
+ list($type) = $value;
+ switch ($type) {
+ case "keyword":
+ return $value[1];
+ case "color":
+ // [1] - red component (either number for a %)
+ // [2] - green component
+ // [3] - blue component
+ // [4] - optional alpha component
+ list(, $r, $g, $b) = $value;
+
+ $r = round($r);
+ $g = round($g);
+ $b = round($b);
+
+ if (count($value) == 5 && $value[4] != 1) { // rgba
+ return 'rgba('.$r.', '.$g.', '.$b.', '.$value[4].')';
+ }
+
+ $h = sprintf("#%02x%02x%02x", $r, $g, $b);
+
+ // Converting hex color to short notation (e.g. #003399 to #039)
+ if ($h[1] === $h[2] && $h[3] === $h[4] && $h[5] === $h[6]) {
+ $h = '#' . $h[1] . $h[3] . $h[5];
+ }
+
+ return $h;
+ case "number":
+ return round($value[1], self::$numberPrecision) . $value[2];
+ case "string":
+ return $value[1] . $this->compileStringContent($value) . $value[1];
+ case "function":
+ $args = !empty($value[2]) ? $this->compileValue($value[2]) : "";
+ return "$value[1]($args)";
+ case "list":
+ $value = $this->extractInterpolation($value);
+ if ($value[0] != "list") return $this->compileValue($value);
+
+ list(, $delim, $items) = $value;
+ foreach ($items as &$item) {
+ $item = $this->compileValue($item);
+ }
+ return implode("$delim ", $items);
+ case "interpolated": # node created by extractInterpolation
+ list(, $interpolate, $left, $right) = $value;
+ list(,, $whiteLeft, $whiteRight) = $interpolate;
+
+ $left = count($left[2]) > 0 ?
+ $this->compileValue($left).$whiteLeft : "";
+
+ $right = count($right[2]) > 0 ?
+ $whiteRight.$this->compileValue($right) : "";
+
+ return $left.$this->compileValue($interpolate).$right;
+
+ case "interpolate": # raw parse node
+ list(, $exp) = $value;
+
+ // strip quotes if it's a string
+ $reduced = $this->reduce($exp);
+ if ($reduced[0] == "string") {
+ $reduced = array("keyword",
+ $this->compileStringContent($reduced));
+ }
+
+ return $this->compileValue($reduced);
+ default:
+ throw new exception("unknown value type: $type");
+ }
+ }
+
+ protected function compileStringContent($string) {
+ $parts = array();
+ foreach ($string[2] as $part) {
+ if (is_array($part)) {
+ $parts[] = $this->compileValue($part);
+ } else {
+ $parts[] = $part;
+ }
+ }
+
+ return implode($parts);
+ }
+
+ // doesn't need to be recursive, compileValue will handle that
+ protected function extractInterpolation($list) {
+ $items = $list[2];
+ foreach ($items as $i => $item) {
+ if ($item[0] == "interpolate") {
+ $before = array("list", $list[1], array_slice($items, 0, $i));
+ $after = array("list", $list[1], array_slice($items, $i + 1));
+ return array("interpolated", $item, $before, $after);
+ }
+ }
+ return $list;
+ }
+
+ // 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);
+ }
+
+ if (is_null($childSelectors)) {
+ $selectors = $env->selectors;
+ } else {
+ $selectors = array();
+ foreach ($env->selectors as $parent) {
+ foreach ($childSelectors as $child) {
+ $selectors[] = $this->joinSelectors($parent, $child);
+ }
+ }
+ }
+
+ return $this->multiplySelectors($env->parent, $selectors);
+ }
+
+ // looks for & to replace, or append parent before child
+ protected function joinSelectors($parent, $child) {
+ $setSelf = false;
+ $out = array();
+ foreach ($child as $part) {
+ $newPart = array();
+ foreach ($part as $p) {
+ if ($p == self::$selfSelector) {
+ $setSelf = true;
+ foreach ($parent as $i => $parentPart) {
+ if ($i > 0) {
+ $out[] = $newPart;
+ $newPart = array();
+ }
+
+ foreach ($parentPart as $pp) {
+ $newPart[] = $pp;
+ }
+ }
+ } else {
+ $newPart[] = $p;
+ }
+ }
+
+ $out[] = $newPart;
+ }
+
+ return $setSelf ? $out : array_merge($parent, $child);
+ }
+
+ protected function multiplyMedia($env, $childQueries = null) {
+ if (is_null($env) ||
+ !empty($env->block->type) && $env->block->type != "media")
+ {
+ return $childQueries;
+ }
+
+ // plain old block, skip
+ if (empty($env->block->type)) {
+ return $this->multiplyMedia($env->parent, $childQueries);
+ }
+
+ $parentQueries = $env->block->queryList;
+ if ($childQueries == null) {
+ $childQueries = $parentQueries;
+ } else {
+ $originalQueries = $childQueries;
+ $childQueries = array();
+
+ foreach ($parentQueries as $parentQuery){
+ foreach ($originalQueries as $childQuery) {
+ $childQueries []= array_merge($parentQuery, $childQuery);
+ }
+ }
+ }
+
+ return $this->multiplyMedia($env->parent, $childQueries);
+ }
+
+ // convert something to list
+ protected function coerceList($item, $delim = ",") {
+ if (!is_null($item) && $item[0] == "list") {
+ return $item;
+ }
+
+ return array("list", $delim, is_null($item) ? array(): array($item));
+ }
+
+ protected function applyArguments($argDef, $argValues) {
+ $argValues = (array)$argValues;
+
+ $keywordArgs = array();
+ $remaining = array();
+
+ // assign the keyword args
+ foreach ($argValues as $arg) {
+ if (!empty($arg[0])) {
+ $keywordArgs[$arg[0][1]] = $arg[1];
+ } else {
+ $remaining[] = $arg[1];
+ }
+ }
+
+ foreach ($argDef as $i => $arg) {
+ list($name, $default) = $arg;
+
+ if (isset($remaining[$i])) {
+ $val = $remaining[$i];
+ } elseif (isset($keywordArgs[$name])) {
+ $val = $keywordArgs[$name];
+ } elseif (!empty($default)) {
+ $val = $default;
+ } else {
+ $val = self::$defaultValue;
+ }
+
+ $this->set($name, $this->reduce($val, true), true);
+ }
+ }
+
+ protected function pushEnv($block=null) {
+ $env = new stdclass;
+ $env->parent = $this->env;
+ $env->store = array();
+ $env->block = $block;
+ $env->depth = isset($this->env->depth) ? $this->env->depth + 1 : 0;
+
+ $this->env = $env;
+ return $env;
+ }
+
+ protected function normalizeName($name) {
+ return str_replace("-", "_", $name);
+ }
+
+ protected function getStoreEnv() {
+ return isset($this->storeEnv) ? $this->storeEnv : $this->env;
+ }
+
+ protected function set($name, $value, $shadow=false) {
+ $name = $this->normalizeName($name);
+ if ($shadow) {
+ $this->setRaw($name, $value);
+ } else {
+ $this->setExisting($name, $value);
+ }
+ }
+
+ // todo: this is bugged?
+ protected function setExisting($name, $value, $env = null) {
+ if (is_null($env)) $env = $this->getStoreEnv();
+
+ if (isset($env->store[$name])) {
+ $env->store[$name] = $value;
+ } elseif (!is_null($env->parent)) {
+ $this->setExisting($name, $value, $env->parent);
+ } else {
+ $this->env->store[$name] = $value;
+ }
+ }
+
+ protected function setRaw($name, $value) {
+ $this->env->store[$name] = $value;
+ }
+
+ protected function get($name, $defaultValue = null, $env = null) {
+ $name = $this->normalizeName($name);
+
+ if (is_null($env)) $env = $this->getStoreEnv();
+ if (is_null($defaultValue)) $defaultValue = self::$defaultValue;
+
+ if (isset($env->store[$name])) {
+ return $env->store[$name];
+ } elseif (!is_null($env->parent)) {
+ return $this->get($name, $defaultValue, $env->parent);
+ }
+
+ return $defaultValue; // found nothing
+ }
+
+ protected function popEnv() {
+ $env = $this->env;
+ $this->env = $this->env->parent;
+ return $env;
+ }
+
+ public function getParsedFiles() {
+ return $this->parsedFiles;
+ }
+
+ public function addImportPath($path) {
+ $this->importPaths[] = $path;
+ }
+
+ public function setImportPaths($path) {
+ $this->importPaths = (array)$path;
+ }
+
+ public function setFormatter($formatterName) {
+ $this->formatter = $formatterName;
+ }
+
+ public function registerFunction($name, $func) {
+ $this->userFunctions[$this->normalizeName($name)] = $func;
+ }
+
+ public function unregisterFunction($name) {
+ unset($this->userFunctions[$this->normalizeName($name)]);
+ }
+
+ protected function importFile($path, $out) {
+ // see if tree is cached
+ $realPath = realpath($path);
+ if (isset($this->importCache[$realPath])) {
+ $tree = $this->importCache[$realPath];
+ } else {
+ $code = file_get_contents($path);
+ $parser = new scss_parser($path);
+ $tree = $parser->parse($code);
+ $this->parsedFiles[] = $path;
+
+ $this->importCache[$realPath] = $tree;
+ }
+
+ $pi = pathinfo($path);
+ array_unshift($this->importPaths, $pi['dirname']);
+ $this->compileChildren($tree->children, $out);
+ array_shift($this->importPaths);
+ }
+
+ // results the file path for an import url if it exists
+ protected function findImport($url) {
+ $urls = array();
+
+ // for "normal" scss imports (ignore vanilla css and external requests)
+ if (!preg_match('/\.css|^http:\/\/$/', $url)) {
+ // try both normal and the _partial filename
+ $urls = array($url, preg_replace('/[^\/]+$/', '_\0', $url));
+ }
+
+ foreach ($this->importPaths as $dir) {
+ if (is_string($dir)) {
+ // check urls for normal import paths
+ foreach ($urls as $full) {
+ $full = $dir .
+ (!empty($dir) && substr($dir, -1) != '/' ? '/' : '') .
+ $full;
+
+ if ($this->fileExists($file = $full.'.scss') ||
+ $this->fileExists($file = $full))
+ {
+ return $file;
+ }
+ }
+ } else {
+ // check custom callback for import path
+ $file = call_user_func($dir,$url,$this);
+ if ($file !== null) {
+ return $file;
+ }
+ }
+ }
+
+ return null;
+ }
+
+ protected function fileExists($name) {
+ return is_file($name);
+ }
+
+ protected function callBuiltin($name, $args, &$returnValue) {
+ // try a lib function
+ $name = $this->normalizeName($name);
+ $libName = "lib_".$name;
+ $f = array($this, $libName);
+ $prototype = isset(self::$$libName) ? self::$$libName : null;
+
+ if (is_callable($f)) {
+ $sorted = $this->sortArgs($prototype, $args);
+ foreach ($sorted as &$val) {
+ $val = $this->reduce($val, true);
+ }
+ $returnValue = call_user_func($f, $sorted, $this);
+ } else if (isset($this->userFunctions[$name])) {
+ // see if we can find a user function
+ $fn = $this->userFunctions[$name];
+
+ foreach ($args as &$val) {
+ $val = $this->reduce($val[1], true);
+ }
+
+ $returnValue = call_user_func($fn, $args, $this);
+ }
+
+ if (isset($returnValue)) {
+ // coerce a php value into a scss one
+ if (is_numeric($returnValue)) {
+ $returnValue = array('number', $returnValue, "");
+ } elseif (is_bool($returnValue)) {
+ $returnValue = $returnValue ? self::$true : self::$false;
+ } elseif (!is_array($returnValue)) {
+ $returnValue = array('keyword', $returnValue);
+ }
+
+ return true;
+ }
+
+ return false;
+ }
+
+ // sorts any keyword arguments
+ // TODO: merge with apply arguments
+ protected function sortArgs($prototype, $args) {
+ $keyArgs = array();
+ $posArgs = array();
+
+ foreach ($args as $arg) {
+ list($key, $value) = $arg;
+ $key = $key[1];
+ if (empty($key)) {
+ $posArgs[] = $value;
+ } else {
+ $keyArgs[$key] = $value;
+ }
+ }
+
+ if (is_null($prototype)) return $posArgs;
+
+ $finalArgs = array();
+ foreach ($prototype as $i => $names) {
+ if (isset($posArgs[$i])) {
+ $finalArgs[] = $posArgs[$i];
+ continue;
+ }
+
+ $set = false;
+ foreach ((array)$names as $name) {
+ if (isset($keyArgs[$name])) {
+ $finalArgs[] = $keyArgs[$name];
+ $set = true;
+ break;
+ }
+ }
+
+ if (!$set) {
+ $finalArgs[] = null;
+ }
+ }
+
+ return $finalArgs;
+ }
+
+ protected function coerceForExpression($value) {
+ if ($color = $this->coerceColor($value)) {
+ return $color;
+ }
+
+ return $value;
+ }
+
+ protected function coerceColor($value) {
+ switch ($value[0]) {
+ case "color": return $value;
+ case "keyword":
+ $name = $value[1];
+ if (isset(self::$cssColors[$name])) {
+ list($r, $g, $b) = explode(',', self::$cssColors[$name]);
+ return array('color', $r, $g, $b);
+ }
+ return null;
+ }
+
+ return null;
+ }
+
+ protected function coerceString($value) {
+ switch ($value[0]) {
+ case "string":
+ return $value;
+ case "keyword":
+ return array("string", "", array($value[1]));
+ }
+ return null;
+ }
+
+ protected function assertColor($value) {
+ if ($color = $this->coerceColor($value)) return $color;
+ throw new exception("expecting color");
+ }
+
+ protected function assertNumber($value) {
+ if ($value[0] != "number")
+ throw new exception("expecting number");
+ return $value[1];
+ }
+
+ protected function coercePercent($value) {
+ if ($value[0] == "number") {
+ if ($value[2] == "%") {
+ return $value[1] / 100;
+ }
+ return $value[1];
+ }
+ return 0;
+ }
+
+ // make sure a color's components don't go out of bounds
+ protected function fixColor($c) {
+ foreach (range(1, 3) as $i) {
+ if ($c[$i] < 0) $c[$i] = 0;
+ if ($c[$i] > 255) $c[$i] = 255;
+ }
+
+ return $c;
+ }
+
+ function toHSL($r, $g, $b) {
+ $r = $r / 255;
+ $g = $g / 255;
+ $b = $b / 255;
+
+ $min = min($r, $g, $b);
+ $max = max($r, $g, $b);
+
+ $L = ($min + $max) / 2;
+ if ($min == $max) {
+ $S = $H = 0;
+ } else {
+ if ($L < 0.5)
+ $S = ($max - $min)/($max + $min);
+ 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);
+
+ }
+
+ return array('hsl',
+ ($H < 0 ? $H + 6 : $H)*60,
+ $S*100,
+ $L*100,
+ );
+ }
+
+ function toRGB_helper($comp, $temp1, $temp2) {
+ if ($comp < 0) $comp += 1.0;
+ elseif ($comp > 1) $comp -= 1.0;
+
+ 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;
+
+ return $temp1;
+ }
+
+ // 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;
+
+ $S = min(100, max(0, $S));
+ $L = min(100, max(0, $L));
+
+ $H = $H / 360;
+ $S = $S / 100;
+ $L = $L / 100;
+
+ if ($S == 0) {
+ $r = $g = $b = $L;
+ } else {
+ $temp2 = $L < 0.5 ?
+ $L*(1.0 + $S) :
+ $L + $S - $L * $S;
+
+ $temp1 = 2.0 * $L - $temp2;
+
+ $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);
+ }
+
+ $out = array('color', $r*255, $g*255, $b*255);
+ return $out;
+ }
+
+ // Built in functions
+
+ protected static $lib_if = array("condition", "if-true", "if-false");
+ protected function lib_if($args) {
+ list($cond,$t, $f) = $args;
+ if ($cond == self::$false) return $f;
+ return $t;
+ }
+
+ protected static $lib_rgb = array("red", "green", "blue");
+ protected function lib_rgb($args) {
+ list($r,$g,$b) = $args;
+ return array("color", $r[1], $g[1], $b[1]);
+ }
+
+ protected static $lib_rgba = array(
+ array("red", "color"),
+ "green", "blue", "alpha");
+ protected function lib_rgba($args) {
+ if ($color = $this->coerceColor($args[0])) {
+ $num = is_null($args[1]) ? $args[3] : $args[1];
+ $alpha = $this->assertNumber($num);
+ $color[4] = $alpha;
+ return $color;
+ }
+
+ list($r,$g,$b, $a) = $args;
+ return array("color", $r[1], $g[1], $b[1], $a[1]);
+ }
+
+ // helper function for adjust_color, change_color, and scale_color
+ protected function alter_color($args, $fn) {
+ $color = $this->assertColor($args[0]);
+
+ foreach (array(1,2,3,7) as $i) {
+ if (!is_null($args[$i])) {
+ $val = $this->assertNumber($args[$i]);
+ $ii = $i == 7 ? 4 : $i; // alpha
+ $color[$ii] =
+ $this->$fn(isset($color[$ii]) ? $color[$ii] : 0, $val, $i);
+ }
+ }
+
+ if (!is_null($args[4]) || !is_null($args[5]) || !is_null($args[6])) {
+ $hsl = $this->toHSL($color[1], $color[2], $color[3]);
+ foreach (array(4,5,6) as $i) {
+ if (!is_null($args[$i])) {
+ $val = $this->assertNumber($args[$i]);
+ $hsl[$i - 3] = $this->$fn($hsl[$i - 3], $val, $i);
+ }
+ }
+
+ $rgb = $this->toRGB($hsl[1], $hsl[2], $hsl[3]);
+ if (isset($color[4])) $rgb[4] = $color[4];
+ $color = $rgb;
+ }
+
+ return $color;
+ }
+
+ protected static $lib_adjust_color = array(
+ "color", "red", "green", "blue",
+ "hue", "saturation", "lightness", "alpha"
+ );
+ protected function adjust_color_helper($base, $alter, $i) {
+ return $base += $alter;
+ }
+ protected function lib_adjust_color($args) {
+ return $this->alter_color($args, "adjust_color_helper");
+ }
+
+ protected static $lib_change_color = array(
+ "color", "red", "green", "blue",
+ "hue", "saturation", "lightness", "alpha"
+ );
+ protected function change_color_helper($base, $alter, $i) {
+ return $alter;
+ }
+ protected function lib_change_color($args) {
+ return $this->alter_color($args, "change_color_helper");
+ }
+
+ protected static $lib_scale_color = array(
+ "color", "red", "green", "blue",
+ "hue", "saturation", "lightness", "alpha"
+ );
+ protected function scale_color_helper($base, $scale, $i) {
+ // 1,2,3 - rgb
+ // 4, 5, 6 - hsl
+ // 7 - a
+ switch ($i) {
+ case 1:
+ case 2:
+ case 3:
+ $max = 255; break;
+ case 4:
+ $max = 360; break;
+ case 7:
+ $max = 1; break;
+ default:
+ $max = 100;
+ }
+
+ $scale = $scale / 100;
+ if ($scale < 0) {
+ return $base * $scale + $base;
+ } else {
+ return ($max - $base) * $scale + $base;
+ }
+ }
+ protected function lib_scale_color($args) {
+ return $this->alter_color($args, "scale_color_helper");
+ }
+
+ protected static $lib_ie_hex_str = array("color");
+ protected function lib_ie_hex_str($args) {
+ $color = $this->coerceColor($args[0]);
+ $color[4] = isset($color[4]) ? round(255*$color[4]) : 255;
+
+ return sprintf('#%02X%02X%02X%02X', $color[4], $color[1], $color[2], $color[3]);
+ }
+
+ protected static $lib_red = array("color");
+ protected function lib_red($args) {
+ list($color) = $args;
+ return $color[1];
+ }
+
+ protected static $lib_green = array("color");
+ protected function lib_green($args) {
+ list($color) = $args;
+ return $color[2];
+ }
+
+ protected static $lib_blue = array("color");
+ protected function lib_blue($args) {
+ list($color) = $args;
+ return $color[3];
+ }
+
+ protected static $lib_alpha = array("color");
+ protected function lib_alpha($args) {
+ if ($color = $this->coerceColor($args[0])) {
+ return isset($color[4]) ? $color[4] : 1;
+ }
+
+ // this might be the IE function, so return value unchanged
+ return array("function", "alpha", array("list", ",", $args));
+ }
+
+ protected static $lib_opacity = array("color");
+ protected function lib_opacity($args) {
+ return $this->lib_alpha($args);
+ }
+
+ // mix two colors
+ protected static $lib_mix = array("color-1", "color-2", "weight");
+ protected function lib_mix($args) {
+ list($first, $second, $weight) = $args;
+ $first = $this->assertColor($first);
+ $second = $this->assertColor($second);
+
+ if (is_null($weight)) {
+ $weight = 0.5;
+ } else {
+ $weight = $this->coercePercent($weight);
+ }
+
+ $first_a = isset($first[4]) ? $first[4] : 1;
+ $second_a = isset($second[4]) ? $second[4] : 1;
+
+ $w = $weight * 2 - 1;
+ $a = $first_a - $second_a;
+
+ $w1 = (($w * $a == -1 ? $w : ($w + $a)/(1 + $w * $a)) + 1) / 2.0;
+ $w2 = 1.0 - $w1;
+
+ $new = array('color',
+ $w1 * $first[1] + $w2 * $second[1],
+ $w1 * $first[2] + $w2 * $second[2],
+ $w1 * $first[3] + $w2 * $second[3],
+ );
+
+ if ($first_a != 1.0 || $second_a != 1.0) {
+ $new[] = $first_a * $weight + $second_a * ($weight - 1);
+ }
+
+ return $this->fixColor($new);
+ }
+
+ protected static $lib_hsl = array("hue", "saturation", "lightness");
+ protected function lib_hsl($args) {
+ list($h, $s, $l) = $args;
+ return $this->toRGB($h[1], $s[1], $l[1]);
+ }
+
+ protected static $lib_hsla = array("hue", "saturation",
+ "lightness", "alpha");
+ protected function lib_hsla($args) {
+ list($h, $s, $l, $a) = $args;
+ $color = $this->toRGB($h[1], $s[1], $l[1]);
+ $color[4] = $a[1];
+ return $color;
+ }
+
+ protected static $lib_hue = array("color");
+ protected function lib_hue($args) {
+ $color = $this->assertColor($args[0]);
+ $hsl = $this->toHSL($color[1], $color[2], $color[3]);
+ return array("number", $hsl[1], "deg");
+ }
+
+ protected static $lib_saturation = array("color");
+ protected function lib_saturation($args) {
+ $color = $this->assertColor($args[0]);
+ $hsl = $this->toHSL($color[1], $color[2], $color[3]);
+ return array("number", $hsl[2], "%");
+ }
+
+ protected static $lib_lightness = array("color");
+ protected function lib_lightness($args) {
+ $color = $this->assertColor($args[0]);
+ $hsl = $this->toHSL($color[1], $color[2], $color[3]);
+ return array("number", $hsl[3], "%");
+ }
+
+
+ protected function adjustHsl($color, $idx, $amount) {
+ $hsl = $this->toHSL($color[1], $color[2], $color[3]);
+ $hsl[$idx] += $amount;
+ $out = $this->toRGB($hsl[1], $hsl[2], $hsl[3]);
+ if (isset($color[4])) $out[4] = $color[4];
+ return $out;
+ }
+
+ protected static $lib_adjust_hue = array("color", "degrees");
+ protected function lib_adjust_hue($args) {
+ $color = $this->assertColor($args[0]);
+ $degrees = $this->assertNumber($args[1]);
+ return $this->adjustHsl($color, 1, $degrees);
+ }
+
+ protected static $lib_lighten = array("color", "amount");
+ protected function lib_lighten($args) {
+ $color = $this->assertColor($args[0]);
+ $amount = 100*$this->coercePercent($args[1]);
+ return $this->adjustHsl($color, 3, $amount);
+ }
+
+ protected static $lib_darken = array("color", "amount");
+ protected function lib_darken($args) {
+ $color = $this->assertColor($args[0]);
+ $amount = 100*$this->coercePercent($args[1]);
+ return $this->adjustHsl($color, 3, -$amount);
+ }
+
+ protected static $lib_saturate = array("color", "amount");
+ protected function lib_saturate($args) {
+ $color = $this->assertColor($args[0]);
+ $amount = 100*$this->coercePercent($args[1]);
+ return $this->adjustHsl($color, 2, $amount);
+ }
+
+ protected static $lib_desaturate = array("color", "amount");
+ protected function lib_desaturate($args) {
+ $color = $this->assertColor($args[0]);
+ $amount = 100*$this->coercePercent($args[1]);
+ return $this->adjustHsl($color, 2, -$amount);
+ }
+
+ protected static $lib_grayscale = array("color");
+ protected function lib_grayscale($args) {
+ return $this->adjustHsl($this->assertColor($args[0]), 2, -100);
+ }
+
+ protected static $lib_complement = array("color");
+ protected function lib_complement($args) {
+ return $this->adjustHsl($this->assertColor($args[0]), 1, 180);
+ }
+
+ protected static $lib_invert = array("color");
+ protected function lib_invert($args) {
+ $color = $this->assertColor($args[0]);
+ $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) {
+ $color = $this->assertColor($args[0]);
+ $amount = $this->coercePercent($args[1]);
+
+ $color[4] = (isset($color[4]) ? $color[4] : 1) + $amount;
+ $color[4] = min(1, max(0, $color[4]));
+ return $color;
+ }
+
+ protected static $lib_fade_in = array("color", "amount");
+ protected function lib_fade_in($args) {
+ return $this->lib_opacify($args);
+ }
+
+ // decreases opacity by amount
+ protected static $lib_transparentize = array("color", "amount");
+ protected function lib_transparentize($args) {
+ $color = $this->assertColor($args[0]);
+ $amount = $this->coercePercent($args[1]);
+
+ $color[4] = (isset($color[4]) ? $color[4] : 1) - $amount;
+ $color[4] = min(1, max(0, $color[4]));
+ return $color;
+ }
+
+ protected static $lib_fade_out = array("color", "amount");
+ protected function lib_fade_out($args) {
+ return $this->lib_transparentize($args);
+ }
+
+ protected static $lib_unquote = array("string");
+ protected function lib_unquote($args) {
+ $str = $args[0];
+ if ($str[0] == "string") $str[1] = "";
+ return $str;
+ }
+
+ protected static $lib_quote = array("string");
+ protected function lib_quote($args) {
+ $value = $args[0];
+ if ($value[0] == "string" && !empty($value[1]))
+ return $value;
+ return array("string", '"', array($value));
+ }
+
+ protected static $lib_percentage = array("value");
+ protected function lib_percentage($args) {
+ return array("number",
+ $this->coercePercent($args[0]) * 100,
+ "%");
+ }
+
+ protected static $lib_round = array("value");
+ protected function lib_round($args) {
+ $num = $args[0];
+ $num[1] = round($num[1]);
+ return $num;
+ }
+
+ protected static $lib_floor = array("value");
+ protected function lib_floor($args) {
+ $num = $args[0];
+ $num[1] = floor($num[1]);
+ return $num;
+ }
+
+ protected static $lib_ceil = array("value");
+ protected function lib_ceil($args) {
+ $num = $args[0];
+ $num[1] = ceil($num[1]);
+ return $num;
+ }
+
+ protected static $lib_abs = array("value");
+ protected function lib_abs($args) {
+ $num = $args[0];
+ $num[1] = abs($num[1]);
+ return $num;
+ }
+
+ protected function lib_min($args) {
+ $numbers = $this->getNormalizedNumbers($args);
+ $min = null;
+ foreach ($numbers as $key => $number) {
+ if (null === $min || $number <= $min[1]) {
+ $min = array($key, $number);
+ }
+ }
+
+ return $args[$min[0]];
+ }
+
+ protected function lib_max($args) {
+ $numbers = $this->getNormalizedNumbers($args);
+ $max = null;
+ foreach ($numbers as $key => $number) {
+ if (null === $max || $number >= $max[1]) {
+ $max = array($key, $number);
+ }
+ }
+
+ return $args[$max[0]];
+ }
+
+ protected function getNormalizedNumbers($args) {
+ $unit = null;
+ $originalUnit = null;
+ $numbers = array();
+ foreach ($args as $key => $item) {
+ if ('number' != $item[0]) {
+ throw new Exception(sprintf('%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]));
+ }
+
+ $originalUnit = $item[2];
+ $numbers[$key] = $number[1];
+ }
+
+ return $numbers;
+ }
+
+ protected static $lib_length = array("list");
+ protected function lib_length($args) {
+ $list = $this->coerceList($args[0]);
+ return count($list[2]);
+ }
+
+ protected static $lib_nth = array("list", "n");
+ protected function lib_nth($args) {
+ $list = $this->coerceList($args[0]);
+ $n = $this->assertNumber($args[1]) - 1;
+ return isset($list[2][$n]) ? $list[2][$n] : self::$defaultValue;
+ }
+
+
+ protected function listSeparatorForJoin($list1, $sep) {
+ if (is_null($sep)) return $list1[1];
+ switch ($this->compileValue($sep)) {
+ case "comma":
+ return ",";
+ case "space":
+ return "";
+ default:
+ return $list1[1];
+ }
+ }
+
+ protected static $lib_join = array("list1", "list2", "separator");
+ protected function lib_join($args) {
+ list($list1, $list2, $sep) = $args;
+ $list1 = $this->coerceList($list1, " ");
+ $list2 = $this->coerceList($list2, " ");
+ $sep = $this->listSeparatorForJoin($list1, $sep);
+ return array("list", $sep, array_merge($list1[2], $list2[2]));
+ }
+
+ protected static $lib_append = array("list", "val", "separator");
+ protected function lib_append($args) {
+ list($list1, $value, $sep) = $args;
+ $list1 = $this->coerceList($list1, " ");
+ $sep = $this->listSeparatorForJoin($list1, $sep);
+ return array("list", $sep, array_merge($list1[2], array($value)));
+ }
+
+
+ protected static $lib_type_of = array("value");
+ protected function lib_type_of($args) {
+ $value = $args[0];
+ switch ($value[0]) {
+ case "keyword":
+ if ($value == self::$true || $value == self::$false) {
+ return "bool";
+ }
+
+ if ($this->coerceColor($value)) {
+ return "color";
+ }
+
+ return "string";
+ default:
+ return $value[0];
+ }
+ }
+
+ protected static $lib_unit = array("number");
+ protected function lib_unit($args) {
+ $num = $args[0];
+ if ($num[0] == "number") {
+ return array("string", '"', array($num[2]));
+ }
+ return "";
+ }
+
+ protected static $lib_unitless = array("number");
+ protected function lib_unitless($args) {
+ $value = $args[0];
+ 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
+ }
+
+ static protected $cssColors = array(
+ 'aliceblue' => '240,248,255',
+ 'antiquewhite' => '250,235,215',
+ 'aqua' => '0,255,255',
+ 'aquamarine' => '127,255,212',
+ 'azure' => '240,255,255',
+ 'beige' => '245,245,220',
+ 'bisque' => '255,228,196',
+ 'black' => '0,0,0',
+ 'blanchedalmond' => '255,235,205',
+ 'blue' => '0,0,255',
+ 'blueviolet' => '138,43,226',
+ 'brown' => '165,42,42',
+ 'burlywood' => '222,184,135',
+ 'cadetblue' => '95,158,160',
+ 'chartreuse' => '127,255,0',
+ 'chocolate' => '210,105,30',
+ 'coral' => '255,127,80',
+ 'cornflowerblue' => '100,149,237',
+ 'cornsilk' => '255,248,220',
+ 'crimson' => '220,20,60',
+ 'cyan' => '0,255,255',
+ 'darkblue' => '0,0,139',
+ 'darkcyan' => '0,139,139',
+ 'darkgoldenrod' => '184,134,11',
+ 'darkgray' => '169,169,169',
+ 'darkgreen' => '0,100,0',
+ 'darkgrey' => '169,169,169',
+ 'darkkhaki' => '189,183,107',
+ 'darkmagenta' => '139,0,139',
+ 'darkolivegreen' => '85,107,47',
+ 'darkorange' => '255,140,0',
+ 'darkorchid' => '153,50,204',
+ 'darkred' => '139,0,0',
+ 'darksalmon' => '233,150,122',
+ 'darkseagreen' => '143,188,143',
+ 'darkslateblue' => '72,61,139',
+ 'darkslategray' => '47,79,79',
+ 'darkslategrey' => '47,79,79',
+ 'darkturquoise' => '0,206,209',
+ 'darkviolet' => '148,0,211',
+ 'deeppink' => '255,20,147',
+ 'deepskyblue' => '0,191,255',
+ 'dimgray' => '105,105,105',
+ 'dimgrey' => '105,105,105',
+ 'dodgerblue' => '30,144,255',
+ 'firebrick' => '178,34,34',
+ 'floralwhite' => '255,250,240',
+ 'forestgreen' => '34,139,34',
+ 'fuchsia' => '255,0,255',
+ 'gainsboro' => '220,220,220',
+ 'ghostwhite' => '248,248,255',
+ 'gold' => '255,215,0',
+ 'goldenrod' => '218,165,32',
+ 'gray' => '128,128,128',
+ 'green' => '0,128,0',
+ 'greenyellow' => '173,255,47',
+ 'grey' => '128,128,128',
+ 'honeydew' => '240,255,240',
+ 'hotpink' => '255,105,180',
+ 'indianred' => '205,92,92',
+ 'indigo' => '75,0,130',
+ 'ivory' => '255,255,240',
+ 'khaki' => '240,230,140',
+ 'lavender' => '230,230,250',
+ 'lavenderblush' => '255,240,245',
+ 'lawngreen' => '124,252,0',
+ 'lemonchiffon' => '255,250,205',
+ 'lightblue' => '173,216,230',
+ 'lightcoral' => '240,128,128',
+ 'lightcyan' => '224,255,255',
+ 'lightgoldenrodyellow' => '250,250,210',
+ 'lightgray' => '211,211,211',
+ 'lightgreen' => '144,238,144',
+ 'lightgrey' => '211,211,211',
+ 'lightpink' => '255,182,193',
+ 'lightsalmon' => '255,160,122',
+ 'lightseagreen' => '32,178,170',
+ 'lightskyblue' => '135,206,250',
+ 'lightslategray' => '119,136,153',
+ 'lightslategrey' => '119,136,153',
+ 'lightsteelblue' => '176,196,222',
+ 'lightyellow' => '255,255,224',
+ 'lime' => '0,255,0',
+ 'limegreen' => '50,205,50',
+ 'linen' => '250,240,230',
+ 'magenta' => '255,0,255',
+ 'maroon' => '128,0,0',
+ 'mediumaquamarine' => '102,205,170',
+ 'mediumblue' => '0,0,205',
+ 'mediumorchid' => '186,85,211',
+ 'mediumpurple' => '147,112,219',
+ 'mediumseagreen' => '60,179,113',
+ 'mediumslateblue' => '123,104,238',
+ 'mediumspringgreen' => '0,250,154',
+ 'mediumturquoise' => '72,209,204',
+ 'mediumvioletred' => '199,21,133',
+ 'midnightblue' => '25,25,112',
+ 'mintcream' => '245,255,250',
+ 'mistyrose' => '255,228,225',
+ 'moccasin' => '255,228,181',
+ 'navajowhite' => '255,222,173',
+ 'navy' => '0,0,128',
+ 'oldlace' => '253,245,230',
+ 'olive' => '128,128,0',
+ 'olivedrab' => '107,142,35',
+ 'orange' => '255,165,0',
+ 'orangered' => '255,69,0',
+ 'orchid' => '218,112,214',
+ 'palegoldenrod' => '238,232,170',
+ 'palegreen' => '152,251,152',
+ 'paleturquoise' => '175,238,238',
+ 'palevioletred' => '219,112,147',
+ 'papayawhip' => '255,239,213',
+ 'peachpuff' => '255,218,185',
+ 'peru' => '205,133,63',
+ 'pink' => '255,192,203',
+ 'plum' => '221,160,221',
+ 'powderblue' => '176,224,230',
+ 'purple' => '128,0,128',
+ 'red' => '255,0,0',
+ 'rosybrown' => '188,143,143',
+ 'royalblue' => '65,105,225',
+ 'saddlebrown' => '139,69,19',
+ 'salmon' => '250,128,114',
+ 'sandybrown' => '244,164,96',
+ 'seagreen' => '46,139,87',
+ 'seashell' => '255,245,238',
+ 'sienna' => '160,82,45',
+ 'silver' => '192,192,192',
+ 'skyblue' => '135,206,235',
+ 'slateblue' => '106,90,205',
+ 'slategray' => '112,128,144',
+ 'slategrey' => '112,128,144',
+ 'snow' => '255,250,250',
+ 'springgreen' => '0,255,127',
+ 'steelblue' => '70,130,180',
+ 'tan' => '210,180,140',
+ 'teal' => '0,128,128',
+ 'thistle' => '216,191,216',
+ 'tomato' => '255,99,71',
+ 'turquoise' => '64,224,208',
+ 'violet' => '238,130,238',
+ 'wheat' => '245,222,179',
+ 'white' => '255,255,255',
+ 'whitesmoke' => '245,245,245',
+ 'yellow' => '255,255,0',
+ 'yellowgreen' => '154,205,50'
+ );
+}
+
+class scss_parser {
+ static protected $precedence = array(
+ "or" => 0,
+ "and" => 1,
+
+ '==' => 2,
+ '!=' => 2,
+ '<=' => 2,
+ '>=' => 2,
+ '=' => 2,
+ '<' => 3,
+ '>' => 2,
+
+ '+' => 3,
+ '-' => 3,
+ '*' => 4,
+ '/' => 4,
+ '%' => 4,
+ );
+
+ static protected $operators = array("+", "-", "*", "/", "%",
+ "==", "!=", "<=", ">=", "<", ">", "and", "or");
+
+ static protected $operatorStr;
+ static protected $whitePattern;
+ static protected $commentMulti;
+
+ static protected $commentSingle = "//";
+ static protected $commentMultiLeft = "/*";
+ static protected $commentMultiRight = "*/";
+
+ function __construct($sourceName = null) {
+ $this->sourceName = $sourceName;
+
+ if (empty(self::$operatorStr)) {
+ self::$operatorStr = $this->makeOperatorStr(self::$operators);
+
+ $commentSingle = $this->preg_quote(self::$commentSingle);
+ $commentMultiLeft = $this->preg_quote(self::$commentMultiLeft);
+ $commentMultiRight = $this->preg_quote(self::$commentMultiRight);
+ self::$commentMulti = $commentMultiLeft.'.*?'.$commentMultiRight;
+ self::$whitePattern = '/'.$commentSingle.'[^\n]*\s*|('.self::$commentMulti.')\s*|\s+/Ais';
+ }
+ }
+
+ static protected function makeOperatorStr($operators) {
+ return '('.implode('|', array_map(array('scss_parser','preg_quote'),
+ $operators)).')';
+ }
+
+ function parse($buffer) {
+ $this->count = 0;
+ $this->env = null;
+ $this->inParens = false;
+ $this->pushBlock(null); // root block
+ $this->eatWhiteDefault = true;
+ $this->insertComments = true;
+
+ $this->buffer = $buffer;
+
+ $this->whitespace();
+ while (false !== $this->parseChunk());
+
+ if ($this->count != strlen($this->buffer))
+ $this->throwParseError();
+
+ if (!empty($this->env->parent)) {
+ $this->throwParseError("unclosed block");
+ }
+
+ $this->env->isRoot = true;
+ return $this->env;
+ }
+
+ protected function parseChunk() {
+ $s = $this->seek();
+
+ // the directives
+ if (isset($this->buffer[$this->count]) && $this->buffer[$this->count] == "@") {
+ if ($this->literal("@media") && $this->mediaQueryList($mediaQueryList) && $this->literal("{")) {
+ $media = $this->pushSpecialBlock("media");
+ $media->queryList = $mediaQueryList[2];
+ return true;
+ } else {
+ $this->seek($s);
+ }
+
+ if ($this->literal("@mixin") &&
+ $this->keyword($mixinName) &&
+ ($this->argumentDef($args) || true) &&
+ $this->literal("{"))
+ {
+ $mixin = $this->pushSpecialBlock("mixin");
+ $mixin->name = $mixinName;
+ $mixin->args = $args;
+ return true;
+ } else {
+ $this->seek($s);
+ }
+
+ if ($this->literal("@include") &&
+ $this->keyword($mixinName) &&
+ ($this->literal("(") &&
+ ($this->argValues($argValues) || true) &&
+ $this->literal(")") || true) &&
+ ($this->end() ||
+ $this->literal("{") && $hasBlock = true))
+ {
+ $child = array("include",
+ $mixinName, isset($argValues) ? $argValues : null, null);
+
+ if (!empty($hasBlock)) {
+ $include = $this->pushSpecialBlock("include");
+ $include->child = $child;
+ } else {
+ $this->append($child);
+ }
+
+ return true;
+ } else {
+ $this->seek($s);
+ }
+
+ if ($this->literal("@import") &&
+ $this->valueList($importPath) &&
+ $this->end())
+ {
+ $this->append(array("import", $importPath));
+ return true;
+ } else {
+ $this->seek($s);
+ }
+
+ if ($this->literal("@extend") &&
+ $this->selectors($selector) &&
+ $this->end())
+ {
+ $this->append(array("extend", $selector));
+ return true;
+ } else {
+ $this->seek($s);
+ }
+
+ if ($this->literal("@function") &&
+ $this->keyword($fn_name) &&
+ $this->argumentDef($args) &&
+ $this->literal("{"))
+ {
+ $func = $this->pushSpecialBlock("function");
+ $func->name = $fn_name;
+ $func->args = $args;
+ return true;
+ } else {
+ $this->seek($s);
+ }
+
+ if ($this->literal("@return") && $this->valueList($retVal) && $this->end()) {
+ $this->append(array("return", $retVal));
+ return true;
+ } else {
+ $this->seek($s);
+ }
+
+ if ($this->literal("@each") &&
+ $this->variable($varName) &&
+ $this->literal("in") &&
+ $this->valueList($list) &&
+ $this->literal("{"))
+ {
+ $each = $this->pushSpecialBlock("each");
+ $each->var = $varName[1];
+ $each->list = $list;
+ return true;
+ } else {
+ $this->seek($s);
+ }
+
+ if ($this->literal("@while") &&
+ $this->expression($cond) &&
+ $this->literal("{"))
+ {
+ $while = $this->pushSpecialBlock("while");
+ $while->cond = $cond;
+ return true;
+ } else {
+ $this->seek($s);
+ }
+
+ if ($this->literal("@for") &&
+ $this->variable($varName) &&
+ $this->literal("from") &&
+ $this->expression($start) &&
+ ($this->literal("through") ||
+ ($forUntil = true && $this->literal("to"))) &&
+ $this->expression($end) &&
+ $this->literal("{"))
+ {
+ $for = $this->pushSpecialBlock("for");
+ $for->var = $varName[1];
+ $for->start = $start;
+ $for->end = $end;
+ $for->until = isset($forUntil);
+ return true;
+ } else {
+ $this->seek($s);
+ }
+
+ if ($this->literal("@if") && $this->valueList($cond) && $this->literal("{")) {
+ $if = $this->pushSpecialBlock("if");
+ $if->cond = $cond;
+ $if->cases = array();
+ return true;
+ } else {
+ $this->seek($s);
+ }
+
+ if (($this->literal("@debug") || $this->literal("@warn")) &&
+ $this->valueList($value) &&
+ $this->end()) {
+ $this->append(array("debug", $value, $s));
+ return true;
+ } else {
+ $this->seek($s);
+ }
+
+ if ($this->literal("@content") && $this->end()) {
+ $this->append(array("mixin_content"));
+ return true;
+ } else {
+ $this->seek($s);
+ }
+
+ $last = $this->last();
+ if (!is_null($last) && $last[0] == "if") {
+ list(, $if) = $last;
+ if ($this->literal("@else")) {
+ if ($this->literal("{")) {
+ $else = $this->pushSpecialBlock("else");
+ } elseif ($this->literal("if") && $this->valueList($cond) && $this->literal("{")) {
+ $else = $this->pushSpecialBlock("elseif");
+ $else->cond = $cond;
+ }
+
+ if (isset($else)) {
+ $else->dontAppend = true;
+ $if->cases[] = $else;
+ return true;
+ }
+ }
+
+ $this->seek($s);
+ }
+
+ if ($this->literal("@charset") &&
+ $this->valueList($charset) && $this->end())
+ {
+ $this->append(array("charset", $charset));
+ return true;
+ } else {
+ $this->seek($s);
+ }
+
+ // doesn't match built in directive, do generic one
+ if ($this->literal("@", false) && $this->keyword($dirName) &&
+ ($this->openString("{", $dirValue) || true) &&
+ $this->literal("{"))
+ {
+ $directive = $this->pushSpecialBlock("directive");
+ $directive->name = $dirName;
+ if (isset($dirValue)) $directive->value = $dirValue;
+ return true;
+ }
+
+ $this->seek($s);
+ return false;
+ }
+
+ // property shortcut
+ // captures most properties before having to parse a selector
+ if ($this->keyword($name, false) &&
+ $this->literal(": ") &&
+ $this->valueList($value) &&
+ $this->end())
+ {
+ $name = array("string", "", array($name));
+ $this->append(array("assign", $name, $value));
+ return true;
+ } else {
+ $this->seek($s);
+ }
+
+ // variable assigns
+ if ($this->variable($name) &&
+ $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));
+ return true;
+ } else {
+ $this->seek($s);
+ }
+
+ // misc
+ if ($this->literal("-->")) {
+ return true;
+ }
+
+ // opening css block
+ $oldComments = $this->insertComments;
+ $this->insertComments = false;
+ if ($this->selectors($selectors) && $this->literal("{")) {
+ $this->pushBlock($selectors);
+ $this->insertComments = $oldComments;
+ return true;
+ } else {
+ $this->seek($s);
+ }
+ $this->insertComments = $oldComments;
+
+ // property assign, or nested assign
+ if ($this->propertyName($name) && $this->literal(":")) {
+ $foundSomething = false;
+ if ($this->valueList($value)) {
+ $this->append(array("assign", $name, $value));
+ $foundSomething = true;
+ }
+
+ if ($this->literal("{")) {
+ $propBlock = $this->pushSpecialBlock("nestedprop");
+ $propBlock->prefix = $name;
+ $foundSomething = true;
+ } elseif ($foundSomething) {
+ $foundSomething = $this->end();
+ }
+
+ if ($foundSomething) {
+ return true;
+ }
+
+ $this->seek($s);
+ } else {
+ $this->seek($s);
+ }
+
+ // closing a block
+ if ($this->literal("}")) {
+ $block = $this->popBlock();
+ if (isset($block->type) && $block->type == "include") {
+ $include = $block->child;
+ unset($block->child);
+ $include[3] = $block;
+ $this->append($include);
+ } else if (empty($block->dontAppend)) {
+ $type = isset($block->type) ? $block->type : "block";
+ $this->append(array($type, $block));
+ }
+ return true;
+ }
+
+ // extra stuff
+ if ($this->literal(";") ||
+ $this->literal("<!--"))
+ {
+ return true;
+ }
+
+ 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;
+ }
+ else return false;
+ }
+
+ return $this->match($this->preg_quote($what), $m, $eatWhitespace);
+ }
+
+ // tree builders
+
+ protected function pushBlock($selectors) {
+ $b = new stdclass;
+ $b->parent = $this->env; // not sure if we need this yet
+
+ $b->selectors = $selectors;
+ $b->children = array();
+
+ $this->env = $b;
+ return $b;
+ }
+
+ protected function pushSpecialBlock($type) {
+ $block = $this->pushBlock(null);
+ $block->type = $type;
+ return $block;
+ }
+
+ protected function popBlock() {
+ if (empty($this->env->parent)) {
+ $this->throwParseError("unexpected }");
+ }
+
+ $old = $this->env;
+ $this->env = $this->env->parent;
+ unset($old->parent);
+ return $old;
+ }
+
+ protected function append($statement) {
+ $this->env->children[] = $statement;
+ }
+
+ // last child that was appended
+ protected function last() {
+ $i = count($this->env->children) - 1;
+ if (isset($this->env->children[$i]))
+ return $this->env->children[$i];
+ }
+
+ // high level parsers (they return parts of ast)
+
+ protected function mediaQueryList(&$out) {
+ return $this->genericList($out, "mediaQuery", ",", false);
+ }
+
+ protected function mediaQuery(&$out) {
+ $s = $this->seek();
+
+ $expressions = null;
+ $parts = array();
+
+ if (($this->literal("only") && ($only = true) || $this->literal("not") && ($not = true) || true) && $this->keyword($mediaType)) {
+ $prop = array("mediaType");
+ if (isset($only)) $prop[] = "only";
+ if (isset($not)) $prop[] = "not";
+ $prop[] = $mediaType;
+ $parts[] = $prop;
+ } else {
+ $this->seek($s);
+ }
+
+
+ if (!empty($mediaType) && !$this->literal("and")) {
+ // ~
+ } else {
+ $this->genericList($expressions, "mediaExpression", "and", false);
+ if (is_array($expressions)) $parts = array_merge($parts, $expressions[2]);
+ }
+
+ $out = $parts;
+ return true;
+ }
+
+ protected function mediaExpression(&$out) {
+ $s = $this->seek();
+ $value = null;
+ if ($this->literal("(") &&
+ $this->keyword($feature) &&
+ ($this->literal(":") && $this->expression($value) || true) &&
+ $this->literal(")"))
+ {
+ $out = array("mediaExp", $feature);
+ if ($value) $out[] = $value;
+ return true;
+ }
+
+ $this->seek($s);
+ return false;
+ }
+
+ protected function argValues(&$out) {
+ if ($this->genericList($list, "argValue", ",", false)) {
+ $out = $list[2];
+ return true;
+ }
+ return false;
+ }
+
+ protected function argValue(&$out) {
+ $s = $this->seek();
+
+ $keyword = null;
+ if (!$this->variable($keyword) || !$this->literal(":")) {
+ $this->seek($s);
+ $keyword = null;
+ }
+
+ if ($this->genericList($value, "expression")) {
+ $out = array($keyword, $value);
+ return true;
+ }
+
+ return false;
+ }
+
+
+ protected function valueList(&$out) {
+ return $this->genericList($out, "commaList");
+ }
+
+ protected function commaList(&$out) {
+ return $this->genericList($out, "expression", ",");
+ }
+
+ protected function genericList(&$out, $parseItem, $delim="", $flatten=true) {
+ $s = $this->seek();
+ $items = array();
+ while ($this->$parseItem($value)) {
+ $items[] = $value;
+ if ($delim) {
+ if (!$this->literal($delim)) break;
+ }
+ }
+
+ if (count($items) == 0) {
+ $this->seek($s);
+ return false;
+ }
+
+ if ($flatten && count($items) == 1) {
+ $out = $items[0];
+ } else {
+ $out = array("list", $delim, $items);
+ }
+
+ return true;
+ }
+
+ protected function expression(&$out) {
+ $s = $this->seek();
+
+ if ($this->literal("(")) {
+ if ($this->literal(")")) {
+ $out = array("list", "", array());
+ return true;
+ }
+
+ if ($this->valueList($out) && $this->literal(')') && $out[0] == "list") {
+ return true;
+ }
+
+ $this->seek($s);
+ }
+
+ if ($this->value($lhs)) {
+ $out = $this->expHelper($lhs, 0);
+ return true;
+ }
+
+ return false;
+ }
+
+ protected function expHelper($lhs, $minP) {
+ $opstr = self::$operatorStr;
+
+ $ss = $this->seek();
+ $whiteBefore = isset($this->buffer[$this->count - 1]) &&
+ ctype_space($this->buffer[$this->count - 1]);
+ while ($this->match($opstr, $m) && self::$precedence[$m[1]] >= $minP) {
+ $whiteAfter = isset($this->buffer[$this->count - 1]) &&
+ ctype_space($this->buffer[$this->count - 1]);
+
+ $op = $m[1];
+
+ // don't turn negative numbers into expressions
+ if ($op == "-" && $whiteBefore) {
+ if (!$whiteAfter) break;
+ }
+
+ if (!$this->value($rhs)) break;
+
+ // peek and see if rhs belongs to next operator
+ if ($this->peek($opstr, $next) && self::$precedence[$next[1]] > self::$precedence[$op]) {
+ $rhs = $this->expHelper($rhs, self::$precedence[$next[1]]);
+ }
+
+ $lhs = array("exp", $op, $lhs, $rhs, $this->inParens, $whiteBefore, $whiteAfter);
+ $ss = $this->seek();
+ $whiteBefore = isset($this->buffer[$this->count - 1]) &&
+ ctype_space($this->buffer[$this->count - 1]);
+ }
+
+ $this->seek($ss);
+ return $lhs;
+ }
+
+ protected function value(&$out) {
+ $s = $this->seek();
+
+ if ($this->literal("not", false) && $this->whitespace() && $this->value($inner)) {
+ $out = array("unary", "not", $inner, $this->inParens);
+ return true;
+ } else {
+ $this->seek($s);
+ }
+
+ if ($this->literal("+") && $this->value($inner)) {
+ $out = array("unary", "+", $inner, $this->inParens);
+ return true;
+ } else {
+ $this->seek($s);
+ }
+
+ // negation
+ if ($this->literal("-", false) &&
+ ($this->variable($inner) ||
+ $this->unit($inner) ||
+ $this->parenValue($inner)))
+ {
+ $out = array("unary", "-", $inner, $this->inParens);
+ return true;
+ } else {
+ $this->seek($s);
+ }
+
+ if ($this->parenValue($out)) return true;
+ if ($this->interpolation($out)) return true;
+ if ($this->variable($out)) return true;
+ if ($this->color($out)) return true;
+ if ($this->unit($out)) return true;
+ if ($this->string($out)) return true;
+ if ($this->func($out)) return true;
+ if ($this->progid($out)) return true;
+
+ if ($this->keyword($keyword)) {
+ $out = array("keyword", $keyword);
+ return true;
+ }
+
+ return false;
+ }
+
+ // value wrappen in parentheses
+ protected function parenValue(&$out) {
+ $s = $this->seek();
+
+ $inParens = $this->inParens;
+ if ($this->literal("(") &&
+ ($this->inParens = true) && $this->expression($exp) &&
+ $this->literal(")"))
+ {
+ $out = $exp;
+ $this->inParens = $inParens;
+ return true;
+ } else {
+ $this->inParens = $inParens;
+ $this->seek($s);
+ }
+
+ return false;
+ }
+
+ protected function progid(&$out) {
+ $s = $this->seek();
+ if ($this->literal("progid:", false) &&
+ $this->openString("(", $fn) &&
+ $this->literal("("))
+ {
+ $this->openString(")", $args, "(");
+ if ($this->literal(")")) {
+ $out = array("string", "", array(
+ "progid:", $fn, "(", $args, ")"
+ ));
+ return true;
+ }
+ }
+
+ $this->seek($s);
+ return false;
+ }
+
+ protected function func(&$func) {
+ $s = $this->seek();
+
+ if ($this->keyword($name, false) &&
+ $this->literal("("))
+ {
+ if ($name != "expression" && false == preg_match("/^(-[a-z]+-)?calc$/", $name)) {
+ $ss = $this->seek();
+ if ($this->argValues($args) && $this->literal(")")) {
+ $func = array("fncall", $name, $args);
+ return true;
+ }
+ $this->seek($ss);
+ }
+
+ if (($this->openString(")", $str, "(") || true ) &&
+ $this->literal(")"))
+ {
+ $args = array();
+ if (!empty($str)) {
+ $args[] = array(null, array("string", "", array($str)));
+ }
+
+ $func = array("fncall", $name, $args);
+ return true;
+ }
+ }
+
+ $this->seek($s);
+ return false;
+ }
+
+ protected function argumentDef(&$out) {
+ $s = $this->seek();
+ $this->literal("(");
+
+ $args = array();
+ while ($this->variable($var)) {
+ $arg = array($var[1], null);
+
+ $ss = $this->seek();
+ if ($this->literal(":") && $this->expression($defaultVal)) {
+ $arg[1] = $defaultVal;
+ } else {
+ $this->seek($ss);
+ }
+
+ $args[] = $arg;
+ if (!$this->literal(",")) break;
+ }
+
+ if (!$this->literal(")")) {
+ $this->seek($s);
+ return false;
+ }
+
+ $out = $args;
+ return true;
+ }
+
+ protected function color(&$out) {
+ $color = array('color');
+
+ if ($this->match('(#([0-9a-f]{6})|#([0-9a-f]{3}))', $m)) {
+ if (isset($m[3])) {
+ $num = $m[3];
+ $width = 16;
+ } else {
+ $num = $m[2];
+ $width = 256;
+ }
+
+ $num = hexdec($num);
+ foreach (array(3,2,1) as $i) {
+ $t = $num % $width;
+ $num /= $width;
+
+ $color[$i] = $t * (256/$width) + $t * floor(16/$width);
+ }
+
+ $out = $color;
+ return true;
+ }
+
+ return false;
+ }
+
+ protected function unit(&$unit) {
+ if ($this->match('([0-9]*(\.)?[0-9]+)([%a-zA-Z]+)?', $m)) {
+ $unit = array("number", $m[1], empty($m[3]) ? "" : $m[3]);
+ return true;
+ }
+ return false;
+ }
+
+ protected function string(&$out) {
+ $s = $this->seek();
+ if ($this->literal('"', false)) {
+ $delim = '"';
+ } elseif ($this->literal("'", false)) {
+ $delim = "'";
+ } else {
+ return false;
+ }
+
+ $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)) {
+ $content[] = $m[1];
+ if ($m[2] == "#{") {
+ $this->count -= strlen($m[2]);
+ if ($this->interpolation($inter, false)) {
+ $content[] = $inter;
+ } else {
+ $this->count += strlen($m[2]);
+ $content[] = "#{"; // ignore it
+ }
+ } elseif ($m[2] == '\\') {
+ $content[] = $m[2];
+ if ($this->literal($delim, false)) {
+ $content[] = $delim;
+ }
+ } else {
+ $this->count -= strlen($delim);
+ break; // delim
+ }
+ }
+
+ $this->eatWhiteDefault = $oldWhite;
+
+ if ($this->literal($delim)) {
+ $out = array("string", $delim, $content);
+ return true;
+ }
+
+ $this->seek($s);
+ return false;
+ }
+
+ protected function mixedKeyword(&$out) {
+ $s = $this->seek();
+
+ $parts = array();
+
+ $oldWhite = $this->eatWhiteDefault;
+ $this->eatWhiteDefault = false;
+
+ while (true) {
+ if ($this->keyword($key)) {
+ $parts[] = $key;
+ continue;
+ }
+
+ if ($this->interpolation($inter)) {
+ $parts[] = $inter;
+ continue;
+ }
+
+ break;
+ }
+
+ $this->eatWhiteDefault = $oldWhite;
+
+ if (count($parts) == 0) return false;
+
+ $out = $parts;
+ return true;
+ }
+
+ // an unbounded string stopped by $end
+ protected function openString($end, &$out, $nestingOpen=null) {
+ $oldWhite = $this->eatWhiteDefault;
+ $this->eatWhiteDefault = false;
+
+ $stop = array("'", '"', "#{", $end);
+ $stop = array_map(array($this, "preg_quote"), $stop);
+ $stop[] = self::$commentMulti;
+
+ $patt = '(.*?)('.implode("|", $stop).')';
+
+ $nestingLevel = 0;
+
+ $content = array();
+ while ($this->match($patt, $m, false)) {
+ if (!empty($m[1])) {
+ $content[] = $m[1];
+ if ($nestingOpen) {
+ $nestingLevel += substr_count($m[1], $nestingOpen);
+ }
+ }
+
+ $tok = $m[2];
+
+ $this->count-= strlen($tok);
+ if ($tok == $end) {
+ if ($nestingLevel == 0) {
+ break;
+ } else {
+ $nestingLevel--;
+ }
+ }
+
+ if (($tok == "'" || $tok == '"') && $this->string($str)) {
+ $content[] = $str;
+ continue;
+ }
+
+ if ($tok == "#{" && $this->interpolation($inter)) {
+ $content[] = $inter;
+ continue;
+ }
+
+ $content[] = $tok;
+ $this->count+= strlen($tok);
+ }
+
+ $this->eatWhiteDefault = $oldWhite;
+
+ if (count($content) == 0) return false;
+
+ // trim the end
+ if (is_string(end($content))) {
+ $content[count($content) - 1] = rtrim(end($content));
+ }
+
+ $out = array("string", "", $content);
+ return true;
+ }
+
+ // $lookWhite: save information about whitespace before and after
+ protected function interpolation(&$out, $lookWhite=true) {
+ $oldWhite = $this->eatWhiteDefault;
+ $this->eatWhiteDefault = true;
+
+ $s = $this->seek();
+ if ($this->literal("#{") && $this->valueList($value) && $this->literal("}", false)) {
+
+ // TODO: don't error if out of bounds
+
+ if ($lookWhite) {
+ $left = preg_match('/\s/', $this->buffer[$s - 1]) ? " " : "";
+ $right = preg_match('/\s/', $this->buffer[$this->count]) ? " ": "";
+ } else {
+ $left = $right = false;
+ }
+
+ $out = array("interpolate", $value, $left, $right);
+ $this->eatWhiteDefault = $oldWhite;
+ if ($this->eatWhiteDefault) $this->whitespace();
+ return true;
+ }
+
+ $this->seek($s);
+ $this->eatWhiteDefault = $oldWhite;
+ return false;
+ }
+
+ // low level parsers
+
+ // returns an array of parts or a string
+ protected function propertyName(&$out) {
+ $s = $this->seek();
+ $parts = array();
+
+ $oldWhite = $this->eatWhiteDefault;
+ $this->eatWhiteDefault = false;
+
+ while (true) {
+ if ($this->interpolation($inter)) {
+ $parts[] = $inter;
+ } elseif ($this->keyword($text)) {
+ $parts[] = $text;
+ } elseif (count($parts) == 0 && $this->match('[:.#]', $m, false)) {
+ // css hacks
+ $parts[] = $m[0];
+ } else {
+ break;
+ }
+ }
+
+ $this->eatWhiteDefault = $oldWhite;
+ if (count($parts) == 0) return false;
+
+ // match comment hack
+ if (preg_match(self::$whitePattern,
+ $this->buffer, $m, null, $this->count))
+ {
+ if (!empty($m[0])) {
+ $parts[] = $m[0];
+ $this->count += strlen($m[0]);
+ }
+ }
+
+ $this->whitespace(); // get any extra whitespace
+
+ $out = array("string", "", $parts);
+ return true;
+ }
+
+ // comma separated list of selectors
+ protected function selectors(&$out) {
+ $s = $this->seek();
+ $selectors = array();
+ while ($this->selector($sel)) {
+ $selectors[] = $sel;
+ if (!$this->literal(",")) break;
+ while ($this->literal(",")); // ignore extra
+ }
+
+ if (count($selectors) == 0) {
+ $this->seek($s);
+ return false;
+ }
+
+ $out = $selectors;
+ return true;
+ }
+
+ // whitepsace separated list of selectorSingle
+ protected function selector(&$out) {
+ $selector = array();
+
+ while (true) {
+ if ($this->match('[>+~]+', $m)) {
+ $selector[] = array($m[0]);
+ } elseif ($this->selectorSingle($part)) {
+ $selector[] = $part;
+ $this->whitespace();
+ } else {
+ break;
+ }
+
+ }
+
+ if (count($selector) == 0) {
+ return false;
+ }
+
+ $out = $selector;
+ return true;
+ }
+
+ // the parts that make up
+ // div[yes=no]#something.hello.world:nth-child(-2n+1)
+ protected function selectorSingle(&$out) {
+ $oldWhite = $this->eatWhiteDefault;
+ $this->eatWhiteDefault = false;
+
+ $parts = array();
+
+ if ($this->literal("*", false)) {
+ $parts[] = "*";
+ }
+
+ while (true) {
+ // see if we can stop early
+ if ($this->match("\s*[{,]", $m)) {
+ $this->count--;
+ break;
+ }
+
+ $s = $this->seek();
+ // self
+ if ($this->literal("&", false)) {
+ $parts[] = scssc::$selfSelector;
+ continue;
+ }
+
+ if ($this->literal(".", false)) {
+ $parts[] = ".";
+ continue;
+ }
+
+ if ($this->literal("|", false)) {
+ $parts[] = "|";
+ continue;
+ }
+
+ // for keyframes
+ if ($this->unit($unit)) {
+ $parts[] = $unit;
+ continue;
+ }
+
+ if ($this->keyword($name)) {
+ $parts[] = $name;
+ continue;
+ }
+
+ if ($this->interpolation($inter)) {
+ $parts[] = $inter;
+ continue;
+ }
+
+ if ($this->literal("#", false)) {
+ $parts[] = "#";
+ continue;
+ }
+
+ // a pseudo selector
+ if ($this->match("::?", $m) && $this->mixedKeyword($nameParts)) {
+ $parts[] = $m[0];
+ foreach ($nameParts as $sub) {
+ $parts[] = $sub;
+ }
+
+ $ss = $this->seek();
+ if ($this->literal("(") &&
+ ($this->openString(")", $str, "(") || true ) &&
+ $this->literal(")"))
+ {
+ $parts[] = "(";
+ if (!empty($str)) $parts[] = $str;
+ $parts[] = ")";
+ } else {
+ $this->seek($ss);
+ }
+
+ continue;
+ } else {
+ $this->seek($s);
+ }
+
+ // attribute selector
+ // TODO: replace with open string?
+ if ($this->literal("[", false)) {
+ $attrParts = array("[");
+ // keyword, string, operator
+ while (true) {
+ if ($this->literal("]", false)) {
+ $this->count--;
+ break; // get out early
+ }
+
+ if ($this->match('\s+', $m)) {
+ $attrParts[] = " ";
+ continue;
+ }
+ if ($this->string($str)) {
+ $attrParts[] = $str;
+ continue;
+ }
+
+ if ($this->keyword($word)) {
+ $attrParts[] = $word;
+ continue;
+ }
+
+ if ($this->interpolation($inter, false)) {
+ $attrParts[] = $inter;
+ 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;
+ }
+ continue;
+ }
+ $this->seek($s);
+ // should just break here?
+ }
+
+ break;
+ }
+
+ $this->eatWhiteDefault = $oldWhite;
+
+ if (count($parts) == 0) return false;
+
+ $out = $parts;
+ return true;
+ }
+
+ protected function variable(&$out) {
+ $s = $this->seek();
+ if ($this->literal("$", false) && $this->keyword($name)) {
+ $out = array("var", $name);
+ return true;
+ }
+ $this->seek($s);
+ return false;
+ }
+
+ protected function keyword(&$word, $eatWhitespace = null) {
+ if ($this->match('([\w_\-\*!"\'\\\\][\w\-_"\'\\\\]*)',
+ $m, $eatWhitespace))
+ {
+ $word = $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} == '}') {
+ // if there is end of file or a closing block next then we don't need a ;
+ return true;
+ }
+ return false;
+ }
+
+ // advance counter to next occurrence of $what
+ // $until - don't include $what in advance
+ // $allowNewline, if string, will be used as valid char set
+ protected function to($what, &$out, $until = false, $allowNewline = false) {
+ if (is_string($allowNewline)) {
+ $validChars = $allowNewline;
+ } else {
+ $validChars = $allowNewline ? "." : "[^\n]";
+ }
+ if (!$this->match('('.$validChars.'*?)'.$this->preg_quote($what), $m, !$until)) return false;
+ if ($until) $this->count -= strlen($what); // give back $what
+ $out = $m[1];
+ return true;
+ }
+
+ protected function throwParseError($msg = "parse error", $count = null) {
+ $count = is_null($count) ? $this->count : $count;
+
+ $line = $this->getLineNo($count);
+
+ if (!empty($this->sourceName)) {
+ $loc = "$this->sourceName on line $line";
+ } else {
+ $loc = "line: $line";
+ }
+
+ if ($this->peek("(.*?)(\n|$)", $m, $count)) {
+ throw new exception("$msg: failed at `$m[1]` $loc");
+ } else {
+ throw new exception("$msg: $loc");
+ }
+ }
+
+ public function getLineNo($pos) {
+ return 1 + substr_count(substr($this->buffer, 0, $pos), "\n");
+ }
+
+ // try to match something on head of buffer
+ protected function match($regex, &$out, $eatWhitespace = null) {
+ if (is_null($eatWhitespace)) $eatWhitespace = $this->eatWhiteDefault;
+
+ $r = '/'.$regex.'/Ais';
+ if (preg_match($r, $this->buffer, $out, null, $this->count)) {
+ $this->count += strlen($out[0]);
+ if ($eatWhitespace) $this->whitespace();
+ return true;
+ }
+ return false;
+ }
+
+ // match some whitespace
+ protected function whitespace() {
+ $gotWhite = false;
+ while (preg_match(self::$whitePattern, $this->buffer, $m, null, $this->count)) {
+ if ($this->insertComments) {
+ if (isset($m[1]) && empty($this->commentsSeen[$this->count])) {
+ $this->append(array("comment", $m[1]));
+ $this->commentsSeen[$this->count] = true;
+ }
+ }
+ $this->count += strlen($m[0]);
+ $gotWhite = true;
+ }
+ return $gotWhite;
+ }
+
+ protected function peek($regex, &$out, $from=null) {
+ if (is_null($from)) $from = $this->count;
+
+ $r = '/'.$regex.'/Ais';
+ $result = preg_match($r, $this->buffer, $out, null, $from);
+
+ return $result;
+ }
+
+ protected function seek($where = null) {
+ if ($where === null) return $this->count;
+ else $this->count = $where;
+ return true;
+ }
+
+ static function preg_quote($what) {
+ return preg_quote($what, '/');
+ }
+
+ protected function show() {
+ if ($this->peek("(.*?)(\n|$)", $m, $this->count)) {
+ return $m[1];
+ }
+ return "";
+ }
+
+ // turn list of length 1 into value type
+ protected function flattenList($value) {
+ if ($value[0] == "list" && count($value[2]) == 1) {
+ return $this->flattenList($value[2][0]);
+ }
+ return $value;
+ }
+}
+
+class scss_formatter {
+ public $indentChar = " ";
+
+ public $break = "\n";
+ public $open = " {";
+ public $close = "}";
+ public $tagSeparator = ", ";
+ public $assignSeparator = ": ";
+
+ public function __construct() {
+ $this->indentLevel = 0;
+ }
+
+ public function indentStr($n = 0) {
+ return str_repeat($this->indentChar, max($this->indentLevel + $n, 0));
+ }
+
+ public function property($name, $value) {
+ return $name . $this->assignSeparator . $value . ";";
+ }
+
+ public function block($block) {
+ if (empty($block->lines) && empty($block->children)) return;
+
+ $inner = $pre = $this->indentStr();
+
+ if (!empty($block->selectors)) {
+ echo $pre .
+ implode($this->tagSeparator, $block->selectors) .
+ $this->open . $this->break;
+ $this->indentLevel++;
+ $inner = $this->indentStr();
+ }
+
+ if (!empty($block->lines)) {
+ $glue = $this->break.$inner;
+ echo $inner . implode($glue, $block->lines);
+ if (!empty($block->children)) {
+ echo $this->break;
+ }
+ }
+
+ foreach ($block->children as $child) {
+ $this->block($child);
+ }
+
+ if (!empty($block->selectors)) {
+ $this->indentLevel--;
+ if (empty($block->children)) echo $this->break;
+ echo $pre . $this->close . $this->break;
+ }
+ }
+}
+
+
+class scss_formatter_nested extends scss_formatter {
+ public $close = " }";
+
+ // adjust the depths of all children, depth first
+ public function adjustAllChildren($block) {
+ // flatten empty nested blocks
+ $children = array();
+ foreach ($block->children as $i => $child) {
+ if (empty($child->lines) && empty($child->children)) {
+ if (isset($block->children[$i + 1])) {
+ $block->children[$i + 1]->depth = $child->depth;
+ }
+ continue;
+ }
+ $children[] = $child;
+ }
+ $block->children = $children;
+
+ // make relative to parent
+ foreach ($block->children as $child) {
+ $this->adjustAllChildren($child);
+ $child->depth = $child->depth - $block->depth;
+ }
+ }
+
+ public function block($block) {
+ if ($block->type == "root") {
+ $this->adjustAllChildren($block);
+ }
+
+ $inner = $pre = $this->indentStr($block->depth - 1);
+ if (!empty($block->selectors)) {
+ echo $pre .
+ implode($this->tagSeparator, $block->selectors) .
+ $this->open . $this->break;
+ $this->indentLevel++;
+ $inner = $this->indentStr($block->depth - 1);
+ }
+
+ if (!empty($block->lines)) {
+ $glue = $this->break.$inner;
+ echo $inner . implode($glue, $block->lines);
+ if (!empty($block->children)) echo $this->break;
+ }
+
+ foreach ($block->children as $i => $child) {
+ // echo "*** block: ".$block->depth." child: ".$child->depth."\n";
+ $this->block($child);
+ if ($i < count($block->children) - 1) {
+ echo $this->break;
+
+ if (isset($block->children[$i + 1])) {
+ $next = $block->children[$i + 1];
+ if ($next->depth == max($block->depth, 1) && $child->depth >= $next->depth) {
+ echo $this->break;
+ }
+ }
+ }
+ }
+
+ if (!empty($block->selectors)) {
+ $this->indentLevel--;
+ echo $this->close;
+ }
+
+ if ($block->type == "root") {
+ echo $this->break;
+ }
+ }
+}
+
+class scss_formatter_compressed extends scss_formatter {
+ public $open = "{";
+ public $tagSeparator = ",";
+ public $assignSeparator = ":";
+ public $break = "";
+
+ public function indentStr($n = 0) {
+ return "";
+ }
+}
+
+class scss_server {
+
+ protected function join($left, $right) {
+ return rtrim($left, "/") . "/" . ltrim($right, "/");
+ }
+
+ 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"]));
+ }
+ }
+
+ protected function findInput() {
+ if ($input = $this->inputName()) {
+ $name = $this->join($this->dir, $input);
+ if (is_readable($name)) return $name;
+ }
+ return false;
+ }
+
+ protected function cacheName($fname) {
+ return $this->join($this->cacheDir, md5($fname) . ".css");
+ }
+
+ protected function importsCacheName($out) {
+ return $out . ".imports";
+ }
+
+ protected function needsCompile($in, $out) {
+ if (!is_file($out)) return true;
+
+ $mtime = filemtime($out);
+ if (filemtime($in) > $mtime) return true;
+
+ // look for modified imports
+ $icache = $this->importsCacheName($out);
+ if (is_readable($icache)) {
+ $imports = unserialize(file_get_contents($icache));
+ foreach ($imports as $import) {
+ if (filemtime($import) > $mtime) return true;
+ }
+ }
+ return false;
+ }
+
+ 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");
+ $css = "/* compiled by scssphp $v on $t (${elapsed}s) */\n\n" . $css;
+
+ file_put_contents($out, $css);
+ file_put_contents($this->importsCacheName($out),
+ serialize($this->scss->getParsedFiles()));
+ return $css;
+ }
+
+ public function serve() {
+ if ($input = $this->findInput()) {
+ $output = $this->cacheName($input);
+ header("Content-type: text/css");
+
+ if ($this->needsCompile($input, $output)) {
+ try {
+ echo $this->compile($input, $output);
+ } catch (exception $e) {
+ header('HTTP/1.1 500 Internal Server Error');
+ echo "Parse error: " . $e->getMessage() . "\n";
+ }
+ } else {
+ header('X-SCSS-Cache: true');
+ echo file_get_contents($output);
+ }
+
+ return;
+ }
+
+ header('HTTP/1.0 404 Not Found');
+ header("Content-type: text");
+ $v = scssc::$VERSION;
+ echo "/* INPUT NOT FOUND scss $v */\n";
+ }
+
+ public function __construct($dir, $cacheDir=null, $scss=null) {
+ $this->dir = $dir;
+
+ if (is_null($cacheDir)) {
+ $cacheDir = $this->join($dir, "scss_cache");
+ }
+
+ $this->cacheDir = $cacheDir;
+ if (!is_dir($this->cacheDir)) mkdir($this->cacheDir);
+
+ if (is_null($scss)) {
+ $scss = new scssc();
+ $scss->setImportPaths($this->dir);
+ }
+ $this->scss = $scss;
+ }
+
+ static public function serveFrom($path) {
+ $server = new self($path);
+ $server->serve();
+ }
+}
+
+
diff --git a/plugins/jetpack/modules/enhanced-distribution.php b/plugins/jetpack/modules/enhanced-distribution.php
index c3bc7aaf..b2a80155 100644
--- a/plugins/jetpack/modules/enhanced-distribution.php
+++ b/plugins/jetpack/modules/enhanced-distribution.php
@@ -6,4 +6,25 @@
* First Introduced: 1.2
*/
-// Stub
+Jetpack_Sync::sync_posts( __FILE__ );
+Jetpack_Sync::sync_comments( __FILE__ );
+
+function jetpack_enhanced_distribution_activate() {
+ Jetpack::check_privacy( __FILE__ );
+}
+
+
+// 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' );
+ list( $old_version ) = explode( ':', $old_version );
+
+ if ( version_compare( $old_version, '1.9-something', '>=' ) ) {
+ return;
+ }
+
+ Jetpack::check_privacy( __FILE__ );
+}
+
+add_action( 'jetpack_activate_module_enhanced-distribution', 'jetpack_enhanced_distribution_activate' );
+add_action( 'jetpack_before_activate_default_modules', 'jetpack_enhanced_distribution_before_activate_default_modules' );
diff --git a/plugins/jetpack/modules/featured-content/featured-content.php b/plugins/jetpack/modules/featured-content/featured-content.php
new file mode 100644
index 00000000..9cd511ed
--- /dev/null
+++ b/plugins/jetpack/modules/featured-content/featured-content.php
@@ -0,0 +1,454 @@
+<?php
+
+/**
+ * Featured Content.
+ *
+ * This module will allow users to define a subset of posts
+ * to be displayed in a theme-designated featured content area.
+ *
+ * This feature will only be activated for themes that declare
+ * that they support it. This can be done by adding code similar
+ * to the following during the "after_setup_theme" action:
+ *
+ * add_theme_support( 'featured-content', array(
+ * 'featured_content_filter' => 'mytheme_get_featured_content',
+ * 'description' => 'Describe the featured content area.',
+ * 'max_posts' => 20,
+ * ) );
+ *
+ * For maximum compatibility with different methods of posting
+ * users will designate a featured post tag to associate posts
+ * to. Since this tag now has special meaning beyond that of a
+ * normal tags, users will have the ability to hide it from the
+ * front-end of their site.
+ */
+class Featured_Content {
+
+ /**
+ * The maximum number of posts that a featured content
+ * area can contain. We define a default value here but
+ * themes can override this by defining a "max_posts"
+ * entry in the second parameter passed in the call to
+ * add_theme_support( 'featured-content' ).
+ *
+ * @see Featured_Content::init()
+ */
+ public static $max_posts = 15;
+
+ /**
+ * Instantiate.
+ *
+ * All custom functionality will be hooked into the "init" action.
+ */
+ public static function setup() {
+ add_action( 'init', array( __class__, 'init' ) );
+ }
+
+ /**
+ * Conditionally Hook into WordPress.
+ *
+ * Themes must declare that they support this module by adding
+ * add_theme_support( 'featured-content' ); during after_setup_theme.
+ *
+ * If no theme support is found there is no need to hook into
+ * WordPress. We'll just return early instead.
+ *
+ * @uses Featured_Content::$max_posts
+ */
+ public static function init() {
+ $theme_support = get_theme_support( 'featured-content' );
+
+ // Return early if theme does not support featured content.
+ if ( ! $theme_support )
+ return;
+
+ // An array of named arguments must be passed as
+ // the second parameter of add_theme_support().
+ if ( ! isset( $theme_support[0] ) )
+ return;
+
+ // Return early if "featured_content_filter" has not been defined.
+ if ( ! isset( $theme_support[0]['featured_content_filter'] ) )
+ return;
+
+ $filter = $theme_support[0]['featured_content_filter'];
+
+ // Themes can override the number of max posts.
+ if ( isset( $theme_support[0]['max_posts'] ) )
+ self::$max_posts = absint( $theme_support[0]['max_posts'] );
+
+ add_filter( $filter, array( __CLASS__, 'get_featured_posts' ) );
+ add_action( 'admin_init', array( __CLASS__, 'register_setting' ) );
+ add_action( 'save_post', array( __CLASS__, 'delete_transient' ) );
+ add_action( 'delete_post_tag', array( __CLASS__, 'delete_post_tag' ) );
+ add_action( 'pre_get_posts', array( __CLASS__, 'pre_get_posts' ) );
+
+ // Hide "featured" tag from the front-end.
+ if ( self::get_setting( 'hide-tag' ) ) {
+ add_filter( 'get_terms', array( __CLASS__, 'hide_featured_term' ), 10, 2 );
+ add_filter( 'get_the_terms', array( __CLASS__, 'hide_the_featured_term' ), 10, 3 );
+ }
+ }
+
+ /**
+ * Get Featured Posts.
+ *
+ * This method is not intended to be called directly.
+ * Theme developers should place a filter directly in
+ * their theme and then pass its name as a value of the
+ * "featured_content_filter" key in the array passed as
+ * the $args parameter during the call to:
+ * add_theme_support( 'featured-content', $args ).
+ *
+ * @uses Featured_Content::get_featured_post_ids()
+ *
+ * @return array|bool
+ */
+ public static function get_featured_posts() {
+ $post_ids = self::get_featured_post_ids();
+
+ // User has disabled Featured Content.
+ if ( false === $post_ids )
+ return false;
+
+ // No need to query if there is are no featured posts.
+ if ( empty( $post_ids ) )
+ return array();
+
+ $featured_posts = get_posts( array(
+ 'include' => $post_ids,
+ 'posts_per_page' => count( $post_ids )
+ ) );
+
+ return $featured_posts;
+ }
+
+ /**
+ * Get Featured Post IDs.
+ *
+ * This function will return the an array containing the
+ * post IDs of all featured posts.
+ *
+ * Sets the "featured_content_ids" transient.
+ *
+ * @return array|false Array of post IDs. false if user has disabled this feature.
+ */
+ public static function get_featured_post_ids() {
+ $settings = self::get_setting();
+
+ // Return false if the user has disabled this feature.
+ $tag = $settings['tag-id'];
+ if ( empty( $tag ) )
+ return false;
+
+ // Return array of cached results if they exist.
+ $featured_ids = get_transient( 'featured_content_ids' );
+ if ( ! empty( $featured_ids ) )
+ return array_map( 'absint', (array) $featured_ids );
+
+ // Query for featured posts.
+ $featured = get_posts( array(
+ 'numberposts' => $settings['quantity'],
+ 'tax_query' => array(
+ array(
+ 'field' => 'term_id',
+ 'taxonomy' => 'post_tag',
+ 'terms' => $tag,
+ ),
+ ),
+ ) );
+
+ // Return empty array if no featured content exists.
+ if ( ! $featured )
+ return array();
+
+ // Ensure correct format before save/return.
+ $featured_ids = wp_list_pluck( (array) $featured, 'ID' );
+ $featured_ids = array_map( 'absint', $featured_ids );
+
+ set_transient( 'featured_content_ids', $featured_ids );
+
+ return $featured_ids;
+ }
+
+ /**
+ * Delete Transient.
+ *
+ * Hooks in the "save_post" action.
+ * @see Featured_Content::validate_settings().
+ */
+ public static function delete_transient() {
+ delete_transient( 'featured_content_ids' );
+ }
+
+ /**
+ * Exclude featured posts from the blog query when the blog is the front-page.
+ *
+ * Filter the home page posts, and remove any featured post ID's from it. Hooked
+ * onto the 'pre_get_posts' action, this changes the parameters of the query
+ * before it gets any posts.
+ *
+ * @uses Featured_Content::get_featured_post_ids();
+ * @param WP_Query $query
+ * @return WP_Query Possibly modified WP_query
+ */
+ public static function pre_get_posts( $query = false ) {
+
+ // Bail if not home, not a query, not main query.
+ if ( ! is_home() || ! is_a( $query, 'WP_Query' ) || ! $query->is_main_query() )
+ return;
+
+ $page_on_front = get_option( 'page_on_front' );
+
+ // Bail if the blog page is not the front page.
+ if ( ! empty( $page_on_front ) )
+ return;
+
+ $featured = self::get_featured_post_ids();
+
+ // Bail if no featured posts.
+ if ( ! $featured )
+ return;
+
+ // We need to respect post ids already in the blacklist.
+ $post__not_in = $query->get( 'post__not_in' );
+
+ if ( ! empty( $post__not_in ) ) {
+ $featured = array_merge( (array) $post__not_in, $featured );
+ $featured = array_unique( $featured );
+ }
+
+ $query->set( 'post__not_in', $featured );
+ }
+
+ /**
+ * Reset tag option when the saved tag is deleted.
+ *
+ * It's important to mention that the transient needs
+ * to be deleted too. While it may not be obvious by
+ * looking at the function alone, the transient is
+ * deleted by Featured_Content::validate_settings().
+ *
+ * @param int $tag_id the term_id of the tag that has been deleted.
+ *
+ * Hooks in the "delete_post_tag" action.
+ * @see Featured_Content::validate_settings().
+ */
+ public static function delete_post_tag( $tag_id ) {
+ $settings = self::get_setting();
+
+ if ( empty( $settings['tag-id'] ) )
+ return;
+
+ if ( $tag_id != $settings['tag-id'] )
+ return;
+
+ $settings['tag-id'] = 0;
+ $settings = self::validate_settings( $settings );
+ update_option( 'featured-content', $settings );
+ }
+
+ /**
+ * Hide featured tag from displaying when global terms are queried from the front-end.
+ *
+ * Hooks into the "get_terms" filter.
+ *
+ * @param array $terms A list of term objects. This is the return value of get_terms().
+ * @param array $taxonomies An array of taxonomy slugs.
+ *
+ * @uses Featured_Content::get_setting()
+ */
+ public static function hide_featured_term( $terms, $taxonomies ) {
+
+ // This filter is only appropriate on the front-end.
+ if ( is_admin() )
+ return $terms;
+
+ // We only want to hide the featured tag.
+ if ( ! in_array( 'post_tag', $taxonomies ) )
+ return $terms;
+
+ // Bail if no terms were returned.
+ if ( empty( $terms ) )
+ return $terms;
+
+ foreach( $terms as $order => $term ) {
+ if ( self::get_setting( 'tag-id' ) == $term->term_id && 'post_tag' == $term->taxonomy )
+ unset( $terms[$order] );
+ }
+
+ return $terms;
+ }
+
+ /**
+ * Hide featured tag from displaying when terms associated with
+ * a post object are queried from the front-end.
+ *
+ * Hooks into the "get_the_terms" filter.
+ *
+ * @param array $terms A list of term objects. This is the return value of get_the_terms().
+ * @param int $id The ID field for the post object that terms are associated with.
+ * @param array $taxonomy An array of taxonomy slugs.
+ *
+ * @uses Featured_Content::get_setting()
+ */
+ public static function hide_the_featured_term( $terms, $id, $taxonomy ) {
+
+ // This filter is only appropriate on the front-end.
+ if ( is_admin() )
+ return $terms;
+
+ // Make sure we are in the correct taxonomy.
+ if ( ! 'post_tag' == $taxonomy )
+ return $terms;
+
+ // No terms? Return early!
+ if ( empty( $terms ) )
+ return $terms;
+
+ foreach( $terms as $order => $term ) {
+ if ( self::get_setting( 'tag-id' ) == $term->term_id )
+ unset( $terms[$term->term_id] );
+ }
+
+ return $terms;
+ }
+
+ /**
+ * Register custom setting on the Settings -> Reading screen.
+ *
+ * @uses Featured_Content::render_form()
+ * @uses Featured_Content::validate_settings()
+ */
+ public static function register_setting() {
+ add_settings_field( 'featured-content', __( 'Featured content', 'jetpack' ), array( __class__, 'render_form' ), 'reading' );
+ register_setting( 'reading', 'featured-content', array( __class__, 'validate_settings' ) );
+ }
+
+ /**
+ * Renders all form fields on the Settings -> Reading screen.
+ */
+ public static function render_form() {
+ $settings = self::get_setting();
+
+ $tag_name = '';
+ if ( ! empty( $settings['tag-id'] ) ) {
+ $tag = get_term( $settings['tag-id'], 'post_tag' );
+ if ( ! is_wp_error( $tag ) && isset( $tag->name ) )
+ $tag_name = $tag->name;
+ }
+ ?>
+ <div id="featured-content-ui">
+ <p>
+ <label for="featured-content-tag-name"><?php echo _e( 'Tag name:', 'jetpack' ); ?></label>
+ <input type="text" id="featured-content-tag-name" name="featured-content[tag-name]" value="<?php echo esc_attr( $tag_name ); ?>">
+ <input type="hidden" id="featured-content-tag-id" name="featured-content[tag-id]" value="<?php echo esc_attr( $settings['tag-id'] ); ?>">
+ </p>
+ <p>
+ <label for="featured-content-quantity"><?php _e( 'Number of posts:', 'jetpack' ); ?></label>
+ <input class="small-text" type="number" step="1" min="1" max="<?php echo esc_attr( self::$max_posts ); ?>" id="featured-content-quantity" name="featured-content[quantity]" value="<?php echo esc_attr( $settings['quantity'] ); ?>">
+ </p>
+ <p>
+ <input type="checkbox" id="featured-content-hide-tag" name="featured-content[hide-tag]" <?php checked( $settings['hide-tag'], 1 ); ?>">
+ <label for="featured-content-hide-tag"><?php _e( 'Hide tag from displaying in post meta and tag clouds.', 'jetpack' ); ?></label>
+ </p>
+ </div>
+ <?php
+ }
+
+ /**
+ * Get Settings.
+ *
+ * Get all settings recognized by this module. This
+ * function will return all settings whether or not
+ * they have been stored in the database yet. This
+ * ensures that all keys are available at all times.
+ *
+ * In the event that you only require one setting, you
+ * may pass its name as the first parameter to the function
+ * and only that value will be returned.
+ *
+ * @uses Featured_Content::self::sanitize_quantity()
+ *
+ * @param string $key The key of a recognized setting.
+ * @return mixed Array of all settings by default. A single value if passed as first parameter.
+ */
+ public static function get_setting( $key = 'all' ) {
+ $saved = (array) get_option( 'featured-content' );
+
+ $defaults = array(
+ 'hide-tag' => 1,
+ 'quantity' => 5,
+ 'tag-id' => 0,
+ );
+
+ $options = wp_parse_args( $saved, $defaults );
+ $options = array_intersect_key( $options, $defaults );
+ $options['quantity'] = self::sanitize_quantity( $options['quantity'] );
+
+ if ( 'all' != $key ) {
+ if ( isset( $options[$key] ) )
+ return $options[$key];
+ else
+ return false;
+ }
+
+ return $options;
+ }
+
+ /**
+ * Validate Settings.
+ *
+ * Make sure that all user supplied content is in an
+ * expected format before saving to the database. This
+ * function will also delete the transient set in
+ * Featured_Content::get_featured_content().
+ *
+ * @uses Featured_Content::self::sanitize_quantity()
+ * @uses Featured_Content::self::delete_transient()
+ */
+ function validate_settings( $input ) {
+ $output = array();
+
+ if ( isset( $input['tag-id'] ) )
+ $output['tag-id'] = absint( $input['tag-id'] );
+
+ if ( isset( $input['tag-name'] ) ) {
+ $new_tag = wp_create_tag( $input['tag-name'] );
+ if ( ! is_wp_error( $new_tag ) && isset( $new_tag['term_id'] ) )
+ $tag = get_term( $new_tag['term_id'], 'post_tag' );
+ if ( isset( $tag->term_id ) )
+ $output['tag-id'] = $tag->term_id;
+ }
+
+ if ( isset( $input['quantity'] ) )
+ $output['quantity'] = self::sanitize_quantity( $input['quantity'] );
+
+ $output['hide-tag'] = ( isset( $input['hide-tag'] ) ) ? 1 : 0;
+
+ self::delete_transient();
+
+ return $output;
+ }
+
+ /**
+ * Sanitize Quantity
+ *
+ * @param int $input The value to sanitize.
+ * @output int A number between 1 and FeaturedContent::$max_posts.
+ *
+ * @uses Featured_Content::$max_posts
+ */
+ public static function sanitize_quantity( $input ) {
+ $quantity = absint( $input );
+
+ if ( $quantity > self::$max_posts )
+ $quantity = self::$max_posts;
+ else if ( 1 > $quantity )
+ $quantity = 1;
+
+ return $quantity;
+ }
+}
+
+Featured_Content::setup();
diff --git a/plugins/jetpack/modules/gravatar-hovercards.php b/plugins/jetpack/modules/gravatar-hovercards.php
index 3678837f..9edd15a5 100644
--- a/plugins/jetpack/modules/gravatar-hovercards.php
+++ b/plugins/jetpack/modules/gravatar-hovercards.php
@@ -6,13 +6,13 @@
* First Introduced: 1.1
*/
-define( 'GROFILES__CACHE_BUSTER', 'aa' ); // Break CDN cache, increment when gravatar.com/js/gprofiles.js changes
+define( 'GROFILES__CACHE_BUSTER', gmdate( 'YM' ) . 'aa' ); // Break CDN cache, increment when gravatar.com/js/gprofiles.js changes
function grofiles_hovercards_init() {
- add_filter( 'get_avatar', 'grofiles_get_avatar', 10, 2 );
- add_action( 'wp_footer', 'grofiles_attach_cards' );
- add_action( 'wp_footer', 'grofiles_extra_data' );
- add_action( 'admin_init', 'grofiles_add_settings' );
+ add_filter( 'get_avatar', 'grofiles_get_avatar', 10, 2 );
+ add_action( 'wp_enqueue_scripts', 'grofiles_attach_cards' );
+ add_action( 'wp_footer', 'grofiles_extra_data' );
+ add_action( 'admin_init', 'grofiles_add_settings' );
add_action( 'load-index.php', 'grofiles_admin_cards' );
add_action( 'load-users.php', 'grofiles_admin_cards' );
@@ -141,7 +141,7 @@ function grofiles_get_avatar( $avatar, $author ) {
if ( false !== strpos( $author, '@' ) ) {
grofiles_gravatars_to_append( $author );
} else {
- if ( $user = get_userdatabylogin( $author ) )
+ if ( $user = get_user_by( 'slug', $author ) )
grofiles_gravatars_to_append( $user->ID );
}
} else if ( isset( $author->comment_type ) ) {
@@ -171,8 +171,8 @@ function grofiles_attach_cards() {
if ( 'disabled' == get_option( 'gravatar_disable_hovercards' ) )
return;
- wp_enqueue_script( 'grofiles-cards', ( is_ssl() ? 'https://secure' : 'http://s' ) . '.gravatar.com/js/gprofiles.js?' . GROFILES__CACHE_BUSTER, array( 'jquery' ) );
- wp_enqueue_script( 'wpgroho', plugins_url( 'wpgroho.js', __FILE__ ), array( 'grofiles-cards' ) );
+ wp_enqueue_script( 'grofiles-cards', ( is_ssl() ? 'https://secure' : 'http://s' ) . '.gravatar.com/js/gprofiles.js', array( 'jquery' ), GROFILES__CACHE_BUSTER, true );
+ wp_enqueue_script( 'wpgroho', plugins_url( 'wpgroho.js', __FILE__ ), array( 'grofiles-cards' ), false, true );
if ( is_user_logged_in() ) {
$cu = wp_get_current_user();
$my_hash = md5( $cu->user_email );
@@ -182,7 +182,6 @@ function grofiles_attach_cards() {
$my_hash = '';
}
wp_localize_script( 'wpgroho', 'WPGroHo', compact( 'my_hash' ) );
- wp_print_scripts( 'wpgroho' );
}
function grofiles_attach_cards_forced() {
diff --git a/plugins/jetpack/modules/holiday-snow.php b/plugins/jetpack/modules/holiday-snow.php
new file mode 100644
index 00000000..93e3f9cf
--- /dev/null
+++ b/plugins/jetpack/modules/holiday-snow.php
@@ -0,0 +1,72 @@
+<?php
+
+/**
+ * Holiday Snow
+ * Adds falling snow to a blog starting December 1 and ending January 3.
+ * Not a module that is activated/deactivated
+ * First Introduced: 2.0.3 ??
+ */
+
+class Jetpack_Holiday_Snow_Settings {
+ function __construct() {
+ add_filter( 'admin_init' , array( &$this , 'register_fields' ) );
+ }
+
+ public function register_fields() {
+ register_setting( 'general', jetpack_holiday_snow_option_name(), 'esc_attr' );
+ add_settings_field( jetpack_holiday_snow_option_name(), '<label for="' . esc_attr( jetpack_holiday_snow_option_name() ) . '">' . __( 'Snow' , 'jetpack') . '</label>' , array( &$this, 'blog_field_html' ) , 'general' );
+ add_action( 'update_option_' . jetpack_holiday_snow_option_name(), array( &$this, 'holiday_snow_option_updated' ) );
+ }
+
+ public function blog_field_html() {
+ $id = esc_attr( jetpack_holiday_snow_option_name() );
+ ?>
+ <label for="<?php echo $id; ?>">
+ <input type="checkbox" name="<?php echo $id; ?>" id="<?php echo $id; ?>" value="letitsnow"<?php checked( get_option( jetpack_holiday_snow_option_name() ), 'letitsnow' ); ?> />
+ <span><?php _e( 'Show falling snow on my blog until January 4<sup>th</sup>.' , 'jetpack'); ?></span>
+ </label>
+ <?php
+ }
+
+ public function holiday_snow_option_updated() {
+ do_action( 'jetpack_holiday_snow_option_updated' );
+ }
+}
+
+function jetpack_holiday_snow_script() {
+ if ( ! apply_filters( 'jetpack_holiday_chance_of_snow', true ) )
+ return;
+
+ do_action( 'jetpack_holiday_snowing' );
+
+ $snowstorm_url = apply_filters( 'jetpack_holiday_snow_js_url', plugins_url( 'holiday-snow/snowstorm.js', __FILE__ ) );
+ wp_enqueue_script( 'snowstorm', $snowstorm_url, array(), '1.43.20111201' );
+}
+
+function jetpack_maybe_holiday_snow() {
+ if ( ! jetpack_is_holiday_snow_season() )
+ return;
+
+ if ( is_admin() ) {
+ global $jetpack_holiday_snow;
+ $jetpack_holiday_snow = new Jetpack_Holiday_Snow_Settings();
+ } elseif ( get_option( jetpack_holiday_snow_option_name() ) ) {
+ add_action( 'init', 'jetpack_holiday_snow_script' );
+ }
+}
+
+function jetpack_holiday_snow_option_name() {
+ return apply_filters( 'jetpack_holiday_snow_option_name', 'jetpack_holiday_snow_enabled' );
+}
+
+function jetpack_is_holiday_snow_season() {
+ $today = time();
+ $first_snow_day = mktime( 0, 0, 0, 12, 1 );
+ $last_snow_day = mktime( 0, 0, 0, 1, 4 );
+
+ $snow = ( $today >= $first_snow_day || $today < $last_snow_day );
+
+ return apply_filters( 'jetpack_is_holiday_snow_season', $snow );
+}
+
+jetpack_maybe_holiday_snow();
diff --git a/plugins/jetpack/modules/holiday-snow/snowstorm.js b/plugins/jetpack/modules/holiday-snow/snowstorm.js
new file mode 100644
index 00000000..96fd609d
--- /dev/null
+++ b/plugins/jetpack/modules/holiday-snow/snowstorm.js
@@ -0,0 +1,539 @@
+/** @license
+ * DHTML Snowstorm! JavaScript-based Snow for web pages
+ * --------------------------------------------------------
+ * Version 1.43.20111201 (Previous rev: 1.42.20111120)
+ * Copyright (c) 2007, Scott Schiller. All rights reserved.
+ * Code provided under the BSD License:
+ * http://schillmania.com/projects/snowstorm/license.txt
+ */
+
+/*global window, document, navigator, clearInterval, setInterval */
+/*jslint white: false, onevar: true, plusplus: false, undef: true, nomen: true, eqeqeq: true, bitwise: true, regexp: true, newcap: true, immed: true */
+
+var snowStorm = (function(window, document) {
+
+ // --- common properties ---
+
+ this.autoStart = true; // Whether the snow should start automatically or not.
+ this.flakesMax = 60; // Limit total amount of snow made (falling + sticking)
+ this.flakesMaxActive = 60; // Limit amount of snow falling at once (less = lower CPU use)
+ this.animationInterval = 40; // Theoretical "miliseconds per frame" measurement. 20 = fast + smooth, but high CPU use. 50 = more conservative, but slower
+ this.excludeMobile = true; // Snow is likely to be bad news for mobile phones' CPUs (and batteries.) By default, be nice.
+ this.flakeBottom = null; // Integer for Y axis snow limit, 0 or null for "full-screen" snow effect
+ this.followMouse = true; // Snow movement can respond to the user's mouse
+ this.snowColor = '#fff'; // Don't eat (or use?) yellow snow.
+ this.snowCharacter = '&bull;'; // &bull; = bullet, &middot; is square on some systems etc.
+ this.snowStick = false; // Whether or not snow should "stick" at the bottom. When off, will never collect.
+ this.targetElement = null; // element which snow will be appended to (null = document.body) - can be an element ID eg. 'myDiv', or a DOM node reference
+ this.useMeltEffect = true; // When recycling fallen snow (or rarely, when falling), have it "melt" and fade out if browser supports it
+ this.useTwinkleEffect = false; // Allow snow to randomly "flicker" in and out of view while falling
+ this.usePositionFixed = false; // true = snow does not shift vertically when scrolling. May increase CPU load, disabled by default - if enabled, used only where supported
+
+ // --- less-used bits ---
+
+ this.freezeOnBlur = true; // Only snow when the window is in focus (foreground.) Saves CPU.
+ this.flakeLeftOffset = 0; // Left margin/gutter space on edge of container (eg. browser window.) Bump up these values if seeing horizontal scrollbars.
+ this.flakeRightOffset = 0; // Right margin/gutter space on edge of container
+ this.flakeWidth = 5; // Max pixel width reserved for snow element
+ this.flakeHeight = 5; // Max pixel height reserved for snow element
+ this.vMaxX = 2.5; // Maximum X velocity range for snow
+ this.vMaxY = 2.5; // Maximum Y velocity range for snow
+ this.zIndex = 100000; // CSS stacking order applied to each snowflake
+
+ // --- End of user section ---
+
+ var s = this, storm = this, i,
+ // UA sniffing and backCompat rendering mode checks for fixed position, etc.
+ isIE = navigator.userAgent.match(/msie/i),
+ isIE6 = navigator.userAgent.match(/msie 6/i),
+ isWin98 = navigator.appVersion.match(/windows 98/i),
+ isMobile = navigator.userAgent.match(/mobile|opera m(ob|in)/i),
+ isBackCompatIE = (isIE && document.compatMode === 'BackCompat'),
+ noFixed = (isMobile || isBackCompatIE || isIE6),
+ screenX = null, screenX2 = null, screenY = null, scrollY = null, vRndX = null, vRndY = null,
+ windOffset = 1,
+ windMultiplier = 2,
+ flakeTypes = 6,
+ fixedForEverything = false,
+ opacitySupported = (function(){
+ try {
+ document.createElement('div').style.opacity = '0.5';
+ } catch(e) {
+ return false;
+ }
+ return true;
+ }()),
+ didInit = false,
+ docFrag = document.createDocumentFragment();
+
+ this.timers = [];
+ this.flakes = [];
+ this.disabled = false;
+ this.active = false;
+ this.meltFrameCount = 20;
+ this.meltFrames = [];
+
+ this.events = (function() {
+
+ var old = (!window.addEventListener && window.attachEvent), slice = Array.prototype.slice,
+ evt = {
+ add: (old?'attachEvent':'addEventListener'),
+ remove: (old?'detachEvent':'removeEventListener')
+ };
+
+ function getArgs(oArgs) {
+ var args = slice.call(oArgs), len = args.length;
+ if (old) {
+ args[1] = 'on' + args[1]; // prefix
+ if (len > 3) {
+ args.pop(); // no capture
+ }
+ } else if (len === 3) {
+ args.push(false);
+ }
+ return args;
+ }
+
+ function apply(args, sType) {
+ var element = args.shift(),
+ method = [evt[sType]];
+ if (old) {
+ element[method](args[0], args[1]);
+ } else {
+ element[method].apply(element, args);
+ }
+ }
+
+ function addEvent() {
+ apply(getArgs(arguments), 'add');
+ }
+
+ function removeEvent() {
+ apply(getArgs(arguments), 'remove');
+ }
+
+ return {
+ add: addEvent,
+ remove: removeEvent
+ };
+
+ }());
+
+ function rnd(n,min) {
+ if (isNaN(min)) {
+ min = 0;
+ }
+ return (Math.random()*n)+min;
+ }
+
+ function plusMinus(n) {
+ return (parseInt(rnd(2),10)===1?n*-1:n);
+ }
+
+ this.randomizeWind = function() {
+ var i;
+ vRndX = plusMinus(rnd(s.vMaxX,0.2));
+ vRndY = rnd(s.vMaxY,0.2);
+ if (this.flakes) {
+ for (i=0; i<this.flakes.length; i++) {
+ if (this.flakes[i].active) {
+ this.flakes[i].setVelocities();
+ }
+ }
+ }
+ };
+
+ this.scrollHandler = function() {
+ var i;
+ // "attach" snowflakes to bottom of window if no absolute bottom value was given
+ scrollY = (s.flakeBottom?0:parseInt(window.scrollY||document.documentElement.scrollTop||document.body.scrollTop,10));
+ if (isNaN(scrollY)) {
+ scrollY = 0; // Netscape 6 scroll fix
+ }
+ if (!fixedForEverything && !s.flakeBottom && s.flakes) {
+ for (i=s.flakes.length; i--;) {
+ if (s.flakes[i].active === 0) {
+ s.flakes[i].stick();
+ }
+ }
+ }
+ };
+
+ this.resizeHandler = function() {
+ if (window.innerWidth || window.innerHeight) {
+ screenX = window.innerWidth-16-s.flakeRightOffset;
+ screenY = (s.flakeBottom?s.flakeBottom:window.innerHeight);
+ } else {
+ screenX = (document.documentElement.clientWidth||document.body.clientWidth||document.body.scrollWidth)-(!isIE?8:0)-s.flakeRightOffset;
+ screenY = s.flakeBottom?s.flakeBottom:(document.documentElement.clientHeight||document.body.clientHeight||document.body.scrollHeight);
+ }
+ screenX2 = parseInt(screenX/2,10);
+ };
+
+ this.resizeHandlerAlt = function() {
+ screenX = s.targetElement.offsetLeft+s.targetElement.offsetWidth-s.flakeRightOffset;
+ screenY = s.flakeBottom?s.flakeBottom:s.targetElement.offsetTop+s.targetElement.offsetHeight;
+ screenX2 = parseInt(screenX/2,10);
+ };
+
+ this.freeze = function() {
+ // pause animation
+ var i;
+ if (!s.disabled) {
+ s.disabled = 1;
+ } else {
+ return false;
+ }
+ for (i=s.timers.length; i--;) {
+ clearInterval(s.timers[i]);
+ }
+ };
+
+ this.resume = function() {
+ if (s.disabled) {
+ s.disabled = 0;
+ } else {
+ return false;
+ }
+ s.timerInit();
+ };
+
+ this.toggleSnow = function() {
+ if (!s.flakes.length) {
+ // first run
+ s.start();
+ } else {
+ s.active = !s.active;
+ if (s.active) {
+ s.show();
+ s.resume();
+ } else {
+ s.stop();
+ s.freeze();
+ }
+ }
+ };
+
+ this.stop = function() {
+ var i;
+ this.freeze();
+ for (i=this.flakes.length; i--;) {
+ this.flakes[i].o.style.display = 'none';
+ }
+ s.events.remove(window,'scroll',s.scrollHandler);
+ s.events.remove(window,'resize',s.resizeHandler);
+ if (s.freezeOnBlur) {
+ if (isIE) {
+ s.events.remove(document,'focusout',s.freeze);
+ s.events.remove(document,'focusin',s.resume);
+ } else {
+ s.events.remove(window,'blur',s.freeze);
+ s.events.remove(window,'focus',s.resume);
+ }
+ }
+ };
+
+ this.show = function() {
+ var i;
+ for (i=this.flakes.length; i--;) {
+ this.flakes[i].o.style.display = 'block';
+ }
+ };
+
+ this.SnowFlake = function(parent,type,x,y) {
+ var s = this, storm = parent;
+ this.type = type;
+ this.x = x||parseInt(rnd(screenX-20),10);
+ this.y = (!isNaN(y)?y:-rnd(screenY)-12);
+ this.vX = null;
+ this.vY = null;
+ this.vAmpTypes = [1,1.2,1.4,1.6,1.8]; // "amplification" for vX/vY (based on flake size/type)
+ this.vAmp = this.vAmpTypes[this.type];
+ this.melting = false;
+ this.meltFrameCount = storm.meltFrameCount;
+ this.meltFrames = storm.meltFrames;
+ this.meltFrame = 0;
+ this.twinkleFrame = 0;
+ this.active = 1;
+ this.fontSize = (10+(this.type/5)*10);
+ this.o = document.createElement('div');
+ this.o.innerHTML = storm.snowCharacter;
+ this.o.style.color = storm.snowColor;
+ this.o.style.position = (fixedForEverything?'fixed':'absolute');
+ this.o.style.width = storm.flakeWidth+'px';
+ this.o.style.height = storm.flakeHeight+'px';
+ this.o.style.fontFamily = 'arial,verdana';
+ this.o.style.cursor = 'default';
+ this.o.style.overflow = 'hidden';
+ this.o.style.fontWeight = 'normal';
+ this.o.style.zIndex = storm.zIndex;
+ docFrag.appendChild(this.o);
+
+ this.refresh = function() {
+ if (isNaN(s.x) || isNaN(s.y)) {
+ // safety check
+ return false;
+ }
+ s.o.style.left = s.x+'px';
+ s.o.style.top = s.y+'px';
+ };
+
+ this.stick = function() {
+ if (noFixed || (storm.targetElement !== document.documentElement && storm.targetElement !== document.body)) {
+ s.o.style.top = (screenY+scrollY-storm.flakeHeight)+'px';
+ } else if (storm.flakeBottom) {
+ s.o.style.top = storm.flakeBottom+'px';
+ } else {
+ s.o.style.display = 'none';
+ s.o.style.top = 'auto';
+ s.o.style.bottom = '0px';
+ s.o.style.position = 'fixed';
+ s.o.style.display = 'block';
+ }
+ };
+
+ this.vCheck = function() {
+ if (s.vX>=0 && s.vX<0.2) {
+ s.vX = 0.2;
+ } else if (s.vX<0 && s.vX>-0.2) {
+ s.vX = -0.2;
+ }
+ if (s.vY>=0 && s.vY<0.2) {
+ s.vY = 0.2;
+ }
+ };
+
+ this.move = function() {
+ var vX = s.vX*windOffset, yDiff;
+ s.x += vX;
+ s.y += (s.vY*s.vAmp);
+ if (s.x >= screenX || screenX-s.x < storm.flakeWidth) { // X-axis scroll check
+ s.x = 0;
+ } else if (vX < 0 && s.x-storm.flakeLeftOffset < -storm.flakeWidth) {
+ s.x = screenX-storm.flakeWidth-1; // flakeWidth;
+ }
+ s.refresh();
+ yDiff = screenY+scrollY-s.y;
+ if (yDiff<storm.flakeHeight) {
+ s.active = 0;
+ if (storm.snowStick) {
+ s.stick();
+ } else {
+ s.recycle();
+ }
+ } else {
+ if (storm.useMeltEffect && s.active && s.type < 3 && !s.melting && Math.random()>0.998) {
+ // ~1/1000 chance of melting mid-air, with each frame
+ s.melting = true;
+ s.melt();
+ // only incrementally melt one frame
+ // s.melting = false;
+ }
+ if (storm.useTwinkleEffect) {
+ if (!s.twinkleFrame) {
+ if (Math.random()>0.9) {
+ s.twinkleFrame = parseInt(Math.random()*20,10);
+ }
+ } else {
+ s.twinkleFrame--;
+ s.o.style.visibility = (s.twinkleFrame && s.twinkleFrame%2===0?'hidden':'visible');
+ }
+ }
+ }
+ };
+
+ this.animate = function() {
+ // main animation loop
+ // move, check status, die etc.
+ s.move();
+ };
+
+ this.setVelocities = function() {
+ s.vX = vRndX+rnd(storm.vMaxX*0.12,0.1);
+ s.vY = vRndY+rnd(storm.vMaxY*0.12,0.1);
+ };
+
+ this.setOpacity = function(o,opacity) {
+ if (!opacitySupported) {
+ return false;
+ }
+ o.style.opacity = opacity;
+ };
+
+ this.melt = function() {
+ if (!storm.useMeltEffect || !s.melting) {
+ s.recycle();
+ } else {
+ if (s.meltFrame < s.meltFrameCount) {
+ s.setOpacity(s.o,s.meltFrames[s.meltFrame]);
+ s.o.style.fontSize = s.fontSize-(s.fontSize*(s.meltFrame/s.meltFrameCount))+'px';
+ s.o.style.lineHeight = storm.flakeHeight+2+(storm.flakeHeight*0.75*(s.meltFrame/s.meltFrameCount))+'px';
+ s.meltFrame++;
+ } else {
+ s.recycle();
+ }
+ }
+ };
+
+ this.recycle = function() {
+ s.o.style.display = 'none';
+ s.o.style.position = (fixedForEverything?'fixed':'absolute');
+ s.o.style.bottom = 'auto';
+ s.setVelocities();
+ s.vCheck();
+ s.meltFrame = 0;
+ s.melting = false;
+ s.setOpacity(s.o,1);
+ s.o.style.padding = '0px';
+ s.o.style.margin = '0px';
+ s.o.style.fontSize = s.fontSize+'px';
+ s.o.style.lineHeight = (storm.flakeHeight+2)+'px';
+ s.o.style.textAlign = 'center';
+ s.o.style.verticalAlign = 'baseline';
+ s.x = parseInt(rnd(screenX-storm.flakeWidth-20),10);
+ s.y = parseInt(rnd(screenY)*-1,10)-storm.flakeHeight;
+ s.refresh();
+ s.o.style.display = 'block';
+ s.active = 1;
+ };
+
+ this.recycle(); // set up x/y coords etc.
+ this.refresh();
+
+ };
+
+ this.snow = function() {
+ var active = 0, used = 0, waiting = 0, flake = null, i;
+ for (i=s.flakes.length; i--;) {
+ if (s.flakes[i].active === 1) {
+ s.flakes[i].move();
+ active++;
+ } else if (s.flakes[i].active === 0) {
+ used++;
+ } else {
+ waiting++;
+ }
+ if (s.flakes[i].melting) {
+ s.flakes[i].melt();
+ }
+ }
+ if (active<s.flakesMaxActive) {
+ flake = s.flakes[parseInt(rnd(s.flakes.length),10)];
+ if (flake.active === 0) {
+ flake.melting = true;
+ }
+ }
+ };
+
+ this.mouseMove = function(e) {
+ if (!s.followMouse) {
+ return true;
+ }
+ var x = parseInt(e.clientX,10);
+ if (x<screenX2) {
+ windOffset = -windMultiplier+(x/screenX2*windMultiplier);
+ } else {
+ x -= screenX2;
+ windOffset = (x/screenX2)*windMultiplier;
+ }
+ };
+
+ this.createSnow = function(limit,allowInactive) {
+ var i;
+ for (i=0; i<limit; i++) {
+ s.flakes[s.flakes.length] = new s.SnowFlake(s,parseInt(rnd(flakeTypes),10));
+ if (allowInactive || i>s.flakesMaxActive) {
+ s.flakes[s.flakes.length-1].active = -1;
+ }
+ }
+ storm.targetElement.appendChild(docFrag);
+ };
+
+ this.timerInit = function() {
+ s.timers = (!isWin98?[setInterval(s.snow,s.animationInterval)]:[setInterval(s.snow,s.animationInterval*3),setInterval(s.snow,s.animationInterval)]);
+ };
+
+ this.init = function() {
+ var i;
+ for (i=0; i<s.meltFrameCount; i++) {
+ s.meltFrames.push(1-(i/s.meltFrameCount));
+ }
+ s.randomizeWind();
+ s.createSnow(s.flakesMax); // create initial batch
+ s.events.add(window,'resize',s.resizeHandler);
+ s.events.add(window,'scroll',s.scrollHandler);
+ if (s.freezeOnBlur) {
+ if (isIE) {
+ s.events.add(document,'focusout',s.freeze);
+ s.events.add(document,'focusin',s.resume);
+ } else {
+ s.events.add(window,'blur',s.freeze);
+ s.events.add(window,'focus',s.resume);
+ }
+ }
+ s.resizeHandler();
+ s.scrollHandler();
+ if (s.followMouse) {
+ s.events.add(isIE?document:window,'mousemove',s.mouseMove);
+ }
+ s.animationInterval = Math.max(20,s.animationInterval);
+ s.timerInit();
+ };
+
+ this.start = function(bFromOnLoad) {
+ if (!didInit) {
+ didInit = true;
+ } else if (bFromOnLoad) {
+ // already loaded and running
+ return true;
+ }
+ if (typeof s.targetElement === 'string') {
+ var targetID = s.targetElement;
+ s.targetElement = document.getElementById(targetID);
+ if (!s.targetElement) {
+ throw new Error('Snowstorm: Unable to get targetElement "'+targetID+'"');
+ }
+ }
+ if (!s.targetElement) {
+ s.targetElement = (!isIE?(document.documentElement?document.documentElement:document.body):document.body);
+ }
+ if (s.targetElement !== document.documentElement && s.targetElement !== document.body) {
+ s.resizeHandler = s.resizeHandlerAlt; // re-map handler to get element instead of screen dimensions
+ }
+ s.resizeHandler(); // get bounding box elements
+ s.usePositionFixed = (s.usePositionFixed && !noFixed); // whether or not position:fixed is supported
+ fixedForEverything = s.usePositionFixed;
+ if (screenX && screenY && !s.disabled) {
+ s.init();
+ s.active = true;
+ }
+ };
+
+ function doDelayedStart() {
+ window.setTimeout(function() {
+ s.start(true);
+ }, 20);
+ // event cleanup
+ s.events.remove(isIE?document:window,'mousemove',doDelayedStart);
+ }
+
+ function doStart() {
+ if (!s.excludeMobile || !isMobile) {
+ if (s.freezeOnBlur) {
+ s.events.add(isIE?document:window,'mousemove',doDelayedStart);
+ } else {
+ doDelayedStart();
+ }
+ }
+ // event cleanup
+ s.events.remove(window, 'load', doStart);
+ }
+
+ // hooks for starting the snow
+ if (s.autoStart) {
+ s.events.add(window, 'load', doStart, false);
+ }
+
+ return this;
+
+}(window, document)); \ No newline at end of file
diff --git a/plugins/jetpack/modules/infinite-scroll.php b/plugins/jetpack/modules/infinite-scroll.php
new file mode 100644
index 00000000..857ac6e0
--- /dev/null
+++ b/plugins/jetpack/modules/infinite-scroll.php
@@ -0,0 +1,196 @@
+<?php
+/**
+ * Module Name: Infinite Scroll
+ * Module Description: Automatically pull the next set of posts into view when the reader approaches the bottom of the page.
+ * Sort Order: 14
+ * First Introduced: 2.0
+ */
+
+/**
+ * Jetpack-specific elements of Infinite Scroll
+ */
+class Jetpack_Infinite_Scroll_Extras {
+ /**
+ * Class variables
+ */
+ // Oh look, a singleton
+ private static $__instance = null;
+
+ // Option names
+ private $option_name_google_analytics = 'infinite_scroll_google_analytics';
+
+ /**
+ * Singleton implementation
+ *
+ * @return object
+ */
+ public static function instance() {
+ if ( ! is_a( self::$__instance, 'Jetpack_Infinite_Scroll_Extras' ) )
+ self::$__instance = new Jetpack_Infinite_Scroll_Extras;
+
+ return self::$__instance;
+ }
+
+ /**
+ * Register actions and filters
+ *
+ * @uses add_action, add_filter
+ * @return null
+ */
+ private function __construct() {
+ add_action( 'jetpack_modules_loaded', array( $this, 'action_jetpack_modules_loaded' ) );
+
+ add_action( 'admin_init', array( $this, 'action_admin_init' ), 11 );
+
+ add_action( 'after_setup_theme', array( $this, 'action_after_setup_theme' ), 5 );
+
+ add_filter( 'infinite_scroll_js_settings', array( $this, 'filter_infinite_scroll_js_settings' ) );
+
+ add_action( 'wp_enqueue_scripts', array( $this, 'action_wp_enqueue_scripts' ) );
+ }
+
+ /**
+ * Enable "Configure" button on module card
+ *
+ * @uses Jetpack::enable_module_configurable, Jetpack::module_configuration_load
+ * @action jetpack_modules_loaded
+ * @return null
+ */
+ public function action_jetpack_modules_loaded() {
+ Jetpack::enable_module_configurable( __FILE__ );
+ Jetpack::module_configuration_load( __FILE__, array( $this, 'module_configuration_load' ) );
+ }
+
+ /**
+ * Redirect configure button to Settings > Reading
+ *
+ * @uses wp_safe_redirect, admin_url
+ * @return null
+ */
+ public function module_configuration_load() {
+ wp_safe_redirect( admin_url( 'options-reading.php#infinite-scroll-options' ) );
+ exit;
+ }
+
+ /**
+ * Register Google Analytics setting
+ *
+ * @uses add_settings_field, __, register_setting
+ * @action admin_init
+ * @return null
+ */
+ public function action_admin_init() {
+ add_settings_field( $this->option_name_google_analytics, '<span id="infinite-scroll-google-analytics">' . __( 'Use Google Analytics with Infinite Scroll', 'jetpack' ) . '</span>', array( $this, 'setting_google_analytics' ), 'reading' );
+ register_setting( 'reading', $this->option_name_google_analytics, array( $this, 'sanitize_boolean_value' ) );
+ }
+
+ /**
+ * Render Google Analytics option
+ *
+ * @uses checked, get_option, __
+ * @return html
+ */
+ public function setting_google_analytics() {
+ echo '<label><input name="infinite_scroll_google_analytics" type="checkbox" value="1" ' . checked( true, (bool) get_option( $this->option_name_google_analytics, false ), false ) . ' /> ' . __( 'Track each Infinite Scroll post load as a page view in Google Analytics', 'jetpack' ) . '</br><small>' . __( 'By checking the box above, each new set of posts loaded via Infinite Scroll will be recorded as a page view in Google Analytics.', 'jetpack' ) . '</small>' . '</label>';
+ }
+
+ /**
+ * Sanitize value as a boolean
+ *
+ * @param mixed $value
+ * @return bool
+ */
+ public function sanitize_boolean_value( $value ) {
+ return (bool) $value;
+ }
+
+ /**
+ * 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 action_after_setup_theme() {
+ $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 );
+ }
+ }
+
+ /**
+ * 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
+ * @filter infinite_scroll_js_settings
+ * @return array
+ */
+ public function filter_infinite_scroll_js_settings( $settings ) {
+ // Provide WP Stats info for tracking Infinite Scroll loads
+ // Abort if Stats module isn't active
+ if ( in_array( 'stats', Jetpack::get_active_modules() ) ) {
+ // Abort if user is logged in but logged-in users shouldn't be tracked.
+ if ( is_user_logged_in() ) {
+ $stats_options = stats_get_options();
+ $track_loggedin_users = isset( $stats_options['reg_users'] ) ? (bool) $stats_options['reg_users'] : false;
+
+ if ( ! $track_loggedin_users )
+ return $settings;
+ }
+
+ // 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;
+
+ // Pagetype parameter
+ $settings['stats'] .= '&x_pagetype=infinite';
+ if ( 'click' == $settings['type'] )
+ $settings['stats'] .= '-click';
+
+ $settings['stats'] .= '-jetpack';
+ }
+
+ // Check if Google Analytics tracking is requested
+ $settings['google_analytics'] = (bool) get_option( $this->option_name_google_analytics );
+
+ return $settings;
+ }
+
+ /**
+ * Load VideoPress scripts if plugin is active.
+ *
+ * @global $videopress
+ * @action wp_enqueue_scripts
+ * @return null
+ */
+ public function action_wp_enqueue_scripts() {
+ global $videopress;
+ if ( ! empty( $videopress ) && The_Neverending_Home_Page::archive_supports_infinity() && is_a( $videopress, 'VideoPress' ) && method_exists( $videopress, 'enqueue_scripts' ) )
+ $videopress->enqueue_scripts();
+ }
+}
+Jetpack_Infinite_Scroll_Extras::instance();
+
+/**
+ * Load main IS file
+ */
+require_once( dirname( __FILE__ ) . "/infinite-scroll/infinity.php" );
+
+/**
+ * Remove the IS annotation loading function bundled with the IS plugin in favor of the Jetpack-specific version in Jetpack_Infinite_Scroll_Extras::action_after_setup_theme();
+ */
+remove_action( 'after_setup_theme', 'the_neverending_home_page_theme_support', 5 ); \ No newline at end of file
diff --git a/plugins/jetpack/modules/infinite-scroll/infinity.css b/plugins/jetpack/modules/infinite-scroll/infinity.css
new file mode 100644
index 00000000..dd39179e
--- /dev/null
+++ b/plugins/jetpack/modules/infinite-scroll/infinity.css
@@ -0,0 +1,137 @@
+/* =Infinity Styles
+-------------------------------------------------------------- */
+
+.infinite-wrap {
+/* border-top: 2px solid #444;
+ border-top: 2px solid rgba(68,68,68,0.8);
+ padding: 20px 0 0; */
+}
+.infinite-loader {
+ color: #000;
+ display: block;
+ height: 28px;
+ margin: 10px;
+ text-indent: -9999px;
+}
+#infinite-handle span {
+ background: #333;
+ border-radius: 1px;
+ color: #eee;
+ cursor: pointer;
+ font-size: 13px;
+ padding: 6px 16px;
+}
+
+/**
+ * For smaller viewports, remove the down-arrow icon and turn
+ * the button into a block element, spanning the content's full width.
+ */
+@media (max-width: 800px) {
+ #infinite-handle span:before {
+ display: none;
+ }
+ #infinite-handle span {
+ display: block;
+ }
+}
+
+/**
+ * Footer
+ */
+#infinite-footer {
+ position: fixed;
+ bottom: -50px;
+ left: 0;
+ width: 100%;
+}
+#infinite-footer a {
+ text-decoration: none;
+}
+#infinite-footer .blog-info a:hover,
+#infinite-footer .blog-credits a:hover {
+ color: #444;
+ text-decoration: underline;
+}
+#infinite-footer .container {
+ background: rgba( 255, 255, 255, 0.8 );
+ border-color: #ccc;
+ border-color: rgba( 0, 0, 0, 0.1 );
+ border-style: solid;
+ border-width: 1px 0 0;
+ box-sizing: border-box;
+ margin: 0 auto;
+ overflow: hidden;
+ padding: 1px 20px;
+ width: 640px;
+}
+#infinite-footer .blog-info,
+#infinite-footer .blog-credits {
+ -moz-box-sizing: border-box;
+ -webkit-box-sizing: border-box;
+ box-sizing: border-box;
+ line-height: 25px;
+}
+#infinite-footer .blog-info {
+ float: left;
+ overflow: hidden;
+ text-align: left;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+ width: 40%;
+}
+#infinite-footer .blog-credits {
+ font-weight: normal;
+ float: right;
+ width: 60%;
+}
+#infinite-footer .blog-info a {
+ color: #111;
+ font-size: 14px;
+ font-weight: bold;
+}
+#infinite-footer .blog-credits {
+ color: #888;
+ font-size: 12px;
+ text-align: right;
+}
+#infinite-footer .blog-credits a {
+ color: #666;
+}
+
+/**
+ * Hooks to infinity-end body class to restore footer
+ */
+.infinity-end.neverending #infinite-footer {
+ display: none;
+}
+
+/**
+ * Responsive structure for the footer
+ */
+@media (max-width: 640px) {
+ #infinite-footer .container {
+ -moz-box-sizing: border-box;
+ -webkit-box-sizing: border-box;
+ box-sizing: border-box;
+ width: 100%;
+ }
+ #infinite-footer .blog-info {
+ width: 30%;
+ }
+ #infinite-footer .blog-credits {
+ width: 70%;
+ }
+ #infinite-footer .blog-info a,
+ #infinite-footer .blog-credits {
+ font-size: 10px;
+ }
+}
+
+/**
+ * No fixed footer on small viewports
+ */
+@media ( max-width: 640px ) {
+ #infinite-footer {
+ position: static;
+ }
+} \ No newline at end of file
diff --git a/plugins/jetpack/modules/infinite-scroll/infinity.js b/plugins/jetpack/modules/infinite-scroll/infinity.js
new file mode 100644
index 00000000..7a76d8f2
--- /dev/null
+++ b/plugins/jetpack/modules/infinite-scroll/infinity.js
@@ -0,0 +1,490 @@
+(function($){ // Open closure
+
+// Local vars
+var Scroller, ajaxurl, stats, type, text, totop, timer;
+
+// IE requires special handling
+var isIE = ( -1 != navigator.userAgent.search( 'MSIE' ) );
+if ( isIE ) {
+ var IEVersion = navigator.userAgent.match(/MSIE\s?(\d+)\.?\d*;/);
+ var IEVersion = parseInt( IEVersion[1] );
+}
+
+/**
+ * Loads new posts when users scroll near the bottom of the page.
+ */
+Scroller = function( settings ) {
+ var self = this;
+
+ // Initialize our variables
+ this.id = settings.id;
+ this.body = $( document.body );
+ this.window = $( window );
+ this.element = $( '#' + settings.id );
+ this.wrapperClass = settings.wrapper_class;
+ this.ready = true;
+ this.disabled = false;
+ this.page = 1;
+ this.offset = settings.offset;
+ this.order = settings.order;
+ this.throttle = false;
+ this.handle = '<div id="infinite-handle"><span>' + text.replace( '\\', '' ) + '</span></div>';
+ this.google_analytics = settings.google_analytics;
+ this.history = settings.history;
+ this.origURL = window.location.href;
+
+ // Footer settings
+ this.footer = $( '#infinite-footer' );
+ this.footer.wrap = settings.footer;
+
+ // We have two type of infinite scroll
+ // cases 'scroll' and 'click'
+
+ if ( type == 'scroll' ) {
+ // Bind refresh to the scroll event
+ // Throttle to check for such case every 300ms
+
+ // On event the case becomes a fact
+ this.window.bind( 'scroll.infinity', function() {
+ this.throttle = true;
+ });
+
+ // Go back top method
+ self.gotop();
+
+ setInterval( function() {
+ if ( this.throttle ) {
+ // Once the case is the case, the action occurs and the fact is no more
+ this.throttle = false;
+ // Reveal or hide footer
+ self.thefooter();
+ // Fire the refresh
+ self.refresh();
+ }
+ }, 300 );
+
+ // Ensure that enough posts are loaded to fill the initial viewport, to compensate for short posts and large displays.
+ self.ensureFilledViewport();
+ this.body.bind( 'post-load', { self: self }, self.checkViewportOnLoad );
+ } else if ( type == 'click' ) {
+ this.element.append( self.handle );
+ this.element.delegate( '#infinite-handle', 'click.infinity', function() {
+ // Handle the handle
+ $( '#infinite-handle' ).remove();
+ // Fire the refresh
+ self.refresh();
+ });
+ }
+};
+
+/**
+ * Check whether we should fetch any additional posts.
+ *
+ * By default, checks whether the bottom of the viewport is within one
+ * viewport-height of the bottom of the content.
+ */
+Scroller.prototype.check = function() {
+ var bottom = this.window.scrollTop() + this.window.height(),
+ threshold = this.element.offset().top + this.element.outerHeight(false) - this.window.height();
+
+ return bottom > threshold;
+};
+
+/**
+ * Renders the results from a successful response.
+ */
+Scroller.prototype.render = function( response ) {
+ this.body.addClass( 'infinity-success' );
+
+ // Check if we can wrap the html
+ this.element.append( response.html );
+
+ this.body.trigger( 'post-load' );
+ this.ready = true;
+};
+
+/**
+ * Returns the object used to query for new posts.
+ */
+Scroller.prototype.query = function() {
+ return {
+ page: this.page,
+ order: this.order,
+ scripts: window.infiniteScroll.settings.scripts,
+ styles: window.infiniteScroll.settings.styles
+ };
+};
+
+/**
+ * Scroll back to top.
+ */
+Scroller.prototype.gotop = function() {
+ var blog = $( '#infinity-blog-title' );
+
+ blog.attr( 'title', totop );
+
+ // Scroll to top on blog title
+ blog.bind( 'click', function( e ) {
+ $( 'html, body' ).animate( { scrollTop: 0 }, 'fast' );
+ e.preventDefault();
+ });
+};
+
+
+/**
+ * The infinite footer.
+ */
+Scroller.prototype.thefooter = function() {
+ var self = this,
+ width;
+
+ // Check if we have an id for the page wrapper
+ if ( $.type( this.footer.wrap ) === "string" ) {
+ width = $( 'body #' + this.footer.wrap ).outerWidth( false );
+
+ // Make the footer match the width of the page
+ if ( width > 479 )
+ this.footer.find( '.container' ).css( 'width', width );
+ }
+
+ // Reveal footer
+ if ( this.window.scrollTop() >= 350 )
+ self.footer.animate( { 'bottom': 0 }, 'fast' );
+ else if ( this.window.scrollTop() < 350 )
+ self.footer.animate( { 'bottom': '-50px' }, 'fast' );
+};
+
+
+/**
+ * Controls the flow of the refresh. Don't mess.
+ */
+Scroller.prototype.refresh = function() {
+ var self = this,
+ query, jqxhr, load, loader, color;
+
+ // If we're disabled, ready, or don't pass the check, bail.
+ if ( this.disabled || ! this.ready || ! this.check() )
+ return;
+
+ // Let's get going -- set ready to false to prevent
+ // multiple refreshes from occurring at once.
+ this.ready = false;
+
+ // Create a loader element to show it's working.
+ loader = '<span class="infinite-loader"></span>';
+ this.element.append( loader );
+
+ loader = this.element.find( '.infinite-loader' );
+ color = loader.css( 'color' );
+
+ try {
+ loader.spin( 'medium-left', color );
+ } catch ( error ) { }
+
+ // Generate our query vars.
+ query = $.extend({
+ action: 'infinite_scroll'
+ }, this.query() );
+
+ // Fire the ajax request.
+ jqxhr = $.get( infiniteScroll.settings.ajaxurl, query );
+
+ // Allow refreshes to occur again if an error is triggered.
+ jqxhr.fail( function() {
+ loader.hide();
+ self.ready = true;
+ });
+
+ // Success handler
+ jqxhr.done( function( response ) {
+
+ // On success, let's hide the loader circle.
+ loader.hide();
+
+ // Check for and parse our response.
+ if ( ! response )
+ return;
+
+ response = $.parseJSON( response );
+
+ if ( ! response || ! response.type )
+ return;
+
+ // If there are no remaining posts...
+ if ( response.type == 'empty' ) {
+ // Disable the scroller.
+ self.disabled = true;
+ // Update body classes, allowing the footer to return to static positioning
+ self.body.addClass( 'infinity-end' ).removeClass( 'infinity-success' );
+
+ // If we've succeeded...
+ } else if ( response.type == 'success' ) {
+ // If additional scripts are required by the incoming set of posts, parse them
+ if ( response.scripts ) {
+ $( response.scripts ).each( function() {
+ // Add script handle to list of those already parsed
+ window.infiniteScroll.settings.scripts.push( this.handle );
+
+ // Output extra data, if present
+ if ( this.extra_data ) {
+ var data = document.createElement('script'),
+ dataContent = document.createTextNode( "//<![CDATA[ \n" + this.extra_data + "\n//]]>" );
+
+ data.type = 'text/javascript';
+ data.appendChild( dataContent );
+
+ document.getElementsByTagName( this.footer ? 'body' : 'head' )[0].appendChild(data);
+ }
+
+ // Build script tag and append to DOM in requested location
+ var script = document.createElement('script');
+ script.type = 'text/javascript';
+ script.src = this.src;
+ script.id = this.handle;
+ document.getElementsByTagName( this.footer ? 'body' : 'head' )[0].appendChild(script);
+ } );
+ }
+
+ // If additional stylesheets are required by the incoming set of posts, parse them
+ if ( response.styles ) {
+ $( response.styles ).each( function() {
+ // Add stylesheet handle to list of those already parsed
+ window.infiniteScroll.settings.styles.push( this.handle );
+
+ // Build link tag
+ var style = document.createElement('link');
+ style.rel = 'stylesheet';
+ style.href = this.src;
+ style.id = this.handle + '-css';
+
+ // Destroy link tag if a conditional statement is present and either the browser isn't IE, or the conditional doesn't evaluate true
+ if ( this.conditional && ( ! isIE || ! eval( this.conditional.replace( /%ver/g, IEVersion ) ) ) )
+ var style = false;
+
+ // Append link tag if necessary
+ if ( style )
+ document.getElementsByTagName('head')[0].appendChild(style);
+ } );
+ }
+
+ // Increment the page number
+ self.page++;
+
+ // Record pageview in WP Stats, if available.
+ if ( stats )
+ new Image().src = document.location.protocol + '//stats.wordpress.com/g.gif?' + stats + '&post=0&baba=' + Math.random();
+
+ // Add new posts to the postflair object
+ if ( 'object' == typeof response.postflair && 'object' == typeof WPCOM_sharing_counts )
+ WPCOM_sharing_counts = $.extend( WPCOM_sharing_counts, response.postflair );
+
+ // Render the results
+ self.render.apply( self, arguments );
+
+ // If 'click' type, add back the handle
+ if ( type == 'click' )
+ self.element.append( self.handle );
+
+ // Fire Google Analytics pageview
+ if ( self.google_analytics && 'object' == typeof _gaq )
+ _gaq.push(['_trackPageview', self.history.path.replace( /%d/, self.page ) ]);
+ }
+ });
+
+ return jqxhr;
+};
+
+/**
+ * Trigger IS to load additional posts if the initial posts don't fill the window.
+ * On large displays, or when posts are very short, the viewport may not be filled with posts, so we overcome this by loading additional posts when IS initializes.
+ */
+Scroller.prototype.ensureFilledViewport = function() {
+ var self = this,
+ windowHeight = self.window.height(),
+ postsHeight = self.element.height()
+ aveSetHeight = 0,
+ wrapperQty = 0;
+
+ // Account for situations where postsHeight is 0 because child list elements are floated
+ if ( postsHeight === 0 ) {
+ $( self.element.selector + ' > li' ).each( function() {
+ postsHeight += $( this ).height();
+ } );
+
+ if ( postsHeight === 0 ) {
+ self.body.unbind( 'post-load', self.checkViewportOnLoad );
+ return;
+ }
+ }
+
+ // Calculate average height of a set of posts to prevent more posts than needed from being loaded.
+ $( '.' + self.wrapperClass ).each( function() {
+ aveSetHeight += $( this ).height();
+ wrapperQty++;
+ } );
+
+ if ( wrapperQty > 0 )
+ aveSetHeight = aveSetHeight / wrapperQty;
+ else
+ aveSetHeight = 0;
+
+ // Load more posts if space permits, otherwise stop checking for a full viewport
+ if ( postsHeight < windowHeight && ( postsHeight + aveSetHeight < windowHeight ) ) {
+ self.ready = true;
+ self.refresh();
+ }
+ else {
+ self.body.unbind( 'post-load', self.checkViewportOnLoad );
+ }
+}
+
+/**
+ * Event handler for ensureFilledViewport(), tied to the post-load trigger.
+ * Necessary to ensure that the variable `this` contains the scroller when used in ensureFilledViewport(). Since this function is tied to an event, `this` becomes the DOM element the event is tied to.
+ */
+Scroller.prototype.checkViewportOnLoad = function( ev ) {
+ ev.data.self.ensureFilledViewport();
+}
+
+/**
+ * Identify archive page that corresponds to majority of posts shown in the current browser window.
+ */
+Scroller.prototype.determineURL = function () {
+ var self = window.infiniteScroll.scroller,
+ windowTop = $( window ).scrollTop(),
+ windowBottom = windowTop + $( window ).height(),
+ windowSize = windowBottom - windowTop,
+ setsInView = [],
+ pageNum = false;
+
+ // Find out which sets are in view
+ $( '.' + self.wrapperClass ).each( function() {
+ var id = $( this ).attr( 'id' ),
+ setTop = $( this ).offset().top,
+ setHeight = $( this ).outerHeight( false ),
+ setBottom = 0,
+ setPageNum = $( this ).data( 'page-num' );
+
+ // Account for containers that have no height because their children are floated elements.
+ if ( 0 == setHeight ) {
+ $( '> *', this ).each( function() {
+ setHeight += $( this ).outerHeight( false );
+ } );
+ }
+
+ // Determine position of bottom of set by adding its height to the scroll position of its top.
+ setBottom = setTop + setHeight;
+
+ // Populate setsInView object. While this logic could all be combined into a single conditional statement, this is easier to understand.
+ if ( setTop < windowTop && setBottom > windowBottom ) { // top of set is above window, bottom is below
+ setsInView.push({'id': id, 'top': setTop, 'bottom': setBottom, 'pageNum': setPageNum });
+ }
+ else if( setTop > windowTop && setTop < windowBottom ) { // top of set is between top (gt) and bottom (lt)
+ setsInView.push({'id': id, 'top': setTop, 'bottom': setBottom, 'pageNum': setPageNum });
+ }
+ else if( setBottom > windowTop && setBottom < windowBottom ) { // bottom of set is between top (gt) and bottom (lt)
+ setsInView.push({'id': id, 'top': setTop, 'bottom': setBottom, 'pageNum': setPageNum });
+ }
+ } );
+
+ // Parse number of sets found in view in an attempt to update the URL to match the set that comprises the majority of the window.
+ if ( 0 == setsInView.length ) {
+ pageNum = -1;
+ }
+ else if ( 1 == setsInView.length ) {
+ var setData = setsInView.pop();
+
+ // If the first set of IS posts is in the same view as the posts loaded in the template by WordPress, determine how much of the view is comprised of IS-loaded posts
+ if ( ( ( windowBottom - setData.top ) / windowSize ) < 0.5 )
+ pageNum = -1;
+ else
+ pageNum = setData.pageNum;
+ }
+ else {
+ var majorityPercentageInView = 0;
+
+ // Identify the IS set that comprises the majority of the current window and set the URL to it.
+ $.each( setsInView, function( i, setData ) {
+ var topInView = 0,
+ bottomInView = 0,
+ percentOfView = 0;
+
+ // Figure percentage of view the current set represents
+ if ( setData.top > windowTop && setData.top < windowBottom )
+ topInView = ( windowBottom - setData.top ) / windowSize;
+
+ if ( setData.bottom > windowTop && setData.bottom < windowBottom )
+ bottomInView = ( setData.bottom - windowTop ) / windowSize;
+
+ // Figure out largest percentage of view for current set
+ if ( topInView >= bottomInView )
+ percentOfView = topInView;
+ else if ( bottomInView >= topInView )
+ percentOfView = bottomInView;
+
+ // Does current set's percentage of view supplant the largest previously-found set?
+ if ( percentOfView > majorityPercentageInView ) {
+ pageNum = setData.pageNum;
+ majorityPercentageInView = percentOfView;
+ }
+ } );
+ }
+
+ // If a page number could be determined, update the URL
+ // -1 indicates that the original requested URL should be used.
+ if ( 'number' == typeof pageNum ) {
+ if ( pageNum != -1 )
+ pageNum += ( 0 == self.offset ) ? 1 : self.offset;
+
+ self.updateURL( pageNum );
+ }
+}
+
+/**
+ * Update address bar to reflect archive page URL for a given page number.
+ * Checks if URL is different to prevent polution of browser history.
+ */
+Scroller.prototype.updateURL = function( page ) {
+ var self = this,
+ pageSlug = -1 == page ? self.origURL : window.location.protocol + '//' + self.history.host + self.history.path.replace( /%d/, page );
+
+ if ( window.location.href != pageSlug )
+ history.pushState( null, null, pageSlug );
+}
+
+/**
+ * Ready, set, go!
+ */
+$( document ).ready( function() {
+ // Check for our variables
+ if ( ! infiniteScroll )
+ return;
+
+ // Set ajaxurl (for brevity)
+ ajaxurl = infiniteScroll.settings.ajaxurl;
+
+ // Set stats, used for tracking stats
+ stats = infiniteScroll.settings.stats;
+
+ // Define what type of infinity we have, grab text for click-handle
+ type = infiniteScroll.settings.type;
+ text = infiniteScroll.settings.text;
+ totop = infiniteScroll.settings.totop;
+
+ // Initialize the scroller (with the ID of the element from the theme)
+ infiniteScroll.scroller = new Scroller( infiniteScroll.settings );
+
+ /**
+ * Monitor user scroll activity to update URL to correspond to archive page for current set of IS posts
+ * IE only supports pushState() in v10 and above, so don't bother if those conditions aren't met.
+ */
+ if ( ! isIE || ( isIE && IEVersion >= 10 ) ) {
+ $( window ).bind( 'scroll', function() {
+ clearTimeout( timer );
+ timer = setTimeout( infiniteScroll.scroller.determineURL , 100 );
+ });
+ }
+});
+
+
+})(jQuery); // Close closure \ No newline at end of file
diff --git a/plugins/jetpack/modules/infinite-scroll/infinity.php b/plugins/jetpack/modules/infinite-scroll/infinity.php
new file mode 100644
index 00000000..ad57f1b6
--- /dev/null
+++ b/plugins/jetpack/modules/infinite-scroll/infinity.php
@@ -0,0 +1,945 @@
+<?php
+
+/*
+Plugin Name: The Neverending Home Page.
+Plugin URI: http://automattic.com/
+Description: Adds infinite scrolling support to the front-end blog post view for themes, pulling the next set of posts automatically into view when the reader approaches the bottom of the page.
+Version: 1.1
+Author: Automattic
+Author URI: http://automattic.com/
+License: GNU General Public License v2 or later
+License URI: http://www.gnu.org/licenses/gpl-2.0.html
+*/
+
+/**
+ * Class: The_Neverending_Home_Page relies on add_theme_support, expects specific
+ * styling from each theme; including fixed footer.
+ */
+class The_Neverending_Home_Page {
+ /**
+ *
+ */
+ function __construct() {
+ add_filter( 'pre_get_posts', array( $this, 'posts_per_page_query' ) );
+
+ add_action( 'admin_init', array( $this, 'settings_api_init' ) );
+ add_action( 'template_redirect', array( $this, 'action_template_redirect' ) );
+ add_action( 'template_redirect', array( $this, 'ajax_response' ) );
+ add_action( 'custom_ajax_infinite_scroll', array( $this, 'query' ) );
+ add_action( 'the_post', array( $this, 'preserve_more_tag' ) );
+ add_action( 'get_footer', array( $this, 'footer' ) );
+
+ // Plugin compatibility
+ add_filter( 'grunion_contact_form_redirect_url', array( $this, 'filter_grunion_redirect_url' ) );
+
+ // Parse IS settings from theme
+ self::get_settings();
+ }
+
+ /**
+ * Initialize our static variables
+ */
+ static $the_time = null;
+ static $settings = null; // Don't access directly, instead use self::get_settings().
+
+ static $option_name_enabled = 'infinite_scroll';
+
+ /**
+ * Parse IS settings provided by theme
+ *
+ * @uses get_theme_support, infinite_scroll_has_footer_widgets, sanitize_title, add_action, get_option, wp_parse_args, is_active_sidebar
+ * @return object
+ */
+ static function get_settings() {
+ if ( is_null( self::$settings ) ) {
+ $css_pattern = '#[^A-Z\d\-_]#i';
+
+ $settings = $defaults = array(
+ 'type' => 'scroll', // scroll | click
+ 'requested_type' => 'scroll', // store the original type for use when logic overrides it
+ 'footer_widgets' => false, // true | false | sidebar_id | array of sidebar_ids -- last two are checked with is_active_sidebar
+ 'container' => 'content', // container html id
+ 'wrapper' => true, // true | false | html class
+ 'render' => false, // optional function, otherwise the `content` template part will be used
+ 'footer' => true, // boolean to enable or disable the infinite footer | string to provide an html id to derive footer width from
+ 'posts_per_page' => false // int | false to set based on IS type
+ );
+
+ // Validate settings passed through add_theme_support()
+ $_settings = get_theme_support( 'infinite-scroll' );
+
+ if ( is_array( $_settings ) ) {
+ // Preferred implementation, where theme provides an array of options
+ if ( isset( $_settings[0] ) && is_array( $_settings[0] ) ) {
+ foreach ( $_settings[0] as $key => $value ) {
+ switch ( $key ) {
+ case 'type' :
+ if ( in_array( $value, array( 'scroll', 'click' ) ) )
+ $settings[ $key ] = $settings['requested_type'] = $value;
+
+ break;
+
+ case 'footer_widgets' :
+ if ( is_string( $value ) )
+ $settings[ $key ] = sanitize_title( $value );
+ elseif ( is_array( $value ) )
+ $settings[ $key ] = array_map( 'sanitize_title', $value );
+ elseif ( is_bool( $value ) )
+ $settings[ $key ] = $value;
+
+ break;
+
+ case 'container' :
+ case 'wrapper' :
+ if ( 'wrapper' == $key && is_bool( $value ) ) {
+ $settings[ $key ] = $value;
+ }
+ else {
+ $value = preg_replace( $css_pattern, '', $value );
+
+ if ( ! empty( $value ) )
+ $settings[ $key ] = $value;
+ }
+
+ break;
+
+ case 'render' :
+ if ( false !== $value && is_callable( $value ) ) {
+ $settings[ $key ] = $value;
+
+ add_action( 'infinite_scroll_render', $value );
+ }
+
+ break;
+
+ case 'footer' :
+ if ( is_bool( $value ) ) {
+ $settings[ $key ] = $value;
+ }
+ elseif ( is_string( $value ) ) {
+ $value = preg_replace( $css_pattern, '', $value );
+
+ if ( ! empty( $value ) )
+ $settings[ $key ] = $value;
+ }
+
+ break;
+
+ case 'posts_per_page' :
+ if ( is_numeric( $value ) )
+ $settings[ $key ] = (int) $value;
+
+ break;
+
+ default:
+ continue;
+
+ break;
+ }
+ }
+ }
+ // Checks below are for backwards compatibility
+ elseif ( is_string( $_settings[0] ) ) {
+ // Container to append new posts to
+ $settings['container'] = preg_replace( $css_pattern, '', $_settings[0] );
+
+ // Wrap IS elements?
+ if ( isset( $_settings[1] ) )
+ $settings['wrapper'] = (bool) $_settings[1];
+ }
+ }
+
+ // Always ensure all values are present in the final array
+ $settings = wp_parse_args( $settings, $defaults );
+
+ // Check if a legacy `infinite_scroll_has_footer_widgets()` function is defined and override the footer_widgets parameter's value.
+ // Otherwise, if a widget area ID or array of IDs was provided in the footer_widgets parameter, check if any contains any widgets.
+ // It is safe to use `is_active_sidebar()` before the sidebar is registered as this function doesn't check for a sidebar's existence when determining if it contains any widgets.
+ if ( function_exists( 'infinite_scroll_has_footer_widgets' ) ) {
+ $settings['footer_widgets'] = (bool) infinite_scroll_has_footer_widgets();
+ }
+ elseif ( is_array( $settings['footer_widgets'] ) ) {
+ $sidebar_ids = $settings['footer_widgets'];
+ $settings['footer_widgets'] = false;
+
+ foreach ( $sidebar_ids as $sidebar_id ) {
+ if ( is_active_sidebar( $sidebar_id ) ) {
+ $settings['footer_widgets'] = true;
+ break;
+ }
+ }
+
+ unset( $sidebar_ids );
+ unset( $sidebar_id );
+ }
+ elseif ( is_string( $settings['footer_widgets'] ) ) {
+ $settings['footer_widgets'] = (bool) is_active_sidebar( $settings['footer_widgets'] );
+ }
+
+ // For complex logic, let themes filter the `footer_widgets` parameter.
+ $settings['footer_widgets'] = apply_filters( 'infinite_scroll_has_footer_widgets', $settings['footer_widgets'] );
+
+ // Finally, after all of the sidebar checks and filtering, ensure that a boolean value is present, otherwise set to default of `false`.
+ if ( ! is_bool( $settings['footer_widgets'] ) )
+ $settings['footer_widgets'] = false;
+
+ // Ensure that IS is enabled and no footer widgets exist if the IS type isn't already "click".
+ if ( 'click' != $settings['type'] ) {
+ // Check the setting status
+ $disabled = '' === get_option( self::$option_name_enabled ) ? true : false;
+
+ // Footer content or Reading option check
+ if ( $settings['footer_widgets'] || $disabled )
+ $settings['type'] = 'click';
+ }
+
+ // Backwards compatibility for posts_per_page setting
+ if ( false === $settings['posts_per_page'] )
+ $settings['posts_per_page'] = 'click' == $settings['type'] ? (int) get_option( 'posts_per_page' ) : 7;
+
+ // Store final settings in a class static to avoid reparsing
+ self::$settings = apply_filters( 'infinite_scroll_settings', $settings );
+ }
+
+ return (object) self::$settings;
+ }
+
+ /**
+ * Has infinite scroll been triggered?
+ */
+ static function got_infinity() {
+ return isset( $_GET[ 'infinity' ] );
+ }
+
+ /**
+ * The more tag will be ignored by default if the blog page isn't our homepage.
+ * Let's force the $more global to false.
+ */
+ function preserve_more_tag( $array ) {
+ global $more;
+
+ if ( self::got_infinity() )
+ $more = 0; //0 = show content up to the more tag. Add more link.
+
+ return $array;
+ }
+
+ /**
+ * Add a checkbox field to Settings > Reading
+ * for enabling infinite scroll.
+ *
+ * Only show if the current theme supports infinity.
+ *
+ * @uses current_theme_supports, add_settings_field, __, register_setting
+ * @action admin_init
+ * @return null
+ */
+ function settings_api_init() {
+ if ( ! current_theme_supports( 'infinite-scroll' ) )
+ return;
+
+ // Add the setting field [infinite_scroll] and place it in Settings > Reading
+ add_settings_field( self::$option_name_enabled, '<span id="infinite-scroll-options">' . __( 'To infinity and beyond', 'jetpack' ) . '</span>', array( $this, 'infinite_setting_html' ), 'reading' );
+ register_setting( 'reading', self::$option_name_enabled, 'esc_attr' );
+ }
+
+ /**
+ * HTML code to display a checkbox true/false option
+ * for the infinite_scroll setting.
+ */
+ function infinite_setting_html() {
+ $notice = '<em>' . __( "We've disabled this option for you since you have footer widgets in Appearance &rarr; Widgets, or because your theme does not support infinite scroll.", 'jetpack' ) . '</em>';
+
+ // If the blog has footer widgets, show a notice instead of the checkbox
+ if ( self::get_settings()->footer_widgets || 'click' == self::get_settings()->requested_type ) {
+ echo '<label>' . $notice . '</label>';
+ } else {
+ echo '<label><input name="infinite_scroll" type="checkbox" value="1" ' . checked( 1, '' !== get_option( self::$option_name_enabled ), false ) . ' /> ' . __( 'Scroll Infinitely', 'jetpack' ) . '</br><small>' . sprintf( __( '(Shows %s posts on each load)', 'jetpack' ), number_format_i18n( self::get_settings()->posts_per_page ) ) . '</small>' . '</label>';
+ }
+ }
+
+ /**
+ * Does the legwork to determine whether the feature is enabled.
+ *
+ * @uses current_theme_supports, self::archive_supports_infinity, self::get_settings, self::set_last_post_time, add_filter, wp_enqueue_script, plugins_url, wp_enqueue_style, add_action
+ * @action template_redirect
+ * @return null
+ */
+ function action_template_redirect() {
+ // Check that we support infinite scroll, and are on the home page.
+ if ( ! current_theme_supports( 'infinite-scroll' ) || ! self::archive_supports_infinity() )
+ return;
+
+ $id = self::get_settings()->container;
+
+ // Check that we have an id.
+ if ( empty( $id ) )
+ return;
+
+ // Bail if there are not enough posts for infinity.
+ if ( ! self::set_last_post_time() )
+ return;
+
+ // Add a class to the body.
+ add_filter( 'body_class', array( $this, 'body_class' ) );
+
+ // Add our scripts.
+ wp_enqueue_script( 'the-neverending-homepage', plugins_url( 'infinity.js', __FILE__ ), array( 'jquery' ), '20130101' );
+
+ // Add our default styles.
+ wp_enqueue_style( 'the-neverending-homepage', plugins_url( 'infinity.css', __FILE__ ), array(), '20120612' );
+
+ add_action( 'wp_enqueue_scripts', array( $this, 'enqueue_spinner_scripts' ) );
+
+ add_action( 'wp_head', array( $this, 'action_wp_head' ), 2 );
+
+ add_action( 'wp_footer', array( $this, 'action_wp_footer' ), 99999999 );
+
+ add_filter( 'infinite_scroll_results', array( $this, 'filter_infinite_scroll_results' ), 10, 3 );
+ }
+
+ /**
+ * Enqueue spinner scripts.
+ */
+ function enqueue_spinner_scripts() {
+ wp_enqueue_script( 'jquery.spin' );
+ }
+
+ /**
+ * Adds an 'infinite-scroll' class to the body.
+ */
+ function body_class( $classes ) {
+ $classes[] = 'infinite-scroll';
+
+ if ( 'scroll' == self::get_settings()->type )
+ $classes[] = 'neverending';
+
+ return $classes;
+ }
+
+ /**
+ * Grab the timestamp for the last post.
+ * @return string 'Y-m-d H:i:s' or null
+ */
+ function set_last_post_time( $date = false ) {
+ global $posts;
+ $count = count( $posts );
+
+ if ( ! empty( $date ) && preg_match( '|\d{4}\-\d{2}\-\d{2}|', $_GET['date'] ) ) {
+ self::$the_time = "$date 00:00:00";
+ return self::$the_time;
+ }
+
+ // If we don't have enough posts for infinity, return early
+ if ( ! $count || $count < self::get_settings()->posts_per_page )
+ return self::$the_time;
+
+ $last_post = end( $posts );
+
+ // If the function is called again but we already have a value, return it
+ if ( null != self::$the_time ) {
+ return self::$the_time;
+ }
+ else if ( isset( $last_post->post_date_gmt ) ) {
+ // Grab the latest post time in Y-m-d H:i:s gmt format
+ self::$the_time = $last_post->post_date_gmt;
+ }
+
+ return self::$the_time;
+ }
+
+ /**
+ * Create a where clause that will make sure post queries
+ * will always return results prior to (descending sort)
+ * or before (ascending sort) the last post date.
+ *
+ * @param string $where
+ * @param object $query
+ * @filter posts_where
+ * @return string
+ */
+ function query_time_filter( $where, $query ) {
+ global $wpdb;
+
+ $operator = 'ASC' == $query->get( 'order' ) ? '>' : '<';
+
+ // Construct the date query using our timestamp
+ $where .= $wpdb->prepare( " AND post_date_gmt {$operator} %s", self::set_last_post_time() );
+
+ return $where;
+ }
+
+ /**
+ * Let's overwrite the default post_per_page setting to always display a fixed amount.
+ *
+ * @global $wp_the_query Used to provide compatibility back to WP 3.2
+ * @param object $query
+ * @uses self::archive_supports_infinity, self::get_settings
+ * @return null
+ */
+ function posts_per_page_query( $query ) {
+ global $wp_the_query;
+
+ if ( self::archive_supports_infinity() && $query === $wp_the_query ) // After 3.3, this line would be: if ( self::archive_supports_infinity() && $query->is_main_query() )
+ $query->set( 'posts_per_page', self::get_settings()->posts_per_page );
+ }
+
+ /**
+ * Check if the IS output should be wrapped in a div.
+ * Setting value can be a boolean or a string specifying the class applied to the div.
+ *
+ * @uses self::get_settings
+ * @return bool
+ */
+ function has_wrapper() {
+ return (bool) self::get_settings()->wrapper;
+ }
+
+ /**
+ * Returns the Ajax url
+ *
+ * @global $wp
+ * @uses home_url, is_ssl, add_query_arg, trailingslashit, apply_filters
+ * @return string
+ */
+ function ajax_url() {
+ global $wp;
+
+ // When using default permalinks, $wp->request will be null, so we reconstruct the request from the query arguments WP parsed.
+ if ( is_null( $wp->request ) ) {
+ $base_url = home_url( '/', is_ssl() ? 'https' : 'http' );
+ $base_url = add_query_arg( $wp->query_vars, $base_url );
+ } else {
+ $base_url = home_url( trailingslashit( $wp->request ), is_ssl() ? 'https' : 'http' );
+ }
+
+ $ajaxurl = add_query_arg( array( 'infinity' => 'scrolling' ), $base_url );
+
+ return apply_filters( 'infinite_scroll_ajax_url', $ajaxurl );
+ }
+
+ /**
+ * Our own Ajax response, avoiding calling admin-ajax
+ */
+ function ajax_response() {
+ // Only proceed if the url query has a key of "Infinity"
+ if ( ! self::got_infinity() )
+ return false;
+
+ define( 'DOING_AJAX', true );
+
+ @header( 'Content-Type: text/html; charset=' . get_option( 'blog_charset' ) );
+ send_nosniff_header();
+
+ do_action( 'custom_ajax_infinite_scroll' );
+ die( '0' );
+ }
+
+ /**
+ * Prints the relevant infinite scroll settings in JS.
+ *
+ * @uses self::get_settings, esc_js, esc_url_raw, self::has_wrapper, __, apply_filters, do_action
+ * @action wp_head
+ * @return string
+ */
+ function action_wp_head() {
+ global $wp_query, $wp_the_query, $wp_rewrite;
+
+ // Base JS settings
+ $js_settings = array(
+ 'id' => self::get_settings()->container,
+ 'ajaxurl' => esc_url_raw( self::ajax_url() ),
+ 'type' => esc_js( self::get_settings()->type ),
+ 'wrapper' => self::has_wrapper(),
+ 'wrapper_class' => is_string( self::get_settings()->wrapper ) ? esc_js( self::get_settings()->wrapper ) : 'infinite-wrap',
+ 'footer' => is_string( self::get_settings()->footer ) ? esc_js( self::get_settings()->footer ) : self::get_settings()->footer,
+ 'text' => esc_js( __( 'Older posts', 'jetpack' ) ),
+ 'totop' => esc_js( __( 'Scroll back to top', 'jetpack' ) ),
+ 'order' => 'DESC',
+ 'scripts' => array(),
+ 'styles' => array(),
+ 'google_analytics' => false,
+ 'offset' => $wp_query->get( 'paged' ),
+ 'history' => array(
+ 'host' => preg_replace( '#^http(s)?://#i', '', untrailingslashit( get_option( 'home' ) ) ),
+ 'path' => self::get_request_path(),
+ 'use_trailing_slashes' => $wp_rewrite->use_trailing_slashes
+ )
+ );
+
+ // Optional order param
+ if ( isset( $_GET['order'] ) ) {
+ $order = strtoupper( $_GET['order'] );
+
+ if ( in_array( $order, array( 'ASC', 'DESC' ) ) )
+ $js_settings['order'] = $order;
+ }
+
+ $js_settings = apply_filters( 'infinite_scroll_js_settings', $js_settings );
+
+ do_action( 'infinite_scroll_wp_head' );
+
+ ?>
+ <script type="text/javascript">
+ //<![CDATA[
+ var infiniteScroll = <?php echo json_encode( array( 'settings' => $js_settings ) ); ?>;
+ //]]>
+ </script>
+ <?php
+ }
+
+ /**
+ * Build path data for current request.
+ * Used for Google Analytics and pushState history tracking.
+ *
+ * @global $wp_rewrite
+ * @global $wp
+ * @uses user_trailingslashit, sanitize_text_field, add_query_arg
+ * @return string|bool
+ */
+ private function get_request_path() {
+ global $wp_rewrite;
+
+ if ( $wp_rewrite->using_permalinks() ) {
+ global $wp;
+
+ // If called too early, bail
+ if ( ! isset( $wp->request ) )
+ return false;
+
+ // Determine path for paginated version of current request
+ if ( false != preg_match( '#' . $wp_rewrite->pagination_base . '/\d+/?$#i', $wp->request ) )
+ $path = preg_replace( '#' . $wp_rewrite->pagination_base . '/\d+$#i', $wp_rewrite->pagination_base . '/%d', $wp->request );
+ else
+ $path = $wp->request . '/' . $wp_rewrite->pagination_base . '/%d';
+
+ // Slashes everywhere we need them
+ if ( 0 !== strpos( $path, '/' ) )
+ $path = '/' . $path;
+
+ $path = user_trailingslashit( $path );
+ }
+ else {
+ // Clean up raw $_GET input
+ $path = array_map( 'sanitize_text_field', $_GET );
+ $path = array_filter( $path );
+
+ $path['paged'] = '%d';
+
+ $path = add_query_arg( $path, '/' );
+ }
+
+ return empty( $path ) ? false : $path;
+ }
+
+ /**
+ * Provide IS with a list of the scripts and stylesheets already present on the page.
+ * Since posts may contain require additional assets that haven't been loaded, this data will be used to track the additional assets.
+ *
+ * @global $wp_scripts, $wp_styles
+ * @action wp_footer
+ * @return string
+ */
+ function action_wp_footer() {
+ global $wp_scripts, $wp_styles;
+
+ $scripts = is_a( $wp_scripts, 'WP_Scripts' ) ? $wp_scripts->done : array();
+ $scripts = apply_filters( 'infinite_scroll_existing_scripts', $scripts );
+
+ $styles = is_a( $wp_styles, 'WP_Styles' ) ? $wp_styles->done : array();
+ $styles = apply_filters( 'infinite_scroll_existing_stylesheets', $styles );
+
+ ?><script type="text/javascript">
+ jQuery.extend( infiniteScroll.settings.scripts, <?php echo json_encode( $scripts ); ?> );
+ jQuery.extend( infiniteScroll.settings.styles, <?php echo json_encode( $styles ); ?> );
+ </script><?php
+ }
+
+ /**
+ * Identify additional scripts required by the latest set of IS posts and provide the necessary data to the IS response handler.
+ *
+ * @global $wp_scripts
+ * @uses sanitize_text_field, add_query_arg
+ * @filter infinite_scroll_results
+ * @return array
+ */
+ function filter_infinite_scroll_results( $results, $query_args, $wp_query ) {
+ // Don't bother unless there are posts to display
+ if ( 'success' != $results['type'] )
+ return $results;
+
+ // Parse and sanitize the script handles already output
+ $initial_scripts = isset( $_GET['scripts'] ) && is_array( $_GET['scripts'] ) ? array_map( 'sanitize_text_field', $_GET['scripts'] ) : false;
+
+ if ( is_array( $initial_scripts ) ) {
+ global $wp_scripts;
+
+ // Identify new scripts needed by the latest set of IS posts
+ $new_scripts = array_diff( $wp_scripts->done, $initial_scripts );
+
+ // If new scripts are needed, extract relevant data from $wp_scripts
+ if ( ! empty( $new_scripts ) ) {
+ $results['scripts'] = array();
+
+ foreach ( $new_scripts as $handle ) {
+ // Abort if somehow the handle doesn't correspond to a registered script
+ if ( ! isset( $wp_scripts->registered[ $handle ] ) )
+ continue;
+
+ // Provide basic script data
+ $script_data = array(
+ 'handle' => $handle,
+ 'footer' => ( is_array( $wp_scripts->in_footer ) && in_array( $handle, $wp_scripts->in_footer ) ),
+ 'extra_data' => $wp_scripts->print_extra_script( $handle, false )
+ );
+
+ // Base source
+ $src = $wp_scripts->registered[ $handle ]->src;
+
+ // Take base_url into account
+ if ( strpos( $src, 'http' ) !== 0 )
+ $src = $wp_scripts->base_url . $src;
+
+ // Version and additional arguments
+ if ( null === $wp_scripts->registered[ $handle ]->ver )
+ $ver = '';
+ else
+ $ver = $wp_scripts->registered[ $handle ]->ver ? $wp_scripts->registered[ $handle ]->ver : $wp_scripts->default_version;
+
+ if ( isset($wp_scripts->args[ $handle ] ) )
+ $ver = $ver ? $ver . '&amp;' . $wp_scripts->args[$handle] : $wp_scripts->args[$handle];
+
+ // Full script source with version info
+ $script_data['src'] = add_query_arg( 'ver', $ver, $src );
+
+ // Add script to data that will be returned to IS JS
+ array_push( $results['scripts'], $script_data );
+ }
+ }
+ }
+
+ // Expose additional script data to filters, but only include in final `$results` array if needed.
+ if ( ! isset( $results['scripts'] ) )
+ $results['scripts'] = array();
+
+ $results['scripts'] = apply_filters( 'infinite_scroll_additional_scripts', $results['scripts'], $initial_scripts, $results, $query_args, $wp_query );
+
+ if ( empty( $results['scripts'] ) )
+ unset( $results['scripts' ] );
+
+ // Parse and sanitize the style handles already output
+ $initial_styles = isset( $_GET['styles'] ) && is_array( $_GET['styles'] ) ? array_map( 'sanitize_text_field', $_GET['styles'] ) : false;
+
+ if ( is_array( $initial_styles ) ) {
+ global $wp_styles;
+
+ // Identify new styles needed by the latest set of IS posts
+ $new_styles = array_diff( $wp_styles->done, $initial_styles );
+
+ // If new styles are needed, extract relevant data from $wp_styles
+ if ( ! empty( $new_styles ) ) {
+ $results['styles'] = array();
+
+ foreach ( $new_styles as $handle ) {
+ // Abort if somehow the handle doesn't correspond to a registered stylesheet
+ if ( ! isset( $wp_styles->registered[ $handle ] ) )
+ continue;
+
+ // Provide basic style data
+ $style_data = array(
+ 'handle' => $handle,
+ 'media' => 'all'
+ );
+
+ // Base source
+ $src = $wp_styles->registered[ $handle ]->src;
+
+ // Take base_url into account
+ if ( strpos( $src, 'http' ) !== 0 )
+ $src = $wp_styles->base_url . $src;
+
+ // Version and additional arguments
+ if ( null === $wp_styles->registered[ $handle ]->ver )
+ $ver = '';
+ else
+ $ver = $wp_styles->registered[ $handle ]->ver ? $wp_styles->registered[ $handle ]->ver : $wp_styles->default_version;
+
+ if ( isset($wp_styles->args[ $handle ] ) )
+ $ver = $ver ? $ver . '&amp;' . $wp_styles->args[$handle] : $wp_styles->args[$handle];
+
+ // Full stylesheet source with version info
+ $style_data['src'] = add_query_arg( 'ver', $ver, $src );
+
+ // Parse stylesheet's conditional comments if present, converting to logic executable in JS
+ if ( isset( $wp_styles->registered[ $handle ]->extra['conditional'] ) && $wp_styles->registered[ $handle ]->extra['conditional'] ) {
+ // First, convert conditional comment operators to standard logical operators. %ver is replaced in JS with the IE version
+ $style_data['conditional'] = str_replace( array(
+ 'lte',
+ 'lt',
+ 'gte',
+ 'gt'
+ ), array(
+ '%ver <=',
+ '%ver <',
+ '%ver >=',
+ '%ver >',
+ ), $wp_styles->registered[ $handle ]->extra['conditional'] );
+
+ // Next, replace any !IE checks. These shouldn't be present since WP's conditional stylesheet implementation doesn't support them, but someone could be _doing_it_wrong().
+ $style_data['conditional'] = preg_replace( '#!\s*IE(\s*\d+){0}#i', '1==2', $style_data['conditional'] );
+
+ // Lastly, remove the IE strings
+ $style_data['conditional'] = str_replace( 'IE', '', $style_data['conditional'] );
+ }
+
+ // Parse requested media context for stylesheet
+ if ( isset( $wp_styles->registered[ $handle ]->args ) )
+ $style_data['media'] = esc_attr( $wp_styles->registered[ $handle ]->args );
+
+ // Add stylesheet to data that will be returned to IS JS
+ array_push( $results['styles'], $style_data );
+ }
+ }
+ }
+
+ // Expose additional stylesheet data to filters, but only include in final `$results` array if needed.
+ if ( ! isset( $results['styles'] ) )
+ $results['styles'] = array();
+
+ $results['styles'] = apply_filters( 'infinite_scroll_additional_stylesheets', $results['styles'], $initial_styles, $results, $query_args, $wp_query );
+
+ if ( empty( $results['styles'] ) )
+ unset( $results['styles' ] );
+
+ // Lastly, return the IS results array
+ return $results;
+ }
+
+ /**
+ * Runs the query and returns the results via JSON.
+ * Triggered by an AJAX request.
+ *
+ * @global $wp_query
+ * @global $wp_the_query
+ * @uses current_user_can, get_option, self::set_last_post_time, current_user_can, apply_filters, self::get_settings, add_filter, WP_Query, remove_filter, have_posts, wp_head, do_action, add_action, this::render, this::has_wrapper, esc_attr, wp_footer, sharing_register_post_for_share_counts, get_the_id
+ * @return string or null
+ */
+ function query() {
+ global $wp_query, $wp_the_query;
+
+ if ( ! isset( $_GET['page'] ) || ! current_theme_supports( 'infinite-scroll' ) )
+ die;
+
+ $page = (int) $_GET['page'];
+ $sticky = get_option( 'sticky_posts' );
+
+ if ( ! empty( $_GET['date'] ) )
+ self::set_last_post_time( $_GET['date'] );
+
+ $post_status = array( 'publish' );
+ if ( current_user_can( 'read_private_posts' ) )
+ array_push( $post_status, 'private' );
+
+ $order = in_array( $_GET['order'], array( 'ASC', 'DESC' ) ) ? $_GET['order'] : 'DESC';
+
+ $query_args = array_merge( $wp_the_query->query_vars, array(
+ 'paged' => $page,
+ 'post_status' => $post_status,
+ 'posts_per_page' => self::get_settings()->posts_per_page,
+ 'post__not_in' => ( array ) $sticky,
+ 'order' => $order
+ ) );
+
+ // By default, don't query for a specific page of a paged post object.
+ // This argument comes from merging $wp_the_query.
+ // Since IS is only used on archives, we should always display the first page of any paged content.
+ unset( $query_args['page'] );
+
+ $query_args = apply_filters( 'infinite_scroll_query_args', $query_args );
+
+ // Add query filter that checks for posts below the date
+ add_filter( 'posts_where', array( $this, 'query_time_filter' ), 10, 2 );
+
+ $wp_the_query = $wp_query = new WP_Query( $query_args );
+
+ remove_filter( 'posts_where', array( $this, 'query_time_filter' ), 10, 2 );
+
+ $results = array();
+
+ if ( have_posts() ) {
+ // Fire wp_head to ensure that all necessary scripts are enqueued. Output isn't used, but scripts are extracted in self::action_wp_footer.
+ ob_start();
+ wp_head();
+ ob_end_clean();
+
+ $results['type'] = 'success';
+
+ // First, try theme's specified rendering handler, either specified via `add_theme_support` or by hooking to this action directly.
+ ob_start();
+ do_action( 'infinite_scroll_render' );
+ $results['html'] = ob_get_clean();
+
+ // Fall back if a theme doesn't specify a rendering function. Because themes may hook additional functions to the `infinite_scroll_render` action, `has_action()` is ineffective here.
+ if ( empty( $results['html'] ) ) {
+ add_action( 'infinite_scroll_render', array( $this, 'render' ) );
+ rewind_posts();
+
+ ob_start();
+ do_action( 'infinite_scroll_render' );
+ $results['html'] = ob_get_clean();
+ }
+
+ // If primary and fallback rendering methods fail, prevent further IS rendering attempts. Otherwise, wrap the output if requested.
+ if ( empty( $results['html'] ) ) {
+ unset( $results['html'] );
+ do_action( 'infinite_scroll_empty' );
+ $results['type'] = 'empty';
+ }
+ elseif ( $this->has_wrapper() ) {
+ $wrapper_classes = is_string( self::get_settings()->wrapper ) ? self::get_settings()->wrapper : 'infinite-wrap';
+ $wrapper_classes .= ' infinite-view-' . $page;
+ $wrapper_classes = trim( $wrapper_classes );
+
+ $results['html'] = '<div class="' . esc_attr( $wrapper_classes ) . '" id="infinite-view-' . $page . '" data-page-num="' . $page . '">' . $results['html'] . '</div>';
+ }
+
+ // Fire wp_footer to ensure that all necessary scripts are enqueued. Output isn't used, but scripts are extracted in self::action_wp_footer.
+ ob_start();
+ wp_footer();
+ ob_end_clean();
+
+ // Loop through posts to capture sharing data for new posts loaded via Infinite Scroll
+ if ( 'success' == $results['type'] && function_exists( 'sharing_register_post_for_share_counts' ) ) {
+ global $jetpack_sharing_counts;
+
+ while( have_posts() ) {
+ the_post();
+
+ sharing_register_post_for_share_counts( get_the_ID() );
+ }
+
+ $results['postflair'] = array_flip( $jetpack_sharing_counts );
+ }
+ } else {
+ do_action( 'infinite_scroll_empty' );
+ $results['type'] = 'empty';
+ }
+
+ echo json_encode( apply_filters( 'infinite_scroll_results', $results, $query_args, $wp_query ) );
+ die;
+ }
+
+ /**
+ * Rendering fallback used when themes don't specify their own handler.
+ *
+ * @uses have_posts, the_post, get_template_part, get_post_format
+ * @action infinite_scroll_render
+ * @return string
+ */
+ function render() {
+ while ( have_posts() ) {
+ the_post();
+
+ get_template_part( 'content', get_post_format() );
+ }
+ }
+
+ /**
+ * Allow plugins to filter what archives Infinite Scroll supports
+ *
+ * @uses apply_filters, current_theme_supports, is_home, is_archive, self::get_settings
+ * @return bool
+ */
+ public static function archive_supports_infinity() {
+ return (bool) apply_filters( 'infinite_scroll_archive_supported', current_theme_supports( 'infinite-scroll' ) && ( is_home() || is_archive() ), self::get_settings() );
+ }
+
+ /**
+ * The Infinite Blog Footer
+ *
+ * @uses self::get_settings, self::set_last_post_time, self::archive_supports_infinity, __, wp_get_theme, get_current_theme, apply_filters, home_url, esc_attr, get_bloginfo, bloginfo
+ * @return string or null
+ */
+ function footer() {
+ // Bail if theme requested footer not show
+ if ( false == self::get_settings()->footer )
+ return;
+
+ // Bail if there are not enough posts for infinity.
+ if ( ! self::set_last_post_time() )
+ return;
+
+ // We only need the new footer for the 'scroll' type
+ if ( 'scroll' != self::get_settings()->type || ! self::archive_supports_infinity() )
+ return;
+
+ $credits = '<a href="http://wordpress.org/" rel="generator">Proudly powered by WordPress</a> ';
+ $credits .= sprintf( __( 'Theme: %1$s.', 'jetpack' ), function_exists( 'wp_get_theme' ) ? wp_get_theme()->Name : get_current_theme() );
+ $credits = apply_filters( 'infinite_scroll_credit', $credits );
+
+ ?>
+ <div id="infinite-footer">
+ <div class="container">
+ <div class="blog-info">
+ <a id="infinity-blog-title" href="<?php echo home_url( '/' ); ?>" title="<?php echo esc_attr( get_bloginfo( 'name', 'display' ) ); ?>" rel="home">
+ <?php bloginfo( 'name' ); ?>
+ </a>
+ </div>
+ <div class="blog-credits">
+ <?php echo $credits; ?>
+ </div>
+ </div>
+ </div><!-- #infinite-footer -->
+ <?php
+ }
+
+ /**
+ * Ensure that IS doesn't interfere with Grunion by stripping IS query arguments from the Grunion redirect URL.
+ * When arguments are present, Grunion redirects to the IS AJAX endpoint.
+ *
+ * @param string $url
+ * @uses remove_query_arg
+ * @filter grunion_contact_form_redirect_url
+ * @return string
+ */
+ public function filter_grunion_redirect_url( $url ) {
+ // Remove IS query args, if present
+ if ( false !== strpos( $url, 'infinity=scrolling' ) ) {
+ $url = remove_query_arg( array(
+ 'infinity',
+ 'action',
+ 'page',
+ 'order',
+ 'scripts',
+ 'styles'
+ ), $url );
+ }
+
+ return $url;
+ }
+};
+
+/**
+ * Initialize The_Neverending_Home_Page
+ */
+function the_neverending_home_page_init() {
+ if ( ! current_theme_supports( 'infinite-scroll' ) )
+ return;
+
+ new The_Neverending_Home_Page;
+}
+add_action( 'init', 'the_neverending_home_page_init', 20 );
+
+/**
+ * Check whether the current theme is infinite-scroll aware.
+ * If so, include the files which add theme support.
+ */
+function the_neverending_home_page_theme_support() {
+ $theme_name = get_stylesheet();
+
+ $customization_file = apply_filters( 'infinite_scroll_customization_file', dirname( __FILE__ ) . "/themes/{$theme_name}.php", $theme_name );
+
+ if ( is_readable( $customization_file ) )
+ require_once( $customization_file );
+}
+add_action( 'after_setup_theme', 'the_neverending_home_page_theme_support', 5 );
diff --git a/plugins/jetpack/modules/infinite-scroll/themes/twentyeleven.css b/plugins/jetpack/modules/infinite-scroll/themes/twentyeleven.css
new file mode 100644
index 00000000..cc232785
--- /dev/null
+++ b/plugins/jetpack/modules/infinite-scroll/themes/twentyeleven.css
@@ -0,0 +1,45 @@
+/* =Infinity Styles
+-------------------------------------------------------------- */
+.infinite-scroll #main:after {
+ clear: both;
+ content: '';
+ display: block;
+}
+.infinite-scroll #content {
+ margin-bottom: 40px;
+}
+.infinite-scroll.neverending #content {
+ margin-bottom: 70px;
+}
+.infinite-scroll .infinite-wrap {
+ border-top: none;
+ padding-top: 0;
+}
+.infinite-scroll .infinite-wrap .hentry:last-child {
+ border-bottom: 1px solid #ddd;
+}
+.infinite-scroll .infinite-wrap:last-of-type .hentry:last-child {
+ border-bottom: none;
+}
+
+/**
+ * Elements to hide:
+ * (footer widgets, post navigation, regular footer)
+ */
+.infinite-scroll.neverending #colophon #supplementary,
+.infinite-scroll #nav-below,
+.infinite-scroll.neverending #colophon {
+ display: none;
+}
+
+/* Hooks to infinity-end body class to restore footer */
+.infinity-end.neverending #colophon {
+ display: block;
+}
+
+/* For responsive CSS */
+@media (max-width: 800px) {
+ .infinite-scroll #infinite-handle {
+ padding-bottom: 40px;
+ }
+} \ No newline at end of file
diff --git a/plugins/jetpack/modules/infinite-scroll/themes/twentyeleven.php b/plugins/jetpack/modules/infinite-scroll/themes/twentyeleven.php
new file mode 100644
index 00000000..a80ee810
--- /dev/null
+++ b/plugins/jetpack/modules/infinite-scroll/themes/twentyeleven.php
@@ -0,0 +1,27 @@
+<?php
+/**
+ * Infinite Scroll Theme Assets
+ *
+ * Register support for @Twenty Eleven and enqueue relevant styles.
+ */
+
+/**
+ * Add theme support for infinity scroll
+ */
+function twenty_eleven_infinite_scroll_init() {
+ add_theme_support( 'infinite-scroll', array(
+ 'container' => 'content',
+ 'footer_widgets' => array( 'sidebar-3', 'sidebar-4', 'sidebar-5' ),
+ 'footer' => 'page',
+ ) );
+}
+add_action( 'init', 'twenty_eleven_infinite_scroll_init' );
+
+/**
+ * Enqueue CSS stylesheet with theme styles for infinity.
+ */
+function twenty_eleven_infinite_scroll_enqueue_styles() {
+ // Add theme specific styles.
+ wp_enqueue_style( 'infinity-twentyeleven', plugins_url( 'twentyeleven.css', __FILE__ ), array( 'the-neverending-homepage' ), '20121002' );
+}
+add_action( 'wp_enqueue_scripts', 'twenty_eleven_infinite_scroll_enqueue_styles', 25 ); \ No newline at end of file
diff --git a/plugins/jetpack/modules/infinite-scroll/themes/twentyten.css b/plugins/jetpack/modules/infinite-scroll/themes/twentyten.css
new file mode 100644
index 00000000..889abb3f
--- /dev/null
+++ b/plugins/jetpack/modules/infinite-scroll/themes/twentyten.css
@@ -0,0 +1,25 @@
+/* =Infinity Styles
+-------------------------------------------------------------- */
+.infinite-scroll #wrapper {
+ margin-bottom: 40px;
+}
+.infinite-scroll #content {
+ margin-bottom: 50px;
+}
+.infinite-scroll #content .infinite-wrap {
+ padding-top: 0;
+ border-top: 0;
+}
+/* Elements to hide */
+.infinite-scroll #nav-above,
+.infinite-scroll #nav-below,
+.infinite-scroll.neverending #footer {
+ display: none;
+}
+/* Restore the footer when IS is finished */
+.infinity-end.neverending #footer {
+ display: block;
+}
+#infinite-footer .blog-info a {
+ font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
+} \ No newline at end of file
diff --git a/plugins/jetpack/modules/infinite-scroll/themes/twentyten.php b/plugins/jetpack/modules/infinite-scroll/themes/twentyten.php
new file mode 100644
index 00000000..094cef9c
--- /dev/null
+++ b/plugins/jetpack/modules/infinite-scroll/themes/twentyten.php
@@ -0,0 +1,48 @@
+<?php
+/**
+ * Infinite Scroll Theme Assets
+ *
+ * Register support for @Twenty Ten and enqueue relevant styles.
+ */
+
+/**
+ * Add theme support for infinity scroll
+ */
+function twenty_ten_infinite_scroll_init() {
+ add_theme_support( 'infinite-scroll', array(
+ 'container' => 'content',
+ 'render' => 'twenty_ten_infinite_scroll_render',
+ 'footer' => 'wrapper',
+ ) );
+}
+add_action( 'init', 'twenty_ten_infinite_scroll_init' );
+
+/**
+ * Set the code to be rendered on for calling posts,
+ * hooked to template parts when possible.
+ *
+ * Note: must define a loop.
+ */
+function twenty_ten_infinite_scroll_render() {
+ get_template_part( 'loop' );
+}
+
+/**
+ * Enqueue CSS stylesheet with theme styles for infinity.
+ */
+function twenty_ten_infinite_scroll_enqueue_styles() {
+ // Add theme specific styles.
+ wp_enqueue_style( 'infinity-twentyten', plugins_url( 'twentyten.css', __FILE__ ), array( 'the-neverending-homepage' ), '20121002' );
+}
+add_action( 'wp_enqueue_scripts', 'twenty_ten_infinite_scroll_enqueue_styles', 25 );
+
+/**
+ * Do we have footer widgets?
+ */
+function twenty_ten_has_footer_widgets( $has_widgets ) {
+ if ( is_active_sidebar( 'first-footer-widget-area' ) || is_active_sidebar( 'second-footer-widget-area' ) || is_active_sidebar( 'third-footer-widget-area' ) || is_active_sidebar( 'fourth-footer-widget-area' ) )
+ $has_widgets = true;
+
+ return $has_widgets;
+}
+add_filter( 'infinite_scroll_has_footer_widgets', 'twenty_ten_has_footer_widgets' ); \ No newline at end of file
diff --git a/plugins/jetpack/modules/infinite-scroll/themes/twentytwelve.css b/plugins/jetpack/modules/infinite-scroll/themes/twentytwelve.css
new file mode 100644
index 00000000..032c2c9a
--- /dev/null
+++ b/plugins/jetpack/modules/infinite-scroll/themes/twentytwelve.css
@@ -0,0 +1,33 @@
+/* =Infinity Styles
+-------------------------------------------------------------- */
+.infinite-scroll .site-content:after {
+ clear: both;
+ content: '';
+ display: block;
+}
+.infinite-wrap {
+ border-top: 0;
+}
+.infinite-scroll.neverending .site-content {
+ margin-bottom: 48px;
+ margin-bottom: 3.428571429rem;
+}
+
+/* Elements to hide: post navigation, regular footer */
+.infinite-scroll #nav-below,
+.infinite-scroll.neverending #colophon {
+ display: none;
+}
+
+/* Hooks to infinity-end body class to restore footer */
+.infinity-end.neverending #colophon {
+ display: block;
+}
+
+/* For responsive CSS */
+@media (max-width: 599px) {
+ .infinite-scroll #infinite-handle {
+ padding-bottom: 48px;
+ padding-bottom: 3.428571429rem;
+ }
+} \ No newline at end of file
diff --git a/plugins/jetpack/modules/infinite-scroll/themes/twentytwelve.php b/plugins/jetpack/modules/infinite-scroll/themes/twentytwelve.php
new file mode 100644
index 00000000..f8b77011
--- /dev/null
+++ b/plugins/jetpack/modules/infinite-scroll/themes/twentytwelve.php
@@ -0,0 +1,46 @@
+<?php
+/**
+ * Infinite Scroll Theme Assets
+ *
+ * Register support for Twenty Twelve and enqueue relevant styles.
+ */
+
+/**
+ * Add theme support for infinite scroll
+ */
+function twenty_twelve_infinite_scroll_init() {
+ add_theme_support( 'infinite-scroll', array(
+ 'container' => 'content',
+ 'footer' => 'page'
+ ) );
+}
+add_action( 'after_setup_theme', 'twenty_twelve_infinite_scroll_init' );
+
+/**
+ * Enqueue CSS stylesheet with theme styles for infinity.
+ */
+function twenty_twelve_infinite_scroll_enqueue_styles() {
+ // Add theme specific styles.
+ wp_enqueue_style( 'infinity-twentytwelve', plugins_url( 'twentytwelve.css', __FILE__ ), array( 'the-neverending-homepage' ), '20120817' );
+}
+add_action( 'wp_enqueue_scripts', 'twenty_twelve_infinite_scroll_enqueue_styles', 25 );
+
+/**
+ * Handle `footer_widgets` argument for mobile devices
+ *
+ * @param bool $has_widgets
+ * @uses jetpack_is_mobile, is_front_page, is_active_sidebar
+ * @filter infinite_scroll_has_footer_widgets
+ * @return bool
+ */
+function twenty_twelve_has_footer_widgets( $has_widgets ) {
+ if ( function_exists( 'jetpack_is_mobile' ) && jetpack_is_mobile() ) {
+ if ( is_front_page() && ( is_active_sidebar( 'sidebar-2' ) || is_active_sidebar( 'sidebar-3' ) ) )
+ $has_widgets = true;
+ elseif ( is_active_sidebar( 'sidebar-1' ) )
+ $has_widgets = true;
+ }
+
+ return $has_widgets;
+}
+add_filter( 'infinite_scroll_has_footer_widgets', 'twenty_twelve_has_footer_widgets' ); \ No newline at end of file
diff --git a/plugins/jetpack/modules/json-api.php b/plugins/jetpack/modules/json-api.php
new file mode 100644
index 00000000..fb1473ff
--- /dev/null
+++ b/plugins/jetpack/modules/json-api.php
@@ -0,0 +1,19 @@
+<?php
+/**
+ * Module Name: JSON API
+ * Module Description: Allow applications to securely access your content through the cloud.
+ * Sort Order: 100
+ * First Introduced: 1.9
+ */
+
+function jetpack_json_api_toggle() {
+ $jetpack = Jetpack::init();
+ $jetpack->sync->register( 'noop' );
+
+ if ( false !== strpos( current_filter(), 'jetpack_activate_module_' ) ) {
+ Jetpack::check_privacy( __FILE__ );
+ }
+}
+
+add_action( 'jetpack_activate_module_json-api', 'jetpack_json_api_toggle' );
+add_action( 'jetpack_deactivate_module_json-api', 'jetpack_json_api_toggle' );
diff --git a/plugins/jetpack/modules/latex.php b/plugins/jetpack/modules/latex.php
index 4e4d9db2..76066cd6 100644
--- a/plugins/jetpack/modules/latex.php
+++ b/plugins/jetpack/modules/latex.php
@@ -1,7 +1,7 @@
<?php
/**
* Module Name: Beautiful Math
- * Module Description: Mark up your posts with the <img src="http://l.wordpress.com/latex.php?latex=%5CLaTeX&amp;bg=transparent&amp;fg=000&amp;s=-2" alt="LaTeX logo" title="LaTeX" style="vertical-align: -25%" /> markup language, perfect for complex mathematical equations and other &#252;ber-geekery.
+ * 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
*/
@@ -64,11 +64,11 @@ function latex_entity_decode( $latex ) {
}
function latex_render( $latex, $fg, $bg, $s = 0 ) {
- $url = ( is_ssl() ? 'https://s-ssl.wordpress.com' : 'http://s0.wp.com' ) . "/latex.php?latex=" . urlencode( $latex ) . "&bg=$bg&fg=$fg&s=$s";
+ $url = "//s0.wp.com/latex.php?latex=" . urlencode( $latex ) . "&bg=" . $bg . "&fg=" . $fg . "&s=" . $s;
$url = esc_url( $url );
$alt = str_replace( '\\', '&#92;', esc_attr( $latex ) );
- return "<img src='$url' alt='$alt' title='$alt' class='latex' />";
+ return '<img src="' . $url . '" alt="' . $alt . '" title="' . $alt . '" class="latex" />';
}
/**
diff --git a/plugins/jetpack/modules/likes.php b/plugins/jetpack/modules/likes.php
new file mode 100644
index 00000000..753d4ab4
--- /dev/null
+++ b/plugins/jetpack/modules/likes.php
@@ -0,0 +1,970 @@
+<?php
+/**
+ * Module Name: Likes
+ * Module Description: Likes are a way for people to show their appreciation for content you have written. It’s also a way for you to show the world how popular your content has become.
+ * First Introduced: 2.2
+ * Sort Order: 4
+ */
+class Jetpack_Likes {
+ var $version = '20130226';
+
+ function &init() {
+ static $instance = NULL;
+
+ if ( ! $instance ) {
+ $instance = new Jetpack_Likes;
+ }
+
+ return $instance;
+ }
+
+ function __construct() {
+ $this->in_jetpack = ( defined( 'IS_WPCOM' ) && IS_WPCOM ) ? false : true;
+
+ add_action( 'init', array( &$this, 'action_init' ) );
+
+ if ( $this->in_jetpack ) {
+ 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( 'Jetpack_Likes', 'configuration_redirect' ) );
+
+ add_action('admin_print_scripts-settings_page_sharing', array( &$this, 'load_jp_css' ) );
+ add_filter( 'sharing_show_buttons_on_row_start', array( $this, 'configuration_target_area' ) );
+
+ $active = Jetpack::get_active_modules();
+
+ if ( ! in_array( 'sharedaddy', $active ) && ! in_array( 'publicize', $active ) ) {
+ add_action( 'admin_menu', array( $this, 'sharing_menu' ) ); // we don't have a sharing page yet
+ }
+
+ if ( in_array( 'publicize', $active ) && ! in_array( 'sharedaddy', $active ) ) {
+ add_action( 'pre_admin_screen_sharing', array( $this, 'sharing_block' ), 20 ); // we have a sharing page but not the global options area
+ add_action( 'pre_admin_screen_sharing', array( $this, 'updated_message' ), -10 );
+ }
+
+ if( ! in_array( 'sharedaddy', $active ) ) {
+ add_action( 'admin_init', array( $this, 'process_update_requests_if_sharedaddy_not_loaded' ) );
+ add_action( 'sharing_global_options', array( $this, 'admin_settings_showbuttonon_init' ), 19 );
+ add_action( 'sharing_admin_update', array( $this, 'admin_settings_showbuttonon_callback' ), 19 );
+ add_action( 'admin_init', array( $this, 'add_meta_box' ) );
+ } else {
+ add_filter( 'sharing_meta_box_title', array( $this, 'add_likes_to_sharing_meta_box_title' ) );
+ add_action( 'start_sharing_meta_box_content', array( $this, 'meta_box_content' ) );
+ }
+ } else { // wpcom
+ add_action( 'admin_init', array( $this, 'add_meta_box' ) );
+ add_action( 'end_likes_meta_box_content', array( $this, 'sharing_meta_box_content' ) );
+ add_filter( 'likes_meta_box_title', array( $this, 'add_likes_to_sharing_meta_box_title' ) );
+ }
+
+ add_action( 'admin_bar_menu', array( $this, 'admin_bar_likes' ), 60 );
+
+ add_action( 'save_post', array( $this, 'meta_box_save' ) );
+ add_action( 'sharing_global_options', array( $this, 'admin_settings_init' ), 20 );
+ add_action( 'sharing_admin_update', array( $this, 'admin_settings_callback' ), 20 );
+ }
+
+ 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#likes' ) );
+ die();
+ }
+
+ /**
+ * Loads Jetpack's CSS on the sharing page so we can use .jetpack-targetable
+ */
+ function load_jp_css() {
+ Jetpack::init()->admin_styles();
+ }
+
+ /**
+ * Adds in the jetpack-targetable class so when we visit sharing#likes our like settings get highlighted by a yellow box
+ * @param string $html row heading for the sharedaddy "which page" setting
+ * @return string html with the jetpack-targetable class and likes id. tbody gets closed after the like settings
+ */
+ function configuration_target_area( $html = '' ) {
+ $html = "<tbody id='likes' class='jetpack-targetable'>" . $html;
+ return $html;
+ }
+
+ /**
+ * Replaces the "Sharing" title for the post screen metabox with "Likes and Shares"
+ * @param string $title The current title of the metabox, not needed/used.
+ */
+ function add_likes_to_sharing_meta_box_title( $title ) {
+ return __( 'Likes and Shares', 'jetpack' );
+ }
+
+ /**
+ * Adds a metabox to the post screen if the sharing one doesn't currently exist.
+ */
+ function add_meta_box() {
+ if ( apply_filters( 'post_flair_disable', false ) )
+ return;
+
+ $post_types = get_post_types( array( 'public' => true ) );
+ $title = apply_filters( 'likes_meta_box_title', __( 'Likes', 'jetpack' ) );
+ foreach( $post_types as $post_type ) {
+ add_meta_box( 'likes_meta', $title, array( $this, 'meta_box_content' ), $post_type, 'advanced', 'high' );
+ }
+ }
+
+ function meta_box_save( $post_id ) {
+ if ( defined('DOING_AUTOSAVE') && DOING_AUTOSAVE )
+ return $post_id;
+
+ // Record sharing disable. Only needs to be done for WPCOM
+ if ( ! $this->in_jetpack ) {
+ if ( isset( $_POST['post_type'] ) && ( 'post' == $_POST['post_type'] || 'page' == $_POST['post_type'] ) ) {
+ if ( isset( $_POST['wpl_sharing_status_hidden'] ) && !isset( $_POST['wpl_enable_post_sharing'] ) ) {
+ update_post_meta( $post_id, 'sharing_disabled', 1 );
+ } else {
+ delete_post_meta( $post_id, 'sharing_disabled' );
+ }
+ }
+ }
+
+ if ( empty( $_POST['wpl_like_status_hidden'] ) )
+ return $post_id;
+
+ if ( 'post' == $_POST['post_type'] ) {
+ if ( !current_user_can( 'edit_post', $post_id ) ) {
+ return $post_id;
+ }
+ }
+
+ // Record a change in like status for this post - only if it contradicts the
+ // site like setting.
+ if ( ( $this->is_enabled_sitewide() && empty( $_POST['wpl_enable_post_likes'] ) ) || ( ! $this->is_enabled_sitewide() && !empty( $_POST['wpl_enable_post_likes'] ) ) ) {
+ update_post_meta( $post_id, 'switch_like_status', 1 );
+ //$g_gif = file_get_contents( 'http://stats.wordpress.com/g.gif?v=wpcom-no-pv&x_likes=switched_post_like_status' ); @todo stat
+ } else {
+ delete_post_meta( $post_id, 'switch_like_status' );
+ }
+
+ return $post_id;
+ }
+
+ /**
+ * Shows the likes option in the post screen metabox.
+ */
+ function meta_box_content( $post ) {
+ $post_id = ! empty( $post->ID ) ? (int) $post->ID : get_the_ID();
+ $checked = true;
+ $disabled = ! $this->is_enabled_sitewide();
+ $switched_status = get_post_meta( $post_id, 'switch_like_status', true );
+
+ if ( $disabled && empty( $switched_status ) || false == $disabled && !empty( $switched_status ) )
+ $checked = false;
+
+ do_action( 'start_likes_meta_box_content', $post );
+ ?>
+
+ <p>
+ <label for="wpl_enable_post_likes">
+ <input type="checkbox" name="wpl_enable_post_likes" id="wpl_enable_post_likes" value="1" <?php checked( $checked ); ?>>
+ <?php esc_html_e( 'Show likes.', 'jetpack' ); ?>
+ </label>
+ <input type="hidden" name="wpl_like_status_hidden" value="1" />
+ </p> <?php
+ do_action( 'end_likes_meta_box_content', $post );
+ }
+
+ /**
+ * WordPress.com: Metabox option for sharing (sharedaddy will handle this on the JP blog)
+ */
+ function sharing_meta_box_content( $post ) {
+ $post_id = ! empty( $post->ID ) ? (int) $post->ID : get_the_ID();
+ $disabled = get_post_meta( $post_id, 'sharing_disabled', true ); ?>
+ <p>
+ <label for="wpl_enable_post_sharing">
+ <input type="checkbox" name="wpl_enable_post_sharing" id="wpl_enable_post_sharing" value="1" <?php checked( !$disabled ); ?>>
+ <?php _e( 'Show sharing buttons.', 'jetpack' ); ?>
+ </label>
+ <input type="hidden" name="wpl_sharing_status_hidden" value="1" />
+ </p> <?php
+ }
+
+ /**
+ * The actual options block to be inserted into the sharing page.
+ */
+ function admin_settings_init() { ?>
+ <tr>
+ <th scope="row">
+ <label><?php esc_html_e( 'WordPress.com Likes are', 'jetpack' ); ?></label>
+ </th>
+ <td>
+ <div>
+ <label>
+ <input type="radio" class="code" name="wpl_default" value="on" <?php checked( $this->is_enabled_sitewide(), true ); ?> />
+ <?php esc_html_e( 'On for all posts', 'jetpack' ); ?>
+ </label>
+ </div>
+ <div>
+ <label>
+ <input type="radio" class="code" name="wpl_default" value="off" <?php checked( $this->is_enabled_sitewide(), false ); ?> />
+ <?php esc_html_e( 'Turned on per post', 'jetpack' ); ?>
+ </label>
+ <div>
+ </td>
+ </tr> <?php /*
+ <tr>
+ <th scope="row">
+ <label><?php esc_html_e( 'Comment Likes', 'jetpack' ); ?></label>
+ </th>
+ <td>
+ <div>
+ <label>
+ <input type="checkbox" class="code" name="jetpack_comment_likes_enabled" value="1" <?php checked( $this->is_comments_enabled(), true ); ?> />
+ <?php esc_html_e( 'Allow people to like comments', 'jetpack' ); ?>
+ </label>
+ </div>
+ </td>
+ </tr> */ ?>
+ </tbody> <?php // closes the tbody attached to sharing_show_buttons_on_row_start... ?>
+ <?php }
+
+ /**
+ * If sharedaddy is not loaded, we don't have the "Show buttons on" yet, so we need to add that since it affects likes too.
+ */
+ function admin_settings_showbuttonon_init() { ?>
+ <?php echo apply_filters( 'sharing_show_buttons_on_row_start', '<tr valign="top">' ); ?>
+ <th scope="row"><label><?php _e( 'Show buttons on', 'jetpack' ); ?></label></th>
+ <td>
+ <?php
+ $br = false;
+ $shows = array_values( get_post_types( array( 'public' => true ) ) );
+ array_unshift( $shows, 'index' );
+ $global = $this->get_options();
+ foreach ( $shows as $show ) :
+ if ( 'index' == $show ) {
+ $label = __( 'Front Page, Archive Pages, and Search Results', 'jetpack' );
+ } else {
+ $post_type_object = get_post_type_object( $show );
+ $label = $post_type_object->labels->name;
+ }
+ ?>
+ <?php if ( $br ) echo '<br />'; ?><label><input type="checkbox"<?php checked( in_array( $show, $global['show'] ) ); ?> name="show[]" value="<?php echo esc_attr( $show ); ?>" /> <?php echo esc_html( $label ); ?></label>
+ <?php $br = true; endforeach; ?>
+ </td>
+ <?php echo apply_filters( 'sharing_show_buttons_on_row_end', '</tr>' ); ?>
+ <?php }
+
+
+ /**
+ * If sharedaddy is not loaded, we still need to save the the settings of the "Show buttons on" option.
+ */
+ function admin_settings_showbuttonon_callback() {
+ $options = get_option( 'sharing-options' );
+ if ( !is_array( $options ) )
+ $options = array();
+
+ $shows = array_values( get_post_types( array( 'public' => true ) ) );
+ $shows[] = 'index';
+ $data = $_POST;
+
+ if ( isset( $data['show'] ) ) {
+ if ( is_scalar( $data['show'] ) ) {
+ switch ( $data['show'] ) {
+ case 'posts' :
+ $data['show'] = array( 'post', 'page' );
+ break;
+ case 'index' :
+ $data['show'] = array( 'index' );
+ break;
+ case 'posts-index' :
+ $data['show'] = array( 'post', 'page', 'index' );
+ break;
+ }
+ }
+
+ if ( $data['show'] = array_intersect( $data['show'], $shows ) ) {
+ $options['global']['show'] = $data['show'];
+ }
+ } else {
+ $options['global']['show'] = array();
+ }
+
+ update_option( 'sharing-options', $options );
+ }
+
+ /**
+ * Adds the admin update hook so we can save settings even if Sharedaddy is not enabled.
+ */
+ function process_update_requests_if_sharedaddy_not_loaded() {
+ if ( isset( $_GET['page'] ) && ( $_GET['page'] == 'sharing.php' || $_GET['page'] == 'sharing' ) ) {
+ if ( isset( $_POST['_wpnonce'] ) && wp_verify_nonce( $_POST['_wpnonce'], 'sharing-options' ) ) {
+ do_action( 'sharing_admin_update' );
+ wp_safe_redirect( admin_url( 'options-general.php?page=sharing&update=saved' ) );
+ die();
+ }
+ }
+ }
+
+ /**
+ * Saves the setting in the database, bumps a stat on WordPress.com
+ */
+ function admin_settings_callback() {
+ // We're looking for these, and doing a dance to set some stats and save
+ // them together in array option.
+ $new_state = !empty( $_POST['wpl_default'] ) ? $_POST['wpl_default'] : 'on';
+ $db_state = $this->is_enabled_sitewide();
+
+ /** Default State *********************************************************/
+
+ // Checked (enabled)
+ switch( $new_state ) {
+ case 'off' :
+ if ( true == $db_state && ! $this->in_jetpack ) {
+ $g_gif = file_get_contents( 'http://stats.wordpress.com/g.gif?v=wpcom-no-pv&x_likes=disabled_likes' );
+ }
+ update_option( 'disabled_likes', 1 );
+ break;
+ case 'on' :
+ default:
+ if ( false == $db_state && ! $this->in_jetpack ) {
+ $g_gif = file_get_contents( 'http://stats.wordpress.com/g.gif?v=wpcom-no-pv&x_likes=reenabled_likes' );
+ }
+ delete_option( 'disabled_likes' );
+ break;
+ }
+
+
+ // comment setting
+ $new_comments_state = !empty( $_POST['jetpack_comment_likes_enabled'] ) ? $_POST['jetpack_comment_likes_enabled'] : false;
+ switch( (bool) $new_comments_state ) {
+ case true:
+ update_option( 'jetpack_comment_likes_enabled', 1 );
+ break;
+ case false:
+ default:
+ update_option( 'jetpack_comment_likes_enabled', 0 );
+ break;
+ }
+ }
+
+ /**
+ * Adds the 'sharing' menu to the settings menu.
+ * Only ran if sharedaddy and publicize 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() {
+ $this->updated_message(); ?>
+ <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' ) ?>
+ <?php $this->sharing_block(); ?>
+ </div> <?php
+ }
+
+ /**
+ * Returns the settings have been saved message.
+ */
+ function updated_message() {
+ if ( isset( $_GET['update'] ) && $_GET['update'] == 'saved' )
+ echo '<div class="updated"><p>' . esc_html__( 'Settings have been saved', 'jetpack' ) . '</p></div>';
+ }
+
+ /**
+ * Returns just the "sharing buttons" w/ like option block, so it can be inserted into different sharing page contexts
+ */
+ function sharing_block() { ?>
+ <h3><?php esc_html_e( 'Sharing Buttons', 'jetpack' ); ?></h3>
+ <form method="post" action="">
+ <table class="form-table">
+ <tbody>
+ <?php do_action( 'sharing_global_options' ); ?>
+ </tbody>
+ </table>
+
+ <p class="submit">
+ <input type="submit" name="submit" class="button-primary" value="<?php esc_attr_e( 'Save Changes', 'jetpack' ); ?>" />
+ </p>
+
+ <input type="hidden" name="_wpnonce" value="<?php echo wp_create_nonce( 'sharing-options' );?>" />
+ </form> <?php
+ }
+
+ function action_init() {
+ if ( is_admin() )
+ return;
+
+ if ( ( defined( 'XMLRPC_REQUEST' ) && XMLRPC_REQUEST ) ||
+ ( defined( 'APP_REQUEST' ) && APP_REQUEST ) ||
+ ( defined( 'REST_API_REQUEST' ) && REST_API_REQUEST ) ||
+ ( defined( 'COOKIE_AUTH_REQUEST' ) && COOKIE_AUTH_REQUEST ) ||
+ ( defined( 'JABBER_SERVER' ) && JABBER_SERVER ) )
+ return;
+
+ // Comment Likes widget has been disabled, pending performance improvements.
+ // add_filter( 'comment_text', array( &$this, 'comment_likes' ), 10, 2 );
+
+ if ( $this->in_jetpack ) {
+ add_filter( 'the_content', array( &$this, 'post_likes' ), 30, 1 );
+ wp_enqueue_script( 'postmessage', plugins_url( '_inc/postmessage.js', dirname(__FILE__) ), array( 'jquery' ), JETPACK__VERSION, false );
+ wp_enqueue_script( 'jquery_inview', plugins_url( '_inc/jquery.inview.js', dirname(__FILE__) ), array( 'jquery' ), JETPACK__VERSION, false );
+ wp_enqueue_style( 'jetpack_likes', plugins_url( 'likes/style.css', __FILE__ ), array(), JETPACK__VERSION );
+ } else {
+ add_filter( 'post_flair', array( &$this, 'post_likes' ), 30, 1 );
+ add_filter( 'post_flair_block_css', array( $this, 'post_flair_service_enabled_like' ) );
+
+ wp_enqueue_script( 'postmessage', '/wp-content/js/postmessage.js', array( 'jquery' ), JETPACK__VERSION, false );
+ wp_enqueue_script( 'jquery_inview', '/wp-content/js/jquery/jquery.inview.js', array( 'jquery' ), JETPACK__VERSION, false );
+ wp_enqueue_style( 'jetpack_likes', plugins_url( 'jetpack-likes.css', __FILE__ ), array(), JETPACK__VERSION );
+ }
+ }
+
+ function post_likes( $content ) {
+ global $post;
+
+ 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' );
+ $url = home_url();
+ $url_parts = parse_url( $url );
+ $domain = $url_parts['host'];
+ }
+
+ add_filter( 'wp_footer', array( $this, 'likes_master' ) );
+
+ $src = sprintf( '%1$s://widgets.wp.com/likes/#blog_id=%2$d&post_id=%3$d&origin=%1$s://%4$s', $protocol, $blog_id, $post->ID, $domain );
+ $name = sprintf( 'like-post-frame-%1$d-%2$d', $blog_id, $post->ID );
+ $wrapper = sprintf( 'like-post-wrapper-%1$d-%2$d', $blog_id, $post->ID );
+
+ $html = "<div class='sharedaddy sd-block sd-like jetpack-likes-widget-wrapper jetpack-likes-widget-unloaded' id='$wrapper' data-src='$src' data-name='$name'><h3 class='sd-title'>" . esc_html__( 'Like this:', 'jetpack' ) . '</h3>';
+ $html .= "<div class='post-likes-widget-placeholder' style='height:55px'><span class='button'><span>" . esc_html__( 'Like', 'jetpack' ) . '</span></span> <span class="loading">' . esc_html__( 'Loading...', 'jetpack' ) . '</span></div>';
+ $html .= "<span class='sd-text-color'></span><a class='sd-link-color'></a>";
+ $html .= '</div>';
+
+ return $content . $html;
+ }
+
+ function comment_likes( $content, $comment = null ) {
+ if ( empty( $comment ) )
+ return $content;
+
+ if ( ! $this->is_comments_enabled() )
+ 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' );
+ $url = home_url();
+ $url_parts = parse_url( $url );
+ $domain = $url_parts['host'];
+ }
+
+ add_filter( 'wp_footer', array( $this, 'likes_master' ) );
+
+ $src = sprintf( '%1$s://widgets.wp.com/likes/#blog_id=%2$d&comment_id=%3$d&origin=%1$s://%4$s', $protocol, $blog_id, $comment->comment_ID, $domain );
+ $name = sprintf( 'like-comment-frame-%1$d-%2$d', $blog_id, $comment->comment_ID );
+ $wrapper = sprintf( 'like-comment-wrapper-%1$d-%2$d', $blog_id, $comment->comment_ID );
+
+ $html = "<div><div class='jetpack-likes-widget-wrapper jetpack-likes-widget-unloaded' id='$wrapper'>";
+ $html .= "<iframe class='comment-likes-widget jetpack-likes-widget' name='$name' height='16px' width='100%' data='$src'></iframe>";
+ $html .= '</div></div>';
+ return $content . $html;
+ }
+
+ function post_flair_service_enabled_like( $classes ) {
+ $classes[] = 'sd-like-enabled';
+ return $classes;
+ }
+
+ function admin_bar_likes() {
+ global $wp_admin_bar, $post;
+
+ if ( ! $this->is_admin_bar_button_visible() ) {
+ return;
+ }
+
+ $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' );
+ $url = home_url();
+ $url_parts = parse_url( $url );
+ $domain = $url_parts['host'];
+ }
+
+ add_filter( 'wp_footer', array( $this, 'likes_master' ) );
+
+ $src = sprintf( '%1$s://widgets.wp.com/likes/#blog_id=%2$d&post_id=%3$d&origin=%1$s://%4$s', $protocol, $blog_id, $post->ID, $domain );
+
+ $html = "<iframe class='admin-bar-likes-widget jetpack-likes-widget' frameBorder='0' name='admin-bar-likes-widget' src='$src'></iframe>";
+
+ $node = array(
+ 'id' => 'admin-bar-likes-widget',
+ 'meta' => array(
+ 'html' => $html
+ )
+ );
+
+ $wp_admin_bar->add_node( $node );
+ }
+
+ function likes_master() {
+ $protocol = 'http';
+ if ( is_ssl() )
+ $protocol = 'https';
+
+ $locale = ( '' == get_locale() || 'en' == get_locale() ) ? '' : '&lang=' . strtolower( substr( get_locale(), 0, 2 ) );
+ $src = sprintf( '%1$s://widgets.wp.com/likes/master.html?ver=%2$s#ver=%2$s%3$s', $protocol, $this->version, $locale );
+
+ $likersText = wp_kses( __( '<span>%d</span> bloggers like this:', 'jetpack' ), array( 'span' => array() ) );
+?>
+ <iframe src='<?php echo $src; ?>' id='likes-master' name='likes-master' style='display:none;'></iframe>
+ <div id='likes-other-gravatars'><div class="likes-text"><?php echo $likersText; ?></div><ul class="wpl-avatars sd-like-gravatars"></ul></div>
+ <script type="text/javascript">
+ //<![CDATA[
+ var jetpackLikesWidgetQueue = [];
+ var jetpackLikesMasterReady = false;
+
+ function JetpackLikespostMessage( message, target ) {
+ if ( "string" === typeof message ){
+ try{
+ message = JSON.parse( message );
+ }
+ catch(e) {
+ return;
+ }
+ }
+
+ pm( {
+ target: target,
+ type: 'likesMessage',
+ data: message,
+ origin: '*'
+ } );
+ }
+
+ function JetpackLikesMessageListener( event ) {
+ if ( "undefined" == typeof event.event )
+ return;
+
+ if ( 'masterReady' == event.event ) {
+ jQuery( document ).ready( function() {
+ jetpackLikesMasterReady = true;
+
+ var stylesData = {
+ event: 'injectStyles'
+ };
+
+ if ( jQuery( 'iframe.admin-bar-likes-widget' ).length > 0 ) {
+ JetpackLikespostMessage( { event: 'adminBarEnabled' }, window.frames[ 'likes-master' ] );
+
+ stylesData.adminBarStyles = {
+ background: jQuery( '#wpadminbar .quicklinks li#wp-admin-bar-wpl-like > a' ).css( 'background' )
+ };
+ }
+
+ if ( !window.addEventListener )
+ jQuery( '#wp-admin-bar-admin-bar-likes-widget' ).hide();
+
+ stylesData.textStyles = {
+ color: jQuery( '.sd-text-color').css( 'color' ),
+ fontFamily: jQuery( '.sd-text-color' ).css( 'font-family' ),
+ fontSize: jQuery( '.sd-text-color' ).css( 'font-size' ),
+ direction: jQuery( '.sd-text-color' ).css( 'direction' ),
+ fontWeight: jQuery( '.sd-text-color' ).css( 'font-weight' ),
+ fontStyle: jQuery( '.sd-text-color' ).css( 'font-style' ),
+ textDecoration: jQuery( '.sd-text-color' ).css('text-decoration')
+ };
+
+ stylesData.linkStyles = {
+ color: jQuery( '.sd-link-color' ).css('color'),
+ fontFamily: jQuery( '.sd-link-color' ).css('font-family'),
+ fontSize: jQuery( '.sd-link-color' ).css('font-size'),
+ textDecoration: jQuery( '.sd-link-color' ).css('text-decoration'),
+ fontWeight: jQuery( '.sd-link-color' ).css( 'font-weight' ),
+ fontStyle: jQuery( '.sd-link-color' ).css( 'font-style' )
+ };
+
+ JetpackLikespostMessage( stylesData, window.frames[ 'likes-master' ] );
+
+ var requests = [];
+ jQuery( '.jetpack-likes-widget-wrapper' ).each( function( i ) {
+ var regex = /like-(post|comment)-wrapper-(\d+)-(\d+)/;
+ var match = regex.exec( this.id );
+ if ( ! match || match.length != 4 )
+ return;
+
+ var info = {
+ blog_id: match[2],
+ width: this.width
+ };
+
+ if ( 'post' == match[1] ) {
+ info.post_id = match[3];
+ } else if ( 'comment' == match[1] ) {
+ info.comment_id = match[3];
+ }
+
+ requests.push( info );
+ });
+
+ JetpackLikespostMessage( { event: 'initialBatch', requests: requests }, window.frames['likes-master'] );
+
+ jQuery( document ).on( 'inview', 'div.jetpack-likes-widget-unloaded', function() {
+ jetpackLikesWidgetQueue.push( this.id );
+ });
+ });
+ }
+
+ if ( 'showLikeWidget' == event.event ) {
+ setTimeout( JetpackLikesWidgetQueueHandler, 10 );
+ jQuery( '#' + event.id + ' .post-likes-widget-placeholder' ).fadeOut( 'fast', function() {
+ jQuery( '#' + event.id + ' .post-likes-widget' ).fadeIn( 'fast' );
+ JetpackLikespostMessage( { event: 'likeWidgetDisplayed', blog_id: event.blog_id, post_id: event.post_id }, window.frames['likes-master'] );
+ });
+ }
+
+ if ( 'showOtherGravatars' == event.event ) {
+ var $container = jQuery( '#likes-other-gravatars' );
+ var $list = $container.find( 'ul' );
+
+ $container.hide();
+ $list.html( '' );
+
+ $container.find( '.likes-text span' ).text( event.total );
+
+ jQuery.each( event.likers, function( i, liker ) {
+ $list.append( '<li class="' + liker.css_class + '"><a href="' + liker.profile_URL + '" class="wpl-liker" rel="nofollow" target="_parent"><img src="' + liker.avatar_URL + '" alt="' + liker.name + '" width="30" height="30" style="padding-right: 3px;" /></a></li>');
+ } );
+
+ var offset = jQuery( "[name='" + event.parent + "']" ).offset();
+
+ $container.css( 'left', offset.left + event.position.left - 10 + 'px' );
+ $container.css( 'top', offset.top + event.position.top - 33 + 'px' );
+
+ var rowLength = Math.floor( event.width / 37 );
+ var height = ( Math.ceil( event.likers.length / rowLength ) * 37 ) + 13;
+ if ( height > 204 ) {
+ height = 204;
+ }
+
+ $container.css( 'height', height + 'px' );
+ $container.css( 'width', rowLength * 37 - 7 + 'px' );
+
+ $list.css( 'width', rowLength * 37 + 'px' );
+
+ $container.fadeIn( 'slow' );
+
+ var scrollbarWidth = $list[0].offsetWidth - $list[0].clientWidth;
+ if ( scrollbarWidth > 0 ) {
+ $container.width( $container.width() + scrollbarWidth );
+ $list.width( $list.width() + scrollbarWidth );
+ }
+ }
+ }
+
+ pm.bind( 'likesMessage', function(e) { JetpackLikesMessageListener(e); } );
+
+ jQuery( document ).click( function( e ) {
+ var $container = jQuery( '#likes-other-gravatars' );
+
+ if ( $container.has( e.target ).length === 0 ) {
+ $container.fadeOut( 'slow' );
+ }
+ });
+
+ function JetpackLikesWidgetQueueHandler() {
+ var wrapperID;
+ if ( ! jetpackLikesMasterReady ) {
+ setTimeout( JetpackLikesWidgetQueueHandler, 500 );
+ return;
+ }
+
+ if ( jetpackLikesWidgetQueue.length > 0 ) {
+ // We may have a widget that needs creating now
+ var found = false;
+ while( jetpackLikesWidgetQueue.length > 0 ) {
+ // Grab the first member of the queue that isn't already loading.
+ wrapperID = jetpackLikesWidgetQueue.splice( 0, 1 )[0];
+ if ( jQuery( '#' + wrapperID ).hasClass( 'jetpack-likes-widget-unloaded' ) ) {
+ found = true;
+ break;
+ }
+ }
+ if ( ! found ) {
+ setTimeout( JetpackLikesWidgetQueueHandler, 500 );
+ return;
+ }
+ } else if ( jQuery( 'div.jetpack-likes-widget-unloaded' ).length > 0 ) {
+ // Get the next unloaded widget
+ wrapperID = jQuery( 'div.jetpack-likes-widget-unloaded' ).first()[0].id;
+ if ( ! wrapperID ) {
+ // Everything is currently loaded
+ setTimeout( JetpackLikesWidgetQueueHandler, 500 );
+ return;
+ }
+ }
+
+ var $wrapper = jQuery( '#' + wrapperID );
+ $wrapper.find( 'iframe' ).remove();
+
+ $wrapper.find( '.post-likes-widget-placeholder' ).after( "<iframe class='post-likes-widget jetpack-likes-widget' name='" + $wrapper.data( 'name' ) + "' height='55px' width='100%' frameBorder='0' src='" + $wrapper.data( 'src' ) + "'></iframe>" );
+
+
+ $wrapper.removeClass( 'jetpack-likes-widget-unloaded' ).addClass( 'jetpack-likes-widget-loading' );
+
+ $wrapper.find( 'iframe' ).load( function( e ) {
+ var $iframe = jQuery( e.target );
+ $wrapper.removeClass( 'jetpack-likes-widget-loading' ).addClass( 'jetpack-likes-widget-loaded' );
+
+ JetpackLikespostMessage( { event: 'loadLikeWidget', name: $iframe.attr( 'name' ), width: $iframe.width() }, window.frames[ 'likes-master' ] );
+ });
+ }
+ setInterval( JetpackLikesWidgetQueueHandler, 250 );
+ //]]>
+ </script>
+<?php
+ }
+
+ /**
+ * Get the 'disabled_likes' option from the DB of the current blog.
+ *
+ * @return array
+ */
+ function get_options() {
+ $setting = array();
+ $setting['disabled'] = get_option( 'disabled_likes' );
+ $sharing = get_option( 'sharing-options' );
+
+ // Default visibility settings
+ if ( ! isset( $sharing['global']['show'] ) ) {
+ $sharing['global']['show'] = array( 'post', 'page' );
+
+ // Scalar check
+ } elseif ( is_scalar( $sharing['global']['show'] ) ) {
+ switch ( $sharing['global']['show'] ) {
+ case 'posts' :
+ $sharing['global']['show'] = array( 'post', 'page' );
+ break;
+ case 'index' :
+ $sharing['global']['show'] = array( 'index' );
+ break;
+ case 'posts-index' :
+ $sharing['global']['show'] = array( 'post', 'page', 'index' );
+ break;
+ }
+ }
+
+ // Ensure it's always an array (even if not previously empty or scalar)
+ $setting['show'] = !empty( $sharing['global']['show'] ) ? (array) $sharing['global']['show'] : array();
+
+ return apply_filters( 'wpl_get_options', $setting );
+ }
+
+ /** _is_ functions ************************************************************/
+
+ /**
+ * Are likes visible in this context?
+ *
+ * Some of this code was taken and modified from sharing_display() to ensure
+ * similar logic and filters apply here, too.
+ */
+ function is_likes_visible() {
+
+ global $wp_current_filter; // Used to check 'get_the_excerpt' filter
+ global $post; // Used to apply 'sharing_show' filter
+
+ // Never show on feeds or previews
+ if ( is_feed() || is_preview() || is_comments_popup() ) {
+ $enabled = false;
+
+ // Not a feed or preview, so what is it?
+ } else {
+
+ if ( in_the_loop() ) {
+ // If in the loop, check if the current post is likeable
+ $enabled = $this->is_post_likeable();
+ } else {
+ // Otherwise, check and see if likes are enabled sitewide
+ $enabled = $this->is_enabled_sitewide();
+ }
+
+ /** Other Checks ******************************************************/
+
+ // Do not show on excerpts
+ if ( in_array( 'get_the_excerpt', (array) $wp_current_filter ) ) {
+ $enabled = false;
+
+ // Sharing Setting Overrides ****************************************
+ } else {
+ // Single post
+ if ( is_singular( 'post' ) ) {
+ if ( ! $this->is_single_post_enabled() ) {
+ $enabled = false;
+ }
+
+ // Single page
+ } elseif ( is_page() ) {
+ if ( ! $this->is_single_page_enabled() ) {
+ $enabled = false;
+ }
+
+ // Attachment
+ } elseif ( is_attachment() ) {
+ if ( ! $this->is_attachment_enabled() ) {
+ $enabled = false;
+ }
+
+ // All other loops
+ } elseif ( ! $this->is_index_enabled() ) {
+ $enabled = false;
+ }
+ }
+ }
+
+ // Run through the sharing filters
+ $enabled = apply_filters( 'sharing_show', $enabled, $post );
+
+ return (bool) apply_filters( 'wpl_is_likes_visible', $enabled );
+ }
+
+ /**
+ * Returns the current state of the "WordPress.com Likes are" option.
+ * @return boolean true if enabled sitewide, false if not
+ */
+ function is_enabled_sitewide() {
+ return (bool) apply_filters( 'wpl_is_enabled_sitewide', ! get_option( 'disabled_likes' ) );
+ }
+
+ /**
+ * Returns if comment likes are enabled. Defaults to 'on'
+ * @todo decide what the default should be
+ * @return boolean true if we should show comment likes, false if not
+ */
+ function is_comments_enabled() {
+ return (bool) apply_filters( 'jetpack_comment_likes_enabled', get_option( 'jetpack_comment_likes_enabled', true ) );
+ }
+
+ function is_admin_bar_button_visible() {
+ global $wp_admin_bar;
+
+ if ( ! is_object( $wp_admin_bar ) )
+ return false;
+
+ if ( ( ! is_singular( 'post' ) && ! is_attachment() && ! is_page() ) )
+ return false;
+
+ if ( ! $this->is_likes_visible() )
+ return false;
+
+ if ( ! $this->is_post_likeable() )
+ return false;
+
+ return (bool) apply_filters( 'jetpack_admin_bar_likes_enabled', true );
+ }
+
+ /**
+ * Are likes enabled for this post?
+ *
+ * @param int $post_id
+ * @retun bool
+ */
+ function is_post_likeable( $post_id = 0 ) {
+ $post = get_post( $post_id );
+ if ( !$post || is_wp_error( $post ) ) {
+ return false;
+ }
+
+ $sitewide_likes_enabled = (bool) Jetpack_Likes::is_enabled_sitewide();
+ $post_likes_switched = (bool) get_post_meta( $post->ID, 'switch_like_status', true );
+
+ $post_likes_enabled = $sitewide_likes_enabled;
+ if ( $post_likes_switched ) {
+ $post_likes_enabled = ! $post_likes_enabled;
+ }
+
+ return $post_likes_enabled;
+ }
+
+ /**
+ * Are Post Likes enabled on archive/front/search pages?
+ *
+ * @return bool
+ */
+ function is_index_enabled() {
+ $options = $this->get_options();
+ return (bool) apply_filters( 'wpl_is_index_disabled', (bool) in_array( 'index', $options['show'] ) );
+ }
+
+ /**
+ * Are Post Likes enabled on single posts?
+ *
+ * @return bool
+ */
+ function is_single_post_enabled() {
+ $options = $this->get_options();
+ return (bool) apply_filters( 'wpl_is_single_post_disabled', (bool) in_array( 'post', $options['show'] ) );
+ }
+
+ /**
+ * Are Post Likes enabled on single pages?
+ *
+ * @return bool
+ */
+ function is_single_page_enabled() {
+ $options = $this->get_options();
+ return (bool) apply_filters( 'wpl_is_single_page_disabled', (bool) in_array( 'page', $options['show'] ) );
+ }
+
+ /**
+ * Are Media Likes enabled on single pages?
+ *
+ * @return bool
+ */
+ function is_attachment_enabled() {
+ $options = $this->get_options();
+ return (bool) apply_filters( 'wpl_is_attachment_disabled', (bool) in_array( 'attachment', $options['show'] ) );
+ }
+
+}
+
+Jetpack_Likes::init();
diff --git a/plugins/jetpack/modules/likes/style.css b/plugins/jetpack/modules/likes/style.css
new file mode 100644
index 00000000..3885cace
--- /dev/null
+++ b/plugins/jetpack/modules/likes/style.css
@@ -0,0 +1,189 @@
+#wpadminbar li#wp-admin-bar-admin-bar-likes-widget {
+ width: 61px;
+}
+
+#wpadminbar iframe.admin-bar-likes-widget {
+ width: 61px;
+ height: 28px;
+ min-height: 28px;
+ border-width: 0px;
+ position: absolute;
+ top: 0;
+}
+
+div.jetpack-likes-widget-wrapper {
+ width: 100%;
+}
+
+#likes-other-gravatars {
+ display: none;
+ position: absolute;
+ padding: 10px;
+ background-color: #000;
+ border-width: 0;
+ opacity: 0.88;
+ filter: alpha(opacity=88);
+ box-shadow: 0 0 10px black;
+ min-width: 130px;
+ z-index: 1000;
+}
+
+#likes-other-gravatars .likes-text {
+ color: white;
+ font-size: 14px;
+ padding-bottom: 5px;
+}
+
+#likes-other-gravatars ul,
+#likes-other-gravatars li {
+ margin: 0;
+ padding: 0;
+ text-indent: 0;
+ list-style-type: none;
+}
+
+#likes-other-gravatars li::before {
+ content: "";
+}
+
+#likes-other-gravatars ul.wpl-avatars {
+ overflow: auto;
+ display: block;
+ position: absolute;
+ max-height: 190px;
+}
+
+#likes-other-gravatars ul.wpl-avatars li {
+ width: 32px;
+ height: 32px;
+ float: left;
+ margin: 0 5px 5px 0;
+}
+
+#likes-other-gravatars ul.wpl-avatars li a {
+ margin: 0 2px 0 0;
+ border-bottom: none !important;
+ display: block;
+}
+
+#likes-other-gravatars ul.wpl-avatars li a img {
+ background: none;
+ border: none;
+ margin: 0 !important;
+ padding: 0 !important;
+ position: static;
+}
+
+
+div.sd-box {
+ border-top: 1px solid #ddd;
+ border-top: 1px solid rgba(0,0,0,.13);
+}
+
+h3.sd-title {
+ font-size: 12px;
+ font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif;
+ margin: 3px 0;
+ padding: 0;
+ text-transform: none;
+ letter-spacing: 0;
+ line-height: 1;
+ font-weight: bold;
+ width: 15.625%; /* 100px / 640px */ float: left;
+ position: static;
+ background: none;
+ border: none;
+}
+
+.rtl .sd-title {
+ float: right;
+ text-align: right;
+}
+
+.entry-content .post-likes-widget, .post-likes-widget,
+.comment-likes-widget {
+ margin: 0;
+ border-width: 0;
+}
+
+.post-likes-widget-placeholder {
+ margin: 0;
+ border-width: 0;
+}
+
+.post-likes-widget-placeholder .button {
+ margin: 0;
+ padding: 0;
+ display: inline-block;
+ background: #efefef;
+ background: -moz-linear-gradient(top, #f7f7f7 0%, #efefef 100%);
+ background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,#f7f7f7), color-stop(100%,#efefef));
+ background: -webkit-linear-gradient(top, #f7f7f7 0%,#efefef 100%);
+ background: -o-linear-gradient(top, #f7f7f7 0%,#efefef 100%);
+ background: -ms-linear-gradient(top, #f7f7f7 0%,#efefef 100%);
+ background: linear-gradient(top, #f7f7f7 0%,#efefef 100%);
+ border-radius: 3px;
+ border: 1px solid #ddd !important;
+ box-shadow: inset 0 1px 0 #fff;
+ color: #999;
+ text-decoration: none;
+ line-height: 1;
+ font-size: 12px;
+ font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif;
+ font-weight: normal;
+}
+
+.post-likes-widget-placeholder .button span {
+ padding: 1px 5px 1px 2px;
+ display: block;
+ opacity: .8;
+ line-height: 1.5em;
+ text-shadow: none;
+}
+
+.post-likes-widget-placeholder .button span:before {
+ color: #97A8CC;
+ font-family: "Noticons";
+ content: '\2605';
+ font-size: 16px;
+ line-height: 0;
+ text-shadow: 0 1px 0 #fff;
+ position: relative;
+ top: 3px;
+}
+
+.post-likes-widget-placeholder .loading {
+ color: #999;
+ font-size: 12px;
+ font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif;
+}
+
+.post-likes-widget {
+ width: 82.125%;
+ display: none;
+ float: right;
+}
+
+/* Like Special cases (display on it's own) */
+
+div.sharedaddy.sd-like-enabled .sd-like h3 {
+ display: none;
+}
+
+div.sharedaddy.sd-like-enabled .sd-like .post-likes-widget {
+ width: 100%;
+ float: none;
+}
+
+div.sharedaddy.sd-rating-enabled .sd-like .post-likes-widget, div.sharedaddy.sd-sharing-enabled .sd-like .post-likes-widget {
+ width: 82.125%;
+ float: right;
+}
+
+div.sharedaddy.sd-rating-enabled .sd-like h3, div.sharedaddy.sd-sharing-enabled .sd-like h3 {
+ display: block;
+}
+
+.comment-likes-widget {
+ width: 100%;
+}
diff --git a/plugins/jetpack/modules/minileven.php b/plugins/jetpack/modules/minileven.php
new file mode 100644
index 00000000..e2047e50
--- /dev/null
+++ b/plugins/jetpack/modules/minileven.php
@@ -0,0 +1,108 @@
+<?php
+
+/**
+ * Module Name: Mobile Theme
+ * Module Description: Automatically optimize your site for mobile devices.
+ * Sort Order: 11
+ * First Introduced: 1.8
+ */
+
+function jetpack_load_minileven() {
+ include dirname( __FILE__ ) . "/minileven/minileven.php";
+
+ if ( get_option( 'wp_mobile_app_promos' ) != '1' )
+ remove_action( 'wp_mobile_theme_footer', 'jetpack_mobile_app_promo' );
+}
+
+add_action( 'jetpack_modules_loaded', 'minileven_loaded' );
+
+function minileven_loaded() {
+ Jetpack::enable_module_configurable( __FILE__ );
+ Jetpack::module_configuration_load( __FILE__, 'minileven_configuration_load' );
+ Jetpack::module_configuration_screen( __FILE__, 'minileven_configuration_screen' );
+}
+
+function minileven_configuration_load() {
+ if ( isset( $_POST['action'] ) && $_POST['action'] == 'save_options' && $_POST['_wpnonce'] == wp_create_nonce( 'minileven' ) ) {
+ if ( isset( $_POST['wp_mobile_excerpt'] ) )
+ update_option( 'wp_mobile_excerpt', '1' == $_POST['wp_mobile_excerpt'] ? '1' : '0' );
+
+ update_option( 'wp_mobile_app_promos', ( isset( $_POST['wp_mobile_app_promos'] ) ) ? '1' : '0' );
+
+ Jetpack::state( 'message', 'module_configured' );
+ wp_safe_redirect( Jetpack::module_configuration_url( 'minileven' ) );
+ exit;
+ }
+}
+
+function minileven_configuration_screen() {
+ $excerpts = ( 0 == get_option( 'wp_mobile_excerpt' ) ) ? 0 : 1;
+ $promos = ( '1' == get_option( 'wp_mobile_app_promos' ) ) ? 1 : 0;
+
+ ?>
+ <form method="post">
+ <input type="hidden" name="action" value="save_options" />
+ <?php wp_nonce_field( 'minileven' ); ?>
+ <table id="menu" class="form-table">
+ <tr valign="top">
+ <th scope="row"><?php _e( 'Excerpts', 'jetpack' ); ?></th>
+ <td>
+ <label>
+ <input name="wp_mobile_excerpt" type="radio" value="1" class="code" <?php checked( 1, $excerpts, true ); ?> />
+ <?php _e( 'Enable excerpts on front page and on archive pages', 'jetpack' ); ?>
+ </label>
+ <br />
+ <label>
+ <input name="wp_mobile_excerpt" type="radio" value="0" class="code" <?php checked( 0, $excerpts, true ); ?> />
+ <?php _e( 'Show full posts on front page and on archive pages', 'jetpack' ); ?>
+ </label>
+ </td>
+ </tr>
+ <tr valign="top">
+ <th scope="row"><?php _e( 'Mobile App Promos', 'jetpack' ); ?></th>
+ <td>
+ <label>
+ <input name="wp_mobile_app_promos" type="checkbox" value="1" <?php checked( 1, $promos, true ); ?> />
+ <?php _e ( 'Show a promo for the WordPress mobile apps in the footer of the mobile theme.', 'jetpack' ); ?>
+ </label>
+ </td>
+ </tr>
+ </table>
+ <p class="submit">
+ <input type="submit" class="button-primary" value="<?php esc_attr_e( __( 'Save configuration', 'jetpack' ) ); ?>" />
+ </p>
+ </form>
+ <h3><?php _e( 'Mobile Apps', 'jetpack' ); ?></h3>
+ <p><?php _e( 'Take WordPress with you.', 'jetpack' ); ?></p>
+ <a href="http://wordpress.org/extend/mobile/"><img src="<?php echo plugin_dir_url( __FILE__ ); ?>/minileven/images/wp-app-devices.png" width="332" height="73" /></a>
+ <p><?php printf( __( 'We have apps for <a href="%s">iOS (iPhone, iPad, iPod Touch)</a>, <a href="%s">Android</a>, <a href="%s">BlackBerry</a>, <a href="%s">Windows Phone</a>, and <a href="%s">more</a>!', 'jetpack' ), 'http://ios.wordpress.org/', 'http://android.wordpress.org/', 'http://blackberry.wordpress.org/', 'http://windowsphone.wordpress.org/', 'http://wordpress.org/extend/mobile/' ); ?></p>
+ <?php
+}
+
+function minileven_theme_root( $theme_root ) {
+ if ( jetpack_check_mobile() ) {
+ return dirname( __FILE__ ) . '/minileven/theme';
+ }
+
+ return $theme_root;
+}
+
+add_filter( 'theme_root', 'minileven_theme_root' );
+
+function minileven_theme_root_uri( $theme_root_uri ) {
+ if ( jetpack_check_mobile() ) {
+ return plugins_url( 'modules/minileven/theme', dirname( __FILE__ ) );
+ }
+
+ return $theme_root_uri;
+}
+
+add_filter( 'theme_root_uri', 'minileven_theme_root_uri' );
+
+function minileven_enabled( $wp_mobile_disable_option ) {
+ return true;
+}
+
+add_filter( 'option_wp_mobile_disable', 'minileven_enabled' );
+
+jetpack_load_minileven(); \ No newline at end of file
diff --git a/plugins/jetpack/modules/minileven/images/wp-app-devices.png b/plugins/jetpack/modules/minileven/images/wp-app-devices.png
new file mode 100644
index 00000000..4b5f4be9
--- /dev/null
+++ b/plugins/jetpack/modules/minileven/images/wp-app-devices.png
Binary files differ
diff --git a/plugins/jetpack/modules/minileven/minileven.php b/plugins/jetpack/modules/minileven/minileven.php
new file mode 100644
index 00000000..ae2ab016
--- /dev/null
+++ b/plugins/jetpack/modules/minileven/minileven.php
@@ -0,0 +1,313 @@
+<?php
+
+// ********** modify blog option 'wp_mobile_template' manually to specify a theme (ex. 'vip/cnnmobile')
+
+// WordPress Mobile Edition
+//
+// Copyright (c) 2002-2008 Alex King
+// http://alexking.org/projects/wordpress
+//
+// Released under the GPL license
+// http://www.opensource.org/licenses/gpl-license.php
+//
+// **********************************************************************
+// 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.
+// *****************************************************************
+
+/*
+Plugin Name: WordPress Mobile Edition
+Plugin URI: http://alexking.org/projects/wordpress
+Description: Show a mobile view of the post/page if the visitor is on a known mobile device. Questions on configuration, etc.? Make sure to read the README.
+Author: Alex King
+Author URI: http://alexking.org
+Version: 2.1a-WPCOM
+*/
+
+$_SERVER['REQUEST_URI'] = ( isset($_SERVER['REQUEST_URI']) ? $_SERVER['REQUEST_URI'] : $_SERVER['SCRIPT_NAME'] . (( isset($_SERVER['QUERY_STRING']) ? '?' . $_SERVER['QUERY_STRING'] : '')));
+
+function jetpack_check_mobile() {
+ if ( ( defined('XMLRPC_REQUEST') && XMLRPC_REQUEST ) || ( defined('APP_REQUEST') && APP_REQUEST ) )
+ return false;
+ if ( !isset($_SERVER["HTTP_USER_AGENT"]) || (isset($_COOKIE['akm_mobile']) && $_COOKIE['akm_mobile'] == 'false') )
+ return false;
+ if ( jetpack_mobile_exclude() )
+ return false;
+ if ( 1 == get_option('wp_mobile_disable') )
+ return false;
+ if ( isset($_COOKIE['akm_mobile']) && $_COOKIE['akm_mobile'] == 'true' )
+ return true;
+
+ $is_mobile = jetpack_is_mobile();
+
+ return apply_filters( 'jetpack_check_mobile', $is_mobile );
+}
+
+function jetpack_mobile_exclude() {
+ $exclude = false;
+ $pages_to_exclude = array(
+ 'wp-admin',
+ 'wp-comments-post.php',
+ 'wp-mail.php',
+ 'wp-login.php',
+ 'wp-activate.php',
+ );
+ foreach ( $pages_to_exclude as $exclude_page ) {
+ if ( strstr( strtolower( $_SERVER['REQUEST_URI'] ), $exclude_page ) )
+ $exclude = true;
+ }
+
+ if ( defined( 'DOING_AJAX' ) && true === DOING_AJAX )
+ $exclude = false;
+
+ if ( isset( $GLOBALS['wp_customize'] ) )
+ return true;
+
+ return $exclude;
+}
+
+function wp_mobile_get_main_template() {
+ remove_action( 'option_template', 'jetpack_mobile_template' );
+ $template = get_option( 'template' );
+ add_action( 'option_template', 'jetpack_mobile_template' );
+ return $template;
+}
+
+function wp_mobile_get_main_stylesheet() {
+ remove_action( 'option_stylesheet', 'jetpack_mobile_stylesheet' );
+ $stylesheet = get_option( 'stylesheet' );
+ add_action( 'option_stylesheet', 'jetpack_mobile_stylesheet' );
+ return $stylesheet;
+}
+
+function jetpack_mobile_stylesheet( $theme ) {
+ return apply_filters( 'jetpack_mobile_stylesheet', 'pub/minileven', $theme );
+}
+
+function jetpack_mobile_template( $theme ) {
+ return apply_filters( 'jetpack_mobile_template', 'pub/minileven', $theme );
+}
+
+function jetpack_mobile_available() {
+ echo '<div style="text-align:center;margin:10px 0;"><a href="'. home_url( '?ak_action=accept_mobile' ) . '">' . __( 'View Mobile Site', 'jetpack' ) . '</a></div>';
+}
+
+function jetpack_mobile_request_handler() {
+ global $wpdb;
+ if (isset($_GET['ak_action'])) {
+ $url = parse_url( get_bloginfo( 'url' ) );
+ $domain = $url['host'];
+ if (!empty($url['path'])) {
+ $path = $url['path'];
+ }
+ else {
+ $path = '/';
+ }
+ $redirect = false;
+ switch ($_GET['ak_action']) {
+ case 'reject_mobile':
+ setcookie(
+ 'akm_mobile'
+ , 'false'
+ , time() + 300000
+ , $path
+ , $domain
+ );
+ $redirect = true;
+
+ do_action( 'mobile_reject_mobile' );
+ break;
+ case 'force_mobile':
+ case 'accept_mobile':
+ setcookie(
+ 'akm_mobile'
+ , 'true'
+ , time() + 300000
+ , $path
+ , $domain
+ );
+ $redirect = true;
+
+ do_action( 'mobile_force_mobile' );
+ break;
+ }
+ if ($redirect) {
+ if ( isset( $_GET['redirect_to'] ) && $_GET['redirect_to'] ) {
+ $go = urldecode( $_GET['redirect_to'] );
+ } else if (!empty($_SERVER['HTTP_REFERER'])) {
+ $go = $_SERVER['HTTP_REFERER'];
+ }
+ else {
+ $go = remove_query_arg( array( 'ak_action' ) );
+ }
+ wp_safe_redirect( $go );
+ exit;
+ }
+ }
+}
+add_action('init', 'jetpack_mobile_request_handler');
+
+function jetpack_mobile_theme_setup() {
+ if ( jetpack_check_mobile() ) {
+ // Redirect to download page if user clicked mobile app promo link in mobile footer
+ if ( isset( $_GET['app-download'] ) ) {
+ do_action( 'mobile_app_promo_download', $_GET['app-download'] );
+
+ switch ( $_GET['app-download'] ) {
+ case 'android':
+ header( 'Location: market://search?q=pname:org.wordpress.android' );
+ exit;
+ break;
+ case 'ios':
+ header( 'Location: http://itunes.apple.com/us/app/wordpress/id335703880?mt=8' );
+ exit;
+ break;
+ case 'blackberry':
+ header( 'Location: http://blackberry.wordpress.org/download/' );
+ exit;
+ break;
+ case 'nokia':
+ header( 'Location: http://nokia.wordpress.org/download/' );
+ exit;
+ break;
+ case 'windowsphone':
+ header( 'Location: http://social.zune.net/redirect?type=phoneApp&id=5f64ad85-f801-e011-9264-00237de2db9e' );
+ exit;
+ break;
+ }
+ }
+
+ add_action('stylesheet', 'jetpack_mobile_stylesheet');
+ add_action('template', 'jetpack_mobile_template');
+ add_action('option_template', 'jetpack_mobile_template');
+ add_action('option_stylesheet', 'jetpack_mobile_stylesheet');
+
+ if ( class_exists( 'Jetpack_Custom_CSS' ) && method_exists( 'Jetpack_Custom_CSS', 'disable' ) && ! get_option( 'wp_mobile_custom_css' ) )
+ add_action( 'init', array( 'Jetpack_Custom_CSS', 'disable' ), 11 );
+
+ do_action( 'mobile_setup' );
+ }
+}
+
+// Need a hook after plugins_loaded (since this code won't be loaded in Jetpack
+// until then) but after init (because it has its own init hooks to add).
+add_action( 'setup_theme', 'jetpack_mobile_theme_setup' );
+
+if (isset($_COOKIE['akm_mobile']) && $_COOKIE['akm_mobile'] == 'false') {
+ add_action('wp_footer', 'jetpack_mobile_available');
+}
+
+add_action( 'wp_footer', 'mobile_admin_bar', 20 );
+function mobile_admin_bar() {
+ global $wp_version;
+
+ if ( jetpack_is_mobile() && 1 != version_compare( $wp_version, '3.5-beta3-22631' ) ) :
+ // This fix was made unnecessary in http://core.trac.wordpress.org/changeset/22636
+ ?>
+ <script type="text/javascript" id='mobile-admin-bar'>
+ jQuery( function( $ ) {
+ var menupop = $( '#wpadminbar .ab-top-menu > li.menupop' )
+ .unbind( 'mouseover' )
+ .unbind( 'mouseout' )
+ .click( function ( e ) {
+ $( this ).toggleClass( 'hover' );
+ $( '#wpadminbar .menupop' ).not( this ).removeClass( 'hover' );
+ } )
+ .children( 'a' )
+ .click( function( e ) {
+ e.preventDefault();
+ } );
+ $( '#wpadminbar' ).css( 'position', 'absolute' );
+ $( '#ab-reblog-box' ).css( 'position', 'absolute' );
+ } );
+ </script>
+ <?php
+ endif;
+}
+
+function jetpack_mobile_app_promo() {
+ ?>
+ <script type="text/javascript">
+ if ( ! navigator.userAgent.match( /wp-(iphone|android|blackberry|nokia|windowsphone)/i ) ) {
+ if ( ( navigator.userAgent.match( /iphone/i ) ) || ( navigator.userAgent.match( /ipod/i ) ) )
+ document.write( '<span id="wpcom-mobile-app-promo" style="margin-top: 10px; font-size: 13px;"><strong>Now Available!</strong> <a href="/index.php?app-download=ios">Download WordPress for iOS</a></span><br /><br />' );
+ else if ( ( navigator.userAgent.match( /android/i ) ) && ( null == navigator.userAgent.match( /playbook/i ) && null == navigator.userAgent.match( /bb10/i ) ) )
+ document.write( '<span id="wpcom-mobile-app-promo" style="margin-top: 10px; font-size: 13px;"><strong>Now Available!</strong> <a href="/index.php?app-download=android">Download WordPress for Android</a></span><br /><br />' );
+ else if ( ( navigator.userAgent.match( /blackberry/i ) ) || ( navigator.userAgent.match( /playbook/i ) ) || ( navigator.userAgent.match( /bb10/i ) ) )
+ document.write( '<span id="wpcom-mobile-app-promo" style="margin-top: 10px; font-size: 13px;"><strong>Now Available!</strong> <a href="/index.php?app-download=blackberry">Download WordPress for BlackBerry</a></span><br /><br />' );
+ else if ( ( navigator.userAgent.match( /windows phone os/i ) ) )
+ document.write( '<span id="wpcom-mobile-app-promo" style="margin-top: 10px; font-size: 13px; line-height: 13px;"><strong>Now Available!</strong> <a href="/index.php?app-download=windowsphone">Download WordPress for <br />Windows Phone</a></span><br /><br />' );
+ else if ( ( navigator.userAgent.match( /nokia/i ) ) )
+ document.write( '<span id="wpcom-mobile-app-promo" style="margin-top: 10px; font-size: 13px;"><strong>Now Available!</strong> <a href="/index.php?app-download=nokia">Download WordPress for Nokia</a></span><br /><br />' );
+ }
+ </script>
+ <?php
+}
+
+add_action( 'wp_mobile_theme_footer', 'jetpack_mobile_app_promo' );
+
+/**
+ * Adds an option to allow your Custom CSS to also be applied to the Mobile Theme.
+ * It's disabled by default, but this should allow people who know what they're
+ * doing to customize the mobile theme.
+ */
+function jetpack_mobile_css_settings() {
+ $mobile_css = get_option( 'wp_mobile_custom_css' );
+
+ ?>
+ <div class="misc-pub-section">
+ <label><?php esc_html_e( 'Mobile-compatible:', 'jetpack' ); ?></label>
+ <span id="mobile-css-display"><?php echo $mobile_css ? __( 'Yes', 'jetpack' ) : __( 'No', 'jetpack' ); ?></span>
+ <a class="edit-mobile-css hide-if-no-js" href="#mobile-css"><?php echo esc_html_e( 'Edit', 'jetpack' ); ?></a>
+ <div id="mobile-css-select" class="hide-if-js">
+ <input type="hidden" name="mobile_css" id="mobile-css" value="<?php echo intval( $mobile_css ); ?>" />
+ <label>
+ <input type="checkbox" id="mobile-css-visible" <?php checked( get_option( 'wp_mobile_custom_css' ) ); ?> />
+ <?php esc_html_e( 'Include this CSS in the Mobile Theme', 'jetpack' ); ?>
+ </label>
+ <p>
+ <a class="save-mobile-css hide-if-no-js button" href="#mobile-css"><?php esc_html_e( 'OK', 'jetpack' ); ?></a>
+ <a class="cancel-mobile-css hide-if-no-js" href="#mobile-css"><?php esc_html_e( 'Cancel', 'jetpack' ); ?></a>
+ </p>
+ </div>
+ </div>
+ <script type="text/javascript">
+ jQuery( function ( $ ) {
+ $( '.edit-mobile-css' ).bind( 'click', function ( e ) {
+ e.preventDefault();
+
+ $( '#mobile-css-select' ).slideDown();
+ $( this ).hide();
+ } );
+
+ $( '.cancel-mobile-css' ).bind( 'click', function ( e ) {
+ e.preventDefault();
+
+ $( '#mobile-css-select' ).slideUp( function () {
+ $( '.edit-mobile-css' ).show();
+
+ $( '#mobile-css-visible' ).prop( 'checked', $( '#mobile-css' ).val() == '1' );
+ } );
+ } );
+
+ $( '.save-mobile-css' ).bind( 'click', function ( e ) {
+ e.preventDefault();
+
+ $( '#mobile-css-select' ).slideUp();
+ $( '#mobile-css-display' ).text( $( '#mobile-css-visible' ).prop( 'checked' ) ? 'Yes' : 'No' );
+ $( '#mobile-css' ).val( $( '#mobile-css-visible' ).prop( 'checked' ) ? '1' : '0' );
+ $( '.edit-mobile-css' ).show();
+ } );
+ } );
+ </script>
+ <?php
+}
+
+add_action( 'custom_css_submitbox_misc_actions', 'jetpack_mobile_css_settings' );
+
+function jetpack_mobile_save_css_settings() {
+ update_option( 'wp_mobile_custom_css', isset( $_POST['mobile_css'] ) && ! empty( $_POST['mobile_css'] ) );
+}
+
+add_action( 'safecss_save_pre', 'jetpack_mobile_save_css_settings' );
diff --git a/plugins/jetpack/modules/minileven/theme/pub/minileven/comments.php b/plugins/jetpack/modules/minileven/theme/pub/minileven/comments.php
new file mode 100644
index 00000000..55c35161
--- /dev/null
+++ b/plugins/jetpack/modules/minileven/theme/pub/minileven/comments.php
@@ -0,0 +1,52 @@
+<?php
+/**
+ * The template for displaying Comments.
+ *
+ * The area of the page that contains both current comments
+ * and the comment form. The actual display of comments is
+ * handled by a callback to minileven_comment() which is
+ * located in the functions.php file.
+ *
+ * @package Minileven
+ */
+?>
+ <div id="comments">
+ <?php if ( post_password_required() ) : ?>
+ <p class="nopassword"><?php _e( 'This post is password protected. Enter the password to view any comments.', 'jetpack' ); ?></p>
+ </div><!-- #comments -->
+ <?php
+ /* Stop the rest of comments.php from being processed,
+ * but don't kill the script entirely -- we still have
+ * to fully load the template.
+ */
+ return;
+ endif;
+ ?>
+
+ <?php // You can start editing here -- including this comment! ?>
+
+ <?php comment_form(); ?>
+
+ <?php if ( have_comments() ) : ?>
+ <ol class="commentlist">
+ <?php
+ /* Loop through and list the comments. Tell wp_list_comments()
+ * to use minileven_comment() to format the comments.
+ * If you want to overload this in a child theme then you can
+ * define minileven_comment() and that will be used instead.
+ * See minileven_comment() in minileven/functions.php for more.
+ */
+ wp_list_comments( array( 'callback' => 'minileven_comment' ) );
+ ?>
+ </ol>
+
+ <?php if ( get_comment_pages_count() > 1 && get_option( 'page_comments' ) ) : // are there comments to navigate through ?>
+ <nav id="comment-nav-below">
+ <h1 class="assistive-text"><?php _e( 'Comment navigation', 'jetpack' ); ?></h1>
+ <div class="nav-previous"><?php previous_comments_link( __( '&larr; Older Comments', 'jetpack' ) ); ?></div>
+ <div class="nav-next"><?php next_comments_link( __( 'Newer Comments &rarr;', 'jetpack' ) ); ?></div>
+ </nav>
+ <?php endif; // check for comment navigation
+ endif; // check for the existence of comments
+ ?>
+ </div><!-- #comments --> \ No newline at end of file
diff --git a/plugins/jetpack/modules/minileven/theme/pub/minileven/content-gallery.php b/plugins/jetpack/modules/minileven/theme/pub/minileven/content-gallery.php
new file mode 100644
index 00000000..cf69f252
--- /dev/null
+++ b/plugins/jetpack/modules/minileven/theme/pub/minileven/content-gallery.php
@@ -0,0 +1,78 @@
+<?php
+/**
+ * The template for displaying posts in the Gallery Post Format on index and archive pages
+ *
+ * Learn more: http://codex.wordpress.org/Post_Formats
+ *
+ * @package Minileven
+ */
+?>
+
+<article id="post-<?php the_ID(); ?>" <?php post_class(); ?>>
+ <header class="entry-header">
+ <hgroup>
+ <h2 class="entry-title"><a href="<?php the_permalink(); ?>" title="<?php echo esc_attr( sprintf( __( 'Permalink to %s', 'jetpack' ), the_title_attribute( 'echo=0' ) ) ); ?>" rel="bookmark"><?php the_title(); ?></a></h2>
+ <h3 class="entry-format"><?php _e( 'Gallery', 'jetpack' ); ?></h3>
+ </hgroup>
+ </header><!-- .entry-header -->
+
+ <div class="entry-content">
+ <?php if ( is_single() ) : ?>
+ <?php the_content( __( 'Continue reading <span class="meta-nav">&rarr;</span>', 'jetpack' ) ); ?>
+
+ <?php else : ?>
+ <?php
+ $images = get_children( array( 'post_parent' => $post->ID, 'post_type' => 'attachment', 'post_mime_type' => 'image', 'orderby' => 'rand', 'order' => 'ASC', 'numberposts' => 999 ) );
+ if ( $images ) :
+ $total_images = count( $images );
+ $large_image = array_shift( $images );
+ $thumb1_image = array_shift( $images );
+ $thumb2_image = array_shift( $images );
+ $thumb3_image = array_shift( $images );
+
+ $image_img_tag = wp_get_attachment_image( $large_image->ID, 'large' );
+ $thumb1_img_tag = wp_get_attachment_image( $thumb1_image->ID, 'thumbnail' );
+ $thumb2_img_tag = wp_get_attachment_image( $thumb2_image->ID, 'thumbnail' );
+ $thumb3_img_tag = wp_get_attachment_image( $thumb3_image->ID, 'thumbnail' );
+ ?>
+ <div class="img-gallery">
+ <div class="gallery-large">
+ <a href="<?php the_permalink(); ?>"><?php echo $image_img_tag; ?></a>
+ </div><!-- .gallery-large -->
+ <?php if ( 3 == $total_images ) : ?>
+ <div class="gallery-thumbs-2">
+ <a href="<?php the_permalink(); ?>" class="gallery-thumb-1"><?php echo $thumb1_img_tag; ?></a>
+ <a href="<?php the_permalink(); ?>" class="gallery-thumb-2"><?php echo $thumb2_img_tag; ?></a>
+ </div><!-- .gallery-thumbs -->
+
+ <?php elseif ( 4 <= $total_images ) : ?>
+ <div class="gallery-thumbs-3">
+ <a href="<?php the_permalink(); ?>" class="gallery-thumb-1"><?php echo $thumb1_img_tag; ?></a>
+ <a href="<?php the_permalink(); ?>" class="gallery-thumb-2"><?php echo $thumb2_img_tag; ?></a>
+ <a href="<?php the_permalink(); ?>" class="gallery-thumb-3"><?php echo $thumb3_img_tag; ?></a>
+ </div><!-- .gallery-thumbs -->
+ </div><!-- .img-gallery -->
+ <?php endif; ?>
+
+ <p class="gallery-info"><em><?php printf( _n( 'This gallery contains <a %1$s>%2$s photo</a>.', 'This gallery contains <a %1$s>%2$s photos</a>.', $total_images, 'jetpack' ),
+ 'href="' . esc_url( get_permalink() ) . '" title="' . esc_attr( sprintf( __( 'Permalink to %s', 'jetpack' ), the_title_attribute( 'echo=0' ) ) ) . '" rel="bookmark"',
+ number_format_i18n( $total_images ) );
+ ?></em></p>
+
+ <?php endif; ?>
+ <?php endif; ?>
+
+ <?php wp_link_pages( array( 'before' => '<div class="page-link"><span>' . __( 'Pages:', 'jetpack' ) . '</span>', 'after' => '</div>' ) ); ?>
+</div><!-- .entry-content -->
+
+ <footer class="entry-meta">
+ <?php minileven_posted_on(); ?>
+ <?php if ( comments_open() ) : ?>
+ <span class="comments-link"><?php comments_popup_link( '<span class="leave-reply">' . __( 'Leave a Reply', 'jetpack' ) . '</span>', __( '<b>1</b> Reply', 'minileven' , 'jetpack'), __( '<b>%</b> Replies', 'minileven' , 'jetpack') ); ?></span>
+ <?php endif; // End if comments_open() ?>
+
+ <?php edit_post_link( __( 'Edit', 'jetpack' ), '<span class="edit-link">', '</span>' ); ?>
+ </footer><!-- #entry-meta -->
+</article><!-- #post-<?php the_ID(); ?> -->
+
+<?php comments_template( '', true ); ?> \ No newline at end of file
diff --git a/plugins/jetpack/modules/minileven/theme/pub/minileven/content.php b/plugins/jetpack/modules/minileven/theme/pub/minileven/content.php
new file mode 100644
index 00000000..9ab03516
--- /dev/null
+++ b/plugins/jetpack/modules/minileven/theme/pub/minileven/content.php
@@ -0,0 +1,60 @@
+<?php
+/**
+ * The default template for displaying content
+ *
+ * @package Minileven
+ */
+?>
+
+ <article id="post-<?php the_ID(); ?>" <?php post_class(); ?>>
+ <header class="entry-header">
+ <?php if ( is_sticky() ) : ?>
+ <hgroup>
+ <h2 class="entry-title"><a href="<?php the_permalink(); ?>" title="<?php echo esc_attr( sprintf( __( 'Permalink to %s', 'jetpack' ), the_title_attribute( 'echo=0' ) ) ); ?>" rel="bookmark"><?php the_title(); ?></a></h2>
+ <h3 class="entry-format"><?php _e( 'Featured', 'jetpack' ); ?></h3>
+ </hgroup>
+ <?php else : ?>
+ <h1 class="entry-title"><a href="<?php the_permalink(); ?>" title="<?php echo esc_attr( sprintf( __( 'Permalink to %s', 'jetpack' ), the_title_attribute( 'echo=0' ) ) ); ?>" rel="bookmark"><?php the_title(); ?></a></h1>
+ <?php endif; ?>
+
+ <div class="entry-meta">
+ <?php if ( is_singular() && is_multi_author() ) : ?>
+ <span class="author-link">
+ <?php _e( 'Posted by ', 'jetpack' ); ?>
+ <a href="<?php echo esc_url( get_author_posts_url( get_the_author_meta( 'ID' ) ) ); ?>" rel="author">
+ <?php printf( __( '%s', 'jetpack' ), get_the_author() ); ?>
+ </a>
+ </span><!-- .author-link -->
+ <?php endif; ?>
+ </div><!-- .entry-meta -->
+ </header><!-- .entry-header -->
+
+ <div class="entry-content">
+ <?php if ( '1' == get_option( 'wp_mobile_excerpt' ) && is_home() || is_search() || is_archive() ) : ?>
+ <?php the_excerpt(); ?>
+ <?php else : ?>
+ <?php the_content( __( 'Continue reading <span class="meta-nav">&rarr;</span>', 'jetpack' ) ); ?>
+ <?php endif; ?>
+ <?php wp_link_pages( array( 'before' => '<div class="page-link"><span>' . __( 'Pages:', 'jetpack' ) . '</span>', 'after' => '</div>' ) ); ?>
+ </div><!-- .entry-content -->
+
+ <footer class="entry-meta">
+ <?php if ( 'post' == get_post_type() ) : ?>
+ <?php minileven_posted_on(); ?>
+ <?php endif; ?>
+ <?php if ( comments_open() ) : ?>
+ <span class="comments-link"><?php comments_popup_link( '<span class="leave-reply">' . __( 'Leave a reply', 'jetpack' ) . '</span>', __( '<b>1</b> Reply', 'minileven' , 'jetpack'), __( '<b>%</b> Replies', 'minileven' , 'jetpack') ); ?></span>
+ <?php endif; // End if comments_open() ?>
+ <?php edit_post_link( __( 'Edit', 'jetpack' ), '<span class="edit-link">', '</span>' ); ?>
+ </footer><!-- #entry-meta -->
+ </article><!-- #post-<?php the_ID(); ?> -->
+
+ <?php if ( is_single() ) : ?>
+ <nav id="nav-single">
+ <h3 class="assistive-text"><?php _e( 'Post navigation', 'jetpack' ); ?></h3>
+ <span class="nav-previous"><?php previous_post_link( '%link', __( '&laquo; Previous', 'jetpack' ) ); ?></span>
+ <span class="nav-next"><?php next_post_link( '%link', __( 'Next &raquo;', 'jetpack' ) ); ?></span>
+ </nav><!-- #nav-single -->
+ <?php endif; ?>
+
+ <?php comments_template( '', true ); ?> \ No newline at end of file
diff --git a/plugins/jetpack/modules/minileven/theme/pub/minileven/footer.php b/plugins/jetpack/modules/minileven/theme/pub/minileven/footer.php
new file mode 100644
index 00000000..b739fe46
--- /dev/null
+++ b/plugins/jetpack/modules/minileven/theme/pub/minileven/footer.php
@@ -0,0 +1,36 @@
+<?php
+/**
+ * The template for displaying the footer.
+ *
+ * Contains the closing of the id=main div and all content after
+ *
+ * @package Minileven
+ */
+?>
+
+ </div><!-- #main -->
+</div><!-- #page -->
+<?php get_sidebar(); ?>
+
+</div><!-- #wrapper -->
+
+<footer id="colophon" role="contentinfo">
+ <div id="site-generator">
+
+<?php
+ global $wp;
+ $current_url = trailingslashit( home_url( add_query_arg( array(), $wp->request ) ) );
+?>
+ <a href="<?php echo $current_url . '?ak_action=reject_mobile'; ?>">View Full Site</a><br />
+ <?php do_action( 'wp_mobile_theme_footer' ); ?>
+ <?php do_action( 'minileven_credits' ); ?>
+ <a href="<?php echo esc_url( __( 'http://wordpress.org/', 'jetpack' ) ); ?>" title="<?php esc_attr_e( 'Semantic Personal Publishing Platform', 'minileven' , 'jetpack'); ?>" rel="generator"><?php printf( __( 'Proudly powered by %s', 'minileven' , 'jetpack'), 'WordPress' ); ?></a>
+ </div>
+</footer><!-- #colophon -->
+
+
+
+<?php wp_footer(); ?>
+
+</body>
+</html> \ No newline at end of file
diff --git a/plugins/jetpack/modules/minileven/theme/pub/minileven/functions.php b/plugins/jetpack/modules/minileven/theme/pub/minileven/functions.php
new file mode 100644
index 00000000..d13cb234
--- /dev/null
+++ b/plugins/jetpack/modules/minileven/theme/pub/minileven/functions.php
@@ -0,0 +1,152 @@
+<?php
+/**
+ * Minileven functions and definitions
+ *
+ * Sets up the theme and provides some helper functions. Some helper functions
+ * are used in the theme as custom template tags. Others are attached to action and
+ * filter hooks in WordPress to change core functionality.
+ *
+ * The first function, minileven_setup(), sets up the theme by registering support
+ * for various features in WordPress, such as post thumbnails, navigation menus, and the like.
+ *
+ * @package Minileven
+ */
+
+/**
+ * Set the content width based on the theme's design and stylesheet.
+ */
+if ( ! isset( $content_width ) )
+ $content_width = 584;
+
+/**
+ * Tell WordPress to run minileven_setup() when the 'after_setup_theme' hook is run.
+ */
+add_action( 'after_setup_theme', 'minileven_setup' );
+
+if ( ! function_exists( 'minileven_setup' ) ):
+/**
+ * Sets up theme defaults and registers support for various WordPress features.
+ */
+function minileven_setup() {
+ global $wp_version;
+
+ /**
+ * Custom template tags for this theme.
+ */
+ require( get_template_directory() . '/inc/template-tags.php' );
+
+ /**
+ * Custom functions that act independently of the theme templates
+ */
+ require( get_template_directory() . '/inc/tweaks.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
+ * to change 'minileven' to the name of your theme in all the template files.
+ */
+ load_theme_textdomain( 'minileven', TEMPLATEPATH . '/languages' );
+
+ // Add default posts and comments RSS feed links to <head>.
+ add_theme_support( 'automatic-feed-links' );
+
+ // This theme uses wp_nav_menu() in one location.
+ register_nav_menu( 'primary', __( 'Primary Menu', 'jetpack' ) );
+
+ // Add support for a variety of post formats
+ add_theme_support( 'post-formats', array( 'gallery' ) );
+
+ // Add support for custom backgrounds
+ if ( version_compare( $wp_version, '3.4', '>=' ) )
+ add_theme_support( 'custom-background' );
+ else
+ add_custom_background();
+
+ // Add support for post thumbnails
+ add_theme_support( 'post-thumbnails' );
+}
+endif; // minileven_setup
+
+/**
+ * Enqueue scripts and styles
+ */
+function minileven_scripts() {
+ global $post;
+
+ wp_enqueue_style( 'style', get_stylesheet_uri() );
+
+ wp_enqueue_script( 'small-menu', get_template_directory_uri() . '/js/small-menu.js', array( 'jquery' ), '20120206', true );
+
+ if ( is_singular() && comments_open() && get_option( 'thread_comments' ) ) {
+ wp_enqueue_script( 'comment-reply' );
+ }
+}
+add_action( 'wp_enqueue_scripts', 'minileven_scripts' );
+
+/**
+ * Register our sidebars and widgetized areas.
+ * @since Minileven 1.0
+ */
+function minileven_widgets_init() {
+ register_sidebar( array(
+ 'name' => __( 'Main Sidebar', 'jetpack' ),
+ 'id' => 'sidebar-1',
+ 'before_widget' => '<aside id="%1$s" class="widget %2$s">',
+ 'after_widget' => "</aside>",
+ 'before_title' => '<h3 class="widget-title">',
+ 'after_title' => '</h3>',
+ ) );
+}
+add_action( 'widgets_init', 'minileven_widgets_init' );
+
+function minileven_posts_per_page() {
+ return 5;
+}
+add_filter('pre_option_posts_per_page', 'minileven_posts_per_page');
+
+/* This function determines the actual theme the user is using. */
+function minileven_actual_current_theme() {
+ if ( function_exists( 'jetpack_mobile_template' ) )
+ remove_action( 'option_template', 'jetpack_mobile_template' );
+
+ $template = get_option( 'template' );
+
+ if ( function_exists( 'jetpack_mobile_template' ) )
+ add_action( 'option_template', 'jetpack_mobile_template' );
+
+ return $template;
+}
+
+/* This function grabs the location of the custom menus from the current theme. If no menu is set in a location
+* it will return a boolean "false". This function helps Minileven know which custom menu to display. */
+function minileven_get_menu_location() {
+ $theme_slug = minileven_actual_current_theme();
+ $mods = get_option( "theme_mods_{$theme_slug}" );
+
+ if ( isset( $mods['nav_menu_locations'] ) && ! empty( $mods['nav_menu_locations'] ) )
+ return $mods['nav_menu_locations'];
+
+ return false;
+}
+
+/* This function grabs the custom background image from the user's current theme so that Minileven can display it. */
+function minileven_get_background() {
+ $theme_slug = minileven_actual_current_theme();
+ $mods = get_option( "theme_mods_$theme_slug" );
+
+ if ( ! empty( $mods ) ) {
+ return array(
+ 'color' => isset( $mods['background_color'] ) ? $mods['background_color'] : null,
+ 'image' => isset( $mods['background_image'] ) ? $mods['background_image'] : null,
+ 'repeat' => isset( $mods['background_repeat'] ) ? $mods['background_repeat'] : null,
+ 'position' => isset( $mods['background_position_x'] ) ? $mods['background_position_x'] : null,
+ 'attachment' => isset( $mods['attachment'] ) ? $mods['attachment'] : null,
+ );
+ }
+ return false;
+}
+
+/**
+ * Implement the Custom Header functions
+ */
+require( get_template_directory() . '/inc/custom-header.php' ); \ No newline at end of file
diff --git a/plugins/jetpack/modules/minileven/theme/pub/minileven/header.php b/plugins/jetpack/modules/minileven/theme/pub/minileven/header.php
new file mode 100644
index 00000000..7c5f9a66
--- /dev/null
+++ b/plugins/jetpack/modules/minileven/theme/pub/minileven/header.php
@@ -0,0 +1,49 @@
+<?php
+/**
+ * The Header for our theme.
+ *
+ * Displays all of the <head> section and everything up till <div id="main">
+ *
+ * @package Minileven
+ */
+?><!DOCTYPE html>
+<html <?php language_attributes(); ?>>
+<head>
+<meta charset="<?php bloginfo( 'charset' ); ?>" />
+<meta name="viewport" content="width=device-width" />
+<title><?php wp_title( '|', true, 'right' ); ?></title>
+<link rel="profile" href="http://gmpg.org/xfn/11" />
+<link rel="pingback" href="<?php bloginfo( 'pingback_url' ); ?>" />
+<?php wp_head(); ?>
+
+<body <?php body_class(); ?>>
+<div id="wrapper">
+<div id="page" class="hfeed">
+ <header id="branding" role="banner">
+ <?php
+ minileven_header();
+ $location = minileven_get_menu_location(); // get the menu locations from the current theme in use
+ ?>
+ <div class="menu-search">
+ <nav id="access" role="navigation">
+ <div class="menu-handle">
+ <h3 class="assistive-text"><?php _e( 'Menu', 'jetpack' ); ?></h3>
+ </div><!-- .menu-handle -->
+ <?php /* Allow screen readers / text browsers to skip the navigation menu and get right to the good stuff. */ ?>
+ <div class="skip-link"><a class="assistive-text" href="#content" title="<?php esc_attr_e( 'Skip to primary content', 'jetpack' ); ?>"><?php _e( 'Skip to primary content', 'minileven' , 'jetpack'); ?></a></div>
+ <?php /* Our navigation menu. If one isn't filled out, wp_nav_menu falls back to wp_page_menu. The menu assiged to the primary position is the one used. If none is assigned, the menu with the lowest ID is used. */
+ if ( false !== $location ) :
+ $menu_id = array_shift( array_values( $location ) ); // acccess the ID of the menu assigned to that location. Using only the first menu ID returned in the array.
+ wp_nav_menu( array( 'theme_location' => 'primary', 'menu' => $menu_id ) );
+ else: // if the $location variable is false, wp_page_menu() is shown instead.
+ wp_nav_menu( array( 'theme_location' => 'primary' ) );
+ endif;
+ ?>
+ </nav><!-- #access -->
+ <div class="search-form">
+ <?php get_search_form(); ?>
+ </div><!-- .search-form-->
+ </div><!-- .menu-search-->
+ </header><!-- #branding -->
+
+ <div id="main"> \ No newline at end of file
diff --git a/plugins/jetpack/modules/minileven/theme/pub/minileven/image.php b/plugins/jetpack/modules/minileven/theme/pub/minileven/image.php
new file mode 100644
index 00000000..be8402bd
--- /dev/null
+++ b/plugins/jetpack/modules/minileven/theme/pub/minileven/image.php
@@ -0,0 +1,98 @@
+<?php
+/**
+ * The template for displaying image attachments.
+ *
+ * @package Minileven
+ */
+
+get_header(); ?>
+
+ <div id="primary" class="image-attachment">
+ <div id="content" role="main">
+
+ <?php while ( have_posts() ) : the_post(); ?>
+
+ <article id="post-<?php the_ID(); ?>" <?php post_class(); ?>>
+ <header class="entry-header">
+ <h1 class="entry-title"><?php the_title(); ?></h1>
+ </header><!-- .entry-header -->
+
+ <div class="entry-content">
+
+ <div class="entry-attachment">
+ <div class="attachment">
+<?php
+ /**
+ * Grab the IDs of all the image attachments in a gallery so we can get the URL of the next adjacent image in a gallery,
+ * or the first image (if we're looking at the last image in a gallery), or, in a gallery of one, just the link to that image file
+ */
+ $attachments = array_values( get_children( array( 'post_parent' => $post->post_parent, 'post_status' => 'inherit', 'post_type' => 'attachment', 'post_mime_type' => 'image', 'order' => 'ASC', 'orderby' => 'menu_order ID' ) ) );
+ foreach ( $attachments as $k => $attachment ) {
+ if ( $attachment->ID == $post->ID )
+ break;
+ }
+ $k++;
+ // If there is more than 1 attachment in a gallery
+ if ( count( $attachments ) > 1 ) {
+ if ( isset( $attachments[ $k ] ) )
+ // get the URL of the next image attachment
+ $next_attachment_url = get_attachment_link( $attachments[ $k ]->ID );
+ else
+ // or get the URL of the first image attachment
+ $next_attachment_url = get_attachment_link( $attachments[ 0 ]->ID );
+ } else {
+ // or, if there's only 1 image, get the URL of the image
+ $next_attachment_url = wp_get_attachment_url();
+ }
+?>
+ <a href="<?php echo esc_url( $next_attachment_url ); ?>" title="<?php echo esc_attr( get_the_title() ); ?>" rel="attachment"><?php
+ $attachment_size = apply_filters( 'minileven_attachment_size', 848 );
+ echo wp_get_attachment_image( $post->ID, array( $attachment_size, 1024 ) ); // filterable image width with 1024px limit for image height.
+ ?></a>
+
+ <?php if ( ! empty( $post->post_excerpt ) ) : ?>
+ <div class="entry-caption">
+ <?php the_excerpt(); ?>
+ </div>
+ <?php endif; ?>
+ </div><!-- .attachment -->
+
+ </div><!-- .entry-attachment -->
+
+ <div class="entry-description">
+ <?php the_content(); ?>
+ <?php wp_link_pages( array( 'before' => '<div class="page-link"><span>' . __( 'Pages:', 'jetpack' ) . '</span>', 'after' => '</div>' ) ); ?>
+ </div><!-- .entry-description -->
+
+ </div><!-- .entry-content -->
+
+ <footer class="entry-meta">
+ <div class="attachment-meta">
+ <?php
+ $metadata = wp_get_attachment_metadata();
+ printf( __( '<span class="entry-gallery">&laquo; <a href="%1$s" title="Back to %2$s" rel="gallery">Back to Gallery</a></span>', 'jetpack' ),
+ esc_url( get_permalink( $post->post_parent ) ),
+ get_the_title( $post->post_parent )
+ );
+ ?>
+ </div><!-- .attachment-meta-->
+ <?php if ( comments_open() ) : ?>
+ <span class="comments-link"><?php comments_popup_link( '<span class="leave-reply">' . __( 'Leave a reply', 'jetpack' ) . '</span>', __( '<b>1</b> Reply', 'minileven' , 'jetpack'), __( '<b>%</b> Replies', 'minileven' , 'jetpack') ); ?></span>
+ <?php endif; // End if comments_open() ?>
+ <?php edit_post_link( __( 'Edit', 'jetpack' ), '<span class="edit-link">', '</span>' ); ?>
+ </footer><!-- #entry-meta -->
+ </article><!-- #post-<?php the_ID(); ?> -->
+
+ <nav id="nav-single">
+ <h3 class="assistive-text"><?php _e( 'Image navigation', 'next-saturday' , 'jetpack' ); ?></h3>
+ <span class="nav-previous"><?php previous_image_link( false, __( '&raquo; Previous' , 'jetpack' ) ); ?></span>
+ <span class="nav-next"><?php next_image_link( false, __( 'Next &raquo; ' , 'jetpack' ) ); ?></span>
+ </nav><!-- #nav-single -->
+
+ <?php comments_template(); ?>
+
+ <?php endwhile; // end of the loop. ?>
+
+ </div><!-- #content -->
+ </div><!-- #primary -->
+<?php get_footer(); ?> \ No newline at end of file
diff --git a/plugins/jetpack/modules/minileven/theme/pub/minileven/inc/custom-header.php b/plugins/jetpack/modules/minileven/theme/pub/minileven/inc/custom-header.php
new file mode 100644
index 00000000..8a7a21e8
--- /dev/null
+++ b/plugins/jetpack/modules/minileven/theme/pub/minileven/inc/custom-header.php
@@ -0,0 +1,101 @@
+<?php
+/**
+ * @package Minileven
+ * @since Minileven 2.0
+ */
+
+/* This function grabs the custom header from the current theme so that Minileven can display it. */
+function minileven_get_header_image() {
+ $theme_slug = minileven_actual_current_theme();
+ $mods = get_option( "theme_mods_{$theme_slug}" );
+
+ if ( isset( $mods['header_image'] ) && 'remove-header' != $mods['header_image'] && 'random-default-image' != $mods['header_image'] && 'random-uploaded-image' != $mods['header_image'] )
+ return $mods['header_image'];
+
+ return false;
+}
+
+/* This function determines whether or not the user is displaying the header on the current theme */
+function minileven_header_text_display() {
+ $theme_slug = minileven_actual_current_theme();
+ $mods = get_option( "theme_mods_{$theme_slug}" );
+
+ if ( isset( $mods['header_textcolor'] ) )
+ return $mods['header_textcolor'];
+
+ return false;
+}
+
+/* This function determines how the header should be displayed. */
+function minileven_header() {
+ $header_image = minileven_get_header_image();
+ $header_text = minileven_header_text_display();
+
+ if ( 'blank' != $header_text ) : ?>
+ <hgroup>
+ <h1 id="site-title"><span><a href="<?php echo esc_url( home_url( '/' ) ); ?>" title="<?php echo esc_attr( get_bloginfo( 'name', 'display' ) ); ?>" rel="home"><?php bloginfo( 'name' ); ?></a></span></h1>
+ <h2 id="site-description"><?php bloginfo( 'description' ); ?></h2>
+ </hgroup>
+<?php endif;
+
+ if ( false !== $header_image ) : ?>
+ <div id="header-img">
+ <a href="<?php echo esc_url( home_url( '/' ) ); ?>">
+ <img src="<?php echo $header_image; ?>" alt="" />
+ </a>
+ </div><!-- #header-img -->
+<?php endif; // end check for header image existence.
+}
+
+/* This function displays the custom background image or color, and custom text color */
+function minileven_show_background_and_header_color() {
+ $background = minileven_get_background();
+ $header_text = minileven_header_text_display();
+
+ $style = '';
+
+ if ( $background['color'] || $background['image'] ) :
+ $style = $background['color'] ? "background-color: #$background[color];" : '';
+
+ if ( $background['image'] ) :
+ $image = " background-image: url('$background[image]');";
+
+ if ( ! in_array( $background['repeat'], array( 'no-repeat', 'repeat-x', 'repeat-y', 'repeat' ) ) )
+ $background['repeat'] = 'repeat';
+ $repeat = " background-repeat: $background[repeat];";
+
+ if ( ! in_array( $background['position'], array( 'center', 'right', 'left' ) ) )
+ $background['position'] = 'left';
+ $position = " background-position: top $background[position];";
+
+ if ( ! in_array( $background['attachment'], array( 'fixed', 'scroll' ) ) )
+ $background['attachment'] = 'scroll';
+ $attachment = " background-attachment: $background[attachment];";
+
+ $style .= $image . $repeat . $position . $attachment;
+ endif;
+ endif;
+?>
+ <style type="text/css">
+ <?php if ( $style ) { ?>
+ body {
+ <?php echo trim( $style ); ?>
+ }
+ <?php } ?>
+ #page {
+ margin: 0.6em 0.6em 0.8em;
+ }
+ #site-generator {
+ border: 0;
+ }
+ <?php if ( 'blank' != $header_text && '1' != get_option( 'wp_mobile_header_color' ) ) : ?>
+ /* If The user has set a header text color, use that */
+ #site-title,
+ #site-title a {
+ color: #<?php echo $header_text; ?>;
+ <?php endif; ?>
+ }
+ </style>
+<?php
+}
+add_action( 'wp_head', 'minileven_show_background_and_header_color' ); \ No newline at end of file
diff --git a/plugins/jetpack/modules/minileven/theme/pub/minileven/inc/template-tags.php b/plugins/jetpack/modules/minileven/theme/pub/minileven/inc/template-tags.php
new file mode 100644
index 00000000..2fc5f6d6
--- /dev/null
+++ b/plugins/jetpack/modules/minileven/theme/pub/minileven/inc/template-tags.php
@@ -0,0 +1,99 @@
+<?php
+/**
+ * Custom template tags for this theme.
+ *
+ * Eventually, some of the functionality here could be replaced by core features
+ *
+ * @package Minileven
+ * @since Minileven 2.0
+ */
+
+/**
+ * Display navigation to next/previous pages when applicable
+ */
+function minileven_content_nav( $nav_id ) {
+ global $wp_query;
+
+ if ( $wp_query->max_num_pages > 1 ) : ?>
+ <nav id="<?php echo $nav_id; ?>">
+ <h3 class="assistive-text"><?php _e( 'Post navigation', 'jetpack' ); ?></h3>
+ <div class="nav-previous"><?php next_posts_link( __( '<span class="meta-nav">&laquo;</span> Older', 'jetpack' ) ); ?></div>
+ <div class="nav-next"><?php previous_posts_link( __( 'Newer <span class="meta-nav">&raquo;</span>', 'jetpack' ) ); ?></div>
+ </nav><!-- #nav-above -->
+ <?php endif;
+}
+
+/**
+ * Template for comments and pingbacks.
+ * Used as a callback by wp_list_comments() for displaying the comments.
+ * @since Minileven 1.0
+ */
+function minileven_comment( $comment, $args, $depth ) {
+ $GLOBALS['comment'] = $comment;
+ switch ( $comment->comment_type ) :
+ case 'pingback' :
+ case 'trackback' :
+ ?>
+ <li class="post pingback">
+ <p><?php _e( 'Pingback:', 'jetpack' ); ?> <?php comment_author_link(); ?></p>
+ <?php
+ break;
+ default :
+ ?>
+ <li <?php comment_class(); ?> id="li-comment-<?php comment_ID(); ?>">
+ <article id="comment-<?php comment_ID(); ?>" class="comment">
+ <footer class="comment-meta">
+ <div class="comment-author vcard">
+ <?php
+ $avatar_size = 32;
+ if ( '0' != $comment->comment_parent )
+ $avatar_size = 24;
+
+ echo get_avatar( $comment, $avatar_size );
+
+ /* translators: 1: comment author, 2: date and time */
+ printf( __( '%1$s on %2$s', 'jetpack' ),
+ sprintf( '<span class="fn">%s</span>', get_comment_author_link() ),
+ sprintf( '<a href="%1$s"><time pubdate datetime="%2$s">%3$s</time></a>',
+ esc_url( get_comment_link( $comment->comment_ID ) ),
+ get_comment_time( 'c' ),
+ /* translators: 1: date, 2: time */
+ sprintf( __( '%1$s at %2$s', 'jetpack' ), get_comment_date(), get_comment_time() )
+ )
+ );
+ ?>
+
+ <?php edit_comment_link( __( 'Edit', 'jetpack' ), '<span class="edit-link">', '</span>' ); ?>
+ </div><!-- .comment-author .vcard -->
+
+ <?php if ( $comment->comment_approved == '0' ) : ?>
+ <em class="comment-awaiting-moderation"><?php _e( 'Your comment is awaiting moderation.', 'jetpack' ); ?></em>
+ <br />
+ <?php endif; ?>
+
+ </footer>
+
+ <div class="comment-content"><?php comment_text(); ?></div>
+
+ <div class="reply">
+ <?php comment_reply_link( array_merge( $args, array( 'reply_text' => __( 'Reply <span>&darr;</span>', 'jetpack' ), 'depth' => $depth, 'max_depth' => $args['max_depth'] ) ) ); ?>
+ </div><!-- .reply -->
+ </article><!-- #comment-## -->
+
+ <?php
+ break;
+ endswitch;
+}
+
+/**
+ * Prints HTML with meta information for the current post-date/time and author.
+ * @since Minileven 1.0
+ */
+function minileven_posted_on() {
+ printf( __( '<span class="entry-date"><a href="%1$s" title="%2$s" rel="bookmark"><time datetime="%3$s" pubdate>%4$s</time></a></span>', 'jetpack' ),
+ esc_url( get_permalink() ),
+ esc_attr( get_the_time() ),
+ esc_attr( get_the_date( 'c' ) ),
+ esc_html( get_the_date() )
+ );
+} \ No newline at end of file
diff --git a/plugins/jetpack/modules/minileven/theme/pub/minileven/inc/tweaks.php b/plugins/jetpack/modules/minileven/theme/pub/minileven/inc/tweaks.php
new file mode 100644
index 00000000..35a96720
--- /dev/null
+++ b/plugins/jetpack/modules/minileven/theme/pub/minileven/inc/tweaks.php
@@ -0,0 +1,94 @@
+<?php
+/**
+ * Custom functions that act independently of the theme templates
+ *
+ * Eventually, some of the functionality here could be replaced by core features
+ *
+ * @package Minileven
+ * @since Minileven 2.0
+ */
+
+/**
+ * Sets the post excerpt length to 40 words.
+ *
+ * To override this length in a child theme, remove the filter and add your own
+ * function tied to the excerpt_length filter hook.
+ */
+function minileven_excerpt_length( $length ) {
+ return 40;
+}
+add_filter( 'excerpt_length', 'minileven_excerpt_length' );
+
+/**
+ * Returns a "Continue Reading" link for excerpts
+ */
+function minileven_continue_reading_link() {
+ return ' <a href="'. esc_url( get_permalink() ) . '">' . __( 'Continue reading <span class="meta-nav">&rarr;</span>', 'jetpack' ) . '</a>';
+}
+
+/**
+ * Replaces "[...]" (appended to automatically generated excerpts) with an ellipsis and minileven_continue_reading_link().
+ */
+function minileven_auto_excerpt_more( $more ) {
+ return ' &hellip;' . minileven_continue_reading_link();
+}
+add_filter( 'excerpt_more', 'minileven_auto_excerpt_more' );
+
+/**
+ * Adds a pretty "Continue Reading" link to custom post excerpts.
+ *
+ * To override this link in a child theme, remove the filter and add your own
+ * function tied to the get_the_excerpt filter hook.
+ */
+function minileven_custom_excerpt_more( $output ) {
+ if ( has_excerpt() && ! is_attachment() ) {
+ $output .= minileven_continue_reading_link();
+ }
+ return $output;
+}
+add_filter( 'get_the_excerpt', 'minileven_custom_excerpt_more' );
+
+/**
+ * Get our wp_nav_menu() fallback, wp_page_menu(), to show a home link.
+ */
+function minileven_page_menu_args( $args ) {
+ $args['show_home'] = true;
+ return $args;
+}
+add_filter( 'wp_page_menu_args', 'minileven_page_menu_args' );
+
+/**
+ * Adds a custom class to the array of body classes, to allow Minileven to be targeted with Custom CSS.
+ */
+function minileven_body_classes( $classes ) {
+ $classes[] = 'mobile-theme';
+ return $classes;
+}
+add_filter( 'body_class', 'minileven_body_classes' );
+
+/**
+ * Filters wp_title to print a neat <title> tag based on what is being viewed.
+ *
+ * @since Minileven 2.0
+ */
+function minileven_wp_title( $title, $sep ) {
+ global $page, $paged;
+
+ if ( is_feed() )
+ return $title;
+
+ // Add the blog name
+ $title .= get_bloginfo( 'name' );
+
+ // Add the blog description for the home/front page.
+ $site_description = get_bloginfo( 'description', 'display' );
+ if ( $site_description && ( is_home() || is_front_page() ) )
+ $title .= " $sep $site_description";
+
+ // Add a page number if necessary:
+ if ( $paged >= 2 || $page >= 2 )
+ $title .= " $sep " . sprintf( __( 'Page %s', 'jetpack' ), max( $paged, $page ) );
+
+ return $title;
+}
+add_filter( 'wp_title', 'minileven_wp_title', 10, 2 );
diff --git a/plugins/jetpack/modules/minileven/theme/pub/minileven/index.php b/plugins/jetpack/modules/minileven/theme/pub/minileven/index.php
new file mode 100644
index 00000000..71aa73b6
--- /dev/null
+++ b/plugins/jetpack/modules/minileven/theme/pub/minileven/index.php
@@ -0,0 +1,73 @@
+<?php
+/**
+ * The main template file.
+ *
+ * This is the most generic template file in a WordPress theme
+ * and one of the two required files for a theme (the other being style.css).
+ * It is used to display a page when nothing more specific matches a query.
+ * E.g., it puts together the home page when no home.php file exists.
+ * Learn more: http://codex.wordpress.org/Template_Hierarchy
+ *
+ * @package Minileven
+ */
+
+get_header(); ?>
+
+ <div id="primary">
+ <div id="content" role="main">
+
+ <?php if ( is_archive() ) : ?>
+ <header class="page-header">
+ <h1 class="page-title">
+ <?php if ( is_day() ) : ?>
+ <?php printf( __( 'Daily Archives: %s', 'jetpack' ), '<span>' . get_the_date() . '</span>' ); ?>
+ <?php elseif ( is_month() ) : ?>
+ <?php printf( __( 'Monthly Archives: %s', 'jetpack' ), '<span>' . get_the_date( 'F Y' ) . '</span>' ); ?>
+ <?php elseif ( is_year() ) : ?>
+ <?php printf( __( 'Yearly Archives: %s', 'jetpack' ), '<span>' . get_the_date( 'Y' ) . '</span>' ); ?>
+ <?php elseif ( is_category() ) : ?>
+ <?php printf( __( 'Posted in %s', 'jetpack' ), '<span>' . single_cat_title( '', false ) . '</span>' ); ?>
+ <?php elseif ( is_tag() ) : ?>
+ <?php printf( __( 'Tagged with %s', 'jetpack' ), '<span>' . single_tag_title( '', false ) . '</span>' ); ?>
+ <?php elseif( is_author() ) : ?>
+ <?php printf( __( 'Posted by', 'jetpack' ), '<span>' . get_the_author() . '</span>' ); ?>
+ <?php else : ?>
+ <?php _e( 'Blog Archives', 'jetpack' ); ?>
+ <?php endif; ?>
+ </h1>
+ </header>
+ <?php endif; ?>
+
+ <?php if ( is_search() ) : ?>
+ <header class="page-header">
+ <h1 class="page-title"><?php printf( __( 'Search Results for: %s', 'jetpack' ), '<span>' . get_search_query() . '</span>' ); ?></h1>
+ </header>
+ <?php endif; ?>
+
+ <?php if ( have_posts() ) : // Start the loop ?>
+ <?php while ( have_posts() ) : the_post(); ?>
+
+ <?php get_template_part( 'content', get_post_format() ); ?>
+
+ <?php endwhile; ?>
+
+ <?php else : ?>
+ <article id="post-0" class="post error404 not-found">
+ <header class="entry-header">
+ <h1 class="entry-title"><?php _e( 'Nothing Found', 'jetpack' ); ?></h1>
+ </header><!-- .entry-header -->
+
+ <div class="entry-content">
+ <p><?php _e( 'Apologies, but no results were found for the requested archive. Perhaps searching will help find a related post.', 'jetpack' ); ?></p>
+ <?php get_search_form(); ?>
+ </div><!-- .entry-content -->
+ </article><!-- #post-0 -->
+
+ <?php endif; ?>
+
+ </div><!-- #content -->
+
+ <?php minileven_content_nav( 'nav-below' ); ?>
+
+ </div><!-- #primary -->
+<?php get_footer(); ?> \ No newline at end of file
diff --git a/plugins/jetpack/modules/minileven/theme/pub/minileven/js/small-menu.js b/plugins/jetpack/modules/minileven/theme/pub/minileven/js/small-menu.js
new file mode 100644
index 00000000..f6e1d060
--- /dev/null
+++ b/plugins/jetpack/modules/minileven/theme/pub/minileven/js/small-menu.js
@@ -0,0 +1,41 @@
+/**
+ * Handles toggling the main navigation menu for small screens.
+ */
+jQuery( document ).ready( function( $ ) {
+ var $subsidiary = $( '#branding' ),
+ timeout = false;
+
+ $.fn.smallMenu = function() {
+ $subsidiary.find( '#access' ).addClass( 'main-small-navigation' );
+ $subsidiary.find( '#access h3' ).removeClass( 'assistive-text' ).addClass( 'menu-label' );
+ $subsidiary.find( '#access .menu-handle' ).addClass( 'menu-toggle' );
+
+ $( '.menu-toggle' ).click( function() {
+ $subsidiary.find( '.menu' ).toggle();
+ $( this ).toggleClass( 'toggled-on' );
+ } );
+ };
+
+ // Check viewport width on first load.
+ if ( $( window ).width() < 4000 )
+ $.fn.smallMenu();
+
+ // Check viewport width when user resizes the browser window.
+ $( window ).resize( function() {
+ var browserWidth = $( window ).width();
+
+ if ( false !== timeout )
+ clearTimeout( timeout );
+
+ timeout = setTimeout( function() {
+ if ( browserWidth < 4000 ) {
+ $.fn.smallMenu();
+ } else {
+ $subsidiary.find( '#access' ).removeClass( 'main-small-navigation' );
+ $subsidiary.find( '#access h3' ).removeClass( 'menu-label' ).addClass( 'assistive-text' );
+ $subsidiary.find( '#access .menu-handle' ).removeClass( 'menu-toggle' );
+ $subsidiary.find( '.menu' ).removeAttr( 'style' );
+ }
+ }, 200 );
+ } );
+} ); \ No newline at end of file
diff --git a/plugins/jetpack/modules/minileven/theme/pub/minileven/page.php b/plugins/jetpack/modules/minileven/theme/pub/minileven/page.php
new file mode 100644
index 00000000..785137ef
--- /dev/null
+++ b/plugins/jetpack/modules/minileven/theme/pub/minileven/page.php
@@ -0,0 +1,42 @@
+<?php
+/**
+ * The template for displaying all pages.
+ *
+ * This is the template that displays all pages by default.
+ * Please note that this is the WordPress construct of pages
+ * and that other 'pages' on your WordPress site will use a
+ * different template.
+ *
+ * @package Minileven
+ */
+
+get_header(); ?>
+
+ <div id="primary">
+ <div id="content" role="main">
+
+ <?php while ( have_posts() ) : the_post(); ?>
+
+ <article id="post-<?php the_ID(); ?>" <?php post_class(); ?>>
+ <header class="entry-header">
+ <h1 class="entry-title"><?php the_title(); ?></h1>
+ </header><!-- .entry-header -->
+
+ <div class="entry-content">
+ <?php the_content(); ?>
+ <?php wp_link_pages( array( 'before' => '<div class="page-link"><span>' . __( 'Pages:', 'jetpack' ) . '</span>', 'after' => '</div>' ) ); ?>
+ </div><!-- .entry-content -->
+ <?php if ( is_user_logged_in() ) : ?>
+ <footer class="entry-meta">
+ <?php edit_post_link( __( 'Edit', 'jetpack' ), '<span class="edit-link">', '</span>' ); ?>
+ </footer><!-- .entry-meta -->
+ <?php endif; ?>
+ </article><!-- #post-<?php the_ID(); ?> -->
+
+ <?php comments_template( '', true ); ?>
+
+ <?php endwhile; // end of the loop. ?>
+
+ </div><!-- #content -->
+ </div><!-- #primary -->
+<?php get_footer(); ?> \ No newline at end of file
diff --git a/plugins/jetpack/modules/minileven/theme/pub/minileven/rtl.css b/plugins/jetpack/modules/minileven/theme/pub/minileven/rtl.css
new file mode 100644
index 00000000..7b67e1f2
--- /dev/null
+++ b/plugins/jetpack/modules/minileven/theme/pub/minileven/rtl.css
@@ -0,0 +1,582 @@
+/*
+Theme Name: Twenty Eleven
+
+Adding support for language written in a Right To Left (RTL) direction is easy -
+it's just a matter of overwriting all the horizontal positioning attributes
+of your CSS stylesheet in a separate stylesheet file named rtl.css.
+
+http://codex.wordpress.org/Right_to_Left_Language_Support
+
+*/
+
+/* =Reset reset
+----------------------------------------------- */
+
+caption, th, td {
+ text-align: right;
+}
+
+/* =Structure
+----------------------------------------------- */
+
+body {
+ direction:rtl;
+ unicode-bidi:embed;
+}
+
+/* Showcase */
+.page-template-showcase-php section.recent-posts {
+ float: left;
+ margin: 0 31% 0 0;
+}
+.page-template-showcase-php #main .widget-area {
+ float: right;
+ margin: 0 0 0 -22.15%;
+}
+
+/* One column */
+
+.one-column article.feature-image.small .entry-summary a {
+ left: auto;
+ right: -9%;
+}
+
+/* Simplify the pullquotes and pull styles */
+.one-column.singular .entry-meta .edit-link a {
+ right: 0px;
+ left: auto;
+}
+/* Make sure we have room for our comment avatars */
+.one-column .commentlist > li.comment {
+ margin-left: 0;
+ margin-right: 102px;
+}
+/* Make sure the logo and search form don't collide */
+.one-column #branding #searchform {
+ right: auto;
+ left: 40px;
+}
+/* Talking avatars take up too much room at this size */
+.one-column .commentlist > li.comment {
+ margin-right: 0;
+}
+.one-column .commentlist > li.comment .comment-meta,
+.one-column .commentlist > li.comment .comment-content {
+ margin-right: 0;
+ margin-left: 85px;
+}
+.one-column .commentlist .avatar {
+ right: auto;
+ left: 1.625em;
+}
+.one-column .commentlist .children .avatar {
+ left: auto;
+ right: 2.2em;
+}
+
+/* =Global
+----------------------------------------------- */
+
+/* Text elements */
+p {
+ margin-bottom: 1.625em;
+}
+ul, ol {
+ margin: 0 2.5em 1.625em 0;
+}
+.ltr ul, ol {
+ margin: 0 0 1.625em 2.5em;
+}
+blockquote {
+ font-family: Arial, sans-serif;
+}
+blockquote em, blockquote i, blockquote cite {
+ font-style: normal;
+}
+
+/* Forms */
+textarea {
+ padding-left: 0;
+ padding-right: 3px;
+}
+input#s {
+ background-position: 97% 6px;
+ padding: 4px 28px 4px 10px;
+}
+
+/* Assistive text */
+#access a.assistive-text:active,
+#access a.assistive-text:focus {
+ left: auto;
+ right: 7.6%;
+}
+
+/* =Header
+----------------------------------------------- */
+
+#site-title {
+ margin-right: 0;
+ margin-left: 270px;
+}
+
+#site-description {
+ margin: 0 0 3.65625em 270px;
+}
+
+/* =Menu
+-------------------------------------------------------------- */
+
+#access {
+ float: right;
+}
+#access ul {
+ margin: 0 -0.8125em 0 0;
+ padding-right: 0;
+}
+#access li {
+ float: right;
+}
+#access ul ul {
+ float: right;
+ left: auto;
+ right: 0;
+}
+#access ul ul ul {
+ left: auto;
+ right: 100%;
+}
+
+/* Search Form */
+#branding #searchform {
+ right: auto;
+ left: 7.6%;
+ text-align: left;
+}
+#branding #s {
+ float: left;
+}
+#branding .only-search + #access div {
+ padding-right: 0;
+ padding-left: 205px;
+}
+
+
+/* =Content
+----------------------------------------------- */
+.entry-title,
+.entry-header .entry-meta {
+ padding-right: 0;
+ padding-left: 76px;
+}
+.entry-content td,
+.comment-content td {
+ padding: 6px 0 6px 10px;
+}
+.page-link span {
+ margin-right: 0;
+ margin-left: 6px;
+}
+.entry-meta .edit-link a {
+ float: left;
+}
+/* Images */
+
+.wp-caption .wp-caption-text,
+.gallery-caption {
+ font-family: Arial, sans-serif;
+}
+.wp-caption .wp-caption-text {
+ padding: 10px 40px 5px 0px;
+}
+.wp-caption .wp-caption-text:before {
+ margin-right: 0;
+ margin-left: 5px;
+ left: auto;
+ right: 10px;
+}
+#content .gallery-columns-4 .gallery-item {
+ padding-right:0;
+ padding-left:2%;
+}
+
+/* Author Info */
+.singular #author-info {
+ margin: 2.2em -35.4% 0 -35.6%;
+}
+#author-avatar {
+ float: right;
+ margin-right: 0;
+ margin-left: -78px;
+}
+#author-description {
+ float: right;
+ margin-left: 0;
+ margin-right: 108px;
+}
+/* Comments link */
+.entry-header .comments-link a {
+ background-image: url(images/comment-bubble-rtl.png);
+ right: auto;
+ left: 0;
+}
+
+/*
+ Post Formats Headings
+*/
+.singular .entry-title,
+.singular .entry-header .entry-meta {
+ padding-left: 0;
+}
+.singular .entry-header .entry-meta {
+ left: auto;
+ right: 0;
+}
+.singular .entry-meta .edit-link a {
+ left: auto;
+ right: 50px;
+}
+
+
+/* =Gallery
+----------------------------------------------- */
+
+.format-gallery .gallery-thumb {
+ float: right;
+ margin: .375em 0 0 1.625em;
+}
+
+
+/* =Status
+----------------------------------------------- */
+
+.format-status img.avatar {
+ float: right;
+ margin: 4px 0 2px 10px;
+}
+
+
+/* =Image
+----------------------------------------------- */
+
+.indexed.format-image div.entry-meta {
+ float: right;
+}
+/* =error404
+----------------------
+------------------------- */
+.error404 #main .widget {
+ float: right;
+ margin-right: auto;
+ margin-left: 3.7%;
+}
+.error404 #main .widget_archive {
+ margin-left: 0;
+}
+.error404 #main .widget_tag_cloud {
+ margin-left: 0;
+}
+
+/* =Showcase
+----------------------------------------------- */
+
+article.intro .edit-link a {
+ right: auto;
+ left: 20px;
+}
+
+/* Featured post */
+section.featured-post {
+ float: right;
+}
+
+/* Small featured post */
+section.featured-post .attachment-small-feature {
+ float: left;
+ margin: 0 0 1.625em -8.9%;
+ right: auto;
+ left: -15px;
+}
+article.feature-image.small {
+ float: right;
+}
+article.feature-image.small .entry-summary p a {
+ left:auto;
+ right: -23.8%;
+ padding: 9px 85px 9px 26px;
+}
+
+/* Large featured post */
+section.feature-image.large .hentry {
+ left:auto;
+ right: 9%;
+ margin: 1.625em 0 0 9%;
+}
+/* Featured Slider */
+.featured-posts .showcase-heading {
+ padding-left: 0;
+ padding-right: 8.9%;
+}
+.featured-posts section.featured-post {
+ left: auto;
+ right: 0;
+}
+#content .feature-slider {
+ right: auto;
+ left: 8.9%;
+}
+.feature-slider li {
+ float: right;
+}
+/* Recent Posts */
+section.recent-posts .other-recent-posts a[rel="bookmark"] {
+ float: right;
+}
+section.recent-posts .other-recent-posts .comments-link a,
+section.recent-posts .other-recent-posts .comments-link > span {
+ padding: 0.3125em 1em 0.3125em 0;
+ left: 0;
+ text-align: left;
+}
+
+/* =Attachments
+----------------------------------------------- */
+
+/* =Navigation
+-------------------------------------------------------------- */
+
+.nav-previous {
+ float: right;
+}
+.nav-next {
+ float: left;
+ text-align: left;
+}
+
+/* Singular navigation */
+#nav-single {
+ float: left;
+ text-align: left;
+}
+#nav-single .nav-next {
+ padding-left: 0;
+ padding-right: .5em;
+}
+
+
+/* =Widgets
+----------------------------------------------- */
+
+.widget ul ul {
+ margin-left: 0;
+ margin-right: 1.5em;
+}
+
+/* Twitter */
+.widget_twitter .timesince {
+ margin-right: 0;
+ margin-left: -10px;
+ text-align: left;
+}
+
+/* =Comments
+----------------------------------------------- */
+
+.commentlist .children li.comment {
+ border-left: none;
+ border-right: 1px solid #ddd;
+ -moz-border-radius: 3px 0 0 3px;
+ border-radius: 3px 0 0 3px;
+}
+.commentlist .children li.comment .comment-meta {
+ margin-left: 0;
+ margin-right: 50px;
+}
+.commentlist .avatar {
+ left: auto;
+ right: -102px;
+}
+.commentlist > li:before {
+ content: url(images/comment-arrow-rtl.png);
+ left:auto;
+ right: -21px;
+}
+.commentlist > li.pingback:before {
+ content: '';
+}
+.commentlist .children .avatar {
+ left: auto;
+ right: 2.2em;
+}
+
+/* Post author highlighting */
+.commentlist > li.bypostauthor:before {
+ content: url(images/comment-arrow-bypostauthor-rtl.png);
+}
+
+/* sidebar-page.php comments */
+/* Make sure we have room for our comment avatars */
+.page-template-sidebar-page-php .commentlist > li.comment,
+.page-template-sidebar-page-php.commentlist .pingback {
+ margin-left: 0;
+ margin-right: 102px;
+}
+
+/* Comment Form */
+#respond .comment-form-author label,
+#respond .comment-form-email label,
+#respond .comment-form-url label,
+#respond .comment-form-comment label {
+ left: auto;
+ right: 4px;
+}
+#respond .comment-form-author label,
+#respond .comment-form-email label,
+#respond .comment-form-url label,
+#respond .comment-form-comment label {
+ -webkit-box-shadow: -1px 2px 2px rgba(204,204,204,0.8);
+ -moz-box-shadow: -1px 2px 2px rgba(204,204,204,0.8);
+ box-shadow: -1px 2px 2px rgba(204,204,204,0.8);
+}
+#respond .comment-form-author .required,
+#respond .comment-form-email .required {
+ left: auto;
+ right: 75%;
+}
+#respond .form-submit {
+ float: left;
+}
+#respond input#submit {
+ left: auto;
+ right: 30px;
+ padding: 5px 22px 5px 42px;
+}
+#respond #cancel-comment-reply-link {
+ margin-left: 0;
+ margin-right: 10px;
+}
+#cancel-comment-reply-link {
+ right: auto;
+ left: 1.625em;
+}
+
+/* =Footer
+----------------------------------------------- */
+
+/* Two Footer Widget Areas */
+#supplementary.two .widget-area {
+ float: right;
+ margin-right: 0;
+ margin-left: 3.7%;
+}
+#supplementary.two .widget-area + .widget-area {
+ margin-left: 0;
+}
+
+/* Three Footer Widget Areas */
+#supplementary.three .widget-area {
+ float: right;
+ margin-right: 0;
+ margin-left: 3.7%;
+}
+#supplementary.three .widget-area + .widget-area + .widget-area {
+ margin-left: 0;
+}
+
+/* Site Generator Line */
+#site-generator .sep {
+ background-position: right center;
+}
+
+
+/* =Responsive Structure
+----------------------------------------------- */
+
+@media (max-width: 800px) {
+ /* Simplify the showcase template when small feature */
+ section.featured-post .attachment-small-feature,
+ .one-column section.featured-post .attachment-small-feature {
+ float: right;
+ }
+ article.feature-image.small {
+ float: left;
+ }
+ article.feature-image.small .entry-summary p a {
+ right: 0;
+ }
+ .singular .entry-meta .edit-link a {
+ left: auto;
+ right: 0px;
+ }
+ /* Make sure we have room for our comment avatars */
+ .commentlist > li.comment,
+ .commentlist .pingback {
+ margin-left: 0;
+ margin-right: 102px;
+ }
+ /* No need to float footer widgets at this size */
+ #colophon #supplementary .widget-area {
+ margin-left: 0;
+ }
+ /* No need to float 404 widgets at this size */
+ .error404 #main .widget {
+ margin-left: 0;
+ }
+}
+@media (max-width: 650px) {
+ /* @media (max-width: 650px) Reduce font-sizes for better readability on smaller devices */
+ #site-title,
+ #site-description {
+ margin-left: 0;
+ }
+ /* Talking avatars take up too much room at this size */
+ .commentlist > li.comment,
+ .commentlist > li.pingback {
+ margin-right: 0 !important;
+ }
+ .commentlist .children .avatar {
+ left: auto;
+ right: 2.2em;
+ }
+ /* Use the available space in the smaller comment form */
+ #respond .comment-form-author .required,
+ #respond .comment-form-email .required {
+ left: auto;
+ right: 95%;
+ }
+ #content .gallery-columns-3 .gallery-item {
+ padding-right: 0;
+ padding-left:2%;
+ }
+}
+@media (max-width: 450px) {
+ #content .gallery-columns-2 .gallery-item {
+ padding-right:0;
+ padding-left:4%;
+ }
+}
+
+/* =Print
+----------------------------------------------- */
+
+@media print {
+ #primary {
+ float: right;
+ }
+ /* Comments */
+ .commentlist .avatar {
+ left: auto;
+ right: 2.2em;
+ }
+ .commentlist li.comment .comment-meta {
+ margin-left: 0;
+ margin-right: 50px;
+ }
+}
+
+/* =IE7
+----------------------------------------------- */
+
+#ie7 section.recent-posts {
+ margin-right: 0;
+ margin-left: 7.6%;
+}
diff --git a/plugins/jetpack/modules/minileven/theme/pub/minileven/screenshot.png b/plugins/jetpack/modules/minileven/theme/pub/minileven/screenshot.png
new file mode 100644
index 00000000..348e94c0
--- /dev/null
+++ b/plugins/jetpack/modules/minileven/theme/pub/minileven/screenshot.png
Binary files differ
diff --git a/plugins/jetpack/modules/minileven/theme/pub/minileven/searchform.php b/plugins/jetpack/modules/minileven/theme/pub/minileven/searchform.php
new file mode 100644
index 00000000..b4fd79aa
--- /dev/null
+++ b/plugins/jetpack/modules/minileven/theme/pub/minileven/searchform.php
@@ -0,0 +1,12 @@
+<?php
+/**
+ * The template for displaying search forms in Minileven
+ *
+ * @package Minileven
+ */
+?>
+ <form method="get" id="searchform" action="<?php echo esc_url( home_url( '/' ) ); ?>">
+ <label for="s" class="assistive-text"><?php _e( 'Search', 'jetpack' ); ?></label>
+ <input type="text" class="field" name="s" id="s" placeholder="<?php esc_attr_e( 'Search', 'jetpack' ); ?>" />
+ <input type="submit" class="submit" name="submit" id="searchsubmit" value="<?php esc_attr_e( 'Search', 'jetpack' ); ?>" />
+ </form>
diff --git a/plugins/jetpack/modules/minileven/theme/pub/minileven/sidebar.php b/plugins/jetpack/modules/minileven/theme/pub/minileven/sidebar.php
new file mode 100644
index 00000000..5d1a4a06
--- /dev/null
+++ b/plugins/jetpack/modules/minileven/theme/pub/minileven/sidebar.php
@@ -0,0 +1,12 @@
+<?php
+/**
+ * The Sidebar containing the main widget area.
+ *
+ * @package Minileven
+ */
+?>
+ <?php if ( is_active_sidebar( 'sidebar-1' ) ) : ?>
+ <div id="secondary" class="widget-area" role="complementary">
+ <?php dynamic_sidebar( 'sidebar-1' ); ?>
+ </div><!-- #secondary .widget-area -->
+ <?php endif; ?> \ No newline at end of file
diff --git a/plugins/jetpack/modules/minileven/theme/pub/minileven/style.css b/plugins/jetpack/modules/minileven/theme/pub/minileven/style.css
new file mode 100644
index 00000000..692a8f4f
--- /dev/null
+++ b/plugins/jetpack/modules/minileven/theme/pub/minileven/style.css
@@ -0,0 +1,1437 @@
+/*
+Theme Name: Minileven
+Theme URI: http://automattic.com
+Author: Automattic
+Author URI: http://automattic.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
+License URI: license.txt
+Tags: dark, light, white, black, gray, one-column, flexible-width, responsive-width, custom-background, custom-header, custom-menu, full-width-template, infinite-scroll, microformats, post-formats, rtl-language-support, sticky-post, theme-options, translation-ready, blog, bright, clean, contemporary, elegant, minimal, modern, photography, simple, tumblelog
+*/
+
+
+/* =Reset default browser CSS. Based on work by Eric Meyer: http://meyerweb.com/eric/tools/css/reset/index.html
+-------------------------------------------------------------- */
+
+html, body, div, span, applet, object, iframe,
+h1, h2, h3, h4, h5, h6, p, blockquote, pre,
+a, abbr, acronym, address, big, cite, code,
+del, dfn, em, font, ins, kbd, q, s, samp,
+small, strike, strong, sub, sup, tt, var,
+dl, dt, dd, ol, ul, li,
+fieldset, form, label, legend,
+table, caption, tbody, tfoot, thead, tr, th, td {
+ border: 0;
+ font-family: inherit;
+ font-size: 100%;
+ font-style: inherit;
+ font-weight: inherit;
+ margin: 0;
+ outline: 0;
+ padding: 0;
+ vertical-align: baseline;
+}
+:focus {/* remember to define focus styles! */
+ outline: 0;
+}
+body {
+ background: #fff;
+ line-height: 1;
+ -webkit-overflow-scrolling: touch;
+}
+ol, ul {
+ list-style: none;
+}
+a img {
+ border: 0;
+}
+article, aside, details, figcaption, figure,
+footer, header, hgroup, menu, nav, section {
+ display: block;
+}
+
+
+/* =Global
+----------------------------------------------- */
+
+body, input, textarea {
+ color: #373737;
+ font: 13px "Helvetica Neue", Helvetica, Arial, sans-serif;
+ line-height: 1.625;
+ word-wrap: break-word;
+}
+body {
+ background: #eee;
+}
+#page {
+ background: #fff;
+}
+
+/* Headings */
+h1,h2,h3,h4,h5,h6 {
+ clear: both;
+}
+hr {
+ background-color: #ccc;
+ border: 0;
+ height: 1px;
+ margin-bottom: 1.625em;
+}
+
+/* Text elements */
+p {
+ margin-bottom: 1.0em;
+}
+ul, ol {
+ margin: 0 0 1.625em 2.5em;
+}
+ul {
+ list-style: square;
+}
+ol {
+ list-style-type: decimal;
+}
+ul ul, ol ol, ul ol, ol ul {
+ margin-bottom: 0;
+}
+dl {
+ margin: 0 1.625em;
+}
+dt {
+ font-weight: bold;
+}
+dd {
+ margin-bottom: 1.625em;
+}
+strong {
+ font-weight: bold;
+}
+cite, em, i {
+ font-style: italic;
+}
+blockquote {
+ font-family: Georgia, "Bitstream Charter", serif;
+ font-style: italic;
+ font-weight: normal;
+ margin: 0;
+}
+blockquote em, blockquote i, blockquote cite {
+ font-style: normal;
+}
+blockquote cite {
+ color: #666;
+ font-size: 0.800em;
+ font-weight: 300;
+ letter-spacing: 0.05em;
+ text-transform: uppercase;
+}
+pre {
+ background: #f4f4f4;
+ font: 1em "Courier 10 Pitch", Courier, monospace;
+ line-height: 1.5;
+ margin-bottom: 1.625em;
+ overflow: auto;
+ padding: 0.75em 1.625em;
+}
+code, kbd {
+ font: 1em Monaco, Consolas, "Andale Mono", "DejaVu Sans Mono", monospace;
+}
+abbr, acronym, dfn {
+ border-bottom: 1px dotted #666;
+ cursor: help;
+}
+address {
+ display: block;
+ margin: 0 0 1.625em;
+}
+ins {
+ background: #fff9c0;
+ text-decoration: none;
+}
+sup,
+sub {
+ font-size: 0.667em;
+ height: 0;
+ line-height: 1;
+ position: relative;
+ vertical-align: baseline;
+}
+sup {
+ bottom: 1ex;
+}
+sub {
+ top: .5ex;
+}
+
+/* Forms */
+input[type=text],
+input[type=email],
+input[type=password],
+textarea {
+ background: #fafafa;
+ -moz-box-shadow: inset 0 1px 1px rgba(0,0,0,0.1);
+ -webkit-box-shadow: inset 0 1px 1px rgba(0,0,0,0.1);
+ box-shadow: inset 0 1px 1px rgba(0,0,0,0.1);
+ border: 1px solid #ddd;
+ color: #888;
+}
+input[type=text]:focus,
+input[type=email]:focus,
+textarea:focus {
+ color: #373737;
+}
+textarea {
+ padding-left: 3px;
+ width: 98%;
+}
+input[type=text],
+input[type=email] {
+ padding: 3px;
+}
+input#s {
+ border-radius: 2px;
+ height: 1.692em;
+ line-height: 1.2em;
+ padding: 0.4em 0.6em;
+}
+input#searchsubmit {
+ display: none;
+}
+
+/* Links */
+a {
+ color: #00a4bc;
+ text-decoration: none;
+}
+/* Assistive text */
+.assistive-text {
+ clip: rect(1px, 1px, 1px, 1px);
+ position: absolute !important;
+ visibility: hidden;
+}
+
+
+/* =Structure
+----------------------------------------------- */
+
+#page {
+ margin: 0 auto;
+ padding: 2.5%;
+}
+
+#primary,
+#secondary {
+ margin: 0 auto;
+ width: auto;
+}
+
+/* Alignment */
+.aligncenter,
+.alignleft,
+.alignright {
+ clear: both;
+ display: block;
+ margin-left: auto;
+ margin-right: auto;
+}
+
+/* Make sure embeds and iframes scale on smaller screens */
+embed,
+iframe,
+object {
+ height: auto;
+ width: auto;
+}
+
+
+/* =Header
+----------------------------------------------- */
+
+#branding hgroup {
+ margin-bottom: 1.3em;
+}
+#site-title,
+#site-description {
+ clear: none;
+}
+#site-title a {
+ color: #111;
+ font-size: 1.846em;
+ font-weight: bold;
+ line-height: 1.3em;
+}
+#site-description {
+ color: #7a7a7a;
+ font-size: 0.923em;
+}
+
+/* Header Image */
+#header-img {
+ text-align: center;
+}
+header img {
+ max-width: 100%;
+ height: auto;
+}
+
+
+/* =Navigation and Search Form
+----------------------------------------------- */
+
+.menu-search {
+ background: #dcdcdc;
+ background: -moz-linear-gradient(#dcdcdc, #c8c8c8);
+ background: -o-linear-gradient(#dcdcdc, #c8c8c8);
+ background: -webkit-gradient(linear, 0% 0%, 0% 100%, from(#dcdcdc), to(#c8c8c8)); /* older webkit syntax */
+ background: -webkit-linear-gradient(#dcdcdc, #c8c8c8);
+ clear: both;
+ margin-bottom: 1.3em;
+ width: 100%;
+}
+.menu-search:after {
+ clear: both;
+ content: "";
+ display: block;
+}
+
+/* Small menu */
+#access {
+ float: left;
+ width: 55%;
+}
+.search-form {
+ float: right;
+ text-align: right;
+ width: 43%;
+}
+.menu-toggle {
+ cursor: pointer;
+}
+#access .toggled-on {
+ background-color: #F1F1F1;
+ background-image: -ms-linear-gradient(top,#f9f9f9,#ececec);
+ background-image: -moz-linear-gradient(top,#f9f9f9,#ececec);
+ background-image: -o-linear-gradient(top,#f9f9f9,#ececec);
+ background-image: -webkit-gradient(linear,left top,left bottom,from(#f9f9f9),to(#ececec));
+ background-image: -webkit-linear-gradient(top,#f9f9f9,#ececec);
+ background-image: linear-gradient(top,#f9f9f9,#ececec);
+ border: 1px solid #e9e9e9;
+ border-bottom-width: 0;
+ box-shadow: 0 1px 0 #fff;
+}
+.main-small-navigation {
+ position: relative;
+ z-index: 99999;
+}
+.main-small-navigation .menu {
+ background: #f9f9f9;
+ border: 1px solid #e9e9e9;
+ position: absolute;
+ width: 100%;
+}
+.main-small-navigation ul li {
+ border-bottom: 1px solid #e9e9e9;
+ padding: 0.5em 0.8em;
+}
+.main-small-navigation ul li:last-of-type {
+ border: none;
+}
+.main-small-navigation ul ul li,
+.main-small-navigation ul ul ul li {
+ border: none;
+ padding-bottom: 0;
+}
+.main-small-navigation a {
+ color: #373737;
+ display: block;
+ font-size: 0.923em;
+ font-weight: bold;
+}
+.main-small-navigation ul {
+ list-style: none;
+ margin: 0;
+ padding: 0.5em 0;
+}
+.main-small-navigation .sub-menu {
+ margin: 0 0 0 15px;
+}
+#access .menu-label {
+ clear: none;
+ font-size: 1.077em;
+ font-weight: bold;
+ line-height: 2.5em;
+ padding-left: 0.8em;
+ text-shadow: 0 1px 0 #fff;
+}
+#access .menu-label:after {
+ color: #999;
+ content: "+";
+ cursor: pointer;
+ font-size: 1em;
+ font-weight: bold;
+ margin-left: 0.5em;
+ width: 18px;
+ height: 18px;
+ text-shadow: 0 1px 0 #fff;
+}
+#access .toggled-on .menu-label:after {
+ content: "-";
+}
+.main-small-navigation .menu {
+ display: none;
+}
+.search-form #s {
+ background: #eee;
+ border: 1px solid #ddd;
+ border-radius: 0;
+ color: #888;
+ font-size: 0.923em;
+ margin-right: 1%;
+ text-shadow: 0 1px 0 #fff;
+ padding: 0.6em;
+ width: 80%;
+}
+
+
+/* =Content
+----------------------------------------------- */
+
+.page-title {
+ color: #666;
+ font-size: 0.769em;
+ font-weight: 500;
+ letter-spacing: 0.1em;
+ line-height: 2.6em;
+ text-transform: uppercase;
+}
+.page-title a {
+ font-size: 0.923em;
+ font-weight: bold;
+ letter-spacing: 0;
+ text-transform: none;
+}
+.hentry,
+.no-results {
+ clear: both;
+ margin: 0 0 1.5em;
+ padding: 0 0 2em;
+ position: relative;
+}
+.hentry {
+ border-bottom: 1px solid #ddd;
+}
+.hentry:last-child,
+.no-results,
+body.singular .hentry {
+ border-bottom: none;
+}
+.blog .sticky .entry-header .entry-meta {
+ clip: rect(1px 1px 1px 1px); /* IE6, IE7 */
+ clip: rect(1px, 1px, 1px, 1px);
+ position: absolute !important;
+}
+.entry-title {
+ clear: both;
+ color: #222;
+ font-size: 1.615em;
+ font-weight: bold;
+ line-height: 1.5em;
+ padding-bottom: .3em;
+ word-wrap: break-word;
+}
+.featured-post .entry-title {
+ font-size: 1.077em;
+}
+.entry-title,
+.entry-title a {
+ color: #222;
+ text-decoration: none;
+}
+.entry-meta {
+ color: #666;
+ clear: both;
+ font-size: 0.923em;
+ line-height: 1.385em;
+ overflow: hidden;
+ padding: 0 0 0.6em 0;
+}
+.entry-meta a {
+ font-weight: bold;
+}
+.entry-content {
+ padding: 0;
+}
+.entry-content h1,
+.entry-content h2,
+.comment-content h1,
+.comment-content h2 {
+ color: #000;
+ font-weight: bold;
+ margin: 0 0 .8125em;
+}
+.entry-content h3,
+.comment-content h3 {
+ font-size: 0.769em;
+ letter-spacing: 0.1em;
+ line-height: 2.6em;
+ text-transform: uppercase;
+}
+.entry-content table,
+.comment-content table {
+ border-bottom: 1px solid #ddd;
+ margin: 0 0 1.625em;
+ width: 100%;
+}
+.entry-content th,
+.comment-content th {
+ color: #666;
+ font-size: 0.769em;
+ font-weight: 500;
+ letter-spacing: 0.1em;
+ line-height: 2.6em;
+ text-transform: uppercase;
+}
+.entry-content td,
+.comment-content td {
+ border-top: 1px solid #ddd;
+ padding: 0.600em 1em 0.600em 0;
+}
+.entry-content #s {
+ width: 75%;
+}
+.comment-content ul,
+.comment-content ol {
+ margin-bottom: 1.625em;
+}
+.comment-content ul ul,
+.comment-content ol ol,
+.comment-content ul ol,
+.comment-content ol ul {
+ margin-bottom: 0;
+}
+dl.gallery-item {
+ margin: 0;
+}
+.page-link {
+ clear: both;
+ display: block;
+ margin: 0 0 1em;
+}
+.page-link a {
+ background: #eee;
+ color: #373737;
+ margin: 0;
+ padding: 0.154em 0.231em;
+ text-decoration: none;
+}
+.page-link span {
+ margin-right: 0.462em;
+}
+.entry-meta .entry-date,
+.entry-meta .entry-gallery {
+ display: block;
+ float: left;
+}
+.entry-meta .comments-link {
+ display: block;
+ float: right;
+}
+.entry-meta .edit-link a {
+ margin-right: .5em;
+}
+.entry-meta .edit-link a,
+.commentlist .edit-link a {
+ background: #eee;
+ -moz-border-radius: 3px;
+ border-radius: 3px;
+ color: #666;
+ float: right;
+ font-size: 1em;
+ font-weight: normal;
+ line-height: 1.5em;
+ text-decoration: none;
+ padding: 0 0.615em;
+}
+.entry-content .edit-link {
+ clear: both;
+ display: block;
+}
+
+/* Images */
+.entry-content img,
+.comment-content img,
+.widget img {
+ display: block;
+ height: auto;
+ margin: 0 auto;
+ max-width: 100%; /* Fluid images for posts, comments, and widgets */
+}
+#content .gallery-columns-3 .gallery-item img,
+#content .gallery-columns-4 .gallery-item img,
+#content .gallery-columns-2 .gallery-item img {
+ width: 100%;
+ height: auto;
+}
+img[class*="align"],
+img[class*="wp-image-"],
+img[class*="attachment-"] {
+ height: auto; /* Make sure images with WordPress-added height and width attributes are scaled correctly */
+}
+img.size-full,
+img.size-large {
+ max-width: 100%;
+ width: auto; /* Prevent stretching of full-size and large-size images with height and width attributes in IE8 */
+ height: auto; /* Make sure images with WordPress-added height and width attributes are scaled correctly */
+}
+.entry-content img.wp-smiley {
+ border: none;
+ margin-bottom: 0;
+ margin-top: 0;
+ padding: 0;
+}
+img.alignleft,
+img.alignright,
+img.aligncenter {
+ margin-bottom: 1.625em;
+}
+p img,
+.wp-caption {
+ margin-top: 0.4em;
+}
+.wp-caption {
+ max-width: 96%;
+}
+.wp-caption img {
+ display: block;
+ margin: 0 auto;
+ max-width: 98%;
+}
+.wp-caption .wp-caption-text,
+.gallery-caption {
+ color: #666;
+ font-family: Georgia, serif;
+ font-size: 0.923em;
+}
+.wp-caption .wp-caption-text {
+ margin-bottom: 0.6em;
+ padding: 0.833em 0 0.417em 0;
+ position: relative;
+}
+#content .gallery {
+ margin: 0 auto 1.625em;
+}
+#content .gallery a img {
+ border: none;
+}
+#content .gallery-columns-3 .gallery-item {
+ width: 31%;
+ padding-right: 2%;
+}
+#content .gallery-columns-4 .gallery-item {
+ width: 23%;
+ padding-right: 2%;
+}
+#content .gallery-columns-2 .gallery-item {
+ width: 45%;
+ padding-right: 4%;
+}
+
+
+/* Make sure embeds and iframes fit their containers */
+embed,
+iframe,
+object {
+ max-width: 100%;
+}
+
+/* Password Protected Posts */
+.post-password-required .entry-header .comments-link {
+ margin: 1.625em 0 0;
+}
+.post-password-required input[type=password] {
+ margin: 0.8125em 0;
+}
+.post-password-required input[type=password]:focus {
+ background: #f7f7f7;
+}
+
+
+/*
+Post Formats Headings
+To hide the headings, display: none the ".entry-header .entry-format" selector,
+and remove the padding rules below.
+*/
+.entry-header .entry-format {
+ color: #666;
+ font-size: 10px;
+ font-weight: 500;
+ letter-spacing: 0.1em;
+ line-height: 2.6em;
+ position: absolute;
+ text-transform: uppercase;
+ top: -5px;
+}
+.entry-header hgroup .entry-title {
+ padding-top: 0.8em;
+}
+
+/* Singular content styles for Posts and Pages */
+.singular .hentry {
+ padding: 1.625em 0 0;
+ position: relative;
+}
+.page .hentry {
+ padding-bottom: .7em;
+}
+.singular .entry-meta .edit-link a {
+ bottom: auto;
+ left: 0;
+ position: absolute;
+ right: auto;
+ top: 40px;
+}
+.single-format-gallery .hentry {
+ margin-bottom: 0;
+}
+.singular #author-info {
+ margin: 2.2em -8.8% 0;
+ padding: 1.538em 8.8%;
+}
+
+
+/* =Gallery Posts
+----------------------------------------------- */
+
+#content .gallery {
+ margin-bottom: 0;
+}
+.format-gallery img {
+ margin: 0;
+}
+.format-gallery .gallery-large {
+ line-height: 1.2em;
+ margin: 0;
+ width: 100%;
+}
+.format-gallery .gallery-thumbs-2,
+ .format-gallery .gallery-thumbs-3 {
+ overflow: hidden;
+ width: 100%;
+}
+.format-gallery .gallery-thumbs-2 img {
+ margin-right: 0.3%;
+ max-width: 48%;
+}
+.format-gallery .gallery-thumbs-3 img {
+ float: left;
+ margin-left: 0.2%;
+ max-width: 33%;
+}
+.format-gallery .gallery-large,
+.format-gallery .gallery-thumbs-2 img,
+ .format-gallery .gallery-thumbs-3 img {
+ display: inline-block;
+}
+.format-gallery .gallery-thumbs-3 .gallery-thumb-1 img {
+ margin: 0;
+}
+.gallery-info {
+ margin-top: 1.3em;
+}
+
+/* =Quote Posts
+----------------------------------------------- */
+
+.format-quote blockquote {
+ color: #555;
+ font-size: 1.308em;
+ margin: 0;
+}
+
+/* =error404
+----------------------------------------------- */
+
+.error404 #main #searchform {
+ background: #f9f9f9;
+ border: 1px solid #ddd;
+ border-width: 1px 0;
+ margin: 0 -8.9% 1.625em;
+ overflow: hidden;
+ padding: 1.625em 8.9%;
+}
+.error404 #main #s {
+ width: 95%;
+}
+.error404 .widgettitle {
+ font-size: 0.769em;
+ letter-spacing: 0.1em;
+ line-height: 2.6em;
+ text-transform: uppercase;
+}
+
+
+/* =Attachments
+----------------------------------------------- */
+
+.image-attachment div.attachment {
+ background: #f9f9f9;
+ border: 1px solid #ddd;
+ border-width: 1px 0;
+ margin: 0 -8.9% 1.625em;
+ overflow: hidden;
+ padding: 1.625em 1.625em 0;
+ text-align: center;
+}
+.image-attachment div.attachment img {
+ display: block;
+ height: auto;
+ margin: 0 auto 1.625em;
+ max-width: 100%;
+}
+.image-attachment div.attachment a img {
+ border-color: #f9f9f9;
+}
+.image-attachment div.attachment a:focus img,
+.image-attachment div.attachment a:hover img,
+.image-attachment div.attachment a:active img {
+ border-color: #ddd;
+ background: #fff;
+}
+.image-attachment .entry-caption p {
+ font-size: 0.769em;
+ letter-spacing: 0.1em;
+ line-height: 2.6em;
+ margin: 0 0 2.6em;
+ text-transform: uppercase;
+}
+
+
+/* =Navigation
+-------------------------------------------------------------- */
+
+#content nav {
+ clear: both;
+}
+#nav-below,
+#nav-single {
+ margin: 0 auto 0.6em;
+ overflow: hidden;
+ width: 100%;
+}
+.nav-previous {
+ float: left;
+ width: 48%;
+}
+.nav-next {
+ float: right;
+ width: 46%;
+}
+#nav-single {
+ display: block;
+ position: static;
+}
+#nav-single .nav-previous {
+ margin-left: 0;
+ width: 50%;
+}
+#nav-single .nav-next {
+ margin-right: 0;
+ width: 49%;
+}
+.nav-previous a,
+.nav-next a {
+ background: #00a4bc;
+ color: #fff;
+ display: block;
+ font-size: 1.231em;
+ font-weight: bold;
+ padding: 1.2em 0;
+ text-align: center;
+ width: 100%;
+}
+#content nav .meta-nav {
+ font-weight: normal;
+}
+#jp-post-flair {
+ margin: 1em auto !important;
+}
+
+
+
+/* =Widget Area & Widgets
+----------------------------------------------- */
+
+.widget-area {
+ background: #f9f9f9;
+ border-top: 1px solid #ddd;
+ color: #666;
+ font-size: 0.923em;
+ padding: .6em 0.8em;
+}
+.widget {
+ border-bottom: 1px solid #ddd;
+ clear: both;
+ margin: 0;
+ padding: 1.625em 0;
+}
+.widget:last-of-type {
+ border: 0;
+}
+.widget-title {
+ color: #666;
+ font-size: 0.833em;
+ letter-spacing: 0.1em;
+ line-height: 2em;
+ margin-bottom: 0.5em;
+ text-transform: uppercase;
+}
+.widget-title a {
+ color: #666;
+}
+.widget ul {
+ margin-bottom: 0;
+}
+.widget ul ul {
+ margin-left: 1.5em;
+}
+.widget ul li {
+ color: #777;
+}
+.widget a {
+ font-weight: bold;
+ text-decoration: none;
+}
+.widget a:hover,
+.widget a:focus,
+.widget a:active {
+ text-decoration: underline;
+}
+
+/* Search Widget */
+.widget_search #s {
+ width: 77%;
+}
+.widget_search #searchsubmit {
+ background: #ddd;
+ border: 1px solid #ccc;
+ -webkit-box-shadow: inset 0px -1px 1px rgba(0, 0, 0, 0.09);
+ -moz-box-shadow: inset 0px -1px 1px rgba(0, 0, 0, 0.09);
+ box-shadow: inset 0px -1px 1px rgba(0, 0, 0, 0.09);
+ color: #888;
+ line-height: 2.083em;
+ position: relative;
+ top: -2px;
+}
+.widget_search #searchsubmit:active {
+ background: #00a4bc;
+ border-color: #0861a5;
+ -webkit-box-shadow: inset 0px 1px 1px rgba(0, 0, 0, 0.1);
+ -moz-box-shadow: inset 0px 1px 1px rgba(0, 0, 0, 0.1);
+ box-shadow: inset 0px 1px 1px rgba(0, 0, 0, 0.1);
+ color: #bfddf3;
+}
+
+/* Twitter */
+.tweets {
+ margin-left: 0;
+}
+.widget_twitter li {
+ list-style-type: none;
+ margin-bottom: 1.167em;
+}
+.widget_twitter .timesince {
+ font-size: 0.917em;
+ font-weight: normal;
+ text-align: right;
+}
+
+/* RSS-Related Widgets */
+.widget_rss img {
+ display: inline-block;
+ margin: 0;
+ vertical-align: middle;
+}
+.widget_rss .rss-date {
+ font-size: 90%;
+}
+.widget_rss_links img,
+.widget_rss_links a:hover img,
+.widget_rss_links a:focus img,
+.widget_rss_links a:active img {
+ background: transparent;
+ border: none;
+ padding: 0;
+}
+
+/* Calendar Widget */
+.widget_calendar #wp-calendar {
+ color: #555;
+ width: 95%;
+ text-align: center;
+}
+.widget_calendar #wp-calendar caption,
+.widget_calendar #wp-calendar td,
+.widget_calendar #wp-calendar th {
+ text-align: center;
+}
+.widget_calendar #wp-calendar caption {
+ font-size: 11px;
+ font-weight: 500;
+ padding: 5px 0 3px 0;
+ text-transform: uppercase;
+}
+.widget_calendar #wp-calendar th {
+ background: #f4f4f4;
+ border-top: 1px solid #ccc;
+ border-bottom: 1px solid #ccc;
+ font-weight: bold;
+}
+.widget_calendar #wp-calendar tfoot td {
+ background: #f4f4f4;
+ border-top: 1px solid #ccc;
+ border-bottom: 1px solid #ccc;
+}
+
+/* Recent Comments */
+.widget_recent_comments td.recentcommentstexttop,
+.widget_recent_comments td.recentcommentstextend {
+ vertical-align: top;
+}
+
+/* Authors Widget */
+.widget_authors ul {
+ margin-left: 0;
+}
+.widget_authors li {
+ background: none !important;
+ overflow: hidden;
+}
+.widget_authors ul ul li {
+ overflow: hidden;
+}
+.widget_authors img {
+ float: left;
+ padding-right: 0.833em;
+ vertical-align: text-top;
+}
+
+/*Flickr Widget */
+.widget_flickr #flickr_badge_wrapper {
+ background-color: transparent;
+ border: none;
+}
+#flickr_badge_uber_wrapper a:hover,
+#flickr_badge_uber_wrapper a:link,
+#flickr_badge_uber_wrapper a:active,
+#flickr_badge_uber_wrapper a:visited {
+ color: #00a4bc !important;
+}
+
+
+/* =Comments
+----------------------------------------------- */
+
+#comments {
+ margin-top: 1.5em;
+}
+#comments-title {
+ color: #000;
+ font-size: 1.154em;
+ font-weight: bold;
+ line-height: 1em;
+ padding: 1em 0;
+}
+#comment-nav-below {
+ overflow: hidden;
+}
+.nopassword,
+.nocomments {
+ color: #aaa;
+ font-size: 1.846em;
+ font-weight: 100;
+ margin: 2em 0;
+ text-align: center;
+}
+.commentlist {
+ list-style: none;
+ margin: 0 auto;
+ width: 100%;
+}
+.commentlist > li.comment {
+ background: #f6f6f6;
+ border: 1px solid #ddd;
+ border-width: 1px 0;
+ margin: 0 -2.5% 0.5em;
+ padding: .8em;
+ position: relative;
+}
+.commentlist > li.comment,
+.commentlist .pingback {
+ width: auto;
+}
+
+/* Reblogs */
+.commentlist > li.reblog {
+ border: 1px solid #eee;
+ -moz-border-radius: 3px;
+ border-radius: 3px;
+ margin: 0 0 1.625em;
+ padding: 1.625em;
+ position: relative;
+}
+.commentlist .reblog .comment-meta {
+ display: none;
+}
+.commentlist .reblog p:first-child {
+ color: #999;
+ font-size: 1em;
+}
+.commentlist .reblog p:first-child a {
+ font-weight: bold;
+}
+.commentlist .pingback,
+.commentlist .pingback p {
+ margin: 0 0 .8em;
+}
+.commentlist .children {
+ list-style: none;
+ margin: 0;
+}
+.commentlist .children li.comment {
+ background: #fff;
+ border-left: 1px solid #ddd;
+ -moz-border-radius: 0 3px 3px 0;
+ border-radius: 0 3px 3px 0;
+ margin: 1.625em 0 0;
+ padding: 1.625em;
+ position: relative;
+}
+.commentlist .children li.comment .fn {
+ display: block;
+}
+.comment-meta .fn {
+ font-style: normal;
+}
+.comment-meta,
+.comment-content {
+ margin-left: 4em;
+}
+.comment-meta {
+ color: #666;
+ font-size: 0.923em;
+}
+.comment-content {
+ padding-top: 0.2em;
+}
+.commentlist .children li.comment .comment-meta {
+ line-height: 1.625em;
+ margin-left: 3.462em;
+}
+.commentlist .children li.comment .comment-content {
+ margin: 1.625em 0 0;
+}
+.comment-meta a {
+ font-weight: bold;
+}
+.comment-meta a:focus,
+.comment-meta a:active,
+.comment-meta a:hover {
+}
+.commentlist .avatar {
+ background: transparent;
+ -moz-border-radius: 3px;
+ border-radius: 3px;
+ display: block;
+ padding: 0;
+ position: absolute;
+ left: 13px;
+ top: 13px;
+}
+.commentlist .children .avatar {
+ background: none;
+ -webkit-box-shadow: none;
+ -moz-box-shadow: none;
+ box-shadow: none;
+ left: 2.2em;
+ padding: 0;
+ position: absolute;
+ top: 2.2em;
+}
+a.comment-reply-link {
+ background: #eee;
+ -moz-border-radius: 3px;
+ border-radius: 3px;
+ color: #666;
+ display: inline-block;
+ font-size: 0.923em;
+ padding: 0 0.615em;
+ text-decoration: none;
+}
+a.comment-reply-link:hover,
+a.comment-reply-link:focus,
+a.comment-reply-link:active {
+ background: #888;
+ color: #fff;
+}
+a.comment-reply-link > span {
+ display: inline-block;
+ position: relative;
+ top: -1px;
+}
+
+/* Post author highlighting */
+.commentlist > li.bypostauthor {
+ background: #ddd;
+ border-color: #d3d3d3;
+}
+.commentlist > li.bypostauthor .comment-meta {
+ color: #575757;
+}
+.commentlist > li.bypostauthor .comment-meta a:focus,
+.commentlist > li.bypostauthor .comment-meta a:active,
+.commentlist > li.bypostauthor .comment-meta a:hover {
+}
+/* Post Author threaded comments */
+.commentlist .children > li.bypostauthor {
+ background: #ddd;
+ border-color: #d3d3d3;
+}
+
+/* Comment Form */
+#respond {
+ margin: 0 auto 1.625em;
+ width: auto;
+}
+#respond input[type="text"],
+#respond textarea {
+ background: #fff;
+ position: relative;
+ padding: 0.615em;
+}
+#respond .comment-form-author,
+#respond .comment-form-email,
+#respond .comment-form-url,
+#respond .comment-form-comment {
+ position: relative;
+}
+#respond .comment-form-author label,
+#respond .comment-form-email label,
+#respond .comment-form-url label,
+#respond .comment-form-comment label {
+ color: #555;
+ display: inline-block;
+ font-size: 1.077em;
+ font-weight: bold;
+ padding: 0.154em 0 0;
+ position: relative;
+}
+#respond textarea {
+ resize: vertical;
+ width: 95%;
+}
+#respond .comment-form-author .required,
+#respond .comment-form-email .required {
+ color: #bd3500;
+ font-size: 1.692em;
+ font-weight: bold;
+ left: 95%;
+ position: absolute;
+ top: 45px;
+ z-index: 1;
+}
+#respond .comment-notes,
+#respond .logged-in-as {
+ font-size: 0.8em;
+ color: #666;
+}
+#respond p {
+ margin: 0.769em 0 0;
+}
+#respond .form-submit {
+ margin: 0;
+}
+#respond input#submit {
+ background: #00a4bc;
+ border: none;
+ border-radius: 3px;
+ box-shadow: 0px 1px 2px rgba(0,0,0,0.3);
+ color: #fff;
+ cursor: pointer;
+ font-size: 1em;
+ font-weight: bold;
+ margin: 0.462em 0;
+ padding: 1em;
+ left: 30px;
+ text-shadow: 0 -1px 0 rgba(0,0,0,0.3);
+}
+#respond input#submit:active {
+ background: #00879c;
+ color: #fff;
+}
+#respond #cancel-comment-reply-link {
+ color: #666;
+ text-decoration: none;
+}
+#respond .logged-in-as a:hover,
+#respond #cancel-comment-reply-link:hover {
+ text-decoration: underline;
+}
+.commentlist #respond {
+ margin: 1.625em 0 0;
+ width: auto;
+}
+#respond .comment-subscription-form {
+ margin: 6px 0;
+}
+#reply-title {
+ color: #373737;
+ font-size: 1.5em;
+ font-weight: bold;
+ line-height: 0.733em;
+}
+.comment #reply-title {
+ margin-top: 1em;
+}
+#cancel-comment-reply-link {
+ color: #888;
+ display: block;
+ font-size: 0.923em;
+ font-weight: normal;
+ line-height: 2.2em;
+ text-decoration: none;
+ text-transform: uppercase;
+}
+#cancel-comment-reply-link:focus,
+#cancel-comment-reply-link:active,
+#cancel-comment-reply-link:hover {
+ color: #ff4b33;
+}
+#respond label {
+ line-height: 2.2em;
+}
+#respond input[type=text] {
+ display: block;
+ height: 1.846em;
+ width: 95%;
+}
+#respond p {
+ font-size: 0.923em;
+}
+p.comment-form-comment {
+ margin: 0;
+}
+.form-allowed-tags {
+ display: none;
+}
+
+
+/* =Footer
+----------------------------------------------- */
+
+#colophon {
+ background: #434343;
+ clear: both;
+ margin-bottom: -2em;
+ padding-bottom: 1em;
+}
+
+/* Site Generator Line */
+#site-generator {
+ background: #434343;
+ border-top: 1px solid #ddd;
+ color: #fff;
+ font-size: 0.923em;
+ line-height: 2.2em;
+ padding: 2.2em 0.5em;
+ text-align: center;
+}
+#site-generator a {
+ color: #00a4bc;
+ font-weight: bold;
+}
+#site-generator .sep {
+ color: transparent;
+ display: inline-block;
+ height: 16px;
+ line-height: 1.231em;
+ margin: 0 0.538em;
+ text-indent: 40px; /* Push the separator just out of the way */
+ width: 3.077em;
+}
+
+
+/* =WP.com
+----------------------------------------------- */
+
+.video-player {
+ max-width: 100% !important;
+}
+.videopress-placeholder,
+.video-player img {
+ max-width: 100% !important;
+ height: auto !important;
+}
+.syntaxhighlighter {
+ overflow: auto;
+}
+.single #content #wp-likebox,
+.page #content #wp-likebox {
+ display: block;
+}
+#wpl-mustlogin {
+width: 240px !important;
+margin-left: -60px !important;
+}
+
+/* WP.com comment form */
+#comments #respond {
+ max-width: 75%;
+ margin: 0 auto 15px;
+}
+.content #comments #respond,
+#comments .commentlist #respond {
+ max-width: 100%;
+}
+#respond textarea {
+ text-indent: 0;
+}
+.singular #content .wpl-likebox {
+ width: 100%;
+}
+#comments #respond {
+ max-width: 100%;
+}
+#wpstats {
+ display: block;
+ margin: -1.8em auto 0;
+}
+#wpstats2 {
+ display: none;
+}
+
+/* Adjust the width of PollDaddy polls */
+.PDS_Poll .pds-box {
+ width: 99% !important;
+}
+
+
+/* Remove margins and padding on outer containers for super-tiny screens */
+@media only screen and (min-device-width: 100px) and (max-device-width: 300px) {
+ #wrapper {
+ margin: 0;
+ padding: 0;
+ }
+ #access {
+ padding: 0.385em 0;
+ }
+ #page,
+ .widget-area,
+ #main {
+ width: 100%;
+ margin: 0;
+ }
+ .widget-area {
+ padding: 0.417em;
+ }
+} \ No newline at end of file
diff --git a/plugins/jetpack/modules/mobile-push.php b/plugins/jetpack/modules/mobile-push.php
new file mode 100644
index 00000000..8981348b
--- /dev/null
+++ b/plugins/jetpack/modules/mobile-push.php
@@ -0,0 +1,11 @@
+<?php
+/**
+ * Module Name: Mobile Push Notifications
+ * Module Description: Receive notifications on your Apple device.
+ * Sort Order: 100
+ * First Introduced: 1.9
+ */
+
+Jetpack_Sync::sync_comments( __FILE__, array(
+ 'comment_stati' => array( 'approved', 'unapproved' ),
+) );
diff --git a/plugins/jetpack/modules/module-extras.php b/plugins/jetpack/modules/module-extras.php
new file mode 100644
index 00000000..5013f7cc
--- /dev/null
+++ b/plugins/jetpack/modules/module-extras.php
@@ -0,0 +1,58 @@
+<?php
+/*
+ * Load module code that is needed even when a module isn't active.
+ * 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' );
diff --git a/plugins/jetpack/modules/module-info.php b/plugins/jetpack/modules/module-info.php
index d1038208..218a9e80 100644
--- a/plugins/jetpack/modules/module-info.php
+++ b/plugins/jetpack/modules/module-info.php
@@ -42,7 +42,7 @@ function vaultpress_jetpack_load_more_link() {
$vaultpress_url = 'http://vaultpress.com/jetpack/';
}
- echo '<a class="button more-info-link" href="' . $vaultpress_url . '">' . __( "Learn More", 'jetpack' ) . '</a>';
+ echo '<a class="button-secondary more-info-link" href="' . $vaultpress_url . '">' . __( "Learn More", 'jetpack' ) . '</a>';
}
add_filter( 'jetpack_learn_more_button_vaultpress', 'vaultpress_jetpack_load_more_link' );
@@ -78,7 +78,7 @@ function grofiles_more_info_connected() { ?>
add_action( 'jetpack_module_more_info_connected_gravatar-hovercards', 'grofiles_more_info_connected' );
function grofiles_load_more_link() {
- echo '<a class="button more-info-link" href="http://blog.gravatar.com/2010/10/06/gravatar-hovercards-on-wordpress-com/">' . esc_html__( 'Learn More', 'jetpack' ) . '</a>';
+ echo '<a class="button-secondary more-info-link" href="http://blog.gravatar.com/2010/10/06/gravatar-hovercards-on-wordpress-com/">' . esc_html__( 'Learn More', 'jetpack' ) . '</a>';
}
add_filter( 'jetpack_learn_more_button_gravatar-hovercards', 'grofiles_load_more_link' );
@@ -112,11 +112,8 @@ function jetpack_shortcodes_more_info_connected() { ?>
'audio' => 'http://support.wordpress.com/audio/',
'blip.tv' => 'http://support.wordpress.com/videos/bliptv/',
'dailymotion' => 'http://support.wordpress.com/videos/dailymotion/',
- 'digg' => 'http://support.wordpress.com/digg/',
'flickr' => 'http://support.wordpress.com/videos/flickr-video/',
- 'googlevideo' => 'http://support.wordpress.com/videos/google-video/',
'scribd' => 'http://support.wordpress.com/scribd/',
- 'slide' => 'http://support.wordpress.com/slideshows/slide/',
'slideshare' => 'http://support.wordpress.com/slideshows/slideshare/',
'soundcloud' => 'http://support.wordpress.com/audio/soundcloud-audio-player/',
'vimeo' => 'http://support.wordpress.com/videos/vimeo/',
@@ -138,7 +135,7 @@ function jetpack_shortcodes_more_info_connected() { ?>
add_action( 'jetpack_module_more_info_connected_shortcodes', 'jetpack_shortcodes_more_info_connected' );
function jetpack_shortcodes_load_more_link( $description ) {
- echo '<a class="button more-info-link" href="http://en.support.wordpress.com/shortcodes/">' . esc_html__( 'Learn More' , 'jetpack' ) . '</a>';
+ echo '<a class="button-secondary more-info-link" href="http://en.support.wordpress.com/shortcodes/">' . esc_html__( 'Learn More' , 'jetpack' ) . '</a>';
}
add_filter( 'jetpack_learn_more_button_shortcodes', 'jetpack_shortcodes_load_more_link' );
@@ -173,7 +170,7 @@ function wpme_more_info_connected() { ?>
add_action( 'jetpack_module_more_info_connected_shortlinks', 'wpme_more_info_connected' );
function wpme_load_more_link( $description ) {
- echo '<a class="button more-info-link" href="http://wp.me/sf2B5-shorten">' . esc_html__( 'Learn More', 'jetpack' ) . '</a>';
+ echo '<a class="button-secondary more-info-link" href="http://wp.me/sf2B5-shorten">' . esc_html__( 'Learn More', 'jetpack' ) . '</a>';
}
add_filter( 'jetpack_learn_more_button_shortlinks', 'wpme_load_more_link' );
@@ -207,10 +204,73 @@ function stats_more_info_connected() { ?>
add_action( 'jetpack_module_more_info_connected_stats', 'stats_more_info_connected' );
function stats_load_more_link( $description ) {
- echo '<a class="button more-info-link" href="http://en.support.wordpress.com/stats/">' . esc_html__( 'Learn More', 'jetpack' ) . '</a>';
+ echo '<a class="button-secondary more-info-link" href="http://en.support.wordpress.com/stats/">' . esc_html__( 'Learn More', 'jetpack' ) . '</a>';
}
add_filter( 'jetpack_learn_more_button_stats', 'stats_load_more_link' );
+// Publicize
+function publicize_more_info() { ?>
+ <div class="jp-info-img">
+ <a href="http://en.support.wordpress.com/publicize/">
+ <img class="jp-info-img" src="<?php echo plugins_url( basename( dirname( dirname( __FILE__ ) ) ) . '/_inc/images/screenshots/publicize.png' ) ?>" alt="<?php esc_attr_e( 'Publicize', 'jetpack' ) ?>" width="328" height="123" />
+ </a>
+ </div>
+
+ <h4><?php esc_html_e( 'Publicize' , 'jetpack' ); ?></h4>
+ <p><?php esc_html_e( 'Publicize allows you to connect your blog to popular social networking sites and automatically share new posts with your friends. You can make a connection for just yourself or for all users on your blog.', 'jetpack' ) ?></p>
+ <p><?php esc_html_e( 'Publicize allows you to share your posts on Facebook, Twitter, Tumblr, Yahoo!, and Linkedin.', 'jetpack' ); ?></p>
+
+<?php if ( 'jetpack_module_more_info_connected_publicize' == current_filter() ) : ?>
+
+ <p><?php printf( __( 'Manage your <a href="%s">Publicize settings</a>.', 'jetpack' ), menu_page_url( 'sharing', false ) ); ?>
+
+<?php endif; ?>
+
+ <p>&rarr; <a href="http://jetpack.me/support/publicize/"><?php esc_html_e( 'More information on using Publicize.', 'jetpack' ); ?></a></p>
+<?php
+}
+
+add_action( 'jetpack_module_more_info_publicize', 'publicize_more_info' );
+add_action( 'jetpack_module_more_info_connected_publicize', 'publicize_more_info' );
+
+function publicize_load_more_link( $description ) {
+ echo '<a class="button-secondary more-info-link" href="http://jetpack.me/support/publicize/">' . esc_html__( 'Learn More', 'jetpack' ) . '</a>';
+}
+add_filter( 'jetpack_learn_more_button_publicize', 'publicize_load_more_link' );
+
+// Notifications
+function notes_more_info() { ?>
+ <div class="jp-info-img">
+ <a href="http://support.wordpress.com/notifications/">
+ <img class="jp-info-img" src="<?php echo plugins_url( basename( dirname( dirname( __FILE__ ) ) ) . '/_inc/images/screenshots/notes.png' ) ?>" alt="<?php esc_attr_e( 'Notifications', 'jetpack' ) ?>" width="300" height="150" />
+ </a>
+ </div>
+
+ <h4><?php esc_html_e( 'Notifications' , 'jetpack' ); ?></h4>
+ <p><?php esc_html_e( 'Keep up with the latest happenings on all your WordPress sites and interact with other WordPress.com users.', 'jetpack' ) ?></p>
+<?php
+}
+add_action( 'jetpack_module_more_info_notes', 'notes_more_info' );
+
+function notes_more_info_connected() { ?>
+ <div class="jp-info-img">
+ <a href="http://support.wordpress.com/notifications/">
+ <img class="jp-info-img" src="<?php echo plugins_url( basename( dirname( dirname( __FILE__ ) ) ) . '/_inc/images/screenshots/notes.png' ) ?>" alt="<?php esc_attr_e( 'Notifications', 'jetpack' ) ?>" width="300" height="150" />
+ </a>
+ </div>
+
+ <h4><?php esc_html_e( 'Notifications' , 'jetpack' ); ?></h4>
+ <p><?php esc_html_e( 'Keep up with the latest happenings on all your WordPress sites and interact with other WordPress.com users.', 'jetpack' ) ?></p>
+ <p><?php printf( __( 'You can view your notifications in the Toolbar and <a href="%s">on WordPress.com</a>.', 'jetpack' ), 'http://wordpress.com/#!/notifications/' ); ?></p>
+<?php
+}
+add_filter( 'jetpack_module_more_info_connected_notes', 'notes_more_info_connected' );
+
+function notes_load_more_link( $description ) {
+ echo '<a class="button-secondary more-info-link" href="http://support.wordpress.com/notifications/">' . esc_html__( 'Learn More', 'jetpack' ) . '</a>';
+}
+add_filter( 'jetpack_learn_more_button_notes', 'notes_load_more_link' );
+
// LaTeX
function latex_more_info() { ?>
@@ -220,9 +280,9 @@ function latex_more_info() { ?>
</a>
</div>
- <h4><img src="http://l.wordpress.com/latex.php?latex=%5CLaTeX&amp;bg=transparent&amp;fg=000&amp;s=1" alt="LaTeX logo" title="LaTeX" style="vertical-align: -27%" /> Makes Beautiful Math</h4>
- <p><?php printf( esc_html__( '%s is a powerful markup language for writing complex mathematical equations, formulas, etc.', 'jetpack' ), '<a href="http://www.latex-project.org/" target="_blank"><img src="http://l.wordpress.com/latex.php?latex=%5CLaTeX&amp;bg=transparent&amp;fg=000&amp;s=-1" alt="LaTeX logo" title="LaTeX" style="vertical-align: -25%" /></a>' ); ?></p>
- <p><?php printf( esc_html__( 'Jetpack combines the power of %s and the simplicity of WordPress to give you the ultimate in math blogging platforms.', 'jetpack' ), '<img src="http://l.wordpress.com/latex.php?latex=%5CLaTeX&amp;bg=transparent&amp;fg=000&amp;s=-1" alt="LaTeX logo" title="LaTeX" style="vertical-align: -25%" />' ); ?></p>
+ <h4><img src="//s0.wp.com/latex.php?latex=%5CLaTeX&amp;bg=transparent&amp;fg=000&amp;s=1" alt="LaTeX logo" title="LaTeX" style="vertical-align: -27%" /> Makes Beautiful Math</h4>
+ <p><?php printf( esc_html__( '%s is a powerful markup language for writing complex mathematical equations, formulas, etc.', 'jetpack' ), '<a href="http://www.latex-project.org/" target="_blank"><img src="//s0.wp.com/latex.php?latex=%5CLaTeX&amp;bg=transparent&amp;fg=000&amp;s=-1" alt="LaTeX logo" title="LaTeX" style="vertical-align: -25%" /></a>' ); ?></p>
+ <p><?php printf( esc_html__( 'Jetpack combines the power of %s and the simplicity of WordPress to give you the ultimate in math blogging platforms.', 'jetpack' ), '<img src="//s0.wp.com/latex.php?latex=%5CLaTeX&amp;bg=transparent&amp;fg=000&amp;s=-1" alt="LaTeX logo" title="LaTeX" style="vertical-align: -25%" />' ); ?></p>
<p><?php esc_html_e( 'Wow, that sounds nerdy.', 'jetpack' ) ?></p>
<?php
}
@@ -235,15 +295,15 @@ function latex_more_info_connected() { ?>
</a>
</div>
- <h4><img src="http://l.wordpress.com/latex.php?latex=%5CLaTeX&amp;bg=transparent&amp;fg=000&amp;s=1" alt="LaTeX logo" title="LaTeX" style="vertical-align: -27%;" /> Makes Beautiful Math</h4>
- <p><?php printf( esc_html__( '%s is a powerful markup language for writing complex mathematical equations, formulas, etc.', 'jetpack' ), '<a href="http://www.latex-project.org/" target="_blank"><img src="http://l.wordpress.com/latex.php?latex=%5CLaTeX&amp;bg=transparent&amp;fg=000&amp;s=-1" alt="LaTeX logo" title="LaTeX" style="vertical-align: -25%" /></a>' ); ?></p>
- <p><?php printf( __( 'Use <code>$latex your latex code here$</code> or <code>[latex]your latex code here[/latex]</code> to include %s in your posts and comments. There are <a href="%s" target="_blank">all sorts of options</a> available.', 'jetpack' ), '<img src="http://l.wordpress.com/latex.php?latex=%5CLaTeX&amp;bg=transparent&amp;fg=000&amp;s=-1" alt="LaTeX logo" title="LaTeX" style="vertical-align: -25%" />', 'http://support.wordpress.com/latex/' ); ?></p>
+ <h4><img src="//s0.wp.com/latex.php?latex=%5CLaTeX&amp;bg=transparent&amp;fg=000&amp;s=1" alt="LaTeX logo" title="LaTeX" style="vertical-align: -27%;" /> Makes Beautiful Math</h4>
+ <p><?php printf( esc_html__( '%s is a powerful markup language for writing complex mathematical equations, formulas, etc.', 'jetpack' ), '<a href="http://www.latex-project.org/" target="_blank"><img src="//s0.wp.com/latex.php?latex=%5CLaTeX&amp;bg=transparent&amp;fg=000&amp;s=-1" alt="LaTeX logo" title="LaTeX" style="vertical-align: -25%" /></a>' ); ?></p>
+ <p><?php printf( __( 'Use <code>$latex your latex code here$</code> or <code>[latex]your latex code here[/latex]</code> to include %s in your posts and comments. There are <a href="%s" target="_blank">all sorts of options</a> available.', 'jetpack' ), '<img src="//s0.wp.com/latex.php?latex=%5CLaTeX&amp;bg=transparent&amp;fg=000&amp;s=-1" alt="LaTeX logo" title="LaTeX" style="vertical-align: -25%" />', 'http://support.wordpress.com/latex/' ); ?></p>
<?php
}
add_action( 'jetpack_module_more_info_connected_latex', 'latex_more_info_connected' );
function latex_load_more_link( $description ) {
- echo '<a class="button more-info-link" href="http://support.wordpress.com/latex/">' . esc_html__( 'Learn More', 'jetpack' ) . '</a>';
+ echo '<a class="button-secondary more-info-link" href="http://support.wordpress.com/latex/">' . esc_html__( 'Learn More', 'jetpack' ) . '</a>';
}
add_filter( 'jetpack_learn_more_button_latex', 'latex_load_more_link' );
@@ -294,7 +354,7 @@ function sharedaddy_more_info_connected() { ?>
add_action( 'jetpack_module_more_info_connected_sharedaddy', 'sharedaddy_more_info_connected' );
function sharedaddy_load_more_link( $description ) {
- echo '<a class="button more-info-link" href="http://support.wordpress.com/sharing/">' . esc_html__( 'Learn More', 'jetpack' ) . '</a>';
+ echo '<a class="button-secondary more-info-link" href="http://support.wordpress.com/sharing/">' . esc_html__( 'Learn More', 'jetpack' ) . '</a>';
}
add_filter( 'jetpack_learn_more_button_sharedaddy', 'sharedaddy_load_more_link' );
@@ -316,7 +376,7 @@ function jpatd_more_info() { ?>
add_action( 'jetpack_module_more_info_after-the-deadline', 'jpatd_more_info' );
function jpatd_load_more_link( $description ) {
- echo '<a class="button more-info-link" href="http://en.support.wordpress.com/proofreading/">' . esc_html__( 'Learn More', 'jetpack' ) . '</a>';
+ echo '<a class="button-secondary more-info-link" href="http://en.support.wordpress.com/proofreading/">' . esc_html__( 'Learn More', 'jetpack' ) . '</a>';
}
add_filter( 'jetpack_learn_more_button_after-the-deadline', 'jpatd_load_more_link' );
@@ -328,7 +388,7 @@ function jetpack_widgets_more_info() { ?>
</div>
<h4><?php esc_html_e( 'Extra Sidebar Widgets' , 'jetpack' ); ?></h4>
-
+
<p><strong><?php esc_html_e( 'The RSS Links Widget ', 'jetpack' ); ?></strong> <?php esc_html_e( "allows you to add links to your blog&#8217;s post and comment RSS feeds in your sidebar. This makes it easy for your readers to stay updated when you post new content or receive new comments.", 'jetpack' ) ?></p>
<p><strong><?php esc_html_e( 'The Twitter Widget ', 'jetpack' ); ?></strong> <?php esc_html_e( "shows your latest tweets within a sidebar on your theme. It&#8217;s an easy way to add more activity to your site. There are also a number of customization options.", 'jetpack' ) ?> <strong><?php esc_html_e( 'The Facebook Like Box Widget ', 'jetpack' ); ?></strong> <?php esc_html_e( "shows your Facebook Like Box within a sidebar on your theme. It&#8217;s a great way to let your readers show their support.", 'jetpack' ) ?> <strong><?php esc_html_e( 'The Image Widget ', 'jetpack' ); ?></strong><?php esc_html_e( "allows you to easily add images to widget areas in your theme. It&#8217;s an easy way to add more visual interest to your site.", 'jetpack' ) ?></p>
@@ -354,7 +414,7 @@ function jetpack_widgets_more_info_connected() { ?>
add_action( 'jetpack_module_more_info_connected_widgets', 'jetpack_widgets_more_info_connected' );
function jetpack_widgets_load_more_link( $description ) {
- echo '<a class="button more-info-link" href="http://en.support.wordpress.com/widgets/">' . esc_html__( 'Learn More', 'jetpack' ) . '</a>';
+ echo '<a class="button-secondary more-info-link" href="http://en.support.wordpress.com/widgets/">' . esc_html__( 'Learn More', 'jetpack' ) . '</a>';
}
add_filter( 'jetpack_learn_more_button_widgets', 'jetpack_widgets_load_more_link' );
@@ -373,12 +433,13 @@ function jetpack_subscriptions_more_info() { ?>
if ( 'jetpack_module_more_info_connected_subscriptions' == current_filter() )
printf( '<p>' . __( 'To use the Subscriptions widget, go to Appearance &#8594; <a href="%s">Widgets</a>. Drag the widget labeled &#8220;Blog Subscriptions (Jetpack)&#8221; into one of your sidebars and configure away.', 'jetpack' ) . '</p>', admin_url( 'widgets.php' ) );
+ printf( '<p>' . __( 'You can also make changes to your Subscription settings at the bottom of the <a href="%s">Discussion Settings</a> page.', 'jetpack' ) . '</p>', admin_url( 'options-discussion.php#jetpack-subscriptions-settings' ) );
}
add_action( 'jetpack_module_more_info_subscriptions', 'jetpack_subscriptions_more_info' );
add_action( 'jetpack_module_more_info_connected_subscriptions', 'jetpack_subscriptions_more_info' );
function jetpack_subscriptions_load_more_link() {
- echo '<a class="button more-info-link" href="http://en.support.wordpress.com/following/">' . esc_html__( 'Learn More', 'jetpack' ) . '</a>';
+ echo '<a class="button-secondary more-info-link" href="http://en.support.wordpress.com/following/">' . esc_html__( 'Learn More', 'jetpack' ) . '</a>';
}
add_action( 'jetpack_learn_more_button_subscriptions', 'jetpack_subscriptions_load_more_link' );
@@ -396,13 +457,32 @@ add_action( 'jetpack_module_more_info_enhanced-distribution', 'jetpack_enhanced_
add_action( 'jetpack_module_more_info_connected_enhanced-distribution', 'jetpack_enhanced_distribution_more_info' );
function jetpack_enhanced_distribution_more_link() {
- echo '<a class="button more-info-link" href="http://en.wordpress.com/firehose/">' . esc_html__( 'Learn More', 'jetpack' ) . '</a>';
+ echo '<a class="button-secondary more-info-link" href="http://en.wordpress.com/firehose/">' . esc_html__( 'Learn More', 'jetpack' ) . '</a>';
}
add_action( 'jetpack_learn_more_button_enhanced-distribution', 'jetpack_enhanced_distribution_more_link' );
+// JSON API
+function jetpack_json_api_more_info() { ?>
+ <h4><?php esc_html_e( 'JSON API' , 'jetpack' ); ?></h4>
+
+ <p><?php esc_html_e( 'Jetpack will allow you to authorize applications and services to securely connect to your blog and allow them to use your content in new ways and offer you new functionality.', 'jetpack' ); ?>
+
+ <p><?php _e( "Developers can use WordPress.com's <a href='http://developer.wordpress.com/docs/oauth2/'>OAuth2</a> authentication system and <a href='http://developer.wordpress.com/docs/api/'>WordPress.com REST API</a> to manage and access your site's content.", 'jetpack' ); ?></p>
+
+<?php
+}
+
+add_action( 'jetpack_module_more_info_json-api', 'jetpack_json_api_more_info' );
+add_action( 'jetpack_module_more_info_connected_json-api', 'jetpack_json_api_more_info' );
+
+function jetpack_json_api_more_link() {
+ echo '<a class="button-secondary more-info-link" href="http://jetpack.me/support/json-api/">' . esc_html__( 'Learn More', 'jetpack' ) . '</a>';
+}
+add_action( 'jetpack_learn_more_button_json-api', 'jetpack_json_api_more_link' );
+
// Contact Form: START
function jetpack_contact_form_learn_more_button() {
- echo '<a class="button more-info-link" href="http://support.wordpress.com/contact-form/">' . __( 'Learn More', 'jetpack' ) . '</a>';
+ echo '<a class="button-secondary more-info-link" href="http://support.wordpress.com/contact-form/">' . __( 'Learn More', 'jetpack' ) . '</a>';
}
function jetpack_contact_form_more_info() {
@@ -429,7 +509,7 @@ add_action( 'jetpack_module_more_info_connected_contact-form', 'jetpack_contact_
// Jetpack Comments: START
function jetpack_comments_learn_more_button() {
- echo '<a class="button more-info-link" href="#">' . __( 'Learn More', 'jetpack' ) . '</a>';
+ echo '<a class="button-secondary more-info-link" href="#">' . __( 'Learn More', 'jetpack' ) . '</a>';
}
function jetpack_comments_more_info() {
@@ -460,13 +540,13 @@ add_action( 'jetpack_module_more_info_connected_comments', 'jetpack_comments_mor
// Gallery Carousel: START
function jetpack_carousel_learn_more_button() {
- echo '<a class="button more-info-link" href="#">' . __( 'Learn More', 'jetpack' ) . '</a>';
+ echo '<a class="button-secondary more-info-link" href="#">' . __( 'Learn More', 'jetpack' ) . '</a>';
}
function jetpack_carousel_more_info() {
?>
<div class="jp-info-img">
- <img class="jp-info-img" src="<?php echo plugins_url( basename( dirname( dirname( __FILE__ ) ) ) . '/screenshot-6.png' ) ?>" alt="<?php esc_attr_e( 'Gallery Carousel Screenshot', 'jetpack' ) ?>" width="300" height="188" />
+ <img class="jp-info-img" src="<?php echo plugins_url( basename( dirname( dirname( __FILE__ ) ) ) . '/_inc/images/screenshots/carousel.png' ) ?>" alt="<?php esc_attr_e( 'Gallery Carousel Screenshot', 'jetpack' ) ?>" width="300" height="188" />
</div>
<h4><?php esc_html_e( 'Carousel', 'jetpack' ); ?></h4>
@@ -479,3 +559,220 @@ add_action( 'jetpack_learn_more_button_carousel', 'jetpack_carousel_learn_more_b
add_action( 'jetpack_module_more_info_carousel', 'jetpack_carousel_more_info' );
add_action( 'jetpack_module_more_info_connected_carousel', 'jetpack_carousel_more_info' );
// Gallery Carousel: STOP
+
+// Custom CSS: START
+function jetpack_custom_css_more_info() {
+ ?>
+ <div class="jp-info-img">
+ <img class="jp-info-img" src="<?php echo plugins_url( basename( dirname( dirname( __FILE__ ) ) ) . '/_inc/images/screenshots/custom-css.png' ) ?>" alt="<?php esc_attr_e( 'Custom CSS', 'jetpack' ) ?>" width="300" height="150" />
+ </div>
+
+ <h4><?php esc_html_e( 'Custom CSS', 'jetpack' ); ?></h4>
+ <p><?php esc_html_e( "The Custom CSS editor gives you the ability to add to or replace your theme's CSS, all while supplying syntax coloring, auto-indentation, and immediate feedback on the validity of the CSS you're writing.", 'jetpack' ); ?></p>
+ <p><?php printf( __( 'To use the CSS editor, go to Appearance &#8594; <a href="%s">Edit CSS</a>.', 'jetpack' ), admin_url( 'themes.php?page=editcss' ) ); ?></p>
+
+ <?php
+}
+
+function jetpack_custom_css_more_button() {
+ echo '<a class="button-secondary more-info-link" href="#">' . __( 'Learn More', 'jetpack' ) . '</a>';
+}
+
+add_action( 'jetpack_learn_more_button_custom-css', 'jetpack_custom_css_more_button' );
+add_action( 'jetpack_module_more_info_custom-css', 'jetpack_custom_css_more_info' );
+// Custom CSS: STOP
+
+// Minileven: START
+function jetpack_minileven_more_info() {
+ ?>
+ <div class="jp-info-img">
+ <img class="jp-info-img" src="<?php echo plugins_url( basename( dirname( dirname( __FILE__ ) ) ) . '/_inc/images/screenshots/mobile-theme.png' ) ?>" alt="<?php esc_attr_e( 'Mobile Theme', 'jetpack' ) ?>" width="300" height="150" />
+ </div>
+
+ <h4><?php esc_html_e( 'Mobile Theme', 'jetpack' ); ?></h4>
+ <p><?php esc_html_e( "There's a good chance that visitors to your site will be using a smartphone, and it's important to provide them with a great reading experience while on the small screen.", 'jetpack' ); ?></p>
+ <p><?php esc_html_e( "Jetpack's mobile theme is optimized for small screens. It uses the header image, background, and widgets from your current theme for a great custom look. Post format support is included, so your photos and galleries will look fantastic on a smartphone.", 'jetpack' ); ?></p>
+ <p><?php esc_html_e( 'Visitors on iPhone, Android, Windows Phone, and other mobile devices will automatically see the mobile theme, with the option to view the full site. You can enable or disable the mobile theme by clicking the "Activate" or "Deactive" button above.', 'jetpack' ); ?></p>
+ <?php
+}
+
+function jetpack_minileven_more_button() {
+ echo '<a class="button-secondary more-info-link" href="#">' . __( 'Learn More', 'jetpack' ) . '</a>';
+}
+
+add_action( 'jetpack_learn_more_button_minileven', 'jetpack_minileven_more_button' );
+add_action( 'jetpack_module_more_info_minileven', 'jetpack_minileven_more_info' );
+// Minileven: STOP
+
+
+// Mobile Push Notifications: START
+function jetpack_mobile_push_notifications_more_info() { ?>
+ <div class="jp-info-img">
+ <img class="jp-info-img" src="<?php echo plugins_url( basename( dirname( dirname( __FILE__ ) ) ) . '/_inc/images/screenshots/mobile-push-notifications.jpg' ) ?>" alt="<?php esc_attr_e( 'Mobile Push Notifications', 'jetpack' ) ?>" width="300" height="150" />
+ </div>
+
+ <h4><?php esc_html_e( 'Mobile Push Notifications' , 'jetpack' ); ?></h4>
+
+ <p><?php _e( 'If you have your blog added to the <a href="http://ios.wordpress.org/">WordPress for iOS app</a>, you’ll now be able to opt in to receive push notifications of new comments, which makes it easier than ever to keep up with your readers and moderate comments on the go.', 'jetpack' ); ?></p>
+
+<?php
+}
+
+function jetpack_mobile_push_notifications_more_link() {
+ echo '<a class="button-secondary more-info-link" href="#">' . __( 'Learn More', 'jetpack' ) . '</a>';
+}
+
+add_action( 'jetpack_learn_more_button_mobile-push', 'jetpack_mobile_push_notifications_more_link' );
+add_action( 'jetpack_module_more_info_mobile-push', 'jetpack_mobile_push_notifications_more_info' );
+// Mobile Push Notifications: STOP
+
+// Infinite Scroll: START
+/**
+ *
+ */
+function jetpack_infinite_scroll_more_info() {
+ $support_text = sprintf( __( 'If you are a theme author, you can learn about adding support for Infinite Scroll at <a href="%1$s">%1$s</a>.', 'jetpack' ), 'http://jetpack.me/support/infinite-scroll/' );
+
+ ?>
+ <h4><?php esc_html_e( 'Infinite Scroll', 'jetpack' ); ?></h4>
+
+ <?php if ( ! Jetpack::is_active() || ( Jetpack::is_active() && current_theme_supports( 'infinite-scroll' ) ) ) : ?>
+ <p><?php esc_html_e( 'When you write great content, all you really want is people to find it, right?', 'jetpack' ); ?></p>
+
+ <p><?php esc_html_e( "With the Infinite Scroll module and a supported theme, that's exactly what happens. Instead of the old way of navigating down a page by scrolling and then clicking a link to get to the next page, waiting for a page refresh&mdash;the document model of the web&mdash;infinite scrolling pulls the next set of posts automatically into view when the reader approaches the bottom of the page, more like an application.", 'jetpack' ); ?></p>
+
+ <?php else : ?>
+ <p><?php echo esc_html( sprintf( __( "At this time, your theme, %s, doesn't support Infinite Scroll. Unlike other Jetpack modules, Infinite Scroll needs information from your theme to function properly.", 'jetpack' ), ( function_exists( 'wp_get_theme' ) ? wp_get_theme()->Name : get_current_theme() ) ) ); ?></p>
+
+ <p><?php esc_html_e( "Until your theme supports Infinite Scroll, you won't be able to activate this module.", 'jetpack' ); ?></p>
+
+ <?php
+
+ if ( current_user_can( 'update_themes' ) ) :
+ ob_start();
+ theme_update_available( function_exists( 'wp_get_theme' ) ? wp_get_theme() : (object) get_theme( get_current_theme() ) );
+ $theme_update_available = ob_get_clean();
+
+ if ( ! empty( $theme_update_available ) ) : ?>
+ <p><?php printf( __( 'There is an update available for your theme. You may wish to check if this update adds Infinite Scroll support by visiting the <a href="%s">WordPress Updates</a> page.', 'jetpack' ), esc_url( admin_url( 'update-core.php' ) ) ); ?></p>
+ <?php else : ?>
+ <p><?php echo $support_text; ?></p>
+ <?php endif; ?>
+ <?php else : ?>
+ <p><?php echo $support_text; ?></p>
+ <?php endif; ?>
+ <?php endif;
+}
+add_action( 'jetpack_module_more_info_infinite-scroll', 'jetpack_infinite_scroll_more_info' );
+
+/**
+ *
+ */
+function jetpack_infinite_scroll_more_button() {
+ echo '<a class="button more-info-link" href="#">' . __( 'Learn More', 'jetpack' ) . '</a>';
+}
+add_action( 'jetpack_learn_more_button_infinite-scroll', 'jetpack_infinite_scroll_more_button' );
+// Infinite Scroll: STOP
+
+
+// Post by Email: START
+function jetpack_post_by_email_more_info() { ?>
+ <div class="jp-info-img">
+ <img class="jp-info-img" src="<?php echo plugins_url( basename( dirname( dirname( __FILE__ ) ) ) . '/_inc/images/screenshots/post-by-email.png' ) ?>" alt="<?php esc_attr_e( 'Post by Email', 'jetpack' ) ?>" width="300" height="115" />
+ </div>
+
+ <h4><?php esc_html_e( 'Post by Email' , 'jetpack' ); ?></h4>
+
+ <p><?php esc_html_e( 'Post by Email is a way of publishing posts on your blog by email. Any email client can be used to send the email, allowing you to publish quickly and easily from devices such as cell phones.', 'jetpack' ); ?></p>
+
+<?php if ( 'jetpack_module_more_info_connected_post-by-email' == current_filter() ) : ?>
+
+ <p><?php printf( __( 'Manage your Post By Email address from your <a href="%s">profile settings</a>.', 'jetpack' ), esc_url( get_edit_profile_url( get_current_user_id() ) . '#post-by-email' ) ); ?>
+
+<?php endif; ?>
+
+ <p>&rarr; <a href="http://jetpack.me/support/post-by-email/"><?php esc_html_e( 'More information on sending emails, attachments, and customizing your posts.', 'jetpack' ); ?></a></p>
+
+<?php
+}
+
+function jetpack_post_by_email_more_link() {
+ echo '<a class="button-secondary more-info-link" href="http://jetpack.me/support/post-by-email/">' . __( 'Learn More', 'jetpack' ) . '</a>';
+}
+
+add_action( 'jetpack_module_more_info_post-by-email', 'jetpack_post_by_email_more_info' );
+add_action( 'jetpack_module_more_info_connected_post-by-email', 'jetpack_post_by_email_more_info' );
+add_action( 'jetpack_learn_more_button_post-by-email', 'jetpack_post_by_email_more_link' );
+// Post by Email: STOP
+
+
+// Photon: START
+/**
+ *
+ */
+function jetpack_photon_more_info() { ?>
+ <h4><?php esc_html_e( 'Photon' , 'jetpack' ); ?></h4>
+
+ <p><?php esc_html_e( "Give your site a boost by loading images in posts from the WordPress.com content delivery network. We cache your images and serve them from our super-fast network, reducing the burden on your Web host with the click of a button.", 'jetpack' ); ?></p>
+<?php
+}
+add_action( 'jetpack_module_more_info_photon', 'jetpack_photon_more_info' );
+
+/**
+ * Display "Learn More" button for Photon module
+ * @uses __
+ * @action jetpack_learn_more_button_photon
+ * @return string
+ */
+function jetpack_photon_more_link() {
+ echo '<a class="button-secondary more-info-link" href="#">' . __( 'Learn More', 'jetpack' ) . '</a>';
+}
+add_action( 'jetpack_learn_more_button_photon', 'jetpack_photon_more_link' );
+// Photon: STOP
+
+// Tiled Galleries: START
+function jetpack_tiled_gallery_more_info() { ?>
+ <h4><?php esc_html_e( 'Tiled Galleries' , 'jetpack' ); ?></h4>
+
+ <div class="jp-info-img">
+ <img class="jp-info-img" src="<?php echo plugins_url( basename( dirname( dirname( __FILE__ ) ) ) . '/_inc/images/screenshots/tiled-gallery.png' ) ?>" alt="<?php esc_attr_e( 'Tiled Galleries', 'jetpack' ) ?>" width="300" height="150" />
+ </div>
+
+ <p><?php esc_html_e( 'Create elegant magazine-style mosaic layouts for your photos without having to use an external graphic editor.', 'jetpack' ); ?></p>
+ <p><?php printf( __( 'When adding a gallery to your post, you now have the option to select a layout style for your images. We\'ve added support for Rectangular, Square, and Circular galleries. By default, galleries will continue to display using the standard thumbnail grid layout. To make the rectangular layout the default for all of your site\'s galleries, head over to <a href="%s">Settings &rarr; Media</a> and check the box next to "Display all your gallery pictures in a cool mosaic."', 'jetpack' ), admin_url( 'options-media.php' ) ); ?></p>
+ <p><em><?php esc_html_e( 'Note: Images in tiled galleries require extra-special processing, so they will be served from WordPress.com\'s CDN even if the Photon module is disabled.', 'jetpack' ); ?></em></p>
+<?php
+}
+add_action( 'jetpack_module_more_info_tiled-gallery', 'jetpack_tiled_gallery_more_info' );
+
+function jetpack_tiled_gallery_more_link() {
+ echo '<a class="button-secondary more-info-link" href="#">' . __( 'Learn More', 'jetpack' ) . '</a>';
+}
+add_action( 'jetpack_learn_more_button_tiled-gallery', 'jetpack_tiled_gallery_more_link' );
+// Tiled Galleries: STOP
+
+// Likes: START
+function jetpack_likes_more_info() { ?>
+
+ <div class="jp-info-img">
+ <a href="http://jetpack.me/support/likes/">
+ <img class="jp-info-img" src="<?php echo plugins_url( basename( dirname( dirname( __FILE__ ) ) ) . '/_inc/images/screenshots/likes.png' ) ?>" alt="<?php esc_attr_e( 'Likes', 'jetpack' ) ?>" width="323" height="69" />
+ </a>
+ </div>
+
+ <h4><?php esc_html_e( 'Likes' , 'jetpack' ); ?></h4>
+
+ <p><?php esc_html_e( 'Likes allow your readers to show their appreciation for your posts and other published content using their WordPress.com accounts. Your readers will then be able to review their liked posts from WordPress.com.', 'jetpack' ) ?></p>
+ <p><?php esc_html_e( 'Displayed below your posts will be how many people have liked your posts and the Gravatars of those who have liked them.', 'jetpack' ); ?></p>
+
+ <p>&rarr; <a href="http://jetpack.me/support/likes/"><?php esc_html_e( 'More information on using Likes.', 'jetpack' ); ?></a></p>
+
+<?php
+}
+add_action( 'jetpack_module_more_info_likes', 'jetpack_likes_more_info' );
+
+function jetpack_likes_more_link() {
+ echo '<a class="button-secondary more-info-link" href="#">' . __( 'Learn More', 'jetpack' ) . '</a>';
+}
+add_action( 'jetpack_learn_more_button_likes', 'jetpack_likes_more_link' );
+// Likes: STOP
diff --git a/plugins/jetpack/modules/notes.php b/plugins/jetpack/modules/notes.php
new file mode 100644
index 00000000..e33f7ff8
--- /dev/null
+++ b/plugins/jetpack/modules/notes.php
@@ -0,0 +1,195 @@
+<?php
+/**
+ * Module Name: Notifications
+ * Module Description: Monitor and manage your site's activity with Notifications in your Toolbar and on WordPress.com.
+ * Sort Order: 1
+ * First Introduced: 1.9
+ */
+
+if ( !defined( 'JETPACK_NOTES__CACHE_BUSTER' ) ) define( 'JETPACK_NOTES__CACHE_BUSTER', JETPACK__VERSION . '-' . gmdate( 'oW' ) );
+
+Jetpack_Sync::sync_options( __FILE__,
+ 'home',
+ 'blogname',
+ 'siteurl',
+ 'permalink_structure',
+ 'category_base',
+ 'tag_base',
+ 'comment_moderation',
+ 'default_comment_status',
+ 'thread_comments',
+ 'thread_comments_depth'
+);
+
+class Jetpack_Notifications {
+ var $jetpack = false;
+
+ /**
+ * Singleton
+ * @static
+ */
+ function &init() {
+ static $instance = array();
+
+ if ( !$instance ) {
+ $instance[0] = new Jetpack_Notifications;
+ }
+
+ return $instance[0];
+ }
+
+ function Jetpack_Notifications() {
+ $this->jetpack = Jetpack::init();
+
+ add_action( 'init', array( &$this, 'action_init' ) );
+
+ //post types that support comments
+ $filt_post_types = array();
+ foreach ( get_post_types() as $post_type ) {
+ if ( post_type_supports( $post_type, 'comments' ) ) {
+ $filt_post_types[] = $post_type;
+ }
+ }
+
+ Jetpack_Sync::sync_posts( __FILE__, array(
+ 'post_types' => $filt_post_types,
+ 'post_stati' => array( 'publish' ),
+ ) );
+ Jetpack_Sync::sync_comments( __FILE__, array(
+ 'post_types' => $filt_post_types,
+ 'post_stati' => array( 'publish' ),
+ 'comment_stati' => array( 'approve', 'approved', '1', 'hold', 'unapproved', 'unapprove', '0', 'spam', 'trash' ),
+ ) );
+ }
+
+ function wpcom_static_url($file) {
+ $i = hexdec( substr( md5( $file ), -1 ) ) % 2;
+ $http = is_ssl() ? 'https' : 'http';
+ $url = $http . '://s' . $i . '.wp.com' . $file;
+ return $url;
+ }
+
+ // return the major version of Internet Explorer the viewer is using or false if it's not IE
+ public static function get_internet_explorer_version() {
+ static $version;
+ if ( isset( $version ) ) {
+ return $version;
+ }
+
+ preg_match( '/MSIE (\d+)/', $_SERVER['HTTP_USER_AGENT'], $matches );
+ $version = empty( $matches[1] ) ? null : $matches[1];
+ if ( empty( $version ) || !$version ) {
+ return false;
+ }
+ return $version;
+ }
+
+ public static function current_browser_is_supported() {
+ static $supported;
+
+ if ( isset( $supported ) ) {
+ return $supported;
+ }
+
+ $ie_version = self::get_internet_explorer_version();
+ if ( false === $ie_version ) {
+ return $supported = true;
+ }
+
+ if ( $ie_version < 8 ) {
+ return $supported = false;
+ }
+
+ return $supported = true;
+ }
+
+ function action_init() {
+ if ( defined( 'DOING_AJAX' ) && DOING_AJAX )
+ return;
+
+ if ( !has_filter( 'show_admin_bar', '__return_true' ) && !is_user_logged_in() )
+ return;
+
+ if ( !self::current_browser_is_supported() )
+ return;
+
+ add_action( 'admin_bar_menu', array( &$this, 'admin_bar_menu'), 120 );
+ add_action( 'wp_head', array( &$this, 'styles_and_scripts'), 120 );
+ add_action( 'admin_head', array( &$this, 'styles_and_scripts') );
+ }
+
+ function styles_and_scripts() {
+ wp_enqueue_style( 'notes-admin-bar-rest', $this->wpcom_static_url( '/wp-content/mu-plugins/notes/admin-bar-rest.css' ), array(), JETPACK_NOTES__CACHE_BUSTER );
+ wp_enqueue_style( 'noticons', $this->wpcom_static_url( '/i/noticons/noticons.css' ), array(), JETPACK_NOTES__CACHE_BUSTER );
+
+ $this->print_js();
+
+ // attempt to use core or plugin libraries if registered
+ if ( wp_script_is( 'mustache', 'registered' ) ) {
+ if ( !wp_script_is( 'mustache', 'queue' ) ) {
+ wp_enqueue_script( 'mustache' );
+ }
+ }
+ else {
+ wp_enqueue_script( 'mustache', $this->wpcom_static_url( '/wp-content/js/mustache.js' ), null, JETPACK_NOTES__CACHE_BUSTER );
+ }
+
+ if ( wp_script_is( 'underscore', 'registered' ) ) {
+ if ( !wp_script_is( 'underscore', 'queue' ) ) {
+ wp_enqueue_script( 'underscore' );
+ }
+ }
+ else {
+ wp_enqueue_script( 'underscore', $this->wpcom_static_url( '/wp-content/js/underscore.js' ), null, JETPACK_NOTES__CACHE_BUSTER );
+ }
+ if ( wp_script_is( 'backbone', 'registered' ) ) {
+ if ( !wp_script_is( 'backbone', 'queue' ) ) {
+ wp_enqueue_script( 'backbone' );
+ }
+ }
+ else {
+ wp_enqueue_script( 'backbone', $this->wpcom_static_url( '/wp-content/js/backbone.js' ), array( 'jquery', 'underscore' ), JETPACK_NOTES__CACHE_BUSTER );
+ }
+
+ wp_enqueue_script( 'notes-postmessage', $this->wpcom_static_url( '/wp-content/js/postmessage.js' ), array(), JETPACK_NOTES__CACHE_BUSTER );
+ wp_enqueue_script( 'notes-rest-common', $this->wpcom_static_url( '/wp-content/mu-plugins/notes/notes-rest-common.js' ), array( 'backbone', 'mustache', 'jquery.spin' ), JETPACK_NOTES__CACHE_BUSTER );
+ wp_enqueue_script( 'notes-admin-bar-rest', $this->wpcom_static_url( '/wp-content/mu-plugins/notes/admin-bar-rest.js' ), array( 'jquery', 'underscore', 'backbone', 'jquery.spin' ), JETPACK_NOTES__CACHE_BUSTER );
+ }
+
+ function admin_bar_menu() {
+ global $wp_admin_bar, $current_blog;
+
+ if ( !is_object( $wp_admin_bar ) )
+ return;
+
+ $classes = 'wpnt-loading wpn-read';
+ $wp_admin_bar->add_menu( array(
+ 'id' => 'notes',
+ 'title' => '<span id="wpnt-notes-unread-count" class="' . esc_attr( $classes ) . '">
+ <span class="noticon noticon-notification" /></span>
+ </span>',
+ 'meta' => array(
+ 'html' => '<div id="wpnt-notes-panel" style="display:none"><div class="wpnt-notes-panel-header"><span class="wpnt-notes-header">' . __('Notifications', 'jetpack') . '</span><span class="wpnt-notes-panel-link"></span></div></div>',
+ 'class' => 'menupop',
+ ),
+ 'parent' => 'top-secondary',
+ ) );
+ }
+
+ function print_js() {
+ $link_accounts_url = is_user_logged_in() && !Jetpack::is_user_connected() ? Jetpack::admin_url() : false;
+?>
+<script type="text/javascript">
+/* <![CDATA[ */
+ var wpNotesIsJetpackClient = true;
+<?php if ( $link_accounts_url ) : ?>
+ var wpNotesLinkAccountsURL = '<?php print $link_accounts_url; ?>';
+<?php endif; ?>
+/* ]]> */
+</script>
+<?php
+ }
+
+}
+
+Jetpack_Notifications::init();
diff --git a/plugins/jetpack/modules/photon.php b/plugins/jetpack/modules/photon.php
new file mode 100644
index 00000000..06db8680
--- /dev/null
+++ b/plugins/jetpack/modules/photon.php
@@ -0,0 +1,9 @@
+<?php
+/**
+ * Module Name: Photon
+ * Module Description: Give your site a boost by loading images from the WordPress.com content delivery network.
+ * Sort Order: 15
+ * First Introduced: 2.0
+ */
+
+Jetpack_Photon::instance(); \ No newline at end of file
diff --git a/plugins/jetpack/modules/photon/photon.js b/plugins/jetpack/modules/photon/photon.js
new file mode 100644
index 00000000..050e73b6
--- /dev/null
+++ b/plugins/jetpack/modules/photon/photon.js
@@ -0,0 +1,42 @@
+(function($){
+ /**
+ * For images lacking explicit dimensions and needing them, try to add them.
+ */
+ var restore_dims = function() {
+ $( 'img[data-recalc-dims]' ).each( function() {
+ if ( this.complete ) {
+ var width = this.width,
+ height = this.height;
+
+ if ( width && width > 0 && height && height > 0 ) {
+ $( this ).attr( {
+ width: width,
+ height: height
+ } );
+
+ reset_for_retina( this );
+ }
+ }
+ else {
+ $( this ).load( arguments.callee );
+ }
+ } );
+ },
+
+ /**
+ * Modify given image's markup so that devicepx-jetpack.js will act on the image and it won't be reprocessed by this script.
+ */
+ reset_for_retina = function( img ) {
+ $( img ).removeAttr( 'data-recalc-dims' ).removeAttr( 'scale' );
+ };
+
+ /**
+ * Check both when page loads, and when IS is triggered.
+ */
+ $( document ).ready( restore_dims );
+
+ if ( "on" in $.fn )
+ $( document.body ).on( 'post-load', restore_dims );
+ else
+ $( document ).delegate( 'body', 'post-load', restore_dims );
+})(jQuery); \ No newline at end of file
diff --git a/plugins/jetpack/modules/post-by-email.php b/plugins/jetpack/modules/post-by-email.php
new file mode 100644
index 00000000..e8cd8873
--- /dev/null
+++ b/plugins/jetpack/modules/post-by-email.php
@@ -0,0 +1,240 @@
+<?php
+
+/**
+ * Module Name: Post by Email
+ * Module Description: Publish posts to your blog directly from your personal email account.
+ * First Introduced: 2.0
+ * Sort Order: 4
+ */
+
+add_action( 'jetpack_modules_loaded', array( 'Jetpack_Post_By_Email', 'init' ) );
+
+Jetpack_Sync::sync_options( __FILE__,
+ 'large_size_w',
+ 'large_size_h',
+ 'thumbnail_size_w',
+ 'thumbnail_size_h',
+ 'medium_size_w',
+ 'medium_size_h'
+);
+
+add_action( 'jetpack_activate_module_post-by-email', array( 'Jetpack_Post_By_Email', 'module_toggle' ) );
+add_action( 'jetpack_deactivate_module_post-by-email', array( 'Jetpack_Post_By_Email', 'module_toggle' ) );
+
+Jetpack::enable_module_configurable( __FILE__ );
+Jetpack::module_configuration_load( __FILE__, array( 'Jetpack_Post_By_Email', 'configuration_redirect' ) );
+
+class Jetpack_Post_By_Email {
+ function &init() {
+ static $instance = NULL;
+
+ if ( !$instance ) {
+ $instance = new Jetpack_Post_By_Email;
+ }
+
+ return $instance;
+ }
+
+ function __construct() {
+ add_action( 'init', array( &$this, 'action_init' ) );
+ }
+
+ function module_toggle() {
+ $jetpack = Jetpack::init();
+ $jetpack->sync->register( 'noop' );
+ }
+
+ function configuration_redirect() {
+ wp_safe_redirect( get_edit_profile_url( get_current_user_id() ) . '#post-by-email' );
+ exit;
+ }
+
+ function action_init() {
+ if ( ! current_user_can( 'edit_posts' ) )
+ return;
+
+ add_action( 'profile_personal_options', array( &$this, 'user_profile' ) );
+ add_action( 'admin_print_scripts-profile.php', array( &$this, 'profile_scripts' ) );
+
+ add_action( 'wp_ajax_jetpack_post_by_email_enable', array( &$this, 'create_post_by_email_address' ) );
+ add_action( 'wp_ajax_jetpack_post_by_email_regenerate', array( &$this, 'regenerate_post_by_email_address' ) );
+ add_action( 'wp_ajax_jetpack_post_by_email_disable', array( &$this, 'delete_post_by_email_address' ) );
+ }
+
+ function profile_scripts() {
+ wp_enqueue_script( 'post-by-email', plugins_url( 'post-by-email/post-by-email.js', __FILE__ ), array( 'jquery' ) );
+ wp_enqueue_style( 'post-by-email', plugins_url( 'post-by-email/post-by-email.css', __FILE__ ) );
+ Jetpack::init()->admin_styles();
+ }
+
+ function check_user_connection() {
+ $user_token = Jetpack_Data::get_access_token( get_current_user_id() );
+ $is_user_connected = $user_token && !is_wp_error( $user_token );
+
+ // If the user is already connected via Jetpack, then we're good
+ if ( $is_user_connected )
+ return true;
+
+ return false;
+ }
+
+ function user_profile() {
+ $blog_name = get_bloginfo( 'blogname' );
+ if ( empty( $blog_name ) ) {
+ $blog_name = home_url( '/' );
+ }
+
+ ?>
+ <div id="post-by-email" class="jetpack-targetable">
+ <h3><?php esc_html_e( 'Post by Email', 'jetpack' ); ?></h3>
+ <table class="form-table">
+ <tr>
+ <th scope="row"><?php esc_html_e( 'Email Address', 'jetpack' ); ?><span id="jp-pbe-spinner" class="spinner"></span></th>
+ <td>
+ <div id="jp-pbe-error" class="jetpack-inline-error"></div> <?php
+
+ if ( $this->check_user_connection() ) {
+ $email = $this->get_post_by_email_address();
+
+ if ( empty( $email ) ) {
+ $enable_hidden = '';
+ $info_hidden = ' style="display: none;"';
+ } else {
+ $enable_hidden = ' style="display: none;"';
+ $info_hidden = '';
+ } ?>
+
+ <input type="button" name="jp-pbe-enable" id="jp-pbe-enable" class="button" value="<?php esc_attr_e( 'Enable Post By Email', 'jetpack' ); ?> "<?php echo $enable_hidden; ?> />
+ <div id="jp-pbe-info"<?php echo $info_hidden; ?>>
+ <p id="jp-pbe-email-wrapper">
+ <input type="text" id="jp-pbe-email" value="<?php echo esc_attr( $email ); ?>" readonly="readonly" class="regular-text" />
+ <span class="description"><a target="_blank" href="http://jetpack.me/support/post-by-email/"><?php esc_html_e( 'More information', 'jetpack' ); ?></a></span>
+ </p>
+ <p>
+ <input type="button" name="jp-pbe-regenerate" id="jp-pbe-regenerate" class="button" value="<?php esc_attr_e( 'Regenerate Address', 'jetpack' ); ?> " />
+ <input type="button" name="jp-pbe-disable" id="jp-pbe-disable" class="button" value="<?php esc_attr_e( 'Disable Post By Email', 'jetpack' ); ?> " />
+ </p>
+ </div> <?php
+ } else {
+ $jetpack = Jetpack::init(); ?>
+
+ <p class="jetpack-inline-message">
+ <?php printf(
+ esc_html( wptexturize( __( 'To use Post By Email, you need to link your %s account to your WordPress.com account.', 'jetpack' ) ) ),
+ '<strong>' . esc_html( $blog_name ) . '</strong>'
+ ); ?><br />
+ <?php echo esc_html( wptexturize( __( "If you don't have a WordPress.com account yet, you can sign up for free in just a few seconds.", 'jetpack' ) ) ); ?>
+ </p>
+ <p>
+ <a href="<?php echo $jetpack->build_connect_url( false, get_edit_profile_url( get_current_user_id() ) . '#post-by-email' ); ?>" class="button button-connector" id="wpcom-connect"><?php esc_html_e( 'Link account with WordPress.com', 'jetpack' ); ?></a>
+ </p>
+ <?php
+ } ?>
+ </td>
+ </tr>
+ </table>
+ </div>
+ <?php
+ }
+
+ function get_post_by_email_address() {
+ Jetpack::load_xml_rpc_client();
+ $xml = new Jetpack_IXR_Client( array(
+ 'user_id' => get_current_user_id(),
+ ) );
+ $xml->query( 'jetpack.getPostByEmailAddress' );
+
+ if ( $xml->isError() )
+ return NULL;
+
+ $response = $xml->getResponse();
+ if ( empty( $response ) )
+ return NULL;
+
+ return $response;
+ }
+
+ function create_post_by_email_address() {
+ Jetpack::load_xml_rpc_client();
+ $xml = new Jetpack_IXR_Client( array(
+ 'user_id' => get_current_user_id(),
+ ) );
+ $xml->query( 'jetpack.createPostByEmailAddress' );
+
+ if ( $xml->isError() ) {
+ echo json_encode( array(
+ 'response' => 'error',
+ 'message' => __( 'Unable to create your Post By Email address. Please try again later.', 'jetpack' )
+ ) );
+ die();
+ }
+
+ $response = $xml->getResponse();
+ if ( empty( $response ) ) {
+ echo json_encode( array(
+ 'response' => 'error',
+ 'message' => __( 'Unable to create your Post By Email address. Please try again later.', 'jetpack' )
+ ) );
+ die();
+ }
+
+ echo $response;
+ die();
+ }
+
+ function regenerate_post_by_email_address() {
+ Jetpack::load_xml_rpc_client();
+ $xml = new Jetpack_IXR_Client( array(
+ 'user_id' => get_current_user_id(),
+ ) );
+ $xml->query( 'jetpack.regeneratePostByEmailAddress' );
+
+ if ( $xml->isError() ) {
+ echo json_encode( array(
+ 'response' => 'error',
+ 'message' => __( 'Unable to regenerate your Post By Email address. Please try again later.', 'jetpack' )
+ ) );
+ die();
+ }
+
+ $response = $xml->getResponse();
+ if ( empty( $response ) ) {
+ echo json_encode( array(
+ 'response' => 'error',
+ 'message' => __( 'Unable to regenerate your Post By Email address. Please try again later.', 'jetpack' )
+ ) );
+ die();
+ }
+
+ echo $response;
+ die();
+ }
+
+ function delete_post_by_email_address() {
+ Jetpack::load_xml_rpc_client();
+ $xml = new Jetpack_IXR_Client( array(
+ 'user_id' => get_current_user_id(),
+ ) );
+ $xml->query( 'jetpack.deletePostByEmailAddress' );
+
+ if ( $xml->isError() ) {
+ echo json_encode( array(
+ 'response' => 'error',
+ 'message' => __( 'Unable to disable your Post By Email address. Please try again later.', 'jetpack' )
+ ) );
+ die();
+ }
+
+ $response = $xml->getResponse();
+ if ( empty( $response ) ) {
+ echo json_encode( array(
+ 'response' => 'error',
+ 'message' => __( 'Unable to disable your Post By Email address. Please try again later.', 'jetpack' )
+ ) );
+ die();
+ }
+
+ echo $response;
+ die();
+ }
+}
diff --git a/plugins/jetpack/modules/post-by-email/post-by-email.css b/plugins/jetpack/modules/post-by-email/post-by-email.css
new file mode 100644
index 00000000..c3b88def
--- /dev/null
+++ b/plugins/jetpack/modules/post-by-email/post-by-email.css
@@ -0,0 +1,6 @@
+#jp-pbe-error {
+ display: none;
+}
+#post-by-email:target .jetpack-inline-message {
+ background-color: #fff;
+}
diff --git a/plugins/jetpack/modules/post-by-email/post-by-email.js b/plugins/jetpack/modules/post-by-email/post-by-email.js
new file mode 100644
index 00000000..7ba14bd7
--- /dev/null
+++ b/plugins/jetpack/modules/post-by-email/post-by-email.js
@@ -0,0 +1,129 @@
+jetpack_post_by_email = {
+ init: function() {
+ jQuery( '#jp-pbe-enable' ).click( jetpack_post_by_email.enable );
+ jQuery( '#jp-pbe-regenerate' ).click( jetpack_post_by_email.regenerate );
+ jQuery( '#jp-pbe-disable' ).click( jetpack_post_by_email.disable );
+ },
+
+ enable: function() {
+ jQuery( '#jp-pbe-enable' ).attr( 'disabled', 'disabled' );
+ jQuery( '#jp-pbe-error' ).fadeOut();
+ jQuery( '#jp-pbe-spinner' ).fadeIn();
+
+ var data = {
+ action: 'jetpack_post_by_email_enable'
+ };
+
+ jQuery.post( ajaxurl, data, jetpack_post_by_email.handle_enabled );
+ },
+
+ handle_enabled: function( response ) {
+ var enabled = false;
+ var error;
+ try {
+ error = JSON.parse( response );
+ } catch ( e ) {
+ enabled = true;
+ }
+
+ jQuery( '#jp-pbe-regenerate' ).removeAttr( 'disabled' );
+ jQuery( '#jp-pbe-disable' ).removeAttr( 'disabled' );
+
+ if ( enabled ) {
+ jQuery( '#jp-pbe-enable' ).fadeOut( 400, function() {
+ jQuery( '#jp-pbe-enable' ).removeAttr( 'disabled' );
+ jQuery( '#jp-pbe-email' ).val( response );
+ jQuery( '#jp-pbe-info' ).fadeIn();
+ });
+ } else {
+ jQuery( '#jp-pbe-error' ).text( error.message );
+ jQuery( '#jp-pbe-error' ).fadeIn();
+ jQuery( '#jp-pbe-enable' ).removeAttr( 'disabled' );
+ }
+
+ jQuery( '#jp-pbe-spinner' ).fadeOut();
+ },
+
+ regenerate: function() {
+ jQuery( '#jp-pbe-regenerate' ).attr( 'disabled', 'disabled' );
+ jQuery( '#jp-pbe-disable' ).attr( 'disabled', 'disabled' );
+ jQuery( '#jp-pbe-error' ).fadeOut();
+ jQuery( '#jp-pbe-spinner' ).fadeIn();
+
+ var data = {
+ action: 'jetpack_post_by_email_regenerate'
+ };
+
+ jQuery.post( ajaxurl, data, jetpack_post_by_email.handle_regenerated );
+ },
+
+ handle_regenerated: function( response ) {
+ var regenerated = false;
+ var error;
+ try {
+ error = JSON.parse( response );
+ } catch ( e ) {
+ regenerated = true;
+ }
+
+ if ( regenerated ) {
+ jQuery( '#jp-pbe-email-wrapper' ).fadeOut( 400, function() {
+ jQuery( '#jp-pbe-email' ).val( response );
+ jQuery( '#jp-pbe-email-wrapper' ).fadeIn();
+ });
+ } else {
+ jQuery( '#jp-pbe-error' ).text( error.message );
+ jQuery( '#jp-pbe-error' ).fadeIn();
+ }
+
+ jQuery( '#jp-pbe-regenerate' ).removeAttr( 'disabled' );
+ jQuery( '#jp-pbe-disable' ).removeAttr( 'disabled' );
+ jQuery( '#jp-pbe-spinner' ).fadeOut();
+ },
+
+ disable: function() {
+ jQuery( '#jp-pbe-regenerate' ).attr( 'disabled', 'disabled' );
+ jQuery( '#jp-pbe-disable' ).attr( 'disabled', 'disabled' );
+ jQuery( '#jp-pbe-error' ).fadeOut();
+ jQuery( '#jp-pbe-spinner' ).fadeIn();
+
+ var data = {
+ action: 'jetpack_post_by_email_disable'
+ };
+
+ jQuery.post( ajaxurl, data, jetpack_post_by_email.handle_disabled );
+ },
+
+ handle_disabled: function( response ) {
+ var disabled = false;
+ var error;
+ try {
+ error = JSON.parse( response );
+ } catch ( e ) {
+ disabled = true;
+ }
+
+ if ( 'error' != error.response ) {
+ disabled = true;
+ }
+
+ if ( disabled ) {
+ jQuery( '#jp-pbe-enable' ).removeAttr( 'disabled' );
+ jQuery( '#jp-pbe-info' ).fadeOut( 400, function() {
+ jQuery( '#jp-pbe-regenerate' ).removeAttr( 'disabled' );
+ jQuery( '#jp-pbe-disable' ).removeAttr( 'disabled' );
+ jQuery( '#jp-pbe-enable' ).fadeIn();
+ });
+ } else {
+ jQuery( '#jp-pbe-regenerate' ).removeAttr( 'disabled' );
+ jQuery( '#jp-pbe-disable' ).removeAttr( 'disabled' );
+
+ jQuery( '#jp-pbe-error' ).text( error.message );
+ jQuery( '#jp-pbe-error' ).fadeIn();
+ }
+
+ jQuery( '#jp-pbe-spinner' ).fadeOut();
+ }
+};
+
+jQuery( function() { jetpack_post_by_email.init(); } );
diff --git a/plugins/jetpack/modules/publicize.php b/plugins/jetpack/modules/publicize.php
new file mode 100644
index 00000000..c9a37e5d
--- /dev/null
+++ b/plugins/jetpack/modules/publicize.php
@@ -0,0 +1,283 @@
+<?php
+/**
+ * Module Name: Publicize
+ * Module Description: Connect your site to popular social networks and automatically share new posts with your friends.
+ * Sort Order: 1
+ * First Introduced: 2.0
+ */
+
+class Jetpack_Publicize {
+
+ var $in_jetpack = true;
+
+ function __construct() {
+ global $publicize_ui;
+
+ $this->in_jetpack = ( class_exists( 'Jetpack' ) && method_exists( 'Jetpack', 'enable_module_configurable' ) ) ? true : false;
+
+ if ( $this->in_jetpack && method_exists( 'Jetpack', 'module_configuration_load' ) ) {
+ Jetpack::enable_module_configurable( __FILE__ );
+ Jetpack::module_configuration_load( __FILE__, array( $this, 'jetpack_configuration_load' ) );
+ Jetpack_Sync::sync_posts( __FILE__ );
+ }
+
+ require_once dirname( __FILE__ ) . '/publicize/publicize.php';
+
+ if ( $this->in_jetpack )
+ require_once dirname( __FILE__ ) . '/publicize/publicize-jetpack.php';
+ else {
+ require_once dirname( dirname( __FILE__ ) ) . '/mu-plugins/keyring/keyring.php';
+ require_once dirname( __FILE__ ) . '/publicize/publicize-wpcom.php';
+ }
+
+ require_once dirname( __FILE__ ) . '/publicize/ui.php';
+ $publicize_ui = new Publicize_UI();
+ $publicize_ui->in_jetpack = $this->in_jetpack;
+
+ // Jetpack specific checks / hooks
+ if ( $this->in_jetpack) {
+ add_action( 'jetpack_activate_module_publicize', array( $this, 'module_state_toggle' ) );
+ add_action( 'jetpack_deactivate_module_publicize', array( $this, 'module_state_toggle' ) );
+
+ // if sharedaddy isn't active, the sharing menu hasn't been added yet
+ $active = Jetpack::get_active_modules();
+ if ( in_array( 'publicize', $active ) && !in_array( 'sharedaddy', $active ) )
+ add_action( 'admin_menu', array( &$publicize_ui, 'sharing_menu' ) );
+ }
+ }
+
+ function module_state_toggle() {
+ // extra check that we are on the JP blog, just incase
+ if ( class_exists( 'Jetpack' ) && $this->in_jetpack ) {
+ $jetpack = Jetpack::init();
+ $jetpack->sync->register( 'noop' );
+ }
+ }
+
+ function jetpack_configuration_load() {
+ wp_safe_redirect( menu_page_url( 'sharing', false ) );
+ exit;
+ }
+}
+
+global $publicize_ui;
+new Jetpack_Publicize;
+
+/**
+* Helper functions for shared use in the services files
+*/
+class Publicize_Util {
+ /**
+ * Truncates a string to be shorter than or equal to the length specified
+ * Attempts to truncate on word boundaries
+ *
+ * @param string $string
+ * @param int $length
+ * @return string
+ */
+ function crop_str( $string, $length = 256 ) {
+ $string = wp_strip_all_tags( (string) $string, true ); // true: collapse Linear Whitespace into " "
+ $length = absint( $length );
+
+ if ( mb_strlen( $string, 'UTF-8' ) <= $length ) {
+ return $string;
+ }
+
+ // @see wp_trim_words()
+ if ( 'characters' == _x( 'words', 'word count: words or characters?', 'jetpack' ) ) {
+ return trim( mb_substr( $string, 0, $length - 1, 'UTF-8' ) ) . "\xE2\x80\xA6"; // ellipsis
+ }
+
+ $words = explode( ' ', $string );
+
+ $return = '';
+ while ( strlen( $word = array_shift( $words ) ) ) {
+ $new_return = $return ? "$return $word" : $word;
+ $new_return_length = mb_strlen( $new_return, 'UTF-8' );
+ if ( $new_return_length < $length - 1 ) {
+ $return = $new_return;
+ continue;
+ } elseif ( $new_return_length == $length - 1 ) {
+ $return = $new_return;
+ break;
+ }
+
+ if ( !$return ) {
+ $return = mb_substr( $new_return, 0, $length - 1, 'UTF-8' );
+ }
+
+ break;
+ }
+
+ return "$return\xE2\x80\xA6"; // ellipsis
+ }
+
+
+ /**
+ * Returns an array of DOMNodes that are comments (including recursing child nodes)
+ *
+ * @param DOMNode $node
+ * @return array
+ */
+
+ function get_comment_nodes( $node ) {
+ $comment_nodes = array();
+ foreach ( $node->childNodes as $child ) {
+
+ if ( XML_COMMENT_NODE === $child->nodeType ) {
+ $comment_nodes[] = $child;
+ }
+
+ if ( $child->hasChildNodes() ) {
+ $child_comment_nodes = self::get_comment_nodes( $child );
+ $comment_nodes = array_merge( $comment_nodes, $child_comment_nodes );
+ }
+ }
+
+ return $comment_nodes;
+ }
+
+ /**
+ * Truncates HTML so that its textContent (text without markup) is shorter than or equal to the length specified.
+ * The length of the returned string may be larger than the specified length due to the markup.
+ * Attempts to truncate on word boundaries.
+ *
+ * @param string $string
+ * @param int $length
+ * @param array $allowed_tags KSES input
+ * @return string
+ */
+ function crop_html( $string, $length = 256, $allowed_tags = array() ) {
+ $tags = $GLOBALS['allowedtags']; // Markup allowed in comments...
+
+ $tags['img'] = array( // ... plus images ...
+ 'alt' => true,
+ 'height' => true,
+ 'src' => true,
+ 'width' => true,
+ );
+
+ // ... and some other basics
+ $tags['p'] = array();
+ $tags['ul'] = array();
+ $tags['ol'] = array();
+ $tags['li'] = array();
+ $tags['br'] = array();
+
+ $tags = array_merge( $tags, $allowed_tags );
+
+ // Clean up, then KSES to really lock it down
+ $string = trim( (string) $string );
+ $string = preg_replace( '@<(script|style)[^>]*?>.*?</\\1>@si', '', $string );
+ $string = wp_kses( $string, $tags );
+
+ $string = mb_convert_encoding( $string, 'HTML-ENTITIES', 'UTF-8' );
+ $dom = new DOMDocument( '1.0', 'UTF-8' );
+ @$dom->loadHTML( "<html><body>$string</body></html>" ); // suppress parser warning
+
+ // Strip comment nodes, if any
+ $comment_nodes = self::get_comment_nodes( $dom->documentElement );
+ foreach ( $comment_nodes as &$comment_node ) {
+ $comment_node->parentNode->removeChild( $comment_node );
+ }
+ if ( $comment_nodes ) {
+ // Update the $string (some return paths work from just $string)
+ $string = $dom->saveHTML();
+ $string = preg_replace( '/^<!DOCTYPE.+?>/', '', $string );
+ $string = str_replace( array('<html>', '</html>', '<body>', '</body>' ), array( '', '', '', '' ), $string );
+ $string = trim( $string );
+ }
+
+ // Find the body
+ $body = false;
+ foreach ( $dom->childNodes as $child ) {
+ if ( XML_ELEMENT_NODE === $child->nodeType && 'html' === strtolower( $child->tagName ) ) {
+ $body = $child->firstChild;
+ break;
+ }
+ }
+
+ if ( !$body ) {
+ return self::crop_str( $string, $length );
+ }
+
+ // If the text (without the markup) is shorter than $length, just return
+ if ( mb_strlen( $body->textContent, 'UTF-8' ) <= $length ) {
+ return $string;
+ }
+
+ $node = false;
+ do {
+ $node = self::remove_innermost_last_child( $body, $node_removed_from );
+ $new_string_length = mb_strlen( $body->textContent, 'UTF-8' );
+ } while ( $new_string_length > $length );
+
+ $new_string = $dom->saveHTML( $body );
+ $new_string = mb_substr( $new_string, 6, -7, 'UTF-8' ); // 6: <body>, 7: </body>
+
+ if ( !$node ) {
+ return $new_string ? $new_string : self::crop_str( $string, $length );
+ }
+
+ $append_string_length = $length - $new_string_length;
+
+ if ( !$append_string_length ) {
+ return $new_string;
+ }
+
+ if ( $append_string_length > 1 && XML_TEXT_NODE === $node->nodeType ) { // 1: ellipsis
+ $append_string = self::crop_str( $node->textContent, $append_string_length ); // includes ellipsis
+ $append_node = $dom->createTextNode( $append_string );
+ $node_removed_from->appendChild( $append_node );
+ $new_string = $dom->saveHTML( $body );
+ $new_string = mb_substr( $new_string, 6, -7, 'UTF-8' );
+ } elseif ( $append_string_length > 9 && XML_ELEMENT_NODE === $node->nodeType && 'p' == strtolower( $node->nodeName ) ) { // 9: '<p>X{\xE2\x80\xA6}</p>'
+ $new_string .= '<p>' . self::crop_str( $node->textContent, $append_string_length - 8 ) . '</p>';
+ }
+
+ // Clean up any empty Paragraphs that might have occurred after removing their children
+ return trim( preg_replace( '#<p>\s*</p>#i', '', $new_string ) );
+ }
+
+ function remove_innermost_last_child( $node, &$node_removed_from ) {
+ $node_removed_from = $node;
+
+ if ( !$node->lastChild ) {
+ return false;
+ }
+
+ if ( $node->lastChild->hasChildNodes() ) {
+ return self::remove_innermost_last_child( $node->lastChild, $node_removed_from );
+ }
+
+ $innermost_last_child = $node->lastChild;
+ $node->removeChild( $innermost_last_child );
+
+ return $innermost_last_child;
+ }
+
+ function bump_stats_extras_publicize_url( $bin, $post_id ) {
+ static $done = array();
+ if ( isset( $done[$post_id] ) ) {
+ return;
+ }
+ $done[$post_id] = true;
+
+ if ( function_exists( 'bump_stats_extras' ) )
+ bump_stats_extras( 'publicize_url', $bin );
+ }
+
+ public static function build_sprintf( $args ) {
+ $search = array();
+ $replace = array();
+ foreach ( $args as $k => $arg ) {
+ if ( 0 == $k ) {
+ $string = $arg;
+ continue;
+ }
+ $search[] = "%$arg%";
+ $replace[] = "%$k\$s";
+ }
+ return str_replace( $search, $replace, $string );
+ }
+}
diff --git a/plugins/jetpack/modules/publicize/assets/connected.gif b/plugins/jetpack/modules/publicize/assets/connected.gif
new file mode 100644
index 00000000..24e0c11f
--- /dev/null
+++ b/plugins/jetpack/modules/publicize/assets/connected.gif
Binary files differ
diff --git a/plugins/jetpack/modules/publicize/assets/facebook-logo.png b/plugins/jetpack/modules/publicize/assets/facebook-logo.png
new file mode 100644
index 00000000..b9181cc4
--- /dev/null
+++ b/plugins/jetpack/modules/publicize/assets/facebook-logo.png
Binary files differ
diff --git a/plugins/jetpack/modules/publicize/assets/linkedin-logo.png b/plugins/jetpack/modules/publicize/assets/linkedin-logo.png
new file mode 100644
index 00000000..27018dcd
--- /dev/null
+++ b/plugins/jetpack/modules/publicize/assets/linkedin-logo.png
Binary files differ
diff --git a/plugins/jetpack/modules/publicize/assets/publicize.css b/plugins/jetpack/modules/publicize/assets/publicize.css
new file mode 100644
index 00000000..4065cb01
--- /dev/null
+++ b/plugins/jetpack/modules/publicize/assets/publicize.css
@@ -0,0 +1,175 @@
+div#publicize-services-block {
+ display: inline-block;
+ clear: both;
+ margin-bottom: 25px;
+ background-color: #fff;
+ width: 100%;
+}
+
+/* Add the logos for the Publicize services */
+span.pub-logos {
+ float: left;
+ display: block;
+ width: 130px;
+ height: 75px;
+ margin-top: -18px;
+ margin-left: 5px;
+ vertical-align: top;
+}
+
+span#facebook { background: url( facebook-logo.png ) 50% 19px no-repeat; background-size: 125px 47px; }
+span#twitter { background: url( twitter-logo.png ) 50% 19px no-repeat; background-size: 125px 47px; }
+span#yahoo { background: url( yahoo-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; }
+
+a.publicize-profile-link, a.publicize-profile-link:visited {
+ text-decoration: none;
+}
+
+a.publicize-profile-link:hover {
+ color: #f1831e;
+}
+
+a.publicize-add-connection, a.publicize-add-connection:visited {
+ display: block;
+ vertical-align: middle;
+ text-decoration: none;
+}
+
+a.publicize-add-connection:hover {
+ color: #f1831e;
+}
+
+div.publicize-service-entry {
+ width: 100%;
+ clear: both;
+ margin-bottom: 10px;
+}
+
+div.publicize-service-left {
+ display: inline-block;
+ width: 150px;
+ vertical-align: top;
+}
+
+div.publicize-service-right {
+ display: inline-block;
+ margin-left: 5px;
+ margin-top: 5px;
+ width: 300px;
+ padding: 10px;
+ background-color: #f1f1f1;
+ -moz-border-radius: 10px;
+ -webkit-border-radius: 10px;
+ border-radius: 10px;
+ border: 1px solid #e5e5e5;
+}
+
+div.publicize-service-right ul {
+ margin-top: 0;
+}
+
+div.publicize-service-right li {
+ list-style-type: none;
+}
+
+.pub-disconnect-button {
+ -webkit-border-image: none;
+ border-bottom-color: #CCC;
+ border-bottom-style: none;
+ border-bottom-width: 0px;
+ border-left-color: #CCC;
+ border-left-style: none;
+ border-left-width: 0px;
+ border-right-color: #CCC;
+ border-right-style: none;
+ border-right-width: 0px;
+ border-top-color: #CCC;
+ border-top-style: none;
+ border-top-width: 0px;
+ color: #CCC;
+ cursor: auto;
+ display: inline;
+ font-family: sans-serif;
+ font-size: 15px;
+ font-style: normal;
+ font-weight: normal;
+ height: auto;
+ line-height: 22px;
+ list-style-image: none;
+ list-style-position: outside;
+ list-style-type: none;
+ margin-bottom: 0px;
+ margin-left: 0px;
+ margin-right: 0px;
+ margin-top: 0px;
+ outline-color: #CCC;
+ outline-style: none;
+ outline-width: 0px;
+ overflow-y: visible;
+ padding-bottom: 0px;
+ padding-left: 0px;
+ padding-right: 0px;
+ padding-top: 0px;
+ position: static;
+ right: auto;
+ text-align: left;
+ text-decoration: none;
+ top: auto;
+ vertical-align: baseline;
+ width: auto;
+ z-index: auto;
+}
+
+.pub-disconnect-button:hover {
+ color: #f1831e;
+}
+
+table#option-profile {
+ padding-bottom: 8px;
+}
+
+table#option-profile td {
+ font-family: "Lucida Grande",Verdana,Arial,sans-serif;
+ vertical-align: middle;
+}
+
+table#option-profile td.radio {
+ padding-right: 20px;
+}
+
+table#option-profile td.thumbnail {
+ padding-right: 20px;
+}
+
+table#option-profile td.details {
+ font-weight: bold; color: #333333
+}
+
+table#option-fb-fanpage td {
+ font-family: "Lucida Grande",Verdana,Arial,sans-serif;
+ vertical-align: middle;
+}
+
+table#option-fb-fanpage td.thumbnail {
+ padding: 5px 20px 5px 20px;
+}
+
+table#option-fb-fanpage td.details {
+ width: 130px;
+ padding-right: 10px;
+}
+
+table#option-fb-fanpage td.details span.name {
+ font-weight: bold; color: #333333;
+}
+
+table#option-fb-fanpage td.details span.category {
+ font-size: 10px; color: #888888;
+}
+
+input.fb-options {
+ font-family: "Lucida Grande",Verdana,Arial,sans-serif;
+ font-size: 12px;
+} \ No newline at end of file
diff --git a/plugins/jetpack/modules/publicize/assets/publicize.js b/plugins/jetpack/modules/publicize/assets/publicize.js
new file mode 100644
index 00000000..bb1f8d4c
--- /dev/null
+++ b/plugins/jetpack/modules/publicize/assets/publicize.js
@@ -0,0 +1,108 @@
+var showOptionsPage;
+
+jQuery( function( $ ) {
+
+ showOptionsPage = function( service, nonce, connection, blogId ) {
+ tb_show( null, null, null );
+ $("body").append( "<div id='TB_load'><img src='" + tb_pathToImage + "' /></div>" );
+ $('#TB_load').show();
+
+ var query = '';
+ if ( null != connection ) {
+ query += '&connection=' + encodeURIComponent( connection );
+ }
+ if ( 'undefined' != typeof( blogId ) && null != blogId ) {
+ query += '&blog_id=' + Number( blogId );
+ }
+
+ $.post( ajaxurl, 'action=publicize_' + service + '_options_page&_wpnonce=' + nonce + query, function( response ) {
+ $("#TB_load").remove();
+
+ try {
+ var obj = jQuery.parseJSON( response );
+ if ( null != obj && 'object' == typeof( obj ) ) {
+ if ( obj.hasOwnProperty( 'fb_redirect' ) ) {
+ location.href = obj.fb_redirect + '&redirect_uri=' + encodeURIComponent( location.href );
+ return;
+ }
+ }
+ } catch (err) {
+ // Do nothing and move on
+ }
+
+ if ( response != '' ) {
+ var blogID = $( 'input[name=wpas_ajax_blog_id]' ).val();
+
+ var message = $( '<div id="wpas-ajax-' + blogID + '" class="wrap"></div>' ).append( response );
+ message.append( '<a href="#TB_inline?thickbox&height=420&width=555&inlineId=wpas-ajax-' + blogID + '" id="wpas-click-' + blogID + '" class="new-thickbox" style="display: none;"></a>' );
+ $('#wpas-message').html( message );
+
+
+ tb_init( 'a.new-thickbox' );
+ $('#wpas-click-' + blogID).click();
+
+ var tb_height = parseInt( $('#TB_ajaxContent').css('height') );
+ var content_height = $('#thickbox-content').height();
+ if ( content_height < tb_height ) {
+ var new_height = content_height + 15;
+ $('#TB_ajaxContent').css( 'height', new_height );
+
+ var new_margin = parseInt( $('#TB_window').css( 'margin-top') ) + (tb_height - new_height) / 2 + 'px'
+ $('#TB_window').css( 'margin-top', new_margin);
+ }
+
+ $('.save-options').unbind('click').click( function() {
+ var sel = $( "input[name='option']:checked" );
+ var global = $( "input[name='global']:checked" );
+
+ var connection = $(this).data('connection');
+ var token = encodeURIComponent( sel.val() );
+ var id = encodeURIComponent( sel.attr( 'id' ) );
+ var type = encodeURIComponent( sel.attr( 'data-type' ) );
+ var nonce = $(this).attr('rel');
+ var global_conn = 'off';
+ var global_nonce = '';
+
+ if ( global.length ) {
+ global_conn = 'on';
+ global_nonce = global.val();
+ }
+
+ $.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';
+ } );
+
+ } );
+ }
+
+ }, 'html' );
+ }
+
+ $( 'body' ).append( '<div id="wpas-message" style="display: none"></div>' );
+ var messageDiv = $( '#wpas-message' );
+
+ $( '.wpas-posts' ).change( function() {
+ var inputs = $(this).parents( 'td:first' ).find( ':input' );
+ var _this = this;
+ var blogID = inputs.filter( '[name=wpas_ajax_blog_id]' ).val();
+
+ $( '#waiting_' + blogID ).show();
+ $.post( ajaxurl, inputs.serialize() + '&action=wpas_post', function( response ) { myblogsResponse.call( _this, blogID, response ) }, 'html' );
+ } );
+
+ $( '.options' ).unbind('click').bind( 'click', function(e) {
+ e.preventDefault();
+ e.stopPropagation();
+
+ var service = $(this).attr('class').replace( 'options ', '' );
+
+ var blogId = null;
+ if( 'undefined' != typeof( $(this).attr('id') ) )
+ blogId = parseInt( $(this).attr('id').replace( 'options-', '' ) );
+
+ var nonce = $(this).attr('href').replace( '#nonce=', '' );
+ var connection = $(this).data( 'connection' );
+ showOptionsPage.call( this, service, nonce, connection, blogId );
+ });
+} );
diff --git a/plugins/jetpack/modules/publicize/assets/rtl/publicize-rtl.css b/plugins/jetpack/modules/publicize/assets/rtl/publicize-rtl.css
new file mode 100644
index 00000000..b56ab2fb
--- /dev/null
+++ b/plugins/jetpack/modules/publicize/assets/rtl/publicize-rtl.css
@@ -0,0 +1,177 @@
+/* This file was automatically generated on Dec 18 2012 19:58:18 */
+
+div#publicize-services-block {
+ display: inline-block;
+ clear: both;
+ margin-bottom: 25px;
+ background-color: #fff;
+ width: 100%;
+}
+
+/* Add the logos for the Publicize services */
+span.pub-logos {
+ float: right;
+ display: block;
+ width: 130px;
+ height: 75px;
+ margin-top: -18px;
+ margin-right: 5px;
+ vertical-align: top;
+}
+
+span#facebook { background: url( ../facebook-logo.png ) 50% 19px no-repeat; background-size: 125px 47px; }
+span#twitter { background: url( ../twitter-logo.png ) 50% 19px no-repeat; background-size: 125px 47px; }
+span#yahoo { background: url( ../yahoo-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; }
+
+a.publicize-profile-link, a.publicize-profile-link:visited {
+ text-decoration: none;
+}
+
+a.publicize-profile-link:hover {
+ color: #f1831e;
+}
+
+a.publicize-add-connection, a.publicize-add-connection:visited {
+ display: block;
+ vertical-align: middle;
+ text-decoration: none;
+}
+
+a.publicize-add-connection:hover {
+ color: #f1831e;
+}
+
+div.publicize-service-entry {
+ width: 100%;
+ clear: both;
+ margin-bottom: 10px;
+}
+
+div.publicize-service-left {
+ display: inline-block;
+ width: 150px;
+ vertical-align: top;
+}
+
+div.publicize-service-right {
+ display: inline-block;
+ margin-right: 5px;
+ margin-top: 5px;
+ width: 300px;
+ padding: 10px;
+ background-color: #f1f1f1;
+ -moz-border-radius: 10px;
+ -webkit-border-radius: 10px;
+ border-radius: 10px;
+ border: 1px solid #e5e5e5;
+}
+
+div.publicize-service-right ul {
+ margin-top: 0;
+}
+
+div.publicize-service-right li {
+ list-style-type: none;
+}
+
+.pub-disconnect-button {
+ -webkit-border-image: none;
+ border-bottom-color: #CCC;
+ border-bottom-style: none;
+ border-bottom-width: 0px;
+ border-right-color: #CCC;
+ border-right-style: none;
+ border-right-width: 0px;
+ border-left-color: #CCC;
+ border-left-style: none;
+ border-left-width: 0px;
+ border-top-color: #CCC;
+ border-top-style: none;
+ border-top-width: 0px;
+ color: #CCC;
+ cursor: auto;
+ display: inline;
+ font-family: sans-serif;
+ font-size: 15px;
+ font-style: normal;
+ font-weight: normal;
+ height: auto;
+ line-height: 22px;
+ list-style-image: none;
+ list-style-position: outside;
+ list-style-type: none;
+ margin-bottom: 0px;
+ margin-right: 0px;
+ margin-left: 0px;
+ margin-top: 0px;
+ outline-color: #CCC;
+ outline-style: none;
+ outline-width: 0px;
+ overflow-y: visible;
+ padding-bottom: 0px;
+ padding-right: 0px;
+ padding-left: 0px;
+ padding-top: 0px;
+ position: static;
+ left: auto;
+ text-align: right;
+ text-decoration: none;
+ top: auto;
+ vertical-align: baseline;
+ width: auto;
+ z-index: auto;
+}
+
+.pub-disconnect-button:hover {
+ color: #f1831e;
+}
+
+table#option-profile {
+ padding-bottom: 8px;
+}
+
+table#option-profile td {
+ font-family: "Lucida Grande",Verdana,Arial,sans-serif;
+ vertical-align: middle;
+}
+
+table#option-profile td.radio {
+ padding-left: 20px;
+}
+
+table#option-profile td.thumbnail {
+ padding-left: 20px;
+}
+
+table#option-profile td.details {
+ font-weight: bold; color: #333333
+}
+
+table#option-fb-fanpage td {
+ font-family: "Lucida Grande",Verdana,Arial,sans-serif;
+ vertical-align: middle;
+}
+
+table#option-fb-fanpage td.thumbnail {
+ padding: 5px 20px 5px 20px;
+}
+
+table#option-fb-fanpage td.details {
+ width: 130px;
+ padding-left: 10px;
+}
+
+table#option-fb-fanpage td.details span.name {
+ font-weight: bold; color: #333333;
+}
+
+table#option-fb-fanpage td.details span.category {
+ font-size: 10px; color: #888888;
+}
+
+input.fb-options {
+ font-family: "Lucida Grande",Verdana,Arial,sans-serif;
+ font-size: 12px;
+} \ No newline at end of file
diff --git a/plugins/jetpack/modules/publicize/assets/spinner.gif b/plugins/jetpack/modules/publicize/assets/spinner.gif
new file mode 100644
index 00000000..6e5bace6
--- /dev/null
+++ b/plugins/jetpack/modules/publicize/assets/spinner.gif
Binary files differ
diff --git a/plugins/jetpack/modules/publicize/assets/tumblr-logo.png b/plugins/jetpack/modules/publicize/assets/tumblr-logo.png
new file mode 100644
index 00000000..1ec62d39
--- /dev/null
+++ b/plugins/jetpack/modules/publicize/assets/tumblr-logo.png
Binary files differ
diff --git a/plugins/jetpack/modules/publicize/assets/twitter-logo.png b/plugins/jetpack/modules/publicize/assets/twitter-logo.png
new file mode 100644
index 00000000..6c7b89b3
--- /dev/null
+++ b/plugins/jetpack/modules/publicize/assets/twitter-logo.png
Binary files differ
diff --git a/plugins/jetpack/modules/publicize/assets/yahoo-logo.png b/plugins/jetpack/modules/publicize/assets/yahoo-logo.png
new file mode 100644
index 00000000..b7ffe2bb
--- /dev/null
+++ b/plugins/jetpack/modules/publicize/assets/yahoo-logo.png
Binary files differ
diff --git a/plugins/jetpack/modules/publicize/publicize-jetpack.php b/plugins/jetpack/modules/publicize/publicize-jetpack.php
new file mode 100644
index 00000000..58e25648
--- /dev/null
+++ b/plugins/jetpack/modules/publicize/publicize-jetpack.php
@@ -0,0 +1,578 @@
+<?php
+
+class Publicize extends Publicize_Base {
+
+ function __construct() {
+ parent::__construct();
+
+ add_action( 'load-settings_page_sharing', array( $this, 'admin_page_load' ), 9 );
+
+ add_action( 'wp_ajax_publicize_tumblr_options_page', array( $this, 'options_page_tumblr' ) );
+ 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_yahoo_options_page', array( $this, 'options_page_yahoo' ) );
+
+ 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_yahoo_options_save', array( $this, 'options_save_yahoo' ) );
+
+ add_action( 'load-settings_page_sharing', array( $this, 'force_user_connection' ) );
+
+ add_action( 'transition_post_status', array( $this, 'save_publicized' ), 10, 3 );
+ }
+
+ function force_user_connection() {
+ global $current_user;
+ $user_token = Jetpack_Data::get_access_token( $current_user->ID );
+ $is_user_connected = $user_token && !is_wp_error( $user_token );
+
+ // If the user is already connected via Jetpack, then we're good
+ if ( $is_user_connected )
+ return;
+
+ // If they're not connected, then remove the Publicize UI and tell them they need to connect first
+ global $publicize_ui;
+ remove_action( 'pre_admin_screen_sharing', array( $publicize_ui, 'admin_page' ) );
+
+ Jetpack::init()->admin_styles();
+ add_action( 'pre_admin_screen_sharing', array( $this, 'admin_page_warning' ), 1 );
+ }
+
+ function admin_page_warning() {
+ $jetpack = Jetpack::init();
+ $blog_name = get_bloginfo( 'blogname' );
+ if ( empty( $blog_name ) ) {
+ $blog_name = home_url( '/' );
+ }
+
+ ?>
+ <div id="message" class="updated jetpack-message jp-connect">
+ <div class="jetpack-wrap-container">
+ <div class="jetpack-text-container">
+ <h4>
+ <p><?php printf(
+ esc_html( wptexturize( __( "To use Publicize, you'll need to link your %s account to your WordPress.com account using the button to the right.", 'jetpack' ) ) ),
+ '<strong>' . esc_html( $blog_name ) . '</strong>'
+ ); ?></p>
+ <p><?php echo esc_html( wptexturize( __( "If you don't have a WordPress.com account yet, you can sign up for free in just a few seconds.", 'jetpack' ) ) ); ?></p>
+ </h4>
+ </div>
+ <div class="jetpack-install-container">
+ <p class="submit"><a href="<?php echo $jetpack->build_connect_url( false, menu_page_url( 'sharing', false ) ); ?>" class="button-connector" id="wpcom-connect"><?php esc_html_e( 'Link account with WordPress.com', 'jetpack' ); ?></a></p>
+ </div>
+ </div>
+ </div>
+ <?php
+ }
+
+ function get_connections( $service_name, $_blog_id = false, $_user_id = false ) {
+ $connections = Jetpack::get_option( 'publicize_connections' );
+ $connections_to_return = array();
+ if ( !empty( $connections ) && is_array( $connections ) ) {
+ if ( !empty( $connections[$service_name] ) ) {
+ foreach( $connections[$service_name] as $id => $connection ) {
+ if ( 0 == $connection['connection_data']['user_id'] || $this->user_id() == $connection['connection_data']['user_id'] ) {
+ $connections_to_return[$id] = $connection;
+ }
+ }
+ }
+ return $connections_to_return;
+ }
+ return false;
+ }
+
+ function get_connection_id( $connection ) {
+ return $connection['connection_data']['id'];
+ }
+
+ function get_connection_meta( $connection ) {
+ $connection['user_id'] = $connection['connection_data']['user_id']; // Allows for shared connections
+ return $connection;
+ }
+
+ function admin_page_load() {
+ if ( isset( $_GET['action'] ) ) {
+ if ( isset( $_GET['service'] ) )
+ $service_name = $_GET['service'];
+
+ switch ( $_GET['action'] ) {
+ case 'error':
+ add_action( 'pre_admin_screen_sharing', array( $this, 'display_connection_error' ), 9 );
+ break;
+
+ case 'request':
+ check_admin_referer( 'keyring-request', 'kr_nonce' );
+ check_admin_referer( "keyring-request-$service_name", 'nonce' );
+
+ $verification = Jetpack::create_nonce( 'publicize' );
+
+ $stats_options = get_option( 'stats_options' );
+ $wpcom_blog_id = Jetpack::get_option('id');
+ $wpcom_blog_id = !empty( $wpcom_blog_id ) ? $wpcom_blog_id : $stats_options['blog_id'];
+
+ $user = wp_get_current_user();
+ $redirect = $this->api_url( $service_name, urlencode_deep( array(
+ 'action' => 'request',
+ 'redirect_uri' => add_query_arg( array( 'action' => 'done' ), menu_page_url( 'sharing', false ) ),
+ 'for' => 'publicize', // required flag that says this connection is intended for publicize
+ 'siteurl' => site_url(),
+ 'state' => $user->ID,
+ 'blog_id' => $wpcom_blog_id,
+ 'secret_1' => $verification['secret_1'],
+ 'secret_2' => $verification['secret_2'],
+ 'eol' => $verification['eol'],
+ ) ) );
+ wp_redirect( $redirect );
+ exit;
+ break;
+
+ case 'completed':
+ // Jetpack blog requests Publicize Connections via new XML-RPC method
+ Jetpack::load_xml_rpc_client();
+ $xml = new Jetpack_IXR_Client();
+ $xml->query( 'jetpack.fetchPublicizeConnections' );
+
+ if ( !$xml->isError() ) {
+ $response = $xml->getResponse();
+ Jetpack::update_option( 'publicize_connections', $response );
+ }
+ break;
+
+ case 'delete':
+ $id = $_GET['id'];
+
+ check_admin_referer( 'keyring-request', 'kr_nonce' );
+ check_admin_referer( "keyring-request-$service_name", 'nonce' );
+
+ Jetpack::load_xml_rpc_client();
+ $xml = new Jetpack_IXR_Client();
+ $xml->query( 'jetpack.deletePublicizeConnection', $id );
+
+ if ( !$xml->isError() ) {
+ $response = $xml->getResponse();
+ Jetpack::update_option( 'publicize_connections', $response );
+ }
+ add_action( 'admin_notices', array( $this, 'display_disconnected' ) );
+ break;
+ }
+ }
+
+ // Errors encountered on WordPress.com's end are passed back as a code
+ if ( isset( $_GET['action'] ) && 'error' == $_GET['action'] ) {
+ // Load Jetpack's styles to handle the box
+ Jetpack::init()->admin_styles();
+ }
+ }
+
+ function display_connection_error() {
+ $code = false;
+ if ( isset( $_GET['service'] ) ) {
+ $service_name = $_GET['service'];
+ $error = sprintf( __( 'There was a problem connecting to %s to create an authorized connection. Please try again in a moment.', 'jetpack' ), Publicize::get_service_label( $service_name ) );
+ } else {
+ if ( isset( $_GET['publicize_error'] ) ) {
+ $code = strtolower( $_GET['publicize_error'] );
+ switch ( $code ) {
+ case '400':
+ $error = __( 'An invalid request was made. This normally means that something intercepted or corrupted the request from your server to the Jetpack Server. Try again and see if it works this time.', 'jetpack' );
+ break;
+ case 'secret_mismatch':
+ $error = __( 'We could not verify that your server is making an authorized request. Please try again, and make sure there is nothing interfering with requests from your server to the Jetpack Server.', 'jetpack' );
+ break;
+ case 'empty_blog_id':
+ $error = __( 'No blog_id was included in your request. Please try disconnecting Jetpack from WordPress.com and then reconnecting it. Once you have done that, try connecting Publicize again.', 'jetpack' );
+ break;
+ case 'empty_state':
+ $error = sprintf( __( 'No user information was included in your request. Please make sure that your user account has connected to Jetpack. Connect your user account by going to the <a href="%s">Jetpack page</a> within wp-admin.', 'jetpack' ), Jetpack::admin_url() );
+ break;
+ default:
+ $error = __( 'Something which should never happen, happened. Sorry about that. If you try again, maybe it will work.', 'jetpack' );
+ break;
+ }
+ } else {
+ $error = __( 'There was a problem connecting with Publicize. Please try again in a moment.', 'jetpack' );
+ }
+ }
+ // Using the same formatting/style as Jetpack::admin_notices() error
+ ?>
+ <div id="message" class="jetpack-message jetpack-err">
+ <div class="squeezer">
+ <h4><?php echo wp_kses( $error, array( 'a' => array( 'href' => true ), 'code' => true, 'strong' => true, 'br' => true, 'b' => true ) ); ?></h4>
+ <?php if ( $code ) : ?>
+ <p><?php printf( __( 'Error code: %s', 'jetpack' ), esc_html( stripslashes( $code ) ) ); ?></p>
+ <?php endif; ?>
+ </div>
+ </div>
+ <?php
+ }
+
+ function display_disconnected() {
+ echo "<div class='updated'>\n";
+ echo '<p>' . esc_html( __( 'That connection has been removed.', 'jetpack' ) ) . "</p>\n";
+ echo "</div>\n\n";
+ }
+
+ function globalization() {
+ if ( 'on' == $_REQUEST['global'] ) {
+ $id = $_REQUEST['connection'];
+
+ if ( !current_user_can( Publicize::GLOBAL_CAP ) )
+ return;
+
+ Jetpack::load_xml_rpc_client();
+ $xml = new Jetpack_IXR_Client();
+ $xml->query( 'jetpack.globalizePublicizeConnection', $id, 'globalize' );
+
+ if ( !$xml->isError() ) {
+ $response = $xml->getResponse();
+ Jetpack::update_option( 'publicize_connections', $response );
+ }
+ }
+ }
+
+ /**
+ * Gets a URL to the public-api actions. Works like WP's admin_url
+ *
+ * @param string $service Shortname of a specific service.
+ * @return URL to specific public-api process
+ */
+ // on WordPress.com this is/calls Keyring::admin_url
+ function api_url( $service = false, $params = array() ) {
+ $url = apply_filters( 'publicize_api_url', 'https://public-api.wordpress.com/connect/?jetpack=publicize' );
+
+ if ( $service )
+ $url = add_query_arg( array( 'service' => $service ), $url );
+
+ if ( count ( $params ) )
+ $url = add_query_arg( $params, $url );
+
+ return $url;
+ }
+
+ function connect_url( $service_name ) {
+ return add_query_arg( array(
+ 'action' => 'request',
+ 'service' => $service_name,
+ 'kr_nonce' => wp_create_nonce( 'keyring-request' ),
+ 'nonce' => wp_create_nonce( "keyring-request-$service_name" ),
+ ), menu_page_url( 'sharing', false ) );
+ }
+
+ function disconnect_url( $service_name, $id ) {
+ return add_query_arg( array (
+ 'action' => 'delete',
+ 'service' => $service_name,
+ 'id' => $id,
+ 'kr_nonce' => wp_create_nonce( 'keyring-request' ),
+ 'nonce' => wp_create_nonce( "keyring-request-$service_name" ),
+ ), menu_page_url( 'sharing', false ) );
+ }
+
+ function get_services( $filter ) {
+ if ( !in_array( $filter, array( 'all', 'connected' ) ) )
+ $filter = 'all';
+
+ $services = array(
+ 'facebook' => array(),
+ 'twitter' => array(),
+ 'linkedin' => array(),
+ 'tumblr' => array(),
+ 'yahoo' => array(),
+ );
+
+ if ( 'all' == $filter ) {
+ return $services;
+ } else {
+ $connected_services = array();
+ foreach ( $services as $service => $empty ) {
+ $connections = $this->get_connections( $service );
+ if ( $connections )
+ $connected_services[$service] = $connections;
+ }
+ return $connected_services;
+ }
+ }
+
+ function get_connection( $service, $id, $_blog_id = false, $_user_id = false ) {
+ // Stub
+ }
+
+ function flag_post_for_publicize( $new_status, $old_status, $post ) {
+ // Stub only. Doesn't need to do anything on Jetpack Client
+ }
+
+ /**
+ * Save a flag locally to indicate that this post has already been Publicized via the selected
+ * connections.
+ */
+ function save_publicized( $new_status, $old_status, $post ) {
+ // Only do this when a post transitions to being published
+ if ( 'publish' == $new_status && 'publish' != $old_status ) {
+ update_post_meta( $post->ID, $this->POST_DONE . 'all', true );
+ }
+ }
+
+ /**
+ * Options Code
+ */
+
+ function options_page_facebook() {
+ $connected_services = Jetpack::get_option( 'publicize_connections' );
+ $connection = $connected_services['facebook'][$_REQUEST['connection']];
+ $options_to_show = $connection['connection_data']['meta']['options_responses'];
+
+ // Nonce check
+ check_admin_referer( 'options_page_facebook_' . $_REQUEST['connection'] );
+
+ $me = $options_to_show[0];
+ $pages = $options_to_show[1]['data'];
+
+ $profile_checked = true;
+ $page_selected = false;
+
+ if ( !empty( $connection['connection_data']['meta']['facebook_page'] ) ) {
+ $found = false;
+ if ( is_array( $pages->data ) ) {
+ foreach ( $pages->data as $page ) {
+ if ( $page->id == $connection['connection_data']['meta']['facebook_page'] ) {
+ $found = true;
+ break;
+ }
+ }
+ }
+
+ if ( $found ) {
+ $profile_checked = false;
+ $page_selected = $connection['connection_data']['meta']['facebook_page'];
+ }
+ }
+
+ ?>
+
+ <div id="thickbox-content">
+
+ <?php
+ ob_start();
+ Publicize_UI::connected_notice( 'Facebook' );
+ $update_notice = ob_get_clean();
+
+ if ( ! empty( $update_notice ) )
+ echo $update_notice;
+ ?>
+
+ <?php if ( !empty( $me['name'] ) ) : ?>
+ <p><?php printf(
+ esc_html__( 'Publicize to my %s:', 'jetpack' ),
+ '<strong>' . esc_html__( 'Facebook Wall', 'jetpack' ) . '</strong>'
+ ); ?></p>
+ <table id="option-profile">
+ <tbody>
+ <tr>
+ <td class="radio"><input type="radio" name="option" data-type="profile" id="<?php echo esc_attr( $me['id'] ) ?>" value="" <?php checked( $profile_checked, true ); ?> /></td>
+ <td class="thumbnail"><label for="<?php echo esc_attr( $me['id'] ) ?>"><img src="<?php echo esc_url( $me['picture']['data']['url'] ) ?>" width="50" height="50" /></label></td>
+ <td class="details"><label for="<?php echo esc_attr( $me['id'] ) ?>"><?php echo esc_html( $me['name'] ) ?></label></td>
+ </tr>
+ </tbody>
+ </table>
+ <?php endif; ?>
+
+ <?php if ( $pages ) : ?>
+
+ <p><?php printf(
+ esc_html__( 'Publicize to my %s:', 'jetpack' ),
+ '<strong>' . esc_html__( 'Facebook Page', 'jetpack' ) . '</strong>'
+ ); ?></p>
+ <table id="option-fb-fanpage">
+ <tbody>
+
+ <?php foreach ( $pages as $i => $page ) : ?>
+ <?php if ( ! isset( $page['perms'] ) ) { continue; } ?>
+ <?php if ( ! ( $i % 2 ) ) : ?>
+ <tr>
+ <?php endif; ?>
+ <td class="radio"><input type="radio" name="option" data-type="page" id="<?php echo esc_attr( $page['id'] ) ?>" value="<?php echo esc_attr( $page['id'] ) ?>" <?php checked( $page_selected && $page_selected == $page['id'], true ); ?> /></td>
+ <td class="thumbnail"><label for="<?php echo esc_attr( $page['id'] ) ?>"><img src="<?php echo esc_url( str_replace( '_s', '_q', $page['picture']['data']['url'] ) ) ?>" width="50" height="50" /></label></td>
+ <td class="details">
+ <label for="<?php echo esc_attr( $page['id'] ) ?>">
+ <span class="name"><?php echo esc_html( $page['name'] ) ?></span><br/>
+ <span class="category"><?php echo esc_html( $page['category'] ) ?></span>
+ </label>
+ </td>
+ <?php if ( ( $i % 2 ) || ( $i == count( $pages ) - 1 ) ): ?>
+ </tr>
+ <?php endif; ?>
+ <?php endforeach; ?>
+
+ </tbody>
+ </table>
+
+ <?php endif; ?>
+
+ <?php Publicize_UI::global_checkbox( 'facebook', $_REQUEST['connection'] ); ?>
+
+ <p style="text-align: center;">
+ <input type="submit" value="<?php esc_attr_e( 'OK', 'jetpack' ) ?>" class="button fb-options save-options" name="save" data-connection="<?php echo esc_attr( $_REQUEST['connection'] ); ?>" rel="<?php echo wp_create_nonce('save_fb_token_' . $_REQUEST['connection'] ) ?>" />
+ </p><br/>
+ </div>
+
+ <?php
+ }
+
+ function options_save_facebook() {
+ // Nonce check
+ check_admin_referer( 'save_fb_token_' . $_REQUEST['connection'] );
+
+ $id = $_POST['connection'];
+
+ // Check for a numeric page ID
+ $page_id = $_POST['selected_id'];
+ if ( !ctype_digit( $page_id ) )
+ die( 'Security check' );
+
+ if ( isset( $_POST['selected_id'] ) && 'profile' == $_POST['type'] ) {
+ // Publish to User Wall/Profile
+ $options = array(
+ 'facebook_page' => null,
+ 'facebook_profile' => true
+ );
+
+ } else {
+ if ( 'page' != $_POST['type'] || !isset( $_POST['selected_id'] ) ) {
+ return;
+ }
+
+ // Publish to Page
+ $options = array(
+ 'facebook_page' => $page_id,
+ 'facebook_profile' => null
+ );
+ }
+
+ Jetpack::load_xml_rpc_client();
+ $xml = new Jetpack_IXR_Client();
+ $xml->query( 'jetpack.setPublicizeOptions', $id, $options );
+
+ if ( !$xml->isError() ) {
+ $response = $xml->getResponse();
+ Jetpack::update_option( 'publicize_connections', $response );
+ }
+
+ $this->globalization();
+ }
+
+ function options_page_tumblr() {
+ // Nonce check
+ check_admin_referer( 'options_page_tumblr_' . $_REQUEST['connection'] );
+
+ $connected_services = Jetpack::get_option( 'publicize_connections' );
+ $connection = $connected_services['tumblr'][$_POST['connection']];
+ $options_to_show = $connection['connection_data']['meta']['options_responses'];
+ $request = $options_to_show[0];
+
+ $blogs = $request['response']['user']['blogs'];
+
+ $blog_selected = false;
+
+ if ( !empty( $connection['connection_data']['meta']['tumblr_base_hostname'] ) ) {
+ foreach ( $blogs as $blog ) {
+ if ( $connection['connection_data']['meta']['tumblr_base_hostname'] == $this->get_basehostname( $blog['url'] ) ) {
+ $blog_selected = $connection['connection_data']['meta']['tumblr_base_hostname'];
+ break;
+ }
+ }
+
+ }
+
+ // Use their Primary blog if they haven't selected one yet
+ if ( !$blog_selected ) {
+ foreach ( $blogs as $blog ) {
+ if ( $blog['primary'] )
+ $blog_selected = $this->get_basehostname( $blog['url'] );
+ }
+ } ?>
+
+ <div id="thickbox-content">
+
+ <?php
+ ob_start();
+ Publicize_UI::connected_notice( 'Tumblr' );
+ $update_notice = ob_get_clean();
+
+ if ( ! empty( $update_notice ) )
+ echo $update_notice;
+ ?>
+
+ <p><?php printf(
+ esc_html__( 'Publicize to my %s:', 'jetpack' ),
+ '<strong>' . esc_html__( 'Tumblr blog', 'jetpack' ) . '</strong>'
+ ); ?></p>
+
+ <ul id="option-tumblr-blog">
+
+ <?php
+ foreach ( $blogs as $blog ) {
+ $url = $this->get_basehostname( $blog['url'] ); ?>
+ <li>
+ <input type="radio" name="option" data-type="blog" id="<?php echo esc_attr( $url ) ?>" value="<?php echo esc_attr( $url ) ?>" <?php checked( $blog_selected == $url, true ); ?> />
+ <label for="<?php echo esc_attr( $url ) ?>"><span class="name"><?php echo esc_html( $blog['title'] ) ?></span></label>
+ </li>
+ <?php } ?>
+
+ </ul>
+
+ <?php Publicize_UI::global_checkbox( 'tumblr', $_REQUEST['connection'] ); ?>
+
+ <p style="text-align: center;">
+ <input type="submit" value="<?php esc_attr_e( 'OK', 'jetpack' ) ?>" class="button tumblr-options save-options" name="save" data-connection="<?php echo esc_attr( $_REQUEST['connection'] ); ?>" rel="<?php echo wp_create_nonce( 'save_tumblr_blog_' . $_REQUEST['connection'] ) ?>" />
+ </p> <br />
+ </div>
+
+ <?php
+ }
+
+ function get_basehostname( $url ) {
+ return parse_url( $url, PHP_URL_HOST );
+ }
+
+ function options_save_tumblr() {
+ // Nonce check
+ check_admin_referer( 'save_tumblr_blog_' . $_REQUEST['connection'] );
+
+ $id = $_POST['connection'];
+
+ $options = array( 'tumblr_base_hostname' => $_POST['selected_id'] );
+
+ Jetpack::load_xml_rpc_client();
+ $xml = new Jetpack_IXR_Client();
+ $xml->query( 'jetpack.setPublicizeOptions', $id, $options );
+
+ if ( !$xml->isError() ) {
+ $response = $xml->getResponse();
+ Jetpack::update_option( 'publicize_connections', $response );
+ }
+
+ $this->globalization();
+ }
+
+ function options_page_twitter() { Publicize_UI::options_page_other( 'twitter' ); }
+ function options_page_linkedin() { Publicize_UI::options_page_other( 'linkedin' ); }
+ function options_page_yahoo() { Publicize_UI::options_page_other( 'yahoo' ); }
+
+ function options_save_twitter() { $this->options_save_other( 'twitter' ); }
+ function options_save_linkedin() { $this->options_save_other( 'linkedin' ); }
+ function options_save_yahoo() { $this->options_save_other( 'yahoo' ); }
+
+ function options_save_other( $service_name ) {
+ // Nonce check
+ check_admin_referer( 'save_' . $service_name . '_token_' . $_REQUEST['connection'] );
+ $this->globalization();
+ }
+
+ // stub
+ function refresh_tokens_message() {
+
+ }
+}
diff --git a/plugins/jetpack/modules/publicize/publicize.php b/plugins/jetpack/modules/publicize/publicize.php
new file mode 100644
index 00000000..16d4541c
--- /dev/null
+++ b/plugins/jetpack/modules/publicize/publicize.php
@@ -0,0 +1,328 @@
+<?php
+
+abstract class Publicize_Base {
+
+ /**
+ * Services that are currently connected to the given user
+ * through publicize.
+ */
+ var $connected_services = array();
+
+ /**
+ * Sservices that are supported by publicize. They don't
+ * neccessarly need to be connected to the current user.
+ */
+ var $services;
+
+ /**
+ * key names for post meta
+ */
+ var $ADMIN_PAGE = 'wpas';
+ var $POST_MESS = '_wpas_mess';
+ var $POST_SKIP = '_wpas_skip_'; // connection id appended to indicate that a connection should NOT be publicized to
+ var $POST_DONE = '_wpas_done_'; // connection id appended to indicate a connection has already been publicized to
+ var $USER_AUTH = 'wpas_authorize';
+ var $USER_OPT = 'wpas_';
+ var $PENDING = '_publicize_pending'; // ready for Publicize to do its thing
+ var $POST_SERVICE_DONE = '_publicize_done_external'; // array of external ids where we've Publicized
+
+ /**
+ * default pieces of the message used in constructing the
+ * content pushed out to other social networks
+ */
+ var $default_prefix = '';
+ var $default_message = '%title%';
+ var $default_suffix = ' %url%';
+
+ /**
+ * What WP capability is require to create/delete global connections?
+ * All users with this cap can unglobalize all other global connections, and globalize any of their own
+ * Globalized connections cannot be unselected by users without this capability when publishing
+ */
+ const GLOBAL_CAP = 'edit_others_posts';
+
+ /**
+ * Sets up the basics of Publicize
+ */
+ function __construct() {
+ $this->default_message = Publicize_Util::build_sprintf( array(
+ apply_filters( 'wpas_default_message', $this->default_message ),
+ 'title',
+ 'url',
+ ) );
+
+ $this->default_prefix = Publicize_Util::build_sprintf( array(
+ apply_filters( 'wpas_default_prefix', $this->default_prefix ),
+ 'url',
+ ) );
+
+ $this->default_suffix = Publicize_Util::build_sprintf( array(
+ apply_filters( 'wpas_default_suffix', $this->default_suffix ),
+ 'url',
+ ) );
+
+
+ // stage 1 and 2 of 3-stage Publicize. Flag for Publicize on creation, save meta,
+ // then check meta and publicze based on that. stage 3 implemented on wpcom
+ add_action( 'transition_post_status', array( $this, 'flag_post_for_publicize' ), 10, 3 );
+ add_action( 'save_post', array( &$this, 'save_meta' ), 20, 2 );
+ }
+
+ /**
+ * Functions to be implemented by the extended class (publicize-wpcom or publicize-jetpack)
+ */
+ abstract function get_connection_id( $connection );
+ abstract function connect_url( $service_name );
+ abstract function disconnect_url( $service_name, $id );
+ abstract function get_connection_meta( $connection );
+ abstract function get_services( $filter );
+ abstract function get_connections( $service, $_blog_id = false, $_user_id = false );
+ abstract function get_connection( $service, $id, $_blog_id = false, $_user_id = false );
+ abstract function flag_post_for_publicize( $new_status, $old_status, $post );
+
+ /**
+ * Shared Functions
+ */
+
+ /**
+ * Returns an external URL to the connection's profile
+ */
+ function get_profile_link( $service_name, $c ) {
+ $cmeta = $this->get_connection_meta( $c );
+
+ if ( isset( $cmeta['connection_data']['meta']['link'] ) ) {
+ return $cmeta['connection_data']['meta']['link'];
+ } elseif ( 'facebook' == $service_name && isset( $cmeta['connection_data']['meta']['facebook_page'] ) ) {
+ return 'http://facebook.com/' . $cmeta['connection_data']['meta']['facebook_page'];
+ } elseif ( 'facebook' == $service_name ) {
+ return 'http://www.facebook.com/' . $cmeta['external_id'];
+ } elseif ( 'tumblr' == $service_name && isset( $cmeta['connection_data']['meta']['tumblr_base_hostname'] ) ) {
+ return 'http://' . $cmeta['connection_data']['meta']['tumblr_base_hostname'];
+ } elseif ( 'twitter' == $service_name ) {
+ return 'http://twitter.com/' . substr( $cmeta['external_display'], 1 ); // Has a leading '@'
+ } else if ( 'yahoo' == $service_name ) {
+ return 'http://profile.yahoo.com/' . $cmeta['external_id'];
+ } else if ( 'linkedin' == $service_name ) {
+ if ( !isset( $cmeta['connection_data']['meta']['profile_url'] ) ) {
+ return false;
+ }
+
+ $profile_url_query = parse_url( $cmeta['connection_data']['meta']['profile_url'], PHP_URL_QUERY );
+ wp_parse_str( $profile_url_query, $profile_url_query_args );
+ if ( !isset( $profile_url_query_args['key'] ) ) {
+ return false;
+ }
+
+ return esc_url_raw( add_query_arg( 'id', urlencode( $profile_url_query_args['key'] ), 'http://www.linkedin.com/profile/view' ) );
+ } else {
+ return false; // no fallback. we just won't link it
+ }
+ }
+
+ /**
+ * Returns a display name for the connection
+ */
+ function get_display_name( $service_name, $c ) {
+ $cmeta = $this->get_connection_meta( $c );
+
+ if ( isset( $cmeta['connection_data']['meta']['display_name'] ) ) {
+ return $cmeta['connection_data']['meta']['display_name'];
+ } elseif ( $service_name == 'tumblr' && isset( $cmeta['connection_data']['meta']['tumblr_base_hostname'] ) ) {
+ return $cmeta['connection_data']['meta']['tumblr_base_hostname'];
+ } elseif ( $service_name == 'twitter' ) {
+ return $cmeta['external_display'];
+ } else {
+ $connection_display = $cmeta['external_display'];
+ if ( empty( $connection_display ) )
+ $connection_display = $cmeta['external_name'];
+ return $connection_display;
+ }
+ }
+
+ function get_service_label( $service_name ) {
+ switch ( $service_name ) {
+ case 'yahoo':
+ return 'Yahoo!';
+ break;
+ case 'linkedin':
+ return 'LinkedIn';
+ break;
+ case 'twitter':
+ case 'facebook':
+ case 'tumblr':
+ default:
+ return ucfirst( $service_name );
+ break;
+ }
+ }
+
+ function show_options_popup( $service_name, $c ) {
+ $cmeta = $this->get_connection_meta( $c );
+
+ // always show if no selection has been made for facebook
+ if ( 'facebook' == $service_name && empty( $cmeta['connection_data']['meta']['facebook_profile'] ) && empty( $cmeta['connection_data']['meta']['facebook_page'] ) )
+ return true;
+
+ // always show if no selection has been made for tumblr
+ if ( 'tumblr' == $service_name && empty ( $cmeta['connection_data']['meta']['tumblr_base_hostname'] ) )
+ return true;
+
+ // if we have the specific conncetion info..
+ if ( isset( $_GET['id'] ) ) {
+ if ( $cmeta['connection_data']['id'] == $_GET['id'] )
+ return true;
+ } else {
+ // otherwise, just show if this is the completed step / first load
+ if ( !empty( $_GET['action'] ) && 'completed' == $_GET['action'] && !empty( $_GET['service'] ) && $service_name == $_GET['service'] && ! in_array( $_GET['service'], array( 'facebook', 'tumblr' ) ) )
+ return true;
+ }
+
+ return false;
+ }
+
+ function user_id() {
+ global $current_user;
+ return $current_user->ID;
+ }
+
+ function blog_id() {
+ return get_current_blog_id();
+ }
+
+ /**
+ * Returns true if a user has a connection to a particular service, false otherwise
+ */
+ function is_enabled( $service, $_blog_id = false, $_user_id = false ) {
+ if ( !$_blog_id )
+ $_blog_id = $this->blog_id();
+
+ if ( !$_user_id )
+ $_user_id = $this->user_id();
+
+ $connections = $this->get_connections( $service, $_blog_id, $_user_id );
+ return ( is_array( $connections ) && count( $connections ) > 0 ? true : false );
+ }
+
+ /**
+ * Fires when a post is saved, checks conditions and saves state in postmeta so that it
+ * can be picked up later by @see ::publicize_post()
+ */
+ function save_meta( $post_id, $post ) {
+ $cron_user = null;
+ $submit_post = true;
+
+ // don't do anything if its not actually a post
+ if ( 'post' !== $post->post_type )
+ return;
+
+ // Don't Publicize during certain contexts:
+
+ // - import
+ if ( defined( 'WP_IMPORTING' ) && WP_IMPORTING )
+ $submit_post = false;
+
+ // - on quick edit, autosave, etc but do fire on p2, quickpress, and instapost ajax
+ if (
+ defined( 'DOING_AJAX' )
+ &&
+ DOING_AJAX
+ &&
+ !did_action( 'p2_ajax' )
+ &&
+ !did_action( 'wp_ajax_json_quickpress_post' )
+ &&
+ !did_action( 'wp_ajax_instapost_publish' )
+ &&
+ !did_action( 'wp_ajax_post_reblog' )
+ ) {
+ $submit_post = false;
+ }
+
+ // - bulk edit
+ if ( isset( $_GET['bulk_edit'] ) )
+ $submit_post = false;
+
+ // - API/XML-RPC Test Posts
+ if (
+ (
+ defined( 'XMLRPC_REQUEST' )
+ &&
+ XMLRPC_REQUEST
+ ||
+ defined( 'APP_REQUEST' )
+ &&
+ APP_REQUEST
+ )
+ &&
+ 0 === strpos( $post->post_title, 'Temporary Post Used For Theme Detection' )
+ ) {
+ $submit_post = false;
+ }
+
+ // only work with certain statuses (avoids inherits, auto drafts etc)
+ if ( !in_array( $post->post_status, array( 'publish', 'draft', 'future' ) ) )
+ $submit_post = false;
+
+ // don't publish password protected posts
+ if ( '' !== $post->post_password )
+ $submit_post = false;
+
+ // Did this request happen via wp-admin?
+ $from_web = 'post' == strtolower( $_SERVER['REQUEST_METHOD'] ) && isset( $_POST[$this->ADMIN_PAGE] );
+
+ if ( ( $from_web || defined( 'POST_BY_EMAIL' ) ) && !empty( $_POST['wpas_title'] ) )
+ update_post_meta( $post_id, $this->POST_MESS, trim( stripslashes( $_POST['wpas_title'] ) ) );
+
+ // change current user to provide context for get_services() if we're running during cron
+ if ( defined( 'DOING_CRON' ) && DOING_CRON ) {
+ $cron_user = (int) $GLOBALS['user_ID'];
+ wp_set_current_user( $post->post_author );
+ }
+
+ /**
+ * In this phase, we mark connections that we want to SKIP. When Publicize is actually triggered,
+ * it will Publicize to everything *except* those marked for skipping.
+ */
+ foreach ( (array) $this->get_services( 'connected' ) as $service_name => $connections ) {
+ foreach ( $connections as $connection ) {
+ if ( false == apply_filters( 'wpas_submit_post?', $submit_post, $post_id, $service_name ) ) {
+ delete_post_meta( $post_id, $this->PENDING );
+ continue;
+ }
+
+ if ( !empty( $connection->unique_id ) )
+ $unique_id = $connection->unique_id;
+ else if ( !empty( $connection['connection_data']['token_id'] ) )
+ $unique_id = $connection['connection_data']['token_id'];
+
+ // This was a wp-admin request, so we need to check the state of checkboxes
+ if ( $from_web ) {
+ // We *unchecked* this stream from the admin page, or it's set to readonly, or it's a new addition
+ if ( empty( $_POST[$this->ADMIN_PAGE]['submit'][$unique_id] ) ) {
+ // Also make sure that the service-specific input isn't there.
+ // If the user connected to a new service 'in-page' then a hidden field with the service
+ // name is added, so we just assume they wanted to Publicize to that service.
+ if ( empty( $_POST[$this->ADMIN_PAGE]['submit'][$service_name] ) ) {
+ // Nothing seems to be checked, so we're going to mark this one to be skipped
+ update_post_meta( $post_id, $this->POST_SKIP . $unique_id, 1 );
+ continue;
+ }
+ } else {
+ // The checkbox for this connection is explicitly checked -- make sure we DON'T skip it
+ delete_post_meta( $post_id, $this->POST_SKIP . $unique_id );
+ }
+ }
+
+ // Users may hook in here and do anything else they need to after meta is written,
+ // and before the post is processed for Publicize.
+ do_action( 'publicize_save_meta', $submit_post, $post_id, $service_name, $connection );
+ }
+ }
+
+ if ( defined( 'DOING_CRON' ) && DOING_CRON ) {
+ wp_set_current_user( $cron_user );
+ }
+
+ // Next up will be ::publicize_post()
+ }
+}
diff --git a/plugins/jetpack/modules/publicize/ui.php b/plugins/jetpack/modules/publicize/ui.php
new file mode 100644
index 00000000..d9d2b37c
--- /dev/null
+++ b/plugins/jetpack/modules/publicize/ui.php
@@ -0,0 +1,545 @@
+<?php
+
+/**
+* Only user facing pieces of Publicize are found here.
+*/
+class Publicize_UI {
+
+ /**
+ * Contains an instance of class 'publicize' which loads Keyring, sets up services, etc.
+ */
+ var $publicize;
+
+ /**
+ * Hooks into WordPress to display the various pieces of UI and load our assets
+ */
+ function __construct() {
+ global $publicize;
+
+ $this->publicize = $publicize = new Publicize;
+
+ // assets (css, js)
+ add_action( 'load-settings_page_sharing', array( &$this, 'load_assets' ) );
+ add_action( 'admin_head-post.php', array( &$this, 'post_page_metabox_assets' ) );
+ add_action( 'admin_head-post-new.php', array( &$this, 'post_page_metabox_assets' ) );
+
+ // management of publicize (sharing screen, ajax/lightbox popup, and metabox on post screen)
+ add_action( 'pre_admin_screen_sharing', array( &$this, 'admin_page' ) );
+ add_action( 'post_submitbox_misc_actions', array( &$this, 'post_page_metabox' ) );
+ }
+
+ /**
+ * If the ShareDaddy plugin is not active we need to add the sharing settings page to the menu still
+ */
+ function sharing_menu() {
+ add_submenu_page( 'options-general.php', __( 'Sharing Settings', 'jetpack' ), __( 'Sharing', 'jetpack' ), 'publish_posts', 'sharing', array( &$this, 'management_page' ) );
+ }
+
+
+ /**
+ * Management page to load if Sharedaddy is not active so the 'pre_admin_screen_sharing' action exists.
+ */
+ function management_page() { ?>
+ <div class="wrap">
+ <div class="icon32" id="icon-options-general"><br /></div>
+ <h2><?php _e( 'Sharing Settings', 'jetpack' ); ?></h2>
+
+ <?php do_action( 'pre_admin_screen_sharing' ) ?>
+
+ </div> <?php
+ }
+
+ /**
+ * styling for the sharing screen and popups
+ * JS for the options and switching
+ */
+ function load_assets() {
+ wp_enqueue_script(
+ 'publicize',
+ plugins_url( 'assets/publicize.js', __FILE__ ),
+ array( 'jquery', 'thickbox' ),
+ '20121019'
+ );
+
+ wp_enqueue_style(
+ 'publicize',
+ plugins_url( 'assets/publicize.css', __FILE__ ),
+ array(),
+ '20120925'
+ );
+
+ add_thickbox();
+ }
+
+ function connected_notice( $service_name ) { ?>
+ <div class='updated'>
+ <p><?php printf( __( 'You have successfully connected your blog with your %s account.', 'jetpack' ), Publicize::get_service_label( $service_name ) ); ?></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
+ */
+ function admin_page() {
+ $_blog_id = get_current_blog_id();
+ ?>
+
+ <form action="" id="publicize-form">
+ <h3 id="publicize"><?php _e( 'Publicize', 'jetpack' ) ?></h3>
+ <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' ); ?>
+ </p>
+
+ <?php
+ if ( $this->in_jetpack )
+ $doc_link = "http://jetpack.me/support/publicize/";
+ else
+ $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>
+
+ <div id="publicize-services-block">
+ <?php
+ foreach ( $this->publicize->get_services( 'all' ) as $name => $service ) :
+ $connect_url = $this->publicize->connect_url( $name );
+ ?>
+ <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>
+ </div>
+
+ <div class="publicize-service-right">
+ <?php if ( $this->publicize->is_enabled( $name ) && $connections = $this->publicize->get_connections( $name ) ) : ?>
+ <ul>
+ <?php
+ foreach( $connections as $c ) :
+ $id = $this->publicize->get_connection_id( $c );
+ $disconnect_url = $this->publicize->disconnect_url( $name, $id );
+
+ $cmeta = $this->publicize->get_connection_meta( $c );
+ $profile_link = $this->publicize->get_profile_link( $name, $c );
+ $connection_display = $this->publicize->get_display_name( $name, $c );
+
+ $options_nonce = wp_create_nonce( 'options_page_' . $name . '_' . $id ); ?>
+
+ <?php if ( $this->publicize->show_options_popup( $name, $c ) ): ?>
+ <script type="text/javascript">
+ jQuery(document).ready( function($) {
+ showOptionsPage.call(
+ this,
+ '<?php echo esc_js( $name ); ?>',
+ '<?php echo esc_js( $options_nonce ); ?>',
+ '<?php echo esc_js( $id ); ?>'
+ );
+ } );
+ </script>
+ <?php endif; ?>
+
+ <li>
+ <?php
+ if ( !empty( $profile_link ) ) : ?>
+ <a class="publicize-profile-link" href="<?php echo esc_url( $profile_link ); ?>">
+ <?php echo esc_html( $connection_display ); ?>
+ </a><?php
+ else :
+ echo esc_html( $connection_display );
+ endif;
+ ?>
+
+ <?php if ( 0 == $cmeta['connection_data']['user_id'] ) : ?>
+ <small>(<?php esc_html_e( 'Shared', 'jetpack' ); ?>)</small>
+
+ <?php if ( current_user_can( Publicize::GLOBAL_CAP ) ) : ?>
+ <a class="pub-disconnect-button" title="<?php esc_html_e( 'Disconnect', 'jetpack' ); ?>" href="<?php echo esc_url( $disconnect_url ); ?>">×</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>
+ <?php endif; ?>
+ </li>
+
+ <?php
+ endforeach;
+ ?>
+ </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>
+ </div>
+ </div>
+ <?php endforeach; ?>
+ </div>
+
+ <?php wp_nonce_field( "wpas_posts_{$_blog_id}", "_wpas_posts_{$_blog_id}_nonce" ); ?>
+ <input type="hidden" id="wpas_ajax_blog_id" name="wpas_ajax_blog_id" value="<?php echo $_blog_id; ?>" />
+ </form><?php
+
+ }
+
+ function global_checkbox( $service_name, $id ) {
+ if ( current_user_can( Publicize::GLOBAL_CAP ) ) : ?>
+ <p>
+ <input id="globalize_<?php echo $service_name; ?>" type="checkbox" name="global" value="<?php echo wp_create_nonce( 'publicize-globalize-' . $id ) ?>" />
+ <label for="globalize_<?php echo $service_name; ?>"><?php _e( 'Make this connection available to all users of this blog?', 'jetpack' ); ?></label>
+ </p>
+ <?php endif;
+ }
+
+ function broken_connection( $service_name, $id ) { ?>
+ <div id="thickbox-content">
+ <div class='error'>
+ <p><?php printf( __( 'There was a problem connecting to %s. Please disconnect and try again.', 'jetpack' ), Publicize::get_service_label( $service_name ) ); ?></p>
+ </div>
+ </div><?php
+ }
+
+ function options_page_other( $service_name ) {
+ // Nonce check
+ check_admin_referer( "options_page_{$service_name}_" . $_REQUEST['connection'] );
+ ?>
+ <div id="thickbox-content">
+ <?php
+ ob_start();
+ Publicize_UI::connected_notice( $service_name );
+ $update_notice = ob_get_clean();
+ if ( ! empty( $update_notice ) )
+ echo $update_notice;
+ ?>
+
+ <?php Publicize_UI::global_checkbox( $service_name, $_REQUEST['connection'] ); ?>
+
+ <p style="text-align: center;">
+ <input type="submit" value="<?php esc_attr_e( 'OK', 'jetpack' ) ?>" class="button <?php echo $service_name; ?>-options save-options" name="save" data-connection="<?php echo esc_attr( $_REQUEST['connection'] ); ?>" rel="<?php echo wp_create_nonce( 'save_'.$service_name.'_token_' . $_REQUEST['connection'] ) ?>" />
+ </p> <br />
+ </div>
+ <?php
+ }
+
+ /**
+ * CSS for styling the publicize message box and counter that displays on the post page.
+ * There is also some Javascript for length counting and some basic display effects.
+ */
+ function post_page_metabox_assets() {
+ global $post;
+ $user_id = empty( $post->post_author ) ? $GLOBALS['user_ID'] : $post->post_author;
+
+ $default_prefix = $this->publicize->default_prefix;
+ $default_prefix = preg_replace( '/%([0-9])\$s/', "' + %\\1\$s + '", esc_js( $default_prefix ) );
+
+ $default_message = $this->publicize->default_message;
+ $default_message = preg_replace( '/%([0-9])\$s/', "' + %\\1\$s + '", esc_js( $default_message ) );
+
+ $default_suffix = $this->publicize->default_suffix;
+ $default_suffix = preg_replace( '/%([0-9])\$s/', "' + %\\1\$s + '", esc_js( $default_suffix ) ); ?>
+
+<script type="text/javascript">
+jQuery( function($) {
+ var wpasTitleCounter = $( '#wpas-title-counter' ),
+ wpasTwitterCheckbox = $( '.wpas-submit-twitter' ).size(),
+ wpasTitle = $('#wpas-title').keyup( function() {
+ var length = wpasTitle.val().length;
+ wpasTitleCounter.text( length );
+ if ( wpasTwitterCheckbox && length > 140 ) {
+ wpasTitleCounter.addClass( 'wpas-twitter-length-limit' );
+ } else {
+ wpasTitleCounter.removeClass( 'wpas-twitter-length-limit' );
+ }
+ } ),
+ authClick = false;
+
+ $('#publicize-disconnected-form-show').click( function() {
+ $('#publicize-form').slideDown( 'fast' );
+ $(this).hide();
+ } );
+
+ $('#publicize-disconnected-form-hide').click( function() {
+ $('#publicize-form').slideUp( 'fast' );
+ $('#publicize-disconnected-form-show').show();
+ } );
+
+ $('#publicize-form-edit').click( function() {
+ $('#publicize-form').slideDown( 'fast', function() {
+ wpasTitle.focus();
+ if ( !wpasTitle.text() ) {
+ var url = $('#shortlink').size() ? $('#shortlink').val() : '';
+
+ var defaultMessage = $.trim( '<?php printf( $default_prefix, 'url' ); printf( $default_message, '$("#title").val()', 'url' ); printf( $default_suffix, 'url' ); ?>' );
+
+ wpasTitle.append( defaultMessage.replace( /<[^>]+>/g,'') );
+
+ var selBeg = defaultMessage.indexOf( $("#title").val() );
+ if ( selBeg < 0 ) {
+ selBeg = 0;
+ selEnd = 0;
+ } else {
+ selEnd = selBeg + $("#title").val().length;
+ }
+
+ var domObj = wpasTitle.get(0);
+ if ( domObj.setSelectionRange ) {
+ domObj.setSelectionRange( selBeg, selEnd );
+ } else if ( domObj.createTextRange ) {
+ var r = domObj.createTextRange();
+ r.moveStart( 'character', selBeg );
+ r.moveEnd( 'character', selEnd );
+ r.select();
+ }
+ }
+ wpasTitle.keyup();
+ } );
+ $('#publicize-defaults').hide();
+ $(this).hide();
+ return false;
+ } );
+
+ $('#publicize-form-hide').click( function() {
+ var newList = $.map( $('#publicize-form').slideUp( 'fast' ).find( ':checked' ), function( el ) {
+ return $.trim( $(el).parent( 'label' ).text() );
+ } );
+ $('#publicize-defaults').html( '<strong>' + newList.join( '</strong>, <strong>' ) + '</strong>' ).show();
+ $('#publicize-form-edit').show();
+ return false;
+ } );
+
+ $('.authorize-link').click( function() {
+ if ( authClick ) {
+ return false;
+ }
+ authClick = true;
+ $(this).after( '<img src="images/loading.gif" class="alignleft" style="margin: 0 .5em" />' );
+ $.ajaxSetup( { async: false } );
+ autosave();
+ return true;
+ } );
+
+ $( '.pub-service' ).click( function() {
+ var service = $(this).data( 'service' ),
+ fakebox = '<input id="wpas-submit-' + service + '" type="hidden" value="1" name="wpas[submit][' + service + ']" />';
+ $( '#add-publicize-check' ).append( fakebox );
+ } );
+} );
+</script>
+
+<style type="text/css">
+#publicize {
+ line-height: 1.5;
+}
+#publicize ul {
+ margin: 4px 0 4px 6px;
+}
+#publicize li {
+ margin: 0;
+}
+#publicize textarea {
+ margin: 4px 0 0;
+ width: 100%
+}
+#publicize ul.not-connected {
+ list-style: square;
+ padding-left: 1em;
+}
+.post-new-php .authorize-link, .post-php .authorize-link {
+ line-height: 1.5em;
+}
+.post-new-php .authorize-message, .post-php .authorize-message {
+ margin-bottom: 0;
+}
+#poststuff #publicize .updated p {
+ margin: .5em 0;
+}
+.wpas-twitter-length-limit {
+ color: red;
+}
+</style><?php
+ }
+
+ /**
+ * Controls the metabox that is displayed on the post page
+ * Allows the user to customize the message that will be sent out to the social network, as well as pick which
+ * networks to publish to. Also displays the character counter and some other information.
+ */
+ function post_page_metabox() {
+ global $post;
+
+ if ( 'post' != $post->post_type )
+ return;
+
+ $user_id = empty( $post->post_author ) ? $GLOBALS['user_ID'] : $post->post_author;
+ $services = $this->publicize->get_services( 'connected' );
+ $available_services = $this->publicize->get_services( 'all' );
+
+ if ( ! is_array( $available_services ) )
+ $available_services = array();
+
+ if ( ! is_array( $services ) )
+ $services = array();
+
+ $active = array(); ?>
+
+ <div id="publicize" class="misc-pub-section misc-pub-section-last">
+ <?php
+ _e( 'Publicize:', 'jetpack' );
+
+ if ( 0 < count( $services ) ) :
+ ob_start();
+ ?>
+
+ <div id="publicize-form" class="hide-if-js">
+ <ul>
+
+ <?php
+ // We can set an _all flag to indicate that this post is completely done as
+ // far as Publicize is concerned. Jetpack uses this approach. All published posts in Jetpack
+ // have Publicize disabled.
+ $all_done = get_post_meta( $post->ID, $this->publicize->POST_DONE . 'all', true ) || ( $this->in_jetpack && 'publish' == $post->post_status );
+
+ // We don't allow Publicizing to the same external id twice, to prevent spam
+ $service_id_done = (array) get_post_meta( $post->ID, $this->publicize->POST_SERVICE_DONE, true );
+
+ foreach ( $services as $name => $connections ) {
+ foreach ( $connections as $connection ) {
+ if ( !$continue = apply_filters( 'wpas_submit_post?', true, $post->ID, $name ) )
+ continue;
+
+ if ( !empty( $connection->unique_id ) )
+ $unique_id = $connection->unique_id;
+ else if ( !empty( $connection['connection_data']['token_id'] ) )
+ $unique_id = $connection['connection_data']['token_id'];
+
+ // Should we be skipping this one?
+ $skip = (
+ get_post_meta( $post->ID, $this->publicize->POST_SKIP . $unique_id, true )
+ ||
+ (
+ is_array( $connection )
+ &&
+ (
+ ( isset( $connection['meta']['external_id'] ) && ! empty( $service_id_done[ $name ][ $connection['meta']['external_id'] ] ) )
+ ||
+ // Jetpack's connection data looks a little different.
+ ( isset( $connection['external_id'] ) && ! empty( $service_id_done[ $name ][ $connection['external_id'] ] ) )
+ )
+ )
+ );
+
+ // Was this connections (OR, old-format service) already Publicized to?
+ $done = ( 1 == get_post_meta( $post->ID, $this->publicize->POST_DONE . $unique_id, true ) || 1 == get_post_meta( $post->ID, $this->publicize->POST_DONE . $name, true ) ); // New and old style flags
+
+ // If this one has already been publicized to, don't let it happen again
+ $disabled = '';
+ if ( $done )
+ $disabled = ' disabled="disabled"';
+
+ // If this is a global connection and this user doesn't have enough permissions to modify
+ // those connections, don't let them change it
+ $cmeta = $this->publicize->get_connection_meta( $connection );
+ $hidden_checkbox = false;
+ if ( !$done && ( 0 == $cmeta['connection_data']['user_id'] && !current_user_can( Publicize::GLOBAL_CAP ) ) ) {
+ $disabled = ' disabled="disabled"';
+ $hidden_checkbox = true;
+ }
+
+ // Determine the state of the checkbox (on/off) and allow filtering
+ $checked = $skip != 1 || $done;
+ $checked = apply_filters( 'publicize_checkbox_default', $checked, $post->ID, $name, $connection );
+
+ // This post has been handled, so disable everything
+ if ( $all_done )
+ $disabled = ' disabled="disabled"';
+
+ $label = sprintf(
+ _x( '%1$s: %2$s', 'Service: Account connected as', 'jetpack' ),
+ esc_html( $this->publicize->get_service_label( $name ) ),
+ esc_html( $this->publicize->get_display_name( $name, $connection ) )
+ );
+ if ( !$skip || $done ) {
+ $active[] = $label;
+ }
+ ?>
+ <li>
+ <label for="wpas-submit-<?php echo esc_attr( $unique_id ); ?>">
+ <input type="checkbox" name="wpas[submit][<?php echo $unique_id; ?>]" id="wpas-submit-<?php echo $unique_id; ?>" class="wpas-submit-<?php echo $name; ?>" value="1" <?php
+ checked( true, $checked );
+ echo $disabled;
+ ?> />
+ <?php
+ if ( $hidden_checkbox ) {
+ // Need to submit a value to force a global connection to post
+ echo '<input type="hidden" name="wpas[submit][' . $unique_id . ']" value="1" />';
+ }
+ echo esc_html( $label );
+ ?>
+ </label>
+ </li>
+ <?php
+ }
+ }
+
+ if ( $title = get_post_meta( $post->ID, $this->publicize->POST_MESS, true ) )
+ $title = esc_html( $title );
+ else
+ $title = '';
+ ?>
+
+ </ul>
+
+ <label for="wpas-title"><?php _e( 'Custom Message:', 'jetpack' ); ?></label>
+ <span id="wpas-title-counter" class="alignright hide-if-no-js">0</span>
+
+ <textarea name="wpas_title" id="wpas-title"<?php disabled( $all_done ); ?>><?php echo $title; ?></textarea>
+
+ <a href="#" class="hide-if-no-js" id="publicize-form-hide"><?php _e( 'Hide', 'jetpack' ); ?></a>
+ <input type="hidden" name="wpas[0]" value="1" />
+
+ </div> <?php // #publicize-form
+
+ $this->publicize->refresh_tokens_message();
+
+ $publicize_form = ob_get_clean();
+ else :
+ echo "&nbsp;" . __( 'Not Connected', 'jetpack' );
+ ob_start();
+ ?>
+
+ <div id="publicize-form" class="hide-if-js">
+ <div id="add-publicize-check" style="display: none;"></div>
+
+ <strong><?php _e( 'Connect to', 'jetpack' ); ?>:</strong>
+
+ <ul class="not-connected">
+ <?php foreach ( $available_services as $service_name => $service ) : ?>
+ <li>
+ <a class="pub-service" data-service="<?php echo esc_attr( $service_name ); ?>" title="<?php echo esc_attr( sprintf( __( 'Connect and share your posts on %s', 'jetpack' ), $this->publicize->get_service_label( $service_name ) ) ); ?>" target="_blank" href="<?php echo $this->publicize->connect_url( $service_name ); ?>">
+ <?php echo esc_html( $this->publicize->get_service_label( $service_name ) ); ?>
+ </a>
+ </li>
+ <?php endforeach; ?>
+ </ul>
+
+ <?php if ( 0 < count( $services ) ) : ?>
+ <a href="#" class="hide-if-no-js" id="publicize-form-hide"><?php _e( 'Hide', 'jetpack' ); ?></a>
+ <?php else : ?>
+ <a href="#" class="hide-if-no-js" id="publicize-disconnected-form-hide"><?php _e( 'Hide', 'jetpack' ); ?></a>
+ <?php endif; ?>
+ </div> <?php // #publicize-form
+
+ $publicize_form = ob_get_clean();
+ endif;
+ ?>
+
+ <span id="publicize-defaults"><strong><?php echo join( '</strong>, <strong>', array_map( 'esc_html', $active ) ); ?></strong></span>
+
+ <?php if ( 0 < count( $services ) ) : ?>
+ <a href="#" id="publicize-form-edit"><?php _e( 'Edit', 'jetpack' ); ?></a>&nbsp;<a href="<?php echo admin_url( 'options-general.php?page=sharing' ); ?>" target="_blank"><?php _e( 'Settings', 'jetpack' ); ?></a><br />
+ <?php else : ?>
+ <a href="#" id="publicize-disconnected-form-show"><?php _e( 'Show', 'jetpack' ); ?></a><br />
+ <?php endif; ?>
+
+ <?php echo apply_filters( 'publicize_form', $publicize_form ); ?>
+
+ </div> <?php // #publicize
+ }
+
+}
diff --git a/plugins/jetpack/modules/sharedaddy.php b/plugins/jetpack/modules/sharedaddy.php
index fa7b36a1..f6f4ad6e 100644
--- a/plugins/jetpack/modules/sharedaddy.php
+++ b/plugins/jetpack/modules/sharedaddy.php
@@ -18,6 +18,6 @@ function sharedaddy_loaded() {
}
function sharedaddy_configuration_load() {
- wp_safe_redirect( menu_page_url( 'sharing', false ) );
+ wp_safe_redirect( menu_page_url( 'sharing', false ) . "#sharing-buttons" );
exit;
}
diff --git a/plugins/jetpack/modules/sharedaddy/admin-sharing.css b/plugins/jetpack/modules/sharedaddy/admin-sharing.css
index fd0e3d43..aea30792 100644
--- a/plugins/jetpack/modules/sharedaddy/admin-sharing.css
+++ b/plugins/jetpack/modules/sharedaddy/admin-sharing.css
@@ -95,54 +95,6 @@
-@media only screen and (-moz-min-device-pixel-ratio: 1.5), only screen and (-o-min-device-pixel-ratio: 3/2), only screen and (-webkit-min-device-pixel-ratio: 1.5), only screen and (min-device-pixel-ratio: 1.5) {
-
- .services ul li#facebook, #available-services .preview-facebook div.option-smart-off{background: #FFF url(images/facebook@2x.png) no-repeat 4px 5px;}
- .services ul li#twitter, #available-services .preview-twitter div.option-smart-off{background: #FFF url(images/twitter@2x.png?1) no-repeat 4px 5px;}
- .services ul li#wordpress, #available-services .preview-wordpress{background: #FFF url(images/wordpress@2x.png?1) no-repeat 4px 5px;}
- .services ul li#digg, #available-services .preview-digg div.option-smart-off{background: #FFF url(images/digg@2x.png) no-repeat 4px 5px;}
- .services ul li#reddit, #available-services .preview-reddit div.option-smart-off{background: #FFF url(images/reddit@2x.png) no-repeat 4px 5px;}
- .services ul li#stumbleupon, #available-services .preview-stumbleupon div.option-smart-off{background: #FFF url(images/stumbleupon@2x.png) no-repeat 4px 5px;}
- .services ul li#email, #available-services .preview-email{background: #FFF url(images/email@2x.png) no-repeat 4px 5px; padding-right: 10px;}
- .services ul li#print, #available-services .preview-print{background: #FFF url(images/print@2x.png) no-repeat 4px 5px; padding-right: 10px;}
- .services ul li#press-this, #available-services .preview-press-this{background: #FFF url(images/wordpress@2x.png) no-repeat 4px 5px; padding-right: 10px;}
- .services ul li#linkedin, #available-services .preview-linkedin div.option-smart-off{background: #FFF url(images/linkedin@2x.png) no-repeat 4px 5px;}
- .services ul li#tumblr,#available-services .preview-tumblr{background: #FFF url(images/tumblr@2x.png) no-repeat 4px 5px; padding-right: 10px;}
- .services ul li#google-plus-1,#available-services .preview-google-plus-1{background: #FFF url(images/googleplus1@2x.png) no-repeat 4px 5px; padding-right: 10px;}
- .services ul li#pinterest,#available-services .preview-pinterest{background: #FFF url(images/pinterest@2x.png) no-repeat 4px 5px; padding-right: 10px;}
- .services ul li.share-custom, #available-services .preview-custom{background: #FFF url(images/custom@2x.png) no-repeat 4px 5px; no-repeat 4px 5px; padding-right: 10px;}
-
- .services ul li#facebook, #available-services .preview-facebook div.option-smart-off,
- .services ul li#twitter, #available-services .preview-twitter div.option-smart-off,
- .services ul li#wordpress, #available-services .preview-wordpress,
- .services ul li#digg, #available-services .preview-digg div.option-smart-off,
- .services ul li#reddit, #available-services .preview-reddit div.option-smart-off,
- .services ul li#stumbleupon, #available-services .preview-stumbleupon div.option-smart-off,
- .services ul li#email, #available-services .preview-email,
- .services ul li#print, #available-services .preview-print,
- .services ul li#press-this, #available-services .preview-press-this,
- .services ul li#linkedin, #available-services .preview-linkedin div.option-smart-off,
- .services ul li#tumblr,#available-services .preview-tumblr,
- .services ul li#google-plus-1,#available-services .preview-google-plus-1,
- .services ul li#pinterest,#available-services .preview-pinterest,
- .services ul li.share-custom, #available-services .preview-custom{
- background-size: 16px 16px;
- }
-
-
-
-
-
-
-
-
-
-
-
-
-}
-
-
/****************************************************************************/
.preview li.preview-item {
@@ -165,6 +117,7 @@
.preview-digg .option-smart-on {
background: #FFF url(images/smart-digg.png) no-repeat top left;
+ background-size: 76px 17px;
width:76px;
height:17px;
margin-top: 2px;
@@ -172,12 +125,14 @@
.preview-reddit .option-smart-on {
background: #FFF url(images/smart-reddit.png) no-repeat top left;
+ background-size: 104px 21px;
width:104px;
height:21px;
}
.preview-stumbleupon .option-smart-on {
background: #FFF url(images/smart-stumbleupon.png) no-repeat top left;
+ background-size: 74px 18px;
width: 74px;
height: 18px;
margin-top: 1px;
@@ -185,44 +140,47 @@
.preview-facebook .option-smart-on {
background: #FFF url(images/smart-like.png) no-repeat top left;
+ background-size: 50px 20px;
width:50px;
height:20px;
}
.preview-twitter .option-smart-on {
background: #FFF url(images/smart-twitter.png?1) no-repeat top left;
+ background-size: 92px 20px;
width:92px;
height:20px;
}
.preview-linkedin .option-smart-on {
background: #FFF url(images/linkedin-smart.png) no-repeat top center;
+ background-size: 99px 18px;
width:99px;
- height:22px;
+ height:18px;
margin-top: 1px;
}
.preview-google-plus-1 .option-smart-on {
background: #FFF url(images/smart-googleplus1.png) no-repeat top left;
- max-width: 70px;
+ background-size: 60px 20px;
width: 60px;
height: 20px;
}
.preview-tumblr .option-smart-on {
background: #FFF url(images/smart-tumblr.png) no-repeat top left;
+ background-size: 62px 20px;
width: 62px;
height: 20px;
}
.preview-pinterest .option-smart-on {
background: #FFF url(images/smart-pinterest.png) no-repeat top left;
+ background-size: 58px 20px;
width: 58px;
height: 20px;
}
-div.sharedaddy .preview-google-plus-1 .no-text { width: 60px; }
-
.services .preview li.share-custom {
border-radius: 6px;
-moz-border-radius: 6px;
@@ -390,3 +348,81 @@ div.sharedaddy .preview-google-plus-1 .no-text { width: 60px; }
#services-config a.remove:hover {
background: #f00;
}
+
+
+
+
+@media print,
+ (-o-min-device-pixel-ratio: 5/4),
+ (-webkit-min-device-pixel-ratio: 1.25),
+ (min-resolution: 120dpi) {
+
+ .services ul li#facebook, #available-services .preview-facebook div.option-smart-off{background: #FFF url(images/facebook@2x.png) no-repeat 4px 5px;}
+ .services ul li#twitter, #available-services .preview-twitter div.option-smart-off{background: #FFF url(images/twitter@2x.png?1) no-repeat 4px 5px;}
+ .services ul li#wordpress, #available-services .preview-wordpress{background: #FFF url(images/wordpress@2x.png?1) no-repeat 4px 5px;}
+ .services ul li#digg, #available-services .preview-digg div.option-smart-off{background: #FFF url(images/digg@2x.png) no-repeat 4px 5px;}
+ .services ul li#reddit, #available-services .preview-reddit div.option-smart-off{background: #FFF url(images/reddit@2x.png) no-repeat 4px 5px;}
+ .services ul li#stumbleupon, #available-services .preview-stumbleupon div.option-smart-off{background: #FFF url(images/stumbleupon@2x.png) no-repeat 4px 5px;}
+ .services ul li#email, #available-services .preview-email{background: #FFF url(images/email@2x.png) no-repeat 4px 5px; padding-right: 10px;}
+ .services ul li#print, #available-services .preview-print{background: #FFF url(images/print@2x.png) no-repeat 4px 5px; padding-right: 10px;}
+ .services ul li#press-this, #available-services .preview-press-this{background: #FFF url(images/wordpress@2x.png) no-repeat 4px 5px; padding-right: 10px;}
+ .services ul li#linkedin, #available-services .preview-linkedin div.option-smart-off{background: #FFF url(images/linkedin@2x.png) no-repeat 4px 5px;}
+ .services ul li#tumblr,#available-services .preview-tumblr{background: #FFF url(images/tumblr@2x.png) no-repeat 4px 5px; padding-right: 10px;}
+ .services ul li#google-plus-1,#available-services .preview-google-plus-1{background: #FFF url(images/googleplus1@2x.png) no-repeat 4px 5px; padding-right: 10px;}
+ .services ul li#pinterest,#available-services .preview-pinterest{background: #FFF url(images/pinterest@2x.png) no-repeat 4px 5px; padding-right: 10px;}
+ .services ul li.share-custom, #available-services .preview-custom{background: #FFF url(images/custom@2x.png) no-repeat 4px 5px; no-repeat 4px 5px; padding-right: 10px;}
+
+ .services ul li#facebook, #available-services .preview-facebook div.option-smart-off,
+ .services ul li#twitter, #available-services .preview-twitter div.option-smart-off,
+ .services ul li#wordpress, #available-services .preview-wordpress,
+ .services ul li#digg, #available-services .preview-digg div.option-smart-off,
+ .services ul li#reddit, #available-services .preview-reddit div.option-smart-off,
+ .services ul li#stumbleupon, #available-services .preview-stumbleupon div.option-smart-off,
+ .services ul li#email, #available-services .preview-email,
+ .services ul li#print, #available-services .preview-print,
+ .services ul li#press-this, #available-services .preview-press-this,
+ .services ul li#linkedin, #available-services .preview-linkedin div.option-smart-off,
+ .services ul li#tumblr,#available-services .preview-tumblr,
+ .services ul li#google-plus-1,#available-services .preview-google-plus-1,
+ .services ul li#pinterest,#available-services .preview-pinterest,
+ .services ul li.share-custom, #available-services .preview-custom{
+ background-size: 16px 16px;
+ }
+
+ .preview-digg .option-smart-on {
+ background-image: url(images/smart-digg@2x.png);
+ }
+
+ .preview-reddit .option-smart-on {
+ background-image: url(images/smart-reddit@2x.png);
+ }
+
+ .preview-stumbleupon .option-smart-on {
+ background-image: url(images/smart-stumbleupon@2x.png);
+ }
+
+ .preview-facebook .option-smart-on {
+ background-image: url(images/smart-like@2x.png);
+ }
+
+ .preview-twitter .option-smart-on {
+ background-image: url(images/smart-twitter@2x.png);
+ }
+
+ .preview-linkedin .option-smart-on {
+ background-image: url(images/linkedin-smart@2x.png);
+ }
+
+ .preview-google-plus-1 .option-smart-on {
+ background-image: url(images/smart-googleplus1@2x.png);
+ }
+
+ .preview-tumblr .option-smart-on {
+ background-image: url(images/smart-tumblr@2x.png);
+ }
+
+ .preview-pinterest .option-smart-on {
+ background-image: url(images/smart-pinterest@2x.png);
+ }
+
+} \ No newline at end of file
diff --git a/plugins/jetpack/modules/sharedaddy/images/icon-facebook-2x.png b/plugins/jetpack/modules/sharedaddy/images/icon-facebook-2x.png
new file mode 100644
index 00000000..a5c71943
--- /dev/null
+++ 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
new file mode 100644
index 00000000..de6a8a14
--- /dev/null
+++ b/plugins/jetpack/modules/sharedaddy/images/icon-facebook.png
Binary files differ
diff --git a/plugins/jetpack/modules/sharedaddy/images/icon-twitter-2x.png b/plugins/jetpack/modules/sharedaddy/images/icon-twitter-2x.png
new file mode 100644
index 00000000..85f2f34c
--- /dev/null
+++ b/plugins/jetpack/modules/sharedaddy/images/icon-twitter-2x.png
Binary files differ
diff --git a/plugins/jetpack/modules/sharedaddy/images/icon-twitter.png b/plugins/jetpack/modules/sharedaddy/images/icon-twitter.png
new file mode 100644
index 00000000..ec41046e
--- /dev/null
+++ b/plugins/jetpack/modules/sharedaddy/images/icon-twitter.png
Binary files differ
diff --git a/plugins/jetpack/modules/sharedaddy/images/icon-wordpress-2x.png b/plugins/jetpack/modules/sharedaddy/images/icon-wordpress-2x.png
new file mode 100644
index 00000000..4d81cabc
--- /dev/null
+++ b/plugins/jetpack/modules/sharedaddy/images/icon-wordpress-2x.png
Binary files differ
diff --git a/plugins/jetpack/modules/sharedaddy/images/icon-wordpress.png b/plugins/jetpack/modules/sharedaddy/images/icon-wordpress.png
new file mode 100644
index 00000000..86f1bd35
--- /dev/null
+++ b/plugins/jetpack/modules/sharedaddy/images/icon-wordpress.png
Binary files differ
diff --git a/plugins/jetpack/modules/sharedaddy/sharedaddy.php b/plugins/jetpack/modules/sharedaddy/sharedaddy.php
index 727005e7..ab8085ca 100644
--- a/plugins/jetpack/modules/sharedaddy/sharedaddy.php
+++ b/plugins/jetpack/modules/sharedaddy/sharedaddy.php
@@ -20,21 +20,27 @@ function sharing_email_send_post( $data ) {
function sharing_add_meta_box() {
$post_types = get_post_types( array( 'public' => true ) );
-
+ $title = apply_filters( 'sharing_meta_box_title', __( 'Sharing', 'jetpack' ) );
foreach( $post_types as $post_type ) {
- add_meta_box( 'sharing_meta', __( 'Sharing', 'jetpack' ), 'sharing_meta_box_content', $post_type, 'advanced', 'high' );
+ add_meta_box( 'sharing_meta', $title, 'sharing_meta_box_content', $post_type, 'advanced', 'high' );
}
}
function sharing_meta_box_content( $post ) {
- $sharing_checked = get_post_meta( $post->ID, 'sharing_disabled', false );
+ do_action( 'start_sharing_meta_box_content', $post );
+
+ $disabled = get_post_meta( $post->ID, 'sharing_disabled', true ); ?>
- if ( empty( $sharing_checked ) || $sharing_checked === false )
- $sharing_checked = ' checked="checked"';
- else
- $sharing_checked = '';
+ <p>
+ <label for="enable_post_sharing">
+ <input type="checkbox" name="enable_post_sharing" id="enable_post_sharing" value="1" <?php checked( !$disabled ); ?>>
+ <?php _e( 'Show sharing buttons.' , 'jetpack'); ?>
+ </label>
+ <input type="hidden" name="sharing_status_hidden" value="1" />
+ </p>
- echo '<p><label for="enable_post_sharing"><input name="enable_post_sharing" id="enable_post_sharing" value="1"' . $sharing_checked . ' type="checkbox"> ' . __( 'Show sharing buttons on this post.', 'jetpack' ) . '</label><input type="hidden" name="sharing_status_hidden" value="1" /></p>';
+ <?php
+ do_action( 'end_sharing_meta_box_content', $post );
}
function sharing_meta_box_save( $post_id ) {
@@ -101,10 +107,6 @@ function sharing_disable_js() {
return false;
}
-if ( !function_exists( 'sharing_register_post_for_share_counts' ) ) {
- function sharing_register_post_for_share_counts() {}
-}
-
function sharing_global_resources() {
$disable = get_option( 'sharedaddy_disable_resources' );
?>
@@ -133,20 +135,17 @@ function sharing_email_check( $true, $post, $data ) {
return $recaptcha_result->is_valid;
}
-// Only run if PHP5
-if ( version_compare( phpversion(), '5.0', '>=' ) ) {
- add_action( 'init', 'sharing_init' );
- add_action( 'admin_init', 'sharing_add_meta_box' );
- add_action( 'save_post', 'sharing_meta_box_save' );
- add_action( 'sharing_email_send_post', 'sharing_email_send_post' );
- add_action( 'sharing_global_options', 'sharing_global_resources' );
- add_action( 'sharing_admin_update', 'sharing_global_resources_save' );
- add_filter( 'sharing_services', 'sharing_restrict_to_single' );
- add_action( 'plugin_action_links_'.basename( dirname( __FILE__ ) ).'/'.basename( __FILE__ ), 'sharing_plugin_settings', 10, 4 );
- add_filter( 'plugin_row_meta', 'sharing_add_plugin_settings', 10, 2 );
-
- if ( defined( 'RECAPTCHA_PRIVATE_KEY' ) ) {
- add_action( 'sharing_email_dialog', 'sharing_email_dialog' );
- add_filter( 'sharing_email_check', 'sharing_email_check', 10, 3 );
- }
-}
+add_action( 'init', 'sharing_init' );
+add_action( 'admin_init', 'sharing_add_meta_box' );
+add_action( 'save_post', 'sharing_meta_box_save' );
+add_action( 'sharing_email_send_post', 'sharing_email_send_post' );
+add_action( 'sharing_global_options', 'sharing_global_resources', 30 );
+add_action( 'sharing_admin_update', 'sharing_global_resources_save' );
+add_filter( 'sharing_services', 'sharing_restrict_to_single' );
+add_action( 'plugin_action_links_'.basename( dirname( __FILE__ ) ).'/'.basename( __FILE__ ), 'sharing_plugin_settings', 10, 4 );
+add_filter( 'plugin_row_meta', 'sharing_add_plugin_settings', 10, 2 );
+
+if ( defined( 'RECAPTCHA_PRIVATE_KEY' ) ) {
+ add_action( 'sharing_email_dialog', 'sharing_email_dialog' );
+ add_filter( 'sharing_email_check', 'sharing_email_check', 10, 3 );
+} \ No newline at end of file
diff --git a/plugins/jetpack/modules/sharedaddy/sharing-service.php b/plugins/jetpack/modules/sharedaddy/sharing-service.php
index 82d7293d..c8efee33 100644
--- a/plugins/jetpack/modules/sharedaddy/sharing-service.php
+++ b/plugins/jetpack/modules/sharedaddy/sharing-service.php
@@ -2,10 +2,15 @@
include_once dirname( __FILE__ ).'/sharing-sources.php';
-define( 'WP_SHARING_PLUGIN_VERSION', '0.3.1' );
+define( 'WP_SHARING_PLUGIN_VERSION', JETPACK__VERSION );
class Sharing_Service {
private $global = false;
+ var $default_sharing_label = '';
+
+ public function __construct() {
+ $this->default_sharing_label = __( 'Share this:', 'jetpack' );
+ }
/**
* Gets a generic list of all services, without any config
@@ -15,11 +20,11 @@ class Sharing_Service {
$all = $this->get_all_services();
$services = array();
-
+
foreach ( $all AS $id => $name ) {
if ( isset( $all[$id] ) ) {
$config = array();
-
+
// Pre-load custom modules otherwise they won't know who they are
if ( substr( $id, 0, 7 ) == 'custom-' && is_array( $options[$id] ) )
$config = $options[$id];
@@ -30,7 +35,7 @@ class Sharing_Service {
return $services;
}
-
+
/**
* Gets a list of all available service names and classes
*/
@@ -50,29 +55,29 @@ class Sharing_Service {
'tumblr' => 'Share_Tumblr',
'pinterest' => 'Share_Pinterest',
);
-
+
// Add any custom services in
$options = $this->get_global_options();
foreach ( (array)$options['custom'] AS $custom_id ) {
$services[$custom_id] = 'Share_Custom';
}
-
+
return apply_filters( 'sharing_services', $services );
}
-
+
public function new_service( $label, $url, $icon ) {
// Validate
$label = trim( wp_html_excerpt( wp_kses( $label, array() ), 30 ) );
$url = trim( esc_url_raw( $url ) );
$icon = trim( esc_url_raw( $icon ) );
-
+
if ( $label && $url && $icon ) {
$options = get_option( 'sharing-options' );
if ( !is_array( $options ) )
$options = array();
-
+
$service_id = 'custom-'.time();
-
+
// Add a new custom service
$options['global']['custom'][] = $service_id;
@@ -85,23 +90,23 @@ class Sharing_Service {
// Return the service
return $service;
}
-
+
return false;
}
-
+
public function delete_service( $service_id ) {
$options = get_option( 'sharing-options' );
if ( isset( $options[$service_id] ) )
unset( $options[$service_id] );
-
+
$key = array_search( $service_id, $options['global']['custom'] );
if ( $key !== false )
unset( $options['global']['custom'][$key] );
-
+
update_option( 'sharing-options', $options );
return true;
}
-
+
public function set_blog_services( array $visible, array $hidden ) {
$services = $this->get_all_services();
// Validate the services
@@ -113,12 +118,12 @@ class Sharing_Service {
// Ensure we don't have the same ones in hidden and visible
$hidden = array_diff( $hidden, $visible );
-
- do_action( 'sharing_get_services_state', array(
+
+ do_action( 'sharing_get_services_state', array(
'services' => $services,
- 'available' => $available,
- 'hidden' => $hidden,
- 'visible' => $visible,
+ 'available' => $available,
+ 'hidden' => $hidden,
+ 'visible' => $visible,
'currently_enabled' => $this->get_blog_services()
) );
@@ -148,10 +153,10 @@ class Sharing_Service {
// Cleanup after any filters that may have produced duplicate services
$enabled['visible'] = array_unique( $enabled['visible'] );
$enabled['hidden'] = array_unique( $enabled['hidden'] );
-
+
// Form the enabled services
$blog = array( 'visible' => array(), 'hidden' => array() );
-
+
foreach ( $blog AS $area => $stuff ) {
foreach ( (array)$enabled[$area] AS $service ) {
if ( isset( $services[$service] ) ) {
@@ -170,7 +175,7 @@ class Sharing_Service {
$blog['all'] = array_flip( array_merge( array_keys( $blog['visible'] ), array_keys( $blog['hidden'] ) ) );
return $blog;
}
-
+
public function get_service( $service_name ) {
$services = $this->get_blog_services();
@@ -179,10 +184,10 @@ class Sharing_Service {
if ( isset( $services['hidden'][$service_name] ) )
return $services['hidden'][$service_name];
-
+
return false;
}
-
+
public function set_global_options( $data ) {
$options = get_option( 'sharing-options' );
@@ -193,20 +198,25 @@ class Sharing_Service {
// Defaults
$options['global'] = array(
'button_style' => 'icon-text',
- 'sharing_label' => __( 'Share this:', 'jetpack' ),
+ 'sharing_label' => $this->default_sharing_label,
'open_links' => 'same',
'show' => array( 'post', 'page' ),
'custom' => isset( $options['global']['custom'] ) ? $options['global']['custom'] : array()
);
-
+
$options['global'] = apply_filters( 'sharing_default_global', $options['global'] );
// Validate options and set from our data
if ( isset( $data['button_style'] ) && in_array( $data['button_style'], array( 'icon-text', 'icon', 'text', 'official' ) ) )
$options['global']['button_style'] = $data['button_style'];
- if ( isset( $data['sharing_label'] ) )
- $options['global']['sharing_label'] = trim( wp_kses( stripslashes( $data['sharing_label'] ), array() ) );
+ if ( isset( $data['sharing_label'] ) ) {
+ if ( $this->default_sharing_label === $data['sharing_label'] ) {
+ $options['global']['sharing_label'] = false;
+ } else {
+ $options['global']['sharing_label'] = trim( wp_kses( stripslashes( $data['sharing_label'] ), array() ) );
+ }
+ }
if ( isset( $data['open_links'] ) && in_array( $data['open_links'], array( 'new', 'same' ) ) )
$options['global']['open_links'] = $data['open_links'];
@@ -238,7 +248,7 @@ class Sharing_Service {
update_option( 'sharing-options', $options );
return $options['global'];
}
-
+
public function get_global_options() {
if ( $this->global === false ) {
$options = get_option( 'sharing-options' );
@@ -247,7 +257,7 @@ class Sharing_Service {
$this->global = $options['global'];
else
$this->global = $this->set_global_options( $options['global'] );
- }
+ }
if ( ! isset( $this->global['show'] ) ) {
$this->global['show'] = array( 'post', 'page' );
@@ -264,24 +274,29 @@ class Sharing_Service {
break;
}
}
+
+ if ( false === $this->global['sharing_label'] ) {
+ $this->global['sharing_label'] = $this->default_sharing_label;
+ }
+
return $this->global;
}
-
+
public function set_service( $id, Sharing_Source $service ) {
// Update the options for this service
$options = get_option( 'sharing-options' );
-
+
// No options yet
if ( !is_array( $options ) )
$options = array();
-
+
do_action( 'sharing_get_button_state', array( 'id' => $id, 'options' => $options, 'service' => $service ) );
-
+
$options[$id] = $service->get_options();
update_option( 'sharing-options', array_filter( $options ) );
}
-
+
// Soon to come to a .org plugin near you!
public function get_total( $service_name = false, $post_id = false, $_blog_id = false ) {
global $wpdb, $blog_id;
@@ -297,40 +312,40 @@ class Sharing_Service {
return (int) $wpdb->get_var( $wpdb->prepare( "SELECT SUM( count ) FROM sharing_stats WHERE blog_id = %d", $_blog_id ) );
}
}
-
+
if ( $post_id > 0 )
return (int) $wpdb->get_var( $wpdb->prepare( "SELECT SUM( count ) FROM sharing_stats WHERE blog_id = %d AND post_id = %d AND share_service = %s", $_blog_id, $post_id, $service_name ) );
else
return (int) $wpdb->get_var( $wpdb->prepare( "SELECT SUM( count ) FROM sharing_stats WHERE blog_id = %d AND share_service = %s", $_blog_id, $service_name ) );
}
-
+
public function get_services_total( $post_id = false ) {
$totals = array();
$services = $this->get_blog_services();
-
+
if ( !empty( $services ) && isset( $services[ 'all' ] ) )
foreach( $services[ 'all' ] as $key => $value ) {
$totals[$key] = new Sharing_Service_Total( $key, $this->get_total( $key, $post_id ) );
}
usort( $totals, array( 'Sharing_Service_Total', 'cmp' ) );
-
+
return $totals;
- }
-
+ }
+
public function get_posts_total() {
$totals = array();
global $wpdb, $blog_id;
-
+
$my_data = $wpdb->get_results( $wpdb->prepare( "SELECT post_id as id, SUM( count ) as total FROM sharing_stats WHERE blog_id = %d GROUP BY post_id ORDER BY count DESC ", $blog_id ) );
-
+
if ( !empty( $my_data ) )
foreach( $my_data as $row )
$totals[] = new Sharing_Post_Total( $row->id, $row->total );
-
+
usort( $totals, array( 'Sharing_Post_Total', 'cmp' ) );
-
+
return $totals;
- }
+ }
}
class Sharing_Service_Total {
@@ -338,16 +353,16 @@ class Sharing_Service_Total {
var $name = '';
var $service = '';
var $total = 0;
-
+
public function Sharing_Service_Total( $id, $total ) {
$services = new Sharing_Service();
$this->id = esc_html( $id );
$this->service = $services->get_service( $id );
$this->total = (int) $total;
-
+
$this->name = $this->service->get_name();
}
-
+
static function cmp( $a, $b ) {
if ( $a->total == $b->total )
return $a->name < $b->name;
@@ -360,14 +375,14 @@ class Sharing_Post_Total {
var $total = 0;
var $title = '';
var $url = '';
-
+
public function Sharing_Post_Total( $id, $total ) {
$this->id = (int) $id;
$this->total = (int) $total;
- $this->title = get_the_title( $this->id );
- $this->url = get_permalink( $this->id );
+ $this->title = get_the_title( $this->id );
+ $this->url = get_permalink( $this->id );
}
-
+
static function cmp( $a, $b ) {
if ( $a->total == $b->total )
return $a->id < $b->id;
@@ -375,16 +390,41 @@ class Sharing_Post_Total {
}
}
+function sharing_register_post_for_share_counts( $post_id ) {
+ global $jetpack_sharing_counts;
+
+ if ( ! isset( $jetpack_sharing_counts ) || ! is_array( $jetpack_sharing_counts ) )
+ $jetpack_sharing_counts = array();
+
+ $jetpack_sharing_counts[ (int) $post_id ] = get_permalink( $post_id );
+}
+
function sharing_add_footer() {
- if ( apply_filters( 'sharing_js', true ) )
+ global $jetpack_sharing_counts;
+
+ if ( apply_filters( 'sharing_js', true ) ) {
+
+ if ( is_array( $jetpack_sharing_counts ) && count( $jetpack_sharing_counts ) ) :
+ $sharing_post_urls = array_filter( $jetpack_sharing_counts );
+ if ( $sharing_post_urls ) :
+?>
+
+ <script type="text/javascript">
+ WPCOM_sharing_counts = <?php echo json_encode( array_flip( $sharing_post_urls ) ); ?>
+ </script>
+<?php
+ endif;
+ endif;
+
wp_print_scripts( 'sharing-js' );
-
+ }
+
$sharer = new Sharing_Service();
$enabled = $sharer->get_blog_services();
foreach ( array_merge( $enabled['visible'], $enabled['hidden'] ) AS $service ) {
$service->display_footer();
}
-}
+}
function sharing_add_header() {
$sharer = new Sharing_Service();
@@ -393,9 +433,9 @@ function sharing_add_header() {
foreach ( array_merge( $enabled['visible'], $enabled['hidden'] ) AS $service ) {
$service->display_header();
}
-
+
if ( count( $enabled['all'] ) > 0 )
- wp_enqueue_style( 'sharedaddy', plugin_dir_url( __FILE__ ) .'sharing.css', array(), WP_SHARING_PLUGIN_VERSION );
+ wp_enqueue_style( 'sharedaddy', plugin_dir_url( __FILE__ ) .'sharing.css', array(), JETPACK__VERSION );
}
add_action( 'wp_head', 'sharing_add_header', 1 );
@@ -409,7 +449,7 @@ function sharing_process_requests() {
$service = $sharer->get_service( $_GET['share'] );
if ( $service ) {
$service->process_request( $post, $_POST );
- }
+ }
}
}
add_action( 'template_redirect', 'sharing_process_requests' );
@@ -421,10 +461,35 @@ function sharing_display( $text = '' ) {
return $text;
}
+ // Don't output flair on excerpts
if ( in_array( 'get_the_excerpt', (array) $wp_current_filter ) ) {
return $text;
}
+ // Don't allow flair 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;
+ }
+ }
+
+ // check whether we are viewing the front page and whether the front page option is checked
+ $options = get_option( 'sharing-options' );
+ $display_options = $options['global']['show'];
+
+ if ( is_front_page() && ( is_array( $display_options ) && ! in_array( 'index', $display_options ) ) )
+ return $text;
+
+ if ( is_attachment() && in_array( 'the_excerpt', (array) $wp_current_filter ) ) {
+ // Many themes run the_excerpt() conditionally on an attachment page, then run the_content().
+ // We only want to output the sharing buttons once. Let's stick with the_content().
+ return $text;
+ }
+
$sharer = new Sharing_Service();
$global = $sharer->get_global_options();
@@ -439,25 +504,25 @@ function sharing_display( $text = '' ) {
// Pass through a filter for final say so
$show = apply_filters( 'sharing_show', $show, $post );
-
+
// Disabled for this post?
$switched_status = get_post_meta( $post->ID, 'sharing_disabled', false );
if ( !empty( $switched_status ) )
$show = false;
-
+
// Allow to be used on P2 ajax requests for latest posts.
if ( defined( 'DOING_AJAX' ) && DOING_AJAX && isset( $_REQUEST['action'] ) && 'get_latest_posts' == $_REQUEST['action'] )
$show = true;
-
+
$sharing_content = '';
-
+
if ( $show ) {
$enabled = $sharer->get_blog_services();
if ( count( $enabled['all'] ) > 0 ) {
global $post;
-
+
$dir = get_option( 'text_direction' );
// Wrapper
@@ -465,7 +530,7 @@ function sharing_display( $text = '' ) {
if ( $global['sharing_label'] != '' )
$sharing_content .= '<h3 class="sd-title">' . $global['sharing_label'] . '</h3>';
$sharing_content .= '<div class="sd-content"><ul>';
-
+
// Visible items
$visible = '';
foreach ( $enabled['visible'] as $id => $service ) {
@@ -486,47 +551,47 @@ function sharing_display( $text = '' ) {
if ( $dir == 'rtl' )
$parts = array_reverse( $parts );
- $sharing_content .= implode( '', $parts );
+ $sharing_content .= implode( '', $parts );
$sharing_content .= '<li class="share-end"></li></ul>';
-
+
if ( count( $enabled['hidden'] ) > 0 ) {
$sharing_content .= '<div class="sharing-hidden"><div class="inner" style="display: none;';
if ( count( $enabled['hidden'] ) == 1 )
$sharing_content .= 'width:150px;';
-
+
$sharing_content .= '">';
-
+
if ( count( $enabled['hidden'] ) == 1 )
$sharing_content .= '<ul style="background-image:none;">';
else
$sharing_content .= '<ul>';
-
+
$count = 1;
foreach ( $enabled['hidden'] as $id => $service ) {
// Individual HTML for sharing service
$sharing_content .= '<li class="share-'.$service->get_class().'">';
$sharing_content .= $service->get_display( $post );
$sharing_content .= '</li>';
-
+
if ( ( $count % 2 ) == 0 )
$sharing_content .= '<li class="share-end"></li>';
$count ++;
}
-
+
// End of wrapper
$sharing_content .= '<li class="share-end"></li></ul></div></div>';
}
$sharing_content .= '<div class="sharing-clear"></div></div></div></div>';
-
+
// Register our JS
- wp_register_script( 'sharing-js', plugin_dir_url( __FILE__ ).'sharing.js', array( 'jquery' ), '20120131' );
+ wp_register_script( 'sharing-js', plugin_dir_url( __FILE__ ).'sharing.js', array( 'jquery' ), '20121205' );
add_action( 'wp_footer', 'sharing_add_footer' );
}
}
-
+
return $text.$sharing_content;
}
diff --git a/plugins/jetpack/modules/sharedaddy/sharing-sources.php b/plugins/jetpack/modules/sharedaddy/sharing-sources.php
index bd8b2eb9..256513cb 100644
--- a/plugins/jetpack/modules/sharedaddy/sharing-sources.php
+++ b/plugins/jetpack/modules/sharedaddy/sharing-sources.php
@@ -5,7 +5,7 @@ abstract class Sharing_Source {
public $smart;
protected $open_links;
protected $id;
-
+
public function __construct( $id, array $settings ) {
$this->id = $id;
@@ -14,41 +14,49 @@ abstract class Sharing_Source {
if ( isset( $settings['open_links'] ) )
$this->open_links = $settings['open_links'];
-
+
if ( isset( $settings['smart'] ) )
$this->smart = $settings['smart'];
}
-
+
+ public function http() {
+ return is_ssl() ? 'https' : 'http';
+ }
+
public function get_id() {
return $this->id;
}
-
+
public function get_class() {
return $this->id;
}
-
+
+ public function get_share_url( $post_id ) {
+ return apply_filters( 'sharing_permalink', get_permalink( $post_id ), $post_id, $this->id );
+ }
+
public function has_custom_button_style() {
return false;
}
-
+
public function get_link( $url, $text, $title, $query = '', $id = false ) {
$klasses = array( 'share-'.$this->get_class(), 'sd-button' );
-
+
if ( $this->button_style == 'icon' || $this->button_style == 'icon-text' )
$klasses[] = 'share-icon';
-
+
if ( $this->button_style == 'icon' ) {
$text = '';
$klasses[] = 'no-text';
}
-
+
if ( !empty( $query ) ) {
if ( stripos( $url, '?' ) === false )
$url .= '?'.$query;
else
$url .= '&amp;'.$query;
}
-
+
if ( $this->button_style == 'text' )
$klasses[] = 'no-icon';
@@ -67,29 +75,29 @@ abstract class Sharing_Source {
abstract public function get_display( $post );
public function display_header() {
- }
-
+ }
+
public function display_footer() {
}
-
+
public function has_advanced_options() {
return false;
}
-
+
public function display_preview() {
$text = '&nbsp;';
if ( !$this->smart )
if ( $this->button_style != 'icon' )
$text = $this->get_name();
-
+
$klasses = array( 'share-'.$this->get_class(), 'sd-button' );
-
+
if ( $this->button_style == 'icon' || $this->button_style == 'icon-text' )
$klasses[] = 'share-icon';
-
+
if ( $this->button_style == 'icon' )
$klasses[] = 'no-text';
-
+
if ( $this->button_style == 'text' )
$klasses[] = 'no-icon';
@@ -107,39 +115,39 @@ abstract class Sharing_Source {
public function get_total( $post = false ) {
global $wpdb, $blog_id;
-
- $name = strtolower( $this->get_id() );
-
+
+ $name = strtolower( $this->get_id() );
+
if ( $post == false ) {
// get total number of shares for service
return (int) $wpdb->get_var( $wpdb->prepare( "SELECT SUM( count ) FROM sharing_stats WHERE blog_id = %d AND share_service = %s", $blog_id, $name ) );
}
-
+
// get total shares for a post
return (int) $wpdb->get_var( $wpdb->prepare( "SELECT count FROM sharing_stats WHERE blog_id = %d AND post_id = %d AND share_service = %s", $blog_id, $post->ID, $name ) );
- }
-
+ }
+
public function get_posts_total() {
global $wpdb, $blog_id;
-
+
$totals = array();
- $name = strtolower( $this->get_id() );
-
+ $name = strtolower( $this->get_id() );
+
$my_data = $wpdb->get_results( $wpdb->prepare( "SELECT post_id as id, SUM( count ) as total FROM sharing_stats WHERE blog_id = %d AND share_service = %s GROUP BY post_id ORDER BY count DESC ", $blog_id, $name ) );
-
+
if ( !empty( $my_data ) )
foreach( $my_data as $row )
$totals[] = new Sharing_Post_Total( $row->id, $row->total );
-
+
usort( $totals, array( 'Sharing_Post_Total', 'cmp' ) );
-
+
return $totals;
}
-
+
public function process_request( $post, array $post_data ) {
do_action( 'sharing_bump_stats', array( 'service' => $this, 'post' => $post ) );
}
-
+
public function js_dialog( $name, $params = array() ) {
$defaults = array(
'menubar' => 1,
@@ -154,9 +162,9 @@ abstract class Sharing_Source {
}
$opts = implode( ',', $opts );
?>
- <script type="text/javascript" charset="utf-8">
- jQuery(document).ready(function(){
- jQuery( '.share-<?php echo $name; ?>' ).click(function(){
+ <script type="text/javascript">
+ jQuery(document).on( 'ready post-load', function(){
+ jQuery( 'a.share-<?php echo $name; ?>' ).on( 'click', function() {
window.open( jQuery(this).attr( 'href' ), 'wpcom<?php echo $name; ?>', '<?php echo $opts; ?>' );
return false;
});
@@ -170,7 +178,7 @@ abstract class Sharing_Advanced_Source extends Sharing_Source {
public function has_advanced_options() {
return true;
}
-
+
abstract public function display_options();
abstract public function update_options( array $data );
abstract public function get_options();
@@ -187,12 +195,12 @@ class Share_Email extends Sharing_Source {
else
$this->smart = false;
}
-
+
public function get_name() {
return __( 'Email', 'jetpack' );
}
- // Default does nothing
+ // Default does nothing
public function process_request( $post, array $post_data ) {
$ajax = false;
if ( isset( $_SERVER['HTTP_X_REQUESTED_WITH'] ) && strtolower( $_SERVER['HTTP_X_REQUESTED_WITH'] ) == 'xmlhttprequest' )
@@ -205,10 +213,10 @@ class Share_Email extends Sharing_Source {
if ( isset( $post_data['target_email'] ) && is_email( $post_data['target_email'] ) )
$target_email = $post_data['target_email'];
-
+
if ( isset( $post_data['source_name'] ) )
$source_name = $post_data['source_name'];
-
+
// Test email
$error = 1; // Failure in data
if ( $source_email && $target_email && $source_name ) {
@@ -219,21 +227,21 @@ class Share_Email extends Sharing_Source {
'target' => $target_email,
'name' => $source_name
);
-
+
if ( ( $data = apply_filters( 'sharing_email_can_send', $data ) ) !== false ) {
// Record stats
parent::process_request( $data['post'], $post_data );
do_action( 'sharing_email_send_post', $data );
}
-
+
// Return a positive regardless of whether the user is subscribed or not
if ( $ajax ) {
?>
<div class="response">
- <div class="response-title"><?php _e( 'This post has been shared!', 'jetpack' ); ?></div>
- <div class="response-sub"><?php printf( __( 'You have shared this post with %s', 'jetpack' ), esc_html( $target_email ) ); ?></div>
- <div class="response-close"><a href="#" class="sharing_cancel"><?php _e( 'Close', 'jetpack' ); ?></a></div>
+ <div class="response-title"><?php _e( 'This post has been shared!', 'jetpack' ); ?></div>
+ <div class="response-sub"><?php printf( __( 'You have shared this post with %s', 'jetpack' ), esc_html( $target_email ) ); ?></div>
+ <div class="response-close"><a href="#" class="sharing_cancel"><?php _e( 'Close', 'jetpack' ); ?></a></div>
</div>
<?php
}
@@ -245,7 +253,7 @@ class Share_Email extends Sharing_Source {
else
$error = 2; // Email check failed
}
-
+
if ( $ajax )
echo $error;
else
@@ -253,24 +261,24 @@ class Share_Email extends Sharing_Source {
die();
}
-
+
public function get_display( $post ) {
return $this->get_link( get_permalink( $post->ID ), _x( 'Email', 'share to', 'jetpack' ), __( 'Click to email this to a friend', 'jetpack' ), 'share=email' );
}
-
+
/**
* Outputs the hidden email dialog
*/
public function display_footer() {
global $current_user;
-
+
$visible = $status = false;
?>
<div id="sharing_email" style="display: none;">
- <form action="" method="post">
+ <form action="<?php echo esc_url( $_SERVER['REQUEST_URI'] ); ?>" method="post">
<label for="target_email"><?php _e( 'Send to Email Address', 'jetpack' ) ?></label>
<input type="text" name="target_email" id="target_email" value="" />
-
+
<?php if ( is_user_logged_in() ) : ?>
<input type="hidden" name="source_name" value="<?php echo esc_attr( $current_user->display_name ); ?>" />
<input type="hidden" name="source_email" value="<?php echo esc_attr( $current_user->user_email ); ?>" />
@@ -278,18 +286,18 @@ class Share_Email extends Sharing_Source {
<label for="source_name"><?php _e( 'Your Name', 'jetpack' ) ?></label>
<input type="text" name="source_name" id="source_name" value="" />
-
+
<label for="source_email"><?php _e( 'Your Email Address', 'jetpack' ) ?></label>
<input type="text" name="source_email" id="source_email" value="" />
<?php endif; ?>
-
+
<?php do_action( 'sharing_email_dialog', 'jetpack' ); ?>
- <img style="float: right; display: none" class="loading" src="<?php echo plugin_dir_url( __FILE__ ) . 'images/loading.gif'; ?>" alt="loading" width="16" height="16" />
+ <img style="float: right; display: none" class="loading" src="<?php echo apply_filters( 'jetpack_static_url', plugin_dir_url( __FILE__ ) . 'images/loading.gif' ); ?>" alt="loading" width="16" height="16" />
<input type="submit" value="<?php _e( 'Send Email', 'jetpack' ); ?>" class="sharing_send" />
<a href="#cancel" class="sharing_cancel"><?php _e( 'Cancel', 'jetpack' ); ?></a>
-
+
<div class="errors errors-1" style="display: none;">
<?php _e( 'Post was not sent - check your email addresses!', 'jetpack' ); ?>
</div>
@@ -297,7 +305,7 @@ class Share_Email extends Sharing_Source {
<div class="errors errors-2" style="display: none;">
<?php _e( 'Email check failed, please try again', 'jetpack' ); ?>
</div>
-
+
<div class="errors errors-3" style="display: none;">
<?php _e( 'Sorry, your blog cannot share posts by email.', 'jetpack' ); ?>
</div>
@@ -317,41 +325,61 @@ class Share_Twitter extends Sharing_Source {
else
$this->smart = false;
}
-
+
public function get_name() {
return __( 'Twitter', 'jetpack' );
}
function sharing_twitter_via( $post ) {
- return '';
+ // Allow themes to customize the via
+ return apply_filters( 'jetpack_sharing_twitter_via', '', $post->ID );
+ }
- // Default 'via' is always us.
- $via = preg_replace( '/(https?:\/\/)|(\.)|(\/)/i', '', home_url() );
+ public function get_related_accounts( $post ) {
+ // Format is 'username' => 'Optional description'
+ $related_accounts = apply_filters( 'jetpack_sharing_twitter_related', array(), $post->ID );
- // Allow themes to customize the via
- return apply_filters( 'sharing_twitter_via', $via, $post->ID );
+ // Example related string: account1,account2:Account 2 description,account3
+ $related = array();
+
+ foreach ( $related_accounts as $related_account_username => $related_account_description ) {
+ // Join the description onto the end of the username
+ if ( $related_account_description )
+ $related_account_username .= ':' . $related_account_description;
+
+ $related[] = $related_account_username;
+ }
+
+ return implode( ',', $related );
}
public function get_display( $post ) {
$via = $this->sharing_twitter_via( $post );
if ( $via ) {
- $via = sprintf( '&via=%1$s', rawurlencode( $via ) );
+ $via = '&via=' . rawurlencode( $via );
+
+ $related = $this->get_related_accounts( $post );
+ if ( ! empty( $related ) && $related !== $via )
+ $via .= '&related=' . rawurlencode( $related );
} else {
$via = '';
}
+
+ $share_url = $this->get_share_url( $post->ID );
+
if ( $this->smart ) {
- return '<div class="twitter_button"><iframe allowtransparency="true" frameborder="0" scrolling="no" src="' . esc_url( 'http://platform.twitter.com/widgets/tweet_button.html?url=' . rawurlencode( apply_filters( 'sharing_permalink', get_permalink( $post->ID ), $post->ID, $this->id ) ) . '&counturl=' . rawurlencode( str_replace( 'https://', 'http://', get_permalink( $post->ID ) ) ) . '&count=horizontal&text=' . rawurlencode( $post->post_title . ':' ) . $via ) . '" style="width:101px; height:20px;"></iframe></div>';
+ return '<div class="twitter_button"><iframe allowtransparency="true" frameborder="0" scrolling="no" src="' . esc_url( $this->http() . '://platform.twitter.com/widgets/tweet_button.html?url=' . rawurlencode( $share_url ) . '&counturl=' . rawurlencode( str_replace( 'https://', 'http://', get_permalink( $post->ID ) ) ) . '&count=horizontal&text=' . rawurlencode( $post->post_title . ':' ) . $via ) . '" style="width:101px; height:20px;"></iframe></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( 'Twitter', 'share to', 'jetpack' ), __( 'Click to share on Twitter', 'jetpack' ), 'share=twitter', 'sharing-twitter-' . $post->ID );
}
}
-
+
public function process_request( $post, array $post_data ) {
$post_title = $post->post_title;
- $post_link = apply_filters( 'sharing_permalink', get_permalink( $post->ID ), $post->ID, $this->id );
+ $post_link = $this->get_share_url( $post->ID );
if ( function_exists( 'mb_stripos' ) ) {
$strlen = 'mb_strlen';
@@ -360,10 +388,13 @@ class Share_Twitter extends Sharing_Source {
$strlen = 'strlen';
$substr = 'substr';
}
-
+
$via = $this->sharing_twitter_via( $post );
if ( $via ) {
- $related = false;
+ $related = $this->get_related_accounts( $post );
+ if ( $related === $via )
+ $related = false;
+
$sig = " via @$via";
} else {
$via = false;
@@ -384,22 +415,22 @@ class Share_Twitter extends Sharing_Source {
// Record stats
parent::process_request( $post, $post_data );
-
+
$url = $post_link;
$twitter_url = add_query_arg(
- urlencode_deep( compact( 'via', 'related', 'text', 'url' ) ),
- sprintf( '%s://twitter.com/intent/tweet', ( is_ssl() ? 'https' : 'http' ) )
+ urlencode_deep( array_filter( compact( 'via', 'related', 'text', 'url' ) ) ),
+ sprintf( '%s://twitter.com/intent/tweet', $this->http() )
);
// Redirect to Twitter
wp_redirect( $twitter_url );
die();
}
-
+
public function has_custom_button_style() {
return $this->smart;
}
-
+
public function display_footer() {
$this->js_dialog( $this->shortname, array( 'height' => 350 ) );
}
@@ -415,7 +446,7 @@ class Share_Stumbleupon extends Sharing_Source {
else
$this->smart = false;
}
-
+
public function get_name() {
return __( 'StumbleUpon', 'jetpack' );
}
@@ -426,17 +457,17 @@ class Share_Stumbleupon extends Sharing_Source {
public function get_display( $post ) {
if ( $this->smart )
- return '<div class="stumbleupon_button"><iframe src="http://www.stumbleupon.com/badge/embed/1/?url=' . urlencode( get_permalink( $post->ID ) ) . '&amp;title=' . urlencode( $post->post_title ) . '" scrolling="no" frameborder="0" style="border:none; overflow:hidden; width:74px; height: 18px;" allowTransparency="true"></iframe></div>';
+ return '<div class="stumbleupon_button"><iframe src="http://www.stumbleupon.com/badge/embed/1/?url=' . rawurlencode( $this->get_share_url( $post->ID ) ) . '&amp;title=' . rawurlencode( $post->post_title ) . '" scrolling="no" frameborder="0" style="border:none; overflow:hidden; width:74px; height: 18px;" allowTransparency="true"></iframe></div>';
else
return $this->get_link( get_permalink( $post->ID ), _x( 'StumbleUpon', 'share to', 'jetpack' ), __( 'Click to share on StumbleUpon', 'jetpack' ), 'share=stumbleupon' );
}
-
+
public function process_request( $post, array $post_data ) {
- $stumbleupon_url = 'http://www.stumbleupon.com/submit?url=' . urlencode( get_permalink( $post->ID ) ) . '&title=' . urlencode( $post->post_title );
-
+ $stumbleupon_url = $this->http() . '://www.stumbleupon.com/submit?url=' . rawurlencode( $this->get_share_url( $post->ID ) ) . '&title=' . rawurlencode( $post->post_title );
+
// Record stats
parent::process_request( $post, $post_data );
-
+
// Redirect to Stumbleupon
wp_redirect( $stumbleupon_url );
die();
@@ -460,17 +491,17 @@ class Share_Reddit extends Sharing_Source {
public function get_display( $post ) {
if ( $this->smart )
- return '<div class="reddit_button"><iframe src="http://www.reddit.com/static/button/button1.html?width=120&amp;url=' . urlencode( apply_filters( 'sharing_permalink', get_permalink( $post->ID ), $post->ID, $this->id ) ) . '&amp;title=' . rawurlencode( $post->post_title ) . '" height="22" width="120" scrolling="no" frameborder="0"></iframe></div>';
+ return '<div class="reddit_button"><iframe src="http://www.reddit.com/static/button/button1.html?width=120&amp;url=' . rawurlencode( $this->get_share_url( $post->ID ) ) . '&amp;title=' . rawurlencode( $post->post_title ) . '" height="22" width="120" scrolling="no" frameborder="0"></iframe></div>';
else
return $this->get_link( get_permalink( $post->ID ), __( 'Reddit', 'share to', 'jetpack' ), __( 'Click to share on Reddit', 'jetpack' ), 'share=reddit' );
- }
-
+ }
+
public function process_request( $post, array $post_data ) {
- $reddit_url = 'http://reddit.com/submit?url=' . urlencode( apply_filters( 'sharing_permalink', get_permalink( $post->ID ), $post->ID, $this->id ) ) . '&title=' . urlencode( $post->post_title );
-
+ $reddit_url = 'http://reddit.com/submit?url=' . rawurlencode( $this->get_share_url( $post->ID ) ) . '&title=' . rawurlencode( $post->post_title );
+
// Record stats
parent::process_request( $post, $post_data );
-
+
// Redirect to Reddit
wp_redirect( $reddit_url );
die();
@@ -487,7 +518,7 @@ class Share_Digg extends Sharing_Source {
else
$this->smart = false;
}
-
+
public function get_name() {
return __( 'Digg', 'jetpack' );
}
@@ -498,24 +529,24 @@ class Share_Digg extends Sharing_Source {
public function get_display( $post ) {
if ( $this->smart ) {
- $url = $this->get_link( 'http://digg.com/submit?url='. urlencode( apply_filters( 'sharing_permalink', get_permalink( $post->ID ), $post->ID, $this->id ) ) . '&amp;title=' . urlencode( $post->post_title ), 'Digg', __( 'Click to Digg this post' ) );
+ $url = $this->get_link( 'http://digg.com/submit?url='. rawurlencode( $this->get_share_url( $post->ID ) ) . '&amp;title=' . rawurlencode( $post->post_title ), 'Digg', __( 'Click to Digg this post', 'jetpack' ) );
return '<div class="digg_button">' . str_replace( 'class="', 'class="DiggThisButton DiggCompact ', $url ) . '</div>';
} else {
- return $this->get_link( get_permalink( $post->ID ), _x( 'Digg', 'share to', 'jetpack' ), __( 'Click to Digg this post' ), 'share=digg' );
+ return $this->get_link( get_permalink( $post->ID ), _x( 'Digg', 'share to', 'jetpack' ), __( 'Click to Digg this post', 'jetpack' ), 'share=digg' );
}
- }
-
+ }
+
public function process_request( $post, array $post_data ) {
- $digg_url = 'http://digg.com/submit?url=' . urlencode( apply_filters( 'sharing_permalink', get_permalink( $post->ID ), $post->ID, $this->id ) ) . '&title=' . urlencode( $post->post_title );
-
+ $digg_url = 'http://digg.com/submit?url=' . rawurlencode( $this->get_share_url( $post->ID ) ) . '&title=' . rawurlencode( $post->post_title );
+
// Record stats
parent::process_request( $post, $post_data );
-
+
// Redirect to Digg
wp_redirect( $digg_url );
die();
}
-
+
public function display_header() {
if ( $this->smart ) {
?>
@@ -553,25 +584,25 @@ class Share_LinkedIn extends Sharing_Source {
}
public function get_display( $post ) {
- $permalink = get_permalink( $post->ID );
+ $share_url = $this->get_share_url( $post->ID );
$display = '';
-
+
if ( $this->smart )
- $display .= sprintf( '<div class="linkedin_button"><script type="in/share" data-url="%s" data-counter="right"></script></div>', esc_url( $permalink ) );
+ $display .= sprintf( '<div class="linkedin_button"><script type="in/share" data-url="%s" data-counter="right"></script></div>', esc_url( $share_url ) );
else
- $display = $this->get_link( $permalink, _x( 'LinkedIn', 'share to', 'jetpack' ), __( 'Click to share on LinkedIn', 'jetpack' ), 'share=linkedin', 'sharing-linkedin-' . $post->ID );
+ $display = $this->get_link( get_permalink( $post->ID ), _x( 'LinkedIn', 'share to', 'jetpack' ), __( 'Click to share on LinkedIn', 'jetpack' ), 'share=linkedin', 'sharing-linkedin-' . $post->ID );
if ( 'icon-text' == $this->button_style || 'text' == $this->button_style )
sharing_register_post_for_share_counts( $post->ID );
return $display;
}
-
+
public function process_request( $post, array $post_data ) {
setup_postdata( $post );
- $post_link = apply_filters( 'sharing_permalink', get_permalink( $post->ID ), $post->ID, $this->id );
+ $post_link = $this->get_share_url( $post->ID );
// http://www.linkedin.com/shareArticle?mini=true&url={articleUrl}&title={articleTitle}&summary={articleSummary}&source={articleSource}
@@ -582,18 +613,16 @@ class Share_LinkedIn extends Sharing_Source {
$encoded_summary = rawurlencode( strip_tags( get_the_excerpt() ) );
if( strlen( $encoded_summary ) > 256 )
$encoded_summary = substr( $encoded_summary, 0, 253 ) . '...';
-
+
$source = get_bloginfo( 'name' );
- $query = add_query_arg( array(
+ $linkedin_url = add_query_arg( array(
'title' => $encoded_title,
'url' => rawurlencode( $post_link ),
'source' => rawurlencode( $source ),
'summary' => $encoded_summary,
- ) );
+ ), 'http://www.linkedin.com/shareArticle?mini=true' );
- $linkedin_url = 'http://www.linkedin.com/shareArticle?mini=true' . $query;
-
// Record stats
parent::process_request( $post, $post_data );
@@ -601,25 +630,36 @@ class Share_LinkedIn extends Sharing_Source {
wp_redirect( $linkedin_url );
die();
}
-
+
public function display_footer() {
- if ( !$this->smart )
+ if ( !$this->smart ) {
$this->js_dialog( $this->shortname, array( 'width' => 580, 'height' => 450 ) );
- else
- echo '<script type="text/javascript" src="//platform.linkedin.com/in.js"></script>';
+ } else {
+ ?><script type="text/javascript">
+ jQuery( document ).ready( function() {
+ jQuery.getScript( 'http://platform.linkedin.com/in.js?async=true', function success() {
+ IN.init();
+ });
+ });
+ jQuery( document.body ).on( 'post-load', function() {
+ if ( typeof IN != 'undefined' )
+ IN.parse();
+ });
+ </script><?php
+ }
}
}
class Share_Facebook extends Sharing_Source {
var $shortname = 'facebook';
private $share_type = 'default';
-
+
public function __construct( $id, array $settings ) {
parent::__construct( $id, $settings );
if ( isset( $settings['share_type'] ) )
$this->share_type = $settings['share_type'];
-
+
if ( 'official' == $this->button_style )
$this->smart = true;
else
@@ -629,21 +669,30 @@ class Share_Facebook extends Sharing_Source {
public function get_name() {
return __( 'Facebook', 'jetpack' );
}
-
+
public function display_header() {
}
-
+
function guess_locale_from_lang( $lang ) {
if ( 'en' == $lang || 'en_US' == $lang || !$lang ) {
return 'en_US';
}
if ( !class_exists( 'GP_Locales' ) ) {
- require JETPACK__PLUGIN_DIR . 'locales.php';
+ if ( !defined( 'JETPACK__GLOTPRESS_LOCALES_PATH' ) || !file_exists( JETPACK__GLOTPRESS_LOCALES_PATH ) ) {
+ return false;
+ }
+
+ require JETPACK__GLOTPRESS_LOCALES_PATH;
}
- // Jetpack: get_locale() returns 'it_IT';
- $locale = GP_Locales::by_field( 'wp_locale', $lang );
+ if ( defined( 'IS_WPCOM' ) && IS_WPCOM ) {
+ // WP.com: get_locale() returns 'it'
+ $locale = GP_Locales::by_slug( $lang );
+ } else {
+ // Jetpack: get_locale() returns 'it_IT';
+ $locale = GP_Locales::by_field( 'wp_locale', $lang );
+ }
if ( !$locale || empty( $locale->facebook_locale ) ) {
return false;
@@ -653,22 +702,25 @@ class Share_Facebook extends Sharing_Source {
}
public function get_display( $post ) {
+ $share_url = $this->get_share_url( $post->ID );
if ( $this->smart ) {
- $url = 'http://www.facebook.com/plugins/like.php?href=' . rawurlencode( get_permalink( $post->ID ) ) . '&amp;layout=button_count&amp;show_faces=false&amp;action=like&amp;colorscheme=light&amp;height=21';
-
+ $url = $this->http() . '://www.facebook.com/plugins/like.php?href=' . rawurlencode( $share_url ) . '&amp;layout=button_count&amp;show_faces=false&amp;action=like&amp;colorscheme=light&amp;height=21';
+
// Default widths to suit English
$inner_w = 90;
-
+
// Locale-specific widths/overrides
$widths = array(
'bg_BG' => 120,
- 'de_DE' => 100,
+ 'cs_CZ' => 135,
+ 'de_DE' => 120,
'da_DK' => 120,
- 'es_ES' => 100,
- 'es_LA' => 100,
+ 'es_ES' => 122,
+ 'es_LA' => 110,
'fi_FI' => 100,
'it_IT' => 100,
'ja_JP' => 100,
+ 'nl_NL' => 130,
'ru_RU' => 128,
);
@@ -676,9 +728,7 @@ class Share_Facebook extends Sharing_Source {
$locale = $this->guess_locale_from_lang( get_locale() );
if ( $locale ) {
- if ( 'en_US' != $locale ) {
- $url .= '&amp;locale=' . $locale;
- }
+ $url .= '&amp;locale=' . $locale;
if ( isset( $widths[$locale] ) ) {
$inner_w = $widths[$locale];
@@ -688,23 +738,23 @@ class Share_Facebook extends Sharing_Source {
$url .= '&amp;width='.$inner_w;
return '<div class="like_button"><iframe src="'.$url.'" scrolling="no" frameborder="0" style="border:none; overflow:hidden; width:'.( $inner_w + 6 ).'px; height:21px;" allowTransparency="true"></iframe></div>';
}
-
+
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( 'Facebook', 'share to', 'jetpack' ), __( 'Share on Facebook', 'jetpack' ), 'share=facebook', 'sharing-facebook-' . $post->ID );
}
-
+
public function process_request( $post, array $post_data ) {
- $fb_url = 'http://www.facebook.com/sharer.php?u=' . urlencode( apply_filters( 'sharing_permalink', get_permalink( $post->ID ), $post->ID, $this->id ) ) . '&t=' . urlencode( $post->post_title );
-
+ $fb_url = $this->http() . '://www.facebook.com/sharer.php?u=' . rawurlencode( $this->get_share_url( $post->ID ) ) . '&t=' . rawurlencode( $post->post_title );
+
// Record stats
parent::process_request( $post, $post_data );
-
+
// Redirect to Facebook
wp_redirect( $fb_url );
die();
}
-
+
public function display_footer() {
$this->js_dialog( $this->shortname );
}
@@ -720,13 +770,13 @@ class Share_Print extends Sharing_Source {
else
$this->smart = false;
}
-
+
public function get_name() {
return __( 'Print', 'jetpack' );
}
public function get_display( $post ) {
- return $this->get_link( get_permalink( $post->ID ). ( ( is_single() || is_page() ) ? '#print': '' ), _x( 'Print', 'share to', 'jetpack' ), __( 'Click to print', 'jetpack' ) );
+ return $this->get_link( get_permalink( $post->ID ) . ( ( is_single() || is_page() ) ? '#print': '' ), _x( 'Print', 'share to', 'jetpack' ), __( 'Click to print', 'jetpack' ) );
}
}
@@ -740,30 +790,30 @@ class Share_PressThis extends Sharing_Source {
else
$this->smart = false;
}
-
+
public function get_name() {
return __( 'Press This', 'jetpack' );
}
public function process_request( $post, array $post_data ) {
global $current_user;
-
+
$blogs = get_blogs_of_user( $current_user->ID );
if ( empty( $blogs ) ) {
wp_safe_redirect( get_permalink( $post->ID ) );
die();
}
-
+
$blog = current( $blogs );
- $url = $blog->siteurl.'/wp-admin/press-this.php?u='.urlencode( apply_filters( 'sharing_permalink', get_permalink( $post->ID ), $post->ID, $this->id ) ).'&t='.urlencode( $post->post_title ).'&v=4';
+ $url = $blog->siteurl.'/wp-admin/press-this.php?u='.rawurlencode( $this->get_share_url( $post->ID ) ).'&t='.rawurlencode( $post->post_title ).'&v=4';
if ( isset( $_GET['sel'] ) )
- $url .= '&s='.urlencode( $_GET['sel'] );
+ $url .= '&s='.rawurlencode( $_GET['sel'] );
// Record stats
parent::process_request( $post, $post_data );
-
+
// Redirect to Press This
wp_safe_redirect( $url );
die();
@@ -786,65 +836,77 @@ class Share_GooglePlus1 extends Sharing_Source {
else
$this->smart = false;
}
-
+
public function get_name() {
return __( 'Google +1', 'jetpack' );
}
- public function get_display( $post ) {
- // Smart or not, return the G+ button
- return '<div class="googleplus1_button"><g:plusone size="medium" callback="sharing_plusone" href="' . esc_attr( get_permalink( $post->ID ) ) . '"></g:plusone></div>';
+ public function has_custom_button_style() {
+ return $this->smart;
}
-
- public function display_preview() {
- ?>
- <div class="option option-smart-on">
- <a href="javascript:void(0);return false;" class="share-<?php echo $this->shortname; ?>">
- <span></span>
- </a>
- </div><?php
+
+ public function get_display( $post ) {
+ $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>';
+ } 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 );
+ }
}
-
+
public function get_state() {
return $this->state;
}
-
+
public function process_request( $post, array $post_data ) {
-
+
if ( isset( $post_data['state'] ) ) {
$this->state = $post_data['state'];
}
// Record stats
parent::process_request( $post, $post_data );
+
+ // Redirect to Google +'s sharing endpoint
+ $url = 'https://plus.google.com/share?url=' . rawurlencode( $this->get_share_url( $post->ID ) );
+ wp_redirect( $url );
die();
}
-
+
public function display_footer() {
global $post;
-?>
- <script type="text/javascript" charset="utf-8">
- function sharing_plusone( obj ) {
- jQuery.ajax( {
- url: '<?php echo get_permalink( $post->ID ) . '?share=google-plus-1'; ?>',
- type: 'POST',
- data: obj
- } );
- }
- </script>
- <script type="text/javascript" src="http://apis.google.com/js/plusone.js"></script>
-<?php
- }
+
+ 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();
+ });
+ </script>
+ <script type="text/javascript" src="//apis.google.com/js/plusone.js"></script> <?php
+ } else {
+ $this->js_dialog( 'google-plus-1', array( 'width' => 600, 'height' => 600 ) );
+ }
+ }
public function get_total( $post = false ) {
global $wpdb, $blog_id;
-
- $name = strtolower( $this->get_id() );
-
+
+ $name = strtolower( $this->get_id() );
+
if ( $post == false ) {
// get total number of shares for service
return $wpdb->get_var( $wpdb->prepare( "SELECT SUM( count ) FROM sharing_stats WHERE blog_id = %d AND share_service = %s", $blog_id, $name ) );
}
-
+
//get total shares for a post
return $wpdb->get_var( $wpdb->prepare( "SELECT count FROM sharing_stats WHERE blog_id = %d AND post_id = %d AND share_service = %s", $blog_id, $post->ID, $name ) );
}
@@ -856,14 +918,14 @@ class Share_Custom extends Sharing_Advanced_Source {
private $url;
public $smart = true;
var $shortname;
-
+
public function get_class() {
return 'custom';
}
public function __construct( $id, array $settings ) {
parent::__construct( $id, $settings );
-
+
$opts = $this->get_options();
if ( isset( $settings['name'] ) ) {
@@ -877,46 +939,46 @@ class Share_Custom extends Sharing_Advanced_Source {
if ( isset( $settings['url'] ) )
$this->url = $settings['url'];
}
-
+
public function get_name() {
return $this->name;
}
-
+
public function get_display( $post ) {
$str = $this->get_link( get_permalink( $post->ID ), esc_html( $this->name ), __( 'Click to share', 'jetpack' ), 'share='.$this->id );
- return str_replace( '<span>', '<span style="background-image:url(' . esc_url( $this->icon ) . ');">', $str );
+ return str_replace( '<span>', '<span style="' . esc_attr( 'background-image:url("' . addcslashes( esc_url_raw( $this->icon ), '"' ) . '");' ) . '">', $str );
}
public function process_request( $post, array $post_data ) {
$url = str_replace( '&amp;', '&', $this->url );
- $url = str_replace( '%post_url%', urlencode( apply_filters( 'sharing_permalink', get_permalink( $post->ID ), $post->ID, $this->id ) ), $url );
- $url = str_replace( '%post_full_url%', urlencode( get_permalink( $post->ID ) ), $url );
- $url = str_replace( '%post_title%', urlencode( $post->post_title ), $url );
+ $url = str_replace( '%post_url%', rawurlencode( $this->get_share_url( $post->ID ) ), $url );
+ $url = str_replace( '%post_full_url%', rawurlencode( get_permalink( $post->ID ) ), $url );
+ $url = str_replace( '%post_title%', rawurlencode( $post->post_title ), $url );
if ( strpos( $url, '%post_tags%' ) !== false ) {
$tags = get_the_tags( $post->ID );
$tagged = '';
-
+
if ( $tags ) {
foreach ( $tags AS $tag ) {
- $tagged[] = urlencode( $tag->name );
+ $tagged[] = rawurlencode( $tag->name );
}
-
+
$tagged = implode( ',', $tagged );
}
$url = str_replace( '%post_tags%', $tagged, $url );
}
-
+
if ( strpos( $url, '%post_excerpt%' ) !== false ) {
$url_excerpt = $post->post_excerpt;
if ( empty( $url_excerpt ) )
$url_excerpt = $post->post_content;
-
+
$url_excerpt = strip_tags( strip_shortcodes( $url_excerpt ) );
$url_excerpt = wp_html_excerpt( $url_excerpt, 100 );
$url_excerpt = rtrim( preg_replace( '/[^ .]*$/', '', $url_excerpt ) );
- $url = str_replace( '%post_excerpt%', urlencode( $url_excerpt ), $url );
+ $url = str_replace( '%post_excerpt%', rawurlencode( $url_excerpt ), $url );
}
// Record stats
@@ -926,7 +988,7 @@ class Share_Custom extends Sharing_Advanced_Source {
wp_redirect( $url );
die();
}
-
+
public function display_options() {
?>
<div class="input">
@@ -964,13 +1026,13 @@ class Share_Custom extends Sharing_Advanced_Source {
$name = trim( wp_html_excerpt( wp_kses( stripslashes( $data['name'] ), array() ), 30 ) );
$url = trim( esc_url_raw( $data['url'] ) );
$icon = trim( esc_url_raw( $data['icon'] ) );
-
+
if ( $name )
$this->name = $name;
if ( $url )
$this->url = $url;
-
+
if ( $icon )
$this->icon = $icon;
}
@@ -982,25 +1044,25 @@ class Share_Custom extends Sharing_Advanced_Source {
'url' => $this->url,
);
}
-
+
public function display_preview() {
$opts = $this->get_options();
-
+
$text = '&nbsp;';
if ( !$this->smart )
if ( $this->button_style != 'icon' )
$text = $this->get_name();
-
+
$klasses = array( 'share-'.$this->shortname );
-
+
if ( $this->button_style == 'icon' || $this->button_style == 'icon-text' )
$klasses[] = 'share-icon';
-
+
if ( $this->button_style == 'icon' ) {
$text = '';
$klasses[] = 'no-text';
}
-
+
if ( $this->button_style == 'text' )
$klasses[] = 'no-icon';
@@ -1028,24 +1090,29 @@ class Share_Tumblr extends Sharing_Source {
else
$this->smart = false;
}
-
+
public function get_name() {
return __( 'Tumblr', 'jetpack' );
}
public function get_display( $post ) {
- if ( $this->smart )
- return '<a href="http://www.tumblr.com/share" title="Share on Tumblr" style="display:inline-block; text-indent:-9999px; overflow:hidden; width:62px; height:20px; background:url(\'http://platform.tumblr.com/v1/share_2.png\') top left no-repeat transparent;">Share on Tumblr</a>';
- else
+ if ( $this->smart ) {
+ $target = '';
+ if ( 'new' == $this->open_links )
+ $target = '_blank';
+
+ return '<a target="' . $target . '" href="http://www.tumblr.com/share/link/?url=' . rawurlencode( $this->get_share_url( $post->ID ) ) . '&name=' . rawurlencode( $post->post_title ) . '" title="Share on Tumblr" style="display:inline-block; text-indent:-9999px; overflow:hidden; width:62px; height:20px; background:url(\'http://platform.tumblr.com/v1/share_2.png\') top left no-repeat transparent;">Share on Tumblr</a>';
+ } else {
return $this->get_link( get_permalink( $post->ID ), _x( 'Tumblr', 'share to', 'jetpack' ), __( 'Click to share on Tumblr', 'jetpack' ), 'share=tumblr' );
+ }
}
-
+
public function process_request( $post, array $post_data ) {
// Record stats
parent::process_request( $post, $post_data );
-
+
// Redirect to Tumblr's sharing endpoint (a la their bookmarklet)
- $url = 'http://www.tumblr.com/share?v=3&u=' . rawurlencode( get_permalink( $post->ID ) ) . '&t=' . rawurlencode( $post->post_title ) . '&s=';
+ $url = 'http://www.tumblr.com/share?v=3&u=' . rawurlencode( $this->get_share_url( $post->ID ) ) . '&t=' . rawurlencode( $post->post_title ) . '&s=';
wp_redirect( $url );
die();
}
@@ -1070,7 +1137,7 @@ class Share_Pinterest extends Sharing_Source {
else
$this->smart = false;
}
-
+
public function get_name() {
return __( 'Pinterest', 'jetpack' );
}
@@ -1078,10 +1145,24 @@ class Share_Pinterest extends Sharing_Source {
public function get_post_image( $content ) {
$image = '';
+ if ( class_exists( 'Jetpack_PostImages' ) ) {
+ // Use the full stack of methods to find an image, except for HTML, which can cause loops
+ $img = Jetpack_PostImages::get_image( $content->ID );
+ if ( !empty( $img['src'] ) )
+ return $img['src'];
+ }
+
+ // If we have to fall back to the following, we only do a few basic image checks
+ $content = $content->post_content;
if ( function_exists('has_post_thumbnail') && has_post_thumbnail() ) {
$thumb_id = get_post_thumbnail_id();
- $thumb = wp_get_attachment_image_src( $thumb_id );
- $image = remove_query_arg( array('w', 'h'), $thumb[0] );
+ $thumb = wp_get_attachment_image_src( $thumb_id, 'full' );
+
+ // This shouldn't be necessary, since has_post_thumbnail() is true,
+ // but... see http://wordpress.org/support/topic/jetpack-youtube-embeds
+ if ( ! $thumb ) return '';
+
+ $image = remove_query_arg( array('w', 'h'), $thumb[0] );
} else if ( preg_match_all('/<img (.+?)>/', $content, $matches) ) {
foreach ( $matches[1] as $attrs ) {
$media = $img = array();
@@ -1091,7 +1172,7 @@ class Share_Pinterest extends Sharing_Source {
continue;
}
else {
- $image = $img['src'];
+ $image = htmlspecialchars_decode( $img['src'] );
break;
}
}
@@ -1102,43 +1183,60 @@ class Share_Pinterest extends Sharing_Source {
public function get_display( $post ) {
if ( $this->smart )
- return '<div class="pinterest_button"><a href="http://pinterest.com/pin/create/button/?url='. rawurlencode( apply_filters( 'sharing_permalink', get_permalink( $post->ID ), $post->ID, $this->id ) ) . '&description=' . rawurlencode( esc_attr( $post->post_title ) ) . '&media=' . rawurlencode( esc_url( $this->get_post_image( $post->post_content ) ) ) . '" class="pin-it-button" count-layout="horizontal"> '. __( 'Pin It', 'sharedaddy') .'</a></div>';
+ return '<div class="pinterest_button"><a href="' . esc_url( 'http://pinterest.com/pin/create/button/?url='. rawurlencode( $this->get_share_url( $post->ID ) ) . '&description=' . rawurlencode( $post->post_title ) . '&media=' . rawurlencode( esc_url_raw( $this->get_post_image( $post ) ) ) ) . '" class="pin-it-button" count-layout="horizontal"> '. __( 'Pin It', 'jetpack') .'</a></div>';
else
return $this->get_link( get_permalink( $post->ID ), _x( 'Pinterest', 'share to', 'jetpack' ), __( 'Click to share on Pinterest', 'jetpack' ), 'share=pinterest' );
}
-
+
public function process_request( $post, array $post_data ) {
- $pinterest_url = 'http://pinterest.com/pin/create/button/?url=' . rawurlencode( get_permalink( $post->ID ) ) . '&description=' . rawurlencode( esc_attr( $post->post_title ) ) . '&media=' . rawurlencode( esc_url( $this->get_post_image( $post->post_content ) ) );
-
// Record stats
parent::process_request( $post, $post_data );
-
- // Redirect to Pinterest
- wp_redirect( $pinterest_url );
- die();
- }
- public function display_footer() {
- if ( !$this->smart ) {
- $this->js_dialog( $this->shortname, array( 'width' => 650, 'height' => 280 ) );
+ // If we're triggering the multi-select panel, then we don't need to redirect to Pinterest
+ if ( !isset( $_GET['js_only'] ) ) {
+ $pinterest_url = esc_url_raw( 'http://pinterest.com/pin/create/button/?url=' . rawurlencode( $this->get_share_url( $post->ID ) ) . '&description=' . rawurlencode( $post->post_title ) . '&media=' . rawurlencode( esc_url_raw( $this->get_post_image( $post ) ) ) );
+ wp_redirect( $pinterest_url );
} else {
-?>
- <script type="text/javascript">
- function pinterest_async_load() {
- var s = document.createElement("script");
- s.type = "text/javascript";
- s.async = true;
- s.src = window.location.protocol + "//assets.pinterest.com/js/pinit.js";
- var x = document.getElementsByTagName("script")[0];
- x.parentNode.insertBefore(s, x);
- }
- jQuery(document).on('ready post-load', function() {
- pinterest_async_load();
- });
- </script>
-<?php
+ echo '// share count bumped';
}
- }
+ die();
+ }
+ public function display_footer() {
+ ?>
+ <?php if ( $this->smart ) : ?>
+ <script type="text/javascript">
+ // Pinterest shared resources
+ var s = document.createElement("script");
+ s.type = "text/javascript";
+ s.async = true;
+ s.src = window.location.protocol + "//assets.pinterest.com/js/pinit.js";
+ var x = document.getElementsByTagName("script")[0];
+ x.parentNode.insertBefore(s, x);
+ </script>
+ <?php else : ?>
+ <script type="text/javascript">
+ jQuery(document).on('ready', function(){
+ jQuery('body').on('click', 'a.share-pinterest', function(e){
+ e.preventDefault();
+
+ // Load Pinterest Bookmarklet code
+ var s = document.createElement("script");
+ s.type = "text/javascript";
+ s.src = window.location.protocol + "//assets.pinterest.com/js/pinmarklet.js?r=" + ( Math.random() * 99999999 );
+ var x = document.getElementsByTagName("script")[0];
+ x.parentNode.insertBefore(s, x);
+
+ // Trigger Stats
+ var s = document.createElement("script");
+ s.type = "text/javascript";
+ s.src = this + ( this.toString().indexOf( '?' ) ? '&' : '?' ) + 'js_only=1';
+ var x = document.getElementsByTagName("script")[0];
+ x.parentNode.insertBefore(s, x);
+ });
+ });
+ </script>
+ <?php endif;
+ }
}
diff --git a/plugins/jetpack/modules/sharedaddy/sharing.css b/plugins/jetpack/modules/sharedaddy/sharing.css
index fb45c924..95a09d0e 100644
--- a/plugins/jetpack/modules/sharedaddy/sharing.css
+++ b/plugins/jetpack/modules/sharedaddy/sharing.css
@@ -40,6 +40,7 @@ div.sharedaddy h3,
border: none;
}
+/* @noflip */
.rtl div.sharedaddy h3,
.rtl #content div.sharedaddy h3,
.rtl #main div.sharedaddy h3,
@@ -121,7 +122,7 @@ div.sharedaddy.sharedaddy-dark .sd-block {
div.sharedaddy .sd-content {
width: 82.125%; /* 530px / 640px */
float: right;
- margin: 0;
+ margin: -2px 0 0 0;
}
div.sharedaddy .sd-content ul {
@@ -134,10 +135,12 @@ div.sharedaddy .sd-content li {
display: block;
}
+/* @noflip */
.rtl div.sharedaddy .sd-content {
float: right;
}
+/* @noflip */
.rtl div.sharedaddy .sd-content li {
float: right;
margin: 0 0 5px 5px !important;
@@ -190,7 +193,7 @@ a.sd-button > span {
padding: 4px 8px;
display: block;
opacity: .8;
- line-height: 1;
+ line-height: 1.5em;
text-shadow: none;
}
@@ -202,8 +205,10 @@ a.sd-button:hover span {
font-size: 90%;
color: #666;
margin-left: 5px;
+ line-height: 1;
}
+/* @noflip */
.rtl .sd-button span.share-count {
margin-right: 5px;
}
@@ -212,7 +217,7 @@ a.sd-button:hover span {
.sd-social-icon-text a.sd-button > span,
a.sd-button > span {
- padding: 3px 5px 3px 23px;
+ padding: 1px 5px 1px 23px;
background-position: 2px center;
background-repeat: no-repeat;
}
@@ -249,17 +254,20 @@ div.sharedaddy .no-icon a span, div.sharedaddy li.no-icon div a span {
background-image: none;
}
+/* @noflip */
.rtl .sd-social-icon-text a.sd-button > span,
.rtl a.sd-button > span {
padding: 3px 23px 3px 5px;
background-position: 98% center;
}
+/* @noflip */
.rtl .sd-social-text a.sd-button > span {
padding-left: 0;
padding-right: 5px
}
+/* @noflip */
.rtl div.sharedaddy .no-icon a span, .rtl div.sharedaddy li.no-icon div a span {
padding-left: 0;
padding-right: 5px
@@ -279,6 +287,10 @@ li.share-twitter a.sd-button > span {
background-image: url('images/twitter.png?1');
}
+li.share-google-plus-1 a.sd-button > span {
+ background-image: url('images/googleplus1.png?1');
+}
+
li.share-linkedin a.sd-button > span {
background-image: url('images/linkedin.png');
}
@@ -332,6 +344,11 @@ a.sd-button.share-more span {
background-size: 16px 16px;
}
+ li.share-google-plus-1 a.sd-button > span {
+ background-image: url('images/googleplus1@2x.png');
+ background-size: 16px 16px;
+ }
+
li.share-linkedin a.sd-button > span {
background-image: url('images/linkedin@2x.png');
background-size: 16px 16px;
@@ -379,19 +396,6 @@ a.sd-button.share-more span {
}
-
-/* Special case for non-smart implementations of Google+ button */
-
-div.sharedaddy .sd-content ul li.share-google-plus-1 {
- line-height: 90%;
- margin-bottom: 2px !important;
- min-height: 20px;
-}
-
-div.sharedaddy .sd-social-official .sd-content ul li.share-google-plus-1 {
- padding-top: 0;
-}
-
/* More pannel */
div.sharedaddy .sharing-hidden .inner {
@@ -413,6 +417,7 @@ div.sharedaddy .sharing-hidden .inner {
box-shadow: 0px 2px 8px rgba(0, 0, 0, .2);
}
+/* @noflip */
.rtl div.sharedaddy .sharing-hidden .inner {
margin-left: 0;
margin-right: -100px;
@@ -422,82 +427,97 @@ div.sharedaddy.sharedaddy-dark .sharing-hidden .inner {
border-color: #222;
}
-div.sd-content a.sd-button > span {
- line-height: 1.5em;
-}
+/* =Sharing: Email Dialog
+-------------------------------------------------------------- */
#sharing_email {
- background-color: #FFFFFF;
- border: 1px solid #CCCCCC;
- border-radius: 3px 3px 3px 3px;
- box-shadow: 0 2px 8px rgba(0, 0, 0, 0.2);
- margin-left: -120px;
- padding: 15px;
- position: absolute;
- text-align: left;
- width: 312px;
- z-index: 1001;
+ width: 312px;
+ padding: 15px;
+ position: absolute;
+ margin-left: -120px;
+ z-index: 1001;
+ background-color: #fff;
+ border: 1px solid #ccc;
+ -moz-border-radius: 3px;
+ -webkit-border-radius: 3px;
+ border-radius: 3px;
+ -moz-box-shadow: 0px 2px 8px #ccc;
+ -webkit-box-shadow: 0px 2px 8px #ccc;
+ box-shadow: 0px 2px 8px #ccc;
+ -webkit-box-shadow: 0px 2px 8px rgba(0, 0, 0, .2);
+ -moz-box-shadow: 0px 2px 8px rgba(0, 0, 0, .2);
+ box-shadow: 0px 2px 8px rgba(0, 0, 0, .2);
+ text-align: left;
}
div.sharedaddy.sharedaddy-dark #sharing_email {
- border-color: #FFFFFF;
+ border-color: #fff;
}
#sharing_email .errors {
- background-color: #771A09;
- color: #FFFFFF;
- font-size: 11px;
- line-height: 11px;
- margin: 10px 0 0;
- padding: 5px 8px;
+ color: #fff;
+ background-color: #771a09;
+ font-size: 11px;
+ padding: 5px 8px;
+ line-height: 11px;
+ margin: 10px 0 0 0;
}
#sharing_email label {
- color: #333333;
- display: block;
- font-size: 11px;
- font-weight: bold;
- padding: 0 0 4px;
- text-align: left;
- text-shadow: none;
+ font-size: 11px;
+ color: #333;
+ font-weight: bold;
+ display: block;
+ padding: 0 0 4px 0;
+ text-align: left;
+ text-shadow: none;
}
#sharing_email input[type="text"] {
- background: none repeat scroll 0 0 #FFFFFF;
- border: 1px solid #CCCCCC;
- color: #333333;
- margin-bottom: 12px;
- width: 98.5%;
+ width: 98.5%;
+ margin-bottom: 12px;
+ border: 1px solid #ccc;
+ background: #fff;
+ color: #333;
}
#sharing_email .sharing_cancel {
- font-size: 11px;
- padding: 0 0 0 10px;
- text-shadow: none;
+ padding: 0 0 0 10px;
+ font-size: 11px;
+ text-shadow: none;
}
#sharing_email .recaptcha {
- height: 123px;
- margin: 10px 0 14px;
- width: 312px;
+ width: 312px;
+ height: 123px;
+ margin: 10px 0 14px 0;
}
/* =RTL
-------------------------------------------------------------- */
+/* @noflip */
body.rtl .sharing ul {
float: right;
}
+
+/* @noflip */
body.rtl .sharing li {
margin: 0 0 0 10px !important;
}
+
+/* @noflip */
.rtl #sharing_email {
- margin-left: 0;
- margin-right: -120px;
- text-align: right;
+ margin-left: -0px;
+ margin-right: -120px;
+ text-align: right
}
+
+/* @noflip */
+.rtl #sharing_email label {
+ text-align: right
+}
+
+/* @noflip */
.rtl #sharing_email .sharing_cancel {
- padding: 0 10px 0 0;
+ padding: 0 10px 0 0;
}
-.rtl #sharing_email label {
- text-align: right;
-} \ No newline at end of file
diff --git a/plugins/jetpack/modules/sharedaddy/sharing.js b/plugins/jetpack/modules/sharedaddy/sharing.js
index a6c29494..68482c60 100644
--- a/plugins/jetpack/modules/sharedaddy/sharing.js
+++ b/plugins/jetpack/modules/sharedaddy/sharing.js
@@ -1,15 +1,21 @@
var WPCOMSharing = {
+ done_urls : [],
get_counts : function( url ) {
+ if ( 'undefined' != typeof WPCOMSharing.done_urls[ WPCOM_sharing_counts[ url ] ] )
+ return;
+
if ( jQuery( '#sharing-facebook-' + WPCOM_sharing_counts[ url ] ).length )
- jQuery.getScript( 'https://graph.facebook.com/' + encodeURIComponent( url ) + '?callback=WPCOMSharing.update_facebook_count' );
+ jQuery.getScript( 'https://api.facebook.com/method/fql.query?query=' + encodeURIComponent( "SELECT total_count, url FROM link_stat WHERE url='" + url + "'" ) + '&format=json&callback=WPCOMSharing.update_facebook_count' );
if ( jQuery( '#sharing-twitter-' + WPCOM_sharing_counts[ url ] ).length )
- jQuery.getScript( 'http://urls.api.twitter.com/1/urls/count.json?callback=WPCOMSharing.update_twitter_count&url=' + encodeURIComponent( url ) );
+ jQuery.getScript( window.location.protocol + '//cdn.api.twitter.com/1/urls/count.json?callback=WPCOMSharing.update_twitter_count&url=' + encodeURIComponent( url ) );
if ( jQuery( '#sharing-linkedin-' + WPCOM_sharing_counts[ url ] ).length )
- jQuery.getScript( 'http://www.linkedin.com/countserv/count/share?format=jsonp&callback=WPCOMSharing.update_linkedin_count&url=' + encodeURIComponent( url ) );
+ jQuery.getScript( window.location.protocol + '//www.linkedin.com/countserv/count/share?format=jsonp&callback=WPCOMSharing.update_linkedin_count&url=' + encodeURIComponent( url ) );
+
+ WPCOMSharing.done_urls[ WPCOM_sharing_counts[ url ] ] = true;
},
update_facebook_count : function( data ) {
- if ( 'undefined' != typeof data.shares && ( data.shares * 1 ) > 0 ) {
- WPCOMSharing.inject_share_count( 'sharing-facebook-' + WPCOM_sharing_counts[ data.id ], data.shares );
+ if ( 'undefined' != typeof data[0].total_count && ( data[0].total_count * 1 ) > 0 ) {
+ WPCOMSharing.inject_share_count( 'sharing-facebook-' + WPCOM_sharing_counts[ data[0].url ], data[0].total_count );
}
},
update_twitter_count : function( data ) {
@@ -40,14 +46,16 @@ var WPCOMSharing = {
return /^((([a-z]|\d|[!#\$%&'\*\+\-\/=\?\^_`{\|}~]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+(\.([a-z]|\d|[!#\$%&'\*\+\-\/=\?\^_`{\|}~]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+)*)|((\x22)((((\x20|\x09)*(\x0d\x0a))?(\x20|\x09)+)?(([\x01-\x08\x0b\x0c\x0e-\x1f\x7f]|\x21|[\x23-\x5b]|[\x5d-\x7e]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(\\([\x01-\x09\x0b\x0c\x0d-\x7f]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]))))*(((\x20|\x09)*(\x0d\x0a))?(\x20|\x09)+)?(\x22)))@((([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.)+(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.?$/i.test( this.val() );
}
} );
-
- if ( 'undefined' != typeof WPCOM_sharing_counts ) {
- for ( var url in WPCOM_sharing_counts ) {
- WPCOMSharing.get_counts( url );
- }
- }
- $( document ).on( 'ready post-load', function() {
+ $( document ).on( 'ready', WPCOMSharing_do );
+ $( document.body ).on( 'post-load', WPCOMSharing_do );
+
+ function WPCOMSharing_do() {
+ if ( 'undefined' != typeof WPCOM_sharing_counts ) {
+ for ( var url in WPCOM_sharing_counts ) {
+ WPCOMSharing.get_counts( url );
+ }
+ }
var $more_sharing_buttons = $( '.sharedaddy a.sharing-anchor' );
$more_sharing_buttons.click( function() {
@@ -58,7 +66,7 @@ var WPCOMSharing = {
if ( $( this ).attr( 'href' ) && $( this ).attr( 'href' ).indexOf( 'share=' ) != -1 )
$( this ).attr( 'href', $( this ).attr( 'href' ) + '&nb=1' );
} );
-
+
// Show hidden buttons
// Touchscreen device: use click.
@@ -71,14 +79,14 @@ var WPCOMSharing = {
// We're in the middle of some other event's animation
return;
}
-
+
if ( true === $more_sharing_pane.data( 'justSlid' ) ) {
// We just finished some other event's animation - don't process click event so that slow-to-react-clickers don't get confused
return;
}
-
+
$( '#sharing_email' ).slideUp( 200 );
-
+
$more_sharing_pane.css( {
left: $more_sharing_button.position().left + 'px',
top: $more_sharing_button.position().top + $more_sharing_button.height() + 3 + 'px'
@@ -95,7 +103,7 @@ var WPCOMSharing = {
// Create a timer to make the area appear if the mouse hovers for a period
var timer = setTimeout( function() {
$( '#sharing_email' ).slideUp( 200 );
-
+
$more_sharing_pane.data( 'justSlid', true );
$more_sharing_pane.css( {
left: $more_sharing_button.position().left + 'px',
@@ -103,44 +111,49 @@ var WPCOMSharing = {
} ).slideDown( 200, function() {
// Mark the item as have being appeared by the hover
$more_sharing_button.data( 'hasoriginal', true ).data( 'hasitem', false );
-
- // Remove all special handlers
- $more_sharing_pane.mouseleave( handler_item_leave ).mouseenter( handler_item_enter );
- $more_sharing_button.mouseleave( handler_original_leave ).mouseenter( handler_original_enter );
+
setTimeout( function() {
$more_sharing_pane.data( 'justSlid', false );
}, 300 );
+
+ if ( $more_sharing_pane.find( '.share-google-plus-1' ).size() ) {
+ // The pane needs to stay open for the Google+ Button
+ return;
+ }
+
+ $more_sharing_pane.mouseleave( handler_item_leave ).mouseenter( handler_item_enter );
+ $more_sharing_button.mouseleave( handler_original_leave ).mouseenter( handler_original_enter );
} );
-
+
// The following handlers take care of the mouseenter/mouseleave for the share button and the share area - if both are left then we close the share area
var handler_item_leave = function() {
$more_sharing_button.data( 'hasitem', false );
-
+
if ( $more_sharing_button.data( 'hasoriginal' ) === false ) {
var timer = setTimeout( close_it, 800 );
$more_sharing_button.data( 'timer2', timer );
}
};
-
+
var handler_item_enter = function() {
$more_sharing_button.data( 'hasitem', true );
clearTimeout( $more_sharing_button.data( 'timer2' ) );
- }
-
+ }
+
var handler_original_leave = function() {
$more_sharing_button.data( 'hasoriginal', false );
-
+
if ( $more_sharing_button.data( 'hasitem' ) === false ) {
var timer = setTimeout( close_it, 800 );
$more_sharing_button.data( 'timer2', timer );
}
};
-
+
var handler_original_enter = function() {
$more_sharing_button.data( 'hasoriginal', true );
clearTimeout( $more_sharing_button.data( 'timer2' ) );
};
-
+
var close_it = function() {
$more_sharing_pane.data( 'justSlid', true );
$more_sharing_pane.slideUp( 200, function() {
@@ -148,36 +161,36 @@ var WPCOMSharing = {
$more_sharing_pane.data( 'justSlid', false );
}, 300 );
} );
-
+
// Clear all hooks
$more_sharing_button.unbind( 'mouseleave', handler_original_leave ).unbind( 'mouseenter', handler_original_enter );
$more_sharing_pane.unbind( 'mouseleave', handler_item_leave ).unbind( 'mouseenter', handler_item_leave );
return false;
};
}, 200 );
-
+
// Remember the timer so we can detect it on the mouseout
$more_sharing_button.data( 'timer', timer );
}
}, function() {
// Mouse out - remove any timer
- $more_sharing_buttons.each( function() {
+ $more_sharing_buttons.each( function() {
clearTimeout( $( this ).data( 'timer' ) );
} );
$more_sharing_buttons.data( 'timer', false );
} );
}
-
+
// Add click functionality
$( '.sharedaddy ul' ).each( function( item ) {
printUrl = function ( uniqueId, urlToPrint ) {
$( 'body:first' ).append( '<iframe style="position:fixed;top:100;left:100;height:1px;width:1px;border:none;" id="printFrame-' + uniqueId + '" name="printFrame-' + uniqueId + '" src="' + urlToPrint + '" onload="frames[\'printFrame-' + uniqueId + '\'].focus();frames[\'printFrame-' + uniqueId + '\'].print();"></iframe>' )
};
-
+
// Print button
$( this ).find( 'a.share-print' ).click( function() {
ref = $( this ).attr( 'href' );
-
+
var do_print = function() {
if ( ref.indexOf( '#print' ) == -1 ) {
uid = new Date().getTime();
@@ -186,7 +199,7 @@ var WPCOMSharing = {
else
print();
}
-
+
// Is the button in a dropdown?
if ( $( this ).parents( '.sharing-hidden' ).length > 0 ) {
$( this ).parents( '.inner' ).slideUp( 0, function() {
@@ -198,11 +211,11 @@ var WPCOMSharing = {
return false;
} );
-
+
// Press This button
$( this ).find( 'a.share-press-this' ).click( function() {
var s = '';
-
+
if ( window.getSelection )
s = window.getSelection();
else if( document.getSelection )
@@ -213,7 +226,7 @@ var WPCOMSharing = {
if ( s )
$( this ).attr( 'href', $( this ).attr( 'href' ) + '&sel=' + encodeURI( s ) );
- if ( !window.open( $( this ).attr( 'href' ), 't', 'toolbar=0,resizable=1,scrollbars=1,status=1,width=720,height=570' ) )
+ if ( !window.open( $( this ).attr( 'href' ), 't', 'toolbar=0,resizable=1,scrollbars=1,status=1,width=720,height=570' ) )
document.location.href = $( this ).attr( 'href' );
return false;
@@ -221,8 +234,8 @@ var WPCOMSharing = {
// Email button
$( this ).find( 'a.share-email' ).click( function() {
- var url = $( this ).attr( 'href' );
-
+ var url = $( this ).attr( 'href' ), key;
+
if ( $( '#sharing_email' ).is( ':visible' ) )
$( '#sharing_email' ).slideUp( 200 );
else {
@@ -232,8 +245,8 @@ var WPCOMSharing = {
$( '#sharing_email form' ).show();
$( '#sharing_email form input[type=submit]' ).removeAttr( 'disabled' );
$( '#sharing_email form a.sharing_cancel' ).show();
-
- var key = '';
+
+ key = '';
if ( $( '#recaptcha_public_key' ).length > 0 )
key = $( '#recaptcha_public_key' ).val();
@@ -245,7 +258,7 @@ var WPCOMSharing = {
left: $( this ).offset().left + 'px',
top: $( this ).offset().top + $( this ).height() + 'px'
} ).slideDown( 200 );
-
+
// Hook up other buttons
$( '#sharing_email a.sharing_cancel' ).unbind( 'click' ).click( function() {
$( '#sharing_email .errors' ).hide();
@@ -253,25 +266,25 @@ var WPCOMSharing = {
$( '#sharing_background' ).fadeOut();
return false;
} );
-
+
// Submit validation
$( '#sharing_email input[type=submit]' ).unbind( 'click' ).click( function() {
var form = $( this ).parents( 'form' );
-
+
// Disable buttons + enable loading icon
$( this ).prop( 'disabled', true );
form.find( 'a.sharing_cancel' ).hide();
form.find( 'img.loading' ).show();
-
+
$( '#sharing_email .errors' ).hide();
$( '#sharing_email .error' ).removeClass( 'error' );
-
+
if ( $( '#sharing_email input[name=source_email]' ).share_is_email() == false )
$( '#sharing_email input[name=source_email]' ).addClass( 'error' );
-
+
if ( $( '#sharing_email input[name=target_email]' ).share_is_email() == false )
$( '#sharing_email input[name=target_email]' ).addClass( 'error' );
-
+
if ( $( '#sharing_email .error' ).length == 0 ) {
// AJAX send the form
$.ajax( {
@@ -298,10 +311,10 @@ var WPCOMSharing = {
}
}
} );
-
+
return false;
}
-
+
form.find( 'img.loading' ).hide();
form.find( 'input[type=submit]' ).removeAttr( 'disabled' );
form.find( 'a.sharing_cancel' ).show();
@@ -310,13 +323,13 @@ var WPCOMSharing = {
return false;
} );
}
-
+
return false;
} );
} );
-
+
$( 'li.share-email, li.share-custom a.sharing-anchor' ).addClass( 'share-service-visible' );
- } );
+ }
})( jQuery );
// Recaptcha code
diff --git a/plugins/jetpack/modules/sharedaddy/sharing.php b/plugins/jetpack/modules/sharedaddy/sharing.php
index 3dd6f8d4..118f851c 100644
--- a/plugins/jetpack/modules/sharedaddy/sharing.php
+++ b/plugins/jetpack/modules/sharedaddy/sharing.php
@@ -6,7 +6,7 @@ class Sharing_Admin {
define( 'WP_SHARING_PLUGIN_URL', plugin_dir_url( __FILE__ ) );
define( 'WP_SHARING_PLUGIN_DIR', plugin_dir_path( __FILE__ ) );
}
-
+
require_once WP_SHARING_PLUGIN_DIR.'sharing-service.php';
add_action( 'admin_init', array( &$this, 'admin_init' ) );
@@ -21,16 +21,16 @@ class Sharing_Admin {
add_action( 'wp_ajax_sharing_new_service', array( &$this, 'ajax_new_service' ) );
add_action( 'wp_ajax_sharing_delete_service', array( &$this, 'ajax_delete_service' ) );
}
-
+
public function sharing_head() {
wp_enqueue_script( 'sharing-js', WP_SHARING_PLUGIN_URL.'admin-sharing.js', array( 'jquery-ui-draggable', 'jquery-ui-droppable', 'jquery-ui-sortable', 'jquery-form' ), 2 );
- wp_enqueue_style( 'sharing-admin', WP_SHARING_PLUGIN_URL.'admin-sharing.css', false, WP_SHARING_PLUGIN_VERSION );
- wp_enqueue_style( 'sharing', WP_SHARING_PLUGIN_URL.'sharing.css', false, WP_SHARING_PLUGIN_VERSION );
- wp_enqueue_script( 'sharing-js-fe', WP_SHARING_PLUGIN_URL . 'sharing.js', array( ), 2 );
+ wp_enqueue_style( 'sharing-admin', WP_SHARING_PLUGIN_URL.'admin-sharing.css', false, JETPACK__VERSION );
+ wp_enqueue_style( 'sharing', WP_SHARING_PLUGIN_URL.'sharing.css', false, JETPACK__VERSION );
+ wp_enqueue_script( 'sharing-js-fe', WP_SHARING_PLUGIN_URL . 'sharing.js', array( ), 3 );
add_thickbox();
}
-
+
public function admin_init() {
if ( isset( $_GET['page'] ) && ( $_GET['page'] == 'sharing.php' || $_GET['page'] == 'sharing' ) )
$this->process_requests();
@@ -41,29 +41,35 @@ class Sharing_Admin {
$sharer = new Sharing_Service();
$sharer->set_global_options( $_POST );
do_action( 'sharing_admin_update' );
-
+
wp_safe_redirect( admin_url( 'options-general.php?page=sharing&update=saved' ) );
die();
}
}
-
+
public function subscription_menu( $user ) {
+ if ( !defined( 'IS_WPCOM' ) || !IS_WPCOM ) {
+ $active = Jetpack::get_active_modules();
+ if ( !in_array( 'publicize', $active ) && !current_user_can( 'manage_options' ) )
+ return;
+ }
+
add_submenu_page( 'options-general.php', __( 'Sharing Settings', 'jetpack' ), __( 'Sharing', 'jetpack' ), 'publish_posts', 'sharing', array( &$this, 'management_page' ) );
}
-
+
public function ajax_save_services() {
if ( isset( $_POST['_wpnonce'] ) && wp_verify_nonce( $_POST['_wpnonce'], 'sharing-options' ) && isset( $_POST['hidden'] ) && isset( $_POST['visible'] ) ) {
$sharer = new Sharing_Service();
-
+
$sharer->set_blog_services( explode( ',', $_POST['visible'] ), explode( ',', $_POST['hidden'] ) );
die();
}
}
-
+
public function ajax_new_service() {
if ( isset( $_POST['_wpnonce'] ) && isset( $_POST['sharing_name'] ) && isset( $_POST['sharing_url'] ) && isset( $_POST['sharing_icon'] ) && wp_verify_nonce( $_POST['_wpnonce'], 'sharing-new_service' ) ) {
$sharer = new Sharing_Service();
- if ( $service = $sharer->new_service( $_POST['sharing_name'], $_POST['sharing_url'], $_POST['sharing_icon'] ) ) {
+ if ( $service = $sharer->new_service( stripslashes( $_POST['sharing_name'] ), stripslashes( $_POST['sharing_url'] ), stripslashes( $_POST['sharing_icon'] ) ) ) {
$this->output_service( $service->get_id(), $service );
echo '<!--->';
$service->button_style = 'icon-text';
@@ -76,14 +82,14 @@ class Sharing_Admin {
// Fail
die( '1' );
}
-
+
public function ajax_delete_service() {
if ( isset( $_POST['_wpnonce'] ) && isset( $_POST['service'] ) && wp_verify_nonce( $_POST['_wpnonce'], 'sharing-options_'.$_POST['service'] ) ) {
$sharer = new Sharing_Service();
$sharer->delete_service( $_POST['service'] );
}
}
-
+
public function ajax_save_options() {
if ( isset( $_POST['_wpnonce'] ) && isset( $_POST['service'] ) && wp_verify_nonce( $_POST['_wpnonce'], 'sharing-options_'.$_POST['service'] ) ) {
$sharer = new Sharing_Service();
@@ -102,29 +108,23 @@ class Sharing_Admin {
die();
}
}
-
+
public function output_preview( $service ) {
$klasses = array( 'advanced', 'preview-item' );
-
- if (
- 'googleplus1' == $service->shortname
- ||
- $service->button_style != 'text'
- ||
- $service->has_custom_button_style()
- ) {
+
+ if ( $service->button_style != 'text' || $service->has_custom_button_style() ) {
$klasses[] = 'preview-'.$service->get_class();
$klasses[] = 'share-'.$service->get_class();
-
+
if ( $service->get_class() != $service->get_id() )
$klasses[] = 'preview-'.$service->get_id();
}
-
+
echo '<li class="'.implode( ' ', $klasses ).'">';
echo $service->display_preview();
echo '</li>';
}
-
+
public function output_service( $id, $service, $show_dropdown = false ) {
?>
<li class="service advanced share-<?php echo $service->get_class(); ?>" id="<?php echo $service->get_id(); ?>">
@@ -161,21 +161,21 @@ class Sharing_Admin {
<div class="wrap">
<div class="icon32" id="icon-options-general"><br /></div>
<h2><?php _e( 'Sharing Settings', 'jetpack' ); ?></h2>
-
+
<?php do_action( 'pre_admin_screen_sharing' ) ?>
-
+
<?php if ( current_user_can( 'manage_options' ) ) : ?>
-
- <h3><?php _e( 'Sharing Buttons' ) ?></h3>
- <p><?php _e( 'Add sharing buttons to your blog and allow your visitors to share posts with their friends.' ) ?></p>
-
+
+ <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>
+
<div id="services-config">
<table id="available-services">
<tr>
<td class="description">
<h3><?php _e( 'Available Services', 'jetpack' ); ?></h3>
<p><?php _e( "Drag and drop the services you'd like to enable into the box below.", 'jetpack' ); ?></p>
- <p><a href="#TB_inline?height=395&amp;width=600&amp;inlineId=new-service" title="<?php echo esc_attr( __( 'Add a new service', 'jetpack' ) ); ?>" class="thickbox"><?php _e( 'Add a new service', 'jetpack' ); ?></a></p>
+ <p><a href="#TB_inline?height=395&amp;width=600&amp;inlineId=new-service" title="<?php echo esc_attr( __( 'Add a new service', 'jetpack' ) ); ?>" class="thickbox" id="add-a-new-service"><?php _e( 'Add a new service', 'jetpack' ); ?></a></p>
</td>
<td class="services">
<ul class="services-available" style="height: 100px;">
@@ -186,11 +186,15 @@ class Sharing_Admin {
?>
<?php endforeach; ?>
</ul>
+ <?php
+ if ( -1 == get_option( 'blog_public' ) )
+ echo '<p><strong>'.__( 'Please note that your services have been restricted because your site is private.', 'jetpack' ).'</strong></p>';
+ ?>
<br class="clearing" />
</td>
</tr>
</table>
-
+
<table id="enabled-services">
<tr>
<td class="description">
@@ -201,19 +205,19 @@ class Sharing_Admin {
<p><?php _e( 'Services dragged here will appear individually.', 'jetpack' ); ?></p>
</td>
<td class="services" id="share-drop-target">
- <h2 id="drag-instructions" <?php if ( count( $enabled['visible'] ) > 0 ) echo ' style="display: none"'; ?>><?php _e( 'Drag and drop available services here', 'jetpack' ); ?></h2>
-
+ <h2 id="drag-instructions" <?php if ( count( $enabled['visible'] ) > 0 ) echo ' style="display: none"'; ?>><?php _e( 'Drag and drop available services here.', 'jetpack' ); ?></h2>
+
<ul class="services-enabled">
<?php foreach ( $enabled['visible'] as $id => $service ) : ?>
<?php $this->output_service( $id, $service, true ); ?>
<?php endforeach; ?>
-
+
<li class="end-fix"></li>
</ul>
- </td>
+ </td>
<td id="hidden-drop-target" class="services">
<p><?php _e( 'Services dragged here will be hidden behind a share button.', 'jetpack' ); ?></p>
-
+
<ul class="services-hidden">
<?php foreach ( $enabled['hidden'] as $id => $service ) : ?>
<?php $this->output_service( $id, $service, true ); ?>
@@ -222,8 +226,8 @@ class Sharing_Admin {
</ul>
</td>
</tr>
- </table>
-
+ </table>
+
<table id="live-preview">
<tr>
<td class="description">
@@ -240,12 +244,12 @@ class Sharing_Admin {
<?php foreach ( $enabled['visible'] as $id => $service ) : ?>
<?php $this->output_preview( $service ); ?>
<?php endforeach; ?>
-
+
<?php if ( count( $enabled['hidden'] ) > 0 ) : ?>
<li class="advanced"><a href="#" class="sharing-anchor sd-button share-more"><span><?php _e( 'More', 'jetpack' ); ?></span></a></li>
<?php endif; ?>
</ul>
-
+
<?php if ( count( $enabled['hidden'] ) > 0 ) : ?>
<div class="sharing-hidden">
<div class="inner" style="display: none; <?php echo count( $enabled['hidden'] ) == 1 ? 'width:150px;' : ''; ?>">
@@ -254,18 +258,18 @@ class Sharing_Admin {
<?php else: ?>
<ul>
<?php endif; ?>
-
+
<?php foreach ( $enabled['hidden'] as $id => $service ) {
$this->output_preview( $service );
}?>
- </ul>
+ </ul>
</div>
</div>
<?php endif; ?>
<ul class="archive" style="display:none;">
- <?php
- foreach ( $sharer->get_all_services_blog() as $id => $service ) :
+ <?php
+ foreach ( $sharer->get_all_services_blog() as $id => $service ) :
if ( isset( $enabled['visible'][$id] ) )
$service = $enabled['visible'][$id];
elseif ( isset( $enabled['hidden'][$id] ) )
@@ -284,7 +288,7 @@ class Sharing_Admin {
</td>
</tr>
</table>
-
+
<form method="post" action="<?php echo admin_url( 'admin-ajax.php' ); ?>" id="save-enabled-shares">
<input type="hidden" name="action" value="sharing_save_services" />
<input type="hidden" name="visible" value="<?php echo implode( ',', array_keys( $enabled['visible'] ) ); ?>" />
@@ -322,7 +326,7 @@ class Sharing_Admin {
</select>
</td>
</tr>
- <tr valign="top">
+ <?php echo apply_filters( 'sharing_show_buttons_on_row_start', '<tr valign="top">' ); ?>
<th scope="row"><label><?php _e( 'Show buttons on', 'jetpack' ); ?></label></th>
<td>
<?php
@@ -338,19 +342,19 @@ class Sharing_Admin {
<?php if ( $br ) echo '<br />'; ?><label><input type="checkbox"<?php checked( in_array( $show, $global['show'] ) ); ?> name="show[]" value="<?php echo esc_attr( $show ); ?>" /> <?php echo esc_html( $label ); ?></label>
<?php $br = true; endforeach; ?>
</td>
- </tr>
-
+ <?php echo apply_filters( 'sharing_show_buttons_on_row_end', '</tr>' ); ?>
+
<?php do_action( 'sharing_global_options' ); ?>
</tbody>
</table>
-
+
<p class="submit">
<input type="submit" name="submit" class="button-primary" value="<?php _e( 'Save Changes', 'jetpack' ); ?>" />
</p>
-
+
<input type="hidden" name="_wpnonce" value="<?php echo wp_create_nonce( 'sharing-options' );?>" />
</form>
-
+
<div id="new-service" style="display: none">
<form method="post" action="<?php echo admin_url( 'admin-ajax.php' ); ?>" id="new-service-form">
<table class="form-table">
@@ -365,9 +369,9 @@ class Sharing_Admin {
<th scope="row" width="100"><label><?php _e( 'Sharing URL', 'jetpack' ); ?></label></th>
<td>
<input type="text" name="sharing_url" id="new_sharing_url" size="40" />
-
+
<p><?php _e( 'You can add the following variables to your service sharing URL:', 'jetpack' ); ?><br/>
- <code>%post_title%</code>, <code>%post_url%</code>, <code>%post_full_url%</code>, <code>%post_excerpt%</code>, <code>%post_full_url%</code>, <code>%post_tags%</code></p>
+ <code>%post_title%</code>, <code>%post_url%</code>, <code>%post_full_url%</code>, <code>%post_excerpt%</code>, <code>%post_tags%</code></p>
</td>
</tr>
<tr valign="top">
@@ -384,7 +388,7 @@ class Sharing_Admin {
<img src="<?php echo admin_url( 'images/loading.gif' ); ?>" width="16" height="16" alt="loading" style="vertical-align: middle; display: none" />
</td>
</tr>
-
+
<?php do_action( 'sharing_new_service_form' ); ?>
</tbody>
</table>
@@ -394,18 +398,18 @@ class Sharing_Admin {
<div class="inerror" style="display: none; margin-top: 15px">
<p><?php _e( 'An error occurred creating your new sharing service - please check you gave valid details.', 'jetpack' ); ?></p>
</div>
-
+
<input type="hidden" name="action" value="sharing_new_service" />
- <input type="hidden" name="_wpnonce" value="<?php echo wp_create_nonce( 'sharing-new_service' );?>" />
+ <input type="hidden" name="_wpnonce" value="<?php echo wp_create_nonce( 'sharing-new_service' );?>" />
</form>
</div>
-
-
+
+
<?php endif; ?>
</div>
-
+
<script type="text/javascript">
var sharing_loading_icon = '<?php echo esc_js( admin_url( "/images/loading.gif" ) ); ?>';
<?php if ( isset( $_GET['create_new_service'] ) && 'true' == $_GET['create_new_service'] ) : ?>
diff --git a/plugins/jetpack/modules/shortcodes/audio.php b/plugins/jetpack/modules/shortcodes/audio.php
index 7b507a5f..9c6b49eb 100644
--- a/plugins/jetpack/modules/shortcodes/audio.php
+++ b/plugins/jetpack/modules/shortcodes/audio.php
@@ -6,7 +6,7 @@
class AudioShortcode {
static $add_script = false;
-
+
/**
* Add all the actions & resgister the shortcode
*/
@@ -161,8 +161,8 @@ class AudioShortcode {
// generate default titles
$file_titles = array();
- for ( $i = 0; $i < $num_files; $i++ ) {
- $file_titles[] = 'Track #' . ($i+1);
+ for ( $i = 0; $i < $num_files; $i++ ) {
+ $file_titles[] = 'Track #' . ($i+1);
}
// replace with real titles if they exist
@@ -190,7 +190,7 @@ class AudioShortcode {
$to_remove = array();
foreach ( $sound_files as $i => $sfile ) {
$file_extension = pathinfo( $sfile, PATHINFO_EXTENSION );
- if ( ! preg_match( '/^(mp3|wav|ogg|oga|m4a|aac|webm)$/', $file_extension ) ) {
+ if ( ! preg_match( '/^(mp3|wav|ogg|oga|m4a|aac|webm)$/i', $file_extension ) ) {
$html5_audio .= '<!-- Audio shortcode unsupported audio format -->';
if ( 1 == $num_files ) {
$html5_audio .= $not_supported;
@@ -199,7 +199,7 @@ class AudioShortcode {
$to_remove[] = $i; // make a note of the bad files
$all_mp3 = false;
continue;
- } elseif ( ! preg_match( '/^mp3$/', $file_extension ) ) {
+ } elseif ( ! preg_match( '/^mp3$/i', $file_extension ) ) {
$all_mp3 = false;
}
@@ -240,9 +240,9 @@ CONTROLS;
// override html5 audio code w/ just not supported code
if ( is_feed() ) {
$html5_audio = $not_supported;
- }
+ }
- if ( $all_mp3 ) {
+ if ( $all_mp3 ) {
// process regular flash player, inserting HTML5 tags into object as fallback
$audio_tags = <<<FLASH
<object id='wp-as-{$post->ID}_{$ap_playerID}-flash' type='application/x-shockwave-flash' data='$swfurl' width='$width' height='24'>
@@ -268,7 +268,7 @@ FLASH;
// mashup the artist/titles for the script
$script_titles = array();
- for ( $i = 0; $i < $num_files; $i++ ) {
+ for ( $i = 0; $i < $num_files; $i++ ) {
$script_titles[] = $file_artists[$i] . $file_titles[$i];
}
@@ -279,16 +279,27 @@ FLASH;
$script = <<<SCRIPT
<script type='text/javascript'>
//<![CDATA[
- jQuery(document).on( 'ready as-script-load', function($) {
- if ( typeof window.audioshortcode != 'undefined' ) {
+ (function() {
+ var prep = function() {
+ if ( 'undefined' === typeof window.audioshortcode ) { return; }
audioshortcode.prep(
'{$post->ID}_{$ap_playerID}',
$script_files,
$script_titles,
$volume,
- $script_loop );
+ $script_loop
+ );
+ };
+ if ( 'undefined' === typeof jQuery ) {
+ if ( document.addEventListener ) {
+ window.addEventListener( 'load', prep, false );
+ } else if ( document.attachEvent ) {
+ window.attachEvent( 'onload', prep );
+ }
+ } else {
+ jQuery(document).on( 'ready as-script-load', prep );
}
- } );
+ })();
//]]>
</script>
SCRIPT;
@@ -297,7 +308,7 @@ SCRIPT;
if ( 0 < $num_good && ! is_feed() ) {
$audio_tags .= $script;
}
-
+
return "<span style='text-align:left;display:block;'><p>$audio_tags</p></span>";
}
@@ -305,11 +316,11 @@ SCRIPT;
* If the theme uses infinite scroll, include jquery at the start
*/
function check_infinite() {
- if ( current_theme_supports( 'infinite-scroll' ) ) {
+ if ( current_theme_supports( 'infinite-scroll' ) && class_exists( 'The_Neverending_Home_Page' ) && The_Neverending_Home_Page::archive_supports_infinity() )
wp_enqueue_script( 'jquery' );
- }
}
+
/**
* Dynamically load the .js, if needed
*
@@ -331,8 +342,8 @@ SCRIPT;
wp_as_js.type = 'text/javascript';
wp_as_js.src = $script_url;
wp_as_js.async = true;
- wp_as_js.onload = function() {
- jQuery( document.body ).trigger( 'as-script-load' );
+ wp_as_js.onload = function() {
+ jQuery( document.body ).trigger( 'as-script-load' );
};
document.getElementsByTagName( 'head' )[0].appendChild( wp_as_js );
} else {
diff --git a/plugins/jetpack/modules/shortcodes/css/rtl/slideshow-shortcode-rtl.css b/plugins/jetpack/modules/shortcodes/css/rtl/slideshow-shortcode-rtl.css
new file mode 100644
index 00000000..167ec706
--- /dev/null
+++ b/plugins/jetpack/modules/shortcodes/css/rtl/slideshow-shortcode-rtl.css
@@ -0,0 +1,131 @@
+/* This file was automatically generated on Jan 06 2013 05:39:50 */
+
+.slideshow-window {
+ background-color: #222;
+ border: 20px solid #222;
+ border-radius: 11px;
+ -moz-border-radius: 11px;
+ -webkit-border-radius: 11px;
+ -khtml-border-radius: 11px;
+ margin-bottom: 20px;
+}
+
+.slideshow-window, .slideshow-window * {
+ -moz-box-sizing: content-box;
+ -webkit-box-sizing: content-box;
+ box-sizing: content-box;
+}
+
+.slideshow-loading {
+ height: 100%;
+ text-align: center;
+ margin: auto;
+}
+
+body div.slideshow-window * img {
+ /* Override any styles that might be present in the page stylesheet */
+ border-width: 0 !important;
+ padding: 0 !important;
+ background-color: transparent !important;
+ background-image: none !important;
+ max-width: 100%;
+}
+
+.slideshow-loading img {
+ vertical-align: middle;
+}
+
+.slideshow-slide {
+ height: 100%;
+ width: 100%;
+ text-align: center;
+ margin: auto;
+ display: none;
+}
+
+.slideshow-slide img {
+ vertical-align: middle;
+}
+
+.slideshow-line-height-hack {
+ overflow: hide;
+ width: 0px;
+ font-size: 0px;
+}
+
+.slideshow-slide-caption {
+ font-size: 13px;
+ font-family: "Helvetica Neue", sans-serif;
+ color: #f7f7f7;
+ text-shadow: #222 2px 1px 1px;
+ line-height: 25px;
+ height: 25px;
+ position: absolute;
+ bottom: 5px;
+ right: 0;
+ z-index: 100;
+ width: 100%;
+ text-align: center;
+}
+
+/* @noflip */
+.slideshow-controls {
+ z-index: 1000;
+ position: absolute;
+ bottom: 30px;
+ margin: auto;
+ text-align: center;
+ width: 100%;
+ display: none;
+ direction:ltr;
+}
+
+body div div.slideshow-controls a,
+body div div.slideshow-controls a:hover {
+ border:2px solid rgba(255,255,255,0.1) !important;
+ background-color: #000 !important;
+ background-color: rgba(0,0,0,0.6) !important;
+ background-image: url('../../img/slideshow-controls.png') !important;
+ background-repeat: no-repeat;
+ background-size: 142px 16px !important;
+ background-position: -34px 8px !important;
+ color: #222 !important;
+ margin: 0 5px !important;
+ padding: 0 !important;
+ display: inline-block !important;
+ *display: inline;
+ zoom: 1;
+ height: 32px !important;
+ width: 32px !important;
+ line-height: 32px !important;
+ text-align: center !important;
+ -khtml-border-radius: 10em !important;
+ -webkit-border-radius: 10em !important;
+ -moz-border-radius: 10em !important;
+ border-radius: 10em !important;
+ -webkit-transition: 300ms border-color ease-out;
+ -moz-transition: 300ms border-color ease-out;
+ -o-transition: 300ms border-color ease-out;
+ transition: 300ms border-color ease-out;
+}
+
+@media only screen and (-webkit-min-device-pixel-ratio: 1.5) {
+ body div div.slideshow-controls a,
+ body div div.slideshow-controls a:hover {
+ background-image: url('../../img/slideshow-controls-2x.png') !important;
+ }
+}
+
+body div div.slideshow-controls a:hover {
+ border-color: rgba(255,255,255,1) !important;
+}
+
+body div div.slideshow-controls a:first-child { background-position: -76px 8px !important;}
+body div div.slideshow-controls a:last-child { background-position: -117px 8px !important;}
+body div div.slideshow-controls a:nth-child(2) { background-position: -34px 8px !important;}
+body div div.slideshow-controls a.running { background-position: -34px 8px !important;}
+body div div.slideshow-controls a.paused { background-position: 9px 8px !important;}
+
+.slideshow-controls a img {
+ border: 50px dotted fuchsia;
+}
diff --git a/plugins/jetpack/modules/shortcodes/css/slideshow-shortcode.css b/plugins/jetpack/modules/shortcodes/css/slideshow-shortcode.css
new file mode 100644
index 00000000..e6d05b23
--- /dev/null
+++ b/plugins/jetpack/modules/shortcodes/css/slideshow-shortcode.css
@@ -0,0 +1,129 @@
+.slideshow-window {
+ background-color: #222;
+ border: 20px solid #222;
+ border-radius: 11px;
+ -moz-border-radius: 11px;
+ -webkit-border-radius: 11px;
+ -khtml-border-radius: 11px;
+ margin-bottom: 20px;
+}
+
+.slideshow-window, .slideshow-window * {
+ -moz-box-sizing: content-box;
+ -webkit-box-sizing: content-box;
+ box-sizing: content-box;
+}
+
+.slideshow-loading {
+ height: 100%;
+ text-align: center;
+ margin: auto;
+}
+
+body div.slideshow-window * img {
+ /* Override any styles that might be present in the page stylesheet */
+ border-width: 0 !important;
+ padding: 0 !important;
+ background-color: transparent !important;
+ background-image: none !important;
+ max-width: 100%;
+}
+
+.slideshow-loading img {
+ vertical-align: middle;
+}
+
+.slideshow-slide {
+ height: 100%;
+ width: 100%;
+ text-align: center;
+ margin: auto;
+ display: none;
+}
+
+.slideshow-slide img {
+ vertical-align: middle;
+}
+
+.slideshow-line-height-hack {
+ overflow: hide;
+ width: 0px;
+ font-size: 0px;
+}
+
+.slideshow-slide-caption {
+ font-size: 13px;
+ font-family: "Helvetica Neue", sans-serif;
+ color: #f7f7f7;
+ text-shadow: #222 1px 1px 2px;
+ line-height: 25px;
+ height: 25px;
+ position: absolute;
+ bottom: 5px;
+ left: 0;
+ z-index: 100;
+ width: 100%;
+ text-align: center;
+}
+
+/* @noflip */
+.slideshow-controls {
+ z-index: 1000;
+ position: absolute;
+ bottom: 30px;
+ margin: auto;
+ text-align: center;
+ width: 100%;
+ display: none;
+ direction:ltr;
+}
+
+body div div.slideshow-controls a,
+body div div.slideshow-controls a:hover {
+ border:2px solid rgba(255,255,255,0.1) !important;
+ background-color: #000 !important;
+ background-color: rgba(0,0,0,0.6) !important;
+ background-image: url('../img/slideshow-controls.png') !important;
+ background-repeat: no-repeat;
+ background-size: 142px 16px !important;
+ background-position: -34px 8px !important;
+ color: #222 !important;
+ margin: 0 5px !important;
+ padding: 0 !important;
+ display: inline-block !important;
+ *display: inline;
+ zoom: 1;
+ height: 32px !important;
+ width: 32px !important;
+ line-height: 32px !important;
+ text-align: center !important;
+ -khtml-border-radius: 10em !important;
+ -webkit-border-radius: 10em !important;
+ -moz-border-radius: 10em !important;
+ border-radius: 10em !important;
+ -webkit-transition: 300ms border-color ease-out;
+ -moz-transition: 300ms border-color ease-out;
+ -o-transition: 300ms border-color ease-out;
+ transition: 300ms border-color ease-out;
+}
+
+@media only screen and (-webkit-min-device-pixel-ratio: 1.5) {
+ body div div.slideshow-controls a,
+ body div div.slideshow-controls a:hover {
+ background-image: url('../img/slideshow-controls-2x.png') !important;
+ }
+}
+
+body div div.slideshow-controls a:hover {
+ border-color: rgba(255,255,255,1) !important;
+}
+
+body div div.slideshow-controls a:first-child { background-position: -76px 8px !important;}
+body div div.slideshow-controls a:last-child { background-position: -117px 8px !important;}
+body div div.slideshow-controls a:nth-child(2) { background-position: -34px 8px !important;}
+body div div.slideshow-controls a.running { background-position: -34px 8px !important;}
+body div div.slideshow-controls a.paused { background-position: 9px 8px !important;}
+
+.slideshow-controls a img {
+ border: 50px dotted fuchsia;
+}
diff --git a/plugins/jetpack/modules/shortcodes/googlemaps.php b/plugins/jetpack/modules/shortcodes/googlemaps.php
index 8ced50ea..fe79a9e6 100644
--- a/plugins/jetpack/modules/shortcodes/googlemaps.php
+++ b/plugins/jetpack/modules/shortcodes/googlemaps.php
@@ -77,7 +77,7 @@ function jetpack_googlemaps_shortcode( $atts ) {
$url = substr( $url, 0, -5 );
$link_url = preg_replace( '!output=embed!', 'source=embed', $url );
- return '<iframe width="' . $width . '" height="' . $height . '" frameborder="0" scrolling="no" marginheight="0" marginwidth="0" src="' . $url . '"></iframe><br /><small><a href="' . $link_url . '" style="text-align:left">View Larger Map</a></small>';
+ return '<div class="googlemaps"><iframe width="' . $width . '" height="' . $height . '" frameborder="0" scrolling="no" marginheight="0" marginwidth="0" src="' . $url . '"></iframe><br /><small><a href="' . $link_url . '" style="text-align:left">View Larger Map</a></small></div>';
}
}
add_shortcode( 'googlemaps', 'jetpack_googlemaps_shortcode' );
diff --git a/plugins/jetpack/modules/shortcodes/img/slideshow-controls-2x.png b/plugins/jetpack/modules/shortcodes/img/slideshow-controls-2x.png
new file mode 100644
index 00000000..2c76ac05
--- /dev/null
+++ b/plugins/jetpack/modules/shortcodes/img/slideshow-controls-2x.png
Binary files differ
diff --git a/plugins/jetpack/modules/shortcodes/img/slideshow-controls.png b/plugins/jetpack/modules/shortcodes/img/slideshow-controls.png
new file mode 100644
index 00000000..09ca4871
--- /dev/null
+++ b/plugins/jetpack/modules/shortcodes/img/slideshow-controls.png
Binary files differ
diff --git a/plugins/jetpack/modules/shortcodes/img/slideshow-loader.gif b/plugins/jetpack/modules/shortcodes/img/slideshow-loader.gif
new file mode 100644
index 00000000..ce1c594e
--- /dev/null
+++ b/plugins/jetpack/modules/shortcodes/img/slideshow-loader.gif
Binary files differ
diff --git a/plugins/jetpack/modules/shortcodes/js/audio-shortcode.js b/plugins/jetpack/modules/shortcodes/js/audio-shortcode.js
new file mode 100644
index 00000000..7697258e
--- /dev/null
+++ b/plugins/jetpack/modules/shortcodes/js/audio-shortcode.js
@@ -0,0 +1,154 @@
+(function($) {
+
+window.audioshortcode = {
+
+ /**
+ * Prep the audio player once the page is ready, add listeners, etc
+ */
+ prep: function( player_id, files, titles, volume, loop ) {
+ // check if the player has already been prepped, no-op if it has
+ var container = $( '#wp-as-' + player_id + '-container' );
+ if ( container.hasClass( 'wp-as-prepped' ) ) {
+ return;
+ }
+ container.addClass( 'wp-as-prepped' );
+
+ // browser doesn't support HTML5 audio, no-op
+ if ( ! document.createElement('audio').canPlayType ) {
+ return;
+ }
+
+ // if the browser removed the script, no-op
+ player = $( '#wp-as-' + player_id ).get(0);
+ if ( typeof player === 'undefined' ) {
+ return;
+ }
+
+ this[player_id] = [];
+ this[player_id].i = 0;
+ this[player_id].files = files;
+ this[player_id].titles = titles;
+ player.volume = volume;
+
+ var type_map = {
+ 'mp3': 'mpeg',
+ 'wav': 'wav',
+ 'ogg': 'ogg',
+ 'oga': 'ogg',
+ 'm4a': 'mp4',
+ 'aac': 'mp4',
+ 'webm': 'webm'
+ };
+
+ // strip out all the files that can't be played
+ for ( var i = this[player_id].files.length-1; i >= 0; i-- ) {
+ var extension = this[player_id].files[i].split( '.' ).pop();
+ var type = 'audio/' + type_map[extension];
+ if ( ! player.canPlayType( type ) ) {
+ this.remove_track( player_id, i );
+ }
+ }
+
+ // bail if there are no more good files
+ if ( 0 == this[player_id].files.length ) {
+ return;
+ }
+ player.src = this[player_id].files[0];
+
+ // show the controls if there are still 2+ files remaining
+ if ( 1 < this[player_id].files.length ) {
+ $( '#wp-as-' + player_id + '-controls' ).show();
+ }
+
+ player.addEventListener( 'error', function() {
+ audioshortcode.remove_track( player_id, audioshortcode[player_id].i );
+ if ( 0 < audioshortcode[player_id].files.length ) {
+ audioshortcode[player_id].i--;
+ audioshortcode.next_track( player_id, false, loop );
+ }
+ }, false );
+
+ player.addEventListener( 'ended', function() {
+ audioshortcode.next_track( player_id, false, loop );
+ }, false );
+
+ player.addEventListener( 'play', function() {
+ var i = audioshortcode[player_id].i;
+ var titles = audioshortcode[player_id].titles;
+ $( '#wp-as-' + player_id + '-playing' ).text( ' ' + titles[i] );
+ }, false );
+
+ player.addEventListener( 'pause', function() {
+ $( '#wp-as-' + player_id + '-playing' ).text( '' );
+ }, false );
+ },
+
+ /**
+ * Remove the track and update the player/controls if needed
+ */
+ remove_track: function( player_id, index ) {
+ this[player_id].files.splice( index, 1 );
+ this[player_id].titles.splice( index, 1 );
+
+ // get rid of player/controls if they can't be played
+ if ( 0 == this[player_id].files.length ) {
+ $( '#wp-as-' + player_id + '-container' ).html( $( '#wp-as-' + player_id + '-nope' ).html() );
+ $( '#wp-as-' + player_id + '-controls' ).html( '' );
+ } else if ( 1 == this[player_id].files.length ) {
+ $( '#wp-as-' + player_id + '-controls' ).html( '' );
+ }
+ },
+
+ /**
+ * Change the src of the player, load the file, then play it
+ */
+ start_track: function( player_id, file ) {
+ var player = $( '#wp-as-' + player_id ).get(0);
+ player.src = file;
+ player.load();
+ player.play();
+ },
+
+ /**
+ * Play the previous track
+ */
+ prev_track: function( player_id ) {
+ var player = $( '#wp-as-' + player_id ).get(0);
+ var files = this[player_id].files;
+ if ( player.paused || 0 == this[player_id].i ) {
+ return
+ };
+
+ player.pause();
+ if ( 0 < this[player_id].i ) {
+ this[player_id].i--;
+ this.start_track( player_id, files[this[player_id].i] );
+ }
+ },
+
+ /**
+ * Play the next track
+ */
+ next_track: function( player_id, fromClick, loop ) {
+ var player = $( '#wp-as-' + player_id ).get(0);
+ var files = this[player_id].files;
+ if ( fromClick && ( player.paused || files.length-1 == this[player_id].i ) ) {
+ return;
+ }
+
+ player.pause();
+ if ( files.length-1 > this[player_id].i ) {
+ this[player_id].i++;
+ this.start_track( player_id, files[this[player_id].i] );
+ } else if ( loop ) {
+ this[player_id].i = 0;
+ this.start_track( player_id, 0 );
+ } else {
+ this[player_id].i = 0;
+ player.src = files[0];
+ $( '#wp-as-' + player_id + '-playing' ).text( '' );
+ }
+ }
+};
+
+})(jQuery);
diff --git a/plugins/jetpack/modules/shortcodes/js/jquery.cycle.js b/plugins/jetpack/modules/shortcodes/js/jquery.cycle.js
new file mode 100644
index 00000000..89d583eb
--- /dev/null
+++ b/plugins/jetpack/modules/shortcodes/js/jquery.cycle.js
@@ -0,0 +1,1551 @@
+/*!
+ * jQuery Cycle Plugin (with Transition Definitions)
+ * Examples and documentation at: http://jquery.malsup.com/cycle/
+ * Copyright (c) 2007-2010 M. Alsup
+ * Version: 2.9999.8 (26-OCT-2012)
+ * Dual licensed under the MIT and GPL licenses.
+ * http://jquery.malsup.com/license.html
+ * Requires: jQuery v1.3.2 or later
+ */
+;(function($, undefined) {
+"use strict";
+
+var ver = '2.9999.8';
+
+// if $.support is not defined (pre jQuery 1.3) add what I need
+if ($.support === undefined) {
+ $.support = {
+ opacity: !($.browser.msie)
+ };
+}
+
+function debug(s) {
+ if ($.fn.cycle.debug)
+ log(s);
+}
+function log() {
+ if (window.console && console.log)
+ console.log('[cycle] ' + Array.prototype.join.call(arguments,' '));
+}
+$.expr[':'].paused = function(el) {
+ return el.cyclePause;
+};
+
+
+// the options arg can be...
+// a number - indicates an immediate transition should occur to the given slide index
+// a string - 'pause', 'resume', 'toggle', 'next', 'prev', 'stop', 'destroy' or the name of a transition effect (ie, 'fade', 'zoom', etc)
+// an object - properties to control the slideshow
+//
+// the arg2 arg can be...
+// the name of an fx (only used in conjunction with a numeric value for 'options')
+// the value true (only used in first arg == 'resume') and indicates
+// that the resume should occur immediately (not wait for next timeout)
+
+$.fn.cycle = function(options, arg2) {
+ var o = { s: this.selector, c: this.context };
+
+ // in 1.3+ we can fix mistakes with the ready state
+ if (this.length === 0 && options != 'stop') {
+ if (!$.isReady && o.s) {
+ log('DOM not ready, queuing slideshow');
+ $(function() {
+ $(o.s,o.c).cycle(options,arg2);
+ });
+ return this;
+ }
+ // is your DOM ready? http://docs.jquery.com/Tutorials:Introducing_$(document).ready()
+ log('terminating; zero elements found by selector' + ($.isReady ? '' : ' (DOM not ready)'));
+ return this;
+ }
+
+ // iterate the matched nodeset
+ return this.each(function() {
+ var opts = handleArguments(this, options, arg2);
+ if (opts === false)
+ return;
+
+ opts.updateActivePagerLink = opts.updateActivePagerLink || $.fn.cycle.updateActivePagerLink;
+
+ // stop existing slideshow for this container (if there is one)
+ if (this.cycleTimeout)
+ clearTimeout(this.cycleTimeout);
+ this.cycleTimeout = this.cyclePause = 0;
+ this.cycleStop = 0; // issue #108
+
+ var $cont = $(this);
+ var $slides = opts.slideExpr ? $(opts.slideExpr, this) : $cont.children();
+ var els = $slides.get();
+
+ if (els.length < 2) {
+ log('terminating; too few slides: ' + els.length);
+ return;
+ }
+
+ var opts2 = buildOptions($cont, $slides, els, opts, o);
+ if (opts2 === false)
+ return;
+
+ var startTime = opts2.continuous ? 10 : getTimeout(els[opts2.currSlide], els[opts2.nextSlide], opts2, !opts2.backwards);
+
+ // if it's an auto slideshow, kick it off
+ if (startTime) {
+ startTime += (opts2.delay || 0);
+ if (startTime < 10)
+ startTime = 10;
+ debug('first timeout: ' + startTime);
+ this.cycleTimeout = setTimeout(function(){go(els,opts2,0,!opts.backwards);}, startTime);
+ }
+ });
+};
+
+function triggerPause(cont, byHover, onPager) {
+ var opts = $(cont).data('cycle.opts');
+ if (!opts)
+ return;
+ var paused = !!cont.cyclePause;
+ if (paused && opts.paused)
+ opts.paused(cont, opts, byHover, onPager);
+ else if (!paused && opts.resumed)
+ opts.resumed(cont, opts, byHover, onPager);
+}
+
+// process the args that were passed to the plugin fn
+function handleArguments(cont, options, arg2) {
+ if (cont.cycleStop === undefined)
+ cont.cycleStop = 0;
+ if (options === undefined || options === null)
+ options = {};
+ if (options.constructor == String) {
+ switch(options) {
+ case 'destroy':
+ case 'stop':
+ var opts = $(cont).data('cycle.opts');
+ if (!opts)
+ return false;
+ cont.cycleStop++; // callbacks look for change
+ if (cont.cycleTimeout)
+ clearTimeout(cont.cycleTimeout);
+ cont.cycleTimeout = 0;
+ if (opts.elements)
+ $(opts.elements).stop();
+ $(cont).removeData('cycle.opts');
+ if (options == 'destroy')
+ destroy(cont, opts);
+ return false;
+ case 'toggle':
+ cont.cyclePause = (cont.cyclePause === 1) ? 0 : 1;
+ checkInstantResume(cont.cyclePause, arg2, cont);
+ triggerPause(cont);
+ return false;
+ case 'pause':
+ cont.cyclePause = 1;
+ triggerPause(cont);
+ return false;
+ case 'resume':
+ cont.cyclePause = 0;
+ checkInstantResume(false, arg2, cont);
+ triggerPause(cont);
+ return false;
+ case 'prev':
+ case 'next':
+ opts = $(cont).data('cycle.opts');
+ if (!opts) {
+ log('options not found, "prev/next" ignored');
+ return false;
+ }
+ $.fn.cycle[options](opts);
+ return false;
+ default:
+ options = { fx: options };
+ }
+ return options;
+ }
+ else if (options.constructor == Number) {
+ // go to the requested slide
+ var num = options;
+ options = $(cont).data('cycle.opts');
+ if (!options) {
+ log('options not found, can not advance slide');
+ return false;
+ }
+ if (num < 0 || num >= options.elements.length) {
+ log('invalid slide index: ' + num);
+ return false;
+ }
+ options.nextSlide = num;
+ if (cont.cycleTimeout) {
+ clearTimeout(cont.cycleTimeout);
+ cont.cycleTimeout = 0;
+ }
+ if (typeof arg2 == 'string')
+ options.oneTimeFx = arg2;
+ go(options.elements, options, 1, num >= options.currSlide);
+ return false;
+ }
+ return options;
+
+ function checkInstantResume(isPaused, arg2, cont) {
+ if (!isPaused && arg2 === true) { // resume now!
+ var options = $(cont).data('cycle.opts');
+ if (!options) {
+ log('options not found, can not resume');
+ return false;
+ }
+ if (cont.cycleTimeout) {
+ clearTimeout(cont.cycleTimeout);
+ cont.cycleTimeout = 0;
+ }
+ go(options.elements, options, 1, !options.backwards);
+ }
+ }
+}
+
+function removeFilter(el, opts) {
+ if (!$.support.opacity && opts.cleartype && el.style.filter) {
+ try { el.style.removeAttribute('filter'); }
+ catch(smother) {} // handle old opera versions
+ }
+}
+
+// unbind event handlers
+function destroy(cont, opts) {
+ if (opts.next)
+ $(opts.next).unbind(opts.prevNextEvent);
+ if (opts.prev)
+ $(opts.prev).unbind(opts.prevNextEvent);
+
+ if (opts.pager || opts.pagerAnchorBuilder)
+ $.each(opts.pagerAnchors || [], function() {
+ this.unbind().remove();
+ });
+ opts.pagerAnchors = null;
+ $(cont).unbind('mouseenter.cycle mouseleave.cycle');
+ if (opts.destroy) // callback
+ opts.destroy(opts);
+}
+
+// one-time initialization
+function buildOptions($cont, $slides, els, options, o) {
+ var startingSlideSpecified;
+ // support metadata plugin (v1.0 and v2.0)
+ var opts = $.extend({}, $.fn.cycle.defaults, options || {}, $.metadata ? $cont.metadata() : $.meta ? $cont.data() : {});
+ var meta = $.isFunction($cont.data) ? $cont.data(opts.metaAttr) : null;
+ if (meta)
+ opts = $.extend(opts, meta);
+ if (opts.autostop)
+ opts.countdown = opts.autostopCount || els.length;
+
+ var cont = $cont[0];
+ $cont.data('cycle.opts', opts);
+ opts.$cont = $cont;
+ opts.stopCount = cont.cycleStop;
+ opts.elements = els;
+ opts.before = opts.before ? [opts.before] : [];
+ opts.after = opts.after ? [opts.after] : [];
+
+ // push some after callbacks
+ if (!$.support.opacity && opts.cleartype)
+ opts.after.push(function() { removeFilter(this, opts); });
+ if (opts.continuous)
+ opts.after.push(function() { go(els,opts,0,!opts.backwards); });
+
+ saveOriginalOpts(opts);
+
+ // clearType corrections
+ if (!$.support.opacity && opts.cleartype && !opts.cleartypeNoBg)
+ clearTypeFix($slides);
+
+ // container requires non-static position so that slides can be position within
+ if ($cont.css('position') == 'static')
+ $cont.css('position', 'relative');
+ if (opts.width)
+ $cont.width(opts.width);
+ if (opts.height && opts.height != 'auto')
+ $cont.height(opts.height);
+
+ if (opts.startingSlide !== undefined) {
+ opts.startingSlide = parseInt(opts.startingSlide,10);
+ if (opts.startingSlide >= els.length || opts.startSlide < 0)
+ opts.startingSlide = 0; // catch bogus input
+ else
+ startingSlideSpecified = true;
+ }
+ else if (opts.backwards)
+ opts.startingSlide = els.length - 1;
+ else
+ opts.startingSlide = 0;
+
+ // if random, mix up the slide array
+ if (opts.random) {
+ opts.randomMap = [];
+ for (var i = 0; i < els.length; i++)
+ opts.randomMap.push(i);
+ opts.randomMap.sort(function(a,b) {return Math.random() - 0.5;});
+ if (startingSlideSpecified) {
+ // try to find the specified starting slide and if found set start slide index in the map accordingly
+ for ( var cnt = 0; cnt < els.length; cnt++ ) {
+ if ( opts.startingSlide == opts.randomMap[cnt] ) {
+ opts.randomIndex = cnt;
+ }
+ }
+ }
+ else {
+ opts.randomIndex = 1;
+ opts.startingSlide = opts.randomMap[1];
+ }
+ }
+ else if (opts.startingSlide >= els.length)
+ opts.startingSlide = 0; // catch bogus input
+ opts.currSlide = opts.startingSlide || 0;
+ var first = opts.startingSlide;
+
+ // set position and zIndex on all the slides
+ $slides.css({position: 'absolute', top:0, left:0}).hide().each(function(i) {
+ var z;
+ if (opts.backwards)
+ z = first ? i <= first ? els.length + (i-first) : first-i : els.length-i;
+ else
+ z = first ? i >= first ? els.length - (i-first) : first-i : els.length-i;
+ $(this).css('z-index', z);
+ });
+
+ // make sure first slide is visible
+ $(els[first]).css('opacity',1).show(); // opacity bit needed to handle restart use case
+ removeFilter(els[first], opts);
+
+ // stretch slides
+ if (opts.fit) {
+ if (!opts.aspect) {
+ if (opts.width)
+ $slides.width(opts.width);
+ if (opts.height && opts.height != 'auto')
+ $slides.height(opts.height);
+ } else {
+ $slides.each(function(){
+ var $slide = $(this);
+ var ratio = (opts.aspect === true) ? $slide.width()/$slide.height() : opts.aspect;
+ if( opts.width && $slide.width() != opts.width ) {
+ $slide.width( opts.width );
+ $slide.height( opts.width / ratio );
+ }
+
+ if( opts.height && $slide.height() < opts.height ) {
+ $slide.height( opts.height );
+ $slide.width( opts.height * ratio );
+ }
+ });
+ }
+ }
+
+ if (opts.center && ((!opts.fit) || opts.aspect)) {
+ $slides.each(function(){
+ var $slide = $(this);
+ $slide.css({
+ "margin-left": opts.width ?
+ ((opts.width - $slide.width()) / 2) + "px" :
+ 0,
+ "margin-top": opts.height ?
+ ((opts.height - $slide.height()) / 2) + "px" :
+ 0
+ });
+ });
+ }
+
+ if (opts.center && !opts.fit && !opts.slideResize) {
+ $slides.each(function(){
+ var $slide = $(this);
+ $slide.css({
+ "margin-left": opts.width ? ((opts.width - $slide.width()) / 2) + "px" : 0,
+ "margin-top": opts.height ? ((opts.height - $slide.height()) / 2) + "px" : 0
+ });
+ });
+ }
+
+ // stretch container
+ var reshape = (opts.containerResize || opts.containerResizeHeight) && !$cont.innerHeight();
+ if (reshape) { // do this only if container has no size http://tinyurl.com/da2oa9
+ var maxw = 0, maxh = 0;
+ for(var j=0; j < els.length; j++) {
+ var $e = $(els[j]), e = $e[0], w = $e.outerWidth(), h = $e.outerHeight();
+ if (!w) w = e.offsetWidth || e.width || $e.attr('width');
+ if (!h) h = e.offsetHeight || e.height || $e.attr('height');
+ maxw = w > maxw ? w : maxw;
+ maxh = h > maxh ? h : maxh;
+ }
+ if (opts.containerResize && maxw > 0 && maxh > 0)
+ $cont.css({width:maxw+'px',height:maxh+'px'});
+ if (opts.containerResizeHeight && maxh > 0)
+ $cont.css({height:maxh+'px'});
+ }
+
+ var pauseFlag = false; // https://github.com/malsup/cycle/issues/44
+ if (opts.pause)
+ $cont.bind('mouseenter.cycle', function(){
+ pauseFlag = true;
+ this.cyclePause++;
+ triggerPause(cont, true);
+ }).bind('mouseleave.cycle', function(){
+ if (pauseFlag)
+ this.cyclePause--;
+ triggerPause(cont, true);
+ });
+
+ if (supportMultiTransitions(opts) === false)
+ return false;
+
+ // apparently a lot of people use image slideshows without height/width attributes on the images.
+ // Cycle 2.50+ requires the sizing info for every slide; this block tries to deal with that.
+ var requeue = false;
+ options.requeueAttempts = options.requeueAttempts || 0;
+ $slides.each(function() {
+ // try to get height/width of each slide
+ var $el = $(this);
+ this.cycleH = (opts.fit && opts.height) ? opts.height : ($el.height() || this.offsetHeight || this.height || $el.attr('height') || 0);
+ this.cycleW = (opts.fit && opts.width) ? opts.width : ($el.width() || this.offsetWidth || this.width || $el.attr('width') || 0);
+
+ if ( $el.is('img') ) {
+ // sigh.. sniffing, hacking, shrugging... this crappy hack tries to account for what browsers do when
+ // an image is being downloaded and the markup did not include sizing info (height/width attributes);
+ // there seems to be some "default" sizes used in this situation
+ var loadingIE = ($.browser.msie && this.cycleW == 28 && this.cycleH == 30 && !this.complete);
+ var loadingFF = ($.browser.mozilla && this.cycleW == 34 && this.cycleH == 19 && !this.complete);
+ var loadingOp = ($.browser.opera && ((this.cycleW == 42 && this.cycleH == 19) || (this.cycleW == 37 && this.cycleH == 17)) && !this.complete);
+ var loadingOther = (this.cycleH === 0 && this.cycleW === 0 && !this.complete);
+ // don't requeue for images that are still loading but have a valid size
+ if (loadingIE || loadingFF || loadingOp || loadingOther) {
+ if (o.s && opts.requeueOnImageNotLoaded && ++options.requeueAttempts < 100) { // track retry count so we don't loop forever
+ log(options.requeueAttempts,' - img slide not loaded, requeuing slideshow: ', this.src, this.cycleW, this.cycleH);
+ setTimeout(function() {$(o.s,o.c).cycle(options);}, opts.requeueTimeout);
+ requeue = true;
+ return false; // break each loop
+ }
+ else {
+ log('could not determine size of image: '+this.src, this.cycleW, this.cycleH);
+ }
+ }
+ }
+ return true;
+ });
+
+ if (requeue)
+ return false;
+
+ opts.cssBefore = opts.cssBefore || {};
+ opts.cssAfter = opts.cssAfter || {};
+ opts.cssFirst = opts.cssFirst || {};
+ opts.animIn = opts.animIn || {};
+ opts.animOut = opts.animOut || {};
+
+ $slides.not(':eq('+first+')').css(opts.cssBefore);
+ $($slides[first]).css(opts.cssFirst);
+
+ if (opts.timeout) {
+ opts.timeout = parseInt(opts.timeout,10);
+ // ensure that timeout and speed settings are sane
+ if (opts.speed.constructor == String)
+ opts.speed = $.fx.speeds[opts.speed] || parseInt(opts.speed,10);
+ if (!opts.sync)
+ opts.speed = opts.speed / 2;
+
+ var buffer = opts.fx == 'none' ? 0 : opts.fx == 'shuffle' ? 500 : 250;
+ while((opts.timeout - opts.speed) < buffer) // sanitize timeout
+ opts.timeout += opts.speed;
+ }
+ if (opts.easing)
+ opts.easeIn = opts.easeOut = opts.easing;
+ if (!opts.speedIn)
+ opts.speedIn = opts.speed;
+ if (!opts.speedOut)
+ opts.speedOut = opts.speed;
+
+ opts.slideCount = els.length;
+ opts.currSlide = opts.lastSlide = first;
+ if (opts.random) {
+ if (++opts.randomIndex == els.length)
+ opts.randomIndex = 0;
+ opts.nextSlide = opts.randomMap[opts.randomIndex];
+ }
+ else if (opts.backwards)
+ opts.nextSlide = opts.startingSlide === 0 ? (els.length-1) : opts.startingSlide-1;
+ else
+ opts.nextSlide = opts.startingSlide >= (els.length-1) ? 0 : opts.startingSlide+1;
+
+ // run transition init fn
+ if (!opts.multiFx) {
+ var init = $.fn.cycle.transitions[opts.fx];
+ if ($.isFunction(init))
+ init($cont, $slides, opts);
+ else if (opts.fx != 'custom' && !opts.multiFx) {
+ log('unknown transition: ' + opts.fx,'; slideshow terminating');
+ return false;
+ }
+ }
+
+ // fire artificial events
+ var e0 = $slides[first];
+ if (!opts.skipInitializationCallbacks) {
+ if (opts.before.length)
+ opts.before[0].apply(e0, [e0, e0, opts, true]);
+ if (opts.after.length)
+ opts.after[0].apply(e0, [e0, e0, opts, true]);
+ }
+ if (opts.next)
+ $(opts.next).bind(opts.prevNextEvent,function(){return advance(opts,1);});
+ if (opts.prev)
+ $(opts.prev).bind(opts.prevNextEvent,function(){return advance(opts,0);});
+ if (opts.pager || opts.pagerAnchorBuilder)
+ buildPager(els,opts);
+
+ exposeAddSlide(opts, els);
+
+ return opts;
+}
+
+// save off original opts so we can restore after clearing state
+function saveOriginalOpts(opts) {
+ opts.original = { before: [], after: [] };
+ opts.original.cssBefore = $.extend({}, opts.cssBefore);
+ opts.original.cssAfter = $.extend({}, opts.cssAfter);
+ opts.original.animIn = $.extend({}, opts.animIn);
+ opts.original.animOut = $.extend({}, opts.animOut);
+ $.each(opts.before, function() { opts.original.before.push(this); });
+ $.each(opts.after, function() { opts.original.after.push(this); });
+}
+
+function supportMultiTransitions(opts) {
+ var i, tx, txs = $.fn.cycle.transitions;
+ // look for multiple effects
+ if (opts.fx.indexOf(',') > 0) {
+ opts.multiFx = true;
+ opts.fxs = opts.fx.replace(/\s*/g,'').split(',');
+ // discard any bogus effect names
+ for (i=0; i < opts.fxs.length; i++) {
+ var fx = opts.fxs[i];
+ tx = txs[fx];
+ if (!tx || !txs.hasOwnProperty(fx) || !$.isFunction(tx)) {
+ log('discarding unknown transition: ',fx);
+ opts.fxs.splice(i,1);
+ i--;
+ }
+ }
+ // if we have an empty list then we threw everything away!
+ if (!opts.fxs.length) {
+ log('No valid transitions named; slideshow terminating.');
+ return false;
+ }
+ }
+ else if (opts.fx == 'all') { // auto-gen the list of transitions
+ opts.multiFx = true;
+ opts.fxs = [];
+ for (var p in txs) {
+ if (txs.hasOwnProperty(p)) {
+ tx = txs[p];
+ if (txs.hasOwnProperty(p) && $.isFunction(tx))
+ opts.fxs.push(p);
+ }
+ }
+ }
+ if (opts.multiFx && opts.randomizeEffects) {
+ // munge the fxs array to make effect selection random
+ var r1 = Math.floor(Math.random() * 20) + 30;
+ for (i = 0; i < r1; i++) {
+ var r2 = Math.floor(Math.random() * opts.fxs.length);
+ opts.fxs.push(opts.fxs.splice(r2,1)[0]);
+ }
+ debug('randomized fx sequence: ',opts.fxs);
+ }
+ return true;
+}
+
+// provide a mechanism for adding slides after the slideshow has started
+function exposeAddSlide(opts, els) {
+ opts.addSlide = function(newSlide, prepend) {
+ var $s = $(newSlide), s = $s[0];
+ if (!opts.autostopCount)
+ opts.countdown++;
+ els[prepend?'unshift':'push'](s);
+ if (opts.els)
+ opts.els[prepend?'unshift':'push'](s); // shuffle needs this
+ opts.slideCount = els.length;
+
+ // add the slide to the random map and resort
+ if (opts.random) {
+ opts.randomMap.push(opts.slideCount-1);
+ opts.randomMap.sort(function(a,b) {return Math.random() - 0.5;});
+ }
+
+ $s.css('position','absolute');
+ $s[prepend?'prependTo':'appendTo'](opts.$cont);
+
+ if (prepend) {
+ opts.currSlide++;
+ opts.nextSlide++;
+ }
+
+ if (!$.support.opacity && opts.cleartype && !opts.cleartypeNoBg)
+ clearTypeFix($s);
+
+ if (opts.fit && opts.width)
+ $s.width(opts.width);
+ if (opts.fit && opts.height && opts.height != 'auto')
+ $s.height(opts.height);
+ s.cycleH = (opts.fit && opts.height) ? opts.height : $s.height();
+ s.cycleW = (opts.fit && opts.width) ? opts.width : $s.width();
+
+ $s.css(opts.cssBefore);
+
+ if (opts.pager || opts.pagerAnchorBuilder)
+ $.fn.cycle.createPagerAnchor(els.length-1, s, $(opts.pager), els, opts);
+
+ if ($.isFunction(opts.onAddSlide))
+ opts.onAddSlide($s);
+ else
+ $s.hide(); // default behavior
+ };
+}
+
+// reset internal state; we do this on every pass in order to support multiple effects
+$.fn.cycle.resetState = function(opts, fx) {
+ fx = fx || opts.fx;
+ opts.before = []; opts.after = [];
+ opts.cssBefore = $.extend({}, opts.original.cssBefore);
+ opts.cssAfter = $.extend({}, opts.original.cssAfter);
+ opts.animIn = $.extend({}, opts.original.animIn);
+ opts.animOut = $.extend({}, opts.original.animOut);
+ opts.fxFn = null;
+ $.each(opts.original.before, function() { opts.before.push(this); });
+ $.each(opts.original.after, function() { opts.after.push(this); });
+
+ // re-init
+ var init = $.fn.cycle.transitions[fx];
+ if ($.isFunction(init))
+ init(opts.$cont, $(opts.elements), opts);
+};
+
+// this is the main engine fn, it handles the timeouts, callbacks and slide index mgmt
+function go(els, opts, manual, fwd) {
+ var p = opts.$cont[0], curr = els[opts.currSlide], next = els[opts.nextSlide];
+
+ // opts.busy is true if we're in the middle of an animation
+ if (manual && opts.busy && opts.manualTrump) {
+ // let manual transitions requests trump active ones
+ debug('manualTrump in go(), stopping active transition');
+ $(els).stop(true,true);
+ opts.busy = 0;
+ clearTimeout(p.cycleTimeout);
+ }
+
+ // don't begin another timeout-based transition if there is one active
+ if (opts.busy) {
+ debug('transition active, ignoring new tx request');
+ return;
+ }
+
+
+ // stop cycling if we have an outstanding stop request
+ if (p.cycleStop != opts.stopCount || p.cycleTimeout === 0 && !manual)
+ return;
+
+ // check to see if we should stop cycling based on autostop options
+ if (!manual && !p.cyclePause && !opts.bounce &&
+ ((opts.autostop && (--opts.countdown <= 0)) ||
+ (opts.nowrap && !opts.random && opts.nextSlide < opts.currSlide))) {
+ if (opts.end)
+ opts.end(opts);
+ return;
+ }
+
+ // if slideshow is paused, only transition on a manual trigger
+ var changed = false;
+ if ((manual || !p.cyclePause) && (opts.nextSlide != opts.currSlide)) {
+ changed = true;
+ var fx = opts.fx;
+ // keep trying to get the slide size if we don't have it yet
+ curr.cycleH = curr.cycleH || $(curr).height();
+ curr.cycleW = curr.cycleW || $(curr).width();
+ next.cycleH = next.cycleH || $(next).height();
+ next.cycleW = next.cycleW || $(next).width();
+
+ // support multiple transition types
+ if (opts.multiFx) {
+ if (fwd && (opts.lastFx === undefined || ++opts.lastFx >= opts.fxs.length))
+ opts.lastFx = 0;
+ else if (!fwd && (opts.lastFx === undefined || --opts.lastFx < 0))
+ opts.lastFx = opts.fxs.length - 1;
+ fx = opts.fxs[opts.lastFx];
+ }
+
+ // one-time fx overrides apply to: $('div').cycle(3,'zoom');
+ if (opts.oneTimeFx) {
+ fx = opts.oneTimeFx;
+ opts.oneTimeFx = null;
+ }
+
+ $.fn.cycle.resetState(opts, fx);
+
+ // run the before callbacks
+ if (opts.before.length)
+ $.each(opts.before, function(i,o) {
+ if (p.cycleStop != opts.stopCount) return;
+ o.apply(next, [curr, next, opts, fwd]);
+ });
+
+ // stage the after callacks
+ var after = function() {
+ opts.busy = 0;
+ $.each(opts.after, function(i,o) {
+ if (p.cycleStop != opts.stopCount) return;
+ o.apply(next, [curr, next, opts, fwd]);
+ });
+ if (!p.cycleStop) {
+ // queue next transition
+ queueNext();
+ }
+ };
+
+ debug('tx firing('+fx+'); currSlide: ' + opts.currSlide + '; nextSlide: ' + opts.nextSlide);
+
+ // get ready to perform the transition
+ opts.busy = 1;
+ if (opts.fxFn) // fx function provided?
+ opts.fxFn(curr, next, opts, after, fwd, manual && opts.fastOnEvent);
+ else if ($.isFunction($.fn.cycle[opts.fx])) // fx plugin ?
+ $.fn.cycle[opts.fx](curr, next, opts, after, fwd, manual && opts.fastOnEvent);
+ else
+ $.fn.cycle.custom(curr, next, opts, after, fwd, manual && opts.fastOnEvent);
+ }
+ else {
+ queueNext();
+ }
+
+ if (changed || opts.nextSlide == opts.currSlide) {
+ // calculate the next slide
+ var roll;
+ opts.lastSlide = opts.currSlide;
+ if (opts.random) {
+ opts.currSlide = opts.nextSlide;
+ if (++opts.randomIndex == els.length) {
+ opts.randomIndex = 0;
+ opts.randomMap.sort(function(a,b) {return Math.random() - 0.5;});
+ }
+ opts.nextSlide = opts.randomMap[opts.randomIndex];
+ if (opts.nextSlide == opts.currSlide)
+ opts.nextSlide = (opts.currSlide == opts.slideCount - 1) ? 0 : opts.currSlide + 1;
+ }
+ else if (opts.backwards) {
+ roll = (opts.nextSlide - 1) < 0;
+ if (roll && opts.bounce) {
+ opts.backwards = !opts.backwards;
+ opts.nextSlide = 1;
+ opts.currSlide = 0;
+ }
+ else {
+ opts.nextSlide = roll ? (els.length-1) : opts.nextSlide-1;
+ opts.currSlide = roll ? 0 : opts.nextSlide+1;
+ }
+ }
+ else { // sequence
+ roll = (opts.nextSlide + 1) == els.length;
+ if (roll && opts.bounce) {
+ opts.backwards = !opts.backwards;
+ opts.nextSlide = els.length-2;
+ opts.currSlide = els.length-1;
+ }
+ else {
+ opts.nextSlide = roll ? 0 : opts.nextSlide+1;
+ opts.currSlide = roll ? els.length-1 : opts.nextSlide-1;
+ }
+ }
+ }
+ if (changed && opts.pager)
+ opts.updateActivePagerLink(opts.pager, opts.currSlide, opts.activePagerClass);
+
+ function queueNext() {
+ // stage the next transition
+ var ms = 0, timeout = opts.timeout;
+ if (opts.timeout && !opts.continuous) {
+ ms = getTimeout(els[opts.currSlide], els[opts.nextSlide], opts, fwd);
+ if (opts.fx == 'shuffle')
+ ms -= opts.speedOut;
+ }
+ else if (opts.continuous && p.cyclePause) // continuous shows work off an after callback, not this timer logic
+ ms = 10;
+ if (ms > 0)
+ p.cycleTimeout = setTimeout(function(){ go(els, opts, 0, !opts.backwards); }, ms);
+ }
+}
+
+// invoked after transition
+$.fn.cycle.updateActivePagerLink = function(pager, currSlide, clsName) {
+ $(pager).each(function() {
+ $(this).children().removeClass(clsName).eq(currSlide).addClass(clsName);
+ });
+};
+
+// calculate timeout value for current transition
+function getTimeout(curr, next, opts, fwd) {
+ if (opts.timeoutFn) {
+ // call user provided calc fn
+ var t = opts.timeoutFn.call(curr,curr,next,opts,fwd);
+ while (opts.fx != 'none' && (t - opts.speed) < 250) // sanitize timeout
+ t += opts.speed;
+ debug('calculated timeout: ' + t + '; speed: ' + opts.speed);
+ if (t !== false)
+ return t;
+ }
+ return opts.timeout;
+}
+
+// expose next/prev function, caller must pass in state
+$.fn.cycle.next = function(opts) { advance(opts,1); };
+$.fn.cycle.prev = function(opts) { advance(opts,0);};
+
+// advance slide forward or back
+function advance(opts, moveForward) {
+ var val = moveForward ? 1 : -1;
+ var els = opts.elements;
+ var p = opts.$cont[0], timeout = p.cycleTimeout;
+ if (timeout) {
+ clearTimeout(timeout);
+ p.cycleTimeout = 0;
+ }
+ if (opts.random && val < 0) {
+ // move back to the previously display slide
+ opts.randomIndex--;
+ if (--opts.randomIndex == -2)
+ opts.randomIndex = els.length-2;
+ else if (opts.randomIndex == -1)
+ opts.randomIndex = els.length-1;
+ opts.nextSlide = opts.randomMap[opts.randomIndex];
+ }
+ else if (opts.random) {
+ opts.nextSlide = opts.randomMap[opts.randomIndex];
+ }
+ else {
+ opts.nextSlide = opts.currSlide + val;
+ if (opts.nextSlide < 0) {
+ if (opts.nowrap) return false;
+ opts.nextSlide = els.length - 1;
+ }
+ else if (opts.nextSlide >= els.length) {
+ if (opts.nowrap) return false;
+ opts.nextSlide = 0;
+ }
+ }
+
+ var cb = opts.onPrevNextEvent || opts.prevNextClick; // prevNextClick is deprecated
+ if ($.isFunction(cb))
+ cb(val > 0, opts.nextSlide, els[opts.nextSlide]);
+ go(els, opts, 1, moveForward);
+ return false;
+}
+
+function buildPager(els, opts) {
+ var $p = $(opts.pager);
+ $.each(els, function(i,o) {
+ $.fn.cycle.createPagerAnchor(i,o,$p,els,opts);
+ });
+ opts.updateActivePagerLink(opts.pager, opts.startingSlide, opts.activePagerClass);
+}
+
+$.fn.cycle.createPagerAnchor = function(i, el, $p, els, opts) {
+ var a;
+ if ($.isFunction(opts.pagerAnchorBuilder)) {
+ a = opts.pagerAnchorBuilder(i,el);
+ debug('pagerAnchorBuilder('+i+', el) returned: ' + a);
+ }
+ else
+ a = '<a href="#">'+(i+1)+'</a>';
+
+ if (!a)
+ return;
+ var $a = $(a);
+ // don't reparent if anchor is in the dom
+ if ($a.parents('body').length === 0) {
+ var arr = [];
+ if ($p.length > 1) {
+ $p.each(function() {
+ var $clone = $a.clone(true);
+ $(this).append($clone);
+ arr.push($clone[0]);
+ });
+ $a = $(arr);
+ }
+ else {
+ $a.appendTo($p);
+ }
+ }
+
+ opts.pagerAnchors = opts.pagerAnchors || [];
+ opts.pagerAnchors.push($a);
+
+ var pagerFn = function(e) {
+ e.preventDefault();
+ opts.nextSlide = i;
+ var p = opts.$cont[0], timeout = p.cycleTimeout;
+ if (timeout) {
+ clearTimeout(timeout);
+ p.cycleTimeout = 0;
+ }
+ var cb = opts.onPagerEvent || opts.pagerClick; // pagerClick is deprecated
+ if ($.isFunction(cb))
+ cb(opts.nextSlide, els[opts.nextSlide]);
+ go(els,opts,1,opts.currSlide < i); // trigger the trans
+// return false; // <== allow bubble
+ };
+
+ if ( /mouseenter|mouseover/i.test(opts.pagerEvent) ) {
+ $a.hover(pagerFn, function(){/* no-op */} );
+ }
+ else {
+ $a.bind(opts.pagerEvent, pagerFn);
+ }
+
+ if ( ! /^click/.test(opts.pagerEvent) && !opts.allowPagerClickBubble)
+ $a.bind('click.cycle', function(){return false;}); // suppress click
+
+ var cont = opts.$cont[0];
+ var pauseFlag = false; // https://github.com/malsup/cycle/issues/44
+ if (opts.pauseOnPagerHover) {
+ $a.hover(
+ function() {
+ pauseFlag = true;
+ cont.cyclePause++;
+ triggerPause(cont,true,true);
+ }, function() {
+ if (pauseFlag)
+ cont.cyclePause--;
+ triggerPause(cont,true,true);
+ }
+ );
+ }
+};
+
+// helper fn to calculate the number of slides between the current and the next
+$.fn.cycle.hopsFromLast = function(opts, fwd) {
+ var hops, l = opts.lastSlide, c = opts.currSlide;
+ if (fwd)
+ hops = c > l ? c - l : opts.slideCount - l;
+ else
+ hops = c < l ? l - c : l + opts.slideCount - c;
+ return hops;
+};
+
+// fix clearType problems in ie6 by setting an explicit bg color
+// (otherwise text slides look horrible during a fade transition)
+function clearTypeFix($slides) {
+ debug('applying clearType background-color hack');
+ function hex(s) {
+ s = parseInt(s,10).toString(16);
+ return s.length < 2 ? '0'+s : s;
+ }
+ function getBg(e) {
+ for ( ; e && e.nodeName.toLowerCase() != 'html'; e = e.parentNode) {
+ var v = $.css(e,'background-color');
+ if (v && v.indexOf('rgb') >= 0 ) {
+ var rgb = v.match(/\d+/g);
+ return '#'+ hex(rgb[0]) + hex(rgb[1]) + hex(rgb[2]);
+ }
+ if (v && v != 'transparent')
+ return v;
+ }
+ return '#ffffff';
+ }
+ $slides.each(function() { $(this).css('background-color', getBg(this)); });
+}
+
+// reset common props before the next transition
+$.fn.cycle.commonReset = function(curr,next,opts,w,h,rev) {
+ $(opts.elements).not(curr).hide();
+ if (typeof opts.cssBefore.opacity == 'undefined')
+ opts.cssBefore.opacity = 1;
+ opts.cssBefore.display = 'block';
+ if (opts.slideResize && w !== false && next.cycleW > 0)
+ opts.cssBefore.width = next.cycleW;
+ if (opts.slideResize && h !== false && next.cycleH > 0)
+ opts.cssBefore.height = next.cycleH;
+ opts.cssAfter = opts.cssAfter || {};
+ opts.cssAfter.display = 'none';
+ $(curr).css('zIndex',opts.slideCount + (rev === true ? 1 : 0));
+ $(next).css('zIndex',opts.slideCount + (rev === true ? 0 : 1));
+};
+
+// the actual fn for effecting a transition
+$.fn.cycle.custom = function(curr, next, opts, cb, fwd, speedOverride) {
+ var $l = $(curr), $n = $(next);
+ var speedIn = opts.speedIn, speedOut = opts.speedOut, easeIn = opts.easeIn, easeOut = opts.easeOut;
+ $n.css(opts.cssBefore);
+ if (speedOverride) {
+ if (typeof speedOverride == 'number')
+ speedIn = speedOut = speedOverride;
+ else
+ speedIn = speedOut = 1;
+ easeIn = easeOut = null;
+ }
+ var fn = function() {
+ $n.animate(opts.animIn, speedIn, easeIn, function() {
+ cb();
+ });
+ };
+ $l.animate(opts.animOut, speedOut, easeOut, function() {
+ $l.css(opts.cssAfter);
+ if (!opts.sync)
+ fn();
+ });
+ if (opts.sync) fn();
+};
+
+// transition definitions - only fade is defined here, transition pack defines the rest
+$.fn.cycle.transitions = {
+ fade: function($cont, $slides, opts) {
+ $slides.not(':eq('+opts.currSlide+')').css('opacity',0);
+ opts.before.push(function(curr,next,opts) {
+ $.fn.cycle.commonReset(curr,next,opts);
+ opts.cssBefore.opacity = 0;
+ });
+ opts.animIn = { opacity: 1 };
+ opts.animOut = { opacity: 0 };
+ opts.cssBefore = { top: 0, left: 0 };
+ }
+};
+
+$.fn.cycle.ver = function() { return ver; };
+
+// override these globally if you like (they are all optional)
+$.fn.cycle.defaults = {
+ activePagerClass: 'activeSlide', // class name used for the active pager link
+ after: null, // transition callback (scope set to element that was shown): function(currSlideElement, nextSlideElement, options, forwardFlag)
+ allowPagerClickBubble: false, // allows or prevents click event on pager anchors from bubbling
+ animIn: null, // properties that define how the slide animates in
+ animOut: null, // properties that define how the slide animates out
+ aspect: false, // preserve aspect ratio during fit resizing, cropping if necessary (must be used with fit option)
+ autostop: 0, // true to end slideshow after X transitions (where X == slide count)
+ autostopCount: 0, // number of transitions (optionally used with autostop to define X)
+ backwards: false, // true to start slideshow at last slide and move backwards through the stack
+ before: null, // transition callback (scope set to element to be shown): function(currSlideElement, nextSlideElement, options, forwardFlag)
+ center: null, // set to true to have cycle add top/left margin to each slide (use with width and height options)
+ cleartype: !$.support.opacity, // true if clearType corrections should be applied (for IE)
+ cleartypeNoBg: false, // set to true to disable extra cleartype fixing (leave false to force background color setting on slides)
+ containerResize: 1, // resize container to fit largest slide
+ containerResizeHeight: 0, // resize containers height to fit the largest slide but leave the width dynamic
+ continuous: 0, // true to start next transition immediately after current one completes
+ cssAfter: null, // properties that defined the state of the slide after transitioning out
+ cssBefore: null, // properties that define the initial state of the slide before transitioning in
+ delay: 0, // additional delay (in ms) for first transition (hint: can be negative)
+ easeIn: null, // easing for "in" transition
+ easeOut: null, // easing for "out" transition
+ easing: null, // easing method for both in and out transitions
+ end: null, // callback invoked when the slideshow terminates (use with autostop or nowrap options): function(options)
+ fastOnEvent: 0, // force fast transitions when triggered manually (via pager or prev/next); value == time in ms
+ fit: 0, // force slides to fit container
+ fx: 'fade', // name of transition effect (or comma separated names, ex: 'fade,scrollUp,shuffle')
+ fxFn: null, // function used to control the transition: function(currSlideElement, nextSlideElement, options, afterCalback, forwardFlag)
+ height: 'auto', // container height (if the 'fit' option is true, the slides will be set to this height as well)
+ manualTrump: true, // causes manual transition to stop an active transition instead of being ignored
+ metaAttr: 'cycle', // data- attribute that holds the option data for the slideshow
+ next: null, // element, jQuery object, or jQuery selector string for the element to use as event trigger for next slide
+ nowrap: 0, // true to prevent slideshow from wrapping
+ onPagerEvent: null, // callback fn for pager events: function(zeroBasedSlideIndex, slideElement)
+ onPrevNextEvent: null, // callback fn for prev/next events: function(isNext, zeroBasedSlideIndex, slideElement)
+ pager: null, // element, jQuery object, or jQuery selector string for the element to use as pager container
+ pagerAnchorBuilder: null, // callback fn for building anchor links: function(index, DOMelement)
+ pagerEvent: 'click.cycle', // name of event which drives the pager navigation
+ pause: 0, // true to enable "pause on hover"
+ pauseOnPagerHover: 0, // true to pause when hovering over pager link
+ prev: null, // element, jQuery object, or jQuery selector string for the element to use as event trigger for previous slide
+ prevNextEvent: 'click.cycle',// event which drives the manual transition to the previous or next slide
+ random: 0, // true for random, false for sequence (not applicable to shuffle fx)
+ randomizeEffects: 1, // valid when multiple effects are used; true to make the effect sequence random
+ requeueOnImageNotLoaded: true, // requeue the slideshow if any image slides are not yet loaded
+ requeueTimeout: 250, // ms delay for requeue
+ rev: 0, // causes animations to transition in reverse (for effects that support it such as scrollHorz/scrollVert/shuffle)
+ shuffle: null, // coords for shuffle animation, ex: { top:15, left: 200 }
+ skipInitializationCallbacks: false, // set to true to disable the first before/after callback that occurs prior to any transition
+ slideExpr: null, // expression for selecting slides (if something other than all children is required)
+ slideResize: 1, // force slide width/height to fixed size before every transition
+ speed: 1000, // speed of the transition (any valid fx speed value)
+ speedIn: null, // speed of the 'in' transition
+ speedOut: null, // speed of the 'out' transition
+ startingSlide: undefined,// zero-based index of the first slide to be displayed
+ sync: 1, // true if in/out transitions should occur simultaneously
+ timeout: 4000, // milliseconds between slide transitions (0 to disable auto advance)
+ timeoutFn: null, // callback for determining per-slide timeout value: function(currSlideElement, nextSlideElement, options, forwardFlag)
+ updateActivePagerLink: null,// callback fn invoked to update the active pager link (adds/removes activePagerClass style)
+ width: null // container width (if the 'fit' option is true, the slides will be set to this width as well)
+};
+
+})(jQuery);
+
+
+/*!
+ * jQuery Cycle Plugin Transition Definitions
+ * This script is a plugin for the jQuery Cycle Plugin
+ * Examples and documentation at: http://malsup.com/jquery/cycle/
+ * Copyright (c) 2007-2010 M. Alsup
+ * Version: 2.73
+ * Dual licensed under the MIT and GPL licenses:
+ * http://www.opensource.org/licenses/mit-license.php
+ * http://www.gnu.org/licenses/gpl.html
+ */
+(function($) {
+"use strict";
+
+//
+// These functions define slide initialization and properties for the named
+// transitions. To save file size feel free to remove any of these that you
+// don't need.
+//
+$.fn.cycle.transitions.none = function($cont, $slides, opts) {
+ opts.fxFn = function(curr,next,opts,after){
+ $(next).show();
+ $(curr).hide();
+ after();
+ };
+};
+
+// not a cross-fade, fadeout only fades out the top slide
+$.fn.cycle.transitions.fadeout = function($cont, $slides, opts) {
+ $slides.not(':eq('+opts.currSlide+')').css({ display: 'block', 'opacity': 1 });
+ opts.before.push(function(curr,next,opts,w,h,rev) {
+ $(curr).css('zIndex',opts.slideCount + (rev !== true ? 1 : 0));
+ $(next).css('zIndex',opts.slideCount + (rev !== true ? 0 : 1));
+ });
+ opts.animIn.opacity = 1;
+ opts.animOut.opacity = 0;
+ opts.cssBefore.opacity = 1;
+ opts.cssBefore.display = 'block';
+ opts.cssAfter.zIndex = 0;
+};
+
+// scrollUp/Down/Left/Right
+$.fn.cycle.transitions.scrollUp = function($cont, $slides, opts) {
+ $cont.css('overflow','hidden');
+ opts.before.push($.fn.cycle.commonReset);
+ var h = $cont.height();
+ opts.cssBefore.top = h;
+ opts.cssBefore.left = 0;
+ opts.cssFirst.top = 0;
+ opts.animIn.top = 0;
+ opts.animOut.top = -h;
+};
+$.fn.cycle.transitions.scrollDown = function($cont, $slides, opts) {
+ $cont.css('overflow','hidden');
+ opts.before.push($.fn.cycle.commonReset);
+ var h = $cont.height();
+ opts.cssFirst.top = 0;
+ opts.cssBefore.top = -h;
+ opts.cssBefore.left = 0;
+ opts.animIn.top = 0;
+ opts.animOut.top = h;
+};
+$.fn.cycle.transitions.scrollLeft = function($cont, $slides, opts) {
+ $cont.css('overflow','hidden');
+ opts.before.push($.fn.cycle.commonReset);
+ var w = $cont.width();
+ opts.cssFirst.left = 0;
+ opts.cssBefore.left = w;
+ opts.cssBefore.top = 0;
+ opts.animIn.left = 0;
+ opts.animOut.left = 0-w;
+};
+$.fn.cycle.transitions.scrollRight = function($cont, $slides, opts) {
+ $cont.css('overflow','hidden');
+ opts.before.push($.fn.cycle.commonReset);
+ var w = $cont.width();
+ opts.cssFirst.left = 0;
+ opts.cssBefore.left = -w;
+ opts.cssBefore.top = 0;
+ opts.animIn.left = 0;
+ opts.animOut.left = w;
+};
+$.fn.cycle.transitions.scrollHorz = function($cont, $slides, opts) {
+ $cont.css('overflow','hidden').width();
+ opts.before.push(function(curr, next, opts, fwd) {
+ if (opts.rev)
+ fwd = !fwd;
+ $.fn.cycle.commonReset(curr,next,opts);
+ opts.cssBefore.left = fwd ? (next.cycleW-1) : (1-next.cycleW);
+ opts.animOut.left = fwd ? -curr.cycleW : curr.cycleW;
+ });
+ opts.cssFirst.left = 0;
+ opts.cssBefore.top = 0;
+ opts.animIn.left = 0;
+ opts.animOut.top = 0;
+};
+$.fn.cycle.transitions.scrollVert = function($cont, $slides, opts) {
+ $cont.css('overflow','hidden');
+ opts.before.push(function(curr, next, opts, fwd) {
+ if (opts.rev)
+ fwd = !fwd;
+ $.fn.cycle.commonReset(curr,next,opts);
+ opts.cssBefore.top = fwd ? (1-next.cycleH) : (next.cycleH-1);
+ opts.animOut.top = fwd ? curr.cycleH : -curr.cycleH;
+ });
+ opts.cssFirst.top = 0;
+ opts.cssBefore.left = 0;
+ opts.animIn.top = 0;
+ opts.animOut.left = 0;
+};
+
+// slideX/slideY
+$.fn.cycle.transitions.slideX = function($cont, $slides, opts) {
+ opts.before.push(function(curr, next, opts) {
+ $(opts.elements).not(curr).hide();
+ $.fn.cycle.commonReset(curr,next,opts,false,true);
+ opts.animIn.width = next.cycleW;
+ });
+ opts.cssBefore.left = 0;
+ opts.cssBefore.top = 0;
+ opts.cssBefore.width = 0;
+ opts.animIn.width = 'show';
+ opts.animOut.width = 0;
+};
+$.fn.cycle.transitions.slideY = function($cont, $slides, opts) {
+ opts.before.push(function(curr, next, opts) {
+ $(opts.elements).not(curr).hide();
+ $.fn.cycle.commonReset(curr,next,opts,true,false);
+ opts.animIn.height = next.cycleH;
+ });
+ opts.cssBefore.left = 0;
+ opts.cssBefore.top = 0;
+ opts.cssBefore.height = 0;
+ opts.animIn.height = 'show';
+ opts.animOut.height = 0;
+};
+
+// shuffle
+$.fn.cycle.transitions.shuffle = function($cont, $slides, opts) {
+ var i, w = $cont.css('overflow', 'visible').width();
+ $slides.css({left: 0, top: 0});
+ opts.before.push(function(curr,next,opts) {
+ $.fn.cycle.commonReset(curr,next,opts,true,true,true);
+ });
+ // only adjust speed once!
+ if (!opts.speedAdjusted) {
+ opts.speed = opts.speed / 2; // shuffle has 2 transitions
+ opts.speedAdjusted = true;
+ }
+ opts.random = 0;
+ opts.shuffle = opts.shuffle || {left:-w, top:15};
+ opts.els = [];
+ for (i=0; i < $slides.length; i++)
+ opts.els.push($slides[i]);
+
+ for (i=0; i < opts.currSlide; i++)
+ opts.els.push(opts.els.shift());
+
+ // custom transition fn (hat tip to Benjamin Sterling for this bit of sweetness!)
+ opts.fxFn = function(curr, next, opts, cb, fwd) {
+ if (opts.rev)
+ fwd = !fwd;
+ var $el = fwd ? $(curr) : $(next);
+ $(next).css(opts.cssBefore);
+ var count = opts.slideCount;
+ $el.animate(opts.shuffle, opts.speedIn, opts.easeIn, function() {
+ var hops = $.fn.cycle.hopsFromLast(opts, fwd);
+ for (var k=0; k < hops; k++) {
+ if (fwd)
+ opts.els.push(opts.els.shift());
+ else
+ opts.els.unshift(opts.els.pop());
+ }
+ if (fwd) {
+ for (var i=0, len=opts.els.length; i < len; i++)
+ $(opts.els[i]).css('z-index', len-i+count);
+ }
+ else {
+ var z = $(curr).css('z-index');
+ $el.css('z-index', parseInt(z,10)+1+count);
+ }
+ $el.animate({left:0, top:0}, opts.speedOut, opts.easeOut, function() {
+ $(fwd ? this : curr).hide();
+ if (cb) cb();
+ });
+ });
+ };
+ $.extend(opts.cssBefore, { display: 'block', opacity: 1, top: 0, left: 0 });
+};
+
+// turnUp/Down/Left/Right
+$.fn.cycle.transitions.turnUp = function($cont, $slides, opts) {
+ opts.before.push(function(curr, next, opts) {
+ $.fn.cycle.commonReset(curr,next,opts,true,false);
+ opts.cssBefore.top = next.cycleH;
+ opts.animIn.height = next.cycleH;
+ opts.animOut.width = next.cycleW;
+ });
+ opts.cssFirst.top = 0;
+ opts.cssBefore.left = 0;
+ opts.cssBefore.height = 0;
+ opts.animIn.top = 0;
+ opts.animOut.height = 0;
+};
+$.fn.cycle.transitions.turnDown = function($cont, $slides, opts) {
+ opts.before.push(function(curr, next, opts) {
+ $.fn.cycle.commonReset(curr,next,opts,true,false);
+ opts.animIn.height = next.cycleH;
+ opts.animOut.top = curr.cycleH;
+ });
+ opts.cssFirst.top = 0;
+ opts.cssBefore.left = 0;
+ opts.cssBefore.top = 0;
+ opts.cssBefore.height = 0;
+ opts.animOut.height = 0;
+};
+$.fn.cycle.transitions.turnLeft = function($cont, $slides, opts) {
+ opts.before.push(function(curr, next, opts) {
+ $.fn.cycle.commonReset(curr,next,opts,false,true);
+ opts.cssBefore.left = next.cycleW;
+ opts.animIn.width = next.cycleW;
+ });
+ opts.cssBefore.top = 0;
+ opts.cssBefore.width = 0;
+ opts.animIn.left = 0;
+ opts.animOut.width = 0;
+};
+$.fn.cycle.transitions.turnRight = function($cont, $slides, opts) {
+ opts.before.push(function(curr, next, opts) {
+ $.fn.cycle.commonReset(curr,next,opts,false,true);
+ opts.animIn.width = next.cycleW;
+ opts.animOut.left = curr.cycleW;
+ });
+ $.extend(opts.cssBefore, { top: 0, left: 0, width: 0 });
+ opts.animIn.left = 0;
+ opts.animOut.width = 0;
+};
+
+// zoom
+$.fn.cycle.transitions.zoom = function($cont, $slides, opts) {
+ opts.before.push(function(curr, next, opts) {
+ $.fn.cycle.commonReset(curr,next,opts,false,false,true);
+ opts.cssBefore.top = next.cycleH/2;
+ opts.cssBefore.left = next.cycleW/2;
+ $.extend(opts.animIn, { top: 0, left: 0, width: next.cycleW, height: next.cycleH });
+ $.extend(opts.animOut, { width: 0, height: 0, top: curr.cycleH/2, left: curr.cycleW/2 });
+ });
+ opts.cssFirst.top = 0;
+ opts.cssFirst.left = 0;
+ opts.cssBefore.width = 0;
+ opts.cssBefore.height = 0;
+};
+
+// fadeZoom
+$.fn.cycle.transitions.fadeZoom = function($cont, $slides, opts) {
+ opts.before.push(function(curr, next, opts) {
+ $.fn.cycle.commonReset(curr,next,opts,false,false);
+ opts.cssBefore.left = next.cycleW/2;
+ opts.cssBefore.top = next.cycleH/2;
+ $.extend(opts.animIn, { top: 0, left: 0, width: next.cycleW, height: next.cycleH });
+ });
+ opts.cssBefore.width = 0;
+ opts.cssBefore.height = 0;
+ opts.animOut.opacity = 0;
+};
+
+// blindX
+$.fn.cycle.transitions.blindX = function($cont, $slides, opts) {
+ var w = $cont.css('overflow','hidden').width();
+ opts.before.push(function(curr, next, opts) {
+ $.fn.cycle.commonReset(curr,next,opts);
+ opts.animIn.width = next.cycleW;
+ opts.animOut.left = curr.cycleW;
+ });
+ opts.cssBefore.left = w;
+ opts.cssBefore.top = 0;
+ opts.animIn.left = 0;
+ opts.animOut.left = w;
+};
+// blindY
+$.fn.cycle.transitions.blindY = function($cont, $slides, opts) {
+ var h = $cont.css('overflow','hidden').height();
+ opts.before.push(function(curr, next, opts) {
+ $.fn.cycle.commonReset(curr,next,opts);
+ opts.animIn.height = next.cycleH;
+ opts.animOut.top = curr.cycleH;
+ });
+ opts.cssBefore.top = h;
+ opts.cssBefore.left = 0;
+ opts.animIn.top = 0;
+ opts.animOut.top = h;
+};
+// blindZ
+$.fn.cycle.transitions.blindZ = function($cont, $slides, opts) {
+ var h = $cont.css('overflow','hidden').height();
+ var w = $cont.width();
+ opts.before.push(function(curr, next, opts) {
+ $.fn.cycle.commonReset(curr,next,opts);
+ opts.animIn.height = next.cycleH;
+ opts.animOut.top = curr.cycleH;
+ });
+ opts.cssBefore.top = h;
+ opts.cssBefore.left = w;
+ opts.animIn.top = 0;
+ opts.animIn.left = 0;
+ opts.animOut.top = h;
+ opts.animOut.left = w;
+};
+
+// growX - grow horizontally from centered 0 width
+$.fn.cycle.transitions.growX = function($cont, $slides, opts) {
+ opts.before.push(function(curr, next, opts) {
+ $.fn.cycle.commonReset(curr,next,opts,false,true);
+ opts.cssBefore.left = this.cycleW/2;
+ opts.animIn.left = 0;
+ opts.animIn.width = this.cycleW;
+ opts.animOut.left = 0;
+ });
+ opts.cssBefore.top = 0;
+ opts.cssBefore.width = 0;
+};
+// growY - grow vertically from centered 0 height
+$.fn.cycle.transitions.growY = function($cont, $slides, opts) {
+ opts.before.push(function(curr, next, opts) {
+ $.fn.cycle.commonReset(curr,next,opts,true,false);
+ opts.cssBefore.top = this.cycleH/2;
+ opts.animIn.top = 0;
+ opts.animIn.height = this.cycleH;
+ opts.animOut.top = 0;
+ });
+ opts.cssBefore.height = 0;
+ opts.cssBefore.left = 0;
+};
+
+// curtainX - squeeze in both edges horizontally
+$.fn.cycle.transitions.curtainX = function($cont, $slides, opts) {
+ opts.before.push(function(curr, next, opts) {
+ $.fn.cycle.commonReset(curr,next,opts,false,true,true);
+ opts.cssBefore.left = next.cycleW/2;
+ opts.animIn.left = 0;
+ opts.animIn.width = this.cycleW;
+ opts.animOut.left = curr.cycleW/2;
+ opts.animOut.width = 0;
+ });
+ opts.cssBefore.top = 0;
+ opts.cssBefore.width = 0;
+};
+// curtainY - squeeze in both edges vertically
+$.fn.cycle.transitions.curtainY = function($cont, $slides, opts) {
+ opts.before.push(function(curr, next, opts) {
+ $.fn.cycle.commonReset(curr,next,opts,true,false,true);
+ opts.cssBefore.top = next.cycleH/2;
+ opts.animIn.top = 0;
+ opts.animIn.height = next.cycleH;
+ opts.animOut.top = curr.cycleH/2;
+ opts.animOut.height = 0;
+ });
+ opts.cssBefore.height = 0;
+ opts.cssBefore.left = 0;
+};
+
+// cover - curr slide covered by next slide
+$.fn.cycle.transitions.cover = function($cont, $slides, opts) {
+ var d = opts.direction || 'left';
+ var w = $cont.css('overflow','hidden').width();
+ var h = $cont.height();
+ opts.before.push(function(curr, next, opts) {
+ $.fn.cycle.commonReset(curr,next,opts);
+ opts.cssAfter.display = '';
+ if (d == 'right')
+ opts.cssBefore.left = -w;
+ else if (d == 'up')
+ opts.cssBefore.top = h;
+ else if (d == 'down')
+ opts.cssBefore.top = -h;
+ else
+ opts.cssBefore.left = w;
+ });
+ opts.animIn.left = 0;
+ opts.animIn.top = 0;
+ opts.cssBefore.top = 0;
+ opts.cssBefore.left = 0;
+};
+
+// uncover - curr slide moves off next slide
+$.fn.cycle.transitions.uncover = function($cont, $slides, opts) {
+ var d = opts.direction || 'left';
+ var w = $cont.css('overflow','hidden').width();
+ var h = $cont.height();
+ opts.before.push(function(curr, next, opts) {
+ $.fn.cycle.commonReset(curr,next,opts,true,true,true);
+ if (d == 'right')
+ opts.animOut.left = w;
+ else if (d == 'up')
+ opts.animOut.top = -h;
+ else if (d == 'down')
+ opts.animOut.top = h;
+ else
+ opts.animOut.left = -w;
+ });
+ opts.animIn.left = 0;
+ opts.animIn.top = 0;
+ opts.cssBefore.top = 0;
+ opts.cssBefore.left = 0;
+};
+
+// toss - move top slide and fade away
+$.fn.cycle.transitions.toss = function($cont, $slides, opts) {
+ var w = $cont.css('overflow','visible').width();
+ var h = $cont.height();
+ opts.before.push(function(curr, next, opts) {
+ $.fn.cycle.commonReset(curr,next,opts,true,true,true);
+ // provide default toss settings if animOut not provided
+ if (!opts.animOut.left && !opts.animOut.top)
+ $.extend(opts.animOut, { left: w*2, top: -h/2, opacity: 0 });
+ else
+ opts.animOut.opacity = 0;
+ });
+ opts.cssBefore.left = 0;
+ opts.cssBefore.top = 0;
+ opts.animIn.left = 0;
+};
+
+// wipe - clip animation
+$.fn.cycle.transitions.wipe = function($cont, $slides, opts) {
+ var w = $cont.css('overflow','hidden').width();
+ var h = $cont.height();
+ opts.cssBefore = opts.cssBefore || {};
+ var clip;
+ if (opts.clip) {
+ if (/l2r/.test(opts.clip))
+ clip = 'rect(0px 0px '+h+'px 0px)';
+ else if (/r2l/.test(opts.clip))
+ clip = 'rect(0px '+w+'px '+h+'px '+w+'px)';
+ else if (/t2b/.test(opts.clip))
+ clip = 'rect(0px '+w+'px 0px 0px)';
+ else if (/b2t/.test(opts.clip))
+ clip = 'rect('+h+'px '+w+'px '+h+'px 0px)';
+ else if (/zoom/.test(opts.clip)) {
+ var top = parseInt(h/2,10);
+ var left = parseInt(w/2,10);
+ clip = 'rect('+top+'px '+left+'px '+top+'px '+left+'px)';
+ }
+ }
+
+ opts.cssBefore.clip = opts.cssBefore.clip || clip || 'rect(0px 0px 0px 0px)';
+
+ var d = opts.cssBefore.clip.match(/(\d+)/g);
+ var t = parseInt(d[0],10), r = parseInt(d[1],10), b = parseInt(d[2],10), l = parseInt(d[3],10);
+
+ opts.before.push(function(curr, next, opts) {
+ if (curr == next) return;
+ var $curr = $(curr), $next = $(next);
+ $.fn.cycle.commonReset(curr,next,opts,true,true,false);
+ opts.cssAfter.display = 'block';
+
+ var step = 1, count = parseInt((opts.speedIn / 13),10) - 1;
+ (function f() {
+ var tt = t ? t - parseInt(step * (t/count),10) : 0;
+ var ll = l ? l - parseInt(step * (l/count),10) : 0;
+ var bb = b < h ? b + parseInt(step * ((h-b)/count || 1),10) : h;
+ var rr = r < w ? r + parseInt(step * ((w-r)/count || 1),10) : w;
+ $next.css({ clip: 'rect('+tt+'px '+rr+'px '+bb+'px '+ll+'px)' });
+ (step++ <= count) ? setTimeout(f, 13) : $curr.css('display', 'none');
+ })();
+ });
+ $.extend(opts.cssBefore, { display: 'block', opacity: 1, top: 0, left: 0 });
+ opts.animIn = { left: 0 };
+ opts.animOut = { left: 0 };
+};
+
+})(jQuery);
diff --git a/plugins/jetpack/modules/shortcodes/js/slideshow-shortcode.js b/plugins/jetpack/modules/shortcodes/js/slideshow-shortcode.js
new file mode 100644
index 00000000..5e132097
--- /dev/null
+++ b/plugins/jetpack/modules/shortcodes/js/slideshow-shortcode.js
@@ -0,0 +1,187 @@
+function JetpackSlideshow( element, width, height, transition ) {
+ this.element = element;
+ this.images = [];
+ this.controls = {};
+ this.transition = transition || 'fade';
+
+ var currentWidth = this.element.width();
+ if ( !width || width > currentWidth )
+ width = currentWidth;
+
+ this.width = width;
+ this.height = height;
+ this.element.css( {
+ 'height': this.height + 'px'
+ } );
+}
+
+JetpackSlideshow.prototype.showLoadingImage = function( toggle ) {
+ if ( toggle ) {
+ this.loadingImage_ = document.createElement( 'div' );
+ this.loadingImage_.className = 'slideshow-loading';
+ var img = document.createElement( 'img' );
+ img.src = jetpackSlideshowSettings.spinner;
+ this.loadingImage_.appendChild( img );
+ this.loadingImage_.appendChild( this.makeZeroWidthSpan() );
+ this.loadingImage_.style.lineHeight = this.height + 'px';
+ this.element.append( this.loadingImage_ );
+ } else if ( this.loadingImage_ ) {
+ this.loadingImage_.parentNode.removeChild( this.loadingImage_ );
+ this.loadingImage_ = null;
+ }
+};
+
+JetpackSlideshow.prototype.init = function() {
+ this.showLoadingImage(true);
+
+ var self = this;
+ // Set up DOM.
+ for ( var i = 0; i < this.images.length; i++ ) {
+ var imageInfo = this.images[i];
+ var img = document.createElement( 'img' );
+ img.src = imageInfo.src + '?w=' + this.width;
+ img.align = 'middle';
+ var caption = document.createElement( 'div' );
+ caption.className = 'slideshow-slide-caption';
+ caption.innerHTML = imageInfo.caption;
+ var container = document.createElement('div');
+ container.className = 'slideshow-slide';
+ container.style.lineHeight = this.height + 'px';
+
+ // Hide loading image once first image has loaded.
+ if ( i == 0 ) {
+ if ( img.complete ) {
+ // IE, image in cache
+ setTimeout( function() {
+ self.finishInit_();
+ }, 1);
+ } else {
+ jQuery( img ).load(function() {
+ self.finishInit_();
+ });
+ }
+ }
+ container.appendChild( img );
+ // I'm not sure where these were coming from, but IE adds
+ // bad values for width/height for portrait-mode images
+ img.removeAttribute('width');
+ img.removeAttribute('height');
+ container.appendChild( this.makeZeroWidthSpan() );
+ container.appendChild( caption );
+ this.element.append( container );
+ }
+};
+
+JetpackSlideshow.prototype.makeZeroWidthSpan = function() {
+ var emptySpan = document.createElement( 'span' );
+ emptySpan.className = 'slideshow-line-height-hack';
+ // Having a NBSP makes IE act weird during transitions, but other
+ // browsers ignore a text node with a space in it as whitespace.
+ if (jQuery.browser.msie) {
+ emptySpan.appendChild( document.createTextNode(' ') );
+ } else {
+ emptySpan.innerHTML = '&nbsp;';
+ }
+ return emptySpan;
+};
+
+JetpackSlideshow.prototype.finishInit_ = function() {
+ this.showLoadingImage( false );
+ this.renderControls_();
+
+ var self = this;
+ // Initialize Cycle instance.
+ this.element.cycle( {
+ fx: this.transition,
+ prev: this.controls.prev,
+ next: this.controls.next,
+ slideExpr: '.slideshow-slide',
+ onPrevNextEvent: function() {
+ return self.onCyclePrevNextClick_.apply( self, arguments );
+ }
+ } );
+
+ var slideshow = this.element;
+ jQuery( this.controls['stop'] ).click( function() {
+ var button = jQuery(this);
+ if ( ! button.hasClass( 'paused' ) ) {
+ slideshow.cycle( 'pause' );
+ button.removeClass( 'running' );
+ button.addClass( 'paused' );
+ } else {
+ button.addClass( 'running' );
+ button.removeClass( 'paused' );
+ slideshow.cycle( 'resume', true );
+ }
+ return false;
+ } );
+
+ var controls = jQuery( this.controlsDiv_ );
+ slideshow.mouseenter( function() {
+ controls.fadeIn();
+ } );
+ slideshow.mouseleave( function() {
+ controls.fadeOut();
+ } );
+
+ this.initialized_ = true;
+};
+
+JetpackSlideshow.prototype.renderControls_ = function() {
+ if ( this.controlsDiv_ )
+ return;
+
+ var controlsDiv = document.createElement( 'div' );
+ controlsDiv.className = 'slideshow-controls';
+
+ controls = [ 'prev', 'stop', 'next' ];
+ for ( var i = 0; i < controls.length; i++ ) {
+ var controlName = controls[i];
+ var a = document.createElement( 'a' );
+ a.href = '#';
+ controlsDiv.appendChild( a );
+ this.controls[controlName] = a;
+ }
+ this.element.append( controlsDiv );
+ this.controlsDiv_ = controlsDiv;
+};
+
+JetpackSlideshow.prototype.onCyclePrevNextClick_ = function( isNext, i, slideElement ) {
+ // If blog_id not present don't track page views
+ if ( ! jetpackSlideshowSettings.blog_id )
+ return;
+
+ var postid = this.images[i].id;
+ var stats = new Image();
+ stats.src = document.location.protocol +
+ '//stats.wordpress.com/g.gif?host=' +
+ escape( document.location.host ) +
+ '&rand=' + Math.random() +
+ '&blog=' + jetpackSlideshowSettings.blog_id +
+ '&subd=' + jetpackSlideshowSettings.blog_subdomain +
+ '&user_id=' + jetpackSlideshowSettings.user_id +
+ '&post=' + postid +
+ '&ref=' + escape( document.location );
+};
+
+( function ( $ ) {
+ function jetpack_slideshow_init() {
+ $( '.jetpack-slideshow-noscript' ).remove();
+
+ $( '.jetpack-slideshow' ).each( function () {
+ var container = $( this );
+
+ if ( container.data( 'processed' ) )
+ return;
+
+ var slideshow = new JetpackSlideshow( container, container.data( 'width' ), container.data( 'height' ), container.data( 'trans' ) );
+ slideshow.images = container.data( 'gallery' );
+ slideshow.init();
+
+ container.data( 'processed', true );
+ } );
+ }
+
+ $( document ).ready( jetpack_slideshow_init );
+ $( 'body' ).on( 'post-load', jetpack_slideshow_init );
+} )( jQuery ); \ No newline at end of file
diff --git a/plugins/jetpack/modules/shortcodes/polldaddy.php b/plugins/jetpack/modules/shortcodes/polldaddy.php
index bcb30f81..7eb30edf 100644
--- a/plugins/jetpack/modules/shortcodes/polldaddy.php
+++ b/plugins/jetpack/modules/shortcodes/polldaddy.php
@@ -193,7 +193,7 @@ SCRIPT;
$data = esc_attr( json_encode( $data ) );
return <<<CONTAINER
-<a name="pd_a_{$poll}"></a>
+<a id="pd_a_{$poll}"></a>
<div class="PDS_Poll" id="PDI_container{$poll}" data-settings="{$data}" style="display:inline-block;{$float}{$margins}"></div>
<div id="PD_superContainer"></div>
<noscript>{$poll_link}</noscript>
@@ -204,7 +204,7 @@ CONTAINER;
$cb = '';
return <<<CONTAINER
-<a name="pd_a_{$poll}"></a>
+<a id="pd_a_{$poll}"></a>
<div class="PDS_Poll" id="PDI_container{$poll}" style="display:inline-block;{$float}{$margins}"></div>
<div id="PD_superContainer"></div>
<script type="text/javascript" charset="UTF-8" src="{$poll_js}{$cb}"></script>
@@ -395,4 +395,4 @@ if ( !function_exists( 'polldaddy_link' ) ) {
add_filter( 'comment_text', 'polldaddy_link', 1 );
}
-} \ No newline at end of file
+}
diff --git a/plugins/jetpack/modules/shortcodes/slideshow.php b/plugins/jetpack/modules/shortcodes/slideshow.php
new file mode 100644
index 00000000..ab1145d8
--- /dev/null
+++ b/plugins/jetpack/modules/shortcodes/slideshow.php
@@ -0,0 +1,208 @@
+<?php
+
+/**
+ * Slideshow shortcode usage: [gallery type="slideshow"] or the older [slideshow]
+ */
+class Jetpack_Slideshow_Shortcode {
+ public $instance_count = 0;
+
+ function __construct() {
+ global $shortcode_tags;
+
+ $needs_scripts = false;
+
+ // Only if the slideshow shortcode has not already been defined.
+ if ( ! array_key_exists( 'slideshow', $shortcode_tags ) ) {
+ add_shortcode( 'slideshow', array( $this, 'shortcode_callback' ) );
+ $needs_scripts = true;
+ }
+
+ // Only if the gallery shortcode has not been redefined.
+ if ( isset( $shortcode_tags['gallery'] ) && $shortcode_tags['gallery'] == 'gallery_shortcode' ) {
+ add_filter( 'post_gallery', array( $this, 'post_gallery' ), 1002, 2 );
+ add_filter( 'jetpack_gallery_types', array( $this, 'add_gallery_type' ), 10 );
+ $needs_scripts = true;
+ }
+
+ if ( $needs_scripts )
+ add_action( 'wp_enqueue_scripts', array( $this, 'maybe_enqueue_scripts' ), 1 );
+ }
+
+ /**
+ * Responds to the [gallery] shortcode, but not an actual shortcode callback.
+ *
+ * @param $value An empty string if nothing has modified the gallery output, the output html otherwise
+ * @param $attr The shortcode attributes array
+ *
+ * @return string The (un)modified $value
+ */
+ function post_gallery( $value, $attr ) {
+ // Bail if somebody else has done something
+ if ( ! empty( $value ) )
+ return $value;
+
+ // If [gallery type="slideshow"] have it behave just like [slideshow]
+ if ( ! empty( $attr['type'] ) && 'slideshow' == $attr['type'] )
+ return $this->shortcode_callback( $attr );
+
+ return $value;
+ }
+
+ /**
+ * Add the Slideshow type to gallery settings
+ *
+ * @param $types An array of types where the key is the value, and the value is the caption.
+ * @see Jetpack_Tiled_Gallery::media_ui_print_templates
+ */
+ function add_gallery_type( $types = array() ) {
+ $types['slideshow'] = esc_html__( 'Slideshow', 'jetpack' );
+ return $types;
+ }
+
+ function shortcode_callback( $attr, $content = null ) {
+ global $post, $content_width;
+
+ $attr = shortcode_atts( array(
+ 'trans' => 'fade',
+ 'order' => 'ASC',
+ 'orderby' => 'menu_order ID',
+ 'id' => $post->ID,
+ 'include' => '',
+ 'exclude' => '',
+ ), $attr );
+
+ if ( 'rand' == strtolower( $attr['order'] ) )
+ $attr['orderby'] = 'none';
+
+ $attr['orderby'] = sanitize_sql_orderby( $attr['orderby'] );
+ if ( ! $attr['orderby'] )
+ $attr['orderby'] = 'menu_order ID';
+
+ // Don't restrict to the current post if include
+ $post_parent = ( empty( $attr['include'] ) ) ? intval( $attr['id'] ) : null;
+
+ $attachments = get_posts( array(
+ 'post_status' => 'inherit',
+ 'post_type' => 'attachment',
+ 'post_mime_type' => 'image',
+ 'posts_per_page' => -1,
+ 'post_parent' => $post_parent,
+ 'order' => $attr['order'],
+ 'orderby' => $attr['orderby'],
+ 'include' => $attr['include'],
+ 'exclude' => $attr['exclude'],
+ ) );
+
+ if ( count( $attachments ) < 2 )
+ return;
+
+ $gallery_instance = sprintf( "gallery-%d-%d", $attr['id'], ++$this->instance_count );
+
+ $gallery = array();
+ foreach ( $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 = 450;
+ 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' => $attr['trans'] ? $attr['trans'] : 'fade',
+ );
+
+ // Show a link to the gallery in feeds.
+ if ( is_feed() )
+ return sprintf( '<a href="%s">%s</a>',
+ esc_url( get_permalink( $post->ID ) . '#' . $gallery_instance . '-slideshow' ),
+ esc_html__( 'Click to view slideshow.', 'jetpack' )
+ );
+
+ return $this->slideshow_js( $js_attr );
+ }
+
+ /**
+ * Render the slideshow js
+ *
+ * Returns the necessary markup and js to fire a slideshow.
+ *
+ * @uses $this->enqueue_scripts()
+ */
+ function slideshow_js( $attr ) {
+ // Enqueue scripts
+ $this->enqueue_scripts();
+
+ if ( $attr['width'] <= 100 )
+ $attr['width'] = 450;
+
+ if ( $attr['height'] <= 100 )
+ $attr['height'] = 450;
+
+ // 40px padding
+ $attr['width'] -= 40;
+ $attr['height'] -= 40;
+
+ $output = '';
+
+ $output .= '<p class="jetpack-slideshow-noscript robots-nocontent">' . esc_html__( 'This slideshow requires JavaScript.', 'jetpack' ) . '</p>';
+ $output .= '<div id="' . esc_attr( $attr['selector'] . '-slideshow' ) . '" class="slideshow-window jetpack-slideshow" data-width="' . esc_attr( $attr['width'] ) . '" data-height="' . esc_attr( $attr['height'] ) . '" data-trans="' . esc_attr( $attr['trans'] ) . '" data-gallery="' . esc_attr( json_encode( $attr['gallery'] ) ) . '"></div>';
+
+ $output .= "
+ <style>
+ #{$attr['selector']}-slideshow .slideshow-slide img {
+ max-height: " . intval( $attr['height'] ) ."px;
+ /* Emulate max-height in IE 6 */
+ _height: expression(this.scrollHeight >= " . intval( $attr['height'] ) . " ? '" . intval( $attr['height'] ) . "px' : 'auto');
+ }
+ </style>
+ ";
+
+ return $output;
+ }
+
+ /**
+ * Infinite Scroll needs the scripts to be present at all times
+ */
+ function maybe_enqueue_scripts() {
+ if ( is_home() && current_theme_supports( 'infinite-scroll' ) )
+ $this->enqueue_scripts();
+ }
+
+ /**
+ * Actually enqueues the scripts and styles.
+ */
+ function enqueue_scripts() {
+ static $enqueued = false;
+
+ if ( $enqueued )
+ return;
+
+ wp_enqueue_script( 'jquery-cycle', plugins_url( '/js/jquery.cycle.js', __FILE__ ) , array( 'jquery' ), '2.9999.8', true );
+ wp_enqueue_script( 'jetpack-slideshow', plugins_url( '/js/slideshow-shortcode.js', __FILE__ ), array( 'jquery-cycle' ), '20121214.1', true );
+ wp_enqueue_style( 'jetpack-slideshow', plugins_url( '/css/slideshow-shortcode.css', __FILE__ ) );
+
+ wp_localize_script( 'jetpack-slideshow', 'jetpackSlideshowSettings', apply_filters( 'jetpack_js_slideshow_settings', array(
+ 'spinner' => plugins_url( '/img/slideshow-loader.gif', __FILE__ ),
+ ) ) );
+
+ $enqueued = true;
+ }
+
+ public static function init() {
+ $gallery = new Jetpack_Slideshow_Shortcode;
+ }
+}
+add_action( 'init', array( 'Jetpack_Slideshow_Shortcode', 'init' ) );
diff --git a/plugins/jetpack/modules/shortcodes/soundcloud.php b/plugins/jetpack/modules/shortcodes/soundcloud.php
index 8aa35380..a7486115 100644
--- a/plugins/jetpack/modules/shortcodes/soundcloud.php
+++ b/plugins/jetpack/modules/shortcodes/soundcloud.php
@@ -1,60 +1,214 @@
<?php
-
/*
Plugin Name: SoundCloud Shortcode
-Plugin URI: http://www.soundcloud.com
-Description: SoundCloud Shortcode. Usage in your posts: [soundcloud]http://soundcloud.com/TRACK_PERMALINK[/soundcloud] . Works also with set or group instead of track. You can provide optional parameters height/width/params like that [soundcloud height="82" params="auto_play=true"]http....
-Version: 1.1.5
-Author: Johannes Wagener <johannes@soundcloud.com> added to wpcom by tott
-Author URI: http://johannes.wagener.cc
-
-[soundcloud url="http://api.soundcloud.com/tracks/9408008"]
-<object height="81" width="100%"> <param name="movie" value="http://player.soundcloud.com/player.swf?url=http%3A%2F%2Fapi.soundcloud.com%2Ftracks%2F8781356"></param> <param name="allowscriptaccess" value="always"></param> <embed allowscriptaccess="always" height="81" src="http://player.soundcloud.com/player.swf?url=http%3A%2F%2Fapi.soundcloud.com%2Ftracks%2F8781356" type="application/x-shockwave-flash" width="100%"></embed> </object> <span><a href="http://soundcloud.com/robokopbeats/robokop-we-move-at-midnight-preview-forthcoming-on-mwm-recordings">Robokop - We move at midnight preview ( FORTHCOMING ON MWM recordings)</a> by <a href="http://soundcloud.com/robokopbeats">Robokop</a></span>
+Plugin URI: http://wordpress.org/extend/plugins/soundcloud-shortcode/
+Description: Converts SoundCloud WordPress shortcodes to a SoundCloud widget. Example: [soundcloud]http://soundcloud.com/forss/flickermood[/soundcloud]
+Version: 2.3
+Author: SoundCloud Inc., simplified for Jetpack by Automattic, Inc.
+Author URI: http://soundcloud.com
+License: GPLv2
+
+Original version: Johannes Wagener <johannes@soundcloud.com>
+Options support: Tiffany Conroy <tiffany@soundcloud.com>
+HTML5 & oEmbed support: Tim Bormans <tim@soundcloud.com>
+*/
+
+/*
+A8C: Taken from http://plugins.svn.wordpress.org/soundcloud-shortcode/trunk/
+at revision 664386.
+
+Commenting out (instead of removing) and replacing code with custom modifs
+so it's eqsy to see what differs from the standard DOTORG version.
+
+All custom modifs are annoted with "A8C" keyword in comment.
*/
-add_filter( "pre_kses", "soundcloud_reverse_shortcode" );
+/* Register oEmbed provider
+ -------------------------------------------------------------------------- */
+
+wp_oembed_add_provider('#https?://(?:api\.)?soundcloud\.com/.*#i', 'http://soundcloud.com/oembed', true);
+
+
+/* Register SoundCloud shortcode
+ -------------------------------------------------------------------------- */
+
+add_shortcode("soundcloud", "soundcloud_shortcode");
+
+
+/**
+ * SoundCloud shortcode handler
+ * @param {string|array} $atts The attributes passed to the shortcode like [soundcloud attr1="value" /].
+ * Is an empty string when no arguments are given.
+ * @param {string} $content The content between non-self closing [soundcloud]…[/soundcloud] tags.
+ * @return {string} Widget embed code HTML
+ */
+function soundcloud_shortcode($atts, $content = null) {
+
+ // Custom shortcode options
+ $shortcode_options = array_merge(array('url' => trim($content)), is_array($atts) ? $atts : array());
+
+ // Turn shortcode option "param" (param=value&param2=value) into array
+ $shortcode_params = array();
+ if (isset($shortcode_options['params'])) {
+ parse_str(html_entity_decode($shortcode_options['params']), $shortcode_params);
+ }
+ $shortcode_options['params'] = $shortcode_params;
+
+ // User preference options
+ $plugin_options = array_filter(array(
+ 'iframe' => soundcloud_get_option('player_iframe', true),
+ 'width' => soundcloud_get_option('player_width'),
+ 'height' => soundcloud_url_has_tracklist($shortcode_options['url']) ? soundcloud_get_option('player_height_multi') : soundcloud_get_option('player_height'),
+ 'params' => array_filter(array(
+ 'auto_play' => soundcloud_get_option('auto_play'),
+ 'show_comments' => soundcloud_get_option('show_comments'),
+ 'color' => soundcloud_get_option('color'),
+ 'theme_color' => soundcloud_get_option('theme_color'),
+ )),
+ ));
+ // Needs to be an array
+ if (!isset($plugin_options['params'])) { $plugin_options['params'] = array(); }
+
+ // plugin options < shortcode options
+ $options = array_merge(
+ $plugin_options,
+ $shortcode_options
+ );
+
+ // plugin params < shortcode params
+ $options['params'] = array_merge(
+ $plugin_options['params'],
+ $shortcode_options['params']
+ );
+
+ // The "url" option is required
+ if (!isset($options['url'])) {
+ return '';
+ } else {
+ $options['url'] = trim($options['url']);
+ }
+
+ // Both "width" and "height" need to be integers
+ if (isset($options['width']) && !preg_match('/^\d+$/', $options['width'])) {
+ // set to 0 so oEmbed will use the default 100% and WordPress themes will leave it alone
+ $options['width'] = 0;
+ }
+ if (isset($options['height']) && !preg_match('/^\d+$/', $options['height'])) { unset($options['height']); }
-function soundcloud_reverse_shortcode_preg_replace_callback( $a ) {
- $pattern = '/([a-zA-Z0-9\-_%=&]*)&?url=([^&]+)&?([a-zA-Z0-9\-_%&=]*)/';
- preg_match( $pattern, str_replace( "&amp;", "&", $a[3] ), $params );
+ // The "iframe" option must be true to load the iframe widget
+ $iframe = soundcloud_booleanize($options['iframe'])
+ // Default to flash widget for permalink urls (e.g. http://soundcloud.com/{username})
+ // because HTML5 widget doesn’t support those yet
+ ? preg_match('/api.soundcloud.com/i', $options['url'])
+ : false;
- return '[soundcloud width="' . esc_attr( $a[2] ) . '" height="' . esc_attr( $a[1] ) . '" params="' . esc_attr( $params[1] . $params[3] ) . '" url="' . urldecode( $params[2] ) . '"]';
+ // Return html embed code
+ if ($iframe) {
+ return soundcloud_iframe_widget($options);
+ } else {
+ return soundcloud_flash_widget($options);
+ }
+
+}
+
+/**
+ * Plugin options getter
+ * @param {string|array} $option Option name
+ * @param {mixed} $default Default value
+ * @return {mixed} Option value
+ */
+function soundcloud_get_option($option, $default = false) {
+ $value = get_option('soundcloud_' . $option);
+ return $value === '' ? $default : $value;
+}
+
+/**
+ * Booleanize a value
+ * @param {boolean|string} $value
+ * @return {boolean}
+ */
+function soundcloud_booleanize($value) {
+ return is_bool($value) ? $value : $value === 'true' ? true : false;
}
-function soundcloud_reverse_shortcode( $content ){
- if ( false === stripos( $content, 'http://player.soundcloud.com/player.swf' ) )
- return $content;
+/**
+ * Decide if a url has a tracklist
+ * @param {string} $url
+ * @return {boolean}
+ */
+function soundcloud_url_has_tracklist($url) {
+ return preg_match('/^(.+?)\/(sets|groups|playlists)\/(.+?)$/', $url);
+}
+
+/**
+ * Parameterize url
+ * @param {array} $match Matched regex
+ * @return {string} Parameterized url
+ */
+function soundcloud_oembed_params_callback($match) {
+ global $soundcloud_oembed_params;
- $pattern = '!<object\s*height="(\d+%?)"\s*width="(\d+%?)".*?src="http://.*?soundcloud\.com/player.swf\?([^"]+)".*?</object>.*?</span>!';
- $pattern_ent = str_replace( '&amp;#0*58;', '&amp;#0*58;|&#0*58;', htmlspecialchars( $pattern, ENT_NOQUOTES ) );
+ // Convert URL to array
+ $url = parse_url(urldecode($match[1]));
+ // Convert URL query to array
+ parse_str($url['query'], $query_array);
+ // Build new query string
+ $query = http_build_query(array_merge($query_array, $soundcloud_oembed_params));
- if ( preg_match( $pattern_ent, $content ) )
- return( preg_replace_callback( $pattern_ent, 'soundcloud_reverse_shortcode_preg_replace_callback', $content ) );
- else
- return( preg_replace_callback( $pattern, 'soundcloud_reverse_shortcode_preg_replace_callback', $content ) );
+ return 'src="' . $url['scheme'] . '://' . $url['host'] . $url['path'] . '?' . $query;
}
-add_shortcode( "soundcloud", "soundcloud_shortcode" );
-
-function soundcloud_shortcode( $atts, $url = '' ) {
- if ( empty( $url ) )
- extract( shortcode_atts( array( 'url' => '', 'params' => '', 'height' => '', 'width' => '100%' ), $atts ) );
- else
- extract( shortcode_atts( array( 'params' => '', 'height' => '', 'width' => '100%' ), $atts ) );
-
- $encoded_url = urlencode( $url );
- if ( $url = parse_url( $url ) ) {
- $splitted_url = split( "/", $url['path'] );
- $media_type = $splitted_url[ count( $splitted_url ) - 2 ];
-
- if ( '' == $height ){
- if ( in_array( $media_type, array( 'groups', 'sets' ) ) )
- $height = 225;
- else
- $height = 81;
- }
- $player_params = "url=$encoded_url&g=1&$params";
-
- return '<object height="' . esc_attr( $height ) . '" width="' . esc_attr( $width ) . '"><param name="movie" value="' . esc_url( "http://player.soundcloud.com/player.swf?$player_params" ) . '"></param><embed height="' . esc_attr( $height ) . '" src="' . esc_url( "http://player.soundcloud.com/player.swf?$player_params" ) . '" type="application/x-shockwave-flash" width="' . esc_attr( $width ) . '"> </embed> </object>';
- }
+/**
+ * Iframe widget embed code
+ * @param {array} $options Parameters
+ * @return {string} Iframe embed code
+ */
+function soundcloud_iframe_widget($options) {
+
+ // Merge in "url" value
+ $options['params'] = array_merge(array(
+ 'url' => $options['url']
+ ), $options['params']);
+
+ // Build URL
+ $url = 'http://w.soundcloud.com/player?' . http_build_query($options['params']);
+ // Set default width if not defined
+ $width = isset($options['width']) && $options['width'] !== 0 ? $options['width'] : '100%';
+ // Set default height if not defined
+ $height = isset($options['height']) && $options['height'] !== 0 ? $options['height'] : (soundcloud_url_has_tracklist($options['url']) ? '450' : '166');
+
+ return sprintf('<iframe width="%s" height="%s" scrolling="no" frameborder="no" src="%s"></iframe>', $width, $height, $url);
}
+
+/**
+ * Legacy Flash widget embed code
+ * @param {array} $options Parameters
+ * @return {string} Flash embed code
+ */
+function soundcloud_flash_widget($options) {
+
+ // Merge in "url" value
+ $options['params'] = array_merge(array(
+ 'url' => $options['url']
+ ), $options['params']);
+
+ // Build URL
+ $url = 'http://player.soundcloud.com/player.swf?' . http_build_query($options['params']);
+ // Set default width if not defined
+ $width = isset($options['width']) && $options['width'] !== 0 ? $options['width'] : '100%';
+ // Set default height if not defined
+ $height = isset($options['height']) && $options['height'] !== 0 ? $options['height'] : (soundcloud_url_has_tracklist($options['url']) ? '255' : '81');
+
+ return preg_replace('/\s\s+/', "", sprintf('<object width="%s" height="%s">
+ <param name="movie" value="%s"></param>
+ <param name="allowscriptaccess" value="always"></param>
+ <embed width="%s" height="%s" src="%s" allowscriptaccess="always" type="application/x-shockwave-flash"></embed>
+ </object>', $width, $height, $url, $width, $height, $url));
+}
+
+
+
+/* Settings
+ -------------------------------------------------------------------------- */
+
+/* A8C: no user-defined options, KISS */
diff --git a/plugins/jetpack/modules/shortcodes/ted.php b/plugins/jetpack/modules/shortcodes/ted.php
new file mode 100644
index 00000000..afd35961
--- /dev/null
+++ b/plugins/jetpack/modules/shortcodes/ted.php
@@ -0,0 +1,68 @@
+<?php
+/*
+ * TED Player embed code
+ * http://www.ted.com
+ *
+ * http://www.ted.com/talks/view/id/210
+ * http://www.ted.com/talks/marc_goodman_a_vision_of_crimes_in_the_future.html
+ * [ted id="210" lang="eng"]
+ * [ted id="http://www.ted.com/talks/view/id/210" lang="eng"]
+ * [ted id=1539 lang=fr width=560 height=315]
+ */
+
+wp_oembed_add_provider( '!https?://(www\.)?ted.com/talks/view/id/.+!i', 'http://www.ted.com/talks/oembed.json', true );
+wp_oembed_add_provider( '!https?://(www\.)?ted.com/talks/[a-zA-Z\-\_]+\.html!i', 'http://www.ted.com/talks/oembed.json', true );
+
+add_shortcode( 'ted', 'shortcode_ted' );
+function shortcode_ted( $atts, $content = '' ) {
+ global $wp_embed;
+
+ $defaults = array(
+ 'id' => '',
+ 'width' => '',
+ 'height' => '',
+ 'lang' => 'eng',
+ );
+ $atts = shortcode_atts( $defaults, $atts );
+
+ if ( empty( $atts['id'] ) )
+ return '<!-- Missing TED ID -->';
+
+ if ( preg_match( "#^[\d]+$#", $atts['id'], $matches ) )
+ $url = 'http://ted.com/talks/view/id/' . $matches[0];
+ elseif ( preg_match( "#^https?://(www\.)?ted\.com/talks/view/id/[0-9]+$#", $atts['id'], $matches ) )
+ $url = $matches[0];
+
+ unset( $atts['id'] );
+
+ $args = array();
+ if ( is_numeric( $atts['width'] ) )
+ $args['width'] = $atts['width'];
+ else if ( $embed_size_w = get_option( 'embed_size_w' ) )
+ $args['width'] = $embed_size_w;
+ else if ( ! empty( $GLOBALS['content_width'] ) )
+ $args['width'] = (int)$GLOBALS['content_width'];
+ else
+ $args['width'] = 500;
+
+ // Default to a 16x9 aspect ratio if there's no height set
+ if ( is_numeric( $atts['height'] ) )
+ $args['height'] = $atts['height'];
+ else
+ $args['height'] = $args['width'] * 0.5625;
+
+ if ( ! empty( $atts['lang'] ) ) {
+ $args['lang'] = sanitize_key( $atts['lang'] );
+ add_filter( 'oembed_fetch_url', 'ted_filter_oembed_fetch_url', 10, 3 );
+ }
+ $retval = $wp_embed->shortcode( $args, $url );
+ remove_filter( 'oembed_fetch_url', 'ted_filter_oembed_fetch_url', 10 );
+ return $retval;
+}
+
+/**
+ * Filter the request URL to also include the $lang parameter
+ */
+function ted_filter_oembed_fetch_url( $provider, $url, $args ) {
+ return add_query_arg( 'lang', $args['lang'], $provider );
+}
diff --git a/plugins/jetpack/modules/shortcodes/videopress.php b/plugins/jetpack/modules/shortcodes/videopress.php
index 912ab4cf..6aebae03 100644
--- a/plugins/jetpack/modules/shortcodes/videopress.php
+++ b/plugins/jetpack/modules/shortcodes/videopress.php
@@ -4,7 +4,7 @@
* @category video
* @author Automattic Inc
* @link http://automattic.com/wordpress-plugins/#videopress VideoPress
- * @version 1.5
+ * @version 1.5.4
* @license http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
*/
@@ -15,8 +15,8 @@ Description: Upload new videos to <a href="http://videopress.com/">VideoPress</a
Author: Automattic, Niall Kennedy, Joseph Scott, Gary Pendergast
Contributor: Hailin Wu
Author URI: http://automattic.com/wordpress-plugins/#videopress
-Version: 1.5
-Stable tag: 1.5
+Version: 1.5.4
+Stable tag: 1.5.4
License: GPL v2 - http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
*/
@@ -33,7 +33,7 @@ class VideoPress {
* @var string
* @since 1.3
*/
- const version = '1.5';
+ 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.
@@ -186,7 +186,7 @@ class VideoPress {
wp_enqueue_script( 'swfobject', $swfobject, false. '2.2' );
wp_enqueue_script( 'jquery', $jquery, false, '1.4.4' );
- wp_enqueue_script( 'videopress', $vpjs, array( 'jquery','swfobject' ), '1.07' );
+ wp_enqueue_script( 'videopress', $vpjs, array( 'jquery','swfobject' ), '1.09' );
$this->js_loaded = true;
return true;
@@ -228,10 +228,24 @@ class VideoPress {
'w' => 0,
'freedom' => false,
'flashonly' => false,
- 'autoplay' => 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);
@@ -250,7 +264,9 @@ class VideoPress {
$options = array(
'freedom' => $freedom,
'force_flash' => (bool) $flashonly,
- 'autoplay' => (bool) $autoplay
+ 'autoplay' => (bool) $autoplay,
+ 'forcestatic' => $forcestatic,
+ 'hd' => (bool) $hd
);
unset( $freedom );
unset( $flashonly );
@@ -586,7 +602,6 @@ class VideoPress_Video {
$url = 'https://v.wordpress.com/data/wordpress.json';
$response = wp_remote_get( $url . '?' . http_build_query( $request_params, null, '&' ), array(
- 'httpversion' => '1.1',
'redirection' => 1,
'user-agent' => 'VideoPress plugin ' . VideoPress::version . '; WordPress ' . $wp_version . ' (' . home_url('/') . ')'
) );
@@ -791,12 +806,15 @@ class VideoPress_Player {
$content = '';
} elseif ( is_wp_error( $this->video ) ) {
$content = $this->error_message( $this->video );
- } elseif ( ( isset( $this->video->restricted_embed ) && $this->video->restricted_embed === true ) || ( isset( $this->options['force_flash'] ) && $this->options['force_flash'] === true ) ) {
+ } 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();
- } elseif ( ! in_the_loop() ) {
- $content = $this->flash_object();
} else {
$content = $this->html5_dynamic();
}
@@ -1029,7 +1047,8 @@ class VideoPress_Player {
'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' ) )
+ '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 ) )
@@ -1046,10 +1065,7 @@ class VideoPress_Player {
$guid = $this->video->guid;
$guid_js = json_encode( $guid );
$html .= '<script type="text/javascript">' . PHP_EOL;
-
- // Only need to wait until document is ready if the JS is being loaded in the footer
- if ( ! $videopress->js_loaded )
- $html .= 'jQuery(document).ready(function() {';
+ $html .= '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;
@@ -1096,8 +1112,7 @@ class VideoPress_Player {
$html .= '}';
// close the jQuery(document).ready() function
- if ( !$videopress->js_loaded )
- $html .= '});';
+ $html .= '});';
}
$html .= '</script>' . PHP_EOL;
$html .= '</div>' . PHP_EOL;
diff --git a/plugins/jetpack/modules/shortcodes/vimeo.php b/plugins/jetpack/modules/shortcodes/vimeo.php
index 2225a5fe..02b417e1 100644
--- a/plugins/jetpack/modules/shortcodes/vimeo.php
+++ b/plugins/jetpack/modules/shortcodes/vimeo.php
@@ -39,14 +39,14 @@ function vimeo_shortcode( $atts ) {
if ( isset( $args['w'] ) ) {
$width = (int) $args['w'];
-
+
if ( ! isset( $args['h'] ) ) {
// The case where w=300 is specified without h=200, otherwise $height
// will always equal the default of 300, no matter what w was set to.
$height = round( ( $width / 640 ) * 360 );
}
}
-
+
if ( isset( $args['h'] ) ) {
$height = (int) $args['h'];
@@ -54,7 +54,7 @@ function vimeo_shortcode( $atts ) {
$width = round( ( $height / 360 ) * 640 );
}
}
-
+
if ( ! $width )
$width = absint( $content_width );
@@ -71,13 +71,13 @@ function vimeo_shortcode( $atts ) {
add_shortcode( 'vimeo', 'vimeo_shortcode' );
function vimeo_embed_to_shortcode( $content ) {
- if ( false === stripos( $content, 'player.vimeo.com/video/' ) )
+ if ( false === stripos( $content, 'player.vimeo.com/video/' ) )
return $content;
$regexp = '!<iframe\s+src=[\'"]http://player\.vimeo\.com/video/(\d+)[\'"]((?:\s+\w+=[\'"][^\'"]*[\'"])*)></iframe>!i';
- $regexp_ent = str_replace( '&amp;#0*58;', '&amp;#0*58;|&#0*58;', htmlspecialchars( $regexp, ENT_NOQUOTES ) );
-
- foreach ( array( 'regexp', 'regexp_ent' ) as $reg ) {
+ $regexp_ent = str_replace( '&amp;#0*58;', '&amp;#0*58;|&#0*58;', htmlspecialchars( $regexp, ENT_NOQUOTES ) );
+
+ foreach ( array( 'regexp', 'regexp_ent' ) as $reg ) {
if ( !preg_match_all( $$reg, $content, $matches, PREG_SET_ORDER ) )
continue;
@@ -86,7 +86,7 @@ function vimeo_embed_to_shortcode( $content ) {
$params = $match[2];
- if ( 'regexp_ent' == $reg )
+ if ( 'regexp_ent' == $reg )
$params = html_entity_decode( $params );
$params = wp_kses_hair( $params, array( 'http' ) );
@@ -95,8 +95,8 @@ function vimeo_embed_to_shortcode( $content ) {
$height = isset( $params['height'] ) ? (int) $params['height']['value'] : 0;
$wh = '';
- if ( $width && $height )
- $wh = ' w=' . $width . ' h=' . $height;
+ if ( $width && $height )
+ $wh = ' w=' . $width . ' h=' . $height;
$shortcode = '[vimeo ' . $id . $wh . ']';
$content = str_replace( $match[0], $shortcode, $content );
diff --git a/plugins/jetpack/modules/shortcodes/youtube.php b/plugins/jetpack/modules/shortcodes/youtube.php
index ebf989f0..74c497ed 100644
--- a/plugins/jetpack/modules/shortcodes/youtube.php
+++ b/plugins/jetpack/modules/shortcodes/youtube.php
@@ -140,6 +140,7 @@ function youtube_sanitize_url( $url ) {
function get_youtube_id( $url ) {
$url = youtube_sanitize_url( $url );
$url = parse_url( $url );
+ $id = false;
if ( ! isset( $url['query'] ) )
return false;
@@ -149,7 +150,8 @@ function get_youtube_id( $url ) {
if ( ! isset( $qargs['v'] ) && ! isset( $qargs['list'] ) )
return false;
- $id = preg_replace( '|[^_a-z0-9-]|i', '', $qargs['list'] );
+ if ( isset( $qargs['list'] ) )
+ $id = preg_replace( '|[^_a-z0-9-]|i', '', $qargs['list'] );
if ( empty( $id ) )
$id = preg_replace( '|[^_a-z0-9-]|i', '', $qargs['v'] );
@@ -175,12 +177,6 @@ function youtube_id( $url ) {
parse_str( $url['query'], $qargs );
- $agent = $_SERVER['HTTP_USER_AGENT'];
- // Bloglines & Google Reader handle YouTube well now, instead of
- // big blank space of yester year, so they can skip this treatment
- if ( is_feed() && ! preg_match( '#' . apply_filters( 'jetpack_shortcode_youtube_whitelist_user_agents', 'Bloglines|FeedFetcher-Google|feedburner' ) . '#i', $agent ) )
- return '<span style="text-align:center; display: block;"><a href="' . get_permalink() . '"><img src="http://img.youtube.com/vi/' . $id . '/2.jpg" alt="" /></a></span>';
-
// calculate the width and height, taking content_width into consideration
global $content_width;
diff --git a/plugins/jetpack/modules/stats.php b/plugins/jetpack/modules/stats.php
index 32c6cf2c..260b3f58 100644
--- a/plugins/jetpack/modules/stats.php
+++ b/plugins/jetpack/modules/stats.php
@@ -10,11 +10,26 @@ if ( defined( 'STATS_VERSION' ) ) {
return;
}
-define( 'STATS_VERSION', '7' );
+define( 'STATS_VERSION', '8' );
defined( 'STATS_DASHBOARD_SERVER' ) or define( 'STATS_DASHBOARD_SERVER', 'dashboard.wordpress.com' );
add_action( 'jetpack_modules_loaded', 'stats_load' );
+// Tell HQ about changed settings
+Jetpack_Sync::sync_options( __FILE__,
+ 'stats_options',
+ 'home',
+ 'siteurl',
+ 'blogname',
+ 'blogdescription',
+ 'gmt_offset',
+ 'timezone_string',
+ 'page_on_front',
+ 'permalink_structure',
+ 'category_base',
+ 'tag_base'
+);
+
function stats_load() {
global $wp_roles;
@@ -23,6 +38,14 @@ function stats_load() {
Jetpack::module_configuration_head( __FILE__, 'stats_configuration_head' );
Jetpack::module_configuration_screen( __FILE__, 'stats_configuration_screen' );
+ // Tell HQ about changed posts
+ $post_stati = get_post_stati( array( 'public' => true ) ); // All public post stati
+ $post_stati[] = 'private'; // Content from private stati will be redacted
+ Jetpack_Sync::sync_posts( __FILE__, array(
+ 'post_types' => get_post_types( array( 'public' => true ) ), // All public post types
+ 'post_stati' => $post_stati,
+ ) );
+
// Generate the tracking code after wp() has queried for posts.
add_action( 'template_redirect', 'stats_template_redirect', 1 );
@@ -32,22 +55,6 @@ function stats_load() {
add_action( 'wp_dashboard_setup', 'stats_register_dashboard_widget' );
- // Tell HQ about changed settings
- add_action( 'update_option_home', 'stats_update_blog' );
- add_action( 'update_option_siteurl', 'stats_update_blog' );
- add_action( 'update_option_blogname', 'stats_update_blog' );
- add_action( 'update_option_blogdescription', 'stats_update_blog' );
- add_action( 'update_option_timezone_string', 'stats_update_blog' );
- add_action( 'add_option_timezone_string', 'stats_update_blog' );
- add_action( 'update_option_gmt_offset', 'stats_update_blog' );
- add_action( 'update_option_page_on_front', 'stats_update_blog' );
- add_action( 'update_option_permalink_structure', 'stats_update_blog' );
- add_action( 'update_option_category_base', 'stats_update_blog' );
- add_action( 'update_option_tag_base', 'stats_update_blog' );
-
- // Tell HQ about changed posts
- add_action( 'save_post', 'stats_update_post', 10, 1 );
-
add_filter( 'jetpack_xmlrpc_methods', 'stats_xmlrpc_methods' );
// Map stats caps
@@ -111,6 +118,7 @@ function stats_template_redirect() {
add_action( 'wp_head', 'stats_add_shutdown_action' );
$blog = Jetpack::get_option( 'id' );
+ $tz = get_option( 'gmt_offset' );
$v = 'ext';
$j = sprintf( '%s:%s', JETPACK__API_VERSION, JETPACK__VERSION );
if ( $wp_the_query->is_single || $wp_the_query->is_page || $wp_the_query->is_posts_page ) {
@@ -134,7 +142,7 @@ function stats_template_redirect() {
$http = is_ssl() ? 'https' : 'http';
$week = gmdate( 'YW' );
- $data = stats_array( compact( 'v', 'j', 'blog', 'post' ) );
+ $data = stats_array( compact( 'v', 'j', 'blog', 'post', 'tz' ) );
$stats_footer = <<<END
@@ -369,7 +377,7 @@ function stats_reports_page() {
'data' => 'data',
'blog_subscribers' => 'int',
'comment_subscribers' => null,
- 'type' => array( 'email', 'pending' ),
+ 'type' => array( 'wpcom', 'email', 'pending' ),
'pagenum' => 'int',
);
foreach ( $args as $var => $vals ) {
@@ -401,24 +409,21 @@ function stats_reports_page() {
$url = add_query_arg( $q, $url );
$method = 'GET';
$timeout = 90;
- $user_id = 1; // means send the wp.com user_id, not 1
+ $user_id = JETPACK_MASTER_USER; // means send the wp.com user_id
$get = Jetpack_Client::remote_request( compact( 'url', 'method', 'timeout', 'user_id' ) );
$get_code = wp_remote_retrieve_response_code( $get );
- $get_code_type = intval( $get_code / 100 );
- if ( is_wp_error( $get ) || ( 2 != $get_code_type && 304 != $get_code ) ) {
- // @todo nicer looking error
- if ( 3 == $get_code_type ) {
- echo '<p>' . __( 'We were unable to get your stats just now (too many redirects). Please try again.', 'jetpack' ) . '</p>';
- } else {
- echo '<p>' . __( 'We were unable to get your stats just now. Please try again.', 'jetpack' ) . '</p>';
- }
+ if ( is_wp_error( $get ) || ( 2 != intval( $get_code / 100 ) && 304 != $get_code ) || empty( $get['body'] ) ) {
+ stats_print_wp_remote_error( $get, $url );
} else {
if ( !empty( $get['headers']['content-type'] ) ) {
$type = $get['headers']['content-type'];
if ( substr( $type, 0, 5 ) == 'image' ) {
+ $img = $get['body'];
header( 'Content-Type: ' . $type );
- die( $get['body'] );
+ header( 'Content-Length: ' . strlen( $img ) );
+ echo $img;
+ die();
}
}
$body = stats_convert_post_titles( $get['body'] );
@@ -442,7 +447,13 @@ function stats_convert_image_urls( $html ) {
}
function stats_convert_chart_urls( $html ) {
- $html = preg_replace( '|https?://[-.a-z0-9]+/wp-includes/charts/([-.a-z0-9]+).php|', 'admin.php?page=stats&noheader&chart=$1', $html );
+ $html = preg_replace_callback( '|https?://[-.a-z0-9]+/wp-includes/charts/([-.a-z0-9]+).php(\??)|',
+ create_function(
+ '$matches',
+ // If there is a query string, change the beginning '?' to a '&' so it fits into the middle of this query string
+ 'return "admin.php?page=stats&noheader&chart=" . $matches[1] . str_replace( "?", "&", $matches[2] );'
+ ),
+ $html );
return $html;
}
@@ -591,7 +602,9 @@ function stats_admin_bar_menu( &$wp_admin_bar ) {
$img_src = esc_attr( add_query_arg( array( 'noheader'=>'', 'proxy'=>'', 'chart'=>'admin-bar-hours-scale' ), $url ) );
$img_src_2x = esc_attr( add_query_arg( array( 'noheader'=>'', 'proxy'=>'', 'chart'=>'admin-bar-hours-scale-2x' ), $url ) );
- $title = __( 'Views over 48 hours. Click for more Site Stats.', 'jetpack' );
+ $alt = esc_attr( __( 'Stats', 'jetpack' ) );
+
+ $title = esc_attr( __( 'Views over 48 hours. Click for more Site Stats.', 'jetpack' ) );
$menu = array( 'id' => 'stats', 'title' => "<div><script type='text/javascript'>var src;if(typeof(window.devicePixelRatio)=='undefined'||window.devicePixelRatio<2){src='$img_src';}else{src='$img_src_2x';}document.write('<img src=\''+src+'\' alt=\'$alt\' title=\'$title\' />');</script></div>", 'href' => $url );
@@ -602,14 +615,6 @@ function stats_update_blog() {
Jetpack::xmlrpc_async_call( 'jetpack.updateBlog', stats_get_blog() );
}
-function stats_update_post( $post ) {
- if ( !$stats_post = stats_get_post( $post ) )
- return;
-
- $jetpack = Jetpack::init();
- $jetpack->sync->post( $stats_post->ID, array_keys( get_object_vars( $stats_post ) ) );
-}
-
function stats_get_blog() {
$home = parse_url( trailingslashit( get_option( 'home' ) ) );
$blog = array(
@@ -632,36 +637,9 @@ function stats_get_blog() {
return array_map( 'esc_html', $blog );
}
-function stats_get_posts( $args ) {
- list( $post_ids ) = $args;
- $post_ids = array_map( 'intval', (array) $post_ids );
- $r = array(
- 'include' => $post_ids,
- 'post_type' => array_values( get_post_types( array( 'public' => true ) ) ),
- 'post_status' => array_values( get_post_stati( array( 'public' => true ) ) ),
- );
- $posts = get_posts( $r );
- foreach ( $posts as $i => $post )
- $posts[$i] = stats_get_post( $post );
- return $posts;
-}
-
-function stats_get_post( $post ) {
- if ( !$post = get_post( $post ) ) {
- return null;
- }
-
- $stats_post = wp_clone( $post );
- $stats_post->permalink = get_permalink( $post );
- foreach ( array( 'post_content', 'post_excerpt', 'post_content_filtered', 'post_password' ) as $do_not_want )
- unset( $stats_post->$do_not_want );
- return $stats_post;
-}
-
function stats_xmlrpc_methods( $methods ) {
$my_methods = array(
'jetpack.getBlog' => 'stats_get_blog',
- 'jetpack.getPosts' => 'stats_get_posts',
);
return array_merge( $methods, $my_methods );
@@ -737,7 +715,7 @@ function stats_dashboard_widget_control() {
</p>
<p>
- <label for="top"><?php _e( 'Show top posts over', 'jetpack'); ?></label>
+ <label for="top"><?php _e( 'Show top posts over', 'jetpack' ); ?></label>
<select id="top" name="top">
<?php
foreach ( $intervals as $val => $label ) {
@@ -750,7 +728,7 @@ function stats_dashboard_widget_control() {
</p>
<p>
- <label for="search"><?php _e( 'Show top search terms over', 'jetpack'); ?></label>
+ <label for="search"><?php _e( 'Show top search terms over', 'jetpack' ); ?></label>
<select id="search" name="search">
<?php
foreach ( $intervals as $val => $label ) {
@@ -892,18 +870,12 @@ function stats_dashboard_widget_content() {
$url = add_query_arg( $q, $url );
$method = 'GET';
$timeout = 90;
- $user_id = 1; // means send the wp.com user_id, not 1
+ $user_id = JETPACK_MASTER_USER;
$get = Jetpack_Client::remote_request( compact( 'url', 'method', 'timeout', 'user_id' ) );
$get_code = wp_remote_retrieve_response_code( $get );
- $get_code_type = intval( $get_code / 100 );
- if ( is_wp_error( $get ) || ( 2 != $get_code_type && 304 != $get_code ) || empty( $get['body'] ) ) {
- // @todo
- if ( 3 == $get_code_type ) {
- echo '<p>' . __( 'We were unable to get your stats just now (too many redirects). Please try again.', 'jetpack' ) . '</p>';
- } else {
- echo '<p>' . __( 'We were unable to get your stats just now. Please try again.', 'jetpack' ) . '</p>';
- }
+ if ( is_wp_error( $get ) || ( 2 != intval( $get_code / 100 ) && 304 != $get_code ) || empty( $get['body'] ) ) {
+ stats_print_wp_remote_error( $get, $url );
} else {
$body = stats_convert_post_titles($get['body']);
$body = stats_convert_chart_urls($body);
@@ -976,6 +948,50 @@ function stats_dashboard_widget_content() {
exit;
}
+function stats_print_wp_remote_error( $get, $url ) {
+ $state_name = 'stats_remote_error_' . substr( md5( $url ), 0, 8 );
+ $previous_error = Jetpack::state( $state_name );
+ $error = md5( serialize( compact( 'get', 'url' ) ) );
+ Jetpack::state( $state_name, $error );
+ if ( $error !== $previous_error ) {
+?>
+ <div class="wrap">
+ <p><?php _e( 'We were unable to get your stats just now. Please reload this page to try again.', 'jetpack' ); ?></p>
+ </div>
+<?php
+ return;
+ }
+?>
+ <div class="wrap">
+ <p><?php printf( __( 'We were unable to get your stats just now. Please reload this page to try again. If this error persists, please <a href="%1$s">contact support</a>. In your report please include the information below.', 'jetpack' ), 'http://support.wordpress.com/contact/?jetpack=needs-service' ); ?></p>
+ <pre>
+ User Agent: "<?php print htmlspecialchars( $_SERVER['HTTP_USER_AGENT'] ); ?>"
+ Page URL: "http<?php print (is_ssl()?'s':'') . '://' . htmlspecialchars( $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI'] ); ?>"
+ API URL: "<?php print clean_url( $url ); ?>"
+<?php
+ if ( is_wp_error( $get ) ) {
+ foreach ( $get->get_error_codes() as $code ) {
+ foreach ( $get->get_error_messages($code) as $message ) {
+ ?>
+ <?php print $code . ': "' . $message . '"' ?>
+
+<?php
+ }
+ }
+ } else {
+ $get_code = wp_remote_retrieve_response_code( $get );
+ $content_length = strlen( wp_remote_retrieve_body( $get ) );
+ ?>
+ Response code: "<?php print $get_code ?>"
+ Content length: "<?php print $content_length ?>"
+
+<?php
+ }
+ ?></pre>
+ </div>
+ <?php
+}
+
function stats_get_csv( $table, $args = null ) {
$defaults = array( 'end' => false, 'days' => false, 'limit' => 3, 'post_id' => false, 'summarize' => '' );
@@ -1034,7 +1050,7 @@ function stats_get_csv( $table, $args = null ) {
function stats_get_remote_csv( $url ) {
$method = 'GET';
$timeout = 90;
- $user_id = 1; // means send the wp.com user_id, not 1
+ $user_id = JETPACK_MASTER_USER;
$get = Jetpack_Client::remote_request( compact( 'url', 'method', 'timeout', 'user_id' ) );
$get_code = wp_remote_retrieve_response_code( $get );
diff --git a/plugins/jetpack/modules/subscriptions.php b/plugins/jetpack/modules/subscriptions.php
index 2355867e..39c639ff 100644
--- a/plugins/jetpack/modules/subscriptions.php
+++ b/plugins/jetpack/modules/subscriptions.php
@@ -8,6 +8,23 @@
add_action( 'jetpack_modules_loaded', 'jetpack_subscriptions_load' );
+Jetpack_Sync::sync_options( __FILE__,
+ 'home',
+ 'blogname',
+ 'siteurl',
+ 'page_on_front',
+ 'permalink_structure',
+ 'category_base',
+ 'rss_use_excerpt',
+ 'subscription_options',
+ 'stb_enabled',
+ 'stc_enabled',
+ 'tag_base'
+);
+
+Jetpack_Sync::sync_posts( __FILE__ );
+Jetpack_Sync::sync_comments( __FILE__ );
+
function jetpack_subscriptions_load() {
Jetpack::enable_module_configurable( __FILE__ );
Jetpack::module_configuration_load( __FILE__, 'jetpack_subscriptions_configuration_load' );
@@ -17,6 +34,7 @@ function jetpack_subscriptions_configuration_load() {
wp_safe_redirect( admin_url( 'options-discussion.php#jetpack-subscriptions-settings' ) );
exit;
}
+
class Jetpack_Subscriptions {
var $jetpack = false;
@@ -43,22 +61,6 @@ class Jetpack_Subscriptions {
// Add Configuration Page
add_action( 'admin_init', array( $this, 'configure' ) );
-
- // Handle Posts
- add_action( 'transition_post_status', array( $this, 'transition_post_status' ), 10, 3 );
- add_action( 'trashed_post', array( $this, 'delete_post' ) );
- add_action( 'delete_post', array( $this, 'delete_post' ) );
-
- // Handle Taxonomy
- add_action( 'created_term', array( $this, 'save_taxonomy'), 10, 3);
- add_action( 'edited_term', array( $this, 'save_taxonomy'), 10, 3 );
- add_action( 'delete_term', array( $this, 'delete_taxonomy'), 10, 3 );
-
- // Handle Comments
- add_action( 'wp_insert_comment', array( $this, 'save_comment' ), 10, 2 );
- add_action( 'transition_comment_status', array( $this, 'transition_comment_status' ), 10, 3 );
- add_action( 'trashed_comment', array( $this, 'delete_comment' ) );
- add_action( 'delete_comment', array( $this, 'delete_comment' ) );
// Set up the subscription widget.
add_action( 'widgets_init', array( $this, 'widget_init' ) );
@@ -82,73 +84,6 @@ class Jetpack_Subscriptions {
return 'publish' === $post->post_status && strlen( (string) $post->post_password ) < 1;
}
- function transition_post_status( $new, $old, $the_post ) {
- if ( 'publish' == $old && 'publish' != $new ) {
- // A published post was trashed or something else
- $this->delete_post( $the_post->ID );
- return;
- }
-
- clean_post_cache( $the_post->ID );
-
- // Publish a new post
- if (
- 'publish' != $old
- &&
- $this->post_is_public( $the_post->ID )
- &&
- ( 'post' == $the_post->post_type || 'page' == $the_post->post_type )
- ) {
- $this->jetpack->sync->post( $the_post->ID );
- }
- }
-
- function save_taxonomy( $term, $tt_id, $taxonomy = null ) {
- if ( is_null( $taxonomy ) )
- return;
-
- $tax = get_term_by( 'id', $term, $taxonomy );
- $this->jetpack->sync->taxonomy( $tax->slug, true, $taxonomy );
- }
-
- function delete_taxonomy( $term, $tt_id, $taxonomy ) {
- $tags = get_terms( $taxonomy, array( 'hide_empty' => 0 ) ); // since we can't figure out what the slug is... we will do an array comparison on the remote site and remove old taxonomy...
- $this->jetpack->sync->delete_taxonomy( $tags, $taxonomy );
- }
-
- function delete_post( $id ) {
- $the_post = get_post( $id );
- if ( 'post' == $the_post->post_type || 'page' == $the_post->post_type )
- $this->jetpack->sync->delete_post( $id );
- }
-
- function save_comment( $id, $comment ) {
- if ( !$this->post_is_public( $comment->comment_post_ID ) ) {
- return;
- }
-
- if ( 1 == $comment->comment_approved ) {
- $this->jetpack->sync->comment( $id );
- }
- }
-
- function transition_comment_status( $new, $old, $the_comment ) {
- if ( !$this->post_is_public( $the_comment->comment_post_ID ) ) {
- return;
- }
-
- if ( 'approved' == $new ) {
- $this->jetpack->sync->comment( $the_comment->comment_ID );
- } else if ( 'approved' == $old && 'approved' != $new ) {
- // Delete comments that are changing to anything but approved
- $this->jetpack->sync->delete_comment( $the_comment->comment_ID );
- }
- }
-
- function delete_comment( $id ) {
- $this->jetpack->sync->delete_comment( $id );
- }
-
/**
* Jetpack_Subscriptions::xmlrpc_methods()
*
@@ -204,6 +139,37 @@ class Jetpack_Subscriptions {
'discussion',
'stc_enabled'
);
+
+ /** Subscription Messaging Options ******************************************************/
+
+ register_setting(
+ 'reading',
+ 'subscription_options',
+ array( $this, 'validate_settings' )
+ );
+
+ add_settings_section(
+ 'email_settings',
+ __( 'Follower Settings', 'jetpack' ),
+ array( $this, 'reading_section' ),
+ 'reading'
+ );
+
+ add_settings_field(
+ 'invitation',
+ __( 'Blog follow email text' , 'jetpack' ),
+ array( $this, 'setting_invitation' ),
+ 'reading',
+ 'email_settings'
+ );
+
+ add_settings_field(
+ 'comment-follow',
+ __( 'Comment follow email text', 'jetpack' ),
+ array( $this, 'setting_comment_follow' ),
+ 'reading',
+ 'email_settings'
+ );
}
/**
@@ -249,6 +215,51 @@ class Jetpack_Subscriptions {
<?php
}
+ function validate_settings( $settings ) {
+ global $allowedposttags;
+
+ $default = $this->get_default_settings();
+
+ // Blog Follow
+ $settings['invitation'] = trim( wp_kses( $settings['invitation'], $allowedposttags ) );
+ if ( empty( $settings['invitation'] ) )
+ $settings['invitation'] = $default['invitation'];
+
+ // Comments Follow (single post)
+ $settings['comment_follow'] = trim( wp_kses( $settings['comment_follow'], $allowedposttags ) );
+ if ( empty( $settings['comment_follow'] ) )
+ $settings['comment_follow'] = $default['comment_follow'];
+
+ return $settings;
+ }
+
+ public function reading_section() {
+ _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>';
+ }
+
+ 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>';
+ }
+
+ 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')
+ );
+ }
+
+ function get_settings() {
+ return wp_parse_args( (array) get_option( 'subscription_options', array() ), $this->get_default_settings() );
+ }
+
/**
* Jetpack_Subscriptions::subscribe()
*
@@ -356,7 +367,9 @@ class Jetpack_Subscriptions {
*/
function widget_submit() {
// Check the nonce.
- check_admin_referer( 'blogsub_subscribe_' . get_current_blog_id() );
+ if ( is_user_logged_in() ) {
+ check_admin_referer( 'blogsub_subscribe_' . get_current_blog_id() );
+ }
if ( empty( $_REQUEST['email'] ) )
return false;
@@ -428,17 +441,19 @@ class Jetpack_Subscriptions {
$str = '';
- if ( FALSE === has_filter( 'comment_form', 'show_subscription_checkbox' ) ) {
+ if ( FALSE === has_filter( 'comment_form', 'show_subscription_checkbox' ) && 1 == get_option( 'stc_enabled', 1 ) ) {
// Subscribe to comments checkbox
$str .= '<p class="comment-subscription-form"><input type="checkbox" name="subscribe_comments" id="subscribe_comments" value="subscribe" style="width: auto; -moz-appearance: checkbox; -webkit-appearance: checkbox;"' . $comments_checked . ' /> ';
- $str .= '<label class="subscribe-label" id="subscribe-label" for="subscribe_comments">' . __( 'Notify me of follow-up comments by email.', 'jetpack' ) . '</label>';
+ $str .= '<label class="subscribe-label" id="subscribe-label" for="subscribe_comments" style="display: inline;">' . __( 'Notify me of follow-up comments by email.', 'jetpack' ) . '</label>';
$str .= '</p>';
}
- // Subscribe to blog checkbox
- $str .= '<p class="comment-subscription-form"><input type="checkbox" name="subscribe_blog" id="subscribe_blog" value="subscribe" style="width: auto; -moz-appearance: checkbox; -webkit-appearance: checkbox;"' . $blog_checked . ' /> ';
- $str .= '<label class="subscribe-label" id="subscribe-blog-label" for="subscribe_blog">' . __( 'Notify me of new posts by email.', 'jetpack' ) . '</label>';
- $str .= '</p>';
+ if ( 1 == get_option( 'stb_enabled', 1 ) ) {
+ // Subscribe to blog checkbox
+ $str .= '<p class="comment-subscription-form"><input type="checkbox" name="subscribe_blog" id="subscribe_blog" value="subscribe" style="width: auto; -moz-appearance: checkbox; -webkit-appearance: checkbox;"' . $blog_checked . ' /> ';
+ $str .= '<label class="subscribe-label" id="subscribe-blog-label" for="subscribe_blog" style="display: inline;">' . __( 'Notify me of new posts by email.', 'jetpack' ) . '</label>';
+ $str .= '</p>';
+ }
echo apply_filters( 'jetpack_comment_subscription_form', $str );
}
@@ -516,83 +531,79 @@ class Jetpack_Subscriptions_Widget extends WP_Widget {
extract( $args );
$instance = wp_parse_args( (array) $instance, $this->defaults() );
- $title = stripslashes( $instance['title'] );
- $subscribe_text = stripslashes( $instance['subscribe_text'] );
- $subscribe_button = stripslashes( $instance['subscribe_button'] );
- $subscribe_logged_in = stripslashes( $instance['subscribe_logged_in'] );
+ $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();
if ( ! is_array( $subscribers_total ) )
$show_subscribers_total = FALSE;
- echo $before_widget;
- echo $before_title . '<label for="subscribe-field">' . esc_attr( $instance['title'] ) . '</label>' . $after_title . "\n";
+ echo $args['before_widget'];
+ echo $args['before_title'] . '<label for="subscribe-field">' . esc_attr( $instance['title'] ) . '</label>' . $args['after_title'] . "\n";
$referer = ( is_ssl() ? 'https' : 'http' ) . '://' . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI'];
// Check for subscription confirmation.
- if ( isset( $_GET['subscribe'] ) && 'success' == $_GET['subscribe'] ) {
- ?>
+ if ( isset( $_GET['subscribe'] ) && 'success' == $_GET['subscribe'] ) : ?>
<div class="success">
- <p><?php _e( 'An email was just sent to confirm your subscription. Please find the email now and click activate to start subscribing.', 'jetpack' ); ?></p>
+ <p><?php esc_html_e( 'An email was just sent to confirm your subscription. Please find the email now and click activate to start subscribing.', 'jetpack' ); ?></p>
</div>
- <?php
- }
+ <?php endif;
// Display any errors
if ( isset( $_GET['subscribe'] ) ) :
switch ( $_GET['subscribe'] ) :
- case 'invalid_email' : ?>
- <p class="error"><?php _e( 'The email you entered was invalid, please check and try again.', 'jetpack' ); ?></p>
- <?php break;
- case 'already' : ?>
- <p class="error"><?php _e( 'You have already subscribed to this site, please check your inbox.', 'jetpack' ); ?></p>
- <?php break;
- case 'success' :
-
- echo wpautop( $subscribe_text );
- break;
- default : ?>
- <p class="error"><?php _e( 'There was an error when subscribing, please try again.', 'jetpack' ) ?></p>
- <?php break;
+ case 'invalid_email' : ?>
+ <p class="error"><?php esc_html_e( 'The email you entered was invalid, please check and try again.', 'jetpack' ); ?></p>
+ <?php break;
+ case 'already' : ?>
+ <p class="error"><?php esc_html_e( 'You have already subscribed to this site, please check your inbox.', 'jetpack' ); ?></p>
+ <?php break;
+ case 'success' :
+ echo wpautop( $subscribe_text );
+ break;
+ default : ?>
+ <p class="error"><?php esc_html_e( 'There was an error when subscribing, please try again.', 'jetpack' ) ?></p>
+ <?php break;
endswitch;
endif;
- $email_address = '';
- if ( ! empty( $current_user->user_email ) )
- $email_address = $current_user->user_email;
-
// Display a subscribe form ?>
- <a name="subscribe-blog"></a>
- <form action="" method="post" accept-charset="utf-8" id="subscribe-blog">
+ <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 ); ?>">
<?php
if ( ! isset ( $_GET['subscribe'] ) ) {
?><p><?php echo $subscribe_text ?></p><?php
}
- if ( $show_subscribers_total && $subscribers_total['value'] > 0 ) {
+ if ( $show_subscribers_total && 0 < $subscribers_total['value'] ) {
echo wpautop( sprintf( _n( 'Join %s other subscriber', 'Join %s other subscribers', $subscribers_total['value'], 'jetpack' ), number_format_i18n( $subscribers_total['value'] ) ) );
}
?>
- <p><input type="text" name="email" style="width: 95%; padding: 1px 2px" value="<?php if ( !empty( $email_address ) ) { echo $email_address; } else { _e( 'Email Address', 'jetpack' ); } ?>" id="subscribe-field" onclick="if ( this.value == '<?php _e( 'Email Address', 'jetpack' ) ?>' ) { this.value = ''; }" onblur="if ( this.value == '' ) { this.value = '<?php _e( 'Email Address', 'jetpack' ) ?>'; }" /></p>
+ <p><input type="text" name="email" style="width: 95%; padding: 1px 2px" value="<?php echo !empty( $current_user->user_email ) ? esc_attr( $current_user->user_email ) : esc_html__( 'Email Address', 'jetpack' ); ?>" id="subscribe-field" onclick="if ( this.value == '<?php esc_html_e( 'Email Address', 'jetpack' ) ?>' ) { this.value = ''; }" onblur="if ( this.value == '' ) { this.value = '<?php esc_html_e( 'Email Address', 'jetpack' ) ?>'; }" /></p>
<p>
<input type="hidden" name="action" value="subscribe" />
<input type="hidden" name="source" value="<?php echo esc_url( $referer ); ?>" />
<input type="hidden" name="sub-type" value="<?php echo esc_attr( $source ); ?>" />
<input type="hidden" name="redirect_fragment" value="<?php echo esc_attr( $widget_id ); ?>" />
- <?php wp_nonce_field( 'blogsub_subscribe_'. get_current_blog_id(), '_wpnonce', false ); ?>
+ <?php
+ if ( is_user_logged_in() ) {
+ wp_nonce_field( 'blogsub_subscribe_'. get_current_blog_id(), '_wpnonce', false );
+ }
+ ?>
<input type="submit" value="<?php echo esc_attr( $subscribe_button ); ?>" name="jetpack_subscriptions_widget" />
</p>
</form>
<?php
- echo "\n" . $after_widget;
+ echo "\n" . $args['after_widget'];
}
function increment_subscriber_count( $current_subs_array = array() ) {
@@ -610,7 +621,7 @@ class Jetpack_Subscriptions_Widget extends WP_Widget {
Jetpack:: load_xml_rpc_client();
$xml = new Jetpack_IXR_Client( array(
- 'user_id' => $GLOBALS['current_user']->ID
+ 'user_id' => JETPACK_MASTER_USER,
) );
$xml->query( 'jetpack.fetchSubscriberCount' );
@@ -638,10 +649,10 @@ class Jetpack_Subscriptions_Widget extends WP_Widget {
function update( $new_instance, $old_instance ) {
$instance = $old_instance;
- $instance['title'] = strip_tags( stripslashes( $new_instance['title'] ) );
+ $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'] = strip_tags( stripslashes( $new_instance['subscribe_button'] ) );
+ $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;
@@ -649,10 +660,10 @@ class Jetpack_Subscriptions_Widget extends WP_Widget {
function defaults() {
return array(
- 'title' => __( 'Subscribe to Blog via Email', 'jetpack' ),
- 'subscribe_text' => __( 'Enter your email address to subscribe to this blog and receive notifications of new posts by email.', 'jetpack' ),
- 'subscribe_button' => __( 'Subscribe', 'jetpack' ),
- 'subscribe_logged_in' => __( 'Click to subscribe to this blog and receive notifications of new posts by email.', 'jetpack' ),
+ 'title' => esc_html__( 'Subscribe to Blog via Email', 'jetpack' ),
+ 'subscribe_text' => esc_html__( 'Enter your email address to subscribe to this blog and receive notifications of new posts by email.', 'jetpack' ),
+ 'subscribe_button' => esc_html__( 'Subscribe', 'jetpack' ),
+ 'subscribe_logged_in' => esc_html__( 'Click to subscribe to this blog and receive notifications of new posts by email.', 'jetpack' ),
'show_subscribers_total' => true,
);
}
@@ -660,9 +671,9 @@ class Jetpack_Subscriptions_Widget extends WP_Widget {
function form( $instance ) {
$instance = wp_parse_args( (array) $instance, $this->defaults() );
- $title = esc_attr( stripslashes( $instance['title'] ) );
- $subscribe_text = esc_attr( stripslashes( $instance['subscribe_text'] ) );
- $subscribe_button = esc_attr( stripslashes( $instance['subscribe_button'] ) );
+ $title = stripslashes( $instance['title'] );
+ $subscribe_text = stripslashes( $instance['subscribe_text'] );
+ $subscribe_button = stripslashes( $instance['subscribe_button'] );
$show_subscribers_total = checked( $instance['show_subscribers_total'], true, false );
$subs_fetch = $this->fetch_subscriber_count();
@@ -702,3 +713,13 @@ class Jetpack_Subscriptions_Widget extends WP_Widget {
}
}
+add_shortcode( 'jetpack_subscription_form', 'jetpack_do_subscription_form' );
+
+function jetpack_do_subscription_form( $args ) {
+ $args['show_subscribers_total'] = empty( $args['show_subscribers_total'] ) ? false : true;
+ $args = shortcode_atts( Jetpack_Subscriptions_Widget::defaults(), $args );
+ ob_start();
+ the_widget( 'Jetpack_Subscriptions_Widget', $args );
+ $output = ob_get_clean();
+ return $output;
+}
diff --git a/plugins/jetpack/modules/tiled-gallery.php b/plugins/jetpack/modules/tiled-gallery.php
new file mode 100644
index 00000000..6db87144
--- /dev/null
+++ b/plugins/jetpack/modules/tiled-gallery.php
@@ -0,0 +1,25 @@
+<?php
+
+/**
+ * Module Name: Tiled Galleries
+ * Module Description: Create elegant magazine-style mosaic layouts for your photos without using an external graphic editor.
+ * First Introduced: 2.1
+ */
+
+function jetpack_load_tiled_gallery() {
+ include dirname( __FILE__ ) . "/tiled-gallery/tiled-gallery.php";
+}
+
+add_action( 'jetpack_modules_loaded', 'jetpack_tiled_gallery_loaded' );
+
+function jetpack_tiled_gallery_loaded() {
+ Jetpack::enable_module_configurable( __FILE__ );
+ Jetpack::module_configuration_load( __FILE__, 'jetpack_tiled_gallery_configuration_load' );
+}
+
+function jetpack_tiled_gallery_configuration_load() {
+ wp_safe_redirect( admin_url( 'options-media.php' ) );
+ exit;
+}
+
+jetpack_load_tiled_gallery(); \ No newline at end of file
diff --git a/plugins/jetpack/modules/tiled-gallery/math/class-constrained-array-rounding.php b/plugins/jetpack/modules/tiled-gallery/math/class-constrained-array-rounding.php
new file mode 100644
index 00000000..dab5fd27
--- /dev/null
+++ b/plugins/jetpack/modules/tiled-gallery/math/class-constrained-array-rounding.php
@@ -0,0 +1,75 @@
+<?php
+
+/**
+ * Lets you round the numeric elements of an array to integers while preserving their sum.
+ *
+ * Usage:
+ *
+ * Jetpack_Constrained_Array_Rounding::get_rounded_constrained_array( $bound_array )
+ * if a specific sum doesn't need to be specified for the bound array
+ *
+ * Jetpack_Constrained_Array_Rounding::get_rounded_constrained_array( $bound_array, $sum )
+ * If the sum of $bound_array must equal $sum after rounding.
+ *
+ * If $sum is less than the sum of the floor of the elements of the array, the class defaults to using the sum of the array elements.
+ */
+class Jetpack_Constrained_Array_Rounding {
+ public static function get_rounded_constrained_array( $bound_array, $sum = false ) {
+ // Convert associative arrays before working with them and convert them back before returning the values
+ $keys = array_keys( $bound_array );
+ $bound_array = array_values( $bound_array );
+
+ $bound_array_int = self::get_int_floor_array( $bound_array );
+
+ $lower_sum = array_sum( wp_list_pluck( $bound_array_int, 'floor' ) );
+ if ( ! $sum || ( $sum < $lower_sum ) ) {
+ // If value of sum is not supplied or is invalid, calculate the sum that the returned array is constrained to match
+ $sum = array_sum( $bound_array );
+ }
+ $diff_sum = $sum - $lower_sum;
+
+ self::adjust_constrained_array( $bound_array_int, $diff_sum );
+
+ $bound_array_fin = wp_list_pluck( $bound_array_int, 'floor' );
+ return array_combine( $keys, $bound_array_fin );
+ }
+
+ private static function get_int_floor_array( $bound_array ) {
+ $bound_array_int_floor = array();
+ foreach ( $bound_array as $i => $value ){
+ $bound_array_int_floor[$i] = array(
+ 'floor' => (int) floor( $value ),
+ 'fraction' => $value - floor( $value ),
+ 'index' => $i,
+ );
+ }
+
+ return $bound_array_int_floor;
+ }
+
+ private static function adjust_constrained_array( &$bound_array_int, $adjustment ) {
+ usort( $bound_array_int, array( 'self', 'cmp_desc_fraction' ) );
+
+ $start = 0;
+ $end = $adjustment - 1;
+ $length = count( $bound_array_int );
+
+ for ( $i = $start; $i <= $end; $i++ ) {
+ $bound_array_int[ $i % $length ]['floor']++;
+ }
+
+ usort( $bound_array_int, array( 'self', 'cmp_asc_index' ) );
+ }
+
+ private static function cmp_desc_fraction( $a, $b ) {
+ if ( $a['fraction'] == $b['fraction'] )
+ return 0;
+ return $a['fraction'] > $b['fraction'] ? -1 : 1;
+ }
+
+ private static function cmp_asc_index( $a, $b ) {
+ if ( $a['index'] == $b['index'] )
+ return 0;
+ return $a['index'] < $b['index'] ? -1 : 1;
+ }
+}
diff --git a/plugins/jetpack/modules/tiled-gallery/tiled-gallery.php b/plugins/jetpack/modules/tiled-gallery/tiled-gallery.php
new file mode 100644
index 00000000..342e8a65
--- /dev/null
+++ b/plugins/jetpack/modules/tiled-gallery/tiled-gallery.php
@@ -0,0 +1,588 @@
+<?php
+
+// Include the class file containing methods for rounding constrained array elements.
+// Here the constrained array element is the dimension of a row, group or an image in the tiled gallery.
+include_once dirname( __FILE__ ) . '/math/class-constrained-array-rounding.php';
+
+class Jetpack_Tiled_Gallery {
+
+ public function __construct() {
+ add_action( 'admin_init', array( $this, 'settings_api_init' ) );
+ add_filter( 'jetpack_gallery_types', array( $this, 'jetpack_gallery_types' ), 9 );
+ }
+
+ public function tiles_enabled() {
+ // Check the setting status
+ return '' != get_option( 'tiled_galleries' );
+ }
+
+ public function set_atts( $atts ) {
+ global $post;
+
+ $this->atts = shortcode_atts( array(
+ 'order' => 'ASC',
+ 'orderby' => 'menu_order ID',
+ 'id' => $post->ID,
+ 'include' => '',
+ 'exclude' => '',
+ 'type' => '',
+ 'grayscale' => false,
+ 'link' => '',
+ ), $atts );
+
+ $this->atts['id'] = (int) $this->atts['id'];
+ $this->float = is_rtl() ? 'right' : 'left';
+
+ // Default to rectangular is tiled galleries are checked
+ if ( $this->tiles_enabled() && ( ! $this->atts['type'] || 'default' == $this->atts['type'] ) )
+ $this->atts['type'] = 'rectangular';
+
+ if ( !$this->atts['orderby'] ) {
+ $this->atts['orderby'] = sanitize_sql_orderby( $this->atts['orderby'] );
+ if ( !$this->atts['orderby'] )
+ $this->atts['orderby'] = 'menu_order ID';
+ }
+
+ if ( 'RAND' == $this->atts['order'] )
+ $this->atts['orderby'] = 'none';
+ }
+
+ public function get_attachments() {
+ extract( $this->atts );
+
+ if ( !empty( $include ) ) {
+ $include = preg_replace( '/[^0-9,]+/', '', $include );
+ $_attachments = get_posts( array('include' => $include, 'post_status' => 'inherit', 'post_type' => 'attachment', 'post_mime_type' => 'image', 'order' => $order, 'orderby' => $orderby) );
+
+ $attachments = array();
+ foreach ( $_attachments as $key => $val ) {
+ $attachments[$val->ID] = $_attachments[$key];
+ }
+ } 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) );
+ } else {
+ $attachments = get_children( array('post_parent' => $id, 'post_status' => 'inherit', 'post_type' => 'attachment', 'post_mime_type' => 'image', 'order' => $order, 'orderby' => $orderby ) );
+ }
+ return $attachments;
+ }
+
+ public function get_attachment_link( $attachment_id, $orig_file ) {
+ if ( isset( $this->atts['link'] ) && $this->atts['link'] == 'file' )
+ return $orig_file;
+ else
+ return get_attachment_link( $attachment_id );
+ }
+
+ public function default_scripts_and_styles() {
+ wp_enqueue_script( 'tiled-gallery', plugins_url( 'tiled-gallery/tiled-gallery.js', __FILE__ ), array( 'jquery' ) );
+ wp_enqueue_style( 'tiled-gallery', plugins_url( 'tiled-gallery/tiled-gallery.css', __FILE__ ), array(), '2012-09-21' );
+ }
+
+ public function gallery_shortcode( $val, $atts ) {
+ if ( ! empty( $val ) ) // something else is overriding post_gallery, like a custom VIP shortcode
+ return $val;
+
+ global $post;
+
+ $this->set_atts( $atts );
+
+ $attachments = $this->get_attachments();
+ if ( empty( $attachments ) )
+ return '';
+
+ if ( is_feed() || defined( 'IS_HTML_EMAIL' ) )
+ return '';
+
+ if ( method_exists( $this, $this->atts['type'] . '_talavera' ) ) {
+ // Enqueue styles and scripts
+ $this->default_scripts_and_styles();
+ $gallery_html = call_user_func_array( array( $this, $this->atts['type'] . '_talavera' ), array( $attachments ) );
+
+ if ( $gallery_html && class_exists( 'Jetpack' ) && class_exists( 'Jetpack_Photon' ) ) {
+ // Tiled Galleries in Jetpack require that Photon be active.
+ // If it's not active, run it just on the gallery output.
+ if ( ! in_array( 'photon', Jetpack::get_active_modules() ) )
+ $gallery_html = Jetpack_Photon::filter_the_content( $gallery_html );
+ }
+
+ return $gallery_html;
+ }
+
+ return '';
+ }
+
+ public function rectangular_talavera( $attachments ) {
+ $grouper = new Jetpack_Tiled_Gallery_Grouper( $attachments );
+
+ Jetpack_Tiled_Gallery_Shape::reset_last_shape();
+
+ $output = $this->generate_carousel_container();
+ foreach ( $grouper->grouped_images as $row ) {
+ $output .= '<div class="gallery-row" style="' . esc_attr( 'width: ' . $row->width . 'px; height: ' . ( $row->height - 4 ) . 'px;' ) . '">';
+ foreach( $row->groups as $group ) {
+ $count = count( $group->images );
+ $output .= '<div class="gallery-group images-' . esc_attr( $count ) . '" style="' . esc_attr( 'width: ' . $group->width . 'px; height: ' . $group->height . 'px;' ) . '">';
+ foreach ( $group->images as $image ) {
+
+ $size = 'large';
+ if ( $image->width < 250 )
+ $size = 'small';
+
+ $image_title = $image->post_title;
+ $orig_file = wp_get_attachment_url( $image->ID );
+ $link = $this->get_attachment_link( $image->ID, $orig_file );
+
+ $img_src = add_query_arg( array( 'w' => $image->width, 'h' => $image->height ), $orig_file );
+
+ $output .= '<div class="tiled-gallery-item tiled-gallery-item-' . esc_attr( $size ) . '"><a href="' . esc_url( $link ) . '"><img ' . $this->generate_carousel_image_args( $image ) . ' src="' . esc_url( $img_src ) . '" width="' . esc_attr( $image->width ) . '" height="' . esc_attr( $image->height ) . '" align="left" title="' . esc_attr( $image_title ) . '" /></a>';
+
+ if ( $this->atts['grayscale'] == true ) {
+ $img_src_grayscale = jetpack_photon_url( $img_src, array( 'filter' => 'grayscale' ) );
+ $output .= '<a href="'. esc_url( $link ) . '"><img ' . $this->generate_carousel_image_args( $image ) . ' class="grayscale" src="' . esc_url( $img_src_grayscale ) . '" width="' . esc_attr( $image->width ) . '" height="' . esc_attr( $image->height ) . '" align="left" title="' . esc_attr( $image_title ) . '" /></a>';
+ }
+
+ if ( trim( $image->post_excerpt ) )
+ $output .= '<div class="tiled-gallery-caption">' . wptexturize( $image->post_excerpt ) . '</div>';
+
+ $output .= '</div>';
+ }
+ $output .= '</div>';
+ }
+ $output .= '</div>';
+ }
+ $output .= '</div>';
+ return $output;
+ }
+
+ public function square_talavera( $attachments ) {
+ $content_width = self::get_content_width();
+ $images_per_row = 3;
+ $margin = 2;
+
+ $margin_space = ( $images_per_row * $margin ) * 2;
+ $size = floor( ( $content_width - $margin_space ) / $images_per_row );
+ $remainder = count( $attachments ) % $images_per_row;
+ if ( $remainder > 0 ) {
+ $remainder_space = ( $remainder * $margin ) * 2;
+ $remainder_size = ceil( ( $content_width - $remainder_space - $margin ) / $remainder );
+ }
+ $output = $this->generate_carousel_container();
+ $c = 1;
+ foreach( $attachments as $image ) {
+ if ( $remainder > 0 && $c <= $remainder )
+ $img_size = $remainder_size;
+ else
+ $img_size = $size;
+
+ $orig_file = wp_get_attachment_url( $image->ID );
+ $link = $this->get_attachment_link( $image->ID, $orig_file );
+ $image_title = $image->post_title;
+
+ $img_src = add_query_arg( array( 'w' => $img_size, 'h' => $img_size, 'crop' => 1 ), $orig_file );
+
+ $output .= '<div class="tiled-gallery-item">';
+ $output .= '<a border="0" href="' . esc_url( $link ) . '"><img ' . $this->generate_carousel_image_args( $image ) . ' style="' . esc_attr( 'margin: ' . $margin . 'px' ) . '" src="' . esc_url( $img_src ) . '" width=' . esc_attr( $img_size ) . ' height=' . esc_attr( $img_size ) . ' title="' . esc_attr( $image_title ) . '" /></a>';
+
+ // Grayscale effect
+ if ( $this->atts['grayscale'] == true ) {
+ $src = urlencode( $image->guid );
+ $output .= '<a border="0" href="' . esc_url( $link ) . '"><img ' . $this->generate_carousel_image_args( $image ) . ' style="margin: 2px" class="grayscale" src="' . esc_url( 'http://en.wordpress.com/imgpress?url=' . urlencode( $image->guid ) . '&resize=' . $img_size . ',' . $img_size . '&filter=grayscale' ) . '" width=' . esc_attr( $img_size ) . ' height=' . esc_attr( $img_size ) . ' title="' . esc_attr( $image_title ) . '" /></a>';
+ }
+
+ // Captions
+ if ( trim( $image->post_excerpt ) )
+ $output .= '<div class="tiled-gallery-caption">' . wptexturize( $image->post_excerpt ) . '</div>';
+ $output .= '</div>';
+ $c ++;
+ }
+ $output .= '</div>';
+ return $output;
+ }
+
+ public function circle_talavera( $attachments ) {
+ return $this->square_talavera( $attachments );
+ }
+
+ public function rectangle_talavera( $attachments ) {
+ return $this->rectangular_talavera( $attachments );
+ }
+
+ function generate_carousel_container() {
+ global $post;
+
+ $html = '<div '. $this->gallery_classes() . ' data-original-width="' . esc_attr( self::get_content_width() ) . '">';
+ $blog_id = (int) get_current_blog_id();
+ $extra_data = array( 'data-carousel-extra' => array( 'blog_id' => $blog_id, 'permalink' => get_permalink( $post->ID ) ) );
+
+ foreach ( (array) $extra_data as $data_key => $data_values ) {
+ $html = str_replace( '<div ', '<div ' . esc_attr( $data_key ) . "='" . json_encode( $data_values ) . "' ", $html );
+ }
+
+ return $html;
+ }
+
+ function generate_carousel_image_args( $image ) {
+ $attachment_id = $image->ID;
+ $orig_file = wp_get_attachment_url( $attachment_id );
+ $meta = wp_get_attachment_metadata( $attachment_id );
+ $size = isset( $meta['width'] ) ? intval( $meta['width'] ) . ',' . intval( $meta['height'] ) : '';
+ $img_meta = ( ! empty( $meta['image_meta'] ) ) ? (array) $meta['image_meta'] : array();
+ $comments_opened = intval( comments_open( $attachment_id ) );
+
+ $medium_file_info = wp_get_attachment_image_src( $attachment_id, 'medium' );
+ $medium_file = isset( $medium_file_info[0] ) ? $medium_file_info[0] : '';
+
+ $large_file_info = wp_get_attachment_image_src( $attachment_id, 'large' );
+ $large_file = isset( $large_file_info[0] ) ? $large_file_info[0] : '';
+ $attachment_title = wptexturize( $image->post_title );
+ $attachment_desc = wpautop( wptexturize( $image->post_content ) );
+
+ // Not yet providing geo-data, need to "fuzzify" for privacy
+ if ( ! empty( $img_meta ) ) {
+ foreach ( $img_meta as $k => $v ) {
+ if ( 'latitude' == $k || 'longitude' == $k )
+ unset( $img_meta[$k] );
+ }
+ }
+
+ $img_meta = json_encode( array_map( 'strval', $img_meta ) );
+
+ $output = sprintf(
+ 'data-attachment-id="%1$d" data-orig-file="%2$s" data-orig-size="%3$s" data-comments-opened="%4$s" data-image-meta="%5$s" data-image-title="%6$s" data-image-description="%7$s" data-medium-file="%8$s" data-large-file="%9$s"',
+ esc_attr( $attachment_id ),
+ esc_url( wp_get_attachment_url( $attachment_id ) ),
+ esc_attr( $size ),
+ esc_attr( $comments_opened ),
+ esc_attr( $img_meta ),
+ esc_attr( $attachment_title ),
+ esc_attr( $attachment_desc ),
+ esc_url( $medium_file ),
+ esc_url( $large_file )
+ );
+ return $output;
+ }
+
+ public function gallery_classes() {
+ $classes = 'class="tiled-gallery type-' . esc_attr( $this->atts['type'] ) . '"';
+ return $classes;
+ }
+
+ public static function gallery_already_redefined() {
+ global $shortcode_tags;
+ if ( ! isset( $shortcode_tags[ 'gallery' ] ) || $shortcode_tags[ 'gallery' ] !== 'gallery_shortcode' )
+ return true;
+ }
+
+ public static function init() {
+ if ( self::gallery_already_redefined() )
+ return;
+
+ $gallery = new Jetpack_Tiled_Gallery;
+ add_filter( 'post_gallery', array( $gallery, 'gallery_shortcode' ), 1001, 2 );
+ }
+
+ public static function get_content_width() {
+ global $content_width;
+
+ $tiled_gallery_content_width = $content_width;
+
+ if ( ! $tiled_gallery_content_width )
+ $tiled_gallery_content_width = 500;
+
+ return apply_filters( 'tiled_gallery_content_width', $tiled_gallery_content_width );
+ }
+
+ /**
+ * Media UI integration
+ */
+ function jetpack_gallery_types( $types ) {
+ $types['rectangular'] = __( 'Tiles', 'jetpack' );
+ $types['square'] = __( 'Square Tiles', 'jetpack' );
+ $types['circle'] = __( 'Circles', 'jetpack' );
+ return $types;
+ }
+
+ /**
+ * Add a checkbox field to the Carousel section in Settings > Media
+ * for setting tiled galleries as the default.
+ */
+ function settings_api_init() {
+ global $wp_settings_sections;
+
+ // Add the setting field [tiled_galleries] and place it in Settings > Media
+ if ( isset( $wp_settings_sections['media']['carousel_section'] ) )
+ $section = 'carousel_section';
+ else
+ $section = 'default';
+
+ add_settings_field( 'tiled_galleries', __( 'Tiled Galleries', 'jetpack' ), array( $this, 'setting_html' ), 'media', $section );
+ register_setting( 'media', 'tiled_galleries', 'esc_attr' );
+ }
+
+ function setting_html() {
+ echo '<label><input name="tiled_galleries" type="checkbox" value="1" ' .
+ checked( 1, '' != get_option( 'tiled_galleries' ), false ) . ' /> ' .
+ __( 'Display all your gallery pictures in a cool mosaic.', 'jetpack' ) . '</br></label>';
+ }
+}
+
+class Jetpack_Tiled_Gallery_Shape {
+ static $shapes_used = array();
+
+ public function __construct( $images ) {
+ $this->images = $images;
+ $this->images_left = count( $images );
+ }
+
+ public function sum_ratios( $number_of_images = 3 ) {
+ return array_sum( array_slice( wp_list_pluck( $this->images, 'ratio' ), 0, $number_of_images ) );
+ }
+
+ public function next_images_are_symmetric() {
+ return $this->images_left > 2 && $this->images[0]->ratio == $this->images[2]->ratio;
+ }
+
+ public function is_not_as_previous( $n = 1 ) {
+ return ! in_array( get_class( $this ), array_slice( self::$shapes_used, -$n ) );
+ }
+
+ public function is_wide_theme() {
+ global $content_width;
+ return $content_width > 1000;
+ }
+
+ public static function set_last_shape( $last_shape ) {
+ self::$shapes_used[] = $last_shape;
+ }
+
+ public static function reset_last_shape() {
+ self::$shapes_used = array();
+ }
+}
+
+class Jetpack_Tiled_Gallery_Three extends Jetpack_Tiled_Gallery_Shape {
+ public $shape = array( 1, 1, 1 );
+
+ public function is_possible() {
+ $ratio = $this->sum_ratios( 3 );
+ return $this->images_left > 2 && $this->is_not_as_previous() &&
+ ( ( $ratio < 2.5 ) || ( $ratio < 5 && $this->next_images_are_symmetric() ) || $this->is_wide_theme() );
+ }
+}
+
+class Jetpack_Tiled_Gallery_Four extends Jetpack_Tiled_Gallery_Shape {
+ public $shape = array( 1, 1, 1, 1 );
+
+ public function is_possible() {
+ return $this->is_not_as_previous() && $this->sum_ratios( 4 ) < 3.5 &&
+ ( $this->images_left == 4 || ( $this->images_left != 8 && $this->images_left > 5 ) );
+ }
+}
+
+class Jetpack_Tiled_Gallery_Five extends Jetpack_Tiled_Gallery_Shape {
+ public $shape = array( 1, 1, 1, 1, 1 );
+
+ public function is_possible() {
+ return $this->is_wide_theme() && $this->is_not_as_previous() && $this->sum_ratios( 5 ) < 5 &&
+ ( $this->images_left == 5 || ( $this->images_left != 10 && $this->images_left > 6 ) );
+ }
+}
+
+class Jetpack_Tiled_Gallery_Two_One extends Jetpack_Tiled_Gallery_Shape {
+ public $shape = array( 2, 1 );
+
+ public function is_possible() {
+ return $this->is_not_as_previous( 3 ) && $this->images_left >= 2 &&
+ $this->images[2]->ratio < 1.6 && $this->images[0]->ratio >=0.9 && $this->images[1]->ratio >= 0.9;
+ }
+}
+
+class Jetpack_Tiled_Gallery_One_Two extends Jetpack_Tiled_Gallery_Shape {
+ public $shape = array( 1, 2 );
+
+ public function is_possible() {
+ return $this->is_not_as_previous( 3 ) && $this->images_left >= 2 &&
+ $this->images[0]->ratio < 1.6 && $this->images[1]->ratio >=0.9 && $this->images[2]->ratio >= 0.9;
+ }
+}
+
+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 &&
+ $this->images[0]->ratio < 0.8 && $this->images[1]->ratio >=0.9 && $this->images[2]->ratio >= 0.9 && $this->images[3]->ratio >= 0.9;
+ }
+}
+
+class Jetpack_Tiled_Gallery_Symmetric_Row extends Jetpack_Tiled_Gallery_Shape {
+ public $shape = array( 1, 2, 1 );
+
+ public function is_possible() {
+ return $this->is_not_as_previous() && $this->images_left >= 3 && $this->images_left != 5 &&
+ $this->images[0]->ratio < 0.8 && $this->images[0]->ratio == $this->images[3]->ratio;
+ }
+}
+
+class Jetpack_Tiled_Gallery_Grouper {
+ public $margin = 4;
+ public function __construct( $attachments ) {
+ $content_width = Jetpack_Tiled_Gallery::get_content_width();
+ $ua_info = new Jetpack_User_Agent_Info();
+
+ $this->last_shape = '';
+ $this->images = $this->get_images_with_sizes( $attachments );
+ $this->grouped_images = $this->get_grouped_images();
+ $this->apply_content_width( $content_width - 5 ); //reduce the margin hack to 5px. It will be further reduced when we fix more themes and the rounding error.
+ }
+
+ public function get_current_row_size() {
+ $images_left = count( $this->images );
+ if ( $images_left < 3 )
+ return array_fill( 0, $images_left, 1 );
+
+ foreach ( array( 'One_Three', 'One_Two', 'Five', 'Four', 'Three', 'Two_One', 'Symmetric_Row' ) as $shape_name ) {
+ $class_name = "Jetpack_Tiled_Gallery_$shape_name";
+ $shape = new $class_name( $this->images );
+ if ( $shape->is_possible() ) {
+ Jetpack_Tiled_Gallery_Shape::set_last_shape( $class_name );
+ return $shape->shape;
+ }
+ }
+
+ Jetpack_Tiled_Gallery_Shape::set_last_shape( 'Two' );
+ return array( 1, 1 );
+ }
+
+ public function get_images_with_sizes( $attachments ) {
+ $images_with_sizes = array();
+
+ foreach ( $attachments as $image ) {
+ $meta = wp_get_attachment_metadata( $image->ID );
+ $image->width_orig = ( $meta['width'] > 0 )? $meta['width'] : 1;
+ $image->height_orig = ( $meta['height'] > 0 )? $meta['height'] : 1;
+ $image->ratio = $image->width_orig / $image->height_orig;
+ $image->ratio = $image->ratio? $image->ratio : 1;
+ $images_with_sizes[] = $image;
+ }
+
+ return $images_with_sizes;
+ }
+
+ public function read_row() {
+ $vector = $this->get_current_row_size();
+
+ $row = array();
+ foreach ( $vector as $group_size ) {
+ $row[] = new Jetpack_Tiled_Gallery_Group( array_splice( $this->images, 0, $group_size ) );
+ }
+
+ return $row;
+ }
+
+ public function get_grouped_images() {
+ $grouped_images = array();
+
+ while( !empty( $this->images ) ) {
+ $grouped_images[] = new Jetpack_Tiled_Gallery_Row( $this->read_row() );
+ }
+
+ return $grouped_images;
+ }
+
+ // todo: split in functions
+ // todo: do not stretch images
+ public function apply_content_width( $width ) {
+ foreach ( $this->grouped_images as $row ) {
+ $row->width = $width;
+ $row->raw_height = 1 / $row->ratio * ( $width - $this->margin * ( count( $row->groups ) - $row->weighted_ratio ) );
+ $row->height = round( $row->raw_height );
+
+ $this->calculate_group_sizes( $row );
+ }
+ }
+
+ public function calculate_group_sizes( $row ) {
+ // Storing the calculated group heights in an array for rounding them later while preserving their sum
+ // This fixes the rounding error that can lead to a few ugly pixels sticking out in the gallery
+ $group_widths_array = array();
+ foreach ( $row->groups as $group ) {
+ $group->height = $row->height;
+ // Storing the raw calculations in a separate property to prevent rounding errors from cascading down and for diagnostics
+ $group->raw_width = ( $row->raw_height - $this->margin * count( $group->images ) ) * $group->ratio + $this->margin;
+ $group_widths_array[] = $group->raw_width;
+ }
+ $rounded_group_widths_array = Jetpack_Constrained_Array_Rounding::get_rounded_constrained_array( $group_widths_array, $row->width );
+
+ foreach ( $row->groups as $group ) {
+ $group->width = array_shift( $rounded_group_widths_array );
+ $this->calculate_image_sizes( $group );
+ }
+ }
+
+ public function calculate_image_sizes( $group ) {
+ // Storing the calculated image heights in an array for rounding them later while preserving their sum
+ // This fixes the rounding error that can lead to a few ugly pixels sticking out in the gallery
+ $image_heights_array = array();
+ foreach ( $group->images as $image ) {
+ $image->width = $group->width - $this->margin;
+ // Storing the raw calculations in a separate property for diagnostics
+ $image->raw_height = ( $group->raw_width - $this->margin ) / $image->ratio;
+ $image_heights_array[] = $image->raw_height;
+ }
+
+ $image_height_sum = $group->height - count( $image_heights_array ) * $this->margin;
+ $rounded_image_heights_array = Jetpack_Constrained_Array_Rounding::get_rounded_constrained_array( $image_heights_array, $image_height_sum );
+
+ foreach ( $group->images as $image ) {
+ $image->height = array_shift( $rounded_image_heights_array );
+ }
+ }
+}
+
+class Jetpack_Tiled_Gallery_Row {
+ public function __construct( $groups ) {
+ $this->groups = $groups;
+ $this->ratio = $this->get_ratio();
+ $this->weighted_ratio = $this->get_weighted_ratio();
+ }
+
+ public function get_ratio() {
+ $ratio = 0;
+ foreach ( $this->groups as $group ) {
+ $ratio += $group->ratio;
+ }
+ return $ratio > 0? $ratio : 1;
+ }
+
+ public function get_weighted_ratio() {
+ $weighted_ratio = 0;
+ foreach ( $this->groups as $group ) {
+ $weighted_ratio += $group->ratio * count( $group->images );
+ }
+ return $weighted_ratio > 0 ? $weighted_ratio : 1;
+ }
+}
+
+class Jetpack_Tiled_Gallery_Group {
+ public function __construct( $images ) {
+ $this->images = $images;
+ $this->ratio = $this->get_ratio();
+ }
+
+ public function get_ratio() {
+ $ratio = 0;
+ foreach ( $this->images as $image ) {
+ if ( $image->ratio )
+ $ratio += 1/$image->ratio;
+ }
+ if ( !$ratio )
+ return 1;
+
+ return 1/$ratio;
+ }
+}
+
+add_action( 'init', array( 'Jetpack_Tiled_Gallery', 'init' ) );
+
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
new file mode 100644
index 00000000..007c0ccf
--- /dev/null
+++ b/plugins/jetpack/modules/tiled-gallery/tiled-gallery/rtl/tiled-gallery-rtl.css
@@ -0,0 +1,88 @@
+/* This file was automatically generated on Jan 05 2013 15:45:53 */
+
+/* =Tiled Gallery Default Styles
+-------------------------------------------------------------- */
+
+.tiled-gallery {
+ clear: both;
+ margin: 0;
+ overflow: hidden;
+}
+.tiled-gallery img {
+ margin: 2px !important; /* Ensure that this value isn't overridden by themes that give content images blanket margins */
+}
+.tiled-gallery .gallery-group {
+ float: right;
+ overflow-y: hidden;
+ position: relative;
+}
+.tiled-gallery .tiled-gallery-item {
+ float: right;
+ margin: 0;
+ position: relative;
+}
+.tiled-gallery .gallery-row {
+ overflow: hidden;
+ margin-bottom: 2px;
+}
+.tiled-gallery .tiled-gallery-item a { /* Needs to reset some properties for theme compatibility */
+ background: transparent;
+ border: none;
+ color: none;
+ margin: 0;
+ padding: 0;
+ text-decoration: none;
+ width: auto;
+}
+.tiled-gallery .tiled-gallery-item img,
+.tiled-gallery .tiled-gallery-item img:hover { /* Needs to reset some properties for theme compatibility */
+ background: none;
+ border: none;
+ box-shadow: none;
+ max-width: 100%;
+ padding: 0;
+ vertical-align: middle;
+}
+.tiled-gallery-caption { /* Captions */
+ background: #eee;
+ background: rgba( 255,255,255,0.8 );
+ color: #333;
+ font-size: 13px;
+ font-weight: 400;
+ overflow: hidden;
+ padding: 10px 0;
+ position: absolute;
+ bottom: 0;
+ text-indent: 10px;
+ text-overflow: ellipsis;
+ width: 100%;
+ white-space: nowrap;
+}
+.tiled-gallery .tiled-gallery-item-small .tiled-gallery-caption { /* Smaller captions */
+ font-size: 11px;
+}
+
+
+/* =Greyscale
+-------------------------------------------------------------- */
+
+.tiled-gallery .tiled-gallery-item img.grayscale {
+ position: absolute;
+ right: 0;
+ top: 0;
+}
+.tiled-gallery .tiled-gallery-item img.grayscale:hover {
+ opacity: 0;
+}
+
+
+/* =Circles Layout
+-------------------------------------------------------------- */
+
+.tiled-gallery.type-circle .tiled-gallery-item img {
+ border-radius: 50% !important; /* Ensure that circles are displayed in themes that add border-radius to all images as a default */
+}
+.tiled-gallery.type-circle .tiled-gallery-caption {
+ display: none;
+ opacity: 0;
+} \ No newline at end of file
diff --git a/plugins/jetpack/modules/tiled-gallery/tiled-gallery/tiled-gallery.css b/plugins/jetpack/modules/tiled-gallery/tiled-gallery/tiled-gallery.css
new file mode 100644
index 00000000..3a1924df
--- /dev/null
+++ b/plugins/jetpack/modules/tiled-gallery/tiled-gallery/tiled-gallery.css
@@ -0,0 +1,85 @@
+/* =Tiled Gallery Default Styles
+-------------------------------------------------------------- */
+
+.tiled-gallery {
+ clear: both;
+ margin: 0;
+ overflow: hidden;
+}
+.tiled-gallery img {
+ margin: 2px !important; /* Ensure that this value isn't overridden by themes that give content images blanket margins */
+}
+.tiled-gallery .gallery-group {
+ float: left;
+ position: relative;
+}
+.tiled-gallery .tiled-gallery-item {
+ float: left;
+ margin: 0;
+ position: relative;
+}
+.tiled-gallery .gallery-row {
+ overflow: hidden;
+ margin-bottom: 2px;
+}
+.tiled-gallery .tiled-gallery-item a { /* Needs to reset some properties for theme compatibility */
+ background: transparent;
+ border: none;
+ color: none;
+ margin: 0;
+ padding: 0;
+ text-decoration: none;
+ width: auto;
+}
+.tiled-gallery .tiled-gallery-item img,
+.tiled-gallery .tiled-gallery-item img:hover { /* Needs to reset some properties for theme compatibility */
+ background: none;
+ border: none;
+ box-shadow: none;
+ max-width: 100%;
+ padding: 0;
+ vertical-align: middle;
+}
+.tiled-gallery-caption { /* Captions */
+ background: #eee;
+ background: rgba( 255,255,255,0.8 );
+ color: #333;
+ font-size: 13px;
+ font-weight: 400;
+ overflow: hidden;
+ padding: 10px 0;
+ position: absolute;
+ bottom: 0;
+ text-indent: 10px;
+ text-overflow: ellipsis;
+ width: 100%;
+ white-space: nowrap;
+}
+.tiled-gallery .tiled-gallery-item-small .tiled-gallery-caption { /* Smaller captions */
+ font-size: 11px;
+}
+
+
+/* =Greyscale
+-------------------------------------------------------------- */
+
+.tiled-gallery .tiled-gallery-item img.grayscale {
+ position: absolute;
+ left: 0;
+ top: 0;
+}
+.tiled-gallery .tiled-gallery-item img.grayscale:hover {
+ opacity: 0;
+}
+
+
+/* =Circles Layout
+-------------------------------------------------------------- */
+
+.tiled-gallery.type-circle .tiled-gallery-item img {
+ border-radius: 50% !important; /* Ensure that circles are displayed in themes that add border-radius to all images as a default */
+}
+.tiled-gallery.type-circle .tiled-gallery-caption {
+ display: none;
+ opacity: 0;
+} \ No newline at end of file
diff --git a/plugins/jetpack/modules/tiled-gallery/tiled-gallery/tiled-gallery.js b/plugins/jetpack/modules/tiled-gallery/tiled-gallery/tiled-gallery.js
new file mode 100644
index 00000000..90f8ed76
--- /dev/null
+++ b/plugins/jetpack/modules/tiled-gallery/tiled-gallery/tiled-gallery.js
@@ -0,0 +1,150 @@
+( function($) {
+
+var TiledGallery = function() {
+ this.resizeTimeout = null;
+
+ this.populate();
+
+ var self = this;
+
+ $( window ).on( 'resize', function () {
+ clearTimeout( self.resizeTimeout );
+
+ self.resizeTimeout = setTimeout( function () { self.resize(); }, 150 );
+ } );
+
+ // Make any new galleries loaded by Infinite Scroll flexible
+ $( 'body' ).on( 'post-load', $.proxy( self.initialize, self ) );
+
+ // Populate and set up captions on newdash galleries.
+ $( document ).on( 'page-rendered.wpcom-newdash', $.proxy( self.populate, self ) );
+
+ this.resize();
+};
+
+TiledGallery.prototype.populate = function() {
+ this.gallery = $( '.tiled-gallery' );
+ this.item = this.gallery.find( '.tiled-gallery-item' );
+ this.caption = this.gallery.find( '.tiled-gallery-caption' );
+
+ this.Captions();
+};
+
+TiledGallery.prototype.initialize = function() {
+ var self = this;
+
+ self.populate();
+
+ // After each image load, run resize in case all images in the gallery are loaded.
+ self.gallery.find( 'img' ).off( 'load.tiled-gallery' ).on( 'load.tiled-gallery', function () {
+ self.resize();
+ } );
+
+ // Run resize now in case all images loaded from cache.
+ self.resize();
+};
+
+/**
+ * Story
+ */
+TiledGallery.prototype.Captions = function() {
+ /* Hide captions */
+ this.caption.hide();
+
+ this.item.on( 'hover', function() {
+ $( this ).find( '.tiled-gallery-caption' ).slideToggle( 'fast' );
+ });
+};
+
+TiledGallery.prototype.resize = function() {
+ var resizeableElements = '.gallery-row, .gallery-group, .tiled-gallery-item img';
+
+ this.gallery.each( function ( galleryIndex, galleryElement ) {
+ var thisGallery = $( galleryElement );
+
+ // All images must be loaded before proceeding.
+ var imagesLoaded = true;
+
+ thisGallery.find( 'img' ).each( function () {
+ if ( ! this.complete ) {
+ imagesLoaded = false;
+ return false;
+ }
+ } );
+
+ if ( ! imagesLoaded ) {
+ var loadCallback = arguments.callee;
+
+ // Once all of the images have loaded,
+ // re-call this containing function.
+ $( window ).load( function () {
+ loadCallback( null, thisGallery );
+ } );
+
+ return;
+ }
+
+ if ( ! thisGallery.data( 'sizes-set' ) ) {
+ // Maintain a record of the original widths and heights of these elements
+ // for proper scaling.
+ thisGallery.data( 'sizes-set', true );
+
+ thisGallery.find( resizeableElements ).each( function () {
+ var thisGalleryElement = $( this );
+
+ // Don't change margins, but remember what they were so they can be
+ // accounted for in size calculations. When the screen width gets
+ // small enough, ignoring the margins can cause images to overflow
+ // into new rows.
+ var extraWidth = ( parseInt( thisGalleryElement.css( 'marginLeft' ), 10 ) || 0 ) + ( parseInt( thisGalleryElement.css( 'marginRight' ), 10 ) || 0 );
+ var extraHeight = ( parseInt( thisGalleryElement.css( 'marginTop' ), 10 ) || 0 ) + ( parseInt( thisGalleryElement.css( 'marginBottom' ), 10 ) || 0 )
+
+ // In some situations, tiled galleries in Firefox have shown scrollbars on the images because
+ // the .outerWidth() call on the image returns a value larger than the container. Restrict
+ // widths used in the resizing functions to the maximum width of the container.
+ var parentElement = $( thisGalleryElement.parents( resizeableElements ).get( 0 ) );
+
+ if ( parentElement && parentElement.data( 'original-width' ) ) {
+ thisGalleryElement
+ .data( 'original-width', Math.min( parentElement.data( 'original-width' ), thisGalleryElement.outerWidth( true ) ) )
+ .data( 'original-height', Math.min( parentElement.data( 'original-height' ), thisGalleryElement.outerHeight( true ) ) );
+ }
+ else {
+ thisGalleryElement
+ .data( 'original-width', thisGalleryElement.outerWidth( true ) )
+ .data( 'original-height', thisGalleryElement.outerHeight( true ) );
+ }
+
+ thisGalleryElement
+ .data( 'extra-width', extraWidth )
+ .data( 'extra-height', extraHeight );
+ } );
+ }
+
+ // Resize everything in the gallery based on the ratio of the current content width
+ // to the original content width;
+ var originalWidth = thisGallery.data( 'original-width' );
+ var currentWidth = thisGallery.parent().width();
+ var resizeRatio = Math.min( 1, currentWidth / originalWidth );
+
+ thisGallery.find( resizeableElements ).each( function () {
+ var thisGalleryElement = $( this );
+
+ thisGalleryElement
+ .width( Math.floor( resizeRatio * thisGalleryElement.data( 'original-width' ) ) - thisGalleryElement.data( 'extra-width' ) )
+ .height( Math.floor( resizeRatio * thisGalleryElement.data( 'original-height' ) ) - thisGalleryElement.data( 'extra-height' ) );
+ } );
+ } );
+};
+
+/**
+ * Ready, set...
+ */
+$( document ).ready( function() {
+
+ // Instance!
+ var TiledGalleryInstance = new TiledGallery;
+
+});
+
+})(jQuery); \ No newline at end of file
diff --git a/plugins/jetpack/modules/widgets.php b/plugins/jetpack/modules/widgets.php
index 3d9c2f8b..5771cdbe 100644
--- a/plugins/jetpack/modules/widgets.php
+++ b/plugins/jetpack/modules/widgets.php
@@ -24,11 +24,22 @@ function jetpack_widgets_configuration_load() {
exit;
}
-function jetpack_register_widgets() {
- register_widget( 'WPCOM_Widget_Facebook_LikeBox' );
- register_widget( 'Jetpack_Gravatar_Profile_Widget' );
+/**
+ * Loads file for front-end widget styles.
+ */
+function jetpack_widgets_styles() {
+ wp_enqueue_style( 'jetpack-widgets', plugins_url( 'widgets/widgets.css', __FILE__ ), array(), '20121003' );
}
+add_action( 'wp_enqueue_scripts', 'jetpack_widgets_styles' );
+
+/**
+ * Add the "(Jetpack)" suffix to the widget names
+ */
+function jetpack_widgets_add_suffix( $widget_name ) {
+ return sprintf( __( '%s (Jetpack)', 'jetpack' ), $widget_name );
+}
+add_filter( 'jetpack_widget_name', 'jetpack_widgets_add_suffix' );
-jetpack_load_widgets();
-add_action( 'widgets_init', 'jetpack_register_widgets' );
+
+jetpack_load_widgets();
diff --git a/plugins/jetpack/modules/widgets/facebook-likebox.php b/plugins/jetpack/modules/widgets/facebook-likebox.php
index 64d03f8a..f8ab2512 100644
--- a/plugins/jetpack/modules/widgets/facebook-likebox.php
+++ b/plugins/jetpack/modules/widgets/facebook-likebox.php
@@ -1,6 +1,15 @@
<?php
/**
+ * Register the widget for use in Appearance -> Widgets
+ */
+add_action( 'widgets_init', 'jetpack_facebook_likebox_init' );
+
+function jetpack_facebook_likebox_init() {
+ register_widget( 'WPCOM_Widget_Facebook_LikeBox' );
+}
+
+/**
* Facebook Like Box widget class
* Display a Facebook Like Box as a widget
* http://developers.facebook.com/docs/reference/plugins/like-box/
@@ -17,7 +26,14 @@ class WPCOM_Widget_Facebook_LikeBox extends WP_Widget {
private $allowed_colorschemes = array( 'light', 'dark' );
function __construct() {
- parent::__construct( 'facebook-likebox', __( 'Facebook Like Box', 'jetpack' ), array( 'classname' => 'widget_facebook_likebox', 'description' => __( 'Display a Facebook Like Box to connect visitors to your Facebook Page', 'jetpack' ) ) );
+ parent::__construct(
+ 'facebook-likebox',
+ apply_filters( 'jetpack_widget_name', __( 'Facebook Like Box', 'jetpack' ) ),
+ array(
+ 'classname' => 'widget_facebook_likebox',
+ 'description' => __( 'Display a Facebook Like Box to connect visitors to your Facebook Page', 'jetpack' )
+ )
+ );
}
function widget( $args, $instance ) {
@@ -29,7 +45,7 @@ class WPCOM_Widget_Facebook_LikeBox extends WP_Widget {
if ( empty( $like_args['href'] ) || ! $this->is_valid_facebook_url( $like_args['href'] ) ) {
if ( current_user_can('edit_theme_options') ) {
echo $before_widget;
- echo '<p>' . sprintf( __( 'It looks like your Facebook URL is incorrectly configured. Please check it in your <a href="%s">widget settings</a>.' ), admin_url( 'widgets.php' ) ) . '</p>';
+ echo '<p>' . sprintf( __( 'It looks like your Facebook URL is incorrectly configured. Please check it in your <a href="%s">widget settings</a>.', 'jetpack' ), admin_url( 'widgets.php' ) ) . '</p>';
echo $after_widget;
}
echo '<!-- Invalid Facebook Page URL -->';
@@ -244,26 +260,33 @@ class WPCOM_Widget_Facebook_LikeBox extends WP_Widget {
}
function guess_locale_from_lang( $lang ) {
- $lang = strtolower( str_replace( '-', '_', $lang ) );
+ if ( 'en' == $lang || 'en_US' == $lang || !$lang ) {
+ return 'en_US';
+ }
+
+ if ( !class_exists( 'GP_Locales' ) ) {
+ if ( !defined( 'JETPACK__GLOTPRESS_LOCALES_PATH' ) || !file_exists( JETPACK__GLOTPRESS_LOCALES_PATH ) ) {
+ return false;
+ }
+
+ require JETPACK__GLOTPRESS_LOCALES_PATH;
+ }
- if ( 5 == strlen( $lang ) ) {
- $lang = substr( $lang, 0, 3 ) . strtoupper( substr( $lang, 3, 2 ) );
- } else if ( 3 == strlen( $lang ) ) {
- $lang = $lang;
+ if ( defined( 'IS_WPCOM' ) && IS_WPCOM ) {
+ // WP.com: get_locale() returns 'it'
+ $locale = GP_Locales::by_slug( $lang );
} else {
- $lang = $lang . '_' . strtoupper( $lang );
+ // Jetpack: get_locale() returns 'it_IT';
+ $locale = GP_Locales::by_field( 'wp_locale', $lang );
}
-
- if ( 'en_EN' == $lang ) {
- $lang = 'en_US';
- } else if ( 'he_HE' == $lang ) {
- $lang = 'he_IL';
- } else if ( 'ja_JA' == $lang )
- $lang = 'ja_JP';
- return $lang;
+ if ( !$locale || empty( $locale->facebook_locale ) ) {
+ return false;
+ }
+
+ return $locale->facebook_locale;
}
-
+
function get_locale() {
return $this->guess_locale_from_lang( get_locale() );
}
diff --git a/plugins/jetpack/modules/widgets/gravatar-profile.css b/plugins/jetpack/modules/widgets/gravatar-profile.css
index 230a3b16..1663ae07 100644
--- a/plugins/jetpack/modules/widgets/gravatar-profile.css
+++ b/plugins/jetpack/modules/widgets/gravatar-profile.css
@@ -1,8 +1,7 @@
.widget-grofile {
}
.widget-grofile h4 {
- margin: 1em 0 1.2em;
- /*font-size: 1.2em;*/
+ margin: 1em 0 .5em;
}
.widget-grofile ul.grofile-urls {
margin-left: 0;
@@ -17,7 +16,7 @@
content: "" !important; /* Kubrick :( */
}
.widget-grofile .grofile-accounts-logo {
- background-image: url('http://0.gravatar.com/images/grav-share-sprite.png');
+ background-image: url('//0.gravatar.com/images/grav-share-sprite.png');
background-repeat: no-repeat;
/*background-position: -16px -16px;*/
width: 16px; /* So we don't show the topmost logo */
@@ -34,3 +33,13 @@
.grofile-thumbnail {
width: 100%;
}
+ @media
+only screen and (-webkit-min-device-pixel-ratio: 1.5),
+only screen and (-o-min-device-pixel-ratio: 3/2),
+only screen and (min--moz-device-pixel-ratio: 1.5),
+only screen and (min-device-pixel-ratio: 1.5) {
+ .widget-grofile .grofile-accounts-logo {
+ background-image: url('//0.gravatar.com/images/grav-share-sprite-2x.png');
+ background-size: 16px 784px;
+ }
+} \ No newline at end of file
diff --git a/plugins/jetpack/modules/widgets/gravatar-profile.php b/plugins/jetpack/modules/widgets/gravatar-profile.php
index c33e6996..190427fe 100644
--- a/plugins/jetpack/modules/widgets/gravatar-profile.php
+++ b/plugins/jetpack/modules/widgets/gravatar-profile.php
@@ -1,15 +1,29 @@
<?php
+
+/**
+ * Register the widget for use in Appearance -> Widgets
+ */
+add_action( 'widgets_init', 'jetpack_gravatar_profile_widget_init' );
+
+function jetpack_gravatar_profile_widget_init() {
+ register_widget( 'Jetpack_Gravatar_Profile_Widget' );
+}
+
/**
* Display a widgetized version of your Gravatar Profile
* http://blog.gravatar.com/2010/03/26/gravatar-profiles/
*/
class Jetpack_Gravatar_Profile_Widget extends WP_Widget {
-
+
function __construct() {
- parent::__construct( 'grofile', __( 'Gravatar Profile', 'jetpack' ), array(
- 'classname' => 'widget-grofile grofile',
- 'description' => __( 'Display a mini version of your Gravatar Profile', 'jetpack' )
- ) );
+ parent::__construct(
+ 'grofile',
+ apply_filters( 'jetpack_widget_name', __( 'Gravatar Profile', 'jetpack' ) ),
+ array(
+ 'classname' => 'widget-grofile grofile',
+ 'description' => __( 'Display a mini version of your Gravatar Profile', 'jetpack' )
+ )
+ );
if ( is_admin() ) {
add_action( 'admin_footer-widgets.php', array( $this, 'admin_script' ) );
@@ -24,7 +38,7 @@ class Jetpack_Gravatar_Profile_Widget extends WP_Widget {
echo $args['before_widget'];
if ( ! empty( $title ) )
echo $args['before_title'] . $title . $args['after_title'];
- echo '<p>' . sprintf( __( 'You need to select what to show in this <a href="%s">Gravatar Profile widget</a>.' ), admin_url( 'widgets.php' ) ) . '</p>';
+ echo '<p>' . sprintf( __( 'You need to select what to show in this <a href="%s">Gravatar Profile widget</a>.', 'jetpack' ), admin_url( 'widgets.php' ) ) . '</p>';
echo $args['after_widget'];
}
return;
@@ -35,24 +49,39 @@ class Jetpack_Gravatar_Profile_Widget extends WP_Widget {
echo $args['before_title'] . $title . $args['after_title'];
$profile = $this->get_profile( $instance['email'] );
-
+
if( ! empty( $profile ) ) {
- $gravatar_url = add_query_arg( 's', 500, $profile['thumbnailUrl'] ); // the default grav returned by grofiles is super small
-
+ $profile = wp_parse_args( $profile, array(
+ 'thumbnailUrl' => '',
+ 'profileUrl' => '',
+ 'displayName' => '',
+ 'aboutMe' => '',
+ 'urls' => array(),
+ 'accounts' => array(),
+ ) );
+ $gravatar_url = add_query_arg( 's', 200, $profile['thumbnailUrl'] ); // the default grav returned by grofiles is super small
+
wp_enqueue_style(
'gravatar-profile-widget',
plugins_url( 'gravatar-profile.css', __FILE__ ),
array(),
'20120711'
);
-
+
+ wp_enqueue_style(
+ 'gravatar-card-services',
+ is_ssl() ? 'https://secure.gravatar.com/css/services.css' : 'http://s.gravatar.com/css/services.css',
+ array(),
+ defined( 'GROFILES__CACHE_BUSTER' ) ? GROFILES__CACHE_BUSTER : gmdate( 'YW' )
+ );
+
?>
- <img src="<?php echo esc_url( $gravatar_url ); ?>" class="grofile-thumbnail no-grav" />
+ <img src="<?php echo esc_url( $gravatar_url ); ?>" class="grofile-thumbnail no-grav" style="width: auto; max-width: 200px;" />
<div class="grofile-meta">
<h4><a href="<?php echo esc_url( $profile['profileUrl'] ); ?>"><?php echo esc_html( $profile['displayName'] ); ?></a></h4>
- <p><?php echo esc_html( wp_kses( $profile['aboutMe'], array() ) ); ?></p>
+ <p><?php echo wp_kses_data( $profile['aboutMe'] ); ?></p>
</div>
-
+
<?php
if( $instance['show_personal_links'] )
@@ -60,15 +89,15 @@ class Jetpack_Gravatar_Profile_Widget extends WP_Widget {
if( $instance['show_account_links'] )
$this->display_accounts( (array) $profile['accounts'] );
-
+
?>
-
- <h4><a href="<?php echo esc_url( $profile['profileUrl'] ); ?>" class="grofile-full-link"><?php esc_html_e( 'View Full Profile &rarr;' ); ?></a></h4>
-
+
+ <h4><a href="<?php echo esc_url( $profile['profileUrl'] ); ?>" class="grofile-full-link"><?php esc_html_e( 'View Full Profile &rarr;', 'jetpack' ); ?></a></h4>
+
<?php
- do_action( 'jetpack_stats_extra', 'widgets', 'grofile' );
-
+ do_action( 'jetpack_stats_extra', 'widget', 'grofile' );
+
} else {
if ( current_user_can( 'edit_theme_options' ) ) {
echo '<p>' . esc_html__( 'Error loading profile', 'jetpack' ) . '</p>';
@@ -85,7 +114,7 @@ class Jetpack_Gravatar_Profile_Widget extends WP_Widget {
<h4><?php esc_html_e( 'Personal Links', 'jetpack' ); ?></h4>
<ul class="grofile-urls grofile-links">
-
+
<?php foreach( $personal_links as $personal_link ) : ?>
<li>
<a href="<?php echo esc_url( $personal_link['value'] ); ?>">
@@ -98,30 +127,30 @@ class Jetpack_Gravatar_Profile_Widget extends WP_Widget {
<?php
}
- function display_accounts( $accounts = array() ) {
+ function display_accounts( $accounts = array() ) {
if ( empty( $accounts ) )
return;
?>
-
+
<h4><?php esc_html_e( 'Verified Services', 'jetpack' ); ?></h4>
<ul class="grofile-urls grofile-accounts">
-
+
<?php foreach( $accounts as $account ) :
if( $account['verified'] != 'true' )
continue;
-
+
$sanitized_service_name = $this->get_sanitized_service_name( $account['shortname'] );
?>
-
+
<li>
<a href="<?php echo esc_url( $account['url'] ); ?>" title="<?php echo sprintf( _x( '%1$s on %2$s', '1: User Name, 2: Service Name (Facebook, Twitter, ...)', 'jetpack' ), esc_html( $account['display'] ), esc_html( $sanitized_service_name ) ); ?>">
<span class="grofile-accounts-logo grofile-accounts-<?php echo esc_attr( $account['shortname'] ); ?> accounts_<?php echo esc_attr( $account['shortname'] ); ?>"></span>
</a>
</li>
-
+
<?php endforeach; ?>
</ul>
-
+
<?php
}
@@ -133,6 +162,12 @@ class Jetpack_Gravatar_Profile_Widget extends WP_Widget {
$show_personal_links = isset( $instance['show_personal_links'] ) ? (bool) $instance['show_personal_links'] : '';
$show_account_links = isset( $instance['show_account_links'] ) ? (bool) $instance['show_account_links'] : '';
+ if ( defined( 'IS_WPCOM' ) && IS_WPCOM ) {
+ $profile_url = admin_url( 'profile.php' );
+ } else {
+ $profile_url = 'https://gravatar.com/profile/edit';
+ }
+
?>
<p>
<label for="<?php echo $this->get_field_id( 'title' ); ?>">
@@ -144,7 +179,7 @@ class Jetpack_Gravatar_Profile_Widget extends WP_Widget {
<label for="<?php echo $this->get_field_id( 'email_user' ); ?>">
<?php esc_html_e( 'Select a user or pick "custom" and enter a custom email address.', 'jetpack' ); ?>
<br />
-
+
<?php wp_dropdown_users( array(
'show_option_none' => __( 'Custom', 'jetpack' ),
'selected' => $email_user,
@@ -154,13 +189,13 @@ class Jetpack_Gravatar_Profile_Widget extends WP_Widget {
) );?>
</label>
</p>
-
+
<p class="gprofile-email-container <?php echo empty( $email_user ) || $email_user == -1 ? '' : 'hidden'; ?>">
<label for="<?php echo $this->get_field_id( 'email' ); ?>"><?php esc_html_e( 'Custom Email Address', 'jetpack' ); ?>
<input class="widefat" id="<?php echo $this->get_field_id('email'); ?>" name="<?php echo $this->get_field_name( 'email' ); ?>" type="text" value="<?php echo esc_attr( $email ); ?>" />
</label>
</p>
-
+
<p>
<label for="<?php echo $this->get_field_id( 'show_personal_links' ); ?>">
<input type="checkbox" name="<?php echo $this->get_field_name( 'show_personal_links' ); ?>" id="<?php echo $this->get_field_id( 'show_personal_links' ); ?>" <?php checked( $show_personal_links ); ?> />
@@ -178,8 +213,8 @@ class Jetpack_Gravatar_Profile_Widget extends WP_Widget {
<small><?php esc_html_e( 'Links to services that you use across the web.', 'jetpack' ); ?></small>
</label>
</p>
-
- <p><a href="<?php echo admin_url( 'profile.php' ); ?>" target="_blank" title="<?php esc_attr_e( 'Opens in new window', 'jetpack' ); ?>"><?php esc_html_e( 'Edit Your Profile', 'jetpack' )?></a> | <a href="http://gravatar.com" target="_blank" title="<?php esc_attr_e( 'Opens in new window', 'jetpack' ); ?>"><?php esc_html_e( "What's a Gravatar?", 'jetpack' ); ?></a></p>
+
+ <p><a href="<?php echo esc_url( $profile_url ); ?>" target="_blank" title="<?php esc_attr_e( 'Opens in new window', 'jetpack' ); ?>"><?php esc_html_e( 'Edit Your Profile', 'jetpack' )?></a> | <a href="http://gravatar.com" target="_blank" title="<?php esc_attr_e( 'Opens in new window', 'jetpack' ); ?>"><?php esc_html_e( "What's a Gravatar?", 'jetpack' ); ?></a></p>
<?php
}
@@ -202,7 +237,7 @@ class Jetpack_Gravatar_Profile_Widget extends WP_Widget {
}
function update( $new_instance, $old_instance ) {
-
+
$instance = array();
$instance['title'] = isset( $new_instance['title'] ) ? wp_kses( $new_instance['title'], array() ) : '';
@@ -216,42 +251,45 @@ class Jetpack_Gravatar_Profile_Widget extends WP_Widget {
$instance['email'] = $user->user_email;
}
+ $hashed_email = md5( strtolower( trim( $instance['email'] ) ) );
+ $cache_key = 'grofile-' . $hashed_email;
+ delete_transient( $cache_key );
+
return $instance;
}
-
+
private function get_profile( $email ) {
$hashed_email = md5( strtolower( trim( $email ) ) );
- $cache_key = 'widget-grofile-' . $hashed_email;
-
- if( ! $profile = get_transient( $cache_key, 'widget' ) ) {
-
+ $cache_key = 'grofile-' . $hashed_email;
+
+ if( ! $profile = get_transient( $cache_key ) ) {
$profile_url = esc_url_raw( sprintf( '%s.gravatar.com/%s.php', ( is_ssl() ? 'https://secure' : 'http://www' ), $hashed_email ), array( 'http', 'https' ) );
-
+
$expire = 300;
$response = wp_remote_get( $profile_url, array( 'User-Agent' => 'WordPress.com Gravatar Profile Widget' ) );
$response_code = wp_remote_retrieve_response_code( $response );
if ( 200 == $response_code ) {
$profile = wp_remote_retrieve_body( $response );
$profile = unserialize( $profile );
-
+
if ( is_array( $profile ) && ! empty( $profile['entry'] ) && is_array( $profile['entry'] ) ) {
$expire = 900; // cache for 15 minutes
$profile = $profile['entry'][0];
} else {
+ // Something strange happend. Cache for 5 minutes.
$profile = array();
}
-
+
} else {
- $expire =
+ $expire = 900; // cache for 15 minutes
$profile = array();
- set_transient( $cache_key . '-response-code', $response_code, $expire );
}
-
+
set_transient( $cache_key, $profile, $expire );
}
return $profile;
}
-
+
private function get_sanitized_service_name( $shortname ) {
// Some services have stylized or mixed cap names *cough* WP *cough*
switch( $shortname ) {
diff --git a/plugins/jetpack/modules/widgets/image-widget.php b/plugins/jetpack/modules/widgets/image-widget.php
index 0e7e227b..03d4d5c7 100644
--- a/plugins/jetpack/modules/widgets/image-widget.php
+++ b/plugins/jetpack/modules/widgets/image-widget.php
@@ -43,7 +43,7 @@ class Jetpack_Image_Widget extends WP_Widget {
if ( '' != $instance['caption'] )
$output = '[caption align="align' . esc_attr( $instance['align'] ) . '" width="' . esc_attr( $instance['img_width'] ) .'" caption="' . esc_attr( $instance['caption'] ) . '"]' . $output . '[/caption]';
- echo '<div style="overflow:hidden;">' . do_shortcode( $output ) . '</div>';
+ echo '<div class="jetpack-image-container">' . do_shortcode( $output ) . '</div>';
}
echo "\n" . $after_widget;
diff --git a/plugins/jetpack/modules/widgets/readmill.php b/plugins/jetpack/modules/widgets/readmill.php
new file mode 100644
index 00000000..7729ed3e
--- /dev/null
+++ b/plugins/jetpack/modules/widgets/readmill.php
@@ -0,0 +1,138 @@
+<?php
+class Jetpack_Readmill_Widget extends WP_Widget {
+ var $default_title, $default_size;
+
+ /**
+ * Registers the widget with WordPress.
+ */
+ function __construct() {
+ parent::__construct(
+ 'jetpack_readmill_widget', // Base ID
+ apply_filters( 'jetpack_widget_name', esc_html__( 'Send To Readmill', 'jetpack' ) ),
+ array(
+ 'description' => esc_html__( 'Readmill is the best book reader for phones and tablets. With this widget you can enable users to send a book to their device with one click.', 'jetpack' ),
+ )
+ );
+
+ if ( is_active_widget( false, false, $this->id_base ) || is_active_widget( false, false, 'monster' ) ) {
+ add_action( 'wp_enqueue_scripts', array( $this, 'enqueue_script' ) );
+ }
+
+ $this->default_title = __( 'Send To Readmill', 'jetpack' );
+ $this->default_size = 'large';
+ }
+
+ function enqueue_script() {
+ wp_enqueue_script( 'readmill', 'https://platform.readmill.com/send.js', array(), '20130220', false );
+ }
+
+ /**
+ * Back-end widget form.
+ *
+ * @see WP_Widget::form()
+ *
+ * @param array $instance Previously saved values from database.
+ */
+ function form( $instance ) {
+ $title = isset( $instance['title' ] ) ? $instance['title'] : false;
+ if ( false === $title ) {
+ $title = $this->default_title;
+ }
+
+ $epub_link = isset( $instance['epub_link'] ) ? $instance['epub_link'] : '';
+ $buy_link = isset( $instance['buy_link'] ) ? $instance['buy_link'] : '';
+ $size = isset( $instance['size'] ) ? $instance['size'] : $this->default_size;
+ ?>
+
+ <p><?php printf( __( "Just enter the URL to your book, make sure it's a PDF or EPUB file, and you are ready to go. For more help, head to <a href='%s'>the Readmill WordPress Widget support page</a>." ), 'http://en.support.wordpress.com/widgets/readmill/' ); ?></p>
+
+ <p>
+ <label for="<?php echo $this->get_field_id( 'title' ); ?>"><?php esc_html_e( 'Title:', 'jetpack' ); ?></label>
+ <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 ); ?>" />
+ </p>
+
+ <p>
+ <label for="<?php echo $this->get_field_id( 'epub_link' ); ?>"><?php esc_html_e( 'Download URL:', 'jetpack' ); ?></label>
+ <input class="widefat" id="<?php echo $this->get_field_id( 'epub_link' ); ?>" name="<?php echo $this->get_field_name( 'epub_link' ); ?>" type="text" value="<?php echo esc_attr( $epub_link ); ?>" />
+ </p>
+
+ <p>
+ <label for="<?php echo $this->get_field_id( 'buy_link' ); ?>"><?php esc_html_e( 'Item URL:', 'jetpack' ); ?></label>
+ <input class="widefat" id="<?php echo $this->get_field_id( 'buy_link' ); ?>" name="<?php echo $this->get_field_name( 'buy_link' ); ?>" type="text" value="<?php echo esc_attr( $buy_link ); ?>" />
+ </p>
+
+ <p>
+ <label><?php esc_html_e( 'What size icon?', 'jetpack' ); ?></label>
+ <ul>
+ <li><label><input id="<?php echo $this->get_field_id( 'size' ); ?>-few" name="<?php echo $this->get_field_name( 'size' ); ?>" type="radio" value="large" <?php checked( 'large', $size ); ?> /> <?php esc_html_e( 'Large', 'jetpack' ); ?></label></li>
+ <li><label><input id="<?php echo $this->get_field_id( 'size' ); ?>-lots" name="<?php echo $this->get_field_name( 'size' ); ?>" type="radio" value="small" <?php checked( 'small', $size ); ?> /> <?php esc_html_e( 'Small', 'jetpack' ); ?></label></li>
+ </ul>
+ </p>
+
+ <?php
+ }
+
+ /**
+ * Sanitize widget form values as they are saved.
+ *
+ * @see WP_Widget::update()
+ *
+ * @param array $new_instance Values just sent to be saved.
+ * @param array $old_instance Previously saved values from database.
+ *
+ * @return array Updated safe values to be saved.
+ */
+ function update( $new_instance, $old_instance ) {
+ $instance = array();
+ $instance['title'] = wp_kses( $new_instance['title'], array() );
+ $instance['epub_link'] = wp_kses( $new_instance['epub_link'], array() );
+ $instance['buy_link'] = wp_kses( $new_instance['buy_link'], array() );
+ $instance['size'] = wp_kses( $new_instance['size'], array() );
+
+ if ( $this->default_title === $instance['title'] ) {
+ $instance['title'] = false; // Store as false in case of language change
+ }
+
+ return $instance;
+ }
+
+ /**
+ * Front-end display of widget.
+ *
+ * @see WP_Widget::widget()
+ *
+ * @param array $args Widget arguments.
+ * @param array $instance Saved values from database.
+ */
+ function widget( $args, $instance ) {
+ $title = isset( $instance['title' ] ) ? $instance['title'] : false;
+
+ if ( false === $title )
+ $title = $this->default_title;
+
+ $title = apply_filters( 'widget_title', $title );
+
+ echo $args['before_widget'];
+
+ if ( ! empty( $title ) )
+ echo $args['before_title'] . $title . $args['after_title'];
+
+ $epub_link = isset( $instance['epub_link'] ) ? $instance['epub_link'] : '';
+ $buy_link = isset( $instance['buy_link'] ) ? $instance['buy_link'] : '';
+ $size = isset( $instance['size'] ) ? $instance['size'] : $this->default_size;
+
+ if ( empty( $epub_link ) && current_user_can( 'edit_theme_options' ) ) :
+ ?><p><?php esc_html_e( 'Your ePub link is empty. Provide an ePub link to display the Send to Readmill widget.', 'jetpack' ); ?></p><?php
+ else :
+ ?><a class="send-to-readmill" href="https://readmill.com" data-download-url="<?php echo esc_attr( $epub_link ); ?>" data-buy-url="<?php echo esc_attr( $epub_link ); ?>" data-display="<?php echo esc_attr( $size ); ?>">Send to Readmill</a><?php
+ endif;
+
+ echo $args['after_widget'];
+ }
+}
+
+function jetpack_readmill_widget_init() {
+ register_widget( 'Jetpack_Readmill_Widget' );
+}
+
+add_action( 'widgets_init', 'jetpack_readmill_widget_init' );
diff --git a/plugins/jetpack/modules/widgets/top-posts.php b/plugins/jetpack/modules/widgets/top-posts.php
new file mode 100644
index 00000000..b6f2c8d7
--- /dev/null
+++ b/plugins/jetpack/modules/widgets/top-posts.php
@@ -0,0 +1,295 @@
+<?php
+
+/*
+ * Currently, this widget depends on the Stats Module. To not load this file
+ * when the Stats Module is not active would potentially bypass Jetpack's
+ * fatal error detection on module activation, so we always load this file.
+ * Instead, we don't register the widget if the Stats Module isn't active.
+ */
+
+/**
+ * Register the widget for use in Appearance -> Widgets
+ */
+add_action( 'widgets_init', 'jetpack_top_posts_widget_init' );
+
+function jetpack_top_posts_widget_init() {
+ // Currently, this widget depends on the Stats Module
+ if (
+ ( !defined( 'IS_WPCOM' ) || !IS_WPCOM )
+ &&
+ !function_exists( 'stats_get_csv' )
+ ) {
+ return;
+ }
+
+ register_widget( 'Jetpack_Top_Posts_Widget' );
+}
+
+class Jetpack_Top_Posts_Widget extends WP_Widget {
+ var $alt_option_name = 'widget_stats_topposts';
+ var $default_title = '';
+
+ function __construct() {
+ parent::__construct(
+ 'top-posts',
+ apply_filters( 'jetpack_widget_name', __( 'Top Posts &amp; Pages', 'jetpack' ) ),
+ array(
+ 'description' => __( 'Shows your most viewed posts and pages.', 'jetpack' ),
+ )
+ );
+
+ $this->default_title = __( 'Top Posts &amp; Pages', 'jetpack' );
+
+ if ( is_active_widget( false, false, $this->id_base ) ) {
+ add_action( 'wp_print_styles', array( $this, 'enqueue_style' ) );
+ }
+ }
+
+ function enqueue_style() {
+ wp_register_style( 'widget-grid-and-list', plugins_url( 'widget-grid-and-list.css', __FILE__ ) );
+ wp_enqueue_style( 'widget-grid-and-list' );
+ }
+
+ function form( $instance ) {
+ $title = isset( $instance['title' ] ) ? $instance['title'] : false;
+ if ( false === $title ) {
+ $title = $this->default_title;
+ }
+
+ $count = isset( $instance['count'] ) ? (int) $instance['count'] : 10;
+ if ( $count < 1 || 20 < $count ) {
+ $count = 10;
+ }
+
+ if ( isset( $instance['display'] ) && in_array( $instance['display'], array( 'grid', 'list', 'text' ) ) ) {
+ $display = $instance['display'];
+ } else {
+ $display = 'text';
+ }
+
+ ?>
+
+ <p>
+ <label for="<?php echo $this->get_field_id( 'title' ); ?>"><?php esc_html_e( 'Title:', 'jetpack' ); ?></label>
+ <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 ); ?>" />
+ </p>
+
+ <p>
+ <label for="<?php echo $this->get_field_id( 'count' ); ?>"><?php esc_html_e( 'Number of posts to show:', 'jetpack' ); ?></label>
+ <input id="<?php echo $this->get_field_id( 'count' ); ?>" name="<?php echo $this->get_field_name( 'count' ); ?>" type="number" value="<?php echo (int) $count; ?>" min="1" max="20" />
+ </p>
+
+ <p>
+ <label><?php esc_html_e( 'Display as:', 'jetpack' ); ?></label>
+ <ul>
+ <li><label><input id="<?php echo $this->get_field_id( 'display' ); ?>-text" name="<?php echo $this->get_field_name( 'display' ); ?>" type="radio" value="text" <?php checked( 'text', $display ); ?> /> <?php esc_html_e( 'Text List', 'jetpack' ); ?></label></li>
+ <li><label><input id="<?php echo $this->get_field_id( 'display' ); ?>-list" name="<?php echo $this->get_field_name( 'display' ); ?>" type="radio" value="list" <?php checked( 'list', $display ); ?> /> <?php esc_html_e( 'Image List', 'jetpack' ); ?></label></li>
+ <li><label><input id="<?php echo $this->get_field_id( 'display' ); ?>-grid" name="<?php echo $this->get_field_name( 'display' ); ?>" type="radio" value="grid" <?php checked( 'grid', $display ); ?> /> <?php esc_html_e( 'Image Grid', 'jetpack' ); ?></label></li>
+ </ul>
+ </p>
+
+ <p><?php esc_html_e( 'Top Posts &amp; Pages by views are calculated from 24-48 hours of stats. They take a while to change.', 'jetpack' ); ?></p>
+
+ <?php
+ }
+
+ function update( $new_instance, $old_instance ) {
+ $instance = array();
+ $instance['title'] = wp_kses( $new_instance['title'], array() );
+ if ( $instance['title'] === $this->default_title ) {
+ $instance['title'] = false; // Store as false in case of language change
+ }
+
+ $instance['count'] = (int) $new_instance['count'];
+ if ( $instance['count'] < 1 || 20 < $instance['count'] ) {
+ $instance['count'] = 10;
+ }
+
+ if ( isset( $new_instance['display'] ) && in_array( $new_instance['display'], array( 'grid', 'list', 'text' ) ) ) {
+ $instance['display'] = $new_instance['display'];
+ } else {
+ $instance['display'] = 'text';
+ }
+
+ return $instance;
+ }
+
+ function widget( $args, $instance ) {
+ $title = isset( $instance['title' ] ) ? $instance['title'] : false;
+ if ( false === $title )
+ $title = $this->default_title;
+ $title = apply_filters( 'widget_title', $title );
+
+ $count = isset( $instance['count'] ) ? (int) $instance['count'] : false;
+ if ( $count < 1 || 20 < $count ) {
+ $count = 10;
+ }
+
+ if ( isset( $instance['display'] ) && in_array( $instance['display'], array( 'grid', 'list', 'text' ) ) ) {
+ $display = $instance['display'];
+ } else {
+ $display = 'text';
+ }
+
+ if ( 'text' != $display ) {
+ $get_image_options = array(
+ 'fallback_to_avatars' => true,
+ 'gravatar_default' => apply_filters( 'jetpack_static_url', is_ssl() ? 'https' : 'http' . '://en.wordpress.com/i/logo/white-gray-80.png' ),
+ );
+ if ( 'grid' == $display ) {
+ if ( $count %2 != 0 ) {
+ $count++;
+ }
+
+ $get_image_options['avatar_size'] = 200;
+ } else {
+ $get_image_options['avatar_size'] = 40;
+ }
+ $get_image_options = apply_filters( 'jetpack_top_posts_widget_image_options', $get_image_options );
+ }
+
+ $posts = $this->get_by_views( $count );
+
+ if ( !$posts ) {
+ $posts = $this->get_fallback_posts();
+ }
+
+ echo $args['before_widget'];
+ if ( ! empty( $title ) )
+ echo $args['before_title'] . $title . $args['after_title'];
+
+ if ( !$posts ) {
+ if ( current_user_can( 'edit_theme_options' ) ) {
+ echo '<p>' . sprintf(
+ __( 'There are no posts to display. <a href="%s">Want more traffic?</a>', 'jetpack' ),
+ 'http://en.support.wordpress.com/getting-more-site-traffic/'
+ ) . '</p>';
+ }
+
+ echo $args['after_widget'];
+ return;
+ }
+
+ switch ( $display ) {
+ case 'list' :
+ case 'grid' :
+ wp_enqueue_style( 'widget-grid-and-list' );
+ foreach ( $posts as &$post ) {
+ $image = Jetpack_PostImages::get_image( $post['post_id'] );
+ $post['image'] = $image['src'];
+ if ( 'blavatar' != $image['from'] && 'gravatar' != $image['from'] ) {
+ $size = (int) $get_image_options['avatar_size'];
+ $post['image'] = jetpack_photon_url( $post['image'], array( 'resize' => "$size,$size" ) );
+ }
+ }
+
+ unset( $post );
+
+ if ( 'grid' == $display ) {
+ echo "<div class='widgets-grid-layout no-grav'>\n";
+ foreach ( $posts as $post ) :
+ ?>
+ <div class="widget-grid-view-image">
+ <a href="<?php echo esc_url( $post['permalink'] ); ?>" title="<?php echo esc_attr( wp_kses( $post['title'], array() ) ); ?>" class="bump-view" data-bump-view="tp"><img src="<?php echo esc_url( $post['image'] ); ?>" /></a>
+ </div>
+
+ <?php
+ endforeach;
+ echo "</div>\n";
+ } else {
+ echo "<ul class='widgets-list-layout no-grav'>\n";
+ foreach ( $posts as $post ) :
+ ?>
+ <li>
+ <img src="<?php echo esc_url( $post['image'] ); ?>" class='widgets-list-layout-blavatar' />
+ <div class="widgets-list-layout-links"><a href="<?php echo esc_url( $post['permalink'] ); ?>" class="bump-view" data-bump-view="tp"><?php echo esc_html( wp_kses( $post['title'], array() ) ); ?></a></div>
+ </li>
+ <?php
+ endforeach;
+ echo "</ul>\n";
+ }
+ break;
+ default :
+ echo '<ul>';
+ foreach ( $posts as $post ) {
+ echo '<li><a href="' . esc_url( $post['permalink'] ) . '" class="bump-view" data-bump-view="tp">' . esc_html( $post['title'] ) . "</a></li>\n";
+ }
+ echo '</ul>';
+ }
+
+ echo $args['after_widget'];
+ }
+
+ function get_by_views( $count ) {
+ global $wpdb;
+ $post_view_posts = stats_get_csv( 'postviews', array( 'days' => 2, 'limit' => 10 ) );
+ if ( !$post_view_posts ) {
+ return array();
+ }
+
+ $post_view_ids = array_filter( wp_list_pluck( $post_view_posts, 'post_id' ) );
+ if ( !$post_view_ids ) {
+ return array();
+ }
+
+ return $this->get_posts( $post_view_ids, $count );
+ }
+
+ function get_fallback_posts() {
+ if ( current_user_can( 'edit_theme_options' ) ) {
+ return array();
+ }
+
+ $post_query = new WP_Query;
+
+ $posts = $post_query->query( array(
+ 'posts_per_page' => 1,
+ 'post_status' => 'publish',
+ 'post_type' => array( 'post', 'page' ),
+ 'no_found_rows' => true,
+ ) );
+
+ if ( !$posts ) {
+ return array();
+ }
+
+ $post = array_pop( $posts );
+
+ return $this->get_posts( $post->ID, 1 );
+ }
+
+ function get_posts( $post_ids, $count ) {
+ $counter = 0;
+
+ $posts = array();
+ foreach ( (array) $post_ids as $post_id ) {
+ $post = get_post( $post_id );
+
+ if ( !$post )
+ continue;
+
+ // hide private and password protected posts
+ if ( 'publish' != $post->post_status || !empty( $post->post_password ) || empty( $post->ID ) )
+ continue;
+
+ // Both get HTML stripped etc on display
+ if ( empty( $post->post_title ) ) {
+ $title_source = $post->post_content;
+ $title = wp_html_excerpt( $title_source, 50 );
+ $title .= '&hellip;';
+ } else {
+ $title = $post->post_title;
+ }
+
+ $permalink = get_permalink( $post->ID );
+
+ $posts[] = compact( 'title', 'permalink', 'post_id' );
+ $counter++;
+
+ if ( $counter == $count )
+ break; // only need to load and show x number of likes
+ }
+
+ return $posts;
+ }
+}
diff --git a/plugins/jetpack/modules/widgets/twitter.php b/plugins/jetpack/modules/widgets/twitter.php
new file mode 100644
index 00000000..ff49abf7
--- /dev/null
+++ b/plugins/jetpack/modules/widgets/twitter.php
@@ -0,0 +1,407 @@
+<?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/widgets/widget-grid-and-list.css b/plugins/jetpack/modules/widgets/widget-grid-and-list.css
new file mode 100644
index 00000000..984016c9
--- /dev/null
+++ b/plugins/jetpack/modules/widgets/widget-grid-and-list.css
@@ -0,0 +1,110 @@
+/* 2-Column Grid Layout */
+
+.widgets-grid-layout {
+ width: 100%;
+}
+
+.widgets-grid-layout:before,
+.widgets-grid-layout:after {
+ content: " ";
+ display: table;
+}
+
+.widgets-grid-layout:after {
+ clear: both;
+}
+
+.widget-grid-view-image {
+ float: left;
+ max-width: 50%;
+}
+
+.widget-grid-view-image a {
+ display: block;
+ margin: 0 2px 4px 0;
+}
+
+.widget-grid-view-image:image:nth-child(even) {
+ float: right;
+}
+
+.widget-grid-view-image:nth-child(even) a {
+ margin: 0 0 4px 2px;
+}
+
+.widgets-grid-layout .widget-grid-view-image img {
+ max-width: 100%;
+ height: auto;
+}
+
+/* Multi-Column Grid Layout */
+
+.widgets-multi-column-grid ul {
+ overflow: hidden;
+ padding: 0;
+ margin: 0;
+ list-style-type: none;
+}
+
+.widgets-multi-column-grid ul li {
+ background: none;
+ clear: none;
+ float: left;
+ margin: 0 -5px -3px 0;
+ padding: 0 8px 6px 0;
+ border: none;
+ list-style-type: none !important;
+}
+
+.widgets-multi-column-grid ul li a {
+ background: none;
+ margin: 0;
+ padding: 0;
+ border: 0;
+}
+
+.widgets-multi-column-grid .avatar {
+ vertical-align: middle;
+}
+
+/* List Layout */
+
+.widgets-list-layout {
+ padding: 0;
+ margin: 0;
+ list-style-type: none;
+}
+
+.widgets-list-layout li:before,
+.widgets-list-layout li:after {
+ content:"";
+ display:table;
+}
+.widgets-list-layout li:after {
+ clear:both;
+}
+.widgets-list-layout li {
+ zoom:1;
+ margin-bottom: 1em;
+ list-style-type: none !important;
+}
+
+.widgets-list-layout .widgets-list-layout-blavatar {
+ float: left;
+ width: 21.276596%;
+ max-width: 40px;
+ height: auto;
+}
+
+.widgets-list-layout-links {
+ float: right;
+ width: 73.404255%;
+}
+
+.widgets-list-layout span {
+ opacity: 0.5;
+}
+
+.widgets-list-layout span:hover {
+ opacity: 0.8;
+}
diff --git a/plugins/jetpack/modules/widgets/widgets.css b/plugins/jetpack/modules/widgets/widgets.css
new file mode 100644
index 00000000..a2d62957
--- /dev/null
+++ b/plugins/jetpack/modules/widgets/widgets.css
@@ -0,0 +1,13 @@
+/*
+ * Widget styles for Jetpack
+ */
+
+/* Clear floats */
+.jetpack-image-container:after {
+ clear: both;
+}
+.jetpack-image-container:before,
+.jetpack-image-container:after {
+ display: table;
+ content: "";
+}