Make WordPress Core

Ticket #31373: 31373.2.patch

File 31373.2.patch, 256.5 KB (added by DrewAPicture, 10 years ago)

Docs: 1st pass audit

  • src/wp-admin/admin-ajax.php

     
    6161        'query-attachments', 'save-attachment', 'save-attachment-compat', 'send-link-to-editor',
    6262        'send-attachment-to-editor', 'save-attachment-order', 'heartbeat', 'get-revision-diffs',
    6363        'save-user-color-scheme', 'update-widget', 'query-themes', 'parse-embed', 'set-attachment-thumbnail',
    64         'parse-media-shortcode', 'destroy-sessions', 'install-plugin', 'update-plugin'
     64        'parse-media-shortcode', 'destroy-sessions', 'install-plugin', 'update-plugin', 'press-this-save-post',
     65        'press-this-add-category',
    6566);
    6667
    6768// Register core Ajax calls.
  • src/wp-admin/css/forms.css

     
    684684
    685685.pressthis {
    686686        margin: 20px 0;
     687        vertical-align: top;
     688        position: relative;
     689        z-index: 1;
    687690}
    688691
    689692.pressthis a,
     
    748751        box-shadow: 0 10px 8px rgba(0, 0, 0, 0.6);
    749752}
    750753
     754.pressthis .button {
     755        margin-left: 10px;
     756        padding: 0;
     757        height: auto;
     758        vertical-align: top;
     759}
     760
     761.pressthis button .dashicons {
     762        margin: 5px 8px 6px 7px;
     763        color: #777;
     764}
     765
     766.press-this-install {
     767        margin: 20px 0 0 0;
     768        padding: 0.7em 2em 1em;
     769        max-width: 520px;
     770}
     771
     772.press-this-install textarea {
     773        width: 100%;
     774        font-size: 1em;
     775}
     776
     777.press-this-install h4 {
     778        margin: 2em 0 1em;
     779}
     780
     781
    751782/*------------------------------------------------------------------------------
    752783  20.0 - Settings
    753784------------------------------------------------------------------------------*/
  • src/wp-admin/css/press-this-editor.css

     
     1/*
     2Press This TinyMCE editor styles :)
     3*/
     4
     5
     6/**
     7* Links
     8*/
     9@import url("//fonts.googleapis.com/css?family=Open+Sans:400italic,700italic,400,600,700");
     10a {
     11        color: #0074a2;
     12}
     13
     14a:visited {
     15        color: #0074a2;
     16}
     17
     18a:hover,
     19a:focus,
     20a:active {
     21        color: #2ea2cc;
     22}
     23
     24
     25/**
     26* Lists
     27*/
     28ul,
     29ol {
     30        margin: 0 0 1.5em 3em;
     31}
     32
     33ul {
     34        list-style: disc;
     35}
     36
     37ol {
     38        list-style: decimal;
     39}
     40
     41li > ul,
     42li > ol {
     43        margin-bottom: 0;
     44        margin-left: 1.5em;
     45}
     46
     47dt {
     48        font-weight: 700;
     49}
     50
     51dd {
     52        margin: 0 1.5em 1.5em;
     53}
     54
     55
     56/**
     57* Media
     58*
     59* Basic image and object styles
     60*/
     61img {
     62        max-width: 100%;
     63        height: auto;
     64}
     65
     66/* Makes sure embeds and iframes fit inside their containers */
     67embed,
     68iframe,
     69object {
     70        max-width: 100%;
     71}
     72
     73
     74/**
     75* TinyMCE styles
     76*
     77* Pretty dang good.
     78*/
     79body {
     80        color: #404040;
     81        font-family: "Open Sans", Helvetica, Arial, sans-serif;
     82        font-size: 20px;
     83        font-weight: 400;
     84        line-height: 1.6;
     85}
     86@media (max-width: 900px) {
     87        body#tinymce {
     88                padding-top: 30px !important;
     89        }
     90}
     91@media (max-width: 640px) {
     92        body {
     93                font-size: 16px;
     94        }
     95}
     96@media (max-width: 320px) {
     97        body {
     98                margin: 0 15px;
     99        }
     100}
     101
     102#tinymce b,
     103#tinymce strong {
     104        /* overrides TinyMCE's !important. Woohoo. */
     105        font-weight: 700 !important;
     106}
     107
     108blockquote {
     109        margin: 1em 1.5em;
     110        color: #9ea7af;
     111        font-size: em(25px);
     112        font-style: italic;
     113}
     114@media (max-width: 900px) {
     115        blockquote {
     116                margin: 1.5em 1em;
     117        }
     118}
     119
     120ul,
     121ol {
     122        margin: 0 0 1.5em .75em;
     123}
     124/*
     125Press This TinyMCE editor styles :)
     126*/
     127
     128
     129/**
     130* Links
     131*/
     132@import url("//fonts.googleapis.com/css?family=Open+Sans:400italic,700italic,400,600,700");
     133a {
     134        color: #0074a2;
     135}
     136
     137a:visited {
     138        color: #0074a2;
     139}
     140
     141a:hover,
     142a:focus,
     143a:active {
     144        color: #2ea2cc;
     145}
     146
     147
     148/**
     149* Lists
     150*/
     151ul,
     152ol {
     153        margin: 0 0 1.5em 3em;
     154}
     155
     156ul {
     157        list-style: disc;
     158}
     159
     160ol {
     161        list-style: decimal;
     162}
     163
     164li > ul,
     165li > ol {
     166        margin-bottom: 0;
     167        margin-left: 1.5em;
     168}
     169
     170dt {
     171        font-weight: 700;
     172}
     173
     174dd {
     175        margin: 0 1.5em 1.5em;
     176}
     177
     178
     179/**
     180* Media
     181*
     182* Basic image and object styles
     183*/
     184img {
     185        max-width: 100%;
     186        height: auto;
     187}
     188
     189/* Makes sure embeds and iframes fit inside their containers */
     190embed,
     191iframe,
     192object {
     193        max-width: 100%;
     194}
     195
     196
     197/**
     198* TinyMCE styles
     199*
     200* Pretty dang good.
     201*/
     202body {
     203        color: #404040;
     204        font-family: "Open Sans", Helvetica, Arial, sans-serif;
     205        font-size: 20px;
     206        font-weight: 400;
     207        line-height: 1.6;
     208}
     209@media (max-width: 900px) {
     210        body#tinymce {
     211                padding-top: 30px !important;
     212        }
     213}
     214@media (max-width: 640px) {
     215        body {
     216                font-size: 16px;
     217        }
     218}
     219@media (max-width: 320px) {
     220        body {
     221                margin: 0 15px;
     222        }
     223}
     224
     225#tinymce b,
     226#tinymce strong {
     227        /* overrides TinyMCE's !important. Woohoo. */
     228        font-weight: 700 !important;
     229}
     230
     231blockquote {
     232        margin: 1em 1.5em;
     233        color: #9ea7af;
     234        font-size: em(25px);
     235        font-style: italic;
     236}
     237@media (max-width: 900px) {
     238        blockquote {
     239                margin: 1.5em 1em;
     240        }
     241}
     242
     243ul,
     244ol {
     245        margin: 0 0 1.5em .75em;
     246}
     247/*
     248Press This TinyMCE editor styles :)
     249*/
     250
     251
     252/**
     253* Links
     254*/
     255@import url("//fonts.googleapis.com/css?family=Open+Sans:400italic,700italic,400,600,700");
     256a {
     257        color: #0074a2;
     258}
     259
     260a:visited {
     261        color: #0074a2;
     262}
     263
     264a:hover,
     265a:focus,
     266a:active {
     267        color: #2ea2cc;
     268}
     269
     270
     271/**
     272* Lists
     273*/
     274ul,
     275ol {
     276        margin: 0 0 1.5em 3em;
     277}
     278
     279ul {
     280        list-style: disc;
     281}
     282
     283ol {
     284        list-style: decimal;
     285}
     286
     287li > ul,
     288li > ol {
     289        margin-bottom: 0;
     290        margin-left: 1.5em;
     291}
     292
     293dt {
     294        font-weight: 700;
     295}
     296
     297dd {
     298        margin: 0 1.5em 1.5em;
     299}
     300
     301
     302/**
     303* Media
     304*
     305* Basic image and object styles
     306*/
     307img {
     308        max-width: 100%;
     309        height: auto;
     310}
     311
     312/* Makes sure embeds and iframes fit inside their containers */
     313embed,
     314iframe,
     315object {
     316        max-width: 100%;
     317}
     318
     319
     320/**
     321* TinyMCE styles
     322*
     323* Pretty dang good.
     324*/
     325body {
     326        color: #404040;
     327        font-family: "Open Sans", Helvetica, Arial, sans-serif;
     328        font-size: 20px;
     329        font-weight: 400;
     330        line-height: 1.6;
     331}
     332@media (max-width: 900px) {
     333        body#tinymce {
     334                padding-top: 30px !important;
     335        }
     336}
     337@media (max-width: 640px) {
     338        body {
     339                font-size: 16px;
     340        }
     341}
     342@media (max-width: 320px) {
     343        body {
     344                margin: 0 15px;
     345        }
     346}
     347
     348#tinymce b,
     349#tinymce strong {
     350        /* overrides TinyMCE's !important. Woohoo. */
     351        font-weight: 700 !important;
     352}
     353
     354blockquote {
     355        margin: 1em 1.5em;
     356        color: #9ea7af;
     357        font-size: em(25px);
     358        font-style: italic;
     359}
     360@media (max-width: 900px) {
     361        blockquote {
     362                margin: 1.5em 1em;
     363        }
     364}
     365
     366ul,
     367ol {
     368        margin: 0 0 1.5em .75em;
     369}
     370/*
     371Press This TinyMCE editor styles :)
     372*/
     373
     374
     375/**
     376* Links
     377*/
     378@import url("//fonts.googleapis.com/css?family=Open+Sans:400italic,700italic,400,600,700");
     379a {
     380        color: #0074a2;
     381}
     382
     383a:visited {
     384        color: #0074a2;
     385}
     386
     387a:hover,
     388a:focus,
     389a:active {
     390        color: #2ea2cc;
     391}
     392
     393
     394/**
     395* Lists
     396*/
     397ul,
     398ol {
     399        margin: 0 0 1.5em 3em;
     400}
     401
     402ul {
     403        list-style: disc;
     404}
     405
     406ol {
     407        list-style: decimal;
     408}
     409
     410li > ul,
     411li > ol {
     412        margin-bottom: 0;
     413        margin-left: 1.5em;
     414}
     415
     416dt {
     417        font-weight: 700;
     418}
     419
     420dd {
     421        margin: 0 1.5em 1.5em;
     422}
     423
     424
     425/**
     426* Media
     427*
     428* Basic image and object styles
     429*/
     430img {
     431        max-width: 100%;
     432        height: auto;
     433}
     434
     435/* Makes sure embeds and iframes fit inside their containers */
     436embed,
     437iframe,
     438object {
     439        max-width: 100%;
     440}
     441
     442
     443/**
     444* TinyMCE styles
     445*
     446* Pretty dang good.
     447*/
     448body {
     449        color: #404040;
     450        font-family: "Open Sans", Helvetica, Arial, sans-serif;
     451        font-size: 20px;
     452        font-weight: 400;
     453        line-height: 1.6;
     454}
     455@media (max-width: 900px) {
     456        body#tinymce {
     457                padding-top: 30px !important;
     458        }
     459}
     460@media (max-width: 640px) {
     461        body {
     462                font-size: 16px;
     463        }
     464}
     465@media (max-width: 320px) {
     466        body {
     467                margin: 0 15px;
     468        }
     469}
     470
     471#tinymce b,
     472#tinymce strong {
     473        /* overrides TinyMCE's !important. Woohoo. */
     474        font-weight: 700 !important;
     475}
     476
     477blockquote {
     478        margin: 1em 1.5em;
     479        color: #9ea7af;
     480        font-size: em(25px);
     481        font-style: italic;
     482}
     483@media (max-width: 900px) {
     484        blockquote {
     485                margin: 1.5em 1em;
     486        }
     487}
     488
     489ul,
     490ol {
     491        margin: 0 0 1.5em .75em;
     492}
  • src/wp-admin/css/press-this.css

     
    1 .press-this #message {
    2         border-left: 4px solid #7ad03a;
    3         padding: 1px 12px;
    4         background-color: #fff;
    5         -webkit-box-shadow: 0 1px 1px 0 rgba(0,0,0,0.1);
    6         box-shadow: 0 1px 1px 0 rgba(0,0,0,0.1);
     1/*
     2Press This styles :)
     3*/
     4
     5
     6/**
     7* Normalize
     8*
     9* normalize.css v3.0.0 | MIT License | git.io/normalize
     10*/
     11html {
     12        font-family: sans-serif;
     13        -ms-text-size-adjust: 100%;
     14        -webkit-text-size-adjust: 100%;
    715}
    816
    9 .press-this #side-sortables .category-tabs li {
    10         display: inline;
    11         line-height: 1.35em;
     17body {
     18        margin: 0;
    1219}
    1320
    14 body.press-this ul.category-tabs li.tabs a {
    15         color: #32373c;
     21*,
     22*:before,
     23*:after {
     24        -webkit-box-sizing: border-box;
     25        -moz-box-sizing: border-box;
     26        box-sizing: border-box;
    1627}
     28@media only screen and (-webkit-min-device-pixel-ratio: 1.5), only screen and (min-resolution: 144dpi) {
     29        *,
     30        *:before,
     31        *:after {
     32                -webkit-font-smoothing: antialiased;
     33        }
     34}
    1735
    18 .press-this #content-resize-handle {
    19         bottom: 2px;
     36article,
     37aside,
     38details,
     39figcaption,
     40figure,
     41footer,
     42header,
     43hgroup,
     44main,
     45nav,
     46section,
     47summary {
     48        display: block;
    2049}
    2150
    22 body.press-this {
    23         color: #32373c;
    24         margin: 0;
    25         padding: 0;
    26         min-width: 708px;
    27         min-height: 400px;
     51audio,
     52canvas,
     53progress,
     54video {
     55        display: inline-block;
     56        vertical-align: baseline;
    2857}
    2958
    30 .press-this #titlediv #title {
    31         font-size: 1.4em;
     59audio:not([controls]) {
     60        display: none;
     61        height: 0;
    3262}
    3363
    34 .press-this #site-heading:before {
    35     top: 3px;
    36     position: relative;
    37     display: inline-block;
    38     font: normal 18px/1 'dashicons';
    39     speak: none;
    40     color: #727272;
    41     content: '\f120';
    42     -webkit-font-smoothing: antialiased;
    43     -moz-osx-font-smoothing: grayscale;
     64[hidden],
     65template {
     66        display: none;
    4467}
    4568
    46 .press-this #wphead {
    47         height: 32px;
    48         margin-left: 0;
    49         margin-right: 0;
    50         margin-bottom: 5px;
     69a {
     70        background: transparent;
    5171}
    5272
    53 .press-this #header-logo {
    54         float: left;
    55         margin: 7px 7px 0;
    56         -webkit-user-select: none;
    57         -moz-user-select: none;
    58         -ms-user-select: none;
    59         user-select: none;
     73a:active,
     74a:hover {
     75        outline: 0;
    6076}
    6177
    62 .press-this #wphead h1 {
    63         font-weight: normal;
    64         font-size: 16px;
    65         line-height: 32px;
    66         margin: 0;
    67         float: left;
     78abbr[title] {
     79        border-bottom: 1px dotted;
    6880}
    6981
    70 .press-this #wphead h1 a {
    71         text-decoration: none;
     82b,
     83strong {
     84        font-weight: bold;
    7285}
    7386
    74 .press-this #wphead h1 a:hover {
    75         text-decoration: underline;
     87dfn {
     88        font-style: italic;
    7689}
    7790
    78 .press-this #message {
    79         margin: 10px 0;
     91h1 {
     92        font-size: 2em;
     93        margin: 0.67em 0;
    8094}
    8195
    82 .press-this .posting {
    83         margin-right: 250px;
     96mark {
     97        background: #ff0;
     98        color: #000;
    8499}
    85100
    86 .press-this-sidebar {
    87         float: right;
    88         width: 240px;
    89         padding-top: 10px;
     101small {
     102        font-size: 80%;
    90103}
    91104
    92 .press-this #title {
    93         margin-left: 0;
    94         margin-right: 0;
    95         -webkit-box-sizing: border-box;
    96         -moz-box-sizing: border-box;
    97         box-sizing: border-box;
     105sub,
     106sup {
     107        font-size: 75%;
     108        line-height: 0;
     109        position: relative;
     110        vertical-align: baseline;
    98111}
    99112
    100 .press-this .tagchecklist {
    101         margin-top: 8px;
     113sup {
     114        top: -0.5em;
    102115}
    103116
    104 .press-this #titlediv {
    105         margin: 0;
     117sub {
     118        bottom: -0.25em;
    106119}
    107120
    108 .press-this #wp-content-wrap #wp-content-editor-tools {
    109         padding: 0;
    110         top: 3px;
     121img {
     122        border: 0;
     123}
     124
     125svg:not(:root) {
    111126        overflow: hidden;
    112127}
    113128
    114 .press-this .wp-media-buttons {
     129figure {
     130        margin: 1em 40px;
     131}
     132
     133hr {
     134        -webkit-box-sizing: content-box;
     135        -moz-box-sizing: content-box;
     136        box-sizing: content-box;
     137        height: 0;
     138}
     139
     140pre {
     141        overflow: auto;
     142}
     143
     144code,
     145kbd,
     146pre,
     147samp {
     148        font-family: monospace, monospace;
     149        font-size: 1em;
     150}
     151
     152button,
     153input,
     154optgroup,
     155select,
     156textarea {
     157        color: inherit;
     158        font: inherit;
     159        margin: 0;
     160}
     161
     162button {
     163        overflow: visible;
     164}
     165
     166button,
     167select {
     168        text-transform: none;
     169}
     170
     171button,
     172html input[type="button"],
     173input[type="reset"],
     174input[type="submit"] {
     175        -webkit-appearance: button;
     176        cursor: pointer;
     177}
     178
     179button[disabled],
     180html input[disabled] {
    115181        cursor: default;
    116         padding: 8px 8px 6px;
    117182}
    118183
    119 .press-this #wp-content-wrap #wp-content-media-buttons a {
     184button::-moz-focus-inner,
     185input::-moz-focus-inner {
     186        border: 0;
    120187        padding: 0;
     188}
     189
     190input {
    121191        line-height: normal;
     192}
     193
     194input[type="checkbox"],
     195input[type="radio"] {
     196        -webkit-box-sizing: border-box;
     197        -moz-box-sizing: border-box;
     198        box-sizing: border-box;
     199        padding: 0;
     200}
     201
     202input[type="number"]::-webkit-inner-spin-button,
     203input[type="number"]::-webkit-outer-spin-button {
    122204        height: auto;
    123         font-size: 16px;
    124205}
    125206
    126 .press-this #wp-content-wrap .mce-toolbar .mce-btn-group .mce-btn {
    127         margin: 0 1px;
     207input[type="search"] {
     208        -webkit-appearance: textfield;
     209        -webkit-box-sizing: content-box;
     210        -moz-box-sizing: content-box;
     211        box-sizing: content-box;
    128212}
    129213
    130 .press-this #wp-content-wrap .mce-toolbar .mce-btn button {
    131         padding: 2px 3px;
     214input[type="search"]::-webkit-search-cancel-button,
     215input[type="search"]::-webkit-search-decoration {
     216        -webkit-appearance: none;
    132217}
    133218
    134 .press-this #wp-content-wrap div.mce-toolbar-grp,
    135 .press-this #wp-content-wrap .quicktags-toolbar {
    136         padding-right: 3px;
     219fieldset {
     220        border: 1px solid #c0c0c0;
     221        margin: 0 2px;
     222        padding: 0.35em 0.625em 0.75em;
    137223}
    138224
    139 .press-this .howto {
    140         margin-top: 2px;
    141         margin-bottom: 3px;
    142         font-size: 12px;
    143         font-style: italic;
    144         display: block;
     225legend {
     226        border: 0;
     227        padding: 0;
    145228}
    146229
    147 .press-this #wp-content-editor-container {
    148         clear: none;
     230textarea {
     231        overflow: auto;
    149232}
    150233
    151 .press-this #poststuff .inside {
    152         margin-top: 18px;
     234optgroup {
     235        font-weight: bold;
    153236}
    154237
    155 .press-this .category-tabs {
    156         margin-bottom: 3px;
     238table {
     239        border-collapse: collapse;
     240        border-spacing: 0;
    157241}
    158242
    159 /* Editor/Main Column */
    160 .press-this #poststuff {
    161         margin: 0 8px;
     243td,
     244th {
    162245        padding: 0;
    163246}
    164247
    165 .press-this #photo-add-url-div input[type="text"] {
    166         width: 220px;
     248.clearfix:before,
     249.clearfix:after {
     250        content: "";
     251        display: table;
    167252}
     253.clearfix:after {
     254        clear: both;
     255}
    168256
    169 #poststuff #editor-toolbar {
    170         height: 30px;
     257.hide-if-js {
     258        display: none;
    171259}
    172260
    173 .posting {
    174         margin-right: 212px;
    175         position: relative;
     261.screen-reader-text,
     262.taghint {
     263        position: absolute;
     264        margin: -1px;
     265        padding: 0;
     266        height: 1px;
     267        width: 1px;
     268        overflow: hidden;
     269        clip: rect(0 0 0 0);
     270        border: 0;
    176271}
    177272
    178 .press-this .inner-sidebar {
    179         width: 200px;
     273
     274/**
     275* Typography
     276*
     277* Base element typographic styles.
     278*/
     279body,
     280button,
     281input,
     282select,
     283textarea {
     284        color: #404040;
     285        font-family: "Open Sans", Helvetica, Arial, sans-serif;
     286        font-size: 20px;
     287        font-weight: 400;
     288        line-height: 1.6;
    180289}
    181290
    182 .press-this .inner-sidebar .sleeve {
    183         padding-top: 5px;
     291h1,
     292h2,
     293h3,
     294h4,
     295h5,
     296h6 {
     297        clear: both;
    184298}
    185299
    186 .press-this #submitdiv p {
     300p {
     301        margin-bottom: 1.5em;
     302}
     303
     304b,
     305strong {
     306        font-weight: 700;
     307}
     308
     309
     310/**
     311* Buttons
     312*
     313* Pushing buttons is what I do.
     314*/
     315.button-primary,
     316.button-subtle,
     317.scan-submit {
     318        display: inline-block;
    187319        margin: 0;
    188         padding: 6px;
     320        padding: 0 10px 1px;
     321        border-width: 1px;
     322        border-style: solid;
     323        -webkit-border-radius: 3px;
     324        border-radius: 3px;
     325        font-size: 13px;
     326        line-height: 2;
     327        text-decoration: none;
     328        white-space: nowrap;
     329        cursor: pointer;
     330        -webkit-appearance: none;
    189331}
    190332
    191 .press-this #submitdiv #publishing-actions {
    192         border-bottom: 1px solid #dfdfdf;
     333.button-primary {
     334        background: #2ea2cc;
     335        border-color: #2581a2;
     336        color: #fff;
    193337}
    194338
    195 .press-this #publish {
    196         float: right;
     339.button-primary:hover,
     340.button-primary:focus {
     341        background: #2991b7;
     342        border-color: #20708e;
     343        color: #fff;
     344        outline: 0;
    197345}
    198346
    199 .press-this #poststuff h2,
    200 .press-this #poststuff h3 {
    201         font-size: 14px;
    202         line-height: 1;
     347.button-primary:active {
     348        background: #2581a2;
     349        border-color: #20708e;
     350        color: #fff;
    203351}
    204352
    205 .press-this #tagsdiv-post_tag h3,
    206 .press-this #categorydiv h3 {
    207         cursor: pointer;
     353.button-primary[disabled],
     354.button-primary:disabled {
     355        color: #c7ced1 !important;
     356        background: #2688ab !important;
     357        border-color: #20708e !important;
    208358}
    209359
    210 .press-this #submitdiv h3 {
    211         cursor: default;
     360.button-primary:visited {
     361        color: #fff;
    212362}
    213363
    214 h3.tb {
    215         font-weight: 600;
    216         font-size: 12px;
    217         margin-left: 5px;
     364.button-subtle {
     365        background: none;
     366        border: 0;
     367        color: #0074a2;
    218368}
    219369
    220 .press-this .postbox,
    221 .press-this .stuffbox {
    222         margin-bottom: 10px;
    223         min-width: 0;
     370.button-subtle:visited {
     371        color: #0074a2;
    224372}
    225373
    226 .press-this #submitdiv:hover .handlediv {
     374.button-subtle:focus,
     375.button-subtle:hover,
     376.button-subtle:active {
     377        color: #2ea2cc;
     378}
     379
     380.button-subtle:focus,
     381.button-subtle:active {
     382        outline: 0;
     383        text-decoration: underline;
     384}
     385
     386.button-reset {
     387        margin: 0;
     388        padding: 0;
     389        border: 0;
    227390        background: none;
     391        cursor: pointer;
     392        -webkit-appearance: none;
    228393}
    229394
    230 .tbtitle {
    231         font-size: 1.7em;
    232         outline: none;
    233         padding: 3px 4px;
    234         border: 1px solid #dfdfdf;
     395.button-reset:focus {
     396        outline: 0;
    235397}
    236398
    237 .press-this .actions {
    238         float: right;
    239         margin: -19px 0 0;
     399
     400/**
     401* Forms
     402*
     403* So many input types.
     404*/
     405button,
     406input,
     407select,
     408textarea {
     409        font-size: 100%;
     410        margin: 0;
     411        vertical-align: baseline;
     412        *vertical-align: middle;
    240413}
    241414
    242 .press-this #extra-fields .actions {
    243         margin: -32px -7px 0 0;
     415[type="checkbox"],
     416[type="radio"] {
     417        padding: 0;
    244418}
    245419
    246 .press-this .actions li {
    247         float: left;
    248         list-style: none;
    249         margin-right: 10px;
     420[type="search"] {
     421        -webkit-appearance: textfield;
     422        -webkit-box-sizing: content-box;
     423        -moz-box-sizing: content-box;
     424        box-sizing: content-box;
    250425}
    251426
    252 #extra-fields .button {
    253         margin-right: 5px;
     427[type="search"]::-webkit-search-decoration {
     428        -webkit-appearance: none;
    254429}
    255430
    256 /* Photo Styles */
    257 #photo_saving {
    258         margin: 0 8px 8px;
    259         vertical-align: middle;
     431button::-moz-focus-inner,
     432input::-moz-focus-inner {
     433        border: 0;
     434        padding: 0;
    260435}
    261436
    262 #img_container_container {
     437[type="text"],
     438[type="email"],
     439[type="url"],
     440[type="password"],
     441[type="search"],
     442textarea {
     443        padding: 0.4em 0.75em;
     444        color: #333;
     445        border: 1px solid #ccc;
     446}
     447
     448[type="text"]:focus,
     449[type="email"]:focus,
     450[type="url"]:focus,
     451[type="password"]:focus,
     452[type="search"]:focus,
     453textarea:focus {
     454        color: #333;
     455        outline: 0;
     456}
     457
     458textarea {
    263459        overflow: auto;
     460        padding-left: 3px;
     461        vertical-align: top;
    264462}
    265463
    266 #extra-fields {
    267         margin-top: 10px;
     464
     465/**
     466* Links
     467*/
     468a {
     469        color: #0074a2;
     470}
     471
     472a:visited {
     473        color: #0074a2;
     474}
     475
     476a:hover,
     477a:focus,
     478a:active {
     479        color: #2ea2cc;
     480}
     481
     482
     483/**
     484* Lists
     485*/
     486ul,
     487ol {
     488        margin: 0 0 1.5em 3em;
     489}
     490
     491ul {
     492        list-style: disc;
     493}
     494
     495ol {
     496        list-style: decimal;
     497}
     498
     499li > ul,
     500li > ol {
     501        margin-bottom: 0;
     502        margin-left: 1.5em;
     503}
     504
     505dt {
     506        font-weight: 700;
     507}
     508
     509dd {
     510        margin: 0 1.5em 1.5em;
     511}
     512
     513
     514/**
     515* Post formats
     516*
     517* Complete styles for post formats UI
     518*/
     519/* TODO if we remove the <br> during merge, this can go. */
     520#post-formats-select br {
     521        display: none;
     522}
     523
     524/* TODO Needed after merge? */
     525.post-format {
     526        width: 0;
     527        height: 0;
     528        position: absolute;
     529        top: -9999px;
     530}
     531
     532.lt-ie9 .post-format {
     533        margin: 17px 12px 0 13px;
     534        width: auto;
     535        height: auto;
     536        position: static;
     537        top: auto;
     538        float: left;
     539        width: 16px;
     540        height: 16px;
     541}
     542
     543.post-format-icon {
    268544        position: relative;
     545        display: block;
     546        padding: 13px 2px 14px 13px;
     547        cursor: pointer;
    269548}
    270549
    271 #extra-fields h2 {
    272         margin: 12px;
     550.post-format-icon:before,
     551.post-format-icon:after {
     552        content: "";
     553        display: inline-block;
     554        width: 20px;
     555        height: 20px;
     556        margin-right: 10px;
     557        font-size: 20px;
     558        line-height: 1;
     559        font-family: dashicons;
     560        text-decoration: inherit;
     561        color: #9ea7af;
     562        font-weight: 400;
     563        font-style: normal;
     564        vertical-align: top;
     565        text-align: center;
     566        -webkit-transition: color .1s ease-in 0;
     567        transition: color .1s ease-in 0;
     568        -webkit-font-smoothing: antialiased;
     569        -moz-osx-font-smoothing: grayscale;
    273570}
    274571
    275 #waiting {
    276         margin-top: 10px;
    277         overflow: hidden;
     572.post-format-icon:before {
     573        content: "\f109";
    278574}
    279575
    280 #waiting span {
     576.post-format-icon:after {
     577        display: none;
     578        content: "\f147";
    281579        float: right;
    282         margin: 0 0 0 5px;
    283580}
    284581
    285 #waiting .spinner {
     582.post-format:checked + .post-format-icon {
     583        -webkit-box-shadow: inset 6px 0 0 #2ea2cc;
     584        box-shadow: inset 6px 0 0 #2ea2cc;
     585        background: rgba(46, 162, 204, 0.1);
     586}
     587
     588.post-format:checked + .post-format-icon:before,
     589.post-format:checked + .post-format-icon:after {
     590        color: #333;
     591}
     592
     593.post-format:focus + .post-format-icon {
     594        background: #2ea2cc;
     595        color: #fff;
     596}
     597
     598.post-format:focus + .post-format-icon:before,
     599.post-format:focus + .post-format-icon:after {
     600        color: #fff;
     601}
     602
     603.post-format:checked + .post-format-icon:after {
    286604        display: block;
    287605}
    288606
    289 #extra-fields .postbox {
    290         margin-bottom: 5px;
     607.lt-ie9 .post-format-icon {
     608        margin-left: 16px;
    291609}
    292610
    293 #extra-fields .titlewrap {
     611.post-format-aside:before {
     612        content: "\f123";
     613}
     614
     615.post-format-image:before {
     616        content: "\f128";
     617}
     618
     619.post-format-video:before {
     620        content: "\f126";
     621}
     622
     623.post-format-audio:before {
     624        content: "\f127";
     625}
     626
     627.post-format-quote:before {
     628        content: "\f122";
     629}
     630
     631.post-format-link:before {
     632        content: "\f103";
     633}
     634
     635.post-format-gallery:before {
     636        content: "\f161";
     637}
     638
     639
     640/**
     641* Tags
     642*
     643* Complete styles for tags UI
     644*/
     645.tagsdiv p {
     646        margin: 0;
     647}
     648
     649.tagsdiv .ajaxtag {
     650        position: relative;
     651}
     652
     653.tagsdiv .newtag {
     654        display: block;
     655        position: relative;
     656        padding: 11px 58px 11px 16px;
     657        width: 100%;
     658        border: 0;
     659        border-bottom: 1px solid #e5e5e5;
     660        font-size: 16px;
     661}
     662
     663.tagsdiv .tagadd {
     664        position: absolute;
     665        top: 0;
     666        right: 0;
     667        bottom: 1px;
     668        border: 0;
     669        -webkit-border-radius: 0;
     670        border-radius: 0;
     671        padding: 0 16px;
     672        background: #f7f7f7;
     673        border-left: 1px solid #f1f1f1;
     674}
     675
     676.tagsdiv .tagadd:hover,
     677.tagsdiv .tagadd:active,
     678.tagsdiv .tagadd:focus {
     679        outline: 0;
     680        background: #2991b7;
     681        border-color: #20708e;
     682        color: #fff;
     683}
     684
     685.tagsdiv .howto {
     686        color: #727272;
     687        font-style: italic;
     688        margin: 10px 0 6px 16px;
     689}
     690
     691
     692/* Tag hint TODO needed? */
     693/* Tag suggestions */
     694.ac_results {
    294695        padding: 0;
    295         overflow: auto;
    296         height: 120px;
     696        margin: -1px 0 0 -1px;
     697        list-style: none;
     698        position: absolute;
     699        z-index: 10000;
     700        display: none;
     701        border: 1px solid #d8d8d8;
     702        background-color: #fff;
     703        font-size: 14px;
    297704}
    298705
    299 #img_container a {
     706.ac_results li {
     707        padding: 6px 16px;
     708        white-space: nowrap;
     709        color: #101010;
     710        text-align: left;
     711}
     712
     713.ac_results .ac_over {
     714        background-color: #e5e5e5;
     715        background-color: #2ea2cc;
     716        color: #fff;
     717        cursor: pointer;
     718}
     719
     720.ac_match {
     721        text-decoration: underline;
     722}
     723
     724/* Tags */
     725.tagchecklist {
     726        padding: 16px 28px 5px;
     727}
     728
     729.tagchecklist:before,
     730.tagchecklist:after {
     731        content: "";
     732        display: table;
     733}
     734
     735.tagchecklist:after {
     736        clear: both;
     737}
     738
     739.tagchecklist span {
    300740        display: block;
     741        margin-right: 25px;
    301742        float: left;
     743        font-size: 13px;
     744        line-height: 1.8;
     745        white-space: nowrap;
     746        cursor: default;
     747}
     748
     749@media (max-width: 600px) {
     750        .tagchecklist span {
     751                margin-bottom: 15px;
     752                font-size: 16px;
     753                line-height: 1.3;
     754        }
     755}
     756
     757.tagchecklist .ntdelbutton {
     758        margin: 1px 0 0 -17px;
     759        cursor: pointer;
     760        width: 20px;
     761        height: 20px;
     762        display: block;
     763        float: left;
     764        text-indent: 0;
    302765        overflow: hidden;
     766        position: absolute;
     767        outline: 0;
    303768}
    304769
    305 #img_container img,
    306 #img_container a {
    307         width: 68px;
    308         height: 68px;
     770.tagchecklist .ntdelbutton:before {
     771        content: '\f153';
     772        display: block;
     773        margin: 2px 0;
     774        height: 20px;
     775        width: 20px;
     776        background: 0 0;
     777        color: #9ea7af;
     778        font: 400 16px/1 dashicons;
     779        text-align: center;
     780        speak: none;
     781        -webkit-font-smoothing: antialiased;
    309782}
    310783
    311 #img_container img {
     784.tagchecklist .ntdelbutton:focus:before {
     785        color: #2ea2cc;
     786}
     787
     788
     789/* THE TAG CLOUD. */
     790.tagsdiv + p {
     791        margin: 0;
     792}
     793
     794.tagcloud-link {
     795        display: block;
     796        padding: 0 16px;
     797        text-decoration: none;
     798        outline: 0;
     799}
     800
     801.tagcloud-link:focus {
     802        text-decoration: underline;
     803}
     804
     805.popular-tags {
    312806        border: none;
    313         background-color: #f4f4f4;
     807        line-height: 2em;
     808        padding: 8px 12px 12px;
     809        text-align: justify;
     810}
     811
     812.popular-tags a {
     813        padding: 0 3px;
     814}
     815
     816.the-tagcloud {
     817        margin: 0;
     818        padding: 16px;
     819}
     820
     821.the-tagcloud a {
     822        text-decoration: none;
     823        outline: 0;
     824}
     825
     826.the-tagcloud a:focus {
     827        text-decoration: underline;
     828}
     829
     830.tagcloud h3 {
     831        margin: 2px 0 12px;
     832}
     833
     834
     835/**
     836* Categories
     837*
     838* Complete styles for post categories UI
     839*/
     840input[type="search"].categories-search,
     841.add-category-name {
     842        display: block;
     843        width: 100%;
     844        padding: 0.85714em 1.07143em;
     845        border: 0;
     846        -webkit-border-radius: 0;
     847        border-radius: 0;
     848        border-bottom: 1px solid #e5e5e5;
     849        font-size: 14px;
     850        -webkit-appearance: none;
     851        appearance: none;
     852}
     853
     854@media (max-width: 600px) {
     855        input[type="search"].categories-search,
     856        .add-category-name {
     857                /* Needs to be 16px to prevent zooming on iOS. Guh. */
     858                font-size: 16px;
     859        }
     860}
     861
     862.add-cat-toggle {
     863        float: right;
     864        margin-top: -33px;
     865}
     866
     867.add-cat-toggle:focus {
     868        text-decoration: none;
     869        color: #2ea2cc;
     870}
     871
     872.add-cat-toggle.is-toggled {
     873        margin-top: -36px;
     874}
     875
     876.add-cat-toggle.is-toggled .dashicons:before {
     877        content: "\f179";
     878}
     879
     880.add-category {
     881        position: relative;
     882        border-bottom: 1px solid #e5e5e5;
     883}
     884
     885.add-category.is-hidden {
     886        display: none;
     887}
     888
     889.add-category .add-cat-submit {
     890        position: absolute;
     891        top: 0;
     892        right: 0;
     893        border: 0;
     894        -webkit-border-radius: 0;
     895        border-radius: 0;
     896        padding: 12px 16px;
     897        background: #f7f7f7;
     898        border-left: 1px solid #f1f1f1;
     899}
     900
     901.add-category .add-cat-submit:hover,
     902.add-category .add-cat-submit:active,
     903.add-category .add-cat-submit:focus {
     904        outline: 0;
     905        background: #2991b7;
     906        border-color: #20708e;
     907        color: #fff;
     908}
     909
     910/* Parent category select */
     911.postform-wrapper {
     912        padding: 12px;
     913}
     914
     915.postform {
     916        display: block;
     917        margin: 0;
     918        width: 100%;
     919        height: 34px;
     920        border: 0;
     921        -webkit-border-radius: 0;
     922        border-radius: 0;
     923        border: 1px solid #e5e5e5;
     924        background: #fff;
     925        -webkit-background-size: 20px 20px;
     926        background-size: 20px 20px;
     927        overflow: hidden;
     928        line-height: 21px;
     929        text-overflow: ellipsis;
     930        text-decoration: none;
     931        vertical-align: top;
     932        white-space: nowrap;
    314933        cursor: pointer;
     934        outline: 0;
    315935}
    316936
    317 #img_container a,
    318 #img_container a:link,
    319 #img_container a:visited {
    320         border: 1px solid #ccc;
     937.postform:focus {
     938        border-color: #0074a2;
     939        -webkit-box-shadow: 0 0 0 3px #2ea2cc;
     940        box-shadow: 0 0 0 3px #2ea2cc;
     941        outline: 0;
     942        -moz-outline: none;
     943        -moz-user-focus: ignore;
     944}
     945
     946.postform::-ms-expand {
     947        display: none;
     948}
     949
     950.postform::-ms-value {
     951        background: none;
     952        color: #727272;
     953}
     954
     955.postform:-moz-focusring {
     956        color: transparent;
     957        text-shadow: 0 0 0 #727272;
     958}
     959
     960/* Category list */
     961.categories-select {
     962        margin: 0;
     963        padding: 0;
     964        list-style: none;
     965}
     966
     967.categories-select ul {
     968        margin: 0;
     969        padding: 0;
     970        list-style: none;
     971}
     972
     973.categories-select input {
     974        clear: none;
     975        position: absolute;
     976        top: 0;
     977        left: 0;
    321978        display: block;
     979        line-height: 0;
     980        width: 100%;
     981        height: 100%;
     982        outline: 0;
     983        padding: 0;
     984        border: 0;
     985        -webkit-border-radius: 0;
     986        border-radius: 0;
     987        text-align: center;
     988        vertical-align: middle;
     989        -webkit-appearance: none;
     990        appearance: none;
     991        cursor: pointer;
     992}
     993
     994.categories-select input:checked {
     995        -webkit-box-shadow: inset 6px 0 0 #2ea2cc;
     996        box-shadow: inset 6px 0 0 #2ea2cc;
     997        background: rgba(46, 162, 204, 0.1);
     998}
     999
     1000.categories-select input:checked:after {
     1001        display: inline-block;
     1002        content: "\f147";
     1003        position: absolute;
     1004        top: 13px;
     1005        right: 0;
     1006        width: 20px;
     1007        height: 20px;
     1008        margin-right: 10px;
     1009        font-size: 20px;
     1010        line-height: 1;
     1011        font-family: dashicons;
     1012        text-decoration: inherit;
     1013        color: #222;
     1014        font-weight: 400;
     1015        font-style: normal;
     1016        vertical-align: top;
     1017        text-align: center;
     1018        -webkit-transition: color .1s ease-in 0;
     1019        transition: color .1s ease-in 0;
     1020        -webkit-font-smoothing: antialiased;
     1021        -moz-osx-font-smoothing: grayscale;
     1022}
     1023
     1024.categories-select input:focus {
     1025        -webkit-box-shadow: inset 6px 0 0 #2ea2cc;
     1026        box-shadow: inset 6px 0 0 #2ea2cc;
     1027        background: rgba(46, 162, 204, 0.05);
     1028}
     1029
     1030.categories-select label {
    3221031        position: relative;
     1032        display: block;
     1033        padding: 13px 16px 14px 16px;
     1034        cursor: pointer;
     1035        background: #fff;
    3231036}
    3241037
    325 #img_container a:hover,
    326 #img_container a:active {
    327         border-color: #000;
    328         z-index: 1000;
    329         border-width: 1px;
     1038.categories-select ul label {
     1039        padding-left: 24px;
    3301040}
    3311041
    332 /* Video */
    333 #embed-code {
     1042.categories-select ul ul label {
     1043        padding-left: 32px;
     1044}
     1045
     1046.categories-select ul ul ul label {
     1047        padding-left: 40px;
     1048}
     1049
     1050.categories-select ul ul ul ul label {
     1051        padding-left: 48px;
     1052}
     1053
     1054.categories-select ul ul ul ul ul label {
     1055        padding-left: 56px;
     1056}
     1057
     1058.categories-select ul ul ul ul ul ul label {
     1059        padding-left: 64px;
     1060}
     1061
     1062.categories-select .is-hidden {
     1063        display: none;
     1064}
     1065
     1066.categories-select .is-hidden.searched-parent {
     1067        display: block;
     1068}
     1069
     1070.lt-ie9 .categories-select input {
     1071        top: 50%;
     1072        right: 10px;
     1073        left: auto;
     1074        margin-top: -8px;
     1075        width: 16px;
     1076        height: 16px;
     1077}
     1078
     1079/* TODO Reformats checkbox on Firefox until we remove checkbox in merge */
     1080@-moz-document url-prefix() {
     1081        .categories-select input {
     1082                top: 50%;
     1083                right: 10px;
     1084                left: auto;
     1085                margin-top: -8px;
     1086                width: 16px;
     1087                height: 16px;
     1088        }
     1089}
     1090
     1091/* Category search */
     1092.categories-search-wrapper {
     1093        position: relative;
     1094}
     1095
     1096.categories-search-wrapper.is-hidden {
     1097        display: none;
     1098}
     1099
     1100.categories-search-wrapper label {
     1101        position: absolute;
     1102        top: 50%;
     1103        right: 10px;
     1104        margin-top: -10px;
     1105        color: #9ea7af;
     1106}
     1107
     1108
     1109/**
     1110* Main
     1111*/
     1112html,
     1113body {
     1114        overflow-x: hidden;
     1115}
     1116
     1117@media (min-width: 901px) {
     1118        html,
     1119        body {
     1120                height: 100%;
     1121        }
     1122}
     1123
     1124html {
     1125        background: #fff;
     1126        -webkit-box-shadow: -10px 0 0 rgba(0, 0, 0, 0.3);
     1127        box-shadow: -10px 0 0 rgba(0, 0, 0, 0.3);
     1128}
     1129
     1130@media (max-width: 900px) {
     1131        body {
     1132                font-size: 16px;
     1133        }
     1134}
     1135
     1136@media (max-width: 320px) {
     1137        body {
     1138                font-size: 14px;
     1139        }
     1140}
     1141
     1142.lt-ie9 {
     1143        overflow: visible;
     1144}
     1145
     1146.adminbar {
     1147        position: relative;
    3341148        width: 100%;
    335         height: 98px;
     1149        padding: 0 0.8em;
     1150        min-height: 3.2em;
     1151        background: #222;
     1152        color: #fff;
     1153        z-index: 9999;
    3361154}
    3371155
    338 /* Categories */
    339 .press-this .categorydiv div.tabs-panel {
    340         height: 100px;
     1156.adminbar:before,
     1157.adminbar:after {
     1158        content: "";
     1159        display: table;
    3411160}
    3421161
    343 /* Tags */
    344 .press-this .tagsdiv .newtag {
    345         width: 120px;
     1162.adminbar:after {
     1163        clear: both;
    3461164}
    3471165
    348 .press-this #content {
    349         margin: 5px 0;
    350         padding: 0 5px;
    351         border: 0 none;
    352         height: 340px;
    353         font-family: Consolas, Monaco, monospace;
    354         font-size: 13px;
    355         line-height: 19px;
    356         background: transparent;
     1166.adminbar .dashicons {
     1167        color: #999;
    3571168}
    3581169
    359 /* Submit */
    360 .press-this #publishing-actions .spinner {
    361         display: inline;
     1170.adminbar button {
     1171        position: absolute;
     1172        top: 50%;
     1173        right: 6px;
     1174        margin-top: -13px;
     1175}
     1176
     1177@media (max-width: 320px) {
     1178        .adminbar {
     1179                min-height: 45px;
     1180        }
     1181}
     1182
     1183.current-site {
     1184        margin-top: 0.5625em;
     1185        font-size: 16px;
     1186        line-height: 44px;
     1187        font-weight: 400;
     1188        overflow: hidden;
     1189        white-space: nowrap;
     1190        text-overflow: ellipsis;
     1191}
     1192
     1193@media (max-width: 600px) {
     1194        .current-site {
     1195                margin: 3px 0 0;
     1196        }
     1197}
     1198
     1199@media (max-width: 320px) {
     1200        .current-site {
     1201                margin: 0;
     1202                font-size: 14px;
     1203        }
     1204}
     1205
     1206.current-site span:nth-child(2) {
     1207        color: #ededed;
     1208}
     1209
     1210@media (max-width: 320px) {
     1211        .current-site span:nth-child(2) {
     1212                font-weight: 600;
     1213        }
     1214}
     1215
     1216.current-site .dashicons-wordpress {
     1217        position: relative;
     1218        top: -1px;
     1219        margin-right: 10px;
    3621220        vertical-align: middle;
    3631221}
    3641222
    365 /* =Media Queries
    366 -------------------------------------------------------------- */
     1223.options-open,
     1224.options-close {
     1225        display: none;
     1226}
    3671227
    368 /* Reset responsive styles in Press This */
    369 @media screen and ( max-width: 782px ) {
    370         .press-this ul.category-tabs li.tabs {
    371                 padding: 3px 5px 5px; /* Reset tabs in Press This to standard size */
     1228@media (max-width: 900px) {
     1229        .options-open,
     1230        .options-close {
     1231                display: block;
    3721232        }
     1233}
    3731234
    374         .press-this a.wp-switch-editor {
    375                 font: 13px/19px "Open Sans", sans-serif;
    376                 margin: 5px 0 0 5px;
    377                 padding: 3px 8px 4px;
     1235.options-open.is-hidden,
     1236.options-close.is-hidden {
     1237        display: none;
     1238}
     1239
     1240.options-open:focus .dashicons {
     1241        color: #fff;
     1242        text-decoration: none;
     1243}
     1244
     1245.options-open .dashicons {
     1246        margin-top: 3px;
     1247}
     1248
     1249.options-close {
     1250        color: #2ea2cc;
     1251}
     1252
     1253.alert {
     1254        position: relative;
     1255        margin: 0;
     1256        padding: 16px 50px;
     1257        border-bottom: 1px solid #e5e5e5;
     1258        font-size: 14px;
     1259}
     1260
     1261.alert:before {
     1262        content: '';
     1263        position: absolute;
     1264        top: 50%;
     1265        left: 30px;
     1266        width: 8px;
     1267        height: 8px;
     1268        margin-top: -4px;
     1269        -webkit-border-radius: 50%;
     1270        border-radius: 50%;
     1271        background: #2ea2cc;
     1272}
     1273
     1274@media (max-width: 600px) {
     1275        .alert {
     1276                padding: 16px 35px;
    3781277        }
     1278        .alert:before {
     1279                left: 15px;
     1280        }
     1281}
    3791282
    380         .press-this #wp-content-media-buttons a {
    381                 padding: 0;
    382                 line-height: normal;
    383                 height: auto;
     1283.alert.is-hidden {
     1284        display: none;
     1285}
     1286.alert.is-error:before {
     1287        background: red;
     1288}
     1289
     1290.scan {
     1291        position: relative;
     1292        border-bottom: 1px solid #e5e5e5;
     1293}
     1294
     1295@media (max-width: 900px) {
     1296        .scan form {
     1297                -webkit-transition: opacity .3s ease-in-out;
     1298                transition: opacity .3s ease-in-out;
    3841299        }
     1300        .scan.is-hidden form {
     1301                opacity: .2;
     1302                pointer-events: none;
     1303        }
     1304}
    3851305
    386         .press-this #wp-content-editor-tools {
    387                 padding: 0;
    388                 top: 3px;
     1306.scan-url {
     1307        display: block;
     1308        border: 0;
     1309        padding: 0.85714em 1.07143em;
     1310        font-size: 14px;
     1311        width: 100%;
     1312}
     1313
     1314@media (max-width: 600px) {
     1315        .scan-url {
     1316                font-size: 16px;
    3891317        }
     1318}
    3901319
    391         .press-this .category-tabs {
    392                 margin-top: 0;
     1320.scan-submit {
     1321        position: absolute;
     1322        top: 0;
     1323        right: 0;
     1324        bottom: 0;
     1325        padding: 0.85714em 1.07143em;
     1326        background: #f7f7f7;
     1327        border-color: #dedede;
     1328        border-bottom: 0;
     1329        border-left: 1px solid #f1f1f1;
     1330        -webkit-border-radius: 0;
     1331        border-radius: 0;
     1332        color: #555;
     1333        font-size: 14px;
     1334        line-height: 1.6;
     1335}
     1336
     1337.scan-submit:hover,
     1338.scan-submit:focus {
     1339        background: #2991b7;
     1340        border-color: #20708e;
     1341        color: #fff;
     1342        outline: 0;
     1343}
     1344
     1345.scan-submit:active {
     1346        background: #2581a2;
     1347        border-color: #20708e;
     1348        color: #fff;
     1349}
     1350
     1351.scan-submit:visited {
     1352        color: #555;
     1353}
     1354
     1355.wrapper {
     1356        position: relative;
     1357        margin-bottom: 60px;
     1358        margin-right: 320px;
     1359}
     1360
     1361.wrapper:before,
     1362.wrapper:after {
     1363        content: "";
     1364        display: table;
     1365}
     1366
     1367.wrapper:after {
     1368        clear: both;
     1369}
     1370
     1371@media (max-width: 900px) {
     1372        .wrapper {
     1373                margin: 0;
     1374                width: 100%;
    3931375        }
     1376}
    3941377
    395         .press-this .tagsdiv .newtag {
    396                 width: 120px;
    397                 padding: 3px 5px;
    398                 margin-bottom: 0;
     1378.editor-wrapper {
     1379        overflow: auto;
     1380        float: left;
     1381        width: 100%;
     1382}
     1383
     1384.editor-wrapper:before,
     1385.editor-wrapper:after {
     1386        content: "";
     1387        display: table;
     1388}
     1389
     1390.editor-wrapper:after {
     1391        clear: both;
     1392}
     1393
     1394.editor {
     1395        padding: 0 1.5em 4.75em;
     1396        max-width: 700px;
     1397        margin: 0 auto;
     1398}
     1399
     1400@media (min-width: 901px) {
     1401        .editor {
     1402                max-width: 760px;
    3991403        }
     1404}
    4001405
    401         .press-this .tagchecklist {
     1406@media (max-width: 320px) {
     1407        .editor {
    4021408                padding: 0;
    403                 margin-bottom: 0;
    4041409        }
     1410}
    4051411
    406         .press-this .wp_themeSkin a.mceButton {
    407                 width: 20px;
    408                 height: 20px;
     1412.post-title,
     1413.post-title-placeholder {
     1414        margin: 0;
     1415        padding: .83em 0;
     1416        width: 100%;
     1417        border-bottom: 1px solid #e5e5e5;
     1418        font-size: 32px;
     1419        line-height: 1.4;
     1420        font-weight: 700;
     1421}
     1422
     1423.post-title:active,
     1424.post-title:focus,
     1425.post-title-placeholder:active,
     1426.post-title-placeholder:focus {
     1427        outline: 0;
     1428        -webkit-box-shadow: inset 0px -3px 0 #2ea2cc;
     1429        box-shadow: inset 0px -3px 0 #2ea2cc;
     1430        border-color: #2ea2cc;
     1431}
     1432
     1433@media (max-width: 900px) {
     1434        .post-title,
     1435        .post-title-placeholder {
     1436                font-size: 24px;
    4091437        }
     1438}
    4101439
    411         .press-this .wp_themeSkin .mceButton .mceIcon {
    412                 margin: 0;
     1440@media (max-height: 400px) {
     1441        .post-title,
     1442        .post-title-placeholder {
     1443                padding: 15px 0;
     1444                font-size: 16px;
    4131445        }
     1446}
    4141447
    415         .press-this #poststuff h3,
    416         .press-this .metabox-holder h3 {
    417                 padding: 7px 12px;
     1448@media (max-width: 320px) {
     1449        .post-title,
     1450        .post-title-placeholder {
     1451                font-size: 16px;
     1452                font-weight: 600;
     1453                padding: 1.14286em 1.42857em;
    4181454        }
     1455}
    4191456
    420         .press-this input[type=checkbox],
    421         .press-this input[type=radio] {
    422                 height: 16px;
    423                 width: 16px;
     1457.post-title {
     1458        /* IE8 fallback */
     1459        background: url(data:image/gif;base64,R0lGODlhAQABAJEAAAAAAP///////wAAACH5BAEHAAIALAAAAAABAAEAAAICVAEAOw==);
     1460        background: none, none;
     1461}
     1462
     1463.post-title:before {
     1464        /* Keeps empty container from collapsing */
     1465        content: '\a0';
     1466        display: inline-block;
     1467        width: 0;
     1468        speak: none;
     1469}
     1470
     1471.post-title-placeholder {
     1472        position: absolute;
     1473        border: 0;
     1474        color: #9ea7af;
     1475        z-index: -1;
     1476}
     1477
     1478.post-title-placeholder.is-hidden {
     1479        display: none;
     1480}
     1481
     1482/* Suggested images */
     1483.featured-container {
     1484        position: relative;
     1485        padding: 2px 0;
     1486        border-bottom: 1px solid #e5e5e5;
     1487}
     1488
     1489.all-media {
     1490        display: none;
     1491        overflow: auto;
     1492        max-height: 150px;
     1493        max-height: 40vw;
     1494}
     1495
     1496.all-media:before, .all-media:after {
     1497        content: "";
     1498        display: table;
     1499}
     1500
     1501.all-media:after {
     1502        clear: both;
     1503}
     1504
     1505@media (min-width: 321px) {
     1506        .all-media {
     1507                max-height: 250px;
     1508                max-height: 40vw;
    4241509        }
     1510}
    4251511
    426         .press-this input[type=checkbox]:checked:before {
    427                 width: 16px;
    428                 font: normal 21px/1 'dashicons';
    429                 margin: -3px 0 0 -4px;
     1512@media (min-width: 601px) {
     1513        .all-media {
     1514                max-height: 200px;
     1515                max-height: 18.75vw;
    4301516        }
     1517}
    4311518
    432         .press-this input[type=radio]:checked:before {
    433                 font: normal 21px/1 'dashicons';
    434                 width: 6px;
    435                 height: 6px;
    436                 margin: 4px;
     1519.wppt-all-media-list {
     1520        list-style: none;
     1521        margin: 0;
     1522        padding: 0;
     1523}
     1524
     1525.suggested-media-thumbnail:focus,
     1526.suggested-media-embed:focus {
     1527        outline: 0;
     1528        -webkit-box-shadow: inset 0 0 0 3px #2ea2cc;
     1529        box-shadow: inset 0 0 0 3px #2ea2cc;
     1530}
     1531
     1532.suggested-media-thumbnail {
     1533        position: relative;
     1534        display: block;
     1535        float: left;
     1536        width: 16.66%;
     1537        padding: 16.66% 0 0 16.66%;
     1538        background-position: center;
     1539        background-repeat: no-repeat;
     1540        -webkit-background-size: cover;
     1541        background-size: cover;
     1542        background-color: #d8d8d8;
     1543        color: #fff;
     1544        color: rgba(255, 255, 255, 0.6);
     1545        cursor: pointer;
     1546}
     1547
     1548.suggested-media-thumbnail:hover,
     1549.suggested-media-thumbnail:active,
     1550.suggested-media-thumbnail:focus {
     1551        color: #fff;
     1552}
     1553
     1554.suggested-media-thumbnail:before,
     1555.suggested-media-thumbnail:after {
     1556        display: inline-block;
     1557        position: absolute;
     1558        font-size: 20px;
     1559        line-height: 1;
     1560        font-family: dashicons;
     1561        text-decoration: inherit;
     1562        font-weight: 400;
     1563        font-style: normal;
     1564        -webkit-transition: color .1s ease-in 0;
     1565        transition: color .1s ease-in 0;
     1566        -webkit-font-smoothing: antialiased;
     1567        -moz-osx-font-smoothing: grayscale;
     1568}
     1569
     1570.suggested-media-thumbnail:before {
     1571        left: 50%;
     1572        top: 50%;
     1573        margin: -20px 0 0 -20px;
     1574        font-size: 40px;
     1575}
     1576
     1577.suggested-media-thumbnail:after {
     1578        content: "\f132";
     1579        right: 3%;
     1580        bottom: 2%;
     1581}
     1582
     1583@media (min-width: 601px) {
     1584        .suggested-media-thumbnail {
     1585                width: 12.5%;
     1586                padding: 12.5% 0 0 12.5%;
    4371587        }
     1588}
    4381589
    439         .press-this ul.categorychecklist ul,
    440         .press-this ul.categorychecklist li {
    441                 margin-top: 0;
    442                 margin-bottom: 0;
     1590.suggested-media-embed:before {
     1591        content: "\f104";
     1592        color: #fff;
     1593        color: rgba(255, 255, 255, 0.9);
     1594}
     1595
     1596.suggested-media-embed.is-audio:hover:before,
     1597.suggested-media-embed.is-audio:active:before,
     1598.suggested-media-embed.is-audio:focus:before,
     1599.suggested-media-embed.is-tweet:hover:before,
     1600.suggested-media-embed.is-tweet:active:before,
     1601.suggested-media-embed.is-tweet:focus:before {
     1602        color: #fff;
     1603}
     1604
     1605.suggested-media-embed.is-video {
     1606        background-color: #222;
     1607}
     1608
     1609.suggested-media-embed.is-video:hover:before,
     1610.suggested-media-embed.is-video:active:before,
     1611.suggested-media-embed.is-video:focus:before {
     1612        color: rgba(255, 255, 255, 0.2);
     1613}
     1614
     1615.suggested-media-embed.is-video:before {
     1616        content: "\f236";
     1617}
     1618
     1619.suggested-media-embed.is-audio {
     1620        background-color: #ff7d44;
     1621}
     1622
     1623.suggested-media-embed.is-audio:before {
     1624        content: "\f127";
     1625}
     1626
     1627.suggested-media-embed.is-tweet {
     1628        background-color: #55acee;
     1629}
     1630
     1631.suggested-media-embed.is-tweet:before {
     1632        content: "\f301";
     1633}
     1634
     1635.all-media-visible .all-media {
     1636        display: block;
     1637}
     1638
     1639.no-media {
     1640        margin: 0;
     1641        padding: 0;
     1642        border: 0;
     1643}
     1644
     1645/* Actions bar */
     1646.press-this-actions {
     1647        position: fixed;
     1648        bottom: 0;
     1649        left: 0;
     1650        width: 100%;
     1651        background: #f1f1f1;
     1652        background: rgba(241, 241, 241, 0.9);
     1653        border-top: 1px solid #e5e5e5;
     1654}
     1655
     1656@media (max-width: 900px) {
     1657        .press-this-actions {
     1658                -webkit-transform: translateY(0);
     1659                -ms-transform: translateY(0);
     1660                transform: translateY(0);
     1661                -webkit-transition: -webkit-transform .3s ease-in-out;
     1662                transition: transform .3s ease-in-out;
    4431663        }
     1664        .press-this-actions.is-hidden {
     1665                -webkit-transform: translateY(100%);
     1666                -ms-transform: translateY(100%);
     1667                transform: translateY(100%);
     1668        }
     1669}
    4441670
    445         .press-this div.quicktags-toolbar input {
    446                 padding: 2px 4px;
     1671.add-media {
     1672        float: left;
     1673        margin: 14px 0 14px 30px;
     1674        font-size: 0;
     1675}
     1676
     1677@media (max-width: 320px) {
     1678        .add-media {
     1679                margin: 10px 0 10px 10px;
    4471680        }
     1681}
    4481682
    449         .press-this textarea,
    450         .press-this input {
    451                 font-size: 14px;
     1683.insert-media {
     1684        color: #9ea7af;
     1685        float: left;
     1686        margin: 0;
     1687        padding: 0;
     1688        border: 0;
     1689        border-right: 1px solid #e5e5e5;
     1690        -webkit-border-radius: 0;
     1691        border-radius: 0;
     1692        background: none;
     1693        -webkit-box-shadow: none;
     1694        box-shadow: none;
     1695        overflow: hidden;
     1696}
     1697
     1698.insert-media:hover,
     1699.insert-media:focus,
     1700.insert-media:active {
     1701        margin: 0;
     1702        background: none;
     1703        border-color: #e5e5e5;
     1704        color: #222;
     1705}
     1706
     1707.insert-media:focus,
     1708.insert-media:active {
     1709        outline: 0;
     1710        color: #2ea2cc;
     1711        text-decoration: none;
     1712}
     1713
     1714.insert-media .dashicons {
     1715        padding: 11px;
     1716        width: 63px;
     1717        height: 58px;
     1718        font-size: 40px;
     1719}
     1720
     1721@media (max-width: 320px) {
     1722        .insert-media .dashicons {
     1723                width: 55px;
     1724                height: 49px;
     1725                padding: 14px;
     1726                font-size: 20px;
    4521727        }
     1728}
    4531729
    454         .press-this .tagchecklist span {
    455                 font-size: 13px;
    456                 line-height: 1.8em;
     1730.post-actions {
     1731        float: right;
     1732        margin: 14px 30px 14px 0;
     1733        font-size: 0;
     1734}
     1735
     1736@media (max-width: 320px) {
     1737        .post-actions {
     1738                margin: 10px 10px 10px 0;
    4571739        }
    4581740}
     1741
     1742/* TinyMCE styles */
     1743.editor .wp-media-buttons {
     1744        float: none;
     1745}
     1746
     1747.editor div.mce-toolbar-grp {
     1748        padding: 0.71429em 0;
     1749        background: none;
     1750        border: 0;
     1751}
     1752
     1753@media (max-height: 400px), (max-width: 320px) {
     1754        .editor div.mce-toolbar-grp {
     1755                padding: 0;
     1756        }
     1757}
     1758
     1759.mce-stack-layout:before,
     1760.mce-stack-layout:after {
     1761        content: "";
     1762        display: table;
     1763}
     1764
     1765.mce-stack-layout:after {
     1766        clear: both;
     1767}
     1768
     1769.mce-container.mce-toolbar {
     1770        float: left;
     1771}
     1772
     1773.mce-container.mce-toolbar:nth-child(2) {
     1774        float: right;
     1775}
     1776
     1777@media (max-width: 600px) {
     1778        #mceu_11,
     1779        #mceu_12 {
     1780                position: absolute;
     1781                margin: -1px;
     1782                padding: 0;
     1783                height: 1px;
     1784                width: 1px;
     1785                overflow: hidden;
     1786                clip: rect(0 0 0 0);
     1787                border: 0;
     1788        }
     1789
     1790        #mceu_11:focus,
     1791        #mceu_12:focus {
     1792                position: static;
     1793                margin: 1px;
     1794                padding: inherit;
     1795                height: auto;
     1796                width: auto;
     1797                overflow: visible;
     1798                clip: auto;
     1799                border: 1px solid #999;
     1800        }
     1801}
     1802
     1803#wp-link-wrap.search-panel-visible {
     1804        font-size: 13px;
     1805}
     1806
     1807/* Options panel (sidebar) */
     1808.options-panel {
     1809        position: relative;
     1810        float: right;
     1811        margin-right: -320px;
     1812        width: 320px;
     1813        border-left: 1px solid #e5e5e5;
     1814        font-size: 14px;
     1815        /* Keeps background the full height of the screen */
     1816        -webkit-box-shadow: 5001px 5000px 0 5000px #fff, 5000px 5000px 0 5000px #e5e5e5;
     1817        box-shadow: 5001px 5000px 0 5000px #fff, 5000px 5000px 0 5000px #e5e5e5;
     1818}
     1819
     1820@media (max-width: 900px) {
     1821        .options-panel {
     1822                background: #fff;
     1823                -webkit-transform: translateX(-100%);
     1824                -ms-transform: translateX(-100%);
     1825                transform: translateX(-100%);
     1826                -webkit-transition: -webkit-transform .3s ease-in-out;
     1827                transition: transform .3s ease-in-out;
     1828        }
     1829
     1830        .options-panel.is-hidden {
     1831                visibility: hidden;
     1832        }
     1833
     1834        .options-panel.is-off-screen {
     1835                -webkit-transform: translateX(0);
     1836                -ms-transform: translateX(0);
     1837                transform: translateX(0);
     1838        }
     1839}
     1840
     1841@media (max-width: 320px) {
     1842        .options-panel {
     1843                margin-right: -100%;
     1844                width: 100%;
     1845                border: 0;
     1846                -webkit-box-shadow: 5001px 5000px 0 5000px #fff;
     1847                box-shadow: 5001px 5000px 0 5000px #fff;
     1848        }
     1849}
     1850
     1851.post-options {
     1852        background: #fff;
     1853        position: absolute;
     1854        right: 0;
     1855        width: 100%;
     1856        overflow-x: hidden;
     1857}
     1858
     1859.post-options .post-option-contents {
     1860        margin-left: 3px;
     1861        color: #333;
     1862}
     1863
     1864.post-options .dashicons-arrow-right-alt2 {
     1865        position: absolute;
     1866        top: 50%;
     1867        right: 8px;
     1868        margin-top: -10px;
     1869}
     1870
     1871.lt-ie9 .options-panel,
     1872.lt-ie9 .post-options {
     1873        border-left: 1px solid #e5e5e5;
     1874}
     1875
     1876.lt-ie9 .post-options.is-off-screen {
     1877        border: 0;
     1878}
     1879
     1880.post-option {
     1881        position: relative;
     1882}
     1883
     1884.post-options .post-option {
     1885        display: block;
     1886        width: 100%;
     1887        padding: 13px 37px 13px 14px;
     1888        border-bottom: 1px solid #e5e5e5;
     1889        text-decoration: none;
     1890        text-align: left;
     1891        color: #9ea7af;
     1892        text-overflow: ellipsis;
     1893        white-space: nowrap;
     1894        overflow: hidden;
     1895        -webkit-transition: -webkit-transform .3s ease-in-out;
     1896        transition: transform .3s ease-in-out;
     1897}
     1898
     1899.post-options .post-option:focus {
     1900        outline: 0;
     1901        -webkit-box-shadow: inset 5px 0 0 #2ea2cc;
     1902        box-shadow: inset 5px 0 0 #2ea2cc;
     1903}
     1904
     1905.is-off-screen > .post-option {
     1906        right: 100%;
     1907}
     1908
     1909.is-hidden > .post-option {
     1910        visibility: hidden;
     1911}
     1912
     1913@media (min-width: 1px) {
     1914        .is-off-screen > .post-option {
     1915                right: auto;
     1916                -webkit-transform: translateX(-100%);
     1917                -ms-transform: translateX(-100%);
     1918                transform: translateX(-100%);
     1919        }
     1920}
     1921
     1922.post-option-title {
     1923        display: inline-block;
     1924        margin: 0 0 0 8px;
     1925        font-size: 14px;
     1926        font-weight: normal;
     1927}
     1928
     1929.setting-modal {
     1930        position: relative;
     1931        top: 0;
     1932        left: 0;
     1933        width: 100%;
     1934        overflow: hidden;
     1935        -webkit-transition: -webkit-transform .3s ease-in-out;
     1936        transition: transform .3s ease-in-out;
     1937}
     1938
     1939.setting-modal.is-hidden {
     1940        visibility: hidden;
     1941        height: 0;
     1942}
     1943
     1944.setting-modal.is-off-screen {
     1945        left: 100%;
     1946}
     1947
     1948@media (min-width: 1px) {
     1949        .setting-modal.is-off-screen {
     1950                left: 0;
     1951                -webkit-transform: translateX(100%);
     1952                -ms-transform: translateX(100%);
     1953                transform: translateX(100%);
     1954        }
     1955}
     1956
     1957.modal-close {
     1958        display: block;
     1959        width: 100%;
     1960        padding: 13px 14px;
     1961        border-bottom: 1px solid #e5e5e5;
     1962        color: #2ea2cc;
     1963        text-decoration: none;
     1964        text-align: left;
     1965}
     1966
     1967.modal-close:focus {
     1968        outline: 0;
     1969        -webkit-box-shadow: inset 5px 0 0 #2ea2cc;
     1970        box-shadow: inset 5px 0 0 #2ea2cc;
     1971}
     1972
     1973.setting-title {
     1974        position: relative;
     1975        top: -1px;
     1976        margin-left: 11px;
     1977}
  • src/wp-admin/css/wp-admin.css

     
    77@import url(revisions.css);
    88@import url(media.css);
    99@import url(themes.css);
    10 @import url(press-this.css);
    1110@import url(about.css);
    1211@import url(nav-menus.css);
    1312@import url(widgets.css);
  • src/wp-admin/includes/ajax-actions.php

     
    29542954
    29552955        wp_send_json_success( $status );
    29562956}
     2957
     2958/**
     2959 * AJAX handler for saving a post from Ptrss This.
     2960 *
     2961 * @since 4.2.0
     2962 */
     2963function wp_ajax_press_this_save_post() {
     2964        if ( empty( $GLOBALS['wp_press_this'] ) ) {
     2965                include( ABSPATH . 'wp-admin/includes/class-wp-press-this.php' );
     2966        }
     2967
     2968        $GLOBALS['wp_press_this']->save_post();
     2969}
     2970
     2971/**
     2972 * AJAX handler for creating new category from Ptrss This.
     2973 *
     2974 * @since 4.2.0
     2975 */
     2976function wp_ajax_press_this_add_category() {
     2977        if ( empty( $GLOBALS['wp_press_this'] ) ) {
     2978                include( ABSPATH . 'wp-admin/includes/class-wp-press-this.php' );
     2979        }
     2980
     2981        $GLOBALS['wp_press_this']->add_category();
     2982}
  • src/wp-admin/includes/class-wp-press-this.php

     
     1<?php
     2/**
     3 * Press This class and display functionality
     4 *
     5 * @package WordPress
     6 * @subpackage Press_This
     7 * @since 4.2.0
     8 */
     9
     10/**
     11 * Press This class
     12 *
     13 * @since 4.2.0
     14 */
     15class WP_Press_This {
     16
     17        /**
     18         * Constructor.
     19         *
     20         * @since 4.2.0
     21         * @access public
     22         */
     23        public function __construct() {}
     24
     25        /**
     26         * App and site settings data, including i18n strings for the client-side.
     27         *
     28         * @since 4.2.0
     29         * @access public
     30         *
     31         * @return array Site settings.
     32         */
     33        public function site_settings() {
     34                $supported_formats = get_theme_support( 'post-formats' );
     35                $post_formats      = array();
     36
     37                if ( ! empty( $supported_formats[0] ) && is_array( $supported_formats[0] ) ) {
     38                        $post_formats[0] = __( 'Standard' );
     39                        foreach ( $supported_formats[0] as $post_format ) {
     40                                $post_formats[ $post_format ] = esc_html( get_post_format_string( $post_format ) );
     41                        }
     42                }
     43
     44                return array(
     45                        'version'         => 5,
     46                        'post_formats'    => $post_formats,
     47
     48                        /**
     49                         * Filter whether Press This should redirect the user in the parent window instead of the popup, upon save.
     50                         *
     51                         * @since 4.2.0
     52                         *
     53                         * @param bool $redir_in_parent Whether to redirect in parent window or not. Default false.
     54                         */
     55                        'redir_in_parent' => apply_filters( 'press_this_redirect_in_parent', __return_false() ),
     56                );
     57        }
     58
     59        /**
     60         * Get the sources images and save them locally, fr posterity, unless we can't.
     61         *
     62         * @since 4.2.0
     63         * @access public
     64         *
     65         * @param int    $post_id Post ID.
     66         * @param string $content Optional. Current expected markup for Press This. Default empty.
     67         * @return string New markup with old image URLs replaced with the local attachment ones if swapped.
     68         */
     69        public function side_load_images( $post_id, $content = '' ) {
     70                $new_content = $content;
     71
     72                preg_match_all( '/<img [^>]+>/', $content, $matches );
     73
     74                if ( ! empty( $matches ) && current_user_can( 'upload_files' ) ) {
     75                        foreach ( (array) $matches[0] as $key => $image ) {
     76                                preg_match( '/src=["\']{1}([^"\']+)["\']{1}/', stripslashes( $image ), $url_matches );
     77
     78                                if ( empty( $url_matches[1] ) ) {
     79                                        continue;
     80                                }
     81
     82                                $image_url = $url_matches[1];
     83
     84                                // Don't try to sideload a file without a file extension, leads to WP upload error.
     85                                if ( ! preg_match( '/[^\?]+\.(jpe?g|jpe|gif|png)\b/i', $image_url ) )
     86                                         continue;
     87
     88                                // See if files exist in content - we don't want to upload non-used selected files.
     89                                if ( false !== strpos( $new_content, htmlspecialchars( $image_url ) ) ) {
     90
     91                                        // Sideload image, which ives us a new image tag, strip the empty alt that comes with it.
     92                                        $upload = str_replace( ' alt=""', '', media_sideload_image( $image_url, $post_id ) );
     93
     94                                        // Preserve assigned class, id, width, height and alt attributes.
     95                                        if ( preg_match_all( '/(class|width|height|id|alt)=\\\?(\"|\')[^"\']+\\\?(\2)/', $image, $attr_matches )
     96                                             && is_array( $attr_matches[0] )
     97                                        ) {
     98                                                foreach ( $attr_matches[0] as $attr ) {
     99                                                        $upload = str_replace( '<img', '<img ' . $attr, $upload );
     100                                                }
     101                                        }
     102
     103                                        /*
     104                                         * Replace the POSTED content <img> with correct uploaded ones.
     105                                         * Regex contains fix for Magic Quotes.
     106                                         */
     107                                        if ( ! is_wp_error( $upload ) ) {
     108                                                $new_content = str_replace( $image, $upload, $new_content );
     109                                        }
     110                                }
     111                        }
     112                }
     113
     114                // Error handling for media_sideload, send original content back.
     115                if ( is_wp_error( $new_content ) ) {
     116                        return $content;
     117                }
     118
     119                return $new_content;
     120        }
     121
     122        /**
     123         * AJAX handler for saving the post as draft or published.
     124         *
     125         * @since 4.2.0
     126         * @access public
     127         */
     128        public function save_post() {
     129                if ( empty( $_POST['pressthis-nonce'] ) || ! wp_verify_nonce( $_POST['pressthis-nonce'], 'press-this' ) ) {
     130                        wp_send_json_error( array( 'errorMessage' => __( 'Cheatin&#8217; uh?' ) ) );
     131                }
     132
     133                if ( empty( $_POST['post_ID'] ) || ! $post_id = (int) $_POST['post_ID'] ) {
     134                        wp_send_json_error( array( 'errorMessage' => __( 'Missing post ID.' ) ) );
     135                }
     136       
     137                if ( ! current_user_can( 'edit_post', $post_id ) ) {
     138                        wp_send_json_error( array( 'errorMessage' => __( 'Cheatin&#8217; uh?' ) ) );
     139                }
     140       
     141                $post = array(
     142                        'ID'            => $post_id,
     143                        'post_title'    => ( ! empty( $_POST['title'] ) ) ? sanitize_text_field( trim( $_POST['title'] ) ) : '',
     144                        'post_content'  => ( ! empty( $_POST['pressthis'] ) ) ? trim( $_POST['pressthis'] ) : '',
     145                        'post_type'     => 'post',
     146                        'post_status'   => 'draft',
     147                        'post_format'   => ( ! empty( $_POST['post_format'] ) ) ? sanitize_text_field( $_POST['post_format'] ) : '',
     148                        'tax_input'     => ( ! empty( $_POST['tax_input'] ) ) ? $_POST['tax_input'] : array(),
     149                        'post_category' => ( ! empty( $_POST['post_category'] ) ) ? $_POST['post_category'] : array(),
     150                );
     151       
     152                if ( ! empty( $_POST['post_status'] ) && 'publish' === $_POST['post_status'] ) {
     153                        if ( current_user_can( 'publish_posts' ) ) {
     154                                $post['post_status'] = 'publish';
     155                        } else {
     156                                $post['post_status'] = 'pending';
     157                        }
     158                }
     159
     160                $new_content = $this->side_load_images( $post_id, $post['post_content'] );
     161
     162                if ( ! is_wp_error( $new_content ) ) {
     163                        $post['post_content'] = $new_content;
     164                }
     165
     166                $updated = wp_update_post( $post, true );
     167       
     168                if ( is_wp_error( $updated ) || intval( $updated ) < 1 ) {
     169                        wp_send_json_error( array( 'errorMessage' => __( 'Error while saving the post. Please try again later.' ) ) );
     170                } else {
     171                        if ( isset( $post['post_format'] ) ) {
     172                                if ( current_theme_supports( 'post-formats', $post['post_format'] ) ) {
     173                                        set_post_format( $post_id, $post['post_format'] );
     174                                } elseif ( $post['post_format'] ) {
     175                                        set_post_format( $post_id, false );
     176                                }
     177                        }
     178
     179                        if ( 'publish' === get_post_status( $post_id ) ) {
     180                                /**
     181                                 * Filter the URL to redirect to when Press This saves.
     182                                 *
     183                                 * @since 4.2.0
     184                                 *
     185                                 * @param string $url     Redirect URL. If `$status` is 'publish', this will be the post permalink.
     186                                 *                        Otherwise, the post edit URL will be used.
     187                                 * @param int    $post_id Post ID.
     188                                 * @param string $status  Post status.
     189                                 */
     190                                $redirect = apply_filters( 'press_this_save_redirect', get_post_permalink( $post_id ), $post_id, $post['post_status'] );
     191                        } else {
     192                                /** This filter is documented in wp-admin/includes/class-wp-press-this.php */
     193                                $redirect = apply_filters( 'press_this_save_redirect', get_edit_post_link( $post_id, 'raw' ), $post_id, $post['post_status'] );
     194                        }
     195       
     196                        wp_send_json_success( array( 'redirect' => $redirect ) );
     197                }
     198        }
     199
     200        /**
     201         * AJAX handler for adding a new category.
     202         *
     203         * @since 4.2.0
     204         * @access public
     205         */
     206        public function add_category() {
     207                if ( false === wp_verify_nonce( $_POST['new_cat_nonce'], 'add-category' ) ) {
     208                        wp_send_json_error();
     209                }
     210
     211                $taxonomy = get_taxonomy( 'category' );
     212
     213                if ( ! current_user_can( $taxonomy->cap->edit_terms ) || empty( $_POST['name'] ) ) {
     214                        wp_send_json_error();
     215                }
     216
     217                $parent = isset( $_POST['parent'] ) && (int) $_POST['parent'] > 0 ? (int) $_POST['parent'] : 0;
     218                $names = explode( ',', $_POST['name'] );
     219                $added = $data = array();
     220
     221                foreach ( $names as $cat_name ) {
     222                        $cat_name = trim( $cat_name );
     223                        $cat_nicename = sanitize_title( $cat_name );
     224
     225                        if ( empty( $cat_nicename ) ) {
     226                                continue;
     227                        }
     228
     229                        // @todo Find a more performant to check existence, maybe get_term() with a separate parent check.
     230                        if ( ! $cat_id = term_exists( $cat_name, $taxonomy->name, $parent ) ) {
     231                                $cat_id = wp_insert_term( $cat_name, $taxonomy->name, array( 'parent' => $parent ) );
     232                        }
     233
     234                        if ( is_wp_error( $cat_id ) ) {
     235                                continue;
     236                        } elseif ( is_array( $cat_id ) ) {
     237                                $cat_id = $cat_id['term_id'];
     238                        }
     239
     240                        $added[] = $cat_id;
     241                }
     242
     243                if ( empty( $added ) ) {
     244                        wp_send_json_error( array( 'errorMessage' => __( 'This category cannot be added. Please change the name and try again.' ) ) );
     245                }
     246
     247                foreach ( $added as $new_cat_id ) {
     248                        $new_cat = get_category( $new_cat_id );
     249
     250                        if ( is_wp_error( $new_cat ) ) {
     251                                wp_send_json_error( array( 'errorMessage' => __( 'Error while adding the category. Please try again later.' ) ) );
     252                        }
     253
     254                        $data[] = array(
     255                                'term_id' => $new_cat->term_id,
     256                                'name' => $new_cat->name,
     257                                'parent' => $new_cat->parent,
     258                        );
     259                }
     260                wp_send_json_success( $data );
     261        }
     262
     263        /**
     264         * Downloads the source's HTML via server-side call for the given URL.
     265         *
     266         * @since 4.2.0
     267         * @access public
     268         *
     269         * @param string $url URL to scan.
     270         * @return string Source's HTML sanitized markup
     271         */
     272        public function fetch_source_html( $url ) {
     273                // Download source page to tmp file.
     274                $source_tmp_file = ( ! empty( $url ) ) ? download_url( $url ) : '';
     275                $source_content  = '';
     276
     277                if ( ! is_wp_error( $source_tmp_file ) && file_exists( $source_tmp_file ) ) {
     278                        // Get the content of the source page from the tmp file..
     279
     280                        $source_content = wp_kses(
     281                                file_get_contents( $source_tmp_file ),
     282                                array(
     283                                        'img' => array(
     284                                                'src'      => array(),
     285                                        ),
     286                                        'iframe' => array(
     287                                                'src'      => array(),
     288                                        ),
     289                                        'link' => array(
     290                                                'rel'      => array(),
     291                                                'itemprop' => array(),
     292                                                'href'     => array(),
     293                                        ),
     294                                        'meta' => array(
     295                                                'property' => array(),
     296                                                'name'     => array(),
     297                                                'content'  => array(),
     298                                        )
     299                                )
     300                        );
     301
     302                        // All done with backward compatibility. Let's do some cleanup, for good measure :)
     303                        unlink( $source_tmp_file );
     304
     305                } else if ( is_wp_error( $source_tmp_file ) ) {
     306                        $source_content = new WP_Error( 'upload-error',  sprintf( __( 'Error: %s' ), sprintf( __( 'Could not download the source URL (native error: %s).' ), $source_tmp_file->get_error_message() ) ) );
     307                } else if ( ! file_exists( $source_tmp_file ) ) {
     308                        $source_content = new WP_Error( 'no-local-file',  sprintf( __( 'Error: %s' ), __( 'Could not save or locate the temporary download file for the source URL.' ) ) );
     309                }
     310
     311                return $source_content;
     312        }
     313
     314        /**
     315         * Fetches and parses _meta, _img, and _links data from the source.
     316         *
     317         * @since 4.2.0
     318         * @access public
     319         *
     320         * @param string $url  URL to scan.
     321         * @param array  $data Optional. Existing data array if you have one. Default empty array.
     322         * @return array New data array.
     323         */
     324        public function source_data_fetch_fallback( $url, $data = array() ) {
     325                if ( empty( $url ) ) {
     326                        return array();
     327                }
     328
     329                // Download source page to tmp file.
     330                $source_content = $this->fetch_source_html( $url );
     331                if ( is_wp_error( $source_content ) ) {
     332                        return array( 'errors' => $source_content->get_error_messages() );
     333                }
     334
     335                // Fetch and gather <img> data.
     336                if ( empty( $data['_img'] ) ) {
     337                        $data['_img'] = array();
     338                }
     339
     340                if ( preg_match_all( '/<img (.+)[\s]?\/>/', $source_content, $matches ) ) {
     341                        if ( ! empty( $matches[0] ) ) {
     342                                foreach ( $matches[0] as $value ) {
     343                                        if ( preg_match( '/<img[^>]+src="([^"]+)"[^>]+\/>/', $value, $new_matches ) ) {
     344                                                if ( ! in_array( $new_matches[1], $data['_img'] ) ) {
     345                                                        $data['_img'][] = $new_matches[1];
     346                                                }
     347                                        }
     348                                }
     349                        }
     350                }
     351
     352                // Fetch and gather <iframe> data.
     353                if ( empty( $data['_embed'] ) ) {
     354                        $data['_embed'] = array();
     355                }
     356
     357                if ( preg_match_all( '/<iframe (.+)[\s][^>]*>/', $source_content, $matches ) ) {
     358                        if ( ! empty( $matches[0] ) ) {
     359                                foreach ( $matches[0] as $value ) {
     360                                        if ( preg_match( '/<iframe[^>]+src=(\'|")([^"]+)(\'|")/', $value, $new_matches ) ) {
     361                                                if ( ! in_array( $new_matches[2], $data['_embed'] ) ) {
     362                                                        if ( preg_match( '/\/\/www\.youtube\.com\/embed\/([^\?]+)\?.+$/', $new_matches[2], $src_matches ) ) {
     363                                                                $data['_embed'][] = 'https://www.youtube.com/watch?v=' . $src_matches[1];
     364                                                        } else if ( preg_match( '/\/\/player\.vimeo\.com\/video\/([\d]+)([\?\/]{1}.*)?$/', $new_matches[2], $src_matches ) ) {
     365                                                                $data['_embed'][] = 'https://vimeo.com/' . (int) $src_matches[1];
     366                                                        } else if ( preg_match( '/\/\/vine\.co\/v\/([^\/]+)\/embed/', $new_matches[2], $src_matches ) ) {
     367                                                                $data['_embed'][] = 'https://vine.co/v/' . $src_matches[1];
     368                                                        }
     369                                                }
     370                                        }
     371                                }
     372                        }
     373                }
     374
     375                // Fetch and gather <meta> data.
     376                if ( empty( $data['_meta'] ) ) {
     377                        $data['_meta'] = array();
     378                }
     379
     380                if ( preg_match_all( '/<meta ([^>]+)[\s]?\/?>/  ', $source_content, $matches ) ) {
     381                        if ( ! empty( $matches[0] ) ) {
     382                                foreach ( $matches[0] as $key => $value ) {
     383                                        if ( preg_match( '/<meta[^>]+(property|name)="(.+)"[^>]+content="(.+)"/', $value, $new_matches ) ) {
     384                                                if ( empty( $data['_meta'][ $new_matches[2] ] ) ) {
     385                                                        if ( preg_match( '/:?(title|description|keywords)$/', $new_matches[2] ) ) {
     386                                                                $data['_meta'][ $new_matches[2] ] = str_replace( '&#039;', "'", str_replace( '&#034;', '', html_entity_decode( $new_matches[3] ) ) );
     387                                                        } else {
     388                                                                $data['_meta'][ $new_matches[2] ] = $new_matches[3];
     389                                                                if ( 'og:url' == $new_matches[2] ) {
     390                                                                        if ( false !== strpos( $new_matches[3], '//www.youtube.com/watch?' )
     391                                                                             || false !== strpos( $new_matches[3], '//www.dailymotion.com/video/' )
     392                                                                             || preg_match( '/\/\/vimeo\.com\/[\d]+$/', $new_matches[3] )
     393                                                                             || preg_match( '/\/\/soundcloud\.com\/.+$/', $new_matches[3] )
     394                                                                             || preg_match( '/\/\/twitter\.com\/[^\/]+\/status\/[\d]+$/', $new_matches[3] )
     395                                                                             || preg_match( '/\/\/vine\.co\/v\/[^\/]+/', $new_matches[3] ) ) {
     396                                                                                if ( ! in_array( $new_matches[3], $data['_embed'] ) ) {
     397                                                                                        $data['_embed'][] = $new_matches[3];
     398                                                                                }
     399                                                                        }
     400                                                                } else if ( 'og:video' == $new_matches[2] || 'og:video:secure_url' == $new_matches[2] ) {
     401                                                                        if ( preg_match( '/\/\/www\.youtube\.com\/v\/([^\?]+)/', $new_matches[3], $src_matches ) ) {
     402                                                                                if ( ! in_array( 'https://www.youtube.com/watch?v=' . $src_matches[1], $data['_embed'] ) ) {
     403                                                                                        $data['_embed'][] = 'https://www.youtube.com/watch?v=' . $src_matches[1];
     404                                                                                }
     405                                                                        } else if ( preg_match( '/\/\/vimeo.com\/moogaloop\.swf\?clip_id=([\d]+)$/', $new_matches[3], $src_matches ) ) {
     406                                                                                if ( ! in_array( 'https://vimeo.com/' . $src_matches[1], $data['_embed'] ) ) {
     407                                                                                        $data['_embed'][] = 'https://vimeo.com/' . $src_matches[1];
     408                                                                                }
     409                                                                        }
     410                                                                } else if ( 'og:image' == $new_matches[2] || 'og:image:secure_url' == $new_matches[2] ) {
     411                                                                        if ( ! in_array( $new_matches[3], $data['_img'] ) ) {
     412                                                                                $data['_img'][] = $new_matches[3];
     413                                                                        }
     414                                                                }
     415                                                        }
     416                                                }
     417                                        }
     418                                }
     419                        }
     420                }
     421
     422                // Fetch and gather <link> data
     423                if ( empty( $data['_links'] ) ) {
     424                        $data['_links'] = array();
     425                }
     426
     427                if ( preg_match_all( '/<link ([^>]+)[\s]?\/>/', $source_content, $matches ) ) {
     428                        if ( ! empty( $matches[0] ) ) {
     429                                foreach ( $matches[0] as $key => $value ) {
     430                                        if ( preg_match( '/<link[^>]+(rel|itemprop)="([^"]+)"[^>]+href="([^"]+)"[^>]+\/>/', $value, $new_matches ) ) {
     431                                                if ( 'alternate' == $new_matches[2] || 'thumbnailUrl' == $new_matches[2] || 'url' == $new_matches[2] ) {
     432                                                        if ( empty( $data['_links'][ $new_matches[2] ] ) ) {
     433                                                                $data['_links'][ $new_matches[2] ] = $new_matches[3];
     434                                                        }
     435                                                }
     436                                        }
     437                                }
     438                        }
     439                }
     440
     441                return $data;
     442        }
     443
     444        /**
     445         * Handles backward-compat with the legacy version of Press This by supporting its query string params.
     446         *
     447         * @since 4.2.0
     448         * @access public
     449         *
     450         * @return array
     451         */
     452        public function merge_or_fetch_data() {
     453                // Merge $_POST and $_GET, as appropriate ($_POST > $_GET), to remain backward compatible.
     454                $data = array_merge_recursive( $_POST, $_GET );
     455
     456                // Get the legacy QS params, or equiv POST data
     457                $data['u'] = ( ! empty( $data['u'] ) && preg_match( '/^https?:/', $data['u'] ) ) ? $data['u'] : '';
     458                $data['s'] = ( ! empty( $data['s'] ) ) ? $data['s'] : '';
     459                $data['t'] = ( ! empty( $data['t'] ) ) ? $data['t'] : '';
     460
     461                /**
     462                 * Filter whether to enable in-source media discovery in Press This.
     463                 *
     464                 * @since 4.2.0
     465                 *
     466                 * @param bool $enable Whether to enable media discovery.
     467                 */
     468                if ( apply_filters( 'enable_press_this_media_discovery', __return_true() ) ) {
     469                        /*
     470                         * If no _meta (a new thing) was passed via $_POST, fetch data from source as fallback,
     471                         * makes PT fully backward compatible
     472                         */
     473                        if ( empty( $data['_meta'] ) && ! empty( $data['u'] ) ) {
     474                                $data = $this->source_data_fetch_fallback( $data['u'], $data );
     475                        }
     476                } else {
     477                        if ( ! empty( $data['_img'] ) ) {
     478                                $data['_img'] = array();
     479                        }
     480                        if ( ! empty( $data['_embed'] ) ) {
     481                                $data['_embed'] = array();
     482                        }
     483                        if ( ! empty( $data['_meta'] ) ) {
     484                                $data['_meta'] = array();
     485                        }
     486                }
     487
     488                /**
     489                 * Filter the Press This data array.
     490                 *
     491                 * @since 4.2.0
     492                 *
     493                 * @param array $data Press This Data array.
     494                 */
     495                return apply_filters( 'press_this_data', $data );
     496        }
     497
     498        /**
     499         * Adds another stylesheet inside TinyMCE.
     500         *
     501         * @since 4.2.0
     502         * @access public
     503         *
     504         * @param string $styles URL to editor stylesheet.
     505         * @return string Possibly modified stylesheets list.
     506         */
     507        public function add_editor_style( $styles ) {
     508                if ( ! empty( $styles ) ) {
     509                        $styles .= ',';
     510                }
     511                return $styles . admin_url( 'css/press-this-editor.css' );
     512        }
     513
     514        /**
     515         * Outputs the post format selection HTML.
     516         *
     517         * @since 4.2.0
     518         * @access public
     519         *
     520         * @param WP_Post $post Post object.
     521         */
     522        function post_formats_html( $post ) {
     523                if ( current_theme_supports( 'post-formats' ) && post_type_supports( $post->post_type, 'post-formats' ) ) {
     524                        $post_formats = get_theme_support( 'post-formats' );
     525
     526                        if ( is_array( $post_formats[0] ) ) {
     527                                $post_format = get_post_format( $post->ID );
     528
     529                                if ( ! $post_format ) {
     530                                        $post_format = '0';
     531                                }
     532
     533                                // Add in the current one if it isn't there yet, in case the current theme doesn't support it.
     534                                if ( $post_format && ! in_array( $post_format, $post_formats[0] ) ) {
     535                                        $post_formats[0][] = $post_format;
     536                                }
     537
     538                                ?>
     539                                <div id="post-formats-select">
     540                                        <input type="radio" name="post_format" class="post-format" id="post-format-0" value="0" <?php checked( $post_format, '0' ); ?> />
     541                                        <label for="post-format-0" class="post-format-icon post-format-standard"><?php echo get_post_format_string( 'standard' ); ?></label>
     542                                        <?php
     543
     544                                        foreach ( $post_formats[0] as $format ) {
     545                                                $attr_format = esc_attr( $format );
     546                                                ?>
     547                                                <br />
     548                                                <input type="radio" name="post_format" class="post-format" id="post-format-<?php echo $attr_format; ?>" value="<?php echo $attr_format; ?>" <?php checked( $post_format, $format ); ?> />
     549                                                <label for="post-format-<?php echo $attr_format ?>" class="post-format-icon post-format-<?php echo $attr_format; ?>"><?php echo esc_html( get_post_format_string( $format ) ); ?></label>
     550                                                <?php
     551                                         }
     552                                         ?>
     553                                </div>
     554                                <?php
     555                        }
     556                }
     557        }
     558
     559        /**
     560         * Outputs the categories HTML.
     561         *
     562         * @since 4.2.0
     563         * @access public
     564         *
     565         * @param WP_Post $post Post object.
     566         */
     567        function categories_html( $post ) {
     568                $taxonomy = get_taxonomy( 'category' );
     569
     570                if ( current_user_can( $taxonomy->cap->edit_terms ) ) {
     571                        ?>
     572                        <button type="button" class="add-cat-toggle button-subtle">
     573                                <span class="dashicons dashicons-plus"></span>
     574                        </button>
     575                        <div class="add-category is-hidden">
     576                                <label class="screen-reader-text" for="new-category"><?php echo $taxonomy->labels->add_new_item; ?></label>
     577                                <input type="text" id="new-category" class="add-category-name" placeholder="<?php echo esc_attr( $taxonomy->labels->new_item_name ); ?>" value="" aria-required="true">
     578                                <label class="screen-reader-text" for="new-category-parent"><?php echo $taxonomy->labels->parent_item_colon; ?></label>
     579                                <div class="postform-wrapper">
     580                                        <?php
     581                                        wp_dropdown_categories( array(
     582                                                'taxonomy'         => 'category',
     583                                                'hide_empty'       => 0,
     584                                                'name'             => 'new-category-parent',
     585                                                'orderby'          => 'name',
     586                                                'hierarchical'     => 1,
     587                                                'show_option_none' => '&mdash; ' . $taxonomy->labels->parent_item . ' &mdash;'
     588                                        ) );
     589                                        ?>
     590                                </div>
     591                                <button type="button" class="button add-cat-submit"><?php _e( 'Add' ); ?></button>
     592                        </div>
     593                <?php } ?>
     594                <div class="categories-search-wrapper">
     595                        <input id="categories-search" type="search" class="categories-search" placeholder="<?php esc_attr_e( 'Search categories' ) ?>">
     596                        <label for="categories-search">
     597                                <span class="dashicons dashicons-search"></span>
     598                        </label>
     599                </div>
     600                <ul class="categories-select">
     601                        <?php wp_terms_checklist( $post->ID, array( 'taxonomy' => 'category' ) ); ?>
     602                </ul>
     603                <?php
     604        }
     605
     606        /**
     607         * Outputs the tags HTML.
     608         *
     609         * @since 4.2.0
     610         * @access public
     611         *
     612         * @param WP_Post $post Post object.
     613         */
     614        function tags_html( $post ) {
     615                $taxonomy              = get_taxonomy( 'post_tag' );
     616                $user_can_assign_terms = current_user_can( $taxonomy->cap->assign_terms );
     617                $esc_tags              = get_terms_to_edit( $post->ID, 'post_tag' );
     618
     619                if ( ! $esc_tags || is_wp_error( $esc_tags ) ) {
     620                        $esc_tags = '';
     621                }
     622                ?>
     623                <div class="tagsdiv" id="post_tag">
     624                        <div class="jaxtag">
     625                        <input type="hidden" name="tax_input[post_tag]" class="the-tags" value="<?php echo $esc_tags; // escaped in get_terms_to_edit() ?>">
     626
     627                        <?php
     628                        if ( $user_can_assign_terms ) {
     629                                ?>
     630                                <div class="ajaxtag hide-if-no-js">
     631                                        <label class="screen-reader-text" for="new-tag-post_tag"><?php _e( 'Tags' ); ?></label>
     632                                        <div class="taghint"><?php echo $taxonomy->labels->add_new_item; ?></div>
     633                                        <p>
     634                                                <input type="text" id="new-tag-post_tag" name="newtag[post_tag]" class="newtag form-input-tip" size="16" autocomplete="off" value="" />
     635                                                <button type="button" class="button tagadd"><?php _e( 'Add' ); ?></button>
     636                                        </p>
     637                                </div>
     638                                <p class="howto">
     639                                        <?php echo $taxonomy->labels->separate_items_with_commas; ?>
     640                                </p>
     641                        <?php } ?>
     642                        </div>
     643                        <div class="tagchecklist"></div>
     644                </div>
     645                <?php
     646                if ( $user_can_assign_terms ) {
     647                        ?>
     648                        <p>
     649                                <a href="#titlediv" class="tagcloud-link" id="link-post_tag"><?php echo $taxonomy->labels->choose_from_most_used; ?></a>
     650                        </p>
     651                        <?php
     652                }
     653        }
     654
     655        /**
     656         * Serves the app's base HTML, which in turns calls the load script.
     657         *
     658         * @since 4.2.0
     659         * @access public
     660         */
     661        public function html() {
     662                global $wp_locale, $hook_suffix;
     663
     664                // Get data, new (POST) and old (GET).
     665                $data = $this->merge_or_fetch_data();
     666
     667                // Get site settings array/data.
     668                $site_settings = $this->site_settings();
     669
     670                // Set the passed data.
     671                $data['_version'] = $site_settings['version'];
     672
     673                // Add press-this-editor.css and remove theme's editor-style.css, if any.
     674                remove_editor_styles();
     675
     676                add_filter( 'mce_css', array( $this, 'add_editor_style' ) );
     677
     678                if ( ! empty( $GLOBALS['is_IE'] ) ) {
     679                        @header( 'X-UA-Compatible: IE=edge' );
     680                }
     681
     682                @header( 'Content-Type: ' . get_option( 'html_type' ) . '; charset=' . get_option( 'blog_charset' ) );
     683
     684?>
     685<!DOCTYPE html>
     686<!--[if IE 7]>         <html class="lt-ie9 lt-ie8" <?php language_attributes(); ?>> <![endif]-->
     687<!--[if IE 8]>         <html class="lt-ie9" <?php language_attributes(); ?>> <![endif]-->
     688<!--[if gt IE 8]><!--> <html <?php language_attributes(); ?>> <!--<![endif]-->
     689<head>
     690        <meta http-equiv="Content-Type" content="<?php esc_attr( bloginfo( 'html_type' ) ); ?>; charset=<?php echo esc_attr( get_option( 'blog_charset' ) ); ?>" />
     691        <meta name="viewport" content="width=device-width">
     692        <title><?php esc_html_e( 'Press This!' ) ?></title>
     693
     694        <script>
     695                window.wpPressThisData   = <?php echo json_encode( $data ) ?>;
     696                window.wpPressThisConfig = <?php echo json_encode( $site_settings ) ?>;
     697        </script>
     698
     699        <script type="text/javascript">
     700                var ajaxurl = '<?php echo esc_js( admin_url( 'admin-ajax.php', 'relative' ) ); ?>',
     701                        pagenow = 'press-this',
     702                        typenow = 'post',
     703                        adminpage = 'press-this-php',
     704                        thousandsSeparator = '<?php echo addslashes( $wp_locale->number_format['thousands_sep'] ); ?>',
     705                        decimalPoint = '<?php echo addslashes( $wp_locale->number_format['decimal_point'] ); ?>',
     706                        isRtl = <?php echo esc_js( (int) is_rtl() ); ?>;
     707        </script>
     708
     709        <?php
     710                /*
     711                 * $post->ID is needed for the embed shortcode so we can show oEmbed previews in the editor.
     712                 * Maybe find a way without it.
     713                 */
     714                $post = get_default_post_to_edit( 'post', true );
     715                $post_ID = (int) $post->ID;
     716
     717                wp_enqueue_style( 'press-this' );
     718                wp_enqueue_script( 'press-this' );
     719                wp_enqueue_script( 'json2' );
     720                wp_enqueue_media( array( 'post' => $post->ID ) );
     721                wp_enqueue_script( 'editor' );
     722
     723                $supports_formats = false;
     724                $post_format      = 0;
     725
     726                if ( current_theme_supports( 'post-formats' ) && post_type_supports( $post->post_type, 'post-formats' ) ) {
     727                        $supports_formats = true;
     728
     729                        if ( ! ( $post_format = get_post_format( $post->ID ) ) ) {
     730                                $post_format = 0;
     731                        }
     732                }
     733
     734                /** This action is documented in wp-admin/admin-header.php */
     735                do_action( 'admin_enqueue_scripts', $hook_suffix );
     736
     737                /** This action is documented in wp-admin/admin-header.php */
     738                do_action( 'admin_print_styles' );
     739
     740                /** This action is documented in wp-admin/admin-header.php */
     741                do_action( 'admin_print_scripts' );
     742
     743        ?>
     744</head>
     745<body>
     746        <div id="adminbar" class="adminbar">
     747                <h1 id="current-site" class="current-site">
     748                        <span class="dashicons dashicons-wordpress"></span>
     749                        <span><?php bloginfo( 'name' ); ?></span>
     750                </h1>
     751                <button type="button" class="options-open button-subtle">
     752                        <span class="dashicons dashicons-tag"></span><span class="screen-reader-text"><?php _e( 'Show post options' ); ?></span>
     753                </button>
     754                <button type="button" class="options-close button-subtle is-hidden"><?php _e( 'Done' ); ?></button>
     755        </div>
     756
     757        <div id="scanbar" class="scan">
     758                <form method="GET">
     759                        <input type="url" name="u" id="url-scan" class="scan-url" value="" placeholder="<?php esc_attr_e( 'Enter a URL to scan' ) ?>" />
     760                        <input type="submit" name="url-scan-submit" id="url-scan-submit" class="scan-submit" value="<?php esc_attr_e( 'Scan' ) ?>" />
     761                </form>
     762        </div>
     763
     764        <form id="pressthis-form" name="pressthis-form" method="POST" autocomplete="off">
     765                <input type="hidden" name="post_ID" id="post_ID" value="<?php echo esc_attr( $post_ID ); ?>" />
     766                <input type="hidden" name="action" value="press-this-save-post" />
     767                <input type="hidden" name="post_status" id="post_status" value="draft" />
     768                <?php
     769                wp_nonce_field( 'press-this', 'pressthis-nonce', false );
     770                wp_nonce_field( 'add-category', '_ajax_nonce-add-category', false );
     771                ?>
     772                <input type="hidden" name="title" id="title-field" value="" />
     773
     774        <div class="wrapper">
     775                <div class="editor-wrapper">
     776                        <div class="alerts">
     777                                <p class="alert is-notice is-hidden should-upgrade-bookmarklet">
     778                                        <?php printf( __( 'You should upgrade <a href="%s" target="_blank">your bookmarklet</a> to the latest version!' ), admin_url( 'tools.php?page=press_this_options' ) ); ?>
     779                                </p>
     780                        </div>
     781
     782                        <div id='app-container' class="editor">
     783                                <span id="title-container-label" class="post-title-placeholder" aria-hidden="true"><?php _e( 'Post title' ); ?></span>
     784                                <h2 id="title-container" class="post-title" contenteditable="true" spellcheck="true" aria-label="<?php esc_attr_e( 'Post title' ); ?>" tabindex="0"></h2>
     785                                <div id='featured-media-container' class="featured-container no-media">
     786                                        <div id='all-media-widget' class="all-media">
     787                                                <div id='all-media-container'></div>
     788                                        </div>
     789                                </div>
     790
     791                                <?php
     792                                wp_editor( '', 'pressthis', array(
     793                                        'drag_drop_upload' => true,
     794                                        'editor_height'    => 600,
     795                                        'media_buttons'    => false,
     796                                        'teeny'            => true,
     797                                        'tinymce'          => array(
     798                                                'resize'                => false,
     799                                                'wordpress_adv_hidden'  => false,
     800                                                'add_unload_trigger'    => false,
     801                                                'statusbar'             => false,
     802                                                'autoresize_min_height' => 600,
     803                                                'wp_autoresize_on'      => true,
     804                                                'plugins'               => 'lists,media,paste,tabfocus,fullscreen,wordpress,wpautoresize,wpeditimage,wpgallery,wplink,wpview',
     805                                                'toolbar1'              => 'bold,italic,bullist,numlist,blockquote,link,unlink',
     806                                                'toolbar2'              => 'undo,redo',
     807                                        ),
     808                                        'quicktags'        => false,
     809                                ) );
     810
     811                                ?>
     812                        </div>
     813                </div>
     814
     815                <div class="options-panel is-off-screen is-hidden">
     816                        <div class="post-options">
     817
     818                                <?php if ( $supports_formats ) : ?>
     819                                        <button type="button" class="button-reset post-option">
     820                                                <span class="dashicons dashicons-admin-post"></span>
     821                                                <span class="post-option-title"><?php _e( 'Format' ); ?></span>
     822                                                <span class="post-option-contents" id="post-option-post-format"><?php echo esc_html( get_post_format_string( $post_format ) ); ?></span>
     823                                                <span class="dashicons dashicons-arrow-right-alt2"></span>
     824                                        </button>
     825                                <?php endif; ?>
     826
     827                                <button type="button" class="button-reset post-option">
     828                                        <span class="dashicons dashicons-category"></span>
     829                                        <span class="post-option-title"><?php _e( 'Categories' ); ?></span>
     830                                        <span class="post-option-contents" id="post-option-category"></span>
     831                                        <span class="dashicons dashicons-arrow-right-alt2"></span>
     832                                </button>
     833
     834                                <button type="button" class="button-reset post-option">
     835                                        <span class="dashicons dashicons-tag"></span>
     836                                        <span class="post-option-title"><?php _e( 'Tags' ); ?></span>
     837                                        <span class="post-option-contents" id="post-option-tags"></span>
     838                                        <span class="dashicons dashicons-arrow-right-alt2"></span>
     839                                </button>
     840                        </div>
     841
     842                        <?php if ( $supports_formats ) : ?>
     843                                <div class="setting-modal is-off-screen is-hidden">
     844                                        <button type="button" class="button-reset modal-close">
     845                                                <span class="dashicons dashicons-arrow-left-alt2"></span><span class="setting-title"><?php _e( 'Post format' ); ?></span>
     846                                        </button>
     847                                        <?php $this->post_formats_html( $post ); ?>
     848                                </div>
     849                        <?php endif; ?>
     850
     851                        <div class="setting-modal is-off-screen is-hidden">
     852                                <button type="button" class="button-reset modal-close"><span class="dashicons dashicons-arrow-left-alt2"></span><span class="setting-title"><?php _e( 'Categories' ); ?></span></button>
     853                                <?php $this->categories_html( $post ); ?>
     854                        </div>
     855
     856                        <div class="setting-modal tags is-off-screen is-hidden">
     857                                <button type="button" class="button-reset modal-close"><span class="dashicons dashicons-arrow-left-alt2"></span><span class="setting-title"><?php _e( 'Tags' ); ?></span></button>
     858                                <?php $this->tags_html( $post ); ?>
     859                        </div>
     860                </div><!-- .options-panel -->
     861        </div><!-- .wrapper -->
     862
     863        <div class="press-this-actions">
     864                <div class="pressthis-media-buttons">
     865                        <button type="button" class="insert-media button-subtle" data-editor="pressthis">
     866                                <span class="dashicons dashicons-admin-media"></span>
     867                                <span class="screen-reader-text"><?php _e( 'Add Media' ); ?></span>
     868                        </button>
     869                </div>
     870                <div class="post-actions">
     871                        <button type="button" class="button-subtle" id="draft-field"><?php _e( 'Save Draft' ); ?></button>
     872                        <button type="button" class="button-primary" id="publish-field"><?php _e( 'Publish' ); ?></button>
     873                </div>
     874        </div>
     875        </form>
     876
     877        <?php
     878
     879                // TODO: consider running "special" press-this hooks here?
     880                // Maybe better so we don't output stuff accidentaly added by plugins. Would probably prevent some errors.
     881                do_action( 'admin_footer', '' );
     882                do_action( 'admin_print_footer_scripts' );
     883
     884        ?>
     885</body>
     886</html>
     887<?php
     888                die();
     889        }
     890}
     891$GLOBALS['wp_press_this'] = new WP_Press_This;
  • src/wp-admin/js/bookmarklet.js

     
     1( function( window, document, href, pt_url ) {
     2        var encodeURI = window.encodeURIComponent,
     3                form = document.createElement( 'form' ),
     4                head = document.getElementsByTagName( 'head' )[0],
     5                img = new Image(),
     6                target = '_press_this_app',
     7                windowWidth, windowHeight,
     8                metas, links, content, imgs, ifrs,
     9                vid, selection;
     10
     11        if ( ! pt_url ) {
     12                return;
     13        }
     14
     15        if ( window.getSelection ) {
     16                selection = window.getSelection() + '';
     17        } else if ( document.getSelection ) {
     18                selection = document.getSelection() + '';
     19        } else if ( document.selection ) {
     20                selection = document.selection.createRange().text;
     21        }
     22
     23        pt_url += ( pt_url.indexOf( '?' ) > -1 ? '&' : '?' ) + 'buster=' + ( new Date().getTime() );
     24
     25        if ( document.title.length && document.title.length <= 512 ) {
     26                pt_url += '&t=' + encodeURI( document.title );
     27        }
     28
     29        if ( selection && selection.length <= 512 ) {
     30                pt_url += '&s=' + encodeURI( selection );
     31        }
     32
     33        if ( href.match( /^https?:/ ) ) {
     34                pt_url += '&u=' + encodeURI( href );
     35        } else {
     36                top.location.href = pt_url;
     37                return;
     38        }
     39
     40        function add( name, value ) {
     41                if ( typeof value === 'undefined' ) {
     42                        return;
     43                }
     44
     45                var input = document.createElement( 'input' );
     46
     47                input.name = name;
     48                input.value = value;
     49                input.type = 'hidden';
     50
     51                form.appendChild( input );
     52        }
     53
     54        if ( href.match( /\/\/www\.youtube\.com\/watch/ ) ) {
     55                add( '_embed[]', href );
     56        } else if ( href.match( /\/\/vimeo\.com\/(.+\/)?([\d]+)$/ ) ) {
     57                add( '_embed[]', href );
     58        } else if ( href.match( /\/\/(www\.)?dailymotion\.com\/video\/.+$/ ) ) {
     59                add( '_embed[]', href );
     60        } else if ( href.match( /\/\/soundcloud\.com\/.+$/ ) ) {
     61                add( '_embed[]', href );
     62        } else if ( href.match( /\/\/twitter\.com\/[^\/]+\/status\/[\d]+$/ ) ) {
     63                add( '_embed[]', href );
     64        } else if ( href.match( /\/\/vine\.co\/v\/[^\/]+/ ) ) {
     65                add( '_embed[]', href );
     66        }
     67
     68        metas = head.getElementsByTagName( 'meta' ) || [];
     69
     70        for ( var m = 0; m < metas.length; m++ ) {
     71                if ( m >= 50 ) {
     72                        break;
     73                }
     74
     75                var q = metas[ m ],
     76                        q_name = q.getAttribute( 'name' ),
     77                        q_prop = q.getAttribute( 'property' ),
     78                        q_cont = q.getAttribute( 'content' );
     79
     80                if ( q_name ) {
     81                        add( '_meta[' + q_name + ']', q_cont );
     82                } else if ( q_prop ) {
     83                        add( '_meta[' + q_prop + ']', q_cont );
     84                }
     85        }
     86
     87        links = head.getElementsByTagName( 'link' ) || [];
     88
     89        for ( var y = 0; y < links.length; y++ ) {
     90                if ( y >= 50 ) {
     91                        break;
     92                }
     93
     94                var g = links[ y ],
     95                        g_rel = g.getAttribute( 'rel' );
     96
     97                if ( g_rel ) {
     98                        switch ( g_rel ) {
     99                                case 'canonical':
     100                                case 'icon':
     101                                case 'shortlink':
     102                                        add( '_links[' + g_rel + ']', g.getAttribute( 'href' ) );
     103                                        break;
     104                                case 'alternate':
     105                                        if ( 'application/json+oembed' === g.getAttribute( 'type' ) ) {
     106                                                add( '_links[' + g_rel + ']', g.getAttribute( 'href' ) );
     107                                        } else if ( 'handheld' === g.getAttribute( 'media' ) ) {
     108                                                add( '_links[' + g_rel + ']', g.getAttribute( 'href' ) );
     109                                        }
     110                        }
     111                }
     112        }
     113
     114        if ( document.body.getElementsByClassName ) {
     115                content = document.body.getElementsByClassName( 'hfeed' )[0];
     116        }
     117
     118        content = document.getElementById( 'content' ) || content || document.body;
     119        imgs = content.getElementsByTagName( 'img' ) || [];
     120
     121        for ( var n = 0; n < imgs.length; n++ ) {
     122                if ( n >= 100 ) {
     123                        break;
     124                }
     125
     126                if ( imgs[ n ].src.indexOf( 'avatar' ) > -1 || imgs[ n ].className.indexOf( 'avatar' ) > -1 ) {
     127                        continue;
     128                }
     129
     130                img.src = imgs[ n ].src;
     131
     132                if ( img.width >= 256 && img.height >= 128 ) {
     133                        add( '_img[]', img.src );
     134                }
     135        }
     136
     137        ifrs = document.body.getElementsByTagName( 'iframe' ) || [];
     138
     139        for ( var p = 0; p < ifrs.length; p++ ) {
     140                if ( p >= 100 ) {
     141                        break;
     142                }
     143
     144                vid = ifrs[ p ].src.match(/\/\/www\.youtube\.com\/embed\/([^\?]+)\?.+$/);
     145
     146                if ( vid && 2 === vid.length ) {
     147                        add( '_embed[]', 'https://www.youtube.com/watch?v=' + vid[1] );
     148                }
     149
     150                vid = ifrs[ p ].src.match( /\/\/player\.vimeo\.com\/video\/([\d]+)$/ );
     151
     152                if ( vid && 2 === vid.length ) {
     153                        add( '_embed[]', 'https://vimeo.com/' + vid[1] );
     154                }
     155
     156                vid = ifrs[ p ].src.match( /\/\/vine\.co\/v\/([^\/]+)\/embed/ );
     157
     158                if ( vid && 2 === vid.length ) {
     159                        add( '_embed[]', 'https://vine.co/v/' + vid[1] );
     160                }
     161        }
     162
     163        if ( document.title && document.title > 512 ) {
     164                add( 't', document.title );
     165        }
     166
     167        if ( selection && selection.length > 512 ) {
     168                add( 's', selection );
     169        }
     170
     171        form.setAttribute( 'method', 'POST' );
     172        form.setAttribute( 'action', pt_url );
     173        form.setAttribute( 'target', target );
     174        form.setAttribute( 'style', 'display: none;' );
     175
     176        windowWidth  = window.outerWidth || document.documentElement.clientWidth || 600;
     177        windowHeight = window.outerHeight || document.documentElement.clientHeight || 700;
     178
     179        windowWidth = ( windowWidth < 800 || windowWidth > 5000 ) ? 600 : ( windowWidth * 0.7 );
     180        windowHeight = ( windowHeight < 800 || windowHeight > 3000 ) ? 700 : ( windowHeight * 0.9 );
     181
     182        window.open( 'about:blank', target, 'width=' + windowWidth + ',height=' + windowHeight );
     183
     184        document.body.appendChild( form );
     185
     186        form.submit();
     187} )( window, document, top.location.href, window.pt_url );
     188( function( window, document, href, pt_url ) {
     189        var encodeURI = window.encodeURIComponent,
     190                form = document.createElement( 'form' ),
     191                head = document.getElementsByTagName( 'head' )[0],
     192                img = new Image(),
     193                target = '_press_this_app',
     194                windowWidth, windowHeight,
     195                metas, links, content, imgs, ifrs,
     196                vid, selection;
     197
     198        if ( ! pt_url ) {
     199                return;
     200        }
     201
     202        if ( window.getSelection ) {
     203                selection = window.getSelection() + '';
     204        } else if ( document.getSelection ) {
     205                selection = document.getSelection() + '';
     206        } else if ( document.selection ) {
     207                selection = document.selection.createRange().text;
     208        }
     209
     210        pt_url += ( pt_url.indexOf( '?' ) > -1 ? '&' : '?' ) + 'buster=' + ( new Date().getTime() );
     211
     212        if ( document.title.length && document.title.length <= 512 ) {
     213                pt_url += '&t=' + encodeURI( document.title );
     214        }
     215
     216        if ( selection && selection.length <= 512 ) {
     217                pt_url += '&s=' + encodeURI( selection );
     218        }
     219
     220        if ( href.match( /^https?:/ ) ) {
     221                pt_url += '&u=' + encodeURI( href );
     222        } else {
     223                top.location.href = pt_url;
     224                return;
     225        }
     226
     227        function add( name, value ) {
     228                if ( typeof value === 'undefined' ) {
     229                        return;
     230                }
     231
     232                var input = document.createElement( 'input' );
     233
     234                input.name = name;
     235                input.value = value;
     236                input.type = 'hidden';
     237
     238                form.appendChild( input );
     239        }
     240
     241        if ( href.match( /\/\/www\.youtube\.com\/watch/ ) ) {
     242                add( '_embed[]', href );
     243        } else if ( href.match( /\/\/vimeo\.com\/(.+\/)?([\d]+)$/ ) ) {
     244                add( '_embed[]', href );
     245        } else if ( href.match( /\/\/(www\.)?dailymotion\.com\/video\/.+$/ ) ) {
     246                add( '_embed[]', href );
     247        } else if ( href.match( /\/\/soundcloud\.com\/.+$/ ) ) {
     248                add( '_embed[]', href );
     249        } else if ( href.match( /\/\/twitter\.com\/[^\/]+\/status\/[\d]+$/ ) ) {
     250                add( '_embed[]', href );
     251        } else if ( href.match( /\/\/vine\.co\/v\/[^\/]+/ ) ) {
     252                add( '_embed[]', href );
     253        }
     254
     255        metas = head.getElementsByTagName( 'meta' ) || [];
     256
     257        for ( var m = 0; m < metas.length; m++ ) {
     258                if ( m >= 50 ) {
     259                        break;
     260                }
     261
     262                var q = metas[ m ],
     263                        q_name = q.getAttribute( 'name' ),
     264                        q_prop = q.getAttribute( 'property' ),
     265                        q_cont = q.getAttribute( 'content' );
     266
     267                if ( q_name ) {
     268                        add( '_meta[' + q_name + ']', q_cont );
     269                } else if ( q_prop ) {
     270                        add( '_meta[' + q_prop + ']', q_cont );
     271                }
     272        }
     273
     274        links = head.getElementsByTagName( 'link' ) || [];
     275
     276        for ( var y = 0; y < links.length; y++ ) {
     277                if ( y >= 50 ) {
     278                        break;
     279                }
     280
     281                var g = links[ y ],
     282                        g_rel = g.getAttribute( 'rel' );
     283
     284                if ( g_rel ) {
     285                        switch ( g_rel ) {
     286                                case 'canonical':
     287                                case 'icon':
     288                                case 'shortlink':
     289                                        add( '_links[' + g_rel + ']', g.getAttribute( 'href' ) );
     290                                        break;
     291                                case 'alternate':
     292                                        if ( 'application/json+oembed' === g.getAttribute( 'type' ) ) {
     293                                                add( '_links[' + g_rel + ']', g.getAttribute( 'href' ) );
     294                                        } else if ( 'handheld' === g.getAttribute( 'media' ) ) {
     295                                                add( '_links[' + g_rel + ']', g.getAttribute( 'href' ) );
     296                                        }
     297                        }
     298                }
     299        }
     300
     301        if ( document.body.getElementsByClassName ) {
     302                content = document.body.getElementsByClassName( 'hfeed' )[0];
     303        }
     304
     305        content = document.getElementById( 'content' ) || content || document.body;
     306        imgs = content.getElementsByTagName( 'img' ) || [];
     307
     308        for ( var n = 0; n < imgs.length; n++ ) {
     309                if ( n >= 100 ) {
     310                        break;
     311                }
     312
     313                if ( imgs[ n ].src.indexOf( 'avatar' ) > -1 || imgs[ n ].className.indexOf( 'avatar' ) > -1 ) {
     314                        continue;
     315                }
     316
     317                img.src = imgs[ n ].src;
     318
     319                if ( img.width >= 256 && img.height >= 128 ) {
     320                        add( '_img[]', img.src );
     321                }
     322        }
     323
     324        ifrs = document.body.getElementsByTagName( 'iframe' ) || [];
     325
     326        for ( var p = 0; p < ifrs.length; p++ ) {
     327                if ( p >= 100 ) {
     328                        break;
     329                }
     330
     331                vid = ifrs[ p ].src.match(/\/\/www\.youtube\.com\/embed\/([^\?]+)\?.+$/);
     332
     333                if ( vid && 2 === vid.length ) {
     334                        add( '_embed[]', 'https://www.youtube.com/watch?v=' + vid[1] );
     335                }
     336
     337                vid = ifrs[ p ].src.match( /\/\/player\.vimeo\.com\/video\/([\d]+)$/ );
     338
     339                if ( vid && 2 === vid.length ) {
     340                        add( '_embed[]', 'https://vimeo.com/' + vid[1] );
     341                }
     342
     343                vid = ifrs[ p ].src.match( /\/\/vine\.co\/v\/([^\/]+)\/embed/ );
     344
     345                if ( vid && 2 === vid.length ) {
     346                        add( '_embed[]', 'https://vine.co/v/' + vid[1] );
     347                }
     348        }
     349
     350        if ( document.title && document.title > 512 ) {
     351                add( 't', document.title );
     352        }
     353
     354        if ( selection && selection.length > 512 ) {
     355                add( 's', selection );
     356        }
     357
     358        form.setAttribute( 'method', 'POST' );
     359        form.setAttribute( 'action', pt_url );
     360        form.setAttribute( 'target', target );
     361        form.setAttribute( 'style', 'display: none;' );
     362
     363        windowWidth  = window.outerWidth || document.documentElement.clientWidth || 600;
     364        windowHeight = window.outerHeight || document.documentElement.clientHeight || 700;
     365
     366        windowWidth = ( windowWidth < 800 || windowWidth > 5000 ) ? 600 : ( windowWidth * 0.7 );
     367        windowHeight = ( windowHeight < 800 || windowHeight > 3000 ) ? 700 : ( windowHeight * 0.9 );
     368
     369        window.open( 'about:blank', target, 'width=' + windowWidth + ',height=' + windowHeight );
     370
     371        document.body.appendChild( form );
     372
     373        form.submit();
     374} )( window, document, top.location.href, window.pt_url );
     375( function( window, document, href, pt_url ) {
     376        var encodeURI = window.encodeURIComponent,
     377                form = document.createElement( 'form' ),
     378                head = document.getElementsByTagName( 'head' )[0],
     379                img = new Image(),
     380                target = '_press_this_app',
     381                windowWidth, windowHeight,
     382                metas, links, content, imgs, ifrs,
     383                vid, selection;
     384
     385        if ( ! pt_url ) {
     386                return;
     387        }
     388
     389        if ( window.getSelection ) {
     390                selection = window.getSelection() + '';
     391        } else if ( document.getSelection ) {
     392                selection = document.getSelection() + '';
     393        } else if ( document.selection ) {
     394                selection = document.selection.createRange().text;
     395        }
     396
     397        pt_url += ( pt_url.indexOf( '?' ) > -1 ? '&' : '?' ) + 'buster=' + ( new Date().getTime() );
     398
     399        if ( document.title.length && document.title.length <= 512 ) {
     400                pt_url += '&t=' + encodeURI( document.title );
     401        }
     402
     403        if ( selection && selection.length <= 512 ) {
     404                pt_url += '&s=' + encodeURI( selection );
     405        }
     406
     407        if ( href.match( /^https?:/ ) ) {
     408                pt_url += '&u=' + encodeURI( href );
     409        } else {
     410                top.location.href = pt_url;
     411                return;
     412        }
     413
     414        function add( name, value ) {
     415                if ( typeof value === 'undefined' ) {
     416                        return;
     417                }
     418
     419                var input = document.createElement( 'input' );
     420
     421                input.name = name;
     422                input.value = value;
     423                input.type = 'hidden';
     424
     425                form.appendChild( input );
     426        }
     427
     428        if ( href.match( /\/\/www\.youtube\.com\/watch/ ) ) {
     429                add( '_embed[]', href );
     430        } else if ( href.match( /\/\/vimeo\.com\/(.+\/)?([\d]+)$/ ) ) {
     431                add( '_embed[]', href );
     432        } else if ( href.match( /\/\/(www\.)?dailymotion\.com\/video\/.+$/ ) ) {
     433                add( '_embed[]', href );
     434        } else if ( href.match( /\/\/soundcloud\.com\/.+$/ ) ) {
     435                add( '_embed[]', href );
     436        } else if ( href.match( /\/\/twitter\.com\/[^\/]+\/status\/[\d]+$/ ) ) {
     437                add( '_embed[]', href );
     438        } else if ( href.match( /\/\/vine\.co\/v\/[^\/]+/ ) ) {
     439                add( '_embed[]', href );
     440        }
     441
     442        metas = head.getElementsByTagName( 'meta' ) || [];
     443
     444        for ( var m = 0; m < metas.length; m++ ) {
     445                if ( m >= 50 ) {
     446                        break;
     447                }
     448
     449                var q = metas[ m ],
     450                        q_name = q.getAttribute( 'name' ),
     451                        q_prop = q.getAttribute( 'property' ),
     452                        q_cont = q.getAttribute( 'content' );
     453
     454                if ( q_name ) {
     455                        add( '_meta[' + q_name + ']', q_cont );
     456                } else if ( q_prop ) {
     457                        add( '_meta[' + q_prop + ']', q_cont );
     458                }
     459        }
     460
     461        links = head.getElementsByTagName( 'link' ) || [];
     462
     463        for ( var y = 0; y < links.length; y++ ) {
     464                if ( y >= 50 ) {
     465                        break;
     466                }
     467
     468                var g = links[ y ],
     469                        g_rel = g.getAttribute( 'rel' );
     470
     471                if ( g_rel ) {
     472                        switch ( g_rel ) {
     473                                case 'canonical':
     474                                case 'icon':
     475                                case 'shortlink':
     476                                        add( '_links[' + g_rel + ']', g.getAttribute( 'href' ) );
     477                                        break;
     478                                case 'alternate':
     479                                        if ( 'application/json+oembed' === g.getAttribute( 'type' ) ) {
     480                                                add( '_links[' + g_rel + ']', g.getAttribute( 'href' ) );
     481                                        } else if ( 'handheld' === g.getAttribute( 'media' ) ) {
     482                                                add( '_links[' + g_rel + ']', g.getAttribute( 'href' ) );
     483                                        }
     484                        }
     485                }
     486        }
     487
     488        if ( document.body.getElementsByClassName ) {
     489                content = document.body.getElementsByClassName( 'hfeed' )[0];
     490        }
     491
     492        content = document.getElementById( 'content' ) || content || document.body;
     493        imgs = content.getElementsByTagName( 'img' ) || [];
     494
     495        for ( var n = 0; n < imgs.length; n++ ) {
     496                if ( n >= 100 ) {
     497                        break;
     498                }
     499
     500                if ( imgs[ n ].src.indexOf( 'avatar' ) > -1 || imgs[ n ].className.indexOf( 'avatar' ) > -1 ) {
     501                        continue;
     502                }
     503
     504                img.src = imgs[ n ].src;
     505
     506                if ( img.width >= 256 && img.height >= 128 ) {
     507                        add( '_img[]', img.src );
     508                }
     509        }
     510
     511        ifrs = document.body.getElementsByTagName( 'iframe' ) || [];
     512
     513        for ( var p = 0; p < ifrs.length; p++ ) {
     514                if ( p >= 100 ) {
     515                        break;
     516                }
     517
     518                vid = ifrs[ p ].src.match(/\/\/www\.youtube\.com\/embed\/([^\?]+)\?.+$/);
     519
     520                if ( vid && 2 === vid.length ) {
     521                        add( '_embed[]', 'https://www.youtube.com/watch?v=' + vid[1] );
     522                }
     523
     524                vid = ifrs[ p ].src.match( /\/\/player\.vimeo\.com\/video\/([\d]+)$/ );
     525
     526                if ( vid && 2 === vid.length ) {
     527                        add( '_embed[]', 'https://vimeo.com/' + vid[1] );
     528                }
     529
     530                vid = ifrs[ p ].src.match( /\/\/vine\.co\/v\/([^\/]+)\/embed/ );
     531
     532                if ( vid && 2 === vid.length ) {
     533                        add( '_embed[]', 'https://vine.co/v/' + vid[1] );
     534                }
     535        }
     536
     537        if ( document.title && document.title > 512 ) {
     538                add( 't', document.title );
     539        }
     540
     541        if ( selection && selection.length > 512 ) {
     542                add( 's', selection );
     543        }
     544
     545        form.setAttribute( 'method', 'POST' );
     546        form.setAttribute( 'action', pt_url );
     547        form.setAttribute( 'target', target );
     548        form.setAttribute( 'style', 'display: none;' );
     549
     550        windowWidth  = window.outerWidth || document.documentElement.clientWidth || 600;
     551        windowHeight = window.outerHeight || document.documentElement.clientHeight || 700;
     552
     553        windowWidth = ( windowWidth < 800 || windowWidth > 5000 ) ? 600 : ( windowWidth * 0.7 );
     554        windowHeight = ( windowHeight < 800 || windowHeight > 3000 ) ? 700 : ( windowHeight * 0.9 );
     555
     556        window.open( 'about:blank', target, 'width=' + windowWidth + ',height=' + windowHeight );
     557
     558        document.body.appendChild( form );
     559
     560        form.submit();
     561} )( window, document, top.location.href, window.pt_url );
  • src/wp-admin/js/post.js

     
    77
    88window.wp = window.wp || {};
    99
    10 // return an array with any duplicate, whitespace or values removed
    11 function array_unique_noempty(a) {
    12         var out = [];
    13         jQuery.each( a, function(key, val) {
    14                 val = jQuery.trim(val);
    15                 if ( val && jQuery.inArray(val, out) == -1 )
    16                         out.push(val);
    17                 } );
    18         return out;
    19 }
    20 
    21 ( function($) {
     10( function( $ ) {
    2211        var titleHasFocus = false;
    2312
    24 tagBox = {
    25         clean : function(tags) {
    26                 var comma = postL10n.comma;
    27                 if ( ',' !== comma )
    28                         tags = tags.replace(new RegExp(comma, 'g'), ',');
    29                 tags = tags.replace(/\s*,\s*/g, ',').replace(/,+/g, ',').replace(/[,\s]+$/, '').replace(/^[,\s]+/, '');
    30                 if ( ',' !== comma )
    31                         tags = tags.replace(/,/g, comma);
    32                 return tags;
    33         },
    34 
    35         parseTags : function(el) {
    36                 var id = el.id, num = id.split('-check-num-')[1], taxbox = $(el).closest('.tagsdiv'),
    37                         thetags = taxbox.find('.the-tags'), comma = postL10n.comma,
    38                         current_tags = thetags.val().split(comma), new_tags = [];
    39                 delete current_tags[num];
    40 
    41                 $.each( current_tags, function(key, val) {
    42                         val = $.trim(val);
    43                         if ( val ) {
    44                                 new_tags.push(val);
    45                         }
    46                 });
    47 
    48                 thetags.val( this.clean( new_tags.join(comma) ) );
    49 
    50                 this.quickClicks(taxbox);
    51                 return false;
    52         },
    53 
    54         quickClicks : function(el) {
    55                 var thetags = $('.the-tags', el),
    56                         tagchecklist = $('.tagchecklist', el),
    57                         id = $(el).attr('id'),
    58                         current_tags, disabled;
    59 
    60                 if ( !thetags.length )
    61                         return;
    62 
    63                 disabled = thetags.prop('disabled');
    64 
    65                 current_tags = thetags.val().split(postL10n.comma);
    66                 tagchecklist.empty();
    67 
    68                 $.each( current_tags, function( key, val ) {
    69                         var span, xbutton;
    70 
    71                         val = $.trim( val );
    72 
    73                         if ( ! val )
    74                                 return;
    75 
    76                         // Create a new span, and ensure the text is properly escaped.
    77                         span = $('<span />').text( val );
    78 
    79                         // If tags editing isn't disabled, create the X button.
    80                         if ( ! disabled ) {
    81                                 xbutton = $( '<a id="' + id + '-check-num-' + key + '" class="ntdelbutton">X</a>' );
    82                                 xbutton.click( function(){ tagBox.parseTags(this); });
    83                                 span.prepend('&nbsp;').prepend( xbutton );
    84                         }
    85 
    86                         // Append the span to the tag list.
    87                         tagchecklist.append( span );
    88                 });
    89         },
    90 
    91         flushTags : function(el, a, f) {
    92                 var tagsval, newtags, text,
    93                         tags = $('.the-tags', el),
    94                         newtag = $('input.newtag', el),
    95                         comma = postL10n.comma;
    96                 a = a || false;
    97 
    98                 text = a ? $(a).text() : newtag.val();
    99                 tagsval = tags.val();
    100                 newtags = tagsval ? tagsval + comma + text : text;
    101 
    102                 newtags = this.clean( newtags );
    103                 newtags = array_unique_noempty( newtags.split(comma) ).join(comma);
    104                 tags.val(newtags);
    105                 this.quickClicks(el);
    106 
    107                 if ( !a )
    108                         newtag.val('');
    109                 if ( 'undefined' == typeof(f) )
    110                         newtag.focus();
    111 
    112                 return false;
    113         },
    114 
    115         get : function(id) {
    116                 var tax = id.substr(id.indexOf('-')+1);
    117 
    118                 $.post(ajaxurl, {'action':'get-tagcloud', 'tax':tax}, function(r, stat) {
    119                         if ( 0 === r || 'success' != stat )
    120                                 r = wpAjax.broken;
    121 
    122                         r = $('<p id="tagcloud-'+tax+'" class="the-tagcloud">'+r+'</p>');
    123                         $('a', r).click(function(){
    124                                 tagBox.flushTags( $(this).closest('.inside').children('.tagsdiv'), this);
    125                                 return false;
    126                         });
    127 
    128                         $('#'+id).after(r);
    129                 });
    130         },
    131 
    132         init : function() {
    133                 var t = this, ajaxtag = $('div.ajaxtag');
    134 
    135                 $('.tagsdiv').each( function() {
    136                         tagBox.quickClicks(this);
    137                 });
    138 
    139                 $('input.tagadd', ajaxtag).click(function(){
    140                         t.flushTags( $(this).closest('.tagsdiv') );
    141                 });
    142 
    143                 $('div.taghint', ajaxtag).click(function(){
    144                         $(this).css('visibility', 'hidden').parent().siblings('.newtag').focus();
    145                 });
    146 
    147                 $('input.newtag', ajaxtag).blur(function() {
    148                         if ( '' === this.value )
    149                                 $(this).parent().siblings('.taghint').css('visibility', '');
    150                 }).focus(function(){
    151                         $(this).parent().siblings('.taghint').css('visibility', 'hidden');
    152                 }).keyup(function(e){
    153                         if ( 13 == e.which ) {
    154                                 tagBox.flushTags( $(this).closest('.tagsdiv') );
    155                                 return false;
    156                         }
    157                 }).keypress(function(e){
    158                         if ( 13 == e.which ) {
    159                                 e.preventDefault();
    160                                 return false;
    161                         }
    162                 }).each(function(){
    163                         var tax = $(this).closest('div.tagsdiv').attr('id');
    164                         $(this).suggest( ajaxurl + '?action=ajax-tag-search&tax=' + tax, { delay: 500, minchars: 2, multiple: true, multipleSep: postL10n.comma + ' ' } );
    165                 });
    166 
    167                 // save tags on post save/publish
    168                 $('#post').submit(function(){
    169                         $('div.tagsdiv').each( function() {
    170                                 tagBox.flushTags(this, false, 1);
    171                         });
    172                 });
    173 
    174                 // tag cloud
    175                 $('a.tagcloud-link').click(function(){
    176                         tagBox.get( $(this).attr('id') );
    177                         $(this).unbind().click(function(){
    178                                 $(this).siblings('.the-tagcloud').toggle();
    179                                 return false;
    180                         });
    181                         return false;
    182                 });
    183         }
    184 };
    185 
    18613commentsBox = {
    18714        st : 0,
    18815
     
    572399
    573400        // multi-taxonomies
    574401        if ( $('#tagsdiv-post_tag').length ) {
    575                 tagBox.init();
     402                window.tagBox && window.tagBox.init();
    576403        } else {
    577404                $('#side-sortables, #normal-sortables, #advanced-sortables').children('div.postbox').each(function(){
    578405                        if ( this.id.indexOf('tagsdiv-') === 0 ) {
    579                                 tagBox.init();
     406                                window.tagBox && window.tagBox.init();
    580407                                return false;
    581408                        }
    582409                });
  • src/wp-admin/js/press-this.js

     
     1/**
     2 * PressThis App
     3 *
     4 */
     5( function( $, window ) {
     6        var PressThis = function() {
     7                var editor,
     8                        saveAlert             = false,
     9                        $div                  = $( '<div>' ),
     10                        siteConfig            = window.wpPressThisConfig || {},
     11                        data                  = window.wpPressThisData || {},
     12                        smallestWidth         = 128,
     13                        interestingImages         = getInterestingImages( data ) || [],
     14                        interestingEmbeds         = getInterestingEmbeds( data ) || [],
     15                        hasEmptyTitleStr      = false,
     16                        suggestedTitleStr     = getSuggestedTitle( data ),
     17                        suggestedContentStr   = getSuggestedContent( data ),
     18                        hasSetFocus           = false,
     19                        catsCache             = [],
     20                        transitionEndEvent    = ( function() {
     21                                var style = document.documentElement.style;
     22
     23                                if ( typeof style.transition !== 'undefined' ) {
     24                                        return 'transitionend';
     25                                }
     26
     27                                if ( typeof style.WebkitTransition !== 'undefined' ) {
     28                                        return 'webkitTransitionEnd';
     29                                }
     30
     31                                return false;
     32                        }() );
     33
     34                /* ***************************************************************
     35                 * HELPER FUNCTIONS
     36                 *************************************************************** */
     37
     38                /**
     39                 * Emulates our PHP __() gettext function, powered by the strings exported in pressThisL10n.
     40                 *
     41                 * @param key string Key of the string to be translated, as found in pressThisL10n.
     42                 * @returns string Original or translated string, or empty string if no key.
     43                 */
     44                function __( key ) {
     45                        if ( key && window.pressThisL10n ) {
     46                                return window.pressThisL10n[key] || key;
     47                        }
     48
     49                        return key || '';
     50                }
     51
     52                /**
     53                 * Strips HTML tags
     54                 *
     55                 * @param string string Text to have the HTML tags striped out of.
     56                 * @returns string Stripped text.
     57                 */
     58                function stripTags( string ) {
     59                        string = string || '';
     60
     61                        return string
     62                                .replace( /<!--[\s\S]*?(-->|$)/g, '' )
     63                                .replace( /<(script|style)[^>]*>[\s\S]*?(<\/\1>|$)/ig, '' )
     64                                .replace( /<\/?[a-z][^>]*>/ig, '' );
     65                }
     66
     67                // TODO: needed?
     68                function entityEncode( text ) {
     69                        return $div.text( text ).html();
     70                }
     71
     72                /**
     73                 * Strip HTML tags and entity encode some of the HTML special chars.
     74                 *
     75                 * @param text string Text.
     76                 * @returns string Sanitized text.
     77                 */
     78                function sanitizeText( text ) {
     79                        text = stripTags( text );
     80
     81                        return text
     82                                .replace( /\\/, '' )
     83                                .replace( /</g, '&lt;' )
     84                                .replace( />/g, '&gt;' )
     85                                .replace( /"/g, '&quot;' )
     86                                .replace( /'/g, '&#039;' );
     87                }
     88
     89                /**
     90                 * Allow only HTTP or protocol relative URLs.
     91                 *
     92                 * @param url string The URL.
     93                 * @returns string Processed URL.
     94                 */
     95                function checkUrl( url ) {
     96                        url = $.trim( url || '' );
     97
     98                        if ( /^(?:https?:)?\/\//.test( url ) ) {
     99                                url = stripTags( url );
     100                                return url.replace( /["\\]+/g, '' );
     101                        }
     102
     103                        return '';
     104                }
     105
     106                /**
     107                 * Gets the source page's canonical link, based on passed location and meta data.
     108                 *
     109                 * @param data object Usually WpPressThis_App.data
     110                 * @returns string Discovered canonical URL, or empty
     111                 */
     112                function getCanonicalLink( data ) {
     113                        if ( ! data || data.length ) {
     114                                return '';
     115                        }
     116
     117                        var link = '';
     118
     119                        if ( data._links ) {
     120                                if ( data._links.canonical && data._links.canonical.length ) {
     121                                        link = data._links.canonical;
     122                                }
     123                        }
     124
     125                        if ( ! link.length && data.u ) {
     126                                link = data.u;
     127                        }
     128
     129                        if ( ! link.length && data._meta ) {
     130                                if ( data._meta['twitter:url'] && data._meta['twitter:url'].length ) {
     131                                        link = data._meta['twitter:url'];
     132                                } else if ( data._meta['og:url'] && data._meta['og:url'].length ) {
     133                                        link = data._meta['og:url'];
     134                                }
     135                        }
     136
     137                        return decodeURI( link );
     138                }
     139
     140                /**
     141                 * Gets the source page's site name, based on passed meta data.
     142                 *
     143                 * @param data object Usually WpPressThis_App.data
     144                 * @returns string Discovered site name, or empty
     145                 */
     146                function getSourceSiteName( data ) {
     147                        if ( ! data || data.length ) {
     148                                return '';
     149                        }
     150
     151                        var name='';
     152
     153                        if ( data._meta ) {
     154                                if ( data._meta['og:site_name'] && data._meta['og:site_name'].length ) {
     155                                        name = data._meta['og:site_name'];
     156                                } else if ( data._meta['application-name'] && data._meta['application-name'].length ) {
     157                                        name = data._meta['application-name'];
     158                                }
     159                        }
     160
     161                        return name.replace( /\\/g, '' );
     162                }
     163
     164                /**
     165                 * Gets the source page's title, based on passed title and meta data.
     166                 *
     167                 * @param data object Usually WpPressThis_App.data
     168                 * @returns string Discovered page title, or empty
     169                 */
     170                function getSuggestedTitle( data ) {
     171                        if ( ! data || data.length ) {
     172                                return __( 'newPost' );
     173                        }
     174
     175                        var title = '';
     176
     177                        if ( data.t ) {
     178                                title = data.t;
     179                        }
     180
     181                        if ( ! title.length && data._meta ) {
     182                                if ( data._meta['twitter:title'] && data._meta['twitter:title'].length ) {
     183                                        title = data._meta['twitter:title'];
     184                                } else if ( data._meta['og:title'] && data._meta['og:title'].length ) {
     185                                        title = data._meta['og:title'];
     186                                } else if ( data._meta.title && data._meta.title.length ) {
     187                                        title = data._meta.title;
     188                                }
     189                        }
     190
     191                        if ( ! title.length ) {
     192                                title = __( 'newPost' );
     193                                hasEmptyTitleStr = true;
     194                        }
     195
     196                        return title.replace( /\\/g, '' );
     197                }
     198
     199                /**
     200                 * Gets the source page's suggested content, based on passed data (description, selection, etc).
     201                 * Features a blockquoted excerpt, as well as content attribution, if any.
     202                 *
     203                 * @param data object Usually WpPressThis_App.data
     204                 * @returns string Discovered content, or empty
     205                 */
     206                function getSuggestedContent( data ) {
     207                        if ( ! data || data.length ) {
     208                                return '';
     209                        }
     210
     211                        var content   = '',
     212                                title     = getSuggestedTitle( data ),
     213                                url       = getCanonicalLink( data ),
     214                                siteName  = getSourceSiteName( data );
     215
     216                        if ( data.s && data.s.length ) {
     217                                content = data.s;
     218                        } else if ( data._meta ) {
     219                                if ( data._meta['twitter:description'] && data._meta['twitter:description'].length ) {
     220                                        content = data._meta['twitter:description'];
     221                                } else if ( data._meta['og:description'] && data._meta['og:description'].length ) {
     222                                        content = data._meta['og:description'];
     223                                } else if ( data._meta.description && data._meta.description.length ) {
     224                                        content = data._meta.description;
     225                                }
     226                        }
     227
     228                        // Wrap suggested content in blockquote tag, if we have any.
     229                        content = ( content.length ? '<blockquote class="press-this-suggested-content">' + sanitizeText( content ) + '</blockquote>' : '' );
     230
     231                        // Add a source attribution if there is one available.
     232                        if ( ( ( title.length && __( 'newPost' ) !== title ) || siteName.length ) && url.length ) {
     233                                content += '<p class="press-this-suggested-source">';
     234                                content += __( 'source' );
     235                                content += ' <cite>';
     236                                content += __( 'sourceLink').replace( '%1$s', encodeURI( url ) ).replace( '%2$s', sanitizeText( title || siteName ) );
     237                                content += '</cite></p>';
     238                        }
     239
     240                        if ( ! content ) {
     241                                content = '';
     242                        }
     243
     244                        return content.replace( /\\/g, '' );
     245                }
     246
     247                /**
     248                 * Tests if what was passed as an embed URL is deemed to be embeddable in the editor.
     249                 *
     250                 * @param url string Passed URl, usually from WpPressThis_App.data._embed
     251                 * @returns boolean
     252                 */
     253                function isEmbeddable( url ) {
     254                        if ( ! url ) {
     255                                return false;
     256                        } else if ( url.match( /\/\/(m\.|www\.)?youtube\.com\/watch\?/ ) || url.match( /\/youtu\.be\/.+$/ ) ) {
     257                                return true;
     258                        } else if ( url.match( /\/\/vimeo\.com\/(.+\/)?[\d]+$/ ) ) {
     259                                return true;
     260                        } else if ( url.match( /\/\/(www\.)?dailymotion\.com\/video\/.+$/ ) ) {
     261                                return true;
     262                        } else if ( url.match( /\/\/soundcloud\.com\/.+$/ ) ) {
     263                                return true;
     264                        } else if ( url.match( /\/\/twitter\.com\/[^\/]+\/status\/[\d]+$/ ) ) {
     265                                return true;
     266                        } else if ( url.match( /\/\/vine\.co\/v\/[^\/]+/ ) ) {
     267                                return true;
     268                        }
     269
     270                        return false;
     271                }
     272
     273                /**
     274                 * Tests if what was passed as an image URL is deemed to be interesting enough to offer to the user for selection.
     275                 *
     276                 * @param src string Passed URl, usually from WpPressThis_App.data._ing
     277                 * @returns boolean Test for false
     278                 */
     279                function isSrcUninterestingPath( src ) {
     280                        if ( src.match( /\/ad[sx]{1}?\// ) ) {
     281                                // Ads
     282                                return true;
     283                        } else if ( src.match( /(\/share-?this[^\.]+?\.[a-z0-9]{3,4})(\?.*)?$/ ) ) {
     284                                // Share-this type button
     285                                return true;
     286                        } else if ( src.match( /\/(spinner|loading|spacer|blank|rss)\.(gif|jpg|png)/ ) ) {
     287                                // Loaders, spinners, spacers
     288                                return true;
     289                        } else if ( src.match( /\/([^\.\/]+[-_]{1})?(spinner|loading|spacer|blank)s?([-_]{1}[^\.\/]+)?\.[a-z0-9]{3,4}/ ) ) {
     290                                // Fancy loaders, spinners, spacers
     291                                return true;
     292                        } else if ( src.match( /([^\.\/]+[-_]{1})?thumb[^.]*\.(gif|jpg|png)$/ ) ) {
     293                                // Thumbnails, too small, usually irrelevant to context
     294                                return true;
     295                        } else if ( src.match( /\/wp-includes\// ) ) {
     296                                // Classic WP interface images
     297                                return true;
     298                        } else if ( src.match( /[^\d]{1}\d{1,2}x\d+\.(gif|jpg|png)$/ ) ) {
     299                                // Most often tiny buttons/thumbs (< 100px wide)
     300                                return true;
     301                        } else if ( src.indexOf( '/g.gif' ) > -1 ) {
     302                                // Classic WP stats gif
     303                                return true;
     304                        } else if ( src.indexOf( '/pixel.mathtag.com' ) > -1 ) {
     305                                // See mathtag.com
     306                                return true;
     307                        }
     308                        return false;
     309                }
     310
     311                /**
     312                 * Get a list of valid embeds from what was passed via WpPressThis_App.data._embed on page load.
     313                 *
     314                 * @returns array
     315                 */
     316                function getInterestingEmbeds() {
     317                        var embeds             = data._embed || [],
     318                                interestingEmbeds  = [],
     319                                alreadySelected    = [];
     320
     321                        if ( embeds.length ) {
     322                                $.each( embeds, function ( i, src ) {
     323                                        if ( !src || !src.length ) {
     324                                                // Skip: no src value
     325                                                return;
     326                                        } else if ( !isEmbeddable( src ) ) {
     327                                                // Skip: not deemed embeddable
     328                                                return;
     329                                        }
     330
     331                                        var schemelessSrc = src.replace( /^https?:/, '' );
     332
     333                                        if ( $.inArray( schemelessSrc, alreadySelected ) > -1 ) {
     334                                                // Skip: already shown
     335                                                return;
     336                                        }
     337
     338                                        interestingEmbeds.push( src );
     339                                        alreadySelected.push( schemelessSrc );
     340                                } );
     341                        }
     342
     343                        return interestingEmbeds;
     344                }
     345
     346                /**
     347                 * Get what is likely the most valuable image from what was passed via WpPressThis_App.data._img and WpPressThis_App.data._meta on page load.
     348                 *
     349                 * @returns array
     350                 */
     351                function getFeaturedImage( data ) {
     352                        var featured = '';
     353
     354                        if ( ! data || ! data._meta ) {
     355                                return '';
     356                        }
     357
     358                        if ( data._meta['twitter:image0:src'] && data._meta['twitter:image0:src'].length ) {
     359                                featured = data._meta['twitter:image0:src'];
     360                        } else if ( data._meta['twitter:image0'] && data._meta['twitter:image0'].length ) {
     361                                featured = data._meta['twitter:image0'];
     362                        } else if ( data._meta['twitter:image:src'] && data._meta['twitter:image:src'].length ) {
     363                                featured = data._meta['twitter:image:src'];
     364                        } else if ( data._meta['twitter:image'] && data._meta['twitter:image'].length ) {
     365                                featured = data._meta['twitter:image'];
     366                        } else if ( data._meta['og:image'] && data._meta['og:image'].length ) {
     367                                featured = data._meta['og:image'];
     368                        } else if ( data._meta['og:image:secure_url'] && data._meta['og:image:secure_url'].length ) {
     369                                featured = data._meta['og:image:secure_url'];
     370                        }
     371
     372                        featured = checkUrl( featured );
     373
     374                        return ( isSrcUninterestingPath( featured ) ) ? '' : featured;
     375                }
     376
     377                /**
     378                 * Get a list of valid images from what was passed via WpPressThis_App.data._img and WpPressThis_App.data._meta on page load.
     379                 *
     380                 * @returns array
     381                 */
     382                function getInterestingImages( data ) {
     383                        var imgs             = data._img || [],
     384                                featuredPict     = getFeaturedImage( data ) || '',
     385                                interestingImgs  = [],
     386                                alreadySelected  = [];
     387
     388                        if ( featuredPict.length ) {
     389                                interestingImgs.push( featuredPict );
     390                                alreadySelected.push( featuredPict.replace(/^https?:/, '') );
     391                        }
     392
     393                        if ( imgs.length ) {
     394                                $.each( imgs, function ( i, src ) {
     395                                        src = src.replace( /http:\/\/[\d]+\.gravatar\.com\//, 'https://secure.gravatar.com/' );
     396                                        src = checkUrl( src );
     397
     398                                        if ( ! src || ! src.length ) {
     399                                                // Skip: no src value
     400                                                return;
     401                                        }
     402
     403                                        var schemelessSrc = src.replace( /^https?:/, '' );
     404
     405                                        if ( Array.prototype.indexOf && alreadySelected.indexOf( schemelessSrc ) > -1 ) {
     406                                                // Skip: already shown
     407                                                return;
     408                                        } else if ( isSrcUninterestingPath( src ) ) {
     409                                                // Skip: spinner, stat, ad, or spacer pict
     410                                                return;
     411                                        } else if ( src.indexOf( 'avatar' ) > -1 && interestingImgs.length >= 15 ) {
     412                                                // Skip:  some type of avatar and we've already gathered more than 23 diff images to show
     413                                                return;
     414                                        }
     415
     416                                        interestingImgs.push( src );
     417                                        alreadySelected.push( schemelessSrc );
     418                                } );
     419                        }
     420
     421                        return interestingImgs;
     422                }
     423
     424                /**
     425                 * Show UX spinner
     426                 */
     427                function showSpinner() {
     428                        $( '#spinner' ).addClass( 'show' );
     429                        $( '.post-actions button' ).each( function() {
     430                                $( this ).attr( 'disabled', 'disabled' );
     431                        } );
     432                }
     433
     434                /**
     435                 * Hide UX spinner
     436                 */
     437                function hideSpinner() {
     438                        $( '#spinner' ).removeClass( 'show' );
     439                        $( '.post-actions button' ).each( function() {
     440                                $( this ).removeAttr( 'disabled' );
     441                        } );
     442                }
     443
     444                /**
     445                 * Submit the post form via AJAX, and redirect to the proper screen if published vs saved as a draft.
     446                 *
     447                 * @param action string publish|draft
     448                 */
     449                function submitPost( action ) {
     450                        saveAlert = false;
     451                        showSpinner();
     452
     453                        var $form = $( '#pressthis-form' );
     454
     455                        if ( 'publish' === action ) {
     456                                $( '#post_status' ).val( 'publish' );
     457                        }
     458
     459                        editor && editor.save();
     460
     461                        $( '#title-field' ).val( sanitizeText( $( '#title-container' ).text() ) );
     462
     463                        // Make sure to flush out the tags with tagBox before saving
     464                        if ( window.tagBox ) {
     465                                $( 'div.tagsdiv' ).each( function() {
     466                                        window.tagBox.flushTags( this, false, 1 );
     467                                } );
     468                        }
     469
     470                        var data = $form.serialize();
     471
     472                        $.ajax( {
     473                                type: 'post',
     474                                url: window.ajaxurl,
     475                                data: data,
     476                                success: function( response ) {
     477                                        if ( ! response.success ) {
     478                                                renderError( response.data.errorMessage );
     479                                                hideSpinner();
     480                                        } else if ( response.data.redirect ) {
     481                                                if ( window.opener && siteConfig.redir_in_parent ) {
     482                                                        try {
     483                                                                window.opener.location.href = response.data.redirect;
     484                                                        } catch( er ) {}
     485
     486                                                        window.self.close();
     487                                                } else {
     488                                                        window.location.href = response.data.redirect;
     489                                                }
     490                                        }
     491                                }
     492                        } );
     493                }
     494
     495                /**
     496                 * Inserts the media a user has selected from the presented list inside the editor, as an image or embed, based on type
     497                 *
     498                 * @param type string img|embed
     499                 * @param src string Source URL
     500                 * @param link string Optional destination link, for images (defaults to src)
     501                 */
     502                function insertSelectedMedia( type, src, link ) {
     503                        var newContent = '';
     504
     505                        if ( ! editor ) {
     506                                return;
     507                        }
     508
     509                        src = checkUrl( src );
     510                        link = checkUrl( link );
     511
     512                        if ( 'img' === type ) {
     513                                if ( ! link || ! link.length ) {
     514                                        link = src;
     515                                }
     516
     517                                newContent = '<a href="' + link + '"><img class="alignnone size-full" src="' + src + '" /></a>\n';
     518                        } else {
     519                                newContent = '[embed]' + src + '[/embed]\n';
     520                        }
     521
     522                        if ( ! hasSetFocus ) {
     523                                // Append to top of content on 1st media insert
     524                                editor.setContent( newContent + editor.getContent() );
     525                        } else {
     526                                // Or add where the cursor was last positioned in TinyMCE
     527                                editor.execCommand( 'mceInsertContent', false, newContent );
     528                        }
     529
     530                        hasSetFocus = true;
     531                }
     532
     533                /**
     534                 * Adds the currently selected post format next to the option, in the options panel.
     535                 *
     536                 * @param format string Post format to be displayed
     537                 */
     538                function setPostFormatString( format ) {
     539                        if ( ! format || ! siteConfig || ! siteConfig.post_formats || ! siteConfig.post_formats[ format ] ) {
     540                                return;
     541                        }
     542                        $( '#post-option-post-format' ).text( siteConfig.post_formats[ format ] );
     543                }
     544
     545                /**
     546                 * Save a new user-generated category via AJAX
     547                 */
     548                function saveNewCategory() {
     549                        var data = {
     550                                action: 'press-this-add-category',
     551                                post_id: $( '#post_ID' ).val() || 0,
     552                                name: $( '#new-category' ).val() || '',
     553                                new_cat_nonce: $( '#_ajax_nonce-add-category' ).val() || '',
     554                                parent: $( '#new-category-parent' ).val() || 0
     555                        };
     556
     557                        $.post( window.ajaxurl, data, function( response ) {
     558                                if ( ! response.success ) {
     559                                        renderError( response.data.errorMessage );
     560                                } else {
     561                                        // TODO: change if/when the html changes.
     562                                        var $parent, $ul,
     563                                                $wrap = $( 'ul.categories-select' );
     564
     565                                        $.each( response.data, function( i, newCat ) {
     566                                                var $node = $( '<li>' ).attr( 'id', 'category-' + newCat.term_id )
     567                                                        .append( $( '<label class="selectit">' ).text( newCat.name )
     568                                                                .append( $( '<input type="checkbox" name="post_category[]" checked>' ).attr( 'value', newCat.term_id ) ) );
     569
     570                                                if ( newCat.parent ) {
     571                                                        if ( ! $ul || ! $ul.length ) {
     572                                                                $parent = $wrap.find( '#category-' + newCat.parent );
     573                                                                $ul = $parent.find( 'ul.children:first' );
     574
     575                                                                if ( ! $ul.length ) {
     576                                                                        $ul = $( '<ul class="children">' ).appendTo( $parent );
     577                                                                }
     578                                                        }
     579
     580                                                        $ul.append( $node );
     581                                                        // TODO: set focus on
     582                                                } else {
     583                                                        $wrap.prepend( $node );
     584                                                }
     585                                        } );
     586
     587                                        refreshCatsCache();
     588                                }
     589                        } );
     590                }
     591
     592                /* ***************************************************************
     593                 * RENDERING FUNCTIONS
     594                 *************************************************************** */
     595
     596                /**
     597                 * Hide the form letting users enter a URL to be scanned, if a URL was already passed.
     598                 */
     599                function renderToolsVisibility() {
     600                        if ( data.u && data.u.match( /^https?:/ ) ) {
     601                                $( '#scanbar' ).hide();
     602                        }
     603                }
     604
     605                /**
     606                 * Render error notice
     607                 *
     608                 * @param msg string Notice/error message
     609                 * @param error string error|notice CSS class for display
     610                 */
     611                function renderNotice( msg, error ) {
     612                        var $alerts = $( '.editor-wrapper div.alerts' ),
     613                                className = error ? 'is-error' : 'is-notice';
     614
     615                        $alerts.append( $( '<p class="' + className + '">' ).text( msg ) );
     616                }
     617
     618                /**
     619                 * Render error notice
     620                 *
     621                 * @param msg string Error message
     622                 */
     623                function renderError( msg ) {
     624                        renderNotice( msg, true );
     625                }
     626
     627                /**
     628                 * Render notices on page load, if any already
     629                 */
     630                function renderStartupNotices() {
     631                        // Render errors sent in the data, if any
     632                        if ( data.errors && data.errors.length ) {
     633                                $.each( data.errors, function( i, msg ) {
     634                                        renderError( msg );
     635                                } );
     636                        }
     637
     638                        // Prompt user to upgrade their bookmarklet if there is a version mismatch.
     639                        if ( data.v && data._version && data.v !== data._version ) {
     640                                $( '.should-upgrade-bookmarklet' ).removeClass( 'is-hidden' );
     641                        }
     642                }
     643
     644                /**
     645                 * Render the suggested title, if any
     646                 */
     647                function renderSuggestedTitle() {
     648                        var suggestedTitle = suggestedTitleStr || '',
     649                                $title = $( '#title-container' );
     650
     651                        if ( ! hasEmptyTitleStr ) {
     652                                $( '#title-field' ).val( suggestedTitle );
     653                                $title.text( suggestedTitle );
     654                                $( '.post-title-placeholder' ).addClass( 'is-hidden' );
     655                        }
     656
     657                        $title.on( 'keyup', function() {
     658                                saveAlert = true;
     659                        }).on( 'paste', function() {
     660                                saveAlert = true;
     661
     662                                setTimeout( function() {
     663                                        $title.text( $title.text() );
     664                                }, 100 );
     665                        } );
     666
     667                }
     668
     669                /**
     670                 * Render the suggested content, if any
     671                 */
     672                function renderSuggestedContent() {
     673                        if ( ! suggestedContentStr || ! suggestedContentStr.length ) {
     674                                return;
     675                        }
     676
     677                        if ( ! editor ) {
     678                                editor = window.tinymce.get( 'pressthis' );
     679                        }
     680
     681                        if ( editor ) {
     682                                editor.setContent( suggestedContentStr );
     683                                editor.on( 'focus', function() {
     684                                        hasSetFocus = true;
     685                                } );
     686                        }
     687
     688                }
     689
     690                /**
     691                 * Render the detected images and embed for selection, if any
     692                 */
     693                function renderDetectedMedia() {
     694                        var mediaContainer = $( '#featured-media-container'),
     695                                listContainer  = $( '#all-media-container' ),
     696                                found          = 0;
     697
     698                        listContainer.empty();
     699
     700                        if ( ( interestingEmbeds && interestingEmbeds.length ) || ( interestingImages && interestingImages.length ) ) {
     701                                listContainer.append( '<h2 class="screen-reader-text">' + __( 'allMediaHeading' ) + '</h2><ul class="wppt-all-media-list"/>' );
     702                        }
     703
     704                        if ( interestingEmbeds && interestingEmbeds.length ) {
     705                                $.each( interestingEmbeds, function ( i, src ) {
     706                                        src = checkUrl( src );
     707
     708                                        if ( ! isEmbeddable( src ) ) {
     709                                                return;
     710                                        }
     711
     712                                        var displaySrc = '',
     713                                                cssClass   = 'suggested-media-thumbnail suggested-media-embed';
     714
     715                                        if ( src.indexOf( 'youtube.com/' ) > -1 ) {
     716                                                displaySrc = 'https://i.ytimg.com/vi/' + src.replace( /.+v=([^&]+).*/, '$1' ) + '/hqdefault.jpg';
     717                                                cssClass += ' is-video';
     718                                        } else if ( src.indexOf( 'youtu.be/' ) > -1 ) {
     719                                                displaySrc = 'https://i.ytimg.com/vi/' + src.replace( /\/([^\/])$/, '$1' ) + '/hqdefault.jpg';
     720                                                cssClass += ' is-video';
     721                                        } else if ( src.indexOf( 'dailymotion.com' ) > -1 ) {
     722                                                displaySrc = src.replace( '/video/', '/thumbnail/video/' );
     723                                                cssClass += ' is-video';
     724                                        } else if ( src.indexOf( 'soundcloud.com' ) > -1 ) {
     725                                                cssClass += ' is-audio';
     726                                        } else if ( src.indexOf( 'twitter.com' ) > -1 ) {
     727                                                cssClass += ' is-tweet';
     728                                        } else {
     729                                                cssClass += ' is-video';
     730                                        }
     731
     732                                        $( '<li></li>', {
     733                                                'id': 'embed-' + i + '-container',
     734                                                'class': cssClass,
     735                                                'tabindex': '0'
     736                                        } ).css( {
     737                                                'background-image': ( displaySrc.length ) ? 'url(' + displaySrc + ')' : null
     738                                        } ).html(
     739                                                '<span class="screen-reader-text">' + __( 'suggestedEmbedAlt' ).replace( '%d', i + 1 ) + '</span>'
     740                                        ).on( 'click keypress', function ( e ) {
     741                                                if ( e.type === 'click' || e.which === 13 ) {
     742                                                        insertSelectedMedia( 'embed',src );
     743                                                }
     744                                        } ).appendTo( '.wppt-all-media-list', listContainer );
     745
     746                                        found++;
     747                                } );
     748                        }
     749
     750                        if ( interestingImages && interestingImages.length ) {
     751                                $.each( interestingImages, function ( i, src ) {
     752                                        src = checkUrl( src );
     753
     754                                        var displaySrc = src.replace(/^(http[^\?]+)(\?.*)?$/, '$1');
     755                                        if ( src.indexOf( 'files.wordpress.com/' ) > -1 ) {
     756                                                displaySrc = displaySrc.replace(/\?.*$/, '') + '?w=' + smallestWidth;
     757                                        } else if ( src.indexOf( 'gravatar.com/' ) > -1 ) {
     758                                                displaySrc = displaySrc.replace( /\?.*$/, '' ) + '?s=' + smallestWidth;
     759                                        } else {
     760                                                displaySrc = src;
     761                                        }
     762
     763                                        $( '<li></li>', {
     764                                                'id': 'img-' + i + '-container',
     765                                                'class': 'suggested-media-thumbnail is-image',
     766                                                'tabindex': '0'
     767                                        } ).css( {
     768                                                'background-image': 'url(' + displaySrc + ')'
     769                                        } ).html(
     770                                                '<span class="screen-reader-text">' +__( 'suggestedImgAlt' ).replace( '%d', i + 1 ) + '</span>'
     771                                        ).on( 'click keypress', function ( e ) {
     772                                                if ( e.type === 'click' || e.which === 13 ) {
     773                                                        insertSelectedMedia( 'img', src, data.u );
     774                                                }
     775                                        } ).appendTo( '.wppt-all-media-list', listContainer );
     776
     777                                        found++;
     778                                } );
     779                        }
     780
     781                        if ( ! found ) {
     782                                mediaContainer.removeClass( 'all-media-visible' ).addClass( 'no-media');
     783                                return;
     784                        }
     785
     786                        mediaContainer.removeClass( 'no-media' ).addClass( 'all-media-visible' );
     787                }
     788
     789                /* ***************************************************************
     790                 * MONITORING FUNCTIONS
     791                 *************************************************************** */
     792
     793                /**
     794                 * Interactive navigation behavior for the options modal (post format, tags, categories)
     795                 */
     796                function monitorOptionsModal() {
     797                        var isOffScreen   = 'is-off-screen',
     798                                isHidden      = 'is-hidden',
     799                                $postOptions  = $( '.post-options' ),
     800                                $postOption   = $( '.post-option' ),
     801                                $settingModal = $( '.setting-modal' ),
     802                                $modalClose   = $( '.modal-close' );
     803
     804                        $postOption.on( 'click', function( event ) {
     805                                var index = $( this ).index(),
     806                                        $targetSettingModal = $settingModal.eq( index );
     807
     808                                event.preventDefault();
     809
     810                                $postOptions
     811                                        .addClass( isOffScreen )
     812                                        .one( transitionEndEvent, function() {
     813                                                $( this ).addClass( isHidden );
     814                                        } );
     815
     816                                $targetSettingModal
     817                                        .removeClass( isOffScreen + ' ' + isHidden )
     818                                        .one( transitionEndEvent, function() {
     819                                                $( this ).find( $modalClose ).focus();
     820                                        } );
     821                        } );
     822
     823                        $modalClose.on( 'click', function( event ) {
     824                                var $targetSettingModal = $( this ).parent(),
     825                                        index = $targetSettingModal.index();
     826
     827                                event.preventDefault();
     828
     829                                $postOptions
     830                                        .removeClass( isOffScreen + ' ' + isHidden );
     831
     832                                $targetSettingModal
     833                                        .addClass( isOffScreen )
     834                                        .one( transitionEndEvent, function() {
     835                                                $( this ).addClass( isHidden );
     836                                        } );
     837
     838                                // For browser that don't support transitionend.
     839                                if ( ! transitionEndEvent ) {
     840                                        setTimeout( function() {
     841                                                $targetSettingModal.addClass( isHidden );
     842                                        }, 350 );
     843                                }
     844
     845                                $postOption.eq( index - 1 ).focus();
     846                        } );
     847                }
     848
     849                /**
     850                 * Interactive behavior for the sidebar toggle, to show the options modals
     851                 */
     852                function monitorSidebarToggle() {
     853                        var $optOpen  = $( '.options-open' ),
     854                                $optClose = $( '.options-close' ),
     855                                $postOption = $( '.post-option' ),
     856                                $sidebar = $( '.options-panel' ),
     857                                $postActions = $( '.press-this-actions' ),
     858                                $scanbar = $( '#scanbar' ),
     859                                isOffScreen = 'is-off-screen',
     860                                isHidden = 'is-hidden',
     861                                ifOffHidden = isOffScreen + ' ' + isHidden;
     862
     863                        $optOpen.on( 'click', function(){
     864                                $optOpen.addClass( isHidden );
     865                                $optClose.removeClass( isHidden );
     866                                $postActions.addClass( isHidden );
     867                                $scanbar.addClass( isHidden );
     868
     869                                $sidebar
     870                                        .removeClass( ifOffHidden )
     871                                        .one( 'transitionend', function() {
     872                                                $postOption.eq( 0 ).focus();
     873                                        } );
     874                        } );
     875
     876                        $optClose.on( 'click', function(){
     877                                $optClose.addClass( isHidden );
     878                                $optOpen.removeClass( isHidden );
     879                                $postActions.removeClass( isHidden );
     880                                $scanbar.removeClass( isHidden );
     881
     882                                $sidebar
     883                                        .addClass( isOffScreen )
     884                                        .one( 'transitionend', function() {
     885                                                $( this ).addClass( isHidden );
     886                                                // Reset to options list
     887                                                $( '.post-options' ).removeClass( ifOffHidden );
     888                                                $( '.setting-modal').addClass( ifOffHidden );
     889                                        } );
     890                        } );
     891                }
     892
     893                /**
     894                 * Interactive behavior for the post title's field placeholder
     895                 */
     896                function monitorPlaceholder() {
     897                        var $selector = $( '#title-container'),
     898                                $placeholder = $('.post-title-placeholder');
     899
     900                        $selector.on( 'focus', function() {
     901                                $placeholder.addClass('is-hidden');
     902                        } );
     903
     904                        $selector.on( 'blur', function() {
     905                                var textLength = $( this ).text().length;
     906
     907                                if ( ! textLength ) {
     908                                        $placeholder.removeClass('is-hidden');
     909                                }
     910                        } );
     911                }
     912
     913                /* ***************************************************************
     914                 * PROCESSING FUNCTIONS
     915                 *************************************************************** */
     916
     917                /**
     918                 * Calls all the rendring related functions to happen on page load
     919                 */
     920                function render(){
     921                        // We're on!
     922                        renderToolsVisibility();
     923                        renderSuggestedTitle();
     924                        renderDetectedMedia();
     925                        $( document ).on( 'tinymce-editor-init', renderSuggestedContent );
     926                        renderStartupNotices();
     927                }
     928
     929                /**
     930                 * Set app events and other state monitoring related code.
     931                 */
     932                function monitor(){
     933                        $( '#current-site a').click( function( e ) {
     934                                e.preventDefault();
     935                        } );
     936
     937                        // Publish and Draft buttons and submit
     938
     939                        $( '#draft-field' ).on( 'click', function() {
     940                                submitPost( 'draft' );
     941                        } );
     942
     943                        $( '#publish-field' ).on( 'click', function() {
     944                                submitPost( 'publish' );
     945                        } );
     946
     947                        monitorOptionsModal();
     948                        monitorSidebarToggle();
     949                        monitorPlaceholder();
     950
     951                        $( '#post-formats-select input' ).on( 'change', function() {
     952                                var $this = $( this );
     953
     954                                if ( $this.is( ':checked' ) ) {
     955                                        setPostFormatString( $this.attr( 'id' ).replace( /^post-format-(.+)$/, '$1' ) );
     956                                }
     957                        } );
     958
     959                        // Needs more work, doesn't detect when the other JS changes the value of #tax-input-post_tag
     960                        $( '#tax-input-post_tag' ).on( 'change', function() {
     961                                var val =  $( this ).val();
     962                                $( '#post-option-tags' ).text( ( val.length ) ? val.replace( /,([^\s])/g, ', $1' ) : '' );
     963                        } );
     964
     965                        $( window ).on( 'beforeunload.press-this', function() {
     966                                if ( saveAlert || ( editor && editor.isDirty() ) ) {
     967                                        return __( 'saveAlert' );
     968                                }
     969                        } );
     970
     971                        $( 'button.add-cat-toggle' ).on( 'click.press-this', function() {
     972                                $( this ).toggleClass( 'is-toggled' );
     973                                $( '.setting-modal .add-category' ).toggleClass( 'is-hidden' );
     974                                $( '.categories-search-wrapper' ).toggleClass( 'is-hidden' );
     975                        } );
     976
     977                        $( 'button.add-cat-submit' ).on( 'click.press-this', saveNewCategory );
     978
     979                        $( '.categories-search' ).on( 'keyup', function() {
     980                                var search = $( this ).val().toLowerCase() || '';
     981
     982                                // Don't search when less thasn 3 extended ASCII chars
     983                                if ( /[\x20-\xFF]+/.test( search ) && search.length < 2 ) {
     984                                        return;
     985                                }
     986
     987                                $.each( catsCache, function( i, cat ) {
     988                                        cat.node.removeClass( 'is-hidden searched-parent' );
     989                                } );
     990
     991                                if ( search ) {
     992                                        $.each( catsCache, function( i, cat ) {
     993                                                if ( cat.text.indexOf( search ) === -1 ) {
     994                                                        cat.node.addClass( 'is-hidden' );
     995                                                } else {
     996                                                        cat.parents.addClass( 'searched-parent' );
     997                                                }
     998                                        } );
     999                                }
     1000                        } );
     1001
     1002                        return true;
     1003                }
     1004
     1005                function refreshCatsCache() {
     1006                        $( '.categories-select' ).find( 'li' ).each( function() {
     1007                                var $this = $( this );
     1008
     1009                                catsCache.push( {
     1010                                        node: $this,
     1011                                        parents: $this.parents( 'li' ),
     1012                                        text: $this.children( 'label' ).text().toLowerCase()
     1013                                } );
     1014                        } );
     1015                }
     1016
     1017                // Let's go!
     1018                $( document ).ready( function() {
     1019                        render();
     1020                        monitor();
     1021                        refreshCatsCache();
     1022                });
     1023
     1024                // Expose public methods
     1025                // TODO: which are needed?
     1026                return {
     1027                        renderNotice: renderNotice,
     1028                        renderError: renderError
     1029                };
     1030        }
     1031
     1032        window.wp = window.wp || {};
     1033        window.wp.pressThis = new PressThis();
     1034
     1035}( jQuery, window ));
     1036/**
     1037 * PressThis App
     1038 *
     1039 */
     1040( function( $, window ) {
     1041        var PressThis = function() {
     1042                var editor,
     1043                        saveAlert             = false,
     1044                        $div                  = $( '<div>' ),
     1045                        siteConfig            = window.wpPressThisConfig || {},
     1046                        data                  = window.wpPressThisData || {},
     1047                        smallestWidth         = 128,
     1048                        interestingImages         = getInterestingImages( data ) || [],
     1049                        interestingEmbeds         = getInterestingEmbeds( data ) || [],
     1050                        hasEmptyTitleStr      = false,
     1051                        suggestedTitleStr     = getSuggestedTitle( data ),
     1052                        suggestedContentStr   = getSuggestedContent( data ),
     1053                        hasSetFocus           = false,
     1054                        catsCache             = [],
     1055                        transitionEndEvent    = ( function() {
     1056                                var style = document.documentElement.style;
     1057
     1058                                if ( typeof style.transition !== 'undefined' ) {
     1059                                        return 'transitionend';
     1060                                }
     1061
     1062                                if ( typeof style.WebkitTransition !== 'undefined' ) {
     1063                                        return 'webkitTransitionEnd';
     1064                                }
     1065
     1066                                return false;
     1067                        }() );
     1068
     1069                /* ***************************************************************
     1070                 * HELPER FUNCTIONS
     1071                 *************************************************************** */
     1072
     1073                /**
     1074                 * Emulates our PHP __() gettext function, powered by the strings exported in pressThisL10n.
     1075                 *
     1076                 * @param key string Key of the string to be translated, as found in pressThisL10n.
     1077                 * @returns string Original or translated string, or empty string if no key.
     1078                 */
     1079                function __( key ) {
     1080                        if ( key && window.pressThisL10n ) {
     1081                                return window.pressThisL10n[key] || key;
     1082                        }
     1083
     1084                        return key || '';
     1085                }
     1086
     1087                /**
     1088                 * Strips HTML tags
     1089                 *
     1090                 * @param string string Text to have the HTML tags striped out of.
     1091                 * @returns string Stripped text.
     1092                 */
     1093                function stripTags( string ) {
     1094                        string = string || '';
     1095
     1096                        return string
     1097                                .replace( /<!--[\s\S]*?(-->|$)/g, '' )
     1098                                .replace( /<(script|style)[^>]*>[\s\S]*?(<\/\1>|$)/ig, '' )
     1099                                .replace( /<\/?[a-z][^>]*>/ig, '' );
     1100                }
     1101
     1102                // TODO: needed?
     1103                function entityEncode( text ) {
     1104                        return $div.text( text ).html();
     1105                }
     1106
     1107                /**
     1108                 * Strip HTML tags and entity encode some of the HTML special chars.
     1109                 *
     1110                 * @param text string Text.
     1111                 * @returns string Sanitized text.
     1112                 */
     1113                function sanitizeText( text ) {
     1114                        text = stripTags( text );
     1115
     1116                        return text
     1117                                .replace( /\\/, '' )
     1118                                .replace( /</g, '&lt;' )
     1119                                .replace( />/g, '&gt;' )
     1120                                .replace( /"/g, '&quot;' )
     1121                                .replace( /'/g, '&#039;' );
     1122                }
     1123
     1124                /**
     1125                 * Allow only HTTP or protocol relative URLs.
     1126                 *
     1127                 * @param url string The URL.
     1128                 * @returns string Processed URL.
     1129                 */
     1130                function checkUrl( url ) {
     1131                        url = $.trim( url || '' );
     1132
     1133                        if ( /^(?:https?:)?\/\//.test( url ) ) {
     1134                                url = stripTags( url );
     1135                                return url.replace( /["\\]+/g, '' );
     1136                        }
     1137
     1138                        return '';
     1139                }
     1140
     1141                /**
     1142                 * Gets the source page's canonical link, based on passed location and meta data.
     1143                 *
     1144                 * @param data object Usually WpPressThis_App.data
     1145                 * @returns string Discovered canonical URL, or empty
     1146                 */
     1147                function getCanonicalLink( data ) {
     1148                        if ( ! data || data.length ) {
     1149                                return '';
     1150                        }
     1151
     1152                        var link = '';
     1153
     1154                        if ( data._links ) {
     1155                                if ( data._links.canonical && data._links.canonical.length ) {
     1156                                        link = data._links.canonical;
     1157                                }
     1158                        }
     1159
     1160                        if ( ! link.length && data.u ) {
     1161                                link = data.u;
     1162                        }
     1163
     1164                        if ( ! link.length && data._meta ) {
     1165                                if ( data._meta['twitter:url'] && data._meta['twitter:url'].length ) {
     1166                                        link = data._meta['twitter:url'];
     1167                                } else if ( data._meta['og:url'] && data._meta['og:url'].length ) {
     1168                                        link = data._meta['og:url'];
     1169                                }
     1170                        }
     1171
     1172                        return decodeURI( link );
     1173                }
     1174
     1175                /**
     1176                 * Gets the source page's site name, based on passed meta data.
     1177                 *
     1178                 * @param data object Usually WpPressThis_App.data
     1179                 * @returns string Discovered site name, or empty
     1180                 */
     1181                function getSourceSiteName( data ) {
     1182                        if ( ! data || data.length ) {
     1183                                return '';
     1184                        }
     1185
     1186                        var name='';
     1187
     1188                        if ( data._meta ) {
     1189                                if ( data._meta['og:site_name'] && data._meta['og:site_name'].length ) {
     1190                                        name = data._meta['og:site_name'];
     1191                                } else if ( data._meta['application-name'] && data._meta['application-name'].length ) {
     1192                                        name = data._meta['application-name'];
     1193                                }
     1194                        }
     1195
     1196                        return name.replace( /\\/g, '' );
     1197                }
     1198
     1199                /**
     1200                 * Gets the source page's title, based on passed title and meta data.
     1201                 *
     1202                 * @param data object Usually WpPressThis_App.data
     1203                 * @returns string Discovered page title, or empty
     1204                 */
     1205                function getSuggestedTitle( data ) {
     1206                        if ( ! data || data.length ) {
     1207                                return __( 'newPost' );
     1208                        }
     1209
     1210                        var title = '';
     1211
     1212                        if ( data.t ) {
     1213                                title = data.t;
     1214                        }
     1215
     1216                        if ( ! title.length && data._meta ) {
     1217                                if ( data._meta['twitter:title'] && data._meta['twitter:title'].length ) {
     1218                                        title = data._meta['twitter:title'];
     1219                                } else if ( data._meta['og:title'] && data._meta['og:title'].length ) {
     1220                                        title = data._meta['og:title'];
     1221                                } else if ( data._meta.title && data._meta.title.length ) {
     1222                                        title = data._meta.title;
     1223                                }
     1224                        }
     1225
     1226                        if ( ! title.length ) {
     1227                                title = __( 'newPost' );
     1228                                hasEmptyTitleStr = true;
     1229                        }
     1230
     1231                        return title.replace( /\\/g, '' );
     1232                }
     1233
     1234                /**
     1235                 * Gets the source page's suggested content, based on passed data (description, selection, etc).
     1236                 * Features a blockquoted excerpt, as well as content attribution, if any.
     1237                 *
     1238                 * @param data object Usually WpPressThis_App.data
     1239                 * @returns string Discovered content, or empty
     1240                 */
     1241                function getSuggestedContent( data ) {
     1242                        if ( ! data || data.length ) {
     1243                                return '';
     1244                        }
     1245
     1246                        var content   = '',
     1247                                title     = getSuggestedTitle( data ),
     1248                                url       = getCanonicalLink( data ),
     1249                                siteName  = getSourceSiteName( data );
     1250
     1251                        if ( data.s && data.s.length ) {
     1252                                content = data.s;
     1253                        } else if ( data._meta ) {
     1254                                if ( data._meta['twitter:description'] && data._meta['twitter:description'].length ) {
     1255                                        content = data._meta['twitter:description'];
     1256                                } else if ( data._meta['og:description'] && data._meta['og:description'].length ) {
     1257                                        content = data._meta['og:description'];
     1258                                } else if ( data._meta.description && data._meta.description.length ) {
     1259                                        content = data._meta.description;
     1260                                }
     1261                        }
     1262
     1263                        // Wrap suggested content in blockquote tag, if we have any.
     1264                        content = ( content.length ? '<blockquote class="press-this-suggested-content">' + sanitizeText( content ) + '</blockquote>' : '' );
     1265
     1266                        // Add a source attribution if there is one available.
     1267                        if ( ( ( title.length && __( 'newPost' ) !== title ) || siteName.length ) && url.length ) {
     1268                                content += '<p class="press-this-suggested-source">';
     1269                                content += __( 'source' );
     1270                                content += ' <cite>';
     1271                                content += __( 'sourceLink').replace( '%1$s', encodeURI( url ) ).replace( '%2$s', sanitizeText( title || siteName ) );
     1272                                content += '</cite></p>';
     1273                        }
     1274
     1275                        if ( ! content ) {
     1276                                content = '';
     1277                        }
     1278
     1279                        return content.replace( /\\/g, '' );
     1280                }
     1281
     1282                /**
     1283                 * Tests if what was passed as an embed URL is deemed to be embeddable in the editor.
     1284                 *
     1285                 * @param url string Passed URl, usually from WpPressThis_App.data._embed
     1286                 * @returns boolean
     1287                 */
     1288                function isEmbeddable( url ) {
     1289                        if ( ! url ) {
     1290                                return false;
     1291                        } else if ( url.match( /\/\/(m\.|www\.)?youtube\.com\/watch\?/ ) || url.match( /\/youtu\.be\/.+$/ ) ) {
     1292                                return true;
     1293                        } else if ( url.match( /\/\/vimeo\.com\/(.+\/)?[\d]+$/ ) ) {
     1294                                return true;
     1295                        } else if ( url.match( /\/\/(www\.)?dailymotion\.com\/video\/.+$/ ) ) {
     1296                                return true;
     1297                        } else if ( url.match( /\/\/soundcloud\.com\/.+$/ ) ) {
     1298                                return true;
     1299                        } else if ( url.match( /\/\/twitter\.com\/[^\/]+\/status\/[\d]+$/ ) ) {
     1300                                return true;
     1301                        } else if ( url.match( /\/\/vine\.co\/v\/[^\/]+/ ) ) {
     1302                                return true;
     1303                        }
     1304
     1305                        return false;
     1306                }
     1307
     1308                /**
     1309                 * Tests if what was passed as an image URL is deemed to be interesting enough to offer to the user for selection.
     1310                 *
     1311                 * @param src string Passed URl, usually from WpPressThis_App.data._ing
     1312                 * @returns boolean Test for false
     1313                 */
     1314                function isSrcUninterestingPath( src ) {
     1315                        if ( src.match( /\/ad[sx]{1}?\// ) ) {
     1316                                // Ads
     1317                                return true;
     1318                        } else if ( src.match( /(\/share-?this[^\.]+?\.[a-z0-9]{3,4})(\?.*)?$/ ) ) {
     1319                                // Share-this type button
     1320                                return true;
     1321                        } else if ( src.match( /\/(spinner|loading|spacer|blank|rss)\.(gif|jpg|png)/ ) ) {
     1322                                // Loaders, spinners, spacers
     1323                                return true;
     1324                        } else if ( src.match( /\/([^\.\/]+[-_]{1})?(spinner|loading|spacer|blank)s?([-_]{1}[^\.\/]+)?\.[a-z0-9]{3,4}/ ) ) {
     1325                                // Fancy loaders, spinners, spacers
     1326                                return true;
     1327                        } else if ( src.match( /([^\.\/]+[-_]{1})?thumb[^.]*\.(gif|jpg|png)$/ ) ) {
     1328                                // Thumbnails, too small, usually irrelevant to context
     1329                                return true;
     1330                        } else if ( src.match( /\/wp-includes\// ) ) {
     1331                                // Classic WP interface images
     1332                                return true;
     1333                        } else if ( src.match( /[^\d]{1}\d{1,2}x\d+\.(gif|jpg|png)$/ ) ) {
     1334                                // Most often tiny buttons/thumbs (< 100px wide)
     1335                                return true;
     1336                        } else if ( src.indexOf( '/g.gif' ) > -1 ) {
     1337                                // Classic WP stats gif
     1338                                return true;
     1339                        } else if ( src.indexOf( '/pixel.mathtag.com' ) > -1 ) {
     1340                                // See mathtag.com
     1341                                return true;
     1342                        }
     1343                        return false;
     1344                }
     1345
     1346                /**
     1347                 * Get a list of valid embeds from what was passed via WpPressThis_App.data._embed on page load.
     1348                 *
     1349                 * @returns array
     1350                 */
     1351                function getInterestingEmbeds() {
     1352                        var embeds             = data._embed || [],
     1353                                interestingEmbeds  = [],
     1354                                alreadySelected    = [];
     1355
     1356                        if ( embeds.length ) {
     1357                                $.each( embeds, function ( i, src ) {
     1358                                        if ( !src || !src.length ) {
     1359                                                // Skip: no src value
     1360                                                return;
     1361                                        } else if ( !isEmbeddable( src ) ) {
     1362                                                // Skip: not deemed embeddable
     1363                                                return;
     1364                                        }
     1365
     1366                                        var schemelessSrc = src.replace( /^https?:/, '' );
     1367
     1368                                        if ( $.inArray( schemelessSrc, alreadySelected ) > -1 ) {
     1369                                                // Skip: already shown
     1370                                                return;
     1371                                        }
     1372
     1373                                        interestingEmbeds.push( src );
     1374                                        alreadySelected.push( schemelessSrc );
     1375                                } );
     1376                        }
     1377
     1378                        return interestingEmbeds;
     1379                }
     1380
     1381                /**
     1382                 * Get what is likely the most valuable image from what was passed via WpPressThis_App.data._img and WpPressThis_App.data._meta on page load.
     1383                 *
     1384                 * @returns array
     1385                 */
     1386                function getFeaturedImage( data ) {
     1387                        var featured = '';
     1388
     1389                        if ( ! data || ! data._meta ) {
     1390                                return '';
     1391                        }
     1392
     1393                        if ( data._meta['twitter:image0:src'] && data._meta['twitter:image0:src'].length ) {
     1394                                featured = data._meta['twitter:image0:src'];
     1395                        } else if ( data._meta['twitter:image0'] && data._meta['twitter:image0'].length ) {
     1396                                featured = data._meta['twitter:image0'];
     1397                        } else if ( data._meta['twitter:image:src'] && data._meta['twitter:image:src'].length ) {
     1398                                featured = data._meta['twitter:image:src'];
     1399                        } else if ( data._meta['twitter:image'] && data._meta['twitter:image'].length ) {
     1400                                featured = data._meta['twitter:image'];
     1401                        } else if ( data._meta['og:image'] && data._meta['og:image'].length ) {
     1402                                featured = data._meta['og:image'];
     1403                        } else if ( data._meta['og:image:secure_url'] && data._meta['og:image:secure_url'].length ) {
     1404                                featured = data._meta['og:image:secure_url'];
     1405                        }
     1406
     1407                        featured = checkUrl( featured );
     1408
     1409                        return ( isSrcUninterestingPath( featured ) ) ? '' : featured;
     1410                }
     1411
     1412                /**
     1413                 * Get a list of valid images from what was passed via WpPressThis_App.data._img and WpPressThis_App.data._meta on page load.
     1414                 *
     1415                 * @returns array
     1416                 */
     1417                function getInterestingImages( data ) {
     1418                        var imgs             = data._img || [],
     1419                                featuredPict     = getFeaturedImage( data ) || '',
     1420                                interestingImgs  = [],
     1421                                alreadySelected  = [];
     1422
     1423                        if ( featuredPict.length ) {
     1424                                interestingImgs.push( featuredPict );
     1425                                alreadySelected.push( featuredPict.replace(/^https?:/, '') );
     1426                        }
     1427
     1428                        if ( imgs.length ) {
     1429                                $.each( imgs, function ( i, src ) {
     1430                                        src = src.replace( /http:\/\/[\d]+\.gravatar\.com\//, 'https://secure.gravatar.com/' );
     1431                                        src = checkUrl( src );
     1432
     1433                                        if ( ! src || ! src.length ) {
     1434                                                // Skip: no src value
     1435                                                return;
     1436                                        }
     1437
     1438                                        var schemelessSrc = src.replace( /^https?:/, '' );
     1439
     1440                                        if ( Array.prototype.indexOf && alreadySelected.indexOf( schemelessSrc ) > -1 ) {
     1441                                                // Skip: already shown
     1442                                                return;
     1443                                        } else if ( isSrcUninterestingPath( src ) ) {
     1444                                                // Skip: spinner, stat, ad, or spacer pict
     1445                                                return;
     1446                                        } else if ( src.indexOf( 'avatar' ) > -1 && interestingImgs.length >= 15 ) {
     1447                                                // Skip:  some type of avatar and we've already gathered more than 23 diff images to show
     1448                                                return;
     1449                                        }
     1450
     1451                                        interestingImgs.push( src );
     1452                                        alreadySelected.push( schemelessSrc );
     1453                                } );
     1454                        }
     1455
     1456                        return interestingImgs;
     1457                }
     1458
     1459                /**
     1460                 * Show UX spinner
     1461                 */
     1462                function showSpinner() {
     1463                        $( '#spinner' ).addClass( 'show' );
     1464                        $( '.post-actions button' ).each( function() {
     1465                                $( this ).attr( 'disabled', 'disabled' );
     1466                        } );
     1467                }
     1468
     1469                /**
     1470                 * Hide UX spinner
     1471                 */
     1472                function hideSpinner() {
     1473                        $( '#spinner' ).removeClass( 'show' );
     1474                        $( '.post-actions button' ).each( function() {
     1475                                $( this ).removeAttr( 'disabled' );
     1476                        } );
     1477                }
     1478
     1479                /**
     1480                 * Submit the post form via AJAX, and redirect to the proper screen if published vs saved as a draft.
     1481                 *
     1482                 * @param action string publish|draft
     1483                 */
     1484                function submitPost( action ) {
     1485                        saveAlert = false;
     1486                        showSpinner();
     1487
     1488                        var $form = $( '#pressthis-form' );
     1489
     1490                        if ( 'publish' === action ) {
     1491                                $( '#post_status' ).val( 'publish' );
     1492                        }
     1493
     1494                        editor && editor.save();
     1495
     1496                        $( '#title-field' ).val( sanitizeText( $( '#title-container' ).text() ) );
     1497
     1498                        // Make sure to flush out the tags with tagBox before saving
     1499                        if ( window.tagBox ) {
     1500                                $( 'div.tagsdiv' ).each( function() {
     1501                                        window.tagBox.flushTags( this, false, 1 );
     1502                                } );
     1503                        }
     1504
     1505                        var data = $form.serialize();
     1506
     1507                        $.ajax( {
     1508                                type: 'post',
     1509                                url: window.ajaxurl,
     1510                                data: data,
     1511                                success: function( response ) {
     1512                                        if ( ! response.success ) {
     1513                                                renderError( response.data.errorMessage );
     1514                                                hideSpinner();
     1515                                        } else if ( response.data.redirect ) {
     1516                                                if ( window.opener && siteConfig.redir_in_parent ) {
     1517                                                        try {
     1518                                                                window.opener.location.href = response.data.redirect;
     1519                                                        } catch( er ) {}
     1520
     1521                                                        window.self.close();
     1522                                                } else {
     1523                                                        window.location.href = response.data.redirect;
     1524                                                }
     1525                                        }
     1526                                }
     1527                        } );
     1528                }
     1529
     1530                /**
     1531                 * Inserts the media a user has selected from the presented list inside the editor, as an image or embed, based on type
     1532                 *
     1533                 * @param type string img|embed
     1534                 * @param src string Source URL
     1535                 * @param link string Optional destination link, for images (defaults to src)
     1536                 */
     1537                function insertSelectedMedia( type, src, link ) {
     1538                        var newContent = '';
     1539
     1540                        if ( ! editor ) {
     1541                                return;
     1542                        }
     1543
     1544                        src = checkUrl( src );
     1545                        link = checkUrl( link );
     1546
     1547                        if ( 'img' === type ) {
     1548                                if ( ! link || ! link.length ) {
     1549                                        link = src;
     1550                                }
     1551
     1552                                newContent = '<a href="' + link + '"><img class="alignnone size-full" src="' + src + '" /></a>\n';
     1553                        } else {
     1554                                newContent = '[embed]' + src + '[/embed]\n';
     1555                        }
     1556
     1557                        if ( ! hasSetFocus ) {
     1558                                // Append to top of content on 1st media insert
     1559                                editor.setContent( newContent + editor.getContent() );
     1560                        } else {
     1561                                // Or add where the cursor was last positioned in TinyMCE
     1562                                editor.execCommand( 'mceInsertContent', false, newContent );
     1563                        }
     1564
     1565                        hasSetFocus = true;
     1566                }
     1567
     1568                /**
     1569                 * Adds the currently selected post format next to the option, in the options panel.
     1570                 *
     1571                 * @param format string Post format to be displayed
     1572                 */
     1573                function setPostFormatString( format ) {
     1574                        if ( ! format || ! siteConfig || ! siteConfig.post_formats || ! siteConfig.post_formats[ format ] ) {
     1575                                return;
     1576                        }
     1577                        $( '#post-option-post-format' ).text( siteConfig.post_formats[ format ] );
     1578                }
     1579
     1580                /**
     1581                 * Save a new user-generated category via AJAX
     1582                 */
     1583                function saveNewCategory() {
     1584                        var data = {
     1585                                action: 'press-this-add-category',
     1586                                post_id: $( '#post_ID' ).val() || 0,
     1587                                name: $( '#new-category' ).val() || '',
     1588                                new_cat_nonce: $( '#_ajax_nonce-add-category' ).val() || '',
     1589                                parent: $( '#new-category-parent' ).val() || 0
     1590                        };
     1591
     1592                        $.post( window.ajaxurl, data, function( response ) {
     1593                                if ( ! response.success ) {
     1594                                        renderError( response.data.errorMessage );
     1595                                } else {
     1596                                        // TODO: change if/when the html changes.
     1597                                        var $parent, $ul,
     1598                                                $wrap = $( 'ul.categories-select' );
     1599
     1600                                        $.each( response.data, function( i, newCat ) {
     1601                                                var $node = $( '<li>' ).attr( 'id', 'category-' + newCat.term_id )
     1602                                                        .append( $( '<label class="selectit">' ).text( newCat.name )
     1603                                                                .append( $( '<input type="checkbox" name="post_category[]" checked>' ).attr( 'value', newCat.term_id ) ) );
     1604
     1605                                                if ( newCat.parent ) {
     1606                                                        if ( ! $ul || ! $ul.length ) {
     1607                                                                $parent = $wrap.find( '#category-' + newCat.parent );
     1608                                                                $ul = $parent.find( 'ul.children:first' );
     1609
     1610                                                                if ( ! $ul.length ) {
     1611                                                                        $ul = $( '<ul class="children">' ).appendTo( $parent );
     1612                                                                }
     1613                                                        }
     1614
     1615                                                        $ul.append( $node );
     1616                                                        // TODO: set focus on
     1617                                                } else {
     1618                                                        $wrap.prepend( $node );
     1619                                                }
     1620                                        } );
     1621
     1622                                        refreshCatsCache();
     1623                                }
     1624                        } );
     1625                }
     1626
     1627                /* ***************************************************************
     1628                 * RENDERING FUNCTIONS
     1629                 *************************************************************** */
     1630
     1631                /**
     1632                 * Hide the form letting users enter a URL to be scanned, if a URL was already passed.
     1633                 */
     1634                function renderToolsVisibility() {
     1635                        if ( data.u && data.u.match( /^https?:/ ) ) {
     1636                                $( '#scanbar' ).hide();
     1637                        }
     1638                }
     1639
     1640                /**
     1641                 * Render error notice
     1642                 *
     1643                 * @param msg string Notice/error message
     1644                 * @param error string error|notice CSS class for display
     1645                 */
     1646                function renderNotice( msg, error ) {
     1647                        var $alerts = $( '.editor-wrapper div.alerts' ),
     1648                                className = error ? 'is-error' : 'is-notice';
     1649
     1650                        $alerts.append( $( '<p class="' + className + '">' ).text( msg ) );
     1651                }
     1652
     1653                /**
     1654                 * Render error notice
     1655                 *
     1656                 * @param msg string Error message
     1657                 */
     1658                function renderError( msg ) {
     1659                        renderNotice( msg, true );
     1660                }
     1661
     1662                /**
     1663                 * Render notices on page load, if any already
     1664                 */
     1665                function renderStartupNotices() {
     1666                        // Render errors sent in the data, if any
     1667                        if ( data.errors && data.errors.length ) {
     1668                                $.each( data.errors, function( i, msg ) {
     1669                                        renderError( msg );
     1670                                } );
     1671                        }
     1672
     1673                        // Prompt user to upgrade their bookmarklet if there is a version mismatch.
     1674                        if ( data.v && data._version && data.v !== data._version ) {
     1675                                $( '.should-upgrade-bookmarklet' ).removeClass( 'is-hidden' );
     1676                        }
     1677                }
     1678
     1679                /**
     1680                 * Render the suggested title, if any
     1681                 */
     1682                function renderSuggestedTitle() {
     1683                        var suggestedTitle = suggestedTitleStr || '',
     1684                                $title = $( '#title-container' );
     1685
     1686                        if ( ! hasEmptyTitleStr ) {
     1687                                $( '#title-field' ).val( suggestedTitle );
     1688                                $title.text( suggestedTitle );
     1689                                $( '.post-title-placeholder' ).addClass( 'is-hidden' );
     1690                        }
     1691
     1692                        $title.on( 'keyup', function() {
     1693                                saveAlert = true;
     1694                        }).on( 'paste', function() {
     1695                                saveAlert = true;
     1696
     1697                                setTimeout( function() {
     1698                                        $title.text( $title.text() );
     1699                                }, 100 );
     1700                        } );
     1701
     1702                }
     1703
     1704                /**
     1705                 * Render the suggested content, if any
     1706                 */
     1707                function renderSuggestedContent() {
     1708                        if ( ! suggestedContentStr || ! suggestedContentStr.length ) {
     1709                                return;
     1710                        }
     1711
     1712                        if ( ! editor ) {
     1713                                editor = window.tinymce.get( 'pressthis' );
     1714                        }
     1715
     1716                        if ( editor ) {
     1717                                editor.setContent( suggestedContentStr );
     1718                                editor.on( 'focus', function() {
     1719                                        hasSetFocus = true;
     1720                                } );
     1721                        }
     1722
     1723                }
     1724
     1725                /**
     1726                 * Render the detected images and embed for selection, if any
     1727                 */
     1728                function renderDetectedMedia() {
     1729                        var mediaContainer = $( '#featured-media-container'),
     1730                                listContainer  = $( '#all-media-container' ),
     1731                                found          = 0;
     1732
     1733                        listContainer.empty();
     1734
     1735                        if ( ( interestingEmbeds && interestingEmbeds.length ) || ( interestingImages && interestingImages.length ) ) {
     1736                                listContainer.append( '<h2 class="screen-reader-text">' + __( 'allMediaHeading' ) + '</h2><ul class="wppt-all-media-list"/>' );
     1737                        }
     1738
     1739                        if ( interestingEmbeds && interestingEmbeds.length ) {
     1740                                $.each( interestingEmbeds, function ( i, src ) {
     1741                                        src = checkUrl( src );
     1742
     1743                                        if ( ! isEmbeddable( src ) ) {
     1744                                                return;
     1745                                        }
     1746
     1747                                        var displaySrc = '',
     1748                                                cssClass   = 'suggested-media-thumbnail suggested-media-embed';
     1749
     1750                                        if ( src.indexOf( 'youtube.com/' ) > -1 ) {
     1751                                                displaySrc = 'https://i.ytimg.com/vi/' + src.replace( /.+v=([^&]+).*/, '$1' ) + '/hqdefault.jpg';
     1752                                                cssClass += ' is-video';
     1753                                        } else if ( src.indexOf( 'youtu.be/' ) > -1 ) {
     1754                                                displaySrc = 'https://i.ytimg.com/vi/' + src.replace( /\/([^\/])$/, '$1' ) + '/hqdefault.jpg';
     1755                                                cssClass += ' is-video';
     1756                                        } else if ( src.indexOf( 'dailymotion.com' ) > -1 ) {
     1757                                                displaySrc = src.replace( '/video/', '/thumbnail/video/' );
     1758                                                cssClass += ' is-video';
     1759                                        } else if ( src.indexOf( 'soundcloud.com' ) > -1 ) {
     1760                                                cssClass += ' is-audio';
     1761                                        } else if ( src.indexOf( 'twitter.com' ) > -1 ) {
     1762                                                cssClass += ' is-tweet';
     1763                                        } else {
     1764                                                cssClass += ' is-video';
     1765                                        }
     1766
     1767                                        $( '<li></li>', {
     1768                                                'id': 'embed-' + i + '-container',
     1769                                                'class': cssClass,
     1770                                                'tabindex': '0'
     1771                                        } ).css( {
     1772                                                'background-image': ( displaySrc.length ) ? 'url(' + displaySrc + ')' : null
     1773                                        } ).html(
     1774                                                '<span class="screen-reader-text">' + __( 'suggestedEmbedAlt' ).replace( '%d', i + 1 ) + '</span>'
     1775                                        ).on( 'click keypress', function ( e ) {
     1776                                                if ( e.type === 'click' || e.which === 13 ) {
     1777                                                        insertSelectedMedia( 'embed',src );
     1778                                                }
     1779                                        } ).appendTo( '.wppt-all-media-list', listContainer );
     1780
     1781                                        found++;
     1782                                } );
     1783                        }
     1784
     1785                        if ( interestingImages && interestingImages.length ) {
     1786                                $.each( interestingImages, function ( i, src ) {
     1787                                        src = checkUrl( src );
     1788
     1789                                        var displaySrc = src.replace(/^(http[^\?]+)(\?.*)?$/, '$1');
     1790                                        if ( src.indexOf( 'files.wordpress.com/' ) > -1 ) {
     1791                                                displaySrc = displaySrc.replace(/\?.*$/, '') + '?w=' + smallestWidth;
     1792                                        } else if ( src.indexOf( 'gravatar.com/' ) > -1 ) {
     1793                                                displaySrc = displaySrc.replace( /\?.*$/, '' ) + '?s=' + smallestWidth;
     1794                                        } else {
     1795                                                displaySrc = src;
     1796                                        }
     1797
     1798                                        $( '<li></li>', {
     1799                                                'id': 'img-' + i + '-container',
     1800                                                'class': 'suggested-media-thumbnail is-image',
     1801                                                'tabindex': '0'
     1802                                        } ).css( {
     1803                                                'background-image': 'url(' + displaySrc + ')'
     1804                                        } ).html(
     1805                                                '<span class="screen-reader-text">' +__( 'suggestedImgAlt' ).replace( '%d', i + 1 ) + '</span>'
     1806                                        ).on( 'click keypress', function ( e ) {
     1807                                                if ( e.type === 'click' || e.which === 13 ) {
     1808                                                        insertSelectedMedia( 'img', src, data.u );
     1809                                                }
     1810                                        } ).appendTo( '.wppt-all-media-list', listContainer );
     1811
     1812                                        found++;
     1813                                } );
     1814                        }
     1815
     1816                        if ( ! found ) {
     1817                                mediaContainer.removeClass( 'all-media-visible' ).addClass( 'no-media');
     1818                                return;
     1819                        }
     1820
     1821                        mediaContainer.removeClass( 'no-media' ).addClass( 'all-media-visible' );
     1822                }
     1823
     1824                /* ***************************************************************
     1825                 * MONITORING FUNCTIONS
     1826                 *************************************************************** */
     1827
     1828                /**
     1829                 * Interactive navigation behavior for the options modal (post format, tags, categories)
     1830                 */
     1831                function monitorOptionsModal() {
     1832                        var isOffScreen   = 'is-off-screen',
     1833                                isHidden      = 'is-hidden',
     1834                                $postOptions  = $( '.post-options' ),
     1835                                $postOption   = $( '.post-option' ),
     1836                                $settingModal = $( '.setting-modal' ),
     1837                                $modalClose   = $( '.modal-close' );
     1838
     1839                        $postOption.on( 'click', function( event ) {
     1840                                var index = $( this ).index(),
     1841                                        $targetSettingModal = $settingModal.eq( index );
     1842
     1843                                event.preventDefault();
     1844
     1845                                $postOptions
     1846                                        .addClass( isOffScreen )
     1847                                        .one( transitionEndEvent, function() {
     1848                                                $( this ).addClass( isHidden );
     1849                                        } );
     1850
     1851                                $targetSettingModal
     1852                                        .removeClass( isOffScreen + ' ' + isHidden )
     1853                                        .one( transitionEndEvent, function() {
     1854                                                $( this ).find( $modalClose ).focus();
     1855                                        } );
     1856                        } );
     1857
     1858                        $modalClose.on( 'click', function( event ) {
     1859                                var $targetSettingModal = $( this ).parent(),
     1860                                        index = $targetSettingModal.index();
     1861
     1862                                event.preventDefault();
     1863
     1864                                $postOptions
     1865                                        .removeClass( isOffScreen + ' ' + isHidden );
     1866
     1867                                $targetSettingModal
     1868                                        .addClass( isOffScreen )
     1869                                        .one( transitionEndEvent, function() {
     1870                                                $( this ).addClass( isHidden );
     1871                                        } );
     1872
     1873                                // For browser that don't support transitionend.
     1874                                if ( ! transitionEndEvent ) {
     1875                                        setTimeout( function() {
     1876                                                $targetSettingModal.addClass( isHidden );
     1877                                        }, 350 );
     1878                                }
     1879
     1880                                $postOption.eq( index - 1 ).focus();
     1881                        } );
     1882                }
     1883
     1884                /**
     1885                 * Interactive behavior for the sidebar toggle, to show the options modals
     1886                 */
     1887                function monitorSidebarToggle() {
     1888                        var $optOpen  = $( '.options-open' ),
     1889                                $optClose = $( '.options-close' ),
     1890                                $postOption = $( '.post-option' ),
     1891                                $sidebar = $( '.options-panel' ),
     1892                                $postActions = $( '.press-this-actions' ),
     1893                                $scanbar = $( '#scanbar' ),
     1894                                isOffScreen = 'is-off-screen',
     1895                                isHidden = 'is-hidden',
     1896                                ifOffHidden = isOffScreen + ' ' + isHidden;
     1897
     1898                        $optOpen.on( 'click', function(){
     1899                                $optOpen.addClass( isHidden );
     1900                                $optClose.removeClass( isHidden );
     1901                                $postActions.addClass( isHidden );
     1902                                $scanbar.addClass( isHidden );
     1903
     1904                                $sidebar
     1905                                        .removeClass( ifOffHidden )
     1906                                        .one( 'transitionend', function() {
     1907                                                $postOption.eq( 0 ).focus();
     1908                                        } );
     1909                        } );
     1910
     1911                        $optClose.on( 'click', function(){
     1912                                $optClose.addClass( isHidden );
     1913                                $optOpen.removeClass( isHidden );
     1914                                $postActions.removeClass( isHidden );
     1915                                $scanbar.removeClass( isHidden );
     1916
     1917                                $sidebar
     1918                                        .addClass( isOffScreen )
     1919                                        .one( 'transitionend', function() {
     1920                                                $( this ).addClass( isHidden );
     1921                                                // Reset to options list
     1922                                                $( '.post-options' ).removeClass( ifOffHidden );
     1923                                                $( '.setting-modal').addClass( ifOffHidden );
     1924                                        } );
     1925                        } );
     1926                }
     1927
     1928                /**
     1929                 * Interactive behavior for the post title's field placeholder
     1930                 */
     1931                function monitorPlaceholder() {
     1932                        var $selector = $( '#title-container'),
     1933                                $placeholder = $('.post-title-placeholder');
     1934
     1935                        $selector.on( 'focus', function() {
     1936                                $placeholder.addClass('is-hidden');
     1937                        } );
     1938
     1939                        $selector.on( 'blur', function() {
     1940                                var textLength = $( this ).text().length;
     1941
     1942                                if ( ! textLength ) {
     1943                                        $placeholder.removeClass('is-hidden');
     1944                                }
     1945                        } );
     1946                }
     1947
     1948                /* ***************************************************************
     1949                 * PROCESSING FUNCTIONS
     1950                 *************************************************************** */
     1951
     1952                /**
     1953                 * Calls all the rendring related functions to happen on page load
     1954                 */
     1955                function render(){
     1956                        // We're on!
     1957                        renderToolsVisibility();
     1958                        renderSuggestedTitle();
     1959                        renderDetectedMedia();
     1960                        $( document ).on( 'tinymce-editor-init', renderSuggestedContent );
     1961                        renderStartupNotices();
     1962                }
     1963
     1964                /**
     1965                 * Set app events and other state monitoring related code.
     1966                 */
     1967                function monitor(){
     1968                        $( '#current-site a').click( function( e ) {
     1969                                e.preventDefault();
     1970                        } );
     1971
     1972                        // Publish and Draft buttons and submit
     1973
     1974                        $( '#draft-field' ).on( 'click', function() {
     1975                                submitPost( 'draft' );
     1976                        } );
     1977
     1978                        $( '#publish-field' ).on( 'click', function() {
     1979                                submitPost( 'publish' );
     1980                        } );
     1981
     1982                        monitorOptionsModal();
     1983                        monitorSidebarToggle();
     1984                        monitorPlaceholder();
     1985
     1986                        $( '#post-formats-select input' ).on( 'change', function() {
     1987                                var $this = $( this );
     1988
     1989                                if ( $this.is( ':checked' ) ) {
     1990                                        setPostFormatString( $this.attr( 'id' ).replace( /^post-format-(.+)$/, '$1' ) );
     1991                                }
     1992                        } );
     1993
     1994                        // Needs more work, doesn't detect when the other JS changes the value of #tax-input-post_tag
     1995                        $( '#tax-input-post_tag' ).on( 'change', function() {
     1996                                var val =  $( this ).val();
     1997                                $( '#post-option-tags' ).text( ( val.length ) ? val.replace( /,([^\s])/g, ', $1' ) : '' );
     1998                        } );
     1999
     2000                        $( window ).on( 'beforeunload.press-this', function() {
     2001                                if ( saveAlert || ( editor && editor.isDirty() ) ) {
     2002                                        return __( 'saveAlert' );
     2003                                }
     2004                        } );
     2005
     2006                        $( 'button.add-cat-toggle' ).on( 'click.press-this', function() {
     2007                                $( this ).toggleClass( 'is-toggled' );
     2008                                $( '.setting-modal .add-category' ).toggleClass( 'is-hidden' );
     2009                                $( '.categories-search-wrapper' ).toggleClass( 'is-hidden' );
     2010                        } );
     2011
     2012                        $( 'button.add-cat-submit' ).on( 'click.press-this', saveNewCategory );
     2013
     2014                        $( '.categories-search' ).on( 'keyup', function() {
     2015                                var search = $( this ).val().toLowerCase() || '';
     2016
     2017                                // Don't search when less thasn 3 extended ASCII chars
     2018                                if ( /[\x20-\xFF]+/.test( search ) && search.length < 2 ) {
     2019                                        return;
     2020                                }
     2021
     2022                                $.each( catsCache, function( i, cat ) {
     2023                                        cat.node.removeClass( 'is-hidden searched-parent' );
     2024                                } );
     2025
     2026                                if ( search ) {
     2027                                        $.each( catsCache, function( i, cat ) {
     2028                                                if ( cat.text.indexOf( search ) === -1 ) {
     2029                                                        cat.node.addClass( 'is-hidden' );
     2030                                                } else {
     2031                                                        cat.parents.addClass( 'searched-parent' );
     2032                                                }
     2033                                        } );
     2034                                }
     2035                        } );
     2036
     2037                        return true;
     2038                }
     2039
     2040                function refreshCatsCache() {
     2041                        $( '.categories-select' ).find( 'li' ).each( function() {
     2042                                var $this = $( this );
     2043
     2044                                catsCache.push( {
     2045                                        node: $this,
     2046                                        parents: $this.parents( 'li' ),
     2047                                        text: $this.children( 'label' ).text().toLowerCase()
     2048                                } );
     2049                        } );
     2050                }
     2051
     2052                // Let's go!
     2053                $( document ).ready( function() {
     2054                        render();
     2055                        monitor();
     2056                        refreshCatsCache();
     2057                });
     2058
     2059                // Expose public methods
     2060                // TODO: which are needed?
     2061                return {
     2062                        renderNotice: renderNotice,
     2063                        renderError: renderError
     2064                };
     2065        }
     2066
     2067        window.wp = window.wp || {};
     2068        window.wp.pressThis = new PressThis();
     2069
     2070}( jQuery, window ));
     2071/**
     2072 * PressThis App
     2073 *
     2074 */
     2075( function( $, window ) {
     2076        var PressThis = function() {
     2077                var editor,
     2078                        saveAlert             = false,
     2079                        $div                  = $( '<div>' ),
     2080                        siteConfig            = window.wpPressThisConfig || {},
     2081                        data                  = window.wpPressThisData || {},
     2082                        smallestWidth         = 128,
     2083                        interestingImages         = getInterestingImages( data ) || [],
     2084                        interestingEmbeds         = getInterestingEmbeds( data ) || [],
     2085                        hasEmptyTitleStr      = false,
     2086                        suggestedTitleStr     = getSuggestedTitle( data ),
     2087                        suggestedContentStr   = getSuggestedContent( data ),
     2088                        hasSetFocus           = false,
     2089                        catsCache             = [],
     2090                        transitionEndEvent    = ( function() {
     2091                                var style = document.documentElement.style;
     2092
     2093                                if ( typeof style.transition !== 'undefined' ) {
     2094                                        return 'transitionend';
     2095                                }
     2096
     2097                                if ( typeof style.WebkitTransition !== 'undefined' ) {
     2098                                        return 'webkitTransitionEnd';
     2099                                }
     2100
     2101                                return false;
     2102                        }() );
     2103
     2104                /* ***************************************************************
     2105                 * HELPER FUNCTIONS
     2106                 *************************************************************** */
     2107
     2108                /**
     2109                 * Emulates our PHP __() gettext function, powered by the strings exported in pressThisL10n.
     2110                 *
     2111                 * @param key string Key of the string to be translated, as found in pressThisL10n.
     2112                 * @returns string Original or translated string, or empty string if no key.
     2113                 */
     2114                function __( key ) {
     2115                        if ( key && window.pressThisL10n ) {
     2116                                return window.pressThisL10n[key] || key;
     2117                        }
     2118
     2119                        return key || '';
     2120                }
     2121
     2122                /**
     2123                 * Strips HTML tags
     2124                 *
     2125                 * @param string string Text to have the HTML tags striped out of.
     2126                 * @returns string Stripped text.
     2127                 */
     2128                function stripTags( string ) {
     2129                        string = string || '';
     2130
     2131                        return string
     2132                                .replace( /<!--[\s\S]*?(-->|$)/g, '' )
     2133                                .replace( /<(script|style)[^>]*>[\s\S]*?(<\/\1>|$)/ig, '' )
     2134                                .replace( /<\/?[a-z][^>]*>/ig, '' );
     2135                }
     2136
     2137                // TODO: needed?
     2138                function entityEncode( text ) {
     2139                        return $div.text( text ).html();
     2140                }
     2141
     2142                /**
     2143                 * Strip HTML tags and entity encode some of the HTML special chars.
     2144                 *
     2145                 * @param text string Text.
     2146                 * @returns string Sanitized text.
     2147                 */
     2148                function sanitizeText( text ) {
     2149                        text = stripTags( text );
     2150
     2151                        return text
     2152                                .replace( /\\/, '' )
     2153                                .replace( /</g, '&lt;' )
     2154                                .replace( />/g, '&gt;' )
     2155                                .replace( /"/g, '&quot;' )
     2156                                .replace( /'/g, '&#039;' );
     2157                }
     2158
     2159                /**
     2160                 * Allow only HTTP or protocol relative URLs.
     2161                 *
     2162                 * @param url string The URL.
     2163                 * @returns string Processed URL.
     2164                 */
     2165                function checkUrl( url ) {
     2166                        url = $.trim( url || '' );
     2167
     2168                        if ( /^(?:https?:)?\/\//.test( url ) ) {
     2169                                url = stripTags( url );
     2170                                return url.replace( /["\\]+/g, '' );
     2171                        }
     2172
     2173                        return '';
     2174                }
     2175
     2176                /**
     2177                 * Gets the source page's canonical link, based on passed location and meta data.
     2178                 *
     2179                 * @param data object Usually WpPressThis_App.data
     2180                 * @returns string Discovered canonical URL, or empty
     2181                 */
     2182                function getCanonicalLink( data ) {
     2183                        if ( ! data || data.length ) {
     2184                                return '';
     2185                        }
     2186
     2187                        var link = '';
     2188
     2189                        if ( data._links ) {
     2190                                if ( data._links.canonical && data._links.canonical.length ) {
     2191                                        link = data._links.canonical;
     2192                                }
     2193                        }
     2194
     2195                        if ( ! link.length && data.u ) {
     2196                                link = data.u;
     2197                        }
     2198
     2199                        if ( ! link.length && data._meta ) {
     2200                                if ( data._meta['twitter:url'] && data._meta['twitter:url'].length ) {
     2201                                        link = data._meta['twitter:url'];
     2202                                } else if ( data._meta['og:url'] && data._meta['og:url'].length ) {
     2203                                        link = data._meta['og:url'];
     2204                                }
     2205                        }
     2206
     2207                        return decodeURI( link );
     2208                }
     2209
     2210                /**
     2211                 * Gets the source page's site name, based on passed meta data.
     2212                 *
     2213                 * @param data object Usually WpPressThis_App.data
     2214                 * @returns string Discovered site name, or empty
     2215                 */
     2216                function getSourceSiteName( data ) {
     2217                        if ( ! data || data.length ) {
     2218                                return '';
     2219                        }
     2220
     2221                        var name='';
     2222
     2223                        if ( data._meta ) {
     2224                                if ( data._meta['og:site_name'] && data._meta['og:site_name'].length ) {
     2225                                        name = data._meta['og:site_name'];
     2226                                } else if ( data._meta['application-name'] && data._meta['application-name'].length ) {
     2227                                        name = data._meta['application-name'];
     2228                                }
     2229                        }
     2230
     2231                        return name.replace( /\\/g, '' );
     2232                }
     2233
     2234                /**
     2235                 * Gets the source page's title, based on passed title and meta data.
     2236                 *
     2237                 * @param data object Usually WpPressThis_App.data
     2238                 * @returns string Discovered page title, or empty
     2239                 */
     2240                function getSuggestedTitle( data ) {
     2241                        if ( ! data || data.length ) {
     2242                                return __( 'newPost' );
     2243                        }
     2244
     2245                        var title = '';
     2246
     2247                        if ( data.t ) {
     2248                                title = data.t;
     2249                        }
     2250
     2251                        if ( ! title.length && data._meta ) {
     2252                                if ( data._meta['twitter:title'] && data._meta['twitter:title'].length ) {
     2253                                        title = data._meta['twitter:title'];
     2254                                } else if ( data._meta['og:title'] && data._meta['og:title'].length ) {
     2255                                        title = data._meta['og:title'];
     2256                                } else if ( data._meta.title && data._meta.title.length ) {
     2257                                        title = data._meta.title;
     2258                                }
     2259                        }
     2260
     2261                        if ( ! title.length ) {
     2262                                title = __( 'newPost' );
     2263                                hasEmptyTitleStr = true;
     2264                        }
     2265
     2266                        return title.replace( /\\/g, '' );
     2267                }
     2268
     2269                /**
     2270                 * Gets the source page's suggested content, based on passed data (description, selection, etc).
     2271                 * Features a blockquoted excerpt, as well as content attribution, if any.
     2272                 *
     2273                 * @param data object Usually WpPressThis_App.data
     2274                 * @returns string Discovered content, or empty
     2275                 */
     2276                function getSuggestedContent( data ) {
     2277                        if ( ! data || data.length ) {
     2278                                return '';
     2279                        }
     2280
     2281                        var content   = '',
     2282                                title     = getSuggestedTitle( data ),
     2283                                url       = getCanonicalLink( data ),
     2284                                siteName  = getSourceSiteName( data );
     2285
     2286                        if ( data.s && data.s.length ) {
     2287                                content = data.s;
     2288                        } else if ( data._meta ) {
     2289                                if ( data._meta['twitter:description'] && data._meta['twitter:description'].length ) {
     2290                                        content = data._meta['twitter:description'];
     2291                                } else if ( data._meta['og:description'] && data._meta['og:description'].length ) {
     2292                                        content = data._meta['og:description'];
     2293                                } else if ( data._meta.description && data._meta.description.length ) {
     2294                                        content = data._meta.description;
     2295                                }
     2296                        }
     2297
     2298                        // Wrap suggested content in blockquote tag, if we have any.
     2299                        content = ( content.length ? '<blockquote class="press-this-suggested-content">' + sanitizeText( content ) + '</blockquote>' : '' );
     2300
     2301                        // Add a source attribution if there is one available.
     2302                        if ( ( ( title.length && __( 'newPost' ) !== title ) || siteName.length ) && url.length ) {
     2303                                content += '<p class="press-this-suggested-source">';
     2304                                content += __( 'source' );
     2305                                content += ' <cite>';
     2306                                content += __( 'sourceLink').replace( '%1$s', encodeURI( url ) ).replace( '%2$s', sanitizeText( title || siteName ) );
     2307                                content += '</cite></p>';
     2308                        }
     2309
     2310                        if ( ! content ) {
     2311                                content = '';
     2312                        }
     2313
     2314                        return content.replace( /\\/g, '' );
     2315                }
     2316
     2317                /**
     2318                 * Tests if what was passed as an embed URL is deemed to be embeddable in the editor.
     2319                 *
     2320                 * @param url string Passed URl, usually from WpPressThis_App.data._embed
     2321                 * @returns boolean
     2322                 */
     2323                function isEmbeddable( url ) {
     2324                        if ( ! url ) {
     2325                                return false;
     2326                        } else if ( url.match( /\/\/(m\.|www\.)?youtube\.com\/watch\?/ ) || url.match( /\/youtu\.be\/.+$/ ) ) {
     2327                                return true;
     2328                        } else if ( url.match( /\/\/vimeo\.com\/(.+\/)?[\d]+$/ ) ) {
     2329                                return true;
     2330                        } else if ( url.match( /\/\/(www\.)?dailymotion\.com\/video\/.+$/ ) ) {
     2331                                return true;
     2332                        } else if ( url.match( /\/\/soundcloud\.com\/.+$/ ) ) {
     2333                                return true;
     2334                        } else if ( url.match( /\/\/twitter\.com\/[^\/]+\/status\/[\d]+$/ ) ) {
     2335                                return true;
     2336                        } else if ( url.match( /\/\/vine\.co\/v\/[^\/]+/ ) ) {
     2337                                return true;
     2338                        }
     2339
     2340                        return false;
     2341                }
     2342
     2343                /**
     2344                 * Tests if what was passed as an image URL is deemed to be interesting enough to offer to the user for selection.
     2345                 *
     2346                 * @param src string Passed URl, usually from WpPressThis_App.data._ing
     2347                 * @returns boolean Test for false
     2348                 */
     2349                function isSrcUninterestingPath( src ) {
     2350                        if ( src.match( /\/ad[sx]{1}?\// ) ) {
     2351                                // Ads
     2352                                return true;
     2353                        } else if ( src.match( /(\/share-?this[^\.]+?\.[a-z0-9]{3,4})(\?.*)?$/ ) ) {
     2354                                // Share-this type button
     2355                                return true;
     2356                        } else if ( src.match( /\/(spinner|loading|spacer|blank|rss)\.(gif|jpg|png)/ ) ) {
     2357                                // Loaders, spinners, spacers
     2358                                return true;
     2359                        } else if ( src.match( /\/([^\.\/]+[-_]{1})?(spinner|loading|spacer|blank)s?([-_]{1}[^\.\/]+)?\.[a-z0-9]{3,4}/ ) ) {
     2360                                // Fancy loaders, spinners, spacers
     2361                                return true;
     2362                        } else if ( src.match( /([^\.\/]+[-_]{1})?thumb[^.]*\.(gif|jpg|png)$/ ) ) {
     2363                                // Thumbnails, too small, usually irrelevant to context
     2364                                return true;
     2365                        } else if ( src.match( /\/wp-includes\// ) ) {
     2366                                // Classic WP interface images
     2367                                return true;
     2368                        } else if ( src.match( /[^\d]{1}\d{1,2}x\d+\.(gif|jpg|png)$/ ) ) {
     2369                                // Most often tiny buttons/thumbs (< 100px wide)
     2370                                return true;
     2371                        } else if ( src.indexOf( '/g.gif' ) > -1 ) {
     2372                                // Classic WP stats gif
     2373                                return true;
     2374                        } else if ( src.indexOf( '/pixel.mathtag.com' ) > -1 ) {
     2375                                // See mathtag.com
     2376                                return true;
     2377                        }
     2378                        return false;
     2379                }
     2380
     2381                /**
     2382                 * Get a list of valid embeds from what was passed via WpPressThis_App.data._embed on page load.
     2383                 *
     2384                 * @returns array
     2385                 */
     2386                function getInterestingEmbeds() {
     2387                        var embeds             = data._embed || [],
     2388                                interestingEmbeds  = [],
     2389                                alreadySelected    = [];
     2390
     2391                        if ( embeds.length ) {
     2392                                $.each( embeds, function ( i, src ) {
     2393                                        if ( !src || !src.length ) {
     2394                                                // Skip: no src value
     2395                                                return;
     2396                                        } else if ( !isEmbeddable( src ) ) {
     2397                                                // Skip: not deemed embeddable
     2398                                                return;
     2399                                        }
     2400
     2401                                        var schemelessSrc = src.replace( /^https?:/, '' );
     2402
     2403                                        if ( $.inArray( schemelessSrc, alreadySelected ) > -1 ) {
     2404                                                // Skip: already shown
     2405                                                return;
     2406                                        }
     2407
     2408                                        interestingEmbeds.push( src );
     2409                                        alreadySelected.push( schemelessSrc );
     2410                                } );
     2411                        }
     2412
     2413                        return interestingEmbeds;
     2414                }
     2415
     2416                /**
     2417                 * Get what is likely the most valuable image from what was passed via WpPressThis_App.data._img and WpPressThis_App.data._meta on page load.
     2418                 *
     2419                 * @returns array
     2420                 */
     2421                function getFeaturedImage( data ) {
     2422                        var featured = '';
     2423
     2424                        if ( ! data || ! data._meta ) {
     2425                                return '';
     2426                        }
     2427
     2428                        if ( data._meta['twitter:image0:src'] && data._meta['twitter:image0:src'].length ) {
     2429                                featured = data._meta['twitter:image0:src'];
     2430                        } else if ( data._meta['twitter:image0'] && data._meta['twitter:image0'].length ) {
     2431                                featured = data._meta['twitter:image0'];
     2432                        } else if ( data._meta['twitter:image:src'] && data._meta['twitter:image:src'].length ) {
     2433                                featured = data._meta['twitter:image:src'];
     2434                        } else if ( data._meta['twitter:image'] && data._meta['twitter:image'].length ) {
     2435                                featured = data._meta['twitter:image'];
     2436                        } else if ( data._meta['og:image'] && data._meta['og:image'].length ) {
     2437                                featured = data._meta['og:image'];
     2438                        } else if ( data._meta['og:image:secure_url'] && data._meta['og:image:secure_url'].length ) {
     2439                                featured = data._meta['og:image:secure_url'];
     2440                        }
     2441
     2442                        featured = checkUrl( featured );
     2443
     2444                        return ( isSrcUninterestingPath( featured ) ) ? '' : featured;
     2445                }
     2446
     2447                /**
     2448                 * Get a list of valid images from what was passed via WpPressThis_App.data._img and WpPressThis_App.data._meta on page load.
     2449                 *
     2450                 * @returns array
     2451                 */
     2452                function getInterestingImages( data ) {
     2453                        var imgs             = data._img || [],
     2454                                featuredPict     = getFeaturedImage( data ) || '',
     2455                                interestingImgs  = [],
     2456                                alreadySelected  = [];
     2457
     2458                        if ( featuredPict.length ) {
     2459                                interestingImgs.push( featuredPict );
     2460                                alreadySelected.push( featuredPict.replace(/^https?:/, '') );
     2461                        }
     2462
     2463                        if ( imgs.length ) {
     2464                                $.each( imgs, function ( i, src ) {
     2465                                        src = src.replace( /http:\/\/[\d]+\.gravatar\.com\//, 'https://secure.gravatar.com/' );
     2466                                        src = checkUrl( src );
     2467
     2468                                        if ( ! src || ! src.length ) {
     2469                                                // Skip: no src value
     2470                                                return;
     2471                                        }
     2472
     2473                                        var schemelessSrc = src.replace( /^https?:/, '' );
     2474
     2475                                        if ( Array.prototype.indexOf && alreadySelected.indexOf( schemelessSrc ) > -1 ) {
     2476                                                // Skip: already shown
     2477                                                return;
     2478                                        } else if ( isSrcUninterestingPath( src ) ) {
     2479                                                // Skip: spinner, stat, ad, or spacer pict
     2480                                                return;
     2481                                        } else if ( src.indexOf( 'avatar' ) > -1 && interestingImgs.length >= 15 ) {
     2482                                                // Skip:  some type of avatar and we've already gathered more than 23 diff images to show
     2483                                                return;
     2484                                        }
     2485
     2486                                        interestingImgs.push( src );
     2487                                        alreadySelected.push( schemelessSrc );
     2488                                } );
     2489                        }
     2490
     2491                        return interestingImgs;
     2492                }
     2493
     2494                /**
     2495                 * Show UX spinner
     2496                 */
     2497                function showSpinner() {
     2498                        $( '#spinner' ).addClass( 'show' );
     2499                        $( '.post-actions button' ).each( function() {
     2500                                $( this ).attr( 'disabled', 'disabled' );
     2501                        } );
     2502                }
     2503
     2504                /**
     2505                 * Hide UX spinner
     2506                 */
     2507                function hideSpinner() {
     2508                        $( '#spinner' ).removeClass( 'show' );
     2509                        $( '.post-actions button' ).each( function() {
     2510                                $( this ).removeAttr( 'disabled' );
     2511                        } );
     2512                }
     2513
     2514                /**
     2515                 * Submit the post form via AJAX, and redirect to the proper screen if published vs saved as a draft.
     2516                 *
     2517                 * @param action string publish|draft
     2518                 */
     2519                function submitPost( action ) {
     2520                        saveAlert = false;
     2521                        showSpinner();
     2522
     2523                        var $form = $( '#pressthis-form' );
     2524
     2525                        if ( 'publish' === action ) {
     2526                                $( '#post_status' ).val( 'publish' );
     2527                        }
     2528
     2529                        editor && editor.save();
     2530
     2531                        $( '#title-field' ).val( sanitizeText( $( '#title-container' ).text() ) );
     2532
     2533                        // Make sure to flush out the tags with tagBox before saving
     2534                        if ( window.tagBox ) {
     2535                                $( 'div.tagsdiv' ).each( function() {
     2536                                        window.tagBox.flushTags( this, false, 1 );
     2537                                } );
     2538                        }
     2539
     2540                        var data = $form.serialize();
     2541
     2542                        $.ajax( {
     2543                                type: 'post',
     2544                                url: window.ajaxurl,
     2545                                data: data,
     2546                                success: function( response ) {
     2547                                        if ( ! response.success ) {
     2548                                                renderError( response.data.errorMessage );
     2549                                                hideSpinner();
     2550                                        } else if ( response.data.redirect ) {
     2551                                                if ( window.opener && siteConfig.redir_in_parent ) {
     2552                                                        try {
     2553                                                                window.opener.location.href = response.data.redirect;
     2554                                                        } catch( er ) {}
     2555
     2556                                                        window.self.close();
     2557                                                } else {
     2558                                                        window.location.href = response.data.redirect;
     2559                                                }
     2560                                        }
     2561                                }
     2562                        } );
     2563                }
     2564
     2565                /**
     2566                 * Inserts the media a user has selected from the presented list inside the editor, as an image or embed, based on type
     2567                 *
     2568                 * @param type string img|embed
     2569                 * @param src string Source URL
     2570                 * @param link string Optional destination link, for images (defaults to src)
     2571                 */
     2572                function insertSelectedMedia( type, src, link ) {
     2573                        var newContent = '';
     2574
     2575                        if ( ! editor ) {
     2576                                return;
     2577                        }
     2578
     2579                        src = checkUrl( src );
     2580                        link = checkUrl( link );
     2581
     2582                        if ( 'img' === type ) {
     2583                                if ( ! link || ! link.length ) {
     2584                                        link = src;
     2585                                }
     2586
     2587                                newContent = '<a href="' + link + '"><img class="alignnone size-full" src="' + src + '" /></a>\n';
     2588                        } else {
     2589                                newContent = '[embed]' + src + '[/embed]\n';
     2590                        }
     2591
     2592                        if ( ! hasSetFocus ) {
     2593                                // Append to top of content on 1st media insert
     2594                                editor.setContent( newContent + editor.getContent() );
     2595                        } else {
     2596                                // Or add where the cursor was last positioned in TinyMCE
     2597                                editor.execCommand( 'mceInsertContent', false, newContent );
     2598                        }
     2599
     2600                        hasSetFocus = true;
     2601                }
     2602
     2603                /**
     2604                 * Adds the currently selected post format next to the option, in the options panel.
     2605                 *
     2606                 * @param format string Post format to be displayed
     2607                 */
     2608                function setPostFormatString( format ) {
     2609                        if ( ! format || ! siteConfig || ! siteConfig.post_formats || ! siteConfig.post_formats[ format ] ) {
     2610                                return;
     2611                        }
     2612                        $( '#post-option-post-format' ).text( siteConfig.post_formats[ format ] );
     2613                }
     2614
     2615                /**
     2616                 * Save a new user-generated category via AJAX
     2617                 */
     2618                function saveNewCategory() {
     2619                        var data = {
     2620                                action: 'press-this-add-category',
     2621                                post_id: $( '#post_ID' ).val() || 0,
     2622                                name: $( '#new-category' ).val() || '',
     2623                                new_cat_nonce: $( '#_ajax_nonce-add-category' ).val() || '',
     2624                                parent: $( '#new-category-parent' ).val() || 0
     2625                        };
     2626
     2627                        $.post( window.ajaxurl, data, function( response ) {
     2628                                if ( ! response.success ) {
     2629                                        renderError( response.data.errorMessage );
     2630                                } else {
     2631                                        // TODO: change if/when the html changes.
     2632                                        var $parent, $ul,
     2633                                                $wrap = $( 'ul.categories-select' );
     2634
     2635                                        $.each( response.data, function( i, newCat ) {
     2636                                                var $node = $( '<li>' ).attr( 'id', 'category-' + newCat.term_id )
     2637                                                        .append( $( '<label class="selectit">' ).text( newCat.name )
     2638                                                                .append( $( '<input type="checkbox" name="post_category[]" checked>' ).attr( 'value', newCat.term_id ) ) );
     2639
     2640                                                if ( newCat.parent ) {
     2641                                                        if ( ! $ul || ! $ul.length ) {
     2642                                                                $parent = $wrap.find( '#category-' + newCat.parent );
     2643                                                                $ul = $parent.find( 'ul.children:first' );
     2644
     2645                                                                if ( ! $ul.length ) {
     2646                                                                        $ul = $( '<ul class="children">' ).appendTo( $parent );
     2647                                                                }
     2648                                                        }
     2649
     2650                                                        $ul.append( $node );
     2651                                                        // TODO: set focus on
     2652                                                } else {
     2653                                                        $wrap.prepend( $node );
     2654                                                }
     2655                                        } );
     2656
     2657                                        refreshCatsCache();
     2658                                }
     2659                        } );
     2660                }
     2661
     2662                /* ***************************************************************
     2663                 * RENDERING FUNCTIONS
     2664                 *************************************************************** */
     2665
     2666                /**
     2667                 * Hide the form letting users enter a URL to be scanned, if a URL was already passed.
     2668                 */
     2669                function renderToolsVisibility() {
     2670                        if ( data.u && data.u.match( /^https?:/ ) ) {
     2671                                $( '#scanbar' ).hide();
     2672                        }
     2673                }
     2674
     2675                /**
     2676                 * Render error notice
     2677                 *
     2678                 * @param msg string Notice/error message
     2679                 * @param error string error|notice CSS class for display
     2680                 */
     2681                function renderNotice( msg, error ) {
     2682                        var $alerts = $( '.editor-wrapper div.alerts' ),
     2683                                className = error ? 'is-error' : 'is-notice';
     2684
     2685                        $alerts.append( $( '<p class="' + className + '">' ).text( msg ) );
     2686                }
     2687
     2688                /**
     2689                 * Render error notice
     2690                 *
     2691                 * @param msg string Error message
     2692                 */
     2693                function renderError( msg ) {
     2694                        renderNotice( msg, true );
     2695                }
     2696
     2697                /**
     2698                 * Render notices on page load, if any already
     2699                 */
     2700                function renderStartupNotices() {
     2701                        // Render errors sent in the data, if any
     2702                        if ( data.errors && data.errors.length ) {
     2703                                $.each( data.errors, function( i, msg ) {
     2704                                        renderError( msg );
     2705                                } );
     2706                        }
     2707
     2708                        // Prompt user to upgrade their bookmarklet if there is a version mismatch.
     2709                        if ( data.v && data._version && data.v !== data._version ) {
     2710                                $( '.should-upgrade-bookmarklet' ).removeClass( 'is-hidden' );
     2711                        }
     2712                }
     2713
     2714                /**
     2715                 * Render the suggested title, if any
     2716                 */
     2717                function renderSuggestedTitle() {
     2718                        var suggestedTitle = suggestedTitleStr || '',
     2719                                $title = $( '#title-container' );
     2720
     2721                        if ( ! hasEmptyTitleStr ) {
     2722                                $( '#title-field' ).val( suggestedTitle );
     2723                                $title.text( suggestedTitle );
     2724                                $( '.post-title-placeholder' ).addClass( 'is-hidden' );
     2725                        }
     2726
     2727                        $title.on( 'keyup', function() {
     2728                                saveAlert = true;
     2729                        }).on( 'paste', function() {
     2730                                saveAlert = true;
     2731
     2732                                setTimeout( function() {
     2733                                        $title.text( $title.text() );
     2734                                }, 100 );
     2735                        } );
     2736
     2737                }
     2738
     2739                /**
     2740                 * Render the suggested content, if any
     2741                 */
     2742                function renderSuggestedContent() {
     2743                        if ( ! suggestedContentStr || ! suggestedContentStr.length ) {
     2744                                return;
     2745                        }
     2746
     2747                        if ( ! editor ) {
     2748                                editor = window.tinymce.get( 'pressthis' );
     2749                        }
     2750
     2751                        if ( editor ) {
     2752                                editor.setContent( suggestedContentStr );
     2753                                editor.on( 'focus', function() {
     2754                                        hasSetFocus = true;
     2755                                } );
     2756                        }
     2757
     2758                }
     2759
     2760                /**
     2761                 * Render the detected images and embed for selection, if any
     2762                 */
     2763                function renderDetectedMedia() {
     2764                        var mediaContainer = $( '#featured-media-container'),
     2765                                listContainer  = $( '#all-media-container' ),
     2766                                found          = 0;
     2767
     2768                        listContainer.empty();
     2769
     2770                        if ( ( interestingEmbeds && interestingEmbeds.length ) || ( interestingImages && interestingImages.length ) ) {
     2771                                listContainer.append( '<h2 class="screen-reader-text">' + __( 'allMediaHeading' ) + '</h2><ul class="wppt-all-media-list"/>' );
     2772                        }
     2773
     2774                        if ( interestingEmbeds && interestingEmbeds.length ) {
     2775                                $.each( interestingEmbeds, function ( i, src ) {
     2776                                        src = checkUrl( src );
     2777
     2778                                        if ( ! isEmbeddable( src ) ) {
     2779                                                return;
     2780                                        }
     2781
     2782                                        var displaySrc = '',
     2783                                                cssClass   = 'suggested-media-thumbnail suggested-media-embed';
     2784
     2785                                        if ( src.indexOf( 'youtube.com/' ) > -1 ) {
     2786                                                displaySrc = 'https://i.ytimg.com/vi/' + src.replace( /.+v=([^&]+).*/, '$1' ) + '/hqdefault.jpg';
     2787                                                cssClass += ' is-video';
     2788                                        } else if ( src.indexOf( 'youtu.be/' ) > -1 ) {
     2789                                                displaySrc = 'https://i.ytimg.com/vi/' + src.replace( /\/([^\/])$/, '$1' ) + '/hqdefault.jpg';
     2790                                                cssClass += ' is-video';
     2791                                        } else if ( src.indexOf( 'dailymotion.com' ) > -1 ) {
     2792                                                displaySrc = src.replace( '/video/', '/thumbnail/video/' );
     2793                                                cssClass += ' is-video';
     2794                                        } else if ( src.indexOf( 'soundcloud.com' ) > -1 ) {
     2795                                                cssClass += ' is-audio';
     2796                                        } else if ( src.indexOf( 'twitter.com' ) > -1 ) {
     2797                                                cssClass += ' is-tweet';
     2798                                        } else {
     2799                                                cssClass += ' is-video';
     2800                                        }
     2801
     2802                                        $( '<li></li>', {
     2803                                                'id': 'embed-' + i + '-container',
     2804                                                'class': cssClass,
     2805                                                'tabindex': '0'
     2806                                        } ).css( {
     2807                                                'background-image': ( displaySrc.length ) ? 'url(' + displaySrc + ')' : null
     2808                                        } ).html(
     2809                                                '<span class="screen-reader-text">' + __( 'suggestedEmbedAlt' ).replace( '%d', i + 1 ) + '</span>'
     2810                                        ).on( 'click keypress', function ( e ) {
     2811                                                if ( e.type === 'click' || e.which === 13 ) {
     2812                                                        insertSelectedMedia( 'embed',src );
     2813                                                }
     2814                                        } ).appendTo( '.wppt-all-media-list', listContainer );
     2815
     2816                                        found++;
     2817                                } );
     2818                        }
     2819
     2820                        if ( interestingImages && interestingImages.length ) {
     2821                                $.each( interestingImages, function ( i, src ) {
     2822                                        src = checkUrl( src );
     2823
     2824                                        var displaySrc = src.replace(/^(http[^\?]+)(\?.*)?$/, '$1');
     2825                                        if ( src.indexOf( 'files.wordpress.com/' ) > -1 ) {
     2826                                                displaySrc = displaySrc.replace(/\?.*$/, '') + '?w=' + smallestWidth;
     2827                                        } else if ( src.indexOf( 'gravatar.com/' ) > -1 ) {
     2828                                                displaySrc = displaySrc.replace( /\?.*$/, '' ) + '?s=' + smallestWidth;
     2829                                        } else {
     2830                                                displaySrc = src;
     2831                                        }
     2832
     2833                                        $( '<li></li>', {
     2834                                                'id': 'img-' + i + '-container',
     2835                                                'class': 'suggested-media-thumbnail is-image',
     2836                                                'tabindex': '0'
     2837                                        } ).css( {
     2838                                                'background-image': 'url(' + displaySrc + ')'
     2839                                        } ).html(
     2840                                                '<span class="screen-reader-text">' +__( 'suggestedImgAlt' ).replace( '%d', i + 1 ) + '</span>'
     2841                                        ).on( 'click keypress', function ( e ) {
     2842                                                if ( e.type === 'click' || e.which === 13 ) {
     2843                                                        insertSelectedMedia( 'img', src, data.u );
     2844                                                }
     2845                                        } ).appendTo( '.wppt-all-media-list', listContainer );
     2846
     2847                                        found++;
     2848                                } );
     2849                        }
     2850
     2851                        if ( ! found ) {
     2852                                mediaContainer.removeClass( 'all-media-visible' ).addClass( 'no-media');
     2853                                return;
     2854                        }
     2855
     2856                        mediaContainer.removeClass( 'no-media' ).addClass( 'all-media-visible' );
     2857                }
     2858
     2859                /* ***************************************************************
     2860                 * MONITORING FUNCTIONS
     2861                 *************************************************************** */
     2862
     2863                /**
     2864                 * Interactive navigation behavior for the options modal (post format, tags, categories)
     2865                 */
     2866                function monitorOptionsModal() {
     2867                        var isOffScreen   = 'is-off-screen',
     2868                                isHidden      = 'is-hidden',
     2869                                $postOptions  = $( '.post-options' ),
     2870                                $postOption   = $( '.post-option' ),
     2871                                $settingModal = $( '.setting-modal' ),
     2872                                $modalClose   = $( '.modal-close' );
     2873
     2874                        $postOption.on( 'click', function( event ) {
     2875                                var index = $( this ).index(),
     2876                                        $targetSettingModal = $settingModal.eq( index );
     2877
     2878                                event.preventDefault();
     2879
     2880                                $postOptions
     2881                                        .addClass( isOffScreen )
     2882                                        .one( transitionEndEvent, function() {
     2883                                                $( this ).addClass( isHidden );
     2884                                        } );
     2885
     2886                                $targetSettingModal
     2887                                        .removeClass( isOffScreen + ' ' + isHidden )
     2888                                        .one( transitionEndEvent, function() {
     2889                                                $( this ).find( $modalClose ).focus();
     2890                                        } );
     2891                        } );
     2892
     2893                        $modalClose.on( 'click', function( event ) {
     2894                                var $targetSettingModal = $( this ).parent(),
     2895                                        index = $targetSettingModal.index();
     2896
     2897                                event.preventDefault();
     2898
     2899                                $postOptions
     2900                                        .removeClass( isOffScreen + ' ' + isHidden );
     2901
     2902                                $targetSettingModal
     2903                                        .addClass( isOffScreen )
     2904                                        .one( transitionEndEvent, function() {
     2905                                                $( this ).addClass( isHidden );
     2906                                        } );
     2907
     2908                                // For browser that don't support transitionend.
     2909                                if ( ! transitionEndEvent ) {
     2910                                        setTimeout( function() {
     2911                                                $targetSettingModal.addClass( isHidden );
     2912                                        }, 350 );
     2913                                }
     2914
     2915                                $postOption.eq( index - 1 ).focus();
     2916                        } );
     2917                }
     2918
     2919                /**
     2920                 * Interactive behavior for the sidebar toggle, to show the options modals
     2921                 */
     2922                function monitorSidebarToggle() {
     2923                        var $optOpen  = $( '.options-open' ),
     2924                                $optClose = $( '.options-close' ),
     2925                                $postOption = $( '.post-option' ),
     2926                                $sidebar = $( '.options-panel' ),
     2927                                $postActions = $( '.press-this-actions' ),
     2928                                $scanbar = $( '#scanbar' ),
     2929                                isOffScreen = 'is-off-screen',
     2930                                isHidden = 'is-hidden',
     2931                                ifOffHidden = isOffScreen + ' ' + isHidden;
     2932
     2933                        $optOpen.on( 'click', function(){
     2934                                $optOpen.addClass( isHidden );
     2935                                $optClose.removeClass( isHidden );
     2936                                $postActions.addClass( isHidden );
     2937                                $scanbar.addClass( isHidden );
     2938
     2939                                $sidebar
     2940                                        .removeClass( ifOffHidden )
     2941                                        .one( 'transitionend', function() {
     2942                                                $postOption.eq( 0 ).focus();
     2943                                        } );
     2944                        } );
     2945
     2946                        $optClose.on( 'click', function(){
     2947                                $optClose.addClass( isHidden );
     2948                                $optOpen.removeClass( isHidden );
     2949                                $postActions.removeClass( isHidden );
     2950                                $scanbar.removeClass( isHidden );
     2951
     2952                                $sidebar
     2953                                        .addClass( isOffScreen )
     2954                                        .one( 'transitionend', function() {
     2955                                                $( this ).addClass( isHidden );
     2956                                                // Reset to options list
     2957                                                $( '.post-options' ).removeClass( ifOffHidden );
     2958                                                $( '.setting-modal').addClass( ifOffHidden );
     2959                                        } );
     2960                        } );
     2961                }
     2962
     2963                /**
     2964                 * Interactive behavior for the post title's field placeholder
     2965                 */
     2966                function monitorPlaceholder() {
     2967                        var $selector = $( '#title-container'),
     2968                                $placeholder = $('.post-title-placeholder');
     2969
     2970                        $selector.on( 'focus', function() {
     2971                                $placeholder.addClass('is-hidden');
     2972                        } );
     2973
     2974                        $selector.on( 'blur', function() {
     2975                                var textLength = $( this ).text().length;
     2976
     2977                                if ( ! textLength ) {
     2978                                        $placeholder.removeClass('is-hidden');
     2979                                }
     2980                        } );
     2981                }
     2982
     2983                /* ***************************************************************
     2984                 * PROCESSING FUNCTIONS
     2985                 *************************************************************** */
     2986
     2987                /**
     2988                 * Calls all the rendring related functions to happen on page load
     2989                 */
     2990                function render(){
     2991                        // We're on!
     2992                        renderToolsVisibility();
     2993                        renderSuggestedTitle();
     2994                        renderDetectedMedia();
     2995                        $( document ).on( 'tinymce-editor-init', renderSuggestedContent );
     2996                        renderStartupNotices();
     2997                }
     2998
     2999                /**
     3000                 * Set app events and other state monitoring related code.
     3001                 */
     3002                function monitor(){
     3003                        $( '#current-site a').click( function( e ) {
     3004                                e.preventDefault();
     3005                        } );
     3006
     3007                        // Publish and Draft buttons and submit
     3008
     3009                        $( '#draft-field' ).on( 'click', function() {
     3010                                submitPost( 'draft' );
     3011                        } );
     3012
     3013                        $( '#publish-field' ).on( 'click', function() {
     3014                                submitPost( 'publish' );
     3015                        } );
     3016
     3017                        monitorOptionsModal();
     3018                        monitorSidebarToggle();
     3019                        monitorPlaceholder();
     3020
     3021                        $( '#post-formats-select input' ).on( 'change', function() {
     3022                                var $this = $( this );
     3023
     3024                                if ( $this.is( ':checked' ) ) {
     3025                                        setPostFormatString( $this.attr( 'id' ).replace( /^post-format-(.+)$/, '$1' ) );
     3026                                }
     3027                        } );
     3028
     3029                        // Needs more work, doesn't detect when the other JS changes the value of #tax-input-post_tag
     3030                        $( '#tax-input-post_tag' ).on( 'change', function() {
     3031                                var val =  $( this ).val();
     3032                                $( '#post-option-tags' ).text( ( val.length ) ? val.replace( /,([^\s])/g, ', $1' ) : '' );
     3033                        } );
     3034
     3035                        $( window ).on( 'beforeunload.press-this', function() {
     3036                                if ( saveAlert || ( editor && editor.isDirty() ) ) {
     3037                                        return __( 'saveAlert' );
     3038                                }
     3039                        } );
     3040
     3041                        $( 'button.add-cat-toggle' ).on( 'click.press-this', function() {
     3042                                $( this ).toggleClass( 'is-toggled' );
     3043                                $( '.setting-modal .add-category' ).toggleClass( 'is-hidden' );
     3044                                $( '.categories-search-wrapper' ).toggleClass( 'is-hidden' );
     3045                        } );
     3046
     3047                        $( 'button.add-cat-submit' ).on( 'click.press-this', saveNewCategory );
     3048
     3049                        $( '.categories-search' ).on( 'keyup', function() {
     3050                                var search = $( this ).val().toLowerCase() || '';
     3051
     3052                                // Don't search when less thasn 3 extended ASCII chars
     3053                                if ( /[\x20-\xFF]+/.test( search ) && search.length < 2 ) {
     3054                                        return;
     3055                                }
     3056
     3057                                $.each( catsCache, function( i, cat ) {
     3058                                        cat.node.removeClass( 'is-hidden searched-parent' );
     3059                                } );
     3060
     3061                                if ( search ) {
     3062                                        $.each( catsCache, function( i, cat ) {
     3063                                                if ( cat.text.indexOf( search ) === -1 ) {
     3064                                                        cat.node.addClass( 'is-hidden' );
     3065                                                } else {
     3066                                                        cat.parents.addClass( 'searched-parent' );
     3067                                                }
     3068                                        } );
     3069                                }
     3070                        } );
     3071
     3072                        return true;
     3073                }
     3074
     3075                function refreshCatsCache() {
     3076                        $( '.categories-select' ).find( 'li' ).each( function() {
     3077                                var $this = $( this );
     3078
     3079                                catsCache.push( {
     3080                                        node: $this,
     3081                                        parents: $this.parents( 'li' ),
     3082                                        text: $this.children( 'label' ).text().toLowerCase()
     3083                                } );
     3084                        } );
     3085                }
     3086
     3087                // Let's go!
     3088                $( document ).ready( function() {
     3089                        render();
     3090                        monitor();
     3091                        refreshCatsCache();
     3092                });
     3093
     3094                // Expose public methods
     3095                // TODO: which are needed?
     3096                return {
     3097                        renderNotice: renderNotice,
     3098                        renderError: renderError
     3099                };
     3100        }
     3101
     3102        window.wp = window.wp || {};
     3103        window.wp.pressThis = new PressThis();
     3104
     3105}( jQuery, window ));
     3106/**
     3107 * PressThis App
     3108 *
     3109 */
     3110( function( $, window ) {
     3111        var PressThis = function() {
     3112                var editor,
     3113                        saveAlert             = false,
     3114                        $div                  = $( '<div>' ),
     3115                        siteConfig            = window.wpPressThisConfig || {},
     3116                        data                  = window.wpPressThisData || {},
     3117                        smallestWidth         = 128,
     3118                        interestingImages         = getInterestingImages( data ) || [],
     3119                        interestingEmbeds         = getInterestingEmbeds( data ) || [],
     3120                        hasEmptyTitleStr      = false,
     3121                        suggestedTitleStr     = getSuggestedTitle( data ),
     3122                        suggestedContentStr   = getSuggestedContent( data ),
     3123                        hasSetFocus           = false,
     3124                        catsCache             = [],
     3125                        transitionEndEvent    = ( function() {
     3126                                var style = document.documentElement.style;
     3127
     3128                                if ( typeof style.transition !== 'undefined' ) {
     3129                                        return 'transitionend';
     3130                                }
     3131
     3132                                if ( typeof style.WebkitTransition !== 'undefined' ) {
     3133                                        return 'webkitTransitionEnd';
     3134                                }
     3135
     3136                                return false;
     3137                        }() );
     3138
     3139                /* ***************************************************************
     3140                 * HELPER FUNCTIONS
     3141                 *************************************************************** */
     3142
     3143                /**
     3144                 * Emulates our PHP __() gettext function, powered by the strings exported in pressThisL10n.
     3145                 *
     3146                 * @param key string Key of the string to be translated, as found in pressThisL10n.
     3147                 * @returns string Original or translated string, or empty string if no key.
     3148                 */
     3149                function __( key ) {
     3150                        if ( key && window.pressThisL10n ) {
     3151                                return window.pressThisL10n[key] || key;
     3152                        }
     3153
     3154                        return key || '';
     3155                }
     3156
     3157                /**
     3158                 * Strips HTML tags
     3159                 *
     3160                 * @param string string Text to have the HTML tags striped out of.
     3161                 * @returns string Stripped text.
     3162                 */
     3163                function stripTags( string ) {
     3164                        string = string || '';
     3165
     3166                        return string
     3167                                .replace( /<!--[\s\S]*?(-->|$)/g, '' )
     3168                                .replace( /<(script|style)[^>]*>[\s\S]*?(<\/\1>|$)/ig, '' )
     3169                                .replace( /<\/?[a-z][^>]*>/ig, '' );
     3170                }
     3171
     3172                // TODO: needed?
     3173                function entityEncode( text ) {
     3174                        return $div.text( text ).html();
     3175                }
     3176
     3177                /**
     3178                 * Strip HTML tags and entity encode some of the HTML special chars.
     3179                 *
     3180                 * @param text string Text.
     3181                 * @returns string Sanitized text.
     3182                 */
     3183                function sanitizeText( text ) {
     3184                        text = stripTags( text );
     3185
     3186                        return text
     3187                                .replace( /\\/, '' )
     3188                                .replace( /</g, '&lt;' )
     3189                                .replace( />/g, '&gt;' )
     3190                                .replace( /"/g, '&quot;' )
     3191                                .replace( /'/g, '&#039;' );
     3192                }
     3193
     3194                /**
     3195                 * Allow only HTTP or protocol relative URLs.
     3196                 *
     3197                 * @param url string The URL.
     3198                 * @returns string Processed URL.
     3199                 */
     3200                function checkUrl( url ) {
     3201                        url = $.trim( url || '' );
     3202
     3203                        if ( /^(?:https?:)?\/\//.test( url ) ) {
     3204                                url = stripTags( url );
     3205                                return url.replace( /["\\]+/g, '' );
     3206                        }
     3207
     3208                        return '';
     3209                }
     3210
     3211                /**
     3212                 * Gets the source page's canonical link, based on passed location and meta data.
     3213                 *
     3214                 * @param data object Usually WpPressThis_App.data
     3215                 * @returns string Discovered canonical URL, or empty
     3216                 */
     3217                function getCanonicalLink( data ) {
     3218                        if ( ! data || data.length ) {
     3219                                return '';
     3220                        }
     3221
     3222                        var link = '';
     3223
     3224                        if ( data._links ) {
     3225                                if ( data._links.canonical && data._links.canonical.length ) {
     3226                                        link = data._links.canonical;
     3227                                }
     3228                        }
     3229
     3230                        if ( ! link.length && data.u ) {
     3231                                link = data.u;
     3232                        }
     3233
     3234                        if ( ! link.length && data._meta ) {
     3235                                if ( data._meta['twitter:url'] && data._meta['twitter:url'].length ) {
     3236                                        link = data._meta['twitter:url'];
     3237                                } else if ( data._meta['og:url'] && data._meta['og:url'].length ) {
     3238                                        link = data._meta['og:url'];
     3239                                }
     3240                        }
     3241
     3242                        return decodeURI( link );
     3243                }
     3244
     3245                /**
     3246                 * Gets the source page's site name, based on passed meta data.
     3247                 *
     3248                 * @param data object Usually WpPressThis_App.data
     3249                 * @returns string Discovered site name, or empty
     3250                 */
     3251                function getSourceSiteName( data ) {
     3252                        if ( ! data || data.length ) {
     3253                                return '';
     3254                        }
     3255
     3256                        var name='';
     3257
     3258                        if ( data._meta ) {
     3259                                if ( data._meta['og:site_name'] && data._meta['og:site_name'].length ) {
     3260                                        name = data._meta['og:site_name'];
     3261                                } else if ( data._meta['application-name'] && data._meta['application-name'].length ) {
     3262                                        name = data._meta['application-name'];
     3263                                }
     3264                        }
     3265
     3266                        return name.replace( /\\/g, '' );
     3267                }
     3268
     3269                /**
     3270                 * Gets the source page's title, based on passed title and meta data.
     3271                 *
     3272                 * @param data object Usually WpPressThis_App.data
     3273                 * @returns string Discovered page title, or empty
     3274                 */
     3275                function getSuggestedTitle( data ) {
     3276                        if ( ! data || data.length ) {
     3277                                return __( 'newPost' );
     3278                        }
     3279
     3280                        var title = '';
     3281
     3282                        if ( data.t ) {
     3283                                title = data.t;
     3284                        }
     3285
     3286                        if ( ! title.length && data._meta ) {
     3287                                if ( data._meta['twitter:title'] && data._meta['twitter:title'].length ) {
     3288                                        title = data._meta['twitter:title'];
     3289                                } else if ( data._meta['og:title'] && data._meta['og:title'].length ) {
     3290                                        title = data._meta['og:title'];
     3291                                } else if ( data._meta.title && data._meta.title.length ) {
     3292                                        title = data._meta.title;
     3293                                }
     3294                        }
     3295
     3296                        if ( ! title.length ) {
     3297                                title = __( 'newPost' );
     3298                                hasEmptyTitleStr = true;
     3299                        }
     3300
     3301                        return title.replace( /\\/g, '' );
     3302                }
     3303
     3304                /**
     3305                 * Gets the source page's suggested content, based on passed data (description, selection, etc).
     3306                 * Features a blockquoted excerpt, as well as content attribution, if any.
     3307                 *
     3308                 * @param data object Usually WpPressThis_App.data
     3309                 * @returns string Discovered content, or empty
     3310                 */
     3311                function getSuggestedContent( data ) {
     3312                        if ( ! data || data.length ) {
     3313                                return '';
     3314                        }
     3315
     3316                        var content   = '',
     3317                                title     = getSuggestedTitle( data ),
     3318                                url       = getCanonicalLink( data ),
     3319                                siteName  = getSourceSiteName( data );
     3320
     3321                        if ( data.s && data.s.length ) {
     3322                                content = data.s;
     3323                        } else if ( data._meta ) {
     3324                                if ( data._meta['twitter:description'] && data._meta['twitter:description'].length ) {
     3325                                        content = data._meta['twitter:description'];
     3326                                } else if ( data._meta['og:description'] && data._meta['og:description'].length ) {
     3327                                        content = data._meta['og:description'];
     3328                                } else if ( data._meta.description && data._meta.description.length ) {
     3329                                        content = data._meta.description;
     3330                                }
     3331                        }
     3332
     3333                        // Wrap suggested content in blockquote tag, if we have any.
     3334                        content = ( content.length ? '<blockquote class="press-this-suggested-content">' + sanitizeText( content ) + '</blockquote>' : '' );
     3335
     3336                        // Add a source attribution if there is one available.
     3337                        if ( ( ( title.length && __( 'newPost' ) !== title ) || siteName.length ) && url.length ) {
     3338                                content += '<p class="press-this-suggested-source">';
     3339                                content += __( 'source' );
     3340                                content += ' <cite>';
     3341                                content += __( 'sourceLink').replace( '%1$s', encodeURI( url ) ).replace( '%2$s', sanitizeText( title || siteName ) );
     3342                                content += '</cite></p>';
     3343                        }
     3344
     3345                        if ( ! content ) {
     3346                                content = '';
     3347                        }
     3348
     3349                        return content.replace( /\\/g, '' );
     3350                }
     3351
     3352                /**
     3353                 * Tests if what was passed as an embed URL is deemed to be embeddable in the editor.
     3354                 *
     3355                 * @param url string Passed URl, usually from WpPressThis_App.data._embed
     3356                 * @returns boolean
     3357                 */
     3358                function isEmbeddable( url ) {
     3359                        if ( ! url ) {
     3360                                return false;
     3361                        } else if ( url.match( /\/\/(m\.|www\.)?youtube\.com\/watch\?/ ) || url.match( /\/youtu\.be\/.+$/ ) ) {
     3362                                return true;
     3363                        } else if ( url.match( /\/\/vimeo\.com\/(.+\/)?[\d]+$/ ) ) {
     3364                                return true;
     3365                        } else if ( url.match( /\/\/(www\.)?dailymotion\.com\/video\/.+$/ ) ) {
     3366                                return true;
     3367                        } else if ( url.match( /\/\/soundcloud\.com\/.+$/ ) ) {
     3368                                return true;
     3369                        } else if ( url.match( /\/\/twitter\.com\/[^\/]+\/status\/[\d]+$/ ) ) {
     3370                                return true;
     3371                        } else if ( url.match( /\/\/vine\.co\/v\/[^\/]+/ ) ) {
     3372                                return true;
     3373                        }
     3374
     3375                        return false;
     3376                }
     3377
     3378                /**
     3379                 * Tests if what was passed as an image URL is deemed to be interesting enough to offer to the user for selection.
     3380                 *
     3381                 * @param src string Passed URl, usually from WpPressThis_App.data._ing
     3382                 * @returns boolean Test for false
     3383                 */
     3384                function isSrcUninterestingPath( src ) {
     3385                        if ( src.match( /\/ad[sx]{1}?\// ) ) {
     3386                                // Ads
     3387                                return true;
     3388                        } else if ( src.match( /(\/share-?this[^\.]+?\.[a-z0-9]{3,4})(\?.*)?$/ ) ) {
     3389                                // Share-this type button
     3390                                return true;
     3391                        } else if ( src.match( /\/(spinner|loading|spacer|blank|rss)\.(gif|jpg|png)/ ) ) {
     3392                                // Loaders, spinners, spacers
     3393                                return true;
     3394                        } else if ( src.match( /\/([^\.\/]+[-_]{1})?(spinner|loading|spacer|blank)s?([-_]{1}[^\.\/]+)?\.[a-z0-9]{3,4}/ ) ) {
     3395                                // Fancy loaders, spinners, spacers
     3396                                return true;
     3397                        } else if ( src.match( /([^\.\/]+[-_]{1})?thumb[^.]*\.(gif|jpg|png)$/ ) ) {
     3398                                // Thumbnails, too small, usually irrelevant to context
     3399                                return true;
     3400                        } else if ( src.match( /\/wp-includes\// ) ) {
     3401                                // Classic WP interface images
     3402                                return true;
     3403                        } else if ( src.match( /[^\d]{1}\d{1,2}x\d+\.(gif|jpg|png)$/ ) ) {
     3404                                // Most often tiny buttons/thumbs (< 100px wide)
     3405                                return true;
     3406                        } else if ( src.indexOf( '/g.gif' ) > -1 ) {
     3407                                // Classic WP stats gif
     3408                                return true;
     3409                        } else if ( src.indexOf( '/pixel.mathtag.com' ) > -1 ) {
     3410                                // See mathtag.com
     3411                                return true;
     3412                        }
     3413                        return false;
     3414                }
     3415
     3416                /**
     3417                 * Get a list of valid embeds from what was passed via WpPressThis_App.data._embed on page load.
     3418                 *
     3419                 * @returns array
     3420                 */
     3421                function getInterestingEmbeds() {
     3422                        var embeds             = data._embed || [],
     3423                                interestingEmbeds  = [],
     3424                                alreadySelected    = [];
     3425
     3426                        if ( embeds.length ) {
     3427                                $.each( embeds, function ( i, src ) {
     3428                                        if ( !src || !src.length ) {
     3429                                                // Skip: no src value
     3430                                                return;
     3431                                        } else if ( !isEmbeddable( src ) ) {
     3432                                                // Skip: not deemed embeddable
     3433                                                return;
     3434                                        }
     3435
     3436                                        var schemelessSrc = src.replace( /^https?:/, '' );
     3437
     3438                                        if ( $.inArray( schemelessSrc, alreadySelected ) > -1 ) {
     3439                                                // Skip: already shown
     3440                                                return;
     3441                                        }
     3442
     3443                                        interestingEmbeds.push( src );
     3444                                        alreadySelected.push( schemelessSrc );
     3445                                } );
     3446                        }
     3447
     3448                        return interestingEmbeds;
     3449                }
     3450
     3451                /**
     3452                 * Get what is likely the most valuable image from what was passed via WpPressThis_App.data._img and WpPressThis_App.data._meta on page load.
     3453                 *
     3454                 * @returns array
     3455                 */
     3456                function getFeaturedImage( data ) {
     3457                        var featured = '';
     3458
     3459                        if ( ! data || ! data._meta ) {
     3460                                return '';
     3461                        }
     3462
     3463                        if ( data._meta['twitter:image0:src'] && data._meta['twitter:image0:src'].length ) {
     3464                                featured = data._meta['twitter:image0:src'];
     3465                        } else if ( data._meta['twitter:image0'] && data._meta['twitter:image0'].length ) {
     3466                                featured = data._meta['twitter:image0'];
     3467                        } else if ( data._meta['twitter:image:src'] && data._meta['twitter:image:src'].length ) {
     3468                                featured = data._meta['twitter:image:src'];
     3469                        } else if ( data._meta['twitter:image'] && data._meta['twitter:image'].length ) {
     3470                                featured = data._meta['twitter:image'];
     3471                        } else if ( data._meta['og:image'] && data._meta['og:image'].length ) {
     3472                                featured = data._meta['og:image'];
     3473                        } else if ( data._meta['og:image:secure_url'] && data._meta['og:image:secure_url'].length ) {
     3474                                featured = data._meta['og:image:secure_url'];
     3475                        }
     3476
     3477                        featured = checkUrl( featured );
     3478
     3479                        return ( isSrcUninterestingPath( featured ) ) ? '' : featured;
     3480                }
     3481
     3482                /**
     3483                 * Get a list of valid images from what was passed via WpPressThis_App.data._img and WpPressThis_App.data._meta on page load.
     3484                 *
     3485                 * @returns array
     3486                 */
     3487                function getInterestingImages( data ) {
     3488                        var imgs             = data._img || [],
     3489                                featuredPict     = getFeaturedImage( data ) || '',
     3490                                interestingImgs  = [],
     3491                                alreadySelected  = [];
     3492
     3493                        if ( featuredPict.length ) {
     3494                                interestingImgs.push( featuredPict );
     3495                                alreadySelected.push( featuredPict.replace(/^https?:/, '') );
     3496                        }
     3497
     3498                        if ( imgs.length ) {
     3499                                $.each( imgs, function ( i, src ) {
     3500                                        src = src.replace( /http:\/\/[\d]+\.gravatar\.com\//, 'https://secure.gravatar.com/' );
     3501                                        src = checkUrl( src );
     3502
     3503                                        if ( ! src || ! src.length ) {
     3504                                                // Skip: no src value
     3505                                                return;
     3506                                        }
     3507
     3508                                        var schemelessSrc = src.replace( /^https?:/, '' );
     3509
     3510                                        if ( Array.prototype.indexOf && alreadySelected.indexOf( schemelessSrc ) > -1 ) {
     3511                                                // Skip: already shown
     3512                                                return;
     3513                                        } else if ( isSrcUninterestingPath( src ) ) {
     3514                                                // Skip: spinner, stat, ad, or spacer pict
     3515                                                return;
     3516                                        } else if ( src.indexOf( 'avatar' ) > -1 && interestingImgs.length >= 15 ) {
     3517                                                // Skip:  some type of avatar and we've already gathered more than 23 diff images to show
     3518                                                return;
     3519                                        }
     3520
     3521                                        interestingImgs.push( src );
     3522                                        alreadySelected.push( schemelessSrc );
     3523                                } );
     3524                        }
     3525
     3526                        return interestingImgs;
     3527                }
     3528
     3529                /**
     3530                 * Show UX spinner
     3531                 */
     3532                function showSpinner() {
     3533                        $( '#spinner' ).addClass( 'show' );
     3534                        $( '.post-actions button' ).each( function() {
     3535                                $( this ).attr( 'disabled', 'disabled' );
     3536                        } );
     3537                }
     3538
     3539                /**
     3540                 * Hide UX spinner
     3541                 */
     3542                function hideSpinner() {
     3543                        $( '#spinner' ).removeClass( 'show' );
     3544                        $( '.post-actions button' ).each( function() {
     3545                                $( this ).removeAttr( 'disabled' );
     3546                        } );
     3547                }
     3548
     3549                /**
     3550                 * Submit the post form via AJAX, and redirect to the proper screen if published vs saved as a draft.
     3551                 *
     3552                 * @param action string publish|draft
     3553                 */
     3554                function submitPost( action ) {
     3555                        saveAlert = false;
     3556                        showSpinner();
     3557
     3558                        var $form = $( '#pressthis-form' );
     3559
     3560                        if ( 'publish' === action ) {
     3561                                $( '#post_status' ).val( 'publish' );
     3562                        }
     3563
     3564                        editor && editor.save();
     3565
     3566                        $( '#title-field' ).val( sanitizeText( $( '#title-container' ).text() ) );
     3567
     3568                        // Make sure to flush out the tags with tagBox before saving
     3569                        if ( window.tagBox ) {
     3570                                $( 'div.tagsdiv' ).each( function() {
     3571                                        window.tagBox.flushTags( this, false, 1 );
     3572                                } );
     3573                        }
     3574
     3575                        var data = $form.serialize();
     3576
     3577                        $.ajax( {
     3578                                type: 'post',
     3579                                url: window.ajaxurl,
     3580                                data: data,
     3581                                success: function( response ) {
     3582                                        if ( ! response.success ) {
     3583                                                renderError( response.data.errorMessage );
     3584                                                hideSpinner();
     3585                                        } else if ( response.data.redirect ) {
     3586                                                if ( window.opener && siteConfig.redir_in_parent ) {
     3587                                                        try {
     3588                                                                window.opener.location.href = response.data.redirect;
     3589                                                        } catch( er ) {}
     3590
     3591                                                        window.self.close();
     3592                                                } else {
     3593                                                        window.location.href = response.data.redirect;
     3594                                                }
     3595                                        }
     3596                                }
     3597                        } );
     3598                }
     3599
     3600                /**
     3601                 * Inserts the media a user has selected from the presented list inside the editor, as an image or embed, based on type
     3602                 *
     3603                 * @param type string img|embed
     3604                 * @param src string Source URL
     3605                 * @param link string Optional destination link, for images (defaults to src)
     3606                 */
     3607                function insertSelectedMedia( type, src, link ) {
     3608                        var newContent = '';
     3609
     3610                        if ( ! editor ) {
     3611                                return;
     3612                        }
     3613
     3614                        src = checkUrl( src );
     3615                        link = checkUrl( link );
     3616
     3617                        if ( 'img' === type ) {
     3618                                if ( ! link || ! link.length ) {
     3619                                        link = src;
     3620                                }
     3621
     3622                                newContent = '<a href="' + link + '"><img class="alignnone size-full" src="' + src + '" /></a>\n';
     3623                        } else {
     3624                                newContent = '[embed]' + src + '[/embed]\n';
     3625                        }
     3626
     3627                        if ( ! hasSetFocus ) {
     3628                                // Append to top of content on 1st media insert
     3629                                editor.setContent( newContent + editor.getContent() );
     3630                        } else {
     3631                                // Or add where the cursor was last positioned in TinyMCE
     3632                                editor.execCommand( 'mceInsertContent', false, newContent );
     3633                        }
     3634
     3635                        hasSetFocus = true;
     3636                }
     3637
     3638                /**
     3639                 * Adds the currently selected post format next to the option, in the options panel.
     3640                 *
     3641                 * @param format string Post format to be displayed
     3642                 */
     3643                function setPostFormatString( format ) {
     3644                        if ( ! format || ! siteConfig || ! siteConfig.post_formats || ! siteConfig.post_formats[ format ] ) {
     3645                                return;
     3646                        }
     3647                        $( '#post-option-post-format' ).text( siteConfig.post_formats[ format ] );
     3648                }
     3649
     3650                /**
     3651                 * Save a new user-generated category via AJAX
     3652                 */
     3653                function saveNewCategory() {
     3654                        var data = {
     3655                                action: 'press-this-add-category',
     3656                                post_id: $( '#post_ID' ).val() || 0,
     3657                                name: $( '#new-category' ).val() || '',
     3658                                new_cat_nonce: $( '#_ajax_nonce-add-category' ).val() || '',
     3659                                parent: $( '#new-category-parent' ).val() || 0
     3660                        };
     3661
     3662                        $.post( window.ajaxurl, data, function( response ) {
     3663                                if ( ! response.success ) {
     3664                                        renderError( response.data.errorMessage );
     3665                                } else {
     3666                                        // TODO: change if/when the html changes.
     3667                                        var $parent, $ul,
     3668                                                $wrap = $( 'ul.categories-select' );
     3669
     3670                                        $.each( response.data, function( i, newCat ) {
     3671                                                var $node = $( '<li>' ).attr( 'id', 'category-' + newCat.term_id )
     3672                                                        .append( $( '<label class="selectit">' ).text( newCat.name )
     3673                                                                .append( $( '<input type="checkbox" name="post_category[]" checked>' ).attr( 'value', newCat.term_id ) ) );
     3674
     3675                                                if ( newCat.parent ) {
     3676                                                        if ( ! $ul || ! $ul.length ) {
     3677                                                                $parent = $wrap.find( '#category-' + newCat.parent );
     3678                                                                $ul = $parent.find( 'ul.children:first' );
     3679
     3680                                                                if ( ! $ul.length ) {
     3681                                                                        $ul = $( '<ul class="children">' ).appendTo( $parent );
     3682                                                                }
     3683                                                        }
     3684
     3685                                                        $ul.append( $node );
     3686                                                        // TODO: set focus on
     3687                                                } else {
     3688                                                        $wrap.prepend( $node );
     3689                                                }
     3690                                        } );
     3691
     3692                                        refreshCatsCache();
     3693                                }
     3694                        } );
     3695                }
     3696
     3697                /* ***************************************************************
     3698                 * RENDERING FUNCTIONS
     3699                 *************************************************************** */
     3700
     3701                /**
     3702                 * Hide the form letting users enter a URL to be scanned, if a URL was already passed.
     3703                 */
     3704                function renderToolsVisibility() {
     3705                        if ( data.u && data.u.match( /^https?:/ ) ) {
     3706                                $( '#scanbar' ).hide();
     3707                        }
     3708                }
     3709
     3710                /**
     3711                 * Render error notice
     3712                 *
     3713                 * @param msg string Notice/error message
     3714                 * @param error string error|notice CSS class for display
     3715                 */
     3716                function renderNotice( msg, error ) {
     3717                        var $alerts = $( '.editor-wrapper div.alerts' ),
     3718                                className = error ? 'is-error' : 'is-notice';
     3719
     3720                        $alerts.append( $( '<p class="' + className + '">' ).text( msg ) );
     3721                }
     3722
     3723                /**
     3724                 * Render error notice
     3725                 *
     3726                 * @param msg string Error message
     3727                 */
     3728                function renderError( msg ) {
     3729                        renderNotice( msg, true );
     3730                }
     3731
     3732                /**
     3733                 * Render notices on page load, if any already
     3734                 */
     3735                function renderStartupNotices() {
     3736                        // Render errors sent in the data, if any
     3737                        if ( data.errors && data.errors.length ) {
     3738                                $.each( data.errors, function( i, msg ) {
     3739                                        renderError( msg );
     3740                                } );
     3741                        }
     3742
     3743                        // Prompt user to upgrade their bookmarklet if there is a version mismatch.
     3744                        if ( data.v && data._version && data.v !== data._version ) {
     3745                                $( '.should-upgrade-bookmarklet' ).removeClass( 'is-hidden' );
     3746                        }
     3747                }
     3748
     3749                /**
     3750                 * Render the suggested title, if any
     3751                 */
     3752                function renderSuggestedTitle() {
     3753                        var suggestedTitle = suggestedTitleStr || '',
     3754                                $title = $( '#title-container' );
     3755
     3756                        if ( ! hasEmptyTitleStr ) {
     3757                                $( '#title-field' ).val( suggestedTitle );
     3758                                $title.text( suggestedTitle );
     3759                                $( '.post-title-placeholder' ).addClass( 'is-hidden' );
     3760                        }
     3761
     3762                        $title.on( 'keyup', function() {
     3763                                saveAlert = true;
     3764                        }).on( 'paste', function() {
     3765                                saveAlert = true;
     3766
     3767                                setTimeout( function() {
     3768                                        $title.text( $title.text() );
     3769                                }, 100 );
     3770                        } );
     3771
     3772                }
     3773
     3774                /**
     3775                 * Render the suggested content, if any
     3776                 */
     3777                function renderSuggestedContent() {
     3778                        if ( ! suggestedContentStr || ! suggestedContentStr.length ) {
     3779                                return;
     3780                        }
     3781
     3782                        if ( ! editor ) {
     3783                                editor = window.tinymce.get( 'pressthis' );
     3784                        }
     3785
     3786                        if ( editor ) {
     3787                                editor.setContent( suggestedContentStr );
     3788                                editor.on( 'focus', function() {
     3789                                        hasSetFocus = true;
     3790                                } );
     3791                        }
     3792
     3793                }
     3794
     3795                /**
     3796                 * Render the detected images and embed for selection, if any
     3797                 */
     3798                function renderDetectedMedia() {
     3799                        var mediaContainer = $( '#featured-media-container'),
     3800                                listContainer  = $( '#all-media-container' ),
     3801                                found          = 0;
     3802
     3803                        listContainer.empty();
     3804
     3805                        if ( ( interestingEmbeds && interestingEmbeds.length ) || ( interestingImages && interestingImages.length ) ) {
     3806                                listContainer.append( '<h2 class="screen-reader-text">' + __( 'allMediaHeading' ) + '</h2><ul class="wppt-all-media-list"/>' );
     3807                        }
     3808
     3809                        if ( interestingEmbeds && interestingEmbeds.length ) {
     3810                                $.each( interestingEmbeds, function ( i, src ) {
     3811                                        src = checkUrl( src );
     3812
     3813                                        if ( ! isEmbeddable( src ) ) {
     3814                                                return;
     3815                                        }
     3816
     3817                                        var displaySrc = '',
     3818                                                cssClass   = 'suggested-media-thumbnail suggested-media-embed';
     3819
     3820                                        if ( src.indexOf( 'youtube.com/' ) > -1 ) {
     3821                                                displaySrc = 'https://i.ytimg.com/vi/' + src.replace( /.+v=([^&]+).*/, '$1' ) + '/hqdefault.jpg';
     3822                                                cssClass += ' is-video';
     3823                                        } else if ( src.indexOf( 'youtu.be/' ) > -1 ) {
     3824                                                displaySrc = 'https://i.ytimg.com/vi/' + src.replace( /\/([^\/])$/, '$1' ) + '/hqdefault.jpg';
     3825                                                cssClass += ' is-video';
     3826                                        } else if ( src.indexOf( 'dailymotion.com' ) > -1 ) {
     3827                                                displaySrc = src.replace( '/video/', '/thumbnail/video/' );
     3828                                                cssClass += ' is-video';
     3829                                        } else if ( src.indexOf( 'soundcloud.com' ) > -1 ) {
     3830                                                cssClass += ' is-audio';
     3831                                        } else if ( src.indexOf( 'twitter.com' ) > -1 ) {
     3832                                                cssClass += ' is-tweet';
     3833                                        } else {
     3834                                                cssClass += ' is-video';
     3835                                        }
     3836
     3837                                        $( '<li></li>', {
     3838                                                'id': 'embed-' + i + '-container',
     3839                                                'class': cssClass,
     3840                                                'tabindex': '0'
     3841                                        } ).css( {
     3842                                                'background-image': ( displaySrc.length ) ? 'url(' + displaySrc + ')' : null
     3843                                        } ).html(
     3844                                                '<span class="screen-reader-text">' + __( 'suggestedEmbedAlt' ).replace( '%d', i + 1 ) + '</span>'
     3845                                        ).on( 'click keypress', function ( e ) {
     3846                                                if ( e.type === 'click' || e.which === 13 ) {
     3847                                                        insertSelectedMedia( 'embed',src );
     3848                                                }
     3849                                        } ).appendTo( '.wppt-all-media-list', listContainer );
     3850
     3851                                        found++;
     3852                                } );
     3853                        }
     3854
     3855                        if ( interestingImages && interestingImages.length ) {
     3856                                $.each( interestingImages, function ( i, src ) {
     3857                                        src = checkUrl( src );
     3858
     3859                                        var displaySrc = src.replace(/^(http[^\?]+)(\?.*)?$/, '$1');
     3860                                        if ( src.indexOf( 'files.wordpress.com/' ) > -1 ) {
     3861                                                displaySrc = displaySrc.replace(/\?.*$/, '') + '?w=' + smallestWidth;
     3862                                        } else if ( src.indexOf( 'gravatar.com/' ) > -1 ) {
     3863                                                displaySrc = displaySrc.replace( /\?.*$/, '' ) + '?s=' + smallestWidth;
     3864                                        } else {
     3865                                                displaySrc = src;
     3866                                        }
     3867
     3868                                        $( '<li></li>', {
     3869                                                'id': 'img-' + i + '-container',
     3870                                                'class': 'suggested-media-thumbnail is-image',
     3871                                                'tabindex': '0'
     3872                                        } ).css( {
     3873                                                'background-image': 'url(' + displaySrc + ')'
     3874                                        } ).html(
     3875                                                '<span class="screen-reader-text">' +__( 'suggestedImgAlt' ).replace( '%d', i + 1 ) + '</span>'
     3876                                        ).on( 'click keypress', function ( e ) {
     3877                                                if ( e.type === 'click' || e.which === 13 ) {
     3878                                                        insertSelectedMedia( 'img', src, data.u );
     3879                                                }
     3880                                        } ).appendTo( '.wppt-all-media-list', listContainer );
     3881
     3882                                        found++;
     3883                                } );
     3884                        }
     3885
     3886                        if ( ! found ) {
     3887                                mediaContainer.removeClass( 'all-media-visible' ).addClass( 'no-media');
     3888                                return;
     3889                        }
     3890
     3891                        mediaContainer.removeClass( 'no-media' ).addClass( 'all-media-visible' );
     3892                }
     3893
     3894                /* ***************************************************************
     3895                 * MONITORING FUNCTIONS
     3896                 *************************************************************** */
     3897
     3898                /**
     3899                 * Interactive navigation behavior for the options modal (post format, tags, categories)
     3900                 */
     3901                function monitorOptionsModal() {
     3902                        var isOffScreen   = 'is-off-screen',
     3903                                isHidden      = 'is-hidden',
     3904                                $postOptions  = $( '.post-options' ),
     3905                                $postOption   = $( '.post-option' ),
     3906                                $settingModal = $( '.setting-modal' ),
     3907                                $modalClose   = $( '.modal-close' );
     3908
     3909                        $postOption.on( 'click', function( event ) {
     3910                                var index = $( this ).index(),
     3911                                        $targetSettingModal = $settingModal.eq( index );
     3912
     3913                                event.preventDefault();
     3914
     3915                                $postOptions
     3916                                        .addClass( isOffScreen )
     3917                                        .one( transitionEndEvent, function() {
     3918                                                $( this ).addClass( isHidden );
     3919                                        } );
     3920
     3921                                $targetSettingModal
     3922                                        .removeClass( isOffScreen + ' ' + isHidden )
     3923                                        .one( transitionEndEvent, function() {
     3924                                                $( this ).find( $modalClose ).focus();
     3925                                        } );
     3926                        } );
     3927
     3928                        $modalClose.on( 'click', function( event ) {
     3929                                var $targetSettingModal = $( this ).parent(),
     3930                                        index = $targetSettingModal.index();
     3931
     3932                                event.preventDefault();
     3933
     3934                                $postOptions
     3935                                        .removeClass( isOffScreen + ' ' + isHidden );
     3936
     3937                                $targetSettingModal
     3938                                        .addClass( isOffScreen )
     3939                                        .one( transitionEndEvent, function() {
     3940                                                $( this ).addClass( isHidden );
     3941                                        } );
     3942
     3943                                // For browser that don't support transitionend.
     3944                                if ( ! transitionEndEvent ) {
     3945                                        setTimeout( function() {
     3946                                                $targetSettingModal.addClass( isHidden );
     3947                                        }, 350 );
     3948                                }
     3949
     3950                                $postOption.eq( index - 1 ).focus();
     3951                        } );
     3952                }
     3953
     3954                /**
     3955                 * Interactive behavior for the sidebar toggle, to show the options modals
     3956                 */
     3957                function monitorSidebarToggle() {
     3958                        var $optOpen  = $( '.options-open' ),
     3959                                $optClose = $( '.options-close' ),
     3960                                $postOption = $( '.post-option' ),
     3961                                $sidebar = $( '.options-panel' ),
     3962                                $postActions = $( '.press-this-actions' ),
     3963                                $scanbar = $( '#scanbar' ),
     3964                                isOffScreen = 'is-off-screen',
     3965                                isHidden = 'is-hidden',
     3966                                ifOffHidden = isOffScreen + ' ' + isHidden;
     3967
     3968                        $optOpen.on( 'click', function(){
     3969                                $optOpen.addClass( isHidden );
     3970                                $optClose.removeClass( isHidden );
     3971                                $postActions.addClass( isHidden );
     3972                                $scanbar.addClass( isHidden );
     3973
     3974                                $sidebar
     3975                                        .removeClass( ifOffHidden )
     3976                                        .one( 'transitionend', function() {
     3977                                                $postOption.eq( 0 ).focus();
     3978                                        } );
     3979                        } );
     3980
     3981                        $optClose.on( 'click', function(){
     3982                                $optClose.addClass( isHidden );
     3983                                $optOpen.removeClass( isHidden );
     3984                                $postActions.removeClass( isHidden );
     3985                                $scanbar.removeClass( isHidden );
     3986
     3987                                $sidebar
     3988                                        .addClass( isOffScreen )
     3989                                        .one( 'transitionend', function() {
     3990                                                $( this ).addClass( isHidden );
     3991                                                // Reset to options list
     3992                                                $( '.post-options' ).removeClass( ifOffHidden );
     3993                                                $( '.setting-modal').addClass( ifOffHidden );
     3994                                        } );
     3995                        } );
     3996                }
     3997
     3998                /**
     3999                 * Interactive behavior for the post title's field placeholder
     4000                 */
     4001                function monitorPlaceholder() {
     4002                        var $selector = $( '#title-container'),
     4003                                $placeholder = $('.post-title-placeholder');
     4004
     4005                        $selector.on( 'focus', function() {
     4006                                $placeholder.addClass('is-hidden');
     4007                        } );
     4008
     4009                        $selector.on( 'blur', function() {
     4010                                var textLength = $( this ).text().length;
     4011
     4012                                if ( ! textLength ) {
     4013                                        $placeholder.removeClass('is-hidden');
     4014                                }
     4015                        } );
     4016                }
     4017
     4018                /* ***************************************************************
     4019                 * PROCESSING FUNCTIONS
     4020                 *************************************************************** */
     4021
     4022                /**
     4023                 * Calls all the rendring related functions to happen on page load
     4024                 */
     4025                function render(){
     4026                        // We're on!
     4027                        renderToolsVisibility();
     4028                        renderSuggestedTitle();
     4029                        renderDetectedMedia();
     4030                        $( document ).on( 'tinymce-editor-init', renderSuggestedContent );
     4031                        renderStartupNotices();
     4032                }
     4033
     4034                /**
     4035                 * Set app events and other state monitoring related code.
     4036                 */
     4037                function monitor(){
     4038                        $( '#current-site a').click( function( e ) {
     4039                                e.preventDefault();
     4040                        } );
     4041
     4042                        // Publish and Draft buttons and submit
     4043
     4044                        $( '#draft-field' ).on( 'click', function() {
     4045                                submitPost( 'draft' );
     4046                        } );
     4047
     4048                        $( '#publish-field' ).on( 'click', function() {
     4049                                submitPost( 'publish' );
     4050                        } );
     4051
     4052                        monitorOptionsModal();
     4053                        monitorSidebarToggle();
     4054                        monitorPlaceholder();
     4055
     4056                        $( '#post-formats-select input' ).on( 'change', function() {
     4057                                var $this = $( this );
     4058
     4059                                if ( $this.is( ':checked' ) ) {
     4060                                        setPostFormatString( $this.attr( 'id' ).replace( /^post-format-(.+)$/, '$1' ) );
     4061                                }
     4062                        } );
     4063
     4064                        // Needs more work, doesn't detect when the other JS changes the value of #tax-input-post_tag
     4065                        $( '#tax-input-post_tag' ).on( 'change', function() {
     4066                                var val =  $( this ).val();
     4067                                $( '#post-option-tags' ).text( ( val.length ) ? val.replace( /,([^\s])/g, ', $1' ) : '' );
     4068                        } );
     4069
     4070                        $( window ).on( 'beforeunload.press-this', function() {
     4071                                if ( saveAlert || ( editor && editor.isDirty() ) ) {
     4072                                        return __( 'saveAlert' );
     4073                                }
     4074                        } );
     4075
     4076                        $( 'button.add-cat-toggle' ).on( 'click.press-this', function() {
     4077                                $( this ).toggleClass( 'is-toggled' );
     4078                                $( '.setting-modal .add-category' ).toggleClass( 'is-hidden' );
     4079                                $( '.categories-search-wrapper' ).toggleClass( 'is-hidden' );
     4080                        } );
     4081
     4082                        $( 'button.add-cat-submit' ).on( 'click.press-this', saveNewCategory );
     4083
     4084                        $( '.categories-search' ).on( 'keyup', function() {
     4085                                var search = $( this ).val().toLowerCase() || '';
     4086
     4087                                // Don't search when less thasn 3 extended ASCII chars
     4088                                if ( /[\x20-\xFF]+/.test( search ) && search.length < 2 ) {
     4089                                        return;
     4090                                }
     4091
     4092                                $.each( catsCache, function( i, cat ) {
     4093                                        cat.node.removeClass( 'is-hidden searched-parent' );
     4094                                } );
     4095
     4096                                if ( search ) {
     4097                                        $.each( catsCache, function( i, cat ) {
     4098                                                if ( cat.text.indexOf( search ) === -1 ) {
     4099                                                        cat.node.addClass( 'is-hidden' );
     4100                                                } else {
     4101                                                        cat.parents.addClass( 'searched-parent' );
     4102                                                }
     4103                                        } );
     4104                                }
     4105                        } );
     4106
     4107                        return true;
     4108                }
     4109
     4110                function refreshCatsCache() {
     4111                        $( '.categories-select' ).find( 'li' ).each( function() {
     4112                                var $this = $( this );
     4113
     4114                                catsCache.push( {
     4115                                        node: $this,
     4116                                        parents: $this.parents( 'li' ),
     4117                                        text: $this.children( 'label' ).text().toLowerCase()
     4118                                } );
     4119                        } );
     4120                }
     4121
     4122                // Let's go!
     4123                $( document ).ready( function() {
     4124                        render();
     4125                        monitor();
     4126                        refreshCatsCache();
     4127                });
     4128
     4129                // Expose public methods
     4130                // TODO: which are needed?
     4131                return {
     4132                        renderNotice: renderNotice,
     4133                        renderError: renderError
     4134                };
     4135        }
     4136
     4137        window.wp = window.wp || {};
     4138        window.wp.pressThis = new PressThis();
     4139
     4140}( jQuery, window ));
  • src/wp-admin/options-writing.php

     
    113113?>
    114114</table>
    115115
    116 <h3 class="title"><?php _e('Press This') ?></h3>
    117 <p><?php _e('Press This is a bookmarklet: a little app that runs in your browser and lets you grab bits of the web.');?></p>
    118 <p><?php _e('Use Press This to clip text, images and videos from any web page. Then edit and add more straight from Press This before you save or publish it in a post on your site.'); ?></p>
    119 <p><?php _e('Drag-and-drop the following link to your bookmarks bar or right click it and add it to your favorites for a posting shortcut.') ?></p>
    120 <p class="pressthis"><a onclick="return false;" oncontextmenu="if(window.navigator.userAgent.indexOf('WebKit')!=-1||window.navigator.userAgent.indexOf('MSIE')!=-1){jQuery('.pressthis-code').show().find('textarea').focus().select();return false;}" href="<?php echo htmlspecialchars( get_shortcut_link() ); ?>"><span><?php _e('Press This') ?></span></a></p>
    121 <div class="pressthis-code" style="display:none;">
    122         <p class="description"><?php _e('If your bookmarks toolbar is hidden: copy the code below, open your Bookmarks manager, create new bookmark, type Press This into the name field and paste the code into the URL field.') ?></p>
    123         <p><textarea rows="5" cols="120" readonly="readonly"><?php echo htmlspecialchars( get_shortcut_link() ); ?></textarea></p>
    124 </div>
    125 
    126116<?php
    127117/** This filter is documented in wp-admin/options.php */
    128118if ( apply_filters( 'enable_post_by_email_configuration', true ) ) {
  • src/wp-admin/press-this.php

     
    1111/** WordPress Administration Bootstrap */
    1212require_once( dirname( __FILE__ ) . '/admin.php' );
    1313
    14 header('Content-Type: ' . get_option('html_type') . '; charset=' . get_option('blog_charset'));
    15 
    1614if ( ! current_user_can( 'edit_posts' ) || ! current_user_can( get_post_type_object( 'post' )->cap->create_posts ) )
    1715        wp_die( __( 'Cheatin&#8217; uh?' ), 403 );
    1816
    19 /**
    20  * Press It form handler.
    21  *
    22  * @since 2.6.0
    23  *
    24  * @return int Post ID
    25  */
    26 function press_it() {
    27 
    28         $post = get_default_post_to_edit();
    29         $post = get_object_vars($post);
    30         $post_ID = $post['ID'] = (int) $_POST['post_id'];
    31 
    32         if ( !current_user_can('edit_post', $post_ID) )
    33                 wp_die(__('You are not allowed to edit this post.'));
    34 
    35         $post['post_category'] = isset($_POST['post_category']) ? $_POST['post_category'] : '';
    36         $post['tax_input'] = isset($_POST['tax_input']) ? $_POST['tax_input'] : '';
    37         $post['post_title'] = isset($_POST['title']) ? $_POST['title'] : '';
    38         $content = isset($_POST['content']) ? $_POST['content'] : '';
    39 
    40         $upload = false;
    41         if ( !empty($_POST['photo_src']) && current_user_can('upload_files') ) {
    42                 foreach( (array) $_POST['photo_src'] as $key => $image) {
    43                         // See if files exist in content - we don't want to upload non-used selected files.
    44                         if ( strpos($_POST['content'], htmlspecialchars($image)) !== false ) {
    45                                 $desc = isset($_POST['photo_description'][$key]) ? $_POST['photo_description'][$key] : '';
    46                                 $upload = media_sideload_image($image, $post_ID, $desc);
    47 
    48                                 // Replace the POSTED content <img> with correct uploaded ones. Regex contains fix for Magic Quotes
    49                                 if ( !is_wp_error($upload) )
    50                                         $content = preg_replace('/<img ([^>]*)src=\\\?(\"|\')'.preg_quote(htmlspecialchars($image), '/').'\\\?(\2)([^>\/]*)\/*>/is', $upload, $content);
    51                         }
    52                 }
    53         }
    54         // Set the post_content and status.
    55         $post['post_content'] = $content;
    56         if ( isset( $_POST['publish'] ) && current_user_can( 'publish_posts' ) )
    57                 $post['post_status'] = 'publish';
    58         elseif ( isset( $_POST['review'] ) )
    59                 $post['post_status'] = 'pending';
    60         else
    61                 $post['post_status'] = 'draft';
    62 
    63         // Error handling for media_sideload.
    64         if ( is_wp_error($upload) ) {
    65                 wp_delete_post($post_ID);
    66                 wp_die( esc_html( $upload->get_error_message() ) );
    67         } else {
    68                 // Post formats.
    69                 if ( isset( $_POST['post_format'] ) ) {
    70                         if ( current_theme_supports( 'post-formats', $_POST['post_format'] ) )
    71                                 set_post_format( $post_ID, $_POST['post_format'] );
    72                         elseif ( '0' == $_POST['post_format'] )
    73                                 set_post_format( $post_ID, false );
    74                 }
    75 
    76                 $post_ID = wp_update_post($post);
    77         }
    78 
    79         return $post_ID;
     17if ( empty( $GLOBALS['wp_press_this'] ) ) {
     18        include( ABSPATH . 'wp-admin/includes/class-wp-press-this.php' );
    8019}
    8120
    82 // For submitted posts.
    83 if ( isset($_REQUEST['action']) && 'post' == $_REQUEST['action'] ) {
    84         check_admin_referer('press-this');
    85         $posted = $post_ID = press_it();
    86 } else {
    87         $post = get_default_post_to_edit('post', true);
    88         $post_ID = $post->ID;
    89 }
    90 
    91 // Set Variables
    92 $title = isset( $_GET['t'] ) ? trim( strip_tags( html_entity_decode( wp_unslash( $_GET['t'] ) , ENT_QUOTES) ) ) : '';
    93 
    94 $selection = '';
    95 if ( !empty($_GET['s']) ) {
    96         $selection = str_replace('&apos;', "'", wp_unslash($_GET['s']));
    97         $selection = trim( htmlspecialchars( html_entity_decode($selection, ENT_QUOTES) ) );
    98 }
    99 
    100 if ( ! empty($selection) ) {
    101         $selection = preg_replace('/(\r?\n|\r)/', '</p><p>', $selection);
    102         $selection = '<p>' . str_replace('<p></p>', '', $selection) . '</p>';
    103 }
    104 
    105 $url = isset($_GET['u']) ? esc_url($_GET['u']) : '';
    106 $image = isset($_GET['i']) ? $_GET['i'] : '';
    107 
    108 if ( !empty($_REQUEST['ajax']) ) {
    109         switch ($_REQUEST['ajax']) {
    110                 case 'video': ?>
    111                         <script type="text/javascript">
    112                                 jQuery('.select').click(function() {
    113                                         append_editor(jQuery('#embed-code').val());
    114                                         jQuery('#extra-fields').hide();
    115                                         jQuery('#extra-fields').html('');
    116                                 });
    117                                 jQuery('.close').click(function() {
    118                                         jQuery('#extra-fields').hide();
    119                                         jQuery('#extra-fields').html('');
    120                                 });
    121                         </script>
    122                         <div class="postbox">
    123                                 <h2><label for="embed-code"><?php _e('Embed Code') ?></label></h2>
    124                                 <div class="inside">
    125                                         <textarea name="embed-code" id="embed-code" rows="8" cols="40"><?php echo esc_textarea( $selection ); ?></textarea>
    126                                         <p id="options"><a href="#" class="select button"><?php _e('Insert Video'); ?></a> <a href="#" class="close button"><?php _e('Cancel'); ?></a></p>
    127                                 </div>
    128                         </div>
    129                         <?php break;
    130 
    131                 case 'photo_thickbox': ?>
    132                         <script type="text/javascript">
    133                                 jQuery('.cancel').click(function() {
    134                                         tb_remove();
    135                                 });
    136                                 jQuery('.select').click(function() {
    137                                         image_selector(this);
    138                                 });
    139                         </script>
    140                         <h3 class="tb"><label for="tb_this_photo_description"><?php _e('Description') ?></label></h3>
    141                         <div class="titlediv">
    142                                 <div class="titlewrap">
    143                                         <input id="tb_this_photo_description" name="photo_description" class="tb_this_photo_description tbtitle text" type="text" onkeypress="if(event.keyCode==13) image_selector(this);" value="<?php echo esc_attr($title);?>"/>
    144                                 </div>
    145                         </div>
    146 
    147                         <p class="centered">
    148                                 <input type="hidden" name="this_photo" value="<?php echo esc_attr( $image ); ?>" id="tb_this_photo" class="tb_this_photo" />
    149                                 <a href="#" class="select">
    150                                         <img src="<?php echo esc_url( $image ); ?>" alt="<?php esc_attr_e( 'Click to insert.' ); ?>" title="<?php esc_attr_e( 'Click to insert.' ); ?>" />
    151                                 </a>
    152                         </p>
    153 
    154                         <p id="options"><a href="#" class="select button"><?php _e('Insert Image'); ?></a> <a href="#" class="cancel button"><?php _e('Cancel'); ?></a></p>
    155                         <?php break;
    156         case 'photo_images':
    157                 /**
    158                  * Retrieve all image URLs from given URI.
    159                  *
    160                  * @since 2.6.0
    161                  *
    162                  * @param string $uri
    163                  * @return string
    164                  */
    165                 function get_images_from_uri($uri) {
    166                         $uri = preg_replace('/\/#.+?$/','', $uri);
    167                         if ( preg_match( '/\.(jpe?g|jpe|gif|png)\b/i', $uri ) && !strpos( $uri, 'blogger.com' ) )
    168                                 return "'" . esc_attr( html_entity_decode($uri) ) . "'";
    169                         $content = wp_remote_fopen($uri);
    170                         if ( false === $content )
    171                                 return '';
    172                         $host = parse_url($uri);
    173                         $pattern = '/<img ([^>]*)src=(\"|\')([^<>\'\"]+)(\2)([^>]*)\/*>/i';
    174                         $content = str_replace(array("\n","\t","\r"), '', $content);
    175                         preg_match_all($pattern, $content, $matches);
    176                         if ( empty($matches[0]) )
    177                                 return '';
    178                         $sources = array();
    179                         foreach ($matches[3] as $src) {
    180 
    181                                 // If no http in URL.
    182                                 if (strpos($src, 'http') === false)
    183                                         // If it doesn't have a relative URI.
    184                                         if ( strpos($src, '../') === false && strpos($src, './') === false && strpos($src, '/') === 0)
    185                                                 $src = 'http://'.str_replace('//','/', $host['host'].'/'.$src);
    186                                         else
    187                                                 $src = 'http://'.str_replace('//','/', $host['host'].'/'.dirname($host['path']).'/'.$src);
    188                                 $sources[] = esc_url($src);
    189                         }
    190                         return "'" . implode("','", $sources) . "'";
    191                 }
    192                 $url = wp_kses(urldecode($url), null);
    193                 echo 'new Array('.get_images_from_uri($url).')';
    194                 break;
    195 
    196         case 'photo_js': ?>
    197                 // Gather images and load some default JS.
    198                 var last = null
    199                 var img, img_tag, aspect, w, h, skip, i, strtoappend = "";
    200                 if(photostorage == false) {
    201                 var my_src = eval(
    202                         jQuery.ajax({
    203                                 type: "GET",
    204                                 url: "<?php echo esc_url($_SERVER['PHP_SELF']); ?>",
    205                                 cache : false,
    206                                 async : false,
    207                                 data: "ajax=photo_images&u=<?php echo urlencode($url); ?>",
    208                                 dataType : "script"
    209                         }).responseText
    210                 );
    211                 if(my_src.length == 0) {
    212                         var my_src = eval(
    213                                 jQuery.ajax({
    214                                         type: "GET",
    215                                         url: "<?php echo esc_url($_SERVER['PHP_SELF']); ?>",
    216                                         cache : false,
    217                                         async : false,
    218                                         data: "ajax=photo_images&u=<?php echo urlencode($url); ?>",
    219                                         dataType : "script"
    220                                 }).responseText
    221                         );
    222                         if(my_src.length == 0) {
    223                                 strtoappend = '<?php _e('Unable to retrieve images or no images on page.'); ?>';
    224                         }
    225                 }
    226                 }
    227                 for (i = 0; i < my_src.length; i++) {
    228                         img = new Image();
    229                         img.src = my_src[i];
    230                         img_attr = 'id="img' + i + '"';
    231                         skip = false;
    232 
    233                         maybeappend = '<a href="?ajax=photo_thickbox&amp;i=' + encodeURIComponent(img.src) + '&amp;u=<?php echo urlencode($url); ?>&amp;height=400&amp;width=500" title="" class="thickbox"><img src="' + img.src + '" ' + img_attr + '/></a>';
    234 
    235                         if (img.width && img.height) {
    236                                 if (img.width >= 30 && img.height >= 30) {
    237                                         aspect = img.width / img.height;
    238                                         scale = (aspect > 1) ? (71 / img.width) : (71 / img.height);
    239 
    240                                         w = img.width;
    241                                         h = img.height;
    242 
    243                                         if (scale < 1) {
    244                                                 w = parseInt(img.width * scale);
    245                                                 h = parseInt(img.height * scale);
    246                                         }
    247                                         img_attr += ' style="width: ' + w + 'px; height: ' + h + 'px;"';
    248                                         strtoappend += maybeappend;
    249                                 }
    250                         } else {
    251                                 strtoappend += maybeappend;
    252                         }
    253                 }
    254 
    255                 function pick(img, desc) {
    256                         if (img) {
    257                                 if('object' == typeof jQuery('.photolist input') && jQuery('.photolist input').length != 0) length = jQuery('.photolist input').length;
    258                                 if(length == 0) length = 1;
    259                                 jQuery('.photolist').append('<input name="photo_src[' + length + ']" value="' + img +'" type="hidden"/>');
    260                                 jQuery('.photolist').append('<input name="photo_description[' + length + ']" value="' + desc +'" type="hidden"/>');
    261                                 insert_editor( "\n\n" + encodeURI('<p style="text-align: center;"><a href="<?php echo $url; ?>"><img src="' + img +'" alt="' + desc + '" /></a></p>'));
    262                         }
    263                         return false;
    264                 }
    265 
    266                 function image_selector(el) {
    267                         var desc, src, parent = jQuery(el).closest('#photo-add-url-div');
    268 
    269                         if ( parent.length ) {
    270                                 desc = parent.find('input.tb_this_photo_description').val() || '';
    271                                 src = parent.find('input.tb_this_photo').val() || ''
    272                         } else {
    273                                 desc = jQuery('#tb_this_photo_description').val() || '';
    274                                 src = jQuery('#tb_this_photo').val() || ''
    275                         }
    276 
    277                         tb_remove();
    278                         pick(src, desc);
    279                         jQuery('#extra-fields').hide();
    280                         jQuery('#extra-fields').html('');
    281                         return false;
    282                 }
    283 
    284                 jQuery('#extra-fields').html('<div class="postbox"><h2><?php _e( 'Add Photos' ); ?> <small id="photo_directions">(<?php _e("click images to select") ?>)</small></h2><ul class="actions"><li><a href="#" id="photo-add-url" class="button button-small"><?php _e("Add from URL") ?> +</a></li></ul><div class="inside"><div class="titlewrap"><div id="img_container"></div></div><p id="options"><a href="#" class="close button"><?php _e('Cancel'); ?></a><a href="#" class="refresh button"><?php _e('Refresh'); ?></a></p></div>');
    285                 jQuery('#img_container').html(strtoappend);
    286                 <?php break;
    287 }
    288 die;
    289 }
    290 
    291         wp_enqueue_style( 'colors' );
    292         wp_enqueue_script( 'post' );
    293         add_thickbox();
    294         _wp_admin_html_begin();
    295 ?>
    296 <title><?php _e('Press This') ?></title>
    297 <script type="text/javascript">
    298 addLoadEvent = function(func){if(typeof jQuery!="undefined")jQuery(document).ready(func);else if(typeof wpOnload!='function'){wpOnload=func;}else{var oldonload=wpOnload;wpOnload=function(){oldonload();func();}}};
    299 var ajaxurl = '<?php echo admin_url( 'admin-ajax.php', 'relative' ); ?>', pagenow = 'press-this', isRtl = <?php echo (int) is_rtl(); ?>;
    300 var photostorage = false;
    301 </script>
    302 
    303 <?php
    304         /** This action is documented in wp-admin/admin-header.php */
    305         do_action( 'admin_enqueue_scripts', 'press-this.php' );
    306 
    307         /**
    308          * Fires when styles are printed for the Press This admin page.
    309          *
    310          * @since 3.7.0
    311          */
    312         do_action( 'admin_print_styles-press-this.php' );
    313 
    314         /** This action is documented in wp-admin/admin-header.php */
    315         do_action( 'admin_print_styles' );
    316 
    317         /**
    318          * Fires when scripts are printed for the Press This admin page.
    319          *
    320          * @since 3.7.0
    321          */
    322         do_action( 'admin_print_scripts-press-this.php' );
    323 
    324         /** This action is documented in wp-admin/admin-header.php */
    325         do_action( 'admin_print_scripts' );
    326 
    327         /**
    328          * Fires in the head tag on the Press This admin page.
    329          *
    330          * @since 3.7.0
    331          */
    332         do_action( 'admin_head-press-this.php' );
    333 
    334         /** This action is documented in wp-admin/admin-header.php */
    335         do_action( 'admin_head' );
    336 ?>
    337         <script type="text/javascript">
    338         var wpActiveEditor = 'content';
    339 
    340         function insert_plain_editor(text) {
    341                 if ( typeof(QTags) != 'undefined' )
    342                         QTags.insertContent(text);
    343         }
    344         function set_editor(text) {
    345                 if ( '' == text || '<p></p>' == text )
    346                         text = '<p><br /></p>';
    347 
    348                 if ( tinyMCE.activeEditor )
    349                         tinyMCE.execCommand('mceSetContent', false, text);
    350         }
    351         function insert_editor(text) {
    352                 if ( '' != text && tinyMCE.activeEditor && ! tinyMCE.activeEditor.isHidden()) {
    353                         tinyMCE.execCommand('mceInsertContent', false, '<p>' + decodeURI(tinymce.DOM.decode(text)) + '</p>', {format : 'raw'});
    354                 } else {
    355                         insert_plain_editor(decodeURI(text));
    356                 }
    357         }
    358         function append_editor(text) {
    359                 if ( '' != text && tinyMCE.activeEditor && ! tinyMCE.activeEditor.isHidden()) {
    360                         tinyMCE.execCommand('mceSetContent', false, tinyMCE.activeEditor.getContent({format : 'raw'}) + '<p>' + text + '</p>');
    361                 } else {
    362                         insert_plain_editor(text);
    363                 }
    364         }
    365 
    366         function show(tab_name) {
    367                 jQuery('#extra-fields').html('');
    368                 switch(tab_name) {
    369                         case 'video' :
    370                                 jQuery('#extra-fields').load('<?php echo esc_url($_SERVER['PHP_SELF']); ?>', { ajax: 'video', s: '<?php echo esc_attr($selection); ?>'}, function() {
    371                                         <?php
    372                                         $content = '';
    373                                         if ( preg_match("/youtube\.com\/watch/i", $url) ) {
    374                                                 list($domain, $video_id) = explode("v=", $url);
    375                                                 $video_id = esc_attr($video_id);
    376                                                 $content = '<object width="425" height="350"><param name="movie" value="http://www.youtube.com/v/' . $video_id . '"></param><param name="wmode" value="transparent"></param><embed src="http://www.youtube.com/v/' . $video_id . '" type="application/x-shockwave-flash" wmode="transparent" width="425" height="350"></embed></object>';
    377 
    378                                         } elseif ( preg_match("/vimeo\.com\/[0-9]+/i", $url) ) {
    379                                                 list($domain, $video_id) = explode(".com/", $url);
    380                                                 $video_id = esc_attr($video_id);
    381                                                 $content = '<object width="400" height="225"><param name="allowfullscreen" value="true" /><param name="allowscriptaccess" value="always" /><param name="movie" value="http://www.vimeo.com/moogaloop.swf?clip_id=' . $video_id . '&amp;server=www.vimeo.com&amp;show_title=1&amp;show_byline=1&amp;show_portrait=0&amp;color=&amp;fullscreen=1" />      <embed src="http://www.vimeo.com/moogaloop.swf?clip_id=' . $video_id . '&amp;server=www.vimeo.com&amp;show_title=1&amp;show_byline=1&amp;show_portrait=0&amp;color=&amp;fullscreen=1" type="application/x-shockwave-flash" allowfullscreen="true" allowscriptaccess="always" width="400" height="225"></embed></object>';
    382 
    383                                                 if ( trim($selection) == '' )
    384                                                         $selection = '<p><a href="http://www.vimeo.com/' . $video_id . '?pg=embed&sec=' . $video_id . '">' . $title . '</a> on <a href="http://vimeo.com?pg=embed&sec=' . $video_id . '">Vimeo</a></p>';
    385 
    386                                         } elseif ( strpos( $selection, '<object' ) !== false ) {
    387                                                 $content = $selection;
    388                                         }
    389                                         ?>
    390                                         jQuery('#embed-code').prepend('<?php echo htmlentities($content); ?>');
    391                                 });
    392                                 jQuery('#extra-fields').show();
    393                                 return false;
    394                                 break;
    395                         case 'photo' :
    396                                 function setup_photo_actions() {
    397                                         jQuery('.close').click(function() {
    398                                                 jQuery('#extra-fields').hide();
    399                                                 jQuery('#extra-fields').html('');
    400                                         });
    401                                         jQuery('.refresh').click(function() {
    402                                                 photostorage = false;
    403                                                 show('photo');
    404                                         });
    405                                         jQuery('#photo-add-url').click(function(){
    406                                                 var form = jQuery('#photo-add-url-div').clone();
    407                                                 jQuery('#img_container').empty().append( form.show() );
    408                                         });
    409                                         jQuery('#waiting').hide();
    410                                         jQuery('#extra-fields').show();
    411                                 }
    412 
    413                                 jQuery('#waiting').show();
    414                                 if(photostorage == false) {
    415                                         jQuery.ajax({
    416                                                 type: "GET",
    417                                                 cache : false,
    418                                                 url: "<?php echo esc_url($_SERVER['PHP_SELF']); ?>",
    419                                                 data: "ajax=photo_js&u=<?php echo urlencode($url)?>",
    420                                                 dataType : "script",
    421                                                 success : function(data) {
    422                                                         eval(data);
    423                                                         photostorage = jQuery('#extra-fields').html();
    424                                                         setup_photo_actions();
    425                                                 }
    426                                         });
    427                                 } else {
    428                                         jQuery('#extra-fields').html(photostorage);
    429                                         setup_photo_actions();
    430                                 }
    431                                 return false;
    432                                 break;
    433                 }
    434         }
    435         jQuery(document).ready(function($) {
    436                 var $contnet = $( '#content' );
    437 
    438                 // Resize screen.
    439                 window.resizeTo(760,580);
    440 
    441                 // Set button actions.
    442                 jQuery('#photo_button').click(function() { show('photo'); return false; });
    443                 jQuery('#video_button').click(function() { show('video'); return false; });
    444 
    445                 // Auto select.
    446                 <?php if ( preg_match("/youtube\.com\/watch/i", $url) ) { ?>
    447                         show('video');
    448                 <?php } elseif ( preg_match("/vimeo\.com\/[0-9]+/i", $url) ) { ?>
    449                         show('video');
    450                 <?php } elseif ( preg_match("/flickr\.com/i", $url) ) { ?>
    451                         show('photo');
    452                 <?php } ?>
    453                 jQuery('#title').unbind();
    454                 jQuery('#publish, #save').click(function() { jQuery('.press-this #publishing-actions .spinner').css('display', 'inline-block'); });
    455 
    456                 $('#tagsdiv-post_tag, #categorydiv').children('h3, .handlediv').click(function(){
    457                         $(this).siblings('.inside').toggle();
    458                 });
    459 
    460                 if ( $( '#wp-content-wrap' ).hasClass( 'html-active' ) && window.switchEditors &&
    461                         ( tinyMCEPreInit.mceInit.content && tinyMCEPreInit.mceInit.content.wpautop ) ) {
    462                         // The Text editor is default, run the initial content through pre_wpautop() to convert the paragraphs
    463                         $contnet.text( window.switchEditors.pre_wpautop( $contnet.text() ) );
    464                 }
    465         });
    466 </script>
    467 </head>
    468 <?php
    469 $admin_body_class = ( is_rtl() ) ? 'rtl' : '';
    470 $admin_body_class .= ' locale-' . sanitize_html_class( strtolower( str_replace( '_', '-', get_locale() ) ) );
    471 ?>
    472 <body class="press-this wp-admin wp-core-ui <?php echo $admin_body_class; ?>">
    473 <form action="press-this.php?action=post" method="post">
    474 <div id="poststuff" class="metabox-holder">
    475         <div id="side-sortables" class="press-this-sidebar">
    476                 <div class="sleeve">
    477                         <?php wp_nonce_field('press-this') ?>
    478                         <input type="hidden" name="post_type" id="post_type" value="text"/>
    479                         <input type="hidden" name="autosave" id="autosave" />
    480                         <input type="hidden" id="original_post_status" name="original_post_status" value="draft" />
    481                         <input type="hidden" id="prev_status" name="prev_status" value="draft" />
    482                         <input type="hidden" id="post_id" name="post_id" value="<?php echo (int) $post_ID; ?>" />
    483 
    484                         <!-- This div holds the photo metadata -->
    485                         <div class="photolist"></div>
    486 
    487                         <div id="submitdiv" class="postbox">
    488                                 <div class="handlediv" title="<?php esc_attr_e( 'Click to toggle' ); ?>"><br /></div>
    489                                 <h3 class="hndle"><?php _e('Press This') ?></h3>
    490                                 <div class="inside">
    491                                         <p id="publishing-actions">
    492                                         <?php
    493                                                 submit_button( __( 'Save Draft' ), 'button', 'draft', false, array( 'id' => 'save' ) );
    494                                                 if ( current_user_can('publish_posts') ) {
    495                                                         submit_button( __( 'Publish' ), 'primary', 'publish', false );
    496                                                 } else {
    497                                                         echo '<br /><br />';
    498                                                         submit_button( __( 'Submit for Review' ), 'primary', 'review', false );
    499                                                 } ?>
    500                                                 <span class="spinner" style="display: none;"></span>
    501                                         </p>
    502                                         <?php if ( current_theme_supports( 'post-formats' ) && post_type_supports( 'post', 'post-formats' ) ) :
    503                                                         $post_formats = get_theme_support( 'post-formats' );
    504                                                         if ( is_array( $post_formats[0] ) ) :
    505                                                                 $default_format = get_option( 'default_post_format', '0' );
    506                                                 ?>
    507                                         <p>
    508                                                 <label for="post_format"><?php _e( 'Post Format:' ); ?>
    509                                                 <select name="post_format" id="post_format">
    510                                                         <option value="0"><?php echo get_post_format_string( 'standard' ); ?></option>
    511                                                 <?php foreach ( $post_formats[0] as $format ): ?>
    512                                                         <option<?php selected( $default_format, $format ); ?> value="<?php echo esc_attr( $format ); ?>"> <?php echo esc_html( get_post_format_string( $format ) ); ?></option>
    513                                                 <?php endforeach; ?>
    514                                                 </select></label>
    515                                         </p>
    516                                         <?php endif; endif; ?>
    517                                 </div>
    518                         </div>
    519 
    520                         <?php $tax = get_taxonomy( 'category' ); ?>
    521                         <div id="categorydiv" class="postbox">
    522                                 <div class="handlediv" title="<?php esc_attr_e( 'Click to toggle' ); ?>"><br /></div>
    523                                 <h3 class="hndle"><?php _e('Categories') ?></h3>
    524                                 <div class="inside">
    525                                 <div id="taxonomy-category" class="categorydiv">
    526 
    527                                         <ul id="category-tabs" class="category-tabs">
    528                                                 <li class="tabs"><a href="#category-all"><?php echo $tax->labels->all_items; ?></a></li>
    529                                                 <li class="hide-if-no-js"><a href="#category-pop"><?php _e( 'Most Used' ); ?></a></li>
    530                                         </ul>
    531 
    532                                         <div id="category-pop" class="tabs-panel" style="display: none;">
    533                                                 <ul id="categorychecklist-pop" class="categorychecklist form-no-clear" >
    534                                                         <?php $popular_ids = wp_popular_terms_checklist( 'category' ); ?>
    535                                                 </ul>
    536                                         </div>
    537 
    538                                         <div id="category-all" class="tabs-panel">
    539                                                 <ul id="categorychecklist" data-wp-lists="list:category" class="categorychecklist form-no-clear">
    540                                                         <?php wp_terms_checklist($post_ID, array( 'taxonomy' => 'category', 'popular_cats' => $popular_ids ) ) ?>
    541                                                 </ul>
    542                                         </div>
    543 
    544                                         <?php if ( !current_user_can($tax->cap->assign_terms) ) : ?>
    545                                         <p><em><?php _e('You cannot modify this Taxonomy.'); ?></em></p>
    546                                         <?php endif; ?>
    547                                         <?php if ( current_user_can($tax->cap->edit_terms) ) : ?>
    548                                                 <div id="category-adder" class="wp-hidden-children">
    549                                                         <h4>
    550                                                                 <a id="category-add-toggle" href="#category-add" class="hide-if-no-js">
    551                                                                         <?php printf( __( '+ %s' ), $tax->labels->add_new_item ); ?>
    552                                                                 </a>
    553                                                         </h4>
    554                                                         <p id="category-add" class="category-add wp-hidden-child">
    555                                                                 <label class="screen-reader-text" for="newcategory"><?php echo $tax->labels->add_new_item; ?></label>
    556                                                                 <input type="text" name="newcategory" id="newcategory" class="form-required form-input-tip" value="<?php echo esc_attr( $tax->labels->new_item_name ); ?>" aria-required="true"/>
    557                                                                 <label class="screen-reader-text" for="newcategory_parent">
    558                                                                         <?php echo $tax->labels->parent_item_colon; ?>
    559                                                                 </label>
    560                                                                 <?php wp_dropdown_categories( array( 'taxonomy' => 'category', 'hide_empty' => 0, 'name' => 'newcategory_parent', 'orderby' => 'name', 'hierarchical' => 1, 'show_option_none' => '&mdash; ' . $tax->labels->parent_item . ' &mdash;' ) ); ?>
    561                                                                 <input type="button" id="category-add-submit" data-wp-lists="add:categorychecklist:category-add" class="button category-add-submit" value="<?php echo esc_attr( $tax->labels->add_new_item ); ?>" />
    562                                                                 <?php wp_nonce_field( 'add-category', '_ajax_nonce-add-category', false ); ?>
    563                                                                 <span id="category-ajax-response"></span>
    564                                                         </p>
    565                                                 </div>
    566                                         <?php endif; ?>
    567                                 </div>
    568                                 </div>
    569                         </div>
    570 
    571                         <div id="tagsdiv-post_tag" class="postbox">
    572                                 <div class="handlediv" title="<?php esc_attr_e( 'Click to toggle' ); ?>"><br /></div>
    573                                 <h3><span><?php _e('Tags'); ?></span></h3>
    574                                 <div class="inside">
    575                                         <div class="tagsdiv" id="post_tag">
    576                                                 <div class="jaxtag">
    577                                                         <label class="screen-reader-text" for="newtag"><?php _e('Tags'); ?></label>
    578                                                         <input type="hidden" name="tax_input[post_tag]" class="the-tags" id="tax-input[post_tag]" value="" />
    579                                                         <div class="ajaxtag">
    580                                                                 <input type="text" name="newtag[post_tag]" class="newtag form-input-tip" size="16" autocomplete="off" value="" />
    581                                                                 <input type="button" class="button tagadd" value="<?php esc_attr_e('Add'); ?>" />
    582                                                         </div>
    583                                                 </div>
    584                                                 <div class="tagchecklist"></div>
    585                                         </div>
    586                                         <p class="tagcloud-link"><a href="#titlediv" class="tagcloud-link" id="link-post_tag"><?php _e('Choose from the most used tags'); ?></a></p>
    587                                 </div>
    588                         </div>
    589                 </div>
    590         </div>
    591         <div class="posting">
    592 
    593                 <div id="wphead">
    594                         <h1 id="site-heading">
    595                                 <a href="<?php echo get_option('home'); ?>/" target="_blank">
    596                                         <span id="site-title"><?php bloginfo('name'); ?></span>
    597                                 </a>
    598                         </h1>
    599                 </div>
    600 
    601                 <?php
    602                 if ( isset($posted) && intval($posted) ) {
    603                         $post_ID = intval($posted); ?>
    604                         <div id="message" class="updated">
    605                         <p><strong><?php _e('Your post has been saved.'); ?></strong>
    606                         <a onclick="window.opener.location.replace(this.href); window.close();" href="<?php echo get_permalink($post_ID); ?>"><?php _e('View post'); ?></a>
    607                         | <a href="<?php echo get_edit_post_link( $post_ID ); ?>" onclick="window.opener.location.replace(this.href); window.close();"><?php _e('Edit Post'); ?></a>
    608                         | <a href="#" onclick="window.close();"><?php _e('Close Window'); ?></a></p>
    609                         </div>
    610                 <?php } ?>
    611 
    612                 <div id="titlediv">
    613                         <div class="titlewrap">
    614                                 <input name="title" id="title" class="text" type="text" value="<?php echo esc_attr($title);?>"/>
    615                         </div>
    616                 </div>
    617 
    618                 <div id="waiting" style="display: none"><span class="spinner"></span> <span><?php esc_html_e( 'Loading&hellip;' ); ?></span></div>
    619 
    620                 <div id="extra-fields" style="display: none"></div>
    621 
    622                 <div class="postdivrich">
    623                 <?php
    624 
    625                 $editor_settings = array(
    626                         'teeny' => true,
    627                         'textarea_rows' => '15'
    628                 );
    629 
    630                 $content = '';
    631                 if ( $selection )
    632                         $content .= $selection;
    633 
    634                 if ( $url ) {
    635                         $content .= '<p>';
    636 
    637                         if ( $selection )
    638                                 $content .= __('via ');
    639 
    640                         $content .= sprintf( "<a href='%s'>%s</a>.</p>", esc_url( $url ), esc_html( $title ) );
    641                 }
    642 
    643                 remove_action( 'media_buttons', 'media_buttons' );
    644                 add_action( 'media_buttons', 'press_this_media_buttons' );
    645                 function press_this_media_buttons() {
    646                         _e( 'Add:' );
    647 
    648                         if ( current_user_can('upload_files') ) {
    649                                 ?>
    650                                 <a id="photo_button" title="<?php esc_attr_e('Insert an Image'); ?>" href="#">
    651                                 <img alt="<?php esc_attr_e('Insert an Image'); ?>" src="<?php echo esc_url( admin_url( 'images/media-button-image.gif?ver=20100531' ) ); ?>"/></a>
    652                                 <?php
    653                         }
    654                         ?>
    655                         <a id="video_button" title="<?php esc_attr_e('Embed a Video'); ?>" href="#"><img alt="<?php esc_attr_e('Embed a Video'); ?>" src="<?php echo esc_url( admin_url( 'images/media-button-video.gif?ver=20100531' ) ); ?>"/></a>
    656                         <?php
    657                 }
    658 
    659                 wp_editor( $content, 'content', $editor_settings );
    660 
    661                 ?>
    662                 </div>
    663         </div>
    664 </div>
    665 </form>
    666 <div id="photo-add-url-div" style="display:none;">
    667         <table><tr>
    668         <td><label for="this_photo"><?php _e('URL') ?></label></td>
    669         <td><input type="text" id="this_photo" name="this_photo" class="tb_this_photo text" onkeypress="if(event.keyCode==13) image_selector(this);" /></td>
    670         </tr><tr>
    671         <td><label for="this_photo_description"><?php _e('Description') ?></label></td>
    672         <td><input type="text" id="this_photo_description" name="photo_description" class="tb_this_photo_description text" onkeypress="if(event.keyCode==13) image_selector(this);" value="<?php echo esc_attr($title);?>"/></td>
    673         </tr><tr>
    674         <td><input type="button" class="button" onclick="image_selector(this)" value="<?php esc_attr_e('Insert Image'); ?>" /></td>
    675         </tr></table>
    676 </div>
    677 <?php
    678 /** This action is documented in wp-admin/admin-footer.php */
    679 do_action( 'admin_footer' );
    680 /** This action is documented in wp-admin/admin-footer.php */
    681 do_action( 'admin_print_footer_scripts' );
    682 ?>
    683 <script type="text/javascript">if(typeof wpOnload=='function')wpOnload();</script>
    684 </body>
    685 </html>
     21$GLOBALS['wp_press_this']->html();
  • src/wp-admin/tools.php

     
    3838<?php if ( current_user_can('edit_posts') ) : ?>
    3939<div class="tool-box">
    4040        <h3 class="title"><?php _e('Press This') ?></h3>
    41         <p><?php _e('Press This is a bookmarklet: a little app that runs in your browser and lets you grab bits of the web.');?></p>
     41        <div class="postbox press-this-install">
     42                <p><?php _e( 'Press This is a little app that lets you grab bits of the web and create new posts with ease.' );?></p>
     43                <p><?php _e( 'Use Press This to clip text, images and videos from any web page. Then edit and add more straight from Press This before you save or publish it in a post on your site.' ); ?></p>
     44        </div>
    4245
    43         <p><?php _e('Use Press This to clip text, images and videos from any web page. Then edit and add more straight from Press This before you save or publish it in a post on your site.'); ?></p>
    44         <p class="description"><?php _e('Drag-and-drop the following link to your bookmarks bar or right click it and add it to your favorites for a posting shortcut.') ?></p>
    45         <p class="pressthis"><a onclick="return false;" oncontextmenu="if(window.navigator.userAgent.indexOf('WebKit')!=-1||window.navigator.userAgent.indexOf('MSIE')!=-1){jQuery('.pressthis-code').show().find('textarea').focus().select();return false;}" href="<?php echo htmlspecialchars( get_shortcut_link() ); ?>"><span><?php _e('Press This') ?></span></a></p>
    46         <div class="pressthis-code" style="display:none;">
    47         <p class="description"><?php _e('If your bookmarks toolbar is hidden: copy the code below, open your Bookmarks manager, create new bookmark, type Press This into the name field and paste the code into the URL field.') ?></p>
    48         <p><textarea rows="5" cols="120" readonly="readonly"><?php echo htmlspecialchars( get_shortcut_link() ); ?></textarea></p>
     46        <form>
     47        <div class="postbox press-this-install">
     48                <h3><?php _e( 'Install Press This' ); ?></h3>
     49                <h4><?php _e( 'Bookmarklet' ); ?></h4>
     50                <p><?php _e( 'Drag the bookmarklet below to your bookmarks bar. Then, when you\'re on a page you want to share, simply "press" it.' ); ?></p>
     51
     52                <p class="pressthis">
     53                        <a class="" onclick="return false;" href="<?php echo htmlspecialchars( get_shortcut_link() ); ?>"><span><?php _e( 'Press This' ) ?></span></a>
     54                        <button type="button" class="button button-secondary js-show-pressthis-code-wrap" aria-expanded="false" aria-controls="pressthis-code-wrap">
     55                                <span class="dashicons dashicons-clipboard"></span>
     56                                <span class="screen-reader-text"><?php _e( 'Copy Press This Bookmarklet' ) ?></span>
     57                        </button>
     58                </p>
     59
     60                <div class="hidden js-pressthis-code-wrap">
     61                        <p id="pressthis-code-desc">
     62                                <?php _e( 'If you can\'t drag it to your bookmarks, copy the following code and create new bookmark. Paste the code into the new bookmark\'s URL field.' ) ?>
     63                        </p>
     64                        <p>
     65                                <textarea class="js-pressthis-code" rows="5" cols="120" readonly="readonly" aria-labelledby="pressthis-code-desc"><?php echo htmlspecialchars( get_shortcut_link() ); ?></textarea>
     66                        </p>
     67                </div>
     68
     69                <h4><?php _e( 'Direct link (best for mobile)' ); ?></h4>
     70                <p><?php _e( 'Follow the link to open Press This. Then add it to your device\'s bookmarks or home screen.' ); ?></p>
     71
     72                <p>
     73                        <a class="button button-secondary" href="<?php echo htmlspecialchars( admin_url( 'press-this.php' ) ); ?>"><?php _e( 'Open Press This' ) ?></a>
     74                </p>
     75                <script>
     76                        jQuery( document ).ready( function( $ ) {
     77                                var $showPressThisWrap = $( '.js-show-pressthis-code-wrap' );
     78                                var $pressthisCode = $( '.js-pressthis-code' );
     79
     80                                $showPressThisWrap.on( 'click', function( event ) {
     81                                        var $this = $( this );
     82
     83                                        $this.parent().next( '.js-pressthis-code-wrap' ).slideToggle( 200 );
     84                                        $this.attr( 'aria-expanded', $this.attr( 'aria-expanded' ) === 'false' ? 'true' : 'false' );
     85                                });
     86
     87                                // Select Press This code when focusing (tabbing) or clicking the textarea.
     88                                $pressthisCode.on( 'click focus', function() {
     89                                        var self = this;
     90                                        setTimeout( function() { self.select(); }, 50 );
     91                                });
     92
     93                        });
     94                </script>
    4995        </div>
     96        </form>
    5097</div>
    5198<?php
    5299endif;
  • src/wp-includes/link-template.php

     
    25942594 * @return string The Press This bookmarklet link URL.
    25952595 */
    25962596function get_shortcut_link() {
    2597         // In case of breaking changes, version this. #WP20071
    2598         $link = "javascript:
    2599                         var d=document,
    2600                         w=window,
    2601                         e=w.getSelection,
    2602                         k=d.getSelection,
    2603                         x=d.selection,
    2604                         s=(e?e():(k)?k():(x?x.createRange().text:0)),
    2605                         f='" . admin_url('press-this.php') . "',
    2606                         l=d.location,
    2607                         e=encodeURIComponent,
    2608                         u=f+'?u='+e(l.href)+'&t='+e(d.title)+'&s='+e(s)+'&v=4';
    2609                         a=function(){if(!w.open(u,'t','toolbar=0,resizable=1,scrollbars=1,status=1,width=720,height=570'))l.href=u;};
    2610                         if (/Firefox/.test(navigator.userAgent)) setTimeout(a, 0); else a();
    2611                         void(0)";
     2597        global $is_IE, $wp_version;
    26122598
    2613         $link = str_replace(array("\r", "\n", "\t"),  '', $link);
     2599        $bookmarklet_version = 5;
     2600        $link = '';
    26142601
     2602        if ( $is_IE ) {
     2603                /**
     2604                 * Return the old/shorter bookmarklet code for MSIE 8 and lower,
     2605                 * since they only support a max length of ~2000 characters for
     2606                 * bookmark[let] URLs, which is way to small for our smarter one.
     2607                 * Do update the version number so users do not get the "upgrade your
     2608                 * bookmarklet" notice when using PT in those browsers.
     2609                 */
     2610                $ua = $_SERVER['HTTP_USER_AGENT'];
     2611               
     2612                if ( ! empty( $ua ) && preg_match( '/\bMSIE (\d)/', $ua, $matches ) && (int) $matches[1] <= 8 ) {
     2613                        $link = "javascript:
     2614                                var d=document,
     2615                                w=window,
     2616                                e=w.getSelection,
     2617                                k=d.getSelection,
     2618                                x=d.selection,
     2619                                s=(e?e():(k)?k():(x?x.createRange().text:0)),
     2620                                f='" . admin_url('press-this.php') . "',
     2621                                l=d.location,
     2622                                e=encodeURIComponent,
     2623                                u=f+'?u='+e(l.href)+'&t='+e(d.title)+'&s='+e(s)+'&v=" . $bookmarklet_version . "';
     2624                                a=function(){if(!w.open(u,'t','toolbar=0,resizable=1,scrollbars=1,status=1,width=600,height=700'))l.href=u;};
     2625                                if (/Firefox/.test(navigator.userAgent)) setTimeout(a, 0); else a();
     2626                                void(0)";
     2627                }
     2628        }
     2629
     2630        if ( empty( $link ) ) {
     2631                $suffix = '.min';
     2632                $develop_src = false !== strpos( $wp_version, '-src' );
     2633
     2634                if ( $develop_src || ( defined( 'SCRIPT_DEBUG' ) && SCRIPT_DEBUG ) ) {
     2635                        $suffix = '';
     2636                }
     2637
     2638                $url = admin_url( 'press-this.php' ) . '?v=' . $bookmarklet_version;
     2639
     2640                $link = 'javascript:' . file_get_contents( ABSPATH . "wp-admin/js/bookmarklet$suffix.js" );
     2641                $link = str_replace( 'window.pt_url', wp_json_encode( $url ), $link );
     2642        }
     2643
     2644        $link = str_replace( array( "\r", "\n", "\t" ),  '', $link );
     2645
    26152646        /**
    26162647         * Filter the Press This bookmarklet link.
    26172648         *
  • src/wp-includes/script-loader.php

     
    439439
    440440                $scripts->add( 'postbox', "/wp-admin/js/postbox$suffix.js", array('jquery-ui-sortable'), false, 1 );
    441441
    442                 $scripts->add( 'post', "/wp-admin/js/post$suffix.js", array('suggest', 'wp-lists', 'postbox'), false, 1 );
     442                $scripts->add( 'tags-box', "/wp-admin/js/tags-box$suffix.js", array( 'jquery', 'suggest' ), false, 1 );
     443                did_action( 'init' ) && $scripts->localize( 'tags-box', 'tagsBoxL10n', array(
     444                        'tagDelimiter' => _x( ',', 'tag delimiter' ),
     445                ) );
     446
     447                $scripts->add( 'post', "/wp-admin/js/post$suffix.js", array( 'suggest', 'wp-lists', 'postbox', 'tags-box' ), false, 1 );
    443448                did_action( 'init' ) && $scripts->localize( 'post', 'postL10n', array(
    444449                        'ok' => __('OK'),
    445450                        'cancel' => __('Cancel'),
     
    461466                        'password' => __('Password Protected'),
    462467                        'privatelyPublished' => __('Privately Published'),
    463468                        'published' => __('Published'),
    464                         'comma' => _x( ',', 'tag delimiter' ),
    465469                        'saveAlert' => __('The changes you made will be lost if you navigate away from this page.'),
    466470                        'savingText' => __('Saving Draft&#8230;'),
    467471                ) );
    468472
     473                $scripts->add( 'press-this', "/wp-admin/js/press-this$suffix.js", array( 'jquery', 'tags-box' ), false, 1 );
     474                did_action( 'init' ) && $scripts->localize( 'press-this', 'pressThisL10n', array(
     475                        /**
     476                         * press_this_source_string: string displayed before the source attribution string, defaults to "Source:".
     477                         *
     478                         * @since 4.2
     479                         * @see https://github.com/MichaelArestad/Press-This/issues/25
     480                         *
     481                         * @param string $string Internationalized source string
     482                         *
     483                         * @return string Source string
     484                         */
     485                        'source' => apply_filters( 'press_this_source_string', __( 'Source:' ) ),
     486
     487                        /**
     488                         * press_this_source_link: HTML link format for the source attribution, can control target, class, etc
     489                         *
     490                         * @since 4.2
     491                         * @see https://github.com/MichaelArestad/Press-This/issues/25
     492                         *
     493                         * @param string $link_format Internationalized link format, %1$s is link href, %2$s is link text
     494                         *
     495                         * @return string Link markup
     496                         */
     497                        'sourceLink' => apply_filters( 'press_this_source_link', __( '<a href="%1$s">%2$s</a>' ) ),
     498                        'newPost' => __( 'Title' ),
     499                        'unexpectedError' => __( 'Sorry, but an unexpected error occurred.' ),
     500                        'saveAlert' => __( 'The changes you made will be lost if you navigate away from this page.' ),
     501                        'allMediaHeading' => __( 'Suggested media' ),
     502                        'suggestedEmbedAlt' => __( 'Suggested embed #%d' ),
     503                        'suggestedImgAlt' => __( 'Suggested image #%d' ),
     504                ) );
     505               
    469506                $scripts->add( 'editor-expand', "/wp-admin/js/editor-expand$suffix.js", array( 'jquery' ), false, 1 );
    470507
    471508                $scripts->add( 'link', "/wp-admin/js/link$suffix.js", array( 'wp-lists', 'postbox' ), false, 1 );
     
    633670        $styles->add( 'wp-color-picker',    "/wp-admin/css/color-picker$suffix.css" );
    634671        $styles->add( 'customize-controls', "/wp-admin/css/customize-controls$suffix.css", array( 'wp-admin', 'colors', 'ie', 'imgareaselect' ) );
    635672        $styles->add( 'customize-widgets',  "/wp-admin/css/customize-widgets$suffix.css", array( 'wp-admin', 'colors' ) );
     673        $styles->add( 'press-this',         "/wp-admin/css/press-this$suffix.css", array( 'open-sans' ) );
     674
    636675        $styles->add( 'ie',                 "/wp-admin/css/ie$suffix.css" );
    637 
    638676        $styles->add_data( 'ie', 'conditional', 'lte IE 7' );
    639677
    640678        // Common dependencies